cmQtAutoMocUic.cxx 66 KB

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