cmQtAutoGenerator.cxx 13 KB

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