Makefile入门
一、Makefile入门
1.1 编译工具及构建工具介绍:
在之前的课程,都是直接使用gcc对代码进行编译,这对简单的工程是可以的,但当我们遇到复杂的工程时,每次用gcc等编译工具去操作就会显得很低效。因此make工具就出现了, make的出现是为了解决手动编译和链接大型工程的问题,它可以避免重复的工作,提高效率,保证正确性。make工具就根据makefile中的命令进行编译和链接的。但是当工程非常大的时候,手写makefile也是非常麻烦的,如果换了个平台makefile又要重新修改,因此更高级的一些构建系统或者工具工具像cmake、qmake、ninja和auto make就出现了,它们可以根据一些配置文件来自动化编译和链接软件项目。
cmake是一个跨平台的构建系统,它可以根据CMakeLists.txt中的指令来生成不同平台和工具的工程文件,例如Makefile、Visual Studio解决方案、Ninja文件等。cmake可以支持多种语言和多种架构,它还提供了一些高级功能,如测试、打包、安装等。
qmake是一个用于Qt项目的构建系统,它可以根据.pro或.pri中的指令来生成Makefile或其他形式的工程文件。
ninja是一个小巧而快速的构建工具,它可以根据ninja.build中的规则来执行编译和链接命令。ninja主要关注于性能和效率,它可以利用多核处理器和并行处理来加速构建过程。ninja通常不需要用户直接编写配置文件,而是由其他构建系统(如cmake)来生成
auto make是一个用于生成Makefile.in文件的工具,Makefile.in是一种用于auto conf的配置文件格式,auto conf是一个用于生成configure脚本的工具。configure脚本是一个用于检测系统环境并生成最终的Makefile文件的脚本Makefile.am是一种用于auto make的配置文件格式,它包含了一些指令和变量,用于定义程序或库的源文件、目标文件、依赖关系和编译选项等。
make是一个经典而通用的构建工具,它可以根据Makefile中的规则来执行编译和链接命令。make可以支持多种平台和工具,它还提供了一些高级功能,如条件判断、函数调用、模式匹配。
1.2 编译的四个阶段:
回顾下编译的四个过程:预处理(Pre-Processing)、编译(Compiling)、汇编 (Assembliang)、链接(Linking)
1.3 Makefile的认知:
1.3.1 什么是Makefile:
相信在Linux系统中经常会用到make这个命令来编译程序,而执行make命令所依赖的文件便是Makefile文件,make命令通过Makefile文件编写的内容对程序进行编译。make命令根据文件更新的时间戳来决定哪些文件需要重新编译这可以避免编译已经编译过的,没有改变的文件,从而提升编译效率。
1.3.2 Makefile的规则与示例:
一个简单的 Makefile 文件包含一系列的“规则”,其样式如下:
目标(target)…: 依赖()…
<tab>命令(command)
当“依赖文件”比“目标文件”更加新时,或者目标文件还没有生成时,就会执行“命令”
Makefile一个示例:
debug :
@echo "hello world"
如果我们要编译下面一个简单的例子:
#include <stdio.h>
int main()
{
printf("Hello World!");
return 0;
}
Makefile修改如下:
debug :
@echo "hello world"
hello : hello.c
gcc hello.c -o hello
执行命令make hello可以生成 hello文件, 执行make debug可以输出“hello world”:
Makefile修改如下:
debug :
@echo "hello world"
hello : hello.c
@gcc hello.c -o hello
clean :
@rm hello
执行命令make clean可以删除hello程序:
以上述为例,介绍一下详细规则:
- 目标(target)通常是要生成的文件的名称,可以是可执行文件(比如上例中的hello就是要生成的可执行文件名)或OBJ 文件, 也可以是一个执行的动作名称(如上述例子中的clean)。
- 依赖(prerequiries)是用来产生目标的材料(比如源文件 ) ,一个目标经常有几个依赖。
- 命令(command)是生成目标时执行的动作,一个规则可以含有几个命令,每个命令占一 行。
- 每个命令行前面必须是一个Tab字符,即命令行第一个字符是Tab。通常当一个依赖发生了变化,规则就会调用命令产生新的目标。
- 但是并非所有目标都有依赖,像上述例子中的“clean”,它只负责清除文件。
- 每一个Makefile文件也可以包含规则外的其他文件。
- 对于上面的Makefile,执行"make"后仅当hello.c文件比hello文件新时,才会执行gcc hello.c -o hello从而生成hello文件,如果没有hello文件时,这个命令也会被执行。运行“make clean”时,由于clean目标没有依赖,它的命令“rm -f hello”就会被强制执行。
二、Makefile的基本语法:
2.1 通配符:
- 当一个目标文件所依赖的依赖文件有很多时,需要写很多条规则,因此可以使用通配符只需要写一行来代替多行的规则。
首先举一个没有使用通配符的例子:共有两个.c文件,分别为a.c,b.c:
/* a.c */
#include <stdio.h>
int main()
{
func_b();
return 0;
}
/* b.c */
#include <stdio.h>
void func_b()
{