cmQtAutoMocUic.cxx 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194
  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 "cmQtAutoMocUic.h"
  4. #include <algorithm>
  5. #include <array>
  6. #include <list>
  7. #include <set>
  8. #include <sstream>
  9. #include <utility>
  10. #include "cm_memory.hxx"
  11. #include "cmAlgorithms.h"
  12. #include "cmCryptoHash.h"
  13. #include "cmGeneratedFileStream.h"
  14. #include "cmMakefile.h"
  15. #include "cmQtAutoGen.h"
  16. #include "cmSystemTools.h"
  17. #include "cmake.h"
  18. #include "cmsys/FStream.hxx"
  19. #if defined(__APPLE__)
  20. # include <unistd.h>
  21. #endif
  22. static constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_"
  23. static constexpr std::size_t UiUnderscoreLength = 3; // Length of "ui_"
  24. cmQtAutoMocUic::IncludeKeyT::IncludeKeyT(std::string const& key,
  25. std::size_t basePrefixLength)
  26. : Key(key)
  27. , Dir(SubDirPrefix(key))
  28. , Base(cmSystemTools::GetFilenameWithoutLastExtension(key))
  29. {
  30. if (basePrefixLength != 0) {
  31. Base = Base.substr(basePrefixLength);
  32. }
  33. }
  34. void cmQtAutoMocUic::ParseCacheT::FileT::Clear()
  35. {
  36. Moc.Macro.clear();
  37. Moc.Include.Underscore.clear();
  38. Moc.Include.Dot.clear();
  39. Moc.Depends.clear();
  40. Uic.Include.clear();
  41. Uic.Depends.clear();
  42. }
  43. cmQtAutoMocUic::ParseCacheT::FileHandleT cmQtAutoMocUic::ParseCacheT::Get(
  44. std::string const& fileName) const
  45. {
  46. auto it = Map_.find(fileName);
  47. if (it != Map_.end()) {
  48. return it->second;
  49. }
  50. return FileHandleT();
  51. }
  52. cmQtAutoMocUic::ParseCacheT::GetOrInsertT
  53. cmQtAutoMocUic::ParseCacheT::GetOrInsert(std::string const& fileName)
  54. {
  55. // Find existing entry
  56. {
  57. auto it = Map_.find(fileName);
  58. if (it != Map_.end()) {
  59. return GetOrInsertT{ it->second, false };
  60. }
  61. }
  62. // Insert new entry
  63. return GetOrInsertT{
  64. Map_.emplace(fileName, std::make_shared<FileT>()).first->second, true
  65. };
  66. }
  67. cmQtAutoMocUic::ParseCacheT::ParseCacheT() = default;
  68. cmQtAutoMocUic::ParseCacheT::~ParseCacheT() = default;
  69. void cmQtAutoMocUic::ParseCacheT::Clear()
  70. {
  71. Map_.clear();
  72. }
  73. bool cmQtAutoMocUic::ParseCacheT::ReadFromFile(std::string const& fileName)
  74. {
  75. cmsys::ifstream fin(fileName.c_str());
  76. if (!fin) {
  77. return false;
  78. }
  79. FileHandleT fileHandle;
  80. std::string line;
  81. while (std::getline(fin, line)) {
  82. // Check if this an empty or a comment line
  83. if (line.empty() || line.front() == '#') {
  84. continue;
  85. }
  86. // Drop carriage return character at the end
  87. if (line.back() == '\r') {
  88. line.pop_back();
  89. if (line.empty()) {
  90. continue;
  91. }
  92. }
  93. // Check if this a file name line
  94. if (line.front() != ' ') {
  95. fileHandle = GetOrInsert(line).first;
  96. continue;
  97. }
  98. // Bad line or bad file handle
  99. if (!fileHandle || (line.size() < 6)) {
  100. continue;
  101. }
  102. constexpr std::size_t offset = 5;
  103. if (cmHasLiteralPrefix(line, " mmc:")) {
  104. fileHandle->Moc.Macro = line.substr(offset);
  105. continue;
  106. }
  107. if (cmHasLiteralPrefix(line, " miu:")) {
  108. fileHandle->Moc.Include.Underscore.emplace_back(line.substr(offset),
  109. MocUnderscoreLength);
  110. continue;
  111. }
  112. if (cmHasLiteralPrefix(line, " mid:")) {
  113. fileHandle->Moc.Include.Dot.emplace_back(line.substr(offset), 0);
  114. continue;
  115. }
  116. if (cmHasLiteralPrefix(line, " mdp:")) {
  117. fileHandle->Moc.Depends.emplace_back(line.substr(offset));
  118. continue;
  119. }
  120. if (cmHasLiteralPrefix(line, " uic:")) {
  121. fileHandle->Uic.Include.emplace_back(line.substr(offset),
  122. UiUnderscoreLength);
  123. continue;
  124. }
  125. if (cmHasLiteralPrefix(line, " udp:")) {
  126. fileHandle->Uic.Depends.emplace_back(line.substr(offset));
  127. continue;
  128. }
  129. }
  130. return true;
  131. }
  132. bool cmQtAutoMocUic::ParseCacheT::WriteToFile(std::string const& fileName)
  133. {
  134. cmGeneratedFileStream ofs(fileName);
  135. if (!ofs) {
  136. return false;
  137. }
  138. ofs << "# Generated by CMake. Changes will be overwritten." << std::endl;
  139. for (auto const& pair : Map_) {
  140. ofs << pair.first << std::endl;
  141. FileT const& file = *pair.second;
  142. if (!file.Moc.Macro.empty()) {
  143. ofs << " mmc:" << file.Moc.Macro << std::endl;
  144. }
  145. for (IncludeKeyT const& item : file.Moc.Include.Underscore) {
  146. ofs << " miu:" << item.Key << std::endl;
  147. }
  148. for (IncludeKeyT const& item : file.Moc.Include.Dot) {
  149. ofs << " mid:" << item.Key << std::endl;
  150. }
  151. for (std::string const& item : file.Moc.Depends) {
  152. ofs << " mdp:" << item << std::endl;
  153. }
  154. for (IncludeKeyT const& item : file.Uic.Include) {
  155. ofs << " uic:" << item.Key << std::endl;
  156. }
  157. for (std::string const& item : file.Uic.Depends) {
  158. ofs << " udp:" << item << std::endl;
  159. }
  160. }
  161. return ofs.Close();
  162. }
  163. cmQtAutoMocUic::BaseSettingsT::BaseSettingsT() = default;
  164. cmQtAutoMocUic::BaseSettingsT::~BaseSettingsT() = default;
  165. cmQtAutoMocUic::MocSettingsT::MocSettingsT()
  166. {
  167. RegExpInclude.compile(
  168. "(^|\n)[ \t]*#[ \t]*include[ \t]+"
  169. "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
  170. }
  171. cmQtAutoMocUic::MocSettingsT::~MocSettingsT() = default;
  172. bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const
  173. {
  174. return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
  175. }
  176. std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const
  177. {
  178. std::string res;
  179. const auto itB = MacroFilters.cbegin();
  180. const auto itE = MacroFilters.cend();
  181. const auto itL = itE - 1;
  182. auto itC = itB;
  183. for (; itC != itE; ++itC) {
  184. // Separator
  185. if (itC != itB) {
  186. if (itC != itL) {
  187. res += ", ";
  188. } else {
  189. res += " or ";
  190. }
  191. }
  192. // Key
  193. res += itC->Key;
  194. }
  195. return res;
  196. }
  197. cmQtAutoMocUic::UicSettingsT::UicSettingsT()
  198. {
  199. RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
  200. "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
  201. }
  202. cmQtAutoMocUic::UicSettingsT::~UicSettingsT() = default;
  203. bool cmQtAutoMocUic::UicSettingsT::skipped(std::string const& fileName) const
  204. {
  205. return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
  206. }
  207. void cmQtAutoMocUic::JobT::LogError(GenT genType,
  208. std::string const& message) const
  209. {
  210. Gen()->AbortError();
  211. Gen()->Log().Error(genType, message);
  212. }
  213. void cmQtAutoMocUic::JobT::LogFileError(GenT genType,
  214. std::string const& filename,
  215. std::string const& message) const
  216. {
  217. Gen()->AbortError();
  218. Gen()->Log().ErrorFile(genType, filename, message);
  219. }
  220. void cmQtAutoMocUic::JobT::LogCommandError(
  221. GenT genType, std::string const& message,
  222. std::vector<std::string> const& command, std::string const& output) const
  223. {
  224. Gen()->AbortError();
  225. Gen()->Log().ErrorCommand(genType, message, command, output);
  226. }
  227. bool cmQtAutoMocUic::JobT::RunProcess(GenT genType,
  228. cmWorkerPool::ProcessResultT& result,
  229. std::vector<std::string> const& command,
  230. std::string* infoMessage)
  231. {
  232. // Log command
  233. if (Log().Verbose()) {
  234. std::string msg;
  235. if ((infoMessage != nullptr) && !infoMessage->empty()) {
  236. msg = *infoMessage;
  237. if (msg.back() != '\n') {
  238. msg += '\n';
  239. }
  240. }
  241. msg += QuotedCommand(command);
  242. msg += '\n';
  243. Log().Info(genType, msg);
  244. }
  245. return cmWorkerPool::JobT::RunProcess(result, command,
  246. BaseConst().AutogenBuildDir);
  247. }
  248. void cmQtAutoMocUic::JobMocPredefsT::Process()
  249. {
  250. // (Re)generate moc_predefs.h on demand
  251. std::unique_ptr<std::string> reason;
  252. if (Log().Verbose()) {
  253. reason = cm::make_unique<std::string>();
  254. }
  255. if (!Update(reason.get())) {
  256. return;
  257. }
  258. std::string const& predefsFileRel = MocConst().PredefsFileRel;
  259. std::string const& predefsFileAbs = MocConst().PredefsFileAbs;
  260. {
  261. cmWorkerPool::ProcessResultT result;
  262. {
  263. // Compose command
  264. std::vector<std::string> cmd = MocConst().PredefsCmd;
  265. // Add includes
  266. cmAppend(cmd, MocConst().Includes);
  267. // Add definitions
  268. for (std::string const& def : MocConst().Definitions) {
  269. cmd.emplace_back("-D" + def);
  270. }
  271. // Execute command
  272. if (!RunProcess(GenT::MOC, result, cmd, reason.get())) {
  273. std::string msg = "The content generation command for ";
  274. msg += Quoted(predefsFileRel);
  275. msg += " failed.\n";
  276. msg += result.ErrorMessage;
  277. LogCommandError(GenT::MOC, msg, cmd, result.StdOut);
  278. return;
  279. }
  280. }
  281. // (Re)write predefs file only on demand
  282. if (cmQtAutoGenerator::FileDiffers(predefsFileAbs, result.StdOut)) {
  283. if (!cmQtAutoGenerator::FileWrite(predefsFileAbs, result.StdOut)) {
  284. std::string msg = "Writing ";
  285. msg += Quoted(predefsFileRel);
  286. msg += " failed.";
  287. LogFileError(GenT::MOC, predefsFileAbs, msg);
  288. return;
  289. }
  290. } else {
  291. // Touch to update the time stamp
  292. if (Log().Verbose()) {
  293. Log().Info(GenT::MOC, "Touching " + Quoted(predefsFileRel));
  294. }
  295. if (!cmSystemTools::Touch(predefsFileAbs, false)) {
  296. std::string msg = "Touching ";
  297. msg += Quoted(predefsFileAbs);
  298. msg += " failed.";
  299. LogFileError(GenT::MOC, predefsFileAbs, msg);
  300. return;
  301. }
  302. }
  303. }
  304. // Read file time afterwards
  305. if (!MocEval().PredefsTime.Load(predefsFileAbs)) {
  306. LogFileError(GenT::MOC, predefsFileAbs, "File time reading failed.");
  307. return;
  308. }
  309. }
  310. bool cmQtAutoMocUic::JobMocPredefsT::Update(std::string* reason) const
  311. {
  312. // Test if the file exists
  313. if (!MocEval().PredefsTime.Load(MocConst().PredefsFileAbs)) {
  314. if (reason != nullptr) {
  315. *reason = "Generating ";
  316. *reason += Quoted(MocConst().PredefsFileRel);
  317. *reason += ", because it doesn't exist.";
  318. }
  319. return true;
  320. }
  321. // Test if the settings changed
  322. if (MocConst().SettingsChanged) {
  323. if (reason != nullptr) {
  324. *reason = "Generating ";
  325. *reason += Quoted(MocConst().PredefsFileRel);
  326. *reason += ", because the moc settings changed.";
  327. }
  328. return true;
  329. }
  330. // Test if the executable is newer
  331. {
  332. std::string const& exec = MocConst().PredefsCmd.at(0);
  333. cmFileTime execTime;
  334. if (execTime.Load(exec)) {
  335. if (MocEval().PredefsTime.Older(execTime)) {
  336. if (reason != nullptr) {
  337. *reason = "Generating ";
  338. *reason += Quoted(MocConst().PredefsFileRel);
  339. *reason += " because it is older than ";
  340. *reason += Quoted(exec);
  341. *reason += ".";
  342. }
  343. return true;
  344. }
  345. }
  346. }
  347. return false;
  348. }
  349. bool cmQtAutoMocUic::JobParseT::ReadFile()
  350. {
  351. // Clear old parse information
  352. FileHandle->ParseData->Clear();
  353. std::string const& fileName = FileHandle->FileName;
  354. // Write info
  355. if (Log().Verbose()) {
  356. Log().Info(GenT::GEN, "Parsing " + Quoted(fileName));
  357. }
  358. // Read file content
  359. {
  360. std::string error;
  361. if (!cmQtAutoGenerator::FileRead(Content, fileName, &error)) {
  362. LogFileError(GenT::GEN, fileName, "Could not read the file: " + error);
  363. return false;
  364. }
  365. }
  366. // Warn if empty
  367. if (Content.empty()) {
  368. Log().WarningFile(GenT::GEN, fileName, "The file is empty.");
  369. return false;
  370. }
  371. return true;
  372. }
  373. void cmQtAutoMocUic::JobParseT::CreateKeys(std::vector<IncludeKeyT>& container,
  374. std::set<std::string> const& source,
  375. std::size_t basePrefixLength)
  376. {
  377. if (source.empty()) {
  378. return;
  379. }
  380. container.reserve(source.size());
  381. for (std::string const& src : source) {
  382. container.emplace_back(src, basePrefixLength);
  383. }
  384. }
  385. void cmQtAutoMocUic::JobParseT::MocMacro()
  386. {
  387. for (KeyExpT const& filter : MocConst().MacroFilters) {
  388. // Run a simple find string check
  389. if (Content.find(filter.Key) == std::string::npos) {
  390. continue;
  391. }
  392. // Run the expensive regular expression check loop
  393. cmsys::RegularExpressionMatch match;
  394. if (filter.Exp.find(Content.c_str(), match)) {
  395. // Keep detected macro name
  396. FileHandle->ParseData->Moc.Macro = filter.Key;
  397. return;
  398. }
  399. }
  400. }
  401. void cmQtAutoMocUic::JobParseT::MocDependecies()
  402. {
  403. if (MocConst().DependFilters.empty()) {
  404. return;
  405. }
  406. // Find dependency strings
  407. std::set<std::string> parseDepends;
  408. for (KeyExpT const& filter : MocConst().DependFilters) {
  409. // Run a simple find string check
  410. if (Content.find(filter.Key) == std::string::npos) {
  411. continue;
  412. }
  413. // Run the expensive regular expression check loop
  414. const char* contentChars = Content.c_str();
  415. cmsys::RegularExpressionMatch match;
  416. while (filter.Exp.find(contentChars, match)) {
  417. {
  418. std::string dep = match.match(1);
  419. if (!dep.empty()) {
  420. parseDepends.emplace(std::move(dep));
  421. }
  422. }
  423. contentChars += match.end();
  424. }
  425. }
  426. // Store dependency strings
  427. {
  428. auto& Depends = FileHandle->ParseData->Moc.Depends;
  429. Depends.reserve(parseDepends.size());
  430. for (std::string const& item : parseDepends) {
  431. Depends.emplace_back(item);
  432. // Replace end of line characters in filenames
  433. std::string& path = Depends.back();
  434. std::replace(path.begin(), path.end(), '\n', ' ');
  435. std::replace(path.begin(), path.end(), '\r', ' ');
  436. }
  437. }
  438. }
  439. void cmQtAutoMocUic::JobParseT::MocIncludes()
  440. {
  441. if (Content.find("moc") == std::string::npos) {
  442. return;
  443. }
  444. std::set<std::string> underscore;
  445. std::set<std::string> dot;
  446. {
  447. const char* contentChars = Content.c_str();
  448. cmsys::RegularExpression const& regExp = MocConst().RegExpInclude;
  449. cmsys::RegularExpressionMatch match;
  450. while (regExp.find(contentChars, match)) {
  451. std::string incString = match.match(2);
  452. std::string const incBase =
  453. cmSystemTools::GetFilenameWithoutLastExtension(incString);
  454. if (cmHasLiteralPrefix(incBase, "moc_")) {
  455. // moc_<BASE>.cpp
  456. // Remove the moc_ part from the base name
  457. underscore.emplace(std::move(incString));
  458. } else {
  459. // <BASE>.moc
  460. dot.emplace(std::move(incString));
  461. }
  462. // Forward content pointer
  463. contentChars += match.end();
  464. }
  465. }
  466. auto& Include = FileHandle->ParseData->Moc.Include;
  467. CreateKeys(Include.Underscore, underscore, MocUnderscoreLength);
  468. CreateKeys(Include.Dot, dot, 0);
  469. }
  470. void cmQtAutoMocUic::JobParseT::UicIncludes()
  471. {
  472. if (Content.find("ui_") == std::string::npos) {
  473. return;
  474. }
  475. std::set<std::string> includes;
  476. {
  477. const char* contentChars = Content.c_str();
  478. cmsys::RegularExpression const& regExp = UicConst().RegExpInclude;
  479. cmsys::RegularExpressionMatch match;
  480. while (regExp.find(contentChars, match)) {
  481. includes.emplace(match.match(2));
  482. // Forward content pointer
  483. contentChars += match.end();
  484. }
  485. }
  486. CreateKeys(FileHandle->ParseData->Uic.Include, includes, UiUnderscoreLength);
  487. }
  488. void cmQtAutoMocUic::JobParseHeaderT::Process()
  489. {
  490. if (!ReadFile()) {
  491. return;
  492. }
  493. // Moc parsing
  494. if (FileHandle->Moc) {
  495. MocMacro();
  496. MocDependecies();
  497. }
  498. // Uic parsing
  499. if (FileHandle->Uic) {
  500. UicIncludes();
  501. }
  502. }
  503. void cmQtAutoMocUic::JobParseSourceT::Process()
  504. {
  505. if (!ReadFile()) {
  506. return;
  507. }
  508. // Moc parsing
  509. if (FileHandle->Moc) {
  510. MocMacro();
  511. MocDependecies();
  512. MocIncludes();
  513. }
  514. // Uic parsing
  515. if (FileHandle->Uic) {
  516. UicIncludes();
  517. }
  518. }
  519. void cmQtAutoMocUic::JobEvaluateT::Process()
  520. {
  521. // Evaluate for moc
  522. if (MocConst().Enabled) {
  523. // Evaluate headers
  524. for (auto const& pair : BaseEval().Headers) {
  525. if (!MocEvalHeader(pair.second)) {
  526. return;
  527. }
  528. }
  529. // Evaluate sources
  530. for (auto const& pair : BaseEval().Sources) {
  531. if (!MocEvalSource(pair.second)) {
  532. return;
  533. }
  534. }
  535. }
  536. // Evaluate for uic
  537. if (UicConst().Enabled) {
  538. if (!UicEval(BaseEval().Headers) || !UicEval(BaseEval().Sources)) {
  539. return;
  540. }
  541. }
  542. // Add discovered header parse jobs
  543. Gen()->CreateParseJobs<JobParseHeaderT>(MocEval().HeadersDiscovered);
  544. // Add generate job after
  545. Gen()->WorkerPool().EmplaceJob<JobGenerateT>();
  546. }
  547. bool cmQtAutoMocUic::JobEvaluateT::MocEvalHeader(SourceFileHandleT source)
  548. {
  549. SourceFileT const& sourceFile = *source;
  550. auto const& parseData = sourceFile.ParseData->Moc;
  551. if (!source->Moc) {
  552. return true;
  553. }
  554. if (!parseData.Macro.empty()) {
  555. // Create a new mapping
  556. MappingHandleT handle = std::make_shared<MappingT>();
  557. handle->SourceFile = std::move(source);
  558. // Absolute build path
  559. if (BaseConst().MultiConfig) {
  560. handle->OutputFile = Gen()->AbsoluteIncludePath(sourceFile.BuildPath);
  561. } else {
  562. handle->OutputFile = Gen()->AbsoluteBuildPath(sourceFile.BuildPath);
  563. }
  564. // Register mapping in headers map
  565. MocRegisterMapping(handle, true);
  566. }
  567. return true;
  568. }
  569. bool cmQtAutoMocUic::JobEvaluateT::MocEvalSource(
  570. SourceFileHandleT const& source)
  571. {
  572. SourceFileT const& sourceFile = *source;
  573. auto const& parseData = sourceFile.ParseData->Moc;
  574. if (!sourceFile.Moc ||
  575. (parseData.Macro.empty() && parseData.Include.Underscore.empty() &&
  576. parseData.Include.Dot.empty())) {
  577. return true;
  578. }
  579. std::string const sourceDir = SubDirPrefix(sourceFile.FileName);
  580. std::string const sourceBase =
  581. cmSystemTools::GetFilenameWithoutLastExtension(sourceFile.FileName);
  582. // For relaxed mode check if the own "moc_" or ".moc" file is included
  583. bool const relaxedMode = MocConst().RelaxedMode;
  584. bool sourceIncludesMocUnderscore = false;
  585. bool sourceIncludesDotMoc = false;
  586. // Check if the sources own "moc_" or ".moc" file is included
  587. if (relaxedMode) {
  588. for (IncludeKeyT const& incKey : parseData.Include.Underscore) {
  589. if (incKey.Base == sourceBase) {
  590. sourceIncludesMocUnderscore = true;
  591. break;
  592. }
  593. }
  594. }
  595. for (IncludeKeyT const& incKey : parseData.Include.Dot) {
  596. if (incKey.Base == sourceBase) {
  597. sourceIncludesDotMoc = true;
  598. break;
  599. }
  600. }
  601. // Check if this source needs to be moc processed but doesn't.
  602. if (!sourceIncludesDotMoc && !parseData.Macro.empty() &&
  603. !(relaxedMode && sourceIncludesMocUnderscore)) {
  604. {
  605. std::string emsg = "The file contains a ";
  606. emsg += Quoted(parseData.Macro);
  607. emsg += " macro, but does not include ";
  608. emsg += Quoted(sourceBase + ".moc");
  609. emsg += "!\nConsider to\n - add #include \"";
  610. emsg += sourceBase;
  611. emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
  612. LogFileError(GenT::MOC, sourceFile.FileName, emsg);
  613. }
  614. return false;
  615. }
  616. // Evaluate "moc_" includes
  617. for (IncludeKeyT const& incKey : parseData.Include.Underscore) {
  618. std::string const headerBase = incKey.Dir + incKey.Base;
  619. SourceFileHandleT header = MocFindIncludedHeader(sourceDir, headerBase);
  620. if (!header) {
  621. {
  622. std::string msg = "The file includes the moc file ";
  623. msg += Quoted(incKey.Key);
  624. msg += ",\nbut the header could not be found "
  625. "in the following locations\n";
  626. msg += MocMessageTestHeaders(headerBase);
  627. LogFileError(GenT::MOC, sourceFile.FileName, msg);
  628. }
  629. return false;
  630. }
  631. // The include might be handled differently in relaxed mode
  632. if (relaxedMode && !sourceIncludesDotMoc && !parseData.Macro.empty() &&
  633. (incKey.Base == sourceBase)) {
  634. // The <BASE>.cpp file includes a Qt macro but does not include the
  635. // <BASE>.moc file. In this case, the moc_<BASE>.cpp should probably
  636. // be generated from <BASE>.cpp instead of <BASE>.h, because otherwise
  637. // it won't build. But warn, since this is not how it is supposed to be
  638. // used. This is for KDE4 compatibility.
  639. {
  640. // Issue a warning
  641. std::string msg = "The file contains a ";
  642. msg += Quoted(parseData.Macro);
  643. msg += " macro, but does not include ";
  644. msg += Quoted(sourceBase + ".moc");
  645. msg += ".\nInstead it includes ";
  646. msg += Quoted(incKey.Key);
  647. msg += ".\nRunning moc on the source\n ";
  648. msg += Quoted(sourceFile.FileName);
  649. msg += "!\nBetter include ";
  650. msg += Quoted(sourceBase + ".moc");
  651. msg += " for compatibility with regular mode.\n";
  652. msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n";
  653. Log().WarningFile(GenT::MOC, sourceFile.FileName, msg);
  654. }
  655. // Create mapping
  656. if (!MocRegisterIncluded(incKey.Key, source, source, false)) {
  657. return false;
  658. }
  659. continue;
  660. }
  661. // Check if header is skipped
  662. if (MocConst().skipped(header->FileName)) {
  663. continue;
  664. }
  665. // Create mapping
  666. if (!MocRegisterIncluded(incKey.Key, source, std::move(header), true)) {
  667. return false;
  668. }
  669. }
  670. // Evaluate ".moc" includes
  671. if (relaxedMode) {
  672. // Relaxed mode
  673. for (IncludeKeyT const& incKey : parseData.Include.Dot) {
  674. // Check if this is the sources own .moc file
  675. bool const ownMoc = (incKey.Base == sourceBase);
  676. if (ownMoc && !parseData.Macro.empty()) {
  677. // Create mapping for the regular use case
  678. if (!MocRegisterIncluded(incKey.Key, source, source, false)) {
  679. return false;
  680. }
  681. continue;
  682. }
  683. // Try to find a header instead but issue a warning.
  684. // This is for KDE4 compatibility.
  685. std::string const headerBase = incKey.Dir + incKey.Base;
  686. SourceFileHandleT header = MocFindIncludedHeader(sourceDir, headerBase);
  687. if (!header) {
  688. std::string msg = "The file includes the moc file ";
  689. msg += Quoted(incKey.Key);
  690. msg += ",\nwhich seems to be the moc file from a different source "
  691. "file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a matching header"
  692. "could not be found in the following locations\n";
  693. msg += MocMessageTestHeaders(headerBase);
  694. LogFileError(GenT::MOC, sourceFile.FileName, msg);
  695. return false;
  696. }
  697. // Check if header is skipped
  698. if (MocConst().skipped(header->FileName)) {
  699. continue;
  700. }
  701. // Issue a warning
  702. if (ownMoc && parseData.Macro.empty()) {
  703. std::string msg = "The file includes the moc file ";
  704. msg += Quoted(incKey.Key);
  705. msg += ", but does not contain a\n";
  706. msg += MocConst().MacrosString();
  707. msg += " macro.\nRunning moc on the header\n ";
  708. msg += Quoted(header->FileName);
  709. msg += "!\nBetter include ";
  710. msg += Quoted("moc_" + incKey.Base + ".cpp");
  711. msg += " for a compatibility with regular mode.\n";
  712. msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n";
  713. Log().WarningFile(GenT::MOC, sourceFile.FileName, msg);
  714. } else {
  715. std::string msg = "The file includes the moc file ";
  716. msg += Quoted(incKey.Key);
  717. msg += " instead of ";
  718. msg += Quoted("moc_" + incKey.Base + ".cpp");
  719. msg += ".\nRunning moc on the header\n ";
  720. msg += Quoted(header->FileName);
  721. msg += "!\nBetter include ";
  722. msg += Quoted("moc_" + incKey.Base + ".cpp");
  723. msg += " for compatibility with regular mode.\n";
  724. msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n";
  725. Log().WarningFile(GenT::MOC, sourceFile.FileName, msg);
  726. }
  727. // Create mapping
  728. if (!MocRegisterIncluded(incKey.Key, source, std::move(header), true)) {
  729. return false;
  730. }
  731. }
  732. } else {
  733. // Strict mode
  734. for (IncludeKeyT const& incKey : parseData.Include.Dot) {
  735. // Check if this is the sources own .moc file
  736. bool const ownMoc = (incKey.Base == sourceBase);
  737. if (!ownMoc) {
  738. // Don't allow <BASE>.moc include other than own in regular mode
  739. std::string msg = "The file includes the moc file ";
  740. msg += Quoted(incKey.Key);
  741. msg += ",\nwhich seems to be the moc file from a different "
  742. "source file.\nThis is not supported. Include ";
  743. msg += Quoted(sourceBase + ".moc");
  744. msg += " to run moc on this source file.";
  745. LogFileError(GenT::MOC, sourceFile.FileName, msg);
  746. return false;
  747. }
  748. // Accept but issue a warning if moc isn't required
  749. if (parseData.Macro.empty()) {
  750. std::string msg = "The file includes the moc file ";
  751. msg += Quoted(incKey.Key);
  752. msg += ", but does not contain a ";
  753. msg += MocConst().MacrosString();
  754. msg += " macro.";
  755. Log().WarningFile(GenT::MOC, sourceFile.FileName, msg);
  756. }
  757. // Create mapping
  758. if (!MocRegisterIncluded(incKey.Key, source, source, false)) {
  759. return false;
  760. }
  761. }
  762. }
  763. return true;
  764. }
  765. cmQtAutoMocUic::SourceFileHandleT
  766. cmQtAutoMocUic::JobEvaluateT::MocFindIncludedHeader(
  767. std::string const& includerDir, std::string const& includeBase) const
  768. {
  769. // Search in vicinity of the source
  770. {
  771. SourceFileHandleT res = MocFindHeader(includerDir + includeBase);
  772. if (res) {
  773. return res;
  774. }
  775. }
  776. // Search in include directories
  777. for (std::string const& path : MocConst().IncludePaths) {
  778. std::string testPath = path;
  779. testPath += '/';
  780. testPath += includeBase;
  781. SourceFileHandleT res = MocFindHeader(testPath);
  782. if (res) {
  783. return res;
  784. }
  785. }
  786. // Return without success
  787. return SourceFileHandleT();
  788. }
  789. cmQtAutoMocUic::SourceFileHandleT cmQtAutoMocUic::JobEvaluateT::MocFindHeader(
  790. std::string const& basePath) const
  791. {
  792. std::string testPath;
  793. testPath.reserve(basePath.size() + 8);
  794. for (std::string const& ext : BaseConst().HeaderExtensions) {
  795. testPath.clear();
  796. testPath += basePath;
  797. testPath += '.';
  798. testPath += ext;
  799. cmFileTime fileTime;
  800. if (fileTime.Load(testPath)) {
  801. // Compute real path of the file
  802. testPath = cmSystemTools::GetRealPath(testPath);
  803. // Return a known file if it exists already
  804. {
  805. auto it = BaseEval().Headers.find(testPath);
  806. if (it != BaseEval().Headers.end()) {
  807. return it->second;
  808. }
  809. }
  810. // Created and return discovered file entry
  811. SourceFileHandleT& res = MocEval().HeadersDiscovered[testPath];
  812. if (!res) {
  813. res = std::make_shared<SourceFileT>(testPath);
  814. res->FileTime = fileTime;
  815. res->Moc = true;
  816. }
  817. return res;
  818. }
  819. }
  820. // Return without success
  821. return SourceFileHandleT();
  822. }
  823. std::string cmQtAutoMocUic::JobEvaluateT::MocMessageTestHeaders(
  824. std::string const& fileBase) const
  825. {
  826. std::ostringstream res;
  827. {
  828. std::string exts = ".{";
  829. exts += cmJoin(BaseConst().HeaderExtensions, ",");
  830. exts += '}';
  831. // Compose result string
  832. res << " " << fileBase << exts << '\n';
  833. for (std::string const& path : MocConst().IncludePaths) {
  834. res << " " << path << '/' << fileBase << exts << '\n';
  835. }
  836. }
  837. return res.str();
  838. }
  839. bool cmQtAutoMocUic::JobEvaluateT::MocRegisterIncluded(
  840. std::string const& includeString, SourceFileHandleT includerFileHandle,
  841. SourceFileHandleT sourceFileHandle, bool sourceIsHeader) const
  842. {
  843. // Check if this file is already included
  844. MappingHandleT& handle = MocEval().Includes[includeString];
  845. if (handle) {
  846. // Check if the output file would be generated from different source files
  847. if (handle->SourceFile != sourceFileHandle) {
  848. std::string msg = "The source files\n ";
  849. msg += Quoted(includerFileHandle->FileName);
  850. msg += '\n';
  851. for (auto const& item : handle->IncluderFiles) {
  852. msg += " ";
  853. msg += Quoted(item->FileName);
  854. msg += '\n';
  855. }
  856. msg += "contain the same include string ";
  857. msg += Quoted(includeString);
  858. msg += ", but\nthe moc file would be generated from different "
  859. "source files\n ";
  860. msg += Quoted(sourceFileHandle->FileName);
  861. msg += " and\n ";
  862. msg += Quoted(handle->SourceFile->FileName);
  863. msg += ".\nConsider to\n"
  864. " - not include the \"moc_<NAME>.cpp\" file\n"
  865. " - add a directory prefix to a \"<NAME>.moc\" include "
  866. "(e.g \"sub/<NAME>.moc\")\n"
  867. " - rename the source file(s)\n";
  868. LogError(GenT::MOC, msg);
  869. return false;
  870. }
  871. // The same mapping already exists. Just add to the includers list.
  872. handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
  873. return true;
  874. }
  875. // Create a new mapping
  876. handle = std::make_shared<MappingT>();
  877. handle->IncludeString = includeString;
  878. handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
  879. handle->SourceFile = std::move(sourceFileHandle);
  880. handle->OutputFile += Gen()->AbsoluteIncludePath(includeString);
  881. // Register mapping in sources/headers map
  882. MocRegisterMapping(handle, sourceIsHeader);
  883. return true;
  884. }
  885. void cmQtAutoMocUic::JobEvaluateT::MocRegisterMapping(
  886. MappingHandleT mappingHandle, bool sourceIsHeader) const
  887. {
  888. auto& regMap =
  889. sourceIsHeader ? MocEval().HeaderMappings : MocEval().SourceMappings;
  890. // Check if source file already gets mapped
  891. auto& regHandle = regMap[mappingHandle->SourceFile->FileName];
  892. if (!regHandle) {
  893. // Yet unknown mapping
  894. regHandle = std::move(mappingHandle);
  895. } else {
  896. // Mappings with include string override those without
  897. if (!mappingHandle->IncludeString.empty()) {
  898. regHandle = std::move(mappingHandle);
  899. }
  900. }
  901. }
  902. bool cmQtAutoMocUic::JobEvaluateT::UicEval(SourceFileMapT const& fileMap)
  903. {
  904. for (auto const& pair : fileMap) {
  905. if (!UicEvalFile(pair.second)) {
  906. return false;
  907. }
  908. }
  909. return true;
  910. }
  911. bool cmQtAutoMocUic::JobEvaluateT::UicEvalFile(
  912. SourceFileHandleT sourceFileHandle)
  913. {
  914. SourceFileT const& sourceFile = *sourceFileHandle;
  915. auto const& Include = sourceFile.ParseData->Uic.Include;
  916. if (!sourceFile.Uic || Include.empty()) {
  917. return true;
  918. }
  919. std::string const sourceDir = SubDirPrefix(sourceFile.FileName);
  920. for (IncludeKeyT const& incKey : Include) {
  921. // Find .ui file name
  922. SourceFileHandleT uiFileHandle =
  923. UicFindIncludedUi(sourceFile.FileName, sourceDir, incKey);
  924. if (!uiFileHandle || UicConst().skipped(uiFileHandle->FileName)) {
  925. continue;
  926. }
  927. // Register mapping
  928. if (!UicRegisterMapping(incKey.Key, std::move(uiFileHandle),
  929. std::move(sourceFileHandle))) {
  930. return false;
  931. }
  932. }
  933. return true;
  934. }
  935. bool cmQtAutoMocUic::JobEvaluateT::UicRegisterMapping(
  936. std::string const& includeString, SourceFileHandleT uiFileHandle,
  937. SourceFileHandleT includerFileHandle)
  938. {
  939. auto& Includes = Gen()->UicEval().Includes;
  940. auto it = Includes.find(includeString);
  941. if (it != Includes.end()) {
  942. MappingHandleT const& handle = it->second;
  943. if (handle->SourceFile != uiFileHandle) {
  944. // The output file already gets generated - from a different .ui file!
  945. std::string msg = "The source files\n ";
  946. msg += Quoted(includerFileHandle->FileName);
  947. msg += '\n';
  948. for (auto const& item : handle->IncluderFiles) {
  949. msg += " ";
  950. msg += Quoted(item->FileName);
  951. msg += '\n';
  952. }
  953. msg += "contain the same include string ";
  954. msg += Quoted(includeString);
  955. msg += ", but\nthe uic file would be generated from different "
  956. "user interface files\n ";
  957. msg += Quoted(uiFileHandle->FileName);
  958. msg += " and\n ";
  959. msg += Quoted(handle->SourceFile->FileName);
  960. msg += ".\nConsider to\n"
  961. " - add a directory prefix to a \"ui_<NAME>.h\" include "
  962. "(e.g \"sub/ui_<NAME>.h\")\n"
  963. " - rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
  964. "include(s)\n";
  965. LogError(GenT::UIC, msg);
  966. return false;
  967. }
  968. // Add includer file to existing mapping
  969. handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
  970. } else {
  971. // New mapping handle
  972. MappingHandleT handle = std::make_shared<MappingT>();
  973. handle->IncludeString = includeString;
  974. handle->IncluderFiles.emplace_back(std::move(includerFileHandle));
  975. handle->SourceFile = std::move(uiFileHandle);
  976. handle->OutputFile += Gen()->AbsoluteIncludePath(includeString);
  977. // Register mapping
  978. Includes.emplace(includeString, std::move(handle));
  979. }
  980. return true;
  981. }
  982. cmQtAutoMocUic::SourceFileHandleT
  983. cmQtAutoMocUic::JobEvaluateT::UicFindIncludedUi(
  984. std::string const& sourceFile, std::string const& sourceDir,
  985. IncludeKeyT const& incKey) const
  986. {
  987. std::string searchFileName = incKey.Base;
  988. searchFileName += ".ui";
  989. // Collect search paths list
  990. std::vector<std::string> testFiles;
  991. {
  992. auto& searchPaths = UicConst().SearchPaths;
  993. testFiles.reserve((searchPaths.size() + 1) * 2);
  994. // Vicinity of the source
  995. testFiles.emplace_back(sourceDir + searchFileName);
  996. if (!incKey.Dir.empty()) {
  997. std::string path = sourceDir;
  998. path += incKey.Dir;
  999. path += searchFileName;
  1000. testFiles.emplace_back(path);
  1001. }
  1002. // AUTOUIC search paths
  1003. if (!searchPaths.empty()) {
  1004. for (std::string const& sPath : searchPaths) {
  1005. std::string path = sPath;
  1006. path += '/';
  1007. path += searchFileName;
  1008. testFiles.emplace_back(std::move(path));
  1009. }
  1010. if (!incKey.Dir.empty()) {
  1011. for (std::string const& sPath : searchPaths) {
  1012. std::string path = sPath;
  1013. path += '/';
  1014. path += incKey.Dir;
  1015. path += searchFileName;
  1016. testFiles.emplace_back(std::move(path));
  1017. }
  1018. }
  1019. }
  1020. }
  1021. // Search for the .ui file!
  1022. for (std::string const& testFile : testFiles) {
  1023. cmFileTime fileTime;
  1024. if (fileTime.Load(testFile)) {
  1025. // .ui file found in files system!
  1026. std::string realPath = cmSystemTools::GetRealPath(testFile);
  1027. // Get or create .ui file handle
  1028. SourceFileHandleT& handle = Gen()->UicEval().UiFiles[realPath];
  1029. if (!handle) {
  1030. // The file wasn't registered, yet
  1031. handle = std::make_shared<SourceFileT>(realPath);
  1032. handle->FileTime = fileTime;
  1033. }
  1034. return handle;
  1035. }
  1036. }
  1037. // Log error
  1038. {
  1039. std::string msg = "The file includes the uic file ";
  1040. msg += Quoted(incKey.Key);
  1041. msg += ",\nbut the user interface file ";
  1042. msg += Quoted(searchFileName);
  1043. msg += "\ncould not be found in the following locations\n";
  1044. for (std::string const& testFile : testFiles) {
  1045. msg += " ";
  1046. msg += Quoted(testFile);
  1047. msg += '\n';
  1048. }
  1049. LogFileError(GenT::UIC, sourceFile, msg);
  1050. }
  1051. return SourceFileHandleT();
  1052. }
  1053. void cmQtAutoMocUic::JobGenerateT::Process()
  1054. {
  1055. // Add moc compile jobs
  1056. if (MocConst().Enabled) {
  1057. for (auto const& pair : MocEval().HeaderMappings) {
  1058. // Register if this mapping is a candidate for mocs_compilation.cpp
  1059. bool const compFile = pair.second->IncludeString.empty();
  1060. if (compFile) {
  1061. MocEval().CompFiles.emplace_back(pair.second->SourceFile->BuildPath);
  1062. }
  1063. if (!MocGenerate(pair.second, compFile)) {
  1064. return;
  1065. }
  1066. }
  1067. for (auto const& pair : MocEval().SourceMappings) {
  1068. if (!MocGenerate(pair.second, false)) {
  1069. return;
  1070. }
  1071. }
  1072. // Add mocs compilations job on demand
  1073. Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>();
  1074. }
  1075. // Add uic compile jobs
  1076. if (UicConst().Enabled) {
  1077. for (auto const& pair : Gen()->UicEval().Includes) {
  1078. if (!UicGenerate(pair.second)) {
  1079. return;
  1080. }
  1081. }
  1082. }
  1083. // Add finish job
  1084. Gen()->WorkerPool().EmplaceJob<JobFinishT>();
  1085. }
  1086. bool cmQtAutoMocUic::JobGenerateT::MocGenerate(MappingHandleT const& mapping,
  1087. bool compFile) const
  1088. {
  1089. std::unique_ptr<std::string> reason;
  1090. if (Log().Verbose()) {
  1091. reason = cm::make_unique<std::string>();
  1092. }
  1093. if (MocUpdate(*mapping, reason.get())) {
  1094. // Create the parent directory
  1095. if (!MakeParentDirectory(mapping->OutputFile)) {
  1096. LogFileError(GenT::MOC, mapping->OutputFile,
  1097. "Could not create parent directory.");
  1098. return false;
  1099. }
  1100. // Add moc job
  1101. Gen()->WorkerPool().EmplaceJob<JobMocT>(mapping, std::move(reason));
  1102. // Check if a moc job for a mocs_compilation.cpp entry was generated
  1103. if (compFile) {
  1104. MocEval().CompUpdated = true;
  1105. }
  1106. }
  1107. return true;
  1108. }
  1109. bool cmQtAutoMocUic::JobGenerateT::MocUpdate(MappingT const& mapping,
  1110. std::string* reason) const
  1111. {
  1112. std::string const& sourceFile = mapping.SourceFile->FileName;
  1113. std::string const& outputFile = mapping.OutputFile;
  1114. // Test if the output file exists
  1115. cmFileTime outputFileTime;
  1116. if (!outputFileTime.Load(outputFile)) {
  1117. if (reason != nullptr) {
  1118. *reason = "Generating ";
  1119. *reason += Quoted(outputFile);
  1120. *reason += ", because it doesn't exist, from ";
  1121. *reason += Quoted(sourceFile);
  1122. }
  1123. return true;
  1124. }
  1125. // Test if any setting changed
  1126. if (MocConst().SettingsChanged) {
  1127. if (reason != nullptr) {
  1128. *reason = "Generating ";
  1129. *reason += Quoted(outputFile);
  1130. *reason += ", because the uic settings changed, from ";
  1131. *reason += Quoted(sourceFile);
  1132. }
  1133. return true;
  1134. }
  1135. // Test if the source file is newer
  1136. if (outputFileTime.Older(mapping.SourceFile->FileTime)) {
  1137. if (reason != nullptr) {
  1138. *reason = "Generating ";
  1139. *reason += Quoted(outputFile);
  1140. *reason += ", because it's older than its source file, from ";
  1141. *reason += Quoted(sourceFile);
  1142. }
  1143. return true;
  1144. }
  1145. // Test if the moc_predefs file is newer
  1146. if (!MocConst().PredefsFileAbs.empty()) {
  1147. if (outputFileTime.Older(MocEval().PredefsTime)) {
  1148. if (reason != nullptr) {
  1149. *reason = "Generating ";
  1150. *reason += Quoted(outputFile);
  1151. *reason += ", because it's older than ";
  1152. *reason += Quoted(MocConst().PredefsFileAbs);
  1153. *reason += ", from ";
  1154. *reason += Quoted(sourceFile);
  1155. }
  1156. return true;
  1157. }
  1158. }
  1159. // Test if the moc executable is newer
  1160. if (outputFileTime.Older(MocConst().ExecutableTime)) {
  1161. if (reason != nullptr) {
  1162. *reason = "Generating ";
  1163. *reason += Quoted(outputFile);
  1164. *reason += ", because it's older than the moc executable, from ";
  1165. *reason += Quoted(sourceFile);
  1166. }
  1167. return true;
  1168. }
  1169. // Test if a dependency file is newer
  1170. {
  1171. // Check dependency timestamps
  1172. std::string const sourceDir = SubDirPrefix(sourceFile);
  1173. for (std::string const& dep : mapping.SourceFile->ParseData->Moc.Depends) {
  1174. // Find dependency file
  1175. auto const depMatch = MocFindDependency(sourceDir, dep);
  1176. if (depMatch.first.empty()) {
  1177. Log().WarningFile(GenT::MOC, sourceFile,
  1178. "Could not find dependency file " + Quoted(dep));
  1179. continue;
  1180. }
  1181. // Test if dependency file is older
  1182. if (outputFileTime.Older(depMatch.second)) {
  1183. if (reason != nullptr) {
  1184. *reason = "Generating ";
  1185. *reason += Quoted(outputFile);
  1186. *reason += ", because it's older than its dependency file ";
  1187. *reason += Quoted(depMatch.first);
  1188. *reason += ", from ";
  1189. *reason += Quoted(sourceFile);
  1190. }
  1191. return true;
  1192. }
  1193. }
  1194. }
  1195. return false;
  1196. }
  1197. std::pair<std::string, cmFileTime>
  1198. cmQtAutoMocUic::JobGenerateT::MocFindDependency(
  1199. std::string const& sourceDir, std::string const& includeString) const
  1200. {
  1201. typedef std::pair<std::string, cmFileTime> ResPair;
  1202. // Search in vicinity of the source
  1203. {
  1204. ResPair res{ sourceDir + includeString, {} };
  1205. if (res.second.Load(res.first)) {
  1206. return res;
  1207. }
  1208. }
  1209. // Search in include directories
  1210. for (std::string const& includePath : MocConst().IncludePaths) {
  1211. ResPair res{ includePath, {} };
  1212. res.first += '/';
  1213. res.first += includeString;
  1214. if (res.second.Load(res.first)) {
  1215. return res;
  1216. }
  1217. }
  1218. // Return empty
  1219. return ResPair();
  1220. }
  1221. bool cmQtAutoMocUic::JobGenerateT::UicGenerate(
  1222. MappingHandleT const& mapping) const
  1223. {
  1224. std::unique_ptr<std::string> reason;
  1225. if (Log().Verbose()) {
  1226. reason = cm::make_unique<std::string>();
  1227. }
  1228. if (UicUpdate(*mapping, reason.get())) {
  1229. // Create the parent directory
  1230. if (!MakeParentDirectory(mapping->OutputFile)) {
  1231. LogFileError(GenT::UIC, mapping->OutputFile,
  1232. "Could not create parent directory.");
  1233. return false;
  1234. }
  1235. // Add uic job
  1236. Gen()->WorkerPool().EmplaceJob<JobUicT>(mapping, std::move(reason));
  1237. }
  1238. return true;
  1239. }
  1240. bool cmQtAutoMocUic::JobGenerateT::UicUpdate(MappingT const& mapping,
  1241. std::string* reason) const
  1242. {
  1243. std::string const& sourceFile = mapping.SourceFile->FileName;
  1244. std::string const& outputFile = mapping.OutputFile;
  1245. // Test if the build file exists
  1246. cmFileTime outputFileTime;
  1247. if (!outputFileTime.Load(outputFile)) {
  1248. if (reason != nullptr) {
  1249. *reason = "Generating ";
  1250. *reason += Quoted(outputFile);
  1251. *reason += ", because it doesn't exist, from ";
  1252. *reason += Quoted(sourceFile);
  1253. }
  1254. return true;
  1255. }
  1256. // Test if the uic settings changed
  1257. if (UicConst().SettingsChanged) {
  1258. if (reason != nullptr) {
  1259. *reason = "Generating ";
  1260. *reason += Quoted(outputFile);
  1261. *reason += ", because the uic settings changed, from ";
  1262. *reason += Quoted(sourceFile);
  1263. }
  1264. return true;
  1265. }
  1266. // Test if the source file is newer
  1267. if (outputFileTime.Older(mapping.SourceFile->FileTime)) {
  1268. if (reason != nullptr) {
  1269. *reason = "Generating ";
  1270. *reason += Quoted(outputFile);
  1271. *reason += " because it's older than the source file ";
  1272. *reason += Quoted(sourceFile);
  1273. }
  1274. return true;
  1275. }
  1276. // Test if the uic executable is newer
  1277. if (outputFileTime.Older(UicConst().ExecutableTime)) {
  1278. if (reason != nullptr) {
  1279. *reason = "Generating ";
  1280. *reason += Quoted(outputFile);
  1281. *reason += ", because it's older than the uic executable, from ";
  1282. *reason += Quoted(sourceFile);
  1283. }
  1284. return true;
  1285. }
  1286. return false;
  1287. }
  1288. void cmQtAutoMocUic::JobMocT::Process()
  1289. {
  1290. std::string const& sourceFile = Mapping->SourceFile->FileName;
  1291. std::string const& outputFile = Mapping->OutputFile;
  1292. // Compose moc command
  1293. std::vector<std::string> cmd;
  1294. cmd.push_back(MocConst().Executable);
  1295. // Add options
  1296. cmAppend(cmd, MocConst().AllOptions);
  1297. // Add predefs include
  1298. if (!MocConst().PredefsFileAbs.empty()) {
  1299. cmd.emplace_back("--include");
  1300. cmd.push_back(MocConst().PredefsFileAbs);
  1301. }
  1302. cmd.emplace_back("-o");
  1303. cmd.push_back(outputFile);
  1304. cmd.push_back(sourceFile);
  1305. // Execute moc command
  1306. cmWorkerPool::ProcessResultT result;
  1307. if (RunProcess(GenT::MOC, result, cmd, Reason.get())) {
  1308. // Moc command success. Print moc output.
  1309. if (!result.StdOut.empty()) {
  1310. Log().Info(GenT::MOC, result.StdOut);
  1311. }
  1312. } else {
  1313. // Moc command failed
  1314. std::string msg = "The moc process failed to compile\n ";
  1315. msg += Quoted(sourceFile);
  1316. msg += "\ninto\n ";
  1317. msg += Quoted(outputFile);
  1318. if (Mapping->IncluderFiles.empty()) {
  1319. msg += ".\n";
  1320. } else {
  1321. msg += "\nincluded by\n";
  1322. for (auto const& item : Mapping->IncluderFiles) {
  1323. msg += " ";
  1324. msg += Quoted(item->FileName);
  1325. msg += '\n';
  1326. }
  1327. }
  1328. msg += result.ErrorMessage;
  1329. LogCommandError(GenT::MOC, msg, cmd, result.StdOut);
  1330. }
  1331. }
  1332. void cmQtAutoMocUic::JobUicT::Process()
  1333. {
  1334. std::string const& sourceFile = Mapping->SourceFile->FileName;
  1335. std::string const& outputFile = Mapping->OutputFile;
  1336. // Compose uic command
  1337. std::vector<std::string> cmd;
  1338. cmd.push_back(UicConst().Executable);
  1339. {
  1340. std::vector<std::string> allOpts = UicConst().TargetOptions;
  1341. auto optionIt = UicConst().Options.find(sourceFile);
  1342. if (optionIt != UicConst().Options.end()) {
  1343. UicMergeOptions(allOpts, optionIt->second,
  1344. (BaseConst().QtVersionMajor == 5));
  1345. }
  1346. cmAppend(cmd, allOpts);
  1347. }
  1348. cmd.emplace_back("-o");
  1349. cmd.emplace_back(outputFile);
  1350. cmd.emplace_back(sourceFile);
  1351. cmWorkerPool::ProcessResultT result;
  1352. if (RunProcess(GenT::UIC, result, cmd, Reason.get())) {
  1353. // Uic command success
  1354. // Print uic output
  1355. if (!result.StdOut.empty()) {
  1356. Log().Info(GenT::UIC, result.StdOut);
  1357. }
  1358. } else {
  1359. // Uic command failed
  1360. std::string msg = "The uic process failed to compile\n ";
  1361. msg += Quoted(sourceFile);
  1362. msg += "\ninto\n ";
  1363. msg += Quoted(outputFile);
  1364. msg += "\nincluded by\n";
  1365. for (auto const& item : Mapping->IncluderFiles) {
  1366. msg += " ";
  1367. msg += Quoted(item->FileName);
  1368. msg += '\n';
  1369. }
  1370. msg += result.ErrorMessage;
  1371. LogCommandError(GenT::UIC, msg, cmd, result.StdOut);
  1372. }
  1373. }
  1374. void cmQtAutoMocUic::JobMocsCompilationT::Process()
  1375. {
  1376. // Compose mocs compilation file content
  1377. std::string content =
  1378. "// This file is autogenerated. Changes will be overwritten.\n";
  1379. if (MocEval().CompFiles.empty()) {
  1380. // Placeholder content
  1381. content += "// No files found that require moc or the moc files are "
  1382. "included\n";
  1383. content += "enum some_compilers { need_more_than_nothing };\n";
  1384. } else {
  1385. // Valid content
  1386. char const clampB = BaseConst().MultiConfig ? '<' : '"';
  1387. char const clampE = BaseConst().MultiConfig ? '>' : '"';
  1388. for (std::string const& mocfile : MocEval().CompFiles) {
  1389. content += "#include ";
  1390. content += clampB;
  1391. content += mocfile;
  1392. content += clampE;
  1393. content += '\n';
  1394. }
  1395. }
  1396. std::string const& compAbs = MocConst().CompFileAbs;
  1397. if (cmQtAutoGenerator::FileDiffers(compAbs, content)) {
  1398. // Actually write mocs compilation file
  1399. if (Log().Verbose()) {
  1400. Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs);
  1401. }
  1402. if (!FileWrite(compAbs, content)) {
  1403. LogFileError(GenT::MOC, compAbs,
  1404. "mocs compilation file writing failed.");
  1405. }
  1406. } else if (MocEval().CompUpdated) {
  1407. // Only touch mocs compilation file
  1408. if (Log().Verbose()) {
  1409. Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs);
  1410. }
  1411. if (!cmSystemTools::Touch(compAbs, false)) {
  1412. LogFileError(GenT::MOC, compAbs,
  1413. "mocs compilation file touching failed.");
  1414. }
  1415. }
  1416. }
  1417. void cmQtAutoMocUic::JobFinishT::Process()
  1418. {
  1419. Gen()->AbortSuccess();
  1420. }
  1421. cmQtAutoMocUic::cmQtAutoMocUic() = default;
  1422. cmQtAutoMocUic::~cmQtAutoMocUic() = default;
  1423. bool cmQtAutoMocUic::Init(cmMakefile* makefile)
  1424. {
  1425. // Utility lambdas
  1426. auto InfoGet = [makefile](const char* key) {
  1427. return makefile->GetSafeDefinition(key);
  1428. };
  1429. auto InfoGetBool = [makefile](const char* key) {
  1430. return makefile->IsOn(key);
  1431. };
  1432. auto InfoGetList = [makefile](const char* key) -> std::vector<std::string> {
  1433. std::vector<std::string> list;
  1434. cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
  1435. return list;
  1436. };
  1437. auto InfoGetLists =
  1438. [makefile](const char* key) -> std::vector<std::vector<std::string>> {
  1439. std::vector<std::vector<std::string>> lists;
  1440. {
  1441. std::string const value = makefile->GetSafeDefinition(key);
  1442. std::string::size_type pos = 0;
  1443. while (pos < value.size()) {
  1444. std::string::size_type next = value.find(ListSep, pos);
  1445. std::string::size_type length =
  1446. (next != std::string::npos) ? next - pos : value.size() - pos;
  1447. // Remove enclosing braces
  1448. if (length >= 2) {
  1449. std::string::const_iterator itBeg = value.begin() + (pos + 1);
  1450. std::string::const_iterator itEnd = itBeg + (length - 2);
  1451. {
  1452. std::string subValue(itBeg, itEnd);
  1453. std::vector<std::string> list;
  1454. cmSystemTools::ExpandListArgument(subValue, list);
  1455. lists.push_back(std::move(list));
  1456. }
  1457. }
  1458. pos += length;
  1459. pos += ListSep.size();
  1460. }
  1461. }
  1462. return lists;
  1463. };
  1464. auto InfoGetConfig = [makefile, this](const char* key) -> std::string {
  1465. const char* valueConf = nullptr;
  1466. {
  1467. std::string keyConf = key;
  1468. keyConf += '_';
  1469. keyConf += InfoConfig();
  1470. valueConf = makefile->GetDefinition(keyConf);
  1471. }
  1472. if (valueConf == nullptr) {
  1473. return makefile->GetSafeDefinition(key);
  1474. }
  1475. return std::string(valueConf);
  1476. };
  1477. auto InfoGetConfigList =
  1478. [&InfoGetConfig](const char* key) -> std::vector<std::string> {
  1479. std::vector<std::string> list;
  1480. cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
  1481. return list;
  1482. };
  1483. auto LogInfoError = [this](std::string const& msg) -> bool {
  1484. std::ostringstream err;
  1485. err << "In " << Quoted(this->InfoFile()) << ":\n" << msg;
  1486. this->Log().Error(GenT::GEN, err.str());
  1487. return false;
  1488. };
  1489. auto MatchSizes = [&LogInfoError](const char* keyA, const char* keyB,
  1490. std::size_t sizeA,
  1491. std::size_t sizeB) -> bool {
  1492. if (sizeA == sizeB) {
  1493. return true;
  1494. }
  1495. std::ostringstream err;
  1496. err << "Lists sizes mismatch " << keyA << '(' << sizeA << ") " << keyB
  1497. << '(' << sizeB << ')';
  1498. return LogInfoError(err.str());
  1499. };
  1500. // -- Read info file
  1501. if (!makefile->ReadListFile(InfoFile())) {
  1502. return LogInfoError("File processing failed");
  1503. }
  1504. // -- Meta
  1505. Logger_.RaiseVerbosity(InfoGet("AM_VERBOSITY"));
  1506. BaseConst_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG");
  1507. {
  1508. unsigned long num = 1;
  1509. if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) {
  1510. num = std::max<unsigned long>(num, 1);
  1511. num = std::min<unsigned long>(num, ParallelMax);
  1512. }
  1513. WorkerPool_.SetThreadCount(static_cast<unsigned int>(num));
  1514. }
  1515. BaseConst_.HeaderExtensions =
  1516. makefile->GetCMakeInstance()->GetHeaderExtensions();
  1517. // - Files and directories
  1518. BaseConst_.IncludeProjectDirsBefore =
  1519. InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
  1520. BaseConst_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
  1521. BaseConst_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
  1522. BaseConst_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
  1523. BaseConst_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
  1524. BaseConst_.AutogenBuildDir = InfoGet("AM_BUILD_DIR");
  1525. if (BaseConst_.AutogenBuildDir.empty()) {
  1526. return LogInfoError("Autogen build directory missing.");
  1527. }
  1528. BaseConst_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR");
  1529. if (BaseConst_.AutogenIncludeDir.empty()) {
  1530. return LogInfoError("Autogen include directory missing.");
  1531. }
  1532. BaseConst_.CMakeExecutable = InfoGetConfig("AM_CMAKE_EXECUTABLE");
  1533. if (BaseConst_.CMakeExecutable.empty()) {
  1534. return LogInfoError("CMake executable file name missing.");
  1535. }
  1536. if (!BaseConst_.CMakeExecutableTime.Load(BaseConst_.CMakeExecutable)) {
  1537. std::string error = "The CMake executable ";
  1538. error += Quoted(BaseConst_.CMakeExecutable);
  1539. error += " does not exist.";
  1540. return LogInfoError(error);
  1541. }
  1542. BaseConst_.ParseCacheFile = InfoGetConfig("AM_PARSE_CACHE_FILE");
  1543. if (BaseConst_.ParseCacheFile.empty()) {
  1544. return LogInfoError("Parse cache file name missing.");
  1545. }
  1546. // - Settings file
  1547. SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE");
  1548. if (SettingsFile_.empty()) {
  1549. return LogInfoError("Settings file name missing.");
  1550. }
  1551. // - Qt environment
  1552. {
  1553. unsigned long qtv = BaseConst_.QtVersionMajor;
  1554. if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(),
  1555. &qtv)) {
  1556. BaseConst_.QtVersionMajor = static_cast<unsigned int>(qtv);
  1557. }
  1558. }
  1559. // - Moc
  1560. MocConst_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE");
  1561. if (!MocConst().Executable.empty()) {
  1562. MocConst_.Enabled = true;
  1563. // Load the executable file time
  1564. if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) {
  1565. std::string error = "The moc executable ";
  1566. error += Quoted(MocConst_.Executable);
  1567. error += " does not exist.";
  1568. return LogInfoError(error);
  1569. }
  1570. for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) {
  1571. MocConst_.SkipList.insert(std::move(sfl));
  1572. }
  1573. MocConst_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
  1574. MocConst_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
  1575. MocConst_.Options = InfoGetList("AM_MOC_OPTIONS");
  1576. MocConst_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
  1577. for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) {
  1578. MocConst_.MacroFilters.emplace_back(
  1579. item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
  1580. }
  1581. {
  1582. auto addFilter = [this, &LogInfoError](std::string const& key,
  1583. std::string const& exp) -> bool {
  1584. auto filterErr = [&LogInfoError, &key, &exp](const char* err) -> bool {
  1585. std::ostringstream ferr;
  1586. ferr << "AUTOMOC_DEPEND_FILTERS: " << err << '\n';
  1587. ferr << " Key: " << Quoted(key) << '\n';
  1588. ferr << " Exp: " << Quoted(exp) << '\n';
  1589. return LogInfoError(ferr.str());
  1590. };
  1591. if (key.empty()) {
  1592. return filterErr("Key is empty");
  1593. }
  1594. if (exp.empty()) {
  1595. return filterErr("Regular expression is empty");
  1596. }
  1597. this->MocConst_.DependFilters.emplace_back(key, exp);
  1598. if (!this->MocConst_.DependFilters.back().Exp.is_valid()) {
  1599. return filterErr("Regular expression compiling failed");
  1600. }
  1601. return true;
  1602. };
  1603. // Insert default filter for Q_PLUGIN_METADATA
  1604. if (BaseConst().QtVersionMajor != 4) {
  1605. if (!addFilter("Q_PLUGIN_METADATA",
  1606. "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
  1607. "[^\\)]*FILE[ \t]*\"([^\"]+)\"")) {
  1608. return false;
  1609. }
  1610. }
  1611. // Insert user defined dependency filters
  1612. std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS");
  1613. if ((flts.size() % 2) != 0) {
  1614. return LogInfoError(
  1615. "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
  1616. }
  1617. for (auto itC = flts.begin(), itE = flts.end(); itC != itE; itC += 2) {
  1618. if (!addFilter(*itC, *(itC + 1))) {
  1619. return false;
  1620. }
  1621. }
  1622. }
  1623. MocConst_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
  1624. }
  1625. // - Uic
  1626. UicConst_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
  1627. if (!UicConst().Executable.empty()) {
  1628. UicConst_.Enabled = true;
  1629. // Load the executable file time
  1630. if (!UicConst_.ExecutableTime.Load(UicConst_.Executable)) {
  1631. std::string error = "The uic executable ";
  1632. error += Quoted(UicConst_.Executable);
  1633. error += " does not exist.";
  1634. return LogInfoError(error);
  1635. }
  1636. for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) {
  1637. UicConst_.SkipList.insert(std::move(sfl));
  1638. }
  1639. UicConst_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
  1640. UicConst_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
  1641. {
  1642. const char* keyFiles = "AM_UIC_OPTIONS_FILES";
  1643. const char* keyOpts = "AM_UIC_OPTIONS_OPTIONS";
  1644. auto sources = InfoGetList(keyFiles);
  1645. auto options = InfoGetLists(keyOpts);
  1646. if (!MatchSizes(keyFiles, keyOpts, sources.size(), options.size())) {
  1647. return false;
  1648. }
  1649. auto fitEnd = sources.cend();
  1650. auto fit = sources.begin();
  1651. auto oit = options.begin();
  1652. while (fit != fitEnd) {
  1653. UicConst_.Options[*fit] = std::move(*oit);
  1654. ++fit;
  1655. ++oit;
  1656. }
  1657. }
  1658. }
  1659. // - Headers and sources
  1660. {
  1661. auto makeSource =
  1662. [&LogInfoError](std::string const& fileName,
  1663. std::string const& fileFlags) -> SourceFileHandleT {
  1664. if (fileFlags.size() != 2) {
  1665. LogInfoError("Invalid file flags string size");
  1666. return SourceFileHandleT();
  1667. }
  1668. cmFileTime fileTime;
  1669. if (!fileTime.Load(fileName)) {
  1670. LogInfoError("The source file " + cmQtAutoGen::Quoted(fileName) +
  1671. " does not exist.");
  1672. return SourceFileHandleT();
  1673. }
  1674. SourceFileHandleT sfh = std::make_shared<SourceFileT>(fileName);
  1675. sfh->FileTime = fileTime;
  1676. sfh->Moc = (fileFlags[0] == 'M');
  1677. sfh->Uic = (fileFlags[1] == 'U');
  1678. return sfh;
  1679. };
  1680. // Headers
  1681. {
  1682. // Get file lists
  1683. const char *keyFiles = "AM_HEADERS", *keyFlags = "AM_HEADERS_FLAGS";
  1684. std::vector<std::string> files = InfoGetList(keyFiles);
  1685. std::vector<std::string> flags = InfoGetList(keyFlags);
  1686. std::vector<std::string> builds;
  1687. if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) {
  1688. return false;
  1689. }
  1690. if (MocConst().Enabled) {
  1691. const char* keyPaths = "AM_HEADERS_BUILD_PATHS";
  1692. builds = InfoGetList(keyPaths);
  1693. if (!MatchSizes(keyFiles, keyPaths, files.size(), builds.size())) {
  1694. return false;
  1695. }
  1696. }
  1697. // Process file lists
  1698. for (std::size_t ii = 0; ii != files.size(); ++ii) {
  1699. std::string& fileName(files[ii]);
  1700. SourceFileHandleT sfh = makeSource(fileName, flags[ii]);
  1701. if (!sfh) {
  1702. return false;
  1703. }
  1704. if (MocConst().Enabled) {
  1705. sfh->BuildPath = std::move(builds[ii]);
  1706. if (sfh->BuildPath.empty()) {
  1707. Log().ErrorFile(GenT::GEN, this->InfoFile(),
  1708. "Header file build path is empty");
  1709. return false;
  1710. }
  1711. }
  1712. BaseEval().Headers.emplace(std::move(fileName), std::move(sfh));
  1713. }
  1714. }
  1715. // Sources
  1716. {
  1717. const char *keyFiles = "AM_SOURCES", *keyFlags = "AM_SOURCES_FLAGS";
  1718. std::vector<std::string> files = InfoGetList(keyFiles);
  1719. std::vector<std::string> flags = InfoGetList(keyFlags);
  1720. if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) {
  1721. return false;
  1722. }
  1723. // Process file lists
  1724. for (std::size_t ii = 0; ii != files.size(); ++ii) {
  1725. std::string& fileName(files[ii]);
  1726. SourceFileHandleT sfh = makeSource(fileName, flags[ii]);
  1727. if (!sfh) {
  1728. return false;
  1729. }
  1730. BaseEval().Sources.emplace(std::move(fileName), std::move(sfh));
  1731. }
  1732. }
  1733. }
  1734. // Init derived information
  1735. // ------------------------
  1736. // Moc variables
  1737. if (MocConst().Enabled) {
  1738. // Mocs compilation file
  1739. MocConst_.CompFileAbs = AbsoluteBuildPath("mocs_compilation.cpp");
  1740. // Moc predefs file
  1741. if (!MocConst_.PredefsCmd.empty()) {
  1742. MocConst_.PredefsFileRel = "moc_predefs";
  1743. if (BaseConst_.MultiConfig) {
  1744. MocConst_.PredefsFileRel += '_';
  1745. MocConst_.PredefsFileRel += InfoConfig();
  1746. }
  1747. MocConst_.PredefsFileRel += ".h";
  1748. MocConst_.PredefsFileAbs = AbsoluteBuildPath(MocConst().PredefsFileRel);
  1749. }
  1750. // Sort include directories on demand
  1751. if (BaseConst().IncludeProjectDirsBefore) {
  1752. // Move strings to temporary list
  1753. std::list<std::string> includes(MocConst().IncludePaths.begin(),
  1754. MocConst().IncludePaths.end());
  1755. MocConst_.IncludePaths.clear();
  1756. MocConst_.IncludePaths.reserve(includes.size());
  1757. // Append project directories only
  1758. {
  1759. std::array<std::string const*, 2> const movePaths = {
  1760. { &BaseConst().ProjectBinaryDir, &BaseConst().ProjectSourceDir }
  1761. };
  1762. for (std::string const* ppath : movePaths) {
  1763. std::list<std::string>::iterator it = includes.begin();
  1764. while (it != includes.end()) {
  1765. std::string const& path = *it;
  1766. if (cmSystemTools::StringStartsWith(path, ppath->c_str())) {
  1767. MocConst_.IncludePaths.push_back(path);
  1768. it = includes.erase(it);
  1769. } else {
  1770. ++it;
  1771. }
  1772. }
  1773. }
  1774. }
  1775. // Append remaining directories
  1776. MocConst_.IncludePaths.insert(MocConst_.IncludePaths.end(),
  1777. includes.begin(), includes.end());
  1778. }
  1779. // Compose moc includes list
  1780. {
  1781. std::set<std::string> frameworkPaths;
  1782. for (std::string const& path : MocConst().IncludePaths) {
  1783. MocConst_.Includes.push_back("-I" + path);
  1784. // Extract framework path
  1785. if (cmHasLiteralSuffix(path, ".framework/Headers")) {
  1786. // Go up twice to get to the framework root
  1787. std::vector<std::string> pathComponents;
  1788. cmSystemTools::SplitPath(path, pathComponents);
  1789. frameworkPaths.emplace(cmSystemTools::JoinPath(
  1790. pathComponents.begin(), pathComponents.end() - 2));
  1791. }
  1792. }
  1793. // Append framework includes
  1794. for (std::string const& path : frameworkPaths) {
  1795. MocConst_.Includes.emplace_back("-F");
  1796. MocConst_.Includes.push_back(path);
  1797. }
  1798. }
  1799. // Setup single list with all options
  1800. {
  1801. // Add includes
  1802. MocConst_.AllOptions.insert(MocConst_.AllOptions.end(),
  1803. MocConst().Includes.begin(),
  1804. MocConst().Includes.end());
  1805. // Add definitions
  1806. for (std::string const& def : MocConst().Definitions) {
  1807. MocConst_.AllOptions.push_back("-D" + def);
  1808. }
  1809. // Add options
  1810. MocConst_.AllOptions.insert(MocConst_.AllOptions.end(),
  1811. MocConst().Options.begin(),
  1812. MocConst().Options.end());
  1813. }
  1814. }
  1815. return true;
  1816. }
  1817. template <class JOBTYPE>
  1818. void cmQtAutoMocUic::CreateParseJobs(SourceFileMapT const& sourceMap)
  1819. {
  1820. cmFileTime const parseCacheTime = BaseEval().ParseCacheTime;
  1821. ParseCacheT& parseCache = BaseEval().ParseCache;
  1822. for (auto& src : sourceMap) {
  1823. // Get or create the file parse data reference
  1824. ParseCacheT::GetOrInsertT cacheEntry = parseCache.GetOrInsert(src.first);
  1825. src.second->ParseData = std::move(cacheEntry.first);
  1826. // Create a parse job if the cache file was missing or is older
  1827. if (cacheEntry.second || src.second->FileTime.Newer(parseCacheTime)) {
  1828. BaseEval().ParseCacheChanged = true;
  1829. WorkerPool().EmplaceJob<JOBTYPE>(src.second);
  1830. }
  1831. }
  1832. }
  1833. void cmQtAutoMocUic::InitJobs()
  1834. {
  1835. // Add moc_predefs.h job
  1836. if (MocConst().Enabled && !MocConst().PredefsCmd.empty()) {
  1837. WorkerPool().EmplaceJob<JobMocPredefsT>();
  1838. }
  1839. // Add header parse jobs
  1840. CreateParseJobs<JobParseHeaderT>(BaseEval().Headers);
  1841. // Add source parse jobs
  1842. CreateParseJobs<JobParseSourceT>(BaseEval().Sources);
  1843. // Add evaluate job
  1844. WorkerPool().EmplaceJob<JobEvaluateT>();
  1845. }
  1846. bool cmQtAutoMocUic::Process()
  1847. {
  1848. SettingsFileRead();
  1849. ParseCacheRead();
  1850. if (!CreateDirectories()) {
  1851. return false;
  1852. }
  1853. InitJobs();
  1854. if (!WorkerPool_.Process(this)) {
  1855. return false;
  1856. }
  1857. if (JobError_) {
  1858. return false;
  1859. }
  1860. if (!ParseCacheWrite()) {
  1861. return false;
  1862. }
  1863. if (!SettingsFileWrite()) {
  1864. return false;
  1865. }
  1866. return true;
  1867. }
  1868. void cmQtAutoMocUic::SettingsFileRead()
  1869. {
  1870. // Compose current settings strings
  1871. {
  1872. cmCryptoHash cryptoHash(cmCryptoHash::AlgoSHA256);
  1873. std::string const sep(";");
  1874. auto cha = [&cryptoHash, &sep](std::string const& value) {
  1875. cryptoHash.Append(value);
  1876. cryptoHash.Append(sep);
  1877. };
  1878. if (MocConst_.Enabled) {
  1879. cryptoHash.Initialize();
  1880. cha(MocConst().Executable);
  1881. for (auto const& value : MocConst().AllOptions) {
  1882. cha(value);
  1883. }
  1884. cha(BaseConst().IncludeProjectDirsBefore ? "TRUE" : "FALSE");
  1885. for (auto const& value : MocConst().PredefsCmd) {
  1886. cha(value);
  1887. }
  1888. for (auto const& filter : MocConst().DependFilters) {
  1889. cha(filter.Key);
  1890. }
  1891. for (auto const& filter : MocConst().MacroFilters) {
  1892. cha(filter.Key);
  1893. }
  1894. SettingsStringMoc_ = cryptoHash.FinalizeHex();
  1895. }
  1896. if (UicConst().Enabled) {
  1897. cryptoHash.Initialize();
  1898. cha(UicConst().Executable);
  1899. for (auto const& value : UicConst().TargetOptions) {
  1900. cha(value);
  1901. }
  1902. for (const auto& item : UicConst().Options) {
  1903. cha(item.first);
  1904. for (auto const& svalue : item.second) {
  1905. cha(svalue);
  1906. }
  1907. }
  1908. SettingsStringUic_ = cryptoHash.FinalizeHex();
  1909. }
  1910. }
  1911. // Read old settings and compare
  1912. {
  1913. std::string content;
  1914. if (cmQtAutoGenerator::FileRead(content, SettingsFile_)) {
  1915. if (MocConst().Enabled) {
  1916. if (SettingsStringMoc_ != SettingsFind(content, "moc")) {
  1917. MocConst_.SettingsChanged = true;
  1918. }
  1919. }
  1920. if (UicConst().Enabled) {
  1921. if (SettingsStringUic_ != SettingsFind(content, "uic")) {
  1922. UicConst_.SettingsChanged = true;
  1923. }
  1924. }
  1925. // In case any setting changed remove the old settings file.
  1926. // This triggers a full rebuild on the next run if the current
  1927. // build is aborted before writing the current settings in the end.
  1928. if (MocConst().SettingsChanged || UicConst().SettingsChanged) {
  1929. cmSystemTools::RemoveFile(SettingsFile_);
  1930. }
  1931. } else {
  1932. // Settings file read failed
  1933. if (MocConst().Enabled) {
  1934. MocConst_.SettingsChanged = true;
  1935. }
  1936. if (UicConst().Enabled) {
  1937. UicConst_.SettingsChanged = true;
  1938. }
  1939. }
  1940. }
  1941. }
  1942. bool cmQtAutoMocUic::SettingsFileWrite()
  1943. {
  1944. // Only write if any setting changed
  1945. if (MocConst().SettingsChanged || UicConst().SettingsChanged) {
  1946. if (Log().Verbose()) {
  1947. Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_));
  1948. }
  1949. // Compose settings file content
  1950. std::string content;
  1951. {
  1952. auto SettingAppend = [&content](const char* key,
  1953. std::string const& value) {
  1954. if (!value.empty()) {
  1955. content += key;
  1956. content += ':';
  1957. content += value;
  1958. content += '\n';
  1959. }
  1960. };
  1961. SettingAppend("moc", SettingsStringMoc_);
  1962. SettingAppend("uic", SettingsStringUic_);
  1963. }
  1964. // Write settings file
  1965. std::string error;
  1966. if (!cmQtAutoGenerator::FileWrite(SettingsFile_, content, &error)) {
  1967. Log().ErrorFile(GenT::GEN, SettingsFile_,
  1968. "Settings file writing failed. " + error);
  1969. // Remove old settings file to trigger a full rebuild on the next run
  1970. cmSystemTools::RemoveFile(SettingsFile_);
  1971. return false;
  1972. }
  1973. }
  1974. return true;
  1975. }
  1976. void cmQtAutoMocUic::ParseCacheRead()
  1977. {
  1978. const char* reason = nullptr;
  1979. // Don't read the cache if it is invalid
  1980. if (!BaseEval().ParseCacheTime.Load(BaseConst().ParseCacheFile)) {
  1981. reason = "Refreshing parse cache because it doesn't exist.";
  1982. } else if (MocConst().SettingsChanged || UicConst().SettingsChanged) {
  1983. reason = "Refreshing parse cache because the settings changed.";
  1984. } else if (BaseEval().ParseCacheTime.Older(
  1985. BaseConst().CMakeExecutableTime)) {
  1986. reason =
  1987. "Refreshing parse cache because it is older than the CMake executable.";
  1988. }
  1989. if (reason != nullptr) {
  1990. // Don't read but refresh the complete parse cache
  1991. if (Log().Verbose()) {
  1992. Log().Info(GenT::GEN, reason);
  1993. }
  1994. BaseEval().ParseCacheChanged = true;
  1995. } else {
  1996. // Read parse cache
  1997. BaseEval().ParseCache.ReadFromFile(BaseConst().ParseCacheFile);
  1998. }
  1999. }
  2000. bool cmQtAutoMocUic::ParseCacheWrite()
  2001. {
  2002. if (BaseEval().ParseCacheChanged) {
  2003. if (Log().Verbose()) {
  2004. Log().Info(GenT::GEN,
  2005. "Writing parse cache file " +
  2006. Quoted(BaseConst().ParseCacheFile));
  2007. }
  2008. if (!BaseEval().ParseCache.WriteToFile(BaseConst().ParseCacheFile)) {
  2009. Log().ErrorFile(GenT::GEN, BaseConst().ParseCacheFile,
  2010. "Parse cache file writing failed.");
  2011. return false;
  2012. }
  2013. }
  2014. return true;
  2015. }
  2016. bool cmQtAutoMocUic::CreateDirectories()
  2017. {
  2018. // Create AUTOGEN include directory
  2019. if (!cmSystemTools::MakeDirectory(BaseConst().AutogenIncludeDir)) {
  2020. Log().ErrorFile(GenT::GEN, BaseConst().AutogenIncludeDir,
  2021. "Could not create directory.");
  2022. return false;
  2023. }
  2024. return true;
  2025. }
  2026. void cmQtAutoMocUic::Abort(bool error)
  2027. {
  2028. if (error) {
  2029. JobError_.store(true);
  2030. }
  2031. WorkerPool_.Abort();
  2032. }
  2033. std::string cmQtAutoMocUic::AbsoluteBuildPath(
  2034. std::string const& relativePath) const
  2035. {
  2036. std::string res(BaseConst().AutogenBuildDir);
  2037. res += '/';
  2038. res += relativePath;
  2039. return res;
  2040. }
  2041. std::string cmQtAutoMocUic::AbsoluteIncludePath(
  2042. std::string const& relativePath) const
  2043. {
  2044. std::string res(BaseConst().AutogenIncludeDir);
  2045. res += '/';
  2046. res += relativePath;
  2047. return res;
  2048. }