cmGccDepfileLexerHelper.cxx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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 "cmGccDepfileLexerHelper.h"
  4. #include <algorithm>
  5. #include <cstdio>
  6. #include <memory>
  7. #include <string>
  8. #include <vector>
  9. #include "cmGccDepfileReaderTypes.h"
  10. #include "LexerParser/cmGccDepfileLexer.h"
  11. #ifdef _WIN32
  12. # include <cctype>
  13. # include "cmsys/Encoding.h"
  14. #endif
  15. bool cmGccDepfileLexerHelper::readFile(const char* filePath)
  16. {
  17. #ifdef _WIN32
  18. wchar_t* wpath = cmsysEncoding_DupToWide(filePath);
  19. FILE* file = _wfopen(wpath, L"rb");
  20. free(wpath);
  21. #else
  22. FILE* file = fopen(filePath, "r");
  23. #endif
  24. if (!file) {
  25. return false;
  26. }
  27. this->newEntry();
  28. yyscan_t scanner;
  29. cmGccDepfile_yylex_init(&scanner);
  30. cmGccDepfile_yyset_extra(this, scanner);
  31. cmGccDepfile_yyrestart(file, scanner);
  32. cmGccDepfile_yylex(scanner);
  33. cmGccDepfile_yylex_destroy(scanner);
  34. this->sanitizeContent();
  35. fclose(file);
  36. return this->HelperState != State::Failed;
  37. }
  38. void cmGccDepfileLexerHelper::newEntry()
  39. {
  40. if (this->HelperState == State::Rule && !this->Content.empty()) {
  41. if (!this->Content.back().rules.empty() &&
  42. !this->Content.back().rules.back().empty()) {
  43. this->HelperState = State::Failed;
  44. }
  45. return;
  46. }
  47. this->HelperState = State::Rule;
  48. this->Content.emplace_back();
  49. this->newRule();
  50. }
  51. void cmGccDepfileLexerHelper::newRule()
  52. {
  53. auto& entry = this->Content.back();
  54. if (entry.rules.empty() || !entry.rules.back().empty()) {
  55. entry.rules.emplace_back();
  56. }
  57. }
  58. void cmGccDepfileLexerHelper::newDependency()
  59. {
  60. if (this->HelperState == State::Failed) {
  61. return;
  62. }
  63. this->HelperState = State::Dependency;
  64. auto& entry = this->Content.back();
  65. if (entry.paths.empty() || !entry.paths.back().empty()) {
  66. entry.paths.emplace_back();
  67. }
  68. }
  69. void cmGccDepfileLexerHelper::newRuleOrDependency()
  70. {
  71. if (this->HelperState == State::Rule) {
  72. this->newRule();
  73. } else if (this->HelperState == State::Dependency) {
  74. this->newDependency();
  75. }
  76. }
  77. void cmGccDepfileLexerHelper::addToCurrentPath(const char* s)
  78. {
  79. if (this->Content.empty()) {
  80. return;
  81. }
  82. cmGccStyleDependency* dep = &this->Content.back();
  83. std::string* dst = nullptr;
  84. switch (this->HelperState) {
  85. case State::Rule: {
  86. if (dep->rules.empty()) {
  87. return;
  88. }
  89. dst = &dep->rules.back();
  90. } break;
  91. case State::Dependency: {
  92. if (dep->paths.empty()) {
  93. return;
  94. }
  95. dst = &dep->paths.back();
  96. } break;
  97. case State::Failed:
  98. return;
  99. }
  100. dst->append(s);
  101. }
  102. void cmGccDepfileLexerHelper::sanitizeContent()
  103. {
  104. for (auto it = this->Content.begin(); it != this->Content.end();) {
  105. // remove duplicate path entries
  106. std::sort(it->paths.begin(), it->paths.end());
  107. auto last = std::unique(it->paths.begin(), it->paths.end());
  108. it->paths.erase(last, it->paths.end());
  109. // Remove empty paths and normalize windows paths
  110. for (auto pit = it->paths.begin(); pit != it->paths.end();) {
  111. if (pit->empty()) {
  112. pit = it->paths.erase(pit);
  113. } else {
  114. #if defined(_WIN32)
  115. // Unescape the colon following the drive letter.
  116. // Some versions of GNU compilers can escape this character.
  117. // c\:\path must be transformed to c:\path
  118. if (pit->size() >= 3 && std::toupper((*pit)[0]) >= 'A' &&
  119. std::toupper((*pit)[0]) <= 'Z' && (*pit)[1] == '\\' &&
  120. (*pit)[2] == ':') {
  121. pit->erase(1, 1);
  122. }
  123. #endif
  124. ++pit;
  125. }
  126. }
  127. // Remove empty rules
  128. for (auto rit = it->rules.begin(); rit != it->rules.end();) {
  129. if (rit->empty()) {
  130. rit = it->rules.erase(rit);
  131. } else {
  132. ++rit;
  133. }
  134. }
  135. // Remove the entry if rules are empty or do not have any paths
  136. if (it->rules.empty() || it->paths.empty()) {
  137. it = this->Content.erase(it);
  138. } else {
  139. ++it;
  140. }
  141. }
  142. }