Updates will be applied on October 27th between 12pm - 12:45pm EDT (UTC-0400). Gitlab may be slow during the maintenance window.

cmListFileCache.cxx 13.2 KB
Newer Older
1
2
3
/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Will Schroeder's avatar
Will Schroeder committed
4

5
6
  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.
Will Schroeder's avatar
Will Schroeder committed
7

8
9
10
11
  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/
12
#include "cmListFileCache.h"
13
14

#include "cmListFileLexer.h"
15
#include "cmLocalGenerator.h"
16
#include "cmSystemTools.h"
17
#include "cmMakefile.h"
18
#include "cmVersion.h"
19
20

#include <cmsys/RegularExpression.hxx>
21

22
23
24
25
#ifdef __BORLANDC__
# pragma warn -8060 /* possibly incorrect assignment */
#endif

26
27
28
29
30
31
32
//----------------------------------------------------------------------------
struct cmListFileParser
{
  cmListFileParser(cmListFile* lf, cmMakefile* mf, const char* filename);
  ~cmListFileParser();
  bool ParseFile();
  bool ParseFunction(const char* name, long line);
33
  bool AddArgument(cmListFileLexer_Token* token,
34
35
36
37
38
39
                   cmListFileArgument::Delimiter delim);
  cmListFile* ListFile;
  cmMakefile* Makefile;
  const char* FileName;
  cmListFileLexer* Lexer;
  cmListFileFunction Function;
40
  enum { SeparationOkay, SeparationWarning, SeparationError} Separation;
41
};
42

43
44
45
46
47
//----------------------------------------------------------------------------
cmListFileParser::cmListFileParser(cmListFile* lf, cmMakefile* mf,
                                   const char* filename):
  ListFile(lf), Makefile(mf), FileName(filename),
  Lexer(cmListFileLexer_New())
48
{
49
}
50

51
52
53
54
55
//----------------------------------------------------------------------------
cmListFileParser::~cmListFileParser()
{
  cmListFileLexer_Delete(this->Lexer);
}
56

57
58
59
//----------------------------------------------------------------------------
bool cmListFileParser::ParseFile()
{
60
  // Open the file.
61
62
  cmListFileLexer_BOM bom;
  if(!cmListFileLexer_SetFileName(this->Lexer, this->FileName, &bom))
63
    {
64
    cmSystemTools::Error("cmListFileCache: error can not open file ",
65
                         this->FileName);
66
67
    return false;
    }
68

69
70
71
72
73
74
75
76
77
78
79
80
  // Verify the Byte-Order-Mark, if any.
  if(bom != cmListFileLexer_BOM_None &&
     bom != cmListFileLexer_BOM_UTF8)
    {
    cmListFileLexer_SetFileName(this->Lexer, 0, 0);
    cmOStringStream m;
    m << "File\n  " << this->FileName << "\n"
      << "starts with a Byte-Order-Mark that is not UTF-8.";
    this->Makefile->IssueMessage(cmake::FATAL_ERROR, m.str());
    return false;
    }

81
82
83
  // Use a simple recursive-descent parser to process the token
  // stream.
  bool haveNewline = true;
84
85
  while(cmListFileLexer_Token* token =
        cmListFileLexer_Scan(this->Lexer))
86
    {
87
88
89
90
    if(token->type == cmListFileLexer_Token_Space)
      {
      }
    else if(token->type == cmListFileLexer_Token_Newline)
91
      {
92
      haveNewline = true;
93
      }
94
95
96
97
    else if(token->type == cmListFileLexer_Token_CommentBracket)
      {
      haveNewline = false;
      }
98
    else if(token->type == cmListFileLexer_Token_Identifier)
99
      {
100
101
102
      if(haveNewline)
        {
        haveNewline = false;
103
        if(this->ParseFunction(token->text, token->line))
104
          {
105
          this->ListFile->Functions.push_back(this->Function);
106
107
108
          }
        else
          {
109
          return false;
110
111
112
113
114
115
          }
        }
      else
        {
        cmOStringStream error;
        error << "Error in cmake code at\n"
116
              << this->FileName << ":" << token->line << ":\n"
117
              << "Parse error.  Expected a newline, got "
118
              << cmListFileLexer_GetTypeAsString(this->Lexer, token->type)
119
              << " with text \"" << token->text << "\".";
120
        cmSystemTools::Error(error.str().c_str());
121
        return false;
122
123
124
125
126
127
        }
      }
    else
      {
      cmOStringStream error;
      error << "Error in cmake code at\n"
128
            << this->FileName << ":" << token->line << ":\n"
129
            << "Parse error.  Expected a command name, got "
130
            << cmListFileLexer_GetTypeAsString(this->Lexer, token->type)
131
            << " with text \""
132
133
            << token->text << "\".";
      cmSystemTools::Error(error.str().c_str());
134
      return false;
135
      }
136
    }
137
138
139
140
141
142
143
144
  return true;
}

