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