How to write a Makefile
怎么写一个好的Makefile
Introduction
Make is one of the original Unix tools for Software Engineering. By S.I. Feldman of AT&T Bell Labs circa 1975. But there are public domain versions (eg. GNU) and versions for other systems (eg. Vax/VMS).
Related tools are the language compilers (cc, f77, lex, yacc, etc.) and shell programming tools (eg. awk, sed, cp, rm, etc.). You need to know how to use these.
Important adjuncts are lint (source code checking for obvious errors) ctags (locate functions, etc. in source code) and mkdepend. These are nice, and good programmers use them.
Important, and related tools, are the software revision systems SCCS (Source Code Control System) and RCS (Revision Control System -- the recommended choice)
The idea is to automate and optimize the construction of programs/files -- ie. to leave enough foot prints so that others can follow.
介绍部分
Makefile Naming
make is going to look for a file called Makefile, if not found then a file called makefile. Use the first (so the name stands out in listings).
You can get away without any Makefile (but shouldn't)! Make has default rules it knows about.
Makefile 的命名
make 首先寻找一个叫 Makefile 的文件,如果没有找到才去找叫 makefile的文件,尽量去使用第一个名字.
Makefile Components
-
Comments
Comments are any text beginning with the pound (#) sign. A comment can start anywhere on a line and continue until the end of the line. For example:
# $Id: slides,v 1.2 1992/02/14 21:00:58 reggers Exp $
- 注释
注释开始于一个 # 符号,以后的一行都是被注释掉的.比如:
# $Id: slides,v 1.2 1992/02/14 21:00:58 reggers Exp $
-
Macros
Make has a simple macro definition and substitution mechanism. Macros are defined in a Makefile as
MACROS= -me
PSROFF= groff -Tps
DITROFF= groff -Tdvi
CFLAGS= -O -systype bsd43There are lots of default macros -- you should honor the existing naming conventions. To find out what rules/macros make is using type:
% make -p
NOTE: That your environment variables are exported into the make as macros. They will override the defaults.
You can set macros on the make command line:
% make "CFLAGS= -O" "LDFLAGS=-s" printenv cc -O printenv.c -s -o printenv
- 宏
Make拥有一套简单的宏替换机制.在Makefile中宏这样定义:
MACROS= -me
PSROFF= groff -Tps
DITROFF= groff -Tdvi
Make 中有大量的默认宏替换,你应该尽量躲避这些名字.
如果要找到哪些 规则/宏 在使用,应该键入:
% make -p
NOTE:环境变量可以象宏一样引入到Make中,他们会覆盖初始的宏规则
你可以在make命令中设定宏
% make "CFLAGS= -O" "LDFLAGS=-s" printenv
cc -O printenv.c -s -o printenv
-
Targets
You make a particular target (eg. make all), in none specified then the first target found:
paper.dvi: $(SRCS)
$(DITROFF) $(MACROS) $(SRCS) >paper.dviNOTE: The the line beginning with $(DITROFF) begins with TAB not spaces.
The target is made if any of the dependent files have changed. The dependent files in this case are represented by the $(SRCS) statement.
- 目标
如果没有特定目标的话,第一个目标会被作为最终的目标,当然,你也可以定义一个特定的目标(比如 make all ):
paper.dvi: $(SRCS)
NOTE: $(DITROFF)前面是一个TAB 而不是一个空格.
$(DITROFF) $(MACROS) $(SRCS) >paper.dvi
如果任何一个依赖文件改变的话,目标会被重新编译, 在本例中,$(SRCS)语句会被替换成依赖文件.
-
Continuation of Lines
Use a back slash (). This is important for long macros and/or rules.
- 行的连续
反斜杠用来表示下一个是上一行的内容. 在一个长的宏或者规则中,这是非常重要的.
-
Conventional Macros
There are lots of default macros (type "make -p" to print out the defaults). Most are pretty obvious from the rules in which they are used:
AR = ar
GFLAGS =
GET = get
ASFLAGS =
MAS = mas
AS = as
FC = f77
CFLAGS =
CC = cc
LDFLAGS =
LD = ld
LFLAGS =
LEX = lex
YFLAGS =
YACC = yacc
LOADLIBS =
MAKE = make
MAKEARGS = 'SHELL=/bin/sh'
SHELL = /bin/sh
MAKEFLAGS = b - 默认宏定义
这里罗列了许多默认的宏定义(用 make -p 来显示)
AR = ar
GFLAGS =
GET = get
ASFLAGS =
MAS = mas
AS = as
FC = f77
CFLAGS =
CC = cc
LDFLAGS =
LD = ld
LFLAGS =
LEX = lex
YFLAGS =
YACC = yacc
LOADLIBS =
MAKE = make
MAKEARGS = 'SHELL=/bin/sh'
SHELL = /bin/sh
MAKEFLAGS = b
-
Special Macros
Before issuing any command in a target rule set there are certain special macros predefined.
-
$@ is the name of the file to be made.
-
-
$? is the names of the changed dependents.
So, for example, we could use a rule
printenv: printenv.c
$(CC) $(CFLAGS) $? $(LDFLAGS) -o $@alternatively:
printenv: printenv.c
$(CC) $(CFLAGS) $@.c $(LDFLAGS) -o $@There are two more special macros used in implicit rules. They are:
-
$< the name of the related file that caused the action.
-
-
$* the prefix shared by target and dependent files.
- 特定的宏定义
在我们讨论目标规则定义之前,让我们先来看看一些非常特殊的预设宏定义
- $@ 是我们要生成的文件的名字
- $? 是改变了的依赖的名字.例如,我们可以定义这样的规则.
printenv: printenv.c
$(CC) $(CFLAGS) $? $(LDFLAGS) -o $@或者:
printenv: printenv.c
$(CC) $(CFLAGS) $@.c $(LDFLAGS) -o $@
- $< 表示引起动作的被依赖文件的名字.通常是第一个依赖文件的名字.
- $* 表示目标和依赖文件相同的前缀.
-
Makefile Target Rules
The general syntax of a Makefile Target Rule is
target [target...] : [dependent ....]
[ command ...]Items in brackets are optional, ellipsis means one or more. Note the tab to preface each command is required.
The semantics is pretty simple. When you say "make target" make finds the target rule that applies and, if any of the dependents are newer than the target, make executes the commands one at a time (after macro substitution). If any dependents have to be made, that happens first (so you have a recursion).
A make will terminate if any command returns a failure status. That's why you see rules like:
clean:
-rm *.o *~ core paperMake ignores the returned status on command lines that begin with a dash. eg. who cares if there is no core file?
Make will echo the commands, after macro substition to show you what's happening as it happens. Sometimes you might want to turn that off. For example:
install:
@echo You must be root to install - Makefile目标规则
通常的目标规则语法如下
目标(target) [目标(target)...] : [依赖文件(dependent) ....]
在中括号中的元素是可选的, 省略号代表很多个.注意在每个命令之前要有一个TAB
[ 命令(command) ...]
语义(semantics)非常简单,当你说 "make 目标(target)" 的时候,make会去执行相应的规则.如果任何的依赖文件比目标更加新的话,make就执行相应的命令(在宏扩展之后). 如果有任何的依赖需要生成,make会去生成它,所以你可以递归定义这些规则.
如果任何一个命令返回错误,make会终止. 这就是你看到这些规则的原因.
clean:
Make 忽略以横杠开头命令的返回值.
-rm *.o *~ core paper
在宏替代之后 ,Make 将会回显(echo)什么发生了.Sometimes you might want to turn that off. 例如
install:
@echo You must be root to install
-
Example Target Rules
For example, to manage sources stored within RCS (sometimes you'll need to "check out" a source file):
SRCS=x.c y.c z.c
$(SRCS):
co $@To manage sources stored within SCCS (sometimes you'll need to "get" a source file):
$(SRCS):
sccs get $@Alternativley, to manage sources stored within SCCS or RCS let's generalize with a macro that we can set as required.
SRCS=x.c y.c z.c
# GET= sccs get
GET= co
$(SRCS):
$(GET) $@For example, to construct a library of object files
lib.a: x.o y.o z.o
ar rvu lib.a x.o y.o z.o
ranlib lib.aAlternatively, to be a bit more fancy you could use:
OBJ=x.o y.o z.o
AR=ar
lib.a: $(OBJ)
$(AR) rvu $@ $(OBJ)
ranlib $@Since AR is a default macro already assigned to "ar" you can get away without defining it (but shouldn't).
If you get used to using macros you'll be able to make a few rules that you can use over and over again.
For example, to construct a library in some other directoryINC=../misc
OTHERS=../misc/lib.a
$(OTHERS):
cd $(INC); make lib.aBeware:, the following will not work (but you'd think it should)
INC=../misc
OTHERS=../misc/lib.a
$(OTHERS):
cd $(INC)
make lib.aEach command in the target rule is executed in a separate shell. This makes for some interesting constructs and long continuation lines.
To generate a tags file
SRCS=x.c y.c z.c
CTAGS=ctags -x >tags
tags: $(SRCS)
${CTAGS} $(SRCS)On large projects a tags file, that lists all functions and their invocations is a handy tool.
To generate a listing of likely bugs in your problemslint:
lint $(CFLAGS) $(SRCS)Lint is a really good tool for finding those obvious bugs that slip into programs -- eg. type classes, bad argu- ment list, etc.
- 示例规则
例如,在RCS中管理源文件(当你需要"check out"一个源文件的时候.)
SRCS=x.c y.c z.c
$(SRCS):
co $@在SRCS中管理源文件(当你需要"get"一个源文件的时候)
$(SRCS):
sccs get $@或者,在SCCS或者RCS下管理源文件,让我们一般化这个规则,在我们需要的时候改变设置.
SRCS=x.c y.c z.c
# GET= sccs get
GET= co
$(SRCS):
$(GET) $@例如, 为目标代码构造一个静态库
lib.a: x.o y.o z.o
ar rvu lib.a x.o y.o z.o
ranlib lib.a或者,你可以使用更花哨一点的
OBJ=x.o y.o z.o
AR=ar
lib.a: $(OBJ)
$(AR) rvu $@ $(OBJ)
ranlib $@
因为 AR 已经被预定义成了"ar" , 所以你可以不定义它而直接使用(不过不推荐).
如果你习惯了使用宏,你可以定义一些你要经常使用的规则.
例如,你可以在其他的文件夹构建一个库.
INC=../misc
OTHERS=../misc/lib.a
$(OTHERS):
cd $(INC); make lib.a小心, 下面的可能不能工作(不过我认为应该可以)
INC=../misc
OTHERS=../misc/lib.a
$(OTHERS):
cd $(INC)
make lib.a
每一个在目标规则中的命令在单独的shell中执行,这产生了很多有趣的结构(constructs)和一些长的连续行.
SRCS=x.c y.c z.c
CTAGS=ctags -x >tags
tags: $(SRCS)
${CTAGS} $(SRCS)
有些工具可以在大型的工程产生一个符号文件,里面列出了所有的函数和他们的符咒.
lint:
Lint是一个很好的用来寻找一些混入代码中明显的错误的工具.例如 类型错误,参数表错误等等.
lint $(CFLAGS) $(SRCS)
译者注:现在lint基本已经成为静态检查的代名词了.如C语言中的splint.python中的pylint.
-
Some Basic Make Rule
People have come to expect certain targets in Makefiles. You should always browse first, but it's reasonable to expect that the targets all (or just make), install, and clean will be found.
-
make all -- should compile everything so that you can do local testing before installing things.
-
-
make install -- should install things in the right places. But watch out that things are installed in the right place for your system.
-
make clean -- should clean things up. Get rid of the executables, any temporary files, object files, etc.
You may encounter other common targets, some have been already mentioned (tags and lint).
-
An Example Makefile for printenv
# make the printenv command
#
OWNER=bin
GROUP=bin
CTAGS= ctags -x >tags
CFLAGS= -O
LDFLAGS= -s
CC=cc
GET=co
SRCS=printenv.c
OBJS=printenv.o
SHAR=shar
MANDIR=/usr/man/manl/printenv.l
BINDIR=/usr/local/bin
DEPEND= makedepend $(CFLAGS)
all: printenv
# To get things out of the revision control system
$(SRCS):
$(GET) $@
# To make an object from source
$(CC) $(CFLAGS) -c $*.c
# To make an executable
printenv: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS)
# To install things in the right place
install: printenv printenv.man
$(INSTALL) -c -o $(OWNER) -g $(GROUP) -m 755 printenv $(BINDIR)
$(INSTALL) -c -o $(OWNER) -g $(GROUP) -m 644 printenv.man $(MANDIR)
# where are functions/procedures?
tags: $(SRCS)
$(CTAGS) $(SRCS)
# what have I done wrong?
lint: $(SRCS)
lint $(CFLAGS) $(SRCS)
# what are the source dependencies
depend: $(SRCS)
$(DEPEND) $(SRCS)
# to make a shar distribution
shar: clean
$(SHAR) README Makefile printenv.man $(SRCS) >shar
# clean out the dross
clean:
-rm printenv *~ *.o *.bak core tags shar
# DO NOT DELETE THIS LINE -- make depend depends on it.
printenv.o: /usr/include/stdio.h -
一些基本的Make规则
人们常常预测一些明显的规则,You should always browse first, but it's reasonable to expect that the targets all (or just make), install, and clean will be found.
-
-
make all -- 应该是编译所有的文件,所以你应该在安装他们之前做本地测试.
- make install -- 应该是安装文件在正确的为之,但是注意他们是为安装你你系统中的正确位置.
- make clean -- 应该是做清扫工作,删除可执行文件,临时文件,目标文件.等等.
-
-
-
你可能会遇到另外一些常用的目标规则,有些前面已经提到了.(tags & lint)
-
一个printenv的makefile文件
# make the printenv command
#
OWNER=bin
GROUP=bin
CTAGS= ctags -x >tags
CFLAGS= -O
LDFLAGS= -s
CC=cc
GET=co
SRCS=printenv.c
OBJS=printenv.o
SHAR=shar
MANDIR=/usr/man/manl/printenv.l
BINDIR=/usr/local/bin
DEPEND= makedepend $(CFLAGS)
all: printenv
# 从版本控制系统中把文件提取出来
$(SRCS):
$(GET) $@
# 生成目标文件
$(CC) $(CFLAGS) -c $*.c
# 生成一个可执行文件
printenv: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS)
# 安装他们在正确的位置
install: printenv printenv.man
$(INSTALL) -c -o $(OWNER) -g $(GROUP) -m 755 printenv $(BINDIR)
$(INSTALL) -c -o $(OWNER) -g $(GROUP) -m 644 printenv.man $(MANDIR)
# 那些函数在那里?
tags: $(SRCS)
$(CTAGS) $(SRCS)
# what have I done wrong?
lint: $(SRCS)
lint $(CFLAGS) $(SRCS)
# what are the source dependencies
depend: $(SRCS)
$(DEPEND) $(SRCS)
# to make a shar distribution
shar: clean
$(SHAR) README Makefile printenv.man $(SRCS) >shar
# clean out the dross
clean:
-rm printenv *~ *.o *.bak core tags shar
# DO NOT DELETE THIS LINE -- make depend depends on it.
printenv.o: /usr/include/stdio.h -
Makefile 隐式规则
考虑我们在 printenv 中的规则
printenv: printenv.c
$(CC) $(CFLAGS) printenv.c $(LDFLAGS) -o printenv再一般化一点
printenv: printenv.c
$(CC) $(CFLAGS) $@.c $(LDFLAGS) -o $@The command is one that ought to work in all cases where we build an executable x out of the source code x.c This can be stated as an implicit rule:
这个命令应该可以很好的工作当我们从叫 X.C 源文件中生成一个教 X 的目标的情况下. 可以定义这样的隐式规则.
.c:
$(CC) $(CFLAGS) $@.c $(LDFLAGS) -o $@这个隐式规则说明了怎么从x.c中生成x-----运行cc x.c 还有把输出文件命名为 x . 这个规则是隐式的因为没有任何特别的目标被提到. 可是使用所有的情况...
另外一个一般的隐式规则是从.c(源文件)构造.o(目标文件).
.o.c:
$(CC) $(CFLAGS) -c $<或者
.o.c:
$(CC) $(CFLAGS) -c $*.c -
生成依赖关系
在源代码中包含另外一个头文件是很常见的事情.例如
% cat program.c #include #include "defs.h" #include "glob.h" etc.... main(argc,argv) etc...
隐式规则仅仅能够包含一部分的依赖关系. 它仅仅能够控制.c 到 .o的情况
控制这样情况的一般模型是把依赖关系分别列出来
etc...
$(CC) $(CFLAGS) -c $*.c
etc...
program.o: program.c defs.h glob.h
一般情况下 , 一个隐式的规则和一个分离的依赖关系列表能够完成大部分的工作. 而且足够简单一下就可以看出什么依赖关系存在.
然而, 还有很多很好的工具用来自动生成依赖关系列表. 例如:
DEPEND= makedepend $(CFLAGS)
etc...
# what are the source dependencies
depend: $(SRCS)
$(DEPEND) $(SRCS)
etc....
# DO NOT DELETE THIS LINE -- ....
printenv.o: /usr/include/stdio.h
这些工具在目前非常普遍,他们比较容易使用和理解.他们只是一些shell脚本运行cpp去寻找他们的依赖关系.
并把这些依赖关系接入到Makkefile的最后...
Based on Make and Makefiles by
Reg Quinton Computing and Communications Services The University of Western Ontario London, Ontario N6A 5B7 Canada翻译by:
Anson Zhang
Software engineering
UESTC
China,Chengdu.
kzjeef@gmail.com

0 条评论:
发表评论
订阅 博文评论 [Atom]
<< 主页