如何使用CMake正确添加包含目录

Mat*_* M. 208 c++ ubuntu cmake

大约一年前,我询问了CMake中的头依赖关系.

我最近意识到问题似乎是CMake认为那些头文件是项目外部的.至少,在生成Code :: Blocks项目时,头文件不会出现在项目中(源文件会出现).因此,在我看来,CMake认为这些标题是项目的外部,并且不会在依赖项中跟踪它们.

在CMake教程中快速搜索只指出include_directories哪个似乎没有我想做的...

向CMake发出信号通知特定目录是否包含要包含的标头的正确方法是什么,并且生成的Makefile应该跟踪这些标头?

Sir*_*ius 226

必须做两件事.

首先添加要包含的目录:

target_include_directories(test PRIVATE ${YOUR_DIRECTORY})
Run Code Online (Sandbox Code Playgroud)

如果您在没有支持的情况下遇到非常旧的CMake版本(2.8.10或更早版本)target_include_directories,您也可以使用旧版include_directories:

include_directories(${YOUR_DIRECTORY})
Run Code Online (Sandbox Code Playgroud)

然后,您还必须将头文件添加到当前目标的源文件列表中,例如:

set(SOURCES file.cpp file2.cpp ${YOUR_DIRECTORY}/file1.h ${YOUR_DIRECTORY}/file2.h)
add_executable(test ${SOURCES})
Run Code Online (Sandbox Code Playgroud)

这样,头文件将在Makefile中显示为依赖项,例如在生成的Visual Studio项目中,如果生成一个.

如何将这些头文件用于多个目标:

set(HEADER_FILES ${YOUR_DIRECTORY}/file1.h ${YOUR_DIRECTORY}/file2.h)

add_library(mylib libsrc.cpp ${HEADER_FILES})
target_include_directories(mylib PRIVATE ${YOUR_DIRECTORY})
add_executable(myexec execfile.cpp ${HEADER_FILES})
target_include_directories(myexec PRIVATE ${YOUR_DIRECTORY})
Run Code Online (Sandbox Code Playgroud)

  • @donturner您不必将“.h”文件添加到“add_executable”中。但是,它确实有一个很好的好处,可以使文件显示在“Visual Studio”项目中的预期位置。`Makefiles` 使用内部 `cmake -E cmake_depends` 从源文件生成依赖项(跳过 `add_executable` 中的头文件)。此扫描仪存在已知的[问题](https://gitlab.kitware.com/cmake/cmake/issues/16277)。另外,“CMake 的 Makefile 生成器依赖项扫描器仅进行近似预处理。”计算头包含在内,此类事情将不起作用。 (4认同)
  • 这是错误的,你应该_get_使用`include_directories`而不是`target_include_directories`.前者为该目录中的所有目标递归设置它; 而后者则将其设定为目标.执行前者会破坏CMake中目标图的概念,而是依赖于文件层次结构的副作用. (2认同)
  • 很多次我试图找到这个答案,但只能找到类似问题的答案,并且建议使用 GLOB,我最终放弃寻找。如果这个答案包含/链接到其他问题或者它们的措辞更好以明显不同,那将非常方便。 (2认同)

Rei*_*ica 65

首先,您使用include_directories()告诉CMake将目录添加-I到编译命令行.其次,列出您add_executable()add_library()电话中的标题.

例如,如果您的项目源是src,并且您需要标题include,您可以这样做:

include_directories(include)

add_executable(MyExec
  src/main.c
  src/other_source.c
  include/header1.h
  include/header2.h
)
Run Code Online (Sandbox Code Playgroud)

  • @ColinDBennett您不必为了依赖性原因列出它们 - 如果不这样做,CMake会很好地计算构建依赖性.但是如果列出它们,它们将被视为项目的一部分,并将在IDE中列出(这是问题的主题). (53认同)
  • 你真的需要在`add_executable`中添加标题吗?我以为CMake会自动找出包含文件依赖项. (18认同)

小智 60

项目结构

\n
.\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 CMakeLists.txt\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 external //We simulate that code is provided by an "external" library outside of src\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 CMakeLists.txt\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 conversion.cpp\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 conversion.hpp\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 README.md\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 src\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 CMakeLists.txt\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 evolution   //propagates the system in a time step\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 CMakeLists.txt\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 evolution.cpp\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 evolution.hpp\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 initial    //produces the initial state\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 CMakeLists.txt\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 initial.cpp\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 initial.hpp\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 io   //contains a function to print a row\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 CMakeLists.txt\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 io.cpp\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 io.hpp\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.cpp      //the main function\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 parser   //parses the command-line input\n\xe2\x94\x82       \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 CMakeLists.txt\n\xe2\x94\x82       \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 parser.cpp\n\xe2\x94\x82       \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 parser.hpp\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 tests  //contains two unit tests using the Catch2 library\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 catch.hpp\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 CMakeLists.txt\n    \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 test.cpp\n
Run Code Online (Sandbox Code Playgroud)\n

怎么做

\n

1. 顶层 CMakeLists.txt 与配方 1 非常相似,使用函数和宏进行代码重用

\n
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)\n  \nproject(recipe-07 LANGUAGES CXX)\n\nset(CMAKE_CXX_STANDARD 11)\nset(CMAKE_CXX_EXTENSIONS OFF)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\ninclude(GNUInstallDirs)\nset(CMAKE_ARCHIVE_OUTPUT_DIRECTORY\n  ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})\nset(CMAKE_LIBRARY_OUTPUT_DIRECTORY\n  ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})\nset(CMAKE_RUNTIME_OUTPUT_DIRECTORY\n  ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})\n\n# defines targets and sources\nadd_subdirectory(src)\n\n# contains an "external" library we will link to\nadd_subdirectory(external)\n\n# enable testing and define tests\nenable_testing()\nadd_subdirectory(tests)\n
Run Code Online (Sandbox Code Playgroud)\n

2.目标和源在src/CMakeLists.txt中定义(转换目标除外)

\n
add_executable(automata main.cpp)\n  \nadd_subdirectory(evolution)\nadd_subdirectory(initial)\nadd_subdirectory(io)\nadd_subdirectory(parser)\n\ntarget_link_libraries(automata\n  PRIVATE\n    conversion\n    evolution\n    initial\n    io\n    parser\n  )\n
Run Code Online (Sandbox Code Playgroud)\n

3.转换库定义在external/CMakeLists.txt中

\n
add_library(conversion "")\n\ntarget_sources(conversion\n  PRIVATE\n    ${CMAKE_CURRENT_LIST_DIR}/conversion.cpp\n  PUBLIC\n    ${CMAKE_CURRENT_LIST_DIR}/conversion.hpp\n  )\n\ntarget_include_directories(conversion\n  PUBLIC\n    ${CMAKE_CURRENT_LIST_DIR}\n  )\n
Run Code Online (Sandbox Code Playgroud)\n

4. src/CMakeLists.txt 文件添加更多子目录,这些子目录又包含 CMakeLists.txt 文件。它们的结构都很相似;src/evolution/CMakeLists.txt 包含以下内容:

\n
add_library(evolution "")\n\ntarget_sources(evolution\n  PRIVATE\n    ${CMAKE_CURRENT_LIST_DIR}/evolution.cpp\n  PUBLIC\n    ${CMAKE_CURRENT_LIST_DIR}/evolution.hpp\n  )\ntarget_include_directories(evolution\n  PUBLIC\n    ${CMAKE_CURRENT_LIST_DIR}\n  )\n
Run Code Online (Sandbox Code Playgroud)\n

5.单元测试注册在tests/CMakeLists.txt中

\n
add_executable(cpp_test test.cpp)\n\ntarget_link_libraries(cpp_test evolution)\n\nadd_test(\n  NAME\n    test_evolution\n  COMMAND\n    $<TARGET_FILE:cpp_test>\n  )\n
Run Code Online (Sandbox Code Playgroud)\n

如何运行它

\n
$ mkdir -p build\n$ cd build\n$ cmake ..\n$ cmake --build .\n
Run Code Online (Sandbox Code Playgroud)\n

参考: https: //github.com/sun1211/cmake_with_add_subdirectory

\n

  • 该问题没有询问任何有关单元测试或使用第三方项目的问题。对我来说,这个答案的这些部分似乎是不必要的噪音。当然,它在要求提供该信息的问题中可能很有用,但这里的情况并非如此。 (3认同)

bru*_*iuz 12

如果将CMake与其他创建Makefile的方法(例如make或qmake)进行比较,则CMake更像是一种脚本语言.它不像Python那么酷,但仍然如此.

如果在各种开源项目中查看人们如何包含目录,就没有像" 正确的方法 " 这样的东西.但有两种方法可以做到这一点.

  1. 粗体include_directories会将目录附加到当前项目以及您将通过一系列add_subdirectory命令追加的所有其他后代项目.有时人们会说这种做法是遗留的.

  2. 更优雅的方法是使用target_include_directories.它允许为特定项目/目标附加目录,而不会(可能)不必要的继承或各种包含目录的冲突.还允许执行甚至是微妙的配置,并为此命令附加以下标记之一.

PRIVATE - 仅用于此指定的构建目标

PUBLIC - 将其用于指定目标和与此项目链接的目标

接口 - 仅用于与当前项目链接的目标

PS:

  1. 这两个命令都允许将目录标记为SYSTEM,以提示指示目录将包含警告不是您的业务.

  2. 类似的答案是其他命令对target_compile_definitions/add_definitions, target_compile_options/CMAKE_C_FLAGS

  • “将向当前项目以及您将通过一系列 add_subdirectory 附加的所有其他后代项目附加一个目录”——不幸的是,_这是错误的_。事实是,它适用于相同的所有目标(甚至是那些在“include_directories”调用之前的目标)以及“add_subdirectories”中在调用之后(而不是之前)的目标......这就是为什么我们说它是遗产。**永远不要使用`include_directories` (4认同)

off*_*555 7

添加include_directories("/your/path/here")

这将类似于gcc使用-I/your/path/here/option 调用。

确保在路径两边加上双引号。其他人没有提及,这让我呆了2天。因此,此答案适用于刚接触CMake且非常困惑的人。


Sey*_*aki 6

我有同样的问题。

我的项目目录如下:

    --project
    ---Classes
    ----Application
    -----.h and .c files
    ----OtherFolders
    --main.cpp
Run Code Online (Sandbox Code Playgroud)

以及我以前在所有这些文件夹中包括的文件:

    file(GLOB source_files
            "*.h"
            "*.cpp"
            "Classes/*/*.cpp"
            "Classes/*/*.h"
    )

    add_executable(Server ${source_files})
Run Code Online (Sandbox Code Playgroud)

它完全有效。

  • 记住 cmake 是一个“构建系统生成器”而不是使用文件 glob 的“构建系统”在现代 cmake(CMake 版本 3.0 及更高版本)中不是一个好主意,因为文件 glob 是在“构建”时间而不是“构建”时评估的系统生成时间。请参阅链接:https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1 (3认同)

小智 5

你有两个选择。

老人:

include_directories(${PATH_TO_DIRECTORY})
Run Code Online (Sandbox Code Playgroud)

和新的

target_include_directories(executable-name PRIVATE ${PATH_TO_DIRECTORY})
Run Code Online (Sandbox Code Playgroud)

要使用target_include_directories,您需要定义可执行文件 - add_executable(executable-name sourcefiles)

所以你的代码应该看起来像

add_executable(executable-name sourcefiles)
target_include_directories(executable-name PRIVATE ${PATH_TO_DIRECTORY})
Run Code Online (Sandbox Code Playgroud)

您可以在此处阅读更多信息https://cmake.org/cmake/help/latest/command/target_include_directories.html