cmMakeDepend.cxx 9.9 KB

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