1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmVariableWatchCommand.h"
#include <limits>
#include <memory>
#include <utility>
#include "cmExecutionStatus.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmValue.h"
#include "cmVariableWatch.h"
#include "cmake.h"
class cmLocalGenerator;
namespace {
struct cmVariableWatchCallbackData
{
bool InCallback;
std::string Command;
};
void cmVariableWatchCommandVariableAccessed(std::string const& variable,
int access_type, void* client_data,
char const* newValue,
cmMakefile const* mf)
{
cmVariableWatchCallbackData* data =
static_cast<cmVariableWatchCallbackData*>(client_data);
if (data->InCallback) {
return;
}
data->InCallback = true;
auto accessString = cmVariableWatch::GetAccessAsString(access_type);
/// Ultra bad!!
cmMakefile* makefile = const_cast<cmMakefile*>(mf);
std::string stack = *mf->GetProperty("LISTFILE_STACK");
if (!data->Command.empty()) {
cmValue const currentListFile =
mf->GetDefinition("CMAKE_CURRENT_LIST_FILE");
auto const fakeLineNo =
std::numeric_limits<decltype(cmListFileArgument::Line)>::max();
std::vector<cmListFileArgument> newLFFArgs{
{ variable, cmListFileArgument::Quoted, fakeLineNo },
{ accessString, cmListFileArgument::Quoted, fakeLineNo },
{ newValue ? newValue : "", cmListFileArgument::Quoted, fakeLineNo },
{ *currentListFile, cmListFileArgument::Quoted, fakeLineNo },
{ stack, cmListFileArgument::Quoted, fakeLineNo }
};
cmListFileFunction newLFF{ data->Command, fakeLineNo, fakeLineNo,
std::move(newLFFArgs) };
cmExecutionStatus status(*makefile);
if (!makefile->ExecuteCommand(newLFF, status)) {
cmSystemTools::Error(
cmStrCat("Error in cmake code at\nUnknown:0:\nA command failed "
"during the invocation of callback \"",
data->Command, "\"."));
}
} else {
makefile->IssueMessage(
MessageType::LOG,
cmStrCat("Variable \"", variable, "\" was accessed using ", accessString,
" with value \"", (newValue ? newValue : ""), "\"."));
}
data->InCallback = false;
}
void deleteVariableWatchCallbackData(void* client_data)
{
cmVariableWatchCallbackData* data =
static_cast<cmVariableWatchCallbackData*>(client_data);
delete data;
}
/** This command does not really have a final pass but it needs to
stay alive since it owns variable watch callback information. */
class FinalAction
{
public:
/* NOLINTNEXTLINE(performance-unnecessary-value-param) */
FinalAction(cmMakefile* makefile, std::string variable)
: Action{ std::make_shared<Impl>(makefile, std::move(variable)) }
{
}
void operator()(cmLocalGenerator&, cmListFileBacktrace const&) const {}
private:
struct Impl
{
Impl(cmMakefile* makefile, std::string variable)
: Makefile{ makefile }
, Variable{ std::move(variable) }
{
}
~Impl()
{
this->Makefile->GetCMakeInstance()->GetVariableWatch()->RemoveWatch(
this->Variable, cmVariableWatchCommandVariableAccessed);
}
cmMakefile* const Makefile;
std::string const Variable;
};
std::shared_ptr<Impl const> Action;
};
} // anonymous namespace
bool cmVariableWatchCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
if (args.empty()) {
status.SetError("must be called with at least one argument.");
return false;
}
std::string const& variable = args[0];
std::string command;
if (args.size() > 1) {
command = args[1];
}
if (variable == "CMAKE_CURRENT_LIST_FILE") {
status.SetError(cmStrCat("cannot be set on the variable: ", variable));
return false;
}
auto* const data = new cmVariableWatchCallbackData;
data->InCallback = false;
data->Command = std::move(command);
if (!status.GetMakefile().GetCMakeInstance()->GetVariableWatch()->AddWatch(
variable, cmVariableWatchCommandVariableAccessed, data,
deleteVariableWatchCallbackData)) {
deleteVariableWatchCallbackData(data);
return false;
}
status.GetMakefile().AddGeneratorAction(
FinalAction{ &status.GetMakefile(), variable });
return true;
}
|