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

huangapple go评论56阅读模式
英文:

How can I overwrite interface compile options coming from a linked library in CMake?

问题

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

```cmake
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是一个错误?

注意:对于我的实际情况,不需要的编译标志实际上来自深层次的依赖。例如,修改最小示例以使third-party具有到FooINTERFACE_LINK_LIBRARIES,而Foo具有INTERFACE_COMPILE_OPTIONS-fno-rtti,而不是third-party


<details>
<summary>英文:</summary>

I am running into a problem where my target has to link to a third party imported library that propagates a few unwanted compilation flags through `INTERFACE_COMPILE_OPTIONS` that I want to overwrite. Here&#39;s a minimal example to illustrate the problem:

```cmake
project(sample)

add_library(third-party INTERFACE IMPORTED)
set_target_properties(third-party PROPERTIES INTERFACE_COMPILE_OPTIONS &quot;-fno-rtti&quot;)

add_executable(sample main.cpp)
target_link_libraries(sample PUBLIC third-party)
target_compile_options(sample PUBLIC &quot;-frtti&quot;)

In this example, the unwanted compilation flag that is being propagated by the third-party imported target is -fno-rtti. Unfortunately, adding target_compile_options(sample PUBLIC &quot;-frtti&quot;) does not work because if I do make VERBOSE=1 to investigate the exact compilation command, for some reason, CMake decides to put my -frtti before the third-party library's -fno-rtti (and compilers tend to take the later when binary opposite flags contradict).

What is the correct way to overwrite compilation flags coming from a linked imported library in CMake? Is it a bug that CMake puts INTERFACE_COMPILE_OPTIONS from linked targets after its own COMPILE_OPTIONS thus giving priority to INTERFACE_COMPILE_OPTIONS from linked targets?

Note: For my real situation, the unwanted compilation flag actually came from dependencies layers down. E.g. modify the minimal example so that third-party has INTERFACE_LINK_LIBRARIES to Foo and Foo has INTERFACE_COMPILE_OPTIONS -fno-rtti instead of third-party.

答案1

得分: 2

这是您要翻译的部分:

My first question would be whether you should really be trying to be doing this. If a library specifies that a compile option is an interface compile option, then my first instinct is to trust them, and if I have doubts, to find out why- not to try to wrangle it away. Not that I'm doubting you, but if you haven't yet, you should try that kind of thinking.

如果库规定编译选项是接口编译选项,我的第一个问题是,您是否真的应该尝试这样做。我的第一反应是相信他们,如果我有疑虑,就要弄清楚为什么,而不是试图去处理它。并不是我怀疑您,但如果您还没有尝试,您应该尝试这种思考方式。

If you do that thinking and come to the conclusion that such a compile option shouldn't have been specified as being part of the INTERFACE of the target, then you should contact the maintainer of that CMake script for that depdendency and (humbly) explain why, requesting a change.

如果您经过思考得出结论,认为这样的编译选项不应该被指定为目标的 INTERFACE 的一部分,那么您应该联系依赖项的 CMake 脚本的维护者,并(谦卑地)解释原因,并请求更改。

As for why CMake puts your "-frtti" before the target's interface "-fno-rtti", see the docs for the COMPILE_OPTIONS target property:

至于为什么 CMake 将您的 "-frtti" 放在目标接口的 "-fno-rtti" 之前,请查看 COMPILE_OPTIONS 目标属性的文档:

The options will be added after flags in the CMAKE_<LANG>_FLAGS and CMAKE_<LANG>_FLAGS_<CONFIG> variables, but before those propagated from dependencies by the INTERFACE_COMPILE_OPTIONS property.

选项将添加到 CMAKE_<LANG>_FLAGSCMAKE_<LANG>_FLAGS_<CONFIG> 变量中的标志之后,但在通过 INTERFACE_COMPILE_OPTIONS 属性从依赖项传播的选项之前。

Okay, since you asked, if you really want to do something to fix this specific problem of yours without doing anything to touch the third party dependencies at whichever layer of transitive dependency it's at, I offer this hack:

好的,既然您提出了这个问题,如果您确实想要解决这个特定的问题,而不对第三方依赖项进行任何更改,无论它在传递性依赖关系的哪一层,我提供这个 hack

CMake does compile option de-duplications like so:

CMake 对编译选项进行去重处理,如下所示:

The final set of options used for a target is constructed by accumulating options from the current target and the usage requirements of its dependencies. The set of options is de-duplicated to avoid repetition.

用于目标的最终选项集是通过累积来自当前目标以及其依赖项的使用要求的选项来构建的。为了避免重复,选项集被去重处理。

From experimentation, the deduplication keeps the left-most / first copy of any duplicates.

从实验中得知,去重处理会保留任何重复项的最左侧/第一份副本。

So to hack your solution, just do this:

因此,要使用这个 hack 来解决问题,只需执行以下操作:

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

Since you said your flags get put earlier than the interface flags, this will cause your flags to be the first copy of anything that duplicates. So the hacky inserted "-fno-rtti" will be kept instead of the third-party library's, and then the compiler will do its thing and keep the last toggle value of a binary flag, which will then be "-frtti".

因为您说您的标志被放在接口标志之前,所以这将导致您的标志是重复项中的第一个副本。因此,这个 hack 插入的 "-fno-rtti" 将被保留,而不是第三方库的,然后编译器会执行其操作并保留二进制标志的最后切换值,然后为 "-frtti"。

英文:

My first question would be whether you should really be trying to be doing this. If a library specifies that a compile option is an interface compile option, then my first instinct is to trust them, and if I have doubts, to find out why- not to try to wrangle it away. Not that I'm doubting you, but if you haven't yet, you should try that kind of thinking.

If you do that thinking and come to the conclusion that such a compile option shouldn't have been specified as being part of the INTERFACE of the target, then you should contact the maintainer of that CMake script for that depdendency and (humbly) explain why, requesting a change.


<sup>Note: This first section was written before the asker further clarified their context. I leave it because it is informative and may be useful to future readers.</sup>

That being said, you could use list(REMOVE_ITEM) to solve this problem.

Example usage:

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

Don't let the long lines of code fool you. There's nothing complicated going on. It gets the target property to a variable, modifies the variable, and then writes the modified value back to the target property. It's only long because of choice of long variable names. I prefer variable names to be clear rather than terse in certain kinds of scenarios- this being one of them.

This will affect all targets that link with that target, whether directly or transitively. It could effect other targets in ways that you might not want. If that's the case, then read on to find out how to do the "override" for specific dependent targets instead of all of them.


As for why CMake puts your "-frtti" before the target's interface "-fno-rtti", see the docs for the COMPILE_OPTIONS target property:

> The options will be added after flags in the CMAKE_&lt;LANG&gt;_FLAGS and CMAKE_&lt;LANG&gt;_FLAGS_&lt;CONFIG&gt; variables, but before those propagated from dependencies by the INTERFACE_COMPILE_OPTIONS property.

Slightly related info: From a previous discussion with Craig Scott (one of the CMake maintainers), I learned that sometimes CMake uses such logic to favour certain prioritizations of include directory flags. It happens that compilers tend to search include directories from first to last (left to right), so the first one that gives a match for something is used, but they also tend to favour right-most / later flags when two flags are the binary opposite toggles of each other. As far as I know, CMake doesn't do anything special to handle that kind of nuance, and possibly made a decision in the past to favour certain logical prioritizations of include directory ordering.


Okay, since you asked, if you really want to do something to fix this specific problem of yours without doing anything to touch the third party dependencies at whichever layer of transitive dependency it's at, I offer this hack:

CMake does compile option de-duplications like so:

> The final set of options used for a target is constructed by accumulating options from the current target and the usage requirements of its dependencies. The set of options is de-duplicated to avoid repetition.

From experimentation, the deduplication keeps the left-most / first copy of any duplicates.

So to hack your solution, just do this:

target_compile_options(sample PUBLIC &quot;-fno-rtti;-frtti&quot;)

Since you said your flags get put earlier than the interface flags, this will cause your flags to be the first copy of anything that duplicates. So the hacky inserted "-fno-rtti" will be kept instead of the third-party library's, and then the compiler will do its thing and keep the last toggle value of a binary flag, which will then be "-frtti".

I see this as a hack, and I personally think such behaviour is surprising in a bad way. But it works to solve your problem here. Remember that discussion I mentioned I had with Craig Scott? I had it precisely because of this behaviour. link.


Note: If you are a project maintainer and want to add an interface compile option to one of your targets while enabling dependent targets to choose not to inherit that interface compile option, use the following approach suggested by Ben Boeckel (one of the CMake maintainers)

> something like $&lt;$&lt;NOT:$&lt;TARGET_PROPERTY:skip_this_flag&gt;&gt;:-fsome-flag&gt; can be used so that consuming targets can set their own skip_this_flag property to ignore it.

huangapple
  • 本文由 发表于 2023年3月1日 12:59:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/75599736.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定