CMake实用知识点之二

1 CMP0003

该策略与链接指令的相对路径解析有关,就是在链接共享库时如何解释相对路径。

背景介绍

  • OLD 行为: 在旧行为下,相对路径被直接传递给链接器。这可能会导致在构建和运行时出现未定义的行为,因为相对路径是相对于构建目录解释的,而不是相对于目标文件的目录。
  • NEW 行为: 在新行为下,CMake 将相对路径转换为绝对路径,确保链接器始终能够正确找到共享库。

使用方法

通过设置cmake_policy(SET CMP0003 NEW),告诉CMake使用新行为处理相对路径。这通常是更安全和可靠的选择,尤其是在复杂的项目中。

示例代码

add_executable(MyExecutable main.cpp)
target_link_libraries(MyExecutable ../lib/mylib)

如果不设置 CMP0003 策略,CMake 可能会将 ../lib/mylib 作为相对路径传递给链接器。在一些情况下,这可能会导致链接失败或者运行时找不到库的问题。而通过设置 cmake_policy(SET CMP0003 NEW),CMake 会将 ../lib/mylib 转换为绝对路径,例如 /path/to/project/lib/mylib,这样可以确保链接器正确找到库文件。

# CMake会解析命令cmake_policy是否存在,若存在返回true,否则返回false
# 注意:这是命令函数
if(COMMAND cmake_policy)
  cmake_policy(SET CMP0003 NEW)
endif()

2 ccache

set(CMAKE_C_COMPILER "ccache") 在 CMake 脚本中将 C 编译器设置为 ccache。不过,通常这是不对的,因为 ccache 不是一个编译器,而是一个编译器缓存工具,需要编译器来加载这个缓存工具。ccache 是一个用于加速 C/C++ 编译的工具,通过缓存编译结果,当相同的代码再次编译时,可以直接从缓存中获取结果,而不需要实际调用编译器。

要正确地使用 ccache 与 CMake,需要设置 C 和 C++ 编译器来使用 ccache 包装实际的编译器,如 gcc 或 clang。下面是一个示例,展示了如何在 CMake 中正确地使用 ccache:

set(CMAKE_C_COMPILER_LAUNCHER ccache)
set(CMAKE_CXX_COMPILER_LAUNCHER ccache)

这个配置告诉 CMake 在调用实际的 C 和 C++ 编译器之前,使用 ccache。

cmake_minimum_required(VERSION 3.0)
project(MyProject)

# 使用 ccache 作为编译器缓存工具
set(CMAKE_C_COMPILER_LAUNCHER ccache)
set(CMAKE_CXX_COMPILER_LAUNCHER ccache)

add_executable(MyExecutable main.cpp)

通过这种方式,可以确保每次构建都会使用 ccache,从而加快重复构建的速度。

  1. 安装 ccache: sudo apt-get install ccache

  2. 配置环境变量(可选): 如果希望 ccache 始终生效,可以在你的 shell 配置文件中添加以下内容:

     export PATH="/usr/lib/ccache:$PATH"
    
  3. 运行 CMake 构建:

     mkdir build
     cd build
     cmake ..
     make
    

通过这种方式,ccache 会被自动使用,帮助加速编译过程.

3 string

在 CMake 中,string 命令用于处理和操作字符串。它提供了多种功能,可以用于字符串的操作,如查找子字符串、替换子字符串、转换大小写等。以下是一些常见的 string 命令及其用法:

