本文共 11960 字,大约阅读时间需要 39 分钟。
makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。makefile 文件是许多编译器--包括 Windows NT 下的编译器--维护编译信息的常用方法,只是在集成开发环境中,用户通过友好的界面修改 makefile 文件而已。
在 UNIX 系统中,习惯使用 Makefile 作为 makefile 文件。如果要使用其他文件作为 makefile,则可利用类似下面的 make 命令选项指定 makefile 文件。
一个文件,指示程序如何编译和链接程序。makefile文件的默认名称是名副其实的Makefile,但可以指定一个命令行选项的名称。make程序有助于您在开发大型程序跟踪整个程序,其中部分已经改变,只有那些编译自上次编译的程序,它已经改变了部分。
关于编译阶段
编译一个小的C程序至少需要一个单一的文件.h文件(如适用)。虽然命令执行此任务只需CC file.c中,有3个步骤,以取得最终的可执行程序,如下所示:
编译阶段:所有的C语言代码.c文件中被转换成一个低级语言汇编语言;决策.s文件。
汇编阶段:前阶段所作的汇编语言代码,然后转换成目标代码的代码片段,该计算机直接理解。目标代码文件.o 结束。
为什么需要Makefile?
对于本次教程中的讨论,假定有以下的源文件。
main.cpp 文件的内容
#include |
hello.cpp 文件的内容
#include |
factorial.cpp 文件的内容
#include "functions.h"int factorial(int n){ if(n!=1){ return(n * factorial(n-1)); } else return 1;} |
functions.h 内容
void print_hello();int factorial(int n); |
琐碎的方法来编译的文件,并获得一个可执行文件,通过运行以下命令:
CC main.cpp hello.cpp factorial.cpp -o hello |
这上面的命令将生成二进制的Hello。在我们的例子中,我们只有四个文件,我们知道的函数调用序列,因此它可能是可行的,上面写的命令的手,准备最后的二进制。但对于大的项目,我们将有源代码文件成千上万的文件,就很难保持二进制版本。
make命令允许您管理大型程序或程序组。当开始编写较大的程序,你会发现,重新编译较大的程序,需要更长的时间比重新编译的短节目。此外会发现通常只能在一小部分的程序(如单一功能正在调试),其余的程序不变。
在随后的章节中,我们将看到项目是如何准备一个makefile。
Makefile 宏
make程序允许您使用宏,这是类似的变量。 = 一对一个Makefile中定义的宏。例如:
MACROS= -mePSROFF= groff -TpsDITROFF= groff -TdviCFLAGS= -O -systype bsd43LIBS = "-lncurses -lm -lsdl"MYFACE = ":*)" |
目标规则集发出任何命令之前,有一些特殊的预定义宏。
$@ 到的文件的名称。
$? 是改变的眷属的名字。
因此,举例来说,我们可以使用一个规则
hello: main.cpp hello.cpp factorial.cpp $(CC) $(CFLAGS) $? $(LDFLAGS) -o $@alternatively:hello: main.cpp hello.cpp factorial.cpp $(CC) $(CFLAGS) $@.cpp $(LDFLAGS) -o $@ |
在这个例子中$@代表 hello, $? 或$@.cpp将拾取所有更改的源文件。
有两个比较特殊的隐含规则中使用的宏。它们是
$< 导致该操作的相应的文件中的名称。
$* 前缀共享目标和相关文件。
常见的隐含规则的构造 .o(对象)文件,.cpp(源文件)。
.o.cpp: $(CC) $(CFLAGS) -c $ |
注意:要获得更详细的关于Makefile规则,在另一部分。
有很多默认的宏(输入“make -p”打印出来的默认值)。大多数使用它们的规则是很明显的:
这些预定义变量,即。在隐含规则中使用的宏分为两大类:那些程序名(例如CC)和那些含有参数的程序(如CFLAGS)。
下面是一些比较常见的变量用作内置规则:makefile文件的程序名称的表。
AR | Archive-maintaining program; default `ar'. |
AS | Program for compiling assembly files; default `as'. |
CC | Program for compiling C programs; default `cc'. |
CO | Program for checking out files from RCS; default `co'. |
CXX | Program for compiling C++ programs; default `g++'. |
CPP | Program for running the C preprocessor, with results to standard output; default `$(CC) -E'. |
FC | Program for compiling or preprocessing Fortran and Ratfor programs; default `f77'. |
GET | Program for extracting a file from SCCS; default `get'. |
LEX | Program to use to turn Lex grammars into source code; default `lex'. |
YACC | Program to use to turn Yacc grammars into source code; default `yacc'. |
LINT | Program to use to run lint on source code; default `lint'. |
M2C | Program to use to compile Modula-2 source code; default `m2c'. |
PC | Program for compiling Pascal programs; default `pc'. |
MAKEINFO | Program to convert a Texinfo source file into an Info file; default `makeinfo'. |
TEX | Program to make TeX dvi files from TeX source; default `tex'. |
TEXI2DVI | Program to make TeX dvi files from Texinfo source; default `texi2dvi'. |
WEAVE | Program to translate Web into TeX; default `weave'. |
CWEAVE | Program to translate C Web into TeX; default `cweave'. |
TANGLE | Program to translate Web into Pascal; default `tangle'. |
CTANGLE | Program to translate C Web into C; default `ctangle'. |
RM | Command to remove a file; default `rm -f'. |
这里是一个变量,其值是上述程序的额外的参数表。所有这些的默认值是空字符串,除非另有说明。
ARFLAGS | Flags to give the archive-maintaining program; default `rv'. |
ASFLAGS | Extra flags to give to the assembler (when explicitly invoked on a `.s' or `.S' file). |
CFLAGS | Extra flags to give to the C compiler. |
CXXFLAGS | Extra flags to give to the C compiler. |
COFLAGS | Extra flags to give to the RCS co program. |
CPPFLAGS | Extra flags to give to the C preprocessor and programs that use it (the C and Fortran compilers). |
FFLAGS | Extra flags to give to the Fortran compiler. |
GFLAGS | Extra flags to give to the SCCS get program. |
LDFLAGS | Extra flags to give to compilers when they are supposed to invoke the linker, `ld'. |
LFLAGS | Extra flags to give to Lex. |
YFLAGS | Extra flags to give to Yacc. |
PFLAGS | Extra flags to give to the Pascal compiler. |
RFLAGS | Extra flags to give to the Fortran compiler for Ratfor programs. |
LINTFLAGS | Extra flags to give to lint. |
注:您可以取消`-R'或` --no-builtin-variables“选项隐含规则使用的所有变量。
如,也可以在命令行中定义的宏
make CPP = /home/courses/cop4530/spring02 |
Makefile 定义依赖性
这是很常见的,最终的二进制文件将依赖于各种源代码和源代码的头文件。依存关系是重要的,因为他们告诉对任何目标的源。请看下面的例子
hello: main.o factorial.o hello.o $(CC) main.o factorial.o hello.o -o hello |
在这里,我们告诉hello 依赖main.o,factorial.o和hello.o,所以每当有任何变化,这些目标文件将采取行动。
同时我们会告诉如何准备 .o文件,所以我们必须定义这些依赖也如下
main.o: main.cpp functions.h $(CC) -c main.cppfactorial.o: factorial.cpp functions.h $(CC) -c factorial.cpphello.o: hello.cpp functions.h $(CC) -c hello.cpp |
Makefile 定义规则
一个Makefile目标规则的一般语法
target [target...] : [dependent ....] [ command ...] |
方括号中的项是可选的,省略号是指一个或多个。注意标签,每个命令前需要。
下面给出一个简单的例子,定义了一个规则使您的目标从 hello 其他三个文件。
hello: main.o factorial.o hello.o $(CC) main.o factorial.o hello.o -o hello |
注:在这个例子中,你必须放弃规则,使所有对象从源文件的文件
语义是相当简单的。当"make target"发现目标规则适用,如有眷属的新目标,使执行的命令一次一个(后宏替换)。如果有任何依赖进行,即先发生(让您拥有一个递归)。
如果有任何命令返回一个失败状态,MAKE将终止。这就是为什么看到规则,如:
clean: -rm *.o *~ core paper |
Make忽略一个破折号开头的命令行返回的状态。例如。如果没有核心文件,谁在乎呢?
Make 会 echo 宏字符串替换的命令后,告诉发生了什么事,因为它发生。有时可能想要把它们关掉。例如:
install: @echo You must be root to install |
大家所期望的Makefile的 某些目标。应该总是先浏览,但它的合理预期的目标(或只是做),安装,清洁,会发现。
make all - 编译一切,让你可以在本地测试,之前安装的东西。
make install - 应安装在正确的地方的东西。但看出来的东西都安装在正确的地方为系统。
make clean - 应该清理的东西。摆脱的可执行文件,任何临时文件,目标文件等。
Makefile的隐含规则
该命令应该在所有情况下,我们建立一个可执行x的的源代码x.cpp的作为一个隐含的规则,这可以说:
.cpp: $(CC) $(CFLAGS) $@.cpp $(LDFLAGS) -o $@ |
这种隐含的规则说,如何make c, x.c- 运行x.c 调用输出x。规则是隐式的,因为没有特定的目标提到。它可用于在所有的情况下。
另一种常见的隐含规则的构造 .o(对象)文件和 .cpp (源文件)。
.o.cpp: $(CC) $(CFLAGS) -c $ |
Makefile 自定义后缀规则
就其本身而言,make已经知道,为了创建一个 .o文件,就必须使用 cc-c 相应的c文件。 建成MAKE这些规则,可以利用这一点来缩短Makefile。如果仅仅只是表示 .h 文件的 Makefile依赖线,依赖于目前的目标是,MAKE会知道,相应的文件已规定。你甚至不需要编译器包括命令。
这减少了我们的Makefile更多,如下所示:
OBJECTS = main.o hello.o factorial.ohello: $(OBJECTS) cc $(OBJECTS) -o hellohellp.o: functions.hmain.o: functions.h factorial.o: functions.h |
Make 使用一个特殊的目标,故名 .SUFFIXES允许你定义自己的后缀。例如,依赖线:
.SUFFIXES: .foo .bar |
告诉make ,将使用这些特殊的后缀,以使自己的规则。
如何让 make 已经知道如何从 .c 文件生成 .o文件。类似的可以定义规则以下列方式:
.foo.bar: tr '[A-Z][a-z]' '[N-Z][A-M][n-z][a-m]' < $< > $@.c.o: $(CC) $(CFLAGS) -c $< |
第一条规则允许你创建一个 .bar 文件从 .foo文件。 (不要担心它做什么,它基本上打乱文件)第二条规则 .c文件创建一个 .o 文件中使用的默认规则。
Makefile 指令
有好几种指令以不同的形式。让程序可能不支持所有指令。因此,请检查make是否支持指令,我们这里解释。 GNU make支持这些指令
条件指令
条件的指令
ifeq 指令开始的条件,指定的条件。它包含两个参数,用逗号分隔,并用括号括起。两个参数进行变量替换,然后对它们进行比较。该行的makefile继IFEQ的服从如果两个参数的匹配,否则会被忽略。
ifneq 指令开始的条件,指定的条件。它包含两个参数,用逗号分隔,并用括号括起。两个参数进行变量替换,然后对它们进行比较。makefile ifneq 遵守如果两个参数不匹配,否则会被忽略。
ifdef 指令开始的条件,指定的条件。它包含单参数。如果给定的参数为真,则条件为真。
ifndef 指令开始的条件,指定的条件。它包含单参数。如果给定的是假的,那么条件为真。
else 指令会导致以下行如果前面的条件未能被遵守。在上面的例子中,这意味着第二个选择连接命令时使用的第一种选择是不使用。它是可选的,在有条件有一个else。
endif 指令结束条件。每一个条件必须与endif结束。
条件式指令的语法
一个简单的条件,没有其他的语法如下:
conditional-directivetext-if-trueendif |
文本如果真可以是任何行文字,被视为makefile文件的一部分,如果条件为真。如果条件是假的,没有文字来代替。
一个复杂的语法条件如下:
conditional-directivetext-if-trueelsetext-if-falseendif |
如果条件为真时,文本,如果真正的使用,否则,如果假文本来代替。的文本,如果错误的数量可以是任意的文本行。
有条件的指令的语法是相同的,无论是简单或复杂的条件。有四种不同的测试不同条件下的指令。这里是一个表:
ifeq (arg1, arg2)ifeq 'arg1' 'arg2'ifeq "arg1" "arg2"ifeq "arg1" 'arg2'ifeq 'arg1' "arg2" |
上述条件相反的指令如下
ifneq (arg1, arg2)ifneq 'arg1' 'arg2'ifneq "arg1" "arg2"ifneq "arg1" 'arg2'ifneq 'arg1' "arg2" |
条件式指令示例
libs_for_gcc = -lgnunormal_libs =foo: $(objects)ifeq ($(CC),gcc) $(CC) -o foo $(objects) $(libs_for_gcc)else $(CC) -o foo $(objects) $(normal_libs)endif |
include 指令
include指令告诉make暂停读取当前makefile文件和读取一个或多个其它的makefile,然后再继续。该指令是一行在makefile中,看起来像这样:
include filenames... |
文件名可以包含shell文件名模式。允许额外的空格开头的行被忽略,但不允许一个标签。例如,如果有三个`.mk',`.mk' files, `a.mk', `b.mk', and `c.mk', and $(bar) 扩展到bash中,然后下面的表达式。
include foo *.mk $(bar)is equivalent toinclude foo a.mk b.mk c.mk bish bash |
当MAKE处理包括指令,它包含的makefile暂停读取,并从各列出文件中依次读取。当这个过程完成,使读取指令出现在其中的makefile的恢复。
override 指令
如果一个变量已经设置的命令参数,在makefile中被忽略的普通任务。如果要设置makefile的变量,即使它被设置的命令参数,可以使用一个override指令,这是一行看起来像这样:
override variable = valueoroverride variable := value |
Makefile include
makefile include 指令
include指令告诉make暂停读取当前makefile文件和读取一个或多个其它的makefile,然后再继续。该指令是一行在makefile中,看起来像这样:
include filenames... |
文件名可以包含shell文件名模式。允许额外的空格开头的行被忽略,但不允许一个标签。例如,如果有三个`.mk',`.mk' files, `a.mk', `b.mk', and `c.mk', and $(bar) 扩展到bash中,然后下面的表达式。
include foo *.mk $(bar)is equivalent toinclude foo a.mk b.mk c.mk bish bash |
当MAKE处理包括指令,它包含的makefile暂停读取,并从各列出文件中依次读取。当这个过程完成,使读取指令出现在其中的makefile的恢复。
Makefile 文件重新编译
make 程序是一个智能的实用程序和工作根据在源文件中的变化。如果有四个文件main.cpp,hello.cpp,factorial.cpp和functions.h。这里所有reamining文件是依赖functions.h,main.cpp的是依赖于hello.cpp,factorical.cpp。因此,如果做任何改变functions.h然后将重新编译所有源文件来生成新的对象文件。但是,如果做任何改变main.cpp,因为这是不依赖任何其他的过滤,那么在这种情况下,只有main.cpp文件将被重新编译和hellp.cpp factorial.cpp将无法重新编译。
虽然编译一个文件时,MAKE检查目标文件和比较时间表带,如果源文件有更新的时间戳比目标文件,然后将生成新的对象文件,假设源文件已被改变。
避免重新编译
有可能是项目包括成千上万的文件。有时候可能已经改变了一个源文件,但不想重新编译所有依赖于它的文件。例如,假设添加宏到一个头文件或声明,许多其他文件依赖。假设在头文件中的任何变化需要重新编译所有相关文件,但要知道,他们并不需要重新编译,你宁可不要浪费时间等待他们的编译。
如果预期改变头文件的问题之前,可以使用`-t'标志位。这个标志告诉make命令不运行的规则,而是来标记目标,迄今为止,通过改变它的最后修改日期。遵循以下步骤:
使用命令'make'来重新编译真的需要重新编译源文件。
在头文件中进行更改。
使用命令`-t'来纪念所有的目标文件为最新。下一次运行make,在头文件中的变化不会引起任何重新编译。
如果已经改变了头文件的时候,有一些文件就需要重新编译,做到这一点已经太晚了。相反,可以使用`-o文件“的标志,这标志着一个指定的文件作为”old“。这意味着该文件本身不会被重制并没有别的其交代将被重制。遵循以下步骤:
重新编译源文件,需要编制独立的特定头文件的原因,`make -o headerfile'。如果涉及几个头文件,使用一个单独的`-o'选项,每个头文件。
轻触所有目标文件使用`make -t'.
Makefile 其他功能
make 递归使用
递归使用的手段使用,make在makefile作为命令。这种技术是非常有用的,当你想要的makefile各种子系统组成一个更大的系统。例如,假设你有一个子目录,子目录都有其自己的makefile,并且您希望所在目录的makefile中运行make子目录。可以做到这一点如以下:
subsystem: cd subdir && $(MAKE)or, equivalently subsystem: $(MAKE) -C subdir |
可以编写递归复制这个例子只是通过make命令,但有很多事情,了解他们是如何和为什么工作的,以及如何子涉及到顶层make。
通信变量到子make
顶层make变量的值可以被传递到子通过环境,通过显式请求。这些变数定义子作为默认值,但不会覆盖子的makefile使用makefile中所指定的,除非使用`-e'开关
向下传递,或导出,一个变量,变量和其值的环境中运行每个命令添加。子make反过来,make使用环境变量值来初始化它的表格
特殊变量SHELL和MAKEFLAGS总是导出(除非取消导出)。 MAKEFILES导出,如果把它设置到任何东西。
如果想导出特定变量的一个子制造,使用导出指令,像这样:
export variable ... |
如果想阻止一个变量被导出的,使用撤消导出的指令,像这样:
unexport variable ... |
MAKEFILES 变量
MAKEFILES如果环境变量的定义,make额外的makefile 名称列表(由空格分隔)之前被读取别人认为其值。这很像include指令:不同的目录中查找这些文件。
makefile的主要用途是MAKE递归调用之间的通信
头文件包含在不同的目录
如果已经把你的头文件在不同的目录,在不同的目录中运行make,那么它需要告诉头文件的路径。这是可以做到的makefile中使用-I选项。假设该functions.h文件可在/home/yiibai/header头和其他文件/home/yiibai/src/然后进行文件将被写入如下。
INCLUDES = -I "/home/yiibai/header"CC = gccLIBS = -lmCFLAGS = -g -WallOBJ = main.o factorial.o hello.ohello: ${OBJ} ${CC} ${CFLAGS} ${INCLUDES} -o $@ ${OBJS} ${LIBS}.cpp.o: ${CC} ${CFLAGS} ${INCLUDES} -c $< |
追加更多的文本变量
通常,它用于添加更多的文字,已定义的变量的值。make 这行包含'+ =',像这样:
objects += another.o |
这需要值的变量对象,并添加文字`another.o'(前面由一个单一的空间)。因此:
objects = main.o hello.o factorial.oobjects += another.o |
设置`文件main.o hello.o factorial.o another.o'的对象。
使用'+ ='是类似于:
objects = main.o hello.o factorial.oobjects := $(objects) another.o |
Makefile中的续行
如果不喜欢太大的行,在Makefile中,然后你可以打破线使用反斜杠“\”,如下图所示
OBJ = main.o factorial.o \ hello.ois equivalent toOBJ = main.o factorial.o hello.o |
从命令提示符下运行的Makefile
如果已经准备好请示的Makefile的名称为“Makefile”文件,然后简单地写在命令提示符下,它将运行Makefile文件。但是,如果有任何其他的名字的Makefile,然后使用以下命令
make -f your-makefile-name |
makefile 例子
这是一个例子编译hello程序Makefile。此程序包含三个文件main.cpp,factorial.cpp,hello.cpp。
# Define required macros hereSHELL = /bin/shOBJS = main.o factorial.o hello.oCFLAG = -Wall -gCC = gccINCLUDE =LIBS = -lmhello:${OBJ} ${CC} ${CFLAGS} ${INCLUDES} -o $@ ${OBJS} ${LIBS}clean: -rm -f *.o core *.core.cpp.o: ${CC} ${CFLAGS} ${INCLUDES} -c $< |
现在可以建立hello 程序使用“make”打招呼。如果发出命令“make clean”,则它会删除所有的对象可在当前目录中的文件和核心文件。
转载地址:http://qzpgi.baihongyu.com/