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