常见用法

  • 字符串长度

    string(LENGTH "Hello, World!" len)
    message(STATUS "The length of the string is: ${len}")

  • 子字符串提取

    string(SUBSTRING "Hello, World!" 7 5 substr)
    message(STATUS "The substring is: ${substr}")

  • 查找子字符串

    string(FIND "Hello, World!" "World" pos)
    message(STATUS "The position of 'World' is: ${pos}")

  • 字符串替换

    将“Hello, World”字符串中的World替换成CMake

    string(REPLACE "World" "CMake" new_string "Hello, World!")
    message(STATUS "The new string is: ${new_string}")

  • 字符串比较

    string(COMPARE EQUAL "foo" "foo" result)
    if (result)
    message(STATUS "Strings are equal")
    else()
    message(STATUS "Strings are not equal")
    endif()

  • 转换大小写

    string(TOUPPER "Hello, World!" upper)
    message(STATUS "Uppercase: ${upper}")

    string(TOLOWER "Hello, World!" lower)
    message(STATUS "Lowercase: ${lower}")

  • 字符串拼接

    set(str1 "Hello")
    set(str2 "World")
    set(full_str "${str1}, ${str2}!")
    message(STATUS "The concatenated string is: ${full_str}")

实际示例

假设有一个项目,需要根据某些条件动态生成一些文件名。可以使用 string 命令来处理这些字符串。

cmake_minimum_required(VERSION 3.0)
project(MyProject)

# 提取文件名的基础部分
string(REGEX REPLACE "(.+)\\..*" "\\1" base_name "example.txt")
message(STATUS "Base name: ${base_name}")


SET( MARS_VERSION 2.7.1.240708_rc )
# 将每一个段连续的数字都读取出来,并放在变量MARS_VERSIONS中,且以数组的方式存放
STRING( REGEX MATCHALL "[0-9]+" MARS_VERSIONS ${MARS_VERSION} ) 
LIST( GET MARS_VERSIONS 0 MARS_VERSION_MAJOR) # 
LIST( GET MARS_VERSIONS 1 MARS_VERSION_MINOR)
LIST( GET MARS_VERSIONS 2 MARS_VERSION_PATCH)

# 将文件名转换为大写
string(TOUPPER "${base_name}" upper_base_name)
message(STATUS "Uppercase base name: ${upper_base_name}")

# 生成新的文件名
set(new_file "${upper_base_name}.log")
message(STATUS "New file name: ${new_file}")

在这个示例中,首先提取文件名的基础部分,然后将其转换为大写,最后生成一个新的文件名。

总结

CMake 中的 string 命令非常强大,适用于各种字符串操作和处理任务。这些功能在构建复杂项目时尤其有用,可以帮助你动态生成配置、处理文件路径和名称等。掌握这些命令将使你的 CMake 脚本更加灵活和强大。

4 list

在 CMake 中,list 命令用于操作 CMake 列表。CMake 列表是以分号分隔的一系列字符串,它们可以用来存储和管理多个值。list 命令提供了一组函数来操作这些列表,包括添加、删除、查找、替换和排序列表项等。以下是一些常见的 list 命令及其用法示例:

4.1 常见用法

  1. 创建和初始化列表

set(MY_LIST "item1;item2;item3")
或者通过追加的方式:

list(APPEND MY_LIST "item1" "item2" "item3")

  1. 添加元素: list(APPEND MY_LIST "item4" "item5")

  2. 插入元素:在指定位置插入元素:list(INSERT MY_LIST 1 "new_item"),在索引 1 位置插入 new_item。

  3. 删除元素:

    • 根据索引删除元素:list(REMOVE_AT MY_LIST 1),删除索引 1 位置的元素。
    • 根据值删除元素:list(REMOVE_ITEM MY_LIST "item3"),删除列表中所有等于 item3 的元素。
  4. 查找元素:list(FIND MY_LIST "item2" INDEX)
    查找 item2 在列表中的索引,并存储在变量 INDEX 中。如果未找到,INDEX 的值将为 -1。

  5. 获取列表长度:

    • list(LENGTH MY_LIST LENGTH), 获取列表的长度,并存储在变量 LENGTH 中。
    • list(GET MY_LIST 0 FIRST_ITEM), 获取索引 0 位置的元素,并存储在变量 FIRST_ITEM 中。
  6. 合并两个列表:list(APPEND LIST1 ${LIST2}) 将 LIST2 的元素追加到 LIST1 中。

  7. 分割字符串为列表: string(REPLACE ";" ";" MY_LIST "item1;item2;item3"), 这会将字符串 "item1;item2;item3" 转换为列表 MY_LIST。

  8. 连接列表为字符串: list(JOIN MY_LIST "," JOINED_STRING),这会将 MY_LIST 中的元素使用逗号连接成一个字符串,并存储在变量 JOINED_STRING 中。

  9. 获取列表元素: list(GET MARS_VERSION 0 MARS_VERSION_MAJOR), 将列表中的第一个元素取出来放到变量MARS_VERSION_MAJOR中

