| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmGccDepfileLexerHelper.h" | |
| #include <algorithm> | |
| #include <cstdio> | |
| #include <string> | |
| #include <vector> | |
| #include "cmGccDepfileReaderTypes.h" | |
| #include "cmSystemTools.h" | |
| #include "LexerParser/cmGccDepfileLexer.h" | |
| #ifdef _WIN32 | |
| # include "cmsys/String.h" | |
| #endif | |
| bool cmGccDepfileLexerHelper::readFile(char const* filePath) | |
| { | |
| FILE* file = cmsys::SystemTools::Fopen(filePath, "rb"); | |
| if (!file) { | |
| return false; | |
| } | |
| this->newEntry(); | |
| yyscan_t scanner; | |
| cmGccDepfile_yylex_init(&scanner); | |
| cmGccDepfile_yyset_extra(this, scanner); | |
| cmGccDepfile_yyrestart(file, scanner); | |
| cmGccDepfile_yylex(scanner); | |
| cmGccDepfile_yylex_destroy(scanner); | |
| this->sanitizeContent(); | |
| fclose(file); | |
| return this->HelperState != State::Failed; | |
| } | |
| void cmGccDepfileLexerHelper::newEntry() | |
| { | |
| if (this->HelperState == State::Rule && !this->Content.empty()) { | |
| if (!this->Content.back().rules.empty() && | |
| !this->Content.back().rules.back().empty()) { | |
| this->HelperState = State::Failed; | |
| } | |
| return; | |
| } | |
| this->HelperState = State::Rule; | |
| this->Content.emplace_back(); | |
| this->newRule(); | |
| } | |
| void cmGccDepfileLexerHelper::newRule() | |
| { | |
| auto& entry = this->Content.back(); | |
| if (entry.rules.empty() || !entry.rules.back().empty()) { | |
| entry.rules.emplace_back(); | |
| } | |
| } | |
| void cmGccDepfileLexerHelper::newDependency() | |
| { | |
| if (this->HelperState == State::Failed) { | |
| return; | |
| } | |
| this->HelperState = State::Dependency; | |
| auto& entry = this->Content.back(); | |
| if (entry.paths.empty() || !entry.paths.back().empty()) { | |
| entry.paths.emplace_back(); | |
| } | |
| } | |
| void cmGccDepfileLexerHelper::newRuleOrDependency() | |
| { | |
| if (this->HelperState == State::Rule) { | |
| this->newRule(); | |
| } else if (this->HelperState == State::Dependency) { | |
| this->newDependency(); | |
| } | |
| } | |
| void cmGccDepfileLexerHelper::addToCurrentPath(char const* s) | |
| { | |
| if (this->Content.empty()) { | |
| return; | |
| } | |
| cmGccStyleDependency* dep = &this->Content.back(); | |
| std::string* dst = nullptr; | |
| switch (this->HelperState) { | |
| case State::Rule: { | |
| if (dep->rules.empty()) { | |
| return; | |
| } | |
| dst = &dep->rules.back(); | |
| } break; | |
| case State::Dependency: { | |
| if (dep->paths.empty()) { | |
| return; | |
| } | |
| dst = &dep->paths.back(); | |
| } break; | |
| case State::Failed: | |
| return; | |
| } | |
| dst->append(s); | |
| } | |
| void cmGccDepfileLexerHelper::sanitizeContent() | |
| { | |
| for (auto it = this->Content.begin(); it != this->Content.end();) { | |
| // remove duplicate path entries | |
| std::sort(it->paths.begin(), it->paths.end()); | |
| auto last = std::unique(it->paths.begin(), it->paths.end()); | |
| it->paths.erase(last, it->paths.end()); | |
| // Remove empty paths and normalize windows paths | |
| for (auto pit = it->paths.begin(); pit != it->paths.end();) { | |
| if (pit->empty()) { | |
| pit = it->paths.erase(pit); | |
| } else { | |
| #if defined(_WIN32) | |
| // Unescape the colon following the drive letter. | |
| // Some versions of GNU compilers can escape this character. | |
| // c\:\path must be transformed to c:\path | |
| if (pit->size() >= 3) { | |
| auto pit0 = static_cast<char>(cmsysString_toupper((*pit)[0])); | |
| if (pit0 >= 'A' && pit0 <= 'Z' && (*pit)[1] == '\\' && | |
| (*pit)[2] == ':') { | |
| pit->erase(1, 1); | |
| } | |
| } | |
| #endif | |
| ++pit; | |
| } | |
| } | |
| // Remove empty rules | |
| for (auto rit = it->rules.begin(); rit != it->rules.end();) { | |
| if (rit->empty()) { | |
| rit = it->rules.erase(rit); | |
| } else { | |
| ++rit; | |
| } | |
| } | |
| // Remove the entry if rules are empty | |
| if (it->rules.empty()) { | |
| it = this->Content.erase(it); | |
| } else { | |
| ++it; | |
| } | |
| } | |
| } |