有多少人了解或写过Makefile,用过make?
你眼里的make/Makefile是做什么用的?
有多少人了解或用过其他构建工具? CMake、 Scons等
有多少人用过IDE? Visual Studio、 Eclipse等
IDE又在幕后为我们做了些什么?
当你按下VS的build键或菜单项时, VS会根据工程文件... ...
一个项目有几十上百个源文件,怎么编译构建?
gcc -o foo 0.c 1.c 2.c 3.c ..... 99.c
或gcc -o foo *.c只改一个文件,所有文件都得重新编译?
程序员与机器
Ø 从C源代码构建最终的执行文件,哪些是程序员的创造,哪些是机器应行之事?
1. 教会机器,如何把一个 .c 文件编译成 .o 文件
2. 教会机器,如何把若干个 .o 文件链接成可执行文件
3. 告诉机器,你的项目由哪些 .c 文件构成,最终想生成一个可执行文件
Ø 收发邮件、下载文件、制作安装包、运行单元测试
Ø 构建内核时,自动提取SVN版本号, ... ...
Ø make做起来比较困难的事,也可以借助更高一级的工具来完成,比如Automake
何为makemake?
Ø 命令make读取Makefile,分析其中的规则
Ø 比对目标文件和源文件的时间戳,决定执行哪些命令
Ø 一个项目由多个文件组成, make只处理比目标文件更新的源文件
Ø 对于规模很小的程序,写个脚本或批处理文件也能搞定,但Makefile写起来可能更简单
Ø 是工具就需要学习,熟能生巧
make & Makefilemake & Makefile
Ø 支持分离编译(separate compilation)
Ø 描述项目文件之间的依赖关系(dependencies)
Ø Linux下惯用Makefile,由命令make解析
Makefile组成
Ø 规则:显式、隐含
Ø rule: explicit, implicit
Ø 变量(宏)
Ø variable (macro)
Ø 指示符(条件)
Ø directives (conditionals)
Ø 注释符# – 注释#至行尾的所有内容
Ø 续行符\ – 将一条命令拆分到多行
MakefileMakefile样例
Ø Makefile主要组成:规则
Ø 不带参数执行make,缺省执行第一个目标(一般为all)
Ø 所有的目标都被make认为是文件,如果目标文件存在,则认为事情已做完
示例:
foobar: foo.o bar.o
gcc -o foobar foo.o bar.o
foo.o: foo.c foo.h
gcc -c foo.c
bar.o: bar.c foo.h
gcc -c bar.c
或
foobar: foo.c bar.c
gcc $@ $^
________________________
# -o outfile,指定可执行文件名
# -c compile, 只编译不连接
规则
Ø 目标 : 依赖
Ø “目标” 的构建依赖于 “依赖” 先构建出来。
Ø这里, “目标” 和 “依赖” 都是文件系统中的文件,而 “依赖”本身也可以是一个 “目标” 。
Ø如果 “依赖” 的文件时间新于 “目标” 的文件时间,表示“目标” 需要重新构建。
Ø如果 “目标” 文件不存在,也会触发构建过程。
Ø 一个目标可以有多个依赖
Ø目标 : 依赖1 依赖2 依赖3
Ø 也可以写成:
Ø目标 : 依赖1
Ø目标 : 依赖2
Ø a b : c d 等价于 a : c d b : c d
Ø -n参数可以观察make到底会执行什么指令,但并不真的执行
Ø -rm指示make忽略掉后面这条指令的返回值,不要因为返回值非0而中断
示例:
foobar: foo.o bar.o
gcc -o foobar foo.o bar.o
foo.o: foo.c foo.h
gcc -c foo.c
bar.o: bar.c foo.h
gcc -c bar.c
clean:
-rm *.o foobar
隐含规则
Ø 隐含规则是从另一种文件产生一种文件的标准方式
Ø 产生.o文件有许多规则 — 从.c、 .p文件等, make会套用第一条符合条件的规则
Ø 未给指定对象文件定义规则,则make将套用隐含规则
定义隐含规则
%.o : %.c
$(C) -c –g $<
C = gcc
OBJS = foo.o bar.o
HDRS = foo.h
foobar : foo.o bar.o
$(C) -o foobar $(OBJS)
$(OBJS) : $(HDRS)
如何避免隐含规则?空命令。
target: ; # 此目标不会套用隐含规则
变量
命令行里定义的变量会替换Makefile中定义的变量
make C=cc
Ø 定义变量很简单,使用 = 赋值即可;使用 += 追加
Ø “ 直接展开 ” 式变量使用 “:= ” 定义。使用 “:= ”定义变量时,变量值中对其他量或者函数的引用在定义变量时被展开(对变量进行替换)
Ø 使用一个标量,则用 $(变量名) 这样的形式
Ø $对于Makefile文件有特殊含义,在命令行部分引用变量时写作$$
自动变量
自动变量可以引用规则的某一组成部分
foo.o : foo.c foo.h
gcc -c foo.c
$@ - 规则中目标的名字(foo.o)
$< - 第一个依赖的名字(foo.c)
$^ - 全部依赖的名字(foo.c foo.h)
$? - 所有比目标新的依赖的名字
makemake选项
-f filename – 用于指定不标准的makefile文件名
-t - (touch)标记目标为已更新
-q - (question)待更新的目标,为真则退出并返回0
-n – 打印待执行的命令,但并不真的执行
/ -t, -q和–n不能一起使用 /
-s – 不打印命令的执行过程
-k - 执行命令错误时不终止make的执行,尽可能执行所有的
命令,直到出现致命错误才终止
Generated by Foxit PDF Creator © Foxit Software
http://www.foxitsoftware.com For evaluation only.
伪目标
伪目标(Phony targets):
没有依赖的目标。不代表一个真正的文件名,在执行 make 时
指定这个目标来执行其所在规则定义的命令,有时也称标签。
常用伪目标:
all – 产生所有顶层目标
.PHONY : all
all: my_prog1 my_prog2
clean – 删除由make创建的所有文件
print – 打印变更过的源文件清单
.PHONY : clean
clean:
rm $(OBJS)
Generated by Foxit PDF Creator © Foxit Software
http://www.foxitsoftware.com For evaluation only.
VPATHVPATH
Ø VPATH变量 –可以指定依赖文件的搜索路径,当规则的依赖文
件在当前目录找不到时, make会在此变量所指定的目录中搜索
这些依赖文件
VPATH = dir : dir …
/ VPATH = src:../headers /
Ø vpath指示符(全小写) –选择性搜索
vpath pattern directory
/ vpath %.h headers /
Ø GPATH:
GPATH – if you want targets to be stored in the same directory as
their dependencies.
Generated by Foxit PDF Creator © Foxit Software
http://www.foxitsoftware.com For evaluation only.
变量修饰符
C = gcc
OBJS = foo.o bar.o
SRCS = $(OBJS, .o=.c) #!!!
foobar : $(OBJS)
$(C) -g -c $^
%.o : %.c
$(C) -g -c S<
$(SRCS) : foo.h
Generated by Foxit PDF Creator © Foxit Software
http://www.foxitsoftware.com For evaluation only.
条件指示符
条件指示符包括:
if ifeq ifneq ifdef ifndef
皆以endif结束,复杂的条件可能会用到elif和else
示例:
libs_for_gcc = -lgnu
normal_libs =
ifeq ($(CC),gcc)
libs=$(libs_for_gcc) # 开头没有TAB
else
libs=$(normal_libs) # 开头没有TAB
endif