cmMakeDepend.cxx 10 KB

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