4.2 示例

以下是一个完整的示例,展示了如何在 CMake 中使用 list 命令来操作列表

	cmake_minimum_required(VERSION 3.0)
	project(ListExample)

	# 创建和初始化列表
	set(MY_LIST "item1;item2;item3")

	# 添加元素
	list(APPEND MY_LIST "item4" "item5")

	# 插入元素
	list(INSERT MY_LIST 1 "new_item")

	# 删除元素
	list(REMOVE_AT MY_LIST 2)
	list(REMOVE_ITEM MY_LIST "item5")

	# 查找元素
	list(FIND MY_LIST "item3" INDEX)
	message(STATUS "Index of item3: ${INDEX}")

	# 获取列表长度
	list(LENGTH MY_LIST LENGTH)
	message(STATUS "Length of MY_LIST: ${LENGTH}")

	# 获取列表元素
	list(GET MY_LIST 0 FIRST_ITEM)
	message(STATUS "First item: ${FIRST_ITEM}")

	# 合并两个列表
	set(OTHER_LIST "other1;other2")
	list(APPEND MY_LIST ${OTHER_LIST})

	# 连接列表为字符串
	list(JOIN MY_LIST "," JOINED_STRING)
	message(STATUS "Joined string: ${JOINED_STRING}")

4.3 总结

在 CMake 中,list 命令是处理和操作列表的强大工具。它可以用来创建、修改和查询列表,方便管理和处理多个值。在构建系统中,列表操作非常常见,因此掌握这些命令将有助于编写更加灵活和高效的 CMake 脚本。

5 include

在 CMake 中,include 命令的作用是引入和执行其他 CMake 脚本文件。这允许你将CMake配置拆分成多个文件,以提高可读性和可维护性。include 命令可以加载指定的CMake文件,并在当前作用域内执行其中的命令。

OPTIONAL: 如是没有该文件,也不会报警。更加灵活

include(<filename>)

<filename> 是要包含的 CMake 文件的路径。如果文件不存在,CMake 会发出警告。

  • 假如有如下目录结构:

      project/
      ├── CMakeLists.txt
      ├── src/
      │   └── CMakeLists.txt
      └── cmake/
      		├── CommonSettings.cmake
      		└── CompilerOptions.cmake
    
  • CMakeLists.txt如下:

      cmake_minimum_required(VERSION 3.10)
      project(MyProject)
    
      # 包含通用设置
      include(cmake/CommonSettings.cmake)
    
      # 包含编译器选项
      include(cmake/CompilerOptions.cmake)
    
      # 添加子目录
      add_subdirectory(src)
    
  • CommonSettings.cmake
    在 cmake/CommonSettings.cmake 中,你可以定义一些通用设置,例如:

      # 设置一些通用编译选项
      set(CMAKE_CXX_STANDARD 17)
      set(CMAKE_CXX_STANDARD_REQUIRED ON)
      set(CMAKE_CXX_EXTENSIONS OFF)
    
  • CompilerOptions.cmake
    在 cmake/CompilerOptions.cmake 中,你可以定义一些与编译器相关的选项,例如:

      # 设置编译器选项
      if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
      		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
      elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
      		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
      endif()
    
  • 条件包含

    还可以使用条件语句来有选择地包含文件:

      if (EXISTS "${CMAKE_SOURCE_DIR}/cmake/OptionalSettings.cmake")
      		include(cmake/OptionalSettings.cmake)
      endif()
    
  • include_guard

    为了防止多次包含同一个文件(类似于 C/C++ 中的 include guard),你可以使用 include_guard 命令:

      include_guard(GLOBAL)
    
  • 总结

    include 命令在 CMake 中的主要作用是允许你将构建配置分散到多个文件中,从而提高配置的可维护性和可读性。通过将常见的设置和选项拆分到单独的文件中,你可以更轻松地管理和复用这些配置。

