CMake target_include_directories的作用范围含义

148

与CMake的target_include_directories相关的关键词PUBLICPRIVATEINTERFACE的含义是什么?


1
密切相关:https://dev59.com/f18e5IYBdhLWcg3wJXvm - emlai
2个回答

186

这些关键词用于告诉目标需要传递的包含目录列表何时使用。这里的何时指的是,如果这些包含目录是需要的:

  • 编译该目标本身。
  • 编译依赖于该目标的其他目标(例如使用其公共头文件)。
  • 在上述两种情况中都需要。

当CMake编译目标时,它会使用目标的INCLUDE_DIRECTORIESCOMPILE_DEFINITIONSCOMPILE_OPTIONS属性。当您在target_include_directories()等中使用PRIVATE关键字时,您告诉CMake填充那些目标属性。

当CMake检测到目标A和另一个目标B之间的依赖关系(例如当您使用target_link_libraries(A B)命令时),它会传递性地将B的使用要求传播到A目标。那些目标使用要求是任何依赖于B的目标必须满足的包含目录,编译定义等。它们由上面列出的属性的INTERFACE_*版本指定(如INTERFACE_INCLUDE_DIRECTORIES),并且通过调用target_*()命令时使用INTERFACE关键字进行填充。

PUBLIC关键字的含义大致相当于PRIVATE + INTERFACE

因此,假设您正在创建一个使用一些Boost头文件的库A。您可以执行以下操作:

  • target_include_directories(A PRIVATE ${Boost_INCLUDE_DIRS})如果您只在源文件(.cpp)或私有头文件(.h)中使用这些Boost头文件。
  • target_include_directories(A INTERFACE ${Boost_INCLUDE_DIRS})如果您不在源文件中使用这些Boost头文件(因此不需要它们来编译A)。我实际上想不出一个真实世界的例子。
  • target_include_directories(A PUBLIC ${Boost_INCLUDE_DIRS}) 如果你在 A 库的公共头文件中使用了那些 Boost 头文件,这些头文件既被包含在 A 的一些源文件中,也可能被包含在任何其他客户端代码中。
  • 有关此构建规范和用法要求的更多详细信息,请参见 CMake 3.0 文档中的属性


    43
    关于“INTERFACE”的真实世界例子,target_include_directories(libname INTERFACE include PRIVATE include/libname)意味着您可以直接包含库中的文件,但作为库的用户,您必须首先插入“libname/”。 - KaareZ
    2
    这个回答对我来说关于创建库有意义。但是对于一个可执行目标,如何调用target_include_directories呢? - Norman Pellet
    1
    @NormanPellet:如果您需要设置包含目录以查找这些可执行文件使用的头文件(例如:Boost::Program_options,如果您在main()函数中使用它来解析参数),则可以为可执行目标调用target_include_directories()。在这种情况下,您可能会使用PRIVATE关键字,因为这些文件是编译可执行文件本身所需的。我不知道在可执行文件上是否有INTERFACEPUBLIC的用途。 - TManhente
    这意味着在您的库中,您可以直接包含文件,但作为库的用户,您必须首先插入libname/。对于库中的实现文件可能是正确的,但对于库中的其他“公共”头文件则不行。如果include/libname/file1.h使用#include <file2.h>来包含include/libname/file2.h,当使用您的库的可执行文件包含file1.h(通过#include <libname/file1.h>)时,就会出现问题。因为include/libname不在可执行文件的包含搜索路径中,所以它无法找到file2.h - ToddR
    作为库的用户,您必须首先插入libname/。这是什么意思?您能否详细解释一下? - John
    1
    如果需要为库本身的编译设置一组头文件,但希望在发布库时包含不同版本的相同头文件,则INTERFACE选项可能很有用。发布的头文件可能已经剥离了保护IP等信息。
    • foo/interface/a.h - 这将提供给库的使用者,并且只包含使用库所需的内容
    • foo/include/a.h - 这将用于内部构建库,并且其内容类似于foo/interface/a.h,但具有额外的内容。
    - Vinny

    18

    INTERFACE、PUBLIC和PRIVATE关键字用于指定以下参数的作用范围。PRIVATE和PUBLIC项目将填充<target>的INCLUDE_DIRECTORIES属性。PUBLIC和INTERFACE项目将填充<target>的INTERFACE_INCLUDE_DIRECTORIES属性。以下参数指定包含目录。

    引用自文档:http://www.cmake.org/cmake/help/v3.0/command/target_include_directories.html

    使用我的话重新表述文档:

    • 您想将目录添加到目标包含目录列表中。
    • 使用PRIVATE选项,目录将添加到目标的包含目录中。
    • 使用INTERFACE选项,目标不会被修改,但是INTERFACE_INCLUDE_DIRECTORIES属性会被该目录扩展。该变量是库的公共包含目录列表。
    • 使用PUBLIC选项,会同时执行PRIVATE和INTERFACE两个选项的操作。

    6
    我阅读了CMAKE文档,但仍不清楚它们实际上是什么意思,在什么情况下使用(是用于生成make文件还是编译过程中的某个环节)? - Sirish
    @Sirish:我尝试重新表述文档,希望有所帮助。 - usr1234567
    2
    @usr1234567 如果我们没有指定关键字,那么这个关键字的默认值是什么?我看到很多项目只是使用target_link_librariestarget_include_directories而没有指定PRIVATEPUBLICINTERFACE关键字。 - IgNite
    1
    @IgNite 请参考https://cmake.org/pipermail/cmake/2016-May/063400.html,倒数第二段。这很复杂 :-) - usr1234567

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接