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 "cmQtAutoGenGlobalInitializer.h"
10 #include "cmCustomCommand.h"
11 #include "cmDuration.h"
12 #include "cmGeneratorTarget.h"
13 #include "cmLocalGenerator.h"
14 #include "cmMakefile.h"
15 #include "cmMessageType.h"
16 #include "cmPolicies.h"
17 #include "cmProcessOutput.h"
18 #include "cmQtAutoGen.h"
19 #include "cmQtAutoGenInitializer.h"
21 #include "cmStateTypes.h"
22 #include "cmStringAlgorithms.h"
23 #include "cmSystemTools.h"
27 cmQtAutoGenGlobalInitializer::Keywords::Keywords()
31 , AUTOMOC_EXECUTABLE("AUTOMOC_EXECUTABLE")
32 , AUTOUIC_EXECUTABLE("AUTOUIC_EXECUTABLE")
33 , AUTORCC_EXECUTABLE("AUTORCC_EXECUTABLE")
34 , SKIP_AUTOGEN("SKIP_AUTOGEN")
35 , SKIP_AUTOMOC("SKIP_AUTOMOC")
36 , SKIP_AUTOUIC("SKIP_AUTOUIC")
37 , SKIP_AUTORCC("SKIP_AUTORCC")
38 , AUTOUIC_OPTIONS("AUTOUIC_OPTIONS")
39 , AUTORCC_OPTIONS("AUTORCC_OPTIONS")
45 cmQtAutoGenGlobalInitializer::cmQtAutoGenGlobalInitializer(
46 std::vector<std::unique_ptr<cmLocalGenerator>> const& localGenerators)
48 for (const auto& localGen : localGenerators) {
49 // Detect global autogen and autorcc target names
50 bool globalAutoGenTarget = false;
51 bool globalAutoRccTarget = false;
53 cmMakefile* makefile = localGen->GetMakefile();
54 // Detect global autogen target name
55 if (makefile->IsOn("CMAKE_GLOBAL_AUTOGEN_TARGET")) {
56 std::string targetName =
57 makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTOGEN_TARGET_NAME");
58 if (targetName.empty()) {
59 targetName = "autogen";
61 this->GlobalAutoGenTargets_.emplace(localGen.get(),
62 std::move(targetName));
63 globalAutoGenTarget = true;
66 // Detect global autorcc target name
67 if (makefile->IsOn("CMAKE_GLOBAL_AUTORCC_TARGET")) {
68 std::string targetName =
69 makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTORCC_TARGET_NAME");
70 if (targetName.empty()) {
71 targetName = "autorcc";
73 this->GlobalAutoRccTargets_.emplace(localGen.get(),
74 std::move(targetName));
75 globalAutoRccTarget = true;
79 // Find targets that require AUTOMOC/UIC/RCC processing
80 for (const auto& target : localGen->GetGeneratorTargets()) {
81 // Process only certain target types
82 switch (target->GetType()) {
83 case cmStateEnums::EXECUTABLE:
84 case cmStateEnums::STATIC_LIBRARY:
85 case cmStateEnums::SHARED_LIBRARY:
86 case cmStateEnums::MODULE_LIBRARY:
87 case cmStateEnums::OBJECT_LIBRARY:
91 // Don't process target
94 if (target->IsImported()) {
95 // Don't process target
98 std::set<std::string> const& languages =
99 target->GetAllConfigCompileLanguages();
100 // cmGeneratorTarget::GetAllConfigCompileLanguages caches the target's
101 // sources. Clear it so that OBJECT library targets that are AUTOGEN
102 // initialized after this target get their added mocs_compilation.cpp
103 // source acknowledged by this target.
104 target->ClearSourcesCache();
105 if (languages.count("CSharp")) {
106 // Don't process target if it's a CSharp target
110 bool const moc = target->GetPropertyAsBool(this->kw().AUTOMOC);
111 bool const uic = target->GetPropertyAsBool(this->kw().AUTOUIC);
112 bool const rcc = target->GetPropertyAsBool(this->kw().AUTORCC);
113 if (moc || uic || rcc) {
114 std::string const& mocExec =
115 target->GetSafeProperty(this->kw().AUTOMOC_EXECUTABLE);
116 std::string const& uicExec =
117 target->GetSafeProperty(this->kw().AUTOUIC_EXECUTABLE);
118 std::string const& rccExec =
119 target->GetSafeProperty(this->kw().AUTORCC_EXECUTABLE);
121 // We support Qt4, Qt5 and Qt6
123 cmQtAutoGenInitializer::GetQtVersion(target.get(), mocExec);
124 bool const validQt = (qtVersion.first.Major == 4) ||
125 (qtVersion.first.Major == 5) || (qtVersion.first.Major == 6);
127 bool const mocAvailable = (validQt || !mocExec.empty());
128 bool const uicAvailable = (validQt || !uicExec.empty());
129 bool const rccAvailable = (validQt || !rccExec.empty());
130 bool const mocIsValid = (moc && mocAvailable);
131 bool const uicIsValid = (uic && uicAvailable);
132 bool const rccIsValid = (rcc && rccAvailable);
133 // Disabled AUTOMOC/UIC/RCC warning
134 bool const mocDisabled = (moc && !mocAvailable);
135 bool const uicDisabled = (uic && !uicAvailable);
136 bool const rccDisabled = (rcc && !rccAvailable);
137 if (mocDisabled || uicDisabled || rccDisabled) {
138 cmAlphaNum version = (qtVersion.second == 0)
139 ? cmAlphaNum("<QTVERSION>")
140 : cmAlphaNum(qtVersion.second);
141 cmAlphaNum component = uicDisabled ? "Widgets" : "Core";
143 std::string const msg = cmStrCat(
144 "AUTOGEN: No valid Qt version found for target ",
145 target->GetName(), ". ",
146 cmQtAutoGen::Tools(mocDisabled, uicDisabled, rccDisabled),
147 " disabled. Consider adding:\n", " find_package(Qt", version,
148 " COMPONENTS ", component, ")\n", "to your CMakeLists.txt file.");
149 target->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, msg);
151 if (mocIsValid || uicIsValid || rccIsValid) {
152 // Create autogen target initializer
153 this->Initializers_.emplace_back(
154 cm::make_unique<cmQtAutoGenInitializer>(
155 this, target.get(), qtVersion.first, mocIsValid, uicIsValid,
156 rccIsValid, globalAutoGenTarget, globalAutoRccTarget));
163 cmQtAutoGenGlobalInitializer::~cmQtAutoGenGlobalInitializer() = default;
165 void cmQtAutoGenGlobalInitializer::GetOrCreateGlobalTarget(
166 cmLocalGenerator* localGen, std::string const& name,
167 std::string const& comment)
169 // Test if the target already exists
170 if (localGen->FindGeneratorTargetToUse(name) == nullptr) {
171 cmMakefile* makefile = localGen->GetMakefile();
173 // Create utility target
174 auto cc = cm::make_unique<cmCustomCommand>();
175 cc->SetWorkingDirectory(makefile->GetHomeOutputDirectory().c_str());
176 cc->SetCMP0116Status(cmPolicies::NEW);
177 cc->SetEscapeOldStyle(false);
178 cc->SetComment(comment.c_str());
179 cmTarget* target = localGen->AddUtilityCommand(name, true, std::move(cc));
180 localGen->AddGeneratorTarget(
181 cm::make_unique<cmGeneratorTarget>(target, localGen));
183 // Set FOLDER property in the target
186 makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER");
188 target->SetProperty("FOLDER", folder);
194 void cmQtAutoGenGlobalInitializer::AddToGlobalAutoGen(
195 cmLocalGenerator* localGen, std::string const& targetName)
197 auto it = this->GlobalAutoGenTargets_.find(localGen);
198 if (it != this->GlobalAutoGenTargets_.end()) {
199 cmGeneratorTarget* target = localGen->FindGeneratorTargetToUse(it->second);
200 if (target != nullptr) {
201 target->Target->AddUtility(targetName, false, localGen->GetMakefile());
206 void cmQtAutoGenGlobalInitializer::AddToGlobalAutoRcc(
207 cmLocalGenerator* localGen, std::string const& targetName)
209 auto it = this->GlobalAutoRccTargets_.find(localGen);
210 if (it != this->GlobalAutoRccTargets_.end()) {
211 cmGeneratorTarget* target = localGen->FindGeneratorTargetToUse(it->second);
212 if (target != nullptr) {
213 target->Target->AddUtility(targetName, false, localGen->GetMakefile());
218 cmQtAutoGen::CompilerFeaturesHandle
219 cmQtAutoGenGlobalInitializer::GetCompilerFeatures(
220 std::string const& generator, std::string const& executable,
223 // Check if we have cached features
225 auto it = this->CompilerFeatures_.find(executable);
226 if (it != this->CompilerFeatures_.end()) {
231 // Check if the executable exists
232 if (!cmSystemTools::FileExists(executable, true)) {
233 error = cmStrCat("The \"", generator, "\" executable ",
234 cmQtAutoGen::Quoted(executable), " does not exist.");
235 return cmQtAutoGen::CompilerFeaturesHandle();
238 // Test the executable
242 std::vector<std::string> command;
243 command.emplace_back(executable);
244 command.emplace_back("-h");
246 const bool runResult = cmSystemTools::RunSingleCommand(
247 command, &stdOut, &stdErr, &retVal, nullptr, cmSystemTools::OUTPUT_NONE,
248 cmDuration::zero(), cmProcessOutput::Auto);
250 error = cmStrCat("Test run of \"", generator, "\" executable ",
251 cmQtAutoGen::Quoted(executable), " failed.\n",
252 cmQtAutoGen::QuotedCommand(command), '\n', stdOut, '\n',
254 return cmQtAutoGen::CompilerFeaturesHandle();
258 // Create valid handle
259 cmQtAutoGen::CompilerFeaturesHandle res =
260 std::make_shared<cmQtAutoGen::CompilerFeatures>();
261 res->HelpOutput = std::move(stdOut);
263 // Register compiler features
264 this->CompilerFeatures_.emplace(executable, res);
269 bool cmQtAutoGenGlobalInitializer::generate()
271 return (this->InitializeCustomTargets() && this->SetupCustomTargets());
274 bool cmQtAutoGenGlobalInitializer::InitializeCustomTargets()
276 // Initialize global autogen targets
278 std::string const comment = "Global AUTOGEN target";
279 for (auto const& pair : this->GlobalAutoGenTargets_) {
280 this->GetOrCreateGlobalTarget(pair.first, pair.second, comment);
283 // Initialize global autorcc targets
285 std::string const comment = "Global AUTORCC target";
286 for (auto const& pair : this->GlobalAutoRccTargets_) {
287 this->GetOrCreateGlobalTarget(pair.first, pair.second, comment);
290 // Initialize per target autogen targets
291 for (auto& initializer : this->Initializers_) {
292 if (!initializer->InitCustomTargets()) {
299 bool cmQtAutoGenGlobalInitializer::SetupCustomTargets()
301 for (auto& initializer : this->Initializers_) {
302 if (!initializer->SetupCustomTargets()) {