6 CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT

CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT是一个CMake的内部变量,‌它表示CMAKE_INSTALL_PREFIX被初始化为默认值。‌这个变量通常在第一次运行CMake时设置,‌当在新的构建树中运行CMake且CMAKE_INSTALL_PREFIX环境变量未设置时,‌CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT会被设置为true。

‌这个变量的主要作用是提供一个默认的prefix值,‌同时保留从命令行覆盖这个默认值的能力。‌这意味着,‌如果用户想要改变安装的前缀路径,‌就可以通过命令行选项来实现,‌而不需要手动修改CMakeLists.txt文件中的设置。‌这种机制使得CMake更加灵活和用户友好,‌允许用户在构建和安装过程中根据需要调整安装路径。

7 if(MSVC)

在CMake中,‌使用if(MSVC)是判断是否使用的是Microsoft Visual Studio的编译器。‌

当你在CMake脚本中使用if(MSVC)时,‌你正在检查当前编译器是否为Microsoft Visual Studio的编译器。‌如果条件为真,‌即当前使用的是Microsoft Visual Studio编译器,‌那么CMake会执行if(MSVC)块内的命令。‌这种检查通常用于根据编译器的不同调整构建配置,‌例如设置特定的编译标志或选择不同的源代码路径。‌

此外,‌CMake还提供了其他方法来检测编译器的具体类型和版本,‌例如通过检查CMAKE_CXX_COMPILER_ID变量的值来确定是否使用的是MSVC或其他类型的编译器。‌例如,‌如果使用的是Microsoft Visual Studio编译器,‌CMAKE_CXX_COMPILER_ID的值将为"MSVC"。‌而如果是使用Mingw编译器,‌其值将为"GNU"12。‌

总的来说,‌if(MSVC)在CMake中用于特定于Microsoft Visual Studio编译器的条件判断,‌允许开发者根据编译器的不同进行相应的配置调整。‌

8 set

set(<variable> <value> [[CACHE <type> <docstring> [FORCE]] | PARENT_SCOPE])

<type>的值可以是以下值之一

FILEPATH = File chooser dialog.
PATH     = Directory chooser dialog.
STRING   = Arbitrary string.
BOOL     = Boolean ON/OFF checkbox.
INTERNAL = No GUI entry (used for persistent variables).

9 CMAKE_BUILD_TYPE

它可以有以下四种情况Debug, Release,RelWithDebInfo, 或 MinSizeRel

在 CMake 中,CMAKE_BUILD_TYPE 变量用于指定构建类型,它决定了编译器和链接器的设置。这些设置通常包括优化级别、调试信息、以及其他相关的编译选项。以下是 CMake 中常见的四种构建类型及其区别:

  1. Debug
  • 特点:包含调试信息,禁用优化。
  • 编译器选项:
    • GCC/Clang:-g
    • MSVC:/Zi
  • 用途:用于开发和调试。包含详细的调试信息,方便使用调试器(如 gdb、lldb)进行代码调试。
  1. Release
  • 特点:启用优化,不包含调试信息。
  • 编译器选项:
    • GCC/Clang:-O3 或 -O2(具体取决于 CMake 版本和配置)
    • MSVC:/O2
  • 用途:用于发布。生成的二进制文件运行速度更快,文件尺寸更小,但不包含调试信息,调试难度较大。
  1. RelWithDebInfo (Release with Debug Information)
  • 特点:启用优化,包含调试信息。
  • 编译器选项:
    • GCC/Clang:-O2 -g
    • MSVC:/O2 /Zi
  • 用途:用于需要调试优化后的代码场合。提供了优化后的性能,同时保留了调试信息,便于调试优化后的代码。
  1. MinSizeRel (Minimum Size Release)
  • 特点:优化以减小生成的二进制文件大小,不包含调试信息。
  • 编译器选项:
    • GCC/Clang:-Os
    • MSVC:/O1
  • 用途:用于嵌入式系统或存储受限的环境。目标是生成尽可能小的二进制文件,同时尽量保留一定的性能。

