cmQtAutoGenerators.cxx 53 KB

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