Linux入门,Makefile编写
简介
在Linux下使用GNU的make工具能够比较容易的构建一个属于自己的工程,整个工程的编译只需一个命令(make)就可以完成编译,链接。这依赖于Makefile文件。
Makefile 是和 make 命令一起配合使用的,很多大型项目的编译都是通过 Makefile 来组织的,。
make是一个命令工具,当运行make命令时,会在当前目录下寻找名为“GNUmakefile”、“Makefile”、“makefile”的文件。Makefile文件说明了组成程序的各模块间的相互关系以及更新模块时必须进行的动作,make按照说明自动的维护这些模块。
Makefile文件描述了整个工程的编译、链接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建哪些库文件以及如何创建这些库文件、如何在最好产生我们想要的可执行文件。尽管看起来可能是很复杂的事情,但是为工程编写Makefile的好处是能够使用一行命令来完成“自动化编译”,一旦提供一个(通常对于一个工程来说会是多个)正确的Makefile。编译整个工程所要做的事就是在shell提示符下输入make命令。整个工程完全自动编译,极大提高了效率。
用途:
1.项目代码编译管理
2.节省编译项目时间
3.一次编写终生受益
Makefile规则说明
<target1 >
<target2>
.... :<prerequisites1>
<prerequisites2>
...
[TAB]<command1>
[TAB]<command2>
...
target可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)。对于标签这种特性,在后续的“伪目标”中会有叙述。
<prerequisites>就是,要生成那个target所需要的文件或是目标。
<command>也就是make需要执行的命令。(任意的shell命令)
这是一个文件的依赖关系,也就是说,<target>这一个或多个的目标文件依赖于<prerequisites>中的文件,其生成规则定义在 <command>中。说白一点就是说,<prerequisites>中如果有一个以上的文件比<target>文件要新的话,<command>所定义的命令就会被执行。这就是makefile的规则。也就是makefile中最核心的内容。
下面我们用main.c这个文件编译成一个可执行文件为例来看看Makefile的编写。以下是文件的代码(测试用)
main.c
int main()
{
int a = 10;
int b = 20;
printf("和为:%d\n",a+b);
return 0;
}
将.c 文件放在Makefile同级目录,如图。
以下是Makefile原文:
main:main.c
gcc -c main.c -o main.o
gcc main.o -o main
逐行解释:
main:main.c 意思是:main这个可执行文件是依靠main.c生成的
gcc -c main.c -o main.o 意思是:把这个.c文件编译成.o文件
gcc main.o -o main 意思是:编译这个.o文件为可执行文件main
然后在命令行执行make就生成了一个可执行的二进制文件main,如图:
工作原理:
1.若想生成目标发,检查规则中的依赖条件是否存在,如果不存在,则寻找是否有规则用来生成该依赖文件。
2.检查规则中的目标是否需要更新,必须先检查它的所有依赖,依赖中有任何一个被更新,则目标必须更新。
#依赖不存在,寻找依赖条件
main:main.o
gcc main.o -o main
main.o:main.c
gcc -c main.c
#只要一个文件更新,则只会单独更新对应文件
#只要一个文件更新,则最终目标就会进行更新
#Makefile文件写的时候,倒着来
test:add.o sub.o mul.o div.o
gcc add.o sub.o mul.o div.o -o test
add.o:add.c
gcc -c add.c
sub.o:sub.c
gcc -c sub.c
mul.o:mul.c
gcc -c mul.c
div.o:div.c
gcc -c div.c
如果没有ALL指定终极目标,只执行一个就结束。
当用ALL指定终极目标后,书写顺序就可以不受限制。
ALL:test
add.o:add.c
gcc -c add.c
sub.o:sub.c
gcc -c sub.c
mul.o:mul.c
gcc -c mul.c
div.o:div.c
gcc -c div.c
test:add.o sub.o mul.o div.o
gcc add.o sub.o mul.o div.o -o test
所以我们可以总结出:
1.目标的时间必须晚于依赖条件的时间,否则,更新目录
2.依赖条件如果不存在,找寻新的规则去产生依赖。
3.ALL可以指定终极目标,打破书写顺序
接下来看戏下Makefile中的俩个函数(wildcard和patsubst)
src = $(wildcard *.c) // 等价于src = $(wildcard ./*.c)
意思就是:找到当前路径下所有后缀为.c的文件,赋值给src
obj = $(patsubst %.c,%.o,$(src))
这个意思是把src变量里后缀为.c的文件替换成.o
所以在Makefile里我们就可以这样写:
ALL:test
src = $(wildcard *.c)
obj = $(patsubst %.c,%.o,$(src))
test:$(obj)
gcc $(obj) -o test
然后我们再来看一下clean的用法
.PHONY:clean
clean:
-rm -rf $(obj)
逐行解释:
.PHONY:clean的意思就是clean它不是一个目标文件
clean:没有依赖
-rm -rf $(obj) “-”的作用是,删除不存在文件时,不报错,顺序执行结束。
接下来介绍几个自动化变量
简单说一下其中的变量:
$@
这个表示当前被执行的target的名字
$<
表示第一个依赖的名字
$^
表示所有的依赖文件列表,每个依赖项之间以空格隔开
所以我们在Makefile里也可以这么写
ALL:test
src = $(wildcard *.c)
obj = $(patsubst %.c,%.o,$(src))
test:$(obj)
gcc $^ -o $@
add.o:add.c
gcc -c $< -o $@
sub.o:sub.c
gcc -c $< -o $@
mul.o:mul.c
gcc -c $< -o $@
div.o:div.c
gcc -c $< -o $@