cmQtAutoGenerators.cxx 73 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328
  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. {
  1822. std::string suffix;
  1823. switch (this->MultiConfig) {
  1824. case cmQtAutoGen::SINGLE:
  1825. break;
  1826. case cmQtAutoGen::WRAP:
  1827. suffix = "_CMAKE";
  1828. suffix += this->ConfigSuffix;
  1829. suffix += "_";
  1830. break;
  1831. case cmQtAutoGen::FULL:
  1832. suffix = this->ConfigSuffix;
  1833. break;
  1834. }
  1835. rccFileAbs = cmQtAutoGen::AppendFilenameSuffix(rccJob.RccFile, suffix);
  1836. }
  1837. std::string const rccFileRel = cmSystemTools::RelativePath(
  1838. this->AutogenBuildDir.c_str(), rccFileAbs.c_str());
  1839. // Check if regeneration is required
  1840. bool generate = false;
  1841. std::string generateReason;
  1842. if (!cmSystemTools::FileExists(rccJob.QrcFile)) {
  1843. {
  1844. std::string error = "Could not find the file\n ";
  1845. error += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1846. this->LogError(cmQtAutoGen::RCC, error);
  1847. }
  1848. success = false;
  1849. }
  1850. if (success && !generate && !cmSystemTools::FileExists(rccFileAbs.c_str())) {
  1851. if (this->Verbose) {
  1852. generateReason = "Generating ";
  1853. generateReason += cmQtAutoGen::Quoted(rccFileAbs);
  1854. generateReason += " from its source file ";
  1855. generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1856. generateReason += " because it doesn't exist";
  1857. }
  1858. generate = true;
  1859. }
  1860. if (success && !generate && this->RccSettingsChanged) {
  1861. if (this->Verbose) {
  1862. generateReason = "Generating ";
  1863. generateReason += cmQtAutoGen::Quoted(rccFileAbs);
  1864. generateReason += " from ";
  1865. generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1866. generateReason += " because the RCC settings changed";
  1867. }
  1868. generate = true;
  1869. }
  1870. if (success && !generate) {
  1871. std::string error;
  1872. if (FileIsOlderThan(rccFileAbs, rccJob.QrcFile, &error)) {
  1873. if (this->Verbose) {
  1874. generateReason = "Generating ";
  1875. generateReason += cmQtAutoGen::Quoted(rccFileAbs);
  1876. generateReason += " because it is older than ";
  1877. generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1878. }
  1879. generate = true;
  1880. } else {
  1881. if (!error.empty()) {
  1882. this->LogError(cmQtAutoGen::RCC, error);
  1883. success = false;
  1884. }
  1885. }
  1886. }
  1887. if (success && !generate) {
  1888. // Acquire input file list
  1889. std::vector<std::string> readFiles;
  1890. std::vector<std::string> const* files = nullptr;
  1891. if (!rccJob.Inputs.empty()) {
  1892. files = &rccJob.Inputs;
  1893. } else {
  1894. // Read input file list from qrc file
  1895. std::string error;
  1896. if (cmQtAutoGen::RccListInputs(this->QtMajorVersion, this->RccExecutable,
  1897. rccJob.QrcFile, readFiles, &error)) {
  1898. files = &readFiles;
  1899. } else {
  1900. this->LogFileError(cmQtAutoGen::RCC, rccJob.QrcFile, error);
  1901. success = false;
  1902. }
  1903. }
  1904. // Test if any input file is newer than the build file
  1905. if (files != nullptr) {
  1906. std::string error;
  1907. for (std::string const& resFile : *files) {
  1908. if (!cmSystemTools::FileExists(resFile.c_str())) {
  1909. error = "Could not find the file\n ";
  1910. error += cmQtAutoGen::Quoted(resFile);
  1911. error += "\nwhich is listed in\n ";
  1912. error += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1913. break;
  1914. }
  1915. if (FileIsOlderThan(rccFileAbs, resFile, &error)) {
  1916. if (this->Verbose) {
  1917. generateReason = "Generating ";
  1918. generateReason += cmQtAutoGen::Quoted(rccFileAbs);
  1919. generateReason += " from ";
  1920. generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1921. generateReason += " because it is older than ";
  1922. generateReason += cmQtAutoGen::Quoted(resFile);
  1923. }
  1924. generate = true;
  1925. break;
  1926. }
  1927. if (!error.empty()) {
  1928. break;
  1929. }
  1930. }
  1931. // Print error
  1932. if (!error.empty()) {
  1933. this->LogError(cmQtAutoGen::RCC, error);
  1934. success = false;
  1935. }
  1936. }
  1937. }
  1938. // Regenerate on demand
  1939. if (generate) {
  1940. // Log
  1941. if (this->Verbose) {
  1942. this->LogBold("Generating RCC source " + rccFileRel);
  1943. this->LogInfo(cmQtAutoGen::RCC, generateReason);
  1944. }
  1945. // Make sure the parent directory exists
  1946. if (this->MakeParentDirectory(cmQtAutoGen::RCC, rccFileAbs)) {
  1947. // Compose rcc command
  1948. std::vector<std::string> cmd;
  1949. cmd.push_back(this->RccExecutable);
  1950. cmd.insert(cmd.end(), rccJob.Options.begin(), rccJob.Options.end());
  1951. cmd.push_back("-o");
  1952. cmd.push_back(rccFileAbs);
  1953. cmd.push_back(rccJob.QrcFile);
  1954. std::string output;
  1955. if (this->RunCommand(cmd, output)) {
  1956. // Success
  1957. rccGenerated = true;
  1958. } else {
  1959. {
  1960. std::string emsg = "rcc failed for\n ";
  1961. emsg += cmQtAutoGen::Quoted(rccJob.QrcFile);
  1962. this->LogCommandError(cmQtAutoGen::RCC, emsg, cmd, output);
  1963. }
  1964. cmSystemTools::RemoveFile(rccFileAbs);
  1965. success = false;
  1966. }
  1967. } else {
  1968. // Parent directory creation failed
  1969. success = false;
  1970. }
  1971. }
  1972. // Generate a wrapper source file on demand
  1973. if (success && (this->MultiConfig == cmQtAutoGen::WRAP)) {
  1974. // Wrapper file name
  1975. std::string const& wrapperFileAbs = rccJob.RccFile;
  1976. std::string const wrapperFileRel = cmSystemTools::RelativePath(
  1977. this->AutogenBuildDir.c_str(), wrapperFileAbs.c_str());
  1978. // Wrapper file content
  1979. std::string content = "// This is an autogenerated configuration "
  1980. "wrapper file. Changes will be overwritten.\n"
  1981. "#include \"";
  1982. content += cmSystemTools::GetFilenameName(rccFileRel);
  1983. content += "\"\n";
  1984. // Write content to file
  1985. if (this->FileDiffers(wrapperFileAbs, content)) {
  1986. // Write new wrapper file
  1987. if (this->Verbose) {
  1988. this->LogBold("Generating RCC wrapper " + wrapperFileRel);
  1989. }
  1990. if (!this->FileWrite(cmQtAutoGen::RCC, wrapperFileAbs, content)) {
  1991. this->LogFileError(cmQtAutoGen::RCC, wrapperFileAbs,
  1992. "rcc wrapper file writing failed");
  1993. success = false;
  1994. }
  1995. } else if (rccGenerated) {
  1996. // Just touch the wrapper file
  1997. if (this->Verbose) {
  1998. this->LogInfo(cmQtAutoGen::RCC,
  1999. "Touching RCC wrapper " + wrapperFileRel);
  2000. }
  2001. cmSystemTools::Touch(wrapperFileAbs, false);
  2002. }
  2003. }
  2004. return success;
  2005. }
  2006. void cmQtAutoGenerators::LogBold(std::string const& message) const
  2007. {
  2008. cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue |
  2009. cmsysTerminal_Color_ForegroundBold,
  2010. message.c_str(), true, this->ColorOutput);
  2011. }
  2012. void cmQtAutoGenerators::LogInfo(cmQtAutoGen::Generator genType,
  2013. std::string const& message) const
  2014. {
  2015. std::string msg = cmQtAutoGen::GeneratorName(genType);
  2016. msg += ": ";
  2017. msg += message;
  2018. if (msg.back() != '\n') {
  2019. msg.push_back('\n');
  2020. }
  2021. cmSystemTools::Stdout(msg.c_str(), msg.size());
  2022. }
  2023. void cmQtAutoGenerators::LogWarning(cmQtAutoGen::Generator genType,
  2024. std::string const& message) const
  2025. {
  2026. std::string msg = cmQtAutoGen::GeneratorName(genType);
  2027. msg += " warning:";
  2028. if (message.find('\n') == std::string::npos) {
  2029. // Single line message
  2030. msg.push_back(' ');
  2031. } else {
  2032. // Multi line message
  2033. msg.push_back('\n');
  2034. }
  2035. // Message
  2036. msg += message;
  2037. if (msg.back() != '\n') {
  2038. msg.push_back('\n');
  2039. }
  2040. msg.push_back('\n');
  2041. cmSystemTools::Stdout(msg.c_str(), msg.size());
  2042. }
  2043. void cmQtAutoGenerators::LogFileWarning(cmQtAutoGen::Generator genType,
  2044. std::string const& filename,
  2045. std::string const& message) const
  2046. {
  2047. std::string msg = " ";
  2048. msg += cmQtAutoGen::Quoted(filename);
  2049. msg.push_back('\n');
  2050. // Message
  2051. msg += message;
  2052. this->LogWarning(genType, msg);
  2053. }
  2054. void cmQtAutoGenerators::LogError(cmQtAutoGen::Generator genType,
  2055. std::string const& message) const
  2056. {
  2057. std::string msg;
  2058. msg.push_back('\n');
  2059. msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error");
  2060. // Message
  2061. msg += message;
  2062. if (msg.back() != '\n') {
  2063. msg.push_back('\n');
  2064. }
  2065. msg.push_back('\n');
  2066. cmSystemTools::Stderr(msg.c_str(), msg.size());
  2067. }
  2068. void cmQtAutoGenerators::LogFileError(cmQtAutoGen::Generator genType,
  2069. std::string const& filename,
  2070. std::string const& message) const
  2071. {
  2072. std::string emsg = " ";
  2073. emsg += cmQtAutoGen::Quoted(filename);
  2074. emsg += '\n';
  2075. // Message
  2076. emsg += message;
  2077. this->LogError(genType, emsg);
  2078. }
  2079. void cmQtAutoGenerators::LogCommandError(
  2080. cmQtAutoGen::Generator genType, std::string const& message,
  2081. std::vector<std::string> const& command, std::string const& output) const
  2082. {
  2083. std::string msg;
  2084. msg.push_back('\n');
  2085. msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error");
  2086. msg += message;
  2087. if (msg.back() != '\n') {
  2088. msg.push_back('\n');
  2089. }
  2090. msg.push_back('\n');
  2091. msg += HeadLine("Command");
  2092. msg += QuotedCommand(command);
  2093. if (msg.back() != '\n') {
  2094. msg.push_back('\n');
  2095. }
  2096. msg.push_back('\n');
  2097. msg += HeadLine("Output");
  2098. msg += output;
  2099. if (msg.back() != '\n') {
  2100. msg.push_back('\n');
  2101. }
  2102. msg.push_back('\n');
  2103. cmSystemTools::Stderr(msg.c_str(), msg.size());
  2104. }
  2105. /**
  2106. * @brief Generates the parent directory of the given file on demand
  2107. * @return True on success
  2108. */
  2109. bool cmQtAutoGenerators::MakeParentDirectory(cmQtAutoGen::Generator genType,
  2110. std::string const& filename) const
  2111. {
  2112. bool success = true;
  2113. std::string const dirName = cmSystemTools::GetFilenamePath(filename);
  2114. if (!dirName.empty()) {
  2115. if (!cmSystemTools::MakeDirectory(dirName)) {
  2116. this->LogFileError(genType, filename,
  2117. "Could not create parent directory");
  2118. success = false;
  2119. }
  2120. }
  2121. return success;
  2122. }
  2123. bool cmQtAutoGenerators::FileDiffers(std::string const& filename,
  2124. std::string const& content)
  2125. {
  2126. bool differs = true;
  2127. {
  2128. std::string oldContents;
  2129. if (ReadFile(oldContents, filename)) {
  2130. differs = (oldContents != content);
  2131. }
  2132. }
  2133. return differs;
  2134. }
  2135. bool cmQtAutoGenerators::FileWrite(cmQtAutoGen::Generator genType,
  2136. std::string const& filename,
  2137. std::string const& content)
  2138. {
  2139. std::string error;
  2140. // Make sure the parent directory exists
  2141. if (this->MakeParentDirectory(genType, filename)) {
  2142. cmsys::ofstream outfile;
  2143. outfile.open(filename.c_str(),
  2144. (std::ios::out | std::ios::binary | std::ios::trunc));
  2145. if (outfile) {
  2146. outfile << content;
  2147. // Check for write errors
  2148. if (!outfile.good()) {
  2149. error = "File writing failed";
  2150. }
  2151. } else {
  2152. error = "Opening file for writing failed";
  2153. }
  2154. }
  2155. if (!error.empty()) {
  2156. this->LogFileError(genType, filename, error);
  2157. return false;
  2158. }
  2159. return true;
  2160. }
  2161. /**
  2162. * @brief Runs a command and returns true on success
  2163. * @return True on success
  2164. */
  2165. bool cmQtAutoGenerators::RunCommand(std::vector<std::string> const& command,
  2166. std::string& output) const
  2167. {
  2168. // Log command
  2169. if (this->Verbose) {
  2170. std::string qcmd = QuotedCommand(command);
  2171. qcmd.push_back('\n');
  2172. cmSystemTools::Stdout(qcmd.c_str(), qcmd.size());
  2173. }
  2174. // Execute command
  2175. int retVal = 0;
  2176. bool res = cmSystemTools::RunSingleCommand(
  2177. command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE);
  2178. return (res && (retVal == 0));
  2179. }
  2180. /**
  2181. * @brief Tries to find the header file to the given file base path by
  2182. * appending different header extensions
  2183. * @return True on success
  2184. */
  2185. bool cmQtAutoGenerators::FindHeader(std::string& header,
  2186. std::string const& testBasePath) const
  2187. {
  2188. for (std::string const& ext : this->HeaderExtensions) {
  2189. std::string testFilePath(testBasePath);
  2190. testFilePath.push_back('.');
  2191. testFilePath += ext;
  2192. if (cmSystemTools::FileExists(testFilePath.c_str())) {
  2193. header = testFilePath;
  2194. return true;
  2195. }
  2196. }
  2197. return false;
  2198. }