I would like to remove a set compile flag for a single translation unit. Is there a way to do this? (e.g. using set_property
?)
Note: the compile-flag has no -fno-name
negation (for whatever reason).
I've tried:
get_property(FLAGS TARGET target PROPERTY COMPILE_FLAGS) string(REPLACE "-fname" "" FLAGS ${FLAGS}) set_property(TARGET target PROPERTY COMPILE_FLAGS ${FLAGS})
without any luck. The property I want to remove is part of CMAKE_CXX_FLAGS
and thus this does not work.
This particular solution(?) will work both for targets and for single translation units (a single .cpp
-file). It will require CMake 3.7
or newer, since we need to iterate through all existing targets using the BUILDSYSTEM_TARGETS
property, which was added in 3.7
. If you are unable to use 3.7
or newer, you can adapt apply_global_cxx_flags_to_all_targets()
to take a list of targets and specify them manually. Note that you should consider the macros below as proof-of-concept. They will probably need some tweaking and improvement for any but very small projects.
The first step is to iterate through all existing targets and apply CMAKE_CXX_FLAGS
to each of them. When this is done, we clear CMAKE_CXX_FLAGS
. Below you'll find a macro that will do this. This macro should probably be called somewhere at the bottom of your top-level CMakeLists.txt
, when all targets have been created, all flags have been set etc. It's important to note that the command get_property(_targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
works at directory level, so if you use add_subdirectory()
, you must call this macro in the top-level CMakeLists.txt
of that sub-directory.
# # Applies CMAKE_CXX_FLAGS to all targets in the current CMake directory. # After this operation, CMAKE_CXX_FLAGS is cleared. # 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()
This macro first creates a list from CMAKE_CXX_FLAGS
, then it gets a list of all targets and applies CMAKE_CXX_FLAGS
to each of the targets. Finally, CMAKE_CXX_FLAGS
is cleared. _flag_sync_required
is used to indicate if we need to force a rewrite of cached variables.
The next step depends if you want to remove a flag from a target or from a particular translation unit. If you want to remove a flag from a target, you can use a macro similar to this:
# # Removes the specified compile flag from the specified target. # _target - The target to remove the compile flag from # _flag - The compile flag to remove # # Pre: apply_global_cxx_flags_to_all_targets() must be invoked. # 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()
Removing a flag from a particular translation unit is a bit trickier (unless I have greatly missed something). Anyway, the idea is to first obtain the compile options from the target to which the file belongs, and then applying said options to all source files in that target, which allows us to manipulate the compile flags for individual files. We do this by maintaining a cached list of compile flags for each file we want to remove flags from, and when a remove is requested, we remove it from the cached list and then re-apply the remaining flags. The compile options for the target itself is cleared.
# # Removes the specified compiler flag from the specified file. # _target - The target that _file belongs to # _file - The file to remove the compiler flag from # _flag - The compiler flag to remove. # # Pre: apply_global_cxx_flags_to_all_targets() must be invoked. # macro(remove_flag_from_file _target _file _flag) get_target_property(_target_sources ${_target} SOURCES) # Check if a sync is required, in which case we'll force a rewrite of the cache variables. 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) # On first entry, cache the target compile flags and apply them to each source file # in the target. if(NOT _cached_${_target}_cxx_flags) # Obtain and cache the target compiler options, then clear them. 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 "") # Apply the target compile flags to each source file. foreach(_source_file ${_target_sources}) # Check for pre-existing flags set by set_source_files_properties(). 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() # Apply the compile flags to the current source file. 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) # Cache the compile flags for the specified file. # This is the list that we'll be removing flags from. 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() # Remove the specified flag, then re-apply the rest. 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()
We have this very simple project structure:
source/ CMakeLists.txt foo.cpp bar.cpp main.cpp
Let's assume that foo.cpp
violates -Wunused-variable
, and we want to disable that flag on that particular file. For bar.cpp
, we want to disable -Werror
. For main.cpp
, we want to remove -O2
, for whatever reason.
CMakeLists.txt
cmake_minimum_required(VERSION 3.7 FATAL_ERROR) project(MyProject) # Macros omitted to save space. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wunused-variable") # CMake will apply this to all targets, so this will work too. 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)
If you instead want to remove a flag from a target, you would use remove_flag_from_target()
, for example: remove_flag_from_target(MyTarget -O2)
Output before applying macros
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
Output after applying macros
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
As mentioned, this should be seen as a proof-of-concept. There a few immediate issues to consider:
CMAKE_CXX_FLAGS
(common to all build types), not CMAKE_CXX_FLAGS_<DEBUG|RELEASE>
etc.remove_flag_from_file()
can only handle one target and input file as input at a time.remove_flag_from_file()
expects a filename, not a path. Passing, say, source/foobar/foobar.cpp
will not work, since source/foobar/foobar.cpp
will be compared against foobar.cpp
. This can be fixed by using get_source_file_property()
and the LOCATION
property and placing a precondition that _file
is a full path. remove_flag_from_file()
can probably be optimized and improved greatly.separate_arguments()
assumes Unix.Most of these should be fairly easy to fix.
I hope that this will at least nudge you in the right direction in solving this problem. Let me know if I need to add something to the answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With