packaging: Initial packaging
[platform/upstream/cmake.git] / Source / cmAddCustomCommandCommand.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
8   This software is distributed WITHOUT ANY WARRANTY; without even the
9   implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10   See the License for more information.
11 ============================================================================*/
12 #include "cmAddCustomCommandCommand.h"
13
14 #include "cmTarget.h"
15
16 #include "cmSourceFile.h"
17
18 // cmAddCustomCommandCommand
19 bool cmAddCustomCommandCommand
20 ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
21 {
22   /* Let's complain at the end of this function about the lack of a particular
23      arg. For the moment, let's say that COMMAND, and either TARGET or SOURCE
24      are required.
25   */
26   if (args.size() < 4)
27     {
28       this->SetError("called with wrong number of arguments.");
29       return false;
30     }
31
32   std::string source, target, main_dependency, working;
33   std::string comment_buffer;
34   const char* comment = 0;
35   std::vector<std::string> depends, outputs, output;
36   bool verbatim = false;
37   bool append = false;
38   std::string implicit_depends_lang;
39   cmCustomCommand::ImplicitDependsList implicit_depends;
40
41   // Accumulate one command line at a time.
42   cmCustomCommandLine currentLine;
43
44   // Save all command lines.
45   cmCustomCommandLines commandLines;
46
47   cmTarget::CustomCommandType cctype = cmTarget::POST_BUILD;
48
49   enum tdoing {
50     doing_source,
51     doing_command,
52     doing_target,
53     doing_depends,
54     doing_implicit_depends_lang,
55     doing_implicit_depends_file,
56     doing_main_dependency,
57     doing_output,
58     doing_outputs,
59     doing_comment,
60     doing_working_directory,
61     doing_nothing
62   };
63
64   tdoing doing = doing_nothing;
65
66   for (unsigned int j = 0; j < args.size(); ++j)
67     {
68     std::string const& copy = args[j];
69
70     if(copy == "SOURCE")
71       {
72       doing = doing_source;
73       }
74     else if(copy == "COMMAND")
75       {
76       doing = doing_command;
77
78       // Save the current command before starting the next command.
79       if(!currentLine.empty())
80         {
81         commandLines.push_back(currentLine);
82         currentLine.clear();
83         }
84       }
85     else if(copy == "PRE_BUILD")
86       {
87       cctype = cmTarget::PRE_BUILD;
88       }
89     else if(copy == "PRE_LINK")
90       {
91       cctype = cmTarget::PRE_LINK;
92       }
93     else if(copy == "POST_BUILD")
94       {
95       cctype = cmTarget::POST_BUILD;
96       }
97     else if(copy == "VERBATIM")
98       {
99       verbatim = true;
100       }
101     else if(copy == "APPEND")
102       {
103       append = true;
104       }
105     else if(copy == "TARGET")
106       {
107       doing = doing_target;
108       }
109     else if(copy == "ARGS")
110       {
111       // Ignore this old keyword.
112       }
113     else if (copy == "DEPENDS")
114       {
115       doing = doing_depends;
116       }
117     else if (copy == "OUTPUTS")
118       {
119       doing = doing_outputs;
120       }
121     else if (copy == "OUTPUT")
122       {
123       doing = doing_output;
124       }
125     else if (copy == "WORKING_DIRECTORY")
126       {
127       doing = doing_working_directory;
128       }
129     else if (copy == "MAIN_DEPENDENCY")
130       {
131       doing = doing_main_dependency;
132       }
133     else if (copy == "IMPLICIT_DEPENDS")
134       {
135       doing = doing_implicit_depends_lang;
136       }
137     else if (copy == "COMMENT")
138       {
139       doing = doing_comment;
140       }
141     else
142       {
143       std::string filename;
144       switch (doing)
145         {
146         case doing_output:
147         case doing_outputs:
148           if (!cmSystemTools::FileIsFullPath(copy.c_str()))
149             {
150             // This is an output to be generated, so it should be
151             // under the build tree.  CMake 2.4 placed this under the
152             // source tree.  However the only case that this change
153             // will break is when someone writes
154             //
155             //   add_custom_command(OUTPUT out.txt ...)
156             //
157             // and later references "${CMAKE_CURRENT_SOURCE_DIR}/out.txt".
158             // This is fairly obscure so we can wait for someone to
159             // complain.
160             filename = this->Makefile->GetCurrentOutputDirectory();
161             filename += "/";
162             }
163           filename += copy;
164           cmSystemTools::ConvertToUnixSlashes(filename);
165           break;
166         case doing_source:
167           // We do not want to convert the argument to SOURCE because
168           // that option is only available for backward compatibility.
169           // Old-style use of this command may use the SOURCE==TARGET
170           // trick which we must preserve.  If we convert the source
171           // to a full path then it will no longer equal the target.
172         default:
173           break;
174         }
175
176        switch (doing)
177          {
178          case doing_working_directory:
179            working = copy;
180            break;
181          case doing_source:
182            source = copy;
183            break;
184          case doing_output:
185            output.push_back(filename);
186            break;
187          case doing_main_dependency:
188            main_dependency = copy;
189            break;
190          case doing_implicit_depends_lang:
191            implicit_depends_lang = copy;
192            doing = doing_implicit_depends_file;
193            break;
194          case doing_implicit_depends_file:
195            {
196            // An implicit dependency starting point is also an
197            // explicit dependency.
198            std::string dep = copy;
199            cmSystemTools::ConvertToUnixSlashes(dep);
200            depends.push_back(dep);
201
202            // Add the implicit dependency language and file.
203            cmCustomCommand::ImplicitDependsPair
204              entry(implicit_depends_lang, dep);
205            implicit_depends.push_back(entry);
206
207            // Switch back to looking for a language.
208            doing = doing_implicit_depends_lang;
209            }
210            break;
211          case doing_command:
212            currentLine.push_back(copy);
213            break;
214          case doing_target:
215            target = copy;
216            break;
217          case doing_depends:
218            {
219            std::string dep = copy;
220            cmSystemTools::ConvertToUnixSlashes(dep);
221            depends.push_back(dep);
222            }
223            break;
224          case doing_outputs:
225            outputs.push_back(filename);
226            break;
227          case doing_comment:
228            comment_buffer = copy;
229            comment = comment_buffer.c_str();
230            break;
231          default:
232            this->SetError("Wrong syntax. Unknown type of argument.");
233            return false;
234          }
235       }
236     }
237
238   // Store the last command line finished.
239   if(!currentLine.empty())
240     {
241     commandLines.push_back(currentLine);
242     currentLine.clear();
243     }
244
245   // At this point we could complain about the lack of arguments.  For
246   // the moment, let's say that COMMAND, TARGET are always required.
247   if(output.empty() && target.empty())
248     {
249     this->SetError("Wrong syntax. A TARGET or OUTPUT must be specified.");
250     return false;
251     }
252
253   if(source.empty() && !target.empty() && !output.empty())
254     {
255     this->SetError(
256       "Wrong syntax. A TARGET and OUTPUT can not both be specified.");
257     return false;
258     }
259   if(append && output.empty())
260     {
261     this->SetError("given APPEND option with no OUTPUT.");
262     return false;
263     }
264
265   // Make sure the output names and locations are safe.
266   if(!this->CheckOutputs(output) || !this->CheckOutputs(outputs))
267     {
268     return false;
269     }
270
271   // Check for an append request.
272   if(append)
273     {
274     // Lookup an existing command.
275     if(cmSourceFile* sf =
276        this->Makefile->GetSourceFileWithOutput(output[0].c_str()))
277       {
278       if(cmCustomCommand* cc = sf->GetCustomCommand())
279         {
280         cc->AppendCommands(commandLines);
281         cc->AppendDepends(depends);
282         cc->AppendImplicitDepends(implicit_depends);
283         return true;
284         }
285       }
286
287     // No command for this output exists.
288     cmOStringStream e;
289     e << "given APPEND option with output \"" << output[0].c_str()
290       << "\" which is not already a custom command output.";
291     this->SetError(e.str().c_str());
292     return false;
293     }
294
295   // Convert working directory to a full path.
296   if(!working.empty())
297     {
298     const char* build_dir = this->Makefile->GetCurrentOutputDirectory();
299     working = cmSystemTools::CollapseFullPath(working.c_str(), build_dir);
300     }
301
302   // Choose which mode of the command to use.
303   bool escapeOldStyle = !verbatim;
304   if(source.empty() && output.empty())
305     {
306     // Source is empty, use the target.
307     std::vector<std::string> no_depends;
308     this->Makefile->AddCustomCommandToTarget(target.c_str(), no_depends,
309                                              commandLines, cctype,
310                                              comment, working.c_str(),
311                                              escapeOldStyle);
312     }
313   else if(target.empty())
314     {
315     // Target is empty, use the output.
316     this->Makefile->AddCustomCommandToOutput(output, depends,
317                                              main_dependency.c_str(),
318                                              commandLines, comment,
319                                              working.c_str(), false,
320                                              escapeOldStyle);
321
322     // Add implicit dependency scanning requests if any were given.
323     if(!implicit_depends.empty())
324       {
325       bool okay = false;
326       if(cmSourceFile* sf =
327          this->Makefile->GetSourceFileWithOutput(output[0].c_str()))
328         {
329         if(cmCustomCommand* cc = sf->GetCustomCommand())
330           {
331           okay = true;
332           cc->SetImplicitDepends(implicit_depends);
333           }
334         }
335       if(!okay)
336         {
337         cmOStringStream e;
338         e << "could not locate source file with a custom command producing \""
339           << output[0] << "\" even though this command tried to create it!";
340         this->SetError(e.str().c_str());
341         return false;
342         }
343       }
344     }
345   else
346     {
347     // Use the old-style mode for backward compatibility.
348     this->Makefile->AddCustomCommandOldStyle(target.c_str(), outputs, depends,
349                                              source.c_str(), commandLines,
350                                              comment);
351     }
352
353   return true;
354 }
355
356 //----------------------------------------------------------------------------
357 bool
358 cmAddCustomCommandCommand
359 ::CheckOutputs(const std::vector<std::string>& outputs)
360 {
361   for(std::vector<std::string>::const_iterator o = outputs.begin();
362       o != outputs.end(); ++o)
363     {
364     // Make sure the file will not be generated into the source
365     // directory during an out of source build.
366     if(!this->Makefile->CanIWriteThisFile(o->c_str()))
367       {
368       std::string e = "attempted to have a file \"" + *o +
369         "\" in a source directory as an output of custom command.";
370       this->SetError(e.c_str());
371       cmSystemTools::SetFatalErrorOccured();
372       return false;
373       }
374
375     // Make sure the output file name has no invalid characters.
376     std::string::size_type pos = o->find_first_of("#<>");
377     if(pos != o->npos)
378       {
379       cmOStringStream msg;
380       msg << "called with OUTPUT containing a \"" << (*o)[pos]
381           << "\".  This character is not allowed.";
382       this->SetError(msg.str().c_str());
383       return false;
384       }
385     }
386   return true;
387 }