cmake/make (OBJECT) 依赖问题——当标头改变时不重建

Posted

技术标签:

【中文标题】cmake/make (OBJECT) 依赖问题——当标头改变时不重建【英文标题】:cmake/make (OBJECT) dependency issue--not rebuilding when header changes 【发布时间】:2019-05-15 18:34:01 【问题描述】:

我已经设法重现了我在一个更大的项目中遇到的问题。我认为这是我可以做到的最低限度

关键是我已经显式地在源列表中添加了一个标题,并且编辑它仍然不会导致重新编译任何内容。

~/src/test2/_build£ cat ../CMakeLists.txt
cmake_minimum_required (VERSION 3.14)
set (CMAKE_CXX_STANDARD 11)
# various explicit CXX sets are necessary for this tiny project and don't exist in larger original
project(moduletest CXX)
set (HEADER_DIR "$CMAKE_CURRENT_SOURCE_DIR")
add_subdirectory(submod)
add_library(moduletest_static STATIC "$<TARGET_OBJECTS:submod>")
set_target_properties(moduletest_static PROPERTIES LINKER_LANGUAGE CXX)

~/src/test2/_build£ cat ../submod/CMakeLists.txt
include_directories ($HEADER_DIR)
add_library(submod OBJECT compileme.cpp ../includeme.h)

~/src/test2/_build£ cat ../submod/compileme.cpp
#include "includeme.h"
int function()

  return 5;

输出如下:

~/src/test2/_build£ touch ../submod/compileme.cpp
~/src/test2/_build£ make
[ 50%] Building CXX object submod/CMakeFiles/submod.dir/compileme.cpp.o
[ 50%] Built target submod
[100%] Linking CXX static library libmoduletest_static.a
[100%] Built target moduletest_static
~/src/test2/_build£ touch ../includeme.h
~/src/test2/_build£ make
[ 50%] Built target submod
[100%] Built target moduletest_static

如果我在我的 cpp 文件中删除了 include_directories 的使用,而只删除了 #include "../includeme.h",那么无论我的 add_library 调用如何,一切都会正常工作。 (但这绝对不是我实际项目中的选择)

我还应该注意,“扫描目标子模块的依赖关系”有点像红鲱鱼。在我较大的项目中,我尝试触摸单个 cpp 文件,以便发生这种情况。其他应该编译的 cpp 文件仍然没有。

使用target_include_directories 并没有解决任何问题,无论绝对/相对路径如何。

cmake -GNinja .. 解决了问题

【问题讨论】:

顺便说一句,我无法通过2.83.14 重现它 嗯...你的 gcc 版本是什么? GNU Make 4.1, cmake 3.14.20190309-gba3e8, g++ (Ubuntu 7.4.0-1ubuntu1~18.04) 7.4.0 @R2RT - 我的都是一样的。 (好吧,我的 cmake 有今天的日期,因为我尝试重新编译它..但仍然是 3.14) 你没有两份includeme.h文件吗? 【参考方案1】:

我最初的回答是:

你很可能是在找错树了。源到报头 依赖项不是由 CMake 管理,而是由底层生成器管理,如 头文件不直接参与库编译。在你的 如果有责任检测脏头并重建 .cpp 文件。 标题可以在源中列出,但它只会使它们在 很少的 IDE 并在它们存在时添加完整性检查。

但对于GNU make 的情况,事实并非如此。较新的工具,例如 ninja 在编译代码时能够发出源到头的依赖关系。所以 CMake 只跟踪库到源的依赖关系,而底层工具跟踪源到标头。

对于make,CMake 使用其内部机制为对象 .cpp.o 文件创建depend.make 文件。该算法并不完美,include_directories 传递的绝对路径可能会失败。

作为参考,最初(在生成步骤之后)它会创建带有注释的 depend.make 文件:

# Empty dependencies file for submod.
# This may be replaced when dependencies are built.

一旦调用 make 并第一次构建对象,文件就会被填充:

# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.14

submod/CMakeFiles/submod.dir/compileme.cpp.o: ../includeme.h
submod/CMakeFiles/submod.dir/compileme.cpp.o: ../submod/compileme.cpp

感谢@Fred,让我阅读了所有相关内容。

这个问题也已经在 SO:Handling header files dependencies with cmake

上讨论过

和历史阅读:http://www.aosabook.org/en/cmake.html

【讨论】:

最低更新到 3.14,重新测试。我会研究其他的东西。我认为 add_library 在原始文件中没有损坏,但是使用了 include_directories .. @zzxyz add_library 语法很简单,它需要名称、(可选)类型和来源。 submod 不是来源。 cmake.org/cmake/help/v3.14/command/add_library.html#id2@downvoters,我该如何改进这个答案? ps--我想我必须设置链接器语言,因为我的 add_library 调用中断。 “找错树”很有用。请参阅我的问题的补充:) @zzxyz 我已经调整了对您的编辑的回答,如果在“===原始代码的过时修复===之前的部分,如果它以某种方式解释了我试图回答的部分帖子”关键是我已经明确地在源列表中添加了一个标题并且编辑它仍然不会导致任何东西被重新编译。“,你介意接受它吗?【参考方案2】:

我只是回忆上次我在使用 Makefile 生成器时查看 CMake 如何为“源文件”生成文件依赖项的记忆。

“扫描依赖项”步骤是 CMake 扫描“源文件”并为该“源文件”创建依赖项构建规则。它使用 REGEX 和其他一些规则来执行此操作(它跳过了一些预处理规则)。它倾向于跳过它认为的系统头文件,该头文件可以包含使用绝对包含路径时找到的文件。将其视为误报。

通常的解决方法是不使用include_directories(),并在使用target_include_directories() 时避免使用绝对路径。所以尝试改用target_include_directories( submod PRIVATE .. )

【讨论】:

如我的回答中所述,CMake 不跟踪源头依赖关系。它只为带有注释# Empty dependencies file for submod. # This may be replaced when dependencies are built. 的目标文件准备depend.make文件 @R2RT 你说“源到头的依赖关系不是由 CMake 管理的”。当然,它管理依赖项。 CMake 使用命令cmake -E cmake_depends "Unix Makefiles" ... 生成并填充depends.make。反过来make 使用depends.make 的内容来确定何时重新生成目标文件。如果 CMake 没有在 depends.make 中包含所需的文件,那么它将不会被重建,这就是 OP 所说的问题。 是的,我读到它我错了...我不知道make 不能发出依赖关系,并认为它像Ninja 一样工作。鉴于此,您的答案是正确的,而不是我的。 aosabook.org/en/cmake.html 很奇怪。好的...所以 cmake 仍然可能是罪魁祸首。也赞成这个答案 @zzxyz 不幸的是,我无法通过示例重现该问题。我尝试复制相同的构建结构、文件内容,并尝试使用源内构建和源外构建。但对我来说,includeme.h 总是包含在 depend.make 中。【参考方案3】:

Fred 和 R2RT 都让我找到了 my 问题的正确答案。我的 cmake 构建(从主 github 存储库中提取)是问题所在。我切换到发行版,这个问题就消失了。我不太确定为什么我的构建有问题,但是....你有它。 (cmake v. 从 3.14 升级到 3.10,但我相信 my 构建的 cmake 3.14 是与 3.14 相比的问题)

【讨论】:

【参考方案4】:

也有类似的问题。使用 cmake、ninja 和 gcc 构建对象库即使没有更改也会导致重建,如果我将编译器切换到另一个编译器,则不会检测到标头中的某些更改,并且 ninja 认为无事可做。

然后我切换到 GNU make,现在一切正常,尽管与 ninja 相比它有点慢。对我来说,这看起来像是一个错误。

【讨论】:

以上是关于cmake/make (OBJECT) 依赖问题——当标头改变时不重建的主要内容,如果未能解决你的问题,请参考以下文章

CMake make 库需要 cxx 标准

Cmake “cmake_make_program is not set” 问题。

Cmake:未设置 CMAKE_MAKE_PROGRAM

make cmake catkin_make

巨坑,cmake make相同的代码居然产生不同的编译结果!见鬼了,ngrest

巨坑,cmake make相同的代码居然产生不同的编译结果!见鬼了,ngrest