diff options
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 +) |