makefile

GNU组织建议把编译器为每一个源
文件的自动生成的依赖关系放到一个文件中,为每一个 name.c 的文件都生成一个 name.d
的Makefile文件, .d 文件中就存放对应 .c 文件的依赖关系。
但是make的命令默认是被 /bin/sh ——UNIX的
标准Shell解释执行的

工作方式:
1. 读入所有的Makefile。
2. 读入被include的其它Makefile。
3. 初始化文件中的变量。
2.11. 环境变量MAKEFILES 9
跟我一起写Makefile (PDF重制版), 发布1.0
4. 推导隐晦规则,并分析所有规则。
5. 为所有的目标文件创建依赖关系链。
6. 根据依赖关系,决定哪些目标要重新生成。
7. 执行生成命令。


.c 源文件
.o 中间文件
.a ArchiveFile (多个 .o文件的集合)

格式:
target ... : prerequisites ...
command
...
...
targets : prerequisites ; command
command
...
target:  object file 目标文件 
prerequisites: 生成该target 所以依赖的文件和/或target
command : 该target 要执行的命令 
如果其不与“target:prerequisites”在一行,那么,必须以Tab键开头,如果
和prerequisites在一行,那么可以用分号做为分隔。(见上)

prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行

规则:
make命 令 会 在 当 前 目 录 下 按 顺 序 找 寻 文 件 名
为“GNUmakefile”、“makefile”、“Makefile”的文件
。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错
而在 rm 命令前面加了一个小减号的意思 就是,也许某些文件出现问题,但不要管,继续做后面的事。

# 字符 注释

隐含规则:
<n>.o 的目标的依赖目标会自动推导为 <n>.c ,  C
<n>.o 的目标的依赖目标会自动推导为 <n>.cc 或是 <n>.C , C++
<n>.o 的目标的依赖目标会自动推导为 <n>.s ,默认使用编译品 as 汇编 汇编预处理
<n> 目标依赖于 <n>.o ,通过运行C的编译器来运行链接程序生成(一般是 ld ),其生
成命令是: $(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS) 。这
-r或 --no-builtin-rules 选项来取消所有的预设置的隐含规则
-R 或 --no-builtin-variables 参数来取消你所定义的变量对隐含规则的作
用。
你也可以重新定义一个全新的隐含规则,其在隐含规则中的位置取决于你在哪里写下这
个规则。朝前的位置就靠前
模式规则:
%.o : %.c ; <command ......>;

后缀规则: 一个比较老式的定义隐含规则的方法。后缀规则会被模式规则逐步地取代
即无%
后缀规则不允许任何的依赖文件,如果有依赖文件的话,那就不是后缀规则,那些后缀统统被
认为是文件名,如:
.c.o 那么其就是双后缀规则,意
义就是 .c 是源文件的后缀, .o 是目标文件的后缀。
而要让make知道一些特定的后缀,我们可以使用伪目标 .SUFFIXES 来定义或是删除,如:
.SUFFIXES : .hack .win
把后缀 .hack 和 .win 加入后缀列表中的末尾。
.SUFFIXES : #
.SUFFIXES : .c .o .h # 自己
-r 或 -no-builtin-rules  也会使用得默认的后缀列表为空。
所有的后缀规则在Makefile被载入内存时,会被转换成模式规则
变量:$ 类似于 宏扩展。如果你要使用真实的 $ 字符,那么你需要用 $$ 来表示。 
变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有 : 、 # 、 = 或
是空字符(空格、回车等)。变量是大小写敏感的

规则型变量
= 号,在 = 左侧是变量,右侧是变量的值,右侧变量的值
可以定义在文件的任何一处,也就是说,右侧中的变量不一定非要是已定义好的值,其也可以
使用后面定义的值。如:
:= 操作符
这种方法,前面的变量不能使用后面的变量,只能使用前面已定义好了的变
量。
+= 操作符给变量追加值, 如果变量之前没有定义过,那么, += 会自动变成 = ,如果前面有变量定义,那么 += 会继承
于前次操作的赋值符。如果前一次的是 := ,那么 += 会以 := 作为其赋值符,如:
而后面采用“#”注释符来表示变量定义的终止,这样,我们可以定义出其值是一个空
格的变量。
$(var:a=b) 或是 ${var:a=b} ,其意思是,
把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束
符”。
把变量的值再当成变量

