简述:有两种可接受的方法来完成这个任务。
首先,大多数情况下(90%以上),您可以使用文档建议的
_INIT
变量,如
所示:
set(CMAKE_CXX_FLAGS_INIT "-m32")
其次,如果CMake为您的编译器/平台组合添加了不正确/冲突的标志,您可以通过设置缓存变量而完全覆盖它,
无需FORCE
。
set(CMAKE_CXX_FLAGS "-m32" CACHE STRING "C++ compiler flags")
阅读以下详细信息。
让我们进行几个实验。我们将使用以下的CMakeLists.txt文件:
cmake_minimum_required(VERSION 3.23)
project(test LANGUAGES CXX)
message(STATUS "CMAKE_CXX_FLAGS_DEBUG = ${CMAKE_CXX_FLAGS_DEBUG}")
在大多数系统上,默认情况下,CMake会将
CMAKE_CXX_FLAGS
留空。主要的例外是Windows与MSVC,在这种情况下,它会添加
/EHsc
和(在旧版本中)
/GR
以确保启用标准C++异常处理和RTTI。
由于我没有方便地访问Windows系统,所以我使用
CMAKE_CXX_FLAGS_DEBUG
,它在大多数编译器上具有默认初始化标志。不过,相同的原则适用于两种情况,因为平台模块负责设置这些标志。
$ 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
在这个编译器上,
CMAKE_CXX_FLAGS_DEBUG
被设置为
-g
。这是我们的基线。
实验2:使用force设置缓存
现在我们将创建一个名为
set-cache-force.cmake
的工具链文件:
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG" CACHE STRING "C++ compiler flags" FORCE)
我们将使用这个工具链来配置项目:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache-force.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
我们可以看到,原始的
-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
...
这是极其不良的行为。用户需要编辑您的工具链文件以解决错误或添加其他自定义设置。
实验3:未使用
FORCE
设置的Set-cache
如果我们运行与之前相同的实验,但不设置
FORCE
,那么我们仍然会得到相同的标志,但保留了逐步覆盖工具链文件的能力。
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG" CACHE STRING "C++ compiler flags")
现在我们可以看到它起作用了:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
这意味着它仍然可以被覆盖:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE
...
它甚至可以再次被覆盖:
$ cmake -S . -B build -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE2"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE2
...
实验4:设置普通变量
现在我们将尝试将其设置为普通变量。同样,我们将创建一个名为
set-normal.cmake
的工具链文件:
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG")
再次运行此命令会显示,
-DMY_DEBUG
"获胜",覆盖了CMake的默认标志:
$ cmake -S . -B build --toolchain set-normal.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
与实验2类似,这会防止用户覆盖它...很糟糕!
$ cmake -S . -B build --toolchain set-normal.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
实验5:追加普通变量
现在我们将尝试使用您发布的代码。同样,我们将使用一个名为
append-normal.cmake
的工具链:
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DMY_DEBUG")
现在我们得到了一个非常不同的结果:
$ rm -rf build
$ cmake -S . -B build --toolchain append-normal.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG -DMY_DEBUG
...
这完全是错误的!发生了什么?在项目初始化期间,工具链文件会被多次读取,这导致
-DMY_DEBUG
标志被重复附加。至少这是在第一次运行时发生的:
$ cmake -S . -B build
...
-- CMAKE_CXX_FLAGS_DEBUG = -g -DMY_DEBUG
...
第一次运行后,CMake默认被缓存,因此我们在后续运行中附加到该缓存中。此外,现在CMake只读取您的工具链文件一次。
您必须始终使您的工具链文件幂等。这意味着将其运行两次与将其运行一次是相同的。
实验6:使用_INIT变量
根据文档,这是开发者预期的做法。请查看此处的文档:
https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS_INIT.html
引用:
用于初始化第一次为语言配置构建树时的CMAKE__FLAGS缓存条目的值。该变量应由工具链文件设置。基于环境和目标平台,CMake可能会在值之前或之后添加内容。
现在我们使用一个名为init-var.cmake的工具链文件:
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-DMY_DEBUG")
我们重新运行构建:
$ rm -rf build
$ cmake -S . -B build --toolchain init-var.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG -g
...
现在我们可以看到,CMake将其默认标志附加到我们提供的初始标志中。确实,这仍然允许用户覆盖一些东西。
$ cmake -S . -B build --toolchain init-var.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE
...
根据我的经验,90%以上的情况下,使用实验6(
_INIT
变量)让CMake添加其额外的标志是正确的。但是偶尔你会想要完全覆盖CMake,使用实验3(
set(CACHE)
而不使用
FORCE
)。
你不想做的事情是任何在后续运行中表现不同的操作(如实验5),或者禁用关键的CMake功能(即尊重缓存变量,如实验2和4)。
CMakeLists.txt
只包含编译项目所需最少信息的人,另一个值得考虑的选项是通过预设文件来设置它们。 - Mark Gfile
命令进行验证。 - Irbis