Cmake
cmake
1 CMake简介:
CMake是什么及其用途
CMake是一个开源的跨平台自动化构建系统,它使用一个名为**CMakeLists.txt**的配置文件来定义项目的构建过程。CMake的主要目标是简化和标准化构建过程,特别是在需要跨不同平台(如Windows, Linux, 和 macOS)进行构建的项目中。
它主要用于:
- 自动生成原生构建环境:如Makefile(在Unix系统上)或Visual Studio工程文件(在Windows上)。
- 管理依赖和构建过程:自动检测系统库和程序,处理项目内外部依赖。
- 支持复杂项目:CMake可以很好地处理大型项目,支持目录层次结构和多个目标(可执行文件和库)。
CMake与其他构建系统的比较(如Makefile)
- 跨平台兼容性:CMake的一个主要优势是它的跨平台能力。与Makefile(通常与Unix和Linux系统关联)相比,CMake能够在多种操作系统上运行,并生成针对特定平台的构建文件。
- 易于维护和扩展:CMakeLists文件通常比传统的Makefile更容易编写和维护,特别是对于大型和复杂的项目。
- 现代化和社区支持:CMake得到广泛的社区支持,并且经常更新以支持最新的编程语言和编译器标准,这使得它比一些老旧的构建系统更具吸引力。
- 图形界面工具:CMake提供图形界面工具(如ccmake和CMake GUI),这对于配置项目和调试构建问题非常有用。
- 高级特性:CMake支持复杂的构建场景,例如条件构建、自动查找库和程序、生成安装包等。
尽管CMake在许多方面提供了改进,但在一些简单的场景中,传统的Makefile可能更直接和简单。选择最佳工具往往取决于项目的具体需求和团队的熟悉度。
2 安装和基本设置:
- 如何在不同操作系统(如Windows, Linux, macOS)上安装CMake。
- 如何设置一个基本的CMake项目。
Windows:
- 下载: 访问CMake官方网站,下载适用于Windows的安装程序。
- 安装: 运行下载的安装程序,并按照指示完成安装。
- 添加到环境变量: 确保在安装过程中选择将CMake添加到系统路径。
Linux:
-  使用包管理器: 在大多数Linux发行版中,可以使用包管理器安装CMake。例如,在基于Debian的系统(如Ubuntu)上,可以使用以下命令: sqlCopy code sudo apt-get update sudo apt-get install cmake
-  手动安装: 如果需要最新版本,可以从CMake官网下载源代码包,然后编译安装。 
macOS:
-  使用Homebrew: 如果安装了Homebrew,可以简单地运行以下命令: Copy code brew install cmake
-  手动安装: 访问CMake官方网站下载适用于macOS的安装包,并按照指示进行安装。 
如何设置一个基本的CMake项目
- 创建项目目录: 
  - 创建一个新目录作为项目的根目录。
- 在此目录中创建源代码文件(如**main.cpp**)。
 
- 编写CMakeLists.txt文件: 
  -  在项目根目录中创建一个名为** CMakeLists.txt**的文件。
-  文件内容示例: cmakeCopy code cmake_minimum_required(VERSION 3.10) # 指定CMake最低版本要求 project(MyProject) # 定义项目名称 add_executable(myapp main.cpp) # 创建一个名为myapp的可执行文件
-  这个文件定义了项目的基本信息和构建目标。 
 
-  
- 生成构建系统: 
  -  打开终端或命令提示符。 
-  切换到项目根目录。 
-  运行以下命令来生成构建系统: Copy code cmake .
-  这会在当前目录生成适用于你系统的构建文件(如Makefile)。 
 
-  
- 构建项目: 
  -  在同一目录下运行构建命令,例如: cssCopy code cmake --build .
-  这将编译源代码并生成可执行文件。 
 
-  
3 编写CMakeLists文件:
CMakeLists.txt文件的结构和基本语法
CMakeLists.txt 文件是CMake构建系统的核心,它使用CMake专用的语法编写。以下是其基本结构和语法要点:
- 最低CMake版本: 
  - 使用 cmake_minimum_required(VERSION minimum_version)指定所需的最低CMake版本,这有助于确保构建过程的兼容性。
- 例如: cmake_minimum_required(VERSION 3.10)
 
- 使用 
- 项目名称: 
  - 使用 project(project_name)定义项目名称。
- 例如: project(MyProject)
 
- 使用 
- 设置变量: 
  - 使用 set(VAR_NAME value)来设置变量。
- 例如: set(SOURCE_FILES main.cpp)
 
- 使用 
- 添加可执行文件或库: 
  - 使用 add_executable(executable_name ${SOURCE_FILES})或add_library(library_name ${SOURCE_FILES})。
- 例如: add_executable(myapp ${SOURCE_FILES})
 
- 使用 
- 包含目录和链接库: 
  - 使用 include_directories(dir1 dir2 ...)和target_link_libraries(target lib1 lib2 ...)
 
- 使用 
- 自定义指令: 
  - CMakeLists.txt 支持条件语句(如 if,else,endif)和循环语句(如foreach,while)。
 
- CMakeLists.txt 支持条件语句(如 
添加源文件和头文件
- 源文件: 
  - 将源文件直接列在 add_executable或add_library函数中,或者通过设置一个变量来引用。
- 例如: add_executable(myapp main.cpp utility.cpp)
 
- 将源文件直接列在 
- 头文件: 
  - 通常,头文件不需要在CMakeLists.txt中明确列出。但如果头文件位于非标准目录,需要使用 include_directories(your_header_directory)来包含这些目录。
 
- 通常,头文件不需要在CMakeLists.txt中明确列出。但如果头文件位于非标准目录,需要使用 
设置编译器标志和定义
- 编译器标志: 
  - 使用 target_compile_options来为特定目标设置编译器标志。
- 例如: target_compile_options(myapp PRIVATE -Wall -Wextra)
 
- 使用 
- 预处理器定义: 
  - 使用 add_definitions(-DDEFINE)来添加预处理器定义。
- 例如: add_definitions(-DMY_DEFINE)
 
- 使用 
4 管理项目依赖:
- 直接包含库文件: 
  - 如果你有库的源代码或预编译文件(如**.lib、.dll、.so**等),可以直接将它们包含在项目中。
- 使用 target_link_libraries(target_name path_to_library)来链接库文件。
- 使用 include_directories(path_to_header_files)来包含库的头文件。
 
- 如果你有库的源代码或预编译文件(如**
- 使用包管理器: 
  - 对于一些流行的库,可以通过包管理器(如Conan, vcpkg)来集成。
- 这些工具可以自动处理库的下载、构建和链接。
 
使用 find_package 来定位已安装的库
 
find_package 命令用于在系统中查找并定位已安装的库。
-  使用格式: find_package(LibraryName REQUIRED)
-  如果找到,CMake会设置一些变量,比如 LibraryName_FOUND和库的具体路径。
-  一旦找到库,可以使用 target_link_libraries将其链接到你的目标中。
-  例如,要查找并链接OpenGL库: cmakeCopy code find_package(OpenGL REQUIRED) target_link_libraries(your_target_name ${OPENGL_LIBRARIES})
添加子目录和使用外部项目
- 添加子目录: 
  - 使用 add_subdirectory(subdir)来添加包含另一个CMakeLists.txt文件的子目录。
- 这对于模块化项目结构非常有用,允许在子目录中定义额外的构建目标。
 
- 使用 
- 使用外部项目(如git子模块): 
  - 可以将外部项目作为git子模块或通过其他方式集成到项目中。
- 使用 ExternalProject_Add命令来下载、配置、构建和安装外部项目。
- 这种方法适用于在构建时需要从源代码构建依赖项的场景。
 
5 创建可执行文件和库:
使用 add_executable 和 add_library
 
add_executable:
- 用于从指定的源文件创建一个可执行文件。
- 基本语法: add_executable(<name> <source1> <source2> ... <sourceN>)
- 例如,创建一个名为 “app” 的可执行文件:add_executable(app main.cpp utility.cpp)
add_library:
- 用于创建库(静态或动态)。
- 基本语法: add_library(<name> STATIC|SHARED|MODULE <source1> <source2> ... <sourceN>)
- 例如,创建一个名为 “mylib” 的静态库:add_library(mylib STATIC library.cpp utility.cpp)
静态库与动态库的差异
静态库 (Static Libraries):
- 静态库在编译时被完整地复制到可执行文件中。
- 文件扩展名通常是 .lib或.a。
- 优点:简化部署,因为所有代码都包含在单个可执行文件中。
- 缺点:增加了可执行文件的大小;如果库更新,需要重新编译可执行文件。
动态库 (Dynamic Libraries):
- 动态库在运行时被加载。
- 文件扩展名通常是 .dll(Windows),.so(Linux), 或.dylib(macOS)。
- 优点:减少了程序的总体尺寸,可以实现库的共享和热更新。
- 缺点:部署更复杂,因为需要确保动态库在运行时可用。
在选择静态库和动态库时,需要考虑到应用程序的需求、部署策略和平台限制。CMake提供了灵活性来支持这两种类型的库,使得构建过程更加简化和自动化。
5 高级特性:
使用条件语句和循环
条件语句:
-  在 CMakeLists.txt中,可以使用if,elseif,else, 和endif来创建条件语句。
-  用于根据不同条件(如平台、变量值等)执行不同的构建操作。 
-  示例: cmakeCopy code if(WIN32) # 特定于Windows的配置 elseif(UNIX) # 特定于Unix/Linux的配置 endif()
循环语句:
-  循环语句,如 foreach和while,用于重复执行一组命令。
-  foreach循环通常用于迭代列表。
-  示例: cmakeCopy code foreach(src IN ITEMS src1.cpp src2.cpp src3.cpp) message("Source file: ${src}") endforeach()
定义和使用宏和函数
宏(Macro):
-  宏类似于函数,但不创建新的作用域。 
-  使用 macro(name arg1 arg2 ...)和endmacro()定义宏。
-  示例: cmakeCopy code macro(print_detail var) message("The value of ${var} is: ${${var}}") endmacro() set(VAR1 "Hello") print_detail(VAR1) # 输出 "The value of VAR1 is: Hello"
函数(Function):
-  函数创建自己的作用域,参数和内部变量在函数外不可见。 
-  使用 function(name arg1 arg2 ...)和endfunction()定义函数。
-  示例: cmakeCopy code function(print_sum a b) set(sum ${a} + ${b}) message("Sum: ${sum}") endfunction() print_sum(5 10) # 输出 "Sum: 5 + 10"
生成导入和导出配置
-  CMake可以生成导入和导出配置,使其他项目能够轻松使用库。 
-  使用 install(TARGETS ...)和export(TARGETS ...)命令来指定如何安装和导出库。
-  还可以使用 install(EXPORT ...)和export(EXPORT ...)管理复杂的依赖关系。
-  示例: cmakeCopy code add_library(mylib SHARED src.cpp) install(TARGETS mylib DESTINATION lib) install(EXPORT MyLibConfig DESTINATION share/MyLib/cmake) export(TARGETS mylib FILE MyLibConfig.cmake)
6 测试和安装:
设置和运行测试
CMake通过集成CTest提供了测试支持。以下是设置和运行测试的基本步骤:
-  启用测试: - 在**CMakeLists.txt文件顶部添加enable_testing()**命令,以启用测试功能。
 
- 在**
-  添加测试: -  使用** add_test(NAME test_name COMMAND test_executable)**添加测试。
-  ** test_name是你给测试起的名字,test_executable**是执行测试的可执行文件。
-  示例: cmakeCopy code add_executable(test_app test_app.cpp) add_test(NAME TestApp COMMAND test_app)
 
-  
-  运行测试: - 构建项目后,使用命令**ctest**在终端或命令行中运行测试。
- 可以添加参数来控制测试的运行方式,例如**ctest -V**以获得详细的输出。
 
- 构建项目后,使用命令**
安装构建的项目
CMake允许你定义安装规则,用于将构建的目标(可执行文件、库、头文件等)安装到适当的位置。
-  指定安装规则: -  使用** install()**命令指定应如何安装目标和文件。
-  常见的安装类型包括TARGETS、FILES和DIRECTORY。 
-  示例: cmakeCopy code # 安装可执行文件 install(TARGETS myapp DESTINATION bin) # 安装库 install(TARGETS mylib DESTINATION lib) # 安装头文件 install(FILES myheader.h DESTINATION include)
 
-  
-  生成安装包: - 如果需要,可以使用CPack(CMake的一个组件)来生成安装包。
- 在**CMakeLists.txt中包含include(CPack)**并设置CPack相关的配置。
 
-  执行安装: - 在构建项目后,使用命令**cmake --install .**安装项目。
- 可以指定一个安装前缀来控制安装位置,例如:cmake --install . --prefix "/path/to/install"。
 
- 在构建项目后,使用命令**
7 最佳实践和常见问题:
- 明确版本要求: 
  - 在**CMakeLists.txt的开始指定最低CMake版本,如cmake_minimum_required(VERSION 3.10)**。
 
- 在**
- 项目命名: 
  - 使用**project(ProjectName)**清晰地命名你的项目。
 
- 使用**
- 源文件管理: 
  - 将源文件组织在目录中,而不是在CMakeLists.txt中列出所有文件。
- 考虑使用**file(GLOB ...)或file(GLOB_RECURSE ...)**来自动收集源文件列表。
 
- 避免硬编码路径: 
  - 使用CMake变量而不是硬编码路径,如**${CMAKE_BINARY_DIR}、${PROJECT_SOURCE_DIR}**。
 
- 使用CMake变量而不是硬编码路径,如**
- 模块化和子目录: 
  - 对于较大的项目,使用**add_subdirectory()**将项目分解为多个模块。
 
- 对于较大的项目,使用**
- 使用变量和函数: 
  - 通过自定义函数和宏来避免重复代码。
 
- 优先使用target-specific命令: 
  - 使用**target_include_directories()和target_compile_definitions()**等,而不是全局命令。
 
- 使用**
- 设置合理的默认构建类型: 
  - 如果未指定,设置一个默认的构建类型,如**set(CMAKE_BUILD_TYPE Release)**。
 
- 如果未指定,设置一个默认的构建类型,如**
- 生成和使用导出的配置: 
  - 对于库,使用**install(EXPORT)和export()**来生成和使用可导入的目标。
 
- 对于库,使用**
- 保持向后兼容性: 
  - 确保更改向后兼容,特别是当你的项目被其他项目所依赖时。
 
解决常见的构建问题
- 依赖项查找失败: 
  - 确保所有依赖项已正确安装。
- 使用**find_package()**时,检查是否指定了正确的版本和路径。
 
- 不兼容的CMake版本: 
  - 更新CMake到项目要求的版本或调整**cmake_minimum_required**。
 
- 更新CMake到项目要求的版本或调整**
- 编译器和标志问题: 
  - 确保为项目指定了正确的编译器和编译器标志。
- 使用**target_compile_options()**为特定目标设置编译器标志。
 
- 跨平台构建问题: 
  - 使用条件语句处理不同平台的特定需求。
- 为不同平台的编译器和工具链创建适当的配置。
 
- 链接错误: 
  - 确保所有目标正确链接所需的库。
- 检查库是否以正确的顺序链接。
 
- 构建速度慢: 
  - 使用预编译头文件。
- 考虑使用分布式构建或ccache等工具。
 
8 案例研究和实例:
让我们通过一个简单的C++项目来展示CMake的使用。这个项目将包括一个主要的可执行文件和一个静态库。
项目结构
假设我们的项目结构如下所示:
scssCopy code
MyProject/
│
├── CMakeLists.txt  (项目根CMakeLists文件)
│
├── src/
│   ├── main.cpp    (主程序源文件)
│
└── lib/
    ├── MathFunctions.cpp  (库源文件)
    ├── MathFunctions.h    (库头文件)
    └── CMakeLists.txt  (库的CMakeLists文件)
根CMakeLists.txt
在项目的根目录下的**CMakeLists.txt**:
cmakeCopy code
cmake_minimum_required(VERSION 3.10)
project(MyProject)
# 指定C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# 添加子目录
add_subdirectory(lib)
# 包含头文件目录
include_directories(lib)
# 添加可执行文件
add_executable(myapp src/main.cpp)
# 链接库到可执行文件
target_link_libraries(myapp MathFunctions)
库的CMakeLists.txt
在**lib/目录下的CMakeLists.txt**:
cmakeCopy code
# 创建静态库
add_library(MathFunctions MathFunctions.cpp)
# 指定库的公共头文件
target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
分步讲解
- 根CMakeLists.txt: 
  - cmake_minimum_required(VERSION 3.10):指定CMake的最低版本。
- project(MyProject):定义项目名称。
- 设置C++标准:确保使用C++11。
- add_subdirectory(lib):添加子目录,CMake将查找该目录中的CMakeLists.txt。
- include_directories(lib):包含静态库的头文件目录。
- add_executable(myapp src/main.cpp):创建一个名为**- myapp**的可执行文件。
- target_link_libraries(myapp MathFunctions):将**- MathFunctions库链接到- myapp**可执行文件。
 
- 库的CMakeLists.txt: 
  - add_library(MathFunctions MathFunctions.cpp):创建一个名为**- MathFunctions**的静态库。
- target_include_directories:定义库的头文件目录,这样在项目的其他地方就可以找到这些头文件。
 
9 资源和进一步学习:
推荐书籍
- “Mastering CMake”: 
  - 作者:Ken Martin和Bill Hoffman。
- 详细介绍了CMake的高级特性和最佳实践,适合那些希望深入了解CMake的人。
 
- “Professional CMake: A Practical Guide”: 
  - 作者:Craig Scott。
- 一本面向中级到高级用户的实用指南,涵盖了CMake的许多高级主题。
 
- “CMake Cookbook”: 
  - 作者:Radovan Bast 和 Roberto Di Remigio。
- 提供了一系列具体的示例和配方,适合需要解决特定构建问题的开发者。
 
在线教程和其他资源
- CMake官方文档: 
  - 网址:CMake Official Documentation
- 提供了全面的参考材料,包括命令、模块和策略的详细描述。
 
- CMake教程: 
  - 网址:CMake Tutorial
- 官方教程,从基础知识到更复杂的主题逐步介绍。
 
- CMake FAQ: 
  - 网址:CMake FAQ
- 回答了一些常见的问题,对解决特定问题很有帮助。
 
- 在线课程和视频: 
  - 各大在线教育平台(如Udemy, Coursera, YouTube)上提供了关于CMake的课程和教学视频。
 
CMake社区和论坛
- CMake 论坛: 
  - 网址:CMake Discourse
- 社区成员经常在这里讨论问题、分享经验和提供帮助。
 
- Stack Overflow: 
  - 在Stack Overflow上,有许多关于CMake的问题和答案,适合搜索特定问题的解决方案。
 
- GitHub: 
  - 许多开源项目使用CMake,查看这些项目的CMakeLists文件可以提供实际使用案例和灵感。
 
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!