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.Linuxmake --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中使用变量

说明:

  1. 变量在声明时需要给与初值,而在使用时,需要给在变量名前家上 "$" 符号,但最好用小括号 "()" 或者大括号 "{}" 把变量给括起来,如果你要使用真实的 "$ "字符,那么你需要用 "$$" 来表示
  2. 在 Makefile 中的定义的变量,就像是 C/C++ 语言中的宏一样,他代表了一个文本字串,在 Makefile 中执行的时候其会自动原模原样地展开在所使用的地方.其与 C/C++ 所不同的是,你可以在 Makefile 中改变其值.
  3. 变量的命名字可以包含字符、数字,下划线 (可以是数字开头),但不应该含有 ":" 、"#"、"=" 或是空字符(空格、回车等).变量是大小写敏感.

使用:

  • 变量的定义:变量名 = 变量实际内容
  • 使用变量:$(变量名)

例子:

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)

自动化变量

  1. $@  
    表示规则的目标文件名.如果目标是一个文档文件(Linux中,一般称.a文件为文档文件,也称为静态库文件),那么它代表这个文档的文件名.在多目标模式规则中,它代表的是哪个触发规则被执行的目标文件名.
  2. $%  
    当规则的目标文件是一个静态库文件时,代表静态库的一个成员名.例如,规则的目标是“foo.a(bar.o)”,那么,“$%”的值就为“bar.o”,“$@”的值为“foo.a”.如果目标不是静态库文件,其值为空.
  3. $<  
    规则的第一个依赖文件名.如果是一个目标文件使用隐含规则来重建,则它代表由隐含规则加入的第一个依赖文件.
  4. $?  
    所有比目标文件更新的依赖文件列表,空格分割.如果目标是静态库文件名,代表的是库成员(.o文件).
  5. $^  
    规则的所有依赖文件列表,使用空格分隔.如果目标是静态库文件,它所代表的只能是所有库成员(.o文件)名.一个文件可重复的出现在目标的依赖中,变量“$^”只记录它的一次引用情况.就是说变量“$^”会去掉重复的依赖文件.
  6. $+  
    类似“$^”,但是它保留了依赖文件中重复出现的文件.主要用在程序链接时库的交叉引用场合.
  7. $*
    这个变量表示目标模式中"%"及其之前的部分.如果目标是"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 还会在下面的几个目录下找:

    1. 如果 make 执行时,有 “-I” 或 “--include-dir” 参数,那么make就会在这个参数所指定的目录下去寻找.
    2. 如果目录 <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 作用相似,但是它更为灵活.它可以在不同的搜索目录中指定不同的文件

使用方法:

  1. vpath pattern dir  为符合模式 pattern 的文件指定搜索目录 dir ,如果有多个目录可以以 ":" 分隔多个目录
  2. vpath pattern  清除符合模式 pattern 的文件搜索目录
  3. 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)
  1. make 会在当前目录下找名字叫“makefile”或“makefile”的文件.
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到 “a” 这个文件,並把这个文件作为最終的目标文件.
  3. 如果 a 文件不存在,或是 a 所依赖的后面的 .o 文件的文件修改时间要比 a 这个文件新,那么,他就会執行后面所定义的命令來生成 a 这个文件.
  4. 如果 a 所依赖的.o文件也不存在,那么 make 会在当前文件中找目标为 .o 文件的依赖性,如果找到再根据那一个规则生成 .o 文件.(这有點像一个堆栈的过程)
  5. 当然,你的 C 文件和 H 文件存在的,於是 make 会生成 .o 文件,然后再用 .o 文件生成 make 的终极任务,也就是执行文件 a 了. 

这就是整个 make 的依赖性, make 会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件.在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么 make 就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make 根本不理.make 只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么 make 就不会工作.

make 工作执行步骤

  1. 读入所有的 makefile.
  2. 读入被 include 的其它 makefile.
  3. 初始化文件中的变量.
  4. 推导隐晦规则,并分析所有规则.
  5. 为所有的目标文件创建依赖关系链.
  6. 根据依赖关系,决定哪些目标要重新生成.
  7. 执行生成命令.

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

你可能感兴趣的:(makefile 简单使用)