cmQtAutoGeneratorMocUic.cxx 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733
  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 "cmQtAutoGen.h"
  4. #include "cmQtAutoGeneratorMocUic.h"
  5. #include <algorithm>
  6. #include <array>
  7. #include <list>
  8. #include <memory>
  9. #include <sstream>
  10. #include <string.h>
  11. #include <utility>
  12. #include "cmAlgorithms.h"
  13. #include "cmCryptoHash.h"
  14. #include "cmMakefile.h"
  15. #include "cmOutputConverter.h"
  16. #include "cmSystemTools.h"
  17. #include "cmake.h"
  18. #if defined(__APPLE__)
  19. #include <unistd.h>
  20. #endif
  21. // -- Static variables
  22. static const char* SettingsKeyMoc = "AM_MOC_SETTINGS_HASH";
  23. static const char* SettingsKeyUic = "AM_UIC_SETTINGS_HASH";
  24. // -- Static functions
  25. static std::string SubDirPrefix(std::string const& fileName)
  26. {
  27. std::string res(cmSystemTools::GetFilenamePath(fileName));
  28. if (!res.empty()) {
  29. res += '/';
  30. }
  31. return res;
  32. }
  33. static bool ListContains(std::vector<std::string> const& list,
  34. std::string const& entry)
  35. {
  36. return (std::find(list.begin(), list.end(), entry) != list.end());
  37. }
  38. // -- Class methods
  39. cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic()
  40. : MultiConfig(cmQtAutoGen::WRAP)
  41. , IncludeProjectDirsBefore(false)
  42. , QtVersionMajor(4)
  43. , MocSettingsChanged(false)
  44. , MocPredefsChanged(false)
  45. , MocRelaxedMode(false)
  46. , UicSettingsChanged(false)
  47. {
  48. // Precompile regular expressions
  49. this->MocRegExpInclude.compile(
  50. "[\n][ \t]*#[ \t]*include[ \t]+"
  51. "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
  52. this->UicRegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+"
  53. "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
  54. }
  55. bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile)
  56. {
  57. // -- Meta
  58. this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
  59. // Utility lambdas
  60. auto InfoGet = [makefile](const char* key) {
  61. return makefile->GetSafeDefinition(key);
  62. };
  63. auto InfoGetBool = [makefile](const char* key) {
  64. return makefile->IsOn(key);
  65. };
  66. auto InfoGetList = [makefile](const char* key) -> std::vector<std::string> {
  67. std::vector<std::string> list;
  68. cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
  69. return list;
  70. };
  71. auto InfoGetLists =
  72. [makefile](const char* key) -> std::vector<std::vector<std::string>> {
  73. std::vector<std::vector<std::string>> lists;
  74. {
  75. std::string const value = makefile->GetSafeDefinition(key);
  76. std::string::size_type pos = 0;
  77. while (pos < value.size()) {
  78. std::string::size_type next = value.find(cmQtAutoGen::listSep, pos);
  79. std::string::size_type length =
  80. (next != std::string::npos) ? next - pos : value.size() - pos;
  81. // Remove enclosing braces
  82. if (length >= 2) {
  83. std::string::const_iterator itBeg = value.begin() + (pos + 1);
  84. std::string::const_iterator itEnd = itBeg + (length - 2);
  85. {
  86. std::string subValue(itBeg, itEnd);
  87. std::vector<std::string> list;
  88. cmSystemTools::ExpandListArgument(subValue, list);
  89. lists.push_back(std::move(list));
  90. }
  91. }
  92. pos += length;
  93. pos += cmQtAutoGen::listSep.size();
  94. }
  95. }
  96. return lists;
  97. };
  98. auto InfoGetConfig = [makefile, this](const char* key) -> std::string {
  99. const char* valueConf = nullptr;
  100. {
  101. std::string keyConf = key;
  102. keyConf += '_';
  103. keyConf += this->GetInfoConfig();
  104. valueConf = makefile->GetDefinition(keyConf);
  105. }
  106. if (valueConf == nullptr) {
  107. valueConf = makefile->GetSafeDefinition(key);
  108. }
  109. return std::string(valueConf);
  110. };
  111. auto InfoGetConfigList =
  112. [&InfoGetConfig](const char* key) -> std::vector<std::string> {
  113. std::vector<std::string> list;
  114. cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
  115. return list;
  116. };
  117. // -- Read info file
  118. if (!makefile->ReadListFile(this->GetInfoFile().c_str())) {
  119. this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(),
  120. "File processing failed");
  121. return false;
  122. }
  123. // -- Meta
  124. this->MultiConfig = cmQtAutoGen::MultiConfigType(InfoGet("AM_MULTI_CONFIG"));
  125. this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX");
  126. if (this->ConfigSuffix.empty()) {
  127. this->ConfigSuffix = "_";
  128. this->ConfigSuffix += this->GetInfoConfig();
  129. }
  130. this->SettingsFile = InfoGetConfig("AM_SETTINGS_FILE");
  131. if (this->SettingsFile.empty()) {
  132. this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(),
  133. "Settings file name missing");
  134. return false;
  135. }
  136. // - Files and directories
  137. this->ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
  138. this->ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
  139. this->CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
  140. this->CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
  141. this->IncludeProjectDirsBefore =
  142. InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
  143. this->AutogenBuildDir = InfoGet("AM_BUILD_DIR");
  144. if (this->AutogenBuildDir.empty()) {
  145. this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(),
  146. "Autogen build directory missing");
  147. return false;
  148. }
  149. // - Qt environment
  150. if (!cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR"),
  151. &this->QtVersionMajor)) {
  152. this->QtVersionMajor = 4;
  153. }
  154. this->MocExecutable = InfoGet("AM_QT_MOC_EXECUTABLE");
  155. this->UicExecutable = InfoGet("AM_QT_UIC_EXECUTABLE");
  156. // - Moc
  157. if (this->MocEnabled()) {
  158. this->MocSkipList = InfoGetList("AM_MOC_SKIP");
  159. this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
  160. #ifdef _WIN32
  161. {
  162. std::string const win32("WIN32");
  163. if (!ListContains(this->MocDefinitions, win32)) {
  164. this->MocDefinitions.push_back(win32);
  165. }
  166. }
  167. #endif
  168. this->MocIncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
  169. this->MocOptions = InfoGetList("AM_MOC_OPTIONS");
  170. this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
  171. {
  172. std::vector<std::string> const MocMacroNames =
  173. InfoGetList("AM_MOC_MACRO_NAMES");
  174. for (std::string const& item : MocMacroNames) {
  175. this->MocMacroFilters.emplace_back(
  176. item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
  177. }
  178. }
  179. {
  180. std::vector<std::string> const mocDependFilters =
  181. InfoGetList("AM_MOC_DEPEND_FILTERS");
  182. // Insert Q_PLUGIN_METADATA dependency filter
  183. if (this->QtVersionMajor != 4) {
  184. this->MocDependFilterPush("Q_PLUGIN_METADATA",
  185. "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
  186. "[^\\)]*FILE[ \t]*\"([^\"]+)\"");
  187. }
  188. // Insert user defined dependency filters
  189. if ((mocDependFilters.size() % 2) == 0) {
  190. for (std::vector<std::string>::const_iterator
  191. dit = mocDependFilters.begin(),
  192. ditEnd = mocDependFilters.end();
  193. dit != ditEnd; dit += 2) {
  194. if (!this->MocDependFilterPush(*dit, *(dit + 1))) {
  195. return false;
  196. }
  197. }
  198. } else {
  199. this->LogFileError(
  200. cmQtAutoGen::MOC, this->GetInfoFile(),
  201. "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
  202. return false;
  203. }
  204. }
  205. this->MocPredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
  206. }
  207. // - Uic
  208. if (this->UicEnabled()) {
  209. this->UicSkipList = InfoGetList("AM_UIC_SKIP");
  210. this->UicSearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
  211. this->UicTargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
  212. {
  213. auto sources = InfoGetList("AM_UIC_OPTIONS_FILES");
  214. auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS");
  215. // Compare list sizes
  216. if (sources.size() != options.size()) {
  217. std::ostringstream ost;
  218. ost << "files/options lists sizes mismatch (" << sources.size() << "/"
  219. << options.size() << ")";
  220. this->LogFileError(cmQtAutoGen::UIC, this->GetInfoFile(), ost.str());
  221. return false;
  222. }
  223. auto fitEnd = sources.cend();
  224. auto fit = sources.begin();
  225. auto oit = options.begin();
  226. while (fit != fitEnd) {
  227. this->UicOptions[*fit] = std::move(*oit);
  228. ++fit;
  229. ++oit;
  230. }
  231. }
  232. }
  233. // Initialize source file jobs
  234. {
  235. // Utility lambdas
  236. auto AddJob = [this](std::map<std::string, SourceJob>& jobs,
  237. std::string&& sourceFile) {
  238. const bool moc = !this->MocSkip(sourceFile);
  239. const bool uic = !this->UicSkip(sourceFile);
  240. if (moc || uic) {
  241. SourceJob& job = jobs[std::move(sourceFile)];
  242. job.Moc = moc;
  243. job.Uic = uic;
  244. }
  245. };
  246. // Add header jobs
  247. for (std::string& hdr : InfoGetList("AM_HEADERS")) {
  248. AddJob(this->HeaderJobs, std::move(hdr));
  249. }
  250. // Add source jobs
  251. {
  252. std::vector<std::string> sources = InfoGetList("AM_SOURCES");
  253. // Add header(s) for the source file
  254. for (std::string const& src : sources) {
  255. const bool srcMoc = !this->MocSkip(src);
  256. const bool srcUic = !this->UicSkip(src);
  257. if (!srcMoc && !srcUic) {
  258. continue;
  259. }
  260. // Search for the default header file and a private header
  261. std::array<std::string, 2> headerBases;
  262. headerBases[0] = SubDirPrefix(src);
  263. headerBases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src);
  264. headerBases[1] = headerBases[0];
  265. headerBases[1] += "_p";
  266. for (std::string const& headerBase : headerBases) {
  267. std::string header;
  268. if (this->FindHeader(header, headerBase)) {
  269. const bool moc = srcMoc && !this->MocSkip(header);
  270. const bool uic = srcUic && !this->UicSkip(header);
  271. if (moc || uic) {
  272. SourceJob& job = this->HeaderJobs[std::move(header)];
  273. job.Moc = moc;
  274. job.Uic = uic;
  275. }
  276. }
  277. }
  278. }
  279. // Add Source jobs
  280. for (std::string& src : sources) {
  281. AddJob(this->SourceJobs, std::move(src));
  282. }
  283. }
  284. }
  285. // Init derived information
  286. // ------------------------
  287. // Init file path checksum generator
  288. this->FilePathChecksum.setupParentDirs(
  289. this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir,
  290. this->ProjectBinaryDir);
  291. // include directory
  292. this->AutogenIncludeDir = "include";
  293. if (this->MultiConfig != cmQtAutoGen::SINGLE) {
  294. this->AutogenIncludeDir += this->ConfigSuffix;
  295. }
  296. this->AutogenIncludeDir += "/";
  297. // Moc variables
  298. if (this->MocEnabled()) {
  299. // Mocs compilation file
  300. this->MocCompFileRel = "mocs_compilation";
  301. if (this->MultiConfig == cmQtAutoGen::FULL) {
  302. this->MocCompFileRel += this->ConfigSuffix;
  303. }
  304. this->MocCompFileRel += ".cpp";
  305. this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath(
  306. this->AutogenBuildDir, this->MocCompFileRel);
  307. // Moc predefs file
  308. if (!this->MocPredefsCmd.empty()) {
  309. this->MocPredefsFileRel = "moc_predefs";
  310. if (this->MultiConfig != cmQtAutoGen::SINGLE) {
  311. this->MocPredefsFileRel += this->ConfigSuffix;
  312. }
  313. this->MocPredefsFileRel += ".h";
  314. this->MocPredefsFileAbs = cmSystemTools::CollapseCombinedPath(
  315. this->AutogenBuildDir, this->MocPredefsFileRel);
  316. }
  317. // Sort include directories on demand
  318. if (this->IncludeProjectDirsBefore) {
  319. // Move strings to temporary list
  320. std::list<std::string> includes;
  321. includes.insert(includes.end(), this->MocIncludePaths.begin(),
  322. this->MocIncludePaths.end());
  323. this->MocIncludePaths.clear();
  324. this->MocIncludePaths.reserve(includes.size());
  325. // Append project directories only
  326. {
  327. std::array<std::string const*, 2> const movePaths = {
  328. { &this->ProjectBinaryDir, &this->ProjectSourceDir }
  329. };
  330. for (std::string const* ppath : movePaths) {
  331. std::list<std::string>::iterator it = includes.begin();
  332. while (it != includes.end()) {
  333. std::string const& path = *it;
  334. if (cmSystemTools::StringStartsWith(path, ppath->c_str())) {
  335. this->MocIncludePaths.push_back(path);
  336. it = includes.erase(it);
  337. } else {
  338. ++it;
  339. }
  340. }
  341. }
  342. }
  343. // Append remaining directories
  344. this->MocIncludePaths.insert(this->MocIncludePaths.end(),
  345. includes.begin(), includes.end());
  346. }
  347. // Compose moc includes list
  348. {
  349. std::set<std::string> frameworkPaths;
  350. for (std::string const& path : this->MocIncludePaths) {
  351. this->MocIncludes.push_back("-I" + path);
  352. // Extract framework path
  353. if (cmHasLiteralSuffix(path, ".framework/Headers")) {
  354. // Go up twice to get to the framework root
  355. std::vector<std::string> pathComponents;
  356. cmSystemTools::SplitPath(path, pathComponents);
  357. std::string frameworkPath = cmSystemTools::JoinPath(
  358. pathComponents.begin(), pathComponents.end() - 2);
  359. frameworkPaths.insert(frameworkPath);
  360. }
  361. }
  362. // Append framework includes
  363. for (std::string const& path : frameworkPaths) {
  364. this->MocIncludes.push_back("-F");
  365. this->MocIncludes.push_back(path);
  366. }
  367. }
  368. // Setup single list with all options
  369. {
  370. // Add includes
  371. this->MocAllOptions.insert(this->MocAllOptions.end(),
  372. this->MocIncludes.begin(),
  373. this->MocIncludes.end());
  374. // Add definitions
  375. for (std::string const& def : this->MocDefinitions) {
  376. this->MocAllOptions.push_back("-D" + def);
  377. }
  378. // Add options
  379. this->MocAllOptions.insert(this->MocAllOptions.end(),
  380. this->MocOptions.begin(),
  381. this->MocOptions.end());
  382. }
  383. }
  384. return true;
  385. }
  386. void cmQtAutoGeneratorMocUic::SettingsFileRead(cmMakefile* makefile)
  387. {
  388. // Compose current settings strings
  389. {
  390. cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
  391. std::string const sep(" ~~~ ");
  392. if (this->MocEnabled()) {
  393. std::string str;
  394. str += this->MocExecutable;
  395. str += sep;
  396. str += cmJoin(this->MocAllOptions, ";");
  397. str += sep;
  398. str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE";
  399. str += sep;
  400. str += cmJoin(this->MocPredefsCmd, ";");
  401. str += sep;
  402. this->SettingsStringMoc = crypt.HashString(str);
  403. }
  404. if (this->UicEnabled()) {
  405. std::string str;
  406. str += this->UicExecutable;
  407. str += sep;
  408. str += cmJoin(this->UicTargetOptions, ";");
  409. for (const auto& item : this->UicOptions) {
  410. str += sep;
  411. str += item.first;
  412. str += sep;
  413. str += cmJoin(item.second, ";");
  414. }
  415. str += sep;
  416. this->SettingsStringUic = crypt.HashString(str);
  417. }
  418. }
  419. // Read old settings
  420. if (makefile->ReadListFile(this->SettingsFile.c_str())) {
  421. {
  422. auto SMatch = [makefile](const char* key, std::string const& value) {
  423. return (value == makefile->GetSafeDefinition(key));
  424. };
  425. if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) {
  426. this->MocSettingsChanged = true;
  427. }
  428. if (!SMatch(SettingsKeyUic, this->SettingsStringUic)) {
  429. this->UicSettingsChanged = true;
  430. }
  431. }
  432. // In case any setting changed remove the old settings file.
  433. // This triggers a full rebuild on the next run if the current
  434. // build is aborted before writing the current settings in the end.
  435. if (this->SettingsChanged()) {
  436. cmSystemTools::RemoveFile(this->SettingsFile);
  437. }
  438. } else {
  439. // If the file could not be read re-generate everythiung.
  440. this->MocSettingsChanged = true;
  441. this->UicSettingsChanged = true;
  442. }
  443. }
  444. bool cmQtAutoGeneratorMocUic::SettingsFileWrite()
  445. {
  446. bool success = true;
  447. // Only write if any setting changed
  448. if (this->SettingsChanged()) {
  449. if (this->GetVerbose()) {
  450. this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " +
  451. cmQtAutoGen::Quoted(this->SettingsFile));
  452. }
  453. // Compose settings file content
  454. std::string settings;
  455. {
  456. auto SettingAppend = [&settings](const char* key,
  457. std::string const& value) {
  458. settings += "set(";
  459. settings += key;
  460. settings += " ";
  461. settings += cmOutputConverter::EscapeForCMake(value);
  462. settings += ")\n";
  463. };
  464. SettingAppend(SettingsKeyMoc, this->SettingsStringMoc);
  465. SettingAppend(SettingsKeyUic, this->SettingsStringUic);
  466. }
  467. // Write settings file
  468. if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) {
  469. this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile,
  470. "Settings file writing failed");
  471. // Remove old settings file to trigger a full rebuild on the next run
  472. cmSystemTools::RemoveFile(this->SettingsFile);
  473. success = false;
  474. }
  475. }
  476. return success;
  477. }
  478. bool cmQtAutoGeneratorMocUic::Process(cmMakefile* makefile)
  479. {
  480. // the program goes through all .cpp files to see which moc files are
  481. // included. It is not really interesting how the moc file is named, but
  482. // what file the moc is created from. Once a moc is included the same moc
  483. // may not be included in the mocs_compilation.cpp file anymore.
  484. // OTOH if there's a header containing Q_OBJECT where no corresponding
  485. // moc file is included anywhere a moc_<filename>.cpp file is created and
  486. // included in the mocs_compilation.cpp file.
  487. if (!this->InitInfoFile(makefile)) {
  488. return false;
  489. }
  490. // Read latest settings
  491. this->SettingsFileRead(makefile);
  492. // Create AUTOGEN include directory
  493. {
  494. std::string const incDirAbs = cmSystemTools::CollapseCombinedPath(
  495. this->AutogenBuildDir, this->AutogenIncludeDir);
  496. if (!cmSystemTools::MakeDirectory(incDirAbs)) {
  497. this->LogFileError(cmQtAutoGen::GEN, incDirAbs,
  498. "Could not create directory");
  499. return false;
  500. }
  501. }
  502. // Parse source files
  503. for (const auto& item : this->SourceJobs) {
  504. if (!this->ParseSourceFile(item.first, item.second)) {
  505. return false;
  506. }
  507. }
  508. // Parse header files
  509. for (const auto& item : this->HeaderJobs) {
  510. if (!this->ParseHeaderFile(item.first, item.second)) {
  511. return false;
  512. }
  513. }
  514. // Read missing dependency information
  515. if (!this->ParsePostprocess()) {
  516. return false;
  517. }
  518. // Generate files
  519. if (!this->MocGenerateAll()) {
  520. return false;
  521. }
  522. if (!this->UicGenerateAll()) {
  523. return false;
  524. }
  525. if (!this->SettingsFileWrite()) {
  526. return false;
  527. }
  528. return true;
  529. }
  530. /**
  531. * @return True on success
  532. */
  533. bool cmQtAutoGeneratorMocUic::ParseSourceFile(std::string const& absFilename,
  534. const SourceJob& job)
  535. {
  536. std::string contentText;
  537. std::string error;
  538. bool success = this->FileRead(contentText, absFilename, &error);
  539. if (success) {
  540. if (!contentText.empty()) {
  541. if (job.Moc) {
  542. success = this->MocParseSourceContent(absFilename, contentText);
  543. }
  544. if (success && job.Uic) {
  545. success = this->UicParseContent(absFilename, contentText);
  546. }
  547. } else {
  548. this->LogFileWarning(cmQtAutoGen::GEN, absFilename,
  549. "The source file is empty");
  550. }
  551. } else {
  552. this->LogFileError(cmQtAutoGen::GEN, absFilename,
  553. "Could not read the source file: " + error);
  554. }
  555. return success;
  556. }
  557. /**
  558. * @return True on success
  559. */
  560. bool cmQtAutoGeneratorMocUic::ParseHeaderFile(std::string const& absFilename,
  561. const SourceJob& job)
  562. {
  563. std::string contentText;
  564. std::string error;
  565. bool success = this->FileRead(contentText, absFilename, &error);
  566. if (success) {
  567. if (!contentText.empty()) {
  568. if (job.Moc) {
  569. this->MocParseHeaderContent(absFilename, contentText);
  570. }
  571. if (job.Uic) {
  572. success = this->UicParseContent(absFilename, contentText);
  573. }
  574. } else {
  575. this->LogFileWarning(cmQtAutoGen::GEN, absFilename,
  576. "The header file is empty");
  577. }
  578. } else {
  579. this->LogFileError(cmQtAutoGen::GEN, absFilename,
  580. "Could not read the header file: " + error);
  581. }
  582. return success;
  583. }
  584. /**
  585. * @return True on success
  586. */
  587. bool cmQtAutoGeneratorMocUic::ParsePostprocess()
  588. {
  589. bool success = true;
  590. // Read missing dependencies
  591. for (auto& item : this->MocJobsIncluded) {
  592. if (!item->DependsValid) {
  593. std::string content;
  594. std::string error;
  595. if (this->FileRead(content, item->SourceFile, &error)) {
  596. this->MocFindDepends(item->SourceFile, content, item->Depends);
  597. item->DependsValid = true;
  598. } else {
  599. std::string emsg = "Could not read file\n ";
  600. emsg += item->SourceFile;
  601. emsg += "\nrequired by moc include \"";
  602. emsg += item->IncludeString;
  603. emsg += "\".\n";
  604. emsg += error;
  605. this->LogFileError(cmQtAutoGen::MOC, item->Includer, emsg);
  606. success = false;
  607. break;
  608. }
  609. }
  610. }
  611. return success;
  612. }
  613. /**
  614. * @brief Tests if the file should be ignored for moc scanning
  615. * @return True if the file should be ignored
  616. */
  617. bool cmQtAutoGeneratorMocUic::MocSkip(std::string const& absFilename) const
  618. {
  619. if (this->MocEnabled()) {
  620. // Test if the file name is on the skip list
  621. if (!ListContains(this->MocSkipList, absFilename)) {
  622. return false;
  623. }
  624. }
  625. return true;
  626. }
  627. /**
  628. * @brief Tests if the C++ content requires moc processing
  629. * @return True if moc is required
  630. */
  631. bool cmQtAutoGeneratorMocUic::MocRequired(std::string const& contentText,
  632. std::string* macroName)
  633. {
  634. for (KeyRegExp& filter : this->MocMacroFilters) {
  635. // Run a simple find string operation before the expensive
  636. // regular expression check
  637. if (contentText.find(filter.Key) != std::string::npos) {
  638. if (filter.RegExp.find(contentText)) {
  639. // Return macro name on demand
  640. if (macroName != nullptr) {
  641. *macroName = filter.Key;
  642. }
  643. return true;
  644. }
  645. }
  646. }
  647. return false;
  648. }
  649. std::string cmQtAutoGeneratorMocUic::MocStringMacros() const
  650. {
  651. std::string res;
  652. const auto itB = this->MocMacroFilters.cbegin();
  653. const auto itE = this->MocMacroFilters.cend();
  654. const auto itL = itE - 1;
  655. auto itC = itB;
  656. for (; itC != itE; ++itC) {
  657. // Separator
  658. if (itC != itB) {
  659. if (itC != itL) {
  660. res += ", ";
  661. } else {
  662. res += " or ";
  663. }
  664. }
  665. // Key
  666. res += itC->Key;
  667. }
  668. return res;
  669. }
  670. std::string cmQtAutoGeneratorMocUic::MocStringHeaders(
  671. std::string const& fileBase) const
  672. {
  673. std::string res = fileBase;
  674. res += ".{";
  675. res += cmJoin(this->HeaderExtensions, ",");
  676. res += "}";
  677. return res;
  678. }
  679. std::string cmQtAutoGeneratorMocUic::MocFindIncludedHeader(
  680. std::string const& sourcePath, std::string const& includeBase) const
  681. {
  682. std::string header;
  683. // Search in vicinity of the source
  684. if (!this->FindHeader(header, sourcePath + includeBase)) {
  685. // Search in include directories
  686. for (std::string const& path : this->MocIncludePaths) {
  687. std::string fullPath = path;
  688. fullPath.push_back('/');
  689. fullPath += includeBase;
  690. if (this->FindHeader(header, fullPath)) {
  691. break;
  692. }
  693. }
  694. }
  695. // Sanitize
  696. if (!header.empty()) {
  697. header = cmSystemTools::GetRealPath(header);
  698. }
  699. return header;
  700. }
  701. bool cmQtAutoGeneratorMocUic::MocFindIncludedFile(
  702. std::string& absFile, std::string const& sourcePath,
  703. std::string const& includeString) const
  704. {
  705. bool success = false;
  706. // Search in vicinity of the source
  707. {
  708. std::string testPath = sourcePath;
  709. testPath += includeString;
  710. if (cmSystemTools::FileExists(testPath.c_str())) {
  711. absFile = cmSystemTools::GetRealPath(testPath);
  712. success = true;
  713. }
  714. }
  715. // Search in include directories
  716. if (!success) {
  717. for (std::string const& path : this->MocIncludePaths) {
  718. std::string fullPath = path;
  719. fullPath.push_back('/');
  720. fullPath += includeString;
  721. if (cmSystemTools::FileExists(fullPath.c_str())) {
  722. absFile = cmSystemTools::GetRealPath(fullPath);
  723. success = true;
  724. break;
  725. }
  726. }
  727. }
  728. return success;
  729. }
  730. bool cmQtAutoGeneratorMocUic::MocDependFilterPush(std::string const& key,
  731. std::string const& regExp)
  732. {
  733. std::string error;
  734. if (!key.empty()) {
  735. if (!regExp.empty()) {
  736. KeyRegExp filter;
  737. filter.Key = key;
  738. if (filter.RegExp.compile(regExp)) {
  739. this->MocDependFilters.push_back(std::move(filter));
  740. } else {
  741. error = "Regular expression compiling failed";
  742. }
  743. } else {
  744. error = "Regular expression is empty";
  745. }
  746. } else {
  747. error = "Key is empty";
  748. }
  749. if (!error.empty()) {
  750. std::string emsg = "AUTOMOC_DEPEND_FILTERS: ";
  751. emsg += error;
  752. emsg += "\n";
  753. emsg += " Key: ";
  754. emsg += cmQtAutoGen::Quoted(key);
  755. emsg += "\n";
  756. emsg += " RegExp: ";
  757. emsg += cmQtAutoGen::Quoted(regExp);
  758. emsg += "\n";
  759. this->LogError(cmQtAutoGen::MOC, emsg);
  760. return false;
  761. }
  762. return true;
  763. }
  764. void cmQtAutoGeneratorMocUic::MocFindDepends(std::string const& absFilename,
  765. std::string const& contentText,
  766. std::set<std::string>& depends)
  767. {
  768. if (this->MocDependFilters.empty() && contentText.empty()) {
  769. return;
  770. }
  771. std::vector<std::string> matches;
  772. for (KeyRegExp& filter : this->MocDependFilters) {
  773. // Run a simple find string check
  774. if (contentText.find(filter.Key) != std::string::npos) {
  775. // Run the expensive regular expression check loop
  776. const char* contentChars = contentText.c_str();
  777. while (filter.RegExp.find(contentChars)) {
  778. std::string match = filter.RegExp.match(1);
  779. if (!match.empty()) {
  780. matches.emplace_back(std::move(match));
  781. }
  782. contentChars += filter.RegExp.end();
  783. }
  784. }
  785. }
  786. if (!matches.empty()) {
  787. std::string const sourcePath = SubDirPrefix(absFilename);
  788. for (std::string const& match : matches) {
  789. // Find the dependency file
  790. std::string incFile;
  791. if (this->MocFindIncludedFile(incFile, sourcePath, match)) {
  792. depends.insert(incFile);
  793. if (this->GetVerbose()) {
  794. this->LogInfo(cmQtAutoGen::MOC, "Found dependency:\n " +
  795. cmQtAutoGen::Quoted(absFilename) + "\n " +
  796. cmQtAutoGen::Quoted(incFile));
  797. }
  798. } else {
  799. this->LogFileWarning(cmQtAutoGen::MOC, absFilename,
  800. "Could not find dependency file " +
  801. cmQtAutoGen::Quoted(match));
  802. }
  803. }
  804. }
  805. }
  806. /**
  807. * @return True on success
  808. */
  809. bool cmQtAutoGeneratorMocUic::MocParseSourceContent(
  810. std::string const& absFilename, std::string const& contentText)
  811. {
  812. if (this->GetVerbose()) {
  813. this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename);
  814. }
  815. auto AddJob = [this, &absFilename](std::string const& sourceFile,
  816. std::string const& includeString,
  817. std::string const* content) {
  818. auto job = cm::make_unique<MocJobIncluded>();
  819. job->SourceFile = sourceFile;
  820. job->BuildFileRel = this->AutogenIncludeDir;
  821. job->BuildFileRel += includeString;
  822. job->Includer = absFilename;
  823. job->IncludeString = includeString;
  824. job->DependsValid = (content != nullptr);
  825. if (job->DependsValid) {
  826. this->MocFindDepends(sourceFile, *content, job->Depends);
  827. }
  828. this->MocJobsIncluded.push_back(std::move(job));
  829. };
  830. struct MocInc
  831. {
  832. std::string Inc; // full include string
  833. std::string Dir; // include string directory
  834. std::string Base; // include string file base
  835. };
  836. // Extract moc includes from file
  837. std::vector<MocInc> mocIncsUsc;
  838. std::vector<MocInc> mocIncsDot;
  839. {
  840. const char* contentChars = contentText.c_str();
  841. if (strstr(contentChars, "moc") != nullptr) {
  842. while (this->MocRegExpInclude.find(contentChars)) {
  843. std::string incString = this->MocRegExpInclude.match(1);
  844. std::string incDir(SubDirPrefix(incString));
  845. std::string incBase =
  846. cmSystemTools::GetFilenameWithoutLastExtension(incString);
  847. if (cmHasLiteralPrefix(incBase, "moc_")) {
  848. // moc_<BASE>.cxx
  849. // Remove the moc_ part from the base name
  850. mocIncsUsc.push_back(MocInc{ std::move(incString), std::move(incDir),
  851. incBase.substr(4) });
  852. } else {
  853. // <BASE>.moc
  854. mocIncsDot.push_back(MocInc{ std::move(incString), std::move(incDir),
  855. std::move(incBase) });
  856. }
  857. // Forward content pointer
  858. contentChars += this->MocRegExpInclude.end();
  859. }
  860. }
  861. }
  862. std::string selfMacroName;
  863. const bool selfRequiresMoc = this->MocRequired(contentText, &selfMacroName);
  864. // Check if there is anything to do
  865. if (!selfRequiresMoc && mocIncsUsc.empty() && mocIncsDot.empty()) {
  866. return true;
  867. }
  868. // Scan file variables
  869. std::string const scanFileDir = SubDirPrefix(absFilename);
  870. std::string const scanFileBase =
  871. cmSystemTools::GetFilenameWithoutLastExtension(absFilename);
  872. // Relaxed mode variables
  873. bool ownDotMocIncluded = false;
  874. std::string ownMocUscInclude;
  875. std::string ownMocUscHeader;
  876. // Process moc_<BASE>.cxx includes
  877. for (const MocInc& mocInc : mocIncsUsc) {
  878. std::string const header =
  879. this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base);
  880. if (!header.empty()) {
  881. // Check if header is skipped
  882. if (this->MocSkip(header)) {
  883. continue;
  884. }
  885. // Register moc job
  886. AddJob(header, mocInc.Inc, nullptr);
  887. // Store meta information for relaxed mode
  888. if (this->MocRelaxedMode && (mocInc.Base == scanFileBase)) {
  889. ownMocUscInclude = mocInc.Inc;
  890. ownMocUscHeader = header;
  891. }
  892. } else {
  893. std::string emsg = "The file includes the moc file ";
  894. emsg += cmQtAutoGen::Quoted(mocInc.Inc);
  895. emsg += ", but could not find the header ";
  896. emsg += cmQtAutoGen::Quoted(this->MocStringHeaders(mocInc.Base));
  897. this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
  898. return false;
  899. }
  900. }
  901. // Process <BASE>.moc includes
  902. for (const MocInc& mocInc : mocIncsDot) {
  903. const bool ownMoc = (mocInc.Base == scanFileBase);
  904. if (this->MocRelaxedMode) {
  905. // Relaxed mode
  906. if (selfRequiresMoc && ownMoc) {
  907. // Add self
  908. AddJob(absFilename, mocInc.Inc, &contentText);
  909. ownDotMocIncluded = true;
  910. } else {
  911. // In relaxed mode try to find a header instead but issue a warning.
  912. // This is for KDE4 compatibility
  913. std::string const header =
  914. this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base);
  915. if (!header.empty()) {
  916. // Check if header is skipped
  917. if (this->MocSkip(header)) {
  918. continue;
  919. }
  920. // Register moc job
  921. AddJob(header, mocInc.Inc, nullptr);
  922. if (!selfRequiresMoc) {
  923. if (ownMoc) {
  924. std::string emsg = "The file includes the moc file ";
  925. emsg += cmQtAutoGen::Quoted(mocInc.Inc);
  926. emsg += ", but does not contain a ";
  927. emsg += this->MocStringMacros();
  928. emsg += " macro.\nRunning moc on\n ";
  929. emsg += cmQtAutoGen::Quoted(header);
  930. emsg += "!\nBetter include ";
  931. emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
  932. emsg += " for a compatibility with strict mode.\n"
  933. "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
  934. this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
  935. } else {
  936. std::string emsg = "The file includes the moc file ";
  937. emsg += cmQtAutoGen::Quoted(mocInc.Inc);
  938. emsg += " instead of ";
  939. emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
  940. emsg += ".\nRunning moc on\n ";
  941. emsg += cmQtAutoGen::Quoted(header);
  942. emsg += "!\nBetter include ";
  943. emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
  944. emsg += " for compatibility with strict mode.\n"
  945. "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
  946. this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
  947. }
  948. }
  949. } else {
  950. std::string emsg = "The file includes the moc file ";
  951. emsg += cmQtAutoGen::Quoted(mocInc.Inc);
  952. emsg += ", which seems to be the moc file from a different "
  953. "source file. CMake also could not find a matching "
  954. "header.";
  955. this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
  956. return false;
  957. }
  958. }
  959. } else {
  960. // Strict mode
  961. if (ownMoc) {
  962. // Include self
  963. AddJob(absFilename, mocInc.Inc, &contentText);
  964. ownDotMocIncluded = true;
  965. // Accept but issue a warning if moc isn't required
  966. if (!selfRequiresMoc) {
  967. std::string emsg = "The file includes the moc file ";
  968. emsg += cmQtAutoGen::Quoted(mocInc.Inc);
  969. emsg += ", but does not contain a ";
  970. emsg += this->MocStringMacros();
  971. emsg += " macro.";
  972. this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
  973. }
  974. } else {
  975. // Don't allow <BASE>.moc include other than self in strict mode
  976. std::string emsg = "The file includes the moc file ";
  977. emsg += cmQtAutoGen::Quoted(mocInc.Inc);
  978. emsg += ", which seems to be the moc file from a different "
  979. "source file.\nThis is not supported. Include ";
  980. emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
  981. emsg += " to run moc on this source file.";
  982. this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
  983. return false;
  984. }
  985. }
  986. }
  987. if (selfRequiresMoc && !ownDotMocIncluded) {
  988. // In this case, check whether the scanned file itself contains a Q_OBJECT.
  989. // If this is the case, the moc_foo.cpp should probably be generated from
  990. // foo.cpp instead of foo.h, because otherwise it won't build.
  991. // But warn, since this is not how it is supposed to be used.
  992. if (this->MocRelaxedMode && !ownMocUscInclude.empty()) {
  993. // This is for KDE4 compatibility:
  994. std::string emsg = "The file contains a ";
  995. emsg += selfMacroName;
  996. emsg += " macro, but does not include ";
  997. emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
  998. emsg += ". Instead it includes ";
  999. emsg += cmQtAutoGen::Quoted(ownMocUscInclude);
  1000. emsg += ".\nRunning moc on\n ";
  1001. emsg += cmQtAutoGen::Quoted(absFilename);
  1002. emsg += "!\nBetter include ";
  1003. emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
  1004. emsg += " for compatibility with strict mode.\n"
  1005. "(CMAKE_AUTOMOC_RELAXED_MODE warning)";
  1006. this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
  1007. // Remove own header job
  1008. {
  1009. auto itC = this->MocJobsIncluded.begin();
  1010. auto itE = this->MocJobsIncluded.end();
  1011. for (; itC != itE; ++itC) {
  1012. if ((*itC)->SourceFile == ownMocUscHeader) {
  1013. if ((*itC)->IncludeString == ownMocUscInclude) {
  1014. this->MocJobsIncluded.erase(itC);
  1015. break;
  1016. }
  1017. }
  1018. }
  1019. }
  1020. // Add own source job
  1021. AddJob(absFilename, ownMocUscInclude, &contentText);
  1022. } else {
  1023. // Otherwise always error out since it will not compile:
  1024. std::string emsg = "The file contains a ";
  1025. emsg += selfMacroName;
  1026. emsg += " macro, but does not include ";
  1027. emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
  1028. emsg += "!\nConsider to\n - add #include \"";
  1029. emsg += scanFileBase;
  1030. emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
  1031. this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
  1032. return false;
  1033. }
  1034. }
  1035. return true;
  1036. }
  1037. void cmQtAutoGeneratorMocUic::MocParseHeaderContent(
  1038. std::string const& absFilename, std::string const& contentText)
  1039. {
  1040. if (this->GetVerbose()) {
  1041. this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename);
  1042. }
  1043. auto const fit =
  1044. std::find_if(this->MocJobsIncluded.cbegin(), this->MocJobsIncluded.cend(),
  1045. [&absFilename](std::unique_ptr<MocJobIncluded> const& job) {
  1046. return job->SourceFile == absFilename;
  1047. });
  1048. if (fit == this->MocJobsIncluded.cend()) {
  1049. if (this->MocRequired(contentText)) {
  1050. auto job = cm::make_unique<MocJobAuto>();
  1051. job->SourceFile = absFilename;
  1052. {
  1053. std::string& bld = job->BuildFileRel;
  1054. bld = this->FilePathChecksum.getPart(absFilename);
  1055. bld += '/';
  1056. bld += "moc_";
  1057. bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename);
  1058. if (this->MultiConfig != cmQtAutoGen::SINGLE) {
  1059. bld += this->ConfigSuffix;
  1060. }
  1061. bld += ".cpp";
  1062. }
  1063. this->MocFindDepends(absFilename, contentText, job->Depends);
  1064. this->MocJobsAuto.push_back(std::move(job));
  1065. }
  1066. }
  1067. }
  1068. bool cmQtAutoGeneratorMocUic::MocGenerateAll()
  1069. {
  1070. if (!this->MocEnabled()) {
  1071. return true;
  1072. }
  1073. // Look for name collisions in included moc files
  1074. {
  1075. bool collision = false;
  1076. std::map<std::string, std::vector<MocJobIncluded const*>> collisions;
  1077. for (auto const& job : this->MocJobsIncluded) {
  1078. auto& list = collisions[job->IncludeString];
  1079. if (!list.empty()) {
  1080. collision = true;
  1081. }
  1082. list.push_back(job.get());
  1083. }
  1084. if (collision) {
  1085. std::string emsg =
  1086. "Included moc files with the same name will be "
  1087. "generated from different sources.\n"
  1088. "Consider to\n"
  1089. " - not include the \"moc_<NAME>.cpp\" file\n"
  1090. " - add a directory prefix to a \"<NAME>.moc\" include "
  1091. "(e.g \"sub/<NAME>.moc\")\n"
  1092. " - rename the source file(s)\n"
  1093. "Include conflicts\n"
  1094. "-----------------\n";
  1095. const auto& colls = collisions;
  1096. for (auto const& coll : colls) {
  1097. if (coll.second.size() > 1) {
  1098. emsg += cmQtAutoGen::Quoted(coll.first);
  1099. emsg += " included in\n";
  1100. for (const MocJobIncluded* job : coll.second) {
  1101. emsg += " - ";
  1102. emsg += cmQtAutoGen::Quoted(job->Includer);
  1103. emsg += "\n";
  1104. }
  1105. emsg += "would be generated from\n";
  1106. for (const MocJobIncluded* job : coll.second) {
  1107. emsg += " - ";
  1108. emsg += cmQtAutoGen::Quoted(job->SourceFile);
  1109. emsg += "\n";
  1110. }
  1111. }
  1112. }
  1113. this->LogError(cmQtAutoGen::MOC, emsg);
  1114. return false;
  1115. }
  1116. }
  1117. // (Re)generate moc_predefs.h on demand
  1118. if (!this->MocPredefsCmd.empty()) {
  1119. if (this->MocSettingsChanged ||
  1120. !cmSystemTools::FileExists(this->MocPredefsFileAbs)) {
  1121. if (this->GetVerbose()) {
  1122. this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel);
  1123. }
  1124. std::string output;
  1125. {
  1126. // Compose command
  1127. std::vector<std::string> cmd = this->MocPredefsCmd;
  1128. // Add includes
  1129. cmd.insert(cmd.end(), this->MocIncludes.begin(),
  1130. this->MocIncludes.end());
  1131. // Add definitions
  1132. for (std::string const& def : this->MocDefinitions) {
  1133. cmd.push_back("-D" + def);
  1134. }
  1135. // Execute command
  1136. if (!this->RunCommand(cmd, output)) {
  1137. this->LogCommandError(cmQtAutoGen::MOC,
  1138. "moc_predefs generation failed", cmd, output);
  1139. return false;
  1140. }
  1141. }
  1142. // (Re)write predefs file only on demand
  1143. if (this->FileDiffers(this->MocPredefsFileAbs, output)) {
  1144. if (this->FileWrite(cmQtAutoGen::MOC, this->MocPredefsFileAbs,
  1145. output)) {
  1146. this->MocPredefsChanged = true;
  1147. } else {
  1148. this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs,
  1149. "moc_predefs file writing failed");
  1150. return false;
  1151. }
  1152. } else {
  1153. // Touch to update the time stamp
  1154. if (this->GetVerbose()) {
  1155. this->LogInfo(cmQtAutoGen::MOC,
  1156. "Touching moc_predefs " + this->MocPredefsFileRel);
  1157. }
  1158. cmSystemTools::Touch(this->MocPredefsFileAbs, false);
  1159. }
  1160. }
  1161. // Add moc_predefs.h to moc file dependencies
  1162. for (auto const& item : this->MocJobsIncluded) {
  1163. item->Depends.insert(this->MocPredefsFileAbs);
  1164. }
  1165. for (auto const& item : this->MocJobsAuto) {
  1166. item->Depends.insert(this->MocPredefsFileAbs);
  1167. }
  1168. }
  1169. // Generate moc files that are included by source files.
  1170. for (auto const& item : this->MocJobsIncluded) {
  1171. if (!this->MocGenerateFile(*item)) {
  1172. return false;
  1173. }
  1174. }
  1175. // Generate moc files that are _not_ included by source files.
  1176. bool autoNameGenerated = false;
  1177. for (auto const& item : this->MocJobsAuto) {
  1178. if (!this->MocGenerateFile(*item, &autoNameGenerated)) {
  1179. return false;
  1180. }
  1181. }
  1182. // Compose mocs compilation file content
  1183. {
  1184. std::string mocs =
  1185. "// This file is autogenerated. Changes will be overwritten.\n";
  1186. if (this->MocJobsAuto.empty()) {
  1187. // Placeholder content
  1188. mocs +=
  1189. "// No files found that require moc or the moc files are included\n";
  1190. mocs += "enum some_compilers { need_more_than_nothing };\n";
  1191. } else {
  1192. // Valid content
  1193. for (const auto& item : this->MocJobsAuto) {
  1194. mocs += "#include \"";
  1195. mocs += item->BuildFileRel;
  1196. mocs += "\"\n";
  1197. }
  1198. }
  1199. if (this->FileDiffers(this->MocCompFileAbs, mocs)) {
  1200. // Actually write mocs compilation file
  1201. if (this->GetVerbose()) {
  1202. this->LogBold("Generating MOC compilation " + this->MocCompFileRel);
  1203. }
  1204. if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs, mocs)) {
  1205. this->LogFileError(cmQtAutoGen::MOC, this->MocCompFileAbs,
  1206. "mocs compilation file writing failed");
  1207. return false;
  1208. }
  1209. } else if (autoNameGenerated) {
  1210. // Only touch mocs compilation file
  1211. if (this->GetVerbose()) {
  1212. this->LogInfo(cmQtAutoGen::MOC,
  1213. "Touching mocs compilation " + this->MocCompFileRel);
  1214. }
  1215. cmSystemTools::Touch(this->MocCompFileAbs, false);
  1216. }
  1217. }
  1218. return true;
  1219. }
  1220. /**
  1221. * @return True on success
  1222. */
  1223. bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob,
  1224. bool* generated)
  1225. {
  1226. bool success = true;
  1227. std::string const mocFileAbs = cmSystemTools::CollapseCombinedPath(
  1228. this->AutogenBuildDir, mocJob.BuildFileRel);
  1229. bool generate = false;
  1230. std::string generateReason;
  1231. if (!generate && !cmSystemTools::FileExists(mocFileAbs.c_str())) {
  1232. if (this->GetVerbose()) {
  1233. generateReason = "Generating ";
  1234. generateReason += cmQtAutoGen::Quoted(mocFileAbs);
  1235. generateReason += " from its source file ";
  1236. generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
  1237. generateReason += " because it doesn't exist";
  1238. }
  1239. generate = true;
  1240. }
  1241. if (!generate && this->MocSettingsChanged) {
  1242. if (this->GetVerbose()) {
  1243. generateReason = "Generating ";
  1244. generateReason += cmQtAutoGen::Quoted(mocFileAbs);
  1245. generateReason += " from ";
  1246. generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
  1247. generateReason += " because the MOC settings changed";
  1248. }
  1249. generate = true;
  1250. }
  1251. if (!generate && this->MocPredefsChanged) {
  1252. if (this->GetVerbose()) {
  1253. generateReason = "Generating ";
  1254. generateReason += cmQtAutoGen::Quoted(mocFileAbs);
  1255. generateReason += " from ";
  1256. generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
  1257. generateReason += " because moc_predefs.h changed";
  1258. }
  1259. generate = true;
  1260. }
  1261. if (!generate) {
  1262. std::string error;
  1263. if (FileIsOlderThan(mocFileAbs, mocJob.SourceFile, &error)) {
  1264. if (this->GetVerbose()) {
  1265. generateReason = "Generating ";
  1266. generateReason += cmQtAutoGen::Quoted(mocFileAbs);
  1267. generateReason += " because it's older than its source file ";
  1268. generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
  1269. }
  1270. generate = true;
  1271. } else {
  1272. if (!error.empty()) {
  1273. this->LogError(cmQtAutoGen::MOC, error);
  1274. success = false;
  1275. }
  1276. }
  1277. }
  1278. if (success && !generate) {
  1279. // Test if a dependency file is newer
  1280. std::string error;
  1281. for (std::string const& depFile : mocJob.Depends) {
  1282. if (FileIsOlderThan(mocFileAbs, depFile, &error)) {
  1283. if (this->GetVerbose()) {
  1284. generateReason = "Generating ";
  1285. generateReason += cmQtAutoGen::Quoted(mocFileAbs);
  1286. generateReason += " from ";
  1287. generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
  1288. generateReason += " because it is older than ";
  1289. generateReason += cmQtAutoGen::Quoted(depFile);
  1290. }
  1291. generate = true;
  1292. break;
  1293. }
  1294. if (!error.empty()) {
  1295. this->LogError(cmQtAutoGen::MOC, error);
  1296. success = false;
  1297. break;
  1298. }
  1299. }
  1300. }
  1301. if (generate) {
  1302. // Log
  1303. if (this->GetVerbose()) {
  1304. this->LogBold("Generating MOC source " + mocJob.BuildFileRel);
  1305. this->LogInfo(cmQtAutoGen::MOC, generateReason);
  1306. }
  1307. // Make sure the parent directory exists
  1308. if (this->MakeParentDirectory(cmQtAutoGen::MOC, mocFileAbs)) {
  1309. // Compose moc command
  1310. std::vector<std::string> cmd;
  1311. cmd.push_back(this->MocExecutable);
  1312. // Add options
  1313. cmd.insert(cmd.end(), this->MocAllOptions.begin(),
  1314. this->MocAllOptions.end());
  1315. // Add predefs include
  1316. if (!this->MocPredefsFileAbs.empty()) {
  1317. cmd.push_back("--include");
  1318. cmd.push_back(this->MocPredefsFileAbs);
  1319. }
  1320. cmd.push_back("-o");
  1321. cmd.push_back(mocFileAbs);
  1322. cmd.push_back(mocJob.SourceFile);
  1323. // Execute moc command
  1324. std::string output;
  1325. if (this->RunCommand(cmd, output)) {
  1326. // Success
  1327. if (generated != nullptr) {
  1328. *generated = true;
  1329. }
  1330. } else {
  1331. // Moc command failed
  1332. {
  1333. std::string emsg = "moc failed for\n ";
  1334. emsg += cmQtAutoGen::Quoted(mocJob.SourceFile);
  1335. this->LogCommandError(cmQtAutoGen::MOC, emsg, cmd, output);
  1336. }
  1337. cmSystemTools::RemoveFile(mocFileAbs);
  1338. success = false;
  1339. }
  1340. } else {
  1341. // Parent directory creation failed
  1342. success = false;
  1343. }
  1344. }
  1345. return success;
  1346. }
  1347. /**
  1348. * @brief Tests if the file name is in the skip list
  1349. */
  1350. bool cmQtAutoGeneratorMocUic::UicSkip(std::string const& absFilename) const
  1351. {
  1352. if (this->UicEnabled()) {
  1353. // Test if the file name is on the skip list
  1354. if (!ListContains(this->UicSkipList, absFilename)) {
  1355. return false;
  1356. }
  1357. }
  1358. return true;
  1359. }
  1360. bool cmQtAutoGeneratorMocUic::UicParseContent(std::string const& absFilename,
  1361. std::string const& contentText)
  1362. {
  1363. if (this->GetVerbose()) {
  1364. this->LogInfo(cmQtAutoGen::UIC, "Checking: " + absFilename);
  1365. }
  1366. std::vector<std::string> includes;
  1367. // Extracte includes
  1368. {
  1369. const char* contentChars = contentText.c_str();
  1370. if (strstr(contentChars, "ui_") != nullptr) {
  1371. while (this->UicRegExpInclude.find(contentChars)) {
  1372. includes.push_back(this->UicRegExpInclude.match(1));
  1373. contentChars += this->UicRegExpInclude.end();
  1374. }
  1375. }
  1376. }
  1377. for (std::string const& includeString : includes) {
  1378. std::string uiInputFile;
  1379. if (!UicFindIncludedFile(uiInputFile, absFilename, includeString)) {
  1380. return false;
  1381. }
  1382. // Check if this file should be skipped
  1383. if (this->UicSkip(uiInputFile)) {
  1384. continue;
  1385. }
  1386. // Check if the job already exists
  1387. bool jobExists = false;
  1388. for (const auto& job : this->UicJobs) {
  1389. if ((job->SourceFile == uiInputFile) &&
  1390. (job->IncludeString == includeString)) {
  1391. jobExists = true;
  1392. break;
  1393. }
  1394. }
  1395. if (!jobExists) {
  1396. auto job = cm::make_unique<UicJob>();
  1397. job->SourceFile = uiInputFile;
  1398. job->BuildFileRel = this->AutogenIncludeDir;
  1399. job->BuildFileRel += includeString;
  1400. job->Includer = absFilename;
  1401. job->IncludeString = includeString;
  1402. this->UicJobs.push_back(std::move(job));
  1403. }
  1404. }
  1405. return true;
  1406. }
  1407. bool cmQtAutoGeneratorMocUic::UicFindIncludedFile(
  1408. std::string& absFile, std::string const& sourceFile,
  1409. std::string const& includeString)
  1410. {
  1411. bool success = false;
  1412. std::string searchFile =
  1413. cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3);
  1414. searchFile += ".ui";
  1415. // Collect search paths list
  1416. std::vector<std::string> testFiles;
  1417. {
  1418. std::string const searchPath = SubDirPrefix(includeString);
  1419. std::string searchFileFull;
  1420. if (!searchPath.empty()) {
  1421. searchFileFull = searchPath;
  1422. searchFileFull += searchFile;
  1423. }
  1424. // Vicinity of the source
  1425. {
  1426. std::string const sourcePath = SubDirPrefix(sourceFile);
  1427. testFiles.push_back(sourcePath + searchFile);
  1428. if (!searchPath.empty()) {
  1429. testFiles.push_back(sourcePath + searchFileFull);
  1430. }
  1431. }
  1432. // AUTOUIC search paths
  1433. if (!this->UicSearchPaths.empty()) {
  1434. for (std::string const& sPath : this->UicSearchPaths) {
  1435. testFiles.push_back((sPath + "/").append(searchFile));
  1436. }
  1437. if (!searchPath.empty()) {
  1438. for (std::string const& sPath : this->UicSearchPaths) {
  1439. testFiles.push_back((sPath + "/").append(searchFileFull));
  1440. }
  1441. }
  1442. }
  1443. }
  1444. // Search for the .ui file!
  1445. for (std::string const& testFile : testFiles) {
  1446. if (cmSystemTools::FileExists(testFile.c_str())) {
  1447. absFile = cmSystemTools::GetRealPath(testFile);
  1448. success = true;
  1449. break;
  1450. }
  1451. }
  1452. // Log error
  1453. if (!success) {
  1454. std::string emsg = "Could not find ";
  1455. emsg += cmQtAutoGen::Quoted(searchFile);
  1456. emsg += " in\n";
  1457. for (std::string const& testFile : testFiles) {
  1458. emsg += " ";
  1459. emsg += cmQtAutoGen::Quoted(testFile);
  1460. emsg += "\n";
  1461. }
  1462. this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg);
  1463. }
  1464. return success;
  1465. }
  1466. bool cmQtAutoGeneratorMocUic::UicGenerateAll()
  1467. {
  1468. if (!this->UicEnabled()) {
  1469. return true;
  1470. }
  1471. // Look for name collisions in included uic files
  1472. {
  1473. bool collision = false;
  1474. std::map<std::string, std::vector<UicJob const*>> collisions;
  1475. for (auto const& job : this->UicJobs) {
  1476. auto& list = collisions[job->IncludeString];
  1477. if (!list.empty()) {
  1478. collision = true;
  1479. }
  1480. list.push_back(job.get());
  1481. }
  1482. if (collision) {
  1483. std::string emsg =
  1484. "Included uic files with the same name will be "
  1485. "generated from different sources.\n"
  1486. "Consider to\n"
  1487. " - add a directory prefix to a \"ui_<NAME>.h\" include "
  1488. "(e.g \"sub/ui_<NAME>.h\")\n"
  1489. " - rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
  1490. "include(s)\n"
  1491. "Include conflicts\n"
  1492. "-----------------\n";
  1493. const auto& colls = collisions;
  1494. for (auto const& coll : colls) {
  1495. if (coll.second.size() > 1) {
  1496. emsg += cmQtAutoGen::Quoted(coll.first);
  1497. emsg += " included in\n";
  1498. for (const UicJob* job : coll.second) {
  1499. emsg += " - ";
  1500. emsg += cmQtAutoGen::Quoted(job->Includer);
  1501. emsg += "\n";
  1502. }
  1503. emsg += "would be generated from\n";
  1504. for (const UicJob* job : coll.second) {
  1505. emsg += " - ";
  1506. emsg += cmQtAutoGen::Quoted(job->SourceFile);
  1507. emsg += "\n";
  1508. }
  1509. }
  1510. }
  1511. this->LogError(cmQtAutoGen::UIC, emsg);
  1512. return false;
  1513. }
  1514. }
  1515. // Generate ui header files
  1516. for (const auto& item : this->UicJobs) {
  1517. if (!this->UicGenerateFile(*item)) {
  1518. return false;
  1519. }
  1520. }
  1521. return true;
  1522. }
  1523. /**
  1524. * @return True on success
  1525. */
  1526. bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob)
  1527. {
  1528. bool success = true;
  1529. std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath(
  1530. this->AutogenBuildDir, uicJob.BuildFileRel);
  1531. bool generate = false;
  1532. std::string generateReason;
  1533. if (!generate && !cmSystemTools::FileExists(uicFileAbs.c_str())) {
  1534. if (this->GetVerbose()) {
  1535. generateReason = "Generating ";
  1536. generateReason += cmQtAutoGen::Quoted(uicFileAbs);
  1537. generateReason += " from its source file ";
  1538. generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
  1539. generateReason += " because it doesn't exist";
  1540. }
  1541. generate = true;
  1542. }
  1543. if (!generate && this->UicSettingsChanged) {
  1544. if (this->GetVerbose()) {
  1545. generateReason = "Generating ";
  1546. generateReason += cmQtAutoGen::Quoted(uicFileAbs);
  1547. generateReason += " from ";
  1548. generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
  1549. generateReason += " because the UIC settings changed";
  1550. }
  1551. generate = true;
  1552. }
  1553. if (!generate) {
  1554. std::string error;
  1555. if (FileIsOlderThan(uicFileAbs, uicJob.SourceFile, &error)) {
  1556. if (this->GetVerbose()) {
  1557. generateReason = "Generating ";
  1558. generateReason += cmQtAutoGen::Quoted(uicFileAbs);
  1559. generateReason += " because it's older than its source file ";
  1560. generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
  1561. }
  1562. generate = true;
  1563. } else {
  1564. if (!error.empty()) {
  1565. this->LogError(cmQtAutoGen::UIC, error);
  1566. success = false;
  1567. }
  1568. }
  1569. }
  1570. if (generate) {
  1571. // Log
  1572. if (this->GetVerbose()) {
  1573. this->LogBold("Generating UIC header " + uicJob.BuildFileRel);
  1574. this->LogInfo(cmQtAutoGen::UIC, generateReason);
  1575. }
  1576. // Make sure the parent directory exists
  1577. if (this->MakeParentDirectory(cmQtAutoGen::UIC, uicFileAbs)) {
  1578. // Compose uic command
  1579. std::vector<std::string> cmd;
  1580. cmd.push_back(this->UicExecutable);
  1581. {
  1582. std::vector<std::string> allOpts = this->UicTargetOptions;
  1583. auto optionIt = this->UicOptions.find(uicJob.SourceFile);
  1584. if (optionIt != this->UicOptions.end()) {
  1585. cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second,
  1586. (this->QtVersionMajor == 5));
  1587. }
  1588. cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
  1589. }
  1590. cmd.push_back("-o");
  1591. cmd.push_back(uicFileAbs);
  1592. cmd.push_back(uicJob.SourceFile);
  1593. std::string output;
  1594. if (this->RunCommand(cmd, output)) {
  1595. // Success
  1596. } else {
  1597. // Command failed
  1598. {
  1599. std::string emsg = "uic failed for\n ";
  1600. emsg += cmQtAutoGen::Quoted(uicJob.SourceFile);
  1601. emsg += "\nincluded by\n ";
  1602. emsg += cmQtAutoGen::Quoted(uicJob.Includer);
  1603. this->LogCommandError(cmQtAutoGen::UIC, emsg, cmd, output);
  1604. }
  1605. cmSystemTools::RemoveFile(uicFileAbs);
  1606. success = false;
  1607. }
  1608. } else {
  1609. // Parent directory creation failed
  1610. success = false;
  1611. }
  1612. }
  1613. return success;
  1614. }
  1615. /**
  1616. * @brief Tries to find the header file to the given file base path by
  1617. * appending different header extensions
  1618. * @return True on success
  1619. */
  1620. bool cmQtAutoGeneratorMocUic::FindHeader(std::string& header,
  1621. std::string const& testBasePath) const
  1622. {
  1623. for (std::string const& ext : this->HeaderExtensions) {
  1624. std::string testFilePath(testBasePath);
  1625. testFilePath.push_back('.');
  1626. testFilePath += ext;
  1627. if (cmSystemTools::FileExists(testFilePath.c_str())) {
  1628. header = testFilePath;
  1629. return true;
  1630. }
  1631. }
  1632. return false;
  1633. }