resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmAddCustomTargetCommand.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 "cmAddCustomTargetCommand.h"
4
5 #include <utility>
6
7 #include <cm/memory>
8
9 #include "cmCustomCommand.h"
10 #include "cmCustomCommandLines.h"
11 #include "cmExecutionStatus.h"
12 #include "cmGeneratorExpression.h"
13 #include "cmGlobalGenerator.h"
14 #include "cmMakefile.h"
15 #include "cmMessageType.h"
16 #include "cmStateTypes.h"
17 #include "cmStringAlgorithms.h"
18 #include "cmSystemTools.h"
19 #include "cmTarget.h"
20
21 bool cmAddCustomTargetCommand(std::vector<std::string> const& args,
22                               cmExecutionStatus& status)
23 {
24   if (args.empty()) {
25     status.SetError("called with incorrect number of arguments");
26     return false;
27   }
28
29   cmMakefile& mf = status.GetMakefile();
30   std::string const& targetName = args[0];
31
32   // Check the target name.
33   if (targetName.find_first_of("/\\") != std::string::npos) {
34     status.SetError(cmStrCat("called with invalid target name \"", targetName,
35                              "\".  Target names may not contain a slash.  "
36                              "Use ADD_CUSTOM_COMMAND to generate files."));
37     return false;
38   }
39
40   // Accumulate one command line at a time.
41   cmCustomCommandLine currentLine;
42
43   // Save all command lines.
44   cmCustomCommandLines commandLines;
45
46   // Accumulate dependencies.
47   std::vector<std::string> depends;
48   std::vector<std::string> byproducts;
49   std::string working_directory;
50   bool verbatim = false;
51   bool uses_terminal = false;
52   bool command_expand_lists = false;
53   std::string comment_buffer;
54   const char* comment = nullptr;
55   std::vector<std::string> sources;
56   std::string job_pool;
57
58   // Keep track of parser state.
59   enum tdoing
60   {
61     doing_command,
62     doing_depends,
63     doing_byproducts,
64     doing_working_directory,
65     doing_comment,
66     doing_source,
67     doing_job_pool,
68     doing_nothing
69   };
70   tdoing doing = doing_command;
71
72   // Look for the ALL option.
73   bool excludeFromAll = true;
74   unsigned int start = 1;
75   if (args.size() > 1) {
76     if (args[1] == "ALL") {
77       excludeFromAll = false;
78       start = 2;
79     }
80   }
81
82   // Parse the rest of the arguments.
83   for (unsigned int j = start; j < args.size(); ++j) {
84     std::string const& copy = args[j];
85
86     if (copy == "DEPENDS") {
87       doing = doing_depends;
88     } else if (copy == "BYPRODUCTS") {
89       doing = doing_byproducts;
90     } else if (copy == "WORKING_DIRECTORY") {
91       doing = doing_working_directory;
92     } else if (copy == "VERBATIM") {
93       doing = doing_nothing;
94       verbatim = true;
95     } else if (copy == "USES_TERMINAL") {
96       doing = doing_nothing;
97       uses_terminal = true;
98     } else if (copy == "COMMAND_EXPAND_LISTS") {
99       doing = doing_nothing;
100       command_expand_lists = true;
101     } else if (copy == "COMMENT") {
102       doing = doing_comment;
103     } else if (copy == "JOB_POOL") {
104       doing = doing_job_pool;
105     } else if (copy == "COMMAND") {
106       doing = doing_command;
107
108       // Save the current command before starting the next command.
109       if (!currentLine.empty()) {
110         commandLines.push_back(currentLine);
111         currentLine.clear();
112       }
113     } else if (copy == "SOURCES") {
114       doing = doing_source;
115     } else {
116       switch (doing) {
117         case doing_working_directory:
118           working_directory = copy;
119           break;
120         case doing_command:
121           currentLine.push_back(copy);
122           break;
123         case doing_byproducts: {
124           std::string filename;
125           if (!cmSystemTools::FileIsFullPath(copy) &&
126               cmGeneratorExpression::Find(copy) != 0) {
127             filename = cmStrCat(mf.GetCurrentBinaryDirectory(), '/');
128           }
129           filename += copy;
130           cmSystemTools::ConvertToUnixSlashes(filename);
131           if (cmSystemTools::FileIsFullPath(filename)) {
132             filename = cmSystemTools::CollapseFullPath(filename);
133           }
134           byproducts.push_back(filename);
135         } break;
136         case doing_depends: {
137           std::string dep = copy;
138           cmSystemTools::ConvertToUnixSlashes(dep);
139           depends.push_back(std::move(dep));
140         } break;
141         case doing_comment:
142           comment_buffer = copy;
143           comment = comment_buffer.c_str();
144           break;
145         case doing_source:
146           sources.push_back(copy);
147           break;
148         case doing_job_pool:
149           job_pool = copy;
150           break;
151         default:
152           status.SetError("Wrong syntax. Unknown type of argument.");
153           return false;
154       }
155     }
156   }
157
158   std::string::size_type pos = targetName.find_first_of("#<>");
159   if (pos != std::string::npos) {
160     status.SetError(cmStrCat("called with target name containing a \"",
161                              targetName[pos],
162                              "\".  This character is not allowed."));
163     return false;
164   }
165
166   // Some requirements on custom target names already exist
167   // and have been checked at this point.
168   // The following restrictions overlap but depend on policy CMP0037.
169   bool nameOk = cmGeneratorExpression::IsValidTargetName(targetName) &&
170     !cmGlobalGenerator::IsReservedTarget(targetName);
171   if (nameOk) {
172     nameOk = targetName.find(':') == std::string::npos;
173   }
174   if (!nameOk && !mf.CheckCMP0037(targetName, cmStateEnums::UTILITY)) {
175     return false;
176   }
177
178   // Store the last command line finished.
179   if (!currentLine.empty()) {
180     commandLines.push_back(currentLine);
181     currentLine.clear();
182   }
183
184   // Enforce name uniqueness.
185   {
186     std::string msg;
187     if (!mf.EnforceUniqueName(targetName, msg, true)) {
188       status.SetError(msg);
189       return false;
190     }
191   }
192
193   if (commandLines.empty() && !byproducts.empty()) {
194     mf.IssueMessage(MessageType::FATAL_ERROR,
195                     "BYPRODUCTS may not be specified without any COMMAND");
196     return true;
197   }
198   if (commandLines.empty() && uses_terminal) {
199     mf.IssueMessage(MessageType::FATAL_ERROR,
200                     "USES_TERMINAL may not be specified without any COMMAND");
201     return true;
202   }
203   if (commandLines.empty() && command_expand_lists) {
204     mf.IssueMessage(
205       MessageType::FATAL_ERROR,
206       "COMMAND_EXPAND_LISTS may not be specified without any COMMAND");
207     return true;
208   }
209
210   if (uses_terminal && !job_pool.empty()) {
211     status.SetError("JOB_POOL is shadowed by USES_TERMINAL.");
212     return false;
213   }
214
215   // Add the utility target to the makefile.
216   auto cc = cm::make_unique<cmCustomCommand>();
217   cc->SetWorkingDirectory(working_directory.c_str());
218   cc->SetByproducts(byproducts);
219   cc->SetDepends(depends);
220   cc->SetCommandLines(commandLines);
221   cc->SetEscapeOldStyle(!verbatim);
222   cc->SetComment(comment);
223   cc->SetUsesTerminal(uses_terminal);
224   cc->SetCommandExpandLists(command_expand_lists);
225   cc->SetJobPool(job_pool);
226   cmTarget* target =
227     mf.AddUtilityCommand(targetName, excludeFromAll, std::move(cc));
228
229   // Add additional user-specified source files to the target.
230   target->AddSources(sources);
231
232   return true;
233 }