1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmMacroCommand.cxx,v $
6 Date: $Date: 2008/01/23 15:27:59 $
7 Version: $Revision: 1.34 $
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 "cmMacroCommand.h"
21 // define the class for macro commands
22 class cmMacroHelperCommand
: public cmCommand
25 cmMacroHelperCommand() {}
27 ///! clean up any memory allocated by the macro
28 ~cmMacroHelperCommand() {};
31 * This is a virtual constructor for the command.
33 virtual cmCommand
* Clone()
35 cmMacroHelperCommand
*newC
= new cmMacroHelperCommand
;
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
= "Macro named: ";
68 docs
+= this->GetName();
75 virtual const char* GetFullDocumentation()
77 return this->GetTerseDocumentation();
80 cmTypeMacro(cmMacroHelperCommand
, cmCommand
);
82 std::vector
<std::string
> Args
;
83 std::vector
<cmListFileFunction
> Functions
;
87 bool cmMacroHelperCommand::InvokeInitialPass
88 (const std::vector
<cmListFileArgument
>& args
,
89 cmExecutionStatus
&inStatus
)
91 // Expand the argument list to the macro.
92 std::vector
<std::string
> expandedArgs
;
93 this->Makefile
->ExpandArguments(args
, expandedArgs
);
96 cmListFileArgument arg
;
99 // make sure the number of arguments passed is at least the number
100 // required by the signature
101 if (expandedArgs
.size() < this->Args
.size() - 1)
103 std::string errorMsg
=
104 "Macro invoked with incorrect arguments for macro named: ";
105 errorMsg
+= this->Args
[0];
106 this->SetError(errorMsg
.c_str());
110 // set the value of argc
111 cmOStringStream argcDefStream
;
112 argcDefStream
<< expandedArgs
.size();
113 std::string argcDef
= argcDefStream
.str();
115 // declare varuiables for ARGV ARGN but do not compute until needed
118 bool argnDefInitialized
= false;
119 bool argvDefInitialized
= false;
121 // Invoke all the functions that were collected in the block.
122 cmListFileFunction newLFF
;
124 for(unsigned int c
= 0; c
< this->Functions
.size(); ++c
)
126 // Replace the formal arguments and then invoke the command.
127 newLFF
.Arguments
.clear();
128 newLFF
.Arguments
.reserve(this->Functions
[c
].Arguments
.size());
129 newLFF
.Name
= this->Functions
[c
].Name
;
130 newLFF
.FilePath
= this->Functions
[c
].FilePath
;
131 newLFF
.Line
= this->Functions
[c
].Line
;
132 const char* def
= this->Makefile
->GetDefinition
133 ("CMAKE_MACRO_REPORT_DEFINITION_LOCATION");
134 bool macroReportLocation
= false;
135 if(def
&& !cmSystemTools::IsOff(def
))
137 macroReportLocation
= true;
140 // for each argument of the current function
141 for (std::vector
<cmListFileArgument
>::const_iterator k
=
142 this->Functions
[c
].Arguments
.begin();
143 k
!= this->Functions
[c
].Arguments
.end(); ++k
)
146 // replace formal arguments
147 for (unsigned int j
= 1; j
< this->Args
.size(); ++j
)
150 variable
+= this->Args
[j
];
152 cmSystemTools::ReplaceString(tmps
, variable
.c_str(),
153 expandedArgs
[j
-1].c_str());
156 cmSystemTools::ReplaceString(tmps
, "${ARGC}",argcDef
.c_str());
159 if (tmps
.find("${ARGN}") != std::string::npos
)
161 if (!argnDefInitialized
)
163 std::vector
<std::string
>::const_iterator eit
;
164 std::vector
<std::string
>::size_type cnt
= 0;
165 for ( eit
= expandedArgs
.begin(); eit
!= expandedArgs
.end(); ++eit
)
167 if ( cnt
>= this->Args
.size()-1 )
169 if ( argnDef
.size() > 0 )
177 argnDefInitialized
= true;
179 cmSystemTools::ReplaceString(tmps
, "${ARGN}", argnDef
.c_str());
182 // if the current argument of the current function has ${ARGV in it
183 // then try replacing ARGV values
184 if (tmps
.find("${ARGV") != std::string::npos
)
188 // repleace ARGV, compute it only once
189 if (!argvDefInitialized
)
191 std::vector
<std::string
>::const_iterator eit
;
192 for ( eit
= expandedArgs
.begin(); eit
!= expandedArgs
.end(); ++eit
)
194 if ( argvDef
.size() > 0 )
200 argvDefInitialized
= true;
202 cmSystemTools::ReplaceString(tmps
, "${ARGV}", argvDef
.c_str());
204 // also replace the ARGV1 ARGV2 ... etc
205 for (unsigned int t
= 0; t
< expandedArgs
.size(); ++t
)
207 sprintf(argvName
,"${ARGV%i}",t
);
208 cmSystemTools::ReplaceString(tmps
, argvName
,
209 expandedArgs
[t
].c_str());
214 arg
.Quoted
= k
->Quoted
;
215 if(macroReportLocation
)
217 // Report the location of the argument where the macro was
219 arg
.FilePath
= k
->FilePath
;
224 // Report the location of the argument where the macro was
228 arg
.FilePath
= args
[0].FilePath
;
229 arg
.Line
= args
[0].Line
;
233 arg
.FilePath
= "Unknown";
237 newLFF
.Arguments
.push_back(arg
);
239 cmExecutionStatus status
;
240 if(!this->Makefile
->ExecuteCommand(newLFF
,status
))
244 arg
.FilePath
= args
[0].FilePath
;
245 arg
.Line
= args
[0].Line
;
249 arg
.FilePath
= "Unknown";
252 cmOStringStream error
;
253 error
<< "Error in cmake code at\n"
254 << arg
.FilePath
<< ":" << arg
.Line
<< ":\n"
255 << "A command failed during the invocation of macro \""
256 << this->Args
[0].c_str() << "\".";
257 cmSystemTools::Error(error
.str().c_str());
260 if (status
.GetReturnInvoked())
262 inStatus
.SetReturnInvoked(true);
265 if (status
.GetBreakInvoked())
267 inStatus
.SetBreakInvoked(true);
274 bool cmMacroFunctionBlocker::
275 IsFunctionBlocked(const cmListFileFunction
& lff
, cmMakefile
&mf
,
278 // record commands until we hit the ENDMACRO
279 // at the ENDMACRO call we shift gears and start looking for invocations
280 if(!cmSystemTools::Strucmp(lff
.Name
.c_str(),"macro"))
284 else if(!cmSystemTools::Strucmp(lff
.Name
.c_str(),"endmacro"))
286 // if this is the endmacro for this macro then execute
289 std::string name
= this->Args
[0];
290 std::vector
<std::string
>::size_type cc
;
292 for ( cc
= 0; cc
< this->Args
.size(); cc
++ )
294 name
+= " " + this->Args
[cc
];
297 mf
.AddMacro(this->Args
[0].c_str(), name
.c_str());
298 // create a new command and add it to cmake
299 cmMacroHelperCommand
*f
= new cmMacroHelperCommand();
300 f
->Args
= this->Args
;
301 f
->Functions
= this->Functions
;
302 std::string newName
= "_" + this->Args
[0];
303 mf
.GetCMakeInstance()->RenameCommand(this->Args
[0].c_str(),
307 // remove the function blocker now that the macro is defined
308 mf
.RemoveFunctionBlocker(lff
);
313 // decrement for each nested macro that ends
318 // if it wasn't an endmacro and we are not executing then we must be
320 this->Functions
.push_back(lff
);
325 bool cmMacroFunctionBlocker::
326 ShouldRemove(const cmListFileFunction
& lff
, cmMakefile
&mf
)
328 if(!cmSystemTools::Strucmp(lff
.Name
.c_str(),"endmacro"))
330 std::vector
<std::string
> expandedArguments
;
331 mf
.ExpandArguments(lff
.Arguments
, expandedArguments
);
332 if ((!expandedArguments
.empty() &&
333 (expandedArguments
[0] == this->Args
[0]))
334 || cmSystemTools::IsOn
335 (mf
.GetPropertyOrDefinition("CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS")))
344 void cmMacroFunctionBlocker::
345 ScopeEnded(cmMakefile
&mf
)
347 // macros should end with an EndMacro
348 cmSystemTools::Error(
349 "The end of a CMakeLists file was reached with a MACRO statement that "
350 "was not closed properly. Within the directory: ",
351 mf
.GetCurrentDirectory(), " with macro ",
352 this->Args
[0].c_str());
355 bool cmMacroCommand::InitialPass(std::vector
<std::string
> const& args
,
360 this->SetError("called with incorrect number of arguments");
364 // create a function blocker
365 cmMacroFunctionBlocker
*f
= new cmMacroFunctionBlocker();
366 for(std::vector
<std::string
>::const_iterator j
= args
.begin();
367 j
!= args
.end(); ++j
)
369 f
->Args
.push_back(*j
);
371 this->Makefile
->AddFunctionBlocker(f
);