自动化变量:
$@ : 表示规则中的目标文件集
$% : 仅当目标是函数库文件中,表示规则中的目标成员名
$< : 依赖目标中的第一个目标名字。
$? : 所有比目标新的依赖目标的集合。以空格分隔。
$^ : 所有的依赖目标的集合  以空格分隔。
$+ : 这个变量很像 $^ ,也是所有依赖目标的集合
$* : 这个变量表示目标模式中 % 及其之前的部分
$(@D) 表示 $@ 的目录部分(不以斜杠作为结尾),如果 $@ 值是 dir/foo.o ,那么 $(@D)
就是 dir ,而如果 $@ 中没有包含斜杠的话,其值就是 . (当前目录)。
$(@F) 表示 $@ 的文件部分,如果 $@ 值是 dir/foo.o ,那么 $(@F) 就是 foo.o , $(@F)
相当于函数 $(notdir $@) 。
$(*D), $(*F) 和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例
子, $(*D) 返回 dir ,而 $(*F) 返回 foo
$(%D), $(%F) 分 别 表 示 了 函 数 包 文 件 成 员 的 目 录 部 分 和 文 件 部 分 。 这 对 于 形 同
archive(member) 形式的目标中的 member 中包含了不同的目录很有用。
$(<D), $(<F) 分别表示依赖文件的目录部分和文件部分。
$(^D), $(^F) 分别表示所有依赖文件的目录部分和文件部分。(无相同的)
$(+D), $(+F) 分别表示所有依赖文件的目录部分和文件部分。(可以有相同的)
$(?D), $(?F) 分别表示被更新的依赖文件的目录部分和文件部分


目标变量 ( 局部变量): Target-specific Variable”,它可
以和“全局变量”同名,因为它的作用范围只在这条规则以及连带规则中,所以其值也只在作用
范围内有效。而不会影响规则链以外的全局变量的值。
<target ...> : <variable-assignment>;
<target ...> : overide <variable-assignment
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
$( CC) $( CFLAGS) prog.o foo.o bar.o
模式变量(Pattern-specific Variable)
%.o : CFLAGS = -O
同样,模式变量的语法和“目标变量”一样:
<pattern ...>; : <variable-assignment>;
<pattern ...>; : override <variable-assignment>;
环境变量
MAKEFILES make会把这个变量中的值做一个类似于 include 的动作  它和include 不同的是,从这个环境变                                                  量中引入的Makefile的“目标”不会起作用,如果环境变量中定义的文件发现错误,make也会不理。
VPATH  : 如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义   了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。
目录由“冒号”分隔
系统变量“MAKELEVEL”,其意思是,如果我
们的make有一个嵌套执行的动作(参见前面的“嵌套使用make”),那么,这个变量会记录了我
们的当前Makefile的调用层数
CFLAGS 环境变量
MAKECMDGOALS  这个变量中会存放你所指定的终极目标的列表,如果在命令行上,你没有指定目标,那么,这个变量是空值。这个变量可以让你使用在一些比较特殊的情形下
伪目标:
• all:这个伪目标是所有目标的目标,其功能一般是编译所有的目标。
• clean:这个伪目标功能是删除所有被make创建的文件。
• install:这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目
标中去。
• print:这个伪目标的功能是例出改变过的源文件。
• tar:这个伪目标功能是把源程序打包备份。也就是一个tar文件。
• dist:这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。
• TAGS:这个伪目标功能是更新所有的目标,以备完整地重编译使用。
• check和test:这两个伪目标一般用来测试makefile的流程。

命令
make的 -f 和 --file  :指定makefile 的名字 make -f Make.Linux 或 make --file Make.AIX
include <filename> :包含其他多的makefile  被包含的文件会原模原样的放在当前文件的包含位置
在 include 前面可以有一些空字符,但是绝不能是 Tab 键开始
-I 或 --include-dir     那么make就会在这个参数所指定的目录下去寻找。
. 如果目录 <prefix>/include (一般是: /usr/local/bin 或 /usr/include )存
在的话,make也会去找。
-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系
@  make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用 @ 字符在命令行前,
那么,这个命令将不被make显示出来,
-n 或 --just-print ,那么其只是显示命令,但不会执行命令,
参数 -s 或 --silent 或 --quiet 则是全面禁止命令的显示。
给make加上 -i 或是 --ignore-errors 参数,那么,Makefile中所有命令都会忽略错误。而如果一个规则是以 .IGNORE 作为目标的,那么这个规则中的所有命令将会忽略错误。这些是不同级别的防止命令出错的方法,
-k 或是 --keep-going , 如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则。
-e 参数。 总控Makefile的变量可以传递到下级的Makefile中(如果你显示的声明),但是不会覆盖下层的Makefile中所定义的变量,除非指定了 -e 参数。
如果你要传递变量到下级Makefile中,那么你可以使用这样的声明: export <variable ...>;
如果你不想让某些变量传递到下级Makefile中,那么你可以这样声明:unexport <variable ...>;
如果你要传递所有的变量,那么,只要一个export就行了。后面什么也不用跟,表示传递所有的变量。
要注意的是,有两个变量,一个是 SHELL ,一个是 MAKEFLAGS ,这两个变量不管你是
否export,其总是要传递到下层 Makefile中,
-w 或是 --print-directory  会在make的过程中输出一些信息,让你看到目前的工作目录。
当你使用 -C 参数来指定make下层Makefile时, -w 会被自动打开的。如果参数中有 -s (--slient )或是 --no-print-directory ,那么, -w 总是失效的。

