cmQtAutoGenerators.cxx 50 KB

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