mirror of
https://github.com/Kitware/CMake.git
synced 2026-04-22 06:09:14 -05:00
Merge topic 'codegen'
197cb419d1 add_custom_command: Add CODEGEN support
Acked-by: Kitware Robot <kwrobot@kitware.com>
Tested-by: buildbot <buildbot@kitware.com>
Merge-request: !9581
This commit is contained in:
@@ -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
|
||||
""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
|
||||
@@ -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
|
||||
=================================
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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"; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
+3
-1
@@ -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) \
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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
|
||||
)
|
||||
@@ -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\.
|
||||
@@ -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
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
[^0]
|
||||
@@ -0,0 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.29)
|
||||
project(${RunCMake_TEST} LANGUAGES C)
|
||||
|
||||
include(${RunCMake_TEST}.cmake)
|
||||
|
||||
enable_testing()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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")
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,2 @@
|
||||
CMake Error:
|
||||
Cannot append IMPLICIT_DEPENDS to existing CODEGEN custom command\.
|
||||
@@ -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
|
||||
)
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
#error "This file should not be compiled"
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
// hello
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -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
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
int main(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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
|
||||
)
|
||||
@@ -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()
|
||||
@@ -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
|
||||
)
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -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
|
||||
)
|
||||
Reference in New Issue
Block a user