示例:假设我有一个简单的 CMake 项目,以下是如何指定不同的构建类型:

# 生成 Debug 构建
cmake -DCMAKE_BUILD_TYPE=Debug ..

# 生成 Release 构建
cmake -DCMAKE_BUILD_TYPE=Release ..

# 生成 RelWithDebInfo 构建
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..

# 生成 MinSizeRel 构建
cmake -DCMAKE_BUILD_TYPE=MinSizeRel ..

总结

  • Debug:用于开发和调试,包含调试信息,禁用优化。
  • Release:用于发布,启用优化,不包含调试信息。
  • RelWithDebInfo:用于需要调试优化后的代码,启用优化,并包含调试信息。
  • MinSizeRel:用于存储受限的环境,启用优化以减小文件大小,不包含调试信息。

这些构建类型帮助开发人员根据不同的需求(如开发调试、发布优化、调试优化代码、减小文件大小)生成合适的二进制文件。

10 CMAKE_CXX_FLAGS

在 CMake 中,CMAKE_CXX_FLAGSCMAKE_CXX_FLAGS_<CONFIG> 用于设置编译器的标志,但它们的作用范围不同:

  • CMAKE_CXX_FLAGS:这是一个全局变量,包含所有构建类型通用的编译器标志。无论你选择什么构建类型(Debug、Release、RelWithDebInfo、MinSizeRel),这些标志都会应用。

  • CMAKE_CXX_FLAGS_<CONFIG>:这些是特定于某个构建类型的编译器标志。<CONFIG> 可以是 DEBUG、RELEASE、RELWITHDEBINFO、MINSIZEREL 等。只有在对应的构建类型被选中时,这些标志才会应用。

      cmake_minimum_required(VERSION 3.10)
      project(MyProject)
    
      # 设置全局的编译器标志
      set(CMAKE_CXX_FLAGS "-Wall -Wextra")
    
      # 设置特定构建类型的编译器标志
      set(CMAKE_CXX_FLAGS_DEBUG "-g")
      set(CMAKE_CXX_FLAGS_RELEASE "-O3")
      set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
      set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os")
    