override”指示符 如果有变量是通常make的命令行参数设置的,那么Makefile中对这个变量的赋值会被忽略。如
果你想在Makefile中设置这类参数的值,那么,你可以使用“override”指示符。其语法是:
override <variable>; = <value>;
override <variable>; := <value>;
当然,你还可以追加:
override <variable>; += <more text>;
对于多行的变量定义,我们用define指示符,在define指示符前,也同样可以使用override指示
符,如:
override define foo
bar
endef 不是很懂
条件编译 ifeq 、 else 和 endif 。ifneq
libs_for_gcc = -lgnu
normal_libs =
foo : $(objects)
ifeq ($(CC),gcc)
$( CC) -o foo $( objects) $( libs_for_gcc
else
$( CC) -o foo $( objects) $( normal_libs)
endif
ifeq (<arg1>, <arg2>)
ifeq '<arg1>' '<arg2>'
ifeq "<arg1>" "<arg2>"
ifeq "<arg1>" '<arg2>'
ifeq '<arg1>' "<arg2>"
ifneq (<arg1>, <arg2>)
ifneq '<arg1>' '<arg2>'
ifneq "<arg1>" "<arg2>"
ifneq "<arg1>" '<arg2>'
ifneq '<arg1>' "<arg2>"

-b, -m 这两个参数的作用是忽略和其它版本make的兼容性。
-B, --always-make 认为所有的目标都需要更新(重编译)。
-C <dir>, --directory=<dir> 指定读取makefile的目录。如果有多个“-C”参数,make的解释
是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:“make -C
~hchen/test -C prog”等价于“make -C ~hchen/test/prog”。
-debug[=<options>] 输出make的调试信息。它有几种不同的级别可供选择,如果没有参数,
那就是输出最简单的调试信息。下面是<options>的取值:
• a: 也就是all,输出所有的调试信息。(会非常的多)
• b: 也就是basic,只输出简单的调试信息。即输出不需要重编译的目标。
• v: 也就是verbose,在b选项的级别之上。输出的信息包括哪个makefile被解析,不需
要被重编译的依赖文件(或是依赖目标)等。
• i: 也就是implicit,输出所以的隐含规则。
• j: 也就是jobs,输出执行规则中命令的详细信息,如命令的PID、返回码等。
• m: 也就是makefile,输出make读取makefile,更新makefile,执行makefile的信息。
-d 相当于“–debug=a”。
-e, --environment-overrides 指明环境变量的值覆盖makefile中定义的变量的值。
-f=<file>, --file=<file>, --makefile=<file> 指定需要执行的makefile。
-h, --help 显示帮助信息。
8.4. 检查规则47
跟我一起写Makefile (PDF重制版), 发布1.0
-i , --ignore-errors 在执行时忽略所有的错误。
-I <dir>, --include-dir=<dir> 指定一个被包含makefile的搜索目标。可以使用多个“-I”参
数来指定多个目录。
-j [<jobsnum>], --jobs[=<jobsnum>] 指同时运行命令的个数。如果没有这个参数,make运
行命令时能运行多少就运行多少。如果有一个以上的“-j”参数,那么仅最后一个“-j”才是
有效的。(注意这个参数在MS-DOS中是无用的)
-k, --keep-going 出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就
不会被执行了。
-l <load>, --load-average[=<load>], -max-load[=<load>] 指定make运行命令的负载。
-n, --just-print, --dry-run, --recon 仅输出执行过程中的命令序列,但并不执行。
-o <file>, --old-file=<file>, --assume-old=<file> 不重新生成的指定的<file>,即使这个
目标的依赖文件新于它。
-p, --print-data-base 输出makefile中的所有数据,包括所有的规则和变量。这个参数会
让一个简单的makefile都会输出一堆信息。如果你只是想输出信息而不想执行makefile,
你可以使用“make -qp”命令。如果你想查看执行makefile前的预设变量和规则,你可以使
用 “make –p –f /dev/null”。这个参数输出的信息会包含着你的makefile文件的文件名和行
号,所以,用这个参数来调试你的 makefile会是很有用的,特别是当你的环境变量很复杂
的时候。
-q, --question 不运行命令,也不输出。仅仅是检查所指定的目标是否需要更新。如果
是0则说明要更新,如果是2则说明有错误发生。
-r, --no-builtin-rules 禁止make使用任何隐含规则。
-R, --no-builtin-variabes 禁止make使用任何作用于变量上的隐含规则。
-s, --silent, --quiet 在命令运行时不输出命令的输出。
-S, --no-keep-going, --stop 取消“-k”选项的作用。因为有些时候,make的选项是从环境
变量“MAKEFLAGS”中继承下来的。所以你可以在命令行中使用这个参数来让环境变量
中的“-k”选项失效。
-t, --touch 相当于UNIX的touch命令,只是把目标的修改日期变成最新的,也就是阻止生成
目标的命令运行。
-v, --version 输出make程序的版本、版权等关于make的信息。
-w, --print-directory 输出运行makefile之前和之后的信息。这个参数对于跟踪嵌套式调
用make时很有用。
--no-print-directory 禁止“-w”选项。
-W <file>, --what-if=<file>, --new-file=<file>, --assume-file=<file> 假 定 目
标<file>;需要更新,如果和“-n”选项使用,那么这个参数会输出该目标更新时的运
行动作。如果没有“-n”那么就像运行UNIX的“touch”命令一样,使得<file>;的修改时间为
当前时间。
--warn-undefined-variables 只要make发现有未定义的变量,那么就输出警告信息
并行机制( -j 参数)  但就目前而言,你还是应该不要尽量不要使用 -j 参数。
make命令执行后有三个 退出码:
0 表示成功执行。
1 如果make运行时出现任何错误,其返回1。
2 如果你使用了make的“-q”选项,并且make使得一些目标不需要更新,那么返回2。
命令包 
调用方式和变量一样加$
以define开始,以endef结束,如:
define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef



函数
调用:
函数调用以 $ 开头,以圆括号或花括
号把函数名和参数括起

$(subst <from >,<to >,<text >)
• 名称:字符串替换函数
• 功能:把字串 <text> 中的 <from> 字符串替换成 <to>
• 返回:函数返回被替换过后的字符串。


通配符
  * , ? 和 ~ 。
% 的意思是匹配零或若干字符,
% 代表一个或多个字符
关键字:
objects := $( wildcard *.o)
vpath”关键字  vpath <pattern> <directories> 为 符 合 模 式<pattern>的 文 件 指 定 搜 索 目
录<directories>。
vpath <pattern> 清除符合模式<pattern>的文件的搜索目录。
vpath 清除所有已被设置好了的文件搜索目录。
伪目标
.PHONY :
伪目标只是一个标签不会生成文件

静态模式

<targets ...> : <target-pattern> : <prereq-patterns ...>
<commands>
...

targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。
target-parrtern是指明了targets的模式,也就是的目标集模式。
prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。
这样描述这三个东西,可能还是没有说清楚,还是举个例子来说明一下吧。如果我们的<target-parrtern>定义成 %.o ,意思是我们的<target>;集合中都是以 .o 结尾的,而如果我们的<prereq-parrterns>定义成 %.c ,意思是对<target-parrtern>所形成的目标集进行二次定义,其计算方法
是,取<target-parrtern>模式中的 % (也就是去掉了 .o 这个结尾),并为其加上 .c 这个结尾,
形成的新集合。

上述语句中的 $(sources:.c=.d) 中的 .c=.d 的意思是做一个替换,把变量 $(sources)
所有 .c 的字串都替换成 .d










make会一条一条的执行其后的命
令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔
这两条命令。比如你的第一条命令是cd命令,你希望第二条命令得在cd之后的基础上运行,那
么你就不能把这两条命令写在两行上,而应该把这两条命令写在一行上,用分号分隔。如:
• 示例一:
19
跟我一起写Makefile (PDF重制版), 发布1.0
exec :
cd /home/hchen
pwd
• 示例二:
exec :
cd /home/hchen; pwd
当我们执行 make exec 时,第一个例子中的cd没有作用,pwd会打印出当前的Makefile目录,
而第二个例子中,cd就起作用了,pwd会打印出“/home/hchen”。































  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值