cmQtAutoGenerators.cxx 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629
  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 "cmQtAutoGenerators.h"
  4. #include <algorithm>
  5. #include <assert.h>
  6. #include <cmConfigure.h>
  7. #include <cmsys/FStream.hxx>
  8. #include <cmsys/Terminal.h>
  9. #include <iostream>
  10. #include <sstream>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <utility>
  14. #include "cmAlgorithms.h"
  15. #include "cmGlobalGenerator.h"
  16. #include "cmMakefile.h"
  17. #include "cmOutputConverter.h"
  18. #include "cmStateDirectory.h"
  19. #include "cmStateSnapshot.h"
  20. #include "cmSystemTools.h"
  21. #include "cm_auto_ptr.hxx"
  22. #include "cmake.h"
  23. #if defined(__APPLE__)
  24. #include <unistd.h>
  25. #endif
  26. // -- Static variables
  27. static const char* MocOldSettingsKey = "AM_MOC_OLD_SETTINGS";
  28. static const char* UicOldSettingsKey = "AM_UIC_OLD_SETTINGS";
  29. static const char* RccOldSettingsKey = "AM_RCC_OLD_SETTINGS";
  30. // -- Static functions
  31. static std::string GetConfigDefinition(cmMakefile* makefile,
  32. const std::string& key,
  33. const std::string& config)
  34. {
  35. std::string keyConf = key;
  36. if (!config.empty()) {
  37. keyConf += "_";
  38. keyConf += config;
  39. }
  40. const char* valueConf = makefile->GetDefinition(keyConf);
  41. if (valueConf != CM_NULLPTR) {
  42. return valueConf;
  43. }
  44. return makefile->GetSafeDefinition(key);
  45. }
  46. static std::string OldSettingsFile(const std::string& targetDirectory)
  47. {
  48. std::string filename(cmSystemTools::CollapseFullPath(targetDirectory));
  49. cmSystemTools::ConvertToUnixSlashes(filename);
  50. filename += "/AutogenOldSettings.cmake";
  51. return filename;
  52. }
  53. static std::string FindMatchingHeader(
  54. const std::string& absPath, const std::string& mocSubDir,
  55. const std::string& basename,
  56. const std::vector<std::string>& headerExtensions)
  57. {
  58. std::string header;
  59. for (std::vector<std::string>::const_iterator ext = headerExtensions.begin();
  60. ext != headerExtensions.end(); ++ext) {
  61. std::string sourceFilePath = absPath + basename + "." + (*ext);
  62. if (cmsys::SystemTools::FileExists(sourceFilePath.c_str())) {
  63. header = sourceFilePath;
  64. break;
  65. }
  66. // Try subdirectory instead
  67. if (!mocSubDir.empty()) {
  68. sourceFilePath = mocSubDir + basename + "." + (*ext);
  69. if (cmsys::SystemTools::FileExists(sourceFilePath.c_str())) {
  70. header = sourceFilePath;
  71. break;
  72. }
  73. }
  74. }
  75. return header;
  76. }
  77. static std::string ExtractSubDir(const std::string& absPath,
  78. const std::string& currentMoc)
  79. {
  80. std::string subDir;
  81. if (currentMoc.find_first_of('/') != std::string::npos) {
  82. subDir = absPath + cmsys::SystemTools::GetFilenamePath(currentMoc) + '/';
  83. }
  84. return subDir;
  85. }
  86. static bool FileNameIsUnique(const std::string& filePath,
  87. const std::map<std::string, std::string>& fileMap)
  88. {
  89. size_t count(0);
  90. const std::string fileName = cmsys::SystemTools::GetFilenameName(filePath);
  91. for (std::map<std::string, std::string>::const_iterator si = fileMap.begin();
  92. si != fileMap.end(); ++si) {
  93. if (cmsys::SystemTools::GetFilenameName(si->first) == fileName) {
  94. ++count;
  95. if (count > 1) {
  96. return false;
  97. }
  98. }
  99. }
  100. return true;
  101. }
  102. static std::string ReadAll(const std::string& filename)
  103. {
  104. cmsys::ifstream file(filename.c_str());
  105. std::ostringstream stream;
  106. stream << file.rdbuf();
  107. file.close();
  108. return stream.str();
  109. }
  110. /**
  111. * @brief Tests if buildFile doesn't exist or is older than sourceFile
  112. * @return True if buildFile doesn't exist or is older than sourceFile
  113. */
  114. static bool FileAbsentOrOlder(const std::string& buildFile,
  115. const std::string& sourceFile)
  116. {
  117. int result = 0;
  118. bool success =
  119. cmsys::SystemTools::FileTimeCompare(buildFile, sourceFile, &result);
  120. return (!success || (result <= 0));
  121. }
  122. static bool ListContains(const std::vector<std::string>& list,
  123. const std::string& entry)
  124. {
  125. return (std::find(list.begin(), list.end(), entry) != list.end());
  126. }
  127. static std::string JoinOptions(const std::map<std::string, std::string>& opts)
  128. {
  129. std::string result;
  130. for (std::map<std::string, std::string>::const_iterator it = opts.begin();
  131. it != opts.end(); ++it) {
  132. if (it != opts.begin()) {
  133. result += "%%%";
  134. }
  135. result += it->first;
  136. result += "===";
  137. result += it->second;
  138. }
  139. return result;
  140. }
  141. static std::string JoinExts(const std::vector<std::string>& lst)
  142. {
  143. std::string result;
  144. if (!lst.empty()) {
  145. const std::string separator = ",";
  146. for (std::vector<std::string>::const_iterator it = lst.begin();
  147. it != lst.end(); ++it) {
  148. if (it != lst.begin()) {
  149. result += separator;
  150. }
  151. result += '.';
  152. result += *it;
  153. }
  154. }
  155. return result;
  156. }
  157. static void UicMergeOptions(std::vector<std::string>& opts,
  158. const std::vector<std::string>& fileOpts,
  159. bool isQt5)
  160. {
  161. static const char* valueOptions[] = { "tr", "translate",
  162. "postfix", "generator",
  163. "include", // Since Qt 5.3
  164. "g" };
  165. std::vector<std::string> extraOpts;
  166. for (std::vector<std::string>::const_iterator it = fileOpts.begin();
  167. it != fileOpts.end(); ++it) {
  168. std::vector<std::string>::iterator existingIt =
  169. std::find(opts.begin(), opts.end(), *it);
  170. if (existingIt != opts.end()) {
  171. const char* o = it->c_str();
  172. if (*o == '-') {
  173. ++o;
  174. }
  175. if (isQt5 && *o == '-') {
  176. ++o;
  177. }
  178. if (std::find_if(cmArrayBegin(valueOptions), cmArrayEnd(valueOptions),
  179. cmStrCmp(*it)) != cmArrayEnd(valueOptions)) {
  180. assert(existingIt + 1 != opts.end());
  181. *(existingIt + 1) = *(it + 1);
  182. ++it;
  183. }
  184. } else {
  185. extraOpts.push_back(*it);
  186. }
  187. }
  188. opts.insert(opts.end(), extraOpts.begin(), extraOpts.end());
  189. }
  190. // -- Class methods
  191. cmQtAutoGenerators::cmQtAutoGenerators()
  192. : Verbose(cmsys::SystemTools::HasEnv("VERBOSE"))
  193. , ColorOutput(true)
  194. , RunMocFailed(false)
  195. , RunUicFailed(false)
  196. , RunRccFailed(false)
  197. , GenerateMocAll(false)
  198. , GenerateUicAll(false)
  199. , GenerateRccAll(false)
  200. {
  201. std::string colorEnv;
  202. cmsys::SystemTools::GetEnv("COLOR", colorEnv);
  203. if (!colorEnv.empty()) {
  204. if (cmSystemTools::IsOn(colorEnv.c_str())) {
  205. this->ColorOutput = true;
  206. } else {
  207. this->ColorOutput = false;
  208. }
  209. }
  210. // Precompile regular expressions
  211. this->RegExpQObject.compile("[\n][ \t]*Q_OBJECT[^a-zA-Z0-9_]");
  212. this->RegExpQGadget.compile("[\n][ \t]*Q_GADGET[^a-zA-Z0-9_]");
  213. this->RegExpMocInclude.compile(
  214. "[\n][ \t]*#[ \t]*include[ \t]+"
  215. "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
  216. this->RegExpUicInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+"
  217. "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
  218. }
  219. bool cmQtAutoGenerators::Run(const std::string& targetDirectory,
  220. const std::string& config)
  221. {
  222. cmake cm;
  223. cm.SetHomeOutputDirectory(targetDirectory);
  224. cm.SetHomeDirectory(targetDirectory);
  225. cm.GetCurrentSnapshot().SetDefaultDefinitions();
  226. cmGlobalGenerator gg(&cm);
  227. cmStateSnapshot snapshot = cm.GetCurrentSnapshot();
  228. snapshot.GetDirectory().SetCurrentBinary(targetDirectory);
  229. snapshot.GetDirectory().SetCurrentSource(targetDirectory);
  230. CM_AUTO_PTR<cmMakefile> mf(new cmMakefile(&gg, snapshot));
  231. gg.SetCurrentMakefile(mf.get());
  232. if (!this->ReadAutogenInfoFile(mf.get(), targetDirectory, config)) {
  233. return false;
  234. }
  235. // Read old settings
  236. this->OldSettingsReadFile(mf.get(), targetDirectory);
  237. // Init and run
  238. this->Init();
  239. if (this->QtMajorVersion == "4" || this->QtMajorVersion == "5") {
  240. if (!this->RunAutogen(mf.get())) {
  241. return false;
  242. }
  243. }
  244. // Write latest settings
  245. if (!this->OldSettingsWriteFile(targetDirectory)) {
  246. return false;
  247. }
  248. return true;
  249. }
  250. bool cmQtAutoGenerators::ReadAutogenInfoFile(
  251. cmMakefile* makefile, const std::string& targetDirectory,
  252. const std::string& config)
  253. {
  254. std::string filename(cmSystemTools::CollapseFullPath(targetDirectory));
  255. cmSystemTools::ConvertToUnixSlashes(filename);
  256. filename += "/AutogenInfo.cmake";
  257. if (!makefile->ReadListFile(filename.c_str())) {
  258. std::ostringstream err;
  259. err << "AutoGen: error processing file: " << filename << std::endl;
  260. this->LogError(err.str());
  261. return false;
  262. }
  263. // - Target names
  264. this->OriginTargetName =
  265. makefile->GetSafeDefinition("AM_ORIGIN_TARGET_NAME");
  266. this->AutogenTargetName = makefile->GetSafeDefinition("AM_TARGET_NAME");
  267. // - Directories
  268. this->ProjectSourceDir = makefile->GetSafeDefinition("AM_CMAKE_SOURCE_DIR");
  269. this->ProjectBinaryDir = makefile->GetSafeDefinition("AM_CMAKE_BINARY_DIR");
  270. this->CurrentSourceDir =
  271. makefile->GetSafeDefinition("AM_CMAKE_CURRENT_SOURCE_DIR");
  272. this->CurrentBinaryDir =
  273. makefile->GetSafeDefinition("AM_CMAKE_CURRENT_BINARY_DIR");
  274. // - Qt environment
  275. this->QtMajorVersion = makefile->GetSafeDefinition("AM_QT_VERSION_MAJOR");
  276. if (this->QtMajorVersion == "") {
  277. this->QtMajorVersion =
  278. makefile->GetSafeDefinition("AM_Qt5Core_VERSION_MAJOR");
  279. }
  280. this->MocExecutable = makefile->GetSafeDefinition("AM_QT_MOC_EXECUTABLE");
  281. this->UicExecutable = makefile->GetSafeDefinition("AM_QT_UIC_EXECUTABLE");
  282. this->RccExecutable = makefile->GetSafeDefinition("AM_QT_RCC_EXECUTABLE");
  283. // - File Lists
  284. cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition("AM_SOURCES"),
  285. this->Sources);
  286. cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition("AM_HEADERS"),
  287. this->Headers);
  288. // - Moc
  289. cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition("AM_SKIP_MOC"),
  290. this->SkipMoc);
  291. this->MocCompileDefinitionsStr =
  292. GetConfigDefinition(makefile, "AM_MOC_COMPILE_DEFINITIONS", config);
  293. this->MocIncludesStr =
  294. GetConfigDefinition(makefile, "AM_MOC_INCLUDES", config);
  295. this->MocOptionsStr = makefile->GetSafeDefinition("AM_MOC_OPTIONS");
  296. // - Uic
  297. cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition("AM_SKIP_UIC"),
  298. this->SkipUic);
  299. cmSystemTools::ExpandListArgument(
  300. GetConfigDefinition(makefile, "AM_UIC_TARGET_OPTIONS", config),
  301. this->UicTargetOptions);
  302. {
  303. std::vector<std::string> uicFilesVec;
  304. std::vector<std::string> uicOptionsVec;
  305. cmSystemTools::ExpandListArgument(
  306. makefile->GetSafeDefinition("AM_UIC_OPTIONS_FILES"), uicFilesVec);
  307. cmSystemTools::ExpandListArgument(
  308. makefile->GetSafeDefinition("AM_UIC_OPTIONS_OPTIONS"), uicOptionsVec);
  309. if (uicFilesVec.size() != uicOptionsVec.size()) {
  310. std::ostringstream err;
  311. err << "AutoGen: Error: Uic files/options lists size missmatch in: "
  312. << filename << std::endl;
  313. this->LogError(err.str());
  314. return false;
  315. }
  316. for (std::vector<std::string>::iterator fileIt = uicFilesVec.begin(),
  317. optionIt = uicOptionsVec.begin();
  318. fileIt != uicFilesVec.end(); ++fileIt, ++optionIt) {
  319. cmSystemTools::ReplaceString(*optionIt, "@list_sep@", ";");
  320. this->UicOptions[*fileIt] = *optionIt;
  321. }
  322. }
  323. // - Rcc
  324. cmSystemTools::ExpandListArgument(
  325. makefile->GetSafeDefinition("AM_RCC_SOURCES"), this->RccSources);
  326. {
  327. std::vector<std::string> rccFilesVec;
  328. std::vector<std::string> rccOptionsVec;
  329. cmSystemTools::ExpandListArgument(
  330. makefile->GetSafeDefinition("AM_RCC_OPTIONS_FILES"), rccFilesVec);
  331. cmSystemTools::ExpandListArgument(
  332. makefile->GetSafeDefinition("AM_RCC_OPTIONS_OPTIONS"), rccOptionsVec);
  333. if (rccFilesVec.size() != rccOptionsVec.size()) {
  334. std::ostringstream err;
  335. err << "AutoGen: Error: RCC files/options lists size missmatch in: "
  336. << filename << std::endl;
  337. this->LogError(err.str());
  338. return false;
  339. }
  340. for (std::vector<std::string>::iterator fileIt = rccFilesVec.begin(),
  341. optionIt = rccOptionsVec.begin();
  342. fileIt != rccFilesVec.end(); ++fileIt, ++optionIt) {
  343. cmSystemTools::ReplaceString(*optionIt, "@list_sep@", ";");
  344. this->RccOptions[*fileIt] = *optionIt;
  345. }
  346. }
  347. {
  348. std::vector<std::string> rccInputLists;
  349. cmSystemTools::ExpandListArgument(
  350. makefile->GetSafeDefinition("AM_RCC_INPUTS"), rccInputLists);
  351. // qrc files in the end of the list may have been empty
  352. if (rccInputLists.size() < this->RccSources.size()) {
  353. rccInputLists.resize(this->RccSources.size());
  354. }
  355. if (this->RccSources.size() != rccInputLists.size()) {
  356. std::ostringstream err;
  357. err << "AutoGen: Error: RCC sources/inputs lists size missmatch in: "
  358. << filename << std::endl;
  359. this->LogError(err.str());
  360. return false;
  361. }
  362. for (std::vector<std::string>::iterator fileIt = this->RccSources.begin(),
  363. inputIt = rccInputLists.begin();
  364. fileIt != this->RccSources.end(); ++fileIt, ++inputIt) {
  365. cmSystemTools::ReplaceString(*inputIt, "@list_sep@", ";");
  366. std::vector<std::string> rccInputFiles;
  367. cmSystemTools::ExpandListArgument(*inputIt, rccInputFiles);
  368. this->RccInputs[*fileIt] = rccInputFiles;
  369. }
  370. }
  371. // - Flags
  372. this->IncludeProjectDirsBefore =
  373. makefile->IsOn("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
  374. this->MocRelaxedMode = makefile->IsOn("AM_MOC_RELAXED_MODE");
  375. return true;
  376. }
  377. std::string cmQtAutoGenerators::MocSettingsStringCompose()
  378. {
  379. std::string res;
  380. res += this->MocCompileDefinitionsStr;
  381. res += " ~~~ ";
  382. res += this->MocIncludesStr;
  383. res += " ~~~ ";
  384. res += this->MocOptionsStr;
  385. res += " ~~~ ";
  386. res += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE";
  387. res += " ~~~ ";
  388. return res;
  389. }
  390. std::string cmQtAutoGenerators::UicSettingsStringCompose()
  391. {
  392. std::string res;
  393. res += cmJoin(this->UicTargetOptions, "@osep@");
  394. res += " ~~~ ";
  395. res += JoinOptions(this->UicOptions);
  396. res += " ~~~ ";
  397. return res;
  398. }
  399. std::string cmQtAutoGenerators::RccSettingsStringCompose()
  400. {
  401. std::string res;
  402. res += JoinOptions(this->RccOptions);
  403. res += " ~~~ ";
  404. return res;
  405. }
  406. void cmQtAutoGenerators::OldSettingsReadFile(
  407. cmMakefile* makefile, const std::string& targetDirectory)
  408. {
  409. if (!this->MocExecutable.empty() || !this->UicExecutable.empty() ||
  410. !this->RccExecutable.empty()) {
  411. // Compose current settings strings
  412. this->MocSettingsString = this->MocSettingsStringCompose();
  413. this->UicSettingsString = this->UicSettingsStringCompose();
  414. this->RccSettingsString = this->RccSettingsStringCompose();
  415. // Read old settings
  416. const std::string filename = OldSettingsFile(targetDirectory);
  417. if (makefile->ReadListFile(filename.c_str())) {
  418. if (!this->MocExecutable.empty()) {
  419. const std::string sol = makefile->GetSafeDefinition(MocOldSettingsKey);
  420. if (sol != this->MocSettingsString) {
  421. this->GenerateMocAll = true;
  422. }
  423. }
  424. if (!this->UicExecutable.empty()) {
  425. const std::string sol = makefile->GetSafeDefinition(UicOldSettingsKey);
  426. if (sol != this->UicSettingsString) {
  427. this->GenerateUicAll = true;
  428. }
  429. }
  430. if (!this->RccExecutable.empty()) {
  431. const std::string sol = makefile->GetSafeDefinition(RccOldSettingsKey);
  432. if (sol != this->RccSettingsString) {
  433. this->GenerateRccAll = true;
  434. }
  435. }
  436. // In case any setting changed remove the old settings file.
  437. // This triggers a full rebuild on the next run if the current
  438. // build is aborted before writing the current settings in the end.
  439. if (this->GenerateMocAll || this->GenerateUicAll ||
  440. this->GenerateRccAll) {
  441. cmSystemTools::RemoveFile(filename);
  442. }
  443. } else {
  444. // If the file could not be read re-generate everythiung.
  445. this->GenerateMocAll = true;
  446. this->GenerateUicAll = true;
  447. this->GenerateRccAll = true;
  448. }
  449. }
  450. }
  451. bool cmQtAutoGenerators::OldSettingsWriteFile(
  452. const std::string& targetDirectory)
  453. {
  454. bool success = true;
  455. // Only write if any setting changed
  456. if (this->GenerateMocAll || this->GenerateUicAll || this->GenerateRccAll) {
  457. const std::string filename = OldSettingsFile(targetDirectory);
  458. cmsys::ofstream outfile;
  459. outfile.open(filename.c_str(), std::ios::trunc);
  460. if (outfile) {
  461. if (!this->MocExecutable.empty()) {
  462. outfile << "set(" << MocOldSettingsKey << " "
  463. << cmOutputConverter::EscapeForCMake(this->MocSettingsString)
  464. << ")\n";
  465. }
  466. if (!this->UicExecutable.empty()) {
  467. outfile << "set(" << UicOldSettingsKey << " "
  468. << cmOutputConverter::EscapeForCMake(this->UicSettingsString)
  469. << ")\n";
  470. }
  471. if (!this->RccExecutable.empty()) {
  472. outfile << "set(" << RccOldSettingsKey << " "
  473. << cmOutputConverter::EscapeForCMake(this->RccSettingsString)
  474. << ")\n";
  475. }
  476. success = outfile.good();
  477. outfile.close();
  478. } else {
  479. success = false;
  480. // Remove old settings file to trigger full rebuild on next run
  481. cmSystemTools::RemoveFile(filename);
  482. {
  483. std::ostringstream err;
  484. err << "AutoGen: Error: Writing old settings file failed: " << filename
  485. << std::endl;
  486. this->LogError(err.str());
  487. }
  488. }
  489. }
  490. return success;
  491. }
  492. void cmQtAutoGenerators::Init()
  493. {
  494. this->AutogenBuildSubDir = this->AutogenTargetName;
  495. this->AutogenBuildSubDir += "/";
  496. this->OutMocCppFilenameRel = this->AutogenBuildSubDir;
  497. this->OutMocCppFilenameRel += "moc_compilation.cpp";
  498. this->OutMocCppFilenameAbs =
  499. this->CurrentBinaryDir + this->OutMocCppFilenameRel;
  500. // Init file path checksum generator
  501. fpathCheckSum.setupParentDirs(this->CurrentSourceDir, this->CurrentBinaryDir,
  502. this->ProjectSourceDir,
  503. this->ProjectBinaryDir);
  504. std::vector<std::string> cdefList;
  505. cmSystemTools::ExpandListArgument(this->MocCompileDefinitionsStr, cdefList);
  506. for (std::vector<std::string>::const_iterator it = cdefList.begin();
  507. it != cdefList.end(); ++it) {
  508. this->MocDefinitions.push_back("-D" + (*it));
  509. }
  510. cmSystemTools::ExpandListArgument(this->MocOptionsStr, this->MocOptions);
  511. std::vector<std::string> incPaths;
  512. cmSystemTools::ExpandListArgument(this->MocIncludesStr, incPaths);
  513. std::set<std::string> frameworkPaths;
  514. for (std::vector<std::string>::const_iterator it = incPaths.begin();
  515. it != incPaths.end(); ++it) {
  516. const std::string& path = *it;
  517. this->MocIncludes.push_back("-I" + path);
  518. if (cmHasLiteralSuffix(path, ".framework/Headers")) {
  519. // Go up twice to get to the framework root
  520. std::vector<std::string> pathComponents;
  521. cmsys::SystemTools::SplitPath(path, pathComponents);
  522. std::string frameworkPath = cmsys::SystemTools::JoinPath(
  523. pathComponents.begin(), pathComponents.end() - 2);
  524. frameworkPaths.insert(frameworkPath);
  525. }
  526. }
  527. for (std::set<std::string>::const_iterator it = frameworkPaths.begin();
  528. it != frameworkPaths.end(); ++it) {
  529. this->MocIncludes.push_back("-F");
  530. this->MocIncludes.push_back(*it);
  531. }
  532. if (this->IncludeProjectDirsBefore) {
  533. const std::string binDir = "-I" + this->ProjectBinaryDir;
  534. const std::string srcDir = "-I" + this->ProjectSourceDir;
  535. std::list<std::string> sortedMocIncludes;
  536. std::list<std::string>::iterator it = this->MocIncludes.begin();
  537. while (it != this->MocIncludes.end()) {
  538. if (cmsys::SystemTools::StringStartsWith(*it, binDir.c_str())) {
  539. sortedMocIncludes.push_back(*it);
  540. it = this->MocIncludes.erase(it);
  541. } else {
  542. ++it;
  543. }
  544. }
  545. it = this->MocIncludes.begin();
  546. while (it != this->MocIncludes.end()) {
  547. if (cmsys::SystemTools::StringStartsWith(*it, srcDir.c_str())) {
  548. sortedMocIncludes.push_back(*it);
  549. it = this->MocIncludes.erase(it);
  550. } else {
  551. ++it;
  552. }
  553. }
  554. sortedMocIncludes.insert(sortedMocIncludes.end(),
  555. this->MocIncludes.begin(),
  556. this->MocIncludes.end());
  557. this->MocIncludes = sortedMocIncludes;
  558. }
  559. }
  560. bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile)
  561. {
  562. // the program goes through all .cpp files to see which moc files are
  563. // included. It is not really interesting how the moc file is named, but
  564. // what file the moc is created from. Once a moc is included the same moc
  565. // may not be included in the moc_compilation.cpp file anymore. OTOH if
  566. // there's a header containing Q_OBJECT where no corresponding moc file
  567. // is included anywhere a moc_<filename>.cpp file is created and included in
  568. // the moc_compilation.cpp file.
  569. // key = moc source filepath, value = moc output filepath
  570. std::map<std::string, std::string> includedMocs;
  571. std::map<std::string, std::string> notIncludedMocs;
  572. std::map<std::string, std::vector<std::string> > includedUis;
  573. // collects all headers which may need to be mocced
  574. std::set<std::string> headerFilesMoc;
  575. std::set<std::string> headerFilesUic;
  576. // Parse sources
  577. {
  578. const std::vector<std::string>& headerExtensions =
  579. makefile->GetCMakeInstance()->GetHeaderExtensions();
  580. for (std::vector<std::string>::const_iterator it = this->Sources.begin();
  581. it != this->Sources.end(); ++it) {
  582. const std::string& absFilename = *it;
  583. // Parse source file for MOC/UIC
  584. if (!this->ParseSourceFile(absFilename, headerExtensions, includedMocs,
  585. includedUis, this->MocRelaxedMode)) {
  586. return false;
  587. }
  588. // Find additional headers
  589. this->SearchHeadersForSourceFile(absFilename, headerExtensions,
  590. headerFilesMoc, headerFilesUic);
  591. }
  592. }
  593. // Parse headers
  594. for (std::vector<std::string>::const_iterator it = this->Headers.begin();
  595. it != this->Headers.end(); ++it) {
  596. const std::string& headerName = *it;
  597. if (!this->MocSkipTest(headerName)) {
  598. headerFilesMoc.insert(this->Headers.begin(), this->Headers.end());
  599. }
  600. if (!this->UicSkipTest(headerName)) {
  601. headerFilesUic.insert(this->Headers.begin(), this->Headers.end());
  602. }
  603. }
  604. this->ParseHeaders(headerFilesMoc, headerFilesUic, includedMocs,
  605. notIncludedMocs, includedUis);
  606. // Generate files
  607. if (!this->MocGenerateAll(includedMocs, notIncludedMocs)) {
  608. return false;
  609. }
  610. if (!this->UicGenerateAll(includedUis)) {
  611. return false;
  612. }
  613. if (!this->QrcGenerateAll()) {
  614. return false;
  615. }
  616. return true;
  617. }
  618. /**
  619. * @brief Tests if the C++ content requires moc processing
  620. * @return True if moc is required
  621. */
  622. bool cmQtAutoGenerators::requiresMocing(const std::string& text,
  623. std::string& macroName)
  624. {
  625. // Run a simple check before an expensive regular expression check
  626. if (strstr(text.c_str(), "Q_OBJECT") != CM_NULLPTR) {
  627. if (this->RegExpQObject.find(text)) {
  628. macroName = "Q_OBJECT";
  629. return true;
  630. }
  631. }
  632. if (strstr(text.c_str(), "Q_GADGET") != CM_NULLPTR) {
  633. if (this->RegExpQGadget.find(text)) {
  634. macroName = "Q_GADGET";
  635. return true;
  636. }
  637. }
  638. return false;
  639. }
  640. /**
  641. * @brief Tests if the file should be ignored for moc scanning
  642. * @return True if the file should be ignored
  643. */
  644. bool cmQtAutoGenerators::MocSkipTest(const std::string& absFilename)
  645. {
  646. // Test if moc scanning is enabled
  647. if (!this->MocExecutable.empty()) {
  648. // Test if the file name is on the skip list
  649. if (!ListContains(this->SkipMoc, absFilename)) {
  650. return false;
  651. }
  652. }
  653. return true;
  654. }
  655. /**
  656. * @brief Tests if the file name is in the skip list
  657. */
  658. bool cmQtAutoGenerators::UicSkipTest(const std::string& absFilename)
  659. {
  660. // Test if uic scanning is enabled
  661. if (!this->UicExecutable.empty()) {
  662. // Test if the file name is on the skip list
  663. if (!ListContains(this->SkipUic, absFilename)) {
  664. return false;
  665. }
  666. }
  667. return true;
  668. }
  669. /**
  670. * @return True on success
  671. */
  672. bool cmQtAutoGenerators::ParseSourceFile(
  673. const std::string& absFilename,
  674. const std::vector<std::string>& headerExtensions,
  675. std::map<std::string, std::string>& includedMocs,
  676. std::map<std::string, std::vector<std::string> >& includedUis, bool relaxed)
  677. {
  678. bool success = true;
  679. const std::string contentsString = ReadAll(absFilename);
  680. if (contentsString.empty()) {
  681. std::ostringstream err;
  682. err << "AutoGen: Warning: " << absFilename << "\n"
  683. << "The file is empty\n";
  684. this->LogWarning(err.str());
  685. } else {
  686. // Parse source contents for MOC
  687. if (success && !this->MocSkipTest(absFilename)) {
  688. success = this->ParseContentForMoc(
  689. absFilename, contentsString, headerExtensions, includedMocs, relaxed);
  690. }
  691. // Parse source contents for UIC
  692. if (success && !this->UicSkipTest(absFilename)) {
  693. this->ParseContentForUic(absFilename, contentsString, includedUis);
  694. }
  695. }
  696. return success;
  697. }
  698. void cmQtAutoGenerators::ParseContentForUic(
  699. const std::string& absFilename, const std::string& contentsString,
  700. std::map<std::string, std::vector<std::string> >& includedUis)
  701. {
  702. // Process
  703. if (this->Verbose) {
  704. std::ostringstream err;
  705. err << "AutoUic: Checking " << absFilename << "\n";
  706. this->LogInfo(err.str());
  707. }
  708. const std::string realName = cmsys::SystemTools::GetRealPath(absFilename);
  709. const char* contentChars = contentsString.c_str();
  710. if (strstr(contentChars, "ui_") != CM_NULLPTR) {
  711. while (this->RegExpUicInclude.find(contentChars)) {
  712. const std::string currentUi = this->RegExpUicInclude.match(1);
  713. const std::string basename =
  714. cmsys::SystemTools::GetFilenameWithoutLastExtension(currentUi);
  715. // basename should be the part of the ui filename used for
  716. // finding the correct header, so we need to remove the ui_ part
  717. includedUis[realName].push_back(basename.substr(3));
  718. contentChars += this->RegExpUicInclude.end();
  719. }
  720. }
  721. }
  722. /**
  723. * @return True on success
  724. */
  725. bool cmQtAutoGenerators::ParseContentForMoc(
  726. const std::string& absFilename, const std::string& contentsString,
  727. const std::vector<std::string>& headerExtensions,
  728. std::map<std::string, std::string>& includedMocs, bool relaxed)
  729. {
  730. // Process
  731. if (this->Verbose) {
  732. std::ostringstream err;
  733. err << "AutoMoc: Checking " << absFilename << "\n";
  734. this->LogInfo(err.str());
  735. }
  736. const std::string scannedFileAbsPath =
  737. cmsys::SystemTools::GetFilenamePath(
  738. cmsys::SystemTools::GetRealPath(absFilename)) +
  739. '/';
  740. const std::string scannedFileBasename =
  741. cmsys::SystemTools::GetFilenameWithoutLastExtension(absFilename);
  742. std::string macroName;
  743. const bool requiresMoc = this->requiresMocing(contentsString, macroName);
  744. bool ownDotMocIncluded = false;
  745. bool ownMocUnderscoreIncluded = false;
  746. std::string ownMocUnderscoreFile;
  747. std::string ownMocHeaderFile;
  748. // first a simple string check for "moc" is *much* faster than the regexp,
  749. // and if the string search already fails, we don't have to try the
  750. // expensive regexp
  751. const char* contentChars = contentsString.c_str();
  752. if (strstr(contentChars, "moc") != CM_NULLPTR) {
  753. // Iterate over all included moc files
  754. while (this->RegExpMocInclude.find(contentChars)) {
  755. const std::string currentMoc = this->RegExpMocInclude.match(1);
  756. // Basename of the current moc include
  757. std::string basename =
  758. cmsys::SystemTools::GetFilenameWithoutLastExtension(currentMoc);
  759. // If the moc include is of the moc_foo.cpp style we expect
  760. // the Q_OBJECT class declaration in a header file.
  761. // If the moc include is of the foo.moc style we need to look for
  762. // a Q_OBJECT macro in the current source file, if it contains the
  763. // macro we generate the moc file from the source file.
  764. if (cmHasLiteralPrefix(basename, "moc_")) {
  765. // Include: moc_FOO.cxx
  766. // basename should be the part of the moc filename used for
  767. // finding the correct header, so we need to remove the moc_ part
  768. basename = basename.substr(4);
  769. const std::string mocSubDir =
  770. ExtractSubDir(scannedFileAbsPath, currentMoc);
  771. const std::string headerToMoc = FindMatchingHeader(
  772. scannedFileAbsPath, mocSubDir, basename, headerExtensions);
  773. if (!headerToMoc.empty()) {
  774. includedMocs[headerToMoc] = currentMoc;
  775. if (relaxed && (basename == scannedFileBasename)) {
  776. ownMocUnderscoreIncluded = true;
  777. ownMocUnderscoreFile = currentMoc;
  778. ownMocHeaderFile = headerToMoc;
  779. }
  780. } else {
  781. std::ostringstream err;
  782. err << "AutoMoc: Error: " << absFilename << "\n"
  783. << "The file includes the moc file \"" << currentMoc
  784. << "\", but could not find header \"" << basename << '{'
  785. << JoinExts(headerExtensions) << "}\" ";
  786. if (mocSubDir.empty()) {
  787. err << "in " << scannedFileAbsPath << "\n";
  788. } else {
  789. err << "neither in " << scannedFileAbsPath << " nor in "
  790. << mocSubDir << "\n";
  791. }
  792. this->LogError(err.str());
  793. return false;
  794. }
  795. } else {
  796. // Include: FOO.moc
  797. std::string fileToMoc;
  798. if (relaxed) {
  799. // Mode: Relaxed
  800. if (!requiresMoc || basename != scannedFileBasename) {
  801. const std::string mocSubDir =
  802. ExtractSubDir(scannedFileAbsPath, currentMoc);
  803. const std::string headerToMoc = FindMatchingHeader(
  804. scannedFileAbsPath, mocSubDir, basename, headerExtensions);
  805. if (!headerToMoc.empty()) {
  806. // This is for KDE4 compatibility:
  807. fileToMoc = headerToMoc;
  808. if (!requiresMoc && basename == scannedFileBasename) {
  809. std::ostringstream err;
  810. err << "AutoMoc: Warning: " << absFilename << "\n"
  811. << "The file includes the moc file \"" << currentMoc
  812. << "\", but does not contain a " << macroName
  813. << " macro. Running moc on "
  814. << "\"" << headerToMoc << "\" ! Include \"moc_" << basename
  815. << ".cpp\" for a compatibility with "
  816. "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n";
  817. this->LogWarning(err.str());
  818. } else {
  819. std::ostringstream err;
  820. err << "AutoMoc: Warning: " << absFilename << "\n"
  821. << "The file includes the moc file \"" << currentMoc
  822. << "\" instead of \"moc_" << basename
  823. << ".cpp\". Running moc on "
  824. << "\"" << headerToMoc << "\" ! Include \"moc_" << basename
  825. << ".cpp\" for compatibility with "
  826. "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n";
  827. this->LogWarning(err.str());
  828. }
  829. } else {
  830. std::ostringstream err;
  831. err << "AutoMoc: Error: " << absFilename << "\n"
  832. << "The file includes the moc file \"" << currentMoc
  833. << "\", which seems to be the moc file from a different "
  834. "source file. CMake also could not find a matching "
  835. "header.\n";
  836. this->LogError(err.str());
  837. return false;
  838. }
  839. } else {
  840. // Include self
  841. fileToMoc = absFilename;
  842. ownDotMocIncluded = true;
  843. }
  844. } else {
  845. // Mode: Strict
  846. if (basename != scannedFileBasename) {
  847. // Don't allow FOO.moc include other than self in strict mode
  848. std::ostringstream err;
  849. err << "AutoMoc: Error: " << absFilename << "\n"
  850. << "The file includes the moc file \"" << currentMoc
  851. << "\", which seems to be the moc file from a different "
  852. "source file. This is not supported. Include \""
  853. << scannedFileBasename
  854. << ".moc\" to run moc on this source file.\n";
  855. this->LogError(err.str());
  856. return false;
  857. } else {
  858. // Include self
  859. fileToMoc = absFilename;
  860. ownDotMocIncluded = true;
  861. }
  862. }
  863. if (!fileToMoc.empty()) {
  864. includedMocs[fileToMoc] = currentMoc;
  865. }
  866. }
  867. // Forward content pointer
  868. contentChars += this->RegExpMocInclude.end();
  869. }
  870. }
  871. // In this case, check whether the scanned file itself contains a Q_OBJECT.
  872. // If this is the case, the moc_foo.cpp should probably be generated from
  873. // foo.cpp instead of foo.h, because otherwise it won't build.
  874. // But warn, since this is not how it is supposed to be used.
  875. if (requiresMoc && !ownDotMocIncluded) {
  876. if (relaxed && ownMocUnderscoreIncluded) {
  877. // This is for KDE4 compatibility:
  878. std::ostringstream err;
  879. err << "AutoMoc: Warning: " << absFilename << "\n"
  880. << "The file contains a " << macroName
  881. << " macro, but does not include "
  882. << "\"" << scannedFileBasename << ".moc\", but instead includes "
  883. << "\"" << ownMocUnderscoreFile << "\". Running moc on "
  884. << "\"" << absFilename << "\" ! Better include \""
  885. << scannedFileBasename
  886. << ".moc\" for compatibility with "
  887. "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n";
  888. this->LogWarning(err.str());
  889. // Use scanned source file instead of scanned header file as moc source
  890. includedMocs[absFilename] = ownMocUnderscoreFile;
  891. includedMocs.erase(ownMocHeaderFile);
  892. } else {
  893. // Otherwise always error out since it will not compile:
  894. std::ostringstream err;
  895. err << "AutoMoc: Error: " << absFilename << "\n"
  896. << "The file contains a " << macroName
  897. << " macro, but does not include "
  898. << "\"" << scannedFileBasename << ".moc\" !\n";
  899. this->LogError(err.str());
  900. return false;
  901. }
  902. }
  903. return true;
  904. }
  905. void cmQtAutoGenerators::SearchHeadersForSourceFile(
  906. const std::string& absFilename,
  907. const std::vector<std::string>& headerExtensions,
  908. std::set<std::string>& absHeadersMoc, std::set<std::string>& absHeadersUic)
  909. {
  910. // search for header files and private header files we may need to moc:
  911. std::string basepath = cmsys::SystemTools::GetFilenamePath(
  912. cmsys::SystemTools::GetRealPath(absFilename));
  913. basepath += '/';
  914. basepath += cmsys::SystemTools::GetFilenameWithoutLastExtension(absFilename);
  915. // Search for regular header
  916. for (std::vector<std::string>::const_iterator ext = headerExtensions.begin();
  917. ext != headerExtensions.end(); ++ext) {
  918. const std::string headerName = basepath + "." + (*ext);
  919. if (cmsys::SystemTools::FileExists(headerName.c_str())) {
  920. // Moc headers
  921. if (!this->MocSkipTest(absFilename) && !this->MocSkipTest(headerName)) {
  922. absHeadersMoc.insert(headerName);
  923. }
  924. // Uic headers
  925. if (!this->UicSkipTest(absFilename) && !this->UicSkipTest(headerName)) {
  926. absHeadersUic.insert(headerName);
  927. }
  928. break;
  929. }
  930. }
  931. // Search for private header
  932. for (std::vector<std::string>::const_iterator ext = headerExtensions.begin();
  933. ext != headerExtensions.end(); ++ext) {
  934. const std::string headerName = basepath + "_p." + (*ext);
  935. if (cmsys::SystemTools::FileExists(headerName.c_str())) {
  936. // Moc headers
  937. if (!this->MocSkipTest(absFilename) && !this->MocSkipTest(headerName)) {
  938. absHeadersMoc.insert(headerName);
  939. }
  940. // Uic headers
  941. if (!this->UicSkipTest(absFilename) && !this->UicSkipTest(headerName)) {
  942. absHeadersUic.insert(headerName);
  943. }
  944. break;
  945. }
  946. }
  947. }
  948. void cmQtAutoGenerators::ParseHeaders(
  949. const std::set<std::string>& absHeadersMoc,
  950. const std::set<std::string>& absHeadersUic,
  951. const std::map<std::string, std::string>& includedMocs,
  952. std::map<std::string, std::string>& notIncludedMocs,
  953. std::map<std::string, std::vector<std::string> >& includedUis)
  954. {
  955. // Merged header files list to read files only once
  956. std::set<std::string> headerFiles;
  957. headerFiles.insert(absHeadersMoc.begin(), absHeadersMoc.end());
  958. headerFiles.insert(absHeadersUic.begin(), absHeadersUic.end());
  959. for (std::set<std::string>::const_iterator hIt = headerFiles.begin();
  960. hIt != headerFiles.end(); ++hIt) {
  961. const std::string& headerName = *hIt;
  962. const std::string contents = ReadAll(headerName);
  963. // Parse header content for MOC
  964. if ((absHeadersMoc.find(headerName) != absHeadersMoc.end()) &&
  965. (includedMocs.find(headerName) == includedMocs.end())) {
  966. // Process
  967. if (this->Verbose) {
  968. std::ostringstream err;
  969. err << "AutoMoc: Checking " << headerName << "\n";
  970. this->LogInfo(err.str());
  971. }
  972. std::string macroName;
  973. if (this->requiresMocing(contents, macroName)) {
  974. notIncludedMocs[headerName] = fpathCheckSum.getPart(headerName) +
  975. "/moc_" +
  976. cmsys::SystemTools::GetFilenameWithoutLastExtension(headerName) +
  977. ".cpp";
  978. }
  979. }
  980. // Parse header content for UIC
  981. if (absHeadersUic.find(headerName) != absHeadersUic.end()) {
  982. this->ParseContentForUic(headerName, contents, includedUis);
  983. }
  984. }
  985. }
  986. bool cmQtAutoGenerators::MocGenerateAll(
  987. const std::map<std::string, std::string>& includedMocs,
  988. const std::map<std::string, std::string>& notIncludedMocs)
  989. {
  990. if (this->MocExecutable.empty()) {
  991. return true;
  992. }
  993. // look for name collisions
  994. {
  995. std::multimap<std::string, std::string> collisions;
  996. // Test merged map of included and notIncluded
  997. std::map<std::string, std::string> mergedMocs(includedMocs);
  998. mergedMocs.insert(notIncludedMocs.begin(), notIncludedMocs.end());
  999. if (this->NameCollisionTest(mergedMocs, collisions)) {
  1000. std::ostringstream err;
  1001. err << "AutoMoc: Error: "
  1002. "The same moc file will be generated "
  1003. "from different sources."
  1004. << std::endl
  1005. << "To avoid this error either" << std::endl
  1006. << "- rename the source files or" << std::endl
  1007. << "- do not include the (moc_NAME.cpp|NAME.moc) file" << std::endl;
  1008. this->LogErrorNameCollision(err.str(), collisions);
  1009. return false;
  1010. }
  1011. }
  1012. // generate moc files that are included by source files.
  1013. {
  1014. const std::string subDirPrefix = "include/";
  1015. for (std::map<std::string, std::string>::const_iterator it =
  1016. includedMocs.begin();
  1017. it != includedMocs.end(); ++it) {
  1018. if (!this->MocGenerateFile(it->first, it->second, subDirPrefix)) {
  1019. if (this->RunMocFailed) {
  1020. return false;
  1021. }
  1022. }
  1023. }
  1024. }
  1025. // generate moc files that are _not_ included by source files.
  1026. bool automocCppChanged = false;
  1027. {
  1028. const std::string subDirPrefix;
  1029. for (std::map<std::string, std::string>::const_iterator it =
  1030. notIncludedMocs.begin();
  1031. it != notIncludedMocs.end(); ++it) {
  1032. if (this->MocGenerateFile(it->first, it->second, subDirPrefix)) {
  1033. automocCppChanged = true;
  1034. } else {
  1035. if (this->RunMocFailed) {
  1036. return false;
  1037. }
  1038. }
  1039. }
  1040. }
  1041. // Compose moc_compilation.cpp content
  1042. std::string automocSource;
  1043. {
  1044. std::ostringstream outStream;
  1045. outStream << "/* This file is autogenerated, do not edit*/\n";
  1046. if (notIncludedMocs.empty()) {
  1047. // Dummy content
  1048. outStream << "enum some_compilers { need_more_than_nothing };\n";
  1049. } else {
  1050. // Valid content
  1051. for (std::map<std::string, std::string>::const_iterator it =
  1052. notIncludedMocs.begin();
  1053. it != notIncludedMocs.end(); ++it) {
  1054. outStream << "#include \"" << it->second << "\"\n";
  1055. }
  1056. }
  1057. outStream.flush();
  1058. automocSource = outStream.str();
  1059. }
  1060. // Check if we even need to update moc_compilation.cpp
  1061. if (!automocCppChanged) {
  1062. // compare contents of the moc_compilation.cpp file
  1063. const std::string oldContents = ReadAll(this->OutMocCppFilenameAbs);
  1064. if (oldContents == automocSource) {
  1065. // nothing changed: don't touch the moc_compilation.cpp file
  1066. if (this->Verbose) {
  1067. std::ostringstream err;
  1068. err << "AutoMoc: " << this->OutMocCppFilenameRel << " still up to date"
  1069. << std::endl;
  1070. this->LogInfo(err.str());
  1071. }
  1072. return true;
  1073. }
  1074. }
  1075. // Actually write moc_compilation.cpp
  1076. {
  1077. std::string msg = "Generating MOC compilation ";
  1078. msg += this->OutMocCppFilenameRel;
  1079. this->LogBold(msg);
  1080. }
  1081. // Make sure the parent directory exists
  1082. bool success = this->MakeParentDirectory(this->OutMocCppFilenameAbs);
  1083. if (success) {
  1084. cmsys::ofstream outfile;
  1085. outfile.open(this->OutMocCppFilenameAbs.c_str(), std::ios::trunc);
  1086. if (!outfile) {
  1087. success = false;
  1088. std::ostringstream err;
  1089. err << "AutoMoc: error opening " << this->OutMocCppFilenameAbs << "\n";
  1090. this->LogError(err.str());
  1091. } else {
  1092. outfile << automocSource;
  1093. // Check for write errors
  1094. if (!outfile.good()) {
  1095. success = false;
  1096. std::ostringstream err;
  1097. err << "AutoMoc: error writing " << this->OutMocCppFilenameAbs << "\n";
  1098. this->LogError(err.str());
  1099. }
  1100. }
  1101. }
  1102. return success;
  1103. }
  1104. /**
  1105. * @return True if a moc file was created. False may indicate an error.
  1106. */
  1107. bool cmQtAutoGenerators::MocGenerateFile(const std::string& sourceFile,
  1108. const std::string& mocFileName,
  1109. const std::string& subDirPrefix)
  1110. {
  1111. const std::string mocFileRel =
  1112. this->AutogenBuildSubDir + subDirPrefix + mocFileName;
  1113. const std::string mocFileAbs = this->CurrentBinaryDir + mocFileRel;
  1114. bool generateMoc = this->GenerateMocAll;
  1115. // Test if the source file is newer that the build file
  1116. if (!generateMoc) {
  1117. generateMoc = FileAbsentOrOlder(mocFileAbs, sourceFile);
  1118. }
  1119. if (generateMoc) {
  1120. // Log
  1121. this->LogBold("Generating MOC source " + mocFileRel);
  1122. // Make sure the parent directory exists
  1123. if (!this->MakeParentDirectory(mocFileAbs)) {
  1124. this->RunMocFailed = true;
  1125. return false;
  1126. }
  1127. std::vector<std::string> command;
  1128. command.push_back(this->MocExecutable);
  1129. command.insert(command.end(), this->MocIncludes.begin(),
  1130. this->MocIncludes.end());
  1131. command.insert(command.end(), this->MocDefinitions.begin(),
  1132. this->MocDefinitions.end());
  1133. command.insert(command.end(), this->MocOptions.begin(),
  1134. this->MocOptions.end());
  1135. #ifdef _WIN32
  1136. command.push_back("-DWIN32");
  1137. #endif
  1138. command.push_back("-o");
  1139. command.push_back(mocFileAbs);
  1140. command.push_back(sourceFile);
  1141. if (this->Verbose) {
  1142. this->LogCommand(command);
  1143. }
  1144. std::string output;
  1145. int retVal = 0;
  1146. bool result =
  1147. cmSystemTools::RunSingleCommand(command, &output, &output, &retVal);
  1148. if (!result || retVal) {
  1149. {
  1150. std::ostringstream err;
  1151. err << "AutoMoc: Error: moc process for " << mocFileRel << " failed:\n"
  1152. << output << std::endl;
  1153. this->LogError(err.str());
  1154. }
  1155. cmSystemTools::RemoveFile(mocFileAbs);
  1156. this->RunMocFailed = true;
  1157. return false;
  1158. }
  1159. return true;
  1160. }
  1161. return false;
  1162. }
  1163. bool cmQtAutoGenerators::UicGenerateAll(
  1164. const std::map<std::string, std::vector<std::string> >& includedUis)
  1165. {
  1166. if (this->UicExecutable.empty()) {
  1167. return true;
  1168. }
  1169. // single map with input / output names
  1170. std::map<std::string, std::map<std::string, std::string> > uiGenMap;
  1171. std::map<std::string, std::string> testMap;
  1172. for (std::map<std::string, std::vector<std::string> >::const_iterator it =
  1173. includedUis.begin();
  1174. it != includedUis.end(); ++it) {
  1175. // source file path
  1176. std::string sourcePath = cmsys::SystemTools::GetFilenamePath(it->first);
  1177. sourcePath += '/';
  1178. // insert new map for source file an use new reference
  1179. uiGenMap[it->first] = std::map<std::string, std::string>();
  1180. std::map<std::string, std::string>& sourceMap = uiGenMap[it->first];
  1181. for (std::vector<std::string>::const_iterator sit = it->second.begin();
  1182. sit != it->second.end(); ++sit) {
  1183. const std::string& uiFileName = *sit;
  1184. const std::string uiInputFile = sourcePath + uiFileName + ".ui";
  1185. const std::string uiOutputFile = "ui_" + uiFileName + ".h";
  1186. sourceMap[uiInputFile] = uiOutputFile;
  1187. testMap[uiInputFile] = uiOutputFile;
  1188. }
  1189. }
  1190. // look for name collisions
  1191. {
  1192. std::multimap<std::string, std::string> collisions;
  1193. if (this->NameCollisionTest(testMap, collisions)) {
  1194. std::ostringstream err;
  1195. err << "AutoUic: Error: The same ui_NAME.h file will be generated "
  1196. "from different sources."
  1197. << std::endl
  1198. << "To avoid this error rename the source files." << std::endl;
  1199. this->LogErrorNameCollision(err.str(), collisions);
  1200. return false;
  1201. }
  1202. }
  1203. testMap.clear();
  1204. // generate ui files
  1205. for (std::map<std::string,
  1206. std::map<std::string, std::string> >::const_iterator it =
  1207. uiGenMap.begin();
  1208. it != uiGenMap.end(); ++it) {
  1209. for (std::map<std::string, std::string>::const_iterator sit =
  1210. it->second.begin();
  1211. sit != it->second.end(); ++sit) {
  1212. if (!this->UicGenerateFile(it->first, sit->first, sit->second)) {
  1213. if (this->RunUicFailed) {
  1214. return false;
  1215. }
  1216. }
  1217. }
  1218. }
  1219. return true;
  1220. }
  1221. /**
  1222. * @return True if a uic file was created. False may indicate an error.
  1223. */
  1224. bool cmQtAutoGenerators::UicGenerateFile(const std::string& realName,
  1225. const std::string& uiInputFile,
  1226. const std::string& uiOutputFile)
  1227. {
  1228. const std::string uicFileRel =
  1229. this->AutogenBuildSubDir + "include/" + uiOutputFile;
  1230. const std::string uicFileAbs = this->CurrentBinaryDir + uicFileRel;
  1231. bool generateUic = this->GenerateUicAll;
  1232. // Test if the source file is newer that the build file
  1233. if (!generateUic) {
  1234. generateUic = FileAbsentOrOlder(uicFileAbs, uiInputFile);
  1235. }
  1236. if (generateUic) {
  1237. // Log
  1238. this->LogBold("Generating UIC header " + uicFileRel);
  1239. // Make sure the parent directory exists
  1240. if (!this->MakeParentDirectory(uicFileAbs)) {
  1241. this->RunUicFailed = true;
  1242. return false;
  1243. }
  1244. std::vector<std::string> command;
  1245. command.push_back(this->UicExecutable);
  1246. std::vector<std::string> opts = this->UicTargetOptions;
  1247. std::map<std::string, std::string>::const_iterator optionIt =
  1248. this->UicOptions.find(uiInputFile);
  1249. if (optionIt != this->UicOptions.end()) {
  1250. std::vector<std::string> fileOpts;
  1251. cmSystemTools::ExpandListArgument(optionIt->second, fileOpts);
  1252. UicMergeOptions(opts, fileOpts, this->QtMajorVersion == "5");
  1253. }
  1254. command.insert(command.end(), opts.begin(), opts.end());
  1255. command.push_back("-o");
  1256. command.push_back(uicFileAbs);
  1257. command.push_back(uiInputFile);
  1258. if (this->Verbose) {
  1259. this->LogCommand(command);
  1260. }
  1261. std::string output;
  1262. int retVal = 0;
  1263. bool result =
  1264. cmSystemTools::RunSingleCommand(command, &output, &output, &retVal);
  1265. if (!result || retVal) {
  1266. {
  1267. std::ostringstream err;
  1268. err << "AutoUic: Error: uic process for " << uicFileRel
  1269. << " needed by\n \"" << realName << "\"\nfailed:\n"
  1270. << output << std::endl;
  1271. this->LogError(err.str());
  1272. }
  1273. cmSystemTools::RemoveFile(uicFileAbs);
  1274. this->RunUicFailed = true;
  1275. return false;
  1276. }
  1277. return true;
  1278. }
  1279. return false;
  1280. }
  1281. bool cmQtAutoGenerators::QrcGenerateAll()
  1282. {
  1283. if (this->RccExecutable.empty()) {
  1284. return true;
  1285. }
  1286. // generate single map with input / output names
  1287. std::map<std::string, std::string> qrcGenMap;
  1288. for (std::vector<std::string>::const_iterator si = this->RccSources.begin();
  1289. si != this->RccSources.end(); ++si) {
  1290. const std::string ext = cmsys::SystemTools::GetFilenameLastExtension(*si);
  1291. if (ext == ".qrc") {
  1292. qrcGenMap[*si] = this->AutogenBuildSubDir + fpathCheckSum.getPart(*si) +
  1293. "/qrc_" + cmsys::SystemTools::GetFilenameWithoutLastExtension(*si) +
  1294. ".cpp";
  1295. }
  1296. }
  1297. // look for name collisions
  1298. {
  1299. std::multimap<std::string, std::string> collisions;
  1300. if (this->NameCollisionTest(qrcGenMap, collisions)) {
  1301. std::ostringstream err;
  1302. err << "AutoRcc: Error: The same qrc_NAME.cpp file"
  1303. " will be generated from different sources."
  1304. << std::endl
  1305. << "To avoid this error rename the source .qrc files." << std::endl;
  1306. this->LogErrorNameCollision(err.str(), collisions);
  1307. return false;
  1308. }
  1309. }
  1310. // generate qrc files
  1311. for (std::map<std::string, std::string>::const_iterator si =
  1312. qrcGenMap.begin();
  1313. si != qrcGenMap.end(); ++si) {
  1314. bool unique = FileNameIsUnique(si->first, qrcGenMap);
  1315. if (!this->QrcGenerateFile(si->first, si->second, unique)) {
  1316. if (this->RunRccFailed) {
  1317. return false;
  1318. }
  1319. }
  1320. }
  1321. return true;
  1322. }
  1323. /**
  1324. * @return True if a rcc file was created. False may indicate an error.
  1325. */
  1326. bool cmQtAutoGenerators::QrcGenerateFile(const std::string& qrcInputFile,
  1327. const std::string& qrcOutputFile,
  1328. bool unique_n)
  1329. {
  1330. std::string symbolName =
  1331. cmsys::SystemTools::GetFilenameWithoutLastExtension(qrcInputFile);
  1332. if (!unique_n) {
  1333. symbolName += "_";
  1334. symbolName += fpathCheckSum.getPart(qrcInputFile);
  1335. }
  1336. // Replace '-' with '_'. The former is valid for
  1337. // file names but not for symbol names.
  1338. std::replace(symbolName.begin(), symbolName.end(), '-', '_');
  1339. const std::string qrcBuildFile = this->CurrentBinaryDir + qrcOutputFile;
  1340. bool generateQrc = this->GenerateRccAll;
  1341. // Test if the resources list file is newer than build file
  1342. if (!generateQrc) {
  1343. generateQrc = FileAbsentOrOlder(qrcBuildFile, qrcInputFile);
  1344. }
  1345. // Test if any resource file is newer than the build file
  1346. if (!generateQrc) {
  1347. const std::vector<std::string>& files = this->RccInputs[qrcInputFile];
  1348. for (std::vector<std::string>::const_iterator it = files.begin();
  1349. it != files.end(); ++it) {
  1350. if (FileAbsentOrOlder(qrcBuildFile, *it)) {
  1351. generateQrc = true;
  1352. break;
  1353. }
  1354. }
  1355. }
  1356. if (generateQrc) {
  1357. {
  1358. std::string msg = "Generating RCC source ";
  1359. msg += qrcOutputFile;
  1360. this->LogBold(msg);
  1361. }
  1362. // Make sure the parent directory exists
  1363. if (!this->MakeParentDirectory(qrcOutputFile)) {
  1364. this->RunRccFailed = true;
  1365. return false;
  1366. }
  1367. std::vector<std::string> command;
  1368. command.push_back(this->RccExecutable);
  1369. {
  1370. std::map<std::string, std::string>::const_iterator optionIt =
  1371. this->RccOptions.find(qrcInputFile);
  1372. if (optionIt != this->RccOptions.end()) {
  1373. cmSystemTools::ExpandListArgument(optionIt->second, command);
  1374. }
  1375. }
  1376. command.push_back("-name");
  1377. command.push_back(symbolName);
  1378. command.push_back("-o");
  1379. command.push_back(qrcBuildFile);
  1380. command.push_back(qrcInputFile);
  1381. if (this->Verbose) {
  1382. this->LogCommand(command);
  1383. }
  1384. std::string output;
  1385. int retVal = 0;
  1386. bool result =
  1387. cmSystemTools::RunSingleCommand(command, &output, &output, &retVal);
  1388. if (!result || retVal) {
  1389. {
  1390. std::ostringstream err;
  1391. err << "AutoRcc: Error: rcc process for " << qrcOutputFile
  1392. << " failed:\n"
  1393. << output << std::endl;
  1394. this->LogError(err.str());
  1395. }
  1396. cmSystemTools::RemoveFile(qrcBuildFile);
  1397. this->RunRccFailed = true;
  1398. return false;
  1399. }
  1400. return true;
  1401. }
  1402. return false;
  1403. }
  1404. void cmQtAutoGenerators::LogErrorNameCollision(
  1405. const std::string& message,
  1406. const std::multimap<std::string, std::string>& collisions)
  1407. {
  1408. typedef std::multimap<std::string, std::string>::const_iterator Iter;
  1409. std::ostringstream err;
  1410. // Add message
  1411. err << message;
  1412. // Append collision list
  1413. for (Iter it = collisions.begin(); it != collisions.end(); ++it) {
  1414. err << it->first << " : " << it->second << std::endl;
  1415. }
  1416. this->LogError(err.str());
  1417. }
  1418. void cmQtAutoGenerators::LogBold(const std::string& message)
  1419. {
  1420. cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue |
  1421. cmsysTerminal_Color_ForegroundBold,
  1422. message.c_str(), true, this->ColorOutput);
  1423. }
  1424. void cmQtAutoGenerators::LogInfo(const std::string& message)
  1425. {
  1426. std::cout << message.c_str();
  1427. }
  1428. void cmQtAutoGenerators::LogWarning(const std::string& message)
  1429. {
  1430. std::ostringstream ostr;
  1431. ostr << message << "\n";
  1432. std::cout << message.c_str();
  1433. }
  1434. void cmQtAutoGenerators::LogError(const std::string& message)
  1435. {
  1436. std::ostringstream ostr;
  1437. ostr << message << "\n\n";
  1438. std::cerr << ostr.str();
  1439. }
  1440. void cmQtAutoGenerators::LogCommand(const std::vector<std::string>& command)
  1441. {
  1442. std::ostringstream sbuf;
  1443. for (std::vector<std::string>::const_iterator cmdIt = command.begin();
  1444. cmdIt != command.end(); ++cmdIt) {
  1445. if (cmdIt != command.begin()) {
  1446. sbuf << " ";
  1447. }
  1448. sbuf << *cmdIt;
  1449. }
  1450. if (!sbuf.str().empty()) {
  1451. sbuf << std::endl;
  1452. this->LogInfo(sbuf.str());
  1453. }
  1454. }
  1455. /**
  1456. * @brief Collects name collisions as output/input pairs
  1457. * @return True if there were collisions
  1458. */
  1459. bool cmQtAutoGenerators::NameCollisionTest(
  1460. const std::map<std::string, std::string>& genFiles,
  1461. std::multimap<std::string, std::string>& collisions)
  1462. {
  1463. typedef std::map<std::string, std::string>::const_iterator Iter;
  1464. typedef std::map<std::string, std::string>::value_type VType;
  1465. for (Iter ait = genFiles.begin(); ait != genFiles.end(); ++ait) {
  1466. bool first_match(true);
  1467. for (Iter bit = (++Iter(ait)); bit != genFiles.end(); ++bit) {
  1468. if (ait->second == bit->second) {
  1469. if (first_match) {
  1470. if (collisions.find(ait->second) != collisions.end()) {
  1471. // We already know of this collision from before
  1472. break;
  1473. }
  1474. collisions.insert(VType(ait->second, ait->first));
  1475. first_match = false;
  1476. }
  1477. collisions.insert(VType(bit->second, bit->first));
  1478. }
  1479. }
  1480. }
  1481. return !collisions.empty();
  1482. }
  1483. /**
  1484. * @brief Generates the parent directory of the given file on demand
  1485. * @return True on success
  1486. */
  1487. bool cmQtAutoGenerators::MakeParentDirectory(const std::string& filename)
  1488. {
  1489. bool success = true;
  1490. const std::string dirName = cmSystemTools::GetFilenamePath(filename);
  1491. if (!dirName.empty()) {
  1492. success = cmsys::SystemTools::MakeDirectory(dirName);
  1493. if (!success) {
  1494. std::ostringstream err;
  1495. err << "AutoGen: Directory creation failed: " << dirName << std::endl;
  1496. this->LogError(err.str());
  1497. }
  1498. }
  1499. return success;
  1500. }