【工具】CMake学习
CMake 教程
来自知乎的推荐: ttroy50/cmake-examples: Useful CMake Examples (github.com)
01-basic
A-hello-cmake
基本的 Helloworld 示例。
1 | $ tree |
1 | # Set the minimum version of CMake that can be used |
CMakeLists.txt 是保存 CMake 命令的文件。cmake 运行时会在当前目录中寻找 CMakeLists.txt 文件,如果不存在则会报错。
cmake_minimum_required(VERSION 3.5)
:指定 cmake 的最小版本要求project (hello_cmake)
:提供项目名称信息,也可以包括版本,使用编译语言等信息add_executable(hello_cmake main.cpp)
:重要! 用于指明可执行文件要从哪个源文件被构建。在这个样例中,源文件是 main.cpp。第一个参数是编译得到的可执行文件名,第二个参数是需要编译的若干个源文件。
接下来编译这个例子。输入 cmake .
在当前目录下生成 Makefile 即可。也可以在其他目录下编译,如:
1 | $ mkdir build |
这样的好处是生成的若干中间文件不会冲散原有的目录结构。
B-hello-headers
1 | $ tree |
有头文件,也有两个源文件。放在不同的目录下。
目录路径
cmake 语法指明了一些变量,可用于帮助在您的项目或源代码树中找到有用的目录。
Variable | Info |
---|---|
CMAKE_SOURCE_DIR | The root source directory |
CMAKE_CURRENT_SOURCE_DIR | The current source directory if using sub-projects and directories. |
PROJECT_SOURCE_DIR | The source directory of the current cmake project. |
CMAKE_BINARY_DIR | The root binary / build directory. This is the directory where you ran the cmake command. |
CMAKE_CURRENT_BINARY_DIR | The build directory you are currently in. |
PROJECT_BINARY_DIR | The build directory for the current project. |
源文件变量
除了上面提到的预定义的变量,也可以自己添加变量。例如可以创建一个包含源文件的变量,这样就能够更清晰地描述这些文件,并简便地将它们添加到其他命令语句中,例如add_executable()
。
1 | # Create a sources variable with a link to all cpp files to compile |
Tip
现在的 cmake 并不推荐为源文件设置一个变量。相反,直接在 add_xxx
函数中直接输入源文件的名字更为通用。(那你前面介绍个棒槌??)
引用目录
当你有不同的 include 目录时,可以使用 target_include_derectories()
函数来告知编译器。这样编译时会添加 -I 参数来添加引用目录,例如 -I/directory/path
。
1 | target_include_directories(target |
PRIVATE
指明了 include 的范围。这个会在下个部分解释,也可以参考这个函数的说明文档。
编译
1 | $ mkdir build && cd build && cmake .. |
make 冗余输出(Verbose Output)
在前面的例子中,运行 make 时只能看到 build 的状态。如果想看到完整的调试信息,可以在 make 时添加 VERBOSE=1
。
1 | $ make VERBOSE=1 |
C-static-library
静态库指的是 .a
1 | $ tree |
add_library
用于创建一个库。STATIC
1 | #Generate the static library from the library sources |
充填引用目录
我们再次用到了 target_include_directories
来设置 include 目录。这次设定的范围是 PUBLIC
1 | target_include_directories(hello_library |
设定为 PUBLIC
将会导致 include 目录被用于以下场景:
- 编译库时(hello_library)
- 编译链接该库的任何附加目标时
这里解释范围的概念:
PRIVATE
:仅用于编译该目标本身INTERFACE
:仅编译依赖于该目标的其他目标PUBLIC
:上述两种情况都包含
Tip
对于公共头文件,将 include 目录用子目录命名是一个明智的决定。因为传递给 target_include_directories
的路径应该是 include 目录的根路径(即应该把所有头文件放在一个目录下,可以在目录内部再组织新的目录,但是传递给该函数的时候传递根目录)。引用时也应该这样:
1 |
链接到一个库
当编译一个可执行文件必须用到库时,必须告知编译器。可以使用 target_link_libraries
函数。
1 | # Add an executable with the above sources |
也可以使用 PUBLIC
或者 INTERFACE
来限定范围。
D-shared-library
1 | $ tree |
添加一个动态链接库(共享库)
add_library
中添加 SHARED
即可:
1 | add_library(hello_library SHARED |
此时会创建 hello_library.so
。
别名目标(Alias Target)
顾名思义,别名目标是目标的替代名称,可用于代替只读上下文中的真实目标名称。
1 | add_library(hello::library ALIAS hello_library) |
这允许您在将目标链接到其他目标时使用别名来引用目标。
我不知道这个别名有什么用。百度了一下,ALIAS 目标主要用于为目标提供更多拼写或结构化名称,例如添加一个“命名空间”(例子中的 hello::)
将目标程序链接到一个共享库
仍然是 add_executable
和 target_link_library
的组合。
1 | # Add an executable with the above sources |
build
1 | $ mkdir build && cd build && cmake .. |
可以看到,编译出的程序依赖 libhello_library.so
E-installing(跳过)
先跳过。
F-build-type
cmake 包含一系列内建的配置项,可以指明编译时的优化等级,以及是否包含调试信息等,build type 具体如下:
- Release:添加
-O3 -DNDEBUG
参数 - Debug : 添加
-g
- MinSizeRel:添加
-Os -DNDEBUG
- RelWithDebInfo : 添加
-O2 -g -DNDEBUG
-DNDEBUG 将在编译时定义 NDEBUG 宏。
1 | $ tree |
设置 build type
有两种选择:
- 在图形化界面选择(例如 cmake-gui )
- 在编译时传递给 cmake(
cmake .. -DCMAKE_BUILD_TYPE=Release
)
设置默认的 build type
1 | # Set a default build type if none was specified |
G-compile-flags
CMake 有多种方式来设置编译时的参数:
- 使用
target_compile_definitions()
函数 - 使用
CMAKE_C_FLAGS
和CMAKE_CXX_FLAGS
变量
1 | $ tree |
为每个目标设置 C++ 编译参数
推荐的方式是使用