前言
从自己学习makefile的体验来讲,当我百度输入"makefile XXX"时,想要获取的一般有如下几点:
- 在阅读项目中的makefile文件时,不知道某一句是什么意思。例如:"$@","@<"是什么?语句看着没多少内容,可是啥意思呢?一脸懵逼!!!
- 在构建项目中,按照自己想法写出适配的makefile。看着demo中的makefile,写的好工整啊,自己写的是坨翔么!
- 想要系统学习makefile技能,在网络上找资源。尝试看了一些,奈何这个学习makefile的系统工程看了后面忘了前面。练习不多,时间一长,它认识我,我忘了它!
因此,本文期望从实践出发,通过例子学习语法。而不是先介绍语法规则,然后再用例子。例子从简单到复杂,最终可以应用在项目中。水平有限,欢迎讨论。 本文更新中!
第一节 Makefile 版hello world
1.1 hello world
“hello world!”一般是程序员遇到的第一个程序。那如何编译输出呢?
当前目录下有文件:“main.c”。文件内容如下:
#include <stdio.h>
int main(char argc, char **argv[])
{
printf("Hello world! ");
return 0;
}
在终端,输入:
gcc -o hello main.c
此时,当前目录生成了相应的可执行文件:”hello“。
如果用Makefile是如何来写呢?先放Makefile文件:
hello:
gcc -o hello main.c
对比通过命令来生成hello,只是多了一行"hello:"。makefile是通过make执行的规则脚本。
上例中通过makefile来告诉make,需要生成目标“hello”,以及如何生成-通过执行”gcc -o hello main.c“ 。
来解析下上面最简单的例子:
“hello:” makefile的目标,它存在的意义!官方说法:规则的目标
“gcc -o hello main.c”:告诉make为了目标,要干什么活。官方说法:规则的命令(注意此行gcc前面必须是一个Tab,是必须!)
这里涉及gcc的语法(gcc --help即可查看):gcc [options] file… 其中[options]:" -o Place the output into "
gcc中“-o”选项是表示生成可执行文件,但上例中还隐含了部分内容。main.c 到hello之间并不是一步到位。
还有一些中间文件,其中之一是目标文件“main.o”。
如果我们想要获取中间文件main.o,应该把命令改为:
gcc -c main.c
gcc -o hello main.o
对应makefile文件需要修改为:
hello:
gcc -c main.c
gcc -o hello main.o
此时,在执行命令make后,当前目录不仅仅有可执行文件hello,还有存在目标文件:main.o
现在将makefile文件换一种等价写法:
hello:main.o
gcc -o hello main.o
main.o:
gcc -c main.c
此时“hello:”后面多了"main.o"。此处的的"main.o",官方称为:规则的依赖。
而main.o 是规则的依赖,还是新的规则的目标
以上例为基础,我们再添加一些内容,修改makefile如下:
.PHONY:clean
hello:main.o
gcc -o hello main.o
main.o:
gcc -c main.c
clean:
rm *.o
上例中,增加了一个目标clean。并且clean是“.PHONY”的依赖。
".PHONY"是makefile的特殊的目标,其后的依赖被均被当伪目标。
make clean是一般用来清理生成目标的中间文件。那为何要将clean作为伪目标呢?
第一,clean是执行删除任务,并未生成目标“clean”。
第二,假设clean不是伪目标,若目录中存在命名为“clean”的文件,而clean并没有依赖。此时,make会认为已有目标文件,不会执行清理操作。
1.2 自动化变量
在稍微复杂点的makefile,经常能见到$@, $<, $^。这些称为自动化变量,上面只是部分的自动化变量。
先来认识一下:
$@:规则的目标文件名
$<:规则的第一个依赖文件名,那规则多个依赖呢?
$^:规则依赖文件列表
利用自动变量改写Makefile如下:
.PHONY:clean
hello:main.o
gcc -o $@ $<
main.o:
gcc -c $<
clean:
rm *.o
例如:
hello:main.o
gcc -o $@
<
其
中
< 其中
<其中@代表目标hello, $<代表第一个依赖项 main.o
假设增加另外一个源文件display.c,该文件内容如下:
#include <stdio.h>
void display(void)
{
printf("[%s]: Now, this is another source file ", __FUNCTION__);
return;
}
并在修改main.c 如下:
#include <stdio.h>
extern void display(void);
int main(char argc, char **argv[])
{
printf("Hello world ");
display();
return 0;
}
此时,main中调用函数"display"。生成目标hello因此需要增加另外一个依赖。看到这里,你可以尝试自己修改makefile来生成相应的可执行程序–hello。
makefile修改如下:
.PHONY: clean
hello: main.o display.o
gcc -o $@ $^
main.o:main.c
gcc -c $<
display.o:display.c
gcc -c $<
clean:
rm *.o
可以看到在hello的命令中,将原始的“ < ” , 替 换 为 了 “ <”,替换为了“ <”,替换为了“^”。这是因为hello现在具有多个依赖项了。
1.3 模式规则
通过自动变量,将原始的makefile简化了一些。现在利用模式规则,再进一步简化makefile!如果一个目标中带有“%”,那么这个目标通过模式规则进行匹配。利用%.o:%.c 来替换“main.o:main.c”, “display.o:display.c”。
上例中的makefile可改写为:
.PHONY: clean
hello: main.o display.o
gcc -o $@ $^
%.o:%.c
gcc -c $<
clean:
rm *.o
观察上面这个makefile,自动化变量是不是很好的和模式规则结合起来?特别是工程较大,源文件较多的情况下,特别适合利用上例简化!