使用cmake处理头文件依赖关系

57

我正在一个小的C++项目中使用CMake,目前它运行得很好...只有一个问题 :x

当我更改一个头文件时,通常需要重新编译一些源文件(直接或间接包含它的那些文件),但似乎cmake只检测到某些需要重新编译的源文件,导致状态不正确。我可以通过清除该项目并从头开始重建来解决此问题,但这违反了使用make实用程序的目标:只重新编译所需内容。

因此,我想我做错了什么。

我的项目非常简单:

  • 一个顶级目录,其中包含所有资源,主CMakeLists.txt位于其中
  • 一个“include”目录,其中包含所有公共头文件(在各种子目录中)
  • 一个“src”目录,其中包含源文件的所有子目录,src CMakeLists.txt位于其中
  • “src”目录中每个子目录一个CMakeLists.txt

主目录有:

cmake_minimum_required(VERSION 2.8)

project(FOO)

set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)

# Compiler Options
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++0x -Wall -Wextra -Werror")

include_directories($(FOO_SOURCE_DIR)/include)

add_subdirectory(src)

"src"目录:

add_subdirectory(sub1)
add_subdirectory(sub2)
add_subdirectory(sub3)
add_subdirectory(sub4)

add_executable(foo main.cpp)

target_link_libraries(foo sub1 sub2 sub3 sub4)

sub4 依赖于 sub3,而 sub3 又依赖于 sub2sub2 又依赖于 sub1

以下是一个子目录(sub3)的示例:

set(SUB3_SRCS
    File1.cpp
    File2.cpp
    File3.cpp
    File4.cpp
    File5.cpp
    File6.cpp
    )

add_library(sub3 ${SUB3_SRCS})

target_link_libraries(sub3 sub1 sub2)

如果有人能指出我的错误,我会非常感激。在这里搜索或查看CMake文档都没有发现问题,所以我猜它很容易解决,或者应该直接运行...

(参考资料:我在MSYS上使用CMake版本2.8.2)

编辑

感谢Bill的建议,我已经检查了由CMake生成的depend.make文件,确实缺失(严重缺失)。这是一个例子:

src/sub3/CMakeFiles/sub3.dir/File1.cpp.obj: ../src/sub3/File1.cpp

是的,就是这样,所有的包都没有被引用 :x


1
更多关于未正确编译的文件的详细信息会很有帮助。例如,CMake中的依赖扫描器可以遍历包含另一个头文件的头文件,并正确触发对依赖源文件的重新编译。您能分享一个最简示例来说明您遇到这种行为吗? - Marcus D. Hanwell
@Marcus:这个问题比较棘手,我想把我的项目放在这里,但是有点太大了。从你的回答中,我理解到这不应该发生...我忘了说明我是在Windows上操作的,从MSYS(基于MinGw)shell调用,这可能是一个问题吗?另外,我也使用cmake在MSYS上构建LLVM/Clang项目,但从未遇到过这个问题。 - Matthieu M.
3
听起来可能是Windows/MSYS上依赖项扫描器的一个错误。我在那个环境下工作不多,想知道你是否曾经在Linux/Mac或MSVC项目中看到过类似的行为。 - Marcus D. Hanwell
@Marcus:很不幸,我家里只有Windows电脑,所以我从未在其他环境中见过它,因为我不使用它们。 - Matthieu M.
你尝试过在 set(SUB3_SRCS …) 中列出头文件吗?我总是这样做,而且从未遇到过任何类似的问题。 - emlai
5个回答

20
你应该查看你二进制树中的depend.make文件。它在CMakeFiles/target.dir/depend.make中。尝试找到其中一个缺少你认为应该有的.h文件的文件,然后为CMake创建一个错误报告或发送电子邮件至CMake邮件列表。

啊,谢谢。我搜索了依赖项但找不到它们在哪里,我会检查这个文件的。 - Matthieu M.
2
终于有时间检查了一下,depend.make 基本上表明一个 '.obj' 只依赖于生成它的源文件,完全不涉及包含文件。... - Matthieu M.

16

我遇到了同样的问题。在将include_directories()中的路径从绝对路径改为相对路径后,它添加了适当的依赖项。

看起来CMake试图猜测哪些头文件是系统相关的,哪些是项目相关的。我怀疑以/开头的目录会被传递给-isystem /some/path,因此不会出现在生成的依赖项中。

如果你不能用相对路径替换${FOO_SOURCE_DIR},可以尝试使用适当的CMake函数计算相对路径。例如:

file(RELATIVE_PATH FOO_SOURCE_REL_DIR
     ${CMAKE_CURRENT_SOURCE_DIR}
     ${FOO_SOURCE_DIR}/.)
include_directories(${FOO_SOURCE_REL_DIR}/include)

3
显然,cmake会从依赖树中删除系统包含路径(感谢@ony的提示)。这在大多数情况下可能是有意义的,但有时cmake不知道编译器认为什么是系统路径或不是系统路径。我们正在使用一个自定义的gcc构建,它忽略了/usr/include,但是cmake认为它没有被忽略。要强制cmake将/usr/include作为不被优化的依赖项,请尝试这个技巧:在路径前加上/.
我试图让所有库依赖项都使用cmake的依赖项功能,包括某些在Linux上默认未安装甚至不可用的“第三方”库。例如,Z-lib压缩。
如果Z lib包含在/usr/include中,则以下接口目标将无法正常工作,否则可以。
find_package(ZLIB REQUIRED)
message(status "found zlib ${ZLIB_LIBRARIES}")
message(status "found zlib includes ${ZLIB_INCLUDE_DIRS}")
target_link_libraries(zlib_target INTERFACE ${ZLIB_LIBRARIES})
target_include_directories(zlib_target INTERFACE ${ZLIB_INCLUDE_DIRS})

我把最后一行改成了

target_include_directories(zlib_target INTERFACE /.${ZLIB_INCLUDE_DIRS})

并且它奏效了。现在依赖于 zlib_target 的目标将在编译期间自动获得 -I/./usr/include


3

您是在添加cpp文件的includes之前还是之后运行了cmake?

我也曾遇到过这个问题,重新运行cmake可以解决它。我是在添加include之后才运行的cmake。


0

确保缺失头文件的include语句位于第一条程序指令之前。在我的情况下,cmake depend.make没有包含头文件,因为它跟随了第一条程序指令。


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