cmQtAutoGeneratorInitializer.cxx 34 KB

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