summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Help/command/add_custom_command.rst32
-rw-r--r--Help/manual/cmake-policies.7.rst8
-rw-r--r--Help/policy/CMP0171.rst26
-rw-r--r--Help/release/dev/codegen.rst10
-rw-r--r--Source/cmAddCustomCommandCommand.cxx30
-rw-r--r--Source/cmAddDependenciesCommand.cxx1
-rw-r--r--Source/cmCustomCommand.h5
-rw-r--r--Source/cmGlobalGenerator.cxx49
-rw-r--r--Source/cmGlobalGenerator.h5
-rw-r--r--Source/cmGlobalNinjaGenerator.cxx86
-rw-r--r--Source/cmGlobalUnixMakefileGenerator3.cxx39
-rw-r--r--Source/cmGlobalUnixMakefileGenerator3.h3
-rw-r--r--Source/cmLocalGenerator.cxx6
-rw-r--r--Source/cmLocalUnixMakefileGenerator3.cxx15
-rw-r--r--Source/cmMakefileTargetGenerator.cxx29
-rw-r--r--Source/cmPolicies.h4
-rw-r--r--Source/cmTarget.cxx11
-rw-r--r--Source/cmTarget.h5
-rw-r--r--Source/cmTargetTraceDependencies.cxx11
-rw-r--r--Tests/RunCMake/CMP0171/CMP0171-NEW-result.txt1
-rw-r--r--Tests/RunCMake/CMP0171/CMP0171-NEW-stderr.txt4
-rw-r--r--Tests/RunCMake/CMP0171/CMP0171-NEW.cmake6
-rw-r--r--Tests/RunCMake/CMP0171/CMP0171-OLD-result.txt1
-rw-r--r--Tests/RunCMake/CMP0171/CMP0171-OLD-stderr.txt4
-rw-r--r--Tests/RunCMake/CMP0171/CMP0171-OLD.cmake9
-rw-r--r--Tests/RunCMake/CMP0171/CMP0171-WARN-stderr.txt9
-rw-r--r--Tests/RunCMake/CMP0171/CMP0171-WARN.cmake6
-rw-r--r--Tests/RunCMake/CMP0171/CMP0171-codegen-build-result.txt1
-rw-r--r--Tests/RunCMake/CMP0171/CMP0171-codegen.cmake0
-rw-r--r--Tests/RunCMake/CMP0171/CMakeLists.txt6
-rw-r--r--Tests/RunCMake/CMP0171/RunCMakeTest.cmake18
-rw-r--r--Tests/RunCMake/CMakeLists.txt4
-rw-r--r--Tests/RunCMake/Codegen/CMakeLists.txt7
-rw-r--r--Tests/RunCMake/Codegen/RunCMakeTest.cmake33
-rw-r--r--Tests/RunCMake/Codegen/add-custom-command-depends-build-check.cmake5
-rw-r--r--Tests/RunCMake/Codegen/add-custom-command-depends.cmake16
-rw-r--r--Tests/RunCMake/Codegen/add-dependencies-build-check.cmake5
-rw-r--r--Tests/RunCMake/Codegen/add-dependencies.cmake18
-rw-r--r--Tests/RunCMake/Codegen/append-implicit-depends-result.txt1
-rw-r--r--Tests/RunCMake/Codegen/append-implicit-depends-stderr.txt2
-rw-r--r--Tests/RunCMake/Codegen/append-implicit-depends.cmake19
-rw-r--r--Tests/RunCMake/Codegen/byproducts-build-check.cmake5
-rw-r--r--Tests/RunCMake/Codegen/byproducts.cmake19
-rw-r--r--Tests/RunCMake/Codegen/error.c1
-rw-r--r--Tests/RunCMake/Codegen/exclude-from-all.cmake11
-rw-r--r--Tests/RunCMake/Codegen/generated.h.in1
-rw-r--r--Tests/RunCMake/Codegen/implicit-depends-append-codegen-result.txt1
-rw-r--r--Tests/RunCMake/Codegen/implicit-depends-append-codegen-stderr.txt4
-rw-r--r--Tests/RunCMake/Codegen/implicit-depends-append-codegen.cmake18
-rw-r--r--Tests/RunCMake/Codegen/implicit-depends-result.txt1
-rw-r--r--Tests/RunCMake/Codegen/implicit-depends-stderr.txt4
-rw-r--r--Tests/RunCMake/Codegen/implicit-depends.cmake11
-rw-r--r--Tests/RunCMake/Codegen/main.c4
-rw-r--r--Tests/RunCMake/Codegen/min-graph-1-build-check.cmake13
-rw-r--r--Tests/RunCMake/Codegen/min-graph-1.cmake26
-rw-r--r--Tests/RunCMake/Codegen/min-graph-2-build-check.cmake5
-rw-r--r--Tests/RunCMake/Codegen/min-graph-2.cmake18
-rw-r--r--Tests/RunCMake/Codegen/min-graph-3-build-check.cmake5
-rw-r--r--Tests/RunCMake/Codegen/min-graph-3.cmake12
-rw-r--r--Tests/RunCMake/Codegen/no-codegen-check.cmake5
-rw-r--r--Tests/RunCMake/Codegen/no-codegen.cmake6
-rw-r--r--Tests/RunCMake/Codegen/no-output-result.txt1
-rw-r--r--Tests/RunCMake/Codegen/no-output-stderr.txt4
-rw-r--r--Tests/RunCMake/Codegen/no-output.cmake11
64 files changed, 733 insertions, 3 deletions
diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst
index 77357c0..6169038 100644
--- a/Help/command/add_custom_command.rst
+++ b/Help/command/add_custom_command.rst
@@ -26,6 +26,7 @@ The first signature is for adding a custom command to produce an output:
[JOB_POOL job_pool]
[JOB_SERVER_AWARE <bool>]
[VERBATIM] [APPEND] [USES_TERMINAL]
+ [CODEGEN]
[COMMAND_EXPAND_LISTS]
[DEPENDS_EXPLICIT_ONLY])
@@ -203,6 +204,18 @@ The options are:
``${CC} "-I$<JOIN:$<TARGET_PROPERTY:foo,INCLUDE_DIRECTORIES>,;-I>" foo.cc``
to be properly expanded.
+``CODEGEN``
+ .. versionadded:: 3.31
+
+ Adds the custom command to a global ``codegen`` target that can be
+ used to execute the custom command while avoiding the majority of the
+ build graph.
+
+ This option is supported only by :ref:`Ninja Generators` and
+ :ref:`Makefile Generators`, and is ignored by other generators.
+ Furthermore, this option is allowed only if policy :policy:`CMP0171`
+ is set to ``NEW``.
+
``IMPLICIT_DEPENDS``
Request scanning of implicit dependencies of an input file.
The language given specifies the programming language whose
@@ -454,6 +467,25 @@ will re-run whenever ``in.txt`` changes.
where ``<config>`` is the build configuration, and then compile the generated
source as part of a library.
+.. versionadded:: 3.31
+ Use the ``CODEGEN`` option to add a custom command's outputs to the builtin
+ ``codegen`` target. This is useful to make generated code available for
+ static analysis without building the entire project. For example:
+
+ .. code-block:: cmake
+
+ add_executable(someTool someTool.c)
+
+ add_custom_command(
+ OUTPUT out.c
+ COMMAND someTool -o out.c
+ CODEGEN)
+
+ add_library(myLib out.c)
+
+ A user may build the ``codegen`` target to generate ``out.c``.
+ ``someTool`` is built as dependency, but ``myLib`` is not built at all.
+
Example: Generating Files for Multiple Targets
""""""""""""""""""""""""""""""""""""""""""""""
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index 7155404..90c71b7 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -51,6 +51,14 @@ The :variable:`CMAKE_MINIMUM_REQUIRED_VERSION` variable may also be used
to determine whether to report an error on use of deprecated macros or
functions.
+Policies Introduced by CMake 3.31
+=================================
+
+.. toctree::
+ :maxdepth: 1
+
+ CMP0171: 'codegen' is a reserved target name. </policy/CMP0171>
+
Policies Introduced by CMake 3.30
=================================
diff --git a/Help/policy/CMP0171.rst b/Help/policy/CMP0171.rst
new file mode 100644
index 0000000..c364bf4
--- /dev/null
+++ b/Help/policy/CMP0171.rst
@@ -0,0 +1,26 @@
+CMP0171
+-------
+
+.. versionadded:: 3.31
+
+``codegen`` is a reserved target name.
+
+CMake 3.30 and earlier did not reserve ``codegen`` as a builtin target name,
+leaving projects free to create their own target with that name.
+CMake 3.31 and later prefer to reserve ``codegen`` as a builtin target name
+to drive custom commands created with the ``CODEGEN`` option to
+:command:`add_custom_command`. In order to support building the ``codegen``
+target in scripted environments, e.g., ``cmake --build . --target codegen``,
+the ``codegen`` target needs to be generated even if no custom commands
+use the ``CODEGEN`` option. This policy provides compatibility for projects
+that have not been updated to avoid creating a target named ``codegen``.
+
+The ``OLD`` behavior of this policy allows projects to create a target
+with the name ``codegen``. The ``NEW`` behavior halts with a fatal error
+if a target with the name ``codegen`` is created.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.31
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/release/dev/codegen.rst b/Help/release/dev/codegen.rst
new file mode 100644
index 0000000..e8b61c9
--- /dev/null
+++ b/Help/release/dev/codegen.rst
@@ -0,0 +1,10 @@
+codegen
+-------
+
+* The :ref:`Ninja Generators` and :ref:`Makefile Generators` now produce
+ a ``codegen`` build target. See policy :policy:`CMP0171`. It drives a
+ subset of the build graph sufficient to run custom commands created with
+ :command:`add_custom_command`'s new ``CODEGEN`` option.
+
+* The :command:`add_custom_command` command gained a ``CODEGEN`` option
+ to mark a custom commands outputs as dependencies of a ``codegen`` target.
diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx
index d943011..d5adba7 100644
--- a/Source/cmAddCustomCommandCommand.cxx
+++ b/Source/cmAddCustomCommandCommand.cxx
@@ -53,6 +53,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
bool command_expand_lists = false;
bool depends_explicit_only =
mf.IsOn("CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY");
+ bool codegen = false;
std::string implicit_depends_lang;
cmImplicitDependsList implicit_depends;
@@ -111,6 +112,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
MAKE_STATIC_KEYWORD(VERBATIM);
MAKE_STATIC_KEYWORD(WORKING_DIRECTORY);
MAKE_STATIC_KEYWORD(DEPENDS_EXPLICIT_ONLY);
+ MAKE_STATIC_KEYWORD(CODEGEN);
#undef MAKE_STATIC_KEYWORD
static std::unordered_set<std::string> const keywords{
keyAPPEND,
@@ -135,7 +137,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
keyUSES_TERMINAL,
keyVERBATIM,
keyWORKING_DIRECTORY,
- keyDEPENDS_EXPLICIT_ONLY
+ keyDEPENDS_EXPLICIT_ONLY,
+ keyCODEGEN
};
for (std::string const& copy : args) {
@@ -166,6 +169,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
command_expand_lists = true;
} else if (copy == keyDEPENDS_EXPLICIT_ONLY) {
depends_explicit_only = true;
+ } else if (copy == keyCODEGEN) {
+ codegen = true;
} else if (copy == keyTARGET) {
doing = doing_target;
} else if (copy == keyARGS) {
@@ -322,6 +327,28 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
return false;
}
+ if (codegen) {
+ if (output.empty()) {
+ status.SetError("CODEGEN requires at least 1 OUTPUT.");
+ return false;
+ }
+
+ if (append) {
+ status.SetError("CODEGEN may not be used with APPEND.");
+ return false;
+ }
+
+ if (!implicit_depends.empty()) {
+ status.SetError("CODEGEN is not compatible with IMPLICIT_DEPENDS.");
+ return false;
+ }
+
+ if (mf.GetPolicyStatus(cmPolicies::CMP0171) != cmPolicies::NEW) {
+ status.SetError("CODEGEN option requires policy CMP0171 be set to NEW!");
+ return false;
+ }
+ }
+
// Check for an append request.
if (append) {
mf.AppendCustomCommandToOutput(output[0], depends, implicit_depends,
@@ -355,6 +382,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
cc->SetOutputs(output);
cc->SetMainDependency(main_dependency);
cc->SetDepends(depends);
+ cc->SetCodegen(codegen);
cc->SetImplicitDepends(implicit_depends);
mf.AddCustomCommandToOutput(std::move(cc));
} else {
diff --git a/Source/cmAddDependenciesCommand.cxx b/Source/cmAddDependenciesCommand.cxx
index 2d55a5a..aa04018 100644
--- a/Source/cmAddDependenciesCommand.cxx
+++ b/Source/cmAddDependenciesCommand.cxx
@@ -30,6 +30,7 @@ bool cmAddDependenciesCommand(std::vector<std::string> const& args,
// skip over target_name
for (std::string const& arg : cmMakeRange(args).advance(1)) {
target->AddUtility(arg, false, &mf);
+ target->AddCodegenDependency(arg);
}
} else {
mf.IssueMessage(
diff --git a/Source/cmCustomCommand.h b/Source/cmCustomCommand.h
index 167e601..6f63d0a 100644
--- a/Source/cmCustomCommand.h
+++ b/Source/cmCustomCommand.h
@@ -132,6 +132,10 @@ public:
const std::string& GetTarget() const;
void SetTarget(const std::string& target);
+ /** Record if the custom command can be used for code generation. */
+ bool GetCodegen() const { return Codegen; }
+ void SetCodegen(bool b) { Codegen = b; }
+
private:
std::vector<std::string> Outputs;
std::vector<std::string> Byproducts;
@@ -153,6 +157,7 @@ private:
bool StdPipesUTF8 = false;
bool HasMainDependency_ = false;
bool DependsExplicitOnly = false;
+ bool Codegen = false;
// Policies are NEW for synthesized custom commands, and set by cmMakefile for
// user-created custom commands.
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 019271b..bf2f0fc 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -1398,6 +1398,8 @@ void cmGlobalGenerator::Configure()
}
}
+ this->ReserveGlobalTargetCodegen();
+
// update the cache entry for the number of local generators, this is used
// for progress
this->GetCMakeInstance()->AddCacheEntry(
@@ -2914,6 +2916,53 @@ void cmGlobalGenerator::AddGlobalTarget_Test(
targets.push_back(std::move(gti));
}
+void cmGlobalGenerator::ReserveGlobalTargetCodegen()
+{
+ // Read the policy value at the end of the top-level CMakeLists.txt file
+ // since it's a global policy that affects the whole project.
+ auto& mf = this->Makefiles[0];
+ const auto policyStatus = mf->GetPolicyStatus(cmPolicies::CMP0171);
+
+ this->AllowGlobalTargetCodegen = (policyStatus == cmPolicies::NEW);
+
+ cmTarget* tgt = this->FindTarget("codegen");
+ if (!tgt) {
+ return;
+ }
+
+ MessageType messageType = MessageType::AUTHOR_WARNING;
+ std::ostringstream e;
+ bool issueMessage = false;
+ switch (policyStatus) {
+ case cmPolicies::WARN:
+ e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0171) << "\n";
+ issueMessage = true;
+ CM_FALLTHROUGH;
+ case cmPolicies::OLD:
+ break;
+ case cmPolicies::NEW:
+ case cmPolicies::REQUIRED_IF_USED:
+ case cmPolicies::REQUIRED_ALWAYS:
+ issueMessage = true;
+ messageType = MessageType::FATAL_ERROR;
+ break;
+ }
+ if (issueMessage) {
+ e << "The target name \"codegen\" is reserved.";
+ this->GetCMakeInstance()->IssueMessage(messageType, e.str(),
+ tgt->GetBacktrace());
+ if (messageType == MessageType::FATAL_ERROR) {
+ cmSystemTools::SetFatalErrorOccurred();
+ return;
+ }
+ }
+}
+
+bool cmGlobalGenerator::CheckCMP0171() const
+{
+ return this->AllowGlobalTargetCodegen;
+}
+
void cmGlobalGenerator::AddGlobalTarget_EditCache(
std::vector<GlobalTargetInfo>& targets) const
{
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index 1ca02d9..33c9889 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -653,6 +653,8 @@ public:
virtual std::string& EncodeLiteral(std::string& lit) { return lit; }
+ bool CheckCMP0171() const;
+
protected:
// for a project collect all its targets by following depend
// information, and also collect all the targets
@@ -719,6 +721,8 @@ protected:
void AddGlobalTarget_Install(std::vector<GlobalTargetInfo>& targets);
void CreateGlobalTarget(GlobalTargetInfo const& gti, cmMakefile* mf);
+ void ReserveGlobalTargetCodegen();
+
std::string FindMakeProgramFile;
std::string ConfiguredFilesPath;
cmake* CMakeInstance;
@@ -891,4 +895,5 @@ protected:
bool ToolSupportsColor;
bool InstallTargetEnabled;
bool ConfigureDoneCMP0026AndCMP0024;
+ bool AllowGlobalTargetCodegen;
};
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 96c8f25..7d62fa8 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -25,6 +25,7 @@
#include "cmsys/FStream.hxx"
+#include "cmCustomCommand.h"
#include "cmCxxModuleMapper.h"
#include "cmDyndepCollation.h"
#include "cmFortranParser.h"
@@ -43,6 +44,7 @@
#include "cmOutputConverter.h"
#include "cmRange.h"
#include "cmScanDepFormat.h"
+#include "cmSourceFile.h"
#include "cmState.h"
#include "cmStateDirectory.h"
#include "cmStateSnapshot.h"
@@ -1627,6 +1629,90 @@ void cmGlobalNinjaGenerator::WriteFolderTargets(std::ostream& os)
std::map<std::string, DirectoryTarget> dirTargets =
this->ComputeDirectoryTargets();
+ // Codegen target
+ if (this->CheckCMP0171()) {
+ for (auto const& it : dirTargets) {
+ cmNinjaBuild build("phony");
+ cmGlobalNinjaGenerator::WriteDivider(os);
+ std::string const& currentBinaryDir = it.first;
+ DirectoryTarget const& dt = it.second;
+ std::vector<std::string> configs =
+ dt.LG->GetMakefile()->GetGeneratorConfigs(
+ cmMakefile::IncludeEmptyConfig);
+
+ // Setup target
+ cmNinjaDeps configDeps;
+ build.Comment = cmStrCat("Folder: ", currentBinaryDir);
+ build.Outputs.emplace_back();
+ std::string const buildDirAllTarget =
+ this->ConvertToNinjaPath(cmStrCat(currentBinaryDir, "/codegen"));
+
+ cmNinjaDeps& explicitDeps = build.ExplicitDeps;
+
+ for (auto const& config : configs) {
+ explicitDeps.clear();
+
+ for (DirectoryTarget::Target const& t : dt.Targets) {
+ if (this->IsExcludedFromAllInConfig(t, config)) {
+ continue;
+ }
+
+ 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(explicitDeps),
+ this->MapToNinjaPath());
+ }
+ }
+ }
+
+ build.Outputs.front() = this->BuildAlias(buildDirAllTarget, config);
+ // Write target
+ this->WriteBuild(this->EnableCrossConfigBuild() &&
+ this->CrossConfigs.count(config)
+ ? os
+ : *this->GetImplFileStream(config),
+ build);
+ }
+
+ // Add shortcut target
+ if (this->IsMultiConfig()) {
+ for (auto const& config : configs) {
+ build.ExplicitDeps = { this->BuildAlias(buildDirAllTarget, config) };
+ build.Outputs.front() = buildDirAllTarget;
+ this->WriteBuild(*this->GetConfigFileStream(config), build);
+ }
+
+ if (!this->DefaultFileConfig.empty()) {
+ build.ExplicitDeps.clear();
+ for (auto const& config : this->DefaultConfigs) {
+ build.ExplicitDeps.push_back(
+ this->BuildAlias(buildDirAllTarget, config));
+ }
+ build.Outputs.front() = buildDirAllTarget;
+ this->WriteBuild(*this->GetDefaultFileStream(), build);
+ }
+ }
+
+ // Add target for all configs
+ if (this->EnableCrossConfigBuild()) {
+ build.ExplicitDeps.clear();
+ for (auto const& config : this->CrossConfigs) {
+ build.ExplicitDeps.push_back(
+ this->BuildAlias(buildDirAllTarget, config));
+ }
+ build.Outputs.front() = this->BuildAlias(buildDirAllTarget, "codegen");
+ this->WriteBuild(os, build);
+ }
+ }
+ }
+
+ // All target
for (auto const& it : dirTargets) {
cmNinjaBuild build("phony");
cmGlobalNinjaGenerator::WriteDivider(os);
diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx
index 56748a5..38cc4f6 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.cxx
+++ b/Source/cmGlobalUnixMakefileGenerator3.cxx
@@ -23,6 +23,7 @@
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
+#include "cmTarget.h"
#include "cmTargetDepend.h"
#include "cmValue.h"
#include "cmake.h"
@@ -438,6 +439,10 @@ void cmGlobalUnixMakefileGenerator3::WriteDirectoryRules2(
// Write directory-level rules for "all".
this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "all", true, false);
+ // Write directory-level rules for "codegen".
+ this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "codegen", true,
+ false);
+
// Write directory-level rules for "preinstall".
this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "preinstall", true,
true);
@@ -765,6 +770,17 @@ void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules2(
depends, commands, true);
}
+ // add the codegen rule
+ localName = lg.GetRelativeTargetDirectory(gtarget.get());
+ depends.clear();
+ commands.clear();
+ makeTargetName = cmStrCat(localName, "/codegen");
+ commands.push_back(
+ lg.GetRecursiveMakeCall(makefileName, makeTargetName));
+ this->AppendCodegenTargetDepends(depends, gtarget.get());
+ rootLG.WriteMakeRule(ruleFileStream, "codegen rule for target.",
+ makeTargetName, depends, commands, true);
+
// add the clean rule
localName = lg.GetRelativeTargetDirectory(gtarget.get());
makeTargetName = cmStrCat(localName, "/clean");
@@ -893,6 +909,29 @@ void cmGlobalUnixMakefileGenerator3::AppendGlobalTargetDepends(
}
}
+void cmGlobalUnixMakefileGenerator3::AppendCodegenTargetDepends(
+ std::vector<std::string>& depends, cmGeneratorTarget* target)
+{
+ const std::set<std::string>& codegen_depends =
+ target->Target->GetCodegenDeps();
+
+ for (cmTargetDepend const& i : this->GetTargetDirectDepends(target)) {
+ // Create the target-level dependency.
+ cmGeneratorTarget const* dep = i;
+ if (!dep->IsInBuildSystem()) {
+ continue;
+ }
+ 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);
+ }
+ }
+}
+
void cmGlobalUnixMakefileGenerator3::WriteHelpRule(
std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3* lg)
{
diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h
index ee78351..d4fcf88 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.h
+++ b/Source/cmGlobalUnixMakefileGenerator3.h
@@ -223,6 +223,9 @@ protected:
void AppendGlobalTargetDepends(std::vector<std::string>& depends,
cmGeneratorTarget* target);
+ void AppendCodegenTargetDepends(std::vector<std::string>& depends,
+ cmGeneratorTarget* target);
+
// Target name hooks for superclass.
const char* GetAllTargetName() const override { return "all"; }
const char* GetInstallTargetName() const override { return "install"; }
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index aa3f28e..e6d56d3 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -4626,6 +4626,12 @@ void AppendCustomCommandToOutput(cmLocalGenerator& lg,
if (cmCustomCommand* cc = sf->GetCustomCommand()) {
cc->AppendCommands(commandLines);
cc->AppendDepends(depends);
+ if (cc->GetCodegen() && !implicit_depends.empty()) {
+ lg.GetCMakeInstance()->IssueMessage(
+ MessageType::FATAL_ERROR,
+ "Cannot append IMPLICIT_DEPENDS to existing CODEGEN custom "
+ "command.");
+ }
cc->AppendImplicitDepends(implicit_depends);
return;
}
diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx
index 7def1fe..4a776b4 100644
--- a/Source/cmLocalUnixMakefileGenerator3.cxx
+++ b/Source/cmLocalUnixMakefileGenerator3.cxx
@@ -1763,6 +1763,21 @@ void cmLocalUnixMakefileGenerator3::WriteLocalAllRules(
this->WriteMakeRule(ruleFileStream, "The main all target", "all", depends,
commands, true);
+ // Write the codegen rule.
+ if (this->GetGlobalGenerator()->CheckCMP0171()) {
+ recursiveTarget = cmStrCat(this->GetCurrentBinaryDirectory(), "/codegen");
+ depends.clear();
+ commands.clear();
+ if (regenerate) {
+ depends.emplace_back("cmake_check_build_system");
+ }
+ commands.push_back(this->GetRecursiveMakeCall(mf2Dir, recursiveTarget));
+ AppendEcho(commands, "Finished generating code",
+ cmLocalUnixMakefileGenerator3::EchoColor::EchoGenerate);
+ this->WriteMakeRule(ruleFileStream, "The main codegen target", "codegen",
+ depends, commands, true);
+ }
+
// Write the clean rule.
recursiveTarget = cmStrCat(this->GetCurrentBinaryDirectory(), "/clean");
commands.clear();
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index d5c50bc..9ff0a4a 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -251,6 +251,8 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
std::vector<cmSourceFile const*> customCommands;
this->GeneratorTarget->GetCustomCommands(customCommands,
this->GetConfigName());
+ std::vector<std::string> codegen_depends;
+ codegen_depends.reserve(customCommands.size());
for (cmSourceFile const* sf : customCommands) {
if (this->CMP0113New &&
!this->LocalGenerator->GetCommandsVisited(this->GeneratorTarget)
@@ -273,6 +275,33 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
this->LocalGenerator->MaybeRelativeToCurBinDir(byproduct));
}
}
+
+ if (ccg.GetCC().GetCodegen()) {
+ std::string const& output = ccg.GetOutputs().front();
+
+ // We always attach the actual commands to the first output.
+ codegen_depends.emplace_back(output);
+ }
+ }
+
+ // Some make tools need a special dependency for an empty rule.
+ if (codegen_depends.empty()) {
+ std::string hack = this->GlobalGenerator->GetEmptyRuleHackDepends();
+ if (!hack.empty()) {
+ codegen_depends.emplace_back(std::move(hack));
+ }
+ }
+
+ // Construct the codegen target.
+ {
+ std::string const codegenTarget = cmStrCat(
+ this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget),
+ "/codegen");
+
+ // Write the rule.
+ this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
+ codegenTarget, codegen_depends, {},
+ true);
}
// Add byproducts from build events to the clean rules
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index d893c44..85f3293 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -525,7 +525,9 @@ class cmMakefile;
3, 30, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0170, \
"FETCHCONTENT_FULLY_DISCONNECTED requirements are enforced.", 3, 30, \
- 0, cmPolicies::WARN)
+ 0, cmPolicies::WARN) \
+ SELECT(POLICY, CMP0171, "'codegen' is a reserved target name.", 3, 31, 0, \
+ cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 1284130..a6e6984 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -657,6 +657,7 @@ public:
bool PerConfig;
cmTarget::Visibility TargetVisibility;
std::set<BT<std::pair<std::string, bool>>> Utilities;
+ std::set<std::string> CodegenDependencies;
std::vector<cmCustomCommand> PreBuildCommands;
std::vector<cmCustomCommand> PreLinkCommands;
std::vector<cmCustomCommand> PostBuildCommands;
@@ -1238,6 +1239,16 @@ void cmTarget::AddUtility(BT<std::pair<std::string, bool>> util)
this->impl->Utilities.emplace(std::move(util));
}
+void cmTarget::AddCodegenDependency(std::string const& name)
+{
+ this->impl->CodegenDependencies.emplace(name);
+}
+
+std::set<std::string> const& cmTarget::GetCodegenDeps() const
+{
+ return this->impl->CodegenDependencies;
+}
+
std::set<BT<std::pair<std::string, bool>>> const& cmTarget::GetUtilities()
const
{
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 385dfe7..220ac13 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -173,6 +173,11 @@ public:
void AddUtility(std::string const& name, bool cross,
cmMakefile const* mf = nullptr);
void AddUtility(BT<std::pair<std::string, bool>> util);
+
+ void AddCodegenDependency(std::string const& name);
+
+ std::set<std::string> const& GetCodegenDeps() const;
+
//! Get the utilities used by this target
std::set<BT<std::pair<std::string, bool>>> const& GetUtilities() const;
diff --git a/Source/cmTargetTraceDependencies.cxx b/Source/cmTargetTraceDependencies.cxx
index cc91a42..f14cfbf 100644
--- a/Source/cmTargetTraceDependencies.cxx
+++ b/Source/cmTargetTraceDependencies.cxx
@@ -7,10 +7,12 @@
#include <cmext/algorithm>
+#include "cmCustomCommand.h"
#include "cmCustomCommandGenerator.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmList.h"
+#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmSourceFile.h"
@@ -132,6 +134,8 @@ void cmTargetTraceDependencies::FollowName(std::string const& name)
// The name is a byproduct of a utility target or a PRE_BUILD, PRE_LINK, or
// POST_BUILD command.
this->GeneratorTarget->Target->AddUtility(t->GetName(), false);
+
+ this->GeneratorTarget->Target->AddCodegenDependency(t->GetName());
}
if (cmSourceFile* sf = i->second.Source) {
// For now only follow the dependency if the source file is not a
@@ -213,6 +217,11 @@ void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc)
// Collect target-level dependencies referenced in command lines.
for (auto const& util : ccg.GetUtilities()) {
this->GeneratorTarget->Target->AddUtility(util);
+
+ if (ccg.GetCC().GetCodegen()) {
+ this->GeneratorTarget->Target->AddCodegenDependency(
+ util.Value.first);
+ }
}
// Collect file-level dependencies referenced in DEPENDS.
@@ -226,6 +235,8 @@ void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc)
// The dependency does not name a target and may be a file we
// know how to generate. Queue it.
this->FollowName(dep);
+ } else {
+ this->GeneratorTarget->Target->AddCodegenDependency(dep);
}
}
}
diff --git a/Tests/RunCMake/CMP0171/CMP0171-NEW-result.txt b/Tests/RunCMake/CMP0171/CMP0171-NEW-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMP0171/CMP0171-NEW-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMP0171/CMP0171-NEW-stderr.txt b/Tests/RunCMake/CMP0171/CMP0171-NEW-stderr.txt
new file mode 100644
index 0000000..155ddd2
--- /dev/null
+++ b/Tests/RunCMake/CMP0171/CMP0171-NEW-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at CMP0171-NEW\.cmake:[0-9]+ \(add_custom_target\):
+ The target name "codegen" is reserved\.
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0171/CMP0171-NEW.cmake b/Tests/RunCMake/CMP0171/CMP0171-NEW.cmake
new file mode 100644
index 0000000..547f6c0
--- /dev/null
+++ b/Tests/RunCMake/CMP0171/CMP0171-NEW.cmake
@@ -0,0 +1,6 @@
+# codegen is now a reserved name and this will cause an error since the policy is new.
+add_custom_target(codegen
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+)
diff --git a/Tests/RunCMake/CMP0171/CMP0171-OLD-result.txt b/Tests/RunCMake/CMP0171/CMP0171-OLD-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMP0171/CMP0171-OLD-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMP0171/CMP0171-OLD-stderr.txt b/Tests/RunCMake/CMP0171/CMP0171-OLD-stderr.txt
new file mode 100644
index 0000000..1ae3318
--- /dev/null
+++ b/Tests/RunCMake/CMP0171/CMP0171-OLD-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at CMP0171-OLD\.cmake:[0-9]+ \(add_custom_command\):
+ add_custom_command CODEGEN option requires policy CMP0171 be set to NEW\!
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0171/CMP0171-OLD.cmake b/Tests/RunCMake/CMP0171/CMP0171-OLD.cmake
new file mode 100644
index 0000000..c34b3fb
--- /dev/null
+++ b/Tests/RunCMake/CMP0171/CMP0171-OLD.cmake
@@ -0,0 +1,9 @@
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp
+ # This will cause an error since the CODEGEN option
+ # requires that CMP0171 is set to NEW
+ CODEGEN
+)
diff --git a/Tests/RunCMake/CMP0171/CMP0171-WARN-stderr.txt b/Tests/RunCMake/CMP0171/CMP0171-WARN-stderr.txt
new file mode 100644
index 0000000..ee79553
--- /dev/null
+++ b/Tests/RunCMake/CMP0171/CMP0171-WARN-stderr.txt
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMP0171-WARN\.cmake:[0-9]+ \(add_custom_target\):
+ Policy CMP0171 is not set: 'codegen' is a reserved target name\. Run "cmake
+ --help-policy CMP0171" for policy details. Use the cmake_policy command to
+ set the policy and suppress this warning\.
+
+ The target name "codegen" is reserved\.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.
diff --git a/Tests/RunCMake/CMP0171/CMP0171-WARN.cmake b/Tests/RunCMake/CMP0171/CMP0171-WARN.cmake
new file mode 100644
index 0000000..6d61723
--- /dev/null
+++ b/Tests/RunCMake/CMP0171/CMP0171-WARN.cmake
@@ -0,0 +1,6 @@
+# CMake should warn the user if they have a target named codegen.
+add_custom_target(codegen
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+)
diff --git a/Tests/RunCMake/CMP0171/CMP0171-codegen-build-result.txt b/Tests/RunCMake/CMP0171/CMP0171-codegen-build-result.txt
new file mode 100644
index 0000000..d197c91
--- /dev/null
+++ b/Tests/RunCMake/CMP0171/CMP0171-codegen-build-result.txt
@@ -0,0 +1 @@
+[^0]
diff --git a/Tests/RunCMake/CMP0171/CMP0171-codegen.cmake b/Tests/RunCMake/CMP0171/CMP0171-codegen.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/CMP0171/CMP0171-codegen.cmake
diff --git a/Tests/RunCMake/CMP0171/CMakeLists.txt b/Tests/RunCMake/CMP0171/CMakeLists.txt
new file mode 100644
index 0000000..1a5755c
--- /dev/null
+++ b/Tests/RunCMake/CMP0171/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.29)
+project(${RunCMake_TEST} LANGUAGES C)
+
+include(${RunCMake_TEST}.cmake)
+
+enable_testing()
diff --git a/Tests/RunCMake/CMP0171/RunCMakeTest.cmake b/Tests/RunCMake/CMP0171/RunCMakeTest.cmake
new file mode 100644
index 0000000..75941d7
--- /dev/null
+++ b/Tests/RunCMake/CMP0171/RunCMakeTest.cmake
@@ -0,0 +1,18 @@
+include(RunCMake)
+
+run_cmake("CMP0171-WARN")
+
+run_cmake_with_options(CMP0171-OLD "-DCMAKE_POLICY_DEFAULT_CMP0171=OLD")
+
+run_cmake_with_options(CMP0171-NEW "-DCMAKE_POLICY_DEFAULT_CMP0171=NEW")
+
+# The entire point of this test is to ensure the codegen target is not created
+# unintentionally. It can only be created if CMP0171 is NEW.
+block()
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0171-codegen-build)
+ run_cmake(CMP0171-codegen)
+ set(RunCMake_TEST_NO_CLEAN 1)
+ set(RunCMake_TEST_OUTPUT_MERGE 1)
+ # This command will fail with either 1 or 2 depending.
+ run_cmake_command(CMP0171-codegen-build ${CMAKE_COMMAND} --build . --config Debug --target codegen)
+endblock()
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index bfa59d6..db5ef96 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -180,6 +180,7 @@ add_RunCMake_test(CMP0163)
add_RunCMake_test(CMP0165)
add_RunCMake_test(CMP0169)
add_RunCMake_test(CMP0170)
+add_RunCMake_test(CMP0171)
# The test for Policy 65 requires the use of the
# CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode
@@ -1228,7 +1229,8 @@ add_RunCMake_test(CMakePresetsWorkflow
add_RunCMake_test(VerifyHeaderSets)
add_RunCMake_test(set_tests_properties)
-if(${CMAKE_GENERATOR} MATCHES "Make|Ninja")
+if(CMAKE_GENERATOR MATCHES "Make|Ninja")
+ add_RunCMake_test(Codegen)
add_RunCMake_test(TransformDepfile)
endif()
diff --git a/Tests/RunCMake/Codegen/CMakeLists.txt b/Tests/RunCMake/Codegen/CMakeLists.txt
new file mode 100644
index 0000000..f65150d
--- /dev/null
+++ b/Tests/RunCMake/Codegen/CMakeLists.txt
@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 3.29)
+project(${RunCMake_TEST} LANGUAGES C)
+
+# This value is read from the top level CMakeLists.txt
+cmake_policy(SET CMP0171 NEW)
+
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/Codegen/RunCMakeTest.cmake b/Tests/RunCMake/Codegen/RunCMakeTest.cmake
new file mode 100644
index 0000000..bbd70b0
--- /dev/null
+++ b/Tests/RunCMake/Codegen/RunCMakeTest.cmake
@@ -0,0 +1,33 @@
+include(RunCMake)
+
+function(run_codegen case)
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build)
+
+ run_cmake(${case})
+
+ set(RunCMake_TEST_NO_CLEAN 1)
+
+ run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --target codegen --config Debug)
+endfunction()
+
+# Builds codegen target when there are no custom commands marked codegen
+run_codegen("no-codegen")
+
+# We don't want codegen to drive parts of the project that are EXCLUDE_FROM_ALL
+run_codegen("exclude-from-all")
+
+# Ensures codegen builds minimal build graphs
+run_codegen("min-graph-1")
+run_codegen("min-graph-2")
+run_codegen("min-graph-3")
+
+# Handle specific cases that can affect codegen
+run_codegen("add-dependencies")
+run_codegen("add-custom-command-depends")
+run_codegen("byproducts")
+
+# Error handling
+run_cmake("implicit-depends")
+run_cmake("implicit-depends-append-codegen")
+run_cmake("append-implicit-depends")
+run_cmake("no-output")
diff --git a/Tests/RunCMake/Codegen/add-custom-command-depends-build-check.cmake b/Tests/RunCMake/Codegen/add-custom-command-depends-build-check.cmake
new file mode 100644
index 0000000..d371d73
--- /dev/null
+++ b/Tests/RunCMake/Codegen/add-custom-command-depends-build-check.cmake
@@ -0,0 +1,5 @@
+set(filename "${RunCMake_TEST_BINARY_DIR}/generated.hpp")
+if (NOT EXISTS "${filename}")
+ set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}")
+ return()
+endif()
diff --git a/Tests/RunCMake/Codegen/add-custom-command-depends.cmake b/Tests/RunCMake/Codegen/add-custom-command-depends.cmake
new file mode 100644
index 0000000..793ab5f
--- /dev/null
+++ b/Tests/RunCMake/Codegen/add-custom-command-depends.cmake
@@ -0,0 +1,16 @@
+add_custom_target(foobar
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+)
+
+add_custom_command(
+ OUTPUT generated.hpp
+ # This test will fail if DEPENDS isn't accounted for in the codegen build graph
+ DEPENDS foobar
+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp
+ CODEGEN
+)
+
+add_custom_target(hpp_creator ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp)
diff --git a/Tests/RunCMake/Codegen/add-dependencies-build-check.cmake b/Tests/RunCMake/Codegen/add-dependencies-build-check.cmake
new file mode 100644
index 0000000..d371d73
--- /dev/null
+++ b/Tests/RunCMake/Codegen/add-dependencies-build-check.cmake
@@ -0,0 +1,5 @@
+set(filename "${RunCMake_TEST_BINARY_DIR}/generated.hpp")
+if (NOT EXISTS "${filename}")
+ set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}")
+ return()
+endif()
diff --git a/Tests/RunCMake/Codegen/add-dependencies.cmake b/Tests/RunCMake/Codegen/add-dependencies.cmake
new file mode 100644
index 0000000..fbb7e99
--- /dev/null
+++ b/Tests/RunCMake/Codegen/add-dependencies.cmake
@@ -0,0 +1,18 @@
+add_custom_target(foobar
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+)
+
+add_custom_command(
+ OUTPUT generated.hpp
+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp
+ CODEGEN
+)
+
+add_custom_target(hpp_creator ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp)
+
+# This test will fail if add_dependencies isn't account for in the
+# codegen build graph
+add_dependencies(hpp_creator foobar)
diff --git a/Tests/RunCMake/Codegen/append-implicit-depends-result.txt b/Tests/RunCMake/Codegen/append-implicit-depends-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Codegen/append-implicit-depends-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Codegen/append-implicit-depends-stderr.txt b/Tests/RunCMake/Codegen/append-implicit-depends-stderr.txt
new file mode 100644
index 0000000..c8ef03e
--- /dev/null
+++ b/Tests/RunCMake/Codegen/append-implicit-depends-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error:
+ Cannot append IMPLICIT_DEPENDS to existing CODEGEN custom command\.
diff --git a/Tests/RunCMake/Codegen/append-implicit-depends.cmake b/Tests/RunCMake/Codegen/append-implicit-depends.cmake
new file mode 100644
index 0000000..d212fe5
--- /dev/null
+++ b/Tests/RunCMake/Codegen/append-implicit-depends.cmake
@@ -0,0 +1,19 @@
+add_custom_command(
+ OUTPUT
+ ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
+ COMMAND
+ ${CMAKE_COMMAND} -E
+ copy ${CMAKE_CURRENT_SOURCE_DIR}/error.c
+ ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
+ CODEGEN
+)
+
+add_custom_command(
+ OUTPUT
+ ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
+
+ # ERROR out if IMPLICIT_DEPENDS is used with CODEGEN
+ IMPLICIT_DEPENDS C main.c
+
+ APPEND
+)
diff --git a/Tests/RunCMake/Codegen/byproducts-build-check.cmake b/Tests/RunCMake/Codegen/byproducts-build-check.cmake
new file mode 100644
index 0000000..d371d73
--- /dev/null
+++ b/Tests/RunCMake/Codegen/byproducts-build-check.cmake
@@ -0,0 +1,5 @@
+set(filename "${RunCMake_TEST_BINARY_DIR}/generated.hpp")
+if (NOT EXISTS "${filename}")
+ set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}")
+ return()
+endif()
diff --git a/Tests/RunCMake/Codegen/byproducts.cmake b/Tests/RunCMake/Codegen/byproducts.cmake
new file mode 100644
index 0000000..ea0b6c7
--- /dev/null
+++ b/Tests/RunCMake/Codegen/byproducts.cmake
@@ -0,0 +1,19 @@
+add_custom_target(foobar
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+ BYPRODUCTS
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+)
+
+# This codegen step relies on the BYPRODUCTS of the previous command.
+# If foobar isn't properly accounted for as a dependency it will fail.
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp
+ CODEGEN
+)
+
+add_custom_target(hpp_creator ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp)
diff --git a/Tests/RunCMake/Codegen/error.c b/Tests/RunCMake/Codegen/error.c
new file mode 100644
index 0000000..34cb350
--- /dev/null
+++ b/Tests/RunCMake/Codegen/error.c
@@ -0,0 +1 @@
+#error "This file should not be compiled"
diff --git a/Tests/RunCMake/Codegen/exclude-from-all.cmake b/Tests/RunCMake/Codegen/exclude-from-all.cmake
new file mode 100644
index 0000000..bcd4ac0
--- /dev/null
+++ b/Tests/RunCMake/Codegen/exclude-from-all.cmake
@@ -0,0 +1,11 @@
+add_custom_command(
+ OUTPUT
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+ COMMAND
+ ${CMAKE_COMMAND} -E false
+ CODEGEN
+)
+
+# We don't want codegen to drive parts of the project that are EXCLUDE_FROM_ALL.
+# This tests that foobar is properly excluded from the codegen build.
+add_executable(foobar EXCLUDE_FROM_ALL error.c ${CMAKE_CURRENT_BINARY_DIR}/generated.h)
diff --git a/Tests/RunCMake/Codegen/generated.h.in b/Tests/RunCMake/Codegen/generated.h.in
new file mode 100644
index 0000000..82ccf67
--- /dev/null
+++ b/Tests/RunCMake/Codegen/generated.h.in
@@ -0,0 +1 @@
+// hello
diff --git a/Tests/RunCMake/Codegen/implicit-depends-append-codegen-result.txt b/Tests/RunCMake/Codegen/implicit-depends-append-codegen-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Codegen/implicit-depends-append-codegen-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Codegen/implicit-depends-append-codegen-stderr.txt b/Tests/RunCMake/Codegen/implicit-depends-append-codegen-stderr.txt
new file mode 100644
index 0000000..570cf62
--- /dev/null
+++ b/Tests/RunCMake/Codegen/implicit-depends-append-codegen-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at implicit-depends-append-codegen\.cmake:[0-9]+ \(add_custom_command\):
+ add_custom_command CODEGEN may not be used with APPEND\.
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/Codegen/implicit-depends-append-codegen.cmake b/Tests/RunCMake/Codegen/implicit-depends-append-codegen.cmake
new file mode 100644
index 0000000..76151cc
--- /dev/null
+++ b/Tests/RunCMake/Codegen/implicit-depends-append-codegen.cmake
@@ -0,0 +1,18 @@
+add_custom_command(
+ OUTPUT
+ ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
+
+ # ERROR out if IMPLICIT_DEPENDS is used with CODEGEN
+ IMPLICIT_DEPENDS C main.c
+)
+
+add_custom_command(
+ OUTPUT
+ ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
+ COMMAND
+ ${CMAKE_COMMAND} -E
+ copy ${CMAKE_CURRENT_SOURCE_DIR}/error.c
+ ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
+ CODEGEN
+ APPEND
+)
diff --git a/Tests/RunCMake/Codegen/implicit-depends-result.txt b/Tests/RunCMake/Codegen/implicit-depends-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Codegen/implicit-depends-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Codegen/implicit-depends-stderr.txt b/Tests/RunCMake/Codegen/implicit-depends-stderr.txt
new file mode 100644
index 0000000..b9ea8f4
--- /dev/null
+++ b/Tests/RunCMake/Codegen/implicit-depends-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at implicit-depends\.cmake:[0-9]+ \(add_custom_command\):
+ add_custom_command CODEGEN is not compatible with IMPLICIT_DEPENDS\.
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/Codegen/implicit-depends.cmake b/Tests/RunCMake/Codegen/implicit-depends.cmake
new file mode 100644
index 0000000..011d4b3
--- /dev/null
+++ b/Tests/RunCMake/Codegen/implicit-depends.cmake
@@ -0,0 +1,11 @@
+add_custom_command(
+ OUTPUT
+ ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
+ COMMAND
+ ${CMAKE_COMMAND} -E
+ copy ${CMAKE_CURRENT_SOURCE_DIR}/error.c
+ ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
+ CODEGEN
+ # ERROR out if IMPLICIT_DEPENDS is used with CODEGEN
+ IMPLICIT_DEPENDS C main.c
+)
diff --git a/Tests/RunCMake/Codegen/main.c b/Tests/RunCMake/Codegen/main.c
new file mode 100644
index 0000000..8488f4e
--- /dev/null
+++ b/Tests/RunCMake/Codegen/main.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+ return 0;
+}
diff --git a/Tests/RunCMake/Codegen/min-graph-1-build-check.cmake b/Tests/RunCMake/Codegen/min-graph-1-build-check.cmake
new file mode 100644
index 0000000..32e1557
--- /dev/null
+++ b/Tests/RunCMake/Codegen/min-graph-1-build-check.cmake
@@ -0,0 +1,13 @@
+set(filename "${RunCMake_TEST_BINARY_DIR}/generated.h")
+if (NOT EXISTS "${filename}")
+ set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}")
+ return()
+endif()
+
+# foobar should be built since it was needed
+# by the code generation
+set(filename "${RunCMake_TEST_BINARY_DIR}/foobar.txt")
+if (NOT EXISTS "${filename}")
+ set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}")
+ return()
+endif()
diff --git a/Tests/RunCMake/Codegen/min-graph-1.cmake b/Tests/RunCMake/Codegen/min-graph-1.cmake
new file mode 100644
index 0000000..ea47b8f
--- /dev/null
+++ b/Tests/RunCMake/Codegen/min-graph-1.cmake
@@ -0,0 +1,26 @@
+add_executable(foobar main.c)
+add_custom_command(
+ TARGET foobar POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E
+ copy ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/foobar.txt
+)
+
+add_custom_command(
+ OUTPUT
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+ COMMAND
+ ${CMAKE_COMMAND} -E
+ copy ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+ COMMAND
+ # Generate a header file that requires foobar
+ foobar
+ CODEGEN
+)
+
+add_library(errorlib
+ # If this library is built error.c will cause the build to fail
+ error.c
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+)
diff --git a/Tests/RunCMake/Codegen/min-graph-2-build-check.cmake b/Tests/RunCMake/Codegen/min-graph-2-build-check.cmake
new file mode 100644
index 0000000..fab168b
--- /dev/null
+++ b/Tests/RunCMake/Codegen/min-graph-2-build-check.cmake
@@ -0,0 +1,5 @@
+set(filename "${RunCMake_TEST_BINARY_DIR}/generated.h")
+if (NOT EXISTS "${filename}")
+ set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}")
+ return()
+endif()
diff --git a/Tests/RunCMake/Codegen/min-graph-2.cmake b/Tests/RunCMake/Codegen/min-graph-2.cmake
new file mode 100644
index 0000000..277a901
--- /dev/null
+++ b/Tests/RunCMake/Codegen/min-graph-2.cmake
@@ -0,0 +1,18 @@
+add_custom_command(
+ OUTPUT
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+ COMMAND
+ ${CMAKE_COMMAND} -E
+ copy ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+ CODEGEN
+)
+
+# This target should not be built. It has no reason
+# to be part of the codegen build graph
+add_custom_target(error_custom_target ALL
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+
+ # Cause the build to fail
+ COMMAND ${CMAKE_COMMAND} -E false
+)
diff --git a/Tests/RunCMake/Codegen/min-graph-3-build-check.cmake b/Tests/RunCMake/Codegen/min-graph-3-build-check.cmake
new file mode 100644
index 0000000..734777b
--- /dev/null
+++ b/Tests/RunCMake/Codegen/min-graph-3-build-check.cmake
@@ -0,0 +1,5 @@
+set(filename "${RunCMake_TEST_BINARY_DIR}/error_lib.c")
+if (NOT EXISTS "${filename}")
+ set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}")
+ return()
+endif()
diff --git a/Tests/RunCMake/Codegen/min-graph-3.cmake b/Tests/RunCMake/Codegen/min-graph-3.cmake
new file mode 100644
index 0000000..c7d61dc
--- /dev/null
+++ b/Tests/RunCMake/Codegen/min-graph-3.cmake
@@ -0,0 +1,12 @@
+add_custom_command(
+ OUTPUT
+ ${CMAKE_CURRENT_BINARY_DIR}/error_lib.c
+ COMMAND
+ ${CMAKE_COMMAND} -E
+ copy ${CMAKE_CURRENT_SOURCE_DIR}/error.c
+ ${CMAKE_CURRENT_BINARY_DIR}/error_lib.c
+ CODEGEN
+)
+
+# This test will fail if error_lib.c is actually compiled
+add_executable(foobar ${CMAKE_CURRENT_BINARY_DIR}/error_lib.c)
diff --git a/Tests/RunCMake/Codegen/no-codegen-check.cmake b/Tests/RunCMake/Codegen/no-codegen-check.cmake
new file mode 100644
index 0000000..97fc46b
--- /dev/null
+++ b/Tests/RunCMake/Codegen/no-codegen-check.cmake
@@ -0,0 +1,5 @@
+# Verify generated.hpp was NOT created
+set(unexpected "${RunCMake_TEST_BINARY_DIR}/generated.hpp")
+if(EXISTS "${unexpected}")
+ set(RunCMake_TEST_FAILED "unexpected file created:\n ${unexpected}")
+endif()
diff --git a/Tests/RunCMake/Codegen/no-codegen.cmake b/Tests/RunCMake/Codegen/no-codegen.cmake
new file mode 100644
index 0000000..00ddd03
--- /dev/null
+++ b/Tests/RunCMake/Codegen/no-codegen.cmake
@@ -0,0 +1,6 @@
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp
+)
diff --git a/Tests/RunCMake/Codegen/no-output-result.txt b/Tests/RunCMake/Codegen/no-output-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Codegen/no-output-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Codegen/no-output-stderr.txt b/Tests/RunCMake/Codegen/no-output-stderr.txt
new file mode 100644
index 0000000..7aad679
--- /dev/null
+++ b/Tests/RunCMake/Codegen/no-output-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at no-output\.cmake:[0-9]+ \(add_custom_command\):
+ add_custom_command CODEGEN requires at least 1 OUTPUT\.
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/Codegen/no-output.cmake b/Tests/RunCMake/Codegen/no-output.cmake
new file mode 100644
index 0000000..61eb83c
--- /dev/null
+++ b/Tests/RunCMake/Codegen/no-output.cmake
@@ -0,0 +1,11 @@
+add_custom_target(foobar
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/generated.h
+)
+
+add_custom_command(TARGET foobar POST_BUILD
+ COMMAND
+ ${CMAKE_COMMAND} -E true
+ CODEGEN
+)