Imported Upstream version 2.8.9
[platform/upstream/cmake.git] / Source / cmListFileCache.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 "cmListFileCache.h"
13
14 #include "cmListFileLexer.h"
15 #include "cmSystemTools.h"
16 #include "cmMakefile.h"
17 #include "cmVersion.h"
18
19 #include <cmsys/RegularExpression.hxx>
20
21 #ifdef __BORLANDC__
22 # pragma warn -8060 /* possibly incorrect assignment */
23 #endif
24
25 bool cmListFileCacheParseFunction(cmListFileLexer* lexer,
26                                   cmListFileFunction& function,
27                                   const char* filename);
28
29 bool cmListFile::ParseFile(const char* filename, 
30                            bool topLevel,
31                            cmMakefile *mf)
32 {
33   if(!cmSystemTools::FileExists(filename))
34     {
35     return false;
36     }
37
38   // Create the scanner.
39   cmListFileLexer* lexer = cmListFileLexer_New();
40   if(!lexer)
41     {
42     cmSystemTools::Error("cmListFileCache: error allocating lexer ");
43     return false;
44     }
45
46   // Open the file.
47   if(!cmListFileLexer_SetFileName(lexer, filename))
48     {
49     cmListFileLexer_Delete(lexer);
50     cmSystemTools::Error("cmListFileCache: error can not open file ", 
51                          filename);
52     return false;
53     }
54
55   // Use a simple recursive-descent parser to process the token
56   // stream.
57   this->ModifiedTime = cmSystemTools::ModifiedTime(filename);
58   bool parseError = false;
59   bool haveNewline = true;
60   cmListFileLexer_Token* token;
61   while(!parseError && (token = cmListFileLexer_Scan(lexer)))
62     {
63     if(token->type == cmListFileLexer_Token_Newline)
64       {
65       haveNewline = true;
66       }
67     else if(token->type == cmListFileLexer_Token_Identifier)
68       {
69       if(haveNewline)
70         {
71         haveNewline = false;
72         cmListFileFunction inFunction;
73         inFunction.Name = token->text;
74         inFunction.FilePath = filename;
75         inFunction.Line = token->line;
76         if(cmListFileCacheParseFunction(lexer, inFunction, filename))
77           {
78           this->Functions.push_back(inFunction);
79           }
80         else
81           {
82           parseError = true;
83           }
84         }
85       else
86         {
87         cmOStringStream error;
88         error << "Error in cmake code at\n"
89               << filename << ":" << token->line << ":\n"
90               << "Parse error.  Expected a newline, got "
91               << cmListFileLexer_GetTypeAsString(lexer, token->type)
92               << " with text \"" << token->text << "\".";
93         cmSystemTools::Error(error.str().c_str());
94         parseError = true;
95         }
96       }
97     else
98       {
99       cmOStringStream error;
100       error << "Error in cmake code at\n"
101             << filename << ":" << token->line << ":\n"
102             << "Parse error.  Expected a command name, got "
103             << cmListFileLexer_GetTypeAsString(lexer, token->type)
104             << " with text \""
105             << token->text << "\".";
106       cmSystemTools::Error(error.str().c_str());
107       parseError = true;
108       }
109     }
110   if (parseError)
111     {
112     this->ModifiedTime = 0;
113     }
114
115   cmListFileLexer_Delete(lexer);
116
117   // do we need a cmake_policy(VERSION call?
118   if(topLevel)
119   {
120     bool hasVersion = false;
121     // search for the right policy command
122     for(std::vector<cmListFileFunction>::iterator i 
123           = this->Functions.begin();
124         i != this->Functions.end(); ++i)
125     {
126       if (cmSystemTools::LowerCase(i->Name) == "cmake_minimum_required")
127       {
128         hasVersion = true;
129         break;
130       }
131     }
132     // if no policy command is found this is an error if they use any 
133     // non advanced functions or a lot of functions
134     if(!hasVersion)
135     {
136       bool isProblem = true;
137       if (this->Functions.size() < 30)
138       {
139         // the list of simple commands DO NOT ADD TO THIS LIST!!!!!
140         // these commands must have backwards compatibility forever and
141         // and that is a lot longer than your tiny mind can comprehend mortal
142         std::set<std::string> allowedCommands;
143         allowedCommands.insert("project");
144         allowedCommands.insert("set");
145         allowedCommands.insert("if");
146         allowedCommands.insert("endif");
147         allowedCommands.insert("else");
148         allowedCommands.insert("elseif");
149         allowedCommands.insert("add_executable");
150         allowedCommands.insert("add_library");
151         allowedCommands.insert("target_link_libraries");
152         allowedCommands.insert("option");
153         allowedCommands.insert("message");
154         isProblem = false;
155         for(std::vector<cmListFileFunction>::iterator i 
156               = this->Functions.begin();
157             i != this->Functions.end(); ++i)
158         {
159           std::string name = cmSystemTools::LowerCase(i->Name);
160           if (allowedCommands.find(name) == allowedCommands.end())
161           {
162             isProblem = true;
163             break;
164           }       
165         }
166       }
167       
168       if (isProblem)
169       {
170       // Tell the top level cmMakefile to diagnose
171       // this violation of CMP0000.
172       mf->SetCheckCMP0000(true);
173
174       // Implicitly set the version for the user.
175       mf->SetPolicyVersion("2.4");
176       }
177     }
178   }
179
180   if(topLevel)
181     {
182     bool hasProject = false;
183     // search for a project command
184     for(std::vector<cmListFileFunction>::iterator i 
185           = this->Functions.begin();
186         i != this->Functions.end(); ++i)
187       {
188       if(cmSystemTools::LowerCase(i->Name) == "project")
189         {
190         hasProject = true;
191         break;
192         }
193       }
194     // if no project command is found, add one
195     if(!hasProject)
196       {
197       cmListFileFunction project;
198       project.Name = "PROJECT";
199       cmListFileArgument prj("Project", false, filename, 0);
200       project.Arguments.push_back(prj);
201       this->Functions.insert(this->Functions.begin(),project);
202       }
203     }
204   if(parseError)
205     {
206     return false;
207     }
208   return true;
209 }
210
211 bool cmListFileCacheParseFunction(cmListFileLexer* lexer,
212                                   cmListFileFunction& function,
213                                   const char* filename)
214 {
215   // Command name has already been parsed.  Read the left paren.
216   cmListFileLexer_Token* token;
217   if(!(token = cmListFileLexer_Scan(lexer)))
218     {
219     cmOStringStream error;
220     error << "Error in cmake code at\n"
221           << filename << ":" << cmListFileLexer_GetCurrentLine(lexer) << ":\n"
222           << "Parse error.  Function missing opening \"(\".";
223     cmSystemTools::Error(error.str().c_str());
224     return false;
225     }
226   if(token->type != cmListFileLexer_Token_ParenLeft)
227     {
228     cmOStringStream error;
229     error << "Error in cmake code at\n"
230           << filename << ":" << cmListFileLexer_GetCurrentLine(lexer) << ":\n"
231           << "Parse error.  Expected \"(\", got "
232           << cmListFileLexer_GetTypeAsString(lexer, token->type)
233           << " with text \"" << token->text << "\".";
234     cmSystemTools::Error(error.str().c_str());
235     return false;
236     }
237
238   // Arguments.
239   unsigned long lastLine = cmListFileLexer_GetCurrentLine(lexer);
240   unsigned long parenDepth = 0;
241   while((token = cmListFileLexer_Scan(lexer)))
242     {
243     if(token->type == cmListFileLexer_Token_ParenLeft)
244       {
245       parenDepth++;
246       cmListFileArgument a("(",
247                            false, filename, token->line);
248       function.Arguments.push_back(a);
249       }
250     else if(token->type == cmListFileLexer_Token_ParenRight)
251       {
252       if (parenDepth == 0)
253         {
254         return true;
255         }
256       parenDepth--;
257       cmListFileArgument a(")",
258                            false, filename, token->line);
259       function.Arguments.push_back(a);        
260       }
261     else if(token->type == cmListFileLexer_Token_Identifier ||
262             token->type == cmListFileLexer_Token_ArgumentUnquoted)
263       {
264       cmListFileArgument a(token->text,
265                            false, filename, token->line);
266       function.Arguments.push_back(a);
267       }
268     else if(token->type == cmListFileLexer_Token_ArgumentQuoted)
269       {
270       cmListFileArgument a(token->text,
271                            true, filename, token->line);
272       function.Arguments.push_back(a);
273       }
274     else if(token->type != cmListFileLexer_Token_Newline)
275       {
276       // Error.
277       cmOStringStream error;
278       error << "Error in cmake code at\n"
279             << filename << ":" << cmListFileLexer_GetCurrentLine(lexer) 
280             << ":\n"
281             << "Parse error.  Function missing ending \")\".  "
282             << "Instead found "
283             << cmListFileLexer_GetTypeAsString(lexer, token->type)
284             << " with text \"" << token->text << "\".";
285       cmSystemTools::Error(error.str().c_str());
286       return false;
287       }
288     lastLine = cmListFileLexer_GetCurrentLine(lexer);
289     }
290
291   cmOStringStream error;
292   error << "Error in cmake code at\n"
293         << filename << ":" << lastLine << ":\n"
294         << "Parse error.  Function missing ending \")\".  "
295         << "End of file reached.";
296   cmSystemTools::Error(error.str().c_str());
297
298   return false;
299 }
300
301 //----------------------------------------------------------------------------
302 std::ostream& operator<<(std::ostream& os, cmListFileContext const& lfc)
303 {
304   os << lfc.FilePath;
305   if(lfc.Line)
306     {
307     os << ":" << lfc.Line;
308     if(!lfc.Name.empty())
309       {
310       os << " (" << lfc.Name << ")";
311       }
312     }
313   return os;
314 }