Makefile系列之二 : 命令
一、显示命令 echo
“@”字符可以控制命令是否在屏幕上显示,如
@echo 正在编译XXX模块......
输出:
正在编译XXX模块......
如果没有“@"则输出 :
echo 正在编译XXX模块......
正在编译XXX模块......
二、make参数
下面列举了所有GNU make 3.80版的参数定义。
1)make “-n”或“--just-print”
——只显示命令,但不会执行命令。
make “-s”或“--slient”
——全面禁止命令的显示。
2)make “-i”或“--ignore-errors”
——Makefile中所有命令都会忽略错误。而如果某规则是以“.IGNORE”作为目标的,那么该规则中的所有命令将会忽略错误。
注意 : makefile是由许多规则组成,而规则又由目标、依赖及命令组成,每当命令运行完后,make会检测每个命令的返回码,如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。如果一个规则中的某个命令出错了(命令退出码非零),那么make就会终止执行当前规则,这将有可能终止所有规则的执行。而有些命令执行不成功并不会影响后续命令的执行,所以为了让命令继续执行,可以通过在命令前加“-”来忽略执行命令的错误,如
clean:
-rm -f *.o #忽略删除文件时遇到的错误,让命令继续执行
3)make “-w”或“--print-directory”
——会在make的过程中输出目前的工作目录的信息。比如,如果下级make目录是“/home/hchen/gnu/make”,当使用“make -w”来执行时,那么当进入该目录时,屏幕会输出:
make: Entering directory `/home/hchen/gnu/make'.
而在完成下层make后离开目录时,屏幕会输出:
make: Leaving directory `/home/hchen/gnu/make'
当你使用“-C”参数来指定make下层Makefile时,“-w”会被自动打开的。如果参数中有“-s”(“--slient”)或是“--no-print-directory”,那么,“-w”总是失效的。
4)“-b” “-m”
——这两个参数的作用是忽略和其它版本make的兼容性。
5)“-B” “--always-make”
——认为所有的目标都需要更新(重编译)。
6) “-C <dir>” “--directory=<dir>”
——指定读取makefile的目录。如果有多个“-C”参数,make的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:
“make –C ~hchen/test –C prog” 等价于 “make –C ~hchen/test/prog”。
7) “--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”。
8)“-e” “--environment-overrides”
——指明环境变量的值覆盖makefile中定义的变量的值。
9)“-f=<file>” “--file=<file>” “--makefile=<file>”
——指定需要执行的makefile。
10) “-h” “--help”
——显示帮助信息。
11)“-i” “--ignore-errors”
——在执行时忽略所有的错误。
12)“-I <dir>” “--include-dir=<dir>”
——指定一个被包含makefile的搜索目标。可以使用多个“-I”参数来指定多个目录。
13) “-j [<jobsnum>]” “--jobs[=<jobsnum>]”
——指同时运行命令的个数。如果没有这个参数,make运行命令时能运行多少就运行多少。如果有一个以上的“-j”参数,那么仅最后一个“-j”才是有效的。(注意这个参数在MS-DOS中是无用的)
14) “-k” “--keep-going”
——出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了。
15)“-l <load>” “--load-average[=<load]” “—max-load[=<load>]”
——指定make运行命令的负载。
16)“-n” “--just-print” “--dry-run” “--recon”
——仅输出执行过程中的命令序列,但并不执行。
17)“-o <file>” “--old-file=<file>” “--assume-old=<file>”
——不重新生成的指定的<file>,即使这个目标的依赖文件新于它。
18)“-p” “--print-data-base”
——输出makefile中的所有数据,包括所有的规则和变量。这个参数会让一个简单的makefile都会输出一堆信息。如果你只是想输出信息而不想执行makefile,你可以使用“make -qp”命令。如果你想查看执行makefile前的预设变量和规则,你可以使用“make –p –f /dev/null”。这个参数输出的信息会包含着你的makefile文件的文件名和行号,所以,用这个参数来调试你的makefile会是很有用的, 特别是当你的环境变量很复杂的时候。
19)“-q” “--question”
——不运行命令,也不输出。仅仅是检查所指定的目标是否需要更新。如果是0则说明要更新,如果是2则说明有错误发生。
20)“-r” “--no-builtin-rules”
——禁止make使用任何隐含规则。
21)“-R” “--no-builtin-variabes”
——禁止make使用任何作用于变量上的隐含规则。
22)“-s” “--silent” “--quiet”
——在命令运行时不输出命令的输出。
23)“-S” “--no-keep-going” “--stop”
——取消“-k”选项的作用。因为有些时候,make的选项是从环境变量“MAKEFLAGS”中继承下来的。所以你可以在命令行中使用这个参数来让环境变量中的“-k”选项失效。
24)“-t” “--touch”
——相当于UNIX的touch命令,只是把目标的修改日期变成最新的,也就是阻止生成目标的命令运行。
25)“-v” “--version”
——输出make程序的版本、版权等关于make的信息。
26)“-w” “--print-directory”
——输出运行makefile之前和之后的信息。这个参数对于跟踪嵌套式调用make时很有用。
27)“--no-print-directory”
——禁止“-w”选项。
28) “-W <file>” “--what-if=<file>” “--new-file=<file>” “--assume-file=<file>”
——假定目标<file>需要更新,如果和“-n”选项使用,那么这个参数会输出该目标更新时的运行动作。如果没有“-n”那么就像运行UNIX的“touch”命令一样,使得<file>的修改时间为当前时间。
29)“--warn-undefined-variables”
——只要make发现有未定义的变量,那么就输出警告信息。
三、执行命令
如果要让上一条命令的结果应用在下一条命令时,必须用分号分隔这两条命令。比如你的第一条命令是cd命令,如:
示例一:
exec:
cd /home/hchen
pwd
示例二:
exec:
cd /home/hchen; pwd
当执行“make exec”时,第一个例子中的cd没有作用,pwd会打印出当前的Makefile目录,而第二个例子中,cd就起作用了,pwd会打印出“/home/hchen”。
四、命令包定义
如果Makefile中出现一些相同命令序列,那么我们可以为这些相同的命令序列定义一个变量。定义这种命令序列的语法以“define”开始,以“endef”结束,如:
define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef
“run-yacc”是这个命令包的名字,不能与Makefile中的变量重名。在
“define”和“endef”中的两行就是命令序列。这个命令包中的第一个命令是运行Yacc程序,因为Yacc程序总是生成“y.tab.c”的文件,所以第二行的命令就是把这个文件改改名字。
五、嵌套执行make
在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而不至于把所有的东西全部写在一个Makefile中,这样会很难维护我们的Makefile,这个技术对于我们模块编译和分段编译有着非常大的好处。
例如,我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写:
subsystem:
cd subdir && $(MAKE)
其等价于:
subsystem:
$(MAKE) -C subdir
定义$(MAKE)宏变量可以让我们更方便地为make添加一些参数,这样比较利于维护。这两个例子的意思都是先进入“subdir”目录,然后执行make命令。
我们把这个Makefile叫做“总控Makefile”,总控Makefile的变量可以传递到下级的Makefile中(如果你显式的声明),但是不会覆盖下层的Makefile中所定义的变量,除非指定了“-e”参数。
如果你要传递变量到下级Makefile中,那么你可以使用这样的声明:
export <variable ...>
如果你不想让某些变量传递到下级Makefile中,那么你可以这样声明:
unexport <variable ...>
export variable = value
其等价于:
variable = value
export variable
其等价于:
export variable := value
传递所有的变量,那么,只要一个export就行了。后面什么也不用跟,表示传递所有的变量。
六、检查规则
1)不想makefile中的规则执行起来,只是检查一下命令或是执行的序列。
“-n”
“--just-print”
“--dry-run”
“--recon”
不执行参数,这些参数只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行,这些参数对于我们调试makefile很有用处。
2)
“-t”
“--touch”
这个参数的意思就是把目标文件的时间更新,但不更改目标文件。也就是说,make假装编译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。
3)
“-q”
“--question”
这个参数的行为是找目标的意思,如果目标存在,那么其什么也不会输出,当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。
4)
“-W <file>”
“--what-if=<file>”
“--assume-new=<file>”
“--new-file=<file>”
这个参数需要指定一个文件。一般是是源文件(或依赖文件),Make会根据规则推导来运行依赖于这个文件的命令,一般来说,可以和“-n”参数一同使用,来查看这个依赖文件所发生的规则命令。