cmDependsC.cxx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  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 "cmDependsC.h"
  11. #include "cmFileTimeComparison.h"
  12. #include "cmLocalGenerator.h"
  13. #include "cmMakefile.h"
  14. #include "cmSystemTools.h"
  15. #include <cmsys/FStream.hxx>
  16. #include <ctype.h> // isspace
  17. #define INCLUDE_REGEX_LINE \
  18. "^[ \t]*#[ \t]*(include|import)[ \t]*[<\"]([^\">]+)([\">])"
  19. #define INCLUDE_REGEX_LINE_MARKER "#IncludeRegexLine: "
  20. #define INCLUDE_REGEX_SCAN_MARKER "#IncludeRegexScan: "
  21. #define INCLUDE_REGEX_COMPLAIN_MARKER "#IncludeRegexComplain: "
  22. #define INCLUDE_REGEX_TRANSFORM_MARKER "#IncludeRegexTransform: "
  23. //----------------------------------------------------------------------------
  24. cmDependsC::cmDependsC()
  25. : ValidDeps(0)
  26. {
  27. }
  28. //----------------------------------------------------------------------------
  29. cmDependsC::cmDependsC(cmLocalGenerator* lg,
  30. const char* targetDir,
  31. const std::string& lang,
  32. const std::map<std::string, DependencyVector>* validDeps)
  33. : cmDepends(lg, targetDir)
  34. , ValidDeps(validDeps)
  35. {
  36. cmMakefile* mf = lg->GetMakefile();
  37. // Configure the include file search path.
  38. this->SetIncludePathFromLanguage(lang);
  39. // Configure regular expressions.
  40. std::string scanRegex = "^.*$";
  41. std::string complainRegex = "^$";
  42. {
  43. std::string scanRegexVar = "CMAKE_";
  44. scanRegexVar += lang;
  45. scanRegexVar += "_INCLUDE_REGEX_SCAN";
  46. if(const char* sr = mf->GetDefinition(scanRegexVar))
  47. {
  48. scanRegex = sr;
  49. }
  50. std::string complainRegexVar = "CMAKE_";
  51. complainRegexVar += lang;
  52. complainRegexVar += "_INCLUDE_REGEX_COMPLAIN";
  53. if(const char* cr = mf->GetDefinition(complainRegexVar))
  54. {
  55. complainRegex = cr;
  56. }
  57. }
  58. this->IncludeRegexLine.compile(INCLUDE_REGEX_LINE);
  59. this->IncludeRegexScan.compile(scanRegex.c_str());
  60. this->IncludeRegexComplain.compile(complainRegex.c_str());
  61. this->IncludeRegexLineString = INCLUDE_REGEX_LINE_MARKER INCLUDE_REGEX_LINE;
  62. this->IncludeRegexScanString = INCLUDE_REGEX_SCAN_MARKER;
  63. this->IncludeRegexScanString += scanRegex;
  64. this->IncludeRegexComplainString = INCLUDE_REGEX_COMPLAIN_MARKER;
  65. this->IncludeRegexComplainString += complainRegex;
  66. this->SetupTransforms();
  67. this->CacheFileName = this->TargetDirectory;
  68. this->CacheFileName += "/";
  69. this->CacheFileName += lang;
  70. this->CacheFileName += ".includecache";
  71. this->ReadCacheFile();
  72. }
  73. //----------------------------------------------------------------------------
  74. cmDependsC::~cmDependsC()
  75. {
  76. this->WriteCacheFile();
  77. cmDeleteAll(this->FileCache);
  78. }
  79. //----------------------------------------------------------------------------
  80. bool cmDependsC::WriteDependencies(const std::set<std::string>& sources,
  81. const std::string& obj,
  82. std::ostream& makeDepends,
  83. std::ostream& internalDepends)
  84. {
  85. // Make sure this is a scanning instance.
  86. if(sources.empty() || sources.begin()->empty())
  87. {
  88. cmSystemTools::Error("Cannot scan dependencies without a source file.");
  89. return false;
  90. }
  91. if(obj.empty())
  92. {
  93. cmSystemTools::Error("Cannot scan dependencies without an object file.");
  94. return false;
  95. }
  96. std::set<std::string> dependencies;
  97. bool haveDeps = false;
  98. if (this->ValidDeps != 0)
  99. {
  100. std::map<std::string, DependencyVector>::const_iterator tmpIt =
  101. this->ValidDeps->find(obj);
  102. if (tmpIt!= this->ValidDeps->end())
  103. {
  104. dependencies.insert(tmpIt->second.begin(), tmpIt->second.end());
  105. haveDeps = true;
  106. }
  107. }
  108. if (!haveDeps)
  109. {
  110. // Walk the dependency graph starting with the source file.
  111. int srcFiles = (int)sources.size();
  112. this->Encountered.clear();
  113. for(std::set<std::string>::const_iterator srcIt = sources.begin();
  114. srcIt != sources.end(); ++srcIt)
  115. {
  116. UnscannedEntry root;
  117. root.FileName = *srcIt;
  118. this->Unscanned.push(root);
  119. this->Encountered.insert(*srcIt);
  120. }
  121. std::set<std::string> scanned;
  122. // Use reserve to allocate enough memory for tempPathStr
  123. // so that during the loops no memory is allocated or freed
  124. std::string tempPathStr;
  125. tempPathStr.reserve(4*1024);
  126. while(!this->Unscanned.empty())
  127. {
  128. // Get the next file to scan.
  129. UnscannedEntry current = this->Unscanned.front();
  130. this->Unscanned.pop();
  131. // If not a full path, find the file in the include path.
  132. std::string fullName;
  133. if((srcFiles>0)
  134. || cmSystemTools::FileIsFullPath(current.FileName.c_str()))
  135. {
  136. if(cmSystemTools::FileExists(current.FileName.c_str(), true))
  137. {
  138. fullName = current.FileName;
  139. }
  140. }
  141. else if(!current.QuotedLocation.empty() &&
  142. cmSystemTools::FileExists(current.QuotedLocation.c_str(), true))
  143. {
  144. // The include statement producing this entry was a double-quote
  145. // include and the included file is present in the directory of
  146. // the source containing the include statement.
  147. fullName = current.QuotedLocation;
  148. }
  149. else
  150. {
  151. std::map<std::string, std::string>::iterator
  152. headerLocationIt=this->HeaderLocationCache.find(current.FileName);
  153. if (headerLocationIt!=this->HeaderLocationCache.end())
  154. {
  155. fullName=headerLocationIt->second;
  156. }
  157. else for(std::vector<std::string>::const_iterator i =
  158. this->IncludePath.begin(); i != this->IncludePath.end(); ++i)
  159. {
  160. // Construct the name of the file as if it were in the current
  161. // include directory. Avoid using a leading "./".
  162. tempPathStr =
  163. cmSystemTools::CollapseCombinedPath(*i, current.FileName);
  164. // Look for the file in this location.
  165. if(cmSystemTools::FileExists(tempPathStr.c_str(), true))
  166. {
  167. fullName = tempPathStr;
  168. HeaderLocationCache[current.FileName]=fullName;
  169. break;
  170. }
  171. }
  172. }
  173. // Complain if the file cannot be found and matches the complain
  174. // regex.
  175. if(fullName.empty() &&
  176. this->IncludeRegexComplain.find(current.FileName.c_str()))
  177. {
  178. cmSystemTools::Error("Cannot find file \"",
  179. current.FileName.c_str(), "\".");
  180. return false;
  181. }
  182. // Scan the file if it was found and has not been scanned already.
  183. if(!fullName.empty() && (scanned.find(fullName) == scanned.end()))
  184. {
  185. // Record scanned files.
  186. scanned.insert(fullName);
  187. // Check whether this file is already in the cache
  188. std::map<std::string, cmIncludeLines*>::iterator fileIt=
  189. this->FileCache.find(fullName);
  190. if (fileIt!=this->FileCache.end())
  191. {
  192. fileIt->second->Used=true;
  193. dependencies.insert(fullName);
  194. for (std::vector<UnscannedEntry>::const_iterator incIt=
  195. fileIt->second->UnscannedEntries.begin();
  196. incIt!=fileIt->second->UnscannedEntries.end(); ++incIt)
  197. {
  198. if (this->Encountered.find(incIt->FileName) ==
  199. this->Encountered.end())
  200. {
  201. this->Encountered.insert(incIt->FileName);
  202. this->Unscanned.push(*incIt);
  203. }
  204. }
  205. }
  206. else
  207. {
  208. // Try to scan the file. Just leave it out if we cannot find
  209. // it.
  210. cmsys::ifstream fin(fullName.c_str());
  211. if(fin)
  212. {
  213. // Add this file as a dependency.
  214. dependencies.insert(fullName);
  215. // Scan this file for new dependencies. Pass the directory
  216. // containing the file to handle double-quote includes.
  217. std::string dir = cmSystemTools::GetFilenamePath(fullName);
  218. this->Scan(fin, dir.c_str(), fullName);
  219. }
  220. }
  221. }
  222. srcFiles--;
  223. }
  224. }
  225. // Write the dependencies to the output stream. Makefile rules
  226. // written by the original local generator for this directory
  227. // convert the dependencies to paths relative to the home output
  228. // directory. We must do the same here.
  229. std::string obj_i =
  230. this->LocalGenerator->Convert(obj, cmLocalGenerator::HOME_OUTPUT);
  231. std::string obj_m =
  232. this->LocalGenerator->ConvertToOutputFormat(obj_i,
  233. cmLocalGenerator::MAKERULE);
  234. internalDepends << obj_i << std::endl;
  235. for(std::set<std::string>::const_iterator i=dependencies.begin();
  236. i != dependencies.end(); ++i)
  237. {
  238. makeDepends << obj_m << ": " <<
  239. this->LocalGenerator->Convert(*i,
  240. cmLocalGenerator::HOME_OUTPUT,
  241. cmLocalGenerator::MAKERULE)
  242. << std::endl;
  243. internalDepends << " " << *i << std::endl;
  244. }
  245. makeDepends << std::endl;
  246. return true;
  247. }
  248. //----------------------------------------------------------------------------
  249. void cmDependsC::ReadCacheFile()
  250. {
  251. if(this->CacheFileName.size() == 0)
  252. {
  253. return;
  254. }
  255. cmsys::ifstream fin(this->CacheFileName.c_str());
  256. if(!fin)
  257. {
  258. return;
  259. }
  260. std::string line;
  261. cmIncludeLines* cacheEntry=0;
  262. bool haveFileName=false;
  263. while(cmSystemTools::GetLineFromStream(fin, line))
  264. {
  265. if (line.empty())
  266. {
  267. cacheEntry=0;
  268. haveFileName=false;
  269. continue;
  270. }
  271. //the first line after an empty line is the name of the parsed file
  272. if (haveFileName==false)
  273. {
  274. haveFileName=true;
  275. int newer=0;
  276. cmFileTimeComparison comp;
  277. bool res=comp.FileTimeCompare(this->CacheFileName.c_str(),
  278. line.c_str(), &newer);
  279. if ((res==true) && (newer==1)) //cache is newer than the parsed file
  280. {
  281. cacheEntry=new cmIncludeLines;
  282. this->FileCache[line]=cacheEntry;
  283. }
  284. // file doesn't exist, check that the regular expressions
  285. // haven't changed
  286. else if (res==false)
  287. {
  288. if (line.find(INCLUDE_REGEX_LINE_MARKER) == 0)
  289. {
  290. if (line != this->IncludeRegexLineString)
  291. {
  292. return;
  293. }
  294. }
  295. else if (line.find(INCLUDE_REGEX_SCAN_MARKER) == 0)
  296. {
  297. if (line != this->IncludeRegexScanString)
  298. {
  299. return;
  300. }
  301. }
  302. else if (line.find(INCLUDE_REGEX_COMPLAIN_MARKER) == 0)
  303. {
  304. if (line != this->IncludeRegexComplainString)
  305. {
  306. return;
  307. }
  308. }
  309. else if (line.find(INCLUDE_REGEX_TRANSFORM_MARKER) == 0)
  310. {
  311. if (line != this->IncludeRegexTransformString)
  312. {
  313. return;
  314. }
  315. }
  316. }
  317. }
  318. else if (cacheEntry!=0)
  319. {
  320. UnscannedEntry entry;
  321. entry.FileName = line;
  322. if (cmSystemTools::GetLineFromStream(fin, line))
  323. {
  324. if (line!="-")
  325. {
  326. entry.QuotedLocation=line;
  327. }
  328. cacheEntry->UnscannedEntries.push_back(entry);
  329. }
  330. }
  331. }
  332. }
  333. //----------------------------------------------------------------------------
  334. void cmDependsC::WriteCacheFile() const
  335. {
  336. if(this->CacheFileName.size() == 0)
  337. {
  338. return;
  339. }
  340. cmsys::ofstream cacheOut(this->CacheFileName.c_str());
  341. if(!cacheOut)
  342. {
  343. return;
  344. }
  345. cacheOut << this->IncludeRegexLineString << "\n\n";
  346. cacheOut << this->IncludeRegexScanString << "\n\n";
  347. cacheOut << this->IncludeRegexComplainString << "\n\n";
  348. cacheOut << this->IncludeRegexTransformString << "\n\n";
  349. for (std::map<std::string, cmIncludeLines*>::const_iterator fileIt=
  350. this->FileCache.begin();
  351. fileIt!=this->FileCache.end(); ++fileIt)
  352. {
  353. if (fileIt->second->Used)
  354. {
  355. cacheOut<<fileIt->first.c_str()<<std::endl;
  356. for (std::vector<UnscannedEntry>::const_iterator
  357. incIt=fileIt->second->UnscannedEntries.begin();
  358. incIt!=fileIt->second->UnscannedEntries.end(); ++incIt)
  359. {
  360. cacheOut<<incIt->FileName.c_str()<<std::endl;
  361. if (incIt->QuotedLocation.empty())
  362. {
  363. cacheOut<<"-"<<std::endl;
  364. }
  365. else
  366. {
  367. cacheOut<<incIt->QuotedLocation.c_str()<<std::endl;
  368. }
  369. }
  370. cacheOut<<std::endl;
  371. }
  372. }
  373. }
  374. //----------------------------------------------------------------------------
  375. void cmDependsC::Scan(std::istream& is, const char* directory,
  376. const std::string& fullName)
  377. {
  378. cmIncludeLines* newCacheEntry=new cmIncludeLines;
  379. newCacheEntry->Used=true;
  380. this->FileCache[fullName]=newCacheEntry;
  381. // Read one line at a time.
  382. std::string line;
  383. while(cmSystemTools::GetLineFromStream(is, line))
  384. {
  385. // Transform the line content first.
  386. if(!this->TransformRules.empty())
  387. {
  388. this->TransformLine(line);
  389. }
  390. // Match include directives.
  391. if(this->IncludeRegexLine.find(line.c_str()))
  392. {
  393. // Get the file being included.
  394. UnscannedEntry entry;
  395. entry.FileName = this->IncludeRegexLine.match(2);
  396. cmSystemTools::ConvertToUnixSlashes(entry.FileName);
  397. if(this->IncludeRegexLine.match(3) == "\"" &&
  398. !cmSystemTools::FileIsFullPath(entry.FileName.c_str()))
  399. {
  400. // This was a double-quoted include with a relative path. We
  401. // must check for the file in the directory containing the
  402. // file we are scanning.
  403. entry.QuotedLocation =
  404. cmSystemTools::CollapseCombinedPath(directory, entry.FileName);
  405. }
  406. // Queue the file if it has not yet been encountered and it
  407. // matches the regular expression for recursive scanning. Note
  408. // that this check does not account for the possibility of two
  409. // headers with the same name in different directories when one
  410. // is included by double-quotes and the other by angle brackets.
  411. // It also does not work properly if two header files with the same
  412. // name exist in different directories, and both are included from a
  413. // file their own directory by simply using "filename.h" (#12619)
  414. // This kind of problem will be fixed when a more
  415. // preprocessor-like implementation of this scanner is created.
  416. if (this->IncludeRegexScan.find(entry.FileName.c_str()))
  417. {
  418. newCacheEntry->UnscannedEntries.push_back(entry);
  419. if(this->Encountered.find(entry.FileName) == this->Encountered.end())
  420. {
  421. this->Encountered.insert(entry.FileName);
  422. this->Unscanned.push(entry);
  423. }
  424. }
  425. }
  426. }
  427. }
  428. //----------------------------------------------------------------------------
  429. void cmDependsC::SetupTransforms()
  430. {
  431. // Get the transformation rules.
  432. std::vector<std::string> transformRules;
  433. cmMakefile* mf = this->LocalGenerator->GetMakefile();
  434. if(const char* xform =
  435. mf->GetDefinition("CMAKE_INCLUDE_TRANSFORMS"))
  436. {
  437. cmSystemTools::ExpandListArgument(xform, transformRules, true);
  438. }
  439. for(std::vector<std::string>::const_iterator tri = transformRules.begin();
  440. tri != transformRules.end(); ++tri)
  441. {
  442. this->ParseTransform(*tri);
  443. }
  444. this->IncludeRegexTransformString = INCLUDE_REGEX_TRANSFORM_MARKER;
  445. if(!this->TransformRules.empty())
  446. {
  447. // Construct the regular expression to match lines to be
  448. // transformed.
  449. std::string xform = "^([ \t]*#[ \t]*(include|import)[ \t]*)(";
  450. const char* sep = "";
  451. for(TransformRulesType::const_iterator tri = this->TransformRules.begin();
  452. tri != this->TransformRules.end(); ++tri)
  453. {
  454. xform += sep;
  455. xform += tri->first;
  456. sep = "|";
  457. }
  458. xform += ")[ \t]*\\(([^),]*)\\)";
  459. this->IncludeRegexTransform.compile(xform.c_str());
  460. // Build a string that encodes all transformation rules and will
  461. // change when rules are changed.
  462. this->IncludeRegexTransformString += xform;
  463. for(TransformRulesType::const_iterator tri = this->TransformRules.begin();
  464. tri != this->TransformRules.end(); ++tri)
  465. {
  466. this->IncludeRegexTransformString += " ";
  467. this->IncludeRegexTransformString += tri->first;
  468. this->IncludeRegexTransformString += "(%)=";
  469. this->IncludeRegexTransformString += tri->second;
  470. }
  471. }
  472. }
  473. //----------------------------------------------------------------------------
  474. void cmDependsC::ParseTransform(std::string const& xform)
  475. {
  476. // A transform rule is of the form SOME_MACRO(%)=value-with-%
  477. // We can simply separate with "(%)=".
  478. std::string::size_type pos = xform.find("(%)=");
  479. if(pos == xform.npos || pos == 0)
  480. {
  481. return;
  482. }
  483. std::string name = xform.substr(0, pos);
  484. std::string value = xform.substr(pos+4, xform.npos);
  485. this->TransformRules[name] = value;
  486. }
  487. //----------------------------------------------------------------------------
  488. void cmDependsC::TransformLine(std::string& line)
  489. {
  490. // Check for a transform rule match. Return if none.
  491. if(!this->IncludeRegexTransform.find(line.c_str()))
  492. {
  493. return;
  494. }
  495. TransformRulesType::const_iterator tri =
  496. this->TransformRules.find(this->IncludeRegexTransform.match(3));
  497. if(tri == this->TransformRules.end())
  498. {
  499. return;
  500. }
  501. // Construct the transformed line.
  502. std::string newline = this->IncludeRegexTransform.match(1);
  503. std::string arg = this->IncludeRegexTransform.match(4);
  504. for(const char* c = tri->second.c_str(); *c; ++c)
  505. {
  506. if(*c == '%')
  507. {
  508. newline += arg;
  509. }
  510. else
  511. {
  512. newline += *c;
  513. }
  514. }
  515. // Return the transformed line.
  516. line = newline;
  517. }