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"
4
5 #include <set>
6 #include <utility>
7
8 #include <cm/memory>
9
10 #include "cmCustomCommandLines.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"
20 #include "cmState.h"
21 #include "cmStateTypes.h"
22 #include "cmStringAlgorithms.h"
23 #include "cmSystemTools.h"
24 #include "cmTarget.h"
25 #include "cmValue.h"
26
Keywords()27 cmQtAutoGenGlobalInitializer::Keywords::Keywords()
28 : AUTOMOC("AUTOMOC")
29 , AUTOUIC("AUTOUIC")
30 , AUTORCC("AUTORCC")
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")
40 , qrc("qrc")
41 , ui("ui")
42 {
43 }
44
cmQtAutoGenGlobalInitializer(std::vector<std::unique_ptr<cmLocalGenerator>> const & localGenerators)45 cmQtAutoGenGlobalInitializer::cmQtAutoGenGlobalInitializer(
46 std::vector<std::unique_ptr<cmLocalGenerator>> const& localGenerators)
47 {
48 for (const auto& localGen : localGenerators) {
49 // Detect global autogen and autorcc target names
50 bool globalAutoGenTarget = false;
51 bool globalAutoRccTarget = false;
52 {
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";
60 }
61 this->GlobalAutoGenTargets_.emplace(localGen.get(),
62 std::move(targetName));
63 globalAutoGenTarget = true;
64 }
65
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";
72 }
73 this->GlobalAutoRccTargets_.emplace(localGen.get(),
74 std::move(targetName));
75 globalAutoRccTarget = true;
76 }
77 }
78
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:
88 // Process target
89 break;
90 default:
91 // Don't process target
92 continue;
93 }
94 if (target->IsImported()) {
95 // Don't process target
96 continue;
97 }
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
107 continue;
108 }
109
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);
120
121 // We support Qt4, Qt5 and Qt6
122 auto qtVersion =
123 cmQtAutoGenInitializer::GetQtVersion(target.get(), mocExec);
124 bool const validQt = (qtVersion.first.Major == 4) ||
125 (qtVersion.first.Major == 5) || (qtVersion.first.Major == 6);
126
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";
142
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);
150 }
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));
157 }
158 }
159 }
160 }
161 }
162
163 cmQtAutoGenGlobalInitializer::~cmQtAutoGenGlobalInitializer() = default;
164
GetOrCreateGlobalTarget(cmLocalGenerator * localGen,std::string const & name,std::string const & comment)165 void cmQtAutoGenGlobalInitializer::GetOrCreateGlobalTarget(
166 cmLocalGenerator* localGen, std::string const& name,
167 std::string const& comment)
168 {
169 // Test if the target already exists
170 if (localGen->FindGeneratorTargetToUse(name) == nullptr) {
171 cmMakefile* makefile = localGen->GetMakefile();
172
173 // Create utility target
174 std::vector<std::string> no_byproducts;
175 std::vector<std::string> no_depends;
176 cmCustomCommandLines no_commands;
177 const cmPolicies::PolicyStatus cmp0116_new = cmPolicies::NEW;
178 cmTarget* target = localGen->AddUtilityCommand(
179 name, true, makefile->GetHomeOutputDirectory().c_str(), no_byproducts,
180 no_depends, no_commands, cmp0116_new, false, comment.c_str());
181 localGen->AddGeneratorTarget(
182 cm::make_unique<cmGeneratorTarget>(target, localGen));
183
184 // Set FOLDER property in the target
185 {
186 cmValue folder =
187 makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER");
188 if (folder) {
189 target->SetProperty("FOLDER", folder);
190 }
191 }
192 }
193 }
194
AddToGlobalAutoGen(cmLocalGenerator * localGen,std::string const & targetName)195 void cmQtAutoGenGlobalInitializer::AddToGlobalAutoGen(
196 cmLocalGenerator* localGen, std::string const& targetName)
197 {
198 auto it = this->GlobalAutoGenTargets_.find(localGen);
199 if (it != this->GlobalAutoGenTargets_.end()) {
200 cmGeneratorTarget* target = localGen->FindGeneratorTargetToUse(it->second);
201 if (target != nullptr) {
202 target->Target->AddUtility(targetName, false, localGen->GetMakefile());
203 }
204 }
205 }
206
AddToGlobalAutoRcc(cmLocalGenerator * localGen,std::string const & targetName)207 void cmQtAutoGenGlobalInitializer::AddToGlobalAutoRcc(
208 cmLocalGenerator* localGen, std::string const& targetName)
209 {
210 auto it = this->GlobalAutoRccTargets_.find(localGen);
211 if (it != this->GlobalAutoRccTargets_.end()) {
212 cmGeneratorTarget* target = localGen->FindGeneratorTargetToUse(it->second);
213 if (target != nullptr) {
214 target->Target->AddUtility(targetName, false, localGen->GetMakefile());
215 }
216 }
217 }
218
219 cmQtAutoGen::CompilerFeaturesHandle
GetCompilerFeatures(std::string const & generator,std::string const & executable,std::string & error)220 cmQtAutoGenGlobalInitializer::GetCompilerFeatures(
221 std::string const& generator, std::string const& executable,
222 std::string& error)
223 {
224 // Check if we have cached features
225 {
226 auto it = this->CompilerFeatures_.find(executable);
227 if (it != this->CompilerFeatures_.end()) {
228 return it->second;
229 }
230 }
231
232 // Check if the executable exists
233 if (!cmSystemTools::FileExists(executable, true)) {
234 error = cmStrCat("The \"", generator, "\" executable ",
235 cmQtAutoGen::Quoted(executable), " does not exist.");
236 return cmQtAutoGen::CompilerFeaturesHandle();
237 }
238
239 // Test the executable
240 std::string stdOut;
241 {
242 std::string stdErr;
243 std::vector<std::string> command;
244 command.emplace_back(executable);
245 command.emplace_back("-h");
246 int retVal = 0;
247 const bool runResult = cmSystemTools::RunSingleCommand(
248 command, &stdOut, &stdErr, &retVal, nullptr, cmSystemTools::OUTPUT_NONE,
249 cmDuration::zero(), cmProcessOutput::Auto);
250 if (!runResult) {
251 error = cmStrCat("Test run of \"", generator, "\" executable ",
252 cmQtAutoGen::Quoted(executable), " failed.\n",
253 cmQtAutoGen::QuotedCommand(command), '\n', stdOut, '\n',
254 stdErr);
255 return cmQtAutoGen::CompilerFeaturesHandle();
256 }
257 }
258
259 // Create valid handle
260 cmQtAutoGen::CompilerFeaturesHandle res =
261 std::make_shared<cmQtAutoGen::CompilerFeatures>();
262 res->HelpOutput = std::move(stdOut);
263
264 // Register compiler features
265 this->CompilerFeatures_.emplace(executable, res);
266
267 return res;
268 }
269
generate()270 bool cmQtAutoGenGlobalInitializer::generate()
271 {
272 return (this->InitializeCustomTargets() && this->SetupCustomTargets());
273 }
274
InitializeCustomTargets()275 bool cmQtAutoGenGlobalInitializer::InitializeCustomTargets()
276 {
277 // Initialize global autogen targets
278 {
279 std::string const comment = "Global AUTOGEN target";
280 for (auto const& pair : this->GlobalAutoGenTargets_) {
281 this->GetOrCreateGlobalTarget(pair.first, pair.second, comment);
282 }
283 }
284 // Initialize global autorcc targets
285 {
286 std::string const comment = "Global AUTORCC target";
287 for (auto const& pair : this->GlobalAutoRccTargets_) {
288 this->GetOrCreateGlobalTarget(pair.first, pair.second, comment);
289 }
290 }
291 // Initialize per target autogen targets
292 for (auto& initializer : this->Initializers_) {
293 if (!initializer->InitCustomTargets()) {
294 return false;
295 }
296 }
297 return true;
298 }
299
SetupCustomTargets()300 bool cmQtAutoGenGlobalInitializer::SetupCustomTargets()
301 {
302 for (auto& initializer : this->Initializers_) {
303 if (!initializer->SetupCustomTargets()) {
304 return false;
305 }
306 }
307 return true;
308 }
309