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