Imported Upstream version 2.8.12.2
[platform/upstream/cmake.git] / Source / cmFunctionCommand.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 "cmFunctionCommand.h"
13
14 #include "cmake.h"
15
16 // define the class for function commands
17 class cmFunctionHelperCommand : public cmCommand
18 {
19 public:
20   cmFunctionHelperCommand() {}
21
22   ///! clean up any memory allocated by the function
23   ~cmFunctionHelperCommand() {};
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     cmFunctionHelperCommand *newC = new cmFunctionHelperCommand;
42     // we must copy when we clone
43     newC->Args = this->Args;
44     newC->Functions = this->Functions;
45     newC->Policies = this->Policies;
46     return newC;
47   }
48
49   /**
50    * This determines if the command is invoked when in script mode.
51    */
52   virtual bool IsScriptable() const { return true; }
53
54   /**
55    * This is called when the command is first encountered in
56    * the CMakeLists.txt file.
57    */
58   virtual bool InvokeInitialPass(const std::vector<cmListFileArgument>& args,
59                                  cmExecutionStatus &);
60
61   virtual bool InitialPass(std::vector<std::string> const&,
62                            cmExecutionStatus &) { return false; };
63
64   /**
65    * The name of the command as specified in CMakeList.txt.
66    */
67   virtual const char* GetName() const { return this->Args[0].c_str(); }
68
69   /**
70    * Succinct documentation.
71    */
72   virtual const char* GetTerseDocumentation() const
73   {
74     std::string docs = "Function named: ";
75     docs += this->GetName();
76     return docs.c_str();
77   }
78
79   /**
80    * More documentation.
81    */
82   virtual const char* GetFullDocumentation() const
83   {
84     return this->GetTerseDocumentation();
85   }
86
87   cmTypeMacro(cmFunctionHelperCommand, cmCommand);
88
89   std::vector<std::string> Args;
90   std::vector<cmListFileFunction> Functions;
91   cmPolicies::PolicyMap Policies;
92 };
93
94
95 bool cmFunctionHelperCommand::InvokeInitialPass
96 (const std::vector<cmListFileArgument>& args,
97  cmExecutionStatus & inStatus)
98 {
99   // Expand the argument list to the function.
100   std::vector<std::string> expandedArgs;
101   this->Makefile->ExpandArguments(args, expandedArgs);
102
103   // make sure the number of arguments passed is at least the number
104   // required by the signature
105   if (expandedArgs.size() < this->Args.size() - 1)
106     {
107     std::string errorMsg =
108       "Function invoked with incorrect arguments for function named: ";
109     errorMsg += this->Args[0];
110     this->SetError(errorMsg.c_str());
111     return false;
112     }
113
114   // we push a scope on the makefile
115   cmMakefile::LexicalPushPop lexScope(this->Makefile);
116   cmMakefile::ScopePushPop varScope(this->Makefile);
117   static_cast<void>(varScope);
118
119   // Push a weak policy scope which restores the policies recorded at
120   // function creation.
121   cmMakefile::PolicyPushPop polScope(this->Makefile, true, this->Policies);
122
123   // set the value of argc
124   cmOStringStream strStream;
125   strStream << expandedArgs.size();
126   this->Makefile->AddDefinition("ARGC",strStream.str().c_str());
127   this->Makefile->MarkVariableAsUsed("ARGC");
128
129   // set the values for ARGV0 ARGV1 ...
130   for (unsigned int t = 0; t < expandedArgs.size(); ++t)
131     {
132     cmOStringStream tmpStream;
133     tmpStream << "ARGV" << t;
134     this->Makefile->AddDefinition(tmpStream.str().c_str(),
135                                   expandedArgs[t].c_str());
136     this->Makefile->MarkVariableAsUsed(tmpStream.str().c_str());
137     }
138
139   // define the formal arguments
140   for (unsigned int j = 1; j < this->Args.size(); ++j)
141     {
142     this->Makefile->AddDefinition(this->Args[j].c_str(),
143                                   expandedArgs[j-1].c_str());
144     }
145
146   // define ARGV and ARGN
147   std::vector<std::string>::const_iterator eit;
148   std::string argvDef;
149   std::string argnDef;
150   unsigned int cnt = 0;
151   for ( eit = expandedArgs.begin(); eit != expandedArgs.end(); ++eit )
152     {
153     if ( argvDef.size() > 0 )
154       {
155       argvDef += ";";
156       }
157     argvDef += *eit;
158     if ( cnt >= this->Args.size()-1 )
159       {
160       if ( argnDef.size() > 0 )
161         {
162         argnDef += ";";
163         }
164       argnDef += *eit;
165       }
166     cnt ++;
167     }
168   this->Makefile->AddDefinition("ARGV", argvDef.c_str());
169   this->Makefile->MarkVariableAsUsed("ARGV");
170   this->Makefile->AddDefinition("ARGN", argnDef.c_str());
171   this->Makefile->MarkVariableAsUsed("ARGN");
172
173   // Invoke all the functions that were collected in the block.
174   // for each function
175   for(unsigned int c = 0; c < this->Functions.size(); ++c)
176     {
177     cmExecutionStatus status;
178     if (!this->Makefile->ExecuteCommand(this->Functions[c],status) ||
179         status.GetNestedError())
180       {
181       // The error message should have already included the call stack
182       // so we do not need to report an error here.
183       lexScope.Quiet();
184       polScope.Quiet();
185       inStatus.SetNestedError(true);
186       return false;
187       }
188     if (status.GetReturnInvoked())
189       {
190       return true;
191       }
192     }
193
194   // pop scope on the makefile
195   return true;
196 }
197
198 bool cmFunctionFunctionBlocker::
199 IsFunctionBlocked(const cmListFileFunction& lff, cmMakefile &mf,
200                   cmExecutionStatus &)
201 {
202   // record commands until we hit the ENDFUNCTION
203   // at the ENDFUNCTION call we shift gears and start looking for invocations
204   if(!cmSystemTools::Strucmp(lff.Name.c_str(),"function"))
205     {
206     this->Depth++;
207     }
208   else if(!cmSystemTools::Strucmp(lff.Name.c_str(),"endfunction"))
209     {
210     // if this is the endfunction for this function then execute
211     if (!this->Depth)
212       {
213       std::string name = this->Args[0];
214       std::vector<std::string>::size_type cc;
215       name += "(";
216       for ( cc = 0; cc < this->Args.size(); cc ++ )
217         {
218         name += " " + this->Args[cc];
219         }
220       name += " )";
221
222       // create a new command and add it to cmake
223       cmFunctionHelperCommand *f = new cmFunctionHelperCommand();
224       f->Args = this->Args;
225       f->Functions = this->Functions;
226       mf.RecordPolicies(f->Policies);
227
228       // Set the FilePath on the arguments to match the function since it is
229       // not stored and the original values may be freed
230       for (unsigned int i = 0; i < f->Functions.size(); ++i)
231         {
232         for (unsigned int j = 0; j < f->Functions[i].Arguments.size(); ++j)
233           {
234           f->Functions[i].Arguments[j].FilePath =
235             f->Functions[i].FilePath.c_str();
236           }
237         }
238
239       std::string newName = "_" + this->Args[0];
240       mf.GetCMakeInstance()->RenameCommand(this->Args[0].c_str(),
241                                            newName.c_str());
242       mf.AddCommand(f);
243
244       // remove the function blocker now that the function is defined
245       mf.RemoveFunctionBlocker(this, lff);
246       return true;
247       }
248     else
249       {
250       // decrement for each nested function that ends
251       this->Depth--;
252       }
253     }
254
255   // if it wasn't an endfunction and we are not executing then we must be
256   // recording
257   this->Functions.push_back(lff);
258   return true;
259 }
260
261
262 bool cmFunctionFunctionBlocker::
263 ShouldRemove(const cmListFileFunction& lff, cmMakefile &mf)
264 {
265   if(!cmSystemTools::Strucmp(lff.Name.c_str(),"endfunction"))
266     {
267     std::vector<std::string> expandedArguments;
268     mf.ExpandArguments(lff.Arguments, expandedArguments);
269     // if the endfunction has arguments then make sure
270     // they match the ones in the opening function command
271     if ((expandedArguments.empty() ||
272          (expandedArguments[0] == this->Args[0])))
273       {
274       return true;
275       }
276     }
277
278   return false;
279 }
280
281 bool cmFunctionCommand
282 ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
283 {
284   if(args.size() < 1)
285     {
286     this->SetError("called with incorrect number of arguments");
287     return false;
288     }
289
290   // create a function blocker
291   cmFunctionFunctionBlocker *f = new cmFunctionFunctionBlocker();
292   for(std::vector<std::string>::const_iterator j = args.begin();
293       j != args.end(); ++j)
294     {
295     f->Args.push_back(*j);
296     }
297   this->Makefile->AddFunctionBlocker(f);
298   return true;
299 }
300