CMake 工具链文件 - 设置 CMAKE_CXX_FLAGS

Irb*_*bis 7 c++ cmake

我在工具链文件中看到了以下设置 CMAKE_CXX_FLAGS 的方法:

SET(CMAKE_CXX_FLAGS "-m32" CACHE STRING "C++ compiler flags" FORCE)
Run Code Online (Sandbox Code Playgroud)

我应该在工具链文件中使用它而不是

SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")
Run Code Online (Sandbox Code Playgroud)

它们之间有什么区别?

Ale*_*ing 7

tl;dr:有两种可接受的方法可以做到这一点。

首先,大多数时候(90%+),您可以按照文档的建议_INIT使用变量:

set(CMAKE_CXX_FLAGS_INIT "-m32")
Run Code Online (Sandbox Code Playgroud)

其次,如果 CMake 为您的编译器/平台组合添加了不正确/冲突的标志,您可以通过设置不带 FORCE.

set(CMAKE_CXX_FLAGS "-m32" CACHE STRING "C++ compiler flags")
Run Code Online (Sandbox Code Playgroud)

请继续阅读以了解更多详细信息。


让我们进行一些实验。我们将使用以下 CMakeLists.txt:

cmake_minimum_required(VERSION 3.23)
project(test LANGUAGES CXX)

message(STATUS "CMAKE_CXX_FLAGS_DEBUG = ${CMAKE_CXX_FLAGS_DEBUG}")
Run Code Online (Sandbox Code Playgroud)

在大多数系统上,CMakeCMAKE_CXX_FLAGS默认留空。主要的例外是带有 MSVC 的 Windows,它添加​​了/EHsc和 (在旧版本上)/GR以确保启用标准 C++ 异常处理和 RTTI。

由于我无法立即访问 Windows 系统,因此我使用CMAKE_CXX_FLAGS_DEBUG,它在大多数编译器上都有默认初始化标志。不过,同样的原则适用,因为在这两种情况下设置这些都是平台模块的责任。

实验1:无工具链文件

$ cmake -S . -B build
-- The CXX compiler identification is GNU 11.2.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- CMAKE_CXX_FLAGS_DEBUG = -g
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/build
Run Code Online (Sandbox Code Playgroud)

所以在这个编译器上,CMAKE_CXX_FLAGS_DEBUG设置为-g. 这是我们的基线。

实验2:强制设置缓存

现在我们将创建一个名为的工具链文件set-cache-force.cmake

# set-cache-force.cmake
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG" CACHE STRING "C++ compiler flags" FORCE)
Run Code Online (Sandbox Code Playgroud)

我们将使用此工具链配置项目:

$ rm -rf build
$ cmake -S . -B build --toolchain set-cache-force.cmake 
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
Run Code Online (Sandbox Code Playgroud)

正如我们所看到的,原始-g标志被抑制,-DMY_DEBUG缓存值“获胜”。当然,这不再是真正的调试模式,这应该说明为什么覆盖所有标志并不总是我们想要的。

更糟糕的是,FORCE在这里使用会禁用用户覆盖CMAKE_CXX_FLAGS_DEBUG自己的能力:


$ rm -rf build
$ cmake -S . -B build --toolchain set-cache-force.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
Run Code Online (Sandbox Code Playgroud)

这是非常不受欢迎的行为。用户需要编辑您的工具链文件来解决错误或添加进一步的自定义。

实验3:不强制设置缓存

如果我们运行与以前相同的实验而不FORCE进行设置,那么我们仍然会得到相同的标志,但我们保留增量覆盖工具链文件的能力。

# set-cache.cmake
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG" CACHE STRING "C++ compiler flags")
Run Code Online (Sandbox Code Playgroud)

现在我们可以看到它有效:

$ rm -rf build
$ cmake -S . -B build --toolchain set-cache.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
Run Code Online (Sandbox Code Playgroud)

并且它仍然可以被覆盖:

$ rm -rf build
$ cmake -S . -B build --toolchain set-cache.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE
...
Run Code Online (Sandbox Code Playgroud)

它甚至可以再次被覆盖:

$ cmake -S . -B build -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE2"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE2
...
Run Code Online (Sandbox Code Playgroud)

实验4:设置正态变量

现在我们尝试将其设置为普通变量。再次,我们将创建一个名为的工具链文件set-normal.cmake

# set-normal.cmake
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG")
Run Code Online (Sandbox Code Playgroud)

再次,运行此命令显示-DMY_DEBUG“获胜”,覆盖 CMake 的默认标志:

$ cmake -S . -B build --toolchain set-normal.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
Run Code Online (Sandbox Code Playgroud)

与实验 2 一样,这会阻止用户覆盖它......糟糕!

$ cmake -S . -B build --toolchain set-normal.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
Run Code Online (Sandbox Code Playgroud)

实验5:附加普通变量

现在我们将尝试使用您帖子中的代码。同样,我们将使用一个名为的工具链append-normal.cmake

# append-normal.cmake
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DMY_DEBUG")
Run Code Online (Sandbox Code Playgroud)

现在我们得到了一个非常不同的结果:

$ rm -rf build
$ cmake -S . -B build --toolchain append-normal.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG =  -DMY_DEBUG -DMY_DEBUG
...
Run Code Online (Sandbox Code Playgroud)

这是完全错误的!这里发生了什么?嗯,工具链文件在项目初始化期间被多次读取,这会导致标志-DMY_DEBUG被附加两次。至少这是第一次运行时发生的情况:

$ cmake -S . -B build
...
-- CMAKE_CXX_FLAGS_DEBUG = -g -DMY_DEBUG
...
Run Code Online (Sandbox Code Playgroud)

第一次运行后,CMake 默认值会被缓存,因此我们会在后续运行中附加到该默认值。此外,CMake 现在只读取您的工具链文件一次。

您必须始终使工具链文件具有幂等性。这意味着运行两次与运行一次效果相同。

实验 6:使用_INIT变量

这是开发人员按照文档设计的做事方式。请参阅此处的文档: https: //cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS_INIT.html

CMAKE_<LANG>_FLAGS第一次为 language 配置构建树时用于初始化缓存条目的值<LANG>。该变量旨在由工具链文件设置。CMake 可能会根据环境和目标平台在该值前面或附加内容。

现在我们使用一个名为的工具链文件init-var.cmake

# init-var.cmake
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-DMY_DEBUG")
Run Code Online (Sandbox Code Playgroud)

我们重新运行构建:

$ rm -rf build
$ cmake -S . -B build --toolchain init-var.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG -g
...
Run Code Online (Sandbox Code Playgroud)

现在我们可以看到 CMake 将其默认标志附加到我们提供的初始标志上。事实上,这仍然允许用户覆盖一些东西:

$ cmake -S . -B build --toolchain init-var.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE
...
Run Code Online (Sandbox Code Playgroud)

根据我的经验,90% 以上的情况下,让 CMake 使用实验 6(变量)添加额外标志是正确的_INIT。但时不时地,您会希望使用实验 3(set(CACHE) 不带 FORCE)完全覆盖 CMake。

您不想的是在后续运行中表现不同的行为(如实验 5)或禁用关键 CMake 功能(即尊重缓存变量,如实验 2 和 4)。