cmDependsCompiler.cxx 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmDependsCompiler.h"
  4. #include <algorithm>
  5. #include <iterator>
  6. #include <map>
  7. #include <memory>
  8. #include <string>
  9. #include <unordered_set>
  10. #include <utility>
  11. #include <cm/optional>
  12. #include <cm/string_view>
  13. #include <cm/vector>
  14. #include <cmext/string_view>
  15. #include "cmsys/FStream.hxx"
  16. #include "cmFileTime.h"
  17. #include "cmGccDepfileReader.h"
  18. #include "cmGccDepfileReaderTypes.h"
  19. #include "cmGlobalUnixMakefileGenerator3.h"
  20. #include "cmLocalUnixMakefileGenerator3.h"
  21. #include "cmStringAlgorithms.h"
  22. #include "cmSystemTools.h"
  23. bool cmDependsCompiler::CheckDependencies(
  24. const std::string& internalDepFile, const std::vector<std::string>& depFiles,
  25. cmDepends::DependencyMap& dependencies,
  26. const std::function<bool(const std::string&)>& isValidPath)
  27. {
  28. bool status = true;
  29. bool forceReadDeps = true;
  30. cmFileTime internalDepFileTime;
  31. // read cached dependencies stored in internal file
  32. if (cmSystemTools::FileExists(internalDepFile)) {
  33. internalDepFileTime.Load(internalDepFile);
  34. forceReadDeps = false;
  35. // read current dependencies
  36. cmsys::ifstream fin(internalDepFile.c_str());
  37. if (fin) {
  38. std::string line;
  39. std::string depender;
  40. std::vector<std::string>* currentDependencies = nullptr;
  41. while (std::getline(fin, line)) {
  42. if (line.empty() || line.front() == '#') {
  43. continue;
  44. }
  45. // Drop carriage return character at the end
  46. if (line.back() == '\r') {
  47. line.pop_back();
  48. if (line.empty()) {
  49. continue;
  50. }
  51. }
  52. // Check if this a depender line
  53. if (line.front() != ' ') {
  54. depender = std::move(line);
  55. currentDependencies = &dependencies[depender];
  56. continue;
  57. }
  58. // This is a dependee line
  59. if (currentDependencies != nullptr) {
  60. currentDependencies->emplace_back(line.substr(1));
  61. }
  62. }
  63. fin.close();
  64. }
  65. }
  66. // Now, update dependencies map with all new compiler generated
  67. // dependencies files
  68. cmFileTime depFileTime;
  69. for (auto dep = depFiles.begin(); dep != depFiles.end(); dep++) {
  70. const auto& source = *dep++;
  71. const auto& target = *dep++;
  72. const auto& format = *dep++;
  73. const auto& depFile = *dep;
  74. if (!cmSystemTools::FileExists(depFile)) {
  75. continue;
  76. }
  77. if (!forceReadDeps) {
  78. depFileTime.Load(depFile);
  79. }
  80. if (forceReadDeps || depFileTime.Compare(internalDepFileTime) >= 0) {
  81. status = false;
  82. if (this->Verbose) {
  83. cmSystemTools::Stdout(cmStrCat("Dependencies file \"", depFile,
  84. "\" is newer than depends file \"",
  85. internalDepFile, "\".\n"));
  86. }
  87. std::vector<std::string> depends;
  88. if (format == "custom"_s) {
  89. auto deps = cmReadGccDepfile(
  90. depFile.c_str(), this->LocalGenerator->GetCurrentBinaryDirectory());
  91. if (!deps) {
  92. continue;
  93. }
  94. for (auto& entry : *deps) {
  95. depends = std::move(entry.paths);
  96. if (isValidPath) {
  97. cm::erase_if(depends, isValidPath);
  98. }
  99. // copy depends for each target, except first one, which can be
  100. // moved
  101. for (auto index = entry.rules.size() - 1; index > 0; --index) {
  102. auto& rule_deps = dependencies[entry.rules[index]];
  103. rule_deps.insert(rule_deps.end(), depends.cbegin(),
  104. depends.cend());
  105. }
  106. auto& rule_deps = dependencies[entry.rules.front()];
  107. std::move(depends.cbegin(), depends.cend(),
  108. std::back_inserter(rule_deps));
  109. }
  110. } else {
  111. if (format == "msvc"_s) {
  112. cmsys::ifstream fin(depFile.c_str());
  113. if (!fin) {
  114. continue;
  115. }
  116. std::string line;
  117. if (!isValidPath && !source.empty()) {
  118. // insert source as first dependency
  119. depends.push_back(source);
  120. }
  121. while (cmSystemTools::GetLineFromStream(fin, line)) {
  122. depends.emplace_back(std::move(line));
  123. }
  124. } else if (format == "gcc"_s) {
  125. auto deps = cmReadGccDepfile(
  126. depFile.c_str(), this->LocalGenerator->GetCurrentBinaryDirectory(),
  127. GccDepfilePrependPaths::Deps);
  128. if (!deps) {
  129. continue;
  130. }
  131. // dependencies generated by the compiler contains only one target
  132. depends = std::move(deps->front().paths);
  133. if (depends.empty()) {
  134. // unexpectedly empty, ignore it and continue
  135. continue;
  136. }
  137. // depending of the effective format of the dependencies file
  138. // generated by the compiler, the target can be wrongly identified
  139. // as a dependency so remove it from the list
  140. if (depends.front() == target) {
  141. depends.erase(depends.begin());
  142. }
  143. // ensure source file is the first dependency
  144. if (!source.empty()) {
  145. if (depends.front() != source) {
  146. cm::erase(depends, source);
  147. if (!isValidPath) {
  148. depends.insert(depends.begin(), source);
  149. }
  150. } else if (isValidPath) {
  151. // remove first dependency because it must not be filtered out
  152. depends.erase(depends.begin());
  153. }
  154. }
  155. } else {
  156. // unknown format, ignore it
  157. continue;
  158. }
  159. if (isValidPath) {
  160. cm::erase_if(depends, isValidPath);
  161. if (!source.empty()) {
  162. // insert source as first dependency
  163. depends.insert(depends.begin(), source);
  164. }
  165. }
  166. dependencies[target] = std::move(depends);
  167. }
  168. }
  169. }
  170. return status;
  171. }
  172. void cmDependsCompiler::WriteDependencies(
  173. const cmDepends::DependencyMap& dependencies, std::ostream& makeDepends,
  174. std::ostream& internalDepends)
  175. {
  176. // dependencies file consumed by make tool
  177. const auto& lineContinue = static_cast<cmGlobalUnixMakefileGenerator3*>(
  178. this->LocalGenerator->GetGlobalGenerator())
  179. ->LineContinueDirective;
  180. bool supportLongLineDepend = static_cast<cmGlobalUnixMakefileGenerator3*>(
  181. this->LocalGenerator->GetGlobalGenerator())
  182. ->SupportsLongLineDependencies();
  183. cmDepends::DependencyMap makeDependencies(dependencies);
  184. std::unordered_set<cm::string_view> phonyTargets;
  185. // external dependencies file
  186. for (auto& node : makeDependencies) {
  187. auto target = this->LocalGenerator->ConvertToMakefilePath(
  188. this->LocalGenerator->MaybeRelativeToTopBinDir(node.first));
  189. auto& deps = node.second;
  190. std::transform(deps.cbegin(), deps.cend(), deps.begin(),
  191. [this](const std::string& dep) {
  192. return this->LocalGenerator->ConvertToMakefilePath(
  193. this->LocalGenerator->MaybeRelativeToTopBinDir(dep));
  194. });
  195. bool first_dep = true;
  196. if (supportLongLineDepend) {
  197. makeDepends << target << ": ";
  198. }
  199. for (const auto& dep : deps) {
  200. if (supportLongLineDepend) {
  201. if (first_dep) {
  202. first_dep = false;
  203. makeDepends << dep;
  204. } else {
  205. makeDepends << ' ' << lineContinue << " " << dep;
  206. }
  207. } else {
  208. makeDepends << target << ": " << dep << std::endl;
  209. }
  210. phonyTargets.emplace(dep.data(), dep.length());
  211. }
  212. makeDepends << std::endl << std::endl;
  213. }
  214. // add phony targets
  215. for (const auto& target : phonyTargets) {
  216. makeDepends << std::endl << target << ':' << std::endl;
  217. }
  218. // internal dependencies file
  219. for (const auto& node : dependencies) {
  220. internalDepends << node.first << std::endl;
  221. for (const auto& dep : node.second) {
  222. internalDepends << ' ' << dep << std::endl;
  223. }
  224. internalDepends << std::endl;
  225. }
  226. }
  227. void cmDependsCompiler::ClearDependencies(
  228. const std::vector<std::string>& depFiles)
  229. {
  230. for (auto dep = depFiles.begin(); dep != depFiles.end(); dep++) {
  231. dep += 3;
  232. cmSystemTools::RemoveFile(*dep);
  233. }
  234. }