解释

  • CMAKE_CXX_FLAGS-Wall -Wextra, 这些标志会在所有构建类型中应用。

  • -DBOOST_FUSION_INVOKE_MAX_ARITY=13

      SET ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -DBOOST_FUSION_INVOKE_MAX_ARITY=13 -DRCF_MAX_METHOD_COUNT=400" )
    

    -DBOOST_FUSION_INVOKE_MAX_ARITY=13 是一个编译器选项,用于设置 Boost.Fusion 库中的宏 BOOST_FUSION_INVOKE_MAX_ARITY 的值。

    详细解释 Boost.Fusion 是 Boost 库的一部分,提供了一组用于处理异构数据结构的模板库。Boost.Fusion 中的一些功能需要处理不同数量的参数,而这些参数的最大数量可以通过设置宏 BOOST_FUSION_INVOKE_MAX_ARITY 来控制。

    BOOST_FUSION_INVOKE_MAX_ARITY 定义:它是一个宏,用于指定 Boost.Fusion 库中函数调用(invoke)的最大参数数量。

    默认值:这个值在默认情况下可能设置为某个固定值(如 10),但可以根据需要进行调整。

    作用:增加这个值可以允许 Boost.Fusion 处理更多参数的函数调用,但这也会增加编译时间和内存使用量。

    用法示例: 假设我在编译一个使用 Boost.Fusion 的项目,并且需要处理多达 13 个参数的函数调用。我可以通过以下方式设置这个值:

    在命令行中设置:

      cmake -DBOOST_FUSION_INVOKE_MAX_ARITY=13 ..
    

    在 CMakeLists.txt 中设置

      add_definitions(-DBOOST_FUSION_INVOKE_MAX_ARITY=13) 
    

    在源代码中设置 我也可以在源代码中定义这个宏,但这种方式需要在包含任何 Boost.Fusion 头文件之前进行定义:

    cpp代码如下:

      #define BOOST_FUSION_INVOKE_MAX_ARITY 13 
      #include <boost/fusion/include/invoke.hpp> 
    

    具体示例 假设我有一个使用 Boost.Fusion 的函数,需要处理 13 个参数:

    cpp代码如下:

      #include <boost/fusion/include/invoke.hpp> 
      #include <boost/fusion/tuple.hpp> #include
    
      void my_function(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m) { 
      		std::cout << "Sum: " << (a + b + c + d + e + f + g + h + i + j + k + l + m) << std::endl; 
      }
    
      int main() { 
      	#define BOOST_FUSION_INVOKE_MAX_ARITY 13 
      	boost::fusion::tuple<int, int, int, int, int, int, int, int, int, int, int, int, int> args(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); 
      	boost::fusion::invoke(&my_function, args); 
      	return 0; 
      } 
    

    在这个例子中,我定义了一个接受 13 个参数的函数 my_function,并使用 Boost.Fusion 的 invoke 函数调用它。通过设置 BOOST_FUSION_INVOKE_MAX_ARITY 为 13,我们确保 Boost.Fusion 可以处理这个函数调用。

    总结

    -DBOOST_FUSION_INVOKE_MAX_ARITY=13 是一个编译选项,用于设置 Boost.Fusion 库中处理函数调用的最大参数数量。通过增加这个值,可以让 Boost.Fusion 处理更多参数的函数调用,但这也可能增加编译时间和内存使用量。这个设置在需要处理多参数函数调用的情况下非常有用。

11 定义预处理宏-D

CMakeLists.txt 文件中,设置 CMAKE_CXX_FLAGS-DRCF_MAX_METHOD_COUNT=400 的作用如下:

  • 定义预处理器宏
    -D 选项用于定义一个预处理器宏。在这种情况下,RCF_MAX_METHOD_COUNT 被定义为 400。这意味着在编译代码时,所有引用 RCF_MAX_METHOD_COUNT 的地方都会被替换为 400

  • 影响编译行为
    RCF_MAX_METHOD_COUNT 这个宏的具体作用取决于它在代码中的使用方式。通常,这样的宏定义用于控制代码中的某些配置参数。例如,它可能用于限制或设置某个特定功能的最大值或数量。在这个例子中,RCF_MAX_METHOD_COUNT 可能用于限制某个框架(可能是 Remote Call Framework, RCF)的最大方法数量为 400。

  • 代码可读性和可维护性
    通过在 CMakeLists.txt 中设置预处理器宏,可以使得配置参数更加集中和统一管理。这样,当需要更改这些参数时,只需修改一个地方,而不是在多个文件中手动查找和替换。

具体作用还需要参考项目的相关文档或者代码实现,来看RCF_MAX_METHOD_COUNT 在代码中具体如何使用。例如,它可能用于控制某个数组的大小、某些数据结构的容量、某个循环的次数等等。

12 测试框架CTest

在 CMake 中使用 include(CTest)enable_testing() 有助于为项目配置和启用测试功能。具体作用如下:

