blob: e080df1ec364f891f8224fbf6ae1b29087dd1dda [file] [log] [blame]
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmVariableWatchCommand.h"
#include <sstream>
#include "cmExecutionStatus.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmSystemTools.h"
#include "cmVariableWatch.h"
#include "cmake.h"
struct cmVariableWatchCallbackData
{
bool InCallback;
std::string Command;
};
static void cmVariableWatchCommandVariableAccessed(const std::string& variable,
int access_type,
void* client_data,
const char* newValue,
const cmMakefile* mf)
{
cmVariableWatchCallbackData* data =
static_cast<cmVariableWatchCallbackData*>(client_data);
if (data->InCallback) {
return;
}
data->InCallback = true;
cmListFileFunction newLFF;
cmListFileArgument arg;
bool processed = false;
const char* accessString = cmVariableWatch::GetAccessAsString(access_type);
const char* currentListFile = mf->GetDefinition("CMAKE_CURRENT_LIST_FILE");
/// Ultra bad!!
cmMakefile* makefile = const_cast<cmMakefile*>(mf);
std::string stack = makefile->GetProperty("LISTFILE_STACK");
if (!data->Command.empty()) {
newLFF.Arguments.clear();
newLFF.Arguments.emplace_back(variable, cmListFileArgument::Quoted, 9999);
newLFF.Arguments.emplace_back(accessString, cmListFileArgument::Quoted,
9999);
newLFF.Arguments.emplace_back(newValue ? newValue : "",
cmListFileArgument::Quoted, 9999);
newLFF.Arguments.emplace_back(currentListFile, cmListFileArgument::Quoted,
9999);
newLFF.Arguments.emplace_back(stack, cmListFileArgument::Quoted, 9999);
newLFF.Name = data->Command;
newLFF.Line = 9999;
cmExecutionStatus status;
if (!makefile->ExecuteCommand(newLFF, status)) {
std::ostringstream error;
error << "Error in cmake code at\nUnknown:0:\n"
<< "A command failed during the invocation of callback \""
<< data->Command << "\".";
cmSystemTools::Error(error.str().c_str());
data->InCallback = false;
return;
}
processed = true;
}
if (!processed) {
std::ostringstream msg;
msg << "Variable \"" << variable << "\" was accessed using "
<< accessString << " with value \"" << (newValue ? newValue : "")
<< "\".";
makefile->IssueMessage(cmake::LOG, msg.str());
}
data->InCallback = false;
}
static void deleteVariableWatchCallbackData(void* client_data)
{
cmVariableWatchCallbackData* data =
static_cast<cmVariableWatchCallbackData*>(client_data);
delete data;
}
cmVariableWatchCommand::cmVariableWatchCommand()
{
}
cmVariableWatchCommand::~cmVariableWatchCommand()
{
for (std::string const& wv : this->WatchedVariables) {
this->Makefile->GetCMakeInstance()->GetVariableWatch()->RemoveWatch(
wv, cmVariableWatchCommandVariableAccessed);
}
}
bool cmVariableWatchCommand::InitialPass(std::vector<std::string> const& args,
cmExecutionStatus&)
{
if (args.empty()) {
this->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") {
std::ostringstream ostr;
ostr << "cannot be set on the variable: " << variable;
this->SetError(ostr.str());
return false;
}
cmVariableWatchCallbackData* data = new cmVariableWatchCallbackData;
data->InCallback = false;
data->Command = command;
this->WatchedVariables.insert(variable);
if (!this->Makefile->GetCMakeInstance()->GetVariableWatch()->AddWatch(
variable, cmVariableWatchCommandVariableAccessed, data,
deleteVariableWatchCallbackData)) {
deleteVariableWatchCallbackData(data);
return false;
}
return true;
}