| /* 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 <utility> |
| |
| #include "cmExecutionStatus.h" |
| #include "cmListFileCache.h" |
| #include "cmMakefile.h" |
| #include "cmMessageType.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(*makefile); |
| 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()); |
| data->InCallback = false; |
| return; |
| } |
| processed = true; |
| } |
| if (!processed) { |
| std::ostringstream msg; |
| msg << "Variable \"" << variable << "\" was accessed using " |
| << accessString << " with value \"" << (newValue ? newValue : "") |
| << "\"."; |
| makefile->IssueMessage(MessageType::LOG, msg.str()); |
| } |
| |
| data->InCallback = false; |
| } |
| |
| static 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: |
| FinalAction(cmMakefile* makefile, std::string variable) |
| : Action(std::make_shared<Impl>(makefile, std::move(variable))) |
| { |
| } |
| |
| void operator()(cmMakefile&) 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* Makefile; |
| std::string Variable; |
| }; |
| |
| std::shared_ptr<Impl const> Action; |
| }; |
| |
| 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; |
| |
| if (!this->Makefile->GetCMakeInstance()->GetVariableWatch()->AddWatch( |
| variable, cmVariableWatchCommandVariableAccessed, data, |
| deleteVariableWatchCallbackData)) { |
| deleteVariableWatchCallbackData(data); |
| return false; |
| } |
| |
| this->Makefile->AddFinalAction(FinalAction(this->Makefile, variable)); |
| return true; |
| } |