1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmFunctionCommand.cxx,v $
6 Date: $Date: 2008/01/23 15:27:59 $
7 Version: $Revision: 1.4 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmFunctionCommand.h"
21 // define the class for function commands
22 class cmFunctionHelperCommand
: public cmCommand
25 cmFunctionHelperCommand() {}
27 ///! clean up any memory allocated by the function
28 ~cmFunctionHelperCommand() {};
31 * This is a virtual constructor for the command.
33 virtual cmCommand
* Clone()
35 cmFunctionHelperCommand
*newC
= new cmFunctionHelperCommand
;
36 // we must copy when we clone
37 newC
->Args
= this->Args
;
38 newC
->Functions
= this->Functions
;
43 * This determines if the command is invoked when in script mode.
45 virtual bool IsScriptable() { return true; }
48 * This is called when the command is first encountered in
49 * the CMakeLists.txt file.
51 virtual bool InvokeInitialPass(const std::vector
<cmListFileArgument
>& args
,
54 virtual bool InitialPass(std::vector
<std::string
> const&,
55 cmExecutionStatus
&) { return false; };
58 * The name of the command as specified in CMakeList.txt.
60 virtual const char* GetName() { return this->Args
[0].c_str(); }
63 * Succinct documentation.
65 virtual const char* GetTerseDocumentation()
67 std::string docs
= "Function named: ";
68 docs
+= this->GetName();
75 virtual const char* GetFullDocumentation()
77 return this->GetTerseDocumentation();
80 cmTypeMacro(cmFunctionHelperCommand
, cmCommand
);
82 std::vector
<std::string
> Args
;
83 std::vector
<cmListFileFunction
> Functions
;
87 bool cmFunctionHelperCommand::InvokeInitialPass
88 (const std::vector
<cmListFileArgument
>& args
,
91 // Expand the argument list to the function.
92 std::vector
<std::string
> expandedArgs
;
93 this->Makefile
->ExpandArguments(args
, expandedArgs
);
95 // make sure the number of arguments passed is at least the number
96 // required by the signature
97 if (expandedArgs
.size() < this->Args
.size() - 1)
99 std::string errorMsg
=
100 "Function invoked with incorrect arguments for function named: ";
101 errorMsg
+= this->Args
[0];
102 this->SetError(errorMsg
.c_str());
106 // we push a scope on the makefile
107 this->Makefile
->PushScope();
109 // set the value of argc
110 cmOStringStream strStream
;
111 strStream
<< expandedArgs
.size();
112 this->Makefile
->AddDefinition("ARGC",strStream
.str().c_str());
114 // set the values for ARGV0 ARGV1 ...
115 for (unsigned int t
= 0; t
< expandedArgs
.size(); ++t
)
117 cmOStringStream tmpStream
;
118 tmpStream
<< "ARGV" << t
;
119 this->Makefile
->AddDefinition(tmpStream
.str().c_str(),
120 expandedArgs
[t
].c_str());
123 // define the formal arguments
124 for (unsigned int j
= 1; j
< this->Args
.size(); ++j
)
126 this->Makefile
->AddDefinition(this->Args
[j
].c_str(),
127 expandedArgs
[j
-1].c_str());
130 // define ARGV and ARGN
131 std::vector
<std::string
>::const_iterator eit
;
134 unsigned int cnt
= 0;
135 for ( eit
= expandedArgs
.begin(); eit
!= expandedArgs
.end(); ++eit
)
137 if ( argvDef
.size() > 0 )
142 if ( cnt
>= this->Args
.size()-1 )
144 if ( argnDef
.size() > 0 )
152 this->Makefile
->AddDefinition("ARGV", argvDef
.c_str());
153 this->Makefile
->AddDefinition("ARGN", argnDef
.c_str());
155 // Invoke all the functions that were collected in the block.
157 for(unsigned int c
= 0; c
< this->Functions
.size(); ++c
)
159 cmExecutionStatus status
;
160 if (!this->Makefile
->ExecuteCommand(this->Functions
[c
],status
))
162 cmOStringStream error
;
163 error
<< "Error in cmake code at\n"
164 << this->Functions
[c
].FilePath
<< ":"
165 << this->Functions
[c
].Line
<< ":\n"
166 << "A command failed during the invocation of function \""
167 << this->Args
[0].c_str() << "\".";
168 cmSystemTools::Error(error
.str().c_str());
170 // pop scope on the makefile and return
171 this->Makefile
->PopScope();
174 if (status
.GetReturnInvoked())
176 this->Makefile
->PopScope();
181 // pop scope on the makefile
182 this->Makefile
->PopScope();
186 bool cmFunctionFunctionBlocker::
187 IsFunctionBlocked(const cmListFileFunction
& lff
, cmMakefile
&mf
,
190 // record commands until we hit the ENDFUNCTION
191 // at the ENDFUNCTION call we shift gears and start looking for invocations
192 if(!cmSystemTools::Strucmp(lff
.Name
.c_str(),"function"))
196 else if(!cmSystemTools::Strucmp(lff
.Name
.c_str(),"endfunction"))
198 // if this is the endfunction for this function then execute
201 std::string name
= this->Args
[0];
202 std::vector
<std::string
>::size_type cc
;
204 for ( cc
= 0; cc
< this->Args
.size(); cc
++ )
206 name
+= " " + this->Args
[cc
];
210 // create a new command and add it to cmake
211 cmFunctionHelperCommand
*f
= new cmFunctionHelperCommand();
212 f
->Args
= this->Args
;
213 f
->Functions
= this->Functions
;
215 // Set the FilePath on the arguments to match the function since it is
216 // not stored and the original values may be freed
217 for (unsigned int i
= 0; i
< f
->Functions
.size(); ++i
)
219 for (unsigned int j
= 0; j
< f
->Functions
[i
].Arguments
.size(); ++j
)
221 f
->Functions
[i
].Arguments
[j
].FilePath
=
222 f
->Functions
[i
].FilePath
.c_str();
226 std::string newName
= "_" + this->Args
[0];
227 mf
.GetCMakeInstance()->RenameCommand(this->Args
[0].c_str(),
231 // remove the function blocker now that the function is defined
232 mf
.RemoveFunctionBlocker(lff
);
237 // decrement for each nested function that ends
242 // if it wasn't an endfunction and we are not executing then we must be
244 this->Functions
.push_back(lff
);
249 bool cmFunctionFunctionBlocker::
250 ShouldRemove(const cmListFileFunction
& lff
, cmMakefile
&mf
)
252 if(!cmSystemTools::Strucmp(lff
.Name
.c_str(),"endfunction"))
254 std::vector
<std::string
> expandedArguments
;
255 mf
.ExpandArguments(lff
.Arguments
, expandedArguments
);
256 if ((!expandedArguments
.empty() &&
257 (expandedArguments
[0] == this->Args
[0]))
258 || cmSystemTools::IsOn
259 (mf
.GetPropertyOrDefinition("CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS")))
268 void cmFunctionFunctionBlocker::
269 ScopeEnded(cmMakefile
&mf
)
271 // functions should end with an EndFunction
272 cmSystemTools::Error(
273 "The end of a CMakeLists file was reached with a FUNCTION statement that "
274 "was not closed properly. Within the directory: ",
275 mf
.GetCurrentDirectory(), " with function ",
276 this->Args
[0].c_str());
279 bool cmFunctionCommand
280 ::InitialPass(std::vector
<std::string
> const& args
, cmExecutionStatus
&)
284 this->SetError("called with incorrect number of arguments");
288 // create a function blocker
289 cmFunctionFunctionBlocker
*f
= new cmFunctionFunctionBlocker();
290 for(std::vector
<std::string
>::const_iterator j
= args
.begin();
291 j
!= args
.end(); ++j
)
293 f
->Args
.push_back(*j
);
295 this->Makefile
->AddFunctionBlocker(f
);