|
@@ -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);
|
|
|
}
|