cmQtAutoGenerators.cxx 55 KB

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