/src/CMake/Source/cmAddCustomCommandCommand.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 "cmAddCustomCommandCommand.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <iterator> |
7 | | #include <set> |
8 | | #include <unordered_set> |
9 | | #include <utility> |
10 | | |
11 | | #include <cm/memory> |
12 | | #include <cmext/string_view> |
13 | | |
14 | | #include "cmCustomCommand.h" |
15 | | #include "cmCustomCommandLines.h" |
16 | | #include "cmCustomCommandTypes.h" |
17 | | #include "cmExecutionStatus.h" |
18 | | #include "cmGeneratorExpression.h" |
19 | | #include "cmGlobalGenerator.h" |
20 | | #include "cmMakefile.h" |
21 | | #include "cmMessageType.h" |
22 | | #include "cmPolicies.h" |
23 | | #include "cmStringAlgorithms.h" |
24 | | #include "cmSystemTools.h" |
25 | | #include "cmValue.h" |
26 | | |
27 | | bool cmAddCustomCommandCommand(std::vector<std::string> const& args, |
28 | | cmExecutionStatus& status) |
29 | 0 | { |
30 | | /* Let's complain at the end of this function about the lack of a particular |
31 | | arg. For the moment, let's say that COMMAND, and either TARGET or SOURCE |
32 | | are required. |
33 | | */ |
34 | 0 | if (args.size() < 4) { |
35 | 0 | status.SetError("called with wrong number of arguments."); |
36 | 0 | return false; |
37 | 0 | } |
38 | | |
39 | 0 | cmMakefile& mf = status.GetMakefile(); |
40 | 0 | std::string source; |
41 | 0 | std::string target; |
42 | 0 | std::string main_dependency; |
43 | 0 | std::string working; |
44 | 0 | std::string depfile; |
45 | 0 | std::string job_pool; |
46 | 0 | std::string job_server_aware; |
47 | 0 | std::string comment_buffer; |
48 | 0 | char const* comment = nullptr; |
49 | 0 | std::vector<std::string> depends; |
50 | 0 | std::vector<std::string> outputs; |
51 | 0 | std::vector<std::string> output; |
52 | 0 | std::vector<std::string> byproducts; |
53 | 0 | bool verbatim = false; |
54 | 0 | bool append = false; |
55 | 0 | bool uses_terminal = false; |
56 | 0 | bool command_expand_lists = false; |
57 | 0 | bool depends_explicit_only = |
58 | 0 | mf.IsOn("CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY"); |
59 | 0 | bool codegen = false; |
60 | 0 | std::string implicit_depends_lang; |
61 | 0 | cmImplicitDependsList implicit_depends; |
62 | | |
63 | | // Accumulate one command line at a time. |
64 | 0 | cmCustomCommandLine currentLine; |
65 | | |
66 | | // Save all command lines. |
67 | 0 | cmCustomCommandLines commandLines; |
68 | |
|
69 | 0 | cmCustomCommandType cctype = cmCustomCommandType::POST_BUILD; |
70 | |
|
71 | 0 | enum tdoing |
72 | 0 | { |
73 | 0 | doing_source, |
74 | 0 | doing_command, |
75 | 0 | doing_target, |
76 | 0 | doing_depends, |
77 | 0 | doing_implicit_depends_lang, |
78 | 0 | doing_implicit_depends_file, |
79 | 0 | doing_main_dependency, |
80 | 0 | doing_output, |
81 | 0 | doing_outputs, |
82 | 0 | doing_byproducts, |
83 | 0 | doing_comment, |
84 | 0 | doing_working_directory, |
85 | 0 | doing_depfile, |
86 | 0 | doing_job_pool, |
87 | 0 | doing_job_server_aware, |
88 | 0 | doing_nothing |
89 | 0 | }; |
90 | |
|
91 | 0 | tdoing doing = doing_nothing; |
92 | |
|
93 | 0 | #define MAKE_STATIC_KEYWORD(KEYWORD) \ |
94 | 0 | static const std::string key##KEYWORD = #KEYWORD |
95 | 0 | MAKE_STATIC_KEYWORD(APPEND); |
96 | 0 | MAKE_STATIC_KEYWORD(ARGS); |
97 | 0 | MAKE_STATIC_KEYWORD(BYPRODUCTS); |
98 | 0 | MAKE_STATIC_KEYWORD(COMMAND); |
99 | 0 | MAKE_STATIC_KEYWORD(COMMAND_EXPAND_LISTS); |
100 | 0 | MAKE_STATIC_KEYWORD(COMMENT); |
101 | 0 | MAKE_STATIC_KEYWORD(DEPENDS); |
102 | 0 | MAKE_STATIC_KEYWORD(DEPFILE); |
103 | 0 | MAKE_STATIC_KEYWORD(IMPLICIT_DEPENDS); |
104 | 0 | MAKE_STATIC_KEYWORD(JOB_POOL); |
105 | 0 | MAKE_STATIC_KEYWORD(JOB_SERVER_AWARE); |
106 | 0 | MAKE_STATIC_KEYWORD(MAIN_DEPENDENCY); |
107 | 0 | MAKE_STATIC_KEYWORD(OUTPUT); |
108 | 0 | MAKE_STATIC_KEYWORD(OUTPUTS); |
109 | 0 | MAKE_STATIC_KEYWORD(POST_BUILD); |
110 | 0 | MAKE_STATIC_KEYWORD(PRE_BUILD); |
111 | 0 | MAKE_STATIC_KEYWORD(PRE_LINK); |
112 | 0 | MAKE_STATIC_KEYWORD(SOURCE); |
113 | 0 | MAKE_STATIC_KEYWORD(TARGET); |
114 | 0 | MAKE_STATIC_KEYWORD(USES_TERMINAL); |
115 | 0 | MAKE_STATIC_KEYWORD(VERBATIM); |
116 | 0 | MAKE_STATIC_KEYWORD(WORKING_DIRECTORY); |
117 | 0 | MAKE_STATIC_KEYWORD(DEPENDS_EXPLICIT_ONLY); |
118 | 0 | MAKE_STATIC_KEYWORD(CODEGEN); |
119 | 0 | #undef MAKE_STATIC_KEYWORD |
120 | 0 | static std::unordered_set<std::string> const keywords{ |
121 | 0 | keyAPPEND, |
122 | 0 | keyARGS, |
123 | 0 | keyBYPRODUCTS, |
124 | 0 | keyCOMMAND, |
125 | 0 | keyCOMMAND_EXPAND_LISTS, |
126 | 0 | keyCOMMENT, |
127 | 0 | keyDEPENDS, |
128 | 0 | keyDEPFILE, |
129 | 0 | keyIMPLICIT_DEPENDS, |
130 | 0 | keyJOB_POOL, |
131 | 0 | keyMAIN_DEPENDENCY, |
132 | 0 | keyOUTPUT, |
133 | 0 | keyOUTPUTS, |
134 | 0 | keyPOST_BUILD, |
135 | 0 | keyPRE_BUILD, |
136 | 0 | keyPRE_LINK, |
137 | 0 | keySOURCE, |
138 | 0 | keyJOB_SERVER_AWARE, |
139 | 0 | keyTARGET, |
140 | 0 | keyUSES_TERMINAL, |
141 | 0 | keyVERBATIM, |
142 | 0 | keyWORKING_DIRECTORY, |
143 | 0 | keyDEPENDS_EXPLICIT_ONLY, |
144 | 0 | keyCODEGEN |
145 | 0 | }; |
146 | | /* clang-format off */ |
147 | 0 | static std::set<std::string> const supportedTargetKeywords{ |
148 | 0 | keyARGS, |
149 | 0 | keyBYPRODUCTS, |
150 | 0 | keyCOMMAND, |
151 | 0 | keyCOMMAND_EXPAND_LISTS, |
152 | 0 | keyCOMMENT, |
153 | 0 | keyPOST_BUILD, |
154 | 0 | keyPRE_BUILD, |
155 | 0 | keyPRE_LINK, |
156 | 0 | keyTARGET, |
157 | 0 | keyUSES_TERMINAL, |
158 | 0 | keyVERBATIM, |
159 | 0 | keyWORKING_DIRECTORY |
160 | 0 | }; |
161 | | /* clang-format on */ |
162 | 0 | static std::set<std::string> const supportedOutputKeywords{ |
163 | 0 | keyAPPEND, |
164 | 0 | keyARGS, |
165 | 0 | keyBYPRODUCTS, |
166 | 0 | keyCODEGEN, |
167 | 0 | keyCOMMAND, |
168 | 0 | keyCOMMAND_EXPAND_LISTS, |
169 | 0 | keyCOMMENT, |
170 | 0 | keyDEPENDS, |
171 | 0 | keyDEPENDS_EXPLICIT_ONLY, |
172 | 0 | keyDEPFILE, |
173 | 0 | keyIMPLICIT_DEPENDS, |
174 | 0 | keyJOB_POOL, |
175 | 0 | keyJOB_SERVER_AWARE, |
176 | 0 | keyMAIN_DEPENDENCY, |
177 | 0 | keyOUTPUT, |
178 | 0 | keyUSES_TERMINAL, |
179 | 0 | keyVERBATIM, |
180 | 0 | keyWORKING_DIRECTORY |
181 | 0 | }; |
182 | | /* clang-format off */ |
183 | 0 | static std::set<std::string> const supportedAppendKeywords{ |
184 | 0 | keyAPPEND, |
185 | 0 | keyARGS, |
186 | 0 | keyCOMMAND, |
187 | 0 | keyCOMMENT, // Allowed but ignored |
188 | 0 | keyDEPENDS, |
189 | 0 | keyIMPLICIT_DEPENDS, |
190 | 0 | keyMAIN_DEPENDENCY, // Allowed but ignored |
191 | 0 | keyOUTPUT, |
192 | 0 | keyWORKING_DIRECTORY // Allowed but ignored |
193 | 0 | }; |
194 | | /* clang-format on */ |
195 | 0 | std::set<std::string> keywordsSeen; |
196 | 0 | std::string const* keywordExpectingValue = nullptr; |
197 | 0 | auto const cmp0175 = mf.GetPolicyStatus(cmPolicies::CMP0175); |
198 | |
|
199 | 0 | for (std::string const& copy : args) { |
200 | 0 | if (keywords.count(copy)) { |
201 | | // Check if a preceding keyword expected a value but there wasn't one |
202 | 0 | if (keywordExpectingValue) { |
203 | 0 | std::string const msg = |
204 | 0 | cmStrCat("Keyword ", *keywordExpectingValue, |
205 | 0 | " requires a value, but none was given."); |
206 | 0 | if (cmp0175 == cmPolicies::NEW) { |
207 | 0 | mf.IssueMessage(MessageType::FATAL_ERROR, msg); |
208 | 0 | return false; |
209 | 0 | } |
210 | 0 | if (cmp0175 == cmPolicies::WARN) { |
211 | 0 | mf.IssueMessage( |
212 | 0 | MessageType::AUTHOR_WARNING, |
213 | 0 | cmStrCat(msg, '\n', |
214 | 0 | cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); |
215 | 0 | } |
216 | 0 | } |
217 | 0 | keywordExpectingValue = nullptr; |
218 | 0 | keywordsSeen.insert(copy); |
219 | |
|
220 | 0 | if (copy == keySOURCE) { |
221 | 0 | doing = doing_source; |
222 | 0 | keywordExpectingValue = &keySOURCE; |
223 | 0 | } else if (copy == keyCOMMAND) { |
224 | 0 | doing = doing_command; |
225 | | |
226 | | // Save the current command before starting the next command. |
227 | 0 | if (!currentLine.empty()) { |
228 | 0 | commandLines.push_back(currentLine); |
229 | 0 | currentLine.clear(); |
230 | 0 | } |
231 | 0 | } else if (copy == keyPRE_BUILD) { |
232 | 0 | cctype = cmCustomCommandType::PRE_BUILD; |
233 | 0 | } else if (copy == keyPRE_LINK) { |
234 | 0 | cctype = cmCustomCommandType::PRE_LINK; |
235 | 0 | } else if (copy == keyPOST_BUILD) { |
236 | 0 | cctype = cmCustomCommandType::POST_BUILD; |
237 | 0 | } else if (copy == keyVERBATIM) { |
238 | 0 | verbatim = true; |
239 | 0 | } else if (copy == keyAPPEND) { |
240 | 0 | append = true; |
241 | 0 | } else if (copy == keyUSES_TERMINAL) { |
242 | 0 | uses_terminal = true; |
243 | 0 | } else if (copy == keyCOMMAND_EXPAND_LISTS) { |
244 | 0 | command_expand_lists = true; |
245 | 0 | } else if (copy == keyDEPENDS_EXPLICIT_ONLY) { |
246 | 0 | depends_explicit_only = true; |
247 | 0 | } else if (copy == keyCODEGEN) { |
248 | 0 | codegen = true; |
249 | 0 | } else if (copy == keyTARGET) { |
250 | 0 | doing = doing_target; |
251 | 0 | keywordExpectingValue = &keyTARGET; |
252 | 0 | } else if (copy == keyARGS) { |
253 | | // Ignore this old keyword. |
254 | 0 | } else if (copy == keyDEPENDS) { |
255 | 0 | doing = doing_depends; |
256 | 0 | } else if (copy == keyOUTPUTS) { |
257 | 0 | doing = doing_outputs; |
258 | 0 | } else if (copy == keyOUTPUT) { |
259 | 0 | doing = doing_output; |
260 | 0 | keywordExpectingValue = &keyOUTPUT; |
261 | 0 | } else if (copy == keyBYPRODUCTS) { |
262 | 0 | doing = doing_byproducts; |
263 | 0 | } else if (copy == keyWORKING_DIRECTORY) { |
264 | 0 | doing = doing_working_directory; |
265 | 0 | keywordExpectingValue = &keyWORKING_DIRECTORY; |
266 | 0 | } else if (copy == keyMAIN_DEPENDENCY) { |
267 | 0 | doing = doing_main_dependency; |
268 | 0 | keywordExpectingValue = &keyMAIN_DEPENDENCY; |
269 | 0 | } else if (copy == keyIMPLICIT_DEPENDS) { |
270 | 0 | doing = doing_implicit_depends_lang; |
271 | 0 | } else if (copy == keyCOMMENT) { |
272 | 0 | doing = doing_comment; |
273 | 0 | keywordExpectingValue = &keyCOMMENT; |
274 | 0 | } else if (copy == keyDEPFILE) { |
275 | 0 | doing = doing_depfile; |
276 | 0 | if (!mf.GetGlobalGenerator()->SupportsCustomCommandDepfile()) { |
277 | 0 | status.SetError(cmStrCat("Option DEPFILE not supported by ", |
278 | 0 | mf.GetGlobalGenerator()->GetName())); |
279 | 0 | return false; |
280 | 0 | } |
281 | 0 | keywordExpectingValue = &keyDEPFILE; |
282 | 0 | } else if (copy == keyJOB_POOL) { |
283 | 0 | doing = doing_job_pool; |
284 | 0 | keywordExpectingValue = &keyJOB_POOL; |
285 | 0 | } else if (copy == keyJOB_SERVER_AWARE) { |
286 | 0 | doing = doing_job_server_aware; |
287 | 0 | keywordExpectingValue = &keyJOB_SERVER_AWARE; |
288 | 0 | } |
289 | 0 | } else { |
290 | 0 | keywordExpectingValue = nullptr; // Value is being processed now |
291 | 0 | std::string filename; |
292 | 0 | switch (doing) { |
293 | 0 | case doing_output: |
294 | 0 | case doing_outputs: |
295 | 0 | case doing_byproducts: |
296 | 0 | if (!cmSystemTools::FileIsFullPath(copy) && |
297 | 0 | cmGeneratorExpression::Find(copy) != 0) { |
298 | | // This is an output to be generated, so it should be |
299 | | // under the build tree. |
300 | 0 | filename = cmStrCat(mf.GetCurrentBinaryDirectory(), '/'); |
301 | 0 | } |
302 | 0 | filename += copy; |
303 | 0 | cmSystemTools::ConvertToUnixSlashes(filename); |
304 | 0 | break; |
305 | 0 | case doing_source: |
306 | | // We do not want to convert the argument to SOURCE because |
307 | | // that option is only available for backward compatibility. |
308 | | // Old-style use of this command may use the SOURCE==TARGET |
309 | | // trick which we must preserve. If we convert the source |
310 | | // to a full path then it will no longer equal the target. |
311 | 0 | default: |
312 | 0 | break; |
313 | 0 | } |
314 | | |
315 | 0 | if (cmSystemTools::FileIsFullPath(filename)) { |
316 | 0 | filename = cmSystemTools::CollapseFullPath(filename); |
317 | 0 | } |
318 | 0 | switch (doing) { |
319 | 0 | case doing_depfile: |
320 | 0 | depfile = copy; |
321 | 0 | break; |
322 | 0 | case doing_job_pool: |
323 | 0 | job_pool = copy; |
324 | 0 | break; |
325 | 0 | case doing_job_server_aware: |
326 | 0 | job_server_aware = copy; |
327 | 0 | break; |
328 | 0 | case doing_working_directory: |
329 | 0 | working = copy; |
330 | 0 | break; |
331 | 0 | case doing_source: |
332 | 0 | source = copy; |
333 | 0 | break; |
334 | 0 | case doing_output: |
335 | 0 | output.push_back(filename); |
336 | 0 | break; |
337 | 0 | case doing_main_dependency: |
338 | 0 | main_dependency = copy; |
339 | 0 | break; |
340 | 0 | case doing_implicit_depends_lang: |
341 | 0 | implicit_depends_lang = copy; |
342 | 0 | doing = doing_implicit_depends_file; |
343 | 0 | break; |
344 | 0 | case doing_implicit_depends_file: { |
345 | | // An implicit dependency starting point is also an |
346 | | // explicit dependency. |
347 | 0 | std::string dep = copy; |
348 | | // Upfront path conversion is correct because Genex |
349 | | // are not supported. |
350 | 0 | cmSystemTools::ConvertToUnixSlashes(dep); |
351 | 0 | depends.push_back(dep); |
352 | | |
353 | | // Add the implicit dependency language and file. |
354 | 0 | implicit_depends.emplace_back(implicit_depends_lang, dep); |
355 | | |
356 | | // Switch back to looking for a language. |
357 | 0 | doing = doing_implicit_depends_lang; |
358 | 0 | } break; |
359 | 0 | case doing_command: |
360 | 0 | currentLine.push_back(copy); |
361 | 0 | break; |
362 | 0 | case doing_target: |
363 | 0 | target = copy; |
364 | 0 | break; |
365 | 0 | case doing_depends: |
366 | 0 | depends.push_back(copy); |
367 | 0 | break; |
368 | 0 | case doing_outputs: |
369 | 0 | outputs.push_back(filename); |
370 | 0 | break; |
371 | 0 | case doing_byproducts: |
372 | 0 | byproducts.push_back(filename); |
373 | 0 | break; |
374 | 0 | case doing_comment: |
375 | 0 | if (!comment_buffer.empty()) { |
376 | 0 | std::string const msg = |
377 | 0 | "COMMENT requires exactly one argument, but multiple values " |
378 | 0 | "or COMMENT keywords have been given."; |
379 | 0 | if (cmp0175 == cmPolicies::NEW) { |
380 | 0 | mf.IssueMessage(MessageType::FATAL_ERROR, msg); |
381 | 0 | return false; |
382 | 0 | } |
383 | 0 | if (cmp0175 == cmPolicies::WARN) { |
384 | 0 | mf.IssueMessage( |
385 | 0 | MessageType::AUTHOR_WARNING, |
386 | 0 | cmStrCat(msg, '\n', |
387 | 0 | cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); |
388 | 0 | } |
389 | 0 | } |
390 | 0 | comment_buffer = copy; |
391 | 0 | comment = comment_buffer.c_str(); |
392 | 0 | break; |
393 | 0 | default: |
394 | 0 | status.SetError("Wrong syntax. Unknown type of argument."); |
395 | 0 | return false; |
396 | 0 | } |
397 | 0 | } |
398 | 0 | } |
399 | | |
400 | | // Store the last command line finished. |
401 | 0 | if (!currentLine.empty()) { |
402 | 0 | commandLines.push_back(currentLine); |
403 | 0 | currentLine.clear(); |
404 | 0 | } |
405 | | |
406 | | // At this point we could complain about the lack of arguments. For |
407 | | // the moment, let's say that COMMAND, TARGET are always required. |
408 | 0 | if (output.empty() && target.empty()) { |
409 | 0 | status.SetError("Wrong syntax. A TARGET or OUTPUT must be specified."); |
410 | 0 | return false; |
411 | 0 | } |
412 | | |
413 | 0 | if (source.empty() && !target.empty() && !output.empty()) { |
414 | 0 | status.SetError( |
415 | 0 | "Wrong syntax. A TARGET and OUTPUT can not both be specified."); |
416 | 0 | return false; |
417 | 0 | } |
418 | 0 | if (append && output.empty()) { |
419 | 0 | status.SetError("given APPEND option with no OUTPUT."); |
420 | 0 | return false; |
421 | 0 | } |
422 | 0 | if (!implicit_depends.empty() && !depfile.empty() && |
423 | 0 | mf.GetGlobalGenerator()->GetName() != "Ninja") { |
424 | | // Makefiles generators does not support both at the same time |
425 | 0 | status.SetError("IMPLICIT_DEPENDS and DEPFILE can not both be specified."); |
426 | 0 | return false; |
427 | 0 | } |
428 | | |
429 | 0 | if (codegen) { |
430 | 0 | if (output.empty()) { |
431 | 0 | status.SetError("CODEGEN requires at least 1 OUTPUT."); |
432 | 0 | return false; |
433 | 0 | } |
434 | | |
435 | 0 | if (append) { |
436 | 0 | status.SetError("CODEGEN may not be used with APPEND."); |
437 | 0 | return false; |
438 | 0 | } |
439 | | |
440 | 0 | if (!implicit_depends.empty()) { |
441 | 0 | status.SetError("CODEGEN is not compatible with IMPLICIT_DEPENDS."); |
442 | 0 | return false; |
443 | 0 | } |
444 | | |
445 | 0 | if (mf.GetPolicyStatus(cmPolicies::CMP0171) != cmPolicies::NEW) { |
446 | 0 | status.SetError("CODEGEN option requires policy CMP0171 be set to NEW!"); |
447 | 0 | return false; |
448 | 0 | } |
449 | 0 | } |
450 | | |
451 | | // Check for an append request. |
452 | 0 | if (append) { |
453 | 0 | std::vector<std::string> unsupportedKeywordsUsed; |
454 | 0 | std::set_difference(keywordsSeen.begin(), keywordsSeen.end(), |
455 | 0 | supportedAppendKeywords.begin(), |
456 | 0 | supportedAppendKeywords.end(), |
457 | 0 | std::back_inserter(unsupportedKeywordsUsed)); |
458 | 0 | if (!unsupportedKeywordsUsed.empty()) { |
459 | 0 | std::string const msg = |
460 | 0 | cmJoin(unsupportedKeywordsUsed, ", "_s, |
461 | 0 | "The following keywords are not supported when using " |
462 | 0 | "APPEND with add_custom_command(OUTPUT): "_s); |
463 | 0 | if (cmp0175 == cmPolicies::NEW) { |
464 | 0 | mf.IssueMessage(MessageType::FATAL_ERROR, msg); |
465 | 0 | return false; |
466 | 0 | } |
467 | 0 | if (cmp0175 == cmPolicies::WARN) { |
468 | 0 | mf.IssueMessage( |
469 | 0 | MessageType::AUTHOR_WARNING, |
470 | 0 | cmStrCat(msg, ".\n", |
471 | 0 | cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); |
472 | 0 | } |
473 | 0 | } |
474 | 0 | mf.AppendCustomCommandToOutput(output[0], depends, implicit_depends, |
475 | 0 | commandLines); |
476 | 0 | return true; |
477 | 0 | } |
478 | | |
479 | 0 | if (uses_terminal && !job_pool.empty()) { |
480 | 0 | status.SetError("JOB_POOL is shadowed by USES_TERMINAL."); |
481 | 0 | return false; |
482 | 0 | } |
483 | | |
484 | | // Choose which mode of the command to use. |
485 | 0 | auto cc = cm::make_unique<cmCustomCommand>(); |
486 | 0 | cc->SetByproducts(byproducts); |
487 | 0 | cc->SetCommandLines(commandLines); |
488 | 0 | cc->SetComment(comment); |
489 | 0 | cc->SetWorkingDirectory(working.c_str()); |
490 | 0 | cc->SetEscapeOldStyle(!verbatim); |
491 | 0 | cc->SetUsesTerminal(uses_terminal); |
492 | 0 | cc->SetDepfile(depfile); |
493 | 0 | cc->SetJobPool(job_pool); |
494 | 0 | cc->SetJobserverAware(cmIsOn(job_server_aware)); |
495 | 0 | cc->SetCommandExpandLists(command_expand_lists); |
496 | 0 | cc->SetDependsExplicitOnly(depends_explicit_only); |
497 | 0 | if (source.empty() && output.empty()) { |
498 | | // Source is empty, use the target. |
499 | 0 | if (commandLines.empty()) { |
500 | 0 | std::string const msg = "At least one COMMAND must be given."; |
501 | 0 | if (cmp0175 == cmPolicies::NEW) { |
502 | 0 | mf.IssueMessage(MessageType::FATAL_ERROR, msg); |
503 | 0 | return false; |
504 | 0 | } |
505 | 0 | if (cmp0175 == cmPolicies::WARN) { |
506 | 0 | mf.IssueMessage( |
507 | 0 | MessageType::AUTHOR_WARNING, |
508 | 0 | cmStrCat(msg, '\n', |
509 | 0 | cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); |
510 | 0 | } |
511 | 0 | } |
512 | | |
513 | 0 | std::vector<std::string> unsupportedKeywordsUsed; |
514 | 0 | std::set_difference(keywordsSeen.begin(), keywordsSeen.end(), |
515 | 0 | supportedTargetKeywords.begin(), |
516 | 0 | supportedTargetKeywords.end(), |
517 | 0 | std::back_inserter(unsupportedKeywordsUsed)); |
518 | 0 | if (!unsupportedKeywordsUsed.empty()) { |
519 | 0 | std::string const msg = |
520 | 0 | cmJoin(unsupportedKeywordsUsed, ", "_s, |
521 | 0 | "The following keywords are not supported when using " |
522 | 0 | "add_custom_command(TARGET): "_s); |
523 | 0 | if (cmp0175 == cmPolicies::NEW) { |
524 | 0 | mf.IssueMessage(MessageType::FATAL_ERROR, msg); |
525 | 0 | return false; |
526 | 0 | } |
527 | 0 | if (cmp0175 == cmPolicies::WARN) { |
528 | 0 | mf.IssueMessage( |
529 | 0 | MessageType::AUTHOR_WARNING, |
530 | 0 | cmStrCat(msg, ".\n", |
531 | 0 | cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); |
532 | 0 | } |
533 | 0 | } |
534 | 0 | auto const prePostCount = keywordsSeen.count(keyPRE_BUILD) + |
535 | 0 | keywordsSeen.count(keyPRE_LINK) + keywordsSeen.count(keyPOST_BUILD); |
536 | 0 | if (prePostCount != 1) { |
537 | 0 | std::string msg = |
538 | 0 | "Exactly one of PRE_BUILD, PRE_LINK, or POST_BUILD must be given."; |
539 | 0 | if (cmp0175 == cmPolicies::NEW) { |
540 | 0 | mf.IssueMessage(MessageType::FATAL_ERROR, msg); |
541 | 0 | return false; |
542 | 0 | } |
543 | 0 | if (cmp0175 == cmPolicies::WARN) { |
544 | 0 | msg += " Assuming "; |
545 | 0 | switch (cctype) { |
546 | 0 | case cmCustomCommandType::PRE_BUILD: |
547 | 0 | msg += "PRE_BUILD"; |
548 | 0 | break; |
549 | 0 | case cmCustomCommandType::PRE_LINK: |
550 | 0 | msg += "PRE_LINK"; |
551 | 0 | break; |
552 | 0 | case cmCustomCommandType::POST_BUILD: |
553 | 0 | msg += "POST_BUILD"; |
554 | 0 | } |
555 | 0 | mf.IssueMessage( |
556 | 0 | MessageType::AUTHOR_WARNING, |
557 | 0 | cmStrCat(msg, " to preserve backward compatibility.\n", |
558 | 0 | cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); |
559 | 0 | } |
560 | 0 | } |
561 | 0 | mf.AddCustomCommandToTarget(target, cctype, std::move(cc)); |
562 | 0 | } else if (target.empty()) { |
563 | | // Target is empty, use the output. |
564 | 0 | std::vector<std::string> unsupportedKeywordsUsed; |
565 | 0 | std::set_difference(keywordsSeen.begin(), keywordsSeen.end(), |
566 | 0 | supportedOutputKeywords.begin(), |
567 | 0 | supportedOutputKeywords.end(), |
568 | 0 | std::back_inserter(unsupportedKeywordsUsed)); |
569 | 0 | if (!unsupportedKeywordsUsed.empty()) { |
570 | 0 | std::string const msg = |
571 | 0 | cmJoin(unsupportedKeywordsUsed, ", "_s, |
572 | 0 | "The following keywords are not supported when using " |
573 | 0 | "add_custom_command(OUTPUT): "_s); |
574 | 0 | if (cmp0175 == cmPolicies::NEW) { |
575 | 0 | mf.IssueMessage(MessageType::FATAL_ERROR, msg); |
576 | 0 | return false; |
577 | 0 | } |
578 | 0 | if (cmp0175 == cmPolicies::WARN) { |
579 | 0 | mf.IssueMessage( |
580 | 0 | MessageType::AUTHOR_WARNING, |
581 | 0 | cmStrCat(msg, ".\n", |
582 | 0 | cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); |
583 | 0 | } |
584 | 0 | } |
585 | 0 | cc->SetOutputs(output); |
586 | 0 | cc->SetMainDependency(main_dependency); |
587 | 0 | cc->SetDepends(depends); |
588 | 0 | cc->SetCodegen(codegen); |
589 | 0 | cc->SetImplicitDepends(implicit_depends); |
590 | 0 | mf.AddCustomCommandToOutput(std::move(cc)); |
591 | 0 | } else { |
592 | 0 | mf.IssueMessage( |
593 | 0 | MessageType::FATAL_ERROR, |
594 | 0 | "The SOURCE signatures of add_custom_command are no longer supported."); |
595 | 0 | return false; |
596 | 0 | } |
597 | | |
598 | 0 | return true; |
599 | 0 | } |