CMake:当我尝试添加源目录本身或其子目录之一时,target_include_directories() 会打印错误

2021-12-26 00:00:00 cmake c++ header-only

我正在编写一个 C++ 库(仅限头文件)并且正在使用 CMake 生成我的(Visual Studio)项目和解决方案文件.我也在编写一个测试套件,它是同一个 CMake 项目的一部分.

当我在代表我的仅标头库的目标上调用 target_include_directories() 时,我的问题出现了,这样我的库的使用者就可以找到它的头文件.我收到以下错误消息(即使没有中止生成).

CMakeLists.txt 中的 CMake 错误:目标字体" INTERFACE_INCLUDE_DIRECTORIES 属性包含路径:D:/Projects/GPC/fonts/include"这是源目录中的前缀.

(D:/Projects/GPC/Fonts 是我的库项目的顶级目录.顺便说一句,如果我将头文件移动到顶级目录,问题仍然存在.)

我的 CMakeLists.txt 中的违规行是这样的(为简单起见而改编):

target_include_directories(字体接口"${CMAKE_CURRENT_SOURCE_DIR}/include")

我不明白我做错了什么.如果没有 target_include_directories(),消费者项目的代码就不能包含我的头文件(除非以安装形式存在,但我还没有做到这一点,无论如何我希望能够从它的构建树中使用我的库,无需安装.)

我觉得这里缺少一些基本的东西;但我已经搜索了几个小时没有找到解决方案或解释.

解决方案

问题的根源不是target_include_directories命令本身,而是install 在源路径中具有公共或接口包含目录前缀的目标.>

虽然在从头开始构建库时使用绝对路径是非常好的和可取的,但引入该库的预构建版本的第三方库可能希望使用不同的包含路径.毕竟,您不希望所有用户都镜像您的构建机器的目录结构,只是为了在正确的包含路径中结束.

CMake 的打包机制为这两者提供了支持用例:您可以直接从构建树中拉入库(即,检查源代码,构建它,并将 find_package() 指向目录),或从安装目录(运行make INSTALL 将构建的内容复制到安装目录并将 find_package() 指向 那个 目录).后一种方法需要可重定位(即,我在我的机器上构建和安装,将结果目录发送给您,您将能够在您的机器上从不同的目录结构使用它),而前者不是.

这是一个非常巧妙的功能,但是您必须在设置包含目录时考虑到它.引用 target_include_directories 的手册:

<块引用>

包含目录的使用要求通常不同构建树和安装树.BUILD_INTERFACEINSTALL_INTERFACE 生成器表达式可用于描述根据使用地点分开使用要求.相对的INSTALL_INTERFACE 表达式中允许使用路径,并且相对于安装前缀进行解释.例如:

target_include_directories(mylib PUBLIC$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/mylib>$<INSTALL_INTERFACE:include/mylib># <前缀>/include/mylib)

BUILD_INTERFACEINSTALL_INTERFACE 生成器表达式完成所有魔术:

<块引用>

$

使用install(EXPORT)导出属性时...的内容,否则为空.

$

... 的内容,当使用 export() 导出属性时,或者当目标被同一构建系统中的另一个目标使用时.否则扩展为空字符串.

I am writing a C++ library (header-only) and am using CMake to generate my (Visual Studio) project and solution files. I'm also writing a test suite, which is part of the same CMake project.

My problem occurs when I call target_include_directories() on the target that represents my header-only library, so that consumers of my library may find its header files. I get the following error message (even though generation is NOT aborted).

CMake Error in CMakeLists.txt:
  Target "Fonts" INTERFACE_INCLUDE_DIRECTORIES property contains path:

    "D:/Projects/GPC/fonts/include"

  which is prefixed in the source directory.

(D:/Projects/GPC/Fonts being the top-level directory of my library project. Btw the problem remains if I move my header files to the top directory.)

The offending line in my CMakeLists.txt is this (adapted for simplicity):

target_include_directories(Fonts INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")

I do not understand what I'm doing wrong. Without target_include_directories(), code of consumer projects simply can't include my header files (unless in installed form, but I haven't gotten to that yet, and in any case I want to be able to use my library from its build tree, without installation.)

I feel like I'm missing something basic here; yet I've searched for hours without finding a solution or explanation.

解决方案

The origin of the problem is not the target_include_directories command itself, but the attempt to install a target that has a public or interface include directory prefixed in the source path.

While it is perfectly fine and desirable to use absolute paths when building the library from scratch, a third party library that pulls in a prebuilt version of that library will probably want to use a different include path. After all, you do not want all of your users to mirror the directory structure of your build machine, just to end up in the right include path.

CMake's packaging mechanism provides support for both of these use cases: You may pull in a library directly from the build tree (that is, check out the source, build it, and point find_package() to the directory), or from an install directory (run make INSTALL to copy built stuff to the install directory and point find_package() to that directory). The latter approach needs to be relocatable (that is, I build and install on my machine, send you the resulting directory and you will be able to use it on your machine from a different directory structure), while the former is not.

This is a very neat feature, but you have to account for it when setting up the include directories. Quoting the manual for target_include_directories:

Include directories usage requirements commonly differ between the build-tree and the install-tree. The BUILD_INTERFACE and INSTALL_INTERFACE generator expressions can be used to describe separate usage requirements based on the usage location. Relative paths are allowed within the INSTALL_INTERFACE expression and are interpreted relative to the installation prefix. For example:

target_include_directories(mylib PUBLIC  
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/mylib>  
    $<INSTALL_INTERFACE:include/mylib>  # <prefix>/include/mylib
)

The BUILD_INTERFACE and INSTALL_INTERFACE generator expressions do all the magic:

$<INSTALL_INTERFACE:...>

Content of ... when the property is exported using install(EXPORT), and empty otherwise.

$<BUILD_INTERFACE:...>

Content of ... when the property is exported using export(), or when the target is used by another target in the same buildsystem. Expands to the empty string otherwise.

相关文章