B站链接:20分钟速通Makefile
基本用法
在软件开发中,“构建”(Build)是指将文件转化成可执行文件的过程。
需要借助自动化的构建工具完成这个过程。
对于main.c
文件,使用gcc main.c -o hello
可以将这个文件编译为可执行文件Hello
(前提是一共只需要这一个文件就能进行编译)。
如果这个文件需要message.h
message.c
main.c
三个文件,我们用Make工具来实现构建。
Make通过读取Makefile
中的规则来进行编译。这个工具主要用于C和C++项目的构建中。
Makefile
中的格式:
1 | targer: dependencies |
整个过程有三件事:要生成什么文件,文件的依赖是什么,怎么生成这个文件。
例如,上面所说的需要三个文件的文件,就可以在Makefile
中这样写:
1 | hello: main.c message.c |
首先要检查依赖文件是否更新,如有则会执行下面的命令。这时候输入make
就会执行Makefile
中的命令。
如果依赖文件没有更新(对比目标文件和依赖文件的时间戳),会提醒不需更新,否则会检查更新然后重新编译。
但这时,如果main.c
message.c
有一个更新,就会重新编译。更标准的写法是如下(把编译和链接分开):
1 | hello: main.o message.o |
提高效率。
这样就会先编译两个.c
文件。这样有需要修改源文件时候,只需要重新编译修改了的源文件,其他不需要重新编译。
关于伪目标:只是一个标签,用于执行一些操作。
举个例子,在Makefile
文件末尾加上:
1 | clean: |
然后如果执行make clean
,就会执行这里的rm
命令。
但如果本地也有一个叫clean
的文件,make clean
这个命令就会失效。如果在文件首加上.PHONY: clean
,Make就不会把clean当作文件名来处理。
另一个常用的伪目标是all
。如果只执行make
命令,会只按照第一条规则运行,生成第一条规则对应的文件(如果其依赖文件中包有本条Makefile描述了其规则的文件,则这个依赖文件也会被生成)。按照这个规则,如果在伪目标all
上面加入所有需要生成的文件,执行make all
就能生成所有文件:
1 | all: hello world |
这样,执行make all
就会生成所有文件,然后打印提示语句。同时,如果只想生成其中一个文件,执行make <filename>
即可。
当然,中间文件.o
也可以这样编译,根据文件名选择。只要是Makefile
中定义的目标文件,都能按照这一系列规则进行。
如果两个目标文件的依赖文件和生成规则是一样的,把它们写在一行即可:
1 | hello world: main.o meaasge.o |
这里的$@
代表目标文件。这个变量叫做自动变量。后面会讲解所有的自动变量。
同时,Makefile
中可以定义变量。如targets = hello world
,然后需要输入hello world
这两个目标文件的地方,直接输入$(targets)
即可。通常会定义targets
sources
objects
这些变量。举个完整的例子:
1 |
|
下面给出自动变量:
$@
:目标文件$<
:第一个以来问及那$^
:所有的依赖文件
根据自动变量,或许也能节省一下敲代码的时间。这里不再展开说。
还可使用通配符来简化文件。比如想把所有.c
文件转换成对应的.o
文件,就可以再Makefile
中写道:%.o: %.c
,这样对应了所有的.c
为依赖文件,转化成对应的.o
文件。
在命令行中,可以为make
命令加上参数,除了之前说的指定文件等。如果你的Makefile
名字不叫Makefile
而叫114514
,可以使用make -f 114514
,指定需要哪个Makefile
。在命令后面加上-n
参数可以打印出将会执行的命令,但不会真正执行,可以在调试Makefile
的时候使用。-C
参数用于指定Makefile
执行的目录。
CMake
很多时候我们不需要手动编写Makefile
。CMake
这个工具更加简单好用。
建立一个CMakelist.txt
,在第一行加上最小版本要求,然后加上project命令,设置源文件列表和生成可执行文件的命令,这样就完成了一个最简单的CMake
文件,示意如下:
1 | cmake_minimum_required(VERSION 3.10) |
运行命令cmake
。这样,CMake
会帮助我们自动生成Makefile
文件,就不用自己去写咯。
CMake
还有很多其他功能,这里不再展开。