CVS resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Source / cmDependsC.cxx
blobb4f73f0991ff61b9e78b4011a72055d1db465026
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmDependsC.cxx,v $
5 Language: C++
6 Date: $Date: 2007/12/15 01:31:27 $
7 Version: $Revision: 1.33 $
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 "cmDependsC.h"
19 #include "cmFileTimeComparison.h"
20 #include "cmLocalGenerator.h"
21 #include "cmSystemTools.h"
23 #include <ctype.h> // isspace
26 #define INCLUDE_REGEX_LINE \
27 "^[ \t]*#[ \t]*(include|import)[ \t]*[<\"]([^\">]+)([\">])"
29 #define INCLUDE_REGEX_LINE_MARKER "#IncludeRegexLine: "
30 #define INCLUDE_REGEX_SCAN_MARKER "#IncludeRegexScan: "
31 #define INCLUDE_REGEX_COMPLAIN_MARKER "#IncludeRegexComplain: "
33 //----------------------------------------------------------------------------
34 cmDependsC::cmDependsC():
35 IncludePath(0)
38 //----------------------------------------------------------------------------
39 // yummy look at all those constructor arguments
40 cmDependsC::cmDependsC(std::vector<std::string> const& includes,
41 const char* scanRegex, const char* complainRegex,
42 const cmStdString& cacheFileName):
43 IncludePath(&includes),
44 IncludeRegexLine(INCLUDE_REGEX_LINE),
45 IncludeRegexScan(scanRegex),
46 IncludeRegexComplain(complainRegex),
47 IncludeRegexLineString(INCLUDE_REGEX_LINE_MARKER INCLUDE_REGEX_LINE),
48 IncludeRegexScanString(std::string(INCLUDE_REGEX_SCAN_MARKER)+scanRegex),
49 IncludeRegexComplainString(
50 std::string(INCLUDE_REGEX_COMPLAIN_MARKER)+complainRegex),
51 CacheFileName(cacheFileName)
53 this->ReadCacheFile();
56 //----------------------------------------------------------------------------
57 cmDependsC::~cmDependsC()
59 this->WriteCacheFile();
61 for (std::map<cmStdString, cmIncludeLines*>::iterator it=
62 this->FileCache.begin(); it!=this->FileCache.end(); ++it)
64 delete it->second;
68 //----------------------------------------------------------------------------
69 bool cmDependsC::WriteDependencies(const char *src, const char *obj,
70 std::ostream& makeDepends, std::ostream& internalDepends)
72 // Make sure this is a scanning instance.
73 if(!src || src[0] == '\0')
75 cmSystemTools::Error("Cannot scan dependencies without a source file.");
76 return false;
78 if(!obj || obj[0] == '\0')
80 cmSystemTools::Error("Cannot scan dependencies without an object file.");
81 return false;
83 if(!this->IncludePath)
85 cmSystemTools::Error("Cannot scan dependencies without an include path.");
86 return false;
89 // Walk the dependency graph starting with the source file.
90 bool first = true;
91 UnscannedEntry root;
92 root.FileName = src;
93 this->Unscanned.push(root);
94 this->Encountered.clear();
95 this->Encountered.insert(src);
96 std::set<cmStdString> dependencies;
97 std::set<cmStdString> scanned;
99 // Use reserve to allocate enough memory for both strings,
100 // so that during the loops no memory is allocated or freed
101 std::string cacheKey;
102 cacheKey.reserve(4*1024);
103 std::string tempPathStr;
104 tempPathStr.reserve(4*1024);
106 while(!this->Unscanned.empty())
108 // Get the next file to scan.
109 UnscannedEntry current = this->Unscanned.front();
110 this->Unscanned.pop();
112 // If not a full path, find the file in the include path.
113 std::string fullName;
114 if(first || cmSystemTools::FileIsFullPath(current.FileName.c_str()))
116 if(cmSystemTools::FileExists(current.FileName.c_str(), true))
118 fullName = current.FileName;
121 else if(!current.QuotedLocation.empty() &&
122 cmSystemTools::FileExists(current.QuotedLocation.c_str(), true))
124 // The include statement producing this entry was a double-quote
125 // include and the included file is present in the directory of
126 // the source containing the include statement.
127 fullName = current.QuotedLocation;
129 else
131 // With GCC distribution of STL, assigning to a string directly
132 // throws away the internal buffer of the left-hand-side. We
133 // want to keep the pre-allocated buffer so we use C-style
134 // string assignment and then operator+=. We could call
135 // .clear() instead of assigning to an empty string but the
136 // method does not exist on some older compilers.
137 cacheKey = "";
138 cacheKey += current.FileName;
140 for(std::vector<std::string>::const_iterator i =
141 this->IncludePath->begin(); i != this->IncludePath->end(); ++i)
143 cacheKey+=*i;
145 std::map<cmStdString, cmStdString>::iterator
146 headerLocationIt=this->HeaderLocationCache.find(cacheKey);
147 if (headerLocationIt!=this->HeaderLocationCache.end())
149 fullName=headerLocationIt->second;
151 else for(std::vector<std::string>::const_iterator i =
152 this->IncludePath->begin(); i != this->IncludePath->end(); ++i)
154 // Construct the name of the file as if it were in the current
155 // include directory. Avoid using a leading "./".
157 tempPathStr = "";
158 if((*i) == ".")
160 tempPathStr += current.FileName;
162 else
164 tempPathStr += *i;
165 tempPathStr+="/";
166 tempPathStr+=current.FileName;
169 // Look for the file in this location.
170 if(cmSystemTools::FileExists(tempPathStr.c_str(), true))
172 fullName = tempPathStr;
173 HeaderLocationCache[cacheKey]=fullName;
174 break;
179 // Complain if the file cannot be found and matches the complain
180 // regex.
181 if(fullName.empty() &&
182 this->IncludeRegexComplain.find(current.FileName.c_str()))
184 cmSystemTools::Error("Cannot find file \"",
185 current.FileName.c_str(), "\".");
186 return false;
189 // Scan the file if it was found and has not been scanned already.
190 if(!fullName.empty() && (scanned.find(fullName) == scanned.end()))
192 // Record scanned files.
193 scanned.insert(fullName);
195 // Check whether this file is already in the cache
196 std::map<cmStdString, cmIncludeLines*>::iterator fileIt=
197 this->FileCache.find(fullName);
198 if (fileIt!=this->FileCache.end())
200 fileIt->second->Used=true;
201 dependencies.insert(fullName);
202 for (std::vector<UnscannedEntry>::const_iterator incIt=
203 fileIt->second->UnscannedEntries.begin();
204 incIt!=fileIt->second->UnscannedEntries.end(); ++incIt)
206 if (this->Encountered.find(incIt->FileName) ==
207 this->Encountered.end())
209 this->Encountered.insert(incIt->FileName);
210 this->Unscanned.push(*incIt);
214 else
217 // Try to scan the file. Just leave it out if we cannot find
218 // it.
219 std::ifstream fin(fullName.c_str());
220 if(fin)
222 // Add this file as a dependency.
223 dependencies.insert(fullName);
225 // Scan this file for new dependencies. Pass the directory
226 // containing the file to handle double-quote includes.
227 std::string dir = cmSystemTools::GetFilenamePath(fullName);
228 this->Scan(fin, dir.c_str(), fullName);
233 first = false;
236 // Write the dependencies to the output stream. Makefile rules
237 // written by the original local generator for this directory
238 // convert the dependencies to paths relative to the home output
239 // directory. We must do the same here.
240 internalDepends << obj << std::endl;
241 for(std::set<cmStdString>::iterator i=dependencies.begin();
242 i != dependencies.end(); ++i)
244 makeDepends << obj << ": " <<
245 this->LocalGenerator->Convert(i->c_str(),
246 cmLocalGenerator::HOME_OUTPUT,
247 cmLocalGenerator::MAKEFILE)
248 << std::endl;
249 internalDepends << " " << i->c_str() << std::endl;
251 makeDepends << std::endl;
253 return true;
256 //----------------------------------------------------------------------------
257 void cmDependsC::ReadCacheFile()
259 if(this->CacheFileName.size() == 0)
261 return;
263 std::ifstream fin(this->CacheFileName.c_str());
264 if(!fin)
266 return;
269 std::string line;
270 cmIncludeLines* cacheEntry=0;
271 bool haveFileName=false;
273 while(cmSystemTools::GetLineFromStream(fin, line))
275 if (line.empty())
277 cacheEntry=0;
278 haveFileName=false;
279 continue;
281 //the first line after an empty line is the name of the parsed file
282 if (haveFileName==false)
284 haveFileName=true;
285 int newer=0;
286 cmFileTimeComparison comp;
287 bool res=comp.FileTimeCompare(this->CacheFileName.c_str(),
288 line.c_str(), &newer);
290 if ((res==true) && (newer==1)) //cache is newer than the parsed file
292 cacheEntry=new cmIncludeLines;
293 this->FileCache[line]=cacheEntry;
295 // file doesn't exist, check that the regular expressions
296 // haven't changed
297 else if (res==false)
299 if (line.find(INCLUDE_REGEX_LINE_MARKER) == 0)
301 if (line != this->IncludeRegexLineString)
303 return;
306 else if (line.find(INCLUDE_REGEX_SCAN_MARKER) == 0)
308 if (line != this->IncludeRegexScanString)
310 return;
313 else if (line.find(INCLUDE_REGEX_COMPLAIN_MARKER) == 0)
315 if (line != this->IncludeRegexComplainString)
317 return;
322 else if (cacheEntry!=0)
324 UnscannedEntry entry;
325 entry.FileName = line;
326 if (cmSystemTools::GetLineFromStream(fin, line))
328 if (line!="-")
330 entry.QuotedLocation=line;
332 cacheEntry->UnscannedEntries.push_back(entry);
338 //----------------------------------------------------------------------------
339 void cmDependsC::WriteCacheFile() const
341 if(this->CacheFileName.size() == 0)
343 return;
345 std::ofstream cacheOut(this->CacheFileName.c_str());
346 if(!cacheOut)
348 return;
351 cacheOut << this->IncludeRegexLineString << "\n\n";
352 cacheOut << this->IncludeRegexScanString << "\n\n";
353 cacheOut << this->IncludeRegexComplainString << "\n\n";
355 for (std::map<cmStdString, cmIncludeLines*>::const_iterator fileIt=
356 this->FileCache.begin();
357 fileIt!=this->FileCache.end(); ++fileIt)
359 if (fileIt->second->Used)
361 cacheOut<<fileIt->first.c_str()<<std::endl;
363 for (std::vector<UnscannedEntry>::const_iterator
364 incIt=fileIt->second->UnscannedEntries.begin();
365 incIt!=fileIt->second->UnscannedEntries.end(); ++incIt)
367 cacheOut<<incIt->FileName.c_str()<<std::endl;
368 if (incIt->QuotedLocation.empty())
370 cacheOut<<"-"<<std::endl;
372 else
374 cacheOut<<incIt->QuotedLocation.c_str()<<std::endl;
377 cacheOut<<std::endl;
382 //----------------------------------------------------------------------------
383 void cmDependsC::Scan(std::istream& is, const char* directory,
384 const cmStdString& fullName)
386 cmIncludeLines* newCacheEntry=new cmIncludeLines;
387 newCacheEntry->Used=true;
388 this->FileCache[fullName]=newCacheEntry;
390 // Read one line at a time.
391 std::string line;
392 while(cmSystemTools::GetLineFromStream(is, line))
394 // Match include directives.
395 if(this->IncludeRegexLine.find(line.c_str()))
397 // Get the file being included.
398 UnscannedEntry entry;
399 entry.FileName = this->IncludeRegexLine.match(2);
400 if(this->IncludeRegexLine.match(3) == "\"" &&
401 !cmSystemTools::FileIsFullPath(entry.FileName.c_str()))
403 // This was a double-quoted include with a relative path. We
404 // must check for the file in the directory containing the
405 // file we are scanning.
406 entry.QuotedLocation = directory;
407 entry.QuotedLocation += "/";
408 entry.QuotedLocation += entry.FileName;
411 // Queue the file if it has not yet been encountered and it
412 // matches the regular expression for recursive scanning. Note
413 // that this check does not account for the possibility of two
414 // headers with the same name in different directories when one
415 // is included by double-quotes and the other by angle brackets.
416 // This kind of problem will be fixed when a more
417 // preprocessor-like implementation of this scanner is created.
418 if (this->IncludeRegexScan.find(entry.FileName.c_str()))
420 newCacheEntry->UnscannedEntries.push_back(entry);
421 if(this->Encountered.find(entry.FileName) == this->Encountered.end())
423 this->Encountered.insert(entry.FileName);
424 this->Unscanned.push(entry);