GCC, Makefile, make, CMake, CMakeLists.txt
使用CMake来编译源代码的流程
GCC
GCC介绍
GCC 是一个开源的编译器套件,支持 C、C++、Fortran、Ada、Go 等多种编程语言的编译。负责将源代码转换为可执行文件。
当我们的程序只有一个源文件时,使用 gcc
命令编译非常简单。但是,如果程序包含多个源文件,我们就需要逐个用 gcc
命令编译所有源文件,并且还要在编译过程中手动管理各个文件之间的依赖关系,这样很容易造成混乱和繁琐的工作。
直接使用gcc编译很麻烦的例子
假设我们有一个 C++ 项目,包含三个源文件:
main.cpp
math_functions.cpp
utils.cpp
每个源文件包含不同的功能:
main.cpp
是程序的入口,包含main()
函数。math_functions.cpp
包含数学相关的函数,比如加法、减法等。utils.cpp
包含一些工具函数,比如字符串处理和文件读取等。
我们希望将这三个源文件编译成一个可执行文件 program
。
如果手动使用 gcc
编译:
你需要依次执行以下命令:
gcc -c main.cpp -o main.o
gcc -c math_functions.cpp -o math_functions.o
gcc -c utils.cpp -o utils.o
gcc main.o math_functions.o utils.o -o program
这四条命令的目的是:
- 使用
-c
选项将每个源文件编译成目标文件(.o
文件)。 - 最后将所有的目标文件链接在一起,生成最终的可执行文件
program
。
注意:在最后链接库生成可执行文件的时候,.o文件的顺序有着严格的要求。被依赖的文件应该放在后面,而依赖其他文件的 .o
文件应该放在前面,比如这里main.o文件需要被放到命令的前面。
综上看来,我们发现:如果有多个源文件的时候,直接使用gcc命令进行编译的时候会非常麻烦。
make和Makefile
make和Makefile介绍
Makefile 是一个文本文件,描述编译规则、文件依赖和构建过程。
make 是一个自动化构建工具,基于 Makefile 中的规则,自动化地编译源代码,生成目标文件或可执行文件。
下面我们将来举出一个makefile的例子:
# 变量定义
CC = g++ # 编译器
CFLAGS = -Wall -g # 编译选项(警告、调试信息)
SRC = main.cpp math_functions.cpp utils.cpp # 源文件列表
OBJ = $(SRC:.cpp=.o) # 从源文件生成目标文件(.cpp -> .o)
TARGET = program # 最终生成的可执行文件
# 默认目标(运行 `make` 时会默认执行的目标)
all: $(TARGET)
# 如何生成目标文件
$(TARGET): $(OBJ)
$(CC) $(OBJ) -o $(TARGET)
# 生成 .o 文件规则:编译源文件为目标文件
%.o: %.cpp
$(CC) $(CFLAGS) -c $< -o $@
# 清理中间文件和可执行文件
clean:
rm -f $(OBJ) $(TARGET)
在这个例子中,我们可以看出来,这个makefile文件定义了编译生成可执行程序时的一系列规则和编译顺序,但是本质上还是调用g++编译器来编译各个程序。
使用make和Makefile方式
1.进入到项目目录
2.在包含 Makefile 的目录中,运行命令:
make
优点:我们可以看到通过这种方式进行代码编译的时候,每次不需要输入一串很长的命令,只需要make命令非常简洁。
补充一点:
make clean的作用是删除所有中间文件和生成的文件,这样子就是从头开始编译文件啦。但是我们一般更改了代码之后不会选择执行make clean,直接执行make就行。
make 会自动检查源文件的修改时间,并根据文件的依赖关系判断是否需要重新编译。如果源文件没有修改,make 会跳过不必要的步骤,只编译更改过的文件,因此不需要每次都执行 make clean。
什么时候需要使用make clean?
解决潜在的编译问题:有时由于依赖关系问题或者中间文件未能正确更新,可能导致一些编译错误。此时,运行
make clean
可以清理掉旧的编译文件,确保一个干净的环境,但一般来说都不需要用到make clean。
缺点:
- 随着项目的扩展,源文件和目标文件的增多,手动更新
Makefile
会变得越来越复杂。需要显式地管理所有的依赖关系,确保文件顺序正确,特别是在大型项目中,手动编写和维护这些规则非常繁琐。 - 链接顺序错误、依赖关系遗漏等问题很容易出错,而且调试起来相对麻烦。
有了make和makefile之后,虽然在编译源代码的时候,命令更加地简单,但是编写makefile文件很复杂,容易出错,在文件目录结构发生改变之后,我们需要对makefile进行手动维护。所以我们有更简单的方式来处理源代码的编译生成可执行文件问题。
1.使用IDE (集成开发环境) ,如 Visual Studio、CLion。通常都提供图形化的项目管理工具,可以自动生成和管理 Makefile
,使得开发者不需要手动编写这些文件。
2.使用自动化构建工具Cmake。CMake 是一种现代的构建工具,它可以自动生成 Makefile 或其他构建系统所需的配置文件(比如 Ninja 构建文件、Visual Studio 项目文件等)。你只需要编写一个 CMakeLists.txt 文件,CMake 会根据你的需求自动生成适当的 Makefile。
CMake和CMakeLists.txt
CMake和CMakeLists.txt 介绍
CMakeLists.txt 是 CMake 配置文件,用于描述项目的构建需求。它包含了项目名称、所需的库、源文件、编译选项等信息,CMake 会根据这些信息生成相应的构建系统文件(如 Makefile)。
CMake 是一个跨平台的构建系统生成工具,根据 CMakeLists.txt
文件中的指令,生成适合当前系统和编译器的构建系统文件(如 Makefile)
CMakeLists.txt是谁来编写呢?
当然是我们自己啦,嘻嘻,我们现在的逻辑就是使用CMake工具+CMakeList.txt来生成出Makefile文件,然后用make工具+makefile来编译源文件。
你别慌,相对于自己编写Makefile文件来说,手动编写CmakeLists.txt要简单的多了,下面请听我娓娓道来!!!
CMakeLists.txt 详细分析
下面这是一个CMakeLists.txt的示例
# 设置使用的 CMake 最低版本要求
cmake_minimum_required(VERSION 3.10)
# 设置项目名称
project(MyProject)
# 设置 C++ 编译标准
set(CMAKE_CXX_STANDARD 17)
# 设置源文件列表,这些文件将被 CMake 用来编译最终的可执行文件
set(SOURCE_FILES
main.cpp
math_functions.cpp
)
# 用前面指定的源文件编译生成一个可执行文件(名为 MyProject)
add_executable(MyProject ${SOURCE_FILES})
CMakeLists.txt示例的详细解释:
project(MyProject) :(在 CMake 配置和管理过程中使用这个名称,这个名称和最后生成的可执行文件名称没有关系)这里这个名称平时用的比较少,这里不详细阐述。
add_executable(MyProject ${SOURCE_FILES}),这里设置的名称MyProject为最终生成的可执行文件的名称。
通常情况下,我们会让
project()
和add_executable()
使用相同的名称,这样做是为了保持一致性和简洁性,易于理解和管理。
对比一下Makefile的内容,显而易见可知,CMakeLists.txt编写的内容:
1.非常简洁,不容易出错
2.不需要我们来考虑源文件的依赖顺序问题
3.在项目结构发生改变的时候也比较好维护
Cmake和CmakeLists.txt详细使用示例
项目目录结构:
/my_project
/src
main.cpp
CMakeLists.txt
1.编写源代码和 CMakeLists.txt文件
编写完源代码并且在项目根目录下创建并编写好 CMakeLists.txt
文件
为了保持源代码和构建文件分离,通常我们会在项目根目录下创建一个单独的 build
目录。这样做的好处是,所有的构建文件、临时文件和生成的可执行文件都存放在这个目录下,保持项目根目录干净。
2.创建构建目录并运行CMake 配置:
在项目根目录下执行以下命令:
mkdir build
cd build
进入 build 目录后,运行 cmake 命令来配置项目。我们需要指定 CMakeLists.txt 文件所在的目录(通常是项目的根目录)。cmake 会检查源代码,并生成适用于当前系统的构建文件(比如 Makefile、Visual Studio 项目文件等)。
cmake ..
..
表示当前目录的上一级,即项目根目录,一般来说CMakeLists.txt 文件所在的目录就是根目录,正好是..
3.
构建项目
运行 CMake 命令后,会在构建目录build下生成适用于当前系统的构建文件比如Makefile。此时我们执行命令:
make
执行 make
后,就会根据Makefile文件中的规则来编译源代码并生成对应可执行文件
4.运行程序
构建完成后,我们可以直接运行生成的可执行文件。假设项目名是 MyProject
,我们可以通过以下命令运行它:
./MyProject
亲身实践之后,我发现:使用CMake方式的时候确实是应该构建一个build目录,以此来保持源代码和构建文件分离。
参考文章:CMake 和makefile_makefile cmake-CSDN博客