include(CTest)
  • 引入CTest模块: CTest 是 CMake 自带的测试框架模块,通过 include(CTest) 可以引入该模块。CTest 提供了运行和管理测试的功能。

  • 定义测试目标: 通过引入 CTest 模块,你可以使用 CTest 提供的命令(如 add_test())来定义测试目标。

  • 支持持续集成: 通过引入 CTest 模块,可以更容易地集成到持续集成系统中,如 Jenkins、GitHub Actions 等。

    enable_testing()

  • 启用测试: 该命令在项目中启用测试功能。它会生成必要的测试文件(如 CTestTestfile.cmake),并设置测试的基本配置。

  • 允许定义测试: 在调用 enable_testing() 之后,你可以使用 CMake 提供的 add_test() 命令来添加测试。这些测试可以是可执行文件、脚本或其他可以被执行的程序。

  • 支持运行测试: 启用测试后,你可以使用 ctest 命令来运行项目中的测试。例如,运行 ctest 命令可以执行所有定义的测试并报告结果。

下面是一个简化的示例 CMakeLists.txt,展示了如何使用 include(CTest) 和 enable_testing():

cmake_minimum_required(VERSION 3.10)
project(MyProject)

# 引入CTest模块
include(CTest)

# 启用测试
enable_testing()

# 添加可执行文件
add_executable(my_program main.cpp)

# 添加测试,NAME是MyTest,运行的可执行文件是my_program
add_test(NAME MyTest COMMAND my_program)

在这个示例中:

  • include(CTest): 引入了 CTest 模块,使得我可以使用 add_test() 命令。
  • enable_testing(): 启用了测试功能。
  • add_test(NAME MyTest COMMAND my_program): 定义了一个名为 MyTest 的测试,该测试会运行 my_program 可执行文件。
    通过这种方式,我可以轻松地在 CMake 项目中配置和管理测试,从而确保项目的质量和可靠性。

13 CMakeDependentOption函数

在 CMake 中,include(CMakeDependentOption) 的主要作用是引入 CMakeDependentOption 模块,以便在 CMakeLists.txt 中使用 CMakeDependentOption() 函数。这些函数可以创建相互依赖的选项(即配置选项),根据某些条件来决定是否启用某个选项。这样可以更加灵活地管理项目配置。

具体来说,CMakeDependentOption() 函数可以根据其他选项的值来自动设置当前选项的默认值,并可以在条件不满足时隐藏该选项。这样可以避免用户在配置项目时混淆或误设无效的选项。

13.1 使用 include(CMakeDependentOption) 的步骤:

  1. 引入模块:在 CMakeLists.txt 文件中引入 CMakeDependentOption 模块。

    include(CMakeDependentOption)

  2. 定义依赖选项:使用 CMakeDependentOption() 定义依赖关系

    CMakeDependentOption(
    OPTION_NAME # 选项名称
    "Description of the option" # 选项描述
    ON # 默认值
    "DEPENDENT_OPTION" # 依赖选项或条件
    "CONDITION" # 条件
    )

13.2 示例

假设有一个项目,它有一个主选项 ENABLE_FEATURE,以及一个依赖于它的子选项 ENABLE_SUBFEATURE。我可以如下配置:

cmake_minimum_required(VERSION 3.0)

project(ExampleProject)

include(CMakeDependentOption)

option(ENABLE_FEATURE "Enable the main feature" ON)

CMakeDependentOption(
		ENABLE_SUBFEATURE            # 子选项名称
		"Enable the sub-feature"     # 子选项描述
		ON                           # 默认值
		"ENABLE_FEATURE"             # 依赖选项
		"ENABLE_FEATURE"             # 条件,这个条件如何理解
)

if (ENABLE_FEATURE)
		message(STATUS "Main feature is enabled.")
		if (ENABLE_SUBFEATURE)
				message(STATUS "Sub-feature is enabled.")
		else()
				message(STATUS "Sub-feature is disabled.")
		endif()
else()
		message(STATUS "Main feature is disabled.")
