makefile基本格式
target ... : prerequisites ...
command
...
...格式说明
- target: 个目标文件或者标签(Label)
- prerequisites: 生成 target 所需要的文件
- command: make 需要执行的命令(任意的Shell命令)
这是一个文件的依赖关系,target 这个目标文件依赖于 prerequisites 中的文件,其生成规则定义在 command 中.prerequisites 中如果有一个以上的文件比 target 文件要新的话,command 所定义的命令就会被执行 (makefile中最核心的内容)
例子:
a : b.o c.o d.o gcc -o a b.o c.o d.o b.o : b.c b.h gcc -c b.c c.o : c.c c.h gcc -c c.c d.o : d.c d.h gcc -c d.c clean : rm a b.o c.o d.o
文件名规则
默认的情况下,make 命令会在当前目录下按顺序找寻文件名为 “GNUmakefile”、“makefile”、“Makefile” 的文件,找到了解释这个文件.在这三个文件名中,最好使用 “Makefile” 这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉.最好不要用 “GNUmakefile”,这个文件是 GNU 的 make 识别的.有另外一些 make 只对全小写的 “makefile” 文件名敏感,但是基本上来说,大多数的 make 都支持 “makefile” 和 “Makefile” 这两种默认文件名.
当然,你可以使用别的文件名来书写 Makefile,比如:“Make.Linux”,“Make.Solaris”,“Make.AIX” 等,如果要指定特定的 Makefile,你可以使用 make 的 “-f” 和 “--file” 参数,如:make -f Make.Linux 或make --file Make.AIX.
makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释.
- 显式规则:显式规则说明了,如何生成一个或多的的目标文件.这是由 makefile 的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令.
- 隐晦规则:由于我们的 make 有自动推导的功能,所以隐晦的规则可以让我们比较简略地书写 makefile, 这是由 make 所支持的.
- 变量定义:在 makefile 中我们要定义一系列的变量,变量一般都是字符串,这个有点像你 C 语言中的宏,当 makefile 被执行时,其中的变量都会被扩展到相应的引用位置上.
- 文件指示:其包括了三个部分,一个是在一个 makefile 中引用另一个 makefile, 就像 C 语言中的 include 一样;另一个是指根据某些情况指定 makefile 中的有效部分,就像 C 语言中的预编译 #if 一样;还有就是定义一个多行的命令.有关这一部分的内容,我会在后续的部分中讲述.
- 注释:makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样.如果你要在你的makefile中使用“#”字符,可以用反斜框进行转义,如:“\#”.
变量
makefile中使用变量
说明:
- 变量在声明时需要给与初值,而在使用时,需要给在变量名前家上 "$" 符号,但最好用小括号 "()" 或者大括号 "{}" 把变量给括起来,如果你要使用真实的 "$ "字符,那么你需要用 "$$" 来表示
- 在 Makefile 中的定义的变量,就像是 C/C++ 语言中的宏一样,他代表了一个文本字串,在 Makefile 中执行的时候其会自动原模原样地展开在所使用的地方.其与 C/C++ 所不同的是,你可以在 Makefile 中改变其值.
- 变量的命名字可以包含字符、数字,下划线 (可以是数字开头),但不应该含有 ":" 、"#"、"=" 或是空字符(空格、回车等).变量是大小写敏感.
使用:
- 变量的定义:变量名 = 变量实际内容
- 使用变量:$(变量名)
例子:
objects = b.o c.o d.o a : $(objects) cc -o a $(objects) b.o : b.c b.h cc -c b.c c.o : c.c c.h cc -c c.c d.o : d.c d.h cc -c d.c clean : rm a $(objects)自动化变量
- $@
表示规则的目标文件名.如果目标是一个文档文件(Linux中,一般称.a文件为文档文件,也称为静态库文件),那么它代表这个文档的文件名.在多目标模式规则中,它代表的是哪个触发规则被执行的目标文件名.- $%
当规则的目标文件是一个静态库文件时,代表静态库的一个成员名.例如,规则的目标是“foo.a(bar.o)”,那么,“$%”的值就为“bar.o”,“$@”的值为“foo.a”.如果目标不是静态库文件,其值为空.- $<
规则的第一个依赖文件名.如果是一个目标文件使用隐含规则来重建,则它代表由隐含规则加入的第一个依赖文件.- $?
所有比目标文件更新的依赖文件列表,空格分割.如果目标是静态库文件名,代表的是库成员(.o文件).- $^
规则的所有依赖文件列表,使用空格分隔.如果目标是静态库文件,它所代表的只能是所有库成员(.o文件)名.一个文件可重复的出现在目标的依赖中,变量“$^”只记录它的一次引用情况.就是说变量“$^”会去掉重复的依赖文件.- $+
类似“$^”,但是它保留了依赖文件中重复出现的文件.主要用在程序链接时库的交叉引用场合.- $*
这个变量表示目标模式中"%"及其之前的部分.如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo".这个变量对于构造有关联的文件名是比较有较.如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分.例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo".这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中.如果目标中的后缀是make所不能识别的,那么"$*"就是空值.例子:
test:main.o hello.o g++ -o $@ main.o hello.o main.o:main.cpp hello.h g++ -c $< -Iinclude hello.o:hello.cpp hello.h g++ -c $< -Iinclude .PHONY:clean clean: -rm test hello.o默认变量
- AR :档案管理程序,默认为:ar
- AS :汇编编译程序,默认为:as
- CC :C 语言编译程序,默认为:cc
- CXX :C++ 编译程序,默认为:g++
- CPP :带有标准输出的 C 语言预处理程序,默认为:$(CC) -E
- RM :删除文件的命令:,默认为: rm -f
伪目标
说明
为了让目标不是作为目标文件而是一个标签而已,从而不生成目标文件,让 make 无法生成它的依赖关系和决定它是否执行,只有显示第指明这个"目标"才能让其生效.当然"伪目标"的取名不能和文件名重名,不然就失去了"伪目标"的意义
为了避免和文件重名的这种情况,可以使用一个特殊的表示 .PHONY 来显示指明一个目标是"伪目标",向 make 说明不给是否有这个文件,这个目标就是 "伪目标"
例子
.PHONY : clean clean : -rm edit $(objects)前面说过,.PHONY 意思表示 clean 是一个“伪目标”,而在 rm 命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事.当然,clean 的规则不要放在文件的开头,不然,这就会变成 make 的默认目标,相信谁也不愿意这样.不成文的规矩是——“clean 从来都是放在文件的最后”.
引用其它的Makefile
说明
在 Makefile 使用 include 关键字把别的 Makefile 包含进来,被包含的文件会原模原样的放在当前文件的包含位置
语法格式
include file
注解:file 可以是当前操作系统 Shell 的文件模式(可含路径和通配符)在 include 前可有一些空字符,但绝不能是 [Tab] 键开始.include 和 file 可以用一个或多个空格隔开
例子:
目录结构 -------------------------------------------
| -- makefile
| -- a.mk
| -- b.mk
`-- src
| -- c.mk
`-- d.mk
makefile--------------------------------------------include *.mk src/*.mk #等价于:include a.mk b.mk src/c.mk src/d.mk工作方式:
make 命令开始时,会把找寻 include 所指出的其它 Makefile,并把其内容安置在当前的位置.如果文件都没有指定绝对路径或是相对路径的话,make 会在当前目录下首先寻找,如果当前目录下没有找到,那么,make 还会在下面的几个目录下找:
- 如果 make 执行时,有 “-I” 或 “--include-dir” 参数,那么make就会在这个参数所指定的目录下去寻找.
- 如果目录 <prefix>/include(一般是:/usr/local/bin 或 /usr/include)存在的话,make 也会去找.
如果有文件没有找到的话,make 会生成一条警告信息,但不会马上出现致命错误.它会继续载入其它的文件,一旦完成 makefile 的读取,make 会再重试这些没有找到,或是不能读取的文件,如果还是不行,make 才会出现一条致命信息.如果你想让 make 不理那些无法读取的文件,而继续执行,你可以在 include 前加一个减号 "-" .如:
-include <filename>
其表示,无论 include 过程中出现什么错误,都不要报错继续执行.和其它版本 make 兼容的相关命令是 sinclude,其作用和这一个是一样的.
文件搜索
VPATH
说明:
makefile 文件中有个特殊的变量 VPATH ,这个变量的作用是在 make 在但前目录找不到的情况下,VPATH 变量中保存的路径中去找寻文件,若没有指明这个变量, make 只会在当前的目录下去找寻依赖文件和目标文件,如果有多个目录可以以 ":" 分隔多个目录
例子:
VPATH = src:../headers #在本目录下的 src 目录下去找和在上级目录的 headers 目录下去找依赖文件
vpath
说明:
vpath 是 make 中的关键字,它的作用和上面的 VPATH 作用相似,但是它更为灵活.它可以在不同的搜索目录中指定不同的文件
使用方法:
- vpath pattern dir 为符合模式 pattern 的文件指定搜索目录 dir ,如果有多个目录可以以 ":" 分隔多个目录
- vpath pattern 清除符合模式 pattern 的文件搜索目录
- vpath 清除所有已被设置好了的文件搜索目录
例子:
vpath %.h ../headers #到上级目录下的 headers 目录下去找寻所有以 ".h" 表示的头文件, "%" 字符表示匹配一个以上的字符
例子:
文件目录结构--------------------------------------------------
|-- Makefile
|-- main.cpp
|-- include
| `-- hello.h
`-- src
`-- hello.cpp程序源码--------------------------------------------------
/****************************************/ //hello.h: #ifndef _HELLO_H__ #define _HELLO_H__ #include<iostream> using namespace std; void hello(); #endif /****************************************/ //hello.cpp: #include"hello.h" void hello() { cout<<"Hello world"<<endl; } /****************************************/ //main.cpp: #include"hello.h" int main() { hello(); return 0; }makefile内容 --------------------------------------------------
#VPATH=include:src vpath %.cpp src vpath %.h include test:main.o hello.o g++ -o $@ main.o hello.o main.o:main.cpp hello.h g++ -c $< -I include #----------------------------------------------- #"$<" 为自动化变量,它的作用是把 make 找到的源文件用正确的路径形式表示在 g++ 的命令中 #如这里的 "$<" 表示的就是 "main.ccp" 而下面一个命令中的 "$<" 表示的就是 "src/hello.cpp" #----------------------------------------------- #虽然前面 vpath %.h include 告诉le make ,所依赖的头文件在在本目录和子目录 include 下找 #源文件在本目录和子目录 src 下找,但是不会把这个消息告诉 gcc 或 g++ #所以还是要手动把头文件所在目录告诉给 g++ 或 gcc ,即: I -inlude hello.o:hello.cpp hello.h g++ -c $< -I include .PHONY:clean clean: -rm test hello.o
参考文献
Makefile中自动化变量 http://blog.sina.com.cn/s/blog_45497dfa0100jk09.html
Makefile VPATH和vpath的使用 http://blog.csdn.net/changli_90/article/details/7881905
跟我一起写 Makefile(四)http://blog.csdn.net/haoel/article/details/2889
使用变量 http://wiki.ubuntu.org.cn/index.php?title=%E8%B7%9F%E6%88%91%E4%B8%80%E8%B5%B7%E5%86%99Makefile:%E4%BD%BF%E7%94%A8%E5%8F%98%E9%87%8F&variant=zh-hant
make工作流程
objects = b.o c.o d.o a : $(objects) cc -o a $(objects) b.o : b.c b.h cc -c b.c c.o : c.c c.h cc -c c.c d.o : d.c d.h cc -c d.c clean : rm a $(objects)
- make 会在当前目录下找名字叫“makefile”或“makefile”的文件.
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到 “a” 这个文件,並把这个文件作为最終的目标文件.
- 如果 a 文件不存在,或是 a 所依赖的后面的 .o 文件的文件修改时间要比 a 这个文件新,那么,他就会執行后面所定义的命令來生成 a 这个文件.
- 如果 a 所依赖的.o文件也不存在,那么 make 会在当前文件中找目标为 .o 文件的依赖性,如果找到再根据那一个规则生成 .o 文件.(这有點像一个堆栈的过程)
- 当然,你的 C 文件和 H 文件存在的,於是 make 会生成 .o 文件,然后再用 .o 文件生成 make 的终极任务,也就是执行文件 a 了.
这就是整个 make 的依赖性, make 会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件.在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么 make 就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make 根本不理.make 只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么 make 就不会工作.
make 工作执行步骤
- 读入所有的 makefile.
- 读入被 include 的其它 makefile.
- 初始化文件中的变量.
- 推导隐晦规则,并分析所有规则.
- 为所有的目标文件创建依赖关系链.
- 根据依赖关系,决定哪些目标要重新生成.
- 执行生成命令.
make自动推导
只要 make 看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果 make 找到一个 b.o,那么 b.c 就会是 b.o的依赖文件.并且 gcc -c b.c 也会被推导出来,于是,我们的 makefile 再也不用写得这么复杂.
objects = b.o c.o d.o a : $(objects) cc -o a $(objects) b.o : b.c b.h c.o : c.c c.h d.o : d.c d.h clean : rm a $(objects)
make的参数
下面列举了所有GNU make 3.80版的参数定义.其它版本和产商的make大同小异,不过其它产商的make的具体参数还是请参考各自的产品文档.
“-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”
显示帮助信息.“-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发现有未定义的变量,那么就输出警告信息.
参考链接:
跟我一起写 Makefile(十一)http://blog.csdn.net/haoel/article/details/2896