目录一、CMake ⼯程实践场景1.1 可执⾏⽂件编译-链接-安装1.1.1 单步操作1.1.2 重点命令解释1.1.2.1 cmake_minimum_required1.1.2.2 project1.1.2.3 include1.1.2.4 install1.1.2.5 add_executable一、CMake ⼯程实践场景上⼀章我们介绍了cmake的基础命令⾏介绍了如何⽣成构建⽂件编译链接安装等。本章节我们会带领⼤家从⼀⾏⼀⾏⼿写CMake⽂件的⽅式开始逐步熟悉如何⽤CMake 来管理各个场景下的⼯程需求⽐如编译⼆进制静态库动态库打包发布程序等。过程中也会逐步的介绍⼀些核⼼的语法和函数并⽤⼀些简单的例⼦来演⽰和验证。1.1可执⾏⽂件编译-链接-安装本例⼦我们会编译和链接⼀个最简单的输出 hello bits的C程序 可执⾏程序并本地运⾏然后安装到系统⽬录下。过程中会介绍⼀些遇到的新的CMake函数。1.1.1 单步操作我们创建⼀个新的install_hello⼯程Step 0⽬录结构├──CMakeLists.txt├──build└──main.cppStep 1新建⽂件-CMakeLists.txt# 1 设置最低cmake 版本 cmake_minimum_required(VERSION 3.18) #2 设置项⽬名称 project(InstallHello VERSION 1.2.3 LANGUAGES C CXX ) # 3 添加构建⽬标 add_executable(hello main.cpp) # 4 安装到本地 include(GNUInstallDirs) install(TARGETS hello) # printf message(STATUS PROJECT_NAME: ${PROJECT_NAME}) # print version message(STATUS PROJECT_VERSION: ${PROJECT_VERSION}) message(STATUS PROJECT_VERSION_MAJOR: ${PROJECT_VERSION_MAJOR}) message(STATUS PROJECT_VERSION_MINOR: ${PROJECT_VERSION_MINOR}) message(STATUS PROJECT_VERSION_PATCH: ${PROJECT_VERSION_PATCH})# 1 设置最低cmake 版本 cmake_minimum_required(VERSION 3.18) #2 设置项⽬名称 project(InstallHello VERSION 1.2.3 LANGUAGES C CXX ) # 3 添加构建⽬标 add_executable(hello main.cpp) # 4 安装到本地 include(GNUInstallDirs) install(TARGETS hello) # printf message(STATUS PROJECT_NAME: ${PROJECT_NAME}) # print version message(STATUS PROJECT_VERSION: ${PROJECT_VERSION}) message(STATUS PROJECT_VERSION_MAJOR: ${PROJECT_VERSION_MAJOR}) message(STATUS PROJECT_VERSION_MINOR: ${PROJECT_VERSION_MINOR}) message(STATUS PROJECT_VERSION_PATCH: ${PROJECT_VERSION_PATCH}) # 打印 默认安装路径 message(STATUS CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX})Step 2新建⽂件-main.cpp// main.cpp#includeiostreamintmain() {std::cout Hello Bits std::endl;return0;}Step 3运⾏CMakemkdirbuild cdbuildcmake ../-- CMAKE_INSTALL_PREFIX: /usr/local-- CMAKE_INSTALL_BINDIR: bin-- Configuringdone(0.1s)-- Generatingdone(0.0s)Step 4编译链接cmake --build .[ 50%] Building CXX object CMakeFiles/hello.dir/src/main.cpp.o[100%] Linking CXX executable hello[100%] Built target helloStep 5运⾏./helloHello BitsStep 6安装cmake --install .[100%] Built target helloInstall the project...-- Install configuration:-- Installing: /usr/local/bin/helloStep 7运⾏Hello Bits在 Linux 系统中“安装” 指的是将编译好的⼆进制库⽂件通常是.a .so⽂件及其头⽂件复制到系统标准路径或⾃定义路径以便其他程序可以链接和使⽤该库。放在标准路径下的 动态库和⼆进制在其他程序链接时甚⾄可以不⽤单独指定 gcc的 -L 和 -I 参数gcc就可以在默认路径下⾃动查找从⽽简化编译流程。普通的⼆进制安装到默认路径下也不⽤的在独⽴指定PATHshell 解释器也可以找到⼆进制并运⾏。1.1.2重点命令解释1.1.2.1cmake_minimum_required函数作⽤指定项⽬所需的最低 CMake 版本应放在顶级 CMakeLists.txt 的第⼀⾏。基本形式cmake_minimum_required(VERSION min[...policy_max] [FATAL_ERROR])参数解释参数含义VERSION关键字表⽰后⾯跟的是版本号min版本号major[.minor 指定所需的最低 CMake 版本如 3.18。FATAL_ERROR可选若指定当 CMake 版本不满⾜时会终⽌配置并报错CMake 2.6 默认为此⾏为。版本号说明不同 Linux 发⾏版如 Ubuntu、CentOS、Fedora 等的软件仓库中预装的 CMake 版本可能随系统版本更新⽽变化。1.1.2.2 project函数作⽤指定项⽬名字放在顶级CMakeLists⽂件的第⼆⾏⼦⽬录中⼀般⽆需调⽤。基本形式project(PROJECT-NAME完整形式project(PROJECT-NAME [language-name...])project(PROJECT-NAME[VERSION major[.minor[.patch[.tweak]]]][DESCRIPTION project-description-string][HOMEPAGE_URL url-string][LANGUAGES language-name...])关键参数参数含义PROJECT-NAME项⽬名称如 MyProject⽤于⽣成默认变量如PROJECT_NAME。VERSION可选项⽬版本号如 1.0.0会⾃动定义 PROJECT_VERSION 等变量。DESCRIPTION可选项⽬描述信息⽤于⽣成⽂档或包配置。HOMEPAGE_URL项⽬主⻚ URLLANGUAGES可选指定项⽬⽀持的语⾔如 C、CXX、Fortran、ASM 等。project() 执⾏之后CMake会⾃动创建以下变量可在后续命令中使⽤变量描述PROJECT_NAME项⽬名称如 MyProject。CMAKE_PROJECT_NAME顶级项⽬名称与 PROJECT_NAME 相同。PROJECT_VERSION完整版本号如 1.2.3。PROJECT_VERSION_MAJOR主版本号如 1。PROJECT_VERSION_MINOR次版本号如 2。PROJECT_VERSION_PATCH修订号如 3。PROJECT_SOURCE_DIR顶级 CMakeLists.txt 所在⽬录即源⽂件树根⽬录。PROJECT_BINARY_DIR构建⽬录如 build/。PROJECT_NAME 变量的使⽤场景1 动态库的输出名称2 cmake配置⽂件的名称3 命名空间的名称PROJECT_VERSION 变量的使⽤场景1 打印变量2 ⽣成pkg-config或者.cmake对应的版本配置⽂件3 动态库/静态库的的版本号为了验证以上内置变量我们在本节的顶级CMakeLists.txt⾥新增加⼀些message函数打印下上⾯的⾃定义变量cmake_minimum_required(VERSION 3.10) # 设置项⽬名称 project(hello VERSION 1.2.3 LANGUAGES CXX) # 设置 C 标准 set(CMAKE_CXX_STANDARD 11) # 添加可执⾏⽂件 add_executable(hello src/main.cpp) #add_executable(app src/app.cpp) # 安装可执⾏⽂件 include(GNUInstallDirs) install(TARGETS hello) #install(TARGETS hello RUNTIME DESTINATION bin) # 输出⼀些调试信息 # name message(STATUS PROJECT_NAME: ${PROJECT_NAME}) message(STATUS CMAKE_PROJECT_NAME: ${CMAKE_PROJECT_NAME}) message(STATUS PROJECT_LANGUAGES: ${PROJECT_LANGUAGES}) # version message(STATUS PROJECT_VERSION: ${PROJECT_VERSION}) message(STATUS PROJECT_VERSION_MAJOR: ${PROJECT_VERSION_MAJOR}) message(STATUS PROJECT_VERSION_MINOR: ${PROJECT_VERSION_MINOR}) message(STATUS PROJECT_VERSION_PATCH: ${PROJECT_VERSION_PATCH}) # dir message(STATUS CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}) message(STATUS CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}) message(STATUS PROJECT_SOURCE_DIR: ${PROJECT_SOURCE_DIR}) message(STATUS PROJECT_BINARY_DIR: ${PROJECT_BINARY_DIR}) message(STATUS CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}) message(STATUS CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}) # file message(STATUS CMAKE_CURRENT_LIST_FILE: ${CMAKE_CURRENT_LIST_FILE}) message(STATUS CMAKE_CURRENT_LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}) # 输出当前的 CMAKE_INSTALL_PREFIX message(STATUS CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}) # 输出当前的 CMAKE_INSTALL_BINDIR message(STATUS CMAKE_INSTALL_BINDIR: ${CMAKE_INSTALL_BINDIR})运⾏观察输出结果cmake ../lixiaoshenlixiaoshens-MacBook-Pro build % cmake ../ -- CMAKE_SOURCE_DIR: /Users/lixiaoshen/Desktop/CMakeDemo/hello -- CMAKE_BINARY_DIR: /Users/lixiaoshen/Desktop/CMakeDemo/hello/build -- PROJECT_NAME: hello -- CMAKE_PROJECT_NAME: hello -- PROJECT_VERSION: 1.2.3 -- PROJECT_LANGUAGES: -- PROJECT_VERSION_MAJOR: 1 -- PROJECT_VERSION_MINOR: 2 -- PROJECT_VERSION_PATCH: 3 -- PROJECT_SOURCE_DIR: /Users/lixiaoshen/Desktop/CMakeDemo/hello -- PROJECT_BINARY_DIR: /Users/lixiaoshen/Desktop/CMakeDemo/hello/build -- CMAKE_CURRENT_SOURCE_DIR: /Users/lixiaoshen/Desktop/CMakeDemo/hello -- CMAKE_CURRENT_BINARY_DIR: /Users/lixiaoshen/Desktop/CMakeDemo/hello/build -- CMAKE_CURRENT_LIST_FILE: /Users/lixiaoshen/Desktop/CMakeDemo/hello/CMakeLists.txt -- CMAKE_CURRENT_LIST_DIR: /Users/lixiaoshen/Desktop/CMakeDemo/hello -- CMAKE_INSTALL_PREFIX: /usr/local -- CMAKE_INSTALL_BINDIR: bin1.1.2.3 include函数作⽤加载指定的脚本⽂件或者模块到当前CMakeLists执⾏上下⽂中并运⾏。基本形式include(file|module [OPTIONAL] [RESULT_VARIABLE var] [NO_POLICY_SCOPE])关键参数file|module要运⾏的的⽂件或模块名称。从⽂件或模块加载并运⾏ CMake 代码。1 ⽂件的搜索路径◦如果指定的是相对路径则相对当前的正在执⾏的CMakeLists.txt 所在的⽬录。◦如果是绝对路径那直接从对应的磁盘⽂件读取⽂件并加载执⾏。2 Module 搜索路径顺序◦⾸先在当前⽬录查找指定⽂件。◦然后在 CMAKE_MODULE_PATH变量指定的⽬录中查找。3 执⾏逻辑◦在当前执⾏上下⽂执⾏被包含的camke代码。4 CMake 进⼊⼦⽬录之后内置变量的变化情况变量名进⼊⼦⽬录后是否变化说明CMAKE_CURRENT_SOURCE_DIR不不变化还是⽗⽬录的源代码树的⽬录CMAKE_CURRENT_BINARY_DIR不不变化还是⽗⽬录的构建树的⽬录CMAKE_CURRENT_LIST_FILE✅ 变化变为⼦⽬录的CMakeLists.txt ⽂件全路径CMAKE_CURRENT_LIST_DIR✅ 变化变为⼦⽬录的 CMakeLists.txt ⽂件⽬录举例⼦ root/CMakeLists.txt 中include(⼦⽬录sub/sub.cmake) cmake 在从root 切换到sub时的变量变化情况变量名执⾏CMakeLists.txt时的值执⾏sub.cmake时的值原因CMAKE_CURRENT_SOURCE_DIRrootroot因为执⾏上下⽂依然是A⽬录CMAKE_CURRENT_LIST_FILEroot/CMakeLists.txroot/sub/sub.cmakeCMAKE_CURRENT_LIST_DIRrootroot/sub构造⼀个新的⼯程test_include然后打印对应的变量观察下变量值的变化情况Step 0⽬录结构test_include├──CMakeLists.txt├──build└──sub└──sub.cmakeStep 1新建⽂件-CMakeLists.txtcmake_minimum_required(VERSION 3.18)# 2设置项⽬名称project(TestInclude)# 3打印 内置路径变量message(STATUSfrom top-level CMakeLists.txt)#打印 当前正在执⾏的源代码⽬录---也就是CMakeLists.txt所在的⽬录message(STATUSCMAKE_CURRENT_SOURCE_DIR:${CMAKE_CURRENT_SOURCE_DIR})#打印 当前正在执⾏的cmake脚本的 完整名称message(STATUSCMAKE_CURRENT_LIST_FILE:${CMAKE_CURRENT_LIST_FILE})#打印当前正在执⾏的cmake脚本的 全⽬录message(STATUSCMAKE_CURRENT_LIST_DIR:${CMAKE_CURRENT_LIST_DIR})# 4包含⼦⽬录cmake脚本include(sub/sub.cmake)Step 2新建⽂件-sub.cmakemessage(STATUSfrom sub/sub.cmake)#打印 当前正在执⾏的源代码⽬录---也就是CMakeLists.txt所在的⽬录message(STATUSCMAKE_CURRENT_SOURCE_DIR:${CMAKE_CURRENT_SOURCE_DIR})#打印 当前正在执⾏的cmake脚本的 完整名称message(STATUSCMAKE_CURRENT_LIST_FILE:${CMAKE_CURRENT_LIST_FILE})#打印当前正在执⾏的cmake脚本的 全⽬录message(STATUSCMAKE_CURRENT_LIST_DIR:${CMAKE_CURRENT_LIST_DIR})Step 3运⾏cmakemkdir build cd build cmake ../ -- from top-level CMakeLists.txt -- CMAKE_CURRENT_SOURCE_DIR:/home/bit/workspace/CMakeClass/test_include -- CMAKE_CURRENT_LIST_FILE:/home/bit/workspace/CMakeClass/test_include/CMakeLists.txt -- CMAKE_CURRENT_LIST_DIR:/home/bit/workspace/CMakeClass/test_include -- from sub/sub.cmake -- CMAKE_CURRENT_SOURCE_DIR:/home/bit/workspace/CMakeClass/test_include -- CMAKE_CURRENT_LIST_FILE:/home/bit/workspace/CMakeClass/test_include/sub/sub.cmake -- CMAKE_CURRENT_LIST_DIR:/home/bit/workspace/CMakeClass/test_include/sub1.1.2.4 install函数作⽤安装(简单理解为cp) 将 ⼆进制静态库动态库头⽂件配置⽂件 部署到指定⽬录。基本形式install(TARGETS targets... [EXPORT export-name] [RUNTIME DESTINATION dir] [LIBRARY DESTINATION dir] [ARCHIVE DESTINATION dir] [INCLUDES DESTINATION dir] [...]) install(FILES files... DESTINATION dir [PERMISSIONS permissions...] [CONFIGURATIONS configs...] [COMPONENT component] [...]) install(DIRECTORY dirs... DESTINATION dir [FILE_PERMISSIONS permissions...] [DIRECTORY_PERMISSIONS permissions...] [...]) install(EXPORT export-name DESTINATION dir [NAMESPACE namespace::] [FILE filename] [...])关键参数参数含义TARGETS安装 使⽤add_executable和add_library 构建的⽬标⽂件。FILES安装 ⽂件DIRECTORY安装整个⽬录EXPORT安装导出⽬录⽤于发布⾃⼰的程序供别⼈使⽤。DESTINATION指定安装路径路径可以是绝对路径也可以是相对路径相对于CMAKE_INSTALL_PREFIX 。从上⾯测试include 的⼯程 打印结果可以看到 CMAKE_INSTALL_PREFIX的 默认路径为 /usr/local1.1.2.5 add_executable函数作⽤指⽰ cmake 从源代码⽣成⼀个可执⾏⽂件。基本形式add_executable(target_name [WIN32] [MACOSX_BUNDLE][EXCLUDE_FROM_ALL][source1] [source2 ...])关键参数参数含义target_name可执⾏⽂件的名称不包含扩展名如 myapp项⽬内部唯⼀sources源⽂件列表如 src/main.cpp默认情况下将在与调⽤命令的CMakeLists.txt的⽬录相对应的 build tree directory 中创建可执⾏⽂件。 也可以使⽤RUNTIME_OUTPUT_DIRECTORY⽬标属性的更改默认的输出位置。