cmQtAutoGenerator.cxx 14 KB

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