CMP0058

在 3.3 版本加入.

Ninja 需要明确的自定义命令副产品。

当构建期间生成的中间文件被昂贵的操作或大型依赖树消耗时,可以通过仅在其内容更改时更新文件时间戳来减少增量重建所需的工作。使用这种方法,生成规则必须有一个单独的输出文件,该文件始终使用比规则的任何依赖项更新的新时间戳进行更新,以便构建工具仅在输入更改时重新运行规则。我们将单独的输出文件称为规则的*见证*,将生成的文件称为规则的*副产品*。

副产品可能不会列为输出,因为允许它们的时间戳早于输入。在设计 CMake 时存在的构建工具(如“make”)没有一种方法可以表达副产品。因此 3.2 之前的 CMake 版本无法指定它们。项目通常会在生成它们的规则中留下未声明的副产品。例如:

add_custom_command(
  OUTPUT witness.txt
  COMMAND ${CMAKE_COMMAND} -E copy_if_different
          ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
          byproduct.txt # timestamp may not change
  COMMAND ${CMAKE_COMMAND} -E touch witness.txt
  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
  )
add_custom_target(Provider DEPENDS witness.txt)
add_custom_command(
  OUTPUT generated.c
  COMMAND expensive-task -i byproduct.txt -o generated.c
  DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/byproduct.txt
  )
add_library(Consumer generated.c)
add_dependencies(Consumer Provider)

这适用于除 Ninja 之外的所有生成器。 Ninja 构建工具看到将“byproduct.txt”列为依赖项的规则,但没有将其列为输出的规则。 Ninja 然后抱怨没有办法满足依赖关系并停止构建,即使存在确保 byproduct.txt 在其消费者需要它之前存在的仅顺序依赖关系。有关 Ninja 为何以这种方式工作的更多详细信息,请参阅“Ninja Issue 760”中对此问题的讨论。

Ninja 希望副产品与其他输出一起列出,而不是在生成它们的规则中未声明副产品。此类规则可能会标有“restat”选项,告诉 Ninja 在规则运行后检查输出的时间戳。这可以防止时间戳未更改的副产品导致其依赖项不必要地重新构建。

由于上述方法没有告诉 CMake 什么自定义命令生成“byproduct.txt”,因此 Ninja 生成器没有足够的信息将副产品添加为任何规则的输出。 CMake 2.8.12 及更高版本解决了这个问题,并允许使用上述方法的项目通过生成“虚假”构建规则来告诉 Ninja 容忍此类丢失的文件。但是,此解决方法会阻止 Ninja 诊断真正缺失的依赖项。它在源代码构建中也表现不佳,其中每个自定义命令依赖项,甚至是源文件,都需要以这种方式处理,因为 CMake 没有足够的信息来知道哪些文件是自定义命令的副产品。

介绍副产品

CMake 3.2 为 add_custom_command 和 add_custom_target 命令引入了 BYPRODUCTS 选项。此选项允许明确指定副产品:

add_custom_command(
  OUTPUT witness.txt
  BYPRODUCTS byproduct.txt # explicit byproduct specification
  COMMAND ${CMAKE_COMMAND} -E copy_if_different
          ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
          byproduct.txt # timestamp may not change
...

Ninja 生成器使用 BYPRODUCTS 选项在生成它们的自定义命令的输出中列出副产品,并被其他生成器忽略。

CMake 3.3 及更高版本更倾向于要求项目明确指定自定义命令副产品,以便它可以避免完全使用“虚假”规则解决方法。引入了策略“CMP0058”以提供与仍然需要解决方法的现有项目的兼容性。

此策略对 Ninja 以外的生成器没有影响。此策略的“旧”行为是为构建树中的未知依赖项生成忍者“虚假”规则。此策略的“新”行为是不生成这些,而是​​要求项目明确指定自定义命令“BYPRODUCTS”。

此策略是在 CMake 版本 3.3 中引入的。 CMake 版本 |发布|如果未设置策略然后使用“旧”行为,当它在源外构建树中看到未知依赖项时发出警告。使用 cmake_policy() 命令将策略显式设置为 OLDNEW。策略设置必须在项目的顶级“CMakeLists.txt”文件末尾的范围内,并且具有全局效果。

备注

策略的“旧”行为是 :manual:根据定义 <cmake-policies(7)> 已弃用,并且可能会在未来版本的 CMake 中删除。