cmQtAutoGeneratorInitializer.cxx 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959
  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 "cmQtAutoGeneratorInitializer.h"
  4. #include "cmAlgorithms.h"
  5. #include "cmCustomCommandLines.h"
  6. #include "cmFilePathUuid.h"
  7. #include "cmGeneratorTarget.h"
  8. #include "cmGlobalGenerator.h"
  9. #include "cmLocalGenerator.h"
  10. #include "cmMakefile.h"
  11. #include "cmOutputConverter.h"
  12. #include "cmSourceFile.h"
  13. #include "cmSourceFileLocation.h"
  14. #include "cmStateTypes.h"
  15. #include "cmSystemTools.h"
  16. #include "cmTarget.h"
  17. #include "cmake.h"
  18. #if defined(_WIN32) && !defined(__CYGWIN__)
  19. #include "cmGlobalVisualStudioGenerator.h"
  20. #endif
  21. #include <algorithm>
  22. #include <assert.h>
  23. #include <cmConfigure.h>
  24. #include <cmsys/FStream.hxx>
  25. #include <cmsys/RegularExpression.hxx>
  26. #include <iostream>
  27. #include <map>
  28. #include <set>
  29. #include <sstream>
  30. #include <string.h>
  31. #include <string>
  32. #include <sys/stat.h>
  33. #include <utility>
  34. #include <vector>
  35. static std::string GetAutogenTargetName(cmGeneratorTarget const* target)
  36. {
  37. std::string autogenTargetName = target->GetName();
  38. autogenTargetName += "_automoc";
  39. return autogenTargetName;
  40. }
  41. static std::string GetAutogenTargetDir(cmGeneratorTarget const* target)
  42. {
  43. cmMakefile* makefile = target->Target->GetMakefile();
  44. std::string targetDir = makefile->GetCurrentBinaryDirectory();
  45. targetDir += makefile->GetCMakeInstance()->GetCMakeFilesDirectory();
  46. targetDir += "/";
  47. targetDir += GetAutogenTargetName(target);
  48. targetDir += ".dir/";
  49. return targetDir;
  50. }
  51. static std::string GetAutogenTargetBuildDir(cmGeneratorTarget const* target)
  52. {
  53. cmMakefile* makefile = target->Target->GetMakefile();
  54. std::string targetDir = makefile->GetCurrentBinaryDirectory();
  55. targetDir += "/";
  56. targetDir += GetAutogenTargetName(target);
  57. targetDir += ".dir/";
  58. return targetDir;
  59. }
  60. static void SetupSourceFiles(cmGeneratorTarget const* target,
  61. std::vector<std::string>& skipMoc,
  62. std::vector<std::string>& mocSources,
  63. std::vector<std::string>& mocHeaders,
  64. std::vector<std::string>& skipUic)
  65. {
  66. cmMakefile* makefile = target->Target->GetMakefile();
  67. std::vector<cmSourceFile*> srcFiles;
  68. target->GetConfigCommonSourceFiles(srcFiles);
  69. std::vector<std::string> newRccFiles;
  70. cmFilePathUuid fpathUuid(makefile);
  71. for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin();
  72. fileIt != srcFiles.end(); ++fileIt) {
  73. cmSourceFile* sf = *fileIt;
  74. std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath());
  75. bool skipFileForMoc =
  76. cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOMOC"));
  77. bool generated = cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"));
  78. if (cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOUIC"))) {
  79. skipUic.push_back(absFile);
  80. }
  81. std::string ext = sf->GetExtension();
  82. if (target->GetPropertyAsBool("AUTORCC")) {
  83. if (ext == "qrc" &&
  84. !cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC"))) {
  85. std::string rcc_output_file = GetAutogenTargetBuildDir(target);
  86. // Create output directory
  87. cmSystemTools::MakeDirectory(rcc_output_file.c_str());
  88. rcc_output_file += fpathUuid.get(absFile, "qrc_", ".cpp");
  89. makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES",
  90. rcc_output_file.c_str(), false);
  91. makefile->GetOrCreateSource(rcc_output_file, true);
  92. newRccFiles.push_back(rcc_output_file);
  93. }
  94. }
  95. if (!generated) {
  96. if (skipFileForMoc) {
  97. skipMoc.push_back(absFile);
  98. } else {
  99. cmSystemTools::FileFormat fileType =
  100. cmSystemTools::GetFileFormat(ext.c_str());
  101. if (fileType == cmSystemTools::CXX_FILE_FORMAT) {
  102. mocSources.push_back(absFile);
  103. } else if (fileType == cmSystemTools::HEADER_FILE_FORMAT) {
  104. mocHeaders.push_back(absFile);
  105. }
  106. }
  107. }
  108. }
  109. for (std::vector<std::string>::const_iterator fileIt = newRccFiles.begin();
  110. fileIt != newRccFiles.end(); ++fileIt) {
  111. const_cast<cmGeneratorTarget*>(target)->AddSource(*fileIt);
  112. }
  113. }
  114. static void GetCompileDefinitionsAndDirectories(
  115. cmGeneratorTarget const* target, const std::string& config,
  116. std::string& incs, std::string& defs)
  117. {
  118. std::vector<std::string> includeDirs;
  119. cmLocalGenerator* localGen = target->GetLocalGenerator();
  120. // Get the include dirs for this target, without stripping the implicit
  121. // include dirs off, see https://gitlab.kitware.com/cmake/cmake/issues/13667
  122. localGen->GetIncludeDirectories(includeDirs, target, "CXX", config, false);
  123. incs = cmJoin(includeDirs, ";");
  124. std::set<std::string> defines;
  125. localGen->AddCompileDefinitions(defines, target, config, "CXX");
  126. defs += cmJoin(defines, ";");
  127. }
  128. static void SetupAutoMocTarget(
  129. cmGeneratorTarget const* target, const std::string& autogenTargetName,
  130. std::vector<std::string> const& skipMoc,
  131. std::vector<std::string> const& mocHeaders,
  132. std::map<std::string, std::string>& configIncludes,
  133. std::map<std::string, std::string>& configDefines)
  134. {
  135. cmLocalGenerator* lg = target->GetLocalGenerator();
  136. cmMakefile* makefile = target->Target->GetMakefile();
  137. const char* tmp = target->GetProperty("AUTOMOC_MOC_OPTIONS");
  138. std::string _moc_options = (tmp != CM_NULLPTR ? tmp : "");
  139. makefile->AddDefinition(
  140. "_moc_options", cmOutputConverter::EscapeForCMake(_moc_options).c_str());
  141. makefile->AddDefinition(
  142. "_skip_moc",
  143. cmOutputConverter::EscapeForCMake(cmJoin(skipMoc, ";")).c_str());
  144. makefile->AddDefinition(
  145. "_moc_headers",
  146. cmOutputConverter::EscapeForCMake(cmJoin(mocHeaders, ";")).c_str());
  147. bool relaxedMode = makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE");
  148. makefile->AddDefinition("_moc_relaxed_mode", relaxedMode ? "TRUE" : "FALSE");
  149. std::string _moc_incs;
  150. std::string _moc_compile_defs;
  151. std::vector<std::string> configs;
  152. const std::string& config = makefile->GetConfigurations(configs);
  153. GetCompileDefinitionsAndDirectories(target, config, _moc_incs,
  154. _moc_compile_defs);
  155. makefile->AddDefinition(
  156. "_moc_incs", cmOutputConverter::EscapeForCMake(_moc_incs).c_str());
  157. makefile->AddDefinition(
  158. "_moc_compile_defs",
  159. cmOutputConverter::EscapeForCMake(_moc_compile_defs).c_str());
  160. for (std::vector<std::string>::const_iterator li = configs.begin();
  161. li != configs.end(); ++li) {
  162. std::string config_moc_incs;
  163. std::string config_moc_compile_defs;
  164. GetCompileDefinitionsAndDirectories(target, *li, config_moc_incs,
  165. config_moc_compile_defs);
  166. if (config_moc_incs != _moc_incs) {
  167. configIncludes[*li] = cmOutputConverter::EscapeForCMake(config_moc_incs);
  168. if (_moc_incs.empty()) {
  169. _moc_incs = config_moc_incs;
  170. }
  171. }
  172. if (config_moc_compile_defs != _moc_compile_defs) {
  173. configDefines[*li] =
  174. cmOutputConverter::EscapeForCMake(config_moc_compile_defs);
  175. if (_moc_compile_defs.empty()) {
  176. _moc_compile_defs = config_moc_compile_defs;
  177. }
  178. }
  179. }
  180. const char* qtVersion = makefile->GetDefinition("_target_qt_version");
  181. if (strcmp(qtVersion, "5") == 0) {
  182. cmGeneratorTarget* qt5Moc = lg->FindGeneratorTargetToUse("Qt5::moc");
  183. if (!qt5Moc) {
  184. cmSystemTools::Error("Qt5::moc target not found ",
  185. autogenTargetName.c_str());
  186. return;
  187. }
  188. makefile->AddDefinition("_qt_moc_executable",
  189. qt5Moc->ImportedGetLocation(""));
  190. } else if (strcmp(qtVersion, "4") == 0) {
  191. cmGeneratorTarget* qt4Moc = lg->FindGeneratorTargetToUse("Qt4::moc");
  192. if (!qt4Moc) {
  193. cmSystemTools::Error("Qt4::moc target not found ",
  194. autogenTargetName.c_str());
  195. return;
  196. }
  197. makefile->AddDefinition("_qt_moc_executable",
  198. qt4Moc->ImportedGetLocation(""));
  199. } else {
  200. cmSystemTools::Error("The CMAKE_AUTOMOC feature supports only Qt 4 and "
  201. "Qt 5 ",
  202. autogenTargetName.c_str());
  203. }
  204. }
  205. static void GetUicOpts(cmGeneratorTarget const* target,
  206. const std::string& config, std::string& optString)
  207. {
  208. std::vector<std::string> opts;
  209. target->GetAutoUicOptions(opts, config);
  210. optString = cmJoin(opts, ";");
  211. }
  212. static void SetupAutoUicTarget(
  213. cmGeneratorTarget const* target, std::vector<std::string> const& skipUic,
  214. std::map<std::string, std::string>& configUicOptions)
  215. {
  216. cmLocalGenerator* lg = target->GetLocalGenerator();
  217. cmMakefile* makefile = target->Target->GetMakefile();
  218. std::set<std::string> skipped;
  219. skipped.insert(skipUic.begin(), skipUic.end());
  220. makefile->AddDefinition(
  221. "_skip_uic",
  222. cmOutputConverter::EscapeForCMake(cmJoin(skipUic, ";")).c_str());
  223. std::vector<cmSourceFile*> uiFilesWithOptions =
  224. makefile->GetQtUiFilesWithOptions();
  225. const char* qtVersion = makefile->GetDefinition("_target_qt_version");
  226. std::string _uic_opts;
  227. std::vector<std::string> configs;
  228. const std::string& config = makefile->GetConfigurations(configs);
  229. GetUicOpts(target, config, _uic_opts);
  230. if (!_uic_opts.empty()) {
  231. _uic_opts = cmOutputConverter::EscapeForCMake(_uic_opts);
  232. makefile->AddDefinition("_uic_target_options", _uic_opts.c_str());
  233. }
  234. for (std::vector<std::string>::const_iterator li = configs.begin();
  235. li != configs.end(); ++li) {
  236. std::string config_uic_opts;
  237. GetUicOpts(target, *li, config_uic_opts);
  238. if (config_uic_opts != _uic_opts) {
  239. configUicOptions[*li] =
  240. cmOutputConverter::EscapeForCMake(config_uic_opts);
  241. if (_uic_opts.empty()) {
  242. _uic_opts = config_uic_opts;
  243. }
  244. }
  245. }
  246. std::string uiFileFiles;
  247. std::string uiFileOptions;
  248. const char* sep = "";
  249. for (std::vector<cmSourceFile*>::const_iterator fileIt =
  250. uiFilesWithOptions.begin();
  251. fileIt != uiFilesWithOptions.end(); ++fileIt) {
  252. cmSourceFile* sf = *fileIt;
  253. std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath());
  254. if (!skipped.insert(absFile).second) {
  255. continue;
  256. }
  257. uiFileFiles += sep;
  258. uiFileFiles += absFile;
  259. uiFileOptions += sep;
  260. std::string opts = sf->GetProperty("AUTOUIC_OPTIONS");
  261. cmSystemTools::ReplaceString(opts, ";", "@list_sep@");
  262. uiFileOptions += opts;
  263. sep = ";";
  264. }
  265. makefile->AddDefinition(
  266. "_qt_uic_options_files",
  267. cmOutputConverter::EscapeForCMake(uiFileFiles).c_str());
  268. makefile->AddDefinition(
  269. "_qt_uic_options_options",
  270. cmOutputConverter::EscapeForCMake(uiFileOptions).c_str());
  271. std::string targetName = target->GetName();
  272. if (strcmp(qtVersion, "5") == 0) {
  273. cmGeneratorTarget* qt5Uic = lg->FindGeneratorTargetToUse("Qt5::uic");
  274. if (!qt5Uic) {
  275. // Project does not use Qt5Widgets, but has AUTOUIC ON anyway
  276. } else {
  277. makefile->AddDefinition("_qt_uic_executable",
  278. qt5Uic->ImportedGetLocation(""));
  279. }
  280. } else if (strcmp(qtVersion, "4") == 0) {
  281. cmGeneratorTarget* qt4Uic = lg->FindGeneratorTargetToUse("Qt4::uic");
  282. if (!qt4Uic) {
  283. cmSystemTools::Error("Qt4::uic target not found ", targetName.c_str());
  284. return;
  285. }
  286. makefile->AddDefinition("_qt_uic_executable",
  287. qt4Uic->ImportedGetLocation(""));
  288. } else {
  289. cmSystemTools::Error("The CMAKE_AUTOUIC feature supports only Qt 4 and "
  290. "Qt 5 ",
  291. targetName.c_str());
  292. }
  293. }
  294. static std::string GetRccExecutable(cmGeneratorTarget const* target)
  295. {
  296. cmLocalGenerator* lg = target->GetLocalGenerator();
  297. cmMakefile* makefile = target->Target->GetMakefile();
  298. const char* qtVersion = makefile->GetDefinition("_target_qt_version");
  299. if (!qtVersion) {
  300. qtVersion = makefile->GetDefinition("Qt5Core_VERSION_MAJOR");
  301. if (!qtVersion) {
  302. qtVersion = makefile->GetDefinition("QT_VERSION_MAJOR");
  303. }
  304. if (const char* targetQtVersion =
  305. target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION",
  306. "")) {
  307. qtVersion = targetQtVersion;
  308. }
  309. }
  310. std::string targetName = target->GetName();
  311. if (strcmp(qtVersion, "5") == 0) {
  312. cmGeneratorTarget* qt5Rcc = lg->FindGeneratorTargetToUse("Qt5::rcc");
  313. if (!qt5Rcc) {
  314. cmSystemTools::Error("Qt5::rcc target not found ", targetName.c_str());
  315. return std::string();
  316. }
  317. return qt5Rcc->ImportedGetLocation("");
  318. }
  319. if (strcmp(qtVersion, "4") == 0) {
  320. cmGeneratorTarget* qt4Rcc = lg->FindGeneratorTargetToUse("Qt4::rcc");
  321. if (!qt4Rcc) {
  322. cmSystemTools::Error("Qt4::rcc target not found ", targetName.c_str());
  323. return std::string();
  324. }
  325. return qt4Rcc->ImportedGetLocation("");
  326. }
  327. cmSystemTools::Error("The CMAKE_AUTORCC feature supports only Qt 4 and "
  328. "Qt 5 ",
  329. targetName.c_str());
  330. return std::string();
  331. }
  332. static void MergeRccOptions(std::vector<std::string>& opts,
  333. const std::vector<std::string>& fileOpts,
  334. bool isQt5)
  335. {
  336. static const char* valueOptions[] = { "name", "root", "compress",
  337. "threshold" };
  338. std::vector<std::string> extraOpts;
  339. for (std::vector<std::string>::const_iterator it = fileOpts.begin();
  340. it != fileOpts.end(); ++it) {
  341. std::vector<std::string>::iterator existingIt =
  342. std::find(opts.begin(), opts.end(), *it);
  343. if (existingIt != opts.end()) {
  344. const char* o = it->c_str();
  345. if (*o == '-') {
  346. ++o;
  347. }
  348. if (isQt5 && *o == '-') {
  349. ++o;
  350. }
  351. if (std::find_if(cmArrayBegin(valueOptions), cmArrayEnd(valueOptions),
  352. cmStrCmp(*it)) != cmArrayEnd(valueOptions)) {
  353. assert(existingIt + 1 != opts.end());
  354. *(existingIt + 1) = *(it + 1);
  355. ++it;
  356. }
  357. } else {
  358. extraOpts.push_back(*it);
  359. }
  360. }
  361. opts.insert(opts.end(), extraOpts.begin(), extraOpts.end());
  362. }
  363. static void copyTargetProperty(cmTarget* destinationTarget,
  364. cmTarget* sourceTarget,
  365. const std::string& propertyName)
  366. {
  367. const char* propertyValue = sourceTarget->GetProperty(propertyName);
  368. if (propertyValue) {
  369. destinationTarget->SetProperty(propertyName, propertyValue);
  370. }
  371. }
  372. static std::string cmQtAutoGeneratorsStripCR(std::string const& line)
  373. {
  374. // Strip CR characters rcc may have printed (possibly more than one!).
  375. std::string::size_type cr = line.find('\r');
  376. if (cr != line.npos) {
  377. return line.substr(0, cr);
  378. }
  379. return line;
  380. }
  381. static std::string ReadAll(const std::string& filename)
  382. {
  383. cmsys::ifstream file(filename.c_str());
  384. std::ostringstream stream;
  385. stream << file.rdbuf();
  386. file.close();
  387. return stream.str();
  388. }
  389. /// @brief Reads the resource files list from from a .qrc file - Qt5 version
  390. /// @return True if the .qrc file was successfully parsed
  391. static bool ListQt5RccInputs(cmSourceFile* sf, cmGeneratorTarget const* target,
  392. std::vector<std::string>& depends)
  393. {
  394. std::string rccCommand = GetRccExecutable(target);
  395. bool hasDashDashList = false;
  396. // Read rcc features
  397. {
  398. std::vector<std::string> command;
  399. command.push_back(rccCommand);
  400. command.push_back("--help");
  401. std::string rccStdOut;
  402. std::string rccStdErr;
  403. int retVal = 0;
  404. bool result =
  405. cmSystemTools::RunSingleCommand(command, &rccStdOut, &rccStdErr, &retVal,
  406. CM_NULLPTR, cmSystemTools::OUTPUT_NONE);
  407. if (result && retVal == 0 &&
  408. rccStdOut.find("--list") != std::string::npos) {
  409. hasDashDashList = true;
  410. }
  411. }
  412. // Run rcc list command
  413. std::vector<std::string> command;
  414. command.push_back(rccCommand);
  415. command.push_back(hasDashDashList ? "--list" : "-list");
  416. std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath());
  417. command.push_back(absFile);
  418. std::string rccStdOut;
  419. std::string rccStdErr;
  420. int retVal = 0;
  421. bool result =
  422. cmSystemTools::RunSingleCommand(command, &rccStdOut, &rccStdErr, &retVal,
  423. CM_NULLPTR, cmSystemTools::OUTPUT_NONE);
  424. if (!result || retVal) {
  425. std::ostringstream err;
  426. err << "AUTOGEN: error: Rcc list process for " << sf->GetFullPath()
  427. << " failed:\n"
  428. << rccStdOut << "\n"
  429. << rccStdErr << std::endl;
  430. cmSystemTools::Error(err.str().c_str());
  431. return false;
  432. }
  433. // Parse rcc list output
  434. {
  435. std::istringstream ostr(rccStdOut);
  436. std::string oline;
  437. while (std::getline(ostr, oline)) {
  438. oline = cmQtAutoGeneratorsStripCR(oline);
  439. if (!oline.empty()) {
  440. depends.push_back(oline);
  441. }
  442. }
  443. }
  444. {
  445. std::istringstream estr(rccStdErr);
  446. std::string eline;
  447. while (std::getline(estr, eline)) {
  448. eline = cmQtAutoGeneratorsStripCR(eline);
  449. if (cmHasLiteralPrefix(eline, "RCC: Error in")) {
  450. static std::string searchString = "Cannot find file '";
  451. std::string::size_type pos = eline.find(searchString);
  452. if (pos == std::string::npos) {
  453. std::ostringstream err;
  454. err << "AUTOGEN: error: Rcc lists unparsable output " << eline
  455. << std::endl;
  456. cmSystemTools::Error(err.str().c_str());
  457. return false;
  458. }
  459. pos += searchString.length();
  460. std::string::size_type sz = eline.size() - pos - 1;
  461. depends.push_back(eline.substr(pos, sz));
  462. }
  463. }
  464. }
  465. return true;
  466. }
  467. /// @brief Reads the resource files list from from a .qrc file - Qt4 version
  468. /// @return True if the .qrc file was successfully parsed
  469. static bool ListQt4RccInputs(cmSourceFile* sf,
  470. std::vector<std::string>& depends)
  471. {
  472. const std::string qrcContents = ReadAll(sf->GetFullPath());
  473. cmsys::RegularExpression fileMatchRegex("(<file[^<]+)");
  474. size_t offset = 0;
  475. while (fileMatchRegex.find(qrcContents.c_str() + offset)) {
  476. std::string qrcEntry = fileMatchRegex.match(1);
  477. offset += qrcEntry.size();
  478. cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)");
  479. fileReplaceRegex.find(qrcEntry);
  480. std::string tag = fileReplaceRegex.match(1);
  481. qrcEntry = qrcEntry.substr(tag.size());
  482. if (!cmSystemTools::FileIsFullPath(qrcEntry.c_str())) {
  483. qrcEntry = sf->GetLocation().GetDirectory() + "/" + qrcEntry;
  484. }
  485. depends.push_back(qrcEntry);
  486. }
  487. return true;
  488. }
  489. /// @brief Reads the resource files list from from a .qrc file
  490. /// @return True if the rcc file was successfully parsed
  491. static bool ListQtRccInputs(const std::string& qtMajorVersion,
  492. cmSourceFile* sf, cmGeneratorTarget const* target,
  493. std::vector<std::string>& depends)
  494. {
  495. if (qtMajorVersion == "5") {
  496. return ListQt5RccInputs(sf, target, depends);
  497. }
  498. return ListQt4RccInputs(sf, depends);
  499. }
  500. static void SetupAutoRccTarget(cmGeneratorTarget const* target)
  501. {
  502. std::string _rcc_files;
  503. const char* sepRccFiles = "";
  504. cmMakefile* makefile = target->Target->GetMakefile();
  505. std::vector<cmSourceFile*> srcFiles;
  506. target->GetConfigCommonSourceFiles(srcFiles);
  507. std::string qrcInputs;
  508. const char* qrcInputsSep = "";
  509. std::string rccFileFiles;
  510. std::string rccFileOptions;
  511. const char* optionSep = "";
  512. const char* qtVersion = makefile->GetDefinition("_target_qt_version");
  513. std::vector<std::string> rccOptions;
  514. if (const char* opts = target->GetProperty("AUTORCC_OPTIONS")) {
  515. cmSystemTools::ExpandListArgument(opts, rccOptions);
  516. }
  517. std::string qtMajorVersion = makefile->GetSafeDefinition("QT_VERSION_MAJOR");
  518. if (qtMajorVersion == "") {
  519. qtMajorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR");
  520. }
  521. for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin();
  522. fileIt != srcFiles.end(); ++fileIt) {
  523. cmSourceFile* sf = *fileIt;
  524. std::string ext = sf->GetExtension();
  525. if (ext == "qrc") {
  526. std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath());
  527. bool skip = cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC"));
  528. if (!skip) {
  529. _rcc_files += sepRccFiles;
  530. _rcc_files += absFile;
  531. sepRccFiles = ";";
  532. if (const char* prop = sf->GetProperty("AUTORCC_OPTIONS")) {
  533. std::vector<std::string> optsVec;
  534. cmSystemTools::ExpandListArgument(prop, optsVec);
  535. MergeRccOptions(rccOptions, optsVec, strcmp(qtVersion, "5") == 0);
  536. }
  537. if (!rccOptions.empty()) {
  538. rccFileFiles += optionSep;
  539. rccFileFiles += absFile;
  540. rccFileOptions += optionSep;
  541. }
  542. const char* listSep = "";
  543. for (std::vector<std::string>::const_iterator it = rccOptions.begin();
  544. it != rccOptions.end(); ++it) {
  545. rccFileOptions += listSep;
  546. rccFileOptions += *it;
  547. listSep = "@list_sep@";
  548. }
  549. optionSep = ";";
  550. std::string entriesList;
  551. if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) {
  552. std::vector<std::string> depends;
  553. if (ListQtRccInputs(qtMajorVersion, sf, target, depends)) {
  554. entriesList = cmJoin(depends, "@list_sep@");
  555. } else {
  556. return;
  557. }
  558. }
  559. qrcInputs += qrcInputsSep;
  560. qrcInputs += entriesList;
  561. qrcInputsSep = ";";
  562. }
  563. }
  564. }
  565. makefile->AddDefinition(
  566. "_qt_rcc_inputs_" + target->GetName(),
  567. cmOutputConverter::EscapeForCMake(qrcInputs).c_str());
  568. makefile->AddDefinition(
  569. "_rcc_files", cmOutputConverter::EscapeForCMake(_rcc_files).c_str());
  570. makefile->AddDefinition(
  571. "_qt_rcc_options_files",
  572. cmOutputConverter::EscapeForCMake(rccFileFiles).c_str());
  573. makefile->AddDefinition(
  574. "_qt_rcc_options_options",
  575. cmOutputConverter::EscapeForCMake(rccFileOptions).c_str());
  576. makefile->AddDefinition("_qt_rcc_executable",
  577. GetRccExecutable(target).c_str());
  578. }
  579. void cmQtAutoGeneratorInitializer::InitializeAutogenSources(
  580. cmGeneratorTarget* target)
  581. {
  582. cmMakefile* makefile = target->Target->GetMakefile();
  583. if (target->GetPropertyAsBool("AUTOMOC")) {
  584. std::string automocTargetName = GetAutogenTargetName(target);
  585. std::string mocCppFile = makefile->GetCurrentBinaryDirectory();
  586. mocCppFile += "/";
  587. mocCppFile += automocTargetName;
  588. mocCppFile += ".cpp";
  589. makefile->GetOrCreateSource(mocCppFile, true);
  590. makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", mocCppFile.c_str(),
  591. false);
  592. target->AddSource(mocCppFile);
  593. }
  594. }
  595. void cmQtAutoGeneratorInitializer::InitializeAutogenTarget(
  596. cmLocalGenerator* lg, cmGeneratorTarget* target)
  597. {
  598. cmMakefile* makefile = target->Target->GetMakefile();
  599. std::string qtMajorVersion = makefile->GetSafeDefinition("QT_VERSION_MAJOR");
  600. if (qtMajorVersion == "") {
  601. qtMajorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR");
  602. }
  603. // create a custom target for running generators at buildtime:
  604. std::string autogenTargetName = GetAutogenTargetName(target);
  605. std::string targetDir = GetAutogenTargetDir(target);
  606. cmCustomCommandLine currentLine;
  607. currentLine.push_back(cmSystemTools::GetCMakeCommand());
  608. currentLine.push_back("-E");
  609. currentLine.push_back("cmake_autogen");
  610. currentLine.push_back(targetDir);
  611. currentLine.push_back("$<CONFIGURATION>");
  612. cmCustomCommandLines commandLines;
  613. commandLines.push_back(currentLine);
  614. std::string workingDirectory =
  615. cmSystemTools::CollapseFullPath("", makefile->GetCurrentBinaryDirectory());
  616. std::vector<std::string> depends;
  617. if (const char* autogenDepends =
  618. target->GetProperty("AUTOGEN_TARGET_DEPENDS")) {
  619. cmSystemTools::ExpandListArgument(autogenDepends, depends);
  620. }
  621. std::vector<std::string> toolNames;
  622. if (target->GetPropertyAsBool("AUTOMOC")) {
  623. toolNames.push_back("moc");
  624. }
  625. if (target->GetPropertyAsBool("AUTOUIC")) {
  626. toolNames.push_back("uic");
  627. }
  628. if (target->GetPropertyAsBool("AUTORCC")) {
  629. toolNames.push_back("rcc");
  630. }
  631. std::string tools = toolNames[0];
  632. toolNames.erase(toolNames.begin());
  633. while (toolNames.size() > 1) {
  634. tools += ", " + toolNames[0];
  635. toolNames.erase(toolNames.begin());
  636. }
  637. if (toolNames.size() == 1) {
  638. tools += " and " + toolNames[0];
  639. }
  640. std::string autogenComment = "Automatic " + tools + " for target ";
  641. autogenComment += target->GetName();
  642. #if defined(_WIN32) && !defined(__CYGWIN__)
  643. bool usePRE_BUILD = false;
  644. cmGlobalGenerator* gg = lg->GetGlobalGenerator();
  645. if (gg->GetName().find("Visual Studio") != std::string::npos) {
  646. cmGlobalVisualStudioGenerator* vsgg =
  647. static_cast<cmGlobalVisualStudioGenerator*>(gg);
  648. // Under VS >= 7 use a PRE_BUILD event instead of a separate target to
  649. // reduce the number of targets loaded into the IDE.
  650. // This also works around a VS 11 bug that may skip updating the target:
  651. // https://connect.microsoft.com/VisualStudio/feedback/details/769495
  652. usePRE_BUILD = vsgg->GetVersion() >= cmGlobalVisualStudioGenerator::VS7;
  653. if (usePRE_BUILD) {
  654. for (std::vector<std::string>::iterator it = depends.begin();
  655. it != depends.end(); ++it) {
  656. if (!makefile->FindTargetToUse(it->c_str())) {
  657. usePRE_BUILD = false;
  658. break;
  659. }
  660. }
  661. }
  662. }
  663. #endif
  664. std::vector<std::string> rcc_output;
  665. bool const isNinja = lg->GetGlobalGenerator()->GetName() == "Ninja";
  666. if (isNinja
  667. #if defined(_WIN32) && !defined(__CYGWIN__)
  668. || usePRE_BUILD
  669. #endif
  670. ) {
  671. std::vector<cmSourceFile*> srcFiles;
  672. target->GetConfigCommonSourceFiles(srcFiles);
  673. cmFilePathUuid fpathUuid(makefile);
  674. for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin();
  675. fileIt != srcFiles.end(); ++fileIt) {
  676. cmSourceFile* sf = *fileIt;
  677. std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath());
  678. std::string ext = sf->GetExtension();
  679. if (target->GetPropertyAsBool("AUTORCC")) {
  680. if (ext == "qrc" &&
  681. !cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC"))) {
  682. {
  683. std::string rcc_output_file = GetAutogenTargetBuildDir(target);
  684. // Create output directory
  685. cmSystemTools::MakeDirectory(rcc_output_file.c_str());
  686. rcc_output_file += fpathUuid.get(absFile, "qrc_", ".cpp");
  687. rcc_output.push_back(rcc_output_file);
  688. }
  689. if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) {
  690. ListQtRccInputs(qtMajorVersion, sf, target, depends);
  691. #if defined(_WIN32) && !defined(__CYGWIN__)
  692. // Cannot use PRE_BUILD because the resource files themselves
  693. // may not be sources within the target so VS may not know the
  694. // target needs to re-build at all.
  695. usePRE_BUILD = false;
  696. #endif
  697. }
  698. }
  699. }
  700. }
  701. }
  702. #if defined(_WIN32) && !defined(__CYGWIN__)
  703. if (usePRE_BUILD) {
  704. // Add the pre-build command directly to bypass the OBJECT_LIBRARY
  705. // rejection in cmMakefile::AddCustomCommandToTarget because we know
  706. // PRE_BUILD will work for an OBJECT_LIBRARY in this specific case.
  707. std::vector<std::string> no_output;
  708. std::vector<std::string> no_byproducts;
  709. cmCustomCommand cc(makefile, no_output, no_byproducts, depends,
  710. commandLines, autogenComment.c_str(),
  711. workingDirectory.c_str());
  712. cc.SetEscapeOldStyle(false);
  713. cc.SetEscapeAllowMakeVars(true);
  714. target->Target->AddPreBuildCommand(cc);
  715. } else
  716. #endif
  717. {
  718. cmTarget* autogenTarget = makefile->AddUtilityCommand(
  719. autogenTargetName, true, workingDirectory.c_str(),
  720. /*byproducts=*/rcc_output, depends, commandLines, false,
  721. autogenComment.c_str());
  722. cmGeneratorTarget* gt = new cmGeneratorTarget(autogenTarget, lg);
  723. lg->AddGeneratorTarget(gt);
  724. // Set target folder
  725. const char* autogenFolder =
  726. makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
  727. if (!autogenFolder) {
  728. autogenFolder =
  729. makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER");
  730. }
  731. if (autogenFolder && *autogenFolder) {
  732. autogenTarget->SetProperty("FOLDER", autogenFolder);
  733. } else {
  734. // inherit FOLDER property from target (#13688)
  735. copyTargetProperty(gt->Target, target->Target, "FOLDER");
  736. }
  737. target->Target->AddUtility(autogenTargetName);
  738. }
  739. }
  740. void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget(
  741. cmGeneratorTarget const* target)
  742. {
  743. cmMakefile* makefile = target->Target->GetMakefile();
  744. // forget the variables added here afterwards again:
  745. cmMakefile::ScopePushPop varScope(makefile);
  746. static_cast<void>(varScope);
  747. // create a custom target for running generators at buildtime:
  748. std::string autogenTargetName = GetAutogenTargetName(target);
  749. makefile->AddDefinition(
  750. "_moc_target_name",
  751. cmOutputConverter::EscapeForCMake(autogenTargetName).c_str());
  752. makefile->AddDefinition(
  753. "_origin_target_name",
  754. cmOutputConverter::EscapeForCMake(target->GetName()).c_str());
  755. std::string targetDir = GetAutogenTargetDir(target);
  756. const char* qtVersion = makefile->GetDefinition("Qt5Core_VERSION_MAJOR");
  757. if (!qtVersion) {
  758. qtVersion = makefile->GetDefinition("QT_VERSION_MAJOR");
  759. }
  760. if (const char* targetQtVersion =
  761. target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION",
  762. "")) {
  763. qtVersion = targetQtVersion;
  764. }
  765. if (qtVersion) {
  766. makefile->AddDefinition("_target_qt_version", qtVersion);
  767. }
  768. std::vector<std::string> skipUic;
  769. std::vector<std::string> skipMoc;
  770. std::vector<std::string> mocSources;
  771. std::vector<std::string> mocHeaders;
  772. std::map<std::string, std::string> configIncludes;
  773. std::map<std::string, std::string> configDefines;
  774. std::map<std::string, std::string> configUicOptions;
  775. if (target->GetPropertyAsBool("AUTOMOC") ||
  776. target->GetPropertyAsBool("AUTOUIC") ||
  777. target->GetPropertyAsBool("AUTORCC")) {
  778. SetupSourceFiles(target, skipMoc, mocSources, mocHeaders, skipUic);
  779. }
  780. makefile->AddDefinition(
  781. "_cpp_files",
  782. cmOutputConverter::EscapeForCMake(cmJoin(mocSources, ";")).c_str());
  783. if (target->GetPropertyAsBool("AUTOMOC")) {
  784. SetupAutoMocTarget(target, autogenTargetName, skipMoc, mocHeaders,
  785. configIncludes, configDefines);
  786. }
  787. if (target->GetPropertyAsBool("AUTOUIC")) {
  788. SetupAutoUicTarget(target, skipUic, configUicOptions);
  789. }
  790. if (target->GetPropertyAsBool("AUTORCC")) {
  791. SetupAutoRccTarget(target);
  792. }
  793. std::string inputFile = cmSystemTools::GetCMakeRoot();
  794. inputFile += "/Modules/AutogenInfo.cmake.in";
  795. std::string outputFile = targetDir;
  796. outputFile += "/AutogenInfo.cmake";
  797. makefile->AddDefinition(
  798. "_qt_rcc_inputs",
  799. makefile->GetDefinition("_qt_rcc_inputs_" + target->GetName()));
  800. makefile->ConfigureFile(inputFile.c_str(), outputFile.c_str(), false, true,
  801. false);
  802. // Ensure we have write permission in case .in was read-only.
  803. mode_t perm = 0;
  804. #if defined(_WIN32) && !defined(__CYGWIN__)
  805. mode_t mode_write = S_IWRITE;
  806. #else
  807. mode_t mode_write = S_IWUSR;
  808. #endif
  809. cmSystemTools::GetPermissions(outputFile, perm);
  810. if (!(perm & mode_write)) {
  811. cmSystemTools::SetPermissions(outputFile, perm | mode_write);
  812. }
  813. if (!configDefines.empty() || !configIncludes.empty() ||
  814. !configUicOptions.empty()) {
  815. cmsys::ofstream infoFile(outputFile.c_str(), std::ios::app);
  816. if (!infoFile) {
  817. std::string error = "Internal CMake error when trying to open file: ";
  818. error += outputFile;
  819. error += " for writing.";
  820. cmSystemTools::Error(error.c_str());
  821. return;
  822. }
  823. if (!configDefines.empty()) {
  824. for (std::map<std::string, std::string>::iterator
  825. it = configDefines.begin(),
  826. end = configDefines.end();
  827. it != end; ++it) {
  828. infoFile << "set(AM_MOC_COMPILE_DEFINITIONS_" << it->first << " "
  829. << it->second << ")\n";
  830. }
  831. }
  832. if (!configIncludes.empty()) {
  833. for (std::map<std::string, std::string>::iterator
  834. it = configIncludes.begin(),
  835. end = configIncludes.end();
  836. it != end; ++it) {
  837. infoFile << "set(AM_MOC_INCLUDES_" << it->first << " " << it->second
  838. << ")\n";
  839. }
  840. }
  841. if (!configUicOptions.empty()) {
  842. for (std::map<std::string, std::string>::iterator
  843. it = configUicOptions.begin(),
  844. end = configUicOptions.end();
  845. it != end; ++it) {
  846. infoFile << "set(AM_UIC_TARGET_OPTIONS_" << it->first << " "
  847. << it->second << ")\n";
  848. }
  849. }
  850. }
  851. }