//----------------------------------------------------------------------------
bool cmListFile::ParseFile(const char* filename,
                           bool topLevel,
                           cmMakefile *mf)
{
145
146
  if(!cmSystemTools::FileExists(filename) ||
     cmSystemTools::FileIsDirectory(filename))
147
    {
148
    return false;
149
150
    }

151
152
153
154
155
156
157
158
159
160
161
162
  bool parseError = false;
  this->ModifiedTime = cmSystemTools::ModifiedTime(filename);

  {
  cmListFileParser parser(this, mf, filename);
  parseError = !parser.ParseFile();
  }

  if(parseError)
    {
    this->ModifiedTime = 0;
    }
163

164
165
166
  // do we need a cmake_policy(VERSION call?
  if(topLevel)
  {
167
    bool hasVersion = false;
168
    // search for the right policy command
169
    for(std::vector<cmListFileFunction>::iterator i
170
171
172
          = this->Functions.begin();
        i != this->Functions.end(); ++i)
    {
173
174
      if (cmSystemTools::LowerCase(i->Name) == "cmake_minimum_required")
      {
175
        hasVersion = true;
176
177
        break;
      }
178
    }
179
    // if no policy command is found this is an error if they use any
180
    // non advanced functions or a lot of functions
181
    if(!hasVersion)
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
    {
      bool isProblem = true;
      if (this->Functions.size() < 30)
      {
        // the list of simple commands DO NOT ADD TO THIS LIST!!!!!
        // these commands must have backwards compatibility forever and
        // and that is a lot longer than your tiny mind can comprehend mortal
        std::set<std::string> allowedCommands;
        allowedCommands.insert("project");
        allowedCommands.insert("set");
        allowedCommands.insert("if");
        allowedCommands.insert("endif");
        allowedCommands.insert("else");
        allowedCommands.insert("elseif");
        allowedCommands.insert("add_executable");
        allowedCommands.insert("add_library");
        allowedCommands.insert("target_link_libraries");
        allowedCommands.insert("option");
        allowedCommands.insert("message");
        isProblem = false;
202
        for(std::vector<cmListFileFunction>::iterator i
203
204
205
206
207
208
              = this->Functions.begin();
            i != this->Functions.end(); ++i)
        {
          std::string name = cmSystemTools::LowerCase(i->Name);
          if (allowedCommands.find(name) == allowedCommands.end())
          {
Ken Martin's avatar
Ken Martin committed
209
210
            isProblem = true;
            break;
211
          }
212
213
        }
      }
214

215
      if (isProblem)
216
      {
217
218
219
220
221
222
      // Tell the top level cmMakefile to diagnose
      // this violation of CMP0000.
      mf->SetCheckCMP0000(true);

      // Implicitly set the version for the user.
      mf->SetPolicyVersion("2.4");
223
      }
224
    }
225
226
227
  }

  if(topLevel)
228
229
230
    {
    bool hasProject = false;
    // search for a project command
231
    for(std::vector<cmListFileFunction>::iterator i
Ken Martin's avatar
Ken Martin committed
232
233
          = this->Functions.begin();
        i != this->Functions.end(); ++i)
234
      {
Ken Martin's avatar
Ken Martin committed
235
      if(cmSystemTools::LowerCase(i->Name) == "project")
236
237
238
239
240
241
242
243
244
        {
        hasProject = true;
        break;
        }
      }
    // if no project command is found, add one
    if(!hasProject)
      {
      cmListFileFunction project;
Ken Martin's avatar
Ken Martin committed
245
      project.Name = "PROJECT";
246
247
      cmListFileArgument prj("Project", cmListFileArgument::Unquoted,
                             filename, 0);
Ken Martin's avatar
Ken Martin committed
248
249
      project.Arguments.push_back(prj);
      this->Functions.insert(this->Functions.begin(),project);
250
251
      }
    }
252
253
254
255
  if(parseError)
    {
    return false;
    }
256
257
  return true;
}
258

