/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.81k | { |
21 | | #ifdef _WIN32 |
22 | | wchar_t* wpath = cmsysEncoding_DupToWide(filePath); |
23 | | FILE* file = _wfopen(wpath, L"rb"); |
24 | | free(wpath); |
25 | | #else |
26 | 5.81k | FILE* file = fopen(filePath, "r"); |
27 | 5.81k | #endif |
28 | 5.81k | if (!file) { |
29 | 0 | return false; |
30 | 0 | } |
31 | 5.81k | this->newEntry(); |
32 | 5.81k | yyscan_t scanner; |
33 | 5.81k | cmGccDepfile_yylex_init(&scanner); |
34 | 5.81k | cmGccDepfile_yyset_extra(this, scanner); |
35 | 5.81k | cmGccDepfile_yyrestart(file, scanner); |
36 | 5.81k | cmGccDepfile_yylex(scanner); |
37 | 5.81k | cmGccDepfile_yylex_destroy(scanner); |
38 | 5.81k | this->sanitizeContent(); |
39 | 5.81k | fclose(file); |
40 | 5.81k | return this->HelperState != State::Failed; |
41 | 5.81k | } |
42 | | |
43 | | void cmGccDepfileLexerHelper::newEntry() |
44 | 106k | { |
45 | 106k | if (this->HelperState == State::Rule && !this->Content.empty()) { |
46 | 2.30k | if (!this->Content.back().rules.empty() && |
47 | 2.30k | !this->Content.back().rules.back().empty()) { |
48 | 1.32k | this->HelperState = State::Failed; |
49 | 1.32k | } |
50 | 2.30k | return; |
51 | 2.30k | } |
52 | 104k | this->HelperState = State::Rule; |
53 | 104k | this->Content.emplace_back(); |
54 | 104k | this->newRule(); |
55 | 104k | } |
56 | | |
57 | | void cmGccDepfileLexerHelper::newRule() |
58 | 131k | { |
59 | 131k | auto& entry = this->Content.back(); |
60 | 131k | if (entry.rules.empty() || !entry.rules.back().empty()) { |
61 | 129k | entry.rules.emplace_back(); |
62 | 129k | } |
63 | 131k | } |
64 | | |
65 | | void cmGccDepfileLexerHelper::newDependency() |
66 | 2.21M | { |
67 | 2.21M | if (this->HelperState == State::Failed) { |
68 | 1.12k | return; |
69 | 1.12k | } |
70 | 2.21M | this->HelperState = State::Dependency; |
71 | 2.21M | auto& entry = this->Content.back(); |
72 | 2.21M | if (entry.paths.empty() || !entry.paths.back().empty()) { |
73 | 2.21M | entry.paths.emplace_back(); |
74 | 2.21M | } |
75 | 2.21M | } |
76 | | |
77 | | void cmGccDepfileLexerHelper::newRuleOrDependency() |
78 | 2.12M | { |
79 | 2.12M | if (this->HelperState == State::Rule) { |
80 | 27.6k | this->newRule(); |
81 | 2.09M | } else if (this->HelperState == State::Dependency) { |
82 | 2.08M | this->newDependency(); |
83 | 2.08M | } |
84 | 2.12M | } |
85 | | |
86 | | void cmGccDepfileLexerHelper::addToCurrentPath(char const* s) |
87 | 5.90M | { |
88 | 5.90M | if (this->Content.empty()) { |
89 | 0 | return; |
90 | 0 | } |
91 | 5.90M | cmGccStyleDependency* dep = &this->Content.back(); |
92 | 5.90M | std::string* dst = nullptr; |
93 | 5.90M | switch (this->HelperState) { |
94 | 1.30M | case State::Rule: { |
95 | 1.30M | if (dep->rules.empty()) { |
96 | 0 | return; |
97 | 0 | } |
98 | 1.30M | dst = &dep->rules.back(); |
99 | 1.30M | } break; |
100 | 4.35M | case State::Dependency: { |
101 | 4.35M | if (dep->paths.empty()) { |
102 | 0 | return; |
103 | 0 | } |
104 | 4.35M | dst = &dep->paths.back(); |
105 | 4.35M | } break; |
106 | 239k | case State::Failed: |
107 | 239k | return; |
108 | 5.90M | } |
109 | 5.66M | dst->append(s); |
110 | 5.66M | } |
111 | | |
112 | | void cmGccDepfileLexerHelper::sanitizeContent() |
113 | 5.81k | { |
114 | 110k | for (auto it = this->Content.begin(); it != this->Content.end();) { |
115 | | // remove duplicate path entries |
116 | 104k | std::sort(it->paths.begin(), it->paths.end()); |
117 | 104k | auto last = std::unique(it->paths.begin(), it->paths.end()); |
118 | 104k | it->paths.erase(last, it->paths.end()); |
119 | | |
120 | | // Remove empty paths and normalize windows paths |
121 | 382k | for (auto pit = it->paths.begin(); pit != it->paths.end();) { |
122 | 278k | if (pit->empty()) { |
123 | 74.8k | pit = it->paths.erase(pit); |
124 | 203k | } 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 | 203k | ++pit; |
138 | 203k | } |
139 | 278k | } |
140 | | // Remove empty rules |
141 | 233k | for (auto rit = it->rules.begin(); rit != it->rules.end();) { |
142 | 129k | if (rit->empty()) { |
143 | 47.4k | rit = it->rules.erase(rit); |
144 | 82.0k | } else { |
145 | 82.0k | ++rit; |
146 | 82.0k | } |
147 | 129k | } |
148 | | // Remove the entry if rules are empty |
149 | 104k | if (it->rules.empty()) { |
150 | 46.7k | it = this->Content.erase(it); |
151 | 57.4k | } else { |
152 | 57.4k | ++it; |
153 | 57.4k | } |
154 | 104k | } |
155 | 5.81k | } |