您的位置:首页 > 其它

makefile 简单使用

2012-09-21 19:10 148 查看

规则

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


伪目标

说明

为了让目标不是作为目标文件而是一个标签而已,从而不生成目标文件,让 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的使用 /article/10750334.html

跟我一起写 Makefile(四)/article/2551853.html

使用变量 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命令

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(十一)/article/2551860.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: