/src/CMake/Source/cmGccDepfileLexerHelper.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmGccDepfileLexerHelper.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <cstdio> |
7 | | #include <string> |
8 | | #include <vector> |
9 | | |
10 | | #include "cmGccDepfileReaderTypes.h" |
11 | | |
12 | | #include "LexerParser/cmGccDepfileLexer.h" |
13 | | |
14 | | #ifdef _WIN32 |
15 | | # include "cmsys/Encoding.h" |
16 | | # include "cmsys/String.h" |
17 | | #endif |
18 | | |
19 | | bool cmGccDepfileLexerHelper::readFile(char const* filePath) |
20 | 5.67k | { |
21 | | #ifdef _WIN32 |
22 | | wchar_t* wpath = cmsysEncoding_DupToWide(filePath); |
23 | | FILE* file = _wfopen(wpath, L"rb"); |
24 | | free(wpath); |
25 | | #else |
26 | 5.67k | FILE* file = fopen(filePath, "r"); |
27 | 5.67k | #endif |
28 | 5.67k | if (!file) { |
29 | 0 | return false; |
30 | 0 | } |
31 | 5.67k | this->newEntry(); |
32 | 5.67k | yyscan_t scanner; |
33 | 5.67k | cmGccDepfile_yylex_init(&scanner); |
34 | 5.67k | cmGccDepfile_yyset_extra(this, scanner); |
35 | 5.67k | cmGccDepfile_yyrestart(file, scanner); |
36 | 5.67k | cmGccDepfile_yylex(scanner); |
37 | 5.67k | cmGccDepfile_yylex_destroy(scanner); |
38 | 5.67k | this->sanitizeContent(); |
39 | 5.67k | fclose(file); |
40 | 5.67k | return this->HelperState != State::Failed; |
41 | 5.67k | } |
42 | | |
43 | | void cmGccDepfileLexerHelper::newEntry() |
44 | 111k | { |
45 | 111k | if (this->HelperState == State::Rule && !this->Content.empty()) { |
46 | 3.80k | if (!this->Content.back().rules.empty() && |
47 | 3.80k | !this->Content.back().rules.back().empty()) { |
48 | 1.73k | this->HelperState = State::Failed; |
49 | 1.73k | } |
50 | 3.80k | return; |
51 | 3.80k | } |
52 | 107k | this->HelperState = State::Rule; |
53 | 107k | this->Content.emplace_back(); |
54 | 107k | this->newRule(); |
55 | 107k | } |
56 | | |
57 | | void cmGccDepfileLexerHelper::newRule() |
58 | 128k | { |
59 | 128k | auto& entry = this->Content.back(); |
60 | 128k | if (entry.rules.empty() || !entry.rules.back().empty()) { |
61 | 125k | entry.rules.emplace_back(); |
62 | 125k | } |
63 | 128k | } |
64 | | |
65 | | void cmGccDepfileLexerHelper::newDependency() |
66 | 2.23M | { |
67 | 2.23M | if (this->HelperState == State::Failed) { |
68 | 1.36k | return; |
69 | 1.36k | } |
70 | 2.23M | this->HelperState = State::Dependency; |
71 | 2.23M | auto& entry = this->Content.back(); |
72 | 2.23M | if (entry.paths.empty() || !entry.paths.back().empty()) { |
73 | 2.22M | entry.paths.emplace_back(); |
74 | 2.22M | } |
75 | 2.23M | } |
76 | | |
77 | | void cmGccDepfileLexerHelper::newRuleOrDependency() |
78 | 2.12M | { |
79 | 2.12M | if (this->HelperState == State::Rule) { |
80 | 21.0k | this->newRule(); |
81 | 2.09M | } else if (this->HelperState == State::Dependency) { |
82 | 2.09M | this->newDependency(); |
83 | 2.09M | } |
84 | 2.12M | } |
85 | | |
86 | | void cmGccDepfileLexerHelper::addToCurrentPath(char const* s) |
87 | 5.53M | { |
88 | 5.53M | if (this->Content.empty()) { |
89 | 0 | return; |
90 | 0 | } |
91 | 5.53M | cmGccStyleDependency* dep = &this->Content.back(); |
92 | 5.53M | std::string* dst = nullptr; |
93 | 5.53M | switch (this->HelperState) { |
94 | 852k | case State::Rule: { |
95 | 852k | if (dep->rules.empty()) { |
96 | 0 | return; |
97 | 0 | } |
98 | 852k | dst = &dep->rules.back(); |
99 | 852k | } break; |
100 | 4.48M | case State::Dependency: { |
101 | 4.48M | if (dep->paths.empty()) { |
102 | 0 | return; |
103 | 0 | } |
104 | 4.48M | dst = &dep->paths.back(); |
105 | 4.48M | } break; |
106 | 193k | case State::Failed: |
107 | 193k | return; |
108 | 5.53M | } |
109 | 5.33M | dst->append(s); |
110 | 5.33M | } |
111 | | |
112 | | void cmGccDepfileLexerHelper::sanitizeContent() |
113 | 5.67k | { |
114 | 113k | for (auto it = this->Content.begin(); it != this->Content.end();) { |
115 | | // remove duplicate path entries |
116 | 107k | std::sort(it->paths.begin(), it->paths.end()); |
117 | 107k | auto last = std::unique(it->paths.begin(), it->paths.end()); |
118 | 107k | it->paths.erase(last, it->paths.end()); |
119 | | |
120 | | // Remove empty paths and normalize windows paths |
121 | 404k | for (auto pit = it->paths.begin(); pit != it->paths.end();) { |
122 | 297k | if (pit->empty()) { |
123 | 76.9k | pit = it->paths.erase(pit); |
124 | 220k | } else { |
125 | | #if defined(_WIN32) |
126 | | // Unescape the colon following the drive letter. |
127 | | // Some versions of GNU compilers can escape this character. |
128 | | // c\:\path must be transformed to c:\path |
129 | | if (pit->size() >= 3) { |
130 | | auto pit0 = static_cast<char>(cmsysString_toupper((*pit)[0])); |
131 | | if (pit0 >= 'A' && pit0 <= 'Z' && (*pit)[1] == '\\' && |
132 | | (*pit)[2] == ':') { |
133 | | pit->erase(1, 1); |
134 | | } |
135 | | } |
136 | | #endif |
137 | 220k | ++pit; |
138 | 220k | } |
139 | 297k | } |
140 | | // Remove empty rules |
141 | 233k | for (auto rit = it->rules.begin(); rit != it->rules.end();) { |
142 | 125k | if (rit->empty()) { |
143 | 62.5k | rit = it->rules.erase(rit); |
144 | 63.1k | } else { |
145 | 63.1k | ++rit; |
146 | 63.1k | } |
147 | 125k | } |
148 | | // Remove the entry if rules are empty |
149 | 107k | if (it->rules.empty()) { |
150 | 61.7k | it = this->Content.erase(it); |
151 | 61.7k | } else { |
152 | 45.8k | ++it; |
153 | 45.8k | } |
154 | 107k | } |
155 | 5.67k | } |