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

CMake - remove a compile flag for a single translation unit(CMake - 删除单个翻译单元的编译标志)

本文介绍了CMake - 删除单个翻译单元的编译标志的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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?)

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

Note: the compile-flag has no -fno-name negation (for whatever reason).

我试过了:

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

没有任何运气.我要删除的属性是 CMAKE_CXX_FLAGS 的一部分,因此这不起作用.

without any luck. The property I want to remove is part of CMAKE_CXX_FLAGS and thus this does not work.

推荐答案

这个特殊的解决方案 (?) 既适用于目标,也适用于单个翻译单元(单个 .cpp 文件).它将需要 CMake 3.7 或更高版本,因为我们需要使用 BUILDSYSTEM_TARGETS 属性迭代所有现有目标,该属性是在 3.7 中添加的.如果您无法使用 3.7 或更高版本,您可以调整 apply_global_cxx_flags_to_all_targets() 以获取目标列表并手动指定它们.请注意,您应该将下面的宏视为概念验证.对于非常小的项目,他们可能需要进行一些调整和改进.

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.

第一步是遍历所有现有目标并将 CMAKE_CXX_FLAGS 应用于每个目标.完成后,我们清除CMAKE_CXX_FLAGS.您将在下面找到一个可以执行此操作的宏.这个宏可能应该在你的顶级 CMakeLists.txt 底部的某个地方调用,当所有的目标都已经创建,所有的标志都已经设置等.重要的是要注意命令 get_property(_targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS) 工作在目录级别,所以如果你使用add_subdirectory(),你必须在顶级CMakeLists.txt 中调用这个宏> 那个子目录.

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

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

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

示例

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

Example

We have this very simple project structure:

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

让我们假设 foo.cpp 违反了 -Wunused-variable,并且我们希望在该特定文件上禁用该标志.对于bar.cpp,我们要禁用-Werror.对于 main.cpp,我们想删除 -O2,无论出于何种原因.

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)

如果您想从目标中移除标志,您可以使用 remove_flag_from_target(),例如:remove_flag_from_target(MyTarget -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)

应用宏前的输出

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

最后的笔记

如前所述,这应该被视为概念验证.有几个紧迫的问题需要考虑:

Final notes

As mentioned, this should be seen as a proof-of-concept. There a few immediate issues to consider:

  • 它只考虑 CMAKE_CXX_FLAGS(所有构建类型通用),而不考虑 CMAKE_CXX_FLAGS_
  • 宏一次只能处理一个标志
  • remove_flag_from_file() 一次只能处理一个目标和输入文件作为输入.
  • remove_flag_from_file() 需要文件名,而不是路径.例如,传递 source/foobar/foobar.cpp 将不起作用,因为 source/foobar/foobar.cpp 将与 foobar.cpp 进行比较>.这可以通过使用 get_source_file_property()LOCATION 属性并设置 _file 是完整路径的前提条件来解决.
    • 如果一个目标有两个或多个同名文件会怎样?
    • It only considers CMAKE_CXX_FLAGS (common to all build types), not CMAKE_CXX_FLAGS_<DEBUG|RELEASE> etc.
    • The macros can only handle one flag at a time
    • 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.
      • What happens if a target have two or more files with the same name?

      其中大部分应该很容易修复.

      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.

      这篇关于CMake - 删除单个翻译单元的编译标志的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:CMake - 删除单个翻译单元的编译标志

基础教程推荐