在CMake命令行中传递编译器选项

97

我知道如何使用 cmake 命令传递编译器选项:

set(CMAKE_CXX_FLAGS "-Wall -Wno-dev -Wl,-rpath=/home/abcd/libs/")

是否有一种方式可以通过命令行传递选项,以覆盖CMakeLists.txt选项? 类似于:

cmake -Wl,-rpath=/home/abcd/newlibs/ path/to/CMakeLists.txt

或者

cmake -D CMAKE_CXX_FLAGS="-Wno-dev -Wl,-rpath=/home/abcd/libs/" path/to/CMakeLists.txt

我的主要问题是我想知道如何在命令行中添加标志以及如何覆盖现有的编译器标志。


7
-Wno-dev 是一个 CMake 选项,-Wall 是一个编译选项,-Wl 开头是一个链接选项。它们的传递方式不同。虽然编译选项和链接选项有一些共同点,但是 CMake 选项与它们无关。 - Tsyvarev
7个回答

85

是的,您可以追加编译器和链接器选项。但在CMake中,有两件事情需要区分:第一次调用生成构建环境以及所有后续调用,用于在更改CMakeLists.txt文件或依赖项后重新生成该构建环境。

以下是一些可能性(不包括更复杂的工具链变体):

追加编译器标志

  1. The initial content from the cached CMAKE_CXX_FLAGS variable is a combination of CMAKE_CXX_FLAGS_INIT set by CMake itself during OS/toolchain detection and whatever is set in the CXXFLAGS environment variable. So you can initially call:

     cmake -E env CXXFLAGS="-Wall" cmake ..
    
  2. Later, CMake would expect that the user modifies the CMAKE_CXX_FLAGS cached variable directly to append things, e.g., by using an editor like ccmake commit with CMake.

  3. You can easily introduce your own build type like ALL_WARNINGS. The build type specific parts are appended:

      cmake -DCMAKE_CXX_FLAGS_ALL_WARNINGS:STRING="-Wall" -DCMAKE_BUILD_TYPE=ALL_WARNINGS ..
    

添加链接器标志

链接器选项与编译器选项基本相同。只是 CMake 的变量名称取决于目标类型(EXESHAREDMODULE)。

  1. The CMAKE_EXE_LINKER_FLAGS_INIT, CMAKE_SHARED_LINKER_FLAGS_INIT or CMAKE_MODULE_LINKER_FLAGS_INIT do combine with the evironment variable LDFLAGS to CMAKE_EXE_LINKER_FLAGS, CMAKE_SHARED_LINKER_FLAGS and CMAKE_MODULE_LINKER_FLAGS.

    So you can e.g call:

     cmake -E env LDFLAGS="-rpath=/home/abcd/libs/" cmake ..
    
  2. See above.

  3. Build type-specific parts are appended:

     cmake -DCMAKE_SHARED_LINKER_FLAGS_MY_RPATH:STRING="-rpath=/home/abcd/libs/" -DCMAKE_BUILD_TYPE=MY_RPATH ..
    

替代方案

请注意,CMake确实提供了一种特殊的变量来以平台无关的方式设置编译器/链接器标志。因此,您不需要知道特定的编译器/链接器选项。

以下是一些示例:

不幸的是,目前还没有编译器警告级别的相应变量(但是

参考资料


1
不幸的是,在Windows上使用cmake -E env CXXFLAGS="..." ..的方法不起作用 - 即使以管理员权限运行该工具,也会显示“访问被拒绝”的错误。 - AntonK
当我尝试将-E env CXXFLAGS="-Wall"添加到我的cmake命令中时,它会显示CMake Warning: Ignoring extra path from command line: "CXXFLAGS=-Wall" - Aaron Franke

38

我的回答旨在证明一件事:

命令行选项,例如CMAKE_C_FLAGSCMAKE_CXX_FLAGS,始终是追加而不是覆盖。

接下来是具体内容。

准备hello_world文件夹下的文件

hello.c

#include <stdio.h>


int main(int argc, char* argv[]) {
    printf("Hello World!\n");
#ifdef DEFINED_IN_CMAKELISTS
    printf("You are here because you defined DEFINED_IN_CMAKELISTS in CMakeLists and it is not overwritten.\n");
#else
    printf("You are here because CLI CMAKE_C_FLAGS overwrote DEFINED_IN_CMAKELISTS, or you have NOT defined DEFINED_IN_CMAKELISTS.\n");
#endif 
#ifdef DEFINED_IN_CLI
    printf("You are here because you defined DEFINED_IN_CLI when running cmake -DCMAKE_C_FLAGS.\n");
#else
    printf("You are here because you have NOT defined DEFINED_IN_CLI when running cmake -DCMAKE_C_FLAGS.\n");
#endif // #ifdef DEFINED_IN_CLI
    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.4.1 FATAL_ERROR)
project(Hello)

set(HELLO_SRCS Hello.c)

add_executable(Hello ${HELLO_SRCS})

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEFINED_IN_CMAKELISTS")

生成 CMake 文件

$ mkdir _build && cd _build && cmake ..

-- The C compiler identification is AppleClang 11.0.3.11030032
-- The CXX compiler identification is AppleClang 11.0.3.11030032
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/me/Desktop/_dev/playground/cmake/hello_world/_build

制作和运行

$ make

Scanning dependencies of target Hello
[ 50%] Building C object CMakeFiles/Hello.dir/Hello.c.o
[100%] Linking C executable Hello
[100%] Built target Hello
$ ./Hello

Hello World!
You are here because you defined DEFINED_IN_CMAKELISTS in CMakeLists and it is not overwritten.
You are here because you have NOT defined DEFINED_IN_CLI when running cmake -DCMAKE_C_FLAGS.

从命令行定义新的编译选项

$ cmake -DCMAKE_C_FLAGS="-DDEFINED_IN_CLI" ..

-- Configuring done
-- Generating done
-- Build files have been written to: /Users/me/Desktop/_dev/playground/cmake/hello_world/_build

制作和运行

$ make

[ 50%] Building C object CMakeFiles/Hello.dir/Hello.c.o
[100%] Linking C executable Hello
[100%] Built target Hello
$ ./Hello 

Hello World!
You are here because you defined DEFINED_IN_CMAKELISTS in CMakeLists and it is not overwritten.
You are here because you defined DEFINED_IN_CLI when running cmake -DCMAKE_C_FLAGS.

结论

从以上测试结果可以看出,即使没有使用硬编码附加类似于

-DCMAKE_C_FLAGS="${CMAKE_C_FLAGS} -DDEFINED_IN_CLI"

CMake 仍然将命令行选项附加到 CMakeLists.txt 中已有的内容中。


3
超级棒的回答。这个应该被评为第一。 - Roel Van de Paar
1
实验结果有些误导性!在我的环境中(Windows 10,CMake 3.21.4,VS2019),当查看 CMakeCache.txt 时,默认标志是 CMAKE_C_FLAGS:STRING=/DWIN32 /D_WINDOWS /W3,而当指定命令行选项时,这些标志变为 CMAKE_C_FLAGS:STRING=-DDEFINED_IN_CLI,这是完全不同的情况。 - AntonK
2
你的结论有点混乱:你说“即使没有硬编码...”,但是在你使用的CMakeLists.txt文件中,确实包含了这一行代码 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEFINED_IN_CMAKELISTS")。我已经验证,如果你将这一行代码改成set(CMAKE_C_FLAGS "-DDEFINED_IN_CMAKELISTS"),那么cmake -DCMAKE_C_FLAGS="-DDEFINED_IN_CLI" ..就无法按预期工作了。因此你的结论应该是:“如果项目的CMakeLists.txt文件以这种特定方式设置,那么-DCMAKE_C_FLAGS=就能起到作用;但是如果项目的CMakeLists.txt不同,这可能不起作用。” - Quuxplusone
扩展@AntonK的评论-只要您从命令行执行-DCMAKE_C_FLAGS,就会“失去”在CMAKE_CXX_FLAGS_INIT中设置的默认值。因此,命令行-DCMAKE_C_FLAGS并不总是追加。相反,您展示的所有内容都表明,您的自定义CMakeLists.txt将始终附加您设置的特定值-DDEFINED_IN_CMAKELISTS - Paul Molodowitch

16

使用:

cmake -D CMAKE_CXX_FLAGS="-Wno-dev -Wl,-rpath=/home/abcd/libs/" path/to/CMakeLists.txt

这应该可以工作。问题在于如果你find_package()了某个同时也会改变CMAKE_CXX_FLAGS的包,那么它不仅仅是部分工作。


3
好的,我也猜到了需要覆盖。但是如何将一个选项附加到现有选项上呢?在这里,“=”不起作用。 - infoclogged
抱歉,我不知道如何通过命令行附加变量。也许可以尝试使用命令行输入 CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} NewFlag"。 - Tomaz Canabrava
不应该期望它能够正常工作,因为许多 shell 会将 ${SOMETOKEN} 解释为 shell 变量的扩展。然而,单引号可能有效,但我还没有测试过。 - Catskul

5
也许这个方法可行 -
cmake -DCMAKE_CXX_FLAGS="$(CMAKE_CXX_FLAGS) -DYOUR_CUSTOM_DEFINE=1" <rest of original cmake cmdline>

就像Tomaz上面提到的那样。 -m

1
仅提供代码答案是不被鼓励的。请添加一些解释,说明如何解决问题,或者这与现有答案有何不同。来自审核 - Nick
语法略有不同,使用()而非上面所示的{}。我测试了以验证它可以正常工作。此方法将指定内容添加到现有的CXX_FLAGS中,而不会覆盖它。 - M Kelly

1

这里的大部分答案都是正确的,但我也遇到了如何传递 CMAKE_CXX_FLAGS 并添加一个包含空格的 include 目录 (Windows) 的问题。

显然,如果你从命令行运行该参数,你需要对引用特别小心 (参见 here)。

cmake ... -DCMAKE_CXX_FLAGS="-fms-compatibility-version=19.00 --target=i686--windows -X -I """C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um""" "

所以,如果 include 路径中包含空格,并且需要加引号,但您还需要引用 CMAKE_CXX_FLAGS,最终以单引号字符 (") 开始引号,并在需要引号时放置三个引号字符 (""")。

总体来说有点奇怪,花了一些时间才解决这个问题。


0
看:
cmake \ 
-DCMAKE_C_FLAGS_INIT:STRING="<your special cflags for all variants of build type>" \ 
-DCMAKE_CXX_FLAGS_INIT:STRING="<your special cxxflags>" \ 
-DCMAKE_ASM_FLAGS_INIT:STRING="<your special asmflags>" \ 
-DCMAKE_EXE_LINK_FLAGS_INIT:STRING="<your special ldflags>" \ 
-S "<path to folder with root CMakeLists.txt>" \ 
-B "<path to folder for build>"

在`CMakeCache.txt`中创建了变量`CMAKE_C_FLAGS`和其他变量,其中包含您的特殊编译器选项。如果您还想重新定义发布版本的编译器标志,只需添加例如`-DCMAKE_C_FLAGS_RELEASE:STRING="-O2 -g -DNODEBUG"`。并且在命令行中添加了下列标志:
gcc <flags from CMAKE_C_FLAGS_INIT> <flags from CMAKE_C_FLAGS_RELEASE> ...

-1

我只需使用$ENV()运算符来获取环境变量,例如在CMakeLists.txt中:

add_compile_options($ENV{MY_CXXFLAG})

唯一的问题是,$ENV() 仅在配置阶段读取,因此 CMake 在当前构建阶段不会看到环境设置。但是重新配置是通过更改 CMake 文件来触发的,因此我只需使用 touch 来模拟更改即可。以下是一个命令行示例:
touch CMakeLists.txt && MY_CXXFLAG="-D DEBUG" cmake --build build --config Debug

或者你使用了哪些其他选项。通过这个简单的例子,仍然存在一些关于环境变量标志字符串的小问题,例如,有多个选项。但是,在CMakeLists.txt中处理字符串应该不会成为一个大问题,可以很好地解决这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接