您的位置:首页 > 运维架构 > Linux

makefile 文件的编写

2014-12-30 16:32 351 查看


makefile 文件的编写

跟我一起写 Makefile(一)

跟我一起写 Makefile(二)

跟我一起写 Makefile(三)

跟我一起写 Makefile(四)

跟我一起写 Makefile(五)

跟我一起写 Makefile(六)

跟我一起写 Makefile(七)

跟我一起写 Makefile(八)

跟我一起写 Makefile(九)

跟我一起写 Makefile(十)

跟我一起写 Makefile(十一)

跟我一起写 Makefile(十二)

跟我一起写 Makefile(十三)

跟我一起写 Makefile(十四)

规则

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>
#endif
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命令

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

转载自:http://www.cnblogs.com/kzang/articles/2679933.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  makefile c++ linux