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 "cmAddCustomCommandCommand.h"
6 #include <unordered_set>
11 #include "cmCustomCommand.h"
12 #include "cmCustomCommandLines.h"
13 #include "cmCustomCommandTypes.h"
14 #include "cmExecutionStatus.h"
15 #include "cmGeneratorExpression.h"
16 #include "cmGlobalGenerator.h"
17 #include "cmMakefile.h"
18 #include "cmMessageType.h"
19 #include "cmPolicies.h"
20 #include "cmStringAlgorithms.h"
21 #include "cmSystemTools.h"
23 bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
24 cmExecutionStatus& status)
26 /* Let's complain at the end of this function about the lack of a particular
27 arg. For the moment, let's say that COMMAND, and either TARGET or SOURCE
30 if (args.size() < 4) {
31 status.SetError("called with wrong number of arguments.");
35 cmMakefile& mf = status.GetMakefile();
38 std::string main_dependency;
42 std::string comment_buffer;
43 const char* comment = nullptr;
44 std::vector<std::string> depends;
45 std::vector<std::string> outputs;
46 std::vector<std::string> output;
47 std::vector<std::string> byproducts;
48 bool verbatim = false;
50 bool uses_terminal = false;
51 bool command_expand_lists = false;
52 std::string implicit_depends_lang;
53 cmImplicitDependsList implicit_depends;
55 // Accumulate one command line at a time.
56 cmCustomCommandLine currentLine;
58 // Save all command lines.
59 cmCustomCommandLines commandLines;
61 cmCustomCommandType cctype = cmCustomCommandType::POST_BUILD;
69 doing_implicit_depends_lang,
70 doing_implicit_depends_file,
71 doing_main_dependency,
76 doing_working_directory,
82 tdoing doing = doing_nothing;
84 #define MAKE_STATIC_KEYWORD(KEYWORD) \
85 static const std::string key##KEYWORD = #KEYWORD
86 MAKE_STATIC_KEYWORD(APPEND);
87 MAKE_STATIC_KEYWORD(ARGS);
88 MAKE_STATIC_KEYWORD(BYPRODUCTS);
89 MAKE_STATIC_KEYWORD(COMMAND);
90 MAKE_STATIC_KEYWORD(COMMAND_EXPAND_LISTS);
91 MAKE_STATIC_KEYWORD(COMMENT);
92 MAKE_STATIC_KEYWORD(DEPENDS);
93 MAKE_STATIC_KEYWORD(DEPFILE);
94 MAKE_STATIC_KEYWORD(IMPLICIT_DEPENDS);
95 MAKE_STATIC_KEYWORD(JOB_POOL);
96 MAKE_STATIC_KEYWORD(MAIN_DEPENDENCY);
97 MAKE_STATIC_KEYWORD(OUTPUT);
98 MAKE_STATIC_KEYWORD(OUTPUTS);
99 MAKE_STATIC_KEYWORD(POST_BUILD);
100 MAKE_STATIC_KEYWORD(PRE_BUILD);
101 MAKE_STATIC_KEYWORD(PRE_LINK);
102 MAKE_STATIC_KEYWORD(SOURCE);
103 MAKE_STATIC_KEYWORD(TARGET);
104 MAKE_STATIC_KEYWORD(USES_TERMINAL);
105 MAKE_STATIC_KEYWORD(VERBATIM);
106 MAKE_STATIC_KEYWORD(WORKING_DIRECTORY);
107 #undef MAKE_STATIC_KEYWORD
108 static std::unordered_set<std::string> const keywords{
113 keyCOMMAND_EXPAND_LISTS,
132 for (std::string const& copy : args) {
133 if (keywords.count(copy)) {
134 if (copy == keySOURCE) {
135 doing = doing_source;
136 } else if (copy == keyCOMMAND) {
137 doing = doing_command;
139 // Save the current command before starting the next command.
140 if (!currentLine.empty()) {
141 commandLines.push_back(currentLine);
144 } else if (copy == keyPRE_BUILD) {
145 cctype = cmCustomCommandType::PRE_BUILD;
146 } else if (copy == keyPRE_LINK) {
147 cctype = cmCustomCommandType::PRE_LINK;
148 } else if (copy == keyPOST_BUILD) {
149 cctype = cmCustomCommandType::POST_BUILD;
150 } else if (copy == keyVERBATIM) {
152 } else if (copy == keyAPPEND) {
154 } else if (copy == keyUSES_TERMINAL) {
155 uses_terminal = true;
156 } else if (copy == keyCOMMAND_EXPAND_LISTS) {
157 command_expand_lists = true;
158 } else if (copy == keyTARGET) {
159 doing = doing_target;
160 } else if (copy == keyARGS) {
161 // Ignore this old keyword.
162 } else if (copy == keyDEPENDS) {
163 doing = doing_depends;
164 } else if (copy == keyOUTPUTS) {
165 doing = doing_outputs;
166 } else if (copy == keyOUTPUT) {
167 doing = doing_output;
168 } else if (copy == keyBYPRODUCTS) {
169 doing = doing_byproducts;
170 } else if (copy == keyWORKING_DIRECTORY) {
171 doing = doing_working_directory;
172 } else if (copy == keyMAIN_DEPENDENCY) {
173 doing = doing_main_dependency;
174 } else if (copy == keyIMPLICIT_DEPENDS) {
175 doing = doing_implicit_depends_lang;
176 } else if (copy == keyCOMMENT) {
177 doing = doing_comment;
178 } else if (copy == keyDEPFILE) {
179 doing = doing_depfile;
180 if (!mf.GetGlobalGenerator()->SupportsCustomCommandDepfile()) {
181 status.SetError("Option DEPFILE not supported by " +
182 mf.GetGlobalGenerator()->GetName());
185 } else if (copy == keyJOB_POOL) {
186 doing = doing_job_pool;
189 std::string filename;
193 case doing_byproducts:
194 if (!cmSystemTools::FileIsFullPath(copy) &&
195 cmGeneratorExpression::Find(copy) != 0) {
196 // This is an output to be generated, so it should be
197 // under the build tree.
198 filename = cmStrCat(mf.GetCurrentBinaryDirectory(), '/');
201 cmSystemTools::ConvertToUnixSlashes(filename);
204 // We do not want to convert the argument to SOURCE because
205 // that option is only available for backward compatibility.
206 // Old-style use of this command may use the SOURCE==TARGET
207 // trick which we must preserve. If we convert the source
208 // to a full path then it will no longer equal the target.
213 if (cmSystemTools::FileIsFullPath(filename)) {
214 filename = cmSystemTools::CollapseFullPath(filename);
223 case doing_working_directory:
230 output.push_back(filename);
232 case doing_main_dependency:
233 main_dependency = copy;
235 case doing_implicit_depends_lang:
236 implicit_depends_lang = copy;
237 doing = doing_implicit_depends_file;
239 case doing_implicit_depends_file: {
240 // An implicit dependency starting point is also an
241 // explicit dependency.
242 std::string dep = copy;
243 // Upfront path conversion is correct because Genex
244 // are not supported.
245 cmSystemTools::ConvertToUnixSlashes(dep);
246 depends.push_back(dep);
248 // Add the implicit dependency language and file.
249 implicit_depends.emplace_back(implicit_depends_lang, dep);
251 // Switch back to looking for a language.
252 doing = doing_implicit_depends_lang;
255 currentLine.push_back(copy);
261 depends.push_back(copy);
264 outputs.push_back(filename);
266 case doing_byproducts:
267 byproducts.push_back(filename);
270 comment_buffer = copy;
271 comment = comment_buffer.c_str();
274 status.SetError("Wrong syntax. Unknown type of argument.");
280 // Store the last command line finished.
281 if (!currentLine.empty()) {
282 commandLines.push_back(currentLine);
286 // At this point we could complain about the lack of arguments. For
287 // the moment, let's say that COMMAND, TARGET are always required.
288 if (output.empty() && target.empty()) {
289 status.SetError("Wrong syntax. A TARGET or OUTPUT must be specified.");
293 if (source.empty() && !target.empty() && !output.empty()) {
295 "Wrong syntax. A TARGET and OUTPUT can not both be specified.");
298 if (append && output.empty()) {
299 status.SetError("given APPEND option with no OUTPUT.");
302 if (!implicit_depends.empty() && !depfile.empty() &&
303 mf.GetGlobalGenerator()->GetName() != "Ninja") {
304 // Makefiles generators does not support both at the same time
305 status.SetError("IMPLICIT_DEPENDS and DEPFILE can not both be specified.");
309 // Check for an append request.
311 mf.AppendCustomCommandToOutput(output[0], depends, implicit_depends,
316 if (uses_terminal && !job_pool.empty()) {
317 status.SetError("JOB_POOL is shadowed by USES_TERMINAL.");
321 // Choose which mode of the command to use.
322 auto cc = cm::make_unique<cmCustomCommand>();
323 cc->SetByproducts(byproducts);
324 cc->SetCommandLines(commandLines);
325 cc->SetComment(comment);
326 cc->SetWorkingDirectory(working.c_str());
327 cc->SetEscapeOldStyle(!verbatim);
328 cc->SetUsesTerminal(uses_terminal);
329 cc->SetDepfile(depfile);
330 cc->SetJobPool(job_pool);
331 cc->SetCommandExpandLists(command_expand_lists);
332 if (source.empty() && output.empty()) {
333 // Source is empty, use the target.
334 mf.AddCustomCommandToTarget(target, cctype, std::move(cc));
335 } else if (target.empty()) {
336 // Target is empty, use the output.
337 cc->SetOutputs(output);
338 cc->SetMainDependency(main_dependency);
339 cc->SetDepends(depends);
340 cc->SetImplicitDepends(implicit_depends);
341 mf.AddCustomCommandToOutput(std::move(cc));
342 } else if (!byproducts.empty()) {
343 status.SetError("BYPRODUCTS may not be specified with SOURCE signatures");
345 } else if (uses_terminal) {
346 status.SetError("USES_TERMINAL may not be used with SOURCE signatures");
349 bool issueMessage = true;
350 std::ostringstream e;
351 MessageType messageType = MessageType::AUTHOR_WARNING;
352 switch (mf.GetPolicyStatus(cmPolicies::CMP0050)) {
353 case cmPolicies::WARN:
354 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0050) << "\n";
356 case cmPolicies::OLD:
357 issueMessage = false;
359 case cmPolicies::REQUIRED_ALWAYS:
360 case cmPolicies::REQUIRED_IF_USED:
361 case cmPolicies::NEW:
362 messageType = MessageType::FATAL_ERROR;
367 e << "The SOURCE signatures of add_custom_command are no longer "
369 mf.IssueMessage(messageType, e.str());
370 if (messageType == MessageType::FATAL_ERROR) {
375 // Use the old-style mode for backward compatibility.
376 mf.AddCustomCommandOldStyle(target, outputs, depends, source, commandLines,