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