Makefile使用入门


原文作者: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命令的其它一般用法如下. 
make prog1  建立目标 "prog1" 
make -f mymakefile  用 "mymakefile" 作配置文件建立源文件中的第一个目标 
make -f mymakefile prog1  用 "mymakefile" 作配置文件建立目标 "prog1"

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试一试. 
更多参考
1.GNU Make, 一个用于指导重编译的程序 
2.Sun的make官方参考页(Sun official reference page on make) 
关于作者
Suminder Singh Ahuja 是一位有四年经验的软件工程师, 他工作的领域是密集事务技术和数据库,包括TPF, IBM 370/390, VM/CMS, 以及UNIX. 他在Galileo工作了三年, 主管航空订票解决方案. 联系他 Suminder: suminder@rediffmail.com 
July 2002
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值