cmQtAutoGenerators.cxx 53 KB

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