如何避免与使用FetchContent_MakeAvailable“导入”的目标发生冲突?

How can I avoid clashes with targets "imported" with FetchContent_MakeAvailable?

提问人:einpoklum 提问时间:7/11/2022 更新时间:7/12/2022 访问量:918

问:

假设我正在编写一个应用程序,并使用 CMake 管理其构建;我还想通过 FetchContent 机制使用库 mylib。

现在,我自己定义了一堆目标,mylib 的也是如此。如果我要安装 mylib,那么 ,我只会得到它导出的目标,甚至那些目标也会以(至少通常是这样)为前缀。但是使用 FetchContent 时,我的应用程序和 mylib(内部和导出目标)目标都在“全局命名空间”中,并且可能会发生冲突。CMakeLists.txtCMakeLists.txtfind_package(mylib)mylib::

那么,除了精心命名我自己的应用程序的所有目标之外,我还能做些什么来区分这些目标呢?

如果有可能以某种方式将所有 mylib 目标“推入”我选择的命名空间,我真的很喜欢它。


注意:涉及:使用 CMake FetchContent 时如何避免命名空间冲突?

cmake 命名空间 name-clash fetchContent

评论

1赞 Alex Reinking 7/11/2022
你不能。上游有一个关于添加命名空间以解决此问题的未解决问题,但尚未实现。由于这样的问题,我真的很回避 FetchContent。
0赞 einpoklum 7/11/2022
@AlexReinking:我自己不喜欢它,但我的图书馆的用户想要它......

答:

1赞 einpoklum 7/11/2022 #1

正如@AlexReinking,事实上,克雷格·斯科特(Craig Scott)所建议的那样,目前没有像样的解决方案。

您可以遵循以下 CMake 问题,通过这些问题可能会实现解决方案:

  • #22687项目级命名空间(更新)
  • #16414嵌套项目中目标名称的命名空间支持(影响上述内容的较长讨论,推荐阅读)

评论

1赞 Alex Reinking 7/11/2022
Craig 在一次演讲中说,在当前 CMake 时代(直到上述问题得到解决之前),手动命名项目名称(例如、、等)并添加别名(例如、、等)是最佳实践。HalideHalide_RuntimeHalide_ImageIOHalide::HalideHalide::RuntimeHalide::ImageIO
0赞 Alex Reinking 7/11/2022
他在演讲的这一部分中提出了关于安装组件名称的观点。
0赞 einpoklum 7/11/2022
@AlexReinking: 1. 为什么两者都这样做,而不是一开始就在内部使用“命名空间”目标?2. 您通常无法控制这一点,因为子目录/子模块等,甚至可能包含的文件可能会创建自己的目标,而这些目标不遵循此约定。还是我错了?
0赞 Alex Reinking 7/11/2022
对不起,这太长了,无法发表评论。我用完了字符。如果你愿意,我可以为这个问题添加一个更长的答案。
0赞 einpoklum 7/11/2022
@AlexReinking:是的,请做。
3赞 Alex Reinking 7/12/2022 #2

在当前的 CMake (<=3.24) 世界中,没有其他黑盒项目中用于调整目标名称的功能,无论是通过 、 还是 。因此,就目前而言,您有责任避免目标、安装组件、测试名称以及其他任何可能成为问题的地方的名称冲突。find_packageadd_subdirectoryFetchContent

克雷格·斯科特(Craig Scott)在CppCon 2019的(非常好)演讲中说了同样多的话,请参阅此处:https://youtu.be/m0DwB4OvDXk?t=2186

他建议的约定是使用以 . 为前缀的名称。他不建议从字面上使用 ,我也不会,因为这样做会使代码更难阅读(这对于理解第 3 方构建非常有用)。SomeProj_${PROJECT_NAME}_grep

然而,要成为一个好人或公民,仅仅将你的目标命名为是不够的;您还必须提供一个目标 .这有几个原因:add_subdirectoryFetchContentSomeProj_TargetALIASSomeProj::Target

  1. 您从中导入的目标几乎肯定会被命名为 。库的使用者应该可以在不更改其代码的其他部分的情况下轻松切换。ALIAS 目标允许您在这两种情况下公开相同的接口。当 CMake 3.24 推出其新的重定向功能时,这将变得尤为紧迫。find_packageSomeProj::TargetFetchContentfind_packagefind_packageFetchContent
  2. CMake 的函数始终将包含的名称视为目标名称,如果目标不存在,则会引发 configure-time 错误。如果没有 ,它将优先被视为目标,但如果目标不存在,它将变成链接器标志。因此,最好链接到名称中带有的目标。target_link_libraries::::::
  3. 然而,只有和目标可能在其名称中。IMPORTEDALIAS::

点 (2) 和 (3) 足以让我定义别名。

不幸的是,许多(大多数?CMake 构建不是好的 FetchContent 公民,并且会炫耀这个约定。遵循这个约定可以减少你的项目与任何其他项目之间出现集成问题的机会,但显然不能防止两个第三方项目之间可能定义冲突目标的问题。在这些情况下,你只是运气不好。


定义一个名为 that will good with FetchContent 的库的示例:Target

add_library(SomeProj_Target ${sources})
add_library(SomeProj::Target ALIAS SomeProj_Target)
set_target_properties(
  SomeProj_Target
  PROPERTIES
  EXPORT_NAME Target
  OUTPUT_NAME Target  # optional: makes the file libTarget.so on disk
)

install(TARGETS SomeProj_Target EXPORT SomeProj_Targets)
install(EXPORT SomeProj_Targets NAMESPACE SomeProj::)

有关安装组件、包含路径和双重共享/静态导入的更完整示例,请参阅我的博客文章


请参阅这些上游问题,以跟踪这些问题的进度/讨论。

  • #22687项目级命名空间
  • #16414嵌套项目中目标名称的命名空间支持