--- layout: post title: "CMake 基本语法" subtitle: "" description: "CMake 的简明语法。" excerpt: "通过简明的方式对 CMake 构建系统基本语法进行说明。" date: 2020-01-20 15:17:00 author: "Rick Chan" tags: ["Development", "Environment", "CMake"] categories: ["Software"] published: true --- CMake 支持大写、小写、混合大小写的命令。 ## 1. 最小版本号 ```cpp cmake_minimum_required(VERSION 2.8) ``` ## 2. 工程名 ```cpp project() ``` ## 3. Include 路径 *需要出现在 add_executable 和 add_library 等之前。* ```cpp include_directories( ... ) ``` ## 4. 添加库路径 *需要出现在 add_executable 和 add_library 等之前。* ```cpp link_directories( ... ) ``` ## 5. 添加库文件 *已经被废弃了,需要出现在 add_executable 和 add_library 等之前。* ```cpp link_libraries( ... ) ``` 支持直接全路径的写法。 ## 6. 可执行文件目标和源码 ```cpp add_executable( ... ) ``` ## 7. 库文件目标和源码 ```cpp add_library( ... ) ``` ## 8. 为目标添加库文件 *可添加的动态库或静态库,可以在 add_executable 和 add_library 等之后。* ```cpp target_link_libraries( ... ) ``` ## 9. 添加依赖工程 ```cpp add_dependencies( ... ) ``` ## 10. 添加子目录 添加的子目录下的 CMakeLists.txt 和源码将被处理。 ```cpp add_subdirectory( ... ) ``` ## 11. 宏 ```cpp set(A_MACRO ) ``` 可通过 ```bash ${A_MACRO} ``` 方式引用宏。 ## 12. 自定义编译选项 CMake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。 ```cpp option (USE_MYMATH "Use provided math implementation" ON) if (USE_MYMATH) include_directories ("${PROJECT_SOURCE_DIR}/math") add_subdirectory (math) set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions) endif (USE_MYMATH) ``` ## 13. Config File configure_file 命令用于加入一个配置头文件(如 config.h),这个文件由 CMake(从 config.h.in)生成,通过这样的机制,将可以通过预定义一些参数和变量来控制代码的生成。 ```cpp configure_file ( "${PROJECT_SOURCE_DIR}/config.h.in" "${PROJECT_BINARY_DIR}/config.h" ) ``` 编写一个 config.h.in 文件,内容如下: ```cpp #cmakedefine USE_MYMATH ``` ## 14. 编译项目 通常会创建一个 build 目录为 cmake 的工作目录,用于存放 cmake 临时文件: ```bash mkdir build && cd build cmake .. make make install ``` 为了便于交互式的选择该变量的值,可以使用: ```bash ccmake # 或 cmake -i ``` 该命令会提供一个会话式的交互式配置界面。 ## 15. CMake 标准宏 宏取值类型如下: * string * int * bool:TRUE/FALSE ### 15.1. CMAKE_INSTALL_PREFIX 类型:string 用于指定 make install 的安装根目录,默认值为:/usr/local/。 ### 15.2. CMAKE_SKIP_RPATH 类型:bool 简单的来说,就是强制 CMake 不在构建期间和 install 期间给你加上它所认为的 RPATH。 ### 15.3. CMAKE_SKIP_BUILD_RPATH 类型:bool 在构建期间和安装期间不允许 CMake 加入 BUILD RPATH。 ### 15.4. CMKAE_INSTALL_RPATH 类型:bool 在构建期间和安装期间不允许 CMake 加入 INSTALL RPATH。 ### 15.5. CMAKE_BUILD_WITH_INSTALL_RPATH 类型:bool 编译时 RPATH 是否使用 INSTALL RPATH。也就是决定了编译时是否去 INSTALL RPATH 下查找所需的 Lib 库文件 ## 16. CTest CMake 提供了一个称为 CTest 的测试工具。我们要做的只是在项目根目录的 CMakeLists 文件中调用一系列的 add_test 命令。 ```cpp # 启用测试 enable_testing() # 测试程序是否成功运行 add_test (test_run Demo 5 2) # 测试帮助信息是否可以正常提示 add_test (test_usage Demo) set_tests_properties (test_usage PROPERTIES PASS_REGULAR_EXPRESSION "Usage: .* base exponent") # 测试 5 的平方 add_test (test_5_2 Demo 5 2) set_tests_properties (test_5_2 PROPERTIES PASS_REGULAR_EXPRESSION "is 25") # 测试 10 的 5 次方 add_test (test_10_5 Demo 10 5) set_tests_properties (test_10_5 PROPERTIES PASS_REGULAR_EXPRESSION "is 100000") # 测试 2 的 10 次方 add_test (test_2_10 Demo 2 10) set_tests_properties (test_2_10 PROPERTIES PASS_REGULAR_EXPRESSION "is 1024") ``` 上面的代码包含了四个测试。第一个测试 test_run 用来测试程序是否成功运行并返回 0 值。剩下的三个测试分别用来测试 5 的 平方、10 的 5 次方、2 的 10 次方是否都能得到正确的结果。其中 PASS_REGULAR_EXPRESSION 用来测试输出是否包含后面跟着的字符串。 让我们看看测试的结果: ```bash [ehome@xman Demo5]$ make test Running tests... Test project /home/ehome/Documents/programming/C/power/Demo5 Start 1: test_run 1/4 Test #1: test_run ......................... Passed 0.00 sec Start 2: test_5_2 2/4 Test #2: test_5_2 ......................... Passed 0.00 sec Start 3: test_10_5 3/4 Test #3: test_10_5 ........................ Passed 0.00 sec Start 4: test_2_10 4/4 Test #4: test_2_10 ........................ Passed 0.00 sec 100% tests passed, 0 tests failed out of 4 Total Test time (real) = 0.01 sec ``` 如果要测试更多的输入数据,像上面那样一个个写测试用例未免太繁琐。这时可以通过编写宏来实现: ```cpp # 定义一个宏,用来简化测试工作 macro (do_test arg1 arg2 result) add_test (test_${arg1}_${arg2} Demo ${arg1} ${arg2}) set_tests_properties (test_${arg1}_${arg2} PROPERTIES PASS_REGULAR_EXPRESSION ${result}) endmacro (do_test) # 使用该宏进行一系列的数据测试 do_test (5 2 "is 25") do_test (10 5 "is 100000") do_test (2 10 "is 1024") ``` 关于 CTest 的更详细的用法可以通过 man 1 ctest 参考 CTest 的文档。 ## 17. 支持 GDB 让 CMake 支持 gdb 的设置也很容易,只需要指定 Debug 模式下开启 -g 选项: ```cpp set(CMAKE_BUILD_TYPE "Debug") set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb") set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall") ``` ## 18. 添加版本号 首先修改顶层 CMakeLists 文件,在 project 命令之后加入如下两行: ```cpp set (Demo_VERSION_MAJOR 1) set (Demo_VERSION_MINOR 0) ``` 分别指定当前的项目的主版本号和副版本号。 之后,为了在代码中获取版本信息,我们可以修改 config.h.in 文件,添加两个预定义变量: ```cpp // the configured options and settings for Tutorial #define Demo_VERSION_MAJOR @Demo_VERSION_MAJOR@ #define Demo_VERSION_MINOR @Demo_VERSION_MINOR@ ``` 这样就可以直接在代码中打印版本信息了: ```cpp int main(void) { printf("Version %d.%d\n", Demo_VERSION_MAJOR, Demo_VERSION_MINOR); return 0; } ``` ## 19. 生成安装包 首先在顶层的 CMakeLists.txt 文件尾部添加下面几行: ```cpp # 构建一个 CPack 安装包 include (InstallRequiredSystemLibraries) set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") set (CPACK_PACKAGE_VERSION_MAJOR "${Demo_VERSION_MAJOR}") set (CPACK_PACKAGE_VERSION_MINOR "${Demo_VERSION_MINOR}") include (CPack) ``` 上面的代码做了以下几个工作: 1. 导入 InstallRequiredSystemLibraries 模块,以便之后导入 CPack 模块; 2. 设置一些 CPack 相关变量,包括版权信息和版本信息,其中版本信息用了上一节定义的版本号; 3. 导入 CPack 模块。 接下来的工作是像往常一样构建工程,并执行 cpack 命令。 * 生成二进制安装包: ```bash cpack -C CPackConfig.cmake ``` * 生成源码安装包 ```bash cpack -C CPackSourceConfig.cmake ``` 在生成项目后,执行 cpack -C CPackConfig.cmake 命令,此时会在该目录下创建:Demo8-1.0.1-Linux.sh、Demo8-1.0.1-Linux.tar.gz、Demo8-1.0.1-Linux.tar.Z,3 个不同格式的二进制包文件。 关于 CPack 的更详细的用法可以通过 man 1 cpack 参考 CPack 的文档。 ## 20. 内部参考关键字 1. 常用宏 ## 21. 外部参考资料 1. [cmake手册详解](https://blog.csdn.net/chengde6896383/article/details/81330564) 2. [cmake 的link_libraries和target_link_libraries](https://blog.csdn.net/harryhare/article/details/89143410) 3. [【学习cmake】cmake如何使用链接库 (link_directories, LINK_LIBRARIES, target_link_libraries,FIND_PACKAGE)实践篇2](https://blog.csdn.net/KYJL888/article/details/85109782) 4. [CMake 入门实战](https://www.hahack.com/codes/cmake/)