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 "cmQtAutoGenInitializer.h"
6 #include "cmAlgorithms.h"
7 #include "cmCustomCommand.h"
8 #include "cmCustomCommandLines.h"
9 #include "cmDuration.h"
10 #include "cmFilePathChecksum.h"
11 #include "cmGeneratedFileStream.h"
12 #include "cmGeneratorTarget.h"
13 #include "cmGlobalGenerator.h"
14 #include "cmLinkItem.h"
15 #include "cmLocalGenerator.h"
16 #include "cmMakefile.h"
17 #include "cmOutputConverter.h"
18 #include "cmPolicies.h"
19 #include "cmProcessOutput.h"
20 #include "cmSourceFile.h"
21 #include "cmSourceGroup.h"
23 #include "cmStateTypes.h"
24 #include "cmSystemTools.h"
27 #include "cmsys/FStream.hxx"
28 #include "cmsys/SystemInformation.hxx"
40 inline static const char* SafeString(const char* value)
42 return (value != nullptr) ? value : "";
45 inline static std::string GetSafeProperty(cmGeneratorTarget const* target,
48 return std::string(SafeString(target->GetProperty(key)));
51 inline static std::string GetSafeProperty(cmSourceFile const* sf,
54 return std::string(SafeString(sf->GetProperty(key)));
57 static std::size_t GetParallelCPUCount()
59 static std::size_t count = 0;
60 // Detect only on the first call
62 cmsys::SystemInformation info;
64 count = info.GetNumberOfPhysicalCPU();
65 count = std::max<std::size_t>(count, 1);
66 count = std::min<std::size_t>(count, cmQtAutoGen::ParallelMax);
71 static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName,
72 cmQtAutoGen::GeneratorT genType)
74 cmSourceGroup* sourceGroup = nullptr;
75 // Acquire source group
78 std::string groupName;
80 std::array<std::string, 2> props;
81 // Use generator specific group name
83 case cmQtAutoGen::GeneratorT::MOC:
84 props[0] = "AUTOMOC_SOURCE_GROUP";
86 case cmQtAutoGen::GeneratorT::RCC:
87 props[0] = "AUTORCC_SOURCE_GROUP";
90 props[0] = "AUTOGEN_SOURCE_GROUP";
93 props[1] = "AUTOGEN_SOURCE_GROUP";
94 for (std::string& prop : props) {
95 const char* propName = makefile->GetState()->GetGlobalProperty(prop);
96 if ((propName != nullptr) && (*propName != '\0')) {
98 property = std::move(prop);
103 // Generate a source group on demand
104 if (!groupName.empty()) {
105 sourceGroup = makefile->GetOrCreateSourceGroup(groupName);
106 if (sourceGroup == nullptr) {
107 std::ostringstream ost;
108 ost << cmQtAutoGen::GeneratorNameUpper(genType);
109 ost << ": " << property;
110 ost << ": Could not find or create the source group ";
111 ost << cmQtAutoGen::Quoted(groupName);
112 cmSystemTools::Error(ost.str().c_str());
117 if (sourceGroup != nullptr) {
118 sourceGroup->AddGroupFile(fileName);
123 static void AddCleanFile(cmMakefile* makefile, std::string const& fileName)
125 makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", fileName.c_str(),
129 static std::string FileProjectRelativePath(cmMakefile* makefile,
130 std::string const& fileName)
134 std::string pSource = cmSystemTools::RelativePath(
135 makefile->GetCurrentSourceDirectory(), fileName);
136 std::string pBinary = cmSystemTools::RelativePath(
137 makefile->GetCurrentBinaryDirectory(), fileName);
138 if (pSource.size() < pBinary.size()) {
139 res = std::move(pSource);
140 } else if (pBinary.size() < fileName.size()) {
141 res = std::move(pBinary);
149 /* @brief Tests if targetDepend is a STATIC_LIBRARY and if any of its
150 * recursive STATIC_LIBRARY dependencies depends on targetOrigin
151 * (STATIC_LIBRARY cycle).
153 static bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin,
154 cmGeneratorTarget const* targetDepend,
155 std::string const& config)
158 if ((targetOrigin->GetType() == cmStateEnums::STATIC_LIBRARY) &&
159 (targetDepend->GetType() == cmStateEnums::STATIC_LIBRARY)) {
160 std::set<cmGeneratorTarget const*> knownLibs;
161 std::deque<cmGeneratorTarget const*> testLibs;
163 // Insert initial static_library dependency
164 knownLibs.insert(targetDepend);
165 testLibs.push_back(targetDepend);
167 while (!testLibs.empty()) {
168 cmGeneratorTarget const* testTarget = testLibs.front();
169 testLibs.pop_front();
170 // Check if the test target is the origin target (cycle)
171 if (testTarget == targetOrigin) {
175 // Collect all static_library dependencies from the test target
176 cmLinkImplementationLibraries const* libs =
177 testTarget->GetLinkImplementationLibraries(config);
178 if (libs != nullptr) {
179 for (cmLinkItem const& item : libs->Libraries) {
180 cmGeneratorTarget const* depTarget = item.Target;
181 if ((depTarget != nullptr) &&
182 (depTarget->GetType() == cmStateEnums::STATIC_LIBRARY) &&
183 knownLibs.insert(depTarget).second) {
184 testLibs.push_back(depTarget);
193 cmQtAutoGenInitializer::cmQtAutoGenInitializer(
194 cmGeneratorTarget* target, bool mocEnabled, bool uicEnabled, bool rccEnabled,
195 std::string const& qtVersionMajor)
197 , MocEnabled(mocEnabled)
198 , UicEnabled(uicEnabled)
199 , RccEnabled(rccEnabled)
201 , QtVersionMajor(qtVersionMajor)
203 this->QtVersionMinor =
204 cmQtAutoGenInitializer::GetQtMinorVersion(target, this->QtVersionMajor);
207 void cmQtAutoGenInitializer::InitCustomTargets()
209 cmMakefile* makefile = this->Target->Target->GetMakefile();
210 cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
211 cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();
214 this->MultiConfig = globalGen->IsMultiConfig();
215 this->ConfigDefault = makefile->GetConfigurations(this->ConfigsList);
216 if (this->ConfigsList.empty()) {
217 this->ConfigsList.push_back(this->ConfigDefault);
220 // Autogen target name
221 this->AutogenTargetName = this->Target->GetName();
222 this->AutogenTargetName += "_autogen";
224 // Autogen directories
226 // Collapsed current binary directory
227 std::string const cbd = cmSystemTools::CollapseFullPath(
228 "", makefile->GetCurrentBinaryDirectory());
232 this->DirInfo += makefile->GetCMakeInstance()->GetCMakeFilesDirectory();
233 this->DirInfo += '/';
234 this->DirInfo += this->AutogenTargetName;
235 this->DirInfo += ".dir";
236 cmSystemTools::ConvertToUnixSlashes(this->DirInfo);
239 this->DirBuild = GetSafeProperty(this->Target, "AUTOGEN_BUILD_DIR");
240 if (this->DirBuild.empty()) {
241 this->DirBuild = cbd;
242 this->DirBuild += '/';
243 this->DirBuild += this->AutogenTargetName;
245 cmSystemTools::ConvertToUnixSlashes(this->DirBuild);
249 cmSystemTools::ConvertToUnixSlashes(this->DirWork);
254 this->AutogenInfoFile = this->DirInfo;
255 this->AutogenInfoFile += "/AutogenInfo.cmake";
257 this->AutogenSettingsFile = this->DirInfo;
258 this->AutogenSettingsFile += "/AutogenOldSettings.txt";
261 // Autogen target FOLDER property
264 makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
265 if (folder == nullptr) {
267 makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER");
269 // Inherit FOLDER property from target (#13688)
270 if (folder == nullptr) {
271 folder = SafeString(this->Target->Target->GetProperty("FOLDER"));
273 if (folder != nullptr) {
274 this->AutogenFolder = folder;
278 std::set<std::string> autogenDependFiles;
279 std::set<cmTarget*> autogenDependTargets;
280 std::vector<std::string> autogenProvides;
282 // Remove build directories on cleanup
283 AddCleanFile(makefile, this->DirBuild);
284 // Remove old settings on cleanup
286 std::string base = this->DirInfo;
287 base += "/AutogenOldSettings";
288 if (this->MultiConfig) {
289 for (std::string const& cfg : this->ConfigsList) {
290 std::string filename = base;
293 filename += ".cmake";
294 AddCleanFile(makefile, filename);
297 AddCleanFile(makefile, base.append(".cmake"));
301 // Add moc compilation to generated files list
302 if (this->MocEnabled) {
303 std::string mocsComp = this->DirBuild + "/mocs_compilation.cpp";
304 this->AddGeneratedSource(mocsComp, GeneratorT::MOC);
305 autogenProvides.push_back(std::move(mocsComp));
308 // Add autogen includes directory to the origin target INCLUDE_DIRECTORIES
309 if (this->MocEnabled || this->UicEnabled ||
310 (this->RccEnabled && this->MultiConfig)) {
311 std::string includeDir = this->DirBuild;
312 includeDir += "/include";
313 if (this->MultiConfig) {
314 includeDir += "_$<CONFIG>";
316 this->Target->AddIncludeDirectory(includeDir, true);
319 // Acquire rcc executable and features
320 if (this->RccEnabled) {
323 if (this->QtVersionMajor == "5") {
324 cmGeneratorTarget* tgt =
325 localGen->FindGeneratorTargetToUse("Qt5::rcc");
326 if (tgt != nullptr) {
327 this->RccExecutable = SafeString(tgt->ImportedGetLocation(""));
329 err = "AUTORCC: Qt5::rcc target not found";
331 } else if (QtVersionMajor == "4") {
332 cmGeneratorTarget* tgt =
333 localGen->FindGeneratorTargetToUse("Qt4::rcc");
334 if (tgt != nullptr) {
335 this->RccExecutable = SafeString(tgt->ImportedGetLocation(""));
337 err = "AUTORCC: Qt4::rcc target not found";
340 err = "The AUTORCC feature supports only Qt 4 and Qt 5";
344 err += this->Target->GetName();
346 cmSystemTools::Error(err.c_str());
349 // Detect if rcc supports (-)-list
350 if (!this->RccExecutable.empty() && (this->QtVersionMajor == "5")) {
351 std::vector<std::string> command;
352 command.push_back(this->RccExecutable);
353 command.push_back("--help");
354 std::string rccStdOut;
355 std::string rccStdErr;
357 bool result = cmSystemTools::RunSingleCommand(
358 command, &rccStdOut, &rccStdErr, &retVal, nullptr,
359 cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto);
360 if (result && retVal == 0 &&
361 rccStdOut.find("--list") != std::string::npos) {
362 this->RccListOptions.push_back("--list");
364 this->RccListOptions.push_back("-list");
369 // Extract relevant source files
370 std::vector<std::string> generatedSources;
371 std::vector<std::string> generatedHeaders;
373 std::string const qrcExt = "qrc";
374 std::vector<cmSourceFile*> srcFiles;
375 this->Target->GetConfigCommonSourceFiles(srcFiles);
376 for (cmSourceFile* sf : srcFiles) {
377 if (sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
380 // sf->GetExtension() is only valid after sf->GetFullPath() ...
381 std::string const& fPath = sf->GetFullPath();
382 std::string const& ext = sf->GetExtension();
383 // Register generated files that will be scanned by moc or uic
384 if (this->MocEnabled || this->UicEnabled) {
385 cmSystemTools::FileFormat const fileType =
386 cmSystemTools::GetFileFormat(ext.c_str());
387 if ((fileType == cmSystemTools::CXX_FILE_FORMAT) ||
388 (fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
389 std::string const absPath = cmSystemTools::GetRealPath(fPath);
390 if ((this->MocEnabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) ||
391 (this->UicEnabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) {
393 const bool generated = sf->GetPropertyAsBool("GENERATED");
394 if (fileType == cmSystemTools::HEADER_FILE_FORMAT) {
396 generatedHeaders.push_back(absPath);
398 this->Headers.push_back(absPath);
402 generatedSources.push_back(absPath);
404 this->Sources.push_back(absPath);
410 // Register rcc enabled files
411 if (this->RccEnabled && (ext == qrcExt) &&
412 !sf->GetPropertyAsBool("SKIP_AUTORCC")) {
416 qrc.QrcFile = cmSystemTools::GetRealPath(fPath);
418 cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
419 qrc.Generated = sf->GetPropertyAsBool("GENERATED");
422 std::string const opts = GetSafeProperty(sf, "AUTORCC_OPTIONS");
424 cmSystemTools::ExpandListArgument(opts, qrc.Options);
427 this->Qrcs.push_back(std::move(qrc));
431 // cmGeneratorTarget::GetConfigCommonSourceFiles computes the target's
432 // sources meta data cache. Clear it so that OBJECT library targets that
433 // are AUTOGEN initialized after this target get their added
434 // mocs_compilation.cpp source acknowledged by this target.
435 this->Target->ClearSourcesCache();
437 // Read skip files from makefile sources
438 if (this->MocEnabled || this->UicEnabled) {
439 std::string pathError;
440 for (cmSourceFile* sf : makefile->GetSourceFiles()) {
441 // sf->GetExtension() is only valid after sf->GetFullPath() ...
442 // Since we're iterating over source files that might be not in the
443 // target we need to check for path errors (not existing files).
444 std::string const& fPath = sf->GetFullPath(&pathError);
445 if (!pathError.empty()) {
449 cmSystemTools::FileFormat const fileType =
450 cmSystemTools::GetFileFormat(sf->GetExtension().c_str());
451 if (!(fileType == cmSystemTools::CXX_FILE_FORMAT) &&
452 !(fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
455 const bool skipAll = sf->GetPropertyAsBool("SKIP_AUTOGEN");
457 this->MocEnabled && (skipAll || sf->GetPropertyAsBool("SKIP_AUTOMOC"));
459 this->UicEnabled && (skipAll || sf->GetPropertyAsBool("SKIP_AUTOUIC"));
460 if (mocSkip || uicSkip) {
461 std::string const absFile = cmSystemTools::GetRealPath(fPath);
463 this->MocSkip.insert(absFile);
466 this->UicSkip.insert(absFile);
472 // Process GENERATED sources and headers
473 if (!generatedSources.empty() || !generatedHeaders.empty()) {
474 // Check status of policy CMP0071
475 bool policyAccept = false;
476 bool policyWarn = false;
477 cmPolicies::PolicyStatus const CMP0071_status =
478 makefile->GetPolicyStatus(cmPolicies::CMP0071);
479 switch (CMP0071_status) {
480 case cmPolicies::WARN:
483 case cmPolicies::OLD:
484 // Ignore GENERATED file
486 case cmPolicies::REQUIRED_IF_USED:
487 case cmPolicies::REQUIRED_ALWAYS:
488 case cmPolicies::NEW:
489 // Process GENERATED file
495 // Accept GENERATED sources
496 for (std::string const& absFile : generatedHeaders) {
497 this->Headers.push_back(absFile);
498 autogenDependFiles.insert(absFile);
500 for (std::string const& absFile : generatedSources) {
501 this->Sources.push_back(absFile);
502 autogenDependFiles.insert(absFile);
507 msg += cmPolicies::GetPolicyWarning(cmPolicies::CMP0071);
510 std::string property;
511 if (this->MocEnabled && this->UicEnabled) {
512 tools = "AUTOMOC and AUTOUIC";
513 property = "SKIP_AUTOGEN";
514 } else if (this->MocEnabled) {
516 property = "SKIP_AUTOMOC";
517 } else if (this->UicEnabled) {
519 property = "SKIP_AUTOUIC";
521 msg += "For compatibility, CMake is excluding the GENERATED source "
523 for (const std::string& absFile : generatedHeaders) {
524 msg.append(" ").append(Quoted(absFile)).append("\n");
526 for (const std::string& absFile : generatedSources) {
527 msg.append(" ").append(Quoted(absFile)).append("\n");
529 msg += "from processing by ";
532 ". If any of the files should be processed, set CMP0071 to NEW. "
533 "If any of the files should not be processed, "
534 "explicitly exclude them by setting the source file property ";
536 msg += ":\n set_property(SOURCE file.h PROPERTY ";
539 makefile->IssueMessage(cmake::AUTHOR_WARNING, msg);
543 generatedSources.clear();
544 generatedHeaders.clear();
546 // Sort headers and sources
547 if (this->MocEnabled || this->UicEnabled) {
548 std::sort(this->Headers.begin(), this->Headers.end());
549 std::sort(this->Sources.begin(), this->Sources.end());
553 if (!this->Qrcs.empty()) {
554 const bool QtV5 = (this->QtVersionMajor == "5");
555 // Target rcc options
556 std::vector<std::string> optionsTarget;
557 cmSystemTools::ExpandListArgument(
558 GetSafeProperty(this->Target, "AUTORCC_OPTIONS"), optionsTarget);
560 // Check if file name is unique
561 for (Qrc& qrc : this->Qrcs) {
563 for (Qrc const& qrc2 : this->Qrcs) {
564 if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
570 // Path checksum and file names
572 cmFilePathChecksum const fpathCheckSum(makefile);
573 for (Qrc& qrc : this->Qrcs) {
574 qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile);
575 // RCC output file name
577 std::string rccFile = this->DirBuild + "/";
578 rccFile += qrc.PathChecksum;
580 rccFile += qrc.QrcName;
582 qrc.RccFile = std::move(rccFile);
585 std::string base = this->DirInfo;
589 base += qrc.PathChecksum;
592 qrc.InfoFile += "Info.cmake";
593 qrc.SettingsFile = base;
594 qrc.SettingsFile += "Settings.txt";
599 for (Qrc& qrc : this->Qrcs) {
601 std::vector<std::string> opts = optionsTarget;
602 // Merge computed "-name XYZ" option
604 std::string name = qrc.QrcName;
605 // Replace '-' with '_'. The former is not valid for symbol names.
606 std::replace(name.begin(), name.end(), '-', '_');
609 name += qrc.PathChecksum;
611 std::vector<std::string> nameOpts;
612 nameOpts.emplace_back("-name");
613 nameOpts.emplace_back(std::move(name));
614 RccMergeOptions(opts, nameOpts, QtV5);
617 RccMergeOptions(opts, qrc.Options, QtV5);
618 qrc.Options = std::move(opts);
620 for (Qrc& qrc : this->Qrcs) {
621 // Register file at target
622 this->AddGeneratedSource(qrc.RccFile, GeneratorT::RCC);
624 std::vector<std::string> ccOutput;
625 ccOutput.push_back(qrc.RccFile);
626 cmCustomCommandLines commandLines;
628 cmCustomCommandLine currentLine;
629 currentLine.push_back(cmSystemTools::GetCMakeCommand());
630 currentLine.push_back("-E");
631 currentLine.push_back("cmake_autorcc");
632 currentLine.push_back(qrc.InfoFile);
633 currentLine.push_back("$<CONFIGURATION>");
634 commandLines.push_back(std::move(currentLine));
636 std::string ccComment = "Automatic RCC for ";
637 ccComment += FileProjectRelativePath(makefile, qrc.QrcFile);
640 // Create custom rcc target
643 ccName = this->Target->GetName();
645 ccName += qrc.QrcName;
648 ccName += qrc.PathChecksum;
650 std::vector<std::string> ccDepends;
651 // Add the .qrc and info file to the custom target dependencies
652 ccDepends.push_back(qrc.QrcFile);
653 ccDepends.push_back(qrc.InfoFile);
655 cmTarget* autoRccTarget = makefile->AddUtilityCommand(
656 ccName, cmMakefile::TargetOrigin::Generator, true,
657 this->DirWork.c_str(), ccOutput, ccDepends, commandLines, false,
659 // Create autogen generator target
660 localGen->AddGeneratorTarget(
661 new cmGeneratorTarget(autoRccTarget, localGen));
663 // Set FOLDER property in autogen target
664 if (!this->AutogenFolder.empty()) {
665 autoRccTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
668 // Add autogen target to the origin target dependencies
669 this->Target->Target->AddUtility(ccName, makefile);
671 // Create custom rcc command
673 std::vector<std::string> ccByproducts;
674 std::vector<std::string> ccDepends;
675 // Add the .qrc and info file to the custom command dependencies
676 ccDepends.push_back(qrc.QrcFile);
677 ccDepends.push_back(qrc.InfoFile);
679 // Add the resource files to the dependencies
682 if (RccListInputs(qrc.QrcFile, qrc.Resources, error)) {
683 for (std::string const& fileName : qrc.Resources) {
684 // Add resource file to the custom command dependencies
685 ccDepends.push_back(fileName);
688 cmSystemTools::Error(error.c_str());
691 makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends,
692 /*main_dependency*/ std::string(),
693 commandLines, ccComment.c_str(),
694 this->DirWork.c_str());
696 // Reconfigure when .qrc file changes
697 makefile->AddCMakeDependFile(qrc.QrcFile);
702 // Create _autogen target
703 if (this->MocEnabled || this->UicEnabled) {
704 // Add user defined autogen target dependencies
706 std::string const deps =
707 GetSafeProperty(this->Target, "AUTOGEN_TARGET_DEPENDS");
709 std::vector<std::string> extraDeps;
710 cmSystemTools::ExpandListArgument(deps, extraDeps);
711 for (std::string const& depName : extraDeps) {
712 // Allow target and file dependencies
713 auto* depTarget = makefile->FindTargetToUse(depName);
714 if (depTarget != nullptr) {
715 autogenDependTargets.insert(depTarget);
717 autogenDependFiles.insert(depName);
723 // Compose target comment
724 std::string autogenComment;
727 if (this->MocEnabled) {
730 if (this->UicEnabled) {
731 if (!tools.empty()) {
736 autogenComment = "Automatic ";
737 autogenComment += tools;
738 autogenComment += " for target ";
739 autogenComment += this->Target->GetName();
742 // Compose command lines
743 cmCustomCommandLines commandLines;
745 cmCustomCommandLine currentLine;
746 currentLine.push_back(cmSystemTools::GetCMakeCommand());
747 currentLine.push_back("-E");
748 currentLine.push_back("cmake_autogen");
749 currentLine.push_back(this->AutogenInfoFile);
750 currentLine.push_back("$<CONFIGURATION>");
751 commandLines.push_back(std::move(currentLine));
754 // Use PRE_BUILD on demand
755 bool usePRE_BUILD = false;
756 if (globalGen->GetName().find("Visual Studio") != std::string::npos) {
757 // Under VS use a PRE_BUILD event instead of a separate target to
758 // reduce the number of targets loaded into the IDE.
759 // This also works around a VS 11 bug that may skip updating the target:
760 // https://connect.microsoft.com/VisualStudio/feedback/details/769495
763 // Disable PRE_BUILD in some cases
765 // Cannot use PRE_BUILD with file depends
766 if (!autogenDependFiles.empty()) {
767 usePRE_BUILD = false;
770 // Create the autogen target/command
772 // Add additional autogen target dependencies to origin target
773 for (cmTarget* depTarget : autogenDependTargets) {
774 this->Target->Target->AddUtility(depTarget->GetName(), makefile);
777 // Add the pre-build command directly to bypass the OBJECT_LIBRARY
778 // rejection in cmMakefile::AddCustomCommandToTarget because we know
779 // PRE_BUILD will work for an OBJECT_LIBRARY in this specific case.
781 // PRE_BUILD does not support file dependencies!
782 const std::vector<std::string> no_output;
783 const std::vector<std::string> no_deps;
784 cmCustomCommand cc(makefile, no_output, autogenProvides, no_deps,
785 commandLines, autogenComment.c_str(),
786 this->DirWork.c_str());
787 cc.SetEscapeOldStyle(false);
788 cc.SetEscapeAllowMakeVars(true);
789 this->Target->Target->AddPreBuildCommand(cc);
792 // Add link library target dependencies to the autogen target
795 // add_dependencies/addUtility do not support generator expressions.
796 // We depend only on the libraries found in all configs therefore.
797 std::map<cmGeneratorTarget const*, std::size_t> commonTargets;
798 for (std::string const& config : this->ConfigsList) {
799 cmLinkImplementationLibraries const* libs =
800 this->Target->GetLinkImplementationLibraries(config);
801 if (libs != nullptr) {
802 for (cmLinkItem const& item : libs->Libraries) {
803 cmGeneratorTarget const* libTarget = item.Target;
804 if ((libTarget != nullptr) &&
805 !StaticLibraryCycle(this->Target, libTarget, config)) {
806 // Increment target config count
807 commonTargets[libTarget]++;
812 for (auto const& item : commonTargets) {
813 if (item.second == this->ConfigsList.size()) {
814 autogenDependTargets.insert(item.first->Target);
819 // Create autogen target
820 cmTarget* autogenTarget = makefile->AddUtilityCommand(
821 this->AutogenTargetName, cmMakefile::TargetOrigin::Generator, true,
822 this->DirWork.c_str(), /*byproducts=*/autogenProvides,
823 std::vector<std::string>(autogenDependFiles.begin(),
824 autogenDependFiles.end()),
825 commandLines, false, autogenComment.c_str());
826 // Create autogen generator target
827 localGen->AddGeneratorTarget(
828 new cmGeneratorTarget(autogenTarget, localGen));
830 // Forward origin utilities to autogen target
831 for (std::string const& depName : this->Target->Target->GetUtilities()) {
832 autogenTarget->AddUtility(depName, makefile);
834 // Add additional autogen target dependencies to autogen target
835 for (cmTarget* depTarget : autogenDependTargets) {
836 autogenTarget->AddUtility(depTarget->GetName(), makefile);
839 // Set FOLDER property in autogen target
840 if (!this->AutogenFolder.empty()) {
841 autogenTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
844 // Add autogen target to the origin target dependencies
845 this->Target->Target->AddUtility(this->AutogenTargetName, makefile);
850 void cmQtAutoGenInitializer::SetupCustomTargets()
852 cmMakefile* makefile = this->Target->Target->GetMakefile();
854 // Create info directory on demand
855 if (!cmSystemTools::MakeDirectory(this->DirInfo)) {
856 std::string emsg = ("Could not create directory: ");
857 emsg += Quoted(this->DirInfo);
858 cmSystemTools::Error(emsg.c_str());
861 // Configuration include directories
862 std::string includeDir = "include";
863 std::map<std::string, std::string> includeDirs;
864 for (std::string const& cfg : this->ConfigsList) {
865 std::string& dir = includeDirs[cfg];
870 // Generate autogen target info file
871 if (this->MocEnabled || this->UicEnabled) {
872 if (this->MocEnabled) {
873 this->SetupCustomTargetsMoc();
875 if (this->UicEnabled) {
876 this->SetupCustomTargetsUic();
879 // Parallel processing
880 this->Parallel = GetSafeProperty(this->Target, "AUTOGEN_PARALLEL");
881 if (this->Parallel.empty() || (this->Parallel == "AUTO")) {
882 // Autodetect number of CPUs
883 this->Parallel = std::to_string(GetParallelCPUCount());
886 cmGeneratedFileStream ofs;
887 ofs.SetCopyIfDifferent(true);
888 ofs.Open(this->AutogenInfoFile.c_str(), false, true);
891 auto CWrite = [&ofs](const char* key, std::string const& value) {
892 ofs << "set(" << key << " " << cmOutputConverter::EscapeForCMake(value)
895 auto CWriteList = [&CWrite](const char* key,
896 std::vector<std::string> const& list) {
897 CWrite(key, cmJoin(list, ";"));
899 auto CWriteNestedLists = [&CWrite](
900 const char* key, std::vector<std::vector<std::string>> const& lists) {
901 std::vector<std::string> seplist;
902 for (const std::vector<std::string>& list : lists) {
903 std::string blist = "{";
904 blist += cmJoin(list, ";");
906 seplist.push_back(std::move(blist));
908 CWrite(key, cmJoin(seplist, cmQtAutoGen::ListSep));
910 auto CWriteSet = [&CWrite](const char* key,
911 std::set<std::string> const& list) {
912 CWrite(key, cmJoin(list, ";"));
914 auto CWriteMap = [&ofs](const char* key,
915 std::map<std::string, std::string> const& map) {
916 for (auto const& item : map) {
917 ofs << "set(" << key << "_" << item.first << " "
918 << cmOutputConverter::EscapeForCMake(item.second) << ")\n";
921 auto MfDef = [makefile](const char* key) {
922 return std::string(makefile->GetSafeDefinition(key));
927 CWrite("AM_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE");
928 CWrite("AM_PARALLEL", this->Parallel);
930 ofs << "# Directories\n";
931 CWrite("AM_CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
932 CWrite("AM_CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
933 CWrite("AM_CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR"));
934 CWrite("AM_CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR"));
935 CWrite("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE",
936 MfDef("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"));
937 CWrite("AM_BUILD_DIR", this->DirBuild);
938 if (this->MultiConfig) {
939 CWriteMap("AM_INCLUDE_DIR", includeDirs);
941 CWrite("AM_INCLUDE_DIR", includeDir);
945 CWriteList("AM_SOURCES", this->Sources);
946 CWriteList("AM_HEADERS", this->Headers);
947 if (this->MultiConfig) {
948 std::map<std::string, std::string> settingsFiles;
949 for (std::string const& cfg : this->ConfigsList) {
951 AppendFilenameSuffix(this->AutogenSettingsFile, "_" + cfg);
953 CWriteMap("AM_SETTINGS_FILE", settingsFiles);
955 CWrite("AM_SETTINGS_FILE", this->AutogenSettingsFile);
959 CWrite("AM_QT_VERSION_MAJOR", this->QtVersionMajor);
960 CWrite("AM_QT_MOC_EXECUTABLE", this->MocExecutable);
961 CWrite("AM_QT_UIC_EXECUTABLE", this->UicExecutable);
963 if (this->MocEnabled) {
964 ofs << "# MOC settings\n";
965 CWriteSet("AM_MOC_SKIP", this->MocSkip);
966 CWrite("AM_MOC_DEFINITIONS", this->MocDefines);
967 CWriteMap("AM_MOC_DEFINITIONS", this->MocDefinesConfig);
968 CWrite("AM_MOC_INCLUDES", this->MocIncludes);
969 CWriteMap("AM_MOC_INCLUDES", this->MocIncludesConfig);
970 CWrite("AM_MOC_OPTIONS",
971 GetSafeProperty(this->Target, "AUTOMOC_MOC_OPTIONS"));
972 CWrite("AM_MOC_RELAXED_MODE", MfDef("CMAKE_AUTOMOC_RELAXED_MODE"));
973 CWrite("AM_MOC_MACRO_NAMES",
974 GetSafeProperty(this->Target, "AUTOMOC_MACRO_NAMES"));
975 CWrite("AM_MOC_DEPEND_FILTERS",
976 GetSafeProperty(this->Target, "AUTOMOC_DEPEND_FILTERS"));
977 CWrite("AM_MOC_PREDEFS_CMD", this->MocPredefsCmd);
980 if (this->UicEnabled) {
981 ofs << "# UIC settings\n";
982 CWriteSet("AM_UIC_SKIP", this->UicSkip);
983 CWrite("AM_UIC_TARGET_OPTIONS", this->UicOptions);
984 CWriteMap("AM_UIC_TARGET_OPTIONS", this->UicOptionsConfig);
985 CWriteList("AM_UIC_OPTIONS_FILES", this->UicFileFiles);
986 CWriteNestedLists("AM_UIC_OPTIONS_OPTIONS", this->UicFileOptions);
987 CWriteList("AM_UIC_SEARCH_PATHS", this->UicSearchPaths);
994 // Generate auto RCC info files
995 if (this->RccEnabled) {
996 for (Qrc const& qrc : this->Qrcs) {
997 cmGeneratedFileStream ofs;
998 ofs.SetCopyIfDifferent(true);
999 ofs.Open(qrc.InfoFile.c_str(), false, true);
1002 auto CWrite = [&ofs](const char* key, std::string const& value) {
1003 ofs << "set(" << key << " "
1004 << cmOutputConverter::EscapeForCMake(value) << ")\n";
1006 auto CWriteMap = [&ofs](
1007 const char* key, std::map<std::string, std::string> const& map) {
1008 for (auto const& item : map) {
1009 ofs << "set(" << key << "_" << item.first << " "
1010 << cmOutputConverter::EscapeForCMake(item.second) << ")\n";
1015 ofs << "# Configurations\n";
1016 CWrite("ARCC_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE");
1018 ofs << "# Settings file\n";
1019 if (this->MultiConfig) {
1020 std::map<std::string, std::string> settingsFiles;
1021 for (std::string const& cfg : this->ConfigsList) {
1022 settingsFiles[cfg] =
1023 AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg);
1025 CWriteMap("ARCC_SETTINGS_FILE", settingsFiles);
1027 CWrite("ARCC_SETTINGS_FILE", qrc.SettingsFile);
1030 ofs << "# Directories\n";
1031 CWrite("ARCC_BUILD_DIR", this->DirBuild);
1032 if (this->MultiConfig) {
1033 CWriteMap("ARCC_INCLUDE_DIR", includeDirs);
1035 CWrite("ARCC_INCLUDE_DIR", includeDir);
1038 ofs << "# Rcc executable\n";
1039 CWrite("ARCC_RCC_EXECUTABLE", this->RccExecutable);
1040 CWrite("ARCC_RCC_LIST_OPTIONS", cmJoin(this->RccListOptions, ";"));
1042 ofs << "# Rcc job\n";
1043 CWrite("ARCC_SOURCE", qrc.QrcFile);
1044 CWrite("ARCC_OUTPUT_CHECKSUM", qrc.PathChecksum);
1045 CWrite("ARCC_OUTPUT_NAME",
1046 cmSystemTools::GetFilenameName(qrc.RccFile));
1047 CWrite("ARCC_OPTIONS", cmJoin(qrc.Options, ";"));
1048 CWrite("ARCC_INPUTS", cmJoin(qrc.Resources, ";"));
1056 void cmQtAutoGenInitializer::SetupCustomTargetsMoc()
1058 cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
1059 cmMakefile* makefile = this->Target->Target->GetMakefile();
1061 // Moc predefs command
1062 if (this->Target->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
1063 this->QtVersionGreaterOrEqual(5, 8)) {
1064 this->MocPredefsCmd =
1065 makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND");
1068 // Moc includes and compile definitions
1070 auto GetIncludeDirs = [this,
1071 localGen](std::string const& cfg) -> std::string {
1072 // Get the include dirs for this target, without stripping the implicit
1073 // include dirs off, see
1074 // https://gitlab.kitware.com/cmake/cmake/issues/13667
1075 std::vector<std::string> includeDirs;
1076 localGen->GetIncludeDirectories(includeDirs, this->Target, "CXX", cfg,
1078 return cmJoin(includeDirs, ";");
1080 auto GetCompileDefinitions =
1081 [this, localGen](std::string const& cfg) -> std::string {
1082 std::set<std::string> defines;
1083 localGen->AddCompileDefinitions(defines, this->Target, cfg, "CXX");
1084 return cmJoin(defines, ";");
1087 // Default configuration settings
1088 this->MocIncludes = GetIncludeDirs(this->ConfigDefault);
1089 this->MocDefines = GetCompileDefinitions(this->ConfigDefault);
1090 // Other configuration settings
1091 for (std::string const& cfg : this->ConfigsList) {
1093 std::string const configIncludeDirs = GetIncludeDirs(cfg);
1094 if (configIncludeDirs != this->MocIncludes) {
1095 this->MocIncludesConfig[cfg] = configIncludeDirs;
1099 std::string const configCompileDefs = GetCompileDefinitions(cfg);
1100 if (configCompileDefs != this->MocDefines) {
1101 this->MocDefinesConfig[cfg] = configCompileDefs;
1109 std::string mocExec;
1112 if (this->QtVersionMajor == "5") {
1113 cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::moc");
1114 if (tgt != nullptr) {
1115 mocExec = SafeString(tgt->ImportedGetLocation(""));
1117 err = "AUTOMOC: Qt5::moc target not found";
1119 } else if (this->QtVersionMajor == "4") {
1120 cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::moc");
1121 if (tgt != nullptr) {
1122 mocExec = SafeString(tgt->ImportedGetLocation(""));
1124 err = "AUTOMOC: Qt4::moc target not found";
1127 err = "The AUTOMOC feature supports only Qt 4 and Qt 5";
1131 this->MocExecutable = mocExec;
1134 err += this->Target->GetName();
1136 cmSystemTools::Error(err.c_str());
1141 void cmQtAutoGenInitializer::SetupCustomTargetsUic()
1143 cmMakefile* makefile = this->Target->Target->GetMakefile();
1147 std::string const usp =
1148 GetSafeProperty(this->Target, "AUTOUIC_SEARCH_PATHS");
1150 cmSystemTools::ExpandListArgument(usp, this->UicSearchPaths);
1151 std::string const srcDir = makefile->GetCurrentSourceDirectory();
1152 for (std::string& path : this->UicSearchPaths) {
1153 path = cmSystemTools::CollapseFullPath(path, srcDir);
1157 // Uic target options
1159 auto UicGetOpts = [this](std::string const& cfg) -> std::string {
1160 std::vector<std::string> opts;
1161 this->Target->GetAutoUicOptions(opts, cfg);
1162 return cmJoin(opts, ";");
1166 this->UicOptions = UicGetOpts(this->ConfigDefault);
1168 // Configuration specific settings
1169 for (std::string const& cfg : this->ConfigsList) {
1170 std::string const configUicOpts = UicGetOpts(cfg);
1171 if (configUicOpts != this->UicOptions) {
1172 this->UicOptionsConfig[cfg] = configUicOpts;
1176 // .ui files skip and options
1178 std::string const uiExt = "ui";
1179 std::string pathError;
1180 for (cmSourceFile* sf : makefile->GetSourceFiles()) {
1181 // sf->GetExtension() is only valid after sf->GetFullPath() ...
1182 // Since we're iterating over source files that might be not in the
1183 // target we need to check for path errors (not existing files).
1184 std::string const& fPath = sf->GetFullPath(&pathError);
1185 if (!pathError.empty()) {
1189 if (sf->GetExtension() == uiExt) {
1190 std::string const absFile = cmSystemTools::GetRealPath(fPath);
1191 // Check if the .ui file should be skipped
1192 if (sf->GetPropertyAsBool("SKIP_AUTOUIC") ||
1193 sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
1194 this->UicSkip.insert(absFile);
1196 // Check if the .ui file has uic options
1197 std::string const uicOpts = GetSafeProperty(sf, "AUTOUIC_OPTIONS");
1198 if (!uicOpts.empty()) {
1199 // Check if file isn't skipped
1200 if (this->UicSkip.count(absFile) == 0) {
1201 this->UicFileFiles.push_back(absFile);
1202 std::vector<std::string> optsVec;
1203 cmSystemTools::ExpandListArgument(uicOpts, optsVec);
1204 this->UicFileOptions.push_back(std::move(optsVec));
1214 std::string uicExec;
1216 cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
1217 if (this->QtVersionMajor == "5") {
1218 cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::uic");
1219 if (tgt != nullptr) {
1220 uicExec = SafeString(tgt->ImportedGetLocation(""));
1222 // Project does not use Qt5Widgets, but has AUTOUIC ON anyway
1224 } else if (this->QtVersionMajor == "4") {
1225 cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::uic");
1226 if (tgt != nullptr) {
1227 uicExec = SafeString(tgt->ImportedGetLocation(""));
1229 err = "AUTOUIC: Qt4::uic target not found";
1232 err = "The AUTOUIC feature supports only Qt 4 and Qt 5";
1236 this->UicExecutable = uicExec;
1239 err += this->Target->GetName();
1241 cmSystemTools::Error(err.c_str());
1246 void cmQtAutoGenInitializer::AddGeneratedSource(std::string const& filename,
1249 // Register source file in makefile
1250 cmMakefile* makefile = this->Target->Target->GetMakefile();
1252 cmSourceFile* gFile = makefile->GetOrCreateSource(filename, true);
1253 gFile->SetProperty("GENERATED", "1");
1254 gFile->SetProperty("SKIP_AUTOGEN", "On");
1257 // Add source file to source group
1258 AddToSourceGroup(makefile, filename, genType);
1260 // Add source file to target
1261 this->Target->AddSource(filename);
1264 std::string cmQtAutoGenInitializer::GetQtMajorVersion(
1265 cmGeneratorTarget const* target)
1267 cmMakefile* makefile = target->Target->GetMakefile();
1268 std::string qtMajor = makefile->GetSafeDefinition("QT_VERSION_MAJOR");
1269 if (qtMajor.empty()) {
1270 qtMajor = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR");
1272 const char* targetQtVersion =
1273 target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", "");
1274 if (targetQtVersion != nullptr) {
1275 qtMajor = targetQtVersion;
1280 std::string cmQtAutoGenInitializer::GetQtMinorVersion(
1281 cmGeneratorTarget const* target, std::string const& qtVersionMajor)
1283 cmMakefile* makefile = target->Target->GetMakefile();
1284 std::string qtMinor;
1285 if (qtVersionMajor == "5") {
1286 qtMinor = makefile->GetSafeDefinition("Qt5Core_VERSION_MINOR");
1288 if (qtMinor.empty()) {
1289 qtMinor = makefile->GetSafeDefinition("QT_VERSION_MINOR");
1292 const char* targetQtVersion =
1293 target->GetLinkInterfaceDependentStringProperty("QT_MINOR_VERSION", "");
1294 if (targetQtVersion != nullptr) {
1295 qtMinor = targetQtVersion;
1300 bool cmQtAutoGenInitializer::QtVersionGreaterOrEqual(
1301 unsigned long requestMajor, unsigned long requestMinor) const
1303 unsigned long majorUL(0);
1304 unsigned long minorUL(0);
1305 if (cmSystemTools::StringToULong(this->QtVersionMajor.c_str(), &majorUL) &&
1306 cmSystemTools::StringToULong(this->QtVersionMinor.c_str(), &minorUL)) {
1307 return (majorUL > requestMajor) ||
1308 (majorUL == requestMajor && minorUL >= requestMinor);
1313 /// @brief Reads the resource files list from from a .qrc file
1314 /// @arg fileName Must be the absolute path of the .qrc file
1315 /// @return True if the rcc file was successfully read
1316 bool cmQtAutoGenInitializer::RccListInputs(std::string const& fileName,
1317 std::vector<std::string>& files,
1320 if (!cmSystemTools::FileExists(fileName)) {
1321 error = "rcc resource file does not exist:\n ";
1322 error += Quoted(fileName);
1326 if (!RccListOptions.empty()) {
1327 // Use rcc for file listing
1328 if (RccExecutable.empty()) {
1329 error = "rcc executable not available";
1333 // Run rcc list command in the directory of the qrc file with the
1335 // qrc file name argument. This way rcc prints relative paths.
1336 // This avoids issues on Windows when the qrc file is in a path that
1337 // contains non-ASCII characters.
1338 std::string const fileDir = cmSystemTools::GetFilenamePath(fileName);
1339 std::string const fileNameName = cmSystemTools::GetFilenameName(fileName);
1341 bool result = false;
1343 std::string rccStdOut;
1344 std::string rccStdErr;
1346 std::vector<std::string> cmd;
1347 cmd.push_back(RccExecutable);
1348 cmd.insert(cmd.end(), RccListOptions.begin(), RccListOptions.end());
1349 cmd.push_back(fileNameName);
1350 result = cmSystemTools::RunSingleCommand(
1351 cmd, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(),
1352 cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto);
1354 if (!result || retVal) {
1355 error = "rcc list process failed for:\n ";
1356 error += Quoted(fileName);
1364 if (!RccListParseOutput(rccStdOut, rccStdErr, files, error)) {
1368 // We can't use rcc for the file listing.
1369 // Read the qrc file content into string and parse it.
1371 std::string qrcContents;
1373 cmsys::ifstream ifs(fileName.c_str());
1375 std::ostringstream osst;
1376 osst << ifs.rdbuf();
1377 qrcContents = osst.str();
1379 error = "rcc file not readable:\n ";
1380 error += Quoted(fileName);
1385 // Parse string content
1386 RccListParseContent(qrcContents, files);
1390 // Convert relative paths to absolute paths
1391 RccListConvertFullPath(cmSystemTools::GetFilenamePath(fileName), files);