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"
9 #include "cmExecutionStatus.h"
10 #include "cmListFileCache.h"
11 #include "cmMakefile.h"
12 #include "cmMessageType.h"
13 #include "cmStringAlgorithms.h"
14 #include "cmSystemTools.h"
16 #include "cmVariableWatch.h"
19 class cmLocalGenerator
;
22 struct cmVariableWatchCallbackData
28 void cmVariableWatchCommandVariableAccessed(const std::string
& variable
,
29 int access_type
, void* client_data
,
33 cmVariableWatchCallbackData
* data
=
34 static_cast<cmVariableWatchCallbackData
*>(client_data
);
36 if (data
->InCallback
) {
39 data
->InCallback
= true;
41 auto accessString
= cmVariableWatch::GetAccessAsString(access_type
);
44 cmMakefile
* makefile
= const_cast<cmMakefile
*>(mf
);
46 std::string stack
= *mf
->GetProperty("LISTFILE_STACK");
47 if (!data
->Command
.empty()) {
48 cmValue
const currentListFile
=
49 mf
->GetDefinition("CMAKE_CURRENT_LIST_FILE");
50 const auto fakeLineNo
=
51 std::numeric_limits
<decltype(cmListFileArgument::Line
)>::max();
53 std::vector
<cmListFileArgument
> newLFFArgs
{
54 { variable
, cmListFileArgument::Quoted
, fakeLineNo
},
55 { accessString
, cmListFileArgument::Quoted
, fakeLineNo
},
56 { newValue
? newValue
: "", cmListFileArgument::Quoted
, fakeLineNo
},
57 { *currentListFile
, cmListFileArgument::Quoted
, fakeLineNo
},
58 { stack
, cmListFileArgument::Quoted
, fakeLineNo
}
61 cmListFileFunction newLFF
{ data
->Command
, fakeLineNo
, fakeLineNo
,
62 std::move(newLFFArgs
) };
63 cmExecutionStatus
status(*makefile
);
64 if (!makefile
->ExecuteCommand(newLFF
, status
)) {
66 cmStrCat("Error in cmake code at\nUnknown:0:\nA command failed "
67 "during the invocation of callback \"",
68 data
->Command
, "\"."));
71 makefile
->IssueMessage(
73 cmStrCat("Variable \"", variable
, "\" was accessed using ", accessString
,
74 " with value \"", (newValue
? newValue
: ""), "\"."));
77 data
->InCallback
= false;
80 void deleteVariableWatchCallbackData(void* client_data
)
82 cmVariableWatchCallbackData
* data
=
83 static_cast<cmVariableWatchCallbackData
*>(client_data
);
87 /** This command does not really have a final pass but it needs to
88 stay alive since it owns variable watch callback information. */
92 /* NOLINTNEXTLINE(performance-unnecessary-value-param) */
93 FinalAction(cmMakefile
* makefile
, std::string variable
)
94 : Action
{ std::make_shared
<Impl
>(makefile
, std::move(variable
)) }
98 void operator()(cmLocalGenerator
&, const cmListFileBacktrace
&) const {}
103 Impl(cmMakefile
* makefile
, std::string variable
)
104 : Makefile
{ makefile
}
105 , Variable
{ std::move(variable
) }
111 this->Makefile
->GetCMakeInstance()->GetVariableWatch()->RemoveWatch(
112 this->Variable
, cmVariableWatchCommandVariableAccessed
);
115 cmMakefile
* const Makefile
;
116 std::string
const Variable
;
119 std::shared_ptr
<Impl
const> Action
;
121 } // anonymous namespace
123 bool cmVariableWatchCommand(std::vector
<std::string
> const& args
,
124 cmExecutionStatus
& status
)
127 status
.SetError("must be called with at least one argument.");
130 std::string
const& variable
= args
[0];
132 if (args
.size() > 1) {
135 if (variable
== "CMAKE_CURRENT_LIST_FILE") {
136 status
.SetError(cmStrCat("cannot be set on the variable: ", variable
));
140 auto* const data
= new cmVariableWatchCallbackData
;
142 data
->InCallback
= false;
143 data
->Command
= std::move(command
);
145 if (!status
.GetMakefile().GetCMakeInstance()->GetVariableWatch()->AddWatch(
146 variable
, cmVariableWatchCommandVariableAccessed
, data
,
147 deleteVariableWatchCallbackData
)) {
148 deleteVariableWatchCallbackData(data
);
152 status
.GetMakefile().AddGeneratorAction(
153 FinalAction
{ &status
.GetMakefile(), variable
});