cmDependsCompiler.cxx 8.1 KB

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