Makefile, Ninja: Fix no-op codegen builds with no CODEGEN

Ninja: Since commit 2d85779126 (Ninja: Account for explicit target
dependencies via CODEGEN, 2026-09-15, v4.5.0-rc1~162^2), a directory's
codegen phony depends on the full build of every add_dependencies
target (which caused their non-CODEGEN commands to run). Instead,
depend only on those targets' CODEGEN outputs, and follow their own
codegen dependencies in turn.

Makefiles: Since commit 197cb419d1 (add_custom_command: Add CODEGEN
support, 2024-05-27, v3.31.0-rc1~394^2), a target's `<target>/codegen`
rule used to depend on `<dep>/all` for every add_dependencies target,
which fully built them. Instead, depend on `<dep>/codegen`, except when
the target has CODEGEN commands of its own, in which case it should
still depend on `<dep>/all` as the commands may need the dependency's
outputs.

Shape up the prior `no-codegen` test case, which wasn't verifying much
of anything.

Fixes: #28205
This commit is contained in:
Tyler Yankee committed 2026-10-25 19:52:43 -04:00
1 parent 17de9500e8
commit 902ec85438
9 files changed
+79 -24

No files matched your search

+5 -1
View File
@@ -254,7 +254,11 @@ Generating Files
is set to ``NEW``.
Like ``all``, the ``codegen`` target exists per directory and skips
targets and directories marked ``EXCLUDE_FROM_ALL``.
targets and directories marked ``EXCLUDE_FROM_ALL``. It follows
:command:`add_dependencies`: the dependencies' ``CODEGEN`` commands are
also run, but their other commands are not, unless the depending target
has ``CODEGEN`` commands of its own, in which case the dependencies are
built in full first.
This keyword cannot be used with ``APPEND`` (see policy :policy:`CMP0175`).
It can only be set on the first call to ``add_custom_command(OUTPUT...)``
+27 -17
View File
@@ -1704,17 +1704,34 @@ void cmGlobalNinjaGenerator::AppendTargetDependsClosure(
outputs.insert(outs.begin(), outs.end());
}
void cmGlobalNinjaGenerator::AppendCodegenOutputs(
cmGeneratorTarget const* target, cmNinjaDeps& deps,
std::string const& config)
{
std::vector<cmSourceFile const*> customCommandSources;
target->GetCustomCommands(customCommandSources, config);
for (cmSourceFile const* sf : customCommandSources) {
cmCustomCommand const* cc = sf->GetCustomCommand();
if (cc->GetCodegen()) {
std::vector<std::string> const& outputs = cc->GetOutputs();
std::transform(outputs.begin(), outputs.end(), std::back_inserter(deps),
this->MapToNinjaPath());
}
}
}
void cmGlobalNinjaGenerator::AppendCodegenTargetDepends(
cmGeneratorTarget const* target, cmNinjaDeps& deps,
std::string const& config) const
std::string const& config, std::set<cmGeneratorTarget const*>& visited)
{
std::set<std::string> const& codegenDeps = target->Target->GetCodegenDeps();
for (cmTargetDepend const& targetDep :
this->GetTargetDirectDepends(target)) {
if (targetDep->IsInBuildSystem() &&
codegenDeps.count(targetDep->GetName())) {
this->AppendTargetOutputs(targetDep, deps, config,
DependOnTargetArtifact);
codegenDeps.count(targetDep->GetName()) &&
visited.insert(targetDep).second) {
this->AppendCodegenOutputs(targetDep, deps, config);
this->AppendCodegenTargetDepends(targetDep, deps, config, visited);
}
}
}
@@ -1884,25 +1901,18 @@ void cmGlobalNinjaGenerator::WriteFolderTargets(std::ostream& os)
build.Outputs.front() =
this->BuildAlias(buildDirCodegenTarget, config);
std::set<cmGeneratorTarget const*> visited;
for (DirectoryTarget::Target const& t : dt.Targets) {
if (this->IsExcludedFromAllInConfig(t, config)) {
continue;
}
this->AppendCodegenTargetDepends(t.GT, build.ExplicitDeps, config);
std::vector<cmSourceFile const*> customCommandSources;
t.GT->GetCustomCommands(customCommandSources, config);
for (cmSourceFile const* sf : customCommandSources) {
cmCustomCommand const* cc = sf->GetCustomCommand();
if (cc->GetCodegen()) {
auto const& outputs = cc->GetOutputs();
std::transform(outputs.begin(), outputs.end(),
std::back_inserter(build.ExplicitDeps),
this->MapToNinjaPath());
}
if (visited.insert(t.GT).second) {
this->AppendCodegenOutputs(t.GT, build.ExplicitDeps, config);
}
this->AppendCodegenTargetDepends(t.GT, build.ExplicitDeps, config,
visited);
}
for (DirectoryTarget::Dir const& d : dt.Children) {
+4 -2
View File
@@ -526,9 +526,11 @@ private:
void CloseRulesFileStream();
void CleanMetaData();
void AppendCodegenOutputs(cmGeneratorTarget const* target, cmNinjaDeps& deps,
std::string const& config);
void AppendCodegenTargetDepends(cmGeneratorTarget const* target,
cmNinjaDeps& deps,
std::string const& config) const;
cmNinjaDeps& deps, std::string const& config,
std::set<cmGeneratorTarget const*>& visited);
/// Write the common disclaimer text at the top of each build file.
void WriteDisclaimer(std::ostream& os) const;
+21 -4
View File
@@ -13,6 +13,7 @@
#include "cmsys/RegularExpression.hxx"
#include "cmCustomCommand.h"
#include "cmDuration.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
@@ -23,6 +24,7 @@
#include "cmMakefileTargetGenerator.h"
#include "cmMessageType.h"
#include "cmOutputConverter.h"
#include "cmSourceFile.h"
#include "cmState.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
@@ -1000,6 +1002,19 @@ void cmGlobalUnixMakefileGenerator3::AppendCodegenTargetDepends(
std::set<std::string> const& codegen_depends =
target->Target->GetCodegenDeps();
bool hasCodegenCommands = false;
{
std::vector<cmSourceFile const*> customCommands;
cmLocalUnixMakefileGenerator3* lg3 =
static_cast<cmLocalUnixMakefileGenerator3*>(target->GetLocalGenerator());
target->GetCustomCommands(customCommands, lg3->GetConfigName());
hasCodegenCommands =
std::any_of(customCommands.begin(), customCommands.end(),
[](cmSourceFile const* sf) {
return sf->GetCustomCommand()->GetCodegen();
});
}
for (cmTargetDepend const& i : this->GetTargetDirectDepends(target)) {
// Create the target-level dependency.
cmGeneratorTarget const* dep = i;
@@ -1009,10 +1024,12 @@ void cmGlobalUnixMakefileGenerator3::AppendCodegenTargetDepends(
if (codegen_depends.find(dep->GetName()) != codegen_depends.end()) {
cmLocalUnixMakefileGenerator3* lg3 =
static_cast<cmLocalUnixMakefileGenerator3*>(dep->GetLocalGenerator());
std::string tgtName = cmStrCat(
lg3->GetRelativeTargetDirectory(const_cast<cmGeneratorTarget*>(dep)),
"/all");
depends.push_back(tgtName);
std::string const tgtDir =
lg3->GetRelativeTargetDirectory(const_cast<cmGeneratorTarget*>(dep));
depends.push_back(cmStrCat(tgtDir, "/codegen"));
if (hasCodegenCommands) {
depends.push_back(cmStrCat(tgtDir, "/all"));
}
}
}
}
@@ -12,6 +12,7 @@ endfunction()
# Builds codegen target when there are no custom commands marked codegen
run_codegen("no-codegen")
run_codegen("no-codegen-2")
# We don't want codegen to drive parts of the project that are EXCLUDE_FROM_ALL
run_codegen("exclude-from-all")
@@ -0,0 +1,5 @@
# Verify generated.h was NOT created
set(unexpected "${RunCMake_TEST_BINARY_DIR}/generated.h")
if(EXISTS "${unexpected}")
set(RunCMake_TEST_FAILED "unexpected file created:\n ${unexpected}")
endif()
+14
View File
@@ -0,0 +1,14 @@
add_executable(main
main.c
)
add_custom_command(
OUTPUT generated.h
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in
${CMAKE_CURRENT_BINARY_DIR}/generated.h
# no CODEGEN
)
add_custom_target(h_creator DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated.h)
add_dependencies(main h_creator)
+2
View File
@@ -4,3 +4,5 @@ add_custom_command(
${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in
${CMAKE_CURRENT_BINARY_DIR}/generated.hpp
)
add_custom_target(hpp_creator ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp)