packaging: Initial packaging
[platform/upstream/cmake.git] / Source / cmMakeDepend.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 "cmMakeDepend.h"
13 #include "cmSystemTools.h"
14 #include "cmGeneratorExpression.h"
15
16 #include <cmsys/RegularExpression.hxx>
17
18 void cmDependInformation::AddDependencies(cmDependInformation* info)
19 {
20   if(this != info)
21     {
22     this->DependencySet.insert(info);
23     }
24 }
25
26 cmMakeDepend::cmMakeDepend()
27 {
28   this->Verbose = false;
29   this->IncludeFileRegularExpression.compile("^.*$");
30   this->ComplainFileRegularExpression.compile("^$");
31 }
32
33
34 cmMakeDepend::~cmMakeDepend()
35 {
36   for(DependInformationMapType::iterator i =
37         this->DependInformationMap.begin();
38       i != this->DependInformationMap.end(); ++i)
39     {
40     delete i->second;
41     }
42 }
43
44
45 // Set the makefile that depends will be made from.
46 // The pointer is kept so the cmSourceFile array can
47 // be updated with the depend information in the cmMakefile.
48
49 void cmMakeDepend::SetMakefile(cmMakefile* makefile)
50 {
51   this->Makefile = makefile;
52
53   // Now extract the include file regular expression from the makefile.
54   this->IncludeFileRegularExpression.compile(
55     this->Makefile->IncludeFileRegularExpression.c_str());
56   this->ComplainFileRegularExpression.compile(
57     this->Makefile->ComplainFileRegularExpression.c_str());
58
59   // Now extract any include paths from the targets
60   std::set<std::string> uniqueIncludes;
61   std::vector<std::string> orderedAndUniqueIncludes;
62   cmTargets &targets = this->Makefile->GetTargets();
63   for (cmTargets::iterator l = targets.begin();
64        l != targets.end(); ++l)
65     {
66     const char *incDirProp = l->second.GetProperty("INCLUDE_DIRECTORIES");
67     if (!incDirProp)
68       {
69       continue;
70       }
71
72     std::string incDirs = cmGeneratorExpression::Preprocess(incDirProp,
73                       cmGeneratorExpression::StripAllGeneratorExpressions);
74
75     std::vector<std::string> includes;
76     cmSystemTools::ExpandListArgument(incDirs.c_str(), includes);
77
78     for(std::vector<std::string>::const_iterator j = includes.begin();
79         j != includes.end(); ++j)
80       {
81       std::string path = *j;
82       this->Makefile->ExpandVariablesInString(path);
83       if(uniqueIncludes.insert(path).second)
84         {
85         orderedAndUniqueIncludes.push_back(path);
86         }
87       }
88     }
89
90   for(std::vector<std::string>::const_iterator
91     it = orderedAndUniqueIncludes.begin();
92     it != orderedAndUniqueIncludes.end();
93     ++it)
94     {
95     this->AddSearchPath(it->c_str());
96     }
97 }
98
99
100 const cmDependInformation* cmMakeDepend::FindDependencies(const char* file)
101 {
102   cmDependInformation* info = this->GetDependInformation(file,0);
103   this->GenerateDependInformation(info);
104   return info;
105 }
106
107 void cmMakeDepend::GenerateDependInformation(cmDependInformation* info)
108 {
109   // If dependencies are already done, stop now.
110   if(info->DependDone)
111     {
112     return;
113     }
114   else
115     {
116     // Make sure we don't visit the same file more than once.
117     info->DependDone = true;
118     }
119   const char* path = info->FullPath.c_str();
120   if(!path)
121     {
122     cmSystemTools::Error(
123       "Attempt to find dependencies for file without path!");
124     return;
125     }
126
127   bool found = false;
128
129   // If the file exists, use it to find dependency information.
130   if(cmSystemTools::FileExists(path, true))
131     {
132     // Use the real file to find its dependencies.
133     this->DependWalk(info);
134     found = true;
135     }
136
137
138   // See if the cmSourceFile for it has any files specified as
139   // dependency hints.
140   if(info->SourceFile != 0)
141     {
142
143     // Get the cmSourceFile corresponding to this.
144     const cmSourceFile& cFile = *(info->SourceFile);
145     // See if there are any hints for finding dependencies for the missing
146     // file.
147     if(!cFile.GetDepends().empty())
148       {
149       // Dependency hints have been given.  Use them to begin the
150       // recursion.
151       for(std::vector<std::string>::const_iterator file =
152             cFile.GetDepends().begin(); file != cFile.GetDepends().end();
153           ++file)
154         {
155         this->AddDependency(info, file->c_str());
156         }
157
158       // Found dependency information.  We are done.
159       found = true;
160       }
161     }
162
163   if(!found)
164     {
165     // Try to find the file amongst the sources
166     cmSourceFile *srcFile = this->Makefile->GetSource
167       (cmSystemTools::GetFilenameWithoutExtension(path).c_str());
168     if (srcFile)
169       {
170       if (srcFile->GetFullPath() == path)
171         {
172         found=true;
173         }
174       else
175         {
176         //try to guess which include path to use
177         for(std::vector<std::string>::iterator t =
178               this->IncludeDirectories.begin();
179             t != this->IncludeDirectories.end(); ++t)
180           {
181           std::string incpath = *t;
182           if (incpath.size() && incpath[incpath.size() - 1] != '/')
183             {
184             incpath = incpath + "/";
185             }
186           incpath = incpath + path;
187           if (srcFile->GetFullPath() == incpath)
188             {
189             // set the path to the guessed path
190             info->FullPath = incpath;
191             found=true;
192             }
193           }
194         }
195       }
196     }
197
198   if(!found)
199     {
200     // Couldn't find any dependency information.
201     if(this->ComplainFileRegularExpression.find(info->IncludeName.c_str()))
202       {
203       cmSystemTools::Error("error cannot find dependencies for ", path);
204       }
205     else
206       {
207       // Destroy the name of the file so that it won't be output as a
208       // dependency.
209       info->FullPath = "";
210       }
211     }
212 }
213
214 // This function actually reads the file specified and scans it for
215 // #include directives
216 void cmMakeDepend::DependWalk(cmDependInformation* info)
217 {
218   cmsys::RegularExpression includeLine
219     ("^[ \t]*#[ \t]*include[ \t]*[<\"]([^\">]+)[\">]");
220   std::ifstream fin(info->FullPath.c_str());
221   if(!fin)
222     {
223     cmSystemTools::Error("Cannot open ", info->FullPath.c_str());
224     return;
225     }
226
227   // TODO: Write real read loop (see cmSystemTools::CopyFile).
228   std::string line;
229   while( cmSystemTools::GetLineFromStream(fin, line) )
230     {
231     if(includeLine.find(line.c_str()))
232       {
233       // extract the file being included
234       std::string includeFile = includeLine.match(1);
235       // see if the include matches the regular expression
236       if(!this->IncludeFileRegularExpression.find(includeFile))
237         {
238         if(this->Verbose)
239           {
240           std::string message = "Skipping ";
241           message += includeFile;
242           message += " for file ";
243           message += info->FullPath.c_str();
244           cmSystemTools::Error(message.c_str(), 0);
245           }
246         continue;
247         }
248
249       // Add this file and all its dependencies.
250       this->AddDependency(info, includeFile.c_str());
251       }
252     }
253 }
254
255
256 void cmMakeDepend::AddDependency(cmDependInformation* info, const char* file)
257 {
258   cmDependInformation* dependInfo =
259     this->GetDependInformation(file, info->PathOnly.c_str());
260   this->GenerateDependInformation(dependInfo);
261   info->AddDependencies(dependInfo);
262 }
263
264 cmDependInformation* cmMakeDepend::GetDependInformation(const char* file,
265                                                         const char *extraPath)
266 {
267   // Get the full path for the file so that lookup is unambiguous.
268   std::string fullPath = this->FullPath(file, extraPath);
269
270   // Try to find the file's instance of cmDependInformation.
271   DependInformationMapType::const_iterator result =
272     this->DependInformationMap.find(fullPath);
273   if(result != this->DependInformationMap.end())
274     {
275     // Found an instance, return it.
276     return result->second;
277     }
278   else
279     {
280     // Didn't find an instance.  Create a new one and save it.
281     cmDependInformation* info = new cmDependInformation;
282     info->FullPath = fullPath;
283     info->PathOnly = cmSystemTools::GetFilenamePath(fullPath.c_str());
284     info->IncludeName = file;
285     this->DependInformationMap[fullPath] = info;
286     return info;
287     }
288 }
289
290
291 // find the full path to fname by searching the this->IncludeDirectories array
292 std::string cmMakeDepend::FullPath(const char* fname, const char *extraPath)
293 {
294   DirectoryToFileToPathMapType::iterator m;
295   if(extraPath)
296     {
297     m = this->DirectoryToFileToPathMap.find(extraPath);
298     }
299   else
300     {
301     m = this->DirectoryToFileToPathMap.find("");
302     }
303
304   if(m != this->DirectoryToFileToPathMap.end())
305     {
306     FileToPathMapType& map = m->second;
307     FileToPathMapType::iterator p = map.find(fname);
308     if(p != map.end())
309       {
310       return p->second;
311       }
312     }
313
314   if(cmSystemTools::FileExists(fname, true))
315     {
316     std::string fp = cmSystemTools::CollapseFullPath(fname);
317     this->DirectoryToFileToPathMap[extraPath? extraPath: ""][fname] = fp;
318     return fp;
319     }
320
321   for(std::vector<std::string>::iterator i = this->IncludeDirectories.begin();
322       i != this->IncludeDirectories.end(); ++i)
323     {
324     std::string path = *i;
325     if (path.size() && path[path.size() - 1] != '/')
326       {
327       path = path + "/";
328       }
329     path = path + fname;
330     if(cmSystemTools::FileExists(path.c_str(), true)
331        && !cmSystemTools::FileIsDirectory(path.c_str()))
332       {
333       std::string fp = cmSystemTools::CollapseFullPath(path.c_str());
334       this->DirectoryToFileToPathMap[extraPath? extraPath: ""][fname] = fp;
335       return fp;
336       }
337     }
338
339   if (extraPath)
340     {
341     std::string path = extraPath;
342     if (path.size() && path[path.size() - 1] != '/')
343       {
344       path = path + "/";
345       }
346     path = path + fname;
347     if(cmSystemTools::FileExists(path.c_str(), true)
348        && !cmSystemTools::FileIsDirectory(path.c_str()))
349       {
350       std::string fp = cmSystemTools::CollapseFullPath(path.c_str());
351       this->DirectoryToFileToPathMap[extraPath][fname] = fp;
352       return fp;
353       }
354     }
355
356   // Couldn't find the file.
357   return std::string(fname);
358 }
359
360 // Add a directory to the search path
361 void cmMakeDepend::AddSearchPath(const char* path)
362 {
363   this->IncludeDirectories.push_back(path);
364 }