packaging: Initial packaging
[platform/upstream/cmake.git] / Source / cmMacroCommand.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 "cmMacroCommand.h"
13
14 #include "cmake.h"
15
16 // define the class for macro commands
17 class cmMacroHelperCommand : public cmCommand
18 {
19 public:
20   cmMacroHelperCommand() {}
21
22   ///! clean up any memory allocated by the macro
23   ~cmMacroHelperCommand() {};
24
25   /**
26    * This is used to avoid including this command
27    * in documentation. This is mainly used by
28    * cmMacroHelperCommand and cmFunctionHelperCommand
29    * which cannot provide appropriate documentation.
30    */
31   virtual bool ShouldAppearInDocumentation() const
32     {
33     return false;
34     }
35
36   /**
37    * This is a virtual constructor for the command.
38    */
39   virtual cmCommand* Clone()
40   {
41     cmMacroHelperCommand *newC = new cmMacroHelperCommand;
42     // we must copy when we clone
43     newC->Args = this->Args;
44     newC->Functions = this->Functions;
45     newC->FilePath = this->FilePath;
46     newC->Policies = this->Policies;
47     return newC;
48   }
49
50   /**
51    * This determines if the command is invoked when in script mode.
52    */
53   virtual bool IsScriptable() const { return true; }
54
55   /**
56    * This is called when the command is first encountered in
57    * the CMakeLists.txt file.
58    */
59   virtual bool InvokeInitialPass(const std::vector<cmListFileArgument>& args,
60                                  cmExecutionStatus &);
61
62   virtual bool InitialPass(std::vector<std::string> const&,
63                            cmExecutionStatus &) { return false; };
64
65   /**
66    * The name of the command as specified in CMakeList.txt.
67    */
68   virtual const char* GetName() const { return this->Args[0].c_str(); }
69
70   /**
71    * Succinct documentation.
72    */
73   virtual const char* GetTerseDocumentation() const
74   {
75     std::string docs = "Macro named: ";
76     docs += this->GetName();
77     return docs.c_str();
78   }
79
80   /**
81    * More documentation.
82    */
83   virtual const char* GetFullDocumentation() const
84   {
85     return this->GetTerseDocumentation();
86   }
87
88   cmTypeMacro(cmMacroHelperCommand, cmCommand);
89
90   std::vector<std::string> Args;
91   std::vector<cmListFileFunction> Functions;
92   cmPolicies::PolicyMap Policies;
93   std::string FilePath;
94 };
95
96
97 bool cmMacroHelperCommand::InvokeInitialPass
98 (const std::vector<cmListFileArgument>& args,
99  cmExecutionStatus &inStatus)
100 {
101   // Expand the argument list to the macro.
102   std::vector<std::string> expandedArgs;
103   this->Makefile->ExpandArguments(args, expandedArgs);
104
105   std::string tmps;
106   cmListFileArgument arg;
107   std::string variable;
108
109   // make sure the number of arguments passed is at least the number
110   // required by the signature
111   if (expandedArgs.size() < this->Args.size() - 1)
112     {
113     std::string errorMsg =
114       "Macro invoked with incorrect arguments for macro named: ";
115     errorMsg += this->Args[0];
116     this->SetError(errorMsg.c_str());
117     return false;
118     }
119
120   // Enforce matching logical blocks inside the macro.
121   cmMakefile::LexicalPushPop lexScope(this->Makefile);
122
123   // Push a weak policy scope which restores the policies recorded at
124   // macro creation.
125   cmMakefile::PolicyPushPop polScope(this->Makefile, true, this->Policies);
126
127   // set the value of argc
128   cmOStringStream argcDefStream;
129   argcDefStream << expandedArgs.size();
130   std::string argcDef = argcDefStream.str();
131
132   // declare varuiables for ARGV ARGN but do not compute until needed
133   std::string argvDef;
134   std::string argnDef;
135   bool argnDefInitialized = false;
136   bool argvDefInitialized = false;
137   if( this->Functions.size())
138     {
139     this->FilePath = this->Functions[0].FilePath;
140     }
141   // Invoke all the functions that were collected in the block.
142   cmListFileFunction newLFF;
143   // for each function
144   for(unsigned int c = 0; c < this->Functions.size(); ++c)
145     {
146     // Replace the formal arguments and then invoke the command.
147     newLFF.Arguments.clear();
148     newLFF.Arguments.reserve(this->Functions[c].Arguments.size());
149     newLFF.Name = this->Functions[c].Name;
150     newLFF.FilePath = this->Functions[c].FilePath;
151     newLFF.Line = this->Functions[c].Line;
152
153     // for each argument of the current function
154     for (std::vector<cmListFileArgument>::iterator k =
155            this->Functions[c].Arguments.begin();
156          k != this->Functions[c].Arguments.end(); ++k)
157       {
158       // Set the FilePath on the arguments to match the function since it is
159       // not stored and the original values may be freed
160       k->FilePath = this->FilePath.c_str();
161       tmps = k->Value;
162       // replace formal arguments
163       for (unsigned int j = 1; j < this->Args.size(); ++j)
164         {
165         variable = "${";
166         variable += this->Args[j];
167         variable += "}";
168         cmSystemTools::ReplaceString(tmps, variable.c_str(),
169                                      expandedArgs[j-1].c_str());
170         }
171       // replace argc
172       cmSystemTools::ReplaceString(tmps, "${ARGC}",argcDef.c_str());
173
174       // repleace ARGN
175       if (tmps.find("${ARGN}") != std::string::npos)
176         {
177         if (!argnDefInitialized)
178           {
179           std::vector<std::string>::const_iterator eit;
180           std::vector<std::string>::size_type cnt = 0;
181           for ( eit = expandedArgs.begin(); eit != expandedArgs.end(); ++eit )
182             {
183             if ( cnt >= this->Args.size()-1 )
184               {
185               if ( argnDef.size() > 0 )
186                 {
187                 argnDef += ";";
188                 }
189               argnDef += *eit;
190               }
191             cnt ++;
192             }
193           argnDefInitialized = true;
194           }
195         cmSystemTools::ReplaceString(tmps, "${ARGN}", argnDef.c_str());
196         }
197
198       // if the current argument of the current function has ${ARGV in it
199       // then try replacing ARGV values
200       if (tmps.find("${ARGV") != std::string::npos)
201         {
202         char argvName[60];
203
204         // repleace ARGV, compute it only once
205         if (!argvDefInitialized)
206           {
207           std::vector<std::string>::const_iterator eit;
208           for ( eit = expandedArgs.begin(); eit != expandedArgs.end(); ++eit )
209             {
210             if ( argvDef.size() > 0 )
211               {
212               argvDef += ";";
213               }
214             argvDef += *eit;
215             }
216           argvDefInitialized = true;
217           }
218         cmSystemTools::ReplaceString(tmps, "${ARGV}", argvDef.c_str());
219
220         // also replace the ARGV1 ARGV2 ... etc
221         for (unsigned int t = 0; t < expandedArgs.size(); ++t)
222           {
223           sprintf(argvName,"${ARGV%i}",t);
224           cmSystemTools::ReplaceString(tmps, argvName,
225                                        expandedArgs[t].c_str());
226           }
227         }
228
229       arg.Value = tmps;
230       arg.Delim = k->Delim;
231       arg.FilePath = k->FilePath;
232       arg.Line = k->Line;
233       newLFF.Arguments.push_back(arg);
234       }
235     cmExecutionStatus status;
236     if(!this->Makefile->ExecuteCommand(newLFF, status) ||
237        status.GetNestedError())
238       {
239       // The error message should have already included the call stack
240       // so we do not need to report an error here.
241       lexScope.Quiet();
242       polScope.Quiet();
243       inStatus.SetNestedError(true);
244       return false;
245       }
246     if (status.GetReturnInvoked())
247       {
248       inStatus.SetReturnInvoked(true);
249       return true;
250       }
251     if (status.GetBreakInvoked())
252       {
253       inStatus.SetBreakInvoked(true);
254       return true;
255       }
256     }
257   return true;
258 }
259
260 bool cmMacroFunctionBlocker::
261 IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf,
262                   cmExecutionStatus &)
263 {
264   // record commands until we hit the ENDMACRO
265   // at the ENDMACRO call we shift gears and start looking for invocations
266   if(!cmSystemTools::Strucmp(lff.Name.c_str(),"macro"))
267     {
268     this->Depth++;
269     }
270   else if(!cmSystemTools::Strucmp(lff.Name.c_str(),"endmacro"))
271     {
272     // if this is the endmacro for this macro then execute
273     if (!this->Depth)
274       {
275       std::string name = this->Args[0];
276       std::vector<std::string>::size_type cc;
277       name += "(";
278       for ( cc = 0; cc < this->Args.size(); cc ++ )
279         {
280         name += " " + this->Args[cc];
281         }
282       name += " )";
283       mf.AddMacro(this->Args[0].c_str(), name.c_str());
284       // create a new command and add it to cmake
285       cmMacroHelperCommand *f = new cmMacroHelperCommand();
286       f->Args = this->Args;
287       f->Functions = this->Functions;
288       mf.RecordPolicies(f->Policies);
289       std::string newName = "_" + this->Args[0];
290       mf.GetCMakeInstance()->RenameCommand(this->Args[0].c_str(),
291                                            newName.c_str());
292       mf.AddCommand(f);
293
294       // remove the function blocker now that the macro is defined
295       mf.RemoveFunctionBlocker(this, lff);
296       return true;
297       }
298     else
299       {
300       // decrement for each nested macro that ends
301       this->Depth--;
302       }
303     }
304
305   // if it wasn't an endmacro and we are not executing then we must be
306   // recording
307   this->Functions.push_back(lff);
308   return true;
309 }
310
311
312 bool cmMacroFunctionBlocker::
313 ShouldRemove(const cmListFileFunction& lff, cmMakefile &mf)
314 {
315   if(!cmSystemTools::Strucmp(lff.Name.c_str(),"endmacro"))
316     {
317     std::vector<std::string> expandedArguments;
318     mf.ExpandArguments(lff.Arguments, expandedArguments);
319     // if the endmacro has arguments make sure they
320     // match the arguments of the macro
321     if ((expandedArguments.empty() ||
322          (expandedArguments[0] == this->Args[0])))
323       {
324       return true;
325       }
326     }
327
328   return false;
329 }
330
331 bool cmMacroCommand::InitialPass(std::vector<std::string> const& args,
332                                  cmExecutionStatus &)
333 {
334   if(args.size() < 1)
335     {
336     this->SetError("called with incorrect number of arguments");
337     return false;
338     }
339
340   // create a function blocker
341   cmMacroFunctionBlocker *f = new cmMacroFunctionBlocker();
342   for(std::vector<std::string>::const_iterator j = args.begin();
343       j != args.end(); ++j)
344     {
345     f->Args.push_back(*j);
346     }
347   this->Makefile->AddFunctionBlocker(f);
348   return true;
349 }
350