cmDependsCompiler.cxx 7.9 KB

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