cmQtAutoGenerators.cxx 51 KB

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