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