resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmQtAutoGenerator.cxx
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"
4
5 #include <cm3p/json/reader.h>
6
7 #include "cmsys/FStream.hxx"
8
9 #include "cmQtAutoGen.h"
10 #include "cmStringAlgorithms.h"
11 #include "cmSystemTools.h"
12 #include "cmValue.h"
13
14 cmQtAutoGenerator::Logger::Logger()
15 {
16   // Initialize logger
17   {
18     std::string verbose;
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));
23       } else {
24         // Non numeric verbosity
25         this->SetVerbose(cmIsOn(verbose));
26       }
27     }
28   }
29   {
30     std::string colorEnv;
31     cmSystemTools::GetEnv("COLOR", colorEnv);
32     if (!colorEnv.empty()) {
33       this->SetColorOutput(cmIsOn(colorEnv));
34     } else {
35       this->SetColorOutput(true);
36     }
37   }
38 }
39
40 void cmQtAutoGenerator::Logger::RaiseVerbosity(unsigned int value)
41 {
42   if (this->Verbosity_ < value) {
43     this->Verbosity_ = value;
44   }
45 }
46
47 void cmQtAutoGenerator::Logger::SetColorOutput(bool value)
48 {
49   this->ColorOutput_ = value;
50 }
51
52 std::string cmQtAutoGenerator::Logger::HeadLine(cm::string_view title)
53 {
54   return cmStrCat(title, '\n', std::string(title.size(), '-'), '\n');
55 }
56
57 void cmQtAutoGenerator::Logger::Info(GenT genType,
58                                      cm::string_view message) const
59 {
60   std::string msg = cmStrCat(GeneratorName(genType), ": ", message,
61                              cmHasSuffix(message, '\n') ? "" : "\n");
62   {
63     std::lock_guard<std::mutex> lock(this->Mutex_);
64     cmSystemTools::Stdout(msg);
65   }
66 }
67
68 void cmQtAutoGenerator::Logger::Warning(GenT genType,
69                                         cm::string_view message) const
70 {
71   std::string msg;
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");
76   } else {
77     // Multi line message
78     msg = cmStrCat(HeadLine(cmStrCat(GeneratorName(genType), " warning")),
79                    message, cmHasSuffix(message, '\n') ? "\n" : "\n\n");
80   }
81   {
82     std::lock_guard<std::mutex> lock(this->Mutex_);
83     cmSystemTools::Stdout(msg);
84   }
85 }
86
87 void cmQtAutoGenerator::Logger::Error(GenT genType,
88                                       cm::string_view message) const
89 {
90   std::string msg =
91     cmStrCat('\n', HeadLine(cmStrCat(GeneratorName(genType), " error")),
92              message, cmHasSuffix(message, '\n') ? "\n" : "\n\n");
93   {
94     std::lock_guard<std::mutex> lock(this->Mutex_);
95     cmSystemTools::Stderr(msg);
96   }
97 }
98
99 void cmQtAutoGenerator::Logger::ErrorCommand(
100   GenT genType, cm::string_view message,
101   std::vector<std::string> const& command, std::string const& output) const
102 {
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");
109   {
110     std::lock_guard<std::mutex> lock(this->Mutex_);
111     cmSystemTools::Stderr(msg);
112   }
113 }
114
115 bool cmQtAutoGenerator::MakeParentDirectory(std::string const& filename)
116 {
117   bool success = true;
118   std::string const dirName = cmSystemTools::GetFilenamePath(filename);
119   if (!dirName.empty()) {
120     success = static_cast<bool>(cmSystemTools::MakeDirectory(dirName));
121   }
122   return success;
123 }
124
125 bool cmQtAutoGenerator::FileRead(std::string& content,
126                                  std::string const& filename,
127                                  std::string* error)
128 {
129   content.clear();
130   if (!cmSystemTools::FileExists(filename, true)) {
131     if (error != nullptr) {
132       *error = "Not a file.";
133     }
134     return false;
135   }
136
137   unsigned long const length = cmSystemTools::FileLength(filename);
138   cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
139
140   // Use lambda to save destructor calls of ifs
141   return [&ifs, length, &content, error]() -> bool {
142     if (!ifs) {
143       if (error != nullptr) {
144         *error = "Opening the file for reading failed.";
145       }
146       return false;
147     }
148     content.reserve(length);
149     using IsIt = std::istreambuf_iterator<char>;
150     content.assign(IsIt{ ifs }, IsIt{});
151     if (!ifs) {
152       content.clear();
153       if (error != nullptr) {
154         *error = "Reading from the file failed.";
155       }
156       return false;
157     }
158     return true;
159   }();
160 }
161
162 bool cmQtAutoGenerator::FileWrite(std::string const& filename,
163                                   std::string const& content,
164                                   std::string* error)
165 {
166   // Make sure the parent directory exists
167   if (!cmQtAutoGenerator::MakeParentDirectory(filename)) {
168     if (error != nullptr) {
169       *error = "Could not create parent directory.";
170     }
171     return false;
172   }
173   cmsys::ofstream ofs;
174   ofs.open(filename.c_str(),
175            (std::ios::out | std::ios::binary | std::ios::trunc));
176
177   // Use lambda to save destructor calls of ofs
178   return [&ofs, &content, error]() -> bool {
179     if (!ofs) {
180       if (error != nullptr) {
181         *error = "Opening file for writing failed.";
182       }
183       return false;
184     }
185     ofs << content;
186     if (!ofs.good()) {
187       if (error != nullptr) {
188         *error = "File writing failed.";
189       }
190       return false;
191     }
192     return true;
193   }();
194 }
195
196 bool cmQtAutoGenerator::FileDiffers(std::string const& filename,
197                                     std::string const& content)
198 {
199   bool differs = true;
200   std::string oldContents;
201   if (FileRead(oldContents, filename) && (oldContents == content)) {
202     differs = false;
203   }
204   return differs;
205 }
206
207 cmQtAutoGenerator::cmQtAutoGenerator(GenT genType)
208   : GenType_(genType)
209 {
210 }
211
212 cmQtAutoGenerator::~cmQtAutoGenerator() = default;
213
214 bool cmQtAutoGenerator::InfoT::Read(std::istream& istr)
215 {
216   try {
217     istr >> this->Json_;
218   } catch (...) {
219     return false;
220   }
221   return true;
222 }
223
224 bool cmQtAutoGenerator::InfoT::GetJsonArray(std::vector<std::string>& list,
225                                             Json::Value const& jval)
226 {
227   Json::ArrayIndex const arraySize = jval.size();
228   if (arraySize == 0) {
229     return false;
230   }
231
232   bool picked = false;
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());
238       picked = true;
239     }
240   }
241   return picked;
242 }
243
244 bool cmQtAutoGenerator::InfoT::GetJsonArray(
245   std::unordered_set<std::string>& list, Json::Value const& jval)
246 {
247   Json::ArrayIndex const arraySize = jval.size();
248   if (arraySize == 0) {
249     return false;
250   }
251
252   bool picked = false;
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());
258       picked = true;
259     }
260   }
261   return picked;
262 }
263
264 std::string cmQtAutoGenerator::InfoT::ConfigKey(cm::string_view key) const
265 {
266   return cmStrCat(key, '_', this->Gen_.InfoConfig());
267 }
268
269 bool cmQtAutoGenerator::InfoT::GetString(std::string const& key,
270                                          std::string& value,
271                                          bool required) const
272 {
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."));
277     }
278   } else {
279     value = jval.asString();
280     if (value.empty() && required) {
281       return this->LogError(cmStrCat(key, " is empty."));
282     }
283   }
284   return true;
285 }
286
287 bool cmQtAutoGenerator::InfoT::GetStringConfig(std::string const& key,
288                                                std::string& value,
289                                                bool required) const
290 {
291   { // Try config
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."));
297       }
298       value = jval.asString();
299       if (required && value.empty()) {
300         return this->LogError(cmStrCat(configKey, " is empty."));
301       }
302       return true;
303     }
304   }
305   // Try plain
306   return this->GetString(key, value, required);
307 }
308
309 bool cmQtAutoGenerator::InfoT::GetBool(std::string const& key, bool& value,
310                                        bool required) const
311 {
312   Json::Value const& jval = this->Json_[key];
313   if (jval.isBool()) {
314     value = jval.asBool();
315   } else {
316     if (!jval.isNull() || required) {
317       return this->LogError(cmStrCat(key, " is not a boolean."));
318     }
319   }
320   return true;
321 }
322
323 bool cmQtAutoGenerator::InfoT::GetUInt(std::string const& key,
324                                        unsigned int& value,
325                                        bool required) const
326 {
327   Json::Value const& jval = this->Json_[key];
328   if (jval.isUInt()) {
329     value = jval.asUInt();
330   } else {
331     if (!jval.isNull() || required) {
332       return this->LogError(cmStrCat(key, " is not an unsigned integer."));
333     }
334   }
335   return true;
336 }
337
338 bool cmQtAutoGenerator::InfoT::GetArray(std::string const& key,
339                                         std::vector<std::string>& list,
340                                         bool required) const
341 {
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."));
346     }
347   }
348   return GetJsonArray(list, jval) || !required;
349 }
350
351 bool cmQtAutoGenerator::InfoT::GetArray(std::string const& key,
352                                         std::unordered_set<std::string>& list,
353                                         bool required) const
354 {
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."));
359     }
360   }
361   return GetJsonArray(list, jval) || !required;
362 }
363
364 bool cmQtAutoGenerator::InfoT::GetArrayConfig(std::string const& key,
365                                               std::vector<std::string>& list,
366                                               bool required) const
367 {
368   { // Try config
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."));
374       }
375       if (!GetJsonArray(list, jval) && required) {
376         return this->LogError(cmStrCat(configKey, " is empty."));
377       }
378       return true;
379     }
380   }
381   // Try plain
382   return this->GetArray(key, list, required);
383 }
384
385 bool cmQtAutoGenerator::InfoT::LogError(GenT genType,
386                                         cm::string_view message) const
387 {
388   this->Gen_.Log().Error(genType,
389                          cmStrCat("Info error in info file\n",
390                                   Quoted(this->Gen_.InfoFile()), ":\n",
391                                   message));
392   return false;
393 }
394
395 bool cmQtAutoGenerator::InfoT::LogError(cm::string_view message) const
396 {
397   return this->LogError(this->Gen_.GenType_, message);
398 }
399
400 std::string cmQtAutoGenerator::SettingsFind(cm::string_view content,
401                                             cm::string_view key)
402 {
403   cm::string_view res;
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);
412       }
413     }
414   }
415   return std::string(res);
416 }
417
418 std::string cmQtAutoGenerator::MessagePath(cm::string_view path) const
419 {
420   std::string res;
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()));
425   } else {
426     res = std::string(path);
427   }
428   return cmQtAutoGen::Quoted(res);
429 }
430
431 bool cmQtAutoGenerator::Run(cm::string_view infoFile, cm::string_view config)
432 {
433   // Info config
434   this->InfoConfig_ = std::string(config);
435
436   // Info file
437   this->InfoFile_ = std::string(infoFile);
438   cmSystemTools::CollapseFullPath(this->InfoFile_);
439   this->InfoDir_ = cmSystemTools::GetFilenamePath(this->InfoFile_);
440
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"));
446     return false;
447   }
448
449   {
450     InfoT info(*this);
451
452     // Read info file
453     {
454       cmsys::ifstream ifs(this->InfoFile_.c_str(),
455                           (std::ios::in | std::ios::binary));
456       if (!ifs) {
457         this->Log().Error(
458           this->GenType_,
459           cmStrCat("Could not to open info file ", Quoted(this->InfoFile_)));
460         return false;
461       }
462       if (!info.Read(ifs)) {
463         this->Log().Error(
464           this->GenType_,
465           cmStrCat("Could not read info file ", Quoted(this->InfoFile_)));
466         return false;
467       }
468     }
469
470     // -- Read common info settings
471     {
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,
476                           true) ||
477           !info.GetString("CMAKE_BINARY_DIR", this->ProjectDirs_.Binary,
478                           true) ||
479           !info.GetString("CMAKE_CURRENT_SOURCE_DIR",
480                           this->ProjectDirs_.CurrentSource, true) ||
481           !info.GetString("CMAKE_CURRENT_BINARY_DIR",
482                           this->ProjectDirs_.CurrentBinary, true)) {
483         return false;
484       }
485       this->Logger_.RaiseVerbosity(verbosity);
486     }
487
488     // -- Call virtual init from info method.
489     if (!this->InitFromInfo(info)) {
490       return false;
491     }
492   }
493
494   // Call virtual process method.
495   return this->Process();
496 }