想了想还是开始学这个,反正时间充裕。
我的目标包括:
- 学会makefile的基本概念和操作;
- 学会cmake的基本概念和操作。
GNU make:#
参见这篇教程所讲的内容,这里只做一点记录。
介绍makefile:#
makefile可以指定编译的规则,并且自动化编译。
GNU make 是linux环境下的make命令。
重温程序编译链接:#
编译检测语法是否正确,勾勒程序结构,得到中间目标文件;链接实现函数和全局变量的落实,得到可执行文件。
初识makefile:#
makefile的本质就是,描述项目结构、编译规则,然后根据这个进行项目代码的构建。
1
2
3
4target ...: prerequisites ...
recipe
...
...- target: 可以是中间目标文件,也可以是一个可执行文件,还可以是一个标签(label)
- prerequisites: 生成该target所依赖的文件和/或target。
- repice: 该target要执行的命令(任意的shell命令)。
如果pre中有任意文件比target文件更新(比较更新日期),也就是做出了一些变动,就执行recipe。
一个例子:#
如果一个工程有三个头文件和八个源文件(C),我们的makefile应该是下面这个样子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24edit : main.o kbd.o command.o display.o \ # 反斜杠表示换行符 用来提高代码的可读性
insert.o search.o files.o utils.o # 到这里列完了所有的pre
cc -o edit main.o kbd.o command.o display.o \ # 这里就是执行的shell命令了。原教程使用的是linux os,因此命令不太一样。
insert.o search.o files.o utils.o # 这个命令就是使用cc将若干文件生成一个可执行文件edit。
main.o : main.c defs.h
cc -c main.c # main.c中引入了头文件defs.h,所以我们前面写出了依赖关系,后面给出执行的操作,这里生成的是中间目标文件。
kbd.o : kbd.c defs.h command.h
cc -c kbd.c # 类似的内容
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o # 这里指创建完可执行文件后,删除中间文件,只保留我们需要的文件。创建好了这个makefile后,我们就可以在这个makefile的目录下使用make命令进行项目构造了。
当我们只输入make命令后,默认执行下面操作:
make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。
如果edit文件不存在,或是edit所依赖的后面的 .o 文件的文件修改时间要比 edit 这个文件新,那么,他就会执行后面所定义的命令来生成 edit 这个文件。
如果 edit 所依赖的 .o 文件也不存在,那么make会在当前文件中找目标为 .o 文件的依赖性,如果找到则再根据那一个规则生成 .o 文件。(这有点像一个堆栈的过程)
当然,你的C文件和头文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生成make的终极任务,也就是可执行文件 edit 了。
总之就是一层一层的往下搜索,
深度优先,如果到最后找不到文件,就会直接退出,报错。如果命令定义错误,或是编译不成功,make不会管。使用变量:#
makefile支持使用变量将需要重复使用的值进行保存,类似于引用。
上面的例子中,虽然文件不多,但是容易写错,我们可以声明一个变量
1
2objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o然后这么用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit $(objects)提高了代码的复用性。这可以视为变量的作用的一个理解。
make自动推导:#
.o文件必然要使用相关的.c文件,因此我们可以省略写出这个文件,只添加相关的头文件,从而我们的代码修改如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
.PHONY : clean # .PHONY表示clean是一个伪目标文件。它只是一个行为。
clean :
rm edit $(objects)还可以按照头文件到目标文件的映射来写
1
2
3
4
5
6
7
8
9
10
11
12
13objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
.PHONY : clean
clean :
rm edit $(objects)这在头文件重复率较高的时候非常好用,不过依赖性就没那么明显了。
:#
直接看吧,比较简单。
以上就是makefile的概貌和基础。剩下的内容将在我的makefile单独学习的博客中给出答案。
Cmake:#
1. 啥是Cmake:#
cmake是用于 自动生成 用于 构建软件 的 构建脚本,可以简化构建过程和管理依赖关系来帮助管理复杂的软件项目。
2. cmake能干啥:#
- 简化构建过程:只需要描述项目的结构和依赖关系,无需直接操作复杂的构建脚本的语言。
- 自动化配置:自动检测系统环境和可用的编译器,自动调整生成的 构建系统以适应不同的配置需求。
- 模块化设计:将项目分成多个子模块,独立配置和编译。
- 多配置支持:通过调整配置参数,控制编译器/链接器的行为。
- 自定义构建规则:可以定义各种构建规则、编译器选项和预处理宏。
3. cmake的基本结构:#
CMake 的基本结构由一个或多个 CMakeLists.txt 文件组成,通常位于项目的各个子目录中。每个 CMakeLists.txt 文件描述了相应目录的构建规则、依赖项和编译选项。
4. 构建:#
我们想要构建一个程序,需要创建一个
CMakeLists.txt
,并在其中指定以下几个内容:- 使用的cmake版本:
- 我们的项目名称、项目的语言。
- 需要生成的可执行文件的名称,和这个文件的依赖项。
- 其他配置(可选)。
并将这个文件放置在和想要构建的项目的源文件相同的目录中。
之后创建
build
文件夹,然后在build
中配置项目:只需要输入cmake ..
表示对当前文件夹的父文件夹进行cmake
操作。也即我们刚才已经写好了的CMakeLists.txt
和一些源文件夹。如果一切顺利,项目的配置就会在
build
文件夹中生成。于是可以根据这个文件夹下的配置进行编译,只需要使用cmake --build .
即可。