cmVariableWatchCommand.cxx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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 <utility>
  6. #include "cmExecutionStatus.h"
  7. #include "cmListFileCache.h"
  8. #include "cmMakefile.h"
  9. #include "cmMessageType.h"
  10. #include "cmStringAlgorithms.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. cmSystemTools::Error(
  54. cmStrCat("Error in cmake code at\nUnknown:0:\nA command failed "
  55. "during the invocation of callback \"",
  56. data->Command, "\"."));
  57. data->InCallback = false;
  58. return;
  59. }
  60. processed = true;
  61. }
  62. if (!processed) {
  63. makefile->IssueMessage(
  64. MessageType::LOG,
  65. cmStrCat("Variable \"", variable, "\" was accessed using ", accessString,
  66. " with value \"", (newValue ? newValue : ""), "\"."));
  67. }
  68. data->InCallback = false;
  69. }
  70. static void deleteVariableWatchCallbackData(void* client_data)
  71. {
  72. cmVariableWatchCallbackData* data =
  73. static_cast<cmVariableWatchCallbackData*>(client_data);
  74. delete data;
  75. }
  76. /** This command does not really have a final pass but it needs to
  77. stay alive since it owns variable watch callback information. */
  78. class FinalAction
  79. {
  80. public:
  81. /* NOLINTNEXTLINE(performance-unnecessary-value-param) */
  82. FinalAction(cmMakefile* makefile, std::string variable)
  83. : Action(std::make_shared<Impl>(makefile, std::move(variable)))
  84. {
  85. }
  86. void operator()(cmMakefile&) const {}
  87. private:
  88. struct Impl
  89. {
  90. Impl(cmMakefile* makefile, std::string variable)
  91. : Makefile(makefile)
  92. , Variable(std::move(variable))
  93. {
  94. }
  95. ~Impl()
  96. {
  97. this->Makefile->GetCMakeInstance()->GetVariableWatch()->RemoveWatch(
  98. this->Variable, cmVariableWatchCommandVariableAccessed);
  99. }
  100. cmMakefile* Makefile;
  101. std::string Variable;
  102. };
  103. std::shared_ptr<Impl const> Action;
  104. };
  105. bool cmVariableWatchCommand(std::vector<std::string> const& args,
  106. cmExecutionStatus& status)
  107. {
  108. if (args.empty()) {
  109. status.SetError("must be called with at least one argument.");
  110. return false;
  111. }
  112. std::string const& variable = args[0];
  113. std::string command;
  114. if (args.size() > 1) {
  115. command = args[1];
  116. }
  117. if (variable == "CMAKE_CURRENT_LIST_FILE") {
  118. status.SetError(cmStrCat("cannot be set on the variable: ", variable));
  119. return false;
  120. }
  121. cmVariableWatchCallbackData* data = new cmVariableWatchCallbackData;
  122. data->InCallback = false;
  123. data->Command = command;
  124. if (!status.GetMakefile().GetCMakeInstance()->GetVariableWatch()->AddWatch(
  125. variable, cmVariableWatchCommandVariableAccessed, data,
  126. deleteVariableWatchCallbackData)) {
  127. deleteVariableWatchCallbackData(data);
  128. return false;
  129. }
  130. status.GetMakefile().AddFinalAction(
  131. FinalAction(&status.GetMakefile(), variable));
  132. return true;
  133. }