add_custom_command: Add CODEGEN support

By specifying CODEGEN as an argument to add_custom_command the
custom command will be added to a codegen build target.

The intent is to provide a convenient way for users to get
their generated files without having to build the whole project.

This can be helpful for code analysis tools which can be useful
for IDEs and CI.
This commit is contained in:
Juan Ramos
2024-05-27 20:19:05 -07:00
committed by Brad King
parent 033713530a
commit 197cb419d1
64 changed files with 733 additions and 3 deletions
+32
View File
@@ -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
""""""""""""""""""""""""""""""""""""""""""""""
+8
View File
@@ -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
=================================
+26
View File
@@ -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
+10
View File
@@ -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.
+29 -1
View File
@@ -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 {
+1
View File
@@ -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(
+5
View File
@@ -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.
+49
View File
@@ -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
{
+5
View File
@@ -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;
};
+86
View File
@@ -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);
+39
View File
@@ -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)
{
+3
View File
@@ -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"; }
+6
View File
@@ -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;
}
+15
View File
@@ -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();
+29
View File
@@ -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
View File
@@ -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) \
+11
View File
@@ -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
{
+5
View File
@@ -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;
+11
View File
@@ -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\)
+6
View File
@@ -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\)
+9
View File
@@ -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]
+6
View File
@@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.29)
project(${RunCMake_TEST} LANGUAGES C)
include(${RunCMake_TEST}.cmake)
enable_testing()
+18
View File
@@ -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()
+3 -1
View File
@@ -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()
+7
View File
@@ -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)
+33
View File
@@ -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()
+19
View File
@@ -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)
+1
View File
@@ -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)
+1
View File
@@ -0,0 +1 @@
// hello
@@ -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
)
+4
View File
@@ -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()
+26
View File
@@ -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()
+18
View File
@@ -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()
+12
View File
@@ -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()
+6
View File
@@ -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\)
+11
View File
@@ -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
)