cmQtAutoGenerators.cxx 51 KB

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