cmQtAutoGeneratorInitializer.cxx 32 KB

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