resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmAddCustomCommandCommand.cxx
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"
4
5 #include <sstream>
6 #include <unordered_set>
7 #include <utility>
8
9 #include <cm/memory>
10
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"
22
23 bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
24                                cmExecutionStatus& status)
25 {
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
28      are required.
29   */
30   if (args.size() < 4) {
31     status.SetError("called with wrong number of arguments.");
32     return false;
33   }
34
35   cmMakefile& mf = status.GetMakefile();
36   std::string source;
37   std::string target;
38   std::string main_dependency;
39   std::string working;
40   std::string depfile;
41   std::string job_pool;
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;
49   bool append = false;
50   bool uses_terminal = false;
51   bool command_expand_lists = false;
52   std::string implicit_depends_lang;
53   cmImplicitDependsList implicit_depends;
54
55   // Accumulate one command line at a time.
56   cmCustomCommandLine currentLine;
57
58   // Save all command lines.
59   cmCustomCommandLines commandLines;
60
61   cmCustomCommandType cctype = cmCustomCommandType::POST_BUILD;
62
63   enum tdoing
64   {
65     doing_source,
66     doing_command,
67     doing_target,
68     doing_depends,
69     doing_implicit_depends_lang,
70     doing_implicit_depends_file,
71     doing_main_dependency,
72     doing_output,
73     doing_outputs,
74     doing_byproducts,
75     doing_comment,
76     doing_working_directory,
77     doing_depfile,
78     doing_job_pool,
79     doing_nothing
80   };
81
82   tdoing doing = doing_nothing;
83
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{
109     keyAPPEND,
110     keyARGS,
111     keyBYPRODUCTS,
112     keyCOMMAND,
113     keyCOMMAND_EXPAND_LISTS,
114     keyCOMMENT,
115     keyDEPENDS,
116     keyDEPFILE,
117     keyIMPLICIT_DEPENDS,
118     keyJOB_POOL,
119     keyMAIN_DEPENDENCY,
120     keyOUTPUT,
121     keyOUTPUTS,
122     keyPOST_BUILD,
123     keyPRE_BUILD,
124     keyPRE_LINK,
125     keySOURCE,
126     keyTARGET,
127     keyUSES_TERMINAL,
128     keyVERBATIM,
129     keyWORKING_DIRECTORY
130   };
131
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;
138
139         // Save the current command before starting the next command.
140         if (!currentLine.empty()) {
141           commandLines.push_back(currentLine);
142           currentLine.clear();
143         }
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) {
151         verbatim = true;
152       } else if (copy == keyAPPEND) {
153         append = true;
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());
183           return false;
184         }
185       } else if (copy == keyJOB_POOL) {
186         doing = doing_job_pool;
187       }
188     } else {
189       std::string filename;
190       switch (doing) {
191         case doing_output:
192         case doing_outputs:
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(), '/');
199           }
200           filename += copy;
201           cmSystemTools::ConvertToUnixSlashes(filename);
202           break;
203         case doing_source:
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.
209         default:
210           break;
211       }
212
213       if (cmSystemTools::FileIsFullPath(filename)) {
214         filename = cmSystemTools::CollapseFullPath(filename);
215       }
216       switch (doing) {
217         case doing_depfile:
218           depfile = copy;
219           break;
220         case doing_job_pool:
221           job_pool = copy;
222           break;
223         case doing_working_directory:
224           working = copy;
225           break;
226         case doing_source:
227           source = copy;
228           break;
229         case doing_output:
230           output.push_back(filename);
231           break;
232         case doing_main_dependency:
233           main_dependency = copy;
234           break;
235         case doing_implicit_depends_lang:
236           implicit_depends_lang = copy;
237           doing = doing_implicit_depends_file;
238           break;
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);
247
248           // Add the implicit dependency language and file.
249           implicit_depends.emplace_back(implicit_depends_lang, dep);
250
251           // Switch back to looking for a language.
252           doing = doing_implicit_depends_lang;
253         } break;
254         case doing_command:
255           currentLine.push_back(copy);
256           break;
257         case doing_target:
258           target = copy;
259           break;
260         case doing_depends:
261           depends.push_back(copy);
262           break;
263         case doing_outputs:
264           outputs.push_back(filename);
265           break;
266         case doing_byproducts:
267           byproducts.push_back(filename);
268           break;
269         case doing_comment:
270           comment_buffer = copy;
271           comment = comment_buffer.c_str();
272           break;
273         default:
274           status.SetError("Wrong syntax. Unknown type of argument.");
275           return false;
276       }
277     }
278   }
279
280   // Store the last command line finished.
281   if (!currentLine.empty()) {
282     commandLines.push_back(currentLine);
283     currentLine.clear();
284   }
285
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.");
290     return false;
291   }
292
293   if (source.empty() && !target.empty() && !output.empty()) {
294     status.SetError(
295       "Wrong syntax. A TARGET and OUTPUT can not both be specified.");
296     return false;
297   }
298   if (append && output.empty()) {
299     status.SetError("given APPEND option with no OUTPUT.");
300     return false;
301   }
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.");
306     return false;
307   }
308
309   // Check for an append request.
310   if (append) {
311     mf.AppendCustomCommandToOutput(output[0], depends, implicit_depends,
312                                    commandLines);
313     return true;
314   }
315
316   if (uses_terminal && !job_pool.empty()) {
317     status.SetError("JOB_POOL is shadowed by USES_TERMINAL.");
318     return false;
319   }
320
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");
344     return false;
345   } else if (uses_terminal) {
346     status.SetError("USES_TERMINAL may not be used with SOURCE signatures");
347     return false;
348   } else {
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";
355         break;
356       case cmPolicies::OLD:
357         issueMessage = false;
358         break;
359       case cmPolicies::REQUIRED_ALWAYS:
360       case cmPolicies::REQUIRED_IF_USED:
361       case cmPolicies::NEW:
362         messageType = MessageType::FATAL_ERROR;
363         break;
364     }
365
366     if (issueMessage) {
367       e << "The SOURCE signatures of add_custom_command are no longer "
368            "supported.";
369       mf.IssueMessage(messageType, e.str());
370       if (messageType == MessageType::FATAL_ERROR) {
371         return false;
372       }
373     }
374
375     // Use the old-style mode for backward compatibility.
376     mf.AddCustomCommandOldStyle(target, outputs, depends, source, commandLines,
377                                 comment);
378   }
379
380   return true;
381 }