5、编译时的参数
还有一个扩展就是,编译时的参数, -g,-Wall 这些,可以放在 makefile 里面
修改后 makefile 如下:
Makefile 第十版
src = $(wildcard *.c) # hello.c add.c sub.c mul.c div.c obj = $(patsubst %.c, %.o, $(src)) # hello.o add.o sub.o mul.o div.o myArgs = -Wall -g ALL : a.out $(obj) : %.o : %.c gcc -c $< -o $@ $(myArgs) a.out : $(obj) gcc $^ -o $@ $(myArgs) clean : -rm -rf $(obj) a.out .PHONY : clean ALL
执行 make,如下:
6、make 的参数
参数:
- -n:模拟执行 make、 make clean 命令。
- -f:指定文件执行 make 命令。 xxxx.mk
如果 makefile 的名字变化一下,比如,叫 m6
用 m6 执行 makefile, make -f m6
用 m6 执行 clean make -f m6 clean
四、工程源码优化
将上述 .c 文件都放到 src 目录中,.h 文件都放在 inc 目录中,所生成的 .o 文件产物都放在 obj 目录中
使用 tree 命令查看树形结构拓扑
修改 makefile 如下,主要是注意 % 的匹配理解,只匹配文件名,目录位置要手动添加
Makefile 第十一版
src = $(wildcard ./src/*.c) # ./src/hello.c ./src/add.c ... obj = $(patsubst ./src/%.c, ./obj/%.o, $(src)) # ./obj/hello.o ./obj/add.o ... inc_path = ./inc myArgs = -Wall -g ALL : a.out $(obj) : ./obj/%.o : ./src/%.c gcc -c $< -o $@ $(myArgs) -I $(inc_path) a.out : $(obj) gcc $^ -o $@ $(myArgs) clean : -rm -rf $(obj) a.out .PHONY : clean ALL
执行 make 和 make clean,效果如下
如果 makefile 的名字变化一下,比如,叫 m6
用 m6 执行 makefile, make -f m6
用 m6 执行 clean make -f m6 clean
五、Makefile 语法
1、两种变量
在 Makefile 中有两种变量,一种称为即时变量(简单变量),另一种称为延时变量
- 即时变量(简单变量)
A := xxx # A 的值即刻确定,在定义时即确定
- 延时变量
B = xxx # B 的值使用到时才确定
编写下面一个 Makefile
A := abc B = 123 all : @echo $(A) @echo $(B)
执行 make
从上面我们看不出即时变量和延时变量的差别,我们再对 Makefile 进行如下修改:
A := $(C) B = $(C) C = abc all : @echo A = $(A) @echo B = $(B)
执行 make
可以看到 A 的值为空,B 的值为 abc,因为 A 为即时变量,在定义时即确定,所以为空
修改 Makefile 将 C 的赋值放在最后:
A := $(C) B = $(C) #C = abc all : @echo A = $(A) @echo B = $(B) C = abc
执行 make,可以发现结果并不受影响
因为当我们执行 make 的时候,会把 Makefile 整个文件读进来进行分析,然后解析里面的变量,所以变量 C 的赋值放在哪里并不受影响
2、赋值方法
:= # 即时变量 = # 延时变量 ?= # 延时变量,如果是第 1 次定义才起效,如果在前面该变量已定义则忽略这句 += # 附加,他是即时变量还是延时变量取决于前面的定义
①、+=(附加) 使用案例
如下 Makefile
A := $(C) B = $(C) C = 123 all : @echo A = $(A) @echo B = $(B) C += abc
执行 make
②、?= 使用案例
修改 Makefile 如下:
A := $(C) B = $(C) C = 123 D = 777 D ?= 888 all : @echo A = $(A) @echo B = $(B) @echo D = $(D) C += abc
执行 make
六、Makefile 函数
函数调用,很像变量的使用,也是以 $ 来标识的,其语法如下:
$(<function> <arguments>)
或是:
${<function> <arguments>}
这里, 就是函数名,make支持的函数不多。 为函数的参数,参数间以逗号 , 分隔,而函数名和参数之间以“空格”分隔。函数调用以 $ 开头,以圆括号或花括号把函数名和参数括起。
1、foreach 函数
foreach 函数和别的函数非常的不一样。因为这个函数是用来做循环用的,Makefile 中的 foreach 函数几乎是仿照于 Unix 标准 Shell(/bin/sh)中的 for 语句,或是 C-Shell(/bin/csh)中的 foreach 语句而构建的。它的语法是:
$(foreach <var>,<list>,<text>)
这个函数的意思是,把参数 <list> 中的单词逐一取出放到参数 <var> 所指定的变量中,然后再执行 <text> 所包含的表达式。每一次 <text> 会返回一个字符串,循环过程中, <text> 的所返回的每个字符串会以空格分隔,最后当整个循环结束时, <text> 所返回的每个字符串所组成的整个字符串(以空格分隔)将会是 foreach 函数的返回值。
所以, <var> 最好是一个变量名, <list> 可以是一个表达式,而 <text> 中一般会使用 <var> 这个参数来依次枚举 <list> 中的单词。举个例子:
如下 Makefile
names := a b c d files := $(foreach n,$(names),$(n).o) ALL: @echo $(files)
执行 make
上面的例子中, $(name) 中的单词会被挨个取出,并存到变量 n 中, $(n).o 每次根据 $(n) 计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以, $(files) 的值是 a.o b.o c.o d.o 。
注意,foreach中的 <var> 参数是一个临时的局部变量,foreach函数执行完后,参数 <var> 的变量将不在作用,其作用域只在 foreach 函数当中。
2、filter 和 filter-out 函数
$(filter <pattern...>,<text>)
- 名称:过滤函数
- 功能:以 <pattern> 模式过滤 <text> 字符串中的单词,保留符合模式 <pattern> 的单词。可以有多个模式。
- 返回:返回符合模式 <pattern> 的字串。
$(filter-out <pattern...>,<text>)
- 名称:反过滤函数
- 功能:以 <pattern> 模式过滤 <text> 字符串中的单词,去除符合模式 <pattern> 的单词。可以有多个模式。
- 返回:返回不符合模式 <pattern> 的字串。
示例 Makefile 如下:
C = a b b d/ D = $(filter %/, $(C)) E = $(filter-out %/, $(C)) ALL: @echo D = $(D) @echo E = $(E)
执行 make
3、patsubst
$(patsubst <pattern>,<replacement>,<text>)
- 名称:模式字符串替换函数。
- 功能:查找 <text> 中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式 <pattern> ,如果匹配的话,则以 <replacement> 替换。这里, <pattern> 可以包括通配符 % ,表示任意长度的字串。如果 <replacement> 中也包含 % ,那么, <replacement> 中的这个 % 将是 <pattern> 中的那个 % 所代表的字串。(可以用 \ 来转义,以 % 来表示真实含义的 % 字符)
- 返回:函数返回被替换过后的字符串。
- 示例:
$(patsubst %.c,%.o,x.c.c bar.c)
,把字串 x.c.c bar.c 符合模式 %.c 的单词替换成 %.o ,返回结果是 x.c.o bar.o