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 "cmQtAutoGenInitializer.h"
7 #include <initializer_list>
10 #include <sstream> // for basic_ios, istringstream
12 #include <unordered_set>
16 #include <cm/algorithm>
17 #include <cm/iterator>
19 #include <cmext/algorithm>
20 #include <cmext/string_view>
22 #include <cm3p/json/value.h>
23 #include <cm3p/json/writer.h>
25 #include "cmsys/SystemInformation.hxx"
27 #include "cmCustomCommand.h"
28 #include "cmCustomCommandLines.h"
29 #include "cmGeneratedFileStream.h"
30 #include "cmGeneratorExpression.h"
31 #include "cmGeneratorTarget.h"
32 #include "cmGlobalGenerator.h"
33 #include "cmLinkItem.h"
34 #include "cmListFileCache.h"
35 #include "cmLocalGenerator.h"
36 #include "cmMakefile.h"
37 #include "cmMessageType.h"
38 #include "cmPolicies.h"
39 #include "cmProperty.h"
40 #include "cmQtAutoGen.h"
41 #include "cmQtAutoGenGlobalInitializer.h"
42 #include "cmSourceFile.h"
43 #include "cmSourceFileLocationKind.h"
44 #include "cmSourceGroup.h"
46 #include "cmStateTypes.h"
47 #include "cmStringAlgorithms.h"
48 #include "cmSystemTools.h"
54 unsigned int GetParallelCPUCount()
56 static unsigned int count = 0;
57 // Detect only on the first call
59 cmsys::SystemInformation info;
62 cm::clamp(info.GetNumberOfPhysicalCPU(), 1u, cmQtAutoGen::ParallelMax);
67 std::string FileProjectRelativePath(cmMakefile* makefile,
68 std::string const& fileName)
72 std::string pSource = cmSystemTools::RelativePath(
73 makefile->GetCurrentSourceDirectory(), fileName);
74 std::string pBinary = cmSystemTools::RelativePath(
75 makefile->GetCurrentBinaryDirectory(), fileName);
76 if (pSource.size() < pBinary.size()) {
77 res = std::move(pSource);
78 } else if (pBinary.size() < fileName.size()) {
79 res = std::move(pBinary);
88 * Tests if targetDepend is a STATIC_LIBRARY and if any of its
89 * recursive STATIC_LIBRARY dependencies depends on targetOrigin
90 * (STATIC_LIBRARY cycle).
92 bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin,
93 cmGeneratorTarget const* targetDepend,
94 std::string const& config)
97 if ((targetOrigin->GetType() == cmStateEnums::STATIC_LIBRARY) &&
98 (targetDepend->GetType() == cmStateEnums::STATIC_LIBRARY)) {
99 std::set<cmGeneratorTarget const*> knownLibs;
100 std::deque<cmGeneratorTarget const*> testLibs;
102 // Insert initial static_library dependency
103 knownLibs.insert(targetDepend);
104 testLibs.push_back(targetDepend);
106 while (!testLibs.empty()) {
107 cmGeneratorTarget const* testTarget = testLibs.front();
108 testLibs.pop_front();
109 // Check if the test target is the origin target (cycle)
110 if (testTarget == targetOrigin) {
114 // Collect all static_library dependencies from the test target
115 cmLinkImplementationLibraries const* libs =
116 testTarget->GetLinkImplementationLibraries(config);
118 for (cmLinkItem const& item : libs->Libraries) {
119 cmGeneratorTarget const* depTarget = item.Target;
121 (depTarget->GetType() == cmStateEnums::STATIC_LIBRARY) &&
122 knownLibs.insert(depTarget).second) {
123 testLibs.push_back(depTarget);
132 /** Sanitizes file search paths. */
133 class SearchPathSanitizer
136 SearchPathSanitizer(cmMakefile* makefile)
137 : SourcePath_(makefile->GetCurrentSourceDirectory())
140 std::vector<std::string> operator()(
141 std::vector<std::string> const& paths) const;
144 std::string SourcePath_;
147 std::vector<std::string> SearchPathSanitizer::operator()(
148 std::vector<std::string> const& paths) const
150 std::vector<std::string> res;
151 res.reserve(paths.size());
152 for (std::string const& srcPath : paths) {
153 // Collapse relative paths
155 cmSystemTools::CollapseFullPath(srcPath, this->SourcePath_);
156 // Remove suffix slashes
157 while (cmHasSuffix(path, '/')) {
160 // Accept only non empty paths
162 res.emplace_back(std::move(path));
168 /** @brief Writes a CMake info file. */
173 void Set(std::string const& key, std::string const& value)
175 this->Value_[key] = value;
177 void SetConfig(std::string const& key,
178 cmQtAutoGenInitializer::ConfigString const& cfgStr);
179 void SetBool(std::string const& key, bool value)
181 this->Value_[key] = value;
183 void SetUInt(std::string const& key, unsigned int value)
185 this->Value_[key] = value;
189 template <typename CONT>
190 static bool MakeArray(Json::Value& jval, CONT const& container);
192 template <typename CONT>
193 static void MakeStringArray(Json::Value& jval, CONT const& container);
196 template <typename CONT>
197 void SetArray(std::string const& key, CONT const& container);
198 template <typename CONT>
200 std::string const& key,
201 cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr);
203 // -- Array of arrays
204 template <typename CONT, typename FUNC>
205 void SetArrayArray(std::string const& key, CONT const& container, FUNC func);
207 // -- Save to json file
208 bool Save(std::string const& filename);
214 void InfoWriter::SetConfig(std::string const& key,
215 cmQtAutoGenInitializer::ConfigString const& cfgStr)
217 this->Set(key, cfgStr.Default);
218 for (auto const& item : cfgStr.Config) {
219 this->Set(cmStrCat(key, '_', item.first), item.second);
223 template <typename CONT>
224 bool InfoWriter::MakeArray(Json::Value& jval, CONT const& container)
226 jval = Json::arrayValue;
227 std::size_t const listSize = cm::size(container);
231 jval.resize(static_cast<unsigned int>(listSize));
235 template <typename CONT>
236 void InfoWriter::MakeStringArray(Json::Value& jval, CONT const& container)
238 if (MakeArray(jval, container)) {
239 Json::ArrayIndex ii = 0;
240 for (std::string const& item : container) {
246 template <typename CONT>
247 void InfoWriter::SetArray(std::string const& key, CONT const& container)
249 MakeStringArray(this->Value_[key], container);
252 template <typename CONT, typename FUNC>
253 void InfoWriter::SetArrayArray(std::string const& key, CONT const& container,
256 Json::Value& jval = this->Value_[key];
257 if (MakeArray(jval, container)) {
258 Json::ArrayIndex ii = 0;
259 for (auto const& citem : container) {
260 Json::Value& aval = jval[ii++];
261 aval = Json::arrayValue;
267 template <typename CONT>
268 void InfoWriter::SetConfigArray(
269 std::string const& key,
270 cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr)
272 this->SetArray(key, cfgStr.Default);
273 for (auto const& item : cfgStr.Config) {
274 this->SetArray(cmStrCat(key, '_', item.first), item.second);
278 bool InfoWriter::Save(std::string const& filename)
280 cmGeneratedFileStream fileStream;
281 fileStream.SetCopyIfDifferent(true);
282 fileStream.Open(filename, false, true);
287 Json::StyledStreamWriter jsonWriter;
289 jsonWriter.write(fileStream, this->Value_);
294 return fileStream.Close();
297 void AddAutogenExecutableToDependencies(
298 cmQtAutoGenInitializer::GenVarsT const& genVars,
299 std::vector<std::string>& dependencies)
301 if (genVars.ExecutableTarget != nullptr) {
302 dependencies.push_back(genVars.ExecutableTarget->Target->GetName());
303 } else if (!genVars.Executable.empty()) {
304 dependencies.push_back(genVars.Executable);
308 } // End of unnamed namespace
310 cmQtAutoGenInitializer::cmQtAutoGenInitializer(
311 cmQtAutoGenGlobalInitializer* globalInitializer,
312 cmGeneratorTarget* genTarget, IntegerVersion const& qtVersion,
313 bool mocEnabled, bool uicEnabled, bool rccEnabled, bool globalAutogenTarget,
314 bool globalAutoRccTarget)
315 : GlobalInitializer(globalInitializer)
316 , GenTarget(genTarget)
317 , GlobalGen(genTarget->GetGlobalGenerator())
318 , LocalGen(genTarget->GetLocalGenerator())
319 , Makefile(genTarget->Makefile)
320 , PathCheckSum(genTarget->Makefile)
321 , QtVersion(qtVersion)
323 this->AutogenTarget.GlobalTarget = globalAutogenTarget;
324 this->Moc.Enabled = mocEnabled;
325 this->Uic.Enabled = uicEnabled;
326 this->Rcc.Enabled = rccEnabled;
327 this->Rcc.GlobalTarget = globalAutoRccTarget;
330 bool cmQtAutoGenInitializer::InitCustomTargets()
333 this->MultiConfig = this->GlobalGen->IsMultiConfig();
334 this->ConfigDefault = this->Makefile->GetDefaultConfiguration();
336 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
341 this->Makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE");
343 unsigned long iVerb = 0;
344 if (cmStrToULong(def, &iVerb)) {
346 this->Verbosity = static_cast<unsigned int>(iVerb);
348 // Non numeric verbosity
359 this->Makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
361 folder = this->Makefile->GetState()->GetGlobalProperty(
362 "AUTOGEN_TARGETS_FOLDER");
364 // Inherit FOLDER property from target (#13688)
366 folder = this->GenTarget->GetProperty("FOLDER");
369 this->TargetsFolder = *folder;
373 // Check status of policy CMP0071 regarding handling of GENERATED files
374 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0071)) {
375 case cmPolicies::WARN:
376 // Ignore GENERATED files but warn
377 this->CMP0071Warn = true;
379 case cmPolicies::OLD:
380 // Ignore GENERATED files
382 case cmPolicies::REQUIRED_IF_USED:
383 case cmPolicies::REQUIRED_ALWAYS:
384 case cmPolicies::NEW:
385 // Process GENERATED files
386 this->CMP0071Accept = true;
390 // Check status of policy CMP0100 regarding handling of .hh headers
391 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0100)) {
392 case cmPolicies::WARN:
393 // Ignore but .hh files but warn
394 this->CMP0100Warn = true;
396 case cmPolicies::OLD:
399 case cmPolicies::REQUIRED_IF_USED:
400 case cmPolicies::REQUIRED_ALWAYS:
401 case cmPolicies::NEW:
403 this->CMP0100Accept = true;
407 // Common directories
409 // Collapsed current binary directory
410 std::string const cbd = cmSystemTools::CollapseFullPath(
411 std::string(), this->Makefile->GetCurrentBinaryDirectory());
414 this->Dir.Info = cmStrCat(cbd, "/CMakeFiles/", this->GenTarget->GetName(),
416 cmSystemTools::ConvertToUnixSlashes(this->Dir.Info);
419 this->Dir.Build = this->GenTarget->GetSafeProperty("AUTOGEN_BUILD_DIR");
420 if (this->Dir.Build.empty()) {
422 cmStrCat(cbd, '/', this->GenTarget->GetName(), "_autogen");
424 cmSystemTools::ConvertToUnixSlashes(this->Dir.Build);
425 // Cleanup build directory
426 this->AddCleanFile(this->Dir.Build);
429 this->Dir.Work = cbd;
430 cmSystemTools::ConvertToUnixSlashes(this->Dir.Work);
433 this->ConfigFileNamesAndGenex(this->Dir.Include, this->Dir.IncludeGenExp,
434 cmStrCat(this->Dir.Build, "/include"), "");
437 // Moc, Uic and _autogen target settings
438 if (this->MocOrUicEnabled()) {
439 // Init moc specific settings
440 if (this->Moc.Enabled && !this->InitMoc()) {
444 // Init uic specific settings
445 if (this->Uic.Enabled && !this->InitUic()) {
449 // Autogen target name
450 this->AutogenTarget.Name =
451 cmStrCat(this->GenTarget->GetName(), "_autogen");
453 // Autogen target parallel processing
455 std::string const& prop =
456 this->GenTarget->GetSafeProperty("AUTOGEN_PARALLEL");
457 if (prop.empty() || (prop == "AUTO")) {
458 // Autodetect number of CPUs
459 this->AutogenTarget.Parallel = GetParallelCPUCount();
461 this->AutogenTarget.Parallel = 1;
465 // Autogen target info and settings files
468 this->AutogenTarget.InfoFile =
469 cmStrCat(this->Dir.Info, "/AutogenInfo.json");
471 // Used settings file
472 this->ConfigFileNames(this->AutogenTarget.SettingsFile,
473 cmStrCat(this->Dir.Info, "/AutogenUsed"), ".txt");
474 this->ConfigFileClean(this->AutogenTarget.SettingsFile);
477 this->ConfigFileNames(this->AutogenTarget.ParseCacheFile,
478 cmStrCat(this->Dir.Info, "/ParseCache"), ".txt");
479 this->ConfigFileClean(this->AutogenTarget.ParseCacheFile);
482 // Autogen target: Compute user defined dependencies
484 this->AutogenTarget.DependOrigin =
485 this->GenTarget->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS");
487 std::string const& deps =
488 this->GenTarget->GetSafeProperty("AUTOGEN_TARGET_DEPENDS");
490 for (std::string const& depName : cmExpandedList(deps)) {
491 // Allow target and file dependencies
492 auto* depTarget = this->Makefile->FindTargetToUse(depName);
494 this->AutogenTarget.DependTargets.insert(depTarget);
496 this->AutogenTarget.DependFiles.insert(depName);
502 if (this->Moc.Enabled) {
504 if (cmIsOn(this->GenTarget->GetProperty("AUTOMOC_PATH_PREFIX"))) {
505 this->Moc.PathPrefix = true;
508 // CMAKE_AUTOMOC_RELAXED_MODE
509 if (this->Makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE")) {
510 this->Moc.RelaxedMode = true;
511 this->Makefile->IssueMessage(
512 MessageType::AUTHOR_WARNING,
513 cmStrCat("AUTOMOC: CMAKE_AUTOMOC_RELAXED_MODE is "
514 "deprecated an will be removed in the future. Consider "
515 "disabling it and converting the target ",
516 this->GenTarget->GetName(), " to regular mode."));
520 cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MOC_OPTIONS"),
523 cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MACRO_NAMES"),
524 this->Moc.MacroNames);
526 auto filterList = cmExpandedList(
527 this->GenTarget->GetSafeProperty("AUTOMOC_DEPEND_FILTERS"));
528 if ((filterList.size() % 2) != 0) {
529 cmSystemTools::Error(
530 cmStrCat("AutoMoc: AUTOMOC_DEPEND_FILTERS predefs size ",
531 filterList.size(), " is not a multiple of 2."));
534 this->Moc.DependFilters.reserve(1 + (filterList.size() / 2));
535 this->Moc.DependFilters.emplace_back(
537 "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
538 "[^\\)]*FILE[ \t]*\"([^\"]+)\"");
539 for (std::size_t ii = 0; ii != filterList.size(); ii += 2) {
540 this->Moc.DependFilters.emplace_back(filterList[ii],
547 // Init rcc specific settings
548 if (this->Rcc.Enabled && !this->InitRcc()) {
552 // Add autogen include directory to the origin target INCLUDE_DIRECTORIES
553 if (this->MocOrUicEnabled() || (this->Rcc.Enabled && this->MultiConfig)) {
554 this->GenTarget->AddIncludeDirectory(this->Dir.IncludeGenExp, true);
558 if (!this->InitScanFiles()) {
562 // Create autogen target
563 if (this->MocOrUicEnabled() && !this->InitAutogenTarget()) {
567 // Create rcc targets
568 if (this->Rcc.Enabled && !this->InitRccTargets()) {
575 bool cmQtAutoGenInitializer::InitMoc()
577 // Mocs compilation file
578 if (this->GlobalGen->IsXcode()) {
579 // XXX(xcode-per-cfg-src): Drop this Xcode-specific code path
580 // when the Xcode generator supports per-config sources.
581 this->Moc.CompilationFile.Default =
582 cmStrCat(this->Dir.Build, "/mocs_compilation.cpp");
583 this->Moc.CompilationFileGenex = this->Moc.CompilationFile.Default;
585 this->ConfigFileNamesAndGenex(
586 this->Moc.CompilationFile, this->Moc.CompilationFileGenex,
587 cmStrCat(this->Dir.Build, "/mocs_compilation"_s), ".cpp"_s);
591 if (this->GenTarget->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
592 (this->QtVersion >= IntegerVersion(5, 8))) {
594 this->Makefile->GetDefExpandList("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND",
595 this->Moc.PredefsCmd);
597 if (!this->Moc.PredefsCmd.empty()) {
598 this->ConfigFileNames(this->Moc.PredefsFile,
599 cmStrCat(this->Dir.Build, "/moc_predefs"), ".h");
605 SearchPathSanitizer sanitizer(this->Makefile);
607 [this, &sanitizer](std::string const& cfg) -> std::vector<std::string> {
608 // Get the include dirs for this target, without stripping the implicit
609 // include dirs off, see issue #13667.
610 std::vector<std::string> dirs;
611 bool const appendImplicit = (this->QtVersion.Major >= 5);
612 this->LocalGen->GetIncludeDirectoriesImplicit(
613 dirs, this->GenTarget, "CXX", cfg, false, appendImplicit);
614 return sanitizer(dirs);
617 // Default configuration include directories
618 this->Moc.Includes.Default = getDirs(this->ConfigDefault);
619 // Other configuration settings
620 if (this->MultiConfig) {
621 for (std::string const& cfg : this->ConfigsList) {
622 std::vector<std::string> dirs = getDirs(cfg);
623 if (dirs == this->Moc.Includes.Default) {
626 this->Moc.Includes.Config[cfg] = std::move(dirs);
631 // Moc compile definitions
633 auto getDefs = [this](std::string const& cfg) -> std::set<std::string> {
634 std::set<std::string> defines;
635 this->LocalGen->GetTargetDefines(this->GenTarget, cfg, "CXX", defines);
637 if (this->Moc.PredefsCmd.empty()) {
638 // Add WIN32 definition if we don't have a moc_predefs.h
639 defines.insert("WIN32");
645 // Default configuration defines
646 this->Moc.Defines.Default = getDefs(this->ConfigDefault);
647 // Other configuration defines
648 if (this->MultiConfig) {
649 for (std::string const& cfg : this->ConfigsList) {
650 std::set<std::string> defines = getDefs(cfg);
651 if (defines == this->Moc.Defines.Default) {
654 this->Moc.Defines.Config[cfg] = std::move(defines);
661 if (!this->GetQtExecutable(this->Moc, "moc", false)) {
664 // Let the _autogen target depend on the moc executable
665 if (this->Moc.ExecutableTarget) {
666 this->AutogenTarget.DependTargets.insert(
667 this->Moc.ExecutableTarget->Target);
674 bool cmQtAutoGenInitializer::InitUic()
678 std::string const& usp =
679 this->GenTarget->GetSafeProperty("AUTOUIC_SEARCH_PATHS");
681 this->Uic.SearchPaths =
682 SearchPathSanitizer(this->Makefile)(cmExpandedList(usp));
685 // Uic target options
687 auto getOpts = [this](std::string const& cfg) -> std::vector<std::string> {
688 std::vector<std::string> opts;
689 this->GenTarget->GetAutoUicOptions(opts, cfg);
694 this->Uic.Options.Default = getOpts(this->ConfigDefault);
695 // Configuration specific options
696 if (this->MultiConfig) {
697 for (std::string const& cfg : this->ConfigsList) {
698 std::vector<std::string> options = getOpts(cfg);
699 if (options == this->Uic.Options.Default) {
702 this->Uic.Options.Config[cfg] = std::move(options);
709 if (!this->GetQtExecutable(this->Uic, "uic", true)) {
712 // Let the _autogen target depend on the uic executable
713 if (this->Uic.ExecutableTarget) {
714 this->AutogenTarget.DependTargets.insert(
715 this->Uic.ExecutableTarget->Target);
722 bool cmQtAutoGenInitializer::InitRcc()
726 if (!this->GetQtExecutable(this->Rcc, "rcc", false)) {
729 // Evaluate test output on demand
730 CompilerFeatures& features = *this->Rcc.ExecutableFeatures;
731 if (!features.Evaluated) {
732 // Look for list options
733 if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) {
734 if (features.HelpOutput.find("--list") != std::string::npos) {
735 features.ListOptions.emplace_back("--list");
736 } else if (features.HelpOutput.find("-list") != std::string::npos) {
737 features.ListOptions.emplace_back("-list");
740 // Evaluation finished
741 features.Evaluated = true;
748 bool cmQtAutoGenInitializer::InitScanFiles()
750 cmake const* cm = this->Makefile->GetCMakeInstance();
751 auto const& kw = this->GlobalInitializer->kw();
753 auto makeMUFile = [this, &kw](cmSourceFile* sf, std::string const& fullPath,
754 std::vector<size_t> const& configs,
755 bool muIt) -> MUFileHandle {
756 MUFileHandle muf = cm::make_unique<MUFile>();
757 muf->FullPath = fullPath;
759 if (!configs.empty() && configs.size() != this->ConfigsList.size()) {
760 muf->Configs = configs;
762 muf->Generated = sf->GetIsGenerated();
763 bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN);
764 muf->SkipMoc = this->Moc.Enabled &&
765 (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOMOC));
766 muf->SkipUic = this->Uic.Enabled &&
767 (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC));
769 muf->MocIt = this->Moc.Enabled && !muf->SkipMoc;
770 muf->UicIt = this->Uic.Enabled && !muf->SkipUic;
775 auto addMUHeader = [this](MUFileHandle&& muf, cm::string_view extension) {
776 cmSourceFile* sf = muf->SF;
777 const bool muIt = (muf->MocIt || muf->UicIt);
778 if (this->CMP0100Accept || (extension != "hh")) {
780 if (muIt && muf->Generated) {
781 this->AutogenTarget.FilesGenerated.emplace_back(muf.get());
783 this->AutogenTarget.Headers.emplace(sf, std::move(muf));
784 } else if (muIt && this->CMP0100Warn) {
785 // Store file for warning message
786 this->AutogenTarget.CMP0100HeadersWarn.push_back(sf);
790 auto addMUSource = [this](MUFileHandle&& muf) {
791 if ((muf->MocIt || muf->UicIt) && muf->Generated) {
792 this->AutogenTarget.FilesGenerated.emplace_back(muf.get());
794 this->AutogenTarget.Sources.emplace(muf->SF, std::move(muf));
797 // Scan through target files
799 // Scan through target files
800 for (cmGeneratorTarget::AllConfigSource const& acs :
801 this->GenTarget->GetAllConfigSources()) {
802 std::string const& fullPath = acs.Source->GetFullPath();
803 std::string const& extLower =
804 cmSystemTools::LowerCase(acs.Source->GetExtension());
806 // Register files that will be scanned by moc or uic
807 if (this->MocOrUicEnabled()) {
808 if (cm->IsAHeaderExtension(extLower)) {
809 addMUHeader(makeMUFile(acs.Source, fullPath, acs.Configs, true),
811 } else if (cm->IsACLikeSourceExtension(extLower)) {
812 addMUSource(makeMUFile(acs.Source, fullPath, acs.Configs, true));
816 // Register rcc enabled files
817 if (this->Rcc.Enabled) {
818 if ((extLower == kw.qrc) &&
819 !acs.Source->GetPropertyAsBool(kw.SKIP_AUTOGEN) &&
820 !acs.Source->GetPropertyAsBool(kw.SKIP_AUTORCC)) {
823 qrc.QrcFile = fullPath;
825 cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
826 qrc.Generated = acs.Source->GetIsGenerated();
829 std::string const& opts =
830 acs.Source->GetSafeProperty(kw.AUTORCC_OPTIONS);
832 cmExpandList(opts, qrc.Options);
835 this->Rcc.Qrcs.push_back(std::move(qrc));
840 // cmGeneratorTarget::GetAllConfigSources computes the target's
841 // sources meta data cache. Clear it so that OBJECT library targets that
842 // are AUTOGEN initialized after this target get their added
843 // mocs_compilation.cpp source acknowledged by this target.
844 this->GenTarget->ClearSourcesCache();
846 // For source files find additional headers and private headers
847 if (this->MocOrUicEnabled()) {
848 // Header search suffixes and extensions
849 static std::initializer_list<cm::string_view> const suffixes{ "", "_p" };
850 auto const& exts = cm->GetHeaderExtensions();
851 // Scan through sources
852 for (auto const& pair : this->AutogenTarget.Sources) {
853 MUFile const& muf = *pair.second;
854 if (muf.MocIt || muf.UicIt) {
855 // Search for the default header file and a private header
856 std::string const& srcFullPath = muf.SF->ResolveFullPath();
857 std::string basePath = cmStrCat(
858 cmQtAutoGen::SubDirPrefix(srcFullPath),
859 cmSystemTools::GetFilenameWithoutLastExtension(srcFullPath));
860 for (auto const& suffix : suffixes) {
861 std::string const suffixedPath = cmStrCat(basePath, suffix);
862 for (auto const& ext : exts) {
863 std::string fullPath = cmStrCat(suffixedPath, '.', ext);
865 auto constexpr locationKind = cmSourceFileLocationKind::Known;
867 this->Makefile->GetSource(fullPath, locationKind);
869 // Check if we know about this header already
870 if (cm::contains(this->AutogenTarget.Headers, sf)) {
873 // We only accept not-GENERATED files that do exist.
874 if (!sf->GetIsGenerated() &&
875 !cmSystemTools::FileExists(fullPath)) {
878 } else if (cmSystemTools::FileExists(fullPath)) {
879 // Create a new source file for the existing file
880 sf = this->Makefile->CreateSource(fullPath, false, locationKind);
884 auto eMuf = makeMUFile(sf, fullPath, muf.Configs, true);
885 // Only process moc/uic when the parent is processed as well
892 addMUHeader(std::move(eMuf), ext);
900 // Scan through all source files in the makefile to extract moc and uic
901 // parameters. Historically we support non target source file parameters.
902 // The reason is that their file names might be discovered from source files
903 // at generation time.
904 if (this->MocOrUicEnabled()) {
905 for (const auto& sf : this->Makefile->GetSourceFiles()) {
906 // sf->GetExtension() is only valid after sf->ResolveFullPath() ...
907 // Since we're iterating over source files that might be not in the
908 // target we need to check for path errors (not existing files).
909 std::string pathError;
910 std::string const& fullPath = sf->ResolveFullPath(&pathError);
911 if (!pathError.empty() || fullPath.empty()) {
914 std::string const& extLower =
915 cmSystemTools::LowerCase(sf->GetExtension());
917 if (cm->IsAHeaderExtension(extLower)) {
918 if (!cm::contains(this->AutogenTarget.Headers, sf.get())) {
919 auto muf = makeMUFile(sf.get(), fullPath, {}, false);
920 if (muf->SkipMoc || muf->SkipUic) {
921 addMUHeader(std::move(muf), extLower);
924 } else if (cm->IsACLikeSourceExtension(extLower)) {
925 if (!cm::contains(this->AutogenTarget.Sources, sf.get())) {
926 auto muf = makeMUFile(sf.get(), fullPath, {}, false);
927 if (muf->SkipMoc || muf->SkipUic) {
928 addMUSource(std::move(muf));
931 } else if (this->Uic.Enabled && (extLower == kw.ui)) {
933 bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN);
935 (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC));
937 // Check if the .ui file has uic options
938 std::string const uicOpts = sf->GetSafeProperty(kw.AUTOUIC_OPTIONS);
939 if (uicOpts.empty()) {
940 this->Uic.UiFilesNoOptions.emplace_back(fullPath);
942 this->Uic.UiFilesWithOptions.emplace_back(fullPath,
943 cmExpandedList(uicOpts));
946 auto uiHeaderRelativePath = cmSystemTools::RelativePath(
947 this->LocalGen->GetCurrentSourceDirectory(),
948 cmSystemTools::GetFilenamePath(fullPath));
950 // Avoid creating a path containing adjacent slashes
951 if (!uiHeaderRelativePath.empty() &&
952 uiHeaderRelativePath.back() != '/') {
953 uiHeaderRelativePath += '/';
956 auto uiHeaderFilePath = cmStrCat(
957 '/', uiHeaderRelativePath, "ui_"_s,
958 cmSystemTools::GetFilenameWithoutLastExtension(fullPath), ".h"_s);
960 ConfigString uiHeader;
961 std::string uiHeaderGenex;
962 this->ConfigFileNamesAndGenex(
963 uiHeader, uiHeaderGenex, cmStrCat(this->Dir.Build, "/include"_s),
966 this->Uic.UiHeaders.emplace_back(
967 std::make_pair(uiHeader, uiHeaderGenex));
969 // Register skipped .ui file
970 this->Uic.SkipUi.insert(fullPath);
976 // Process GENERATED sources and headers
977 if (this->MocOrUicEnabled() && !this->AutogenTarget.FilesGenerated.empty()) {
978 if (this->CMP0071Accept) {
979 // Let the autogen target depend on the GENERATED files
980 for (MUFile* muf : this->AutogenTarget.FilesGenerated) {
981 this->AutogenTarget.DependFiles.insert(muf->FullPath);
983 } else if (this->CMP0071Warn) {
984 cm::string_view property;
985 if (this->Moc.Enabled && this->Uic.Enabled) {
986 property = "SKIP_AUTOGEN";
987 } else if (this->Moc.Enabled) {
988 property = "SKIP_AUTOMOC";
989 } else if (this->Uic.Enabled) {
990 property = "SKIP_AUTOUIC";
993 for (MUFile* muf : this->AutogenTarget.FilesGenerated) {
994 files += cmStrCat(" ", Quoted(muf->FullPath), '\n');
996 this->Makefile->IssueMessage(
997 MessageType::AUTHOR_WARNING,
999 cmPolicies::GetPolicyWarning(cmPolicies::CMP0071), '\n',
1000 "For compatibility, CMake is excluding the GENERATED source "
1002 files, "from processing by ",
1003 cmQtAutoGen::Tools(this->Moc.Enabled, this->Uic.Enabled, false),
1004 ". If any of the files should be processed, set CMP0071 to NEW. "
1005 "If any of the files should not be processed, "
1006 "explicitly exclude them by setting the source file property ",
1007 property, ":\n set_property(SOURCE file.h PROPERTY ", property,
1012 // Generate CMP0100 warning
1013 if (this->MocOrUicEnabled() &&
1014 !this->AutogenTarget.CMP0100HeadersWarn.empty()) {
1015 cm::string_view property;
1016 if (this->Moc.Enabled && this->Uic.Enabled) {
1017 property = "SKIP_AUTOGEN";
1018 } else if (this->Moc.Enabled) {
1019 property = "SKIP_AUTOMOC";
1020 } else if (this->Uic.Enabled) {
1021 property = "SKIP_AUTOUIC";
1024 for (cmSourceFile* sf : this->AutogenTarget.CMP0100HeadersWarn) {
1025 files += cmStrCat(" ", Quoted(sf->GetFullPath()), '\n');
1027 this->Makefile->IssueMessage(
1028 MessageType::AUTHOR_WARNING,
1030 cmPolicies::GetPolicyWarning(cmPolicies::CMP0100), '\n',
1031 "For compatibility, CMake is excluding the header file(s):\n", files,
1032 "from processing by ",
1033 cmQtAutoGen::Tools(this->Moc.Enabled, this->Uic.Enabled, false),
1034 ". If any of the files should be processed, set CMP0100 to NEW. "
1035 "If any of the files should not be processed, "
1036 "explicitly exclude them by setting the source file property ",
1037 property, ":\n set_property(SOURCE file.hh PROPERTY ", property,
1041 // Process qrc files
1042 if (!this->Rcc.Qrcs.empty()) {
1043 const bool modernQt = (this->QtVersion.Major >= 5);
1044 // Target rcc options
1045 std::vector<std::string> optionsTarget =
1046 cmExpandedList(this->GenTarget->GetSafeProperty(kw.AUTORCC_OPTIONS));
1048 // Check if file name is unique
1049 for (Qrc& qrc : this->Rcc.Qrcs) {
1051 for (Qrc const& qrc2 : this->Rcc.Qrcs) {
1052 if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
1058 // Path checksum and file names
1059 for (Qrc& qrc : this->Rcc.Qrcs) {
1061 qrc.QrcPathChecksum = this->PathCheckSum.getPart(qrc.QrcFile);
1063 qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
1064 "/qrc_", qrc.QrcName, ".cpp");
1065 std::string const base = cmStrCat(this->Dir.Info, "/AutoRcc_",
1066 qrc.QrcName, '_', qrc.QrcPathChecksum);
1067 qrc.LockFile = cmStrCat(base, "_Lock.lock");
1068 qrc.InfoFile = cmStrCat(base, "_Info.json");
1069 this->ConfigFileNames(qrc.SettingsFile, cmStrCat(base, "_Used"), ".txt");
1072 for (Qrc& qrc : this->Rcc.Qrcs) {
1074 std::vector<std::string> opts = optionsTarget;
1075 // Merge computed "-name XYZ" option
1077 std::string name = qrc.QrcName;
1078 // Replace '-' with '_'. The former is not valid for symbol names.
1079 std::replace(name.begin(), name.end(), '-', '_');
1081 name += cmStrCat('_', qrc.QrcPathChecksum);
1083 std::vector<std::string> nameOpts;
1084 nameOpts.emplace_back("-name");
1085 nameOpts.emplace_back(std::move(name));
1086 RccMergeOptions(opts, nameOpts, modernQt);
1088 // Merge file option
1089 RccMergeOptions(opts, qrc.Options, modernQt);
1090 qrc.Options = std::move(opts);
1093 for (Qrc& qrc : this->Rcc.Qrcs) {
1094 if (!qrc.Generated) {
1096 RccLister const lister(this->Rcc.Executable,
1097 this->Rcc.ExecutableFeatures->ListOptions);
1098 if (!lister.list(qrc.QrcFile, qrc.Resources, error)) {
1099 cmSystemTools::Error(error);
1109 bool cmQtAutoGenInitializer::InitAutogenTarget()
1111 // Register info file as generated by CMake
1112 this->Makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
1114 // Determine whether to use a depfile for the AUTOGEN target.
1115 const bool useNinjaDepfile = this->QtVersion >= IntegerVersion(5, 15) &&
1116 this->GlobalGen->GetName().find("Ninja") != std::string::npos;
1118 // Files provided by the autogen target
1119 std::vector<std::string> autogenByproducts;
1120 std::vector<std::string> timestampByproducts;
1121 if (this->Moc.Enabled) {
1122 this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true);
1123 if (useNinjaDepfile) {
1124 if (this->MultiConfig) {
1125 // Make all mocs_compilation_<CONFIG>.cpp files byproducts of the
1126 // ${target}_autogen/timestamp custom command.
1127 // We cannot just use Moc.CompilationFileGenex here, because that
1128 // custom command runs cmake_autogen for each configuration.
1129 for (const auto& p : this->Moc.CompilationFile.Config) {
1130 timestampByproducts.push_back(p.second);
1133 timestampByproducts.push_back(this->Moc.CompilationFileGenex);
1136 autogenByproducts.push_back(this->Moc.CompilationFileGenex);
1140 if (this->Uic.Enabled) {
1141 for (const auto& file : this->Uic.UiHeaders) {
1142 this->AddGeneratedSource(file.first, this->Uic);
1143 autogenByproducts.push_back(file.second);
1147 // Compose target comment
1148 std::string autogenComment;
1151 if (this->Moc.Enabled) {
1154 if (this->Uic.Enabled) {
1155 if (!tools.empty()) {
1160 autogenComment = cmStrCat("Automatic ", tools, " for target ",
1161 this->GenTarget->GetName());
1164 // Compose command lines
1165 // FIXME: Take advantage of our per-config mocs_compilation_$<CONFIG>.cpp
1166 // instead of fiddling with the include directories
1167 std::vector<std::string> configs;
1168 this->GlobalGen->GetQtAutoGenConfigs(configs);
1169 bool stdPipesUTF8 = true;
1170 cmCustomCommandLines commandLines;
1171 for (auto const& config : configs) {
1172 commandLines.push_back(cmMakeCommandLine(
1173 { cmSystemTools::GetCMakeCommand(), "-E", "cmake_autogen",
1174 this->AutogenTarget.InfoFile, config }));
1177 // Use PRE_BUILD on demand
1178 bool usePRE_BUILD = false;
1179 if (this->GlobalGen->GetName().find("Visual Studio") != std::string::npos) {
1180 // Under VS use a PRE_BUILD event instead of a separate target to
1181 // reduce the number of targets loaded into the IDE.
1182 // This also works around a VS 11 bug that may skip updating the target:
1183 // https://connect.microsoft.com/VisualStudio/feedback/details/769495
1184 usePRE_BUILD = true;
1186 // Disable PRE_BUILD in some cases
1188 // Cannot use PRE_BUILD with file depends
1189 if (!this->AutogenTarget.DependFiles.empty()) {
1190 usePRE_BUILD = false;
1192 // Cannot use PRE_BUILD when a global autogen target is in place
1193 if (this->AutogenTarget.GlobalTarget) {
1194 usePRE_BUILD = false;
1197 // Create the autogen target/command
1199 // Add additional autogen target dependencies to origin target
1200 for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
1201 this->GenTarget->Target->AddUtility(depTarget->GetName(), false,
1205 if (!this->Uic.UiFilesNoOptions.empty() ||
1206 !this->Uic.UiFilesWithOptions.empty()) {
1207 // Add a generated timestamp file
1208 ConfigString timestampFile;
1209 std::string timestampFileGenex;
1210 ConfigFileNamesAndGenex(timestampFile, timestampFileGenex,
1211 cmStrCat(this->Dir.Build, "/autouic"_s),
1213 this->AddGeneratedSource(timestampFile, this->Uic);
1215 // Add a step in the pre-build command to touch the timestamp file
1216 commandLines.push_back(
1217 cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", "touch",
1218 timestampFileGenex }));
1220 // UIC needs to be re-run if any of the known UI files change or the
1221 // executable itself has been updated
1222 auto uicDependencies = this->Uic.UiFilesNoOptions;
1223 for (auto const& uiFile : this->Uic.UiFilesWithOptions) {
1224 uicDependencies.push_back(uiFile.first);
1226 AddAutogenExecutableToDependencies(this->Uic, uicDependencies);
1228 // Add a rule file to cause the target to build if a dependency has
1229 // changed, which will trigger the pre-build command to run autogen
1230 std::string no_main_dependency;
1231 cmCustomCommandLines no_command_lines;
1232 this->LocalGen->AddCustomCommandToOutput(
1233 timestampFileGenex, uicDependencies, no_main_dependency,
1234 no_command_lines, /*comment=*/"", this->Dir.Work.c_str(),
1235 /*cmp0116=*/cmPolicies::NEW, /*replace=*/false,
1236 /*escapeOldStyle=*/false, /*uses_terminal=*/false,
1237 /*command_expand_lists=*/false, /*depfile=*/"", /*job_pool=*/"",
1241 // Add the pre-build command directly to bypass the OBJECT_LIBRARY
1242 // rejection in cmMakefile::AddCustomCommandToTarget because we know
1243 // PRE_BUILD will work for an OBJECT_LIBRARY in this specific case.
1245 // PRE_BUILD does not support file dependencies!
1246 const std::vector<std::string> no_output;
1247 const std::vector<std::string> no_deps;
1248 cmCustomCommand cc(no_output, autogenByproducts, no_deps, commandLines,
1249 this->Makefile->GetBacktrace(), autogenComment.c_str(),
1250 this->Dir.Work.c_str(), stdPipesUTF8);
1251 cc.SetEscapeOldStyle(false);
1252 cc.SetEscapeAllowMakeVars(true);
1253 this->GenTarget->Target->AddPreBuildCommand(std::move(cc));
1256 // Add link library target dependencies to the autogen target
1258 if (this->AutogenTarget.DependOrigin) {
1259 // add_dependencies/addUtility do not support generator expressions.
1260 // We depend only on the libraries found in all configs therefore.
1261 std::map<cmGeneratorTarget const*, std::size_t> commonTargets;
1262 for (std::string const& config : this->ConfigsList) {
1263 cmLinkImplementationLibraries const* libs =
1264 this->GenTarget->GetLinkImplementationLibraries(config);
1266 for (cmLinkItem const& item : libs->Libraries) {
1267 cmGeneratorTarget const* libTarget = item.Target;
1269 !StaticLibraryCycle(this->GenTarget, libTarget, config)) {
1270 // Increment target config count
1271 commonTargets[libTarget]++;
1276 for (auto const& item : commonTargets) {
1277 if (item.second == this->ConfigsList.size()) {
1278 this->AutogenTarget.DependTargets.insert(item.first->Target);
1283 std::vector<std::string> dependencies(
1284 this->AutogenTarget.DependFiles.begin(),
1285 this->AutogenTarget.DependFiles.end());
1287 if (useNinjaDepfile) {
1288 // Create a custom command that generates a timestamp file and
1289 // has a depfile assigned. The depfile is created by JobDepFilesMergeT.
1291 // Also create an additional '_autogen_timestamp_deps' that the custom
1292 // command will depend on. It will have no sources or commands to
1293 // execute, but it will have dependencies that would originally be
1294 // assigned to the pre-Qt 5.15 'autogen' target. These dependencies will
1295 // serve as a list of order-only dependencies for the custom command,
1296 // without forcing the custom command to re-execute.
1298 // The dependency tree would then look like
1299 // '_autogen_timestamp_deps (order-only)' <- '/timestamp' file <-
1300 // '_autogen' target.
1301 const auto timestampTargetName =
1302 cmStrCat(this->GenTarget->GetName(), "_autogen_timestamp_deps");
1303 std::vector<std::string> timestampTargetProvides;
1304 cmCustomCommandLines timestampTargetCommandLines;
1306 // Add additional autogen target dependencies to
1307 // '_autogen_timestamp_deps'.
1308 for (const cmTarget* t : this->AutogenTarget.DependTargets) {
1309 dependencies.push_back(t->GetName());
1312 cmTarget* timestampTarget = this->LocalGen->AddUtilityCommand(
1313 timestampTargetName, true, this->Dir.Work.c_str(),
1314 /*byproducts=*/timestampTargetProvides,
1315 /*depends=*/dependencies, timestampTargetCommandLines, cmPolicies::NEW,
1317 this->LocalGen->AddGeneratorTarget(
1318 cm::make_unique<cmGeneratorTarget>(timestampTarget, this->LocalGen));
1320 // Set FOLDER property on the timestamp target, so it appears in the
1321 // appropriate folder in an IDE or in the file api.
1322 if (!this->TargetsFolder.empty()) {
1323 timestampTarget->SetProperty("FOLDER", this->TargetsFolder);
1326 // Make '/timestamp' file depend on '_autogen_timestamp_deps' and on the
1327 // moc and uic executables (whichever are enabled).
1328 dependencies.clear();
1329 dependencies.push_back(timestampTargetName);
1331 AddAutogenExecutableToDependencies(this->Moc, dependencies);
1332 AddAutogenExecutableToDependencies(this->Uic, dependencies);
1334 // Create the custom command that outputs the timestamp file.
1335 const char timestampFileName[] = "timestamp";
1336 const std::string outputFile =
1337 cmStrCat(this->Dir.Build, "/", timestampFileName);
1338 this->AutogenTarget.DepFile = cmStrCat(this->Dir.Build, "/deps");
1339 this->AutogenTarget.DepFileRuleName =
1340 cmStrCat(this->GenTarget->GetName(), "_autogen/", timestampFileName);
1341 commandLines.push_back(cmMakeCommandLine(
1342 { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
1344 this->AddGeneratedSource(outputFile, this->Moc);
1345 const std::string no_main_dependency;
1346 this->LocalGen->AddCustomCommandToOutput(
1347 { outputFile }, timestampByproducts, dependencies, no_main_dependency,
1348 /*implicit_depends=*/{}, commandLines, autogenComment.c_str(),
1349 this->Dir.Work.c_str(),
1350 /*cmp0116=*/cmPolicies::NEW, /*replace=*/false,
1351 /*escapeOldStyle=*/false,
1352 /*uses_terminal=*/false,
1353 /*command_expand_lists=*/false, this->AutogenTarget.DepFile, "",
1356 // Alter variables for the autogen target which now merely wraps the
1358 dependencies.clear();
1359 dependencies.push_back(outputFile);
1360 commandLines.clear();
1361 autogenComment.clear();
1364 // Create autogen target
1365 cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand(
1366 this->AutogenTarget.Name, true, this->Dir.Work.c_str(),
1367 /*byproducts=*/autogenByproducts,
1368 /*depends=*/dependencies, commandLines, cmPolicies::NEW, false,
1369 autogenComment.c_str());
1370 // Create autogen generator target
1371 this->LocalGen->AddGeneratorTarget(
1372 cm::make_unique<cmGeneratorTarget>(autogenTarget, this->LocalGen));
1374 // Forward origin utilities to autogen target
1375 if (this->AutogenTarget.DependOrigin) {
1376 for (BT<std::pair<std::string, bool>> const& depName :
1377 this->GenTarget->GetUtilities()) {
1378 autogenTarget->AddUtility(depName.Value.first, false, this->Makefile);
1381 if (!useNinjaDepfile) {
1382 // Add additional autogen target dependencies to autogen target
1383 for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
1384 autogenTarget->AddUtility(depTarget->GetName(), false, this->Makefile);
1388 // Set FOLDER property in autogen target
1389 if (!this->TargetsFolder.empty()) {
1390 autogenTarget->SetProperty("FOLDER", this->TargetsFolder);
1393 // Add autogen target to the origin target dependencies
1394 this->GenTarget->Target->AddUtility(this->AutogenTarget.Name, false,
1397 // Add autogen target to the global autogen target dependencies
1398 if (this->AutogenTarget.GlobalTarget) {
1399 this->GlobalInitializer->AddToGlobalAutoGen(this->LocalGen,
1400 this->AutogenTarget.Name);
1407 bool cmQtAutoGenInitializer::InitRccTargets()
1409 for (Qrc const& qrc : this->Rcc.Qrcs) {
1410 // Register info file as generated by CMake
1411 this->Makefile->AddCMakeOutputFile(qrc.InfoFile);
1412 // Register file at target
1414 cmSourceFile* sf = this->AddGeneratedSource(qrc.OutputFile, this->Rcc);
1415 sf->SetProperty("SKIP_UNITY_BUILD_INCLUSION", "On");
1418 std::vector<std::string> ccOutput;
1419 ccOutput.push_back(qrc.OutputFile);
1421 std::vector<std::string> ccDepends;
1422 // Add the .qrc and info file to the custom command dependencies
1423 ccDepends.push_back(qrc.QrcFile);
1424 ccDepends.push_back(qrc.InfoFile);
1426 bool stdPipesUTF8 = true;
1427 cmCustomCommandLines commandLines;
1428 if (this->MultiConfig) {
1429 // Build for all configurations
1430 for (std::string const& config : this->ConfigsList) {
1431 commandLines.push_back(
1432 cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
1433 "cmake_autorcc", qrc.InfoFile, config }));
1436 commandLines.push_back(
1437 cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
1438 "cmake_autorcc", qrc.InfoFile, "$<CONFIG>" }));
1440 std::string ccComment =
1441 cmStrCat("Automatic RCC for ",
1442 FileProjectRelativePath(this->Makefile, qrc.QrcFile));
1444 if (qrc.Generated || this->Rcc.GlobalTarget) {
1445 // Create custom rcc target
1448 ccName = cmStrCat(this->GenTarget->GetName(), "_arcc_", qrc.QrcName);
1450 ccName += cmStrCat('_', qrc.QrcPathChecksum);
1453 cmTarget* autoRccTarget = this->LocalGen->AddUtilityCommand(
1454 ccName, true, this->Dir.Work.c_str(), ccOutput, ccDepends,
1455 commandLines, cmPolicies::NEW, false, ccComment.c_str(), false,
1456 false, "", stdPipesUTF8);
1458 // Create autogen generator target
1459 this->LocalGen->AddGeneratorTarget(
1460 cm::make_unique<cmGeneratorTarget>(autoRccTarget, this->LocalGen));
1462 // Set FOLDER property in autogen target
1463 if (!this->TargetsFolder.empty()) {
1464 autoRccTarget->SetProperty("FOLDER", this->TargetsFolder);
1466 if (!this->Rcc.ExecutableTargetName.empty()) {
1467 autoRccTarget->AddUtility(this->Rcc.ExecutableTargetName, false,
1471 // Add autogen target to the origin target dependencies
1472 this->GenTarget->Target->AddUtility(ccName, false, this->Makefile);
1474 // Add autogen target to the global autogen target dependencies
1475 if (this->Rcc.GlobalTarget) {
1476 this->GlobalInitializer->AddToGlobalAutoRcc(this->LocalGen, ccName);
1479 // Create custom rcc command
1481 std::vector<std::string> ccByproducts;
1483 // Add the resource files to the dependencies
1484 for (std::string const& fileName : qrc.Resources) {
1485 // Add resource file to the custom command dependencies
1486 ccDepends.push_back(fileName);
1488 if (!this->Rcc.ExecutableTargetName.empty()) {
1489 ccDepends.push_back(this->Rcc.ExecutableTargetName);
1491 std::string no_main_dependency;
1492 cmImplicitDependsList no_implicit_depends;
1493 this->LocalGen->AddCustomCommandToOutput(
1494 ccOutput, ccByproducts, ccDepends, no_main_dependency,
1495 no_implicit_depends, commandLines, ccComment.c_str(),
1496 this->Dir.Work.c_str(), cmPolicies::NEW, false, true, false, false,
1497 "", "", stdPipesUTF8);
1499 // Reconfigure when .qrc file changes
1500 this->Makefile->AddCMakeDependFile(qrc.QrcFile);
1507 bool cmQtAutoGenInitializer::SetupCustomTargets()
1509 // Create info directory on demand
1510 if (!cmSystemTools::MakeDirectory(this->Dir.Info)) {
1511 cmSystemTools::Error(cmStrCat("AutoGen: Could not create directory: ",
1512 Quoted(this->Dir.Info)));
1516 // Generate autogen target info file
1517 if (this->MocOrUicEnabled()) {
1518 // Write autogen target info files
1519 if (!this->SetupWriteAutogenInfo()) {
1524 // Write AUTORCC info files
1525 return !this->Rcc.Enabled || this->SetupWriteRccInfo();
1528 bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
1531 auto MfDef = [this](std::string const& key) {
1532 return this->Makefile->GetSafeDefinition(key);
1535 // Filtered headers and sources
1536 std::set<std::string> moc_skip;
1537 std::set<std::string> uic_skip;
1538 std::vector<MUFile const*> headers;
1539 std::vector<MUFile const*> sources;
1543 headers.reserve(this->AutogenTarget.Headers.size());
1544 for (auto const& pair : this->AutogenTarget.Headers) {
1545 MUFile const* const muf = pair.second.get();
1547 moc_skip.insert(muf->FullPath);
1550 uic_skip.insert(muf->FullPath);
1552 if (muf->Generated && !this->CMP0071Accept) {
1555 if (muf->MocIt || muf->UicIt) {
1556 headers.emplace_back(muf);
1559 std::sort(headers.begin(), headers.end(),
1560 [](MUFile const* a, MUFile const* b) {
1561 return (a->FullPath < b->FullPath);
1567 sources.reserve(this->AutogenTarget.Sources.size());
1568 for (auto const& pair : this->AutogenTarget.Sources) {
1569 MUFile const* const muf = pair.second.get();
1570 if (muf->Generated && !this->CMP0071Accept) {
1574 moc_skip.insert(muf->FullPath);
1577 uic_skip.insert(muf->FullPath);
1579 if (muf->MocIt || muf->UicIt) {
1580 sources.emplace_back(muf);
1583 std::sort(sources.begin(), sources.end(),
1584 [](MUFile const* a, MUFile const* b) {
1585 return (a->FullPath < b->FullPath);
1593 info.SetBool("MULTI_CONFIG", this->MultiConfig);
1594 info.SetUInt("PARALLEL", this->AutogenTarget.Parallel);
1595 info.SetUInt("VERBOSITY", this->Verbosity);
1598 info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
1599 info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
1600 info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR"));
1601 info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR"));
1602 info.Set("BUILD_DIR", this->Dir.Build);
1603 info.SetConfig("INCLUDE_DIR", this->Dir.Include);
1605 info.SetUInt("QT_VERSION_MAJOR", this->QtVersion.Major);
1606 info.SetUInt("QT_VERSION_MINOR", this->QtVersion.Minor);
1607 info.Set("QT_MOC_EXECUTABLE", this->Moc.Executable);
1608 info.Set("QT_UIC_EXECUTABLE", this->Uic.Executable);
1610 info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
1611 info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile);
1612 info.SetConfig("PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile);
1613 info.Set("DEP_FILE", this->AutogenTarget.DepFile);
1614 info.Set("DEP_FILE_RULE_NAME", this->AutogenTarget.DepFileRuleName);
1615 info.SetArray("CMAKE_LIST_FILES", this->Makefile->GetListFiles());
1616 info.SetArray("HEADER_EXTENSIONS",
1617 this->Makefile->GetCMakeInstance()->GetHeaderExtensions());
1618 auto cfgArray = [this](std::vector<size_t> const& configs) -> Json::Value {
1620 if (!configs.empty()) {
1621 value = Json::arrayValue;
1622 for (size_t ci : configs) {
1623 value.append(this->ConfigsList[ci]);
1628 info.SetArrayArray("HEADERS", headers,
1629 [this, &cfgArray](Json::Value& jval, MUFile const* muf) {
1631 jval[0u] = muf->FullPath;
1632 jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm',
1633 muf->UicIt ? 'U' : 'u');
1634 jval[2u] = this->GetMocBuildPath(*muf);
1635 jval[3u] = cfgArray(muf->Configs);
1638 "SOURCES", sources, [&cfgArray](Json::Value& jval, MUFile const* muf) {
1640 jval[0u] = muf->FullPath;
1641 jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u');
1642 jval[2u] = cfgArray(muf->Configs);
1645 // Write moc settings
1646 if (this->Moc.Enabled) {
1647 info.SetArray("MOC_SKIP", moc_skip);
1648 info.SetConfigArray("MOC_DEFINITIONS", this->Moc.Defines);
1649 info.SetConfigArray("MOC_INCLUDES", this->Moc.Includes);
1650 info.SetArray("MOC_OPTIONS", this->Moc.Options);
1651 info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode);
1652 info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix);
1653 info.SetArray("MOC_MACRO_NAMES", this->Moc.MacroNames);
1655 "MOC_DEPEND_FILTERS", this->Moc.DependFilters,
1656 [](Json::Value& jval, std::pair<std::string, std::string> const& pair) {
1658 jval[0u] = pair.first;
1659 jval[1u] = pair.second;
1661 info.SetConfig("MOC_COMPILATION_FILE", this->Moc.CompilationFile);
1662 info.SetArray("MOC_PREDEFS_CMD", this->Moc.PredefsCmd);
1663 info.SetConfig("MOC_PREDEFS_FILE", this->Moc.PredefsFile);
1666 // Write uic settings
1667 if (this->Uic.Enabled) {
1668 // Add skipped .ui files
1669 uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end());
1671 info.SetArray("UIC_SKIP", uic_skip);
1672 info.SetArrayArray("UIC_UI_FILES", this->Uic.UiFilesWithOptions,
1673 [](Json::Value& jval, UicT::UiFileT const& uiFile) {
1675 jval[0u] = uiFile.first;
1676 InfoWriter::MakeStringArray(jval[1u], uiFile.second);
1678 info.SetConfigArray("UIC_OPTIONS", this->Uic.Options);
1679 info.SetArray("UIC_SEARCH_PATHS", this->Uic.SearchPaths);
1682 info.Save(this->AutogenTarget.InfoFile);
1687 bool cmQtAutoGenInitializer::SetupWriteRccInfo()
1689 for (Qrc const& qrc : this->Rcc.Qrcs) {
1691 auto MfDef = [this](std::string const& key) {
1692 return this->Makefile->GetSafeDefinition(key);
1698 info.SetBool("MULTI_CONFIG", this->MultiConfig);
1699 info.SetUInt("VERBOSITY", this->Verbosity);
1702 info.Set("LOCK_FILE", qrc.LockFile);
1703 info.SetConfig("SETTINGS_FILE", qrc.SettingsFile);
1706 info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
1707 info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
1708 info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR"));
1709 info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR"));
1710 info.Set("BUILD_DIR", this->Dir.Build);
1711 info.SetConfig("INCLUDE_DIR", this->Dir.Include);
1714 info.Set("RCC_EXECUTABLE", this->Rcc.Executable);
1715 info.SetArray("RCC_LIST_OPTIONS",
1716 this->Rcc.ExecutableFeatures->ListOptions);
1719 info.Set("SOURCE", qrc.QrcFile);
1720 info.Set("OUTPUT_CHECKSUM", qrc.QrcPathChecksum);
1721 info.Set("OUTPUT_NAME", cmSystemTools::GetFilenameName(qrc.OutputFile));
1722 info.SetArray("OPTIONS", qrc.Options);
1723 info.SetArray("INPUTS", qrc.Resources);
1725 info.Save(qrc.InfoFile);
1731 cmSourceFile* cmQtAutoGenInitializer::RegisterGeneratedSource(
1732 std::string const& filename)
1734 cmSourceFile* gFile = this->Makefile->GetOrCreateSource(filename, true);
1735 gFile->MarkAsGenerated();
1736 gFile->SetProperty("SKIP_AUTOGEN", "1");
1740 cmSourceFile* cmQtAutoGenInitializer::AddGeneratedSource(
1741 std::string const& filename, GenVarsT const& genVars, bool prepend)
1743 // Register source at makefile
1744 cmSourceFile* gFile = this->RegisterGeneratedSource(filename);
1745 // Add source file to target
1746 this->GenTarget->AddSource(filename, prepend);
1748 // Add source file to source group
1749 this->AddToSourceGroup(filename, genVars.GenNameUpper);
1754 void cmQtAutoGenInitializer::AddGeneratedSource(ConfigString const& filename,
1755 GenVarsT const& genVars,
1758 // XXX(xcode-per-cfg-src): Drop the Xcode-specific part of the condition
1759 // when the Xcode generator supports per-config sources.
1760 if (!this->MultiConfig || this->GlobalGen->IsXcode()) {
1761 this->AddGeneratedSource(filename.Default, genVars, prepend);
1764 for (auto const& cfg : this->ConfigsList) {
1765 std::string const& filenameCfg = filename.Config.at(cfg);
1766 // Register source at makefile
1767 this->RegisterGeneratedSource(filenameCfg);
1768 // Add source file to target for this configuration.
1769 this->GenTarget->AddSource(
1770 cmStrCat("$<$<CONFIG:"_s, cfg, ">:"_s, filenameCfg, ">"_s), prepend);
1771 // Add source file to source group
1772 this->AddToSourceGroup(filenameCfg, genVars.GenNameUpper);
1776 void cmQtAutoGenInitializer::AddToSourceGroup(std::string const& fileName,
1777 cm::string_view genNameUpper)
1779 cmSourceGroup* sourceGroup = nullptr;
1780 // Acquire source group
1782 std::string property;
1783 std::string groupName;
1785 // Prefer generator specific source group name
1786 std::initializer_list<std::string> const props{
1787 cmStrCat(genNameUpper, "_SOURCE_GROUP"), "AUTOGEN_SOURCE_GROUP"
1789 for (std::string const& prop : props) {
1790 cmProp propName = this->Makefile->GetState()->GetGlobalProperty(prop);
1791 if (cmNonempty(propName)) {
1792 groupName = *propName;
1798 // Generate a source group on demand
1799 if (!groupName.empty()) {
1800 sourceGroup = this->Makefile->GetOrCreateSourceGroup(groupName);
1802 cmSystemTools::Error(
1803 cmStrCat(genNameUpper, " error in ", property,
1804 ": Could not find or create the source group ",
1805 cmQtAutoGen::Quoted(groupName)));
1810 sourceGroup->AddGroupFile(fileName);
1814 void cmQtAutoGenInitializer::AddCleanFile(std::string const& fileName)
1816 this->GenTarget->Target->AppendProperty("ADDITIONAL_CLEAN_FILES", fileName,
1820 void cmQtAutoGenInitializer::ConfigFileNames(ConfigString& configString,
1821 cm::string_view prefix,
1822 cm::string_view suffix)
1824 configString.Default = cmStrCat(prefix, suffix);
1825 if (this->MultiConfig) {
1826 for (auto const& cfg : this->ConfigsList) {
1827 configString.Config[cfg] = cmStrCat(prefix, '_', cfg, suffix);
1832 void cmQtAutoGenInitializer::ConfigFileNamesAndGenex(
1833 ConfigString& configString, std::string& genex, cm::string_view const prefix,
1834 cm::string_view const suffix)
1836 this->ConfigFileNames(configString, prefix, suffix);
1837 if (this->MultiConfig) {
1838 genex = cmStrCat(prefix, "_$<CONFIG>"_s, suffix);
1840 genex = configString.Default;
1844 void cmQtAutoGenInitializer::ConfigFileClean(ConfigString& configString)
1846 this->AddCleanFile(configString.Default);
1847 if (this->MultiConfig) {
1848 for (auto const& pair : configString.Config) {
1849 this->AddCleanFile(pair.second);
1854 static cmQtAutoGen::IntegerVersion parseMocVersion(std::string str)
1856 cmQtAutoGen::IntegerVersion result;
1858 static const std::string prelude = "moc ";
1859 size_t pos = str.find(prelude);
1860 if (pos == std::string::npos) {
1864 str.erase(0, prelude.size() + pos);
1865 std::istringstream iss(str);
1868 if (!std::getline(iss, major, '.') || !std::getline(iss, minor, '.')) {
1872 result.Major = static_cast<unsigned int>(std::stoi(major));
1873 result.Minor = static_cast<unsigned int>(std::stoi(minor));
1877 static cmQtAutoGen::IntegerVersion GetMocVersion(
1878 const std::string& mocExecutablePath)
1880 std::string capturedStdOut;
1882 if (!cmSystemTools::RunSingleCommand({ mocExecutablePath, "--version" },
1883 &capturedStdOut, nullptr, &exitCode,
1884 nullptr, cmSystemTools::OUTPUT_NONE)) {
1888 if (exitCode != 0) {
1892 return parseMocVersion(capturedStdOut);
1895 static std::string FindMocExecutableFromMocTarget(cmMakefile* makefile,
1896 unsigned int qtMajorVersion)
1899 const std::string mocTargetName =
1900 "Qt" + std::to_string(qtMajorVersion) + "::moc";
1901 cmTarget* mocTarget = makefile->FindTargetToUse(mocTargetName);
1903 result = mocTarget->GetSafeProperty("IMPORTED_LOCATION");
1908 std::pair<cmQtAutoGen::IntegerVersion, unsigned int>
1909 cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target,
1910 std::string mocExecutable)
1912 // Converts a char ptr to an unsigned int value
1913 auto toUInt = [](const char* const input) -> unsigned int {
1914 unsigned long tmp = 0;
1915 if (input && cmStrToULong(input, &tmp)) {
1916 return static_cast<unsigned int>(tmp);
1920 auto toUInt2 = [](cmProp input) -> unsigned int {
1921 unsigned long tmp = 0;
1922 if (input && cmStrToULong(*input, &tmp)) {
1923 return static_cast<unsigned int>(tmp);
1928 // Initialize return value to a default
1929 std::pair<IntegerVersion, unsigned int> res(
1931 toUInt(target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION",
1934 // Acquire known Qt versions
1935 std::vector<cmQtAutoGen::IntegerVersion> knownQtVersions;
1937 // Qt version variable prefixes
1938 static std::initializer_list<
1939 std::pair<cm::string_view, cm::string_view>> const keys{
1940 { "Qt6Core_VERSION_MAJOR", "Qt6Core_VERSION_MINOR" },
1941 { "Qt5Core_VERSION_MAJOR", "Qt5Core_VERSION_MINOR" },
1942 { "QT_VERSION_MAJOR", "QT_VERSION_MINOR" },
1945 knownQtVersions.reserve(keys.size() * 2);
1947 // Adds a version to the result (nullptr safe)
1948 auto addVersion = [&knownQtVersions, &toUInt2](cmProp major,
1950 cmQtAutoGen::IntegerVersion ver(toUInt2(major), toUInt2(minor));
1951 if (ver.Major != 0) {
1952 knownQtVersions.emplace_back(ver);
1956 // Read versions from variables
1957 for (auto const& keyPair : keys) {
1958 addVersion(target->Makefile->GetDefinition(std::string(keyPair.first)),
1959 target->Makefile->GetDefinition(std::string(keyPair.second)));
1962 // Read versions from directory properties
1963 for (auto const& keyPair : keys) {
1964 addVersion(target->Makefile->GetProperty(std::string(keyPair.first)),
1965 target->Makefile->GetProperty(std::string(keyPair.second)));
1969 // Evaluate known Qt versions
1970 if (!knownQtVersions.empty()) {
1971 if (res.second == 0) {
1972 // No specific version was requested by the target:
1973 // Use highest known Qt version.
1974 res.first = knownQtVersions.at(0);
1976 // Pick a version from the known versions:
1977 for (auto it : knownQtVersions) {
1978 if (it.Major == res.second) {
1986 if (res.first.Major == 0) {
1987 // We could not get the version number from variables or directory
1988 // properties. This might happen if the find_package call for Qt is wrapped
1989 // in a function. Try to find the moc executable path from the available
1990 // targets and call "moc --version" to get the Qt version.
1991 if (mocExecutable.empty()) {
1993 FindMocExecutableFromMocTarget(target->Makefile, res.second);
1995 if (!mocExecutable.empty()) {
1996 res.first = GetMocVersion(mocExecutable);
2003 std::string cmQtAutoGenInitializer::GetMocBuildPath(MUFile const& muf)
2010 std::string basePath =
2011 cmStrCat(this->PathCheckSum.getPart(muf.FullPath), "/moc_",
2012 FileNameWithoutLastExtension(muf.FullPath));
2014 res = cmStrCat(basePath, ".cpp");
2015 if (this->Moc.EmittedBuildPaths.emplace(res).second) {
2019 // File name already emitted.
2020 // Try appending the header suffix to the base path.
2021 basePath = cmStrCat(basePath, '_', muf.SF->GetExtension());
2022 res = cmStrCat(basePath, ".cpp");
2023 if (this->Moc.EmittedBuildPaths.emplace(res).second) {
2027 // File name with header extension already emitted.
2028 // Try adding a number to the base path.
2029 constexpr std::size_t number_begin = 2;
2030 constexpr std::size_t number_end = 256;
2031 for (std::size_t ii = number_begin; ii != number_end; ++ii) {
2032 res = cmStrCat(basePath, '_', ii, ".cpp");
2033 if (this->Moc.EmittedBuildPaths.emplace(res).second) {
2038 // Output file name conflict (unlikely, but still...)
2039 cmSystemTools::Error(
2040 cmStrCat("moc output file name conflict for ", muf.FullPath));
2045 bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars,
2046 const std::string& executable,
2047 bool ignoreMissingTarget) const
2049 auto print_err = [this, &genVars](std::string const& err) {
2050 cmSystemTools::Error(cmStrCat(genVars.GenNameUpper, " for target ",
2051 this->GenTarget->GetName(), ": ", err));
2054 // Custom executable
2056 std::string const prop = cmStrCat(genVars.GenNameUpper, "_EXECUTABLE");
2057 std::string const& val = this->GenTarget->Target->GetSafeProperty(prop);
2059 // Evaluate generator expression
2061 cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
2062 cmGeneratorExpression ge(lfbt);
2063 std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(val);
2064 genVars.Executable = cge->Evaluate(this->LocalGen, "");
2066 if (genVars.Executable.empty() && !ignoreMissingTarget) {
2067 print_err(prop + " evaluates to an empty value");
2071 // Create empty compiler features.
2072 genVars.ExecutableFeatures =
2073 std::make_shared<cmQtAutoGen::CompilerFeatures>();
2078 // Find executable target
2080 // Find executable target name
2081 cm::string_view prefix;
2082 if (this->QtVersion.Major == 4) {
2084 } else if (this->QtVersion.Major == 5) {
2086 } else if (this->QtVersion.Major == 6) {
2089 std::string const targetName = cmStrCat(prefix, executable);
2092 cmGeneratorTarget* genTarget =
2093 this->LocalGen->FindGeneratorTargetToUse(targetName);
2095 genVars.ExecutableTargetName = targetName;
2096 genVars.ExecutableTarget = genTarget;
2097 if (genTarget->IsImported()) {
2098 genVars.Executable = genTarget->ImportedGetLocation("");
2100 genVars.Executable = genTarget->GetLocation("");
2103 if (ignoreMissingTarget) {
2104 // Create empty compiler features.
2105 genVars.ExecutableFeatures =
2106 std::make_shared<cmQtAutoGen::CompilerFeatures>();
2109 print_err(cmStrCat("Could not find ", executable, " executable target ",
2115 // Get executable features
2118 genVars.ExecutableFeatures = this->GlobalInitializer->GetCompilerFeatures(
2119 executable, genVars.Executable, err);
2120 if (!genVars.ExecutableFeatures) {