CMake - 删除单个翻译单元的编译标志

2024-03-29

我想删除单个翻译单元的设置编译标志。有没有办法做到这一点? (例如使用set_property?)

注意:编译标志没有-fno-name否定(无论出于何种原因)。

我试过了:

get_property(FLAGS TARGET target PROPERTY COMPILE_FLAGS)
string(REPLACE "-fname" "" FLAGS ${FLAGS})
set_property(TARGET target PROPERTY COMPILE_FLAGS ${FLAGS})

没有任何运气。我要删除的属性是CMAKE_CXX_FLAGS因此这是行不通的。


这个特定的解决方案(?)将适用于目标和单个翻译单元(单个.cpp-文件)。它将需要CMake 3.7或更新的,因为我们需要使用迭代所有现有的目标BUILDSYSTEM_TARGETS属性,已添加到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该子目录的。

#
# 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()

该宏首先创建一个列表CMAKE_CXX_FLAGS,然后它获取所有目标的列表并应用CMAKE_CXX_FLAGS到每个目标。最后,CMAKE_CXX_FLAGS已清除。_flag_sync_required用于指示我们是否需要强制重写缓存变量。

下一步取决于您是否要从目标或特定翻译单元中删除标志。如果要从目标中删除标志,可以使用类似于以下的宏:

#
# 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()

从特定翻译单元中删除标志有点棘手(除非我错过了一些东西)。无论如何,我们的想法是首先从文件所属的目标获取编译选项,然后将所述选项应用于该目标中的所有源文件,这允许我们操作单个文件的编译标志。为此,我们为要从中删除标志的每个文件维护一个编译标志的缓存列表,当请求删除时,我们将其从缓存列表中删除,然后重新应用剩余的标志。目标本身的编译选项被清除。

#
# 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()

Example

我们有一个非常简单的项目结构:

source/
    CMakeLists.txt
    foo.cpp
    bar.cpp
    main.cpp

我们假设foo.cpp违反-Wunused-variable,我们想要禁用该特定文件上的该标志。为了bar.cpp,我们想要禁用-Werror. For main.cpp,我们要删除-O2,无论出于何种原因。

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)

如果您想从目标中删除标志,您可以使用remove_flag_from_target(), 例如:remove_flag_from_target(MyTarget -O2)

Result

应用宏之前的输出

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> etc.
  • 宏一次只能处理一个标志
  • remove_flag_from_file()一次只能处理一个目标和输入文件作为输入。
  • 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()或许可以大大优化和改进。
  • 致电给separate_arguments()假设是 Unix。

其中大多数应该很容易修复。

我希望这至少能推动您朝着解决这个问题的正确方向前进。如果我需要在答案中添加一些内容,请告诉我。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CMake - 删除单个翻译单元的编译标志 的相关文章

随机推荐