Imported Upstream version 2.8.9
[platform/upstream/cmake.git] / Source / cmDependsC.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 "cmDependsC.h"
13
14 #include "cmFileTimeComparison.h"
15 #include "cmLocalGenerator.h"
16 #include "cmMakefile.h"
17 #include "cmSystemTools.h"
18
19 #include <ctype.h> // isspace
20
21
22 #define INCLUDE_REGEX_LINE \
23   "^[ \t]*#[ \t]*(include|import)[ \t]*[<\"]([^\">]+)([\">])"
24
25 #define INCLUDE_REGEX_LINE_MARKER "#IncludeRegexLine: "
26 #define INCLUDE_REGEX_SCAN_MARKER "#IncludeRegexScan: "
27 #define INCLUDE_REGEX_COMPLAIN_MARKER "#IncludeRegexComplain: "
28 #define INCLUDE_REGEX_TRANSFORM_MARKER "#IncludeRegexTransform: "
29
30 //----------------------------------------------------------------------------
31 cmDependsC::cmDependsC()
32 : ValidDeps(0)
33 {
34 }
35
36 //----------------------------------------------------------------------------
37 cmDependsC::cmDependsC(cmLocalGenerator* lg,
38                    const char* targetDir,
39                    const char* lang,
40                    const std::map<std::string, DependencyVector>* validDeps)
41 : cmDepends(lg, targetDir)
42 , ValidDeps(validDeps)
43 {
44   cmMakefile* mf = lg->GetMakefile();
45
46   // Configure the include file search path.
47   this->SetIncludePathFromLanguage(lang);
48
49   // Configure regular expressions.
50   std::string scanRegex = "^.*$";
51   std::string complainRegex = "^$";
52   {
53   std::string scanRegexVar = "CMAKE_";
54   scanRegexVar += lang;
55   scanRegexVar += "_INCLUDE_REGEX_SCAN";
56   if(const char* sr = mf->GetDefinition(scanRegexVar.c_str()))
57     {
58     scanRegex = sr;
59     }
60   std::string complainRegexVar = "CMAKE_";
61   complainRegexVar += lang;
62   complainRegexVar += "_INCLUDE_REGEX_COMPLAIN";
63   if(const char* cr = mf->GetDefinition(complainRegexVar.c_str()))
64     {
65     complainRegex = cr;
66     }
67   }
68
69   this->IncludeRegexLine.compile(INCLUDE_REGEX_LINE);
70   this->IncludeRegexScan.compile(scanRegex.c_str());
71   this->IncludeRegexComplain.compile(complainRegex.c_str());
72   this->IncludeRegexLineString = INCLUDE_REGEX_LINE_MARKER INCLUDE_REGEX_LINE;
73   this->IncludeRegexScanString = INCLUDE_REGEX_SCAN_MARKER;
74   this->IncludeRegexScanString += scanRegex;
75   this->IncludeRegexComplainString = INCLUDE_REGEX_COMPLAIN_MARKER;
76   this->IncludeRegexComplainString += complainRegex;
77
78   this->SetupTransforms();
79
80   this->CacheFileName = this->TargetDirectory;
81   this->CacheFileName += "/";
82   this->CacheFileName += lang;
83   this->CacheFileName += ".includecache";
84
85   this->ReadCacheFile();
86 }
87
88 //----------------------------------------------------------------------------
89 cmDependsC::~cmDependsC()
90 {
91   this->WriteCacheFile();
92
93   for (std::map<cmStdString, cmIncludeLines*>::iterator it=
94          this->FileCache.begin(); it!=this->FileCache.end(); ++it)
95     {
96     delete it->second;
97     }
98 }
99
100 //----------------------------------------------------------------------------
101 bool cmDependsC::WriteDependencies(const char *src, const char *obj,
102   std::ostream& makeDepends, std::ostream& internalDepends)
103 {
104   // Make sure this is a scanning instance.
105   if(!src || src[0] == '\0')
106     {
107     cmSystemTools::Error("Cannot scan dependencies without a source file.");
108     return false;
109     }
110   if(!obj || obj[0] == '\0')
111     {
112     cmSystemTools::Error("Cannot scan dependencies without an object file.");
113     return false;
114     }
115
116   if (this->ValidDeps != 0)
117     {
118     std::map<std::string, DependencyVector>::const_iterator tmpIt =
119                                                     this->ValidDeps->find(obj);
120     if (tmpIt!= this->ValidDeps->end())
121       {
122       // Write the dependencies to the output stream.  Makefile rules
123       // written by the original local generator for this directory
124       // convert the dependencies to paths relative to the home output
125       // directory.  We must do the same here.
126       internalDepends << obj << std::endl;
127       for(DependencyVector::const_iterator i=tmpIt->second.begin();
128          i != tmpIt->second.end(); ++i)
129         {
130         makeDepends << obj << ": " <<
131            this->LocalGenerator->Convert(i->c_str(),
132                                          cmLocalGenerator::HOME_OUTPUT,
133                                          cmLocalGenerator::MAKEFILE)
134            << std::endl;
135         internalDepends << " " << i->c_str() << std::endl;
136         }
137       makeDepends << std::endl;
138       return true;
139       }
140     }
141
142   // Walk the dependency graph starting with the source file.
143   bool first = true;
144   UnscannedEntry root;
145   root.FileName = src;
146   this->Unscanned.push(root);
147   this->Encountered.clear();
148   this->Encountered.insert(src);
149   std::set<cmStdString> dependencies;
150   std::set<cmStdString> scanned;
151
152   // Use reserve to allocate enough memory for tempPathStr
153   // so that during the loops no memory is allocated or freed
154   std::string tempPathStr;
155   tempPathStr.reserve(4*1024);
156
157   while(!this->Unscanned.empty())
158     {
159     // Get the next file to scan.
160     UnscannedEntry current = this->Unscanned.front();
161     this->Unscanned.pop();
162
163     // If not a full path, find the file in the include path.
164     std::string fullName;
165     if(first || cmSystemTools::FileIsFullPath(current.FileName.c_str()))
166       {
167       if(cmSystemTools::FileExists(current.FileName.c_str(), true))
168         {
169         fullName = current.FileName;
170         }
171       }
172     else if(!current.QuotedLocation.empty() &&
173             cmSystemTools::FileExists(current.QuotedLocation.c_str(), true))
174       {
175       // The include statement producing this entry was a double-quote
176       // include and the included file is present in the directory of
177       // the source containing the include statement.
178       fullName = current.QuotedLocation;
179       }
180     else
181       {
182       std::map<cmStdString, cmStdString>::iterator
183         headerLocationIt=this->HeaderLocationCache.find(current.FileName);
184       if (headerLocationIt!=this->HeaderLocationCache.end())
185         {
186         fullName=headerLocationIt->second;
187         }
188       else for(std::vector<std::string>::const_iterator i =
189             this->IncludePath.begin(); i != this->IncludePath.end(); ++i)
190         {
191         // Construct the name of the file as if it were in the current
192         // include directory.  Avoid using a leading "./".
193
194         tempPathStr = "";
195         if((*i) == ".")
196           {
197           tempPathStr += current.FileName;
198           }
199         else
200           {
201           tempPathStr += *i;
202           tempPathStr+="/";
203           tempPathStr+=current.FileName;
204           }
205
206         // Look for the file in this location.
207         if(cmSystemTools::FileExists(tempPathStr.c_str(), true))
208           {
209           fullName = tempPathStr;
210           HeaderLocationCache[current.FileName]=fullName;
211           break;
212           }
213         }
214       }
215
216     // Complain if the file cannot be found and matches the complain
217     // regex.
218     if(fullName.empty() &&
219        this->IncludeRegexComplain.find(current.FileName.c_str()))
220       {
221       cmSystemTools::Error("Cannot find file \"",
222                            current.FileName.c_str(), "\".");
223       return false;
224       }
225
226     // Scan the file if it was found and has not been scanned already.
227     if(!fullName.empty() && (scanned.find(fullName) == scanned.end()))
228       {
229       // Record scanned files.
230       scanned.insert(fullName);
231
232       // Check whether this file is already in the cache
233       std::map<cmStdString, cmIncludeLines*>::iterator fileIt=
234         this->FileCache.find(fullName);
235       if (fileIt!=this->FileCache.end())
236         {
237         fileIt->second->Used=true;
238         dependencies.insert(fullName);
239         for (std::vector<UnscannedEntry>::const_iterator incIt=
240                fileIt->second->UnscannedEntries.begin();
241              incIt!=fileIt->second->UnscannedEntries.end(); ++incIt)
242           {
243           if (this->Encountered.find(incIt->FileName) ==
244               this->Encountered.end())
245             {
246             this->Encountered.insert(incIt->FileName);
247             this->Unscanned.push(*incIt);
248             }
249           }
250         }
251       else
252         {
253
254         // Try to scan the file.  Just leave it out if we cannot find
255         // it.
256         std::ifstream fin(fullName.c_str());
257         if(fin)
258           {
259           // Add this file as a dependency.
260           dependencies.insert(fullName);
261
262           // Scan this file for new dependencies.  Pass the directory
263           // containing the file to handle double-quote includes.
264           std::string dir = cmSystemTools::GetFilenamePath(fullName);
265           this->Scan(fin, dir.c_str(), fullName);
266           }
267         }
268       }
269
270     first = false;
271     }
272
273   // Write the dependencies to the output stream.  Makefile rules
274   // written by the original local generator for this directory
275   // convert the dependencies to paths relative to the home output
276   // directory.  We must do the same here.
277   internalDepends << obj << std::endl;
278   for(std::set<cmStdString>::iterator i=dependencies.begin();
279       i != dependencies.end(); ++i)
280     {
281     makeDepends << obj << ": " <<
282       this->LocalGenerator->Convert(i->c_str(),
283                                     cmLocalGenerator::HOME_OUTPUT,
284                                     cmLocalGenerator::MAKEFILE)
285                 << std::endl;
286     internalDepends << " " << i->c_str() << std::endl;
287     }
288   makeDepends << std::endl;
289
290   return true;
291 }
292
293 //----------------------------------------------------------------------------
294 void cmDependsC::ReadCacheFile()
295 {
296   if(this->CacheFileName.size() == 0)
297     {
298     return;
299     }
300   std::ifstream fin(this->CacheFileName.c_str());
301   if(!fin)
302     {
303     return;
304     }
305
306   std::string line;
307   cmIncludeLines* cacheEntry=0;
308   bool haveFileName=false;
309
310   while(cmSystemTools::GetLineFromStream(fin, line))
311     {
312     if (line.empty())
313       {
314       cacheEntry=0;
315       haveFileName=false;
316       continue;
317       }
318     //the first line after an empty line is the name of the parsed file
319     if (haveFileName==false)
320       {
321       haveFileName=true;
322       int newer=0;
323       cmFileTimeComparison comp;
324       bool res=comp.FileTimeCompare(this->CacheFileName.c_str(),
325                                     line.c_str(), &newer);
326
327       if ((res==true) && (newer==1)) //cache is newer than the parsed file
328         {
329         cacheEntry=new cmIncludeLines;
330         this->FileCache[line]=cacheEntry;
331         }
332       // file doesn't exist, check that the regular expressions
333       // haven't changed
334       else if (res==false)
335         {
336         if (line.find(INCLUDE_REGEX_LINE_MARKER) == 0)
337           {
338           if (line != this->IncludeRegexLineString)
339             {
340             return;
341             }
342           }
343         else if (line.find(INCLUDE_REGEX_SCAN_MARKER) == 0)
344           {
345           if (line != this->IncludeRegexScanString)
346             {
347             return;
348             }
349           }
350         else if (line.find(INCLUDE_REGEX_COMPLAIN_MARKER) == 0)
351           {
352           if (line != this->IncludeRegexComplainString)
353             {
354             return;
355             }
356           }
357         else if (line.find(INCLUDE_REGEX_TRANSFORM_MARKER) == 0)
358           {
359           if (line != this->IncludeRegexTransformString)
360             {
361             return;
362             }
363           }
364         }
365       }
366     else if (cacheEntry!=0)
367       {
368       UnscannedEntry entry;
369       entry.FileName = line;
370       if (cmSystemTools::GetLineFromStream(fin, line))
371         {
372         if (line!="-")
373           {
374           entry.QuotedLocation=line;
375           }
376         cacheEntry->UnscannedEntries.push_back(entry);
377         }
378       }
379     }
380 }
381
382 //----------------------------------------------------------------------------
383 void cmDependsC::WriteCacheFile() const
384 {
385   if(this->CacheFileName.size() == 0)
386     {
387     return;
388     }
389   std::ofstream cacheOut(this->CacheFileName.c_str());
390   if(!cacheOut)
391     {
392     return;
393     }
394
395   cacheOut << this->IncludeRegexLineString << "\n\n";
396   cacheOut << this->IncludeRegexScanString << "\n\n";
397   cacheOut << this->IncludeRegexComplainString << "\n\n";
398   cacheOut << this->IncludeRegexTransformString << "\n\n";
399
400   for (std::map<cmStdString, cmIncludeLines*>::const_iterator fileIt=
401          this->FileCache.begin();
402        fileIt!=this->FileCache.end(); ++fileIt)
403     {
404     if (fileIt->second->Used)
405       {
406       cacheOut<<fileIt->first.c_str()<<std::endl;
407
408       for (std::vector<UnscannedEntry>::const_iterator
409              incIt=fileIt->second->UnscannedEntries.begin();
410            incIt!=fileIt->second->UnscannedEntries.end(); ++incIt)
411         {
412         cacheOut<<incIt->FileName.c_str()<<std::endl;
413         if (incIt->QuotedLocation.empty())
414           {
415           cacheOut<<"-"<<std::endl;
416           }
417         else
418           {
419           cacheOut<<incIt->QuotedLocation.c_str()<<std::endl;
420           }
421         }
422       cacheOut<<std::endl;
423       }
424    }
425 }
426
427 //----------------------------------------------------------------------------
428 void cmDependsC::Scan(std::istream& is, const char* directory,
429   const cmStdString& fullName)
430 {
431   cmIncludeLines* newCacheEntry=new cmIncludeLines;
432   newCacheEntry->Used=true;
433   this->FileCache[fullName]=newCacheEntry;
434
435   // Read one line at a time.
436   std::string line;
437   while(cmSystemTools::GetLineFromStream(is, line))
438     {
439     // Transform the line content first.
440     if(!this->TransformRules.empty())
441       {
442       this->TransformLine(line);
443       }
444
445     // Match include directives.
446     if(this->IncludeRegexLine.find(line.c_str()))
447       {
448       // Get the file being included.
449       UnscannedEntry entry;
450       entry.FileName = this->IncludeRegexLine.match(2);
451       cmSystemTools::ConvertToUnixSlashes(entry.FileName);
452       if(this->IncludeRegexLine.match(3) == "\"" &&
453          !cmSystemTools::FileIsFullPath(entry.FileName.c_str()))
454         {
455         // This was a double-quoted include with a relative path.  We
456         // must check for the file in the directory containing the
457         // file we are scanning.
458         entry.QuotedLocation = directory;
459         entry.QuotedLocation += "/";
460         entry.QuotedLocation += entry.FileName;
461         }
462
463       // Queue the file if it has not yet been encountered and it
464       // matches the regular expression for recursive scanning.  Note
465       // that this check does not account for the possibility of two
466       // headers with the same name in different directories when one
467       // is included by double-quotes and the other by angle brackets.
468       // It also does not work properly if two header files with the same
469       // name exist in different directories, and both are included from a
470       // file their own directory by simply using "filename.h" (#12619)
471       // This kind of problem will be fixed when a more
472       // preprocessor-like implementation of this scanner is created.
473       if (this->IncludeRegexScan.find(entry.FileName.c_str()))
474         {
475         newCacheEntry->UnscannedEntries.push_back(entry);
476         if(this->Encountered.find(entry.FileName) == this->Encountered.end())
477           {
478           this->Encountered.insert(entry.FileName);
479           this->Unscanned.push(entry);
480           }
481         }
482       }
483     }
484 }
485
486 //----------------------------------------------------------------------------
487 void cmDependsC::SetupTransforms()
488 {
489   // Get the transformation rules.
490   std::vector<std::string> transformRules;
491   cmMakefile* mf = this->LocalGenerator->GetMakefile();
492   if(const char* xform =
493      mf->GetDefinition("CMAKE_INCLUDE_TRANSFORMS"))
494     {
495     cmSystemTools::ExpandListArgument(xform, transformRules, true);
496     }
497   for(std::vector<std::string>::const_iterator tri = transformRules.begin();
498       tri != transformRules.end(); ++tri)
499     {
500     this->ParseTransform(*tri);
501     }
502
503   this->IncludeRegexTransformString = INCLUDE_REGEX_TRANSFORM_MARKER;
504   if(!this->TransformRules.empty())
505     {
506     // Construct the regular expression to match lines to be
507     // transformed.
508     std::string xform = "^([ \t]*#[ \t]*(include|import)[ \t]*)(";
509     const char* sep = "";
510     for(TransformRulesType::const_iterator tri = this->TransformRules.begin();
511         tri != this->TransformRules.end(); ++tri)
512       {
513       xform += sep;
514       xform += tri->first;
515       sep = "|";
516       }
517     xform += ")[ \t]*\\(([^),]*)\\)";
518     this->IncludeRegexTransform.compile(xform.c_str());
519
520     // Build a string that encodes all transformation rules and will
521     // change when rules are changed.
522     this->IncludeRegexTransformString += xform;
523     for(TransformRulesType::const_iterator tri = this->TransformRules.begin();
524         tri != this->TransformRules.end(); ++tri)
525       {
526       this->IncludeRegexTransformString += " ";
527       this->IncludeRegexTransformString += tri->first;
528       this->IncludeRegexTransformString += "(%)=";
529       this->IncludeRegexTransformString += tri->second;
530       }
531     }
532 }
533
534 //----------------------------------------------------------------------------
535 void cmDependsC::ParseTransform(std::string const& xform)
536 {
537   // A transform rule is of the form SOME_MACRO(%)=value-with-%
538   // We can simply separate with "(%)=".
539   std::string::size_type pos = xform.find("(%)=");
540   if(pos == xform.npos || pos == 0)
541     {
542     return;
543     }
544   std::string name = xform.substr(0, pos);
545   std::string value = xform.substr(pos+4, xform.npos);
546   this->TransformRules[name] = value;
547 }
548
549 //----------------------------------------------------------------------------
550 void cmDependsC::TransformLine(std::string& line)
551 {
552   // Check for a transform rule match.  Return if none.
553   if(!this->IncludeRegexTransform.find(line.c_str()))
554     {
555     return;
556     }
557   TransformRulesType::const_iterator tri =
558     this->TransformRules.find(this->IncludeRegexTransform.match(3));
559   if(tri == this->TransformRules.end())
560     {
561     return;
562     }
563
564   // Construct the transformed line.
565   std::string newline = this->IncludeRegexTransform.match(1);
566   std::string arg = this->IncludeRegexTransform.match(4);
567   for(const char* c = tri->second.c_str(); *c; ++c)
568     {
569     if(*c == '%')
570       {
571       newline += arg;
572       }
573     else
574       {
575       newline += *c;
576       }
577     }
578
579   // Return the transformed line.
580   line = newline;
581 }