编译一个小的C程序至少需要一个单一的文件.h文件(如适用)。虽然命令执行此任务只需CC file.c中,有3个步骤,以取得最终的可执行程序,如下所示:
编译阶段:所有的C语言代码.c文件中被转换成一个低级语言汇编语言;决策.s文件。
汇编阶段:前阶段所作的汇编语言代码,然后转换成目标代码的代码片段,该计算机直接理解。目标代码文件.o 结束。
链接阶段:编译程序涉及到链接的对象代码的代码库,其中包含一定的“内置”的功能,如printf的最后阶段。这个阶段产生一个可执行程序,默认情况下,这是名为a.out。
假定有以下的源文件。
main.cpp
hello.cpp
factorial.cpp
functions.h
#include
#include "functions.h"
int main(){
print_hello();
cout << endl;
cout << "The factorial of 5 is " << factorial(5) << endl;
return 0;
}
#include
#include "functions.h"
void print_hello(){
cout << "Hello World!";
}
#include "functions.h"
int factorial(int n){
if(n!=1){
return(n * factorial(n-1));
}
else return 1;
}
void print_hello();
int factorial(int n);
琐碎的方法来编译的文件,并获得一个可执行文件,通过运行以下命令:
CC main.cpp hello.cpp factorial.cpp -o hello
make程序允许您使用宏,这是类似的变量。 = 一对一个Makefile中定义的宏。例如:
MACROS= -me
PSROFF= groff -Tps
DITROFF= groff -Tdvi
CFLAGS= -O -systype bsd43
LIBS := "-lncurses -lm -lsdl"
LIBS += -lstdc++
MYFACE = ":*)"
PROJ_HOME = /home/moon/projects
$(MACROS)
$(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 $@
$<
表示第一个依赖文件。$*
这个变量表示目标模式中“%”及其之前的部分。$*
如果目标是“dir/a.foo.b”,并且目标的模式是“a.%.b”,那么,“$*
”的值就是“dir/a.foo”。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么“$*
”也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么“$*
”就是除了后缀的那一部分。例如:如果目标是“foo.c”,因为“.c”是make所能识别的后缀名,所以,“$*
”的值就是“foo”。这个特性是GNU make的,很有可能不兼容于其它版本的make
,所以,你应该尽量避免使用“$*
”,除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么“$*
”就是空值。。.o:.cpp
$(CC) $(CFLAGS) -c $<
alternatively
.o:.cpp
$(CC) $(CFLAGS) -c $*.cpp
有很多默认的宏(输入“make -p”打印出来的默认值)。大多数使用它们的规则是很明显的:
这些预定义变量,即。在隐含规则中使用的宏分为两大类:那些程序名(例如CC)和那些含有参数的程序(如CFLAGS)。
下面是一些比较常见的变量用作内置规则:makefile文件的程序名称的表。
这里是一个变量,其值是上述程序的额外的参数表。所有这些的默认值是空字符串,除非另有说明。
.s' or
.S’ file).注:您可以取消-R
或--no-builtin-variables
选项隐含规则使用的所有变量。
如,也可以在命令行中定义的宏
make CPP=/home/moon/projects
这是很常见的,最终的二进制文件将依赖于各种源代码和源代码的头文件。依存关系是重要的,因为他们告诉对任何目标的源。请看下面的例子
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.cpp
factorial.o: factorial.cpp functions.h
$(CC) -c factorial.cpp
hello.o: hello.cpp functions.h
$(CC) -c hello.cpp
一个Makefile目标规则的一般语法
target [target…] : [dependent …]
[ command …]
方括号中的项是可选的,省略号是指一个或多个。注意标签,每个命令前需要。
下面给出一个简单的例子,定义了一个规则使您的目标从 hello 其他三个文件。
hello: main.o factorial.o hello.o
$(CC) $? -o $@
注:在这个例子中,你必须放弃规则,使所有对象从源文件的文件
语义是相当简单的。当"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) [email protected] $(LDFLAGS) -o $@
这种隐含的规则说,如何make c, x.c 运行x.c 调用输出x。规则是隐式的,因为没有特定的目标提到。它可用于在所有的情况下。
另一种常见的隐含规则的构造 .o(对象)文件和 .cpp (源文件)。
.o:.cpp
$(CC) $(CFLAGS) -c $<
alternatively
.o:.cpp
$(CC) $(CFLAGS) -c $*.cpp
$^ 表明所有的依赖项
LDFLAGS = -lstdc++
hello:main.o factorial.o hello.o
$(CC) $^ $(LDFLAGS) -o $@
.o:.cpp functions.h
$(CC) -c $<
clean:
-rm *.o hello
install:hello
@echo “You need build hello!”
echo “no @ used”
all:hello install
Makefile 自定义后缀规则
就其本身而言,make已经知道,为了创建一个 .o文件,就必须使用 cc-c 相应的c文件。 建成MAKE这些规则,可以利用这一点来缩短Makefile。如果仅仅只是表示 .h 文件的 Makefile依赖线,依赖于目前的目标是,MAKE会知道,相应的文件已规定。你甚至不需要编译器包括命令。
这减少了我们的Makefile更多,如下所示:
OBJECTS = main.o hello.o factorial.o
hello: $(OBJECTS)
cc $(OBJECTS) -o hello
hello.o: functions.h
main.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 文件中使用的默认规则。
all:hello install
hello:*.cpp *.h
$(CC) $^ $(LDFLAGS) -o $@
clean:
-rm *.o hello
install:hello
@echo “You need build hello!”
echo “no @ used”
有好几种指令以不同的形式。让程序可能不支持所有指令。因此,请检查make是否支持指令,我们这里解释。 GNU make支持这些指令
条件指令
条件的指令
ifeq 指令开始的条件,指定的条件。它包含两个参数,用逗号分隔,并用括号括起。两个参数进行变量替换,然后对它们进行比较。该行的makefile继IFEQ的服从如果两个参数的匹配,否则会被忽略。
ifneq 指令开始的条件,指定的条件。它包含两个参数,用逗号分隔,并用括号括起。两个参数进行变量替换,然后对它们进行比较。makefile ifneq 遵守如果两个参数不匹配,否则会被忽略。
ifdef 指令开始的条件,指定的条件。它包含单参数。如果给定的参数为真,则条件为真。
ifndef 指令开始的条件,指定的条件。它包含单参数。如果给定的是假的,那么条件为真。
else 指令会导致以下行如果前面的条件未能被遵守。在上面的例子中,这意味着第二个选择连接命令时使用的第一种选择是不使用。它是可选的,在有条件有一个else。
endif 指令结束条件。每一个条件必须与endif结束。
条件式指令的语法
一个简单的条件,没有其他的语法如下:
conditional-directive
text-if-true
[else
text-if-false]
endif
文本如果真可以是任何行文字,被视为makefile文件的一部分,如果条件为真。如果条件是假的,没有文字来代替。
一个复杂的语法条件如下:
conditional-directive
text-if-true
else
text-if-false
endif
如果条件为真时,文本,如果真正的使用,否则,如果假文本来代替。的文本,如果错误的数量可以是任意的文本行。
有条件的指令的语法是相同的,无论是简单或复杂的条件。有四种不同的测试不同条件下的指令。这里是一个表:
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 = -lgnu
normal_libs =
foo: ( o b j e c t s ) i f e q ( (objects) ifeq ( (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'文件,
a.mk’, b.mk', and
c.mk’, 和一个 $(bar) 扩展到bash中,然后下面的表达式。
include foo *.mk $(bar)
is equivalent to
include foo a.mk b.mk c.mk bash
当MAKE处理包括指令,它包含的makefile暂停读取,并从各列出文件中依次读取。当这个过程完成,使读取指令出现在其中的makefile的恢复。
override 指令
如果一个变量已经设置的命令参数,在makefile中被忽略的普通任务。如果要设置makefile的变量,即使它被设置的命令参数,可以使用一个override指令,这是一行看起来像这样:
override variable = value
or
override variable := value
=、+=、:=、?=
= 直接赋值
:= 忽略掉之前的赋值
+= 追加
?= 如果前面没有定义,则定义生效
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命令不运行的规则,而是来标记目标,迄今为止,通过改变它的最后修改日期。遵循以下步骤: 1.使用命令'make'来重新编译真的需要重新编译源文件。 2.在头文件中进行更改。 3.使用命令
-t’来记录所有的目标文件为最新。下一次运行make,在头文件中的变化不会引起任何重新编译。
如果已经改变了头文件的时候,有一些文件就需要重新编译,做到这一点已经太晚了。相反,可以使用-o文件“的标志,这标志着一个指定的文件作为”old“。这意味着该文件本身不会被重制并没有别的其交代将被重制。遵循以下步骤: 1.重新编译源文件,需要编制独立的特定头文件的原因,
make -o headerfile’。如果涉及几个头文件,使用一个单独的-o'选项,每个头文件。 2.touch所有目标文件使用
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指令:不同的目录中查找这些文件。
MAKEFILES的主要用途是MAKE递归调用之间的通信
但没有必要,最好不用定义和使用该变量
头文件包含在不同的目录
如果已经把你的头文件在不同的目录,在不同的目录中运行make,那么它需要告诉头文件的路径。这是可以做到的makefile中使用-I选项。假设该functions.h文件可在/home/yidaoyun/header头和其他文件/home/yidaoyun/src/然后进行文件将被写入如下。
INCLUDES = -I “/home/yidaoyun/header”
CC = gcc
LIBS = -lm
CFLAGS = -g -Wall
OBJ = main.o factorial.o hello.o
hello: ${OBJ}
${CC} ${CFLAGS} ${INCLUDES} -o $@ ${OBJS} ${LIBS}
.o:.cpp
${CC} ${CFLAGS} ${INCLUDES} -c $<
追加更多的文本变量
通常,它用于添加更多的文字,已定义的变量的值。make 这行包含’+ =‘,像这样:
objects += another.o
这需要值的变量对象,并添加文字another.o'(前面由一个单一的空间)。因此: objects = main.o hello.o factorial.o objects += another.o 设置
文件main.o hello.o factorial.o another.o’的对象。
使用’+ ='是类似于:
objects = main.o hello.o factorial.o
objects := $(objects) another.o
Makefile中的续行
如果不喜欢太大的行,在Makefile中,然后你可以使用反斜杠\,如下所示
OBJ = main.o factorial.o
hello.o
is equivalent to
OBJ = main.o factorial.o hello.o
从命令提示符下运行的Makefile
如果已经准备好请示的Makefile的名称为“Makefile”文件,然后简单地写在命令提示符下,它将运行Makefile文件。但是,如果有任何其他的名字的Makefile,然后使用以下命令
make -f your-makefile-name
这是一个例子编译hello程序Makefile。此程序包含三个文件main.cpp,factorial.cpp,hello.cpp。
SHELL = /bin/sh
OBJS = main.o factorial.o hello.o
CFLAG = -Wall -g
CC = gcc
INCLUDES =
LIBS = -lstdc++
hello:${OBJ}
${CC} ${CFLAGS} -o $@ $^ ${LIBS}
clean:
-rm -f *.o hello
.o:.cpp
${CC} ${CFLAGS} ${INCLUDES} -c $<
现在可以建立hello 程序使用“make”hello项目。如果发出命令“make clean”,则它会删除所有的对象可在当前目录中的生成文件。