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"
14 #include "cmGeneratorExpression.h"
16 #include <cmsys/RegularExpression.hxx>
18 void cmDependInformation::AddDependencies(cmDependInformation* info)
22 this->DependencySet.insert(info);
26 cmMakeDepend::cmMakeDepend()
28 this->Verbose = false;
29 this->IncludeFileRegularExpression.compile("^.*$");
30 this->ComplainFileRegularExpression.compile("^$");
34 cmMakeDepend::~cmMakeDepend()
36 for(DependInformationMapType::iterator i =
37 this->DependInformationMap.begin();
38 i != this->DependInformationMap.end(); ++i)
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.
49 void cmMakeDepend::SetMakefile(cmMakefile* makefile)
51 this->Makefile = makefile;
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());
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)
66 const char *incDirProp = l->second.GetProperty("INCLUDE_DIRECTORIES");
72 std::string incDirs = cmGeneratorExpression::Preprocess(incDirProp,
73 cmGeneratorExpression::StripAllGeneratorExpressions);
75 std::vector<std::string> includes;
76 cmSystemTools::ExpandListArgument(incDirs.c_str(), includes);
78 for(std::vector<std::string>::const_iterator j = includes.begin();
79 j != includes.end(); ++j)
81 std::string path = *j;
82 this->Makefile->ExpandVariablesInString(path);
83 if(uniqueIncludes.insert(path).second)
85 orderedAndUniqueIncludes.push_back(path);
90 for(std::vector<std::string>::const_iterator
91 it = orderedAndUniqueIncludes.begin();
92 it != orderedAndUniqueIncludes.end();
95 this->AddSearchPath(it->c_str());
100 const cmDependInformation* cmMakeDepend::FindDependencies(const char* file)
102 cmDependInformation* info = this->GetDependInformation(file,0);
103 this->GenerateDependInformation(info);
107 void cmMakeDepend::GenerateDependInformation(cmDependInformation* info)
109 // If dependencies are already done, stop now.
116 // Make sure we don't visit the same file more than once.
117 info->DependDone = true;
119 const char* path = info->FullPath.c_str();
122 cmSystemTools::Error(
123 "Attempt to find dependencies for file without path!");
129 // If the file exists, use it to find dependency information.
130 if(cmSystemTools::FileExists(path, true))
132 // Use the real file to find its dependencies.
133 this->DependWalk(info);
138 // See if the cmSourceFile for it has any files specified as
140 if(info->SourceFile != 0)
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
147 if(!cFile.GetDepends().empty())
149 // Dependency hints have been given. Use them to begin the
151 for(std::vector<std::string>::const_iterator file =
152 cFile.GetDepends().begin(); file != cFile.GetDepends().end();
155 this->AddDependency(info, file->c_str());
158 // Found dependency information. We are done.
165 // Try to find the file amongst the sources
166 cmSourceFile *srcFile = this->Makefile->GetSource
167 (cmSystemTools::GetFilenameWithoutExtension(path).c_str());
170 if (srcFile->GetFullPath() == path)
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)
181 std::string incpath = *t;
182 if (incpath.size() && incpath[incpath.size() - 1] != '/')
184 incpath = incpath + "/";
186 incpath = incpath + path;
187 if (srcFile->GetFullPath() == incpath)
189 // set the path to the guessed path
190 info->FullPath = incpath;
200 // Couldn't find any dependency information.
201 if(this->ComplainFileRegularExpression.find(info->IncludeName.c_str()))
203 cmSystemTools::Error("error cannot find dependencies for ", path);
207 // Destroy the name of the file so that it won't be output as a
214 // This function actually reads the file specified and scans it for
215 // #include directives
216 void cmMakeDepend::DependWalk(cmDependInformation* info)
218 cmsys::RegularExpression includeLine
219 ("^[ \t]*#[ \t]*include[ \t]*[<\"]([^\">]+)[\">]");
220 std::ifstream fin(info->FullPath.c_str());
223 cmSystemTools::Error("Cannot open ", info->FullPath.c_str());
227 // TODO: Write real read loop (see cmSystemTools::CopyFile).
229 while( cmSystemTools::GetLineFromStream(fin, line) )
231 if(includeLine.find(line.c_str()))
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))
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);
249 // Add this file and all its dependencies.
250 this->AddDependency(info, includeFile.c_str());
256 void cmMakeDepend::AddDependency(cmDependInformation* info, const char* file)
258 cmDependInformation* dependInfo =
259 this->GetDependInformation(file, info->PathOnly.c_str());
260 this->GenerateDependInformation(dependInfo);
261 info->AddDependencies(dependInfo);
264 cmDependInformation* cmMakeDepend::GetDependInformation(const char* file,
265 const char *extraPath)
267 // Get the full path for the file so that lookup is unambiguous.
268 std::string fullPath = this->FullPath(file, extraPath);
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())
275 // Found an instance, return it.
276 return result->second;
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;
291 // find the full path to fname by searching the this->IncludeDirectories array
292 std::string cmMakeDepend::FullPath(const char* fname, const char *extraPath)
294 DirectoryToFileToPathMapType::iterator m;
297 m = this->DirectoryToFileToPathMap.find(extraPath);
301 m = this->DirectoryToFileToPathMap.find("");
304 if(m != this->DirectoryToFileToPathMap.end())
306 FileToPathMapType& map = m->second;
307 FileToPathMapType::iterator p = map.find(fname);
314 if(cmSystemTools::FileExists(fname, true))
316 std::string fp = cmSystemTools::CollapseFullPath(fname);
317 this->DirectoryToFileToPathMap[extraPath? extraPath: ""][fname] = fp;
321 for(std::vector<std::string>::iterator i = this->IncludeDirectories.begin();
322 i != this->IncludeDirectories.end(); ++i)
324 std::string path = *i;
325 if (path.size() && path[path.size() - 1] != '/')
330 if(cmSystemTools::FileExists(path.c_str(), true)
331 && !cmSystemTools::FileIsDirectory(path.c_str()))
333 std::string fp = cmSystemTools::CollapseFullPath(path.c_str());
334 this->DirectoryToFileToPathMap[extraPath? extraPath: ""][fname] = fp;
341 std::string path = extraPath;
342 if (path.size() && path[path.size() - 1] != '/')
347 if(cmSystemTools::FileExists(path.c_str(), true)
348 && !cmSystemTools::FileIsDirectory(path.c_str()))
350 std::string fp = cmSystemTools::CollapseFullPath(path.c_str());
351 this->DirectoryToFileToPathMap[extraPath][fname] = fp;
356 // Couldn't find the file.
357 return std::string(fname);
360 // Add a directory to the search path
361 void cmMakeDepend::AddSearchPath(const char* path)
363 this->IncludeDirectories.push_back(path);