"make"使用入门(转)
时间:2006-03-23 来源:rwen2012
"make"使用入门
原文作者:Suminder S. Ahuja
翻译:艾艾
正文:
-
概述
-
makefile介绍
-
makefile组成
-
注释
-
显式规则
-
make的执行
-
使用make的好处
-
-
定义变量
-
引用替换和计算变量名
-
递归扩展和简单扩展变量
-
给变量添加文本
-
自动变量
-
特定目标和特定模式变量值
-
-
隐含规则
-
指示
-
"Define" 指示
-
"Include" 指示
-
"Override" 指示
-
-
-
命令回显
-
伪目标
-
在文件名中使用通配符
-
命令中的错误
-
并行执行
-
用make更新归档文件
-
结论
-
附加参考
-
关于作者
概述
make是UNIX®下的一个工具,用来自动创建程序并且进行优化. 当你开发的程序由许多组件或源文件组成时它特别有用.
一个叫makefile的描述符文件阐明了源文件之间的关系,并给出更新每一个文件的命令. 任何时候当源文件之一有所改动,make就调用 makefile自动重建这个程序. 因为仅重编译那些受改动影响的文件,因此节约了编译时间.同时也有助于降低用命令行建立这些条目时发生人为错误的可能性.
除了编译以外, make也是一个可用于程序安装和更改系统配置的有效工具.
makefile介绍
为每一个工程创建一个名为makefile的配置文件. 每次修改源程序后, 用简单的命令make调用makefile来执行必要的重编译. make在当前工作目录中按以下的顺序查找这个描述文件:
makefile
Makefile
缺省条件下, 简单的make命令只创建文件中第一个目标. make命令的其它一般用法如下.
|
makefile组成
makefile包含的五个主要部分是: 注释, 显式规则, 变量定义, 隐式规则以及指示.
注释
makefile中以'#'开始到行末的内容是注释.一个注释可以延续到多行,只要在行末加上一个('/'),但这个反斜线不能被另一个反斜线所转义. 注释可以放在一行中的任何位置,在'#' 之前的内容不属于注释. 缺省的/bin/sh shell允许命令行注释,但不允许在定义指示的时候加注释.
显式规则
makefile由'规则'(rules)组成. 规则说明在何时和如何重建某些文件,这些文件又可能是其他一些特定文件的目标. 规则由三部分组成: 一个或多个目标(target), 零个或多个先决条件(prerequisites), 零个或多个命令(command).
target ... : prerequisites
command
...
...
目标是make创建的域的通常叫法;例如可执行文件或目标文件. 目标也可以是要执行的动作的名字, 例如, clean (见伪目标). 一旦任何先决条件变化将引起目标被创建. 如果目标存在且比它的先决条件新,则认为目标是"最新"的.
先决条件是一个或多个文件,作为创建目标的输入. 先决条件的目的是定义某些目标对某些源文件的依赖属性.
命令是在有先决条件的规则之中. 它是当任何先决条件改变时make创建或更新目标采取的动作. 一条规则可包含多个命令. 每个在规则中执行的命令由shell解释执行. 缺省情况下, make用/bin/sh shell. 宏SHELL = bin/sh将覆盖缺省shell.
make的执行
make遵循"依赖规则"概念; make 读取当前目录下的makefile,从处理第一个目标开始. make查找每个目标的依赖(先决条件)以知道它们是否也作为目标列了出来 .
make沿依赖链遍历整个递归链直到发现一个没有先决条件的目标,或者其先决条件没有规则. 一旦到达依赖链的末端, make便返回递归,在返回的过程执行每条目标规则中的命令. 对于遇到的所有有规则的先决条件, make采用同样的模式处理 .
一旦所有先决条件规则运行完了, make最后返回第一个目标 . 如果目标不存在, 或者目标比它现在的先决条件生成的早, make运行命令来生成目标, 如以下是一个makefile的例子 (例 1.0).
# Example 1.0 # Linking object files prog1 : main.o file1.o \ file2.o display.o cc -o prog1 main.o file1.o \ file2.o display.o # Compiling source files main.o : main.c mydefs.h cc -c main.c file1.o : file1.c mydefs.h cc -c file1.c file2.o : file2.c command.h cc -c file2.c display.o : display.c command.h cc -c display.c # Compiling source files ..PHONY : clean clean : rm prog1 *.o # End of makefile |
注意: 参考命令回显.
在前面的例子中, 我们已经给变量objs赋了值.
objs = main.o file1.o file2.o display.o
一旦变量定义好了, 它的值可以用$(variable)或 ${variable}提取出来. 然而, 如果圆括号遗漏了, 那么只有变量名的第一个字符被用到. 因此:
·$(objs)得到变量objs的值.
·$objs 得到变量 o 的值(如果有变量o).
make能灵活的使用未定义的变量; 这种情况下值返回空串. 在设定一个变量前, make可以用快捷符'?='检查它是否已被设置. 下面的例子检查 foo是否已经被设置:
foo ?= bar
如果没有, make 将分配给它一个值.
引用替换和计算变量名
还有两个引用变量的高级特性是引用替换和计算变量名.
·引用替换
引用替换用你指定的值替换变量的值. 它的格式象这样:'$(var:x=y)', 意思是取得变量 var的值, 用y替换变量值中每个单词结尾处的x, 并取代结果字串. 下面的例子设定bar为a.c b.c c.c:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
·计算变量名
在变量名内部可以引用另一个变量; 这称为计算变量名.
例:
a = b
b = c
x := $($(a))
本例赋x值为'c'. $(a)展开为 'b', 所以$($(a))即$(b); 而 $(b)展开为'c'.
递归扩展与简单扩展变量
变量有两种方法获得值, 递归扩展变量和简单扩展变量.
递归扩展变量 |
简单扩展变量 |
行赋值用'=' 或 用 "define" 指示 |
用行':='赋值 |
值的指定是逐字被装配的. |
变量在定义时一次扫描获得其值 |
不论何时变量被替代都展开对其他变量的参考. |
不包含对其它变量的参考; 包含它们被定义的值. |
例: foo = $(bar) bar = $(yep) yep = hello 命令: all :;echo $(foo) 将显示'hello'. $(foo)将展开为$(bar), $(bar)展开为$(yep)最终展开为'hello'. |
例: a := foo b := $(a) bar a := hello 等价于: b := foo bar a := hello |
执行是有目的的,却导致make运行速度很慢, 因为每当变量扩展时在此定义中的引用都要执行一遍. |
更容易预测复杂的makefile的编写. |
给变量添加更多文本
我们常需要给一个已存在的变量添加更多的文本. 快捷操作符 '+=' 提供了这种灵活性. 在下面这个例子中, 先取得objs的值然后给这个值加上文本file3.o :
objs += file3.o
这样我们就能够设定objs为main.o file1.o file2.o display.o file3.o:
objs = main.o file1.o file2.o display.o
objs += file3.o
自动变量
基于规则的目标和先决条件,此类变量的值在每条已执行的规则中都各不相同. 在下一节隐式规则的例1.2中, '$@' 用于目标文件名, '$+' 用于源文件名.
通用的自动变量包括以下这些.
$@ 规则目标的文件名 $% 目标成员名, 此时目标是一个归档成员 $< 第一个先决条件名 $? 所有比目标新的先决条件名 $^ 全体用空格符分隔的先决条件名 $+ 类似于'$^', 但是重复的先决条件会以它们在 makefile中列出的次序多次列出。 |
特定目标和特定模式的变量值
·特定目标变量值
根据make所建目标的不同,这个功能可以为相同的变量赋不同的值. 该值在目标的命令脚本的上下文中局部可用. 格式如下:
target ... : variable-assignment
下面的语句在prog1以及它的先决条件的命令脚本中设置CFLAGS为-g :
prog1 : CFLAGS = -g
prog1 : main.o file1.o file2.o display.o
·特定模式变量值
此特征让你为任何匹配这种特别模式的目标定义一个变量. 模式表示为 '%'. 接下来的例子中,对于所有匹配模式%.o的目标分配给CFLAGS值-o:
%.o : CFLAGS = -o
(更多信息请参考 附加参考.)
隐式规则
隐式规则告诉make如何采用惯例方法,这样就不必在每次用到时都要详细指定它们. 其中一条隐式规则是用cc -c命令更新文件'.o',其源文件是相应的'.c'文件. 用隐式规则, 前一个例子中的makefile可以写成下面的形式(例 1.2).
# Example 1.2 #1 # Defining the compiler CC=gcc #2 # Defining the object (objs) variable objs = main.o file1.o file2.o display.o #3 # Linking object files prog1 : $(objs) $(CC) -o $@ $+ echo prog1 : make complete #4 # Tell make how to build .o from .c files %.o:%.c $(CC) -c $+ #5 # Compiling source files. main.o : mydefs.h file1.o : mydefs.h file2.o : command.h display.o : command.h #6 # Removing the executable and object files ..PHONY : clean clean : rm prog1 $(objs) echo clean : make complete # End of makefile |
注意:
1.编译器变量提供了对同一个makefile使用不同的编译器的灵活能力.
2.变量objs定义为全体目标文件.
3.'$@'自动变量意思是目标, '$+' 意思是全体以空格分隔的先决条件.
4.模式规则告诉 make如何转换*.c 文件为 *.o 文件.
5.make有一个内建模式将*.h文件转换为依赖*.o文件.
6.make删除现有目录下的prog1和全部对象文件 (见 伪目标).
以隐含规则建立的命令用到某些预定义的变量, 这些变量分成两类: 一类是程序名 (象 CC) 另一类是对应这些程序的特定变量 (如 CFLAGS).
如下是一些内建规则中用作程序名的变量.
AR 存档文件维护程序, 缺省是 ar AS 做汇编的程序, 缺省是 as CC 编译C程序的程序, 缺省是 cc CXX 编译C++程序的程序 , 缺省是 g++ RM 删除文件命令, 缺省是 rm -f |
下列一些变量它们的值是程序附加的参数.
ARFLAGS 给归档维护程序的标志; 缺省是 rv ASFLAGS 给汇编器的额外标志 CFLAGS 给C编译器的额外标志 CXXFLAGS 给C++编译器的额外标志 |
指示
"Define" 指示
define指示给变量赋值. define指示与跟着的变量名在同一行. 变量值在下一行. 最后一行的endef表示define结束. define作用类似于'=', 举例如下:
define two-lines
echo foo
echo $(bar)
endef
普通变量赋值和define指示的区别在于普通赋值不能含换行符, 而在define的值里分割行的换行符成为变量值的一部分.
以上例子功能等同于:
two-lines = echo foo; echo $(bar)
以分号分开的两条命令作用很象两条分别的shell命令. 然而, 注意到分在两行make将调用两次shell, 对每一行运行一次独立的子shell.
"Include" 指示
指示include告诉make暂停读取当前makefile转而读取每一个列出的文件. 读完以后, make 在指示中出现的地方重新读取makefile. 格式如下:
include filenames...
filenames可以含shell文件名模式.
举个例子, 如果你有三个 '.mk' 文件, 'x.mk', 'y.mk', 及 'z.mk', 并且$(bar)是展开为 bish bash, 那么下列表达式:
include foo *.mk $(bar)
等价于:
include foo x.mk y.mk z.mk bish bash
include指示的一个用法是分配一个公共变量定义集给被各自"makefile"文件处理的程序 .
"Override" 指示
override 指示可以设置makefile中已经用命令参数设置过的变量. 格式如下:
override variable = value
给在命令行中定义的变量添加文本, 用:
override variable += more text
此指示被发明来做更改或增加用户在命令行中指定的值. 举例来说, 假定你总是希望C编译器用-g开关, 但是你又不想让用户象通常一样用命令参数指定其他开关. 你可以用override指示:
override CFLAGS += -g
命令回显
make在一行被执行前先显示它, 叫做回显. 以'@'起始的命令则不回显 . echo也可以用来指示makefile的进度. 如下所示:
echo prog1 : make complete
加上-n标志, make只回显命令但不真正执行它们. 只有这种情况, 即使命令以 '@' 开头亦会显示. 为防止一切回显, 可以用-s标志. 输出就如同每条命令都以'@'开头.
伪目标
伪目标不是一个文件名而是由一个显式请求引起的动作. 在伪目标中的命令不创建任何目标, 但每次目标发生重建时命令都要执行. 举例如下:
clean :
rm prog1 *.o
当命令象下面这样显式的调用时,将删除 prog1及所有目标文件:
make clean
因为rm不是创建一个名为 clean的文件, 可能这个文件根本不曾存在. 但如果某人确实在这个目录下建了名为clean的文件, 伪目标将停止工作. 文件clean在没有先决条件时将不可避免的被认为是最近更新的, 它的命令也不会执行.
为避免这个问题, 目标被定义为 .PHONY. 在下面的例子中, make clean 将运行命令而不论是否真有名clean 的文件:
.PHONY : clean
clean :
rm prog1 *.o
在文件名中用通配符
单个文件名用通配符可指代多个文件. make在目标先决条件, 命令和变量中用到通配符 '*', '?', 以及'[...]'. 通配符可以用在规则命令中, 这些命令被shell展开. 例如, 这是删除所有目标文件的规则:
clean:
rm *.o
当你定义变量时通配符是不会展开的. 即, 如果你这样写:
objects = *.o
那么变量对象的值是真实的字符串 '*.o'.
命令中的错误
命令执行完以后, make 检查退出状态以验证其已经成功完成, 同时发送下一条命令到新shell. 如果返回错误, make 放弃当前规则,很可能放弃所有规则. 要忽略命令行中的错误, 在行开头加上'-'号 (在初始的tab后). '-'号在命令传递给shell执行前丢弃. 下面的例子即使rm不能删除文件它也继续运行, :
clean:
-rm -f *.o
类似的特性也可以通过在make后加-i参数实现; 全体规则中的全部命令的错误都被忽略.
并行执行
缺省情况下, make一次只运行一条命令, 在执行下一条命令前等待上一条命令执行结束. make用'-j'选项可以同时执行多条命令. '-j'选项后缀的整数指定一次可以运行作业的数量.
用make更新归档文件
被叫做成员的归档文件通常用作链接的子程序库. 它们由程序ar维护. 格式如下:
archive(member)
make中独立的文件成员可以作为目标或先决条件. 在下面的例子中, 是要通过复制文件foo.o在归档sublib中创建成员foo.o :
sublib(foo.o) : foo.o
ar cr sublib foo.o
结论
本文是使用makefile入门教程, 内容涉及所有知识面. make还有许多特性可以在下列参考中发现. 用你自己的新makefile试一试.