mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-07 06:09:52 -06:00
cmake_language: Add signature to DEFER calls to later times
Fixes: #19575
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
5
Help/release/dev/cmake_language-DEFER.rst
Normal file
5
Help/release/dev/cmake_language-DEFER.rst
Normal 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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -18,6 +18,7 @@ enum SnapshotType
|
||||
{
|
||||
BaseType,
|
||||
BuildsystemDirectoryType,
|
||||
DeferCallType,
|
||||
FunctionCallType,
|
||||
MacroCallType,
|
||||
IncludeFileType,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
15
Tests/RunCMake/cmake_language/defer_call-stderr.txt
Normal file
15
Tests/RunCMake/cmake_language/defer_call-stderr.txt
Normal 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$
|
||||
8
Tests/RunCMake/cmake_language/defer_call-stdout.txt
Normal file
8
Tests/RunCMake/cmake_language/defer_call-stdout.txt
Normal 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
|
||||
12
Tests/RunCMake/cmake_language/defer_call.cmake
Normal file
12
Tests/RunCMake/cmake_language/defer_call.cmake
Normal 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")
|
||||
11
Tests/RunCMake/cmake_language/defer_call/CMakeLists.txt
Normal file
11
Tests/RunCMake/cmake_language/defer_call/CMakeLists.txt
Normal 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}'")
|
||||
1
Tests/RunCMake/cmake_language/defer_call/include.cmake
Normal file
1
Tests/RunCMake/cmake_language/defer_call/include.cmake
Normal file
@@ -0,0 +1 @@
|
||||
message(STATUS "Deferred Message In Included File: '${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}'")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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$
|
||||
@@ -0,0 +1,2 @@
|
||||
cmake_language(DEFER CALL add_subdirectory defer_call_add_subdirectory)
|
||||
cmake_language(DEFER CALL subdirs defer_call_add_subdirectory)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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$
|
||||
@@ -0,0 +1,2 @@
|
||||
cmake_language(DEFER CALL enable_language C)
|
||||
cmake_language(DEFER CALL project foo C)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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$
|
||||
3
Tests/RunCMake/cmake_language/defer_call_error.cmake
Normal file
3
Tests/RunCMake/cmake_language/defer_call_error.cmake
Normal 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)
|
||||
@@ -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")
|
||||
13
Tests/RunCMake/cmake_language/defer_call_ids-stdout.txt
Normal file
13
Tests/RunCMake/cmake_language/defer_call_ids-stdout.txt
Normal 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
|
||||
14
Tests/RunCMake/cmake_language/defer_call_ids.cmake
Normal file
14
Tests/RunCMake/cmake_language/defer_call_ids.cmake
Normal 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()
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER CALL ${command})
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -0,0 +1,2 @@
|
||||
add_subdirectory(defer_call_invalid_directory)
|
||||
cmake_language(DEFER DIRECTORY defer_call_invalid_directory CALL message "Should not be allowed.")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER DIRECTORY does_not_exist CALL message "Should not be allowed.")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,2 @@
|
||||
^CMake Error at CMakeLists.txt:DEFERRED:
|
||||
cmake_policy PUSH without matching POP$
|
||||
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER CALL cmake_policy PUSH)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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$
|
||||
@@ -0,0 +1,2 @@
|
||||
# Argument syntax error evaluated at deferred call site.
|
||||
cmake_language(DEFER CALL message "Deferred \X Error")
|
||||
@@ -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$
|
||||
3
Tests/RunCMake/cmake_language/defer_call_trace.cmake
Normal file
3
Tests/RunCMake/cmake_language/defer_call_trace.cmake
Normal 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")
|
||||
@@ -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$
|
||||
@@ -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")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
1
Tests/RunCMake/cmake_language/defer_cancel_call_id.cmake
Normal file
1
Tests/RunCMake/cmake_language/defer_cancel_call_id.cmake
Normal file
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER ID id CANCEL_CALL)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER ID_VAR id_var CANCEL_CALL)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)
|
||||
@@ -0,0 +1,2 @@
|
||||
add_subdirectory(defer_cancel_call_invalid_directory)
|
||||
cmake_language(DEFER DIRECTORY defer_cancel_call_invalid_directory CANCEL_CALL _)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER CANCEL_CALL UNKNOWN)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER DIRECTORY "")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER DIRECTORY)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER DIRECTORY . DIRECTORY x CALL message "Should not be allowed.")
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
1
Tests/RunCMake/cmake_language/defer_get_call_id.cmake
Normal file
1
Tests/RunCMake/cmake_language/defer_get_call_id.cmake
Normal file
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER ID id GET_CALL)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER GET_CALL "" var)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER ID_VAR id_var GET_CALL)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER ID id GET_CALL_IDS)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -0,0 +1 @@
|
||||
cmake_language(DEFER ID_VAR id_var GET_CALL_IDS)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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\)$
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -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
Reference in New Issue
Block a user