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:

  1. 下载: 访问CMake官方网站,下载适用于Windows的安装程序。
  2. 安装: 运行下载的安装程序,并按照指示完成安装。
  3. 添加到环境变量: 确保在安装过程中选择将CMake添加到系统路径。

Linux:

  1. 使用包管理器: 在大多数Linux发行版中,可以使用包管理器安装CMake。例如,在基于Debian的系统(如Ubuntu)上,可以使用以下命令:

    sqlCopy code
    sudo apt-get update
    sudo apt-get install cmake
    
    
  2. 手动安装: 如果需要最新版本,可以从CMake官网下载源代码包,然后编译安装。

macOS:

  1. 使用Homebrew: 如果安装了Homebrew,可以简单地运行以下命令:

    Copy code
    brew install cmake
    
    
  2. 手动安装: 访问CMake官方网站下载适用于macOS的安装包,并按照指示进行安装。

如何设置一个基本的CMake项目

  1. 创建项目目录:
    • 创建一个新目录作为项目的根目录。
    • 在此目录中创建源代码文件(如**main.cpp**)。
  2. 编写CMakeLists.txt文件:
    • 在项目根目录中创建一个名为**CMakeLists.txt**的文件。

    • 文件内容示例:

      cmakeCopy code
      cmake_minimum_required(VERSION 3.10) # 指定CMake最低版本要求
      project(MyProject) # 定义项目名称
      
      add_executable(myapp main.cpp) # 创建一个名为myapp的可执行文件
      
      
    • 这个文件定义了项目的基本信息和构建目标。

  3. 生成构建系统:
    • 打开终端或命令提示符。

    • 切换到项目根目录。

    • 运行以下命令来生成构建系统:

      Copy code
      cmake .
      
      
    • 这会在当前目录生成适用于你系统的构建文件(如Makefile)。

  4. 构建项目:
    • 在同一目录下运行构建命令,例如:

      cssCopy code
      cmake --build .
      
      
    • 这将编译源代码并生成可执行文件。

3 编写CMakeLists文件

CMakeLists.txt文件的结构和基本语法

CMakeLists.txt 文件是CMake构建系统的核心,它使用CMake专用的语法编写。以下是其基本结构和语法要点:

  1. 最低CMake版本:
    • 使用 cmake_minimum_required(VERSION minimum_version) 指定所需的最低CMake版本,这有助于确保构建过程的兼容性。
    • 例如: cmake_minimum_required(VERSION 3.10)
  2. 项目名称:
    • 使用 project(project_name) 定义项目名称。
    • 例如: project(MyProject)
  3. 设置变量:
    • 使用 set(VAR_NAME value) 来设置变量。
    • 例如: set(SOURCE_FILES main.cpp)
  4. 添加可执行文件或库:
    • 使用 add_executable(executable_name ${SOURCE_FILES})add_library(library_name ${SOURCE_FILES})
    • 例如: add_executable(myapp ${SOURCE_FILES})
  5. 包含目录和链接库:
    • 使用 include_directories(dir1 dir2 ...)target_link_libraries(target lib1 lib2 ...)
  6. 自定义指令:
    • CMakeLists.txt 支持条件语句(如 if, else, endif)和循环语句(如 foreach, while)。

添加源文件和头文件

  • 源文件:
    • 将源文件直接列在 add_executableadd_library 函数中,或者通过设置一个变量来引用。
    • 例如: add_executable(myapp main.cpp utility.cpp)
  • 头文件:
    • 通常,头文件不需要在CMakeLists.txt中明确列出。但如果头文件位于非标准目录,需要使用 include_directories(your_header_directory) 来包含这些目录。

设置编译器标志和定义

  • 编译器标志:
    • 使用 target_compile_options 来为特定目标设置编译器标志。
    • 例如: target_compile_options(myapp PRIVATE -Wall -Wextra)
  • 预处理器定义:
    • 使用 add_definitions(-DDEFINE) 来添加预处理器定义。
    • 例如: add_definitions(-DMY_DEFINE)

4 管理项目依赖

  1. 直接包含库文件:
    • 如果你有库的源代码或预编译文件(如**.lib.dll.so**等),可以直接将它们包含在项目中。
    • 使用 target_link_libraries(target_name path_to_library) 来链接库文件。
    • 使用 include_directories(path_to_header_files) 来包含库的头文件。
  2. 使用包管理器:
    • 对于一些流行的库,可以通过包管理器(如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})
    
    

添加子目录和使用外部项目

  1. 添加子目录:
    • 使用 add_subdirectory(subdir) 来添加包含另一个 CMakeLists.txt 文件的子目录。
    • 这对于模块化项目结构非常有用,允许在子目录中定义额外的构建目标。
  2. 使用外部项目(如git子模块):
    • 可以将外部项目作为git子模块或通过其他方式集成到项目中。
    • 使用 ExternalProject_Add 命令来下载、配置、构建和安装外部项目。
    • 这种方法适用于在构建时需要从源代码构建依赖项的场景。

5 创建可执行文件和库

使用 add_executableadd_library

add_executable:

  • 用于从指定的源文件创建一个可执行文件。
  • 基本语法: add_executable( ... )
  • 例如,创建一个名为 “app” 的可执行文件:add_executable(app main.cpp utility.cpp)

add_library:

  • 用于创建库(静态或动态)。
  • 基本语法: add_library( STATIC|SHARED|MODULE ... )
  • 例如,创建一个名为 “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()
    
    

循环语句:

  • 循环语句,如 foreachwhile,用于重复执行一组命令。

  • 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提供了测试支持。以下是设置和运行测试的基本步骤:

  1. 启用测试:

    • 在**CMakeLists.txt文件顶部添加enable_testing()**命令,以启用测试功能。
  2. 添加测试:

    • 使用**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)
      
      
  3. 运行测试:

    • 构建项目后,使用命令**ctest**在终端或命令行中运行测试。
    • 可以添加参数来控制测试的运行方式,例如**ctest -V**以获得详细的输出。

安装构建的项目

CMake允许你定义安装规则,用于将构建的目标(可执行文件、库、头文件等)安装到适当的位置。

  1. 指定安装规则:

    • 使用**install()**命令指定应如何安装目标和文件。

    • 常见的安装类型包括TARGETS、FILES和DIRECTORY。

    • 示例:

      cmakeCopy code
      # 安装可执行文件
      install(TARGETS myapp DESTINATION bin)
      
      # 安装库
      install(TARGETS mylib DESTINATION lib)
      
      # 安装头文件
      install(FILES myheader.h DESTINATION include)
      
      
  2. 生成安装包:

    • 如果需要,可以使用CPack(CMake的一个组件)来生成安装包。
    • 在**CMakeLists.txt中包含include(CPack)**并设置CPack相关的配置。
  3. 执行安装:

    • 在构建项目后,使用命令**cmake --install .**安装项目。
    • 可以指定一个安装前缀来控制安装位置,例如:cmake --install . --prefix "/path/to/install"

7 最佳实践和常见问题

  1. 明确版本要求
    • 在**CMakeLists.txt的开始指定最低CMake版本,如cmake_minimum_required(VERSION 3.10)**。
  2. 项目命名
    • 使用**project(ProjectName)**清晰地命名你的项目。
  3. 源文件管理
    • 将源文件组织在目录中,而不是在CMakeLists.txt中列出所有文件。
    • 考虑使用**file(GLOB ...)file(GLOB_RECURSE ...)**来自动收集源文件列表。
  4. 避免硬编码路径
    • 使用CMake变量而不是硬编码路径,如**${CMAKE_BINARY_DIR}${PROJECT_SOURCE_DIR}**。
  5. 模块化和子目录
    • 对于较大的项目,使用**add_subdirectory()**将项目分解为多个模块。
  6. 使用变量和函数
    • 通过自定义函数和宏来避免重复代码。
  7. 优先使用target-specific命令
    • 使用**target_include_directories()target_compile_definitions()**等,而不是全局命令。
  8. 设置合理的默认构建类型
    • 如果未指定,设置一个默认的构建类型,如**set(CMAKE_BUILD_TYPE Release)**。
  9. 生成和使用导出的配置
    • 对于库,使用**install(EXPORT)export()**来生成和使用可导入的目标。
  10. 保持向后兼容性
    • 确保更改向后兼容,特别是当你的项目被其他项目所依赖时。

解决常见的构建问题

  1. 依赖项查找失败
    • 确保所有依赖项已正确安装。
    • 使用**find_package()**时,检查是否指定了正确的版本和路径。
  2. 不兼容的CMake版本
    • 更新CMake到项目要求的版本或调整**cmake_minimum_required**。
  3. 编译器和标志问题
    • 确保为项目指定了正确的编译器和编译器标志。
    • 使用**target_compile_options()**为特定目标设置编译器标志。
  4. 跨平台构建问题
    • 使用条件语句处理不同平台的特定需求。
    • 为不同平台的编译器和工具链创建适当的配置。
  5. 链接错误
    • 确保所有目标正确链接所需的库。
    • 检查库是否以正确的顺序链接。
  6. 构建速度慢
    • 使用预编译头文件。
    • 考虑使用分布式构建或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})

分步讲解

  1. 根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**可执行文件。
  2. 库的CMakeLists.txt:
    • add_library(MathFunctions MathFunctions.cpp):创建一个名为**MathFunctions**的静态库。
    • target_include_directories:定义库的头文件目录,这样在项目的其他地方就可以找到这些头文件。

9 资源和进一步学习

推荐书籍

  1. “Mastering CMake”:
    • 作者:Ken Martin和Bill Hoffman。
    • 详细介绍了CMake的高级特性和最佳实践,适合那些希望深入了解CMake的人。
  2. “Professional CMake: A Practical Guide”:
    • 作者:Craig Scott。
    • 一本面向中级到高级用户的实用指南,涵盖了CMake的许多高级主题。
  3. “CMake Cookbook”:
    • 作者:Radovan Bast 和 Roberto Di Remigio。
    • 提供了一系列具体的示例和配方,适合需要解决特定构建问题的开发者。

在线教程和其他资源

  1. CMake官方文档:
    • 网址:CMake Official Documentation
    • 提供了全面的参考材料,包括命令、模块和策略的详细描述。
  2. CMake教程:
    • 网址:CMake Tutorial
    • 官方教程,从基础知识到更复杂的主题逐步介绍。
  3. CMake FAQ:
    • 网址:CMake FAQ
    • 回答了一些常见的问题,对解决特定问题很有帮助。
  4. 在线课程和视频:
    • 各大在线教育平台(如Udemy, Coursera, YouTube)上提供了关于CMake的课程和教学视频。

CMake社区和论坛

  1. CMake 论坛:
    • 网址:CMake Discourse
    • 社区成员经常在这里讨论问题、分享经验和提供帮助。
  2. Stack Overflow:
    • 在Stack Overflow上,有许多关于CMake的问题和答案,适合搜索特定问题的解决方案。
  3. GitHub:
    • 许多开源项目使用CMake,查看这些项目的CMakeLists文件可以提供实际使用案例和灵感。

你可能感兴趣的:(高效工具,vim)