| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmQtAutoGenerator.h" | |
| #include <iterator> | |
| #include <utility> | |
| #include <cm3p/json/reader.h> | |
| #include "cmsys/FStream.hxx" | |
| #include "cmQtAutoGen.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmValue.h" | |
| cmQtAutoGenerator::Logger::Logger() | |
| { | |
| // Initialize logger | |
| { | |
| std::string verbose; | |
| if (cmSystemTools::GetEnv("VERBOSE", verbose) && !verbose.empty()) { | |
| unsigned long iVerbose = 0; | |
| if (cmStrToULong(verbose, &iVerbose)) { | |
| this->SetVerbosity(static_cast<unsigned int>(iVerbose)); | |
| } else { | |
| // Non numeric verbosity | |
| this->SetVerbose(cmIsOn(verbose)); | |
| } | |
| } | |
| } | |
| { | |
| std::string colorEnv; | |
| cmSystemTools::GetEnv("COLOR", colorEnv); | |
| if (!colorEnv.empty()) { | |
| this->SetColorOutput(cmIsOn(colorEnv)); | |
| } else { | |
| this->SetColorOutput(true); | |
| } | |
| } | |
| } | |
| void cmQtAutoGenerator::Logger::RaiseVerbosity(unsigned int value) | |
| { | |
| if (this->Verbosity_ < value) { | |
| this->Verbosity_ = value; | |
| } | |
| } | |
| void cmQtAutoGenerator::Logger::SetColorOutput(bool value) | |
| { | |
| this->ColorOutput_ = value; | |
| } | |
| std::string cmQtAutoGenerator::Logger::HeadLine(cm::string_view title) | |
| { | |
| return cmStrCat(title, '\n', std::string(title.size(), '-'), '\n'); | |
| } | |
| void cmQtAutoGenerator::Logger::Info(GenT genType, | |
| cm::string_view message) const | |
| { | |
| std::string msg = cmStrCat(GeneratorName(genType), ": ", message, | |
| cmHasSuffix(message, '\n') ? "" : "\n"); | |
| { | |
| std::lock_guard<std::mutex> lock(this->Mutex_); | |
| cmSystemTools::Stdout(msg); | |
| } | |
| } | |
| void cmQtAutoGenerator::Logger::Warning(GenT genType, | |
| cm::string_view message) const | |
| { | |
| std::string msg; | |
| if (message.find('\n') == std::string::npos) { | |
| // Single line message | |
| msg = cmStrCat(GeneratorName(genType), " warning: ", message, | |
| cmHasSuffix(message, '\n') ? "\n" : "\n\n"); | |
| } else { | |
| // Multi line message | |
| msg = cmStrCat(HeadLine(cmStrCat(GeneratorName(genType), " warning")), | |
| message, cmHasSuffix(message, '\n') ? "\n" : "\n\n"); | |
| } | |
| { | |
| std::lock_guard<std::mutex> lock(this->Mutex_); | |
| cmSystemTools::Stdout(msg); | |
| } | |
| } | |
| void cmQtAutoGenerator::Logger::Error(GenT genType, | |
| cm::string_view message) const | |
| { | |
| std::string msg = | |
| cmStrCat('\n', HeadLine(cmStrCat(GeneratorName(genType), " error")), | |
| message, cmHasSuffix(message, '\n') ? "\n" : "\n\n"); | |
| { | |
| std::lock_guard<std::mutex> lock(this->Mutex_); | |
| cmSystemTools::Stderr(msg); | |
| } | |
| } | |
| void cmQtAutoGenerator::Logger::ErrorCommand( | |
| GenT genType, cm::string_view message, | |
| std::vector<std::string> const& command, std::string const& output) const | |
| { | |
| std::string msg = cmStrCat( | |
| '\n', HeadLine(cmStrCat(GeneratorName(genType), " subprocess error")), | |
| message, cmHasSuffix(message, '\n') ? "\n" : "\n\n"); | |
| msg = cmStrCat(std::move(msg), HeadLine("Command"), QuotedCommand(command), | |
| "\n\n"); | |
| msg = cmStrCat(std::move(msg), HeadLine("Output"), output, | |
| cmHasSuffix(output, '\n') ? "\n" : "\n\n"); | |
| { | |
| std::lock_guard<std::mutex> lock(this->Mutex_); | |
| cmSystemTools::Stderr(msg); | |
| } | |
| } | |
| bool cmQtAutoGenerator::MakeParentDirectory(std::string const& filename) | |
| { | |
| bool success = true; | |
| std::string const dirName = cmSystemTools::GetFilenamePath(filename); | |
| if (!dirName.empty()) { | |
| success = static_cast<bool>(cmSystemTools::MakeDirectory(dirName)); | |
| } | |
| return success; | |
| } | |
| bool cmQtAutoGenerator::FileRead(std::string& content, | |
| std::string const& filename, | |
| std::string* error) | |
| { | |
| content.clear(); | |
| if (!cmSystemTools::FileExists(filename, true)) { | |
| if (error) { | |
| *error = "Not a file."; | |
| } | |
| return false; | |
| } | |
| unsigned long const length = cmSystemTools::FileLength(filename); | |
| cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); | |
| // Use lambda to save destructor calls of ifs | |
| return [&ifs, length, &content, error]() -> bool { | |
| if (!ifs) { | |
| if (error) { | |
| *error = "Opening the file for reading failed."; | |
| } | |
| return false; | |
| } | |
| content.reserve(length); | |
| using IsIt = std::istreambuf_iterator<char>; | |
| content.assign(IsIt{ ifs }, IsIt{}); | |
| if (!ifs) { | |
| content.clear(); | |
| if (error) { | |
| *error = "Reading from the file failed."; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }(); | |
| } | |
| bool cmQtAutoGenerator::FileWrite(std::string const& filename, | |
| std::string const& content, | |
| std::string* error) | |
| { | |
| // Make sure the parent directory exists | |
| if (!cmQtAutoGenerator::MakeParentDirectory(filename)) { | |
| if (error) { | |
| *error = "Could not create parent directory."; | |
| } | |
| return false; | |
| } | |
| cmsys::ofstream ofs; | |
| ofs.open(filename.c_str(), | |
| (std::ios::out | std::ios::binary | std::ios::trunc)); | |
| // Use lambda to save destructor calls of ofs | |
| return [&ofs, &content, error]() -> bool { | |
| if (!ofs) { | |
| if (error) { | |
| *error = "Opening file for writing failed."; | |
| } | |
| return false; | |
| } | |
| ofs << content; | |
| if (!ofs.good()) { | |
| if (error) { | |
| *error = "File writing failed."; | |
| } | |
| return false; | |
| } | |
| return true; | |
| }(); | |
| } | |
| bool cmQtAutoGenerator::FileDiffers(std::string const& filename, | |
| std::string const& content) | |
| { | |
| bool differs = true; | |
| std::string oldContents; | |
| if (FileRead(oldContents, filename) && (oldContents == content)) { | |
| differs = false; | |
| } | |
| return differs; | |
| } | |
| cmQtAutoGenerator::cmQtAutoGenerator(GenT genType) | |
| : GenType_(genType) | |
| { | |
| } | |
| cmQtAutoGenerator::~cmQtAutoGenerator() = default; | |
| bool cmQtAutoGenerator::InfoT::Read(std::istream& istr) | |
| { | |
| try { | |
| istr >> this->Json_; | |
| } catch (...) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool cmQtAutoGenerator::InfoT::GetJsonArray(std::vector<std::string>& list, | |
| Json::Value const& jval) | |
| { | |
| Json::ArrayIndex const arraySize = jval.size(); | |
| if (arraySize == 0) { | |
| return false; | |
| } | |
| bool picked = false; | |
| list.reserve(list.size() + arraySize); | |
| for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { | |
| Json::Value const& ival = jval[ii]; | |
| if (ival.isString()) { | |
| list.emplace_back(ival.asString()); | |
| picked = true; | |
| } | |
| } | |
| return picked; | |
| } | |
| bool cmQtAutoGenerator::InfoT::GetJsonArray( | |
| std::unordered_set<std::string>& list, Json::Value const& jval) | |
| { | |
| Json::ArrayIndex const arraySize = jval.size(); | |
| if (arraySize == 0) { | |
| return false; | |
| } | |
| bool picked = false; | |
| list.reserve(list.size() + arraySize); | |
| for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { | |
| Json::Value const& ival = jval[ii]; | |
| if (ival.isString()) { | |
| list.emplace(ival.asString()); | |
| picked = true; | |
| } | |
| } | |
| return picked; | |
| } | |
| std::string cmQtAutoGenerator::InfoT::ConfigKey(cm::string_view key) const | |
| { | |
| return cmStrCat(key, '_', this->Gen_.InfoConfig()); | |
| } | |
| bool cmQtAutoGenerator::InfoT::GetString(std::string const& key, | |
| std::string& value, | |
| bool required) const | |
| { | |
| Json::Value const& jval = this->Json_[key]; | |
| if (!jval.isString()) { | |
| if (!jval.isNull() || required) { | |
| return this->LogError(cmStrCat(key, " is not a string.")); | |
| } | |
| } else { | |
| value = jval.asString(); | |
| if (value.empty() && required) { | |
| return this->LogError(cmStrCat(key, " is empty.")); | |
| } | |
| } | |
| return true; | |
| } | |
| bool cmQtAutoGenerator::InfoT::GetStringConfig(std::string const& key, | |
| std::string& value, | |
| bool required) const | |
| { | |
| { // Try config | |
| std::string const configKey = this->ConfigKey(key); | |
| Json::Value const& jval = this->Json_[configKey]; | |
| if (!jval.isNull()) { | |
| if (!jval.isString()) { | |
| return this->LogError(cmStrCat(configKey, " is not a string.")); | |
| } | |
| value = jval.asString(); | |
| if (required && value.empty()) { | |
| return this->LogError(cmStrCat(configKey, " is empty.")); | |
| } | |
| return true; | |
| } | |
| } | |
| // Try plain | |
| return this->GetString(key, value, required); | |
| } | |
| bool cmQtAutoGenerator::InfoT::GetBool(std::string const& key, bool& value, | |
| bool required) const | |
| { | |
| Json::Value const& jval = this->Json_[key]; | |
| if (jval.isBool()) { | |
| value = jval.asBool(); | |
| } else { | |
| if (!jval.isNull() || required) { | |
| return this->LogError(cmStrCat(key, " is not a boolean.")); | |
| } | |
| } | |
| return true; | |
| } | |
| bool cmQtAutoGenerator::InfoT::GetUInt(std::string const& key, | |
| unsigned int& value, | |
| bool required) const | |
| { | |
| Json::Value const& jval = this->Json_[key]; | |
| if (jval.isUInt()) { | |
| value = jval.asUInt(); | |
| } else { | |
| if (!jval.isNull() || required) { | |
| return this->LogError(cmStrCat(key, " is not an unsigned integer.")); | |
| } | |
| } | |
| return true; | |
| } | |
| bool cmQtAutoGenerator::InfoT::GetArray(std::string const& key, | |
| std::vector<std::string>& list, | |
| bool required) const | |
| { | |
| Json::Value const& jval = this->Json_[key]; | |
| if (!jval.isArray()) { | |
| if (!jval.isNull() || required) { | |
| return this->LogError(cmStrCat(key, " is not an array.")); | |
| } | |
| } | |
| return GetJsonArray(list, jval) || !required; | |
| } | |
| bool cmQtAutoGenerator::InfoT::GetArray(std::string const& key, | |
| std::unordered_set<std::string>& list, | |
| bool required) const | |
| { | |
| Json::Value const& jval = this->Json_[key]; | |
| if (!jval.isArray()) { | |
| if (!jval.isNull() || required) { | |
| return this->LogError(cmStrCat(key, " is not an array.")); | |
| } | |
| } | |
| return GetJsonArray(list, jval) || !required; | |
| } | |
| bool cmQtAutoGenerator::InfoT::GetArrayConfig(std::string const& key, | |
| std::vector<std::string>& list, | |
| bool required) const | |
| { | |
| { // Try config | |
| std::string const configKey = this->ConfigKey(key); | |
| Json::Value const& jval = this->Json_[configKey]; | |
| if (!jval.isNull()) { | |
| if (!jval.isArray()) { | |
| return this->LogError(cmStrCat(configKey, " is not an array string.")); | |
| } | |
| if (!GetJsonArray(list, jval) && required) { | |
| return this->LogError(cmStrCat(configKey, " is empty.")); | |
| } | |
| return true; | |
| } | |
| } | |
| // Try plain | |
| return this->GetArray(key, list, required); | |
| } | |
| bool cmQtAutoGenerator::InfoT::LogError(GenT genType, | |
| cm::string_view message) const | |
| { | |
| this->Gen_.Log().Error(genType, | |
| cmStrCat("Info error in info file\n", | |
| Quoted(this->Gen_.InfoFile()), ":\n", | |
| message)); | |
| return false; | |
| } | |
| bool cmQtAutoGenerator::InfoT::LogError(cm::string_view message) const | |
| { | |
| return this->LogError(this->Gen_.GenType_, message); | |
| } | |
| std::string cmQtAutoGenerator::SettingsFind(cm::string_view content, | |
| cm::string_view key) | |
| { | |
| cm::string_view res; | |
| std::string const prefix = cmStrCat(key, ':'); | |
| cm::string_view::size_type pos = content.find(prefix); | |
| if (pos != cm::string_view::npos) { | |
| pos += prefix.size(); | |
| if (pos < content.size()) { | |
| cm::string_view::size_type posE = content.find('\n', pos); | |
| if ((posE != cm::string_view::npos) && (posE != pos)) { | |
| res = content.substr(pos, posE - pos); | |
| } | |
| } | |
| } | |
| return std::string(res); | |
| } | |
| std::string cmQtAutoGenerator::MessagePath(cm::string_view path) const | |
| { | |
| std::string res; | |
| if (cmHasPrefix(path, this->ProjectDirs().Source)) { | |
| res = cmStrCat("SRC:", path.substr(this->ProjectDirs().Source.size())); | |
| } else if (cmHasPrefix(path, this->ProjectDirs().Binary)) { | |
| res = cmStrCat("BIN:", path.substr(this->ProjectDirs().Binary.size())); | |
| } else { | |
| res = std::string(path); | |
| } | |
| return cmQtAutoGen::Quoted(res); | |
| } | |
| bool cmQtAutoGenerator::Run(cm::string_view infoFile, cm::string_view config, | |
| cm::string_view executableConfig) | |
| { | |
| // Info config | |
| this->InfoConfig_ = std::string(config); | |
| this->ExecutableConfig_ = std::string(executableConfig); | |
| // Info file | |
| this->InfoFile_ = std::string(infoFile); | |
| this->InfoDir_ = cmSystemTools::GetFilenamePath(this->InfoFile_); | |
| // Load info file time | |
| if (!this->InfoFileTime_.Load(this->InfoFile_)) { | |
| cmSystemTools::Stderr(cmStrCat("AutoGen: The info file ", | |
| Quoted(this->InfoFile_), | |
| " is not readable\n")); | |
| return false; | |
| } | |
| { | |
| InfoT info(*this); | |
| // Read info file | |
| { | |
| cmsys::ifstream ifs(this->InfoFile_.c_str(), | |
| (std::ios::in | std::ios::binary)); | |
| if (!ifs) { | |
| this->Log().Error( | |
| this->GenType_, | |
| cmStrCat("Could not to open info file ", Quoted(this->InfoFile_))); | |
| return false; | |
| } | |
| if (!info.Read(ifs)) { | |
| this->Log().Error( | |
| this->GenType_, | |
| cmStrCat("Could not read info file ", Quoted(this->InfoFile_))); | |
| return false; | |
| } | |
| } | |
| // -- Read common info settings | |
| { | |
| unsigned int verbosity = 0; | |
| // Info: setup project directories | |
| if (!info.GetUInt("VERBOSITY", verbosity, false) || | |
| !info.GetString("CMAKE_SOURCE_DIR", this->ProjectDirs_.Source, | |
| true) || | |
| !info.GetString("CMAKE_BINARY_DIR", this->ProjectDirs_.Binary, | |
| true) || | |
| !info.GetString("CMAKE_CURRENT_SOURCE_DIR", | |
| this->ProjectDirs_.CurrentSource, true) || | |
| !info.GetString("CMAKE_CURRENT_BINARY_DIR", | |
| this->ProjectDirs_.CurrentBinary, true)) { | |
| return false; | |
| } | |
| this->Logger_.RaiseVerbosity(verbosity); | |
| } | |
| // -- Call virtual init from info method. | |
| if (!this->InitFromInfo(info)) { | |
| return false; | |
| } | |
| } | |
| // Call virtual process method. | |
| return this->Process(); | |
| } |