cmQtAutoGen.cxx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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 "cmQtAutoGen.h"
  4. #include "cmAlgorithms.h"
  5. #include "cmProcessOutput.h"
  6. #include "cmSystemTools.h"
  7. #include "cmsys/FStream.hxx"
  8. #include "cmsys/RegularExpression.hxx"
  9. #include <algorithm>
  10. #include <iterator>
  11. #include <sstream>
  12. #include <stddef.h>
  13. // - Static variables
  14. std::string const genNameGen = "AutoGen";
  15. std::string const genNameMoc = "AutoMoc";
  16. std::string const genNameUic = "AutoUic";
  17. std::string const genNameRcc = "AutoRcc";
  18. std::string const mcNameSingle = "SINGLE";
  19. std::string const mcNameWrap = "WRAP";
  20. std::string const mcNameFull = "FULL";
  21. // - Static functions
  22. /// @brief Merges newOpts into baseOpts
  23. /// @arg valueOpts list of options that accept a value
  24. void MergeOptions(std::vector<std::string>& baseOpts,
  25. std::vector<std::string> const& newOpts,
  26. std::vector<std::string> const& valueOpts, bool isQt5)
  27. {
  28. typedef std::vector<std::string>::iterator Iter;
  29. typedef std::vector<std::string>::const_iterator CIter;
  30. if (newOpts.empty()) {
  31. return;
  32. }
  33. if (baseOpts.empty()) {
  34. baseOpts = newOpts;
  35. return;
  36. }
  37. std::vector<std::string> extraOpts;
  38. for (CIter fit = newOpts.begin(), fitEnd = newOpts.end(); fit != fitEnd;
  39. ++fit) {
  40. std::string const& newOpt = *fit;
  41. Iter existIt = std::find(baseOpts.begin(), baseOpts.end(), newOpt);
  42. if (existIt != baseOpts.end()) {
  43. if (newOpt.size() >= 2) {
  44. // Acquire the option name
  45. std::string optName;
  46. {
  47. auto oit = newOpt.begin();
  48. if (*oit == '-') {
  49. ++oit;
  50. if (isQt5 && (*oit == '-')) {
  51. ++oit;
  52. }
  53. optName.assign(oit, newOpt.end());
  54. }
  55. }
  56. // Test if this is a value option and change the existing value
  57. if (!optName.empty() && (std::find(valueOpts.begin(), valueOpts.end(),
  58. optName) != valueOpts.end())) {
  59. const Iter existItNext(existIt + 1);
  60. const CIter fitNext(fit + 1);
  61. if ((existItNext != baseOpts.end()) && (fitNext != fitEnd)) {
  62. *existItNext = *fitNext;
  63. ++fit;
  64. }
  65. }
  66. }
  67. } else {
  68. extraOpts.push_back(newOpt);
  69. }
  70. }
  71. // Append options
  72. baseOpts.insert(baseOpts.end(), extraOpts.begin(), extraOpts.end());
  73. }
  74. /// @brief Reads the resource files list from from a .qrc file - Qt4 version
  75. /// @return True if the .qrc file was successfully parsed
  76. static bool RccListInputsQt4(std::string const& fileName,
  77. std::vector<std::string>& files,
  78. std::string* errorMessage)
  79. {
  80. bool allGood = true;
  81. // Read qrc file content into string
  82. std::string qrcContents;
  83. {
  84. cmsys::ifstream ifs(fileName.c_str());
  85. if (ifs) {
  86. std::ostringstream osst;
  87. osst << ifs.rdbuf();
  88. qrcContents = osst.str();
  89. } else {
  90. if (errorMessage != nullptr) {
  91. std::string& err = *errorMessage;
  92. err = "rcc file not readable:\n ";
  93. err += cmQtAutoGen::Quoted(fileName);
  94. err += "\n";
  95. }
  96. allGood = false;
  97. }
  98. }
  99. if (allGood) {
  100. // qrc file directory
  101. std::string qrcDir(cmSystemTools::GetFilenamePath(fileName));
  102. if (!qrcDir.empty()) {
  103. qrcDir += '/';
  104. }
  105. cmsys::RegularExpression fileMatchRegex("(<file[^<]+)");
  106. cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)");
  107. size_t offset = 0;
  108. while (fileMatchRegex.find(qrcContents.c_str() + offset)) {
  109. std::string qrcEntry = fileMatchRegex.match(1);
  110. offset += qrcEntry.size();
  111. {
  112. fileReplaceRegex.find(qrcEntry);
  113. std::string tag = fileReplaceRegex.match(1);
  114. qrcEntry = qrcEntry.substr(tag.size());
  115. }
  116. if (!cmSystemTools::FileIsFullPath(qrcEntry.c_str())) {
  117. qrcEntry = qrcDir + qrcEntry;
  118. }
  119. files.push_back(qrcEntry);
  120. }
  121. }
  122. return allGood;
  123. }
  124. /// @brief Reads the resource files list from from a .qrc file - Qt5 version
  125. /// @return True if the .qrc file was successfully parsed
  126. static bool RccListInputsQt5(std::string const& rccCommand,
  127. std::vector<std::string> const& rccListOptions,
  128. std::string const& fileName,
  129. std::vector<std::string>& files,
  130. std::string* errorMessage)
  131. {
  132. if (rccCommand.empty()) {
  133. cmSystemTools::Error("rcc executable not available");
  134. return false;
  135. }
  136. std::string const fileDir = cmSystemTools::GetFilenamePath(fileName);
  137. std::string const fileNameName = cmSystemTools::GetFilenameName(fileName);
  138. // Run rcc list command
  139. bool result = false;
  140. int retVal = 0;
  141. std::string rccStdOut;
  142. std::string rccStdErr;
  143. {
  144. std::vector<std::string> command;
  145. command.push_back(rccCommand);
  146. command.insert(command.end(), rccListOptions.begin(),
  147. rccListOptions.end());
  148. command.push_back(fileNameName);
  149. result = cmSystemTools::RunSingleCommand(
  150. command, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(),
  151. cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto);
  152. }
  153. if (!result || retVal) {
  154. if (errorMessage != nullptr) {
  155. std::string& err = *errorMessage;
  156. err = "rcc list process failed for:\n ";
  157. err += cmQtAutoGen::Quoted(fileName);
  158. err += "\n";
  159. err += rccStdOut;
  160. err += "\n";
  161. err += rccStdErr;
  162. err += "\n";
  163. }
  164. return false;
  165. }
  166. // Lambda to strip CR characters
  167. auto StripCR = [](std::string& line) {
  168. std::string::size_type cr = line.find('\r');
  169. if (cr != std::string::npos) {
  170. line = line.substr(0, cr);
  171. }
  172. };
  173. // Parse rcc std output
  174. {
  175. std::istringstream ostr(rccStdOut);
  176. std::string oline;
  177. while (std::getline(ostr, oline)) {
  178. StripCR(oline);
  179. if (!oline.empty()) {
  180. files.push_back(oline);
  181. }
  182. }
  183. }
  184. // Parse rcc error output
  185. {
  186. std::istringstream estr(rccStdErr);
  187. std::string eline;
  188. while (std::getline(estr, eline)) {
  189. StripCR(eline);
  190. if (cmHasLiteralPrefix(eline, "RCC: Error in")) {
  191. static std::string searchString = "Cannot find file '";
  192. std::string::size_type pos = eline.find(searchString);
  193. if (pos == std::string::npos) {
  194. if (errorMessage != nullptr) {
  195. std::string& err = *errorMessage;
  196. err = "rcc lists unparsable output:\n";
  197. err += cmQtAutoGen::Quoted(eline);
  198. err += "\n";
  199. }
  200. return false;
  201. }
  202. pos += searchString.length();
  203. std::string::size_type sz = eline.size() - pos - 1;
  204. files.push_back(eline.substr(pos, sz));
  205. }
  206. }
  207. }
  208. // Convert relative paths to absolute paths
  209. for (std::string& resFile : files) {
  210. resFile = cmSystemTools::CollapseCombinedPath(fileDir, resFile);
  211. }
  212. return true;
  213. }
  214. // - Class definitions
  215. std::string const cmQtAutoGen::listSep = "@LSEP@";
  216. std::string const& cmQtAutoGen::GeneratorName(Generator type)
  217. {
  218. switch (type) {
  219. case Generator::GEN:
  220. return genNameGen;
  221. case Generator::MOC:
  222. return genNameMoc;
  223. case Generator::UIC:
  224. return genNameUic;
  225. case Generator::RCC:
  226. return genNameRcc;
  227. }
  228. return genNameGen;
  229. }
  230. std::string cmQtAutoGen::GeneratorNameUpper(Generator genType)
  231. {
  232. return cmSystemTools::UpperCase(cmQtAutoGen::GeneratorName(genType));
  233. }
  234. std::string const& cmQtAutoGen::MultiConfigName(MultiConfig config)
  235. {
  236. switch (config) {
  237. case MultiConfig::SINGLE:
  238. return mcNameSingle;
  239. case MultiConfig::WRAP:
  240. return mcNameWrap;
  241. case MultiConfig::FULL:
  242. return mcNameFull;
  243. }
  244. return mcNameWrap;
  245. }
  246. cmQtAutoGen::MultiConfig cmQtAutoGen::MultiConfigType(std::string const& name)
  247. {
  248. if (name == mcNameSingle) {
  249. return MultiConfig::SINGLE;
  250. }
  251. if (name == mcNameFull) {
  252. return MultiConfig::FULL;
  253. }
  254. return MultiConfig::WRAP;
  255. }
  256. std::string cmQtAutoGen::Quoted(std::string const& text)
  257. {
  258. static const char* rep[18] = { "\\", "\\\\", "\"", "\\\"", "\a", "\\a",
  259. "\b", "\\b", "\f", "\\f", "\n", "\\n",
  260. "\r", "\\r", "\t", "\\t", "\v", "\\v" };
  261. std::string res = text;
  262. for (const char* const* it = cm::cbegin(rep); it != cm::cend(rep); it += 2) {
  263. cmSystemTools::ReplaceString(res, *it, *(it + 1));
  264. }
  265. res = '"' + res;
  266. res += '"';
  267. return res;
  268. }
  269. std::string cmQtAutoGen::AppendFilenameSuffix(std::string const& filename,
  270. std::string const& suffix)
  271. {
  272. std::string res;
  273. auto pos = filename.rfind('.');
  274. if (pos != std::string::npos) {
  275. const auto it_dot = filename.begin() + pos;
  276. res.assign(filename.begin(), it_dot);
  277. res.append(suffix);
  278. res.append(it_dot, filename.end());
  279. } else {
  280. res = filename;
  281. res.append(suffix);
  282. }
  283. return res;
  284. }
  285. void cmQtAutoGen::UicMergeOptions(std::vector<std::string>& baseOpts,
  286. std::vector<std::string> const& newOpts,
  287. bool isQt5)
  288. {
  289. static std::vector<std::string> const valueOpts = {
  290. "tr", "translate", "postfix", "generator",
  291. "include", // Since Qt 5.3
  292. "g"
  293. };
  294. MergeOptions(baseOpts, newOpts, valueOpts, isQt5);
  295. }
  296. void cmQtAutoGen::RccMergeOptions(std::vector<std::string>& baseOpts,
  297. std::vector<std::string> const& newOpts,
  298. bool isQt5)
  299. {
  300. static std::vector<std::string> const valueOpts = { "name", "root",
  301. "compress",
  302. "threshold" };
  303. MergeOptions(baseOpts, newOpts, valueOpts, isQt5);
  304. }
  305. bool cmQtAutoGen::RccListInputs(std::string const& rccCommand,
  306. std::vector<std::string> const& rccListOptions,
  307. std::string const& fileName,
  308. std::vector<std::string>& files,
  309. std::string* errorMessage)
  310. {
  311. bool allGood = false;
  312. if (cmSystemTools::FileExists(fileName.c_str())) {
  313. if (rccListOptions.empty()) {
  314. allGood = RccListInputsQt4(fileName, files, errorMessage);
  315. } else {
  316. allGood = RccListInputsQt5(rccCommand, rccListOptions, fileName, files,
  317. errorMessage);
  318. }
  319. } else {
  320. if (errorMessage != nullptr) {
  321. std::string& err = *errorMessage;
  322. err = "rcc resource file does not exist:\n ";
  323. err += cmQtAutoGen::Quoted(fileName);
  324. err += "\n";
  325. }
  326. }
  327. return allGood;
  328. }