diff options
author | Brad King <brad.king@kitware.com> | 2020-09-21 17:51:35 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2020-09-29 21:12:33 (GMT) |
commit | e8b0359a4318bb682c96e527de7ed7f5be02c38f (patch) | |
tree | 5ea4137b78124e4619bb4aff1856a04b729f93fd /Source | |
parent | 98805494055f8fb4afc2da9f96a487987333981a (diff) | |
download | CMake-e8b0359a4318bb682c96e527de7ed7f5be02c38f.zip CMake-e8b0359a4318bb682c96e527de7ed7f5be02c38f.tar.gz CMake-e8b0359a4318bb682c96e527de7ed7f5be02c38f.tar.bz2 |
cmake_language: Add signature to DEFER calls to later times
Fixes: #19575
Diffstat (limited to 'Source')
-rw-r--r-- | Source/cmCMakeLanguageCommand.cxx | 220 | ||||
-rw-r--r-- | Source/cmCommandArgumentParserHelper.cxx | 12 | ||||
-rw-r--r-- | Source/cmGlobalGenerator.cxx | 7 | ||||
-rw-r--r-- | Source/cmGlobalGenerator.h | 5 | ||||
-rw-r--r-- | Source/cmListFileCache.cxx | 7 | ||||
-rw-r--r-- | Source/cmListFileCache.h | 10 | ||||
-rw-r--r-- | Source/cmMakefile.cxx | 190 | ||||
-rw-r--r-- | Source/cmMakefile.h | 37 | ||||
-rw-r--r-- | Source/cmState.cxx | 15 | ||||
-rw-r--r-- | Source/cmState.h | 2 | ||||
-rw-r--r-- | Source/cmStateTypes.h | 1 | ||||
-rw-r--r-- | Source/cmake.cxx | 2 |
12 files changed, 485 insertions, 23 deletions
diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx index d92ca28..9277c20 100644 --- a/Source/cmCMakeLanguageCommand.cxx +++ b/Source/cmCMakeLanguageCommand.cxx @@ -7,11 +7,14 @@ #include <cstddef> #include <memory> #include <string> +#include <utility> +#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> #include "cmExecutionStatus.h" +#include "cmGlobalGenerator.h" #include "cmListFileCache.h" #include "cmMakefile.h" #include "cmRange.h" @@ -37,9 +40,24 @@ std::array<cm::static_string_view, 12> InvalidCommands{ } // clang-format on }; +std::array<cm::static_string_view, 1> InvalidDeferCommands{ + { + // clang-format off + "return"_s, + } // clang-format on +}; + +struct Defer +{ + std::string Id; + std::string IdVar; + cmMakefile* Directory = nullptr; +}; + bool cmCMakeLanguageCommandCALL(std::vector<cmListFileArgument> const& args, std::string const& callCommand, - size_t startArg, cmExecutionStatus& status) + size_t startArg, cm::optional<Defer> defer, + cmExecutionStatus& status) { // ensure specified command is valid // start/end flow control commands are not allowed @@ -49,6 +67,12 @@ bool cmCMakeLanguageCommandCALL(std::vector<cmListFileArgument> const& args, return FatalError(status, cmStrCat("invalid command specified: "_s, callCommand)); } + if (defer && + std::find(InvalidDeferCommands.cbegin(), InvalidDeferCommands.cend(), + cmd) != InvalidDeferCommands.cend()) { + return FatalError(status, + cmStrCat("invalid command specified: "_s, callCommand)); + } cmMakefile& makefile = status.GetMakefile(); cmListFileContext context = makefile.GetBacktrace().Top(); @@ -66,9 +90,106 @@ bool cmCMakeLanguageCommandCALL(std::vector<cmListFileArgument> const& args, func.Arguments.emplace_back(lfarg); } + if (defer) { + if (defer->Id.empty()) { + defer->Id = makefile.NewDeferId(); + } + if (!defer->IdVar.empty()) { + makefile.AddDefinition(defer->IdVar, defer->Id); + } + cmMakefile* deferMakefile = + defer->Directory ? defer->Directory : &makefile; + if (!deferMakefile->DeferCall(defer->Id, context.FilePath, func)) { + return FatalError( + status, + cmStrCat("DEFER CALL may not be scheduled in directory:\n "_s, + deferMakefile->GetCurrentBinaryDirectory(), + "\nat this time."_s)); + } + return true; + } return makefile.ExecuteCommand(func, status); } +bool cmCMakeLanguageCommandDEFER(Defer const& defer, + std::vector<std::string> const& args, + size_t arg, cmExecutionStatus& status) +{ + cmMakefile* deferMakefile = + defer.Directory ? defer.Directory : &status.GetMakefile(); + if (args[arg] == "CANCEL_CALL"_s) { + ++arg; // Consume CANCEL_CALL. + auto ids = cmMakeRange(args).advance(arg); + for (std::string const& id : ids) { + if (id[0] >= 'A' && id[0] <= 'Z') { + return FatalError( + status, cmStrCat("DEFER CANCEL_CALL unknown argument:\n "_s, id)); + } + if (!deferMakefile->DeferCancelCall(id)) { + return FatalError( + status, + cmStrCat("DEFER CANCEL_CALL may not update directory:\n "_s, + deferMakefile->GetCurrentBinaryDirectory(), + "\nat this time."_s)); + } + } + return true; + } + if (args[arg] == "GET_CALL_IDS"_s) { + ++arg; // Consume GET_CALL_IDS. + if (arg == args.size()) { + return FatalError(status, "DEFER GET_CALL_IDS missing output variable"); + } + std::string const& var = args[arg++]; + if (arg != args.size()) { + return FatalError(status, "DEFER GET_CALL_IDS given too many arguments"); + } + cm::optional<std::string> ids = deferMakefile->DeferGetCallIds(); + if (!ids) { + return FatalError( + status, + cmStrCat("DEFER GET_CALL_IDS may not access directory:\n "_s, + deferMakefile->GetCurrentBinaryDirectory(), + "\nat this time."_s)); + } + status.GetMakefile().AddDefinition(var, *ids); + return true; + } + if (args[arg] == "GET_CALL"_s) { + ++arg; // Consume GET_CALL. + if (arg == args.size()) { + return FatalError(status, "DEFER GET_CALL missing id"); + } + std::string const& id = args[arg++]; + if (arg == args.size()) { + return FatalError(status, "DEFER GET_CALL missing output variable"); + } + std::string const& var = args[arg++]; + if (arg != args.size()) { + return FatalError(status, "DEFER GET_CALL given too many arguments"); + } + if (id.empty()) { + return FatalError(status, "DEFER GET_CALL id may not be empty"); + } + if (id[0] >= 'A' && id[0] <= 'Z') { + return FatalError(status, + cmStrCat("DEFER GET_CALL unknown argument:\n "_s, id)); + } + cm::optional<std::string> call = deferMakefile->DeferGetCall(id); + if (!call) { + return FatalError( + status, + cmStrCat("DEFER GET_CALL may not access directory:\n "_s, + deferMakefile->GetCurrentBinaryDirectory(), + "\nat this time."_s)); + } + status.GetMakefile().AddDefinition(var, *call); + return true; + } + return FatalError(status, + cmStrCat("DEFER operation unknown: "_s, args[arg])); +} + bool cmCMakeLanguageCommandEVAL(std::vector<cmListFileArgument> const& args, cmExecutionStatus& status) { @@ -118,11 +239,105 @@ bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args, } return true; }; + auto finishArgs = [&]() { + std::vector<cmListFileArgument> tmpArgs(args.begin() + rawArg, args.end()); + status.GetMakefile().ExpandArguments(tmpArgs, expArgs); + rawArg = args.size(); + }; if (!moreArgs()) { return FatalError(status, "called with incorrect number of arguments"); } + cm::optional<Defer> maybeDefer; + if (expArgs[expArg] == "DEFER"_s) { + ++expArg; // Consume "DEFER". + + if (!moreArgs()) { + return FatalError(status, "DEFER requires at least one argument"); + } + + Defer defer; + + // Process optional arguments. + while (moreArgs()) { + if (expArgs[expArg] == "CALL"_s) { + break; + } + if (expArgs[expArg] == "CANCEL_CALL"_s || + expArgs[expArg] == "GET_CALL_IDS"_s || + expArgs[expArg] == "GET_CALL"_s) { + if (!defer.Id.empty() || !defer.IdVar.empty()) { + return FatalError(status, + cmStrCat("DEFER "_s, expArgs[expArg], + " does not accept ID or ID_VAR."_s)); + } + finishArgs(); + return cmCMakeLanguageCommandDEFER(defer, expArgs, expArg, status); + } + if (expArgs[expArg] == "DIRECTORY"_s) { + ++expArg; // Consume "DIRECTORY". + if (defer.Directory) { + return FatalError(status, + "DEFER given multiple DIRECTORY arguments"); + } + if (!moreArgs()) { + return FatalError(status, "DEFER DIRECTORY missing value"); + } + std::string dir = expArgs[expArg++]; + if (dir.empty()) { + return FatalError(status, "DEFER DIRECTORY may not be empty"); + } + dir = cmSystemTools::CollapseFullPath( + dir, status.GetMakefile().GetCurrentSourceDirectory()); + defer.Directory = + status.GetMakefile().GetGlobalGenerator()->FindMakefile(dir); + if (!defer.Directory) { + return FatalError(status, + cmStrCat("DEFER DIRECTORY:\n "_s, dir, + "\nis not known. "_s, + "It may not have been processed yet."_s)); + } + } else if (expArgs[expArg] == "ID"_s) { + ++expArg; // Consume "ID". + if (!defer.Id.empty()) { + return FatalError(status, "DEFER given multiple ID arguments"); + } + if (!moreArgs()) { + return FatalError(status, "DEFER ID missing value"); + } + defer.Id = expArgs[expArg++]; + if (defer.Id.empty()) { + return FatalError(status, "DEFER ID may not be empty"); + } + if (defer.Id[0] >= 'A' && defer.Id[0] <= 'Z') { + return FatalError(status, "DEFER ID may not start in A-Z."); + } + } else if (expArgs[expArg] == "ID_VAR"_s) { + ++expArg; // Consume "ID_VAR". + if (!defer.IdVar.empty()) { + return FatalError(status, "DEFER given multiple ID_VAR arguments"); + } + if (!moreArgs()) { + return FatalError(status, "DEFER ID_VAR missing variable name"); + } + defer.IdVar = expArgs[expArg++]; + if (defer.IdVar.empty()) { + return FatalError(status, "DEFER ID_VAR may not be empty"); + } + } else { + return FatalError( + status, cmStrCat("DEFER unknown option:\n "_s, expArgs[expArg])); + } + } + + if (!(moreArgs() && expArgs[expArg] == "CALL"_s)) { + return FatalError(status, "DEFER must be followed by a CALL argument"); + } + + maybeDefer = std::move(defer); + } + if (expArgs[expArg] == "CALL") { ++expArg; // Consume "CALL". @@ -138,7 +353,8 @@ bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args, } // Run the CALL. - return cmCMakeLanguageCommandCALL(args, callCommand, rawArg, status); + return cmCMakeLanguageCommandCALL(args, callCommand, rawArg, + std::move(maybeDefer), status); } if (expArgs[expArg] == "EVAL") { diff --git a/Source/cmCommandArgumentParserHelper.cxx b/Source/cmCommandArgumentParserHelper.cxx index e3d014e..d4f5022 100644 --- a/Source/cmCommandArgumentParserHelper.cxx +++ b/Source/cmCommandArgumentParserHelper.cxx @@ -8,8 +8,11 @@ #include <utility> #include <cm/memory> +#include <cm/optional> +#include <cmext/string_view> #include "cmCommandArgumentLexer.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmProperty.h" #include "cmState.h" @@ -91,7 +94,14 @@ const char* cmCommandArgumentParserHelper::ExpandVariable(const char* var) return nullptr; } if (this->FileLine >= 0 && strcmp(var, "CMAKE_CURRENT_LIST_LINE") == 0) { - return this->AddString(std::to_string(this->FileLine)); + std::string line; + cmListFileContext const& top = this->Makefile->GetBacktrace().Top(); + if (top.DeferId) { + line = cmStrCat("DEFERRED:"_s, *top.DeferId); + } else { + line = std::to_string(this->FileLine); + } + return this->AddString(line); } cmProp value = this->Makefile->GetDefinition(var); if (!value) { diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 46030e0..1197db6 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -15,6 +15,7 @@ #include <cm/memory> #include <cmext/algorithm> +#include <cmext/string_view> #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" @@ -1211,6 +1212,7 @@ void cmGlobalGenerator::Configure() { this->FirstTimeProgress = 0.0f; this->ClearGeneratorMembers(); + this->NextDeferId = 0; cmStateSnapshot snapshot = this->CMakeInstance->GetCurrentSnapshot(); @@ -3256,6 +3258,11 @@ const std::string& cmGlobalGenerator::GetRealPath(const std::string& dir) return i->second; } +std::string cmGlobalGenerator::NewDeferId() +{ + return cmStrCat("__"_s, std::to_string(this->NextDeferId++)); +} + void cmGlobalGenerator::ProcessEvaluationFiles() { std::vector<std::string> generatedFiles; diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 478028e..b532a43 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -508,6 +508,8 @@ public: std::string const& GetRealPath(std::string const& dir); + std::string NewDeferId(); + protected: // for a project collect all its targets by following depend // information, and also collect all the targets @@ -633,6 +635,9 @@ private: std::map<std::string, int> LanguageToLinkerPreference; std::map<std::string, std::string> LanguageToOriginalSharedLibFlags; + // Deferral id generation. + size_t NextDeferId = 0; + // Record hashes for rules and outputs. struct RuleHash { diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index 7ebb02f..d678b56 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -446,7 +446,8 @@ void cmListFileBacktrace::PrintCallStack(std::ostream& out) const cmStateSnapshot bottom = this->GetBottom(); for (Entry const* cur = this->TopEntry->Parent.get(); !cur->IsBottom(); cur = cur->Parent.get()) { - if (cur->Context.Name.empty()) { + if (cur->Context.Name.empty() && + cur->Context.Line != cmListFileContext::DeferPlaceholderLine) { // Skip this whole-file scope. When we get here we already will // have printed a more-specific context within the file. continue; @@ -483,11 +484,13 @@ bool cmListFileBacktrace::Empty() const std::ostream& operator<<(std::ostream& os, cmListFileContext const& lfc) { os << lfc.FilePath; - if (lfc.Line) { + if (lfc.Line > 0) { os << ":" << lfc.Line; if (!lfc.Name.empty()) { os << " (" << lfc.Name << ")"; } + } else if (lfc.Line == cmListFileContext::DeferPlaceholderLine) { + os << ":DEFERRED"; } return os; } diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h index 5773e6a..5617536 100644 --- a/Source/cmListFileCache.h +++ b/Source/cmListFileCache.h @@ -11,6 +11,8 @@ #include <utility> #include <vector> +#include <cm/optional> + #include "cmStateSnapshot.h" /** \class cmListFileCache @@ -72,6 +74,8 @@ public: std::string Name; std::string FilePath; long Line = 0; + static long const DeferPlaceholderLine = -1; + cm::optional<std::string> DeferId; cmListFileContext() = default; cmListFileContext(std::string name, std::string filePath, long line) @@ -81,13 +85,15 @@ public: { } - static cmListFileContext FromCommandContext(cmCommandContext const& lfcc, - std::string const& fileName) + static cmListFileContext FromCommandContext( + cmCommandContext const& lfcc, std::string const& fileName, + cm::optional<std::string> deferId = {}) { cmListFileContext lfc; lfc.FilePath = fileName; lfc.Line = lfcc.Line; lfc.Name = lfcc.Name.Original; + lfc.DeferId = std::move(deferId); return lfc; } }; diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 896839b..ac3a193 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -16,6 +16,7 @@ #include <cm/iterator> #include <cm/memory> #include <cm/optional> +#include <cm/type_traits> // IWYU pragma: keep #include <cm/vector> #include <cmext/algorithm> #include <cmext/string_view> @@ -274,7 +275,9 @@ cmListFileBacktrace cmMakefile::GetBacktrace() const return this->Backtrace; } -void cmMakefile::PrintCommandTrace(const cmListFileFunction& lff) const +void cmMakefile::PrintCommandTrace( + cmListFileFunction const& lff, + cm::optional<std::string> const& deferId) const { // Check if current file in the list of requested to trace... std::vector<std::string> const& trace_only_this_files = @@ -322,6 +325,9 @@ void cmMakefile::PrintCommandTrace(const cmListFileFunction& lff) const builder["indentation"] = ""; val["file"] = full_path; val["line"] = static_cast<Json::Value::Int64>(lff.Line); + if (deferId) { + val["defer"] = *deferId; + } val["cmd"] = lff.Name.Original; val["args"] = Json::Value(Json::arrayValue); for (std::string const& arg : args) { @@ -335,8 +341,11 @@ void cmMakefile::PrintCommandTrace(const cmListFileFunction& lff) const break; } case cmake::TraceFormat::TRACE_HUMAN: - msg << full_path << "(" << lff.Line << "): "; - msg << lff.Name.Original << "("; + msg << full_path << "(" << lff.Line << "):"; + if (deferId) { + msg << "DEFERRED:" << *deferId << ":"; + } + msg << " " << lff.Name.Original << "("; for (std::string const& arg : args) { msg << arg << " "; @@ -361,11 +370,12 @@ class cmMakefileCall { public: cmMakefileCall(cmMakefile* mf, cmListFileFunction const& lff, - cmExecutionStatus& status) + cm::optional<std::string> deferId, cmExecutionStatus& status) : Makefile(mf) { cmListFileContext const& lfc = cmListFileContext::FromCommandContext( - lff, this->Makefile->StateSnapshot.GetExecutionListFile()); + lff, this->Makefile->StateSnapshot.GetExecutionListFile(), + std::move(deferId)); this->Makefile->Backtrace = this->Makefile->Backtrace.Push(lfc); ++this->Makefile->RecursionDepth; this->Makefile->ExecutionStatusStack.push_back(&status); @@ -402,7 +412,8 @@ void cmMakefile::OnExecuteCommand(std::function<void()> callback) } bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, - cmExecutionStatus& status) + cmExecutionStatus& status, + cm::optional<std::string> deferId) { bool result = true; @@ -417,7 +428,7 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, } // Place this call on the call stack. - cmMakefileCall stack_manager(this, lff, status); + cmMakefileCall stack_manager(this, lff, std::move(deferId), status); static_cast<void>(stack_manager); // Check for maximum recursion depth. @@ -445,7 +456,7 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, if (!cmSystemTools::GetFatalErrorOccured()) { // if trace is enabled, print out invoke information if (this->GetCMakeInstance()->GetTrace()) { - this->PrintCommandTrace(lff); + this->PrintCommandTrace(lff, this->Backtrace.Top().DeferId); } // Try invoking the command. bool invokeSucceeded = command(lff.Arguments, status); @@ -663,6 +674,53 @@ private: bool ReportError; }; +class cmMakefile::DeferScope +{ +public: + DeferScope(cmMakefile* mf, std::string const& deferredInFile) + : Makefile(mf) + { + cmListFileContext lfc; + lfc.Line = cmListFileContext::DeferPlaceholderLine; + lfc.FilePath = deferredInFile; + this->Makefile->Backtrace = this->Makefile->Backtrace.Push(lfc); + this->Makefile->DeferRunning = true; + } + + ~DeferScope() + { + this->Makefile->DeferRunning = false; + this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); + } + + DeferScope(const DeferScope&) = delete; + DeferScope& operator=(const DeferScope&) = delete; + +private: + cmMakefile* Makefile; +}; + +class cmMakefile::DeferCallScope +{ +public: + DeferCallScope(cmMakefile* mf, std::string const& deferredFromFile) + : Makefile(mf) + { + this->Makefile->StateSnapshot = + this->Makefile->GetState()->CreateDeferCallSnapshot( + this->Makefile->StateSnapshot, deferredFromFile); + assert(this->Makefile->StateSnapshot.IsValid()); + } + + ~DeferCallScope() { this->Makefile->PopSnapshot(); } + + DeferCallScope(const DeferCallScope&) = delete; + DeferCallScope& operator=(const DeferCallScope&) = delete; + +private: + cmMakefile* Makefile; +}; + bool cmMakefile::ReadListFile(const std::string& filename) { std::string filenametoread = cmSystemTools::CollapseFullPath( @@ -705,7 +763,8 @@ bool cmMakefile::ReadListFileAsString(const std::string& content, } void cmMakefile::RunListFile(cmListFile const& listFile, - std::string const& filenametoread) + std::string const& filenametoread, + DeferCommands* defer) { // add this list file to the list of dependencies this->ListFiles.push_back(filenametoread); @@ -736,6 +795,33 @@ void cmMakefile::RunListFile(cmListFile const& listFile, } } + // Run any deferred commands. + if (defer) { + // Add a backtrace level indicating calls are deferred. + DeferScope scope(this, filenametoread); + + // Iterate by index in case one deferred call schedules another. + // NOLINTNEXTLINE(modernize-loop-convert) + for (size_t i = 0; i < defer->Commands.size(); ++i) { + DeferCommand& d = defer->Commands[i]; + if (d.Id.empty()) { + // Cancelled. + continue; + } + // Mark as executed. + std::string id = std::move(d.Id); + + // The deferred call may have come from another file. + DeferCallScope callScope(this, d.FilePath); + + cmExecutionStatus status(*this); + this->ExecuteCommand(d.Command, status, std::move(id)); + if (cmSystemTools::GetFatalErrorOccured()) { + break; + } + } + } + this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentParentFile); this->AddDefinition("CMAKE_CURRENT_LIST_FILE", currentFile); this->AddDefinition("CMAKE_CURRENT_LIST_DIR", @@ -1678,7 +1764,9 @@ void cmMakefile::Configure() } } - this->RunListFile(listFile, currentStart); + this->Defer = cm::make_unique<DeferCommands>(); + this->RunListFile(listFile, currentStart, this->Defer.get()); + this->Defer.reset(); if (cmSystemTools::GetFatalErrorOccured()) { scope.Quiet(); } @@ -1753,6 +1841,13 @@ void cmMakefile::AddSubDirectory(const std::string& srcPath, const std::string& binPath, bool excludeFromAll, bool immediate) { + if (this->DeferRunning) { + this->IssueMessage( + MessageType::FATAL_ERROR, + "Subdirectories may not be created during deferred execution."); + return; + } + // Make sure the binary directory is unique. if (!this->EnforceUniqueDir(srcPath, binPath)) { return; @@ -2960,6 +3055,68 @@ void cmMakefile::SetRecursionDepth(int recursionDepth) this->RecursionDepth = recursionDepth; } +std::string cmMakefile::NewDeferId() +{ + return this->GetGlobalGenerator()->NewDeferId(); +} + +bool cmMakefile::DeferCall(std::string id, std::string file, + cmListFileFunction lff) +{ + if (!this->Defer) { + return false; + } + this->Defer->Commands.emplace_back( + DeferCommand{ std::move(id), std::move(file), std::move(lff) }); + return true; +} + +bool cmMakefile::DeferCancelCall(std::string const& id) +{ + if (!this->Defer) { + return false; + } + for (DeferCommand& dc : this->Defer->Commands) { + if (dc.Id == id) { + dc.Id.clear(); + } + } + return true; +} + +cm::optional<std::string> cmMakefile::DeferGetCallIds() const +{ + cm::optional<std::string> ids; + if (this->Defer) { + ids = cmJoin( + cmMakeRange(this->Defer->Commands) + .filter([](DeferCommand const& dc) -> bool { return !dc.Id.empty(); }) + .transform( + [](DeferCommand const& dc) -> std::string const& { return dc.Id; }), + ";"); + } + return ids; +} + +cm::optional<std::string> cmMakefile::DeferGetCall(std::string const& id) const +{ + cm::optional<std::string> call; + if (this->Defer) { + std::string tmp; + for (DeferCommand const& dc : this->Defer->Commands) { + if (dc.Id == id) { + tmp = dc.Command.Name.Original; + for (cmListFileArgument const& arg : dc.Command.Arguments) { + tmp = cmStrCat(tmp, ';', arg.Value); + } + break; + } + } + call = std::move(tmp); + } + return call; +} + MessageType cmMakefile::ExpandVariablesInStringNew( std::string& errorstr, std::string& source, bool escapeQuotes, bool noEscapes, bool atOnly, const char* filename, long line, @@ -2997,7 +3154,12 @@ MessageType cmMakefile::ExpandVariablesInStringNew( switch (var.domain) { case NORMAL: if (filename && lookup == lineVar) { - varresult = std::to_string(line); + cmListFileContext const& top = this->Backtrace.Top(); + if (top.DeferId) { + varresult = cmStrCat("DEFERRED:"_s, *top.DeferId); + } else { + varresult = std::to_string(line); + } } else { value = this->GetDefinition(lookup); } @@ -3561,6 +3723,12 @@ void cmMakefile::AddTargetObject(std::string const& tgtName, void cmMakefile::EnableLanguage(std::vector<std::string> const& lang, bool optional) { + if (this->DeferRunning) { + this->IssueMessage( + MessageType::FATAL_ERROR, + "Languages may not be enabled during deferred execution."); + return; + } if (const char* def = this->GetGlobalGenerator()->GetCMakeCFGIntDir()) { this->AddDefinition("CMAKE_CFG_INTDIR", def); } diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index d851dd6..c7940fb 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -15,6 +15,7 @@ #include <unordered_map> #include <vector> +#include <cm/optional> #include <cm/string_view> #include "cmsys/RegularExpression.hxx" @@ -695,7 +696,8 @@ public: /** * Print a command's invocation */ - void PrintCommandTrace(const cmListFileFunction& lff) const; + void PrintCommandTrace(cmListFileFunction const& lff, + cm::optional<std::string> const& deferId = {}) const; /** * Set a callback that is invoked whenever ExecuteCommand is called. @@ -706,8 +708,8 @@ public: * Execute a single CMake command. Returns true if the command * succeeded or false if it failed. */ - bool ExecuteCommand(const cmListFileFunction& lff, - cmExecutionStatus& status); + bool ExecuteCommand(const cmListFileFunction& lff, cmExecutionStatus& status, + cm::optional<std::string> deferId = {}); //! Enable support for named language, if nil then all languages are /// enabled. @@ -965,6 +967,12 @@ public: int GetRecursionDepth() const; void SetRecursionDepth(int recursionDepth); + std::string NewDeferId(); + bool DeferCall(std::string id, std::string fileName, cmListFileFunction lff); + bool DeferCancelCall(std::string const& id); + cm::optional<std::string> DeferGetCallIds() const; + cm::optional<std::string> DeferGetCall(std::string const& id) const; + protected: // add link libraries and directories to the target void AddGlobalLinkInformation(cmTarget& target); @@ -1026,10 +1034,25 @@ private: cmListFileBacktrace Backtrace; int RecursionDepth; + struct DeferCommand + { + // Id is empty for an already-executed or cancelled operation. + std::string Id; + std::string FilePath; + cmListFileFunction Command; + }; + struct DeferCommands + { + std::vector<DeferCommand> Commands; + }; + std::unique_ptr<DeferCommands> Defer; + bool DeferRunning = false; + void DoGenerate(cmLocalGenerator& lg); void RunListFile(cmListFile const& listFile, - const std::string& filenametoread); + const std::string& filenametoread, + DeferCommands* defer = nullptr); bool ParseDefineFlag(std::string const& definition, bool remove); @@ -1080,6 +1103,12 @@ private: class ListFileScope; friend class ListFileScope; + class DeferScope; + friend class DeferScope; + + class DeferCallScope; + friend class DeferCallScope; + class BuildsystemFileScope; friend class BuildsystemFileScope; diff --git a/Source/cmState.cxx b/Source/cmState.cxx index e96c82f..d5ac9ae 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -837,6 +837,21 @@ cmStateSnapshot cmState::CreateBuildsystemDirectorySnapshot( return snapshot; } +cmStateSnapshot cmState::CreateDeferCallSnapshot( + cmStateSnapshot const& originSnapshot, std::string const& fileName) +{ + cmStateDetail::PositionType pos = + this->SnapshotData.Push(originSnapshot.Position, *originSnapshot.Position); + pos->SnapshotType = cmStateEnums::DeferCallType; + pos->Keep = false; + pos->ExecutionListFile = this->ExecutionListFiles.Push( + originSnapshot.Position->ExecutionListFile, fileName); + assert(originSnapshot.Position->Vars.IsValid()); + pos->BuildSystemDirectory->DirectoryEnd = pos; + pos->PolicyScope = originSnapshot.Position->Policies; + return { this, pos }; +} + cmStateSnapshot cmState::CreateFunctionCallSnapshot( cmStateSnapshot const& originSnapshot, std::string const& fileName) { diff --git a/Source/cmState.h b/Source/cmState.h index dc3ba65..2aa57e0 100644 --- a/Source/cmState.h +++ b/Source/cmState.h @@ -55,6 +55,8 @@ public: cmStateSnapshot CreateBaseSnapshot(); cmStateSnapshot CreateBuildsystemDirectorySnapshot( cmStateSnapshot const& originSnapshot); + cmStateSnapshot CreateDeferCallSnapshot( + cmStateSnapshot const& originSnapshot, std::string const& fileName); cmStateSnapshot CreateFunctionCallSnapshot( cmStateSnapshot const& originSnapshot, std::string const& fileName); cmStateSnapshot CreateMacroCallSnapshot( diff --git a/Source/cmStateTypes.h b/Source/cmStateTypes.h index b8c1cca..010d7e3 100644 --- a/Source/cmStateTypes.h +++ b/Source/cmStateTypes.h @@ -18,6 +18,7 @@ enum SnapshotType { BaseType, BuildsystemDirectoryType, + DeferCallType, FunctionCallType, MacroCallType, IncludeFileType, diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 014a707..dc06fae 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -983,7 +983,7 @@ void cmake::PrintTraceFormatVersion() Json::StreamWriterBuilder builder; builder["indentation"] = ""; version["major"] = 1; - version["minor"] = 0; + version["minor"] = 1; val["version"] = version; msg = Json::writeString(builder, val); #endif |