如何覆盖来自CMake中链接库的接口编译选项?

hwamh0ep  于 2023-04-06  发布在  其他
关注(0)|答案(1)|浏览(204)

我遇到了一个问题,我的目标必须链接到一个第三方导入库,该库通过INTERFACE_COMPILE_OPTIONS传播了一些不需要的编译标志,我想覆盖这些标志。请参见下面的最小示例来说明这个问题:

project(sample)

add_library(third-party INTERFACE IMPORTED)
set_target_properties(third-party PROPERTIES INTERFACE_COMPILE_OPTIONS "-fno-rtti")

add_executable(sample main.cpp)
target_link_libraries(sample PUBLIC third-party)
target_compile_options(sample PUBLIC "-frtti")

在本例中,由third-party导入目标传播的不需要的编译标志是-fno-rtti。不幸的是,添加target_compile_options(sample PUBLIC "-frtti")不起作用,因为如果我使用make VERBOSE=1来调查确切的编译命令,出于某种原因,CMake决定将我的-frtti放在第三方库的-fno-rtti之前(当二进制相反标志冲突时,编译器倾向于采用后者)。
在CMake中覆盖来自链接导入库的编译标志的正确方法是什么?CMake将来自链接目标的INTERFACE_COMPILE_OPTIONS放在自己的COMPILE_OPTIONS之后,从而优先考虑来自链接目标的INTERFACE_COMPILE_OPTIONS,这是否是一个bug?
注意:对于我的真实的情况,不需要的编译标志实际上来自依赖关系层以下。例如,修改最小示例,使third-party具有INTERFACE_LINK_LIBRARIESFoo,并且Foo具有INTERFACE_COMPILE_OPTIONS-fno-rtti而不是third-party

eulz3vhy

eulz3vhy1#

我的第一个问题是你是否真的应该尝试这样做。如果一个库指定了一个编译选项是一个接口编译选项,那么我的第一直觉就是相信他们,如果我有疑问,找出原因-而不是试图把它赶走。不是我怀疑你,但是如果你还没有,你应该尝试那种想法。
如果您这样想并得出这样的结论,即这样的编译选项不应该被指定为目标的INTERFACE的一部分,那么您应该联系该CMake脚本的维护者,了解相关性,并(谦虚地)解释原因,请求更改。
注意:这第一部分是在提问者进一步澄清他们的上下文之前写的。我离开它是因为它信息丰富,可能对未来的读者有用。
也就是说,您可以使用list(REMOVE_ITEM)来解决这个问题。
示例用法:

add_library(third-party INTERFACE) # can be IMPORTED. shouldn't matter
set_target_properties(third-party PROPERTIES INTERFACE_COMPILE_OPTIONS "0;1;2;3;a;-fno-rtti;b;c;d")
get_target_property(third-party_interface_compile_options third-party INTERFACE_COMPILE_OPTIONS)
list(REMOVE_ITEM third-party_interface_compile_options "-fno-rtti")
# message("third-party_interface_compile_options: ${third-party_interface_compile_options}")
set_target_properties(third-party PROPERTIES INTERFACE_COMPILE_OPTIONS "${third-party_interface_compile_options}")

不要让长长的代码行欺骗了你。没有什么复杂的事情。它将目标属性获取为变量,修改变量,然后将修改后的值写回目标属性。它之所以长是因为选择了长变量名。在某些情况下,我更喜欢变量名清晰而不是简洁--这就是其中之一。
至于为什么CMake把你的“-frtti“放在目标的接口“-fno-rtti“之前,请参阅the COMPILE_OPTIONS target property的文档:
这些选项将添加在CMAKE_<LANG>_FLAGSCMAKE_<LANG>_FLAGS_<CONFIG>变量中的标志之后,但在通过INTERFACE_COMPILE_OPTIONS属性从依赖项传播的标志之前。
稍微相关的信息:根据之前与克雷格·斯科特的讨论(CMake维护者之一),我了解到有时CMake使用这种逻辑来支持include目录标志的某些优先级。(从左到右),所以第一个给出某个匹配的字符被使用但当两个标志是彼此的二进制相反切换时,它们也倾向于支持最右边/后面的标志。据我所知,CMake没有做任何特殊的事情来处理这种细微差别,并且可能在过去做出决定以支持包含目录排序的某些逻辑优先级。
好吧,既然你问了,如果你真的想做些什么来解决你的这个特定问题,而不想做任何事情来触及第三方依赖项,不管它在传递依赖项的哪一层,我提供这个***hack***:
CMake执行编译选项重复数据删除,如下所示:
用于目标的最终选项集是通过累积当前目标及其相关性的使用要求中的选项来构建的。该选项集将被删除重复项以避免重复。
根据实验,重复数据消除会保留任何重复数据的最左侧/第一个副本。
要破解您的解决方案,只需执行以下操作:

target_compile_options(sample PUBLIC "-fno-rtti;-frtti")

既然你说你的标志比接口标志早放,这将导致你的标志成为任何重复的标志的第一个副本,所以插入的“-fno-rtti“将被保留,而不是第三方库的,然后编译器将做它的事情,并保留二进制标志的最后一个toggle值,这将是“-frtti“。
我认为这是一个黑客,我个人认为这种行为是令人惊讶的坏的方式.但它的工作,以解决您的问题在这里.还记得我提到的讨论我与克雷格斯科特?我有它正是因为这种行为. link.
注意:如果您是一个项目维护者,并且希望向其中一个目标添加接口编译选项,同时使从属目标选择不继承该接口编译选项,请使用the following approach suggested by Ben Boeckel (one of the CMake maintainers)
可以使用类似$<$<NOT:$<TARGET_PROPERTY:skip_this_flag>>:-fsome-flag>的东西,以便消费目标可以设置自己的skip_this_flag属性来忽略它。

相关问题