Makefile

2013.08.25/2014.06.23发布于笔记暂无评论/目录

总结GNU Make的一些基础知识和技巧,以下内容均基于Ubuntu 14.04 x86_64平台的GNU Make 3.81。

变量

变量类型

make支持两种风格的变量定义方式:

  1. 递归扩展变量(recursively expanded variables)
    • 使用=或define指令定义
    • CFLAGS = $(CFLAGS) -O会导致无限递归
    • 变量每次被展开的时候,所使用的函数都会重新被执行,会降低make的效率,更严重的是,wildcard等函数会返回预料之外的结果。
  2. 简单扩展变量(simply expanded varialbes)
    • 使用:=::=定义
    • 在变量定义的时候展开一次

shell变量

shell变量应使用@转义,比如变量${var}要改为$${var}

自动变量

下面是一些常用的自动变量

  • $<: 第一个依赖
  • $@: 目标
  • $?: 修改时间在目标之后的依赖
  • $^: 所有的依赖
  • $|: 所有的order-only依赖

预定义变量

Makefile中预定义了一些变量,通常用于Implicit Rules中,详细列表见Implicit Variables。其中常用的有:

  • CC: C编译器,默认是cc
  • CXX: C++编译器,默认是g++
  • CFLAGS: 传递给C编译器的编译参数
  • CXXFLAGS: 传递给C++编译器的编译参数
  • LDFLAGS: 传递给编译器的链接参数

函数和指令

wildcard

wildcard函数和直接使用通配符*的区别在于,wildcard函数可以在变量定义的时候立即展开(即搜索相应的匹配项),而*只有在规则中才会被展开。[1]

subst

sust函数用于替换字符串,格式是$(subst from,to,text),其中from是要被替换的子串,to是要替换成的子串,text是需要被替换的字符串。注意from和to之间的逗号左右不要随意添加空格,否则也会被视为需要替换的内容。

patsubst

patsubst函数可以匹配并保留第一个没被\转义的%字符,然后在替换成的字符串里使用,比如$(patsubst %.css,%.min.css,hello.css)替换的结果就是hello.min.css,同样的,逗号左右不要随意添加空白字符。类似的功能还可以用Substitution References实现,语法更简洁一些。

filter

filter函数可以按一定的规则过滤目标,然后返回符合规则的数据。

规则

order-only依赖

a: b | c
    command

上面的例子中,a是目标,b是常规依赖,c是order-only依赖。当a存在时,即便c的修改时间晚于a,该规则也不会更新a。

order-only依赖定义于规则的右侧,与常规依赖用|隔开。当目标存在时,不管其是否因order-only依赖而过期,均不更新目标。

static pattern rules

当有大量类似的目标时,static pattern rules会很有用,格式如下

targets ...: target-pattern: prereq-patterns ...
    recipe
    ...

用下面的一个简单例子说明

a.o b.o c.o: %.o: %.c
    gcc -c -o $@ $<

这里targets是a.o b.o c.o,target pattern是%.o,prereq-patterns(只有一个)是%.c。target pattern首先从targets里匹配出一个个的目标,然后按照类似于patsubst的方式替换prereq-patterns中的%字符,生成相应的依赖。在规则的命令中,可以像普通的规则一样使用$<(第一个依赖)和$@(目标)等自动变量

上面的规则可以拆成几条简单的规则:

a.o:a.c
    gcc -c -o $@ $<

b.o:b.c
    gcc -c -o $@ $<

c.o:c.c
    gcc -c -o $@ $<

或者使用字符串替换,更方便一些

source = ${wildcard *.c}
objects = ${source:%.c=%.o}

all: ${objects}

${objects}: %.o: %.c
    gcc -c -o $@ $<

其它

特殊的符号

  • @: 用于规则中的命令之前,可以在make时只显示命令的输出而不显示命令的内容,比如

      all:
          @echo hello
    

    只输出hello,不会输出echo hello这条命令的内容。

传递变量

可以在命令行里设置Makefile的变量值,对如下的makefile

TARGET=test
create:
    mkdir -p ${TARGET}

可以通过如下的命令修改TARGET的值:

make create TARGET=another

.PHONY伪目标

尽可能使用.PHONY标示所有的伪目标,以避免潜在的问题。

常见的一例问题如下:

debug:
    mkdir -p debug
    cd debug && make -f ../Makefile

使用make debug命令时可能会提示Nothing to be done for debug,即debug目标已是最新。然而debug是伪目标,理论上每次执行都应该运行才对。问题出在debug目标的名称和本地的debug文件夹同名,使用.PHONY标识debug为伪目标即可解决此问题。

.PHONY: debug

不要滥用空白字符

  1. objects = a.o b.o # object files,这里${objects}变量的值是`a.o b.o '(包含后面的一个空格)。
  2. str = ${subst abc, 123,abcde},这里${str}变量的值是` 123de'(包含开头的一个空格)。
  3. str = ${subst abc ,123,abcde},这里${str}变量的值还是abcde,因为要替换的子串abc(包含最后的空格)没有找到。

例子

假设有以下的场景,在src目录下有许多css文件,我们需要用yui-compressor将其压缩并输出到output目录,压缩后css的文件名不变,文件名后缀由.css改为.min.css,可以如下定义makefile

# 查找所有的css源文件
CSS_SOURCE_FILES = ${wildcard src/*.css}
# 构建目标名
CSS_OUTPUT_FILES = ${subst src/,output/,${patsubst %.css,%.min.css,${CSS_SOURCE_FILES}}}

all: ${CSS_OUTPUT_FILES}

${CSS_OUTPUT_FILES}: output/%.min.css: src/%.css
    @[ -d output ] || mkdir -p output
    yui-compressor -o $@ $<

clean:
    rm -rf output

.PHONY: all clean

阅读资料


  1. GNU Make Manual: The Function wildcard,引用于2014-05-12。

#makefile#总结

评论