Make 可以简化编译过程,如果有一个近百个源文件的项目,如果有个文件更改后工程需要重新编译,那么一直用gcc -c a.c这些个命令敲来敲去会屎人的。运行make时候,他会寻找指定目录下(默认是 .)的 Makefile 文件并且分析依赖关系进行必要的编译。
目标文件: 依赖文件1 依赖文件2 依赖文件3 。。。。
[tab]编译命令
他的意思是目标文件是依赖于冒号后面几个文件的,如果这些依赖文件有更新的,那么其目标文件也需要更新。
Makefile 中可能有很多以上条目,他们共同组成了一个有向无回路图(DAG图),这样可以传递依赖。make 命令会把 Makefile 文件的第一个目标文件作为默认目标,当执行 make 命令时,make 会考察这个目标文件的依赖关系,进行编译。也可以指定,比如这个 Makefile:
main: a.o b.o
[tab]gcc -o main a.o b.o
a.o : a.c c.h
[tab]gcc -c a.c -o a.o
b.o: b.c c.h
[tab]gcc -c b.c -o b.o
// [tab]的意思是这里用tab字符代替,不能有其他的什么字符
在命令行里执行make,分析关系并生成main,如果是make a.o那么他只会编译到 a.o 。
当然我们还可以设定伪目标,比如:
clean:
[tab]rm a.o b.o
这样执行make clean的时候就把.o文件清除了,这里不会生成什么文件,只进行一些操作,更清楚的做法是在前面加上以下语句:
.PHONY : clean install dest [其他伪目标]
下面来说下变量,Makefile 里的变量按惯例是大写,包括数字字母下划线。当我们需要一个变量的值的时候,通常用 ${NAME} 或者 $(NAME)。他有好几种变量定义的方法。
首先是常规法,就是A=content
,等号两边可以有空格,和shell不一样。
其次是递归法,比如A=$(B),B=$(C),C=haha
,那么当寻找A的定义的时候就会去找B,然后再找C,变量展开的时候就是当他被引用的时候,这种方法效率比较低,因为如果他引用了函数,那么每次展开都要调用函数,而且可能会出现无限递归(A=$(B),B=$(A))
。
然后是直接展开法。这个很容易理解,就像是c语言是按照顺序执行的,当变量定义的时候这个变量就已经展开了(如果他引用了变量A,引用的是他定义时候A的值),当被引用的时候就直接用他代表的字符串替代。但是他用的不是等号 是 := ,比如 A:=hello,A:=$(B)
。
还有嵌套定义: A=B,B=haha,V=$($(A))
类似于这种的V的值是haha
最后是替换引用定义,他会替换后缀,有个例子很好 foo := a.o b.o c.o ,bar := $(foo:.o=.c)
,我们可以知道bar的值就是a.c b.c c.c
。
1.预定义变量,当使用隐式规则的时候他会派上用场,常用的有以下几个:
CC c编译器的名称(默认gcc?)
CPP c预编译器名称(默认$(CC) -E)
CXX c++编译器的名称(默认g++)
CFLAGS c编译器选项,无默认值
CXXFLAGS c++编译器选项,无默认值
2.自动变量,常用有以下:
$@:表示当前规则中的完整目标文件名
$*:不包含扩展名的目标文件名
$<:当前规则中第一个依赖文件名
$^:当前规则所有文件列表
$%:当目标为库文件时,表示库文件名
3.环境变量,Makefile对环境变量是可见的,可以引用.
Makefile还有个常用的东东就是隐式规则,make会自己推导.比如说
c:a.o b.o
[tab]gcc -o c a.o b.o
这时我们可以省略下面的命令,直接用第一行就行。make自动分析生成a,此时预定义变量就有用了,CC,CFLAGS等也派上了用场。
由于把握不了隐式规则的底线和能力,我还是觉得隐式规则应用的不要太多太复杂影响阅读为好。。
基本过程是以下四步:
gcc -E a.c -o a.i // 如果不加-o参数,gcc会把处理过的源文件放到标准输出中
2.预处理后的源文件。c源文件预处理后后缀为 .i , c++为 .ii 。
gcc -S a.i //会在当前文件夹下生成a.s
3.编译后生成的汇编源代码。后缀为 .s , .S 。
gcc -c a.s
//只进行汇编生成目标文件,.o结尾的目标文件可以用
//(ar crv libabc.a a.o b.o c.o )打包成形如lib×××.a的静态库
4.目标文件与库文件进行链接,生成可执行文件。
gcc a.o //在当前文件夹下生成a.out
其中任何一种状态,用 gcc 如果不加 -c , -E , -S 选项都会直接生成可执行文件,如果加上了选项,可以由之前任一状态生成所需要的文件(如 gcc -S a.c 可以直接生成 a.s,gcc -c a.i 可以直接生成 a.o )。如果是c++直接换用g++命令就行。
另外 gcc -v
可以输出编译过程的配置和版本信息。
-fsyntax-only 检查程序中的语法错误,不产生输出信息
-w 禁止所有警告信息
-Wunused 声明了木有用
-Wmain main函数定义不常规
-Wall 提供所有警告
-pedantic-errors 允许ansi c标准列出的全部信息
(注意: 原文的链接在 这里 )