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 "cmQtAutoGenerator.h"
5 #include <cm3p/json/reader.h>
7 #include "cmsys/FStream.hxx"
9 #include "cmQtAutoGen.h"
10 #include "cmStringAlgorithms.h"
11 #include "cmSystemTools.h"
14 cmQtAutoGenerator::Logger::Logger()
19 if (cmSystemTools::GetEnv("VERBOSE", verbose) && !verbose.empty()) {
20 unsigned long iVerbose = 0;
21 if (cmStrToULong(verbose, &iVerbose)) {
22 this->SetVerbosity(static_cast<unsigned int>(iVerbose));
24 // Non numeric verbosity
25 this->SetVerbose(cmIsOn(verbose));
31 cmSystemTools::GetEnv("COLOR", colorEnv);
32 if (!colorEnv.empty()) {
33 this->SetColorOutput(cmIsOn(colorEnv));
35 this->SetColorOutput(true);
40 void cmQtAutoGenerator::Logger::RaiseVerbosity(unsigned int value)
42 if (this->Verbosity_ < value) {
43 this->Verbosity_ = value;
47 void cmQtAutoGenerator::Logger::SetColorOutput(bool value)
49 this->ColorOutput_ = value;
52 std::string cmQtAutoGenerator::Logger::HeadLine(cm::string_view title)
54 return cmStrCat(title, '\n', std::string(title.size(), '-'), '\n');
57 void cmQtAutoGenerator::Logger::Info(GenT genType,
58 cm::string_view message) const
60 std::string msg = cmStrCat(GeneratorName(genType), ": ", message,
61 cmHasSuffix(message, '\n') ? "" : "\n");
63 std::lock_guard<std::mutex> lock(this->Mutex_);
64 cmSystemTools::Stdout(msg);
68 void cmQtAutoGenerator::Logger::Warning(GenT genType,
69 cm::string_view message) const
72 if (message.find('\n') == std::string::npos) {
73 // Single line message
74 msg = cmStrCat(GeneratorName(genType), " warning: ", message,
75 cmHasSuffix(message, '\n') ? "\n" : "\n\n");
78 msg = cmStrCat(HeadLine(cmStrCat(GeneratorName(genType), " warning")),
79 message, cmHasSuffix(message, '\n') ? "\n" : "\n\n");
82 std::lock_guard<std::mutex> lock(this->Mutex_);
83 cmSystemTools::Stdout(msg);
87 void cmQtAutoGenerator::Logger::Error(GenT genType,
88 cm::string_view message) const
91 cmStrCat('\n', HeadLine(cmStrCat(GeneratorName(genType), " error")),
92 message, cmHasSuffix(message, '\n') ? "\n" : "\n\n");
94 std::lock_guard<std::mutex> lock(this->Mutex_);
95 cmSystemTools::Stderr(msg);
99 void cmQtAutoGenerator::Logger::ErrorCommand(
100 GenT genType, cm::string_view message,
101 std::vector<std::string> const& command, std::string const& output) const
103 std::string msg = cmStrCat(
104 '\n', HeadLine(cmStrCat(GeneratorName(genType), " subprocess error")),
105 message, cmHasSuffix(message, '\n') ? "\n" : "\n\n");
106 msg += cmStrCat(HeadLine("Command"), QuotedCommand(command), "\n\n");
107 msg += cmStrCat(HeadLine("Output"), output,
108 cmHasSuffix(output, '\n') ? "\n" : "\n\n");
110 std::lock_guard<std::mutex> lock(this->Mutex_);
111 cmSystemTools::Stderr(msg);
115 bool cmQtAutoGenerator::MakeParentDirectory(std::string const& filename)
118 std::string const dirName = cmSystemTools::GetFilenamePath(filename);
119 if (!dirName.empty()) {
120 success = static_cast<bool>(cmSystemTools::MakeDirectory(dirName));
125 bool cmQtAutoGenerator::FileRead(std::string& content,
126 std::string const& filename,
130 if (!cmSystemTools::FileExists(filename, true)) {
131 if (error != nullptr) {
132 *error = "Not a file.";
137 unsigned long const length = cmSystemTools::FileLength(filename);
138 cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
140 // Use lambda to save destructor calls of ifs
141 return [&ifs, length, &content, error]() -> bool {
143 if (error != nullptr) {
144 *error = "Opening the file for reading failed.";
148 content.reserve(length);
149 using IsIt = std::istreambuf_iterator<char>;
150 content.assign(IsIt{ ifs }, IsIt{});
153 if (error != nullptr) {
154 *error = "Reading from the file failed.";
162 bool cmQtAutoGenerator::FileWrite(std::string const& filename,
163 std::string const& content,
166 // Make sure the parent directory exists
167 if (!cmQtAutoGenerator::MakeParentDirectory(filename)) {
168 if (error != nullptr) {
169 *error = "Could not create parent directory.";
174 ofs.open(filename.c_str(),
175 (std::ios::out | std::ios::binary | std::ios::trunc));
177 // Use lambda to save destructor calls of ofs
178 return [&ofs, &content, error]() -> bool {
180 if (error != nullptr) {
181 *error = "Opening file for writing failed.";
187 if (error != nullptr) {
188 *error = "File writing failed.";
196 bool cmQtAutoGenerator::FileDiffers(std::string const& filename,
197 std::string const& content)
200 std::string oldContents;
201 if (FileRead(oldContents, filename) && (oldContents == content)) {
207 cmQtAutoGenerator::cmQtAutoGenerator(GenT genType)
212 cmQtAutoGenerator::~cmQtAutoGenerator() = default;
214 bool cmQtAutoGenerator::InfoT::Read(std::istream& istr)
224 bool cmQtAutoGenerator::InfoT::GetJsonArray(std::vector<std::string>& list,
225 Json::Value const& jval)
227 Json::ArrayIndex const arraySize = jval.size();
228 if (arraySize == 0) {
233 list.reserve(list.size() + arraySize);
234 for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
235 Json::Value const& ival = jval[ii];
236 if (ival.isString()) {
237 list.emplace_back(ival.asString());
244 bool cmQtAutoGenerator::InfoT::GetJsonArray(
245 std::unordered_set<std::string>& list, Json::Value const& jval)
247 Json::ArrayIndex const arraySize = jval.size();
248 if (arraySize == 0) {
253 list.reserve(list.size() + arraySize);
254 for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) {
255 Json::Value const& ival = jval[ii];
256 if (ival.isString()) {
257 list.emplace(ival.asString());
264 std::string cmQtAutoGenerator::InfoT::ConfigKey(cm::string_view key) const
266 return cmStrCat(key, '_', this->Gen_.InfoConfig());
269 bool cmQtAutoGenerator::InfoT::GetString(std::string const& key,
273 Json::Value const& jval = this->Json_[key];
274 if (!jval.isString()) {
275 if (!jval.isNull() || required) {
276 return this->LogError(cmStrCat(key, " is not a string."));
279 value = jval.asString();
280 if (value.empty() && required) {
281 return this->LogError(cmStrCat(key, " is empty."));
287 bool cmQtAutoGenerator::InfoT::GetStringConfig(std::string const& key,
292 std::string const configKey = this->ConfigKey(key);
293 Json::Value const& jval = this->Json_[configKey];
294 if (!jval.isNull()) {
295 if (!jval.isString()) {
296 return this->LogError(cmStrCat(configKey, " is not a string."));
298 value = jval.asString();
299 if (required && value.empty()) {
300 return this->LogError(cmStrCat(configKey, " is empty."));
306 return this->GetString(key, value, required);
309 bool cmQtAutoGenerator::InfoT::GetBool(std::string const& key, bool& value,
312 Json::Value const& jval = this->Json_[key];
314 value = jval.asBool();
316 if (!jval.isNull() || required) {
317 return this->LogError(cmStrCat(key, " is not a boolean."));
323 bool cmQtAutoGenerator::InfoT::GetUInt(std::string const& key,
327 Json::Value const& jval = this->Json_[key];
329 value = jval.asUInt();
331 if (!jval.isNull() || required) {
332 return this->LogError(cmStrCat(key, " is not an unsigned integer."));
338 bool cmQtAutoGenerator::InfoT::GetArray(std::string const& key,
339 std::vector<std::string>& list,
342 Json::Value const& jval = this->Json_[key];
343 if (!jval.isArray()) {
344 if (!jval.isNull() || required) {
345 return this->LogError(cmStrCat(key, " is not an array."));
348 return GetJsonArray(list, jval) || !required;
351 bool cmQtAutoGenerator::InfoT::GetArray(std::string const& key,
352 std::unordered_set<std::string>& list,
355 Json::Value const& jval = this->Json_[key];
356 if (!jval.isArray()) {
357 if (!jval.isNull() || required) {
358 return this->LogError(cmStrCat(key, " is not an array."));
361 return GetJsonArray(list, jval) || !required;
364 bool cmQtAutoGenerator::InfoT::GetArrayConfig(std::string const& key,
365 std::vector<std::string>& list,
369 std::string const configKey = this->ConfigKey(key);
370 Json::Value const& jval = this->Json_[configKey];
371 if (!jval.isNull()) {
372 if (!jval.isArray()) {
373 return this->LogError(cmStrCat(configKey, " is not an array string."));
375 if (!GetJsonArray(list, jval) && required) {
376 return this->LogError(cmStrCat(configKey, " is empty."));
382 return this->GetArray(key, list, required);
385 bool cmQtAutoGenerator::InfoT::LogError(GenT genType,
386 cm::string_view message) const
388 this->Gen_.Log().Error(genType,
389 cmStrCat("Info error in info file\n",
390 Quoted(this->Gen_.InfoFile()), ":\n",
395 bool cmQtAutoGenerator::InfoT::LogError(cm::string_view message) const
397 return this->LogError(this->Gen_.GenType_, message);
400 std::string cmQtAutoGenerator::SettingsFind(cm::string_view content,
404 std::string const prefix = cmStrCat(key, ':');
405 cm::string_view::size_type pos = content.find(prefix);
406 if (pos != cm::string_view::npos) {
407 pos += prefix.size();
408 if (pos < content.size()) {
409 cm::string_view::size_type posE = content.find('\n', pos);
410 if ((posE != cm::string_view::npos) && (posE != pos)) {
411 res = content.substr(pos, posE - pos);
415 return std::string(res);
418 std::string cmQtAutoGenerator::MessagePath(cm::string_view path) const
421 if (cmHasPrefix(path, this->ProjectDirs().Source)) {
422 res = cmStrCat("SRC:", path.substr(this->ProjectDirs().Source.size()));
423 } else if (cmHasPrefix(path, this->ProjectDirs().Binary)) {
424 res = cmStrCat("BIN:", path.substr(this->ProjectDirs().Binary.size()));
426 res = std::string(path);
428 return cmQtAutoGen::Quoted(res);
431 bool cmQtAutoGenerator::Run(cm::string_view infoFile, cm::string_view config)
434 this->InfoConfig_ = std::string(config);
437 this->InfoFile_ = std::string(infoFile);
438 cmSystemTools::CollapseFullPath(this->InfoFile_);
439 this->InfoDir_ = cmSystemTools::GetFilenamePath(this->InfoFile_);
441 // Load info file time
442 if (!this->InfoFileTime_.Load(this->InfoFile_)) {
443 cmSystemTools::Stderr(cmStrCat("AutoGen: The info file ",
444 Quoted(this->InfoFile_),
445 " is not readable\n"));
454 cmsys::ifstream ifs(this->InfoFile_.c_str(),
455 (std::ios::in | std::ios::binary));
459 cmStrCat("Could not to open info file ", Quoted(this->InfoFile_)));
462 if (!info.Read(ifs)) {
465 cmStrCat("Could not read info file ", Quoted(this->InfoFile_)));
470 // -- Read common info settings
472 unsigned int verbosity = 0;
473 // Info: setup project directories
474 if (!info.GetUInt("VERBOSITY", verbosity, false) ||
475 !info.GetString("CMAKE_SOURCE_DIR", this->ProjectDirs_.Source,
477 !info.GetString("CMAKE_BINARY_DIR", this->ProjectDirs_.Binary,
479 !info.GetString("CMAKE_CURRENT_SOURCE_DIR",
480 this->ProjectDirs_.CurrentSource, true) ||
481 !info.GetString("CMAKE_CURRENT_BINARY_DIR",
482 this->ProjectDirs_.CurrentBinary, true)) {
485 this->Logger_.RaiseVerbosity(verbosity);
488 // -- Call virtual init from info method.
489 if (!this->InitFromInfo(info)) {
494 // Call virtual process method.
495 return this->Process();