这个解决方案既适用于目标文件,也适用于单个翻译单元(一个单独的.cpp
文件)。它需要使用BUILDSYSTEM_TARGETS
属性遍历所有现有目标,因此需要CMake 3.7
或更新版本。如果您无法使用3.7
或更新版本,则可以调整apply_global_cxx_flags_to_all_targets()
以获取目标列表并手动指定它们。请注意,您应该将下面的宏视为概念验证。对于任何但非常小的项目,它们可能需要进行一些微调和改进。
第一步是遍历所有现有目标,并将CMAKE_CXX_FLAGS
应用于每个目标。完成后,我们清除CMAKE_CXX_FLAGS
。下面是一个执行此操作的宏。当所有目标已创建、所有标志已设置等时,应该在顶级CMakeLists.txt
的底部某处调用此宏。重要的是要注意,命令get_property(_targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
在目录级别上工作,因此如果您使用add_subdirectory()
,则必须在该子目录的顶级CMakeLists.txt
中调用此宏。
macro(apply_global_cxx_flags_to_all_targets)
separate_arguments(_global_cxx_flags_list UNIX_COMMAND ${CMAKE_CXX_FLAGS})
get_property(_targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
foreach(_target ${_targets})
target_compile_options(${_target} PUBLIC ${_global_cxx_flags_list})
endforeach()
unset(CMAKE_CXX_FLAGS)
set(_flag_sync_required TRUE)
endmacro()
这个宏首先从
CMAKE_CXX_FLAGS
创建一个列表,然后获取所有目标的列表,并将
CMAKE_CXX_FLAGS
应用于每个目标。最后,清除
CMAKE_CXX_FLAGS
。使用
_flag_sync_required
来指示是否需要强制重写缓存变量。
下一步取决于您是想从目标中删除标志还是从特定的翻译单元中删除标志。如果您想从目标中删除标志,可以使用类似以下的宏:
macro(remove_flag_from_target _target _flag)
get_target_property(_target_cxx_flags ${_target} COMPILE_OPTIONS)
if(_target_cxx_flags)
list(REMOVE_ITEM _target_cxx_flags ${_flag})
set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${_target_cxx_flags}")
endif()
endmacro()
从特定翻译单元中删除标记有点棘手(除非我漏掉了什么)。无论如何,首先要从该文件所属的目标获取编译选项,然后将这些选项应用于该目标中的所有源文件,以允许我们操纵单个文件的编译标志。为此,我们通过维护每个要从中删除标志的文件的缓存编译标志列表来执行此操作,并且在请求删除时,我们会从缓存列表中移除它,然后重新应用其余标志。目标本身的编译选项被清除。
macro(remove_flag_from_file _target _file _flag)
get_target_property(_target_sources ${_target} SOURCES)
if(_flag_sync_required)
unset(_cached_${_target}_cxx_flags CACHE)
unset(_cached_${_target}_${_file}_cxx_flags CACHE)
endif()
get_target_property(_${_target}_cxx_flags ${_target} COMPILE_OPTIONS)
if(NOT _cached_${_target}_cxx_flags)
get_target_property(_target_cxx_flags ${_target} COMPILE_OPTIONS)
set(_cached_${_target}_cxx_flags "${_target_cxx_flags}" CACHE INTERNAL "")
set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "")
foreach(_source_file ${_target_sources})
get_source_file_property(_source_file_cxx_flags ${_source_file} COMPILE_FLAGS)
if(_source_file_cxx_flags)
separate_arguments(_source_file_cxx_flags UNIX_COMMAND ${_source_file_cxx_flags})
list(APPEND _source_file_cxx_flags "${_target_cxx_flags}")
else()
set(_source_file_cxx_flags "${_target_cxx_flags}")
endif()
string(REPLACE ";" " " _source_file_cxx_flags_string "${_source_file_cxx_flags}")
set_source_files_properties(${_source_file} PROPERTIES COMPILE_FLAGS "${_source_file_cxx_flags_string}")
endforeach()
endif()
list(FIND _target_sources ${_file} _file_found_at)
if(_file_found_at GREATER -1)
if(NOT _cached_${_target}_${_file}_cxx_flags)
get_source_file_property(_source_file_cxx_flags ${_file} COMPILE_FLAGS)
separate_arguments(_source_file_cxx_flags UNIX_COMMAND ${_source_file_cxx_flags})
set(_cached_${_target}_${_file}_cxx_flags ${_source_file_cxx_flags} CACHE INTERNAL "")
endif()
list(REMOVE_ITEM _cached_${_target}_${_file}_cxx_flags ${_flag})
string(REPLACE ";" " " _cached_${_target}_${_file}_cxx_flags_string "${_cached_${_target}_${_file}_cxx_flags}")
set_source_files_properties(${_file} PROPERTIES COMPILE_FLAGS "${_cached_${_target}_${_file}_cxx_flags_string}")
endif()
endmacro()
示例
我们有这样一个非常简单的项目结构:
source/
CMakeLists.txt
foo.cpp
bar.cpp
main.cpp
假设
foo.cpp
违反了
-Wunused-variable
规则,我们希望在这个特定文件中禁用该标志。对于
bar.cpp
,我们希望禁用
-Werror
。出于某种原因,我们想要移除
-O2
,是针对
main.cpp
的。
CMakeLists.txt
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
project(MyProject)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wunused-variable")
add_compile_options("-Werror")
add_executable(MyTarget foo.cpp bar.cpp main.cpp)
apply_global_cxx_flags_to_all_targets()
remove_flag_from_file(MyTarget foo.cpp -Wunused-variable)
remove_flag_from_file(MyTarget bar.cpp -Werror)
remove_flag_from_file(MyTarget main.cpp -O2)
如果您想从目标中删除一个标志,您可以使用
remove_flag_from_target()
,例如:
remove_flag_from_target(MyTarget -O2)
结果
应用宏之前的输出
clang++ -Werror -O2 -Wunused-variable -o foo.cpp.o -c foo.cpp
clang++ -Werror -O2 -Wunused-variable -o bar.cpp.o -c bar.cpp
clang++ -Werror -O2 -Wunused-variable -o main.cpp.o -c main.cpp
应用宏后的输出结果
clang++ -Werror -O2 -o foo.cpp.o -c foo.cpp
clang++ -O2 -Wunused-variable -o bar.cpp.o -c bar.cpp
clang++ -Werror -Wunused-variable -o main.cpp.o -c main.cpp
最终备注
如前所述,这应该被视为一个概念验证。需要考虑以下几个问题:
- 它只考虑了
CMAKE_CXX_FLAGS
(对所有构建类型通用),而没有考虑CMAKE_CXX_FLAGS_<DEBUG|RELEASE>
等。
- 这些宏一次只能处理一个标志。
remove_flag_from_file()
一次只能处理一个目标和输入文件。
remove_flag_from_file()
期望一个文件名,而不是一个路径。例如传递source/foobar/foobar.cpp
将不起作用,因为source/foobar/foobar.cpp
将与foobar.cpp
进行比较。可以使用get_source_file_property()
和LOCATION
属性来修复此问题,并放置一个前提条件,即_file
是完整路径。
- 如果一个目标有两个或更多相同名称的文件会发生什么?
remove_flag_from_file()
可能可以进行优化和改进。
- 对
separate_arguments()
的调用假定Unix。
这些问题中大部分应该很容易解决。
我希望这至少能引导你在解决这个问题上朝着正确的方向前进。如果需要添加一些答案,请告诉我。
CMakeLists.txt
中将-fname
标志添加到CMAKE_CXX_FLAGS
中? - FlorianCMAKE_CXX_FLAGS
中的,还是其中一个默认组件? - John