259
260
//----------------------------------------------------------------------------
bool cmListFileParser::ParseFunction(const char* name, long line)
261
{
262
263
264
265
266
267
  // Inintialize a new function call.
  this->Function = cmListFileFunction();
  this->Function.FilePath = this->FileName;
  this->Function.Name = name;
  this->Function.Line = line;

268
269
  // Command name has already been parsed.  Read the left paren.
  cmListFileLexer_Token* token;
270
271
272
  while((token = cmListFileLexer_Scan(this->Lexer)) &&
        token->type == cmListFileLexer_Token_Space) {}
  if(!token)
273
    {
274
    cmOStringStream error;
275
276
    error << "Error in cmake code at\n" << this->FileName << ":"
          << cmListFileLexer_GetCurrentLine(this->Lexer) << ":\n"
277
278
279
          << "Parse error.  Function missing opening \"(\".";
    cmSystemTools::Error(error.str().c_str());
    return false;
280
    }
281
  if(token->type != cmListFileLexer_Token_ParenLeft)
282
    {
283
    cmOStringStream error;
284
285
    error << "Error in cmake code at\n" << this->FileName << ":"
          << cmListFileLexer_GetCurrentLine(this->Lexer) << ":\n"
286
          << "Parse error.  Expected \"(\", got "
287
          << cmListFileLexer_GetTypeAsString(this->Lexer, token->type)
288
          << " with text \"" << token->text << "\".";
289
    cmSystemTools::Error(error.str().c_str());
290
291
    return false;
    }
292
293

  // Arguments.
294
  unsigned long lastLine;
295
  unsigned long parenDepth = 0;
296
297
298
  this->Separation = SeparationOkay;
  while((lastLine = cmListFileLexer_GetCurrentLine(this->Lexer),
         token = cmListFileLexer_Scan(this->Lexer)))
299
    {
300
301
302
303
304
305
    if(token->type == cmListFileLexer_Token_Space ||
       token->type == cmListFileLexer_Token_Newline)
      {
      this->Separation = SeparationOkay;
      continue;
      }
306
    if(token->type == cmListFileLexer_Token_ParenLeft)
307
      {
308
      parenDepth++;
309
      this->Separation = SeparationOkay;
310
311
312
313
      if(!this->AddArgument(token, cmListFileArgument::Unquoted))
        {
        return false;
        }
314
315
316
317
318
319
320
321
      }
    else if(token->type == cmListFileLexer_Token_ParenRight)
      {
      if (parenDepth == 0)
        {
        return true;
        }
      parenDepth--;
322
      this->Separation = SeparationOkay;
323
324
325
326
      if(!this->AddArgument(token, cmListFileArgument::Unquoted))
        {
        return false;
        }
327
      this->Separation = SeparationWarning;
328
      }
329
330
    else if(token->type == cmListFileLexer_Token_Identifier ||
            token->type == cmListFileLexer_Token_ArgumentUnquoted)
331
      {
332
333
334
335
      if(!this->AddArgument(token, cmListFileArgument::Unquoted))
        {
        return false;
        }
336
      this->Separation = SeparationWarning;
337
      }
338
    else if(token->type == cmListFileLexer_Token_ArgumentQuoted)
339
      {
340
341
342
343
      if(!this->AddArgument(token, cmListFileArgument::Quoted))
        {
        return false;
        }
344
      this->Separation = SeparationWarning;
345
      }
346
347
348
349
350
351
352
353
354
355
356
357
    else if(token->type == cmListFileLexer_Token_ArgumentBracket)
      {
      if(!this->AddArgument(token, cmListFileArgument::Bracket))
        {
        return false;
        }
      this->Separation = SeparationError;
      }
    else if(token->type == cmListFileLexer_Token_CommentBracket)
      {
      this->Separation = SeparationError;
      }
358
    else
359
      {
360
      // Error.
361
      cmOStringStream error;
362
363
      error << "Error in cmake code at\n" << this->FileName << ":"
            << cmListFileLexer_GetCurrentLine(this->Lexer) << ":\n"
364
            << "Parse error.  Function missing ending \")\".  "
365
            << "Instead found "
366
            << cmListFileLexer_GetTypeAsString(this->Lexer, token->type)
367
            << " with text \"" << token->text << "\".";
368
      cmSystemTools::Error(error.str().c_str());
369
370
371
372
      return false;
      }
    }

373
374
  cmOStringStream error;
  error << "Error in cmake code at\n"
375
        << this->FileName << ":" << lastLine << ":\n"
376
377
378
        << "Parse error.  Function missing ending \")\".  "
        << "End of file reached.";
  cmSystemTools::Error(error.str().c_str());
379

380
  return false;
381
}
382

383
//----------------------------------------------------------------------------
384
bool cmListFileParser::AddArgument(cmListFileLexer_Token* token,
385
386
387
388
                                   cmListFileArgument::Delimiter delim)
{
  cmListFileArgument a(token->text, delim, this->FileName, token->line);
  this->Function.Arguments.push_back(a);
389
390
  if(this->Separation == SeparationOkay)
    {
391
    return true;
392
    }
393
394
  bool isError = (this->Separation == SeparationError ||
                  delim == cmListFileArgument::Bracket);
395
  cmOStringStream m;
396
  m << "Syntax " << (isError? "Error":"Warning") << " in cmake code at\n"
397
398
399
    << "  " << this->FileName << ":" << token->line << ":"
    << token->column << "\n"
    << "Argument not separated from preceding token by whitespace.";
400
401
  if(isError)
    {
Stephen Kelly's avatar
Stephen Kelly committed
402
    this->Makefile->IssueMessage(cmake::FATAL_ERROR, m.str());
403
404
405
406
    return false;
    }
  else
    {
Stephen Kelly's avatar
Stephen Kelly committed
407
    this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, m.str());
408
409
    return true;
    }
410
411
}

412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
//----------------------------------------------------------------------------
void cmListFileBacktrace::MakeRelative()
{
  if (this->Relative)
    {
    return;
    }
  for (cmListFileBacktrace::iterator i = this->begin();
       i != this->end(); ++i)
    {
    i->FilePath = this->LocalGenerator->Convert(i->FilePath,
                                                cmLocalGenerator::HOME);
    }
  this->Relative = true;
}


429
430
431
432
433
434
435
436
437
438
439
440
441
442
//----------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& os, cmListFileContext const& lfc)
{
  os << lfc.FilePath;
  if(lfc.Line)
    {
    os << ":" << lfc.Line;
    if(!lfc.Name.empty())
      {
      os << " (" << lfc.Name << ")";
      }
    }
  return os;
}