cmQtAutoGenerators.cxx 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmQtAutoGen.h"
  4. #include "cmQtAutoGenerators.h"
  5. #include "cmsys/FStream.hxx"
  6. #include "cmsys/Terminal.h"
  7. #include <algorithm>
  8. #include <array>
  9. #include <list>
  10. #include <memory>
  11. #include <sstream>
  12. #include <string.h>
  13. #include <utility>
  14. #include "cmAlgorithms.h"
  15. #include "cmCryptoHash.h"
  16. #include "cmFilePathChecksum.h"
  17. #include "cmGlobalGenerator.h"
  18. #include "cmMakefile.h"
  19. #include "cmOutputConverter.h"
  20. #include "cmStateDirectory.h"
  21. #include "cmStateSnapshot.h"
  22. #include "cmSystemTools.h"
  23. #include "cmake.h"
  24. #if defined(__APPLE__)
  25. #include <unistd.h>
  26. #endif
  27. // -- Static variables
  28. static const char* SettingsKeyMoc = "AM_MOC_SETTINGS_HASH";
  29. static const char* SettingsKeyUic = "AM_UIC_SETTINGS_HASH";
  30. static const char* SettingsKeyRcc = "AM_RCC_SETTINGS_HASH";
  31. // -- Static functions
  32. static std::string HeadLine(std::string const& title)
  33. {
  34. std::string head = title;
  35. head += '\n';
  36. head.append(head.size() - 1, '-');
  37. head += '\n';
  38. return head;
  39. }
  40. static std::string QuotedCommand(std::vector<std::string> const& command)
  41. {
  42. std::string res;
  43. for (std::string const& item : command) {
  44. if (!res.empty()) {
  45. res.push_back(' ');
  46. }
  47. std::string const cesc = cmQtAutoGen::Quoted(item);
  48. if (item.empty() || (cesc.size() > (item.size() + 2)) ||
  49. (cesc.find(' ') != std::string::npos)) {
  50. res += cesc;
  51. } else {
  52. res += item;
  53. }
  54. }
  55. return res;
  56. }
  57. static std::string SubDirPrefix(std::string const& fileName)
  58. {
  59. std::string res(cmSystemTools::GetFilenamePath(fileName));
  60. if (!res.empty()) {
  61. res += '/';
  62. }
  63. return res;
  64. }
  65. static bool ReadFile(std::string& content, std::string const& filename,
  66. std::string* error = nullptr)
  67. {
  68. bool success = false;
  69. if (cmSystemTools::FileExists(filename)) {
  70. std::size_t const length = cmSystemTools::FileLength(filename);
  71. cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
  72. if (ifs) {
  73. content.resize(length);
  74. ifs.read(&content.front(), content.size());
  75. if (ifs) {
  76. success = true;
  77. } else {
  78. content.clear();
  79. if (error != nullptr) {
  80. error->append("Reading from the file failed.");
  81. }
  82. }
  83. } else if (error != nullptr) {
  84. error->append("Opening the file for reading failed.");
  85. }
  86. } else if (error != nullptr) {
  87. error->append("The file does not exist.");
  88. }
  89. return success;
  90. }
  91. /**
  92. * @brief Tests if buildFile is older than sourceFile
  93. * @return True if buildFile is older than sourceFile.
  94. * False may indicate an error.
  95. */
  96. static bool FileIsOlderThan(std::string const& buildFile,
  97. std::string const& sourceFile,
  98. std::string* error = nullptr)
  99. {
  100. int result = 0;
  101. if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) {
  102. return (result < 0);
  103. }
  104. if (error != nullptr) {
  105. error->append(
  106. "File modification time comparison failed for the files\n ");
  107. error->append(cmQtAutoGen::Quoted(buildFile));
  108. error->append("\nand\n ");
  109. error->append(cmQtAutoGen::Quoted(sourceFile));
  110. }
  111. return false;
  112. }
  113. static bool ListContains(std::vector<std::string> const& list,
  114. std::string const& entry)
  115. {
  116. return (std::find(list.begin(), list.end(), entry) != list.end());
  117. }
  118. // -- Class methods
  119. cmQtAutoGenerators::cmQtAutoGenerators()
  120. : MultiConfig(cmQtAutoGen::WRAP)
  121. , IncludeProjectDirsBefore(false)
  122. , Verbose(cmSystemTools::HasEnv("VERBOSE"))
  123. , ColorOutput(true)
  124. , MocSettingsChanged(false)
  125. , MocPredefsChanged(false)
  126. , MocRelaxedMode(false)
  127. , UicSettingsChanged(false)
  128. , RccSettingsChanged(false)
  129. {
  130. {
  131. std::string colorEnv;
  132. cmSystemTools::GetEnv("COLOR", colorEnv);
  133. if (!colorEnv.empty()) {
  134. this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str());
  135. }
  136. }
  137. // Precompile regular expressions
  138. this->MocRegExpInclude.compile(
  139. "[\n][ \t]*#[ \t]*include[ \t]+"
  140. "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
  141. this->UicRegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+"
  142. "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
  143. }
  144. bool cmQtAutoGenerators::Run(std::string const& targetDirectory,
  145. std::string const& config)
  146. {
  147. cmake cm(cmake::RoleScript);
  148. cm.SetHomeOutputDirectory(targetDirectory);
  149. cm.SetHomeDirectory(targetDirectory);
  150. cm.GetCurrentSnapshot().SetDefaultDefinitions();
  151. cmGlobalGenerator gg(&cm);
  152. cmStateSnapshot snapshot = cm.GetCurrentSnapshot();
  153. snapshot.GetDirectory().SetCurrentBinary(targetDirectory);
  154. snapshot.GetDirectory().SetCurrentSource(targetDirectory);
  155. auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot);
  156. gg.SetCurrentMakefile(makefile.get());
  157. bool success = false;
  158. if (this->InitInfoFile(makefile.get(), targetDirectory, config)) {
  159. // Read latest settings
  160. this->SettingsFileRead(makefile.get());
  161. if (this->Process()) {
  162. // Write current settings
  163. if (this->SettingsFileWrite()) {
  164. success = true;
  165. }
  166. }
  167. }
  168. return success;
  169. }
  170. bool cmQtAutoGenerators::InitInfoFile(cmMakefile* makefile,
  171. std::string const& targetDirectory,
  172. std::string const& config)
  173. {
  174. // -- Meta
  175. this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
  176. // Utility lambdas
  177. auto InfoGet = [makefile](const char* key) {
  178. return makefile->GetSafeDefinition(key);
  179. };
  180. auto InfoGetBool = [makefile](const char* key) {
  181. return makefile->IsOn(key);
  182. };
  183. auto InfoGetList = [makefile](const char* key) -> std::vector<std::string> {
  184. std::vector<std::string> list;
  185. cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
  186. return list;
  187. };
  188. auto InfoGetLists =
  189. [makefile](const char* key) -> std::vector<std::vector<std::string>> {
  190. std::vector<std::vector<std::string>> lists;
  191. {
  192. std::string const value = makefile->GetSafeDefinition(key);
  193. std::string::size_type pos = 0;
  194. while (pos < value.size()) {
  195. std::string::size_type next = value.find(cmQtAutoGen::listSep, pos);
  196. std::string::size_type length =
  197. (next != std::string::npos) ? next - pos : value.size() - pos;
  198. // Remove enclosing braces
  199. if (length >= 2) {
  200. std::string::const_iterator itBeg = value.begin() + (pos + 1);
  201. std::string::const_iterator itEnd = itBeg + (length - 2);
  202. {
  203. std::string subValue(itBeg, itEnd);
  204. std::vector<std::string> list;
  205. cmSystemTools::ExpandListArgument(subValue, list);
  206. lists.push_back(std::move(list));
  207. }
  208. }
  209. pos += length;
  210. pos += cmQtAutoGen::listSep.size();
  211. }
  212. }
  213. return lists;
  214. };
  215. auto InfoGetConfig = [makefile, &config](const char* key) -> std::string {
  216. const char* valueConf = nullptr;
  217. {
  218. std::string keyConf = key;
  219. keyConf += '_';
  220. keyConf += config;
  221. valueConf = makefile->GetDefinition(keyConf);
  222. }
  223. if (valueConf == nullptr) {
  224. valueConf = makefile->GetSafeDefinition(key);
  225. }
  226. return std::string(valueConf);
  227. };
  228. auto InfoGetConfigList =
  229. [&InfoGetConfig](const char* key) -> std::vector<std::string> {
  230. std::vector<std::string> list;
  231. cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
  232. return list;
  233. };
  234. // -- Read info file
  235. this->InfoFile = cmSystemTools::CollapseFullPath(targetDirectory);
  236. cmSystemTools::ConvertToUnixSlashes(this->InfoFile);
  237. this->InfoFile += "/AutogenInfo.cmake";
  238. if (!makefile->ReadListFile(this->InfoFile.c_str())) {
  239. this->LogFileError(cmQtAutoGen::GEN, this->InfoFile,
  240. "File processing failed");
  241. return false;
  242. }
  243. // -- Meta
  244. this->MultiConfig = cmQtAutoGen::MultiConfigType(InfoGet("AM_MULTI_CONFIG"));
  245. this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX");
  246. if (this->ConfigSuffix.empty()) {
  247. this->ConfigSuffix = "_";
  248. this->ConfigSuffix += config;
  249. }
  250. // - Files and directories
  251. this->ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
  252. this->ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
  253. this->CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
  254. this->CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
  255. this->IncludeProjectDirsBefore =
  256. InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
  257. this->AutogenBuildDir = InfoGet("AM_BUILD_DIR");
  258. if (this->AutogenBuildDir.empty()) {
  259. this->LogFileError(cmQtAutoGen::GEN, this->InfoFile,
  260. "Autogen build directory missing");
  261. return false;
  262. }
  263. // - Qt environment
  264. this->QtMajorVersion = InfoGet("AM_QT_VERSION_MAJOR");
  265. this->QtMinorVersion = InfoGet("AM_QT_VERSION_MINOR");
  266. this->MocExecutable = InfoGet("AM_QT_MOC_EXECUTABLE");
  267. this->UicExecutable = InfoGet("AM_QT_UIC_EXECUTABLE");
  268. this->RccExecutable = InfoGet("AM_QT_RCC_EXECUTABLE");
  269. // Check Qt version
  270. if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) {
  271. this->LogFileError(cmQtAutoGen::GEN, this->InfoFile,
  272. "Unsupported Qt version: " +
  273. cmQtAutoGen::Quoted(this->QtMajorVersion));
  274. return false;
  275. }
  276. // - Moc
  277. if (this->MocEnabled()) {
  278. this->MocSkipList = InfoGetList("AM_MOC_SKIP");
  279. this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
  280. #ifdef _WIN32
  281. {
  282. std::string const win32("WIN32");
  283. if (!ListContains(this->MocDefinitions, win32)) {
  284. this->MocDefinitions.push_back(win32);
  285. }
  286. }
  287. #endif
  288. this->MocIncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
  289. this->MocOptions = InfoGetList("AM_MOC_OPTIONS");
  290. this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
  291. {
  292. std::vector<std::string> const MocMacroNames =
  293. InfoGetList("AM_MOC_MACRO_NAMES");
  294. for (std::string const& item : MocMacroNames) {
  295. this->MocMacroFilters.emplace_back(
  296. item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
  297. }
  298. }
  299. {
  300. std::vector<std::string> const mocDependFilters =
  301. InfoGetList("AM_MOC_DEPEND_FILTERS");
  302. // Insert Q_PLUGIN_METADATA dependency filter
  303. if (this->QtMajorVersion != "4") {
  304. this->MocDependFilterPush("Q_PLUGIN_METADATA",
  305. "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
  306. "[^\\)]*FILE[ \t]*\"([^\"]+)\"");
  307. }
  308. // Insert user defined dependency filters
  309. if ((mocDependFilters.size() % 2) == 0) {
  310. for (std::vector<std::string>::const_iterator
  311. dit = mocDependFilters.begin(),
  312. ditEnd = mocDependFilters.end();
  313. dit != ditEnd; dit += 2) {
  314. if (!this->MocDependFilterPush(*dit, *(dit + 1))) {
  315. return false;
  316. }
  317. }
  318. } else {
  319. this->LogFileError(
  320. cmQtAutoGen::MOC, this->InfoFile,
  321. "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
  322. return false;
  323. }
  324. }
  325. this->MocPredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
  326. }
  327. // - Uic
  328. if (this->UicEnabled()) {
  329. this->UicSkipList = InfoGetList("AM_UIC_SKIP");
  330. this->UicSearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS");
  331. this->UicTargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS");
  332. {
  333. auto sources = InfoGetList("AM_UIC_OPTIONS_FILES");
  334. auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS");
  335. // Compare list sizes
  336. if (sources.size() != options.size()) {
  337. std::ostringstream ost;
  338. ost << "files/options lists sizes missmatch (" << sources.size() << "/"
  339. << options.size() << ")";
  340. this->LogFileError(cmQtAutoGen::UIC, this->InfoFile, ost.str());
  341. return false;
  342. }
  343. auto fitEnd = sources.cend();
  344. auto fit = sources.begin();
  345. auto oit = options.begin();
  346. while (fit != fitEnd) {
  347. this->UicOptions[*fit] = std::move(*oit);
  348. ++fit;
  349. ++oit;
  350. }
  351. }
  352. }
  353. // - Rcc
  354. if (this->RccEnabled()) {
  355. // File lists
  356. auto sources = InfoGetList("AM_RCC_SOURCES");
  357. auto builds = InfoGetList("AM_RCC_BUILDS");
  358. auto options = InfoGetLists("AM_RCC_OPTIONS");
  359. auto inputs = InfoGetLists("AM_RCC_INPUTS");
  360. if (sources.size() != builds.size()) {
  361. std::ostringstream ost;
  362. ost << "sources, builds lists sizes missmatch (" << sources.size() << "/"
  363. << builds.size() << ")";
  364. this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, ost.str());
  365. return false;
  366. }
  367. if (sources.size() != options.size()) {
  368. std::ostringstream ost;
  369. ost << "sources, options lists sizes missmatch (" << sources.size()
  370. << "/" << options.size() << ")";
  371. this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, ost.str());
  372. return false;
  373. }
  374. if (sources.size() != inputs.size()) {
  375. std::ostringstream ost;
  376. ost << "sources, inputs lists sizes missmatch (" << sources.size() << "/"
  377. << inputs.size() << ")";
  378. this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, ost.str());
  379. return false;
  380. }
  381. {
  382. auto srcItEnd = sources.end();
  383. auto srcIt = sources.begin();
  384. auto bldIt = builds.begin();
  385. auto optIt = options.begin();
  386. auto inpIt = inputs.begin();
  387. while (srcIt != srcItEnd) {
  388. this->RccJobs.push_back(RccJob{ std::move(*srcIt), std::move(*bldIt),
  389. std::move(*optIt),
  390. std::move(*inpIt) });
  391. ++srcIt;
  392. ++bldIt;
  393. ++optIt;
  394. ++inpIt;
  395. }
  396. }
  397. }
  398. // Initialize source file jobs
  399. {
  400. // Utility lambdas
  401. auto AddJob = [this](std::map<std::string, SourceJob>& jobs,
  402. std::string&& sourceFile) {
  403. const bool moc = !this->MocSkip(sourceFile);
  404. const bool uic = !this->UicSkip(sourceFile);
  405. if (moc || uic) {
  406. SourceJob& job = jobs[std::move(sourceFile)];
  407. job.Moc = moc;
  408. job.Uic = uic;
  409. }
  410. };
  411. // Add header jobs
  412. for (std::string& hdr : InfoGetList("AM_HEADERS")) {
  413. AddJob(this->HeaderJobs, std::move(hdr));
  414. }
  415. // Add source jobs
  416. {
  417. std::vector<std::string> sources = InfoGetList("AM_SOURCES");
  418. // Add header(s) for the source file
  419. for (std::string const& src : sources) {
  420. const bool srcMoc = !this->MocSkip(src);
  421. const bool srcUic = !this->UicSkip(src);
  422. if (!srcMoc && !srcUic) {
  423. continue;
  424. }
  425. // Search for the default header file and a private header
  426. std::array<std::string, 2> headerBases;
  427. headerBases[0] = SubDirPrefix(src);
  428. headerBases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src);
  429. headerBases[1] = headerBases[0];
  430. headerBases[1] += "_p";
  431. for (std::string const& headerBase : headerBases) {
  432. std::string header;
  433. if (this->FindHeader(header, headerBase)) {
  434. const bool moc = srcMoc && !this->MocSkip(header);
  435. const bool uic = srcUic && !this->UicSkip(header);
  436. if (moc || uic) {
  437. SourceJob& job = this->HeaderJobs[std::move(header)];
  438. job.Moc = moc;
  439. job.Uic = uic;
  440. }
  441. }
  442. }
  443. }
  444. // Add Source jobs
  445. for (std::string& src : sources) {
  446. AddJob(this->SourceJobs, std::move(src));
  447. }
  448. }
  449. }
  450. // Init derived information
  451. // ------------------------
  452. // Init file path checksum generator
  453. this->FilePathChecksum.setupParentDirs(
  454. this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir,
  455. this->ProjectBinaryDir);
  456. // include directory
  457. this->AutogenIncludeDir = "include";
  458. if (this->MultiConfig != cmQtAutoGen::SINGLE) {
  459. this->AutogenIncludeDir += this->ConfigSuffix;
  460. }
  461. this->AutogenIncludeDir += "/";
  462. // Moc variables
  463. if (this->MocEnabled()) {
  464. // Mocs compilation file
  465. this->MocCompFileRel = "mocs_compilation";
  466. if (this->MultiConfig == cmQtAutoGen::FULL) {
  467. this->MocCompFileRel += this->ConfigSuffix;
  468. }
  469. this->MocCompFileRel += ".cpp";
  470. this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath(
  471. this->AutogenBuildDir, this->MocCompFileRel);
  472. // Moc predefs file
  473. if (!this->MocPredefsCmd.empty()) {
  474. this->MocPredefsFileRel = "moc_predefs";
  475. if (this->MultiConfig != cmQtAutoGen::SINGLE) {
  476. this->MocPredefsFileRel += this->ConfigSuffix;
  477. }
  478. this->MocPredefsFileRel += ".h";
  479. this->MocPredefsFileAbs = cmSystemTools::CollapseCombinedPath(
  480. this->AutogenBuildDir, this->MocPredefsFileRel);
  481. }
  482. // Sort include directories on demand
  483. if (this->IncludeProjectDirsBefore) {
  484. // Move strings to temporary list
  485. std::list<std::string> includes;
  486. includes.insert(includes.end(), this->MocIncludePaths.begin(),
  487. this->MocIncludePaths.end());
  488. this->MocIncludePaths.clear();
  489. this->MocIncludePaths.reserve(includes.size());
  490. // Append project directories only
  491. {
  492. std::array<std::string const*, 2> const movePaths = {
  493. { &this->ProjectBinaryDir, &this->ProjectSourceDir }
  494. };
  495. for (std::string const* ppath : movePaths) {
  496. std::list<std::string>::iterator it = includes.begin();
  497. while (it != includes.end()) {
  498. std::string const& path = *it;
  499. if (cmSystemTools::StringStartsWith(path, ppath->c_str())) {
  500. this->MocIncludePaths.push_back(path);
  501. it = includes.erase(it);
  502. } else {
  503. ++it;
  504. }
  505. }
  506. }
  507. }
  508. // Append remaining directories
  509. this->MocIncludePaths.insert(this->MocIncludePaths.end(),
  510. includes.begin(), includes.end());
  511. }
  512. // Compose moc includes list
  513. {
  514. std::set<std::string> frameworkPaths;
  515. for (std::string const& path : this->MocIncludePaths) {
  516. this->MocIncludes.push_back("-I" + path);
  517. // Extract framework path
  518. if (cmHasLiteralSuffix(path, ".framework/Headers")) {
  519. // Go up twice to get to the framework root
  520. std::vector<std::string> pathComponents;
  521. cmSystemTools::SplitPath(path, pathComponents);
  522. std::string frameworkPath = cmSystemTools::JoinPath(
  523. pathComponents.begin(), pathComponents.end() - 2);
  524. frameworkPaths.insert(frameworkPath);
  525. }
  526. }
  527. // Append framework includes
  528. for (std::string const& path : frameworkPaths) {
  529. this->MocIncludes.push_back("-F");
  530. this->MocIncludes.push_back(path);
  531. }
  532. }
  533. // Setup single list with all options
  534. {
  535. // Add includes
  536. this->MocAllOptions.insert(this->MocAllOptions.end(),
  537. this->MocIncludes.begin(),
  538. this->MocIncludes.end());
  539. // Add definitions
  540. for (std::string const& def : this->MocDefinitions) {
  541. this->MocAllOptions.push_back("-D" + def);
  542. }
  543. // Add options
  544. this->MocAllOptions.insert(this->MocAllOptions.end(),
  545. this->MocOptions.begin(),
  546. this->MocOptions.end());
  547. }
  548. }
  549. // - Old settings file
  550. {
  551. this->SettingsFile = cmSystemTools::CollapseFullPath(targetDirectory);
  552. cmSystemTools::ConvertToUnixSlashes(this->SettingsFile);
  553. this->SettingsFile += "/AutogenOldSettings";
  554. if (this->MultiConfig != cmQtAutoGen::SINGLE) {
  555. this->SettingsFile += this->ConfigSuffix;
  556. }
  557. this->SettingsFile += ".cmake";
  558. }
  559. return true;
  560. }
  561. void cmQtAutoGenerators::SettingsFileRead(cmMakefile* makefile)
  562. {
  563. // Compose current settings strings
  564. {
  565. cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
  566. std::string const sep(" ~~~ ");
  567. if (this->MocEnabled()) {
  568. std::string str;
  569. str += this->MocExecutable;
  570. str += sep;
  571. str += cmJoin(this->MocAllOptions, ";");
  572. str += sep;
  573. str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE";
  574. str += sep;
  575. str += cmJoin(this->MocPredefsCmd, ";");
  576. str += sep;
  577. this->SettingsStringMoc = crypt.HashString(str);
  578. }
  579. if (this->UicEnabled()) {
  580. std::string str;
  581. str += this->UicExecutable;
  582. str += sep;
  583. str += cmJoin(this->UicTargetOptions, ";");
  584. for (const auto& item : this->UicOptions) {
  585. str += sep;
  586. str += item.first;
  587. str += sep;
  588. str += cmJoin(item.second, ";");
  589. }
  590. str += sep;
  591. this->SettingsStringUic = crypt.HashString(str);
  592. }
  593. if (this->RccEnabled()) {
  594. std::string str;
  595. str += this->RccExecutable;
  596. for (const RccJob& rccJob : this->RccJobs) {
  597. str += sep;
  598. str += rccJob.QrcFile;
  599. str += sep;
  600. str += rccJob.RccFile;
  601. str += sep;
  602. str += cmJoin(rccJob.Options, ";");
  603. }
  604. str += sep;
  605. this->SettingsStringRcc = crypt.HashString(str);
  606. }
  607. }
  608. // Read old settings
  609. if (makefile->ReadListFile(this->SettingsFile.c_str())) {
  610. {
  611. auto SMatch = [makefile](const char* key, std::string const& value) {
  612. return (value == makefile->GetSafeDefinition(key));
  613. };
  614. if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) {
  615. this->MocSettingsChanged = true;
  616. }
  617. if (!SMatch(SettingsKeyUic, this->SettingsStringUic)) {
  618. this->UicSettingsChanged = true;
  619. }
  620. if (!SMatch(SettingsKeyRcc, this->SettingsStringRcc)) {
  621. this->RccSettingsChanged = true;
  622. }
  623. }
  624. // In case any setting changed remove the old settings file.
  625. // This triggers a full rebuild on the next run if the current
  626. // build is aborted before writing the current settings in the end.
  627. if (this->SettingsChanged()) {
  628. cmSystemTools::RemoveFile(this->SettingsFile);
  629. }
  630. } else {
  631. // If the file could not be read re-generate everythiung.
  632. this->MocSettingsChanged = true;
  633. this->UicSettingsChanged = true;
  634. this->RccSettingsChanged = true;
  635. }
  636. }
  637. bool cmQtAutoGenerators::SettingsFileWrite()
  638. {
  639. bool success = true;
  640. // Only write if any setting changed
  641. if (this->SettingsChanged()) {
  642. if (this->Verbose) {
  643. this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " +
  644. cmQtAutoGen::Quoted(this->SettingsFile));
  645. }
  646. // Compose settings file content
  647. std::string settings;
  648. {
  649. auto SettingAppend = [&settings](const char* key,
  650. std::string const& value) {
  651. settings += "set(";
  652. settings += key;
  653. settings += " ";
  654. settings += cmOutputConverter::EscapeForCMake(value);
  655. settings += ")\n";
  656. };
  657. SettingAppend(SettingsKeyMoc, this->SettingsStringMoc);
  658. SettingAppend(SettingsKeyUic, this->SettingsStringUic);
  659. SettingAppend(SettingsKeyRcc, this->SettingsStringRcc);
  660. }
  661. // Write settings file
  662. if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) {
  663. this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile,
  664. "Settings file writing failed");
  665. // Remove old settings file to trigger a full rebuild on the next run
  666. cmSystemTools::RemoveFile(this->SettingsFile);
  667. success = false;
  668. }
  669. }
  670. return success;
  671. }
  672. bool cmQtAutoGenerators::Process()
  673. {
  674. // the program goes through all .cpp files to see which moc files are
  675. // included. It is not really interesting how the moc file is named, but
  676. // what file the moc is created from. Once a moc is included the same moc
  677. // may not be included in the mocs_compilation.cpp file anymore.
  678. // OTOH if there's a header containing Q_OBJECT where no corresponding
  679. // moc file is included anywhere a moc_<filename>.cpp file is created and
  680. // included in the mocs_compilation.cpp file.
  681. // Create AUTOGEN include directory
  682. {
  683. std::string const incDirAbs = cmSystemTools::CollapseCombinedPath(
  684. this->AutogenBuildDir, this->AutogenIncludeDir);
  685. if (!cmSystemTools::MakeDirectory(incDirAbs)) {
  686. this->LogFileError(cmQtAutoGen::GEN, incDirAbs,
  687. "Could not create directory");
  688. return false;
  689. }
  690. }
  691. // Parse source files
  692. for (const auto& item : this->SourceJobs) {
  693. if (!this->ParseSourceFile(item.first, item.second)) {
  694. return false;
  695. }
  696. }
  697. // Parse header files
  698. for (const auto& item : this->HeaderJobs) {
  699. if (!this->ParseHeaderFile(item.first, item.second)) {
  700. return false;
  701. }
  702. }
  703. // Read missing dependency information
  704. if (!this->ParsePostprocess()) {
  705. return false;
  706. }
  707. // Generate files
  708. if (!this->MocGenerateAll()) {
  709. return false;
  710. }
  711. if (!this->UicGenerateAll()) {
  712. return false;
  713. }
  714. if (!this->RccGenerateAll()) {
  715. return false;
  716. }
  717. return true;
  718. }
  719. /**
  720. * @return True on success
  721. */
  722. bool cmQtAutoGenerators::ParseSourceFile(std::string const& absFilename,
  723. const SourceJob& job)
  724. {
  725. std::string contentText;
  726. std::string error;
  727. bool success = ReadFile(contentText, absFilename, &error);
  728. if (success) {
  729. if (!contentText.empty()) {
  730. if (job.Moc) {
  731. success = this->MocParseSourceContent(absFilename, contentText);
  732. }
  733. if (success && job.Uic) {
  734. success = this->UicParseContent(absFilename, contentText);
  735. }
  736. } else {
  737. this->LogFileWarning(cmQtAutoGen::GEN, absFilename,
  738. "The source file is empty");
  739. }
  740. } else {
  741. this->LogFileError(cmQtAutoGen::GEN, absFilename,
  742. "Could not read the source file: " + error);
  743. }
  744. return success;
  745. }
  746. /**
  747. * @return True on success
  748. */
  749. bool cmQtAutoGenerators::ParseHeaderFile(std::string const& absFilename,
  750. const SourceJob& job)
  751. {
  752. std::string contentText;
  753. std::string error;
  754. bool success = ReadFile(contentText, absFilename, &error);
  755. if (success) {
  756. if (!contentText.empty()) {
  757. if (job.Moc) {
  758. this->MocParseHeaderContent(absFilename, contentText);
  759. }
  760. if (job.Uic) {
  761. success = this->UicParseContent(absFilename, contentText);
  762. }
  763. } else {
  764. this->LogFileWarning(cmQtAutoGen::GEN, absFilename,
  765. "The header file is empty");
  766. }
  767. } else {
  768. this->LogFileError(cmQtAutoGen::GEN, absFilename,
  769. "Could not read the header file: " + error);
  770. }
  771. return success;
  772. }
  773. /**
  774. * @return True on success
  775. */
  776. bool cmQtAutoGenerators::ParsePostprocess()
  777. {
  778. bool success = true;
  779. // Read missin dependecies
  780. for (auto& item : this->MocJobsIncluded) {
  781. if (!item->DependsValid) {
  782. std::string content;
  783. std::string error;
  784. if (ReadFile(content, item->SourceFile, &error)) {
  785. this->MocFindDepends(item->SourceFile, content, item->Depends);
  786. item->DependsValid = true;
  787. } else {
  788. std::string emsg = "Could not read file\n ";
  789. emsg += item->SourceFile;
  790. emsg += "\nrequired by moc include \"";
  791. emsg += item->IncludeString;
  792. emsg += "\".\n";
  793. emsg += error;
  794. this->LogFileError(cmQtAutoGen::MOC, item->Includer, emsg);
  795. success = false;
  796. break;
  797. }
  798. }
  799. }
  800. return success;
  801. }
  802. /**
  803. * @brief Tests if the file should be ignored for moc scanning
  804. * @return True if the file should be ignored
  805. */
  806. bool cmQtAutoGenerators::MocSkip(std::string const& absFilename) const
  807. {
  808. if (this->MocEnabled()) {
  809. // Test if the file name is on the skip list
  810. if (!ListContains(this->MocSkipList, absFilename)) {
  811. return false;
  812. }
  813. }
  814. return true;
  815. }
  816. /**
  817. * @brief Tests if the C++ content requires moc processing
  818. * @return True if moc is required
  819. */
  820. bool cmQtAutoGenerators::MocRequired(std::string const& contentText,
  821. std::string* macroName)
  822. {
  823. for (KeyRegExp& filter : this->MocMacroFilters) {
  824. // Run a simple find string operation before the expensive
  825. // regular expression check
  826. if (contentText.find(filter.Key) != std::string::npos) {
  827. if (filter.RegExp.find(contentText)) {
  828. // Return macro name on demand
  829. if (macroName != nullptr) {
  830. *macroName = filter.Key;
  831. }
  832. return true;
  833. }
  834. }
  835. }
  836. return false;
  837. }
  838. std::string cmQtAutoGenerators::MocStringMacros() const
  839. {
  840. std::string res;
  841. const auto itB = this->MocMacroFilters.cbegin();
  842. const auto itE = this->MocMacroFilters.cend();
  843. const auto itL = itE - 1;
  844. auto itC = itB;
  845. for (; itC != itE; ++itC) {
  846. // Separator
  847. if (itC != itB) {
  848. if (itC != itL) {
  849. res += ", ";
  850. } else {
  851. res += " or ";
  852. }
  853. }
  854. // Key
  855. res += itC->Key;
  856. }
  857. return res;
  858. }
  859. std::string cmQtAutoGenerators::MocStringHeaders(
  860. std::string const& fileBase) const
  861. {
  862. std::string res = fileBase;
  863. res += ".{";
  864. res += cmJoin(this->HeaderExtensions, ",");
  865. res += "}";
  866. return res;
  867. }
  868. std::string cmQtAutoGenerators::MocFindIncludedHeader(
  869. std::string const& sourcePath, std::string const& includeBase) const
  870. {
  871. std::string header;
  872. // Search in vicinity of the source
  873. if (!this->FindHeader(header, sourcePath + includeBase)) {
  874. // Search in include directories
  875. for (std::string const& path : this->MocIncludePaths) {
  876. std::string fullPath = path;
  877. fullPath.push_back('/');
  878. fullPath += includeBase;
  879. if (this->FindHeader(header, fullPath)) {
  880. break;
  881. }
  882. }
  883. }
  884. // Sanitize
  885. if (!header.empty()) {
  886. header = cmSystemTools::GetRealPath(header);
  887. }
  888. return header;
  889. }
  890. bool cmQtAutoGenerators::MocFindIncludedFile(
  891. std::string& absFile, std::string const& sourcePath,
  892. std::string const& includeString) const
  893. {
  894. bool success = false;
  895. // Search in vicinity of the source
  896. {
  897. std::string testPath = sourcePath;
  898. testPath += includeString;
  899. if (cmSystemTools::FileExists(testPath.c_str())) {
  900. absFile = cmSystemTools::GetRealPath(testPath);
  901. success = true;
  902. }
  903. }
  904. // Search in include directories
  905. if (!success) {
  906. for (std::string const& path : this->MocIncludePaths) {
  907. std::string fullPath = path;
  908. fullPath.push_back('/');
  909. fullPath += includeString;
  910. if (cmSystemTools::FileExists(fullPath.c_str())) {
  911. absFile = cmSystemTools::GetRealPath(fullPath);
  912. success = true;
  913. break;
  914. }
  915. }
  916. }
  917. return success;
  918. }
  919. bool cmQtAutoGenerators::MocDependFilterPush(std::string const& key,
  920. std::string const& regExp)
  921. {
  922. std::string error;
  923. if (!key.empty()) {
  924. if (!regExp.empty()) {
  925. KeyRegExp filter;
  926. filter.Key = key;
  927. if (filter.RegExp.compile(regExp)) {
  928. this->MocDependFilters.push_back(std::move(filter));
  929. } else {
  930. error = "Regular expression compiling failed";
  931. }
  932. } else {
  933. error = "Regular expression is empty";
  934. }
  935. } else {
  936. error = "Key is empty";
  937. }
  938. if (!error.empty()) {
  939. std::string emsg = "AUTOMOC_DEPEND_FILTERS: ";
  940. emsg += error;
  941. emsg += "\n";
  942. emsg += " Key: ";
  943. emsg += cmQtAutoGen::Quoted(key);
  944. emsg += "\n";
  945. emsg += " RegExp: ";
  946. emsg += cmQtAutoGen::Quoted(regExp);
  947. emsg += "\n";
  948. this->LogError(cmQtAutoGen::MOC, emsg);
  949. return false;
  950. }
  951. return true;
  952. }
  953. void cmQtAutoGenerators::MocFindDepends(std::string const& absFilename,
  954. std::string const& contentText,
  955. std::set<std::string>& depends)
  956. {
  957. if (this->MocDependFilters.empty() && contentText.empty()) {
  958. return;
  959. }
  960. std::vector<std::string> matches;
  961. for (KeyRegExp& filter : this->MocDependFilters) {
  962. // Run a simple find string check
  963. if (contentText.find(filter.Key) != std::string::npos) {
  964. // Run the expensive regular expression check loop
  965. const char* contentChars = contentText.c_str();
  966. while (filter.RegExp.find(contentChars)) {
  967. std::string match = filter.RegExp.match(1);
  968. if (!match.empty()) {
  969. matches.emplace_back(std::move(match));
  970. }
  971. contentChars += filter.RegExp.end();
  972. }
  973. }
  974. }
  975. if (!matches.empty()) {
  976. std::string const sourcePath = SubDirPrefix(absFilename);
  977. for (std::string const& match : matches) {
  978. // Find the dependency file
  979. std::string incFile;
  980. if (this->MocFindIncludedFile(incFile, sourcePath, match)) {
  981. depends.insert(incFile);
  982. if (this->Verbose) {
  983. this->LogInfo(cmQtAutoGen::MOC, "Found dependency:\n " +
  984. cmQtAutoGen::Quoted(absFilename) + "\n " +
  985. cmQtAutoGen::Quoted(incFile));
  986. }
  987. } else {
  988. this->LogFileWarning(cmQtAutoGen::MOC, absFilename,
  989. "Could not find dependency file " +
  990. cmQtAutoGen::Quoted(match));
  991. }
  992. }
  993. }
  994. }
  995. /**
  996. * @return True on success
  997. */
  998. bool cmQtAutoGenerators::MocParseSourceContent(std::string const& absFilename,
  999. std::string const& contentText)
  1000. {
  1001. if (this->Verbose) {
  1002. this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename);
  1003. }
  1004. auto AddJob = [this, &absFilename](std::string const& sourceFile,
  1005. std::string const& includeString,
  1006. std::string const* content) {
  1007. auto job = cm::make_unique<MocJobIncluded>();
  1008. job->SourceFile = sourceFile;
  1009. job->BuildFileRel = this->AutogenIncludeDir;
  1010. job->BuildFileRel += includeString;
  1011. job->Includer = absFilename;
  1012. job->IncludeString = includeString;
  1013. job->DependsValid = (content != nullptr);
  1014. if (job->DependsValid) {
  1015. this->MocFindDepends(sourceFile, *content, job->Depends);
  1016. }
  1017. this->MocJobsIncluded.push_back(std::move(job));
  1018. };
  1019. struct MocInc
  1020. {
  1021. std::string Inc; // full include string
  1022. std::string Dir; // include string directory
  1023. std::string Base; // include string file base
  1024. };
  1025. // Extract moc includes from file
  1026. std::vector<MocInc> mocIncsUsc;
  1027. std::vector<MocInc> mocIncsDot;
  1028. {
  1029. const char* contentChars = contentText.c_str();
  1030. if (strstr(contentChars, "moc") != nullptr) {
  1031. while (this->MocRegExpInclude.find(contentChars)) {
  1032. std::string incString = this->MocRegExpInclude.match(1);
  1033. std::string incDir(SubDirPrefix(incString));
  1034. std::string incBase =
  1035. cmSystemTools::GetFilenameWithoutLastExtension(incString);
  1036. if (cmHasLiteralPrefix(incBase, "moc_")) {
  1037. // moc_<BASE>.cxx
  1038. // Remove the moc_ part from the base name
  1039. mocIncsUsc.push_back(MocInc{ std::move(incString), std::move(incDir),
  1040. incBase.substr(4) });
  1041. } else {
  1042. // <BASE>.moc
  1043. mocIncsDot.push_back(MocInc{ std::move(incString), std::move(incDir),
  1044. std::move(incBase) });
  1045. }
  1046. // Forward content pointer
  1047. contentChars += this->MocRegExpInclude.end();
  1048. }
  1049. }
  1050. }
  1051. std::string selfMacroName;
  1052. const bool selfRequiresMoc = this->MocRequired(contentText, &selfMacroName);
  1053. // Check if there is anything to do
  1054. if (!selfRequiresMoc && mocIncsUsc.empty() && mocIncsDot.empty()) {
  1055. return true;
  1056. }
  1057. // Scan file variables
  1058. std::string const scanFileDir = SubDirPrefix(absFilename);
  1059. std::string const scanFileBase =
  1060. cmSystemTools::GetFilenameWithoutLastExtension(absFilename);
  1061. // Relaxed mode variables
  1062. bool ownDotMocIncluded = false;
  1063. std::string ownMocUscInclude;
  1064. std::string ownMocUscHeader;
  1065. // Process moc_<BASE>.cxx includes
  1066. for (const MocInc& mocInc : mocIncsUsc) {
  1067. std::string const header =
  1068. this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base);
  1069. if (!header.empty()) {
  1070. // Check if header is skipped
  1071. if (this->MocSkip(header)) {
  1072. continue;
  1073. }
  1074. // Register moc job
  1075. AddJob(header, mocInc.Inc, nullptr);
  1076. // Store meta information for relaxed mode
  1077. if (this->MocRelaxedMode && (mocInc.Base == scanFileBase)) {
  1078. ownMocUscInclude = mocInc.Inc;
  1079. ownMocUscHeader = header;
  1080. }
  1081. } else {
  1082. std::string emsg = "The file includes the moc file ";
  1083. emsg += cmQtAutoGen::Quoted(mocInc.Inc);
  1084. emsg += ", but could not find the header ";
  1085. emsg += cmQtAutoGen::Quoted(this->MocStringHeaders(mocInc.Base));
  1086. this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
  1087. return false;
  1088. }
  1089. }
  1090. // Process <BASE>.moc includes
  1091. for (const MocInc& mocInc : mocIncsDot) {
  1092. const bool ownMoc = (mocInc.Base == scanFileBase);
  1093. if (this->MocRelaxedMode) {
  1094. // Relaxed mode
  1095. if (selfRequiresMoc && ownMoc) {
  1096. // Add self
  1097. AddJob(absFilename, mocInc.Inc, &contentText);
  1098. ownDotMocIncluded = true;
  1099. } else {
  1100. // In relaxed mode try to find a header instead but issue a warning.
  1101. // This is for KDE4 compatibility
  1102. std::string const header =
  1103. this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base);
  1104. if (!header.empty()) {
  1105. // Check if header is skipped
  1106. if (this->MocSkip(header)) {
  1107. continue;
  1108. }
  1109. // Register moc job
  1110. AddJob(header, mocInc.Inc, nullptr);
  1111. if (!selfRequiresMoc) {
  1112. if (ownMoc) {
  1113. std::string emsg = "The file includes the moc file ";
  1114. emsg += cmQtAutoGen::Quoted(mocInc.Inc);
  1115. emsg += ", but does not contain a ";
  1116. emsg += this->MocStringMacros();
  1117. emsg += " macro.\nRunning moc on\n ";
  1118. emsg += cmQtAutoGen::Quoted(header);
  1119. emsg += "!\nBetter include ";
  1120. emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
  1121. emsg += " for a compatibility with strict mode.\n"
  1122. "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
  1123. this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
  1124. } else {
  1125. std::string emsg = "The file includes the moc file ";
  1126. emsg += cmQtAutoGen::Quoted(mocInc.Inc);
  1127. emsg += " instead of ";
  1128. emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
  1129. emsg += ".\nRunning moc on\n ";
  1130. emsg += cmQtAutoGen::Quoted(header);
  1131. emsg += "!\nBetter include ";
  1132. emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp");
  1133. emsg += " for compatibility with strict mode.\n"
  1134. "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
  1135. this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
  1136. }
  1137. }
  1138. } else {
  1139. std::string emsg = "The file includes the moc file ";
  1140. emsg += cmQtAutoGen::Quoted(mocInc.Inc);
  1141. emsg += ", which seems to be the moc file from a different "
  1142. "source file. CMake also could not find a matching "
  1143. "header.";
  1144. this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
  1145. return false;
  1146. }
  1147. }
  1148. } else {
  1149. // Strict mode
  1150. if (ownMoc) {
  1151. // Include self
  1152. AddJob(absFilename, mocInc.Inc, &contentText);
  1153. ownDotMocIncluded = true;
  1154. // Accept but issue a warning if moc isn't required
  1155. if (!selfRequiresMoc) {
  1156. std::string emsg = "The file includes the moc file ";
  1157. emsg += cmQtAutoGen::Quoted(mocInc.Inc);
  1158. emsg += ", but does not contain a ";
  1159. emsg += this->MocStringMacros();
  1160. emsg += " macro.";
  1161. this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
  1162. }
  1163. } else {
  1164. // Don't allow <BASE>.moc include other than self in strict mode
  1165. std::string emsg = "The file includes the moc file ";
  1166. emsg += cmQtAutoGen::Quoted(mocInc.Inc);
  1167. emsg += ", which seems to be the moc file from a different "
  1168. "source file.\nThis is not supported. Include ";
  1169. emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
  1170. emsg += " to run moc on this source file.";
  1171. this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
  1172. return false;
  1173. }
  1174. }
  1175. }
  1176. if (selfRequiresMoc && !ownDotMocIncluded) {
  1177. // In this case, check whether the scanned file itself contains a Q_OBJECT.
  1178. // If this is the case, the moc_foo.cpp should probably be generated from
  1179. // foo.cpp instead of foo.h, because otherwise it won't build.
  1180. // But warn, since this is not how it is supposed to be used.
  1181. if (this->MocRelaxedMode && !ownMocUscInclude.empty()) {
  1182. // This is for KDE4 compatibility:
  1183. std::string emsg = "The file contains a ";
  1184. emsg += selfMacroName;
  1185. emsg += " macro, but does not include ";
  1186. emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
  1187. emsg += ". Instead it includes ";
  1188. emsg += cmQtAutoGen::Quoted(ownMocUscInclude);
  1189. emsg += ".\nRunning moc on\n ";
  1190. emsg += cmQtAutoGen::Quoted(absFilename);
  1191. emsg += "!\nBetter include ";
  1192. emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
  1193. emsg += " for compatibility with strict mode.\n"
  1194. "(CMAKE_AUTOMOC_RELAXED_MODE warning)";
  1195. this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg);
  1196. // Remove own header job
  1197. {
  1198. auto itC = this->MocJobsIncluded.begin();
  1199. auto itE = this->MocJobsIncluded.end();
  1200. for (; itC != itE; ++itC) {
  1201. if ((*itC)->SourceFile == ownMocUscHeader) {
  1202. if ((*itC)->IncludeString == ownMocUscInclude) {
  1203. this->MocJobsIncluded.erase(itC);
  1204. break;
  1205. }
  1206. }
  1207. }
  1208. }
  1209. // Add own source job
  1210. AddJob(absFilename, ownMocUscInclude, &contentText);
  1211. } else {
  1212. // Otherwise always error out since it will not compile:
  1213. std::string emsg = "The file contains a ";
  1214. emsg += selfMacroName;
  1215. emsg += " macro, but does not include ";
  1216. emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc");
  1217. emsg += "!\nConsider to\n - add #include \"";
  1218. emsg += scanFileBase;
  1219. emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
  1220. this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg);
  1221. return false;
  1222. }
  1223. }
  1224. return true;
  1225. }
  1226. void cmQtAutoGenerators::MocParseHeaderContent(std::string const& absFilename,
  1227. std::string const& contentText)
  1228. {
  1229. if (this->Verbose) {
  1230. this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename);
  1231. }
  1232. auto const fit =
  1233. std::find_if(this->MocJobsIncluded.cbegin(), this->MocJobsIncluded.cend(),
  1234. [&absFilename](std::unique_ptr<MocJobIncluded> const& job) {
  1235. return job->SourceFile == absFilename;
  1236. });
  1237. if (fit == this->MocJobsIncluded.cend()) {
  1238. if (this->MocRequired(contentText)) {
  1239. auto job = cm::make_unique<MocJobAuto>();
  1240. job->SourceFile = absFilename;
  1241. {
  1242. std::string& bld = job->BuildFileRel;
  1243. bld = this->FilePathChecksum.getPart(absFilename);
  1244. bld += '/';
  1245. bld += "moc_";
  1246. bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename);
  1247. if (this->MultiConfig != cmQtAutoGen::SINGLE) {
  1248. bld += this->ConfigSuffix;
  1249. }
  1250. bld += ".cpp";
  1251. }
  1252. this->MocFindDepends(absFilename, contentText, job->Depends);
  1253. this->MocJobsAuto.push_back(std::move(job));
  1254. }
  1255. }
  1256. }
  1257. bool cmQtAutoGenerators::MocGenerateAll()
  1258. {
  1259. if (!this->MocEnabled()) {
  1260. return true;
  1261. }
  1262. // Look for name collisions in included moc files
  1263. {
  1264. bool collision = false;
  1265. std::map<std::string, std::vector<MocJobIncluded const*>> collisions;
  1266. for (auto const& job : this->MocJobsIncluded) {
  1267. auto& list = collisions[job->IncludeString];
  1268. if (!list.empty()) {
  1269. collision = true;
  1270. }
  1271. list.push_back(job.get());
  1272. }
  1273. if (collision) {
  1274. std::string emsg =
  1275. "Included moc files with the same name will be "
  1276. "generated from different sources.\n"
  1277. "Consider to\n"
  1278. " - not include the \"moc_<NAME>.cpp\" file\n"
  1279. " - add a directory prefix to a \"<NAME>.moc\" include "
  1280. "(e.g \"sub/<NAME>.moc\")\n"
  1281. " - rename the source file(s)\n"
  1282. "Include conflicts\n"
  1283. "-----------------\n";
  1284. const auto& colls = collisions;
  1285. for (auto const& coll : colls) {
  1286. if (coll.second.size() > 1) {
  1287. emsg += cmQtAutoGen::Quoted(coll.first);
  1288. emsg += " included in\n";
  1289. for (const MocJobIncluded* job : coll.second) {
  1290. emsg += " - ";
  1291. emsg += cmQtAutoGen::Quoted(job->Includer);
  1292. emsg += "\n";
  1293. }
  1294. emsg += "would be generated from\n";
  1295. for (const MocJobIncluded* job : coll.second) {
  1296. emsg += " - ";
  1297. emsg += cmQtAutoGen::Quoted(job->SourceFile);
  1298. emsg += "\n";
  1299. }
  1300. }
  1301. }
  1302. this->LogError(cmQtAutoGen::MOC, emsg);
  1303. return false;
  1304. }
  1305. }
  1306. // (Re)generate moc_predefs.h on demand
  1307. if (!this->MocPredefsCmd.empty()) {
  1308. if (this->MocSettingsChanged ||
  1309. !cmSystemTools::FileExists(this->MocPredefsFileAbs)) {
  1310. if (this->Verbose) {
  1311. this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel);
  1312. }
  1313. std::string output;
  1314. {
  1315. // Compose command
  1316. std::vector<std::string> cmd = this->MocPredefsCmd;
  1317. // Add options
  1318. cmd.insert(cmd.end(), this->MocAllOptions.begin(),
  1319. this->MocAllOptions.end());
  1320. // Execute command
  1321. if (!this->RunCommand(cmd, output)) {
  1322. this->LogCommandError(cmQtAutoGen::MOC,
  1323. "moc_predefs generation failed", cmd, output);
  1324. return false;
  1325. }
  1326. }
  1327. // (Re)write predefs file only on demand
  1328. if (this->FileDiffers(this->MocPredefsFileAbs, output)) {
  1329. if (this->FileWrite(cmQtAutoGen::MOC, this->MocPredefsFileAbs,
  1330. output)) {
  1331. this->MocPredefsChanged = true;
  1332. } else {
  1333. this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs,
  1334. "moc_predefs file writing failed");
  1335. return false;
  1336. }
  1337. } else {
  1338. // Touch to update the time stamp
  1339. if (this->Verbose) {
  1340. this->LogInfo(cmQtAutoGen::MOC,
  1341. "Touching moc_predefs " + this->MocPredefsFileRel);
  1342. }
  1343. cmSystemTools::Touch(this->MocPredefsFileAbs, false);
  1344. }
  1345. }
  1346. // Add moc_predefs.h to moc file dependecies
  1347. for (auto const& item : this->MocJobsIncluded) {
  1348. item->Depends.insert(this->MocPredefsFileAbs);
  1349. }
  1350. for (auto const& item : this->MocJobsAuto) {
  1351. item->Depends.insert(this->MocPredefsFileAbs);
  1352. }
  1353. }
  1354. // Generate moc files that are included by source files.
  1355. for (auto const& item : this->MocJobsIncluded) {
  1356. if (!this->MocGenerateFile(*item)) {
  1357. return false;
  1358. }
  1359. }
  1360. // Generate moc files that are _not_ included by source files.
  1361. bool autoNameGenerated = false;
  1362. for (auto const& item : this->MocJobsAuto) {
  1363. if (!this->MocGenerateFile(*item, &autoNameGenerated)) {
  1364. return false;
  1365. }
  1366. }
  1367. // Compose mocs compilation file content
  1368. {
  1369. std::string mocs =
  1370. "// This file is autogenerated. Changes will be overwritten.\n";
  1371. if (this->MocJobsAuto.empty()) {
  1372. // Placeholder content
  1373. mocs +=
  1374. "// No files found that require moc or the moc files are included\n";
  1375. mocs += "enum some_compilers { need_more_than_nothing };\n";
  1376. } else {
  1377. // Valid content
  1378. for (const auto& item : this->MocJobsAuto) {
  1379. mocs += "#include \"";
  1380. mocs += item->BuildFileRel;
  1381. mocs += "\"\n";
  1382. }
  1383. }
  1384. if (this->FileDiffers(this->MocCompFileAbs, mocs)) {
  1385. // Actually write mocs compilation file
  1386. if (this->Verbose) {
  1387. this->LogBold("Generating MOC compilation " + this->MocCompFileRel);
  1388. }
  1389. if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs, mocs)) {
  1390. this->LogFileError(cmQtAutoGen::MOC, this->MocCompFileAbs,
  1391. "mocs compilation file writing failed");
  1392. return false;
  1393. }
  1394. } else if (autoNameGenerated) {
  1395. // Only touch mocs compilation file
  1396. if (this->Verbose) {
  1397. this->LogInfo(cmQtAutoGen::MOC,
  1398. "Touching mocs compilation " + this->MocCompFileRel);
  1399. }
  1400. cmSystemTools::Touch(this->MocCompFileAbs, false);
  1401. }
  1402. }
  1403. return true;
  1404. }
  1405. /**
  1406. * @return True on success
  1407. */
  1408. bool cmQtAutoGenerators::MocGenerateFile(const MocJobAuto& mocJob,
  1409. bool* generated)
  1410. {
  1411. bool success = true;
  1412. std::string const mocFileAbs = cmSystemTools::CollapseCombinedPath(
  1413. this->AutogenBuildDir, mocJob.BuildFileRel);
  1414. bool generate = false;
  1415. std::string generateReason;
  1416. if (!generate && !cmSystemTools::FileExists(mocFileAbs.c_str())) {
  1417. if (this->Verbose) {
  1418. generateReason = "Generating ";
  1419. generateReason += cmQtAutoGen::Quoted(mocFileAbs);
  1420. generateReason += " from its source file ";
  1421. generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
  1422. generateReason += " because it doesn't exist";
  1423. }
  1424. generate = true;
  1425. }
  1426. if (!generate && this->MocSettingsChanged) {
  1427. if (this->Verbose) {
  1428. generateReason = "Generating ";
  1429. generateReason += cmQtAutoGen::Quoted(mocFileAbs);
  1430. generateReason += " from ";
  1431. generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
  1432. generateReason += " because the MOC settings changed";
  1433. }
  1434. generate = true;
  1435. }
  1436. if (!generate && this->MocPredefsChanged) {
  1437. if (this->Verbose) {
  1438. generateReason = "Generating ";
  1439. generateReason += cmQtAutoGen::Quoted(mocFileAbs);
  1440. generateReason += " from ";
  1441. generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
  1442. generateReason += " because moc_predefs.h changed";
  1443. }
  1444. generate = true;
  1445. }
  1446. if (!generate) {
  1447. std::string error;
  1448. if (FileIsOlderThan(mocFileAbs, mocJob.SourceFile, &error)) {
  1449. if (this->Verbose) {
  1450. generateReason = "Generating ";
  1451. generateReason += cmQtAutoGen::Quoted(mocFileAbs);
  1452. generateReason += " because it's older than its source file ";
  1453. generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
  1454. }
  1455. generate = true;
  1456. } else {
  1457. if (!error.empty()) {
  1458. this->LogError(cmQtAutoGen::MOC, error);
  1459. success = false;
  1460. }
  1461. }
  1462. }
  1463. if (success && !generate) {
  1464. // Test if a dependency file is newer
  1465. std::string error;
  1466. for (std::string const& depFile : mocJob.Depends) {
  1467. if (FileIsOlderThan(mocFileAbs, depFile, &error)) {
  1468. if (this->Verbose) {
  1469. generateReason = "Generating ";
  1470. generateReason += cmQtAutoGen::Quoted(mocFileAbs);
  1471. generateReason += " from ";
  1472. generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile);
  1473. generateReason += " because it is older than ";
  1474. generateReason += cmQtAutoGen::Quoted(depFile);
  1475. }
  1476. generate = true;
  1477. break;
  1478. }
  1479. if (!error.empty()) {
  1480. this->LogError(cmQtAutoGen::MOC, error);
  1481. success = false;
  1482. break;
  1483. }
  1484. }
  1485. }
  1486. if (generate) {
  1487. // Log
  1488. if (this->Verbose) {
  1489. this->LogBold("Generating MOC source " + mocJob.BuildFileRel);
  1490. this->LogInfo(cmQtAutoGen::MOC, generateReason);
  1491. }
  1492. // Make sure the parent directory exists
  1493. if (this->MakeParentDirectory(cmQtAutoGen::MOC, mocFileAbs)) {
  1494. // Compose moc command
  1495. std::vector<std::string> cmd;
  1496. cmd.push_back(this->MocExecutable);
  1497. // Add options
  1498. cmd.insert(cmd.end(), this->MocAllOptions.begin(),
  1499. this->MocAllOptions.end());
  1500. // Add predefs include
  1501. if (!this->MocPredefsFileAbs.empty()) {
  1502. cmd.push_back("--include");
  1503. cmd.push_back(this->MocPredefsFileAbs);
  1504. }
  1505. cmd.push_back("-o");
  1506. cmd.push_back(mocFileAbs);
  1507. cmd.push_back(mocJob.SourceFile);
  1508. // Execute moc command
  1509. std::string output;
  1510. if (this->RunCommand(cmd, output)) {
  1511. // Success
  1512. if (generated != nullptr) {
  1513. *generated = true;
  1514. }
  1515. } else {
  1516. // Moc command failed
  1517. {
  1518. std::string emsg = "moc failed for\n ";
  1519. emsg += cmQtAutoGen::Quoted(mocJob.SourceFile);
  1520. this->LogCommandError(cmQtAutoGen::MOC, emsg, cmd, output);
  1521. }
  1522. cmSystemTools::RemoveFile(mocFileAbs);
  1523. success = false;
  1524. }
  1525. } else {
  1526. // Parent directory creation failed
  1527. success = false;
  1528. }
  1529. }
  1530. return success;
  1531. }
  1532. /**
  1533. * @brief Tests if the file name is in the skip list
  1534. */
  1535. bool cmQtAutoGenerators::UicSkip(std::string const& absFilename) const
  1536. {
  1537. if (this->UicEnabled()) {
  1538. // Test if the file name is on the skip list
  1539. if (!ListContains(this->UicSkipList, absFilename)) {
  1540. return false;
  1541. }
  1542. }
  1543. return true;
  1544. }
  1545. bool cmQtAutoGenerators::UicParseContent(std::string const& absFilename,
  1546. std::string const& contentText)
  1547. {
  1548. if (this->Verbose) {
  1549. this->LogInfo(cmQtAutoGen::UIC, "Checking: " + absFilename);
  1550. }
  1551. std::vector<std::string> includes;
  1552. // Extracte includes
  1553. {
  1554. const char* contentChars = contentText.c_str();
  1555. if (strstr(contentChars, "ui_") != nullptr) {
  1556. while (this->UicRegExpInclude.find(contentChars)) {
  1557. includes.push_back(this->UicRegExpInclude.match(1));
  1558. contentChars += this->UicRegExpInclude.end();
  1559. }
  1560. }
  1561. }
  1562. for (std::string const& includeString : includes) {
  1563. std::string uiInputFile;
  1564. if (!UicFindIncludedFile(uiInputFile, absFilename, includeString)) {
  1565. return false;
  1566. }
  1567. // Check if this file should be skipped
  1568. if (this->UicSkip(uiInputFile)) {
  1569. continue;
  1570. }
  1571. // Check if the job already exists
  1572. bool jobExists = false;
  1573. for (const auto& job : this->UicJobs) {
  1574. if ((job->SourceFile == uiInputFile) &&
  1575. (job->IncludeString == includeString)) {
  1576. jobExists = true;
  1577. break;
  1578. }
  1579. }
  1580. if (!jobExists) {
  1581. auto job = cm::make_unique<UicJob>();
  1582. job->SourceFile = uiInputFile;
  1583. job->BuildFileRel = this->AutogenIncludeDir;
  1584. job->BuildFileRel += includeString;
  1585. job->Includer = absFilename;
  1586. job->IncludeString = includeString;
  1587. this->UicJobs.push_back(std::move(job));
  1588. }
  1589. }
  1590. return true;
  1591. }
  1592. bool cmQtAutoGenerators::UicFindIncludedFile(std::string& absFile,
  1593. std::string const& sourceFile,
  1594. std::string const& includeString)
  1595. {
  1596. bool success = false;
  1597. std::string searchFile =
  1598. cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3);
  1599. searchFile += ".ui";
  1600. // Collect search paths list
  1601. std::vector<std::string> testFiles;
  1602. {
  1603. std::string const searchPath = SubDirPrefix(includeString);
  1604. std::string searchFileFull;
  1605. if (!searchPath.empty()) {
  1606. searchFileFull = searchPath;
  1607. searchFileFull += searchFile;
  1608. }
  1609. // Vicinity of the source
  1610. {
  1611. std::string const sourcePath = SubDirPrefix(sourceFile);
  1612. testFiles.push_back(sourcePath + searchFile);
  1613. if (!searchPath.empty()) {
  1614. testFiles.push_back(sourcePath + searchFileFull);
  1615. }
  1616. }
  1617. // AUTOUIC search paths
  1618. if (!this->UicSearchPaths.empty()) {
  1619. for (std::string const& sPath : this->UicSearchPaths) {
  1620. testFiles.push_back((sPath + "/").append(searchFile));
  1621. }
  1622. if (!searchPath.empty()) {
  1623. for (std::string const& sPath : this->UicSearchPaths) {
  1624. testFiles.push_back((sPath + "/").append(searchFileFull));
  1625. }
  1626. }
  1627. }
  1628. }
  1629. // Search for the .ui file!
  1630. for (std::string const& testFile : testFiles) {
  1631. if (cmSystemTools::FileExists(testFile.c_str())) {
  1632. absFile = cmSystemTools::GetRealPath(testFile);
  1633. success = true;
  1634. break;
  1635. }
  1636. }
  1637. // Log error
  1638. if (!success) {
  1639. std::string emsg = "Could not find ";
  1640. emsg += cmQtAutoGen::Quoted(searchFile);
  1641. emsg += " in\n";
  1642. for (std::string const& testFile : testFiles) {
  1643. emsg += " ";
  1644. emsg += cmQtAutoGen::Quoted(testFile);
  1645. emsg += "\n";
  1646. }
  1647. this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg);
  1648. }
  1649. return success;
  1650. }
  1651. bool cmQtAutoGenerators::UicGenerateAll()
  1652. {
  1653. if (!this->UicEnabled()) {
  1654. return true;
  1655. }
  1656. // Look for name collisions in included uic files
  1657. {
  1658. bool collision = false;
  1659. std::map<std::string, std::vector<UicJob const*>> collisions;
  1660. for (auto const& job : this->UicJobs) {
  1661. auto& list = collisions[job->IncludeString];
  1662. if (!list.empty()) {
  1663. collision = true;
  1664. }
  1665. list.push_back(job.get());
  1666. }
  1667. if (collision) {
  1668. std::string emsg =
  1669. "Included uic files with the same name will be "
  1670. "generated from different sources.\n"
  1671. "Consider to\n"
  1672. " - add a directory prefix to a \"ui_<NAME>.h\" include "
  1673. "(e.g \"sub/ui_<NAME>.h\")\n"
  1674. " - rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
  1675. "include(s)\n"
  1676. "Include conflicts\n"
  1677. "-----------------\n";
  1678. const auto& colls = collisions;
  1679. for (auto const& coll : colls) {
  1680. if (coll.second.size() > 1) {
  1681. emsg += cmQtAutoGen::Quoted(coll.first);
  1682. emsg += " included in\n";
  1683. for (const UicJob* job : coll.second) {
  1684. emsg += " - ";
  1685. emsg += cmQtAutoGen::Quoted(job->Includer);
  1686. emsg += "\n";
  1687. }
  1688. emsg += "would be generated from\n";
  1689. for (const UicJob* job : coll.second) {
  1690. emsg += " - ";
  1691. emsg += cmQtAutoGen::Quoted(job->SourceFile);
  1692. emsg += "\n";
  1693. }
  1694. }
  1695. }
  1696. this->LogError(cmQtAutoGen::UIC, emsg);
  1697. return false;
  1698. }
  1699. }
  1700. // Generate ui header files
  1701. for (const auto& item : this->UicJobs) {
  1702. if (!this->UicGenerateFile(*item)) {
  1703. return false;
  1704. }
  1705. }
  1706. return true;
  1707. }
  1708. /**
  1709. * @return True on success
  1710. */
  1711. bool cmQtAutoGenerators::UicGenerateFile(const UicJob& uicJob)
  1712. {
  1713. bool success = true;
  1714. std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath(
  1715. this->AutogenBuildDir, uicJob.BuildFileRel);
  1716. bool generate = false;
  1717. std::string generateReason;
  1718. if (!generate && !cmSystemTools::FileExists(uicFileAbs.c_str())) {
  1719. if (this->Verbose) {
  1720. generateReason = "Generating ";
  1721. generateReason += cmQtAutoGen::Quoted(uicFileAbs);
  1722. generateReason += " from its source file ";
  1723. generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
  1724. generateReason += " because it doesn't exist";
  1725. }
  1726. generate = true;
  1727. }
  1728. if (!generate && this->UicSettingsChanged) {
  1729. if (this->Verbose) {
  1730. generateReason = "Generating ";
  1731. generateReason += cmQtAutoGen::Quoted(uicFileAbs);
  1732. generateReason += " from ";
  1733. generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
  1734. generateReason += " because the UIC settings changed";
  1735. }
  1736. generate = true;
  1737. }
  1738. if (!generate) {
  1739. std::string error;
  1740. if (FileIsOlderThan(uicFileAbs, uicJob.SourceFile, &error)) {
  1741. if (this->Verbose) {
  1742. generateReason = "Generating ";
  1743. generateReason += cmQtAutoGen::Quoted(uicFileAbs);
  1744. generateReason += " because it's older than its source file ";
  1745. generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile);
  1746. }
  1747. generate = true;
  1748. } else {
  1749. if (!error.empty()) {
  1750. this->LogError(cmQtAutoGen::UIC, error);
  1751. success = false;
  1752. }
  1753. }
  1754. }
  1755. if (generate) {
  1756. // Log
  1757. if (this->Verbose) {
  1758. this->LogBold("Generating UIC header " + uicJob.BuildFileRel);
  1759. this->LogInfo(cmQtAutoGen::UIC, generateReason);
  1760. }
  1761. // Make sure the parent directory exists
  1762. if (this->MakeParentDirectory(cmQtAutoGen::UIC, uicFileAbs)) {
  1763. // Compose uic command
  1764. std::vector<std::string> cmd;
  1765. cmd.push_back(this->UicExecutable);
  1766. {
  1767. std::vector<std::string> allOpts = this->UicTargetOptions;
  1768. auto optionIt = this->UicOptions.find(uicJob.SourceFile);
  1769. if (optionIt != this->UicOptions.end()) {
  1770. cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second,
  1771. (this->QtMajorVersion == "5"));
  1772. }
  1773. cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
  1774. }
  1775. cmd.push_back("-o");
  1776. cmd.push_back(uicFileAbs);
  1777. cmd.push_back(uicJob.SourceFile);
  1778. std::string output;
  1779. if (this->RunCommand(cmd, output)) {
  1780. // Success
  1781. } else {
  1782. // Command failed
  1783. {
  1784. std::string emsg = "uic failed for\n ";
  1785. emsg += cmQtAutoGen::Quoted(uicJob.SourceFile);
  1786. emsg += "\nincluded by\n ";
  1787. emsg += cmQtAutoGen::Quoted(uicJob.Includer);
  1788. this->LogCommandError(cmQtAutoGen::UIC, emsg, cmd, output);
  1789. }
  1790. cmSystemTools::RemoveFile(uicFileAbs);
  1791. success = false;
  1792. }
  1793. } else {
  1794. // Parent directory creation failed
  1795. success = false;
  1796. }
  1797. }
  1798. return success;
  1799. }
  1800. bool cmQtAutoGenerators::RccGenerateAll()
  1801. {
  1802. if (!this->RccEnabled()) {
  1803. return true;
  1804. }
  1805. // Generate rcc files
  1806. for (const RccJob& rccJob : this->RccJobs) {
  1807. if (!this->RccGenerateFile(rccJob)) {
  1808. return false;
  1809. }
  1810. }
  1811. return true;
  1812. }
  1813. /**
  1814. * @return True on success
  1815. */
  1816. bool cmQtAutoGenerators::RccGenerateFile(const RccJob& rccJob)
  1817. {
  1818. bool success = true;
  1819. bool rccGenerated = false;
  1820. std::string rccFileAbs;
  1821. if (this->MultiConfig == cmQtAutoGen::SINGLE) {
  1822. rccFileAbs = rccJob.RccFile;
  1823. } else {
  1824. rccFileAbs =
  1825. cmQtAutoGen::AppendFilenameSuffix(rccJob.RccFile, this->ConfigSuffix);
  1826. }
  1827. std::string const rccFileRel = cmSystemTools::RelativePath(
  1828. this->AutogenBuildDir.c_str(), rccFileAbs.c_str());
  1829. // Check if regeneration is required
  1830. bool generate = false;
  1831. std::string generateReason;
  1832. if (!cmSystemTools::FileExists(rccJob.QrcFile)) {
  1833. {
  1834. std::string error = "Could not find the file\n ";
  1835. error += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1836. this->LogError(cmQtAutoGen::RCC, error);
  1837. }
  1838. success = false;
  1839. }
  1840. if (success && !generate && !cmSystemTools::FileExists(rccFileAbs.c_str())) {
  1841. if (this->Verbose) {
  1842. generateReason = "Generating ";
  1843. generateReason += cmQtAutoGen::Quoted(rccFileAbs);
  1844. generateReason += " from its source file ";
  1845. generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1846. generateReason += " because it doesn't exist";
  1847. }
  1848. generate = true;
  1849. }
  1850. if (success && !generate && this->RccSettingsChanged) {
  1851. if (this->Verbose) {
  1852. generateReason = "Generating ";
  1853. generateReason += cmQtAutoGen::Quoted(rccFileAbs);
  1854. generateReason += " from ";
  1855. generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1856. generateReason += " because the RCC settings changed";
  1857. }
  1858. generate = true;
  1859. }
  1860. if (success && !generate) {
  1861. std::string error;
  1862. if (FileIsOlderThan(rccFileAbs, rccJob.QrcFile, &error)) {
  1863. if (this->Verbose) {
  1864. generateReason = "Generating ";
  1865. generateReason += cmQtAutoGen::Quoted(rccFileAbs);
  1866. generateReason += " because it is older than ";
  1867. generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1868. }
  1869. generate = true;
  1870. } else {
  1871. if (!error.empty()) {
  1872. this->LogError(cmQtAutoGen::RCC, error);
  1873. success = false;
  1874. }
  1875. }
  1876. }
  1877. if (success && !generate) {
  1878. // Acquire input file list
  1879. std::vector<std::string> readFiles;
  1880. std::vector<std::string> const* files = nullptr;
  1881. if (!rccJob.Inputs.empty()) {
  1882. files = &rccJob.Inputs;
  1883. } else {
  1884. // Read input file list from qrc file
  1885. std::string error;
  1886. if (cmQtAutoGen::RccListInputs(this->QtMajorVersion, this->RccExecutable,
  1887. rccJob.QrcFile, readFiles, &error)) {
  1888. files = &readFiles;
  1889. } else {
  1890. this->LogFileError(cmQtAutoGen::RCC, rccJob.QrcFile, error);
  1891. success = false;
  1892. }
  1893. }
  1894. // Test if any input file is newer than the build file
  1895. if (files != nullptr) {
  1896. std::string error;
  1897. for (std::string const& resFile : *files) {
  1898. if (!cmSystemTools::FileExists(resFile.c_str())) {
  1899. error = "Could not find the file\n ";
  1900. error += cmQtAutoGen::Quoted(resFile);
  1901. error += "\nwhich is listed in\n ";
  1902. error += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1903. break;
  1904. }
  1905. if (FileIsOlderThan(rccFileAbs, resFile, &error)) {
  1906. if (this->Verbose) {
  1907. generateReason = "Generating ";
  1908. generateReason += cmQtAutoGen::Quoted(rccFileAbs);
  1909. generateReason += " from ";
  1910. generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1911. generateReason += " because it is older than ";
  1912. generateReason += cmQtAutoGen::Quoted(resFile);
  1913. }
  1914. generate = true;
  1915. break;
  1916. }
  1917. if (!error.empty()) {
  1918. break;
  1919. }
  1920. }
  1921. // Print error
  1922. if (!error.empty()) {
  1923. this->LogError(cmQtAutoGen::RCC, error);
  1924. success = false;
  1925. }
  1926. }
  1927. }
  1928. // Regenerate on demand
  1929. if (generate) {
  1930. // Log
  1931. if (this->Verbose) {
  1932. this->LogBold("Generating RCC source " + rccFileRel);
  1933. this->LogInfo(cmQtAutoGen::RCC, generateReason);
  1934. }
  1935. // Make sure the parent directory exists
  1936. if (this->MakeParentDirectory(cmQtAutoGen::RCC, rccFileAbs)) {
  1937. // Compose rcc command
  1938. std::vector<std::string> cmd;
  1939. cmd.push_back(this->RccExecutable);
  1940. cmd.insert(cmd.end(), rccJob.Options.begin(), rccJob.Options.end());
  1941. cmd.push_back("-o");
  1942. cmd.push_back(rccFileAbs);
  1943. cmd.push_back(rccJob.QrcFile);
  1944. std::string output;
  1945. if (this->RunCommand(cmd, output)) {
  1946. // Success
  1947. rccGenerated = true;
  1948. } else {
  1949. {
  1950. std::string emsg = "rcc failed for\n ";
  1951. emsg += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1952. this->LogCommandError(cmQtAutoGen::RCC, emsg, cmd, output);
  1953. }
  1954. cmSystemTools::RemoveFile(rccFileAbs);
  1955. success = false;
  1956. }
  1957. } else {
  1958. // Parent directory creation failed
  1959. success = false;
  1960. }
  1961. }
  1962. // Generate a wrapper source file on demand
  1963. if (success && (this->MultiConfig == cmQtAutoGen::WRAP)) {
  1964. // Wrapper file name
  1965. std::string const& wrapperFileAbs = rccJob.RccFile;
  1966. std::string const wrapperFileRel = cmSystemTools::RelativePath(
  1967. this->AutogenBuildDir.c_str(), wrapperFileAbs.c_str());
  1968. // Wrapper file content
  1969. std::string content = "// This is an autogenerated configuration "
  1970. "wrapper file. Changes will be overwritten.\n"
  1971. "#include \"";
  1972. content += cmSystemTools::GetFilenameName(rccFileRel);
  1973. content += "\"\n";
  1974. // Write content to file
  1975. if (this->FileDiffers(wrapperFileAbs, content)) {
  1976. // Write new wrapper file
  1977. if (this->Verbose) {
  1978. this->LogBold("Generating RCC wrapper " + wrapperFileRel);
  1979. }
  1980. if (!this->FileWrite(cmQtAutoGen::RCC, wrapperFileAbs, content)) {
  1981. this->LogFileError(cmQtAutoGen::RCC, wrapperFileAbs,
  1982. "rcc wrapper file writing failed");
  1983. success = false;
  1984. }
  1985. } else if (rccGenerated) {
  1986. // Just touch the wrapper file
  1987. if (this->Verbose) {
  1988. this->LogInfo(cmQtAutoGen::RCC,
  1989. "Touching RCC wrapper " + wrapperFileRel);
  1990. }
  1991. cmSystemTools::Touch(wrapperFileAbs, false);
  1992. }
  1993. }
  1994. return success;
  1995. }
  1996. void cmQtAutoGenerators::LogBold(std::string const& message) const
  1997. {
  1998. cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue |
  1999. cmsysTerminal_Color_ForegroundBold,
  2000. message.c_str(), true, this->ColorOutput);
  2001. }
  2002. void cmQtAutoGenerators::LogInfo(cmQtAutoGen::Generator genType,
  2003. std::string const& message) const
  2004. {
  2005. std::string msg = cmQtAutoGen::GeneratorName(genType);
  2006. msg += ": ";
  2007. msg += message;
  2008. if (msg.back() != '\n') {
  2009. msg.push_back('\n');
  2010. }
  2011. cmSystemTools::Stdout(msg.c_str(), msg.size());
  2012. }
  2013. void cmQtAutoGenerators::LogWarning(cmQtAutoGen::Generator genType,
  2014. std::string const& message) const
  2015. {
  2016. std::string msg = cmQtAutoGen::GeneratorName(genType);
  2017. msg += " warning:";
  2018. if (message.find('\n') == std::string::npos) {
  2019. // Single line message
  2020. msg.push_back(' ');
  2021. } else {
  2022. // Multi line message
  2023. msg.push_back('\n');
  2024. }
  2025. // Message
  2026. msg += message;
  2027. if (msg.back() != '\n') {
  2028. msg.push_back('\n');
  2029. }
  2030. msg.push_back('\n');
  2031. cmSystemTools::Stdout(msg.c_str(), msg.size());
  2032. }
  2033. void cmQtAutoGenerators::LogFileWarning(cmQtAutoGen::Generator genType,
  2034. std::string const& filename,
  2035. std::string const& message) const
  2036. {
  2037. std::string msg = " ";
  2038. msg += cmQtAutoGen::Quoted(filename);
  2039. msg.push_back('\n');
  2040. // Message
  2041. msg += message;
  2042. this->LogWarning(genType, msg);
  2043. }
  2044. void cmQtAutoGenerators::LogError(cmQtAutoGen::Generator genType,
  2045. std::string const& message) const
  2046. {
  2047. std::string msg;
  2048. msg.push_back('\n');
  2049. msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error");
  2050. // Message
  2051. msg += message;
  2052. if (msg.back() != '\n') {
  2053. msg.push_back('\n');
  2054. }
  2055. msg.push_back('\n');
  2056. cmSystemTools::Stderr(msg.c_str(), msg.size());
  2057. }
  2058. void cmQtAutoGenerators::LogFileError(cmQtAutoGen::Generator genType,
  2059. std::string const& filename,
  2060. std::string const& message) const
  2061. {
  2062. std::string emsg = " ";
  2063. emsg += cmQtAutoGen::Quoted(filename);
  2064. emsg += '\n';
  2065. // Message
  2066. emsg += message;
  2067. this->LogError(genType, emsg);
  2068. }
  2069. void cmQtAutoGenerators::LogCommandError(
  2070. cmQtAutoGen::Generator genType, std::string const& message,
  2071. std::vector<std::string> const& command, std::string const& output) const
  2072. {
  2073. std::string msg;
  2074. msg.push_back('\n');
  2075. msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error");
  2076. msg += message;
  2077. if (msg.back() != '\n') {
  2078. msg.push_back('\n');
  2079. }
  2080. msg.push_back('\n');
  2081. msg += HeadLine("Command");
  2082. msg += QuotedCommand(command);
  2083. if (msg.back() != '\n') {
  2084. msg.push_back('\n');
  2085. }
  2086. msg.push_back('\n');
  2087. msg += HeadLine("Output");
  2088. msg += output;
  2089. if (msg.back() != '\n') {
  2090. msg.push_back('\n');
  2091. }
  2092. msg.push_back('\n');
  2093. cmSystemTools::Stderr(msg.c_str(), msg.size());
  2094. }
  2095. /**
  2096. * @brief Generates the parent directory of the given file on demand
  2097. * @return True on success
  2098. */
  2099. bool cmQtAutoGenerators::MakeParentDirectory(cmQtAutoGen::Generator genType,
  2100. std::string const& filename) const
  2101. {
  2102. bool success = true;
  2103. std::string const dirName = cmSystemTools::GetFilenamePath(filename);
  2104. if (!dirName.empty()) {
  2105. if (!cmSystemTools::MakeDirectory(dirName)) {
  2106. this->LogFileError(genType, filename,
  2107. "Could not create parent directory");
  2108. success = false;
  2109. }
  2110. }
  2111. return success;
  2112. }
  2113. bool cmQtAutoGenerators::FileDiffers(std::string const& filename,
  2114. std::string const& content)
  2115. {
  2116. bool differs = true;
  2117. {
  2118. std::string oldContents;
  2119. if (ReadFile(oldContents, filename)) {
  2120. differs = (oldContents != content);
  2121. }
  2122. }
  2123. return differs;
  2124. }
  2125. bool cmQtAutoGenerators::FileWrite(cmQtAutoGen::Generator genType,
  2126. std::string const& filename,
  2127. std::string const& content)
  2128. {
  2129. std::string error;
  2130. // Make sure the parent directory exists
  2131. if (this->MakeParentDirectory(genType, filename)) {
  2132. cmsys::ofstream outfile;
  2133. outfile.open(filename.c_str(),
  2134. (std::ios::out | std::ios::binary | std::ios::trunc));
  2135. if (outfile) {
  2136. outfile << content;
  2137. // Check for write errors
  2138. if (!outfile.good()) {
  2139. error = "File writing failed";
  2140. }
  2141. } else {
  2142. error = "Opening file for writing failed";
  2143. }
  2144. }
  2145. if (!error.empty()) {
  2146. this->LogFileError(genType, filename, error);
  2147. return false;
  2148. }
  2149. return true;
  2150. }
  2151. /**
  2152. * @brief Runs a command and returns true on success
  2153. * @return True on success
  2154. */
  2155. bool cmQtAutoGenerators::RunCommand(std::vector<std::string> const& command,
  2156. std::string& output) const
  2157. {
  2158. // Log command
  2159. if (this->Verbose) {
  2160. std::string qcmd = QuotedCommand(command);
  2161. qcmd.push_back('\n');
  2162. cmSystemTools::Stdout(qcmd.c_str(), qcmd.size());
  2163. }
  2164. // Execute command
  2165. int retVal = 0;
  2166. bool res = cmSystemTools::RunSingleCommand(
  2167. command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE);
  2168. return (res && (retVal == 0));
  2169. }
  2170. /**
  2171. * @brief Tries to find the header file to the given file base path by
  2172. * appending different header extensions
  2173. * @return True on success
  2174. */
  2175. bool cmQtAutoGenerators::FindHeader(std::string& header,
  2176. std::string const& testBasePath) const
  2177. {
  2178. for (std::string const& ext : this->HeaderExtensions) {
  2179. std::string testFilePath(testBasePath);
  2180. testFilePath.push_back('.');
  2181. testFilePath += ext;
  2182. if (cmSystemTools::FileExists(testFilePath.c_str())) {
  2183. header = testFilePath;
  2184. return true;
  2185. }
  2186. }
  2187. return false;
  2188. }