cmake_language: Add signature to DEFER calls to later times

Fixes: #19575
This commit is contained in:
Brad King
2020-09-21 13:51:35 -04:00
parent 9880549405
commit e8b0359a43
143 changed files with 1038 additions and 25 deletions

View File

@@ -12,6 +12,7 @@ Synopsis
cmake_language(`CALL`_ <command> [<arg>...])
cmake_language(`EVAL`_ CODE <code>...)
cmake_language(`DEFER`_ <options>... CALL <command> [<arg>...])
Introduction
^^^^^^^^^^^^
@@ -99,3 +100,121 @@ is equivalent to
)
include(${CMAKE_CURRENT_BINARY_DIR}/eval.cmake)
Deferring Calls
^^^^^^^^^^^^^^^
.. versionadded:: 3.19
.. _DEFER:
.. code-block:: cmake
cmake_language(DEFER <options>... CALL <command> [<arg>...])
Schedules a call to the named ``<command>`` with the given arguments (if any)
to occur at a later time. By default, deferred calls are executed as if
written at the end of the current directory's ``CMakeLists.txt`` file,
except that they run even after a :command:`return` call. Variable
references in arguments are evaluated at the time the deferred call is
executed.
The options are:
``DIRECTORY <dir>``
Schedule the call for the end of the given directory instead of the
current directory. The ``<dir>`` may reference either a source
directory or its corresponding binary directory. Relative paths are
treated as relative to the current source directory.
The given directory must be known to CMake, being either the top-level
directory or one added by :command:`add_subdirectory`. Furthermore,
the given directory must not yet be finished processing. This means
it can be the current directory or one of its ancestors.
``ID <id>``
Specify an identification for the deferred call.
The id may not be empty and may not begin in a capital letter ``A-Z``.
The id may begin in a ``_`` only if it was generated by another call
that used ``ID_VAR`` to get the id.
``ID_VAR <var>``
Sepcify a variable in which to store the identification for the
deferred call. If ``ID <id>`` is not given, a new identification
will be generated starting in a ``_``.
The currently scheduled list of deferred calls may be retrieved:
.. code-block:: cmake
cmake_language(DEFER [DIRECTORY <dir>] GET_CALL_IDS <var>)
This will store in ``<var>`` a :ref:`Semicolon-separated list <CMake Language
Lists>` of deferred call ids.
Details of a specific call may be retrieved from its id:
.. code-block:: cmake
cmake_language(DEFER [DIRECTORY <dir>] GET_CALL <id> <var>)
This will store in ``<var>`` a :ref:`Semicolon-separated list <CMake Language
Lists>` in which the first element is the name of the command to be
called, and the remaining elements are its unevaluated arguments (any
contained ``;`` characters are included literally and cannot be distinguished
from multiple arguments). If multiple calls are scheduled with the same id,
this retrieves the first one. If no call is scheduled with the given id,
this stores an empty string in the variable.
Deferred calls may be canceled by their id:
.. code-block:: cmake
cmake_language(DEFER [DIRECTORY <dir>] CANCEL_CALL <id>...)
This cancels all deferred calls matching any of the given ids.
Unknown ids are silently ignored.
Deferred Call Examples
""""""""""""""""""""""
For example, the code:
.. code-block:: cmake
cmake_language(DEFER CALL message "${deferred_message}")
cmake_language(DEFER ID_VAR id CALL message "Cancelled Message")
cmake_language(DEFER CANCEL_CALL ${id})
message("Immediate Message")
set(deferred_message "Deferred Message")
prints::
Immediate Message
Deferred Message
The ``Cancelled Message`` is never printed because its command is
cancelled. The ``deferred_message`` variable reference is not evaluated
until the call site, so it can be set after the deferred call is scheduled.
In order to evaluate variable references immediately when scheduling a
deferred call, wrap it using ``cmake_language(EVAL)``. However, note that
arguments will be re-evaluated in the deferred call, though that can be
avoided by using bracket arguments. For example:
.. code-block:: cmake
set(deferred_message "Deferred Message 1")
set(re_evaluated [[${deferred_message}]])
cmake_language(EVAL CODE "
cmake_language(DEFER CALL message [[${deferred_message}]])
cmake_language(DEFER CALL message \"${re_evaluated}\")
")
message("Immediate Message")
set(deferred_message "Deferred Message 2")
also prints::
Immediate Message
Deferred Message 1
Deferred Message 2

View File

@@ -12,6 +12,7 @@ encountered in an included file (via :command:`include` or
:command:`find_package`), it causes processing of the current file to stop
and control is returned to the including file. If it is encountered in a
file which is not included by another file, e.g. a ``CMakeLists.txt``,
deferred calls scheduled by :command:`cmake_language(DEFER)` are invoked and
control is returned to the parent directory if there is one. If return is
called in a function, control is returned to the caller of the function.

View File

@@ -295,6 +295,11 @@ Options
``line``
The line in ``file`` of the function call.
``defer``
Optional member that is present when the function call was deferred
by :command:`cmake_language(DEFER)`. If present, its value is a
string containing the deferred call ``<id>``.
``cmd``
The name of the function that was called.
@@ -317,7 +322,7 @@ Options
{
"version": {
"major": 1,
"minor": 0
"minor": 1
}
}

View File

@@ -0,0 +1,5 @@
cmake_language-DEFER
--------------------
* The :command:`cmake_language` command gained a ``DEFER`` mode to
schedule command calls to occur at the end of processing a directory.

View File

@@ -5,3 +5,7 @@ The line number of the current file being processed.
This is the line number of the file currently being processed by
cmake.
If CMake is currently processing deferred calls scheduled by
the :command:`cmake_language(DEFER)` command, this variable
evaluates to ``DEFERRED`` instead of a specific line number.

View File

@@ -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") {

View File

@@ -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) {

View File

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

View File

@@ -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
{

View File

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

View File

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

View File

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

View File

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

View File

@@ -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)
{

View File

@@ -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(

View File

@@ -18,6 +18,7 @@ enum SnapshotType
{
BaseType,
BuildsystemDirectoryType,
DeferCallType,
FunctionCallType,
MacroCallType,
IncludeFileType,

View File

@@ -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

View File

@@ -56,7 +56,7 @@ with open(trace_file, 'r') as fp:
assert sorted(vers.keys()) == ['version']
assert sorted(vers['version'].keys()) == ['major', 'minor']
assert vers['version']['major'] == 1
assert vers['version']['minor'] == 0
assert vers['version']['minor'] == 1
for i in fp.readlines():
line = json.loads(i)

View File

@@ -32,3 +32,53 @@ run_cmake(eval_message_fatal_error)
run_cmake(eval_no_code)
run_cmake(eval_no_parameters)
run_cmake(eval_variable_outside_message)
run_cmake(defer_call)
run_cmake(defer_call_add_subdirectory)
run_cmake(defer_call_enable_language)
run_cmake(defer_call_ids)
foreach(command IN ITEMS
"function" "endfunction"
"macro" "endmacro"
"if" "elseif" "else" "endif"
"while" "endwhile"
"foreach" "endforeach"
"return"
)
message(STATUS "Running defer_call_invalid_command for ${command}...")
run_cmake_with_options(defer_call_invalid_command -Dcommand=${command})
endforeach()
run_cmake(defer_call_invalid_directory)
run_cmake(defer_call_error)
run_cmake(defer_call_missing_directory)
run_cmake(defer_call_policy_PUSH)
run_cmake(defer_call_syntax_error)
run_cmake_with_options(defer_call_trace --trace-expand)
run_cmake_with_options(defer_call_trace_json --trace --trace-format=json-v1)
run_cmake(defer_cancel_call_unknown_argument)
run_cmake(defer_cancel_call_invalid_directory)
run_cmake(defer_cancel_call_id)
run_cmake(defer_cancel_call_id_var)
run_cmake(defer_directory_empty)
run_cmake(defer_directory_missing)
run_cmake(defer_directory_multiple)
run_cmake(defer_id_empty)
run_cmake(defer_id_missing)
run_cmake(defer_id_multiple)
run_cmake(defer_id_var_empty)
run_cmake(defer_id_var_missing)
run_cmake(defer_id_var_multiple)
run_cmake(defer_get_call_ids_missing_var)
run_cmake(defer_get_call_ids_too_many_args)
run_cmake(defer_get_call_ids_invalid_directory)
run_cmake(defer_get_call_ids_id)
run_cmake(defer_get_call_ids_id_var)
run_cmake(defer_get_call_missing_id)
run_cmake(defer_get_call_missing_var)
run_cmake(defer_get_call_too_many_args)
run_cmake(defer_get_call_id_empty)
run_cmake(defer_get_call_unknown_argument)
run_cmake(defer_get_call_id)
run_cmake(defer_get_call_id_var)
run_cmake(defer_missing_arg)
run_cmake(defer_missing_call)
run_cmake(defer_unknown_option)

View File

@@ -0,0 +1,15 @@
^CMake Deprecation Warning at defer_call/CMakeLists.txt:[0-9]+ \(cmake_policy\):
The OLD behavior for policy CMP0053 will be removed from a future version
of CMake.
The cmake-policies\(7\) manual explains that the OLD behaviors of all
policies are deprecated and that a policy should be set to OLD only under
specific short-term circumstances. Projects should be ported to the NEW
behavior and not rely on setting a policy to OLD.
+
CMake Warning at defer_call/CMakeLists.txt:3 \(message\):
Double-Deferred Warning In Subdirectory:
'[^']*/Tests/RunCMake/cmake_language/defer_call/CMakeLists.txt:DEFERRED:id3'
Call Stack \(most recent call first\):
defer_call/CMakeLists.txt:DEFERRED$

View File

@@ -0,0 +1,8 @@
-- Immediate Message In Subdirectory: ids='__0;__1'
-- Deferred Message In Subdirectory: '[^']*/Tests/RunCMake/cmake_language/defer_call/CMakeLists.txt:DEFERRED:id1'
-- Deferred Message In Included File: '[^']*/Tests/RunCMake/cmake_language/defer_call/include.cmake:1'
-- Immediate Message: ids='__0;__1;__2;__3;__4'
-- First Deferred Message
-- Deferred Message From Subdirectory
-- Deferred Message: ids='__4;__5'
-- Final Deferred Message

View File

@@ -0,0 +1,12 @@
set(message_command "message")
set(final_message "This should not be printed because variable evaluation is deferred too.")
cmake_language(DEFER CALL ${message_command} STATUS "First Deferred Message")
add_subdirectory(defer_call)
cmake_language(DEFER CALL cmake_language DEFER CALL "${final_message_command}" STATUS "${final_message}")
cmake_language(DEFER CALL cmake_language DEFER GET_CALL_IDS ids)
cmake_language(DEFER CALL cmake_language EVAL CODE [[message(STATUS "Deferred Message: ids='${ids}'")]])
cmake_language(DEFER GET_CALL_IDS ids)
message(STATUS "Immediate Message: ids='${ids}'")
set(final_message_command "message")
set(final_message "Final Deferred Message")
set(subdir_message "Deferred Message From Subdirectory")

View File

@@ -0,0 +1,11 @@
cmake_policy(SET CMP0053 OLD)
cmake_language(DEFER ID id1 CALL message STATUS "Deferred Message In Subdirectory: '${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}'")
cmake_language(DEFER ID id2 CALL
cmake_language DEFER ID id3 CALL
message WARNING "Double-Deferred Warning In Subdirectory:\n '${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}'")
cmake_language(DEFER ID id4 CALL include "${CMAKE_CURRENT_LIST_DIR}/include.cmake")
set(subdir_message "This should not be printed because variable evaluation is in deferred scope.")
cmake_language(DEFER DIRECTORY .. CALL message STATUS "${subdir_message}")
cmake_language(DEFER DIRECTORY .. GET_CALL_IDS ids)
message(STATUS "Immediate Message In Subdirectory: ids='${ids}'")

View File

@@ -0,0 +1 @@
message(STATUS "Deferred Message In Included File: '${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}'")

View File

@@ -0,0 +1,9 @@
^CMake Error at defer_call_add_subdirectory.cmake:1 \(add_subdirectory\):
Subdirectories may not be created during deferred execution.
Call Stack \(most recent call first\):
CMakeLists.txt:DEFERRED
+
CMake Error at defer_call_add_subdirectory.cmake:2 \(subdirs\):
Subdirectories may not be created during deferred execution.
Call Stack \(most recent call first\):
CMakeLists.txt:DEFERRED$

View File

@@ -0,0 +1,2 @@
cmake_language(DEFER CALL add_subdirectory defer_call_add_subdirectory)
cmake_language(DEFER CALL subdirs defer_call_add_subdirectory)

View File

@@ -0,0 +1,9 @@
^CMake Error at defer_call_enable_language.cmake:1 \(enable_language\):
Languages may not be enabled during deferred execution.
Call Stack \(most recent call first\):
CMakeLists.txt:DEFERRED
+
CMake Error at defer_call_enable_language.cmake:2 \(project\):
Languages may not be enabled during deferred execution.
Call Stack \(most recent call first\):
CMakeLists.txt:DEFERRED$

View File

@@ -0,0 +1,2 @@
cmake_language(DEFER CALL enable_language C)
cmake_language(DEFER CALL project foo C)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,9 @@
^CMake Error at defer_call_error.cmake:2 \(message\):
Deferred Error
Call Stack \(most recent call first\):
CMakeLists.txt:DEFERRED
+
CMake Error at defer_call_error/CMakeLists.txt:2 \(message\):
Deferred Error from Subdirectory
Call Stack \(most recent call first\):
CMakeLists.txt:DEFERRED$

View File

@@ -0,0 +1,3 @@
# Error message backtrace points here but call stack shows DEFERRED execution.
cmake_language(DEFER CALL message SEND_ERROR "Deferred Error")
add_subdirectory(defer_call_error)

View File

@@ -0,0 +1,2 @@
# Error message backtrace points here but call stack shows DEFERRED execution in parent.
cmake_language(DEFER DIRECTORY .. CALL message SEND_ERROR "Deferred Error from Subdirectory")

View File

@@ -0,0 +1,13 @@
-- Immediate Message: ids='message0;getCallIds1;messageIds1;cancelCall;getCallIds2;messageIds2;toBeCancelled;message3'
-- Immediate Message: message0='message;STATUS;First Deferred Message'
-- Immediate Message: getCallIds1='cmake_language;DEFER;GET_CALL_IDS;ids'
-- Immediate Message: messageIds1='cmake_language;EVAL;CODE;message\(STATUS "Deferred Message: ids='\${ids}'"\)'
-- Immediate Message: cancelCall='cmake_language;DEFER;CANCEL_CALL;toBeCancelled'
-- Immediate Message: getCallIds2='cmake_language;DEFER;GET_CALL_IDS;ids'
-- Immediate Message: messageIds2='cmake_language;EVAL;CODE;message\(STATUS "Deferred Message: ids='\${ids}'"\)'
-- Immediate Message: toBeCancelled='message;STATUS;Cancelled Message'
-- Immediate Message: message3='message;STATUS;Final Deferred Message'
-- First Deferred Message
-- Deferred Message: ids='messageIds1;cancelCall;getCallIds2;messageIds2;toBeCancelled;message3'
-- Deferred Message: ids='messageIds2;message3'
-- Final Deferred Message

View File

@@ -0,0 +1,14 @@
cmake_language(DEFER ID message0 CALL message STATUS "First Deferred Message")
cmake_language(DEFER ID getCallIds1 CALL cmake_language DEFER GET_CALL_IDS ids)
cmake_language(DEFER ID messageIds1 CALL cmake_language EVAL CODE [[message(STATUS "Deferred Message: ids='${ids}'")]])
cmake_language(DEFER ID cancelCall CALL cmake_language DEFER CANCEL_CALL toBeCancelled)
cmake_language(DEFER ID getCallIds2 CALL cmake_language DEFER GET_CALL_IDS ids)
cmake_language(DEFER ID messageIds2 CALL cmake_language EVAL CODE [[message(STATUS "Deferred Message: ids='${ids}'")]])
cmake_language(DEFER ID toBeCancelled CALL message STATUS "Cancelled Message")
cmake_language(DEFER ID message3 CALL message STATUS "Final Deferred Message")
cmake_language(DEFER GET_CALL_IDS ids)
message(STATUS "Immediate Message: ids='${ids}'")
foreach(id ${ids})
cmake_language(DEFER GET_CALL ${id} call)
message(STATUS "Immediate Message: ${id}='${call}'")
endforeach()

View File

@@ -0,0 +1,4 @@
^CMake Error at defer_call_invalid_command.cmake:1 \(cmake_language\):
cmake_language invalid command specified: [A-Za-z_]+
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1 @@
cmake_language(DEFER CALL ${command})

View File

@@ -0,0 +1,9 @@
^CMake Error at defer_call_invalid_directory.cmake:2 \(cmake_language\):
cmake_language DEFER CALL may not be scheduled in directory:
[^
]*/Tests/RunCMake/cmake_language/defer_call_invalid_directory-build/defer_call_invalid_directory
at this time.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1,2 @@
add_subdirectory(defer_call_invalid_directory)
cmake_language(DEFER DIRECTORY defer_call_invalid_directory CALL message "Should not be allowed.")

View File

@@ -0,0 +1,9 @@
^CMake Error at defer_call_missing_directory.cmake:1 \(cmake_language\):
cmake_language DEFER DIRECTORY:
[^
]*/Tests/RunCMake/cmake_language/does_not_exist
is not known. It may not have been processed yet.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1 @@
cmake_language(DEFER DIRECTORY does_not_exist CALL message "Should not be allowed.")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,2 @@
^CMake Error at CMakeLists.txt:DEFERRED:
cmake_policy PUSH without matching POP$

View File

@@ -0,0 +1 @@
cmake_language(DEFER CALL cmake_policy PUSH)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,13 @@
^CMake Error at defer_call_syntax_error.cmake:2 \(message\):
Syntax error in cmake code at
[^
]*/Tests/RunCMake/cmake_language/defer_call_syntax_error.cmake:2
when parsing string
Deferred \\X Error
Invalid character escape '\\X'.
Call Stack \(most recent call first\):
CMakeLists.txt:DEFERRED$

View File

@@ -0,0 +1,2 @@
# Argument syntax error evaluated at deferred call site.
cmake_language(DEFER CALL message "Deferred \X Error")

View File

@@ -0,0 +1,8 @@
[^
]*/Tests/RunCMake/cmake_language/defer_call_trace.cmake\(2\): cmake_language\(DEFER CALL message Deferred Message \)
[^
]*/Tests/RunCMake/cmake_language/defer_call_trace.cmake\(3\): message\(Immediate Message \)
Immediate Message
[^
]*/Tests/RunCMake/cmake_language/defer_call_trace.cmake\(2\):DEFERRED:__0: message\(Deferred Message \)
Deferred Message$

View File

@@ -0,0 +1,3 @@
# The --trace and --trace-expand output point here for deferred call.
cmake_language(DEFER CALL message "Deferred Message")
message("Immediate Message")

View File

@@ -0,0 +1,5 @@
{"args":\["DEFER","CALL","message","Deferred Message"\],"cmd":"cmake_language","file":"[^"]*/Tests/RunCMake/cmake_language/defer_call_trace_json.cmake","frame":2,"line":2,"time":[0-9.]+}
{"args":\["Immediate Message"\],"cmd":"message","file":"[^"]*/Tests/RunCMake/cmake_language/defer_call_trace_json.cmake","frame":2,"line":3,"time":[0-9.]+}
Immediate Message
{"args":\["Deferred Message"],"cmd":"message","defer":"__0","file":"[^"]*/Tests/RunCMake/cmake_language/defer_call_trace_json.cmake","frame":1,"line":2,"time":[0-9.]+}
Deferred Message$

View File

@@ -0,0 +1,3 @@
# The --trace and --trace-expand output point here for deferred call.
cmake_language(DEFER CALL message "Deferred Message")
message("Immediate Message")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at defer_cancel_call_id.cmake:1 \(cmake_language\):
cmake_language DEFER CANCEL_CALL does not accept ID or ID_VAR.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1 @@
cmake_language(DEFER ID id CANCEL_CALL)

View File

@@ -0,0 +1,4 @@
^CMake Error at defer_cancel_call_id_var.cmake:1 \(cmake_language\):
cmake_language DEFER CANCEL_CALL does not accept ID or ID_VAR.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1 @@
cmake_language(DEFER ID_VAR id_var CANCEL_CALL)

View File

@@ -0,0 +1,9 @@
^CMake Error at defer_cancel_call_invalid_directory.cmake:2 \(cmake_language\):
cmake_language DEFER CANCEL_CALL may not update directory:
[^
]*/Tests/RunCMake/cmake_language/defer_cancel_call_invalid_directory-build/defer_cancel_call_invalid_directory
at this time.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@@ -0,0 +1,2 @@
add_subdirectory(defer_cancel_call_invalid_directory)
cmake_language(DEFER DIRECTORY defer_cancel_call_invalid_directory CANCEL_CALL _)

View File

@@ -0,0 +1,6 @@
^CMake Error at defer_cancel_call_unknown_argument.cmake:1 \(cmake_language\):
cmake_language DEFER CANCEL_CALL unknown argument:
UNKNOWN
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1 @@
cmake_language(DEFER CANCEL_CALL UNKNOWN)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at defer_directory_empty.cmake:1 \(cmake_language\):
cmake_language DEFER DIRECTORY may not be empty
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1 @@
cmake_language(DEFER DIRECTORY "")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at defer_directory_missing.cmake:1 \(cmake_language\):
cmake_language DEFER DIRECTORY missing value
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1 @@
cmake_language(DEFER DIRECTORY)

View File

@@ -0,0 +1,4 @@
^CMake Error at defer_directory_multiple.cmake:1 \(cmake_language\):
cmake_language DEFER given multiple DIRECTORY arguments
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1 @@
cmake_language(DEFER DIRECTORY . DIRECTORY x CALL message "Should not be allowed.")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at defer_get_call_id.cmake:1 \(cmake_language\):
cmake_language DEFER GET_CALL does not accept ID or ID_VAR.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1 @@
cmake_language(DEFER ID id GET_CALL)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at defer_get_call_id_empty.cmake:1 \(cmake_language\):
cmake_language DEFER GET_CALL id may not be empty
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1 @@
cmake_language(DEFER GET_CALL "" var)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at defer_get_call_id_var.cmake:1 \(cmake_language\):
cmake_language DEFER GET_CALL does not accept ID or ID_VAR.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1 @@
cmake_language(DEFER ID_VAR id_var GET_CALL)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at defer_get_call_ids_id.cmake:1 \(cmake_language\):
cmake_language DEFER GET_CALL_IDS does not accept ID or ID_VAR.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1 @@
cmake_language(DEFER ID id GET_CALL_IDS)

View File

@@ -0,0 +1,4 @@
^CMake Error at defer_get_call_ids_id_var.cmake:1 \(cmake_language\):
cmake_language DEFER GET_CALL_IDS does not accept ID or ID_VAR.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1 @@
cmake_language(DEFER ID_VAR id_var GET_CALL_IDS)

View File

@@ -0,0 +1,9 @@
^CMake Error at defer_get_call_ids_invalid_directory.cmake:2 \(cmake_language\):
cmake_language DEFER GET_CALL_IDS may not access directory:
[^
]*/Tests/RunCMake/cmake_language/defer_get_call_ids_invalid_directory-build/defer_get_call_ids_invalid_directory
at this time.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@@ -0,0 +1,2 @@
add_subdirectory(defer_get_call_ids_invalid_directory)
cmake_language(DEFER DIRECTORY defer_get_call_ids_invalid_directory GET_CALL_IDS var)

View File

@@ -0,0 +1,4 @@
^CMake Error at defer_get_call_ids_missing_var.cmake:1 \(cmake_language\):
cmake_language DEFER GET_CALL_IDS missing output variable
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

Some files were not shown because too many files have changed in this diff Show More