/src/CMake/Source/cmGeneratorExpressionEvaluator.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmGeneratorExpressionEvaluator.h" |
4 | | |
5 | | #include <iterator> |
6 | | |
7 | | #include <cm/string_view> |
8 | | #include <cmext/string_view> |
9 | | |
10 | | #ifndef CMAKE_BOOTSTRAP |
11 | | # include <cm3p/json/value.h> |
12 | | #endif |
13 | | #include "cmGenExContext.h" |
14 | | #include "cmGenExEvaluation.h" |
15 | | #include "cmGeneratorExpressionNode.h" |
16 | | #include "cmLocalGenerator.h" |
17 | | #include "cmStringAlgorithms.h" |
18 | | #include "cmake.h" |
19 | | |
20 | | GeneratorExpressionContent::GeneratorExpressionContent( |
21 | | char const* startContent, size_t length) |
22 | 46.1k | : StartContent(startContent) |
23 | 46.1k | , ContentLength(length) |
24 | 46.1k | { |
25 | 46.1k | } |
26 | | |
27 | 46.1k | GeneratorExpressionContent::~GeneratorExpressionContent() = default; |
28 | | |
29 | | std::string GeneratorExpressionContent::GetOriginalExpression() const |
30 | 61.9k | { |
31 | 61.9k | return std::string(this->StartContent, this->ContentLength); |
32 | 61.9k | } |
33 | | |
34 | | std::string GeneratorExpressionContent::ProcessArbitraryContent( |
35 | | cmGeneratorExpressionNode const* node, std::string const& identifier, |
36 | | cm::GenEx::Evaluation* eval, cmGeneratorExpressionDAGChecker* dagChecker, |
37 | | std::vector<cmGeneratorExpressionEvaluatorVector>::const_iterator pit) const |
38 | 4.34k | { |
39 | 4.34k | std::string result; |
40 | | |
41 | 4.34k | auto const pend = this->ParamChildren.end(); |
42 | 18.6k | for (; pit != pend; ++pit) { |
43 | 14.3k | for (auto const& pExprEval : *pit) { |
44 | 11.0k | if (node->RequiresLiteralInput()) { |
45 | 191 | if (pExprEval->GetType() != cmGeneratorExpressionEvaluator::Text) { |
46 | 5 | reportError(eval, this->GetOriginalExpression(), |
47 | 5 | cmStrCat("$<", identifier, |
48 | 5 | "> expression requires literal input.")); |
49 | 5 | return std::string(); |
50 | 5 | } |
51 | 191 | } |
52 | 11.0k | result += pExprEval->Evaluate(eval, dagChecker); |
53 | 11.0k | if (eval->HadError) { |
54 | 107 | return std::string(); |
55 | 107 | } |
56 | 11.0k | } |
57 | 14.2k | if ((pit + 1) != pend) { |
58 | 10.0k | result += ","; |
59 | 10.0k | } |
60 | 14.2k | } |
61 | 4.22k | if (node->RequiresLiteralInput()) { |
62 | 46 | std::vector<std::string> parameters; |
63 | 46 | parameters.push_back(result); |
64 | 46 | return node->Evaluate(parameters, eval, this, dagChecker); |
65 | 46 | } |
66 | 4.18k | return result; |
67 | 4.22k | } |
68 | | |
69 | | std::string GeneratorExpressionContent::Evaluate( |
70 | | cm::GenEx::Evaluation* eval, |
71 | | cmGeneratorExpressionDAGChecker* dagChecker) const |
72 | 57.4k | { |
73 | 57.4k | #ifndef CMAKE_BOOTSTRAP |
74 | 57.4k | auto evalProfilingRAII = |
75 | 57.4k | eval->Context.LG->GetCMakeInstance()->CreateProfilingEntry( |
76 | 57.4k | "genex_eval", this->GetOriginalExpression()); |
77 | 57.4k | #endif |
78 | | |
79 | 57.4k | std::string identifier; |
80 | 57.4k | { |
81 | 59.3k | for (auto const& pExprEval : this->IdentifierChildren) { |
82 | 59.3k | identifier += pExprEval->Evaluate(eval, dagChecker); |
83 | 59.3k | if (eval->HadError) { |
84 | 1.82k | return std::string(); |
85 | 1.82k | } |
86 | 59.3k | } |
87 | 57.4k | } |
88 | | |
89 | 55.6k | cmGeneratorExpressionNode const* node = |
90 | 55.6k | cmGeneratorExpressionNode::GetNode(identifier); |
91 | | |
92 | 55.6k | if (!node) { |
93 | 1.11k | reportError(eval, this->GetOriginalExpression(), |
94 | 1.11k | "Expression did not evaluate to a known generator expression"); |
95 | 1.11k | return std::string(); |
96 | 1.11k | } |
97 | | |
98 | 54.4k | if (!node->GeneratesContent()) { |
99 | 3.36k | if (node->NumExpectedParameters() == 1 && |
100 | 3.36k | node->AcceptsArbitraryContentParameter()) { |
101 | 3.36k | if (this->ParamChildren.empty()) { |
102 | 2 | reportError( |
103 | 2 | eval, this->GetOriginalExpression(), |
104 | 2 | cmStrCat("$<", identifier, "> expression requires a parameter.")); |
105 | 2 | } |
106 | 3.36k | } else { |
107 | 0 | std::vector<std::string> parameters; |
108 | 0 | this->EvaluateParameters(node, identifier, eval, dagChecker, parameters); |
109 | 0 | } |
110 | 3.36k | return std::string(); |
111 | 3.36k | } |
112 | | |
113 | 51.1k | std::vector<std::string> parameters; |
114 | 51.1k | this->EvaluateParameters(node, identifier, eval, dagChecker, parameters); |
115 | 51.1k | if (eval->HadError) { |
116 | 782 | return std::string(); |
117 | 782 | } |
118 | | |
119 | 50.3k | if (!parameters.empty()) { |
120 | 47.7k | cm::string_view ARGS_BEGIN = "<ARGS>"_s; |
121 | 47.7k | cm::string_view ARGS_END = "</ARGS>"_s; |
122 | | |
123 | 47.7k | std::vector<std::string> args; |
124 | | // split any parameter with the prefix <ARGS>: |
125 | 72.8k | for (auto const& param : parameters) { |
126 | 72.8k | if (cmHasPrefix(param, ARGS_BEGIN) && cmHasSuffix(param, ARGS_END)) { |
127 | 0 | auto values = param.substr(ARGS_BEGIN.length(), |
128 | 0 | param.length() - ARGS_BEGIN.length() - |
129 | 0 | ARGS_END.length()); |
130 | 0 | if (values.empty()) { |
131 | 0 | args.push_back(std::move(values)); |
132 | 0 | continue; |
133 | 0 | } |
134 | | |
135 | 0 | std::vector<std::string> tokens = |
136 | 0 | cmTokenize(values, ',', cmTokenizerMode::New); |
137 | 0 | args.insert(args.end(), std::make_move_iterator(tokens.begin()), |
138 | 0 | std::make_move_iterator(tokens.end())); |
139 | 72.8k | } else { |
140 | 72.8k | args.push_back(param); |
141 | 72.8k | } |
142 | 72.8k | } |
143 | 47.7k | parameters = std::move(args); |
144 | 47.7k | } |
145 | | |
146 | 50.3k | { |
147 | 50.3k | #ifndef CMAKE_BOOTSTRAP |
148 | 50.3k | auto execProfilingRAII = |
149 | 50.3k | eval->Context.LG->GetCMakeInstance()->CreateProfilingEntry( |
150 | 50.3k | "genex_exec", identifier, [¶meters]() -> Json::Value { |
151 | 0 | Json::Value args = Json::objectValue; |
152 | 0 | if (!parameters.empty()) { |
153 | 0 | args["genexArgs"] = Json::arrayValue; |
154 | 0 | for (auto const& parameter : parameters) { |
155 | 0 | args["genexArgs"].append(parameter); |
156 | 0 | } |
157 | 0 | } |
158 | 0 | return args; |
159 | 0 | }); |
160 | 50.3k | #endif |
161 | | |
162 | 50.3k | return node->Evaluate(parameters, eval, this, dagChecker); |
163 | 51.1k | } |
164 | 51.1k | } |
165 | | |
166 | | std::string GeneratorExpressionContent::EvaluateParameters( |
167 | | cmGeneratorExpressionNode const* node, std::string const& identifier, |
168 | | cm::GenEx::Evaluation* eval, cmGeneratorExpressionDAGChecker* dagChecker, |
169 | | std::vector<std::string>& parameters) const |
170 | 51.1k | { |
171 | 51.1k | int const numExpected = node->NumExpectedParameters(); |
172 | 51.1k | { |
173 | 51.1k | auto pit = this->ParamChildren.begin(); |
174 | 51.1k | auto const pend = this->ParamChildren.end(); |
175 | 51.1k | bool const acceptsArbitraryContent = |
176 | 51.1k | node->AcceptsArbitraryContentParameter(); |
177 | 51.1k | int counter = 1; |
178 | 124k | for (; pit != pend; ++pit, ++counter) { |
179 | 78.0k | if (acceptsArbitraryContent && counter == numExpected) { |
180 | 4.34k | parameters.push_back(this->ProcessArbitraryContent( |
181 | 4.34k | node, identifier, eval, dagChecker, pit)); |
182 | 4.34k | return std::string(); |
183 | 4.34k | } |
184 | 73.6k | std::string parameter; |
185 | 73.6k | if (node->ShouldEvaluateNextParameter(parameters, parameter)) { |
186 | 85.6k | for (auto const& pExprEval : *pit) { |
187 | 85.6k | parameter += pExprEval->Evaluate(eval, dagChecker); |
188 | 85.6k | if (eval->HadError) { |
189 | 400 | return std::string(); |
190 | 400 | } |
191 | 85.6k | } |
192 | 73.6k | } |
193 | 73.2k | parameters.push_back(std::move(parameter)); |
194 | 73.2k | } |
195 | 51.1k | } |
196 | | |
197 | 46.3k | if ((numExpected > cmGeneratorExpressionNode::DynamicParameters && |
198 | 15.3k | static_cast<unsigned int>(numExpected) != parameters.size())) { |
199 | 206 | if (numExpected == 0) { |
200 | 0 | reportError( |
201 | 0 | eval, this->GetOriginalExpression(), |
202 | 0 | cmStrCat("$<", identifier, "> expression requires no parameters.")); |
203 | 206 | } else if (numExpected == 1) { |
204 | 50 | reportError(eval, this->GetOriginalExpression(), |
205 | 50 | cmStrCat("$<", identifier, |
206 | 50 | "> expression requires " |
207 | 50 | "exactly one parameter.")); |
208 | 156 | } else { |
209 | 156 | std::string e = |
210 | 156 | cmStrCat("$<", identifier, "> expression requires ", numExpected, |
211 | 156 | " comma separated parameters, but got ", parameters.size(), |
212 | 156 | " instead."); |
213 | 156 | reportError(eval, this->GetOriginalExpression(), e); |
214 | 156 | } |
215 | 206 | return std::string(); |
216 | 206 | } |
217 | | |
218 | 46.1k | if (numExpected == cmGeneratorExpressionNode::OneOrMoreParameters && |
219 | 20.7k | parameters.empty()) { |
220 | 10 | reportError(eval, this->GetOriginalExpression(), |
221 | 10 | cmStrCat("$<", identifier, |
222 | 10 | "> expression requires at least one parameter.")); |
223 | 46.1k | } else if (numExpected == cmGeneratorExpressionNode::TwoOrMoreParameters && |
224 | 1.69k | parameters.size() < 2) { |
225 | 54 | reportError(eval, this->GetOriginalExpression(), |
226 | 54 | cmStrCat("$<", identifier, |
227 | 54 | "> expression requires at least two parameters.")); |
228 | 46.1k | } else if (numExpected == cmGeneratorExpressionNode::OneOrZeroParameters && |
229 | 53 | parameters.size() > 1) { |
230 | 0 | reportError(eval, this->GetOriginalExpression(), |
231 | 0 | cmStrCat("$<", identifier, |
232 | 0 | "> expression requires one or zero parameters.")); |
233 | 0 | } |
234 | 46.1k | return std::string(); |
235 | 46.3k | } |