CMake Nightly Date Stamp
[kiteware-cmake.git] / Source / cmGccDepfileLexerHelper.cxx
blob9706c50aea656205b525f37a1ee0cfb6772f614f
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"
5 #include <algorithm>
6 #include <cstdio>
7 #include <string>
8 #include <vector>
10 #include "cmGccDepfileReaderTypes.h"
12 #include "LexerParser/cmGccDepfileLexer.h"
14 #ifdef _WIN32
15 # include <cctype>
17 # include "cmsys/Encoding.h"
18 #endif
20 bool cmGccDepfileLexerHelper::readFile(const char* filePath)
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;
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;
44 void cmGccDepfileLexerHelper::newEntry()
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;
51 return;
53 this->HelperState = State::Rule;
54 this->Content.emplace_back();
55 this->newRule();
58 void cmGccDepfileLexerHelper::newRule()
60 auto& entry = this->Content.back();
61 if (entry.rules.empty() || !entry.rules.back().empty()) {
62 entry.rules.emplace_back();
66 void cmGccDepfileLexerHelper::newDependency()
68 if (this->HelperState == State::Failed) {
69 return;
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();
78 void cmGccDepfileLexerHelper::newRuleOrDependency()
80 if (this->HelperState == State::Rule) {
81 this->newRule();
82 } else if (this->HelperState == State::Dependency) {
83 this->newDependency();
87 void cmGccDepfileLexerHelper::addToCurrentPath(const char* s)
89 if (this->Content.empty()) {
90 return;
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;
99 dst = &dep->rules.back();
100 } break;
101 case State::Dependency: {
102 if (dep->paths.empty()) {
103 return;
105 dst = &dep->paths.back();
106 } break;
107 case State::Failed:
108 return;
110 dst->append(s);
113 void cmGccDepfileLexerHelper::sanitizeContent()
115 for (auto it = this->Content.begin(); it != this->Content.end();) {
116 // remove duplicate path entries
117 std::sort(it->paths.begin(), it->paths.end());
118 auto last = std::unique(it->paths.begin(), it->paths.end());
119 it->paths.erase(last, it->paths.end());
121 // Remove empty paths and normalize windows paths
122 for (auto pit = it->paths.begin(); pit != it->paths.end();) {
123 if (pit->empty()) {
124 pit = it->paths.erase(pit);
125 } 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 && std::toupper((*pit)[0]) >= 'A' &&
131 std::toupper((*pit)[0]) <= 'Z' && (*pit)[1] == '\\' &&
132 (*pit)[2] == ':') {
133 pit->erase(1, 1);
135 #endif
136 ++pit;
139 // Remove empty rules
140 for (auto rit = it->rules.begin(); rit != it->rules.end();) {
141 if (rit->empty()) {
142 rit = it->rules.erase(rit);
143 } else {
144 ++rit;
147 // Remove the entry if rules are empty
148 if (it->rules.empty()) {
149 it = this->Content.erase(it);
150 } else {
151 ++it;