【发布时间】:2014-11-20 04:32:53
【问题描述】:
我是 CMake 新手,对与 target_link_libraries() 相关的 PUBLIC、PRIVATE 和 INTERFACE 关键字有些困惑。文档提到它们可用于在一个命令中指定链接依赖项和链接接口。
链接依赖和链接接口到底是什么意思?
【问题讨论】:
标签: cmake
我是 CMake 新手,对与 target_link_libraries() 相关的 PUBLIC、PRIVATE 和 INTERFACE 关键字有些困惑。文档提到它们可用于在一个命令中指定链接依赖项和链接接口。
链接依赖和链接接口到底是什么意思?
【问题讨论】:
标签: cmake
如果您正在创建一个共享库并且您的源 cpp 文件 #include 另一个库的标头(例如,QtNetwork),但您的头文件不包含 QtNetwork 标头,那么 @ 987654327@ 是一个 PRIVATE 依赖项。
如果您的源文件和头文件包含另一个库的头文件,则它是PUBLIC 依赖项。
如果您的源文件以外的头文件包含另一个库的头文件,则它是INTERFACE 依赖项。
PUBLIC 和 INTERFACE 依赖项的其他构建属性会传播到使用库。 http://www.cmake.org/cmake/help/v3.0/manual/cmake-buildsystem.7.html#transitive-usage-requirements
【讨论】:
PUBLIC 或PRIVATE 很重要?它有什么变化?
PRIVATE 不会将您的依赖项添加到链接到您的库的项目中。它更干净,并且还避免了您的依赖项和用户的依赖项之间可能发生的冲突。
PUBLIC 依赖,而不是 INTERFACE 依赖项。
@steveire 接受的答案很棒。我只是想添加一个表格以快速查看差异:
.-----------.------------------.----------------.
| | Linked by target | Link interface |
:-----------+------------------+----------------:
| PUBLIC | X | X |
:-----------+------------------+----------------:
| PRIVATE | X | |
:-----------+------------------+----------------:
| INTERFACE | | X |
'-----------'------------------'----------------'
【讨论】:
不是我的创意,但 extremely useful explanation 帮助我了解了情况。下面引用最重要的部分以供参考:
- 当 A 将 B 链接为 PRIVATE 时,表示 A 在其实现中使用 B,但 B 在 A 的公共 API 的任何部分中都没有使用。任何 调用 A 的代码不需要直接引用 来自 B 的任何东西。这方面的一个例子可能是网络库 A 可以构建为使用多种不同 SSL 中的一种 内部库(B 代表)。 A 呈现统一的 客户端代码的接口,它不引用任何 内部 SSL 数据结构或函数。客户端代码会有 不知道 A 正在使用什么 SSL 实现 (B),也不知道
客户端代码需要关心。- 当 A 将 B 链接为 INTERFACE 时,表示 A 在其实现中没有使用 B,但 B 在 A 的公共 API 中使用。代码 调用 A 可能需要引用来自 B 的事物,以便 拨打此类电话。这方面的一个例子是一个接口库,它 只是将调用转发到另一个库,但实际上并没有 在通过的途中引用对象,而不是通过指针或 参考。另一个例子是 A 在 CMake 中定义为 接口库,这意味着它本身没有实际的实现, 它实际上只是其他库的集合(我是 在这里可能过于简单,但你明白了)。
- 当 A 将 B 链接为 PUBLIC 时,它本质上是 PRIVATE 和 INTERFACE 的组合。它说 A 在其实现中使用 B 并且 B 也用于 A 的公共 API。
首先考虑这对包含搜索路径意味着什么。如果有什么 针对 A 的链接,它还需要来自 B 的任何包含搜索路径,如果 B 在 A 的公共 API 中。因此,如果 A 链接 B 作为 PUBLIC 或 INTERFACE,则为目标 B 定义的任何标头搜索路径也将 适用于链接到 A 的任何内容。任何 PRIVATE 标头搜索路径 B 不会被传递到任何只链接到 A 的东西。 target_include_directories() 命令处理这个问题。情况与 编译标志与 target_compile_definitions() 类似地处理 和 target_compile_options()。
现在考虑实际涉及的库的情况。如果 A 是 一个共享库,那么 A 将在其中编码对 B 的依赖。 可以使用 Linux 上的 ldd、otool 等工具检查此信息 在 Mac 上和 Dependency Walker(又名dependency.exe)之类的东西上 视窗。如果其他代码直接链接到A,那么它也会有 将其编码为对 A 的依赖。但是,它不会有 对 B 的依赖,除非 A 将 B 作为 PUBLIC 或 INTERFACE 链接。到目前为止,那么 好的。但是,如果 A 是静态库,则情况会发生变化。 静态库不携带有关它们的其他库的信息 取决于。因此,当 A 链接 B 作为 PRIVATE 和另一个 目标 C 链接 A,CMake 仍会将 B 添加到库列表中 为 C 链接,因为 A 需要 B 的一部分,但 A 本身 没有将依赖项编码到其中。所以即使 B 是 A的内部实现细节,C仍然需要B添加到 链接器命令,CMake 可以方便地为您处理。
如果你仔细观察,你会注意到,当 A 将 B 链接为 PRIVATE,B 的包含目录从不传播 链接到 A 的东西,但如果 A 是静态库,则 B 的 链接 表现得好像关系是 PUBLIC。静态库的这种 PRIVATE-becomes-PUBLIC 行为仅适用于 这 链接,而不是其他依赖项(编译器选项/标志和包含搜索路径)。这一切的结果是,如果你选择 PRIVATE、PUBLIC 或 INTERFACE 基于点中的解释 以上点,然后 CMake 将确保依赖项传播到 需要它们的地方,无论库是静态的还是 共享。当然,这确实取决于您开发人员不会错过 任何依赖关系或指定错误的 PRIVATE/PUBLIC/INTERFACE 关系。
【讨论】:
有些答案只说明何时使用 PRIVATE/PUBLIC/INTERFACE,但影响被忽略。参考:CMake-Public-Private-Interface
公开
PUBLIC 之后的所有对象都将用于链接到当前目标,并为与当前目标具有依赖关系的其他目标提供接口。
私人
PRIVATE 之后的所有对象将仅用于链接到当前目标。
界面
INTERFACE 之后的所有对象将仅用于为与当前目标具有依赖关系的其他目标提供接口。
【讨论】:
其他帖子已经回答了 PUBLIC/PRIVATE/INTERFACE 关键字的含义。我想再添加一个来澄清“链接依赖”和“链接接口”这两个术语。
“链接接口”一词可能来自于 LINK_INTERFACE_LIBRARIES 属性周围使用的旧 CMake 措辞,该词已被弃用,取而代之的是 INTERFACE_LINK_LIBRARIES。 请参阅 CMP0022 的描述,其中也使用术语“链接接口”。 https://cmake.org/cmake/help/latest/policy/CMP0022.html
INTERFACE_LINK_LIBRARIES 定义链接接口。
【讨论】: