简短的回答是,每个子目录都有自己的变量范围,在调用add_subdirectory() 时使用当前变量值的副本初始化。
详细答案请查看What's the CMake syntax to set and use variables?
目录和目标属性与(全局)变量
CMake 处理add_compile_options() 和CMAKE_CXX_FLAGS 的方式有所不同:
-
您使用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 版本 add_compile_options() 是 add_definitions()。该功能仍然存在,但是将定义与编译选项混合在一起很奇怪。所以add_compile_options() 被发明出来了。
编译器标志的完整生成器公式
它在 CMake 的代码中(参见 cmCommonTargetGenerator::GetFlags()、cmLocalGenerator::AddCompileOptions() 和 cmLocalGenerator::AddLanguageFlags())。
此示例显示了一个没有导出的 DEBUG 构建配置库,没有考虑基于功能的标志或类似 CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 或 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]
测试代码
为了更好地理解这里是我测试编译器选项的代码和我得到的结果:
注意:通常我会使用add_definitions() 和target_compile_definitions() 而不是add_compile_options() 和target_compile_options() 来设置编译器定义,但为了演示编译器选项的传播我(错误)使用-D 标志。
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}")
将导致以下消息:
-- 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
并且正在设置以下预处理器定义:
库
CXX_FLAG
SUB_CXX_FLAG
LATE_SUB_CXX_FLAG
COMPILE_OPTION
SUB_COMPILE_OPTION
LIB_COMPILE_OPTION
主要
CXX_FLAG
LATE_CXX_FLAG
COMPILE_OPTION
MAIN_COMPILE_OPTION
LIB_COMPILE_OPTION
这里有趣的部分是 LATE CXX 标志和 LIB 编译选项传播链接库。
参考文献