1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
5 Distributed under the OSI-approved BSD License (the "License");
6 see accompanying file Copyright.txt for details.
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"
15 #include <cmsys/RegularExpression.hxx>
17 void cmDependInformation::AddDependencies(cmDependInformation* info)
21 this->DependencySet.insert(info);
25 cmMakeDepend::cmMakeDepend()
27 this->Verbose = false;
28 this->IncludeFileRegularExpression.compile("^.*$");
29 this->ComplainFileRegularExpression.compile("^$");
33 cmMakeDepend::~cmMakeDepend()
35 for(DependInformationMapType::iterator i =
36 this->DependInformationMap.begin();
37 i != this->DependInformationMap.end(); ++i)
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.
48 void cmMakeDepend::SetMakefile(cmMakefile* makefile)
50 this->Makefile = makefile;
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());
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)
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)
69 std::string path = *j;
70 this->Makefile->ExpandVariablesInString(path);
71 if(uniqueIncludes.insert(path).second)
73 orderedAndUniqueIncludes.push_back(path);
78 for(std::vector<std::string>::const_iterator
79 it = orderedAndUniqueIncludes.begin();
80 it != orderedAndUniqueIncludes.end();
83 this->AddSearchPath(it->c_str());
88 const cmDependInformation* cmMakeDepend::FindDependencies(const char* file)
90 cmDependInformation* info = this->GetDependInformation(file,0);
91 this->GenerateDependInformation(info);
95 void cmMakeDepend::GenerateDependInformation(cmDependInformation* info)
97 // If dependencies are already done, stop now.
104 // Make sure we don't visit the same file more than once.
105 info->DependDone = true;
107 const char* path = info->FullPath.c_str();
110 cmSystemTools::Error(
111 "Attempt to find dependencies for file without path!");
117 // If the file exists, use it to find dependency information.
118 if(cmSystemTools::FileExists(path, true))
120 // Use the real file to find its dependencies.
121 this->DependWalk(info);
126 // See if the cmSourceFile for it has any files specified as
128 if(info->SourceFile != 0)
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
135 if(!cFile.GetDepends().empty())
137 // Dependency hints have been given. Use them to begin the
139 for(std::vector<std::string>::const_iterator file =
140 cFile.GetDepends().begin(); file != cFile.GetDepends().end();
143 this->AddDependency(info, file->c_str());
146 // Found dependency information. We are done.
153 // Try to find the file amongst the sources
154 cmSourceFile *srcFile = this->Makefile->GetSource
155 (cmSystemTools::GetFilenameWithoutExtension(path).c_str());
158 if (srcFile->GetFullPath() == path)
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)
169 std::string incpath = *t;
170 if (incpath.size() && incpath[incpath.size() - 1] != '/')
172 incpath = incpath + "/";
174 incpath = incpath + path;
175 if (srcFile->GetFullPath() == incpath)
177 // set the path to the guessed path
178 info->FullPath = incpath;
188 // Couldn't find any dependency information.
189 if(this->ComplainFileRegularExpression.find(info->IncludeName.c_str()))
191 cmSystemTools::Error("error cannot find dependencies for ", path);
195 // Destroy the name of the file so that it won't be output as a
202 // This function actually reads the file specified and scans it for
203 // #include directives
204 void cmMakeDepend::DependWalk(cmDependInformation* info)
206 cmsys::RegularExpression includeLine
207 ("^[ \t]*#[ \t]*include[ \t]*[<\"]([^\">]+)[\">]");
208 std::ifstream fin(info->FullPath.c_str());
211 cmSystemTools::Error("Cannot open ", info->FullPath.c_str());
215 // TODO: Write real read loop (see cmSystemTools::CopyFile).
217 while( cmSystemTools::GetLineFromStream(fin, line) )
219 if(includeLine.find(line.c_str()))
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))
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);
237 // Add this file and all its dependencies.
238 this->AddDependency(info, includeFile.c_str());
244 void cmMakeDepend::AddDependency(cmDependInformation* info, const char* file)
246 cmDependInformation* dependInfo =
247 this->GetDependInformation(file, info->PathOnly.c_str());
248 this->GenerateDependInformation(dependInfo);
249 info->AddDependencies(dependInfo);
252 cmDependInformation* cmMakeDepend::GetDependInformation(const char* file,
253 const char *extraPath)
255 // Get the full path for the file so that lookup is unambiguous.
256 std::string fullPath = this->FullPath(file, extraPath);
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())
263 // Found an instance, return it.
264 return result->second;
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;
279 // find the full path to fname by searching the this->IncludeDirectories array
280 std::string cmMakeDepend::FullPath(const char* fname, const char *extraPath)
282 DirectoryToFileToPathMapType::iterator m;
285 m = this->DirectoryToFileToPathMap.find(extraPath);
289 m = this->DirectoryToFileToPathMap.find("");
292 if(m != this->DirectoryToFileToPathMap.end())
294 FileToPathMapType& map = m->second;
295 FileToPathMapType::iterator p = map.find(fname);
302 if(cmSystemTools::FileExists(fname, true))
304 std::string fp = cmSystemTools::CollapseFullPath(fname);
305 this->DirectoryToFileToPathMap[extraPath? extraPath: ""][fname] = fp;
309 for(std::vector<std::string>::iterator i = this->IncludeDirectories.begin();
310 i != this->IncludeDirectories.end(); ++i)
312 std::string path = *i;
313 if (path.size() && path[path.size() - 1] != '/')
318 if(cmSystemTools::FileExists(path.c_str(), true)
319 && !cmSystemTools::FileIsDirectory(path.c_str()))
321 std::string fp = cmSystemTools::CollapseFullPath(path.c_str());
322 this->DirectoryToFileToPathMap[extraPath? extraPath: ""][fname] = fp;
329 std::string path = extraPath;
330 if (path.size() && path[path.size() - 1] != '/')
335 if(cmSystemTools::FileExists(path.c_str(), true)
336 && !cmSystemTools::FileIsDirectory(path.c_str()))
338 std::string fp = cmSystemTools::CollapseFullPath(path.c_str());
339 this->DirectoryToFileToPathMap[extraPath][fname] = fp;
344 // Couldn't find the file.
345 return std::string(fname);
348 // Add a directory to the search path
349 void cmMakeDepend::AddSearchPath(const char* path)
351 this->IncludeDirectories.push_back(path);