CMake和编译过程之间存在对应关系。 如果了解了编译构建过程,就可以更好地理解CMake的相关命令; 如果你了解了它的目的和用途,自然就能更好地使用CMake。
最近的CMake系列文章中,有些朋友在实际使用时仍然感到困惑。 经过沟通,我了解到有些同学可能不是计算机专业,可能对编译原理和流程不太了解,所以作者写了一篇文章:
GCC 编译过程概述。
作为本文的姊妹篇,本文仍以GCC为例。 在对GCC编译流程有一定了解的基础上,我们将进一步了解CMake是如何通过.txt定义项目的编译和构建流程的。
编译和构建的框架
在GCC编译过程概述一文中,主要介绍了源文件是如何编译成机器码.o文件,以及链接器最终如何链接相关库文件得到最终的可执行文件的。
事实上,对于构建的每个目标来说,它都是一个树形结构。 以本系列的开源项目//cmake-为例(当前id:),构建目标、源文件/.o文件和.a文件由一个“Build Tree”组成:
对于最终的可执行文件(demo),必须能够找到所有所需功能的实现。 这些实现可能包含在单个 .o 文件中(demo.o、crtn.o 等),也可能是打包好的库文件(实际上是 .o 文件的集合,例如 .a、libm.a ),所以它将成为构建树的根。
对于某些库文件(模块)来说,它可以是最终可执行文件构建树的子树,也有相应的构建产物(比如这里的.a)。
至于构造树的叶子节点,它们实际上对应的是特定的源文件,但有时它们是预编译的第三方库或系统库。 如果源文件是开源的,开发者可以选择自己从源代码编译(例如本项目中的gtest就是从源代码编译而来。在单元测试可执行文件的构建树中,叶子节点是gtest的开源代码)。
CMake官网上有Build Tree的定义。 您可以检查链接:/cmake/help///cmake.1.html#-to-cmake-
注意理解其含义而不是form 2 GCC编译过程与CMake命令的关系
GCC编译的具体过程实际上是由gcc命令的参数控制的。 这些参数的作用与CMake命令相对应。
在GCC编译过程概述文章中,介绍了gcc命令的常用参数(下面添加了-D和-O):
GCC的编译过程大致是:
预处理:将源文件处理成.ii/.i,处理各种预处理指令,如#、#ifdef、#if等,还有清晰的注释; 编译:将.ii/.i处理成.S/.asm,即机器语言的汇编文件; 汇编:将.asm/.S处理成.o,并将汇编文件转为机器码; :转换各种依赖的静态/动态库文件、.o文件、文件链接成最终的可执行文件或共享库文件。
事实上,gcc命令的参数是针对不同的编译阶段的。 下面分阶段介绍gcc参数与CMake命令的对应关系。
1 预处理
预处理阶段主要是对各种宏进行处理。 在开发过程中,经常使用#ifdef来判断是否定义了相应的宏,以灵活地在不同代码之间切换,例如:
#ifdef UPPER_CASE
#define name "REAL_COOL_ENGINEER"
#else
#define name "real_cool_engineer"
这时,如果需要使用大写版本,可以使用gcc的-D参数:
gcc -DUPPER_CASE ...
在CMake中,可以使用以下命令:
add_definitions(-DUPPER_CASE)
2 编译
编译时,需要将源文件处理成机器码,主要有两个方面:
如何编译源文件中的代码以及如何在源文件中找到内部调用的外部函数?
对于第一点,编译选项有很多种,有很多种类型:
编译警告选项,如:-Wall, -code 优化选项,如:-O0、-Ofast 调试选项,如:-g、-fvar - 预处理选项,如:-M、-MP 代码生成选项,如如: -fPIC 、 -fPIE 等,以及特定于不同语言的选项
所有选项在GNU GCC官方网站上都有详细描述,参见:-。
对于第二点,在源文件内部,调用的外部函数是在头文件中声明的,所以编译器必须能够通过#头文件找到它。 这时就需要使用-I参数来指定头文件的搜索路径,以保证编译器能够找到源文件所使用的头文件。
使用gcc命令时,可以直接将选项作为参数传递,如:
gcc -c xxx.c -Os -g -Wall -Wextra -pedantic -Werror -o xxx.o -Isrc/c
然后在 CMake 中,您可以:
使用命令指定编译选项。 使用命令指定头文件搜索路径。
因此上述gcc命令的效果相当于:
add_compile_options(-Os -g -Wall -Wextra -pedantic -Werror)
include_directories(src/c)
add_library(xxx STATIC xxx.c)
需要注意的是,由于CMake的构建目标必须是库或可执行文件,因此没有仅生成.o文件的命令,因此请使用它。
3 个链接
链接需要做的就是组装最终目标所依赖的所有内容。
这里的可执行文件,从demo.o的main函数开始,链接整个程序执行过程中需要的所有函数的实现; 不同的实现可能在不同的.o文件或库文件中,通过头文件声明函数名,在.o和.a文件中搜索所需的实现; 如果未找到,将引发链接错误。
对于构建目标库文件和项目内的其他.o文件,可以在链接时直接使用它们。 对于外部第三方库或者系统库文件,需要使用-L和-l参数来通知链接器。 。
就像编译一样,链接器除了-L和-l之外,还有很多其他参数,比如:
-pie -pthread -r -s -static -static-pie
详细参数介绍请参见:链接-。
相应地,可以与CMake一起使用的命令有:
对于 -L,请使用 or ies 命令。 对于 -l,使用 或 s 命令指定链接器选项。 使用 或 命令。 上述命令中,以 开头的命令是针对特定目标设置的,否则是针对所有目标设置的。
假设目标程序使用外部库文件/usr/lib/.a,则可以使用命令:
gcc demo.c -L/usr/lib -lmath -pthread
相应地,CMake使用的命令应该是:
add_link_options(-pthread)
add_executable(demo demo.c)
link_directories(/usr/lib)
target_link_libraries(demo math)
三总结
最后用一个表格总结一下本文的核心内容。 GCC编译过程中使用的gcc参数与CMake命令对应表:
享受 CMake!