cmVariableWatchCommand.cxx 4.6 KB

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