endif()

在这个例子中,如果 ENABLE_FEATURE 被禁用(OFF),则ENABLE_SUBFEATURE也会被禁用,且用户在 CMake GUI 或命令行中看不到 ENABLE_SUBFEATURE 选项。这种依赖关系使得配置选项更加直观和便于管理。

14 CMAKE_ROOT

  • CMAKE_ROOT: /usr/share/cmake-3.5

15 自动检测C++编译器

INCLUDE (${CMAKE_ROOT}/Modules/CMakeDetermineCXXCompiler.cmake) 是 CMake 中用于确定 C++ 编译器的模块。这个命令在配置 CMake 项目时有助于自动检测和设置适当的 C++ 编译器,使得构建过程更加简化和自动化。

15.1 作用和功能

自动检测 C++ 编译器:CMakeDetermineCXXCompiler.cmake 模块会搜索系统中可用的 C++ 编译器,并选择一个适合的编译器。例如,它会检查 g++、clang++ 等常见的 C++ 编译器。

设置相关变量:该模块会设置一些关键的 CMake 变量,如 CMAKE_CXX_COMPILER,这些变量在后续的配置和构建过程中会被使用。

检查编译器有效性:它会验证找到的编译器是否可用,确保所选的编译器能够正确编译和链接 C++ 代码

  • 编译器GCC本身包含多个编译器,(如:gcc用于Cg++用于C++),负责将源代码转换为目标代码(中间的机器码)

15.2 使用场景

在一些自定义的 CMake 配置文件或项目中,可能需要显式包含这个模块来确保编译器的检测和设置。这在某些特殊的构建环境或复杂的项目中可能特别有用。

15.2.1 示例

以下是一个示例,说明如何在自定义的 CMake 脚本中使用 CMakeDetermineCXXCompiler.cmake 模块:

cmake_minimum_required(VERSION 3.0)

# 指定项目名称和语言
project(ExampleProject CXX)

# 显式包含 CMakeDetermineCXXCompiler 模块
INCLUDE(${CMAKE_ROOT}/Modules/CMakeDetermineCXXCompiler.cmake)

# 其他项目配置和设置
set(SOURCE_FILES main.cpp)
add_executable(ExampleExecutable ${SOURCE_FILES})

# 输出所选择的编译器
message(STATUS "C++ Compiler: ${CMAKE_CXX_COMPILER}")

在这个示例中,INCLUDE(${CMAKE_ROOT}/Modules/CMakeDetermineCXXCompiler.cmake) 会自动检测系统上的 C++ 编译器,并设置相关变量。然后,我们可以使用这些变量进行进一步的配置和构建过程。

15.3 总结

INCLUDE(${CMAKE_ROOT}/Modules/CMakeDetermineCXXCompiler.cmake) 在 CMake 中主要用于自动检测和设置 C++ 编译器,确保项目能够在不同的构建环境中正确编译和链接。这对提高构建过程的自动化和可移植性非常有帮助。

16GCC

GCC(GNU Compiler Collection)不仅是编译器,也是链接器。GCC 充当了一个集成工具,调用了不同的程序来完成每个阶段的工作。

16.1 编译器和链接器

编译器:GCC 本身包含了多个编译器(如 gcc 用于 C,g++ 用于 C++),负责将源代码转换为目标代码(中间的机器代码)。

链接器:GCC 调用了链接器(通常是 ld),负责将多个目标文件和库文件链接在一起,生成可执行文件。

16.2 GCC 的角色

作为编译器驱动程序:GCC 根据命令行参数自动决定调用预处理器编译器汇编器链接器的顺序。我只需使用一个命令,它就会自动完成整个编译和链接过程。

作为链接器的接口:在链接阶段,GCC 充当接口,将目标文件和库文件传递给链接器(通常是 ld)以生成可执行文件

posted @ 2024-07-19 21:15  绍荣  阅读(47)  评论(0编辑  收藏  举报