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