info make 中的一个例子, 拿它来分析分析
1 PROGRAMS = server client 2 3 server_OBJS = server.o server_priv.o server_access.o 4 server_LIBS = priv protocol 5 6 client_OBJS = client.o client_api.o client_mem.o 7 client_LIBS = protocol 8 9 # Everything after this is generic 10 11 .PHONY: all 12 all: $(PROGRAMS) 13 14 define PROGRAM_template = 15 $(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%) 16 ALL_OBJS += $$($(1)_OBJS) 17 endef 18 19 $(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog)))) 20 21 $(PROGRAMS): 22 $(LINK.o) $^ $(LDLIBS) -o $@ 23 24 clean: 25 rm -f $(ALL_OBJS) $(PROGRAMS)前面几个赋值我们是看得懂的,到了14行,定义了一个命令包.PROGRAM_template
观察其dbase 中定义: 原来就是字符串照印, 是滞后求值。
# makefile (从“Makefile”,行 14) define PROGRAM_template $(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%) ALL_OBJS += $$($(1)_OBJS) endef第19行,对所有\$(PROGRAMS) 进行展开,就是对server client进行操作,
观察dbase中定义:
\# makefile (从“Makefile”,行 19) 执行时会递归求值.
ALL_OBJS = $(server_OBJS) $(client_OBJS)是的,第一遍代入server 为\$(server_OBJS),
第二遍代入client, 形成\$(client_OBJS)
19行foreach 执行的结果,确立了执行文件的依赖, 并确定了ALL_OBJS 定义
那第一条命令依赖跑哪里去了?
原来它跟21行\$(PROGRAMS) 合并了
以client为例,client 依赖\$(client_OBJS) \$(client_LIBS:%=-l%),第二次展开即得到下面结果
第21行,多目标规则被展开为2条规则
server: server.o server_priv.o server_access.o libpriv.so libprotocol.so # 要执行的配方 (从“Makefile”,行 22): $(LINK.o) $^ $(LDLIBS) -o $@其中\$(LINK.o) 是默认变量
# 默认
LINK.o = \$(CC) \$(LDFLAGS) \$(TARGET_ARCH)
使用时递归展开为 cc, 因为LDFLAGS,TARGET_ARCH都为空
第24行clean 规则比较容易
clean: # 要执行的配方 (从“Makefile”,行 25): rm -f $(ALL_OBJS) $(PROGRAMS)如此分析清楚了这个Makefile 到底是怎样工作的.
总结:
make 是制造之意. 解决的是制造问题,管理的是一个工程,所以make是工程管理工具
管理的文件比较少,可以用小的makefile,
管理的文件比较多,可以用大的makefile,
如果只是一个文件,那几乎就不用管理了。
大的工程,成千上万个文件怎么来管理生成自己的目标,这是make要考虑的.
上面是个小工程。所作的工作是
1. 枚举了各个obj, 各个lib
2. 说明了由哪些obj,lib构成执行文件1,执行文件2
3. ....
(大工程也是这样的,可以想像大工程会定义更多的编译工具,编译flag,枚举文件等等事项)
其中那个命令包,定义了每个执行文件依赖与哪些obj文件及库文件.
由于其对每一个执行文件具有通用性,所以定义为宏包来简化书写过程.
补充: “eval”的作用是什么?
foreach 循环取出 server、client
call把程序名填入模板,生成一段规则字符串(此时还只是文本)
以server 为例.
server: \$(server_OBJS) \$(server_LIBS:%=-l%) ALL_OBJS += \$(server_OBJS)
eval把这段文本,解析成真正的 Makefile 规则,并再一次进行变量展开.
server: server.o server_priv.o server_access.o -lpriv -lprotocol ALL_OBJS += server.o server_priv.o server_access.o
没有eval, 它就只是一个字符串, 有了eval,会把字符串变成Makefile 的规则.这就是动态编译.
总之: eval 几乎总是和 define(定义模板)、foreach(循环)、call(填充模板)一起用
eval 把字符串转变为规则.