Linux C++ 开发 系列的前面2篇文章,我们介绍了通过g++来编译C++代码。这对于HelloWorld程序或者简单的Demo程序来说没有题目,但对于包含多个.cpp和多个.h文件的复杂项目来说,直接用g++命令来编译的话,将会使编译的指令非常冗长且难于维护。这个时候我们可以考虑用makefile来构建我们的程序。
1. make 和 Makefile
1.1. 什么是make?
make是一个自动化构建工具,广泛应用于C/C++项目中,但也可以用于其他编程语言。它的主要功能是根据Makefile中的规则自动实行一系列命令,从而生成目标文件。make通过比力目标文件和依赖文件的时间戳来决定是否需要重新构建某个目标,从而避免了不必要的编译,提高了构建效率。
我们安装GCC后,应该默认就已经安装了make,没有没有安装,Ubuntu下可通过如下命令来安装:- sudo apt update
- sudo apt install make
复制代码 安装完成后,你可以通过以下命令来验证make是否安装成功:1.2. 什么是Makefile?
Makefile 是一个文本文件,界说了构建项目的规则和指令。通常界说了多条包含 目标(target)、依赖(dependency)和命令(command) 的规则。
1.3. make 与 Makefile的关系
- Makefile 你可以理解为是自动构建的脚本,内里通过 目标(target)、依赖(dependency)和命令(command) 界说了规则,告诉make工具要怎样一步步构建我们的最终目标。
- make 是一个命令工具,是一个表明并实行Makefile中指令的命令工具,按照Makefile制定的规则,构建最终的目标产物。
2. Makefile的语法
2.1. 根本语法
Makefile的根本语法如下:
- 目标: 通常是需要生成的文件名,也可以是某个操纵(如clean)。
- 依赖: 生成目标文件所依赖的其他文件或其他目标。
- 命令: 生成目标所需实行的shell命令,必须以Tab键开头。
注意: 命令前面必须是tab键,表现命令的开始。不能用4个空格或者两个空格。
2.2. 变量
在Makefile中,可以使用变量来简化规则的编写。变量界说如下:使用变量时,需要在变量名前加上$符号,并用括号括起来:2.3. 伪目标
伪目标是一种特殊的目标,它不代表详细的文件,通常用于实行某些操纵。伪目标需要使用.PHONY声明:- .PHONY: clean
- clean:
- rm -f hello hello.o
复制代码 2.4. 模式规则
模式规则允许界说通用的规则,适用于多个目标。例如:- %.o: %.c
- $(CC) $(CFLAGS) -c $<
复制代码 这条规则表现全部.o文件都依赖于对应的.c文件,并且使用雷同的编译命令。
2.5. 自动变量
make提供了一些自动变量,用于简化命令的编写:
<ul>$@:表现目标文件名。
$ Enumerator --> Company --> Department --> Person</p></blockquote>- ifeq ($(DEBUG),1)
- CFLAGS += -g
- else
- CFLAGS += -O2
- endif
复制代码 3.2.3. Makefile V1.0
- # 编译 demo01.cpp
- demo01.out: demo01.cpp
- g++ ./demo01.cpp -o demo01.out
- # 申明clean为伪目标
- .PHONY: clean
- # 定义 clean 命令
- clean:
- rm -f demo01.out
复制代码 实行make进行编译:- # 编译
- make
- # 运行
- ./demo01.out
- Hello, world!
复制代码 上面的Makefile各人大概会有一些疑问,这里对大概存在的疑问做一些解答。
3.2.3.1. 题目一:为什么没有头文件的依赖?
题目形貌:
如:编译Person.o时,Person.cpp是包含了Person.h的,为什么这条规则不写成:- ./iterator
- ├── Company.cpp
- ├── Company.h
- ├── Department.hpp
- ├── Enumerator.hpp
- ├── Iterator.cpp
- ├── Person.cpp
- ├── Person.h
- └── README.md
复制代码 题目解答:
这种写法也是没有题目的,对于makefile而言,没有语法错误。但是没有这个必要,《Linux C++ 开发3 - 你写的Hello world经过哪些过程才被计算机理解和实行?》一文中我们讲了在程序预处理阶段,预处理器会将全部通过#include包含的头文件更换成真正的内容,以是我们编译的时候只需要对.cpp进行编译即可。
3.2.3.2. 题目二:为什么没有对.hpp的规则界说?
题目形貌:
为什么Department.hpp、Enumerator.hpp不需要编译。
题目解答:
正常,我们创建C++代码文件的时候,一般会创建两个文件:
- 一个是头文件(如:abc.h),用来进行类、函数、常量等的声明。
- 一个是源文件(如:abc.cpp),用来进行类、函数的界说。
但这样每次要创建两个文件,而且要在两个文件上分别进行声明和界说,挺麻烦的。于是为了偷懒,对于一些简单的,没有交错引用的类,我们通常会把声明和界说都放在一个文件中,这个文件通常以.hpp作为后缀(如:abc.hpp)。
.hpp 本质上照旧一个头文件,GCC在编译的时候,会把它当做头文件来处理。以是我们在Makefile中可以不消写对.hpp的编译规则。
3.2.4. Makefile V2.0
GNU的make很强大,它可以自动推导文件以及文件依赖关系背面的命令,于是我们就没必要在每一个.o文件后都写上编译的命令和规则,由于我们的make会自动识别,并本身推导命令。
于是我们的Makefile可以简化为:- # 构建的最终目标 Iterator(可执行文件)
- Iterator:Iterator.o Company.o Person.o
- g++ -o Iterator Iterator.o Company.o Person.o
- # 构建目标 Iterator.o
- Iterator.o:Iterator.cpp
- g++ -c Iterator.cpp
- # 构建目标 Company.o
- Company.o:Company.cpp
- g++ -c Company.cpp
- # 构建目标 Person.o
- Person.o:Person.cpp
- g++ -c Person.cpp
- # 申明clean为伪目标
- .PHONY: clean
- clean:
- rm -f *.o Iterator
复制代码 实行make命令,我们会看到它会自动先去编译.o, 然后再链接生成最终的二进制文件,编译的过程和V1.0是一样的。- # 编译
- make
- # 运行
- ./demo01.out
- Hello, world!
复制代码 各人好,我是陌尘。
IT从业10年+, 北漂过也深漂过,目前暂定居于杭州,未来不知还会飘向何方。
搞了8年C++,也干过2年前端;用Python写过书,也玩过一点PHP,未来还会折腾更多东西,不死不休。
感谢各人的关注,期待与你一起发展。
【SunLogging】扫码二维码,关注微信公众号,阅读更多精彩内容
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |