大约一年前,我询问了 header dependencies in CMake。
我最近意识到问题似乎是 CMake 认为这些头文件是项目外部的。至少,在生成 Code::Blocks 项目时,头文件不会出现在项目中(源文件会出现)。因此,在我看来,CMake 认为这些标头是项目外部的,并且不会在依赖项中跟踪它们。
CMake 教程中的快速搜索仅指向 include_directories
,这似乎没有按照我的意愿进行...
向 CMake 发出信号表明特定目录包含要包含的标头以及应由生成的 Makefile 跟踪这些标头的正确方法是什么?
.cbp
项目文件。现在,如果 cmake 依赖项扫描程序未能正确地将头文件识别为 Makefile 的依赖项,则有一些方法可以解决这个问题,但在某些情况下它会出错,因为它不包含完整的预处理器。
必须做两件事。
首先添加要包含的目录:
target_include_directories(test PRIVATE ${YOUR_DIRECTORY})
如果您遇到不支持 target_include_directories
的非常旧的 CMake 版本(2.8.10 或更早版本),您也可以改用旧版 include_directories
:
include_directories(${YOUR_DIRECTORY})
然后,您还必须将头文件添加到当前目标的源文件列表中,例如:
set(SOURCES file.cpp file2.cpp ${YOUR_DIRECTORY}/file1.h ${YOUR_DIRECTORY}/file2.h)
add_executable(test ${SOURCES})
这样,头文件将在 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})
首先,您使用 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
)
add_executable
添加标头吗?我以为 CMake 会自动找出包含文件的依赖项。
add_executable
的 CMakeList 中?因为这将文件的路径提供给给 CMake,而不是给编译器。 add_executable
的源文件参数是相对于当前源目录的。
添加include_directories("/your/path/here")
。
这类似于使用 -I/your/path/here/
选项调用 gcc
。
确保在路径周围加上双引号。其他人没有提到这一点,这让我坚持了 2 天。所以这个答案适用于那些对 CMake 非常陌生并且非常困惑的人。
如果将 CMake 与其他创建 Makefile 的方法(例如 make 或 qmake)进行比较,CMake 更像是一种脚本语言。它不像 Python 那样很酷,但仍然如此。
如果在各种开源项目中查看人们如何包含目录,则没有像“正确方法”这样的东西。但是有两种方法可以做到这一点。
粗略的 include_directories 会将一个目录附加到当前项目和您将通过一系列 add_subdirectory 命令附加的所有其他后代项目。有时人们说这种方法是遗留问题。更优雅的方法是使用 target_include_directories。它允许为特定项目/目标附加目录,而不会(可能)不必要的继承或各种包含目录的冲突。还允许执行甚至是细微的配置并为此命令附加以下标记之一。
PRIVATE - 仅用于此指定的构建目标
PUBLIC - 将其用于指定目标和与该项目链接的目标
INTERFACE -- 仅用于与当前项目链接的目标
PS:
这两个命令都允许将目录标记为 SYSTEM 以提示指定目录将包含警告不是您的业务。类似的答案是使用其他命令对 target_compile_definitions/add_definitions、target_compile_options/CMAKE_C_FLAGS
include_directories
调用之前出现的目标)以及 add_subdirectories
中在调用 之后出现的目标(不是之前)...这就是为什么我们说它是遗产。 永远不要使用 include_directories
项目结构
.
├── CMakeLists.txt
├── external //We simulate that code is provided by an "external" library outside of src
│ ├── CMakeLists.txt
│ ├── conversion.cpp
│ ├── conversion.hpp
│ └── README.md
├── src
│ ├── CMakeLists.txt
│ ├── evolution //propagates the system in a time step
│ │ ├── CMakeLists.txt
│ │ ├── evolution.cpp
│ │ └── evolution.hpp
│ ├── initial //produces the initial state
│ │ ├── CMakeLists.txt
│ │ ├── initial.cpp
│ │ └── initial.hpp
│ ├── io //contains a function to print a row
│ │ ├── CMakeLists.txt
│ │ ├── io.cpp
│ │ └── io.hpp
│ ├── main.cpp //the main function
│ └── parser //parses the command-line input
│ ├── CMakeLists.txt
│ ├── parser.cpp
│ └── parser.hpp
└── tests //contains two unit tests using the Catch2 library
├── catch.hpp
├── CMakeLists.txt
└── test.cpp
怎么做
1.顶层CMakeLists.txt与Recipe 1非常相似,代码重用函数和宏
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(recipe-07 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(GNUInstallDirs)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
# defines targets and sources
add_subdirectory(src)
# contains an "external" library we will link to
add_subdirectory(external)
# enable testing and define tests
enable_testing()
add_subdirectory(tests)
2.Targets和Sources在src/CMakeLists.txt中定义(转换目标除外)
add_executable(automata main.cpp)
add_subdirectory(evolution)
add_subdirectory(initial)
add_subdirectory(io)
add_subdirectory(parser)
target_link_libraries(automata
PRIVATE
conversion
evolution
initial
io
parser
)
3.转换库定义在external/CMakeLists.txt
add_library(conversion "")
target_sources(conversion
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/conversion.cpp
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/conversion.hpp
)
target_include_directories(conversion
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
4. src/CMakeLists.txt 文件添加了更多子目录,这些子目录又包含 CMakeLists.txt 文件。它们在结构上都相似; src/evolution/CMakeLists.txt 包含以下内容:
add_library(evolution "")
target_sources(evolution
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/evolution.cpp
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/evolution.hpp
)
target_include_directories(evolution
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
5.单元测试注册在tests/CMakeLists.txt
add_executable(cpp_test test.cpp)
target_link_libraries(cpp_test evolution)
add_test(
NAME
test_evolution
COMMAND
$<TARGET_FILE:cpp_test>
)
如何运行它
$ mkdir -p build
$ cd build
$ cmake ..
$ cmake --build .
参考:https://github.com/sun1211/cmake_with_add_subdirectory
我有同样的问题。
我的项目目录是这样的:
--project
---Classes
----Application
-----.h and .c files
----OtherFolders
--main.cpp
以及我曾经将文件包含在所有这些文件夹中的内容:
file(GLOB source_files CONFIGURE_DEPENDS
"*.h"
"*.cpp"
"Classes/*/*.cpp"
"Classes/*/*.h"
)
add_executable(Server ${source_files})
它完全奏效了。
CONFIGURE_DEPENDS
。
这对我有用:
set(SOURCE main.cpp)
add_executable(${PROJECT_NAME} ${SOURCE})
# target_include_directories must be added AFTER add_executable
target_include_directories(${PROJECT_NAME} PUBLIC ${INTERNAL_INCLUDES})
不要忘记包含 ${CMAKE_CURRENT_LIST_DIR}
。这就是给我带来问题的原因。
示例应该是这样的:
target_include_directories(projectname
PUBLIC "${CMAKE_CURRENT_LIST_DIR}/include"
)
PUBLIC 用于您希望包含在父项目中的依赖项。对于那些你不知道的人来说是私人的。
还有另一种选择:
set_property(
TARGET MyApp
APPEND PROPERTY
INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/src"
)
我正在使用 CLion,我的项目结构如下:
--main.cpp
--Class.cpp
--Class.h
--CMakeLists.txt
CMakeLists.txt
之前更改:
add_executable(ProjectName main.cpp)
CMakeLists.txt
在更改后:
add_executable(ProjectName main.cpp Class.cpp Class.h)
通过这样做,程序编译成功。
不定期副业成功案例分享
liba/CMakefile
和libb/CMakefile
中使用LIBROOT_HEADER_FILES
变量吗?include_directories
而不是target_include_directories
。前者为该目录中的所有目标递归地设置它;而后者将其设置为目标。做前者打破了 CMake 中目标图的概念,而是依赖于对文件层次结构的副作用。target_include_directories
的当前概念。如果您不同意这些更改,请随时邀请我聊天。.h
文件添加到add_executable
。但是,它确实具有使文件显示在预期位置的Visual Studio
项目中的好处。Makefiles
使用内部cmake -E cmake_depends
从源文件生成依赖项(跳过add_executable
中的头文件)。有已知的 issues 与此扫描仪。此外,`CMake 的 Makefile 生成器依赖项扫描程序仅进行近似预处理。` 计算的标头包含在内,这种事情将不起作用。