【问题标题】:How does CMake add_executable work internally (dependencies and automatic source discovery)?CMake add_executable 如何在内部工作(依赖项和自动源发现)?
【发布时间】:2020-04-29 11:17:47
【问题描述】:

我正在处理我的第一个 CMake/C 项目,但在构建时遇到了一个问题,这让我重新质疑编译的工作原理。

我希望人们向我解释当您调用 add_executable 时究竟发生了什么以及 make 如何构建 C 文件依赖项。

我的想法

我认为,当调用 add_executable (name, sources) 时,CMake 会将 sources 视为构建目标名称所需的源文件的超集。因此,在内部,他通过分析包含main 的文件并递归地添加包含的.h 文件以及它们在.c 文件中的相关声明来分析REAL MINIMAL 依赖关系。

似乎发生了什么(我想要确认)

CMake 将 sources 视为可执行文件的真正最小依赖项。似乎它会编译 sources 中的任何内容,无论它是否在任何地方使用。

后果

这对我来说真的很烦人。在我的项目中,我使用源发现,这意味着 src 目录中的任何内容都将添加到源中。然后,如果我想编译一个单元测试(make this_unit_test),实际上就是编译我的源目录中的每个 .o 文件,而不是只编译必要的文件。这意味着如果我的项目的一部分没有构建某些东西,我就不能再构建任何测试了。

我能做什么?

如果 CMake 确实以您需要为任何可执行文件指定自己的最小依赖项的方式完成,我如何仍然使用自动源/测试发现?最好的解决方案是来自 CMakeList 文件,这是一个获取源文件列表的函数,并返回它的子集,该子集对应于包含 main 的文件在某个时间点实际包含的内容。人们如何解决这个问题?

【问题讨论】:

  • “在我的项目中我使用源代码发现” - 这到底是什么意思?您的意思是您使用像*.c 这样的文件“通配符”来查找所有源文件,而不是在CMakeLists.txt 中明确列出它们?
  • 是的。我知道cmake说这很糟糕。如果 cmake 添加一个函数来做我想做的事情而不是强迫用户手动列出每个来源,那也不错(我真的不明白人们如何接受它作为一个干净的解决方案)。它可能被接受,因为解析源代码以分离 main 的无用内容比看起来更难。

标签: c cmake compilation


【解决方案1】:

不,CMake 不会 扫描可执行文件的最小源依赖项,它怎么可能呢? CMake 不扫描源文件,也不知道可执行文件最终需要或不需要的包含/定义。此功能必须在编译阶段(在 CMake 完成之后)发生,因为它是解析源文件并解释它们的语义(含义)的预处理器/编译器。有一些静态分析工具可以帮助实现这样的目标,例如 include-what-you-use

在 CMake 中使用 源代码发现 技术(例如 file(GLOB ...))可能容易出错:

  1. 如您所见,源发现机制可能会抓取您不需要用于构建特定目标的文件。
  2. 更糟糕的是,源发现可能遗漏构建特定目标所需的文件。

CMake documentation 本身甚至警告不要这样做!

注意:我们不建议使用 GLOB 从源代码树中收集源文件列表。如果在添加或删除源时没有 CMakeLists.txt 文件发生更改,则生成的构建系统无法知道何时要求 CMake 重新生成。

作为一般经验法则,明确列出每个目标所需的每个单独文件总是最安全的,例如:

add_executable(MyExe 
    main.c
    MyHelperFunctions.c
    MyOtherStructs.c
)

【讨论】:

  • 扫描源文件依赖对于 CMake Makefile 来说很复杂。使用 Makefile 生成器时,Makefile 规则用于运行 CMake 内部命令以扫描 Source Files 的文件以生成依赖列表。如果您使用make V=1 和生成依赖项列表运行,您可以看到这已完成。但是,例如,如果您使用 Ninja 生成器,那么编译器会在编译期间生成依赖项。我想原因是历史上 CMake 支持非常旧的 Make 和不处理依赖生成的编译器。
  • 所以@fdk1342,你是说如果我安装忍者并将其用作生成器,我想要的开箱即用?
  • 下周我会检查你建议的工具 squareskittes。感谢您确认它是这样工作的,并感谢您对大量错别字进行编辑;)
  • @fdk1342 感谢 cmets,我不知道使用 Unix Makefiles 作为生成器时运行的 CMake 内部命令。目前尚不清楚这是否实现了 OP 要求的级别的依赖解决方案,但仍然有帮助!
  • 最好的答案就是这个。 add_executable 用于添加用于此可执行文件的所有文件。如果您添加不属于此可执行文件的内容,那么这是一个错误,因为它会将额外的文件编译并链接到可执行文件中。现在,您可以将任何类型的文件放入add_execuable,CMake 将对这些文件进行分类。根据启用的语言(默认为 C 和 CXX),以 .c.cxx 结尾的文件将被归类为“源文件”。 .h.hh 将被归类为 Header Files。参考source_group命令等IDE相关组织设置。
猜你喜欢
  • 2015-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多