Cmake 设置变量递归吗? [英] Is Cmake set variable recursive?

查看:25
本文介绍了Cmake 设置变量递归吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试修改某个目录下所有目录的编译器标志(即递归地修改所有当前目录的子目录及其所有子目录).所以我发现这里有两种方法:

add_directory(dir1)# ...添加目录(目录)add_compile_options(flag1 flag2 ...)# 或者对于 CMake 版本 <3.0 做一些更像:设置(CMAKE_CXX_FLAGS${CMAKE_CXX_FLAGS} flag1 flag2 ...")

add_compile_options 的手册页非常清楚地说明效果将是当前目录及以下"(这是我想要的)但是对于 set(CMAKE_CXX_FLAGS ...) 我不太确定.

Cmake 设置变量递归吗?

解决方案

简短的回答是,每个子目录都有自己的变量范围,在 add_subdirectory 时使用当前变量值的副本进行初始化() 调用.

有关长答案,请参阅CMake 语法是什么设置和使用变量?

目录&目标属性与(全局)变量

add_compile_options()CMAKE_CXX_FLAGS 由 CMake 处理:

  • 您使用 add_compile_options() 指定的所有内容都附加到 COMPILE_OPTIONS 目录属性.然后使用 add_library()add_executable() 来在创建目标时使用此属性来初始化 COMPILE_OPTIONS 目标属性".

    当解析器进行add_subdirectory()调用时,目录属性的当前状态用于初始化子目录属性.

  • CMAKE_CXX_FLAGS 是一个全局缓存变量.您可以通过定义本地目录范围变量(隐藏全局缓存的变量)来扩展/覆盖它.

    这些变量的上下文被复制到 add_subdirectory() 上的子目录范围内(传播到子目录).

    CMake 在每个 CMakeLists.txt 文件的 end 查看它的值,并将其应用于同一 CMakeLists.txt 中的所有目标代码>(允许延迟声明,另请参见下面的完整公式测试代码).

  • 所以对于 CMake 版本 <3.0 相当于 add_compile_options()add_definitions().功能仍然存在,但将定义与编译选项混合在一起很奇怪.于是发明了 add_compile_options().

编译器标志的完整生成器公式

它在 CMake 的代码中(参见 cmCommonTargetGenerator::GetFlags(), cmLocalGenerator::AddCompileOptions()cmLocalGenerator::AddLanguageFlags()).

这个例子展示了一个没有导出的 DEBUG 构建配置库,没有考虑基于特征的标志或类似 CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDESCMAKE_QUOTE_INCLUDE_PATHS:

 CMAKE_CXX_FLAGS//在目标的 CMakeLists.txt 末尾设置+ CMAKE_CXX_FLAGS_DEBUG+ 包含目录//以 CMAKE_INCLUDE_FLAG_CXX/CMAKE_INCLUDE_SYSTEM_FLAG_CXX 为前缀(CMAKE_INCLUDE_CURRENT_DIR)?+ CMAKE_CURRENT_SOURCE_DIR + CMAKE_CURRENT_BINARY_DIR+ CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES+ 目标[INCLUDE_DIRECTORIES]+ 依赖目标[INTERFACE_INCLUDE_DIRECTORIES]+ 定义标志//使用 add_definitions() 给出的编译器标志+ Target[COMPILE_FLAGS]//已弃用- 由 CMAKE_CXX_FLAG_REGEX 过滤+ 目标[COMPILE_OPTIONS]+ 依赖目标[INTERFACE_COMPILE_OPTIONS]

测试代码

为了更好地理解这里是我测试编译器选项的代码和我得到的结果:

注意:通常我会使用 add_definitions()target_compile_definitions() 而不是 add_compile_options()target_compile_options() 用于设置编译器定义,但为了演示编译器选项的传播,我(错误地)使用了 -D 标志.

CMakeLists.txt

cmake_minimum_required(VERSION 3.0)项目(CxxFlagsTest)设置(CMAKE_CXX_FLAGS${CMAKE_CXX_FLAGS} -DCXX_FLAG")add_compile_options("-DCOMPILE_OPTION")添加子目录(lib)文件(写入 main.cpp "int main() { return 0; }")add_executable(main main.cpp)target_link_libraries(主库)target_compile_options(主私有-DMAIN_COMPILE_OPTION")设置(CMAKE_CXX_FLAGS${CMAKE_CXX_FLAGS} -DLATE_CXX_FLAG")get_target_property(_main_compile_options main COMPILE_OPTIONS)消息(状态主编译选项:${_main_compile_options}")get_directory_property(_root_compile_options COMPILE_OPTIONS)消息(状态root COMPILE_OPTIONS:${_root_compile_options}")消息(状态根 CMAKE_CXX_FLAGS:${CMAKE_CXX_FLAGS}")

lib/CMakeLists.txt

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSUB_CXX_FLAG")add_compile_options("-DSUB_COMPILE_OPTION")文件(写入 lib.cpp "")add_library(lib lib.cpp)target_compile_options(lib PUBLIC-DLIB_COMPILE_OPTION")设置(CMAKE_CXX_FLAGS${CMAKE_CXX_FLAGS} -DLATE_SUB_CXX_FLAG")get_target_property(_lib_compile_options lib COMPILE_OPTIONS)消息(状态lib COMPILE_OPTIONS:${_lib_compile_options}")get_directory_property(_sub_compile_options COMPILE_OPTIONS)消息(状态子编译选项:${_sub_compile_options}")消息(状态子 CMAKE_CXX_FLAGS:${CMAKE_CXX_FLAGS}")

将导致以下消息:

-- lib COMPILE_OPTIONS: -DCOMPILE_OPTION;-DSUB_COMPILE_OPTION;-DLIB_COMPILE_OPTION-- 子 COMPILE_OPTIONS: -DCOMPILE_OPTION;-DSUB_COMPILE_OPTION-- 子 CMAKE_CXX_FLAGS: ... -DCXX_FLAG -DSUB_CXX_FLAG -DLATE_SUB_CXX_FLAG-- 主 COMPILE_OPTIONS: -DCOMPILE_OPTION;-DMAIN_COMPILE_OPTION-- 根 COMPILE_OPTIONS: -DCOMPILE_OPTION-- 根 CMAKE_CXX_FLAGS: ... -DCXX_FLAG -DLATE_CXX_FLAG

并设置以下预处理器定义:

lib

<块引用>

CXX_FLAGSUB_CXX_标志LATE_SUB_CXX_FLAG编译选项SUB_COMPILE_OPTIONLIB_COMPILE_OPTION

主要

<块引用>

CXX_FLAGLATE_CXX_FLAG编译选项MAIN_COMPILE_OPTIONLIB_COMPILE_OPTION

这里有趣的部分是 LATE CXX 标志和传播链接库的 LIB 编译选项.

参考资料

I am trying to modify compiler flags for all the directories below a certain directory (i.e. for all the current directories subdirectories and all their subdirectories recursively). So I found here there is two ways:

add_directory(dir1)
# ...
add_directory(dirN)

add_compile_options(flag1 flag2 ...)
# or for CMake versions < 3.0 to do something more like:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} flag1 flag2 ...")

The man page for add_compile_options is very clear in stating that the effect will be "current directory and below" (which is what I want) but for set(CMAKE_CXX_FLAGS ...) I am not so sure.

Is Cmake set variable recursive?

解决方案

The short answer is, that each sub-directory has it's own variable scope initialized with a copy of the current variable values at the time of the add_subdirectory() call.

For the long answer please see What's the CMake syntax to set and use variables?

Directory & Target Properties vs (Global) Variables

There is a difference between how add_compile_options() and CMAKE_CXX_FLAGS are processed by CMake:

  • Everything that you specify with add_compile_options() is appended to the COMPILE_OPTIONS directory property. Then "this property is used to initialize the COMPILE_OPTIONS target property when a target is created" with add_library() or add_executable().

    And the current state of directory properties are used to initialize sub-directory properties when the parser gets to a add_subdirectory() call.

  • The CMAKE_CXX_FLAGS is a global cached variable. You can extend/overwrite it by defining a local directory scoped variable (hiding the globally cached one).

    Those variable's context is copied into a sub-directories scope on add_subdirectory() (propagating to sub-directories).

    And CMake looks into its value at the end of the each CMakeLists.txt file and applies this to all targets in the same CMakeLists.txt (allowing late declarations, see also Complete Formula and Test Code below).

  • So for CMake versions < 3.0 the equivalent to add_compile_options() was add_definitions(). The functionality is still there, but it was strange to mix definitions with compile options. So add_compile_options() was invented.

The complete Generator-Formula for Compiler Flags

It's in CMake's code (see cmCommonTargetGenerator::GetFlags(), cmLocalGenerator::AddCompileOptions() and cmLocalGenerator::AddLanguageFlags()).

This example shows a DEBUG build configuration library without exports, not taking into account the feature-based flags or something like CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES or CMAKE_QUOTE_INCLUDE_PATHS:

  CMAKE_CXX_FLAGS       // as set at the end of target's CMakeLists.txt
+ CMAKE_CXX_FLAGS_DEBUG

+ Include Directories   // pefixed with CMAKE_INCLUDE_FLAG_CXX/CMAKE_INCLUDE_SYSTEM_FLAG_CXX

    (CMAKE_INCLUDE_CURRENT_DIR) ? 
        + CMAKE_CURRENT_SOURCE_DIR + CMAKE_CURRENT_BINARY_DIR
    + CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES
    + Target[INCLUDE_DIRECTORIES]
    + DependingTargets[INTERFACE_INCLUDE_DIRECTORIES] 

+ Define Flags         // compiler flags given with add_definitions()
+ Target[COMPILE_FLAGS] // deprecated
    - Filtered by CMAKE_CXX_FLAG_REGEX
+ Target[COMPILE_OPTIONS]
+ DependingTargets[INTERFACE_COMPILE_OPTIONS]

Test Code

For a better understanding here is my code for testing the compiler options and the results I get:

Note: Normally I would use add_definitions() and target_compile_definitions() instead of add_compile_options() and target_compile_options() to set compiler definitions, but to demonstrate the propagating of compiler options I (mis-)used -D flags.

CMakeLists.txt

cmake_minimum_required(VERSION 3.0)

project(CxxFlagsTest)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCXX_FLAG")
add_compile_options("-DCOMPILE_OPTION")

add_subdirectory(lib)

file(WRITE main.cpp "int main() { return 0; }")
add_executable(main main.cpp)
target_link_libraries(main lib)

target_compile_options(main PRIVATE "-DMAIN_COMPILE_OPTION")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DLATE_CXX_FLAG")

get_target_property(_main_compile_options main COMPILE_OPTIONS)
message(STATUS "main COMPILE_OPTIONS: ${_main_compile_options}")
get_directory_property(_root_compile_options COMPILE_OPTIONS)
message(STATUS "root COMPILE_OPTIONS: ${_root_compile_options}")
message(STATUS "root CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}")

lib/CMakeLists.txt

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSUB_CXX_FLAG")
add_compile_options("-DSUB_COMPILE_OPTION")

file(WRITE lib.cpp "")
add_library(lib lib.cpp)

target_compile_options(lib PUBLIC "-DLIB_COMPILE_OPTION")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DLATE_SUB_CXX_FLAG")

get_target_property(_lib_compile_options lib COMPILE_OPTIONS)
message(STATUS "lib COMPILE_OPTIONS: ${_lib_compile_options}")
get_directory_property(_sub_compile_options COMPILE_OPTIONS)
message(STATUS "sub COMPILE_OPTIONS: ${_sub_compile_options}")
message(STATUS "sub CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}")

Would result in the following messages:

-- lib COMPILE_OPTIONS: -DCOMPILE_OPTION;-DSUB_COMPILE_OPTION;-DLIB_COMPILE_OPTION
-- sub COMPILE_OPTIONS: -DCOMPILE_OPTION;-DSUB_COMPILE_OPTION
-- sub CMAKE_CXX_FLAGS:  ... -DCXX_FLAG -DSUB_CXX_FLAG -DLATE_SUB_CXX_FLAG
-- main COMPILE_OPTIONS: -DCOMPILE_OPTION;-DMAIN_COMPILE_OPTION
-- root COMPILE_OPTIONS: -DCOMPILE_OPTION
-- root CMAKE_CXX_FLAGS:  ... -DCXX_FLAG -DLATE_CXX_FLAG

And the following pre-processor definitions being set:

lib

CXX_FLAG
SUB_CXX_FLAG
LATE_SUB_CXX_FLAG
COMPILE_OPTION
SUB_COMPILE_OPTION
LIB_COMPILE_OPTION

main

CXX_FLAG
LATE_CXX_FLAG
COMPILE_OPTION
MAIN_COMPILE_OPTION
LIB_COMPILE_OPTION

The interesting parts here are the LATE CXX flags and the LIB compile option propagated the the linked library.

References

这篇关于Cmake 设置变量递归吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