/src/CMake/Source/cmQtAutoGenGlobalInitializer.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmQtAutoGenGlobalInitializer.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <set> |
7 | | #include <utility> |
8 | | |
9 | | #include <cm/memory> |
10 | | #include <cm/optional> |
11 | | |
12 | | #include "cmsys/Directory.hxx" |
13 | | |
14 | | #include "cmCustomCommand.h" |
15 | | #include "cmDiagnostics.h" |
16 | | #include "cmDuration.h" |
17 | | #include "cmGeneratorTarget.h" |
18 | | #include "cmLocalGenerator.h" |
19 | | #include "cmMakefile.h" |
20 | | #include "cmProcessOutput.h" |
21 | | #include "cmQtAutoGen.h" |
22 | | #include "cmQtAutoGenInitializer.h" |
23 | | #include "cmState.h" |
24 | | #include "cmStringAlgorithms.h" |
25 | | #include "cmSystemTools.h" |
26 | | #include "cmTarget.h" |
27 | | #include "cmTargetTypes.h" |
28 | | #include "cmValue.h" |
29 | | |
30 | | cmQtAutoGenGlobalInitializer::Keywords::Keywords() |
31 | 0 | : AUTOMOC("AUTOMOC") |
32 | 0 | , AUTOUIC("AUTOUIC") |
33 | 0 | , AUTORCC("AUTORCC") |
34 | 0 | , AUTOMOC_EXECUTABLE("AUTOMOC_EXECUTABLE") |
35 | 0 | , AUTOUIC_EXECUTABLE("AUTOUIC_EXECUTABLE") |
36 | 0 | , AUTORCC_EXECUTABLE("AUTORCC_EXECUTABLE") |
37 | 0 | , SKIP_AUTOGEN("SKIP_AUTOGEN") |
38 | 0 | , SKIP_AUTOMOC("SKIP_AUTOMOC") |
39 | 0 | , SKIP_AUTOUIC("SKIP_AUTOUIC") |
40 | 0 | , SKIP_AUTORCC("SKIP_AUTORCC") |
41 | 0 | , AUTOUIC_OPTIONS("AUTOUIC_OPTIONS") |
42 | 0 | , AUTORCC_OPTIONS("AUTORCC_OPTIONS") |
43 | 0 | , qrc("qrc") |
44 | 0 | , ui("ui") |
45 | 0 | { |
46 | 0 | } |
47 | | |
48 | | cmQtAutoGenGlobalInitializer::cmQtAutoGenGlobalInitializer( |
49 | | std::vector<std::unique_ptr<cmLocalGenerator>> const& localGenerators) |
50 | 0 | { |
51 | 0 | for (auto const& localGen : localGenerators) { |
52 | | // Detect global autogen and autorcc target names |
53 | 0 | bool globalAutoGenTarget = false; |
54 | 0 | bool globalAutoRccTarget = false; |
55 | 0 | { |
56 | 0 | cmMakefile const* makefile = localGen->GetMakefile(); |
57 | | // Detect global autogen target name |
58 | 0 | if (makefile->IsOn("CMAKE_GLOBAL_AUTOGEN_TARGET")) { |
59 | 0 | std::string targetName = |
60 | 0 | makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTOGEN_TARGET_NAME"); |
61 | 0 | if (targetName.empty()) { |
62 | 0 | targetName = "autogen"; |
63 | 0 | } |
64 | 0 | this->GlobalAutoGenTargets_.emplace(localGen.get(), |
65 | 0 | std::move(targetName)); |
66 | 0 | globalAutoGenTarget = true; |
67 | 0 | } |
68 | | |
69 | | // Detect global autorcc target name |
70 | 0 | if (makefile->IsOn("CMAKE_GLOBAL_AUTORCC_TARGET")) { |
71 | 0 | std::string targetName = |
72 | 0 | makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTORCC_TARGET_NAME"); |
73 | 0 | if (targetName.empty()) { |
74 | 0 | targetName = "autorcc"; |
75 | 0 | } |
76 | 0 | this->GlobalAutoRccTargets_.emplace(localGen.get(), |
77 | 0 | std::move(targetName)); |
78 | 0 | globalAutoRccTarget = true; |
79 | 0 | } |
80 | 0 | } |
81 | | |
82 | | // Find targets that require AUTOMOC/UIC/RCC processing |
83 | 0 | for (auto const& target : localGen->GetGeneratorTargets()) { |
84 | | // Process only certain target types |
85 | 0 | switch (target->GetType()) { |
86 | 0 | case cm::TargetType::EXECUTABLE: |
87 | 0 | case cm::TargetType::STATIC_LIBRARY: |
88 | 0 | case cm::TargetType::SHARED_LIBRARY: |
89 | 0 | case cm::TargetType::MODULE_LIBRARY: |
90 | 0 | case cm::TargetType::OBJECT_LIBRARY: |
91 | | // Process target |
92 | 0 | break; |
93 | 0 | default: |
94 | | // Don't process target |
95 | 0 | continue; |
96 | 0 | } |
97 | 0 | if (target->IsImported()) { |
98 | | // Don't process target |
99 | 0 | continue; |
100 | 0 | } |
101 | 0 | std::set<std::string> const& languages = |
102 | 0 | target->GetAllConfigCompileLanguages(); |
103 | | // cmGeneratorTarget::GetAllConfigCompileLanguages caches the target's |
104 | | // sources. Clear it so that OBJECT library targets that are AUTOGEN |
105 | | // initialized after this target get their added mocs_compilation.cpp |
106 | | // source acknowledged by this target. |
107 | 0 | target->ClearSourcesCache(); |
108 | 0 | if (languages.count("CSharp")) { |
109 | | // Don't process target if it's a CSharp target |
110 | 0 | continue; |
111 | 0 | } |
112 | | |
113 | 0 | bool const moc = target->GetPropertyAsBool(this->kw().AUTOMOC); |
114 | 0 | bool const uic = target->GetPropertyAsBool(this->kw().AUTOUIC); |
115 | 0 | bool const rcc = target->GetPropertyAsBool(this->kw().AUTORCC); |
116 | 0 | if (moc || uic || rcc) { |
117 | 0 | std::string const& mocExec = |
118 | 0 | target->GetSafeProperty(this->kw().AUTOMOC_EXECUTABLE); |
119 | 0 | std::string const& uicExec = |
120 | 0 | target->GetSafeProperty(this->kw().AUTOUIC_EXECUTABLE); |
121 | 0 | std::string const& rccExec = |
122 | 0 | target->GetSafeProperty(this->kw().AUTORCC_EXECUTABLE); |
123 | | |
124 | | // We support Qt4, Qt5 and Qt6 |
125 | 0 | auto const qtVersion = |
126 | 0 | cmQtAutoGenInitializer::GetQtVersion(target.get(), mocExec); |
127 | 0 | bool const validQt = (qtVersion.first.Major == 4) || |
128 | 0 | (qtVersion.first.Major == 5) || (qtVersion.first.Major == 6); |
129 | |
|
130 | 0 | bool const mocAvailable = (validQt || !mocExec.empty()); |
131 | 0 | bool const uicAvailable = (validQt || !uicExec.empty()); |
132 | 0 | bool const rccAvailable = (validQt || !rccExec.empty()); |
133 | 0 | bool const mocIsValid = (moc && mocAvailable); |
134 | 0 | bool const uicIsValid = (uic && uicAvailable); |
135 | 0 | bool const rccIsValid = (rcc && rccAvailable); |
136 | | // Disabled AUTOMOC/UIC/RCC warning |
137 | 0 | bool const mocDisabled = (moc && !mocAvailable); |
138 | 0 | bool const uicDisabled = (uic && !uicAvailable); |
139 | 0 | bool const rccDisabled = (rcc && !rccAvailable); |
140 | 0 | if (mocDisabled || uicDisabled || rccDisabled) { |
141 | 0 | cmAlphaNum version = (qtVersion.second == 0) |
142 | 0 | ? cmAlphaNum("<QTVERSION>") |
143 | 0 | : cmAlphaNum(qtVersion.second); |
144 | 0 | cmAlphaNum component = uicDisabled ? "Widgets" : "Core"; |
145 | |
|
146 | 0 | std::string const msg = cmStrCat( |
147 | 0 | "AUTOGEN: No valid Qt version found for target ", |
148 | 0 | target->GetName(), ". ", |
149 | 0 | cmQtAutoGen::Tools(mocDisabled, uicDisabled, rccDisabled), |
150 | 0 | " disabled. Consider adding:\n find_package(Qt", version, |
151 | 0 | " COMPONENTS ", component, ")\nto your CMakeLists.txt file."); |
152 | 0 | target->Makefile->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, msg); |
153 | 0 | } |
154 | 0 | if (mocIsValid || uicIsValid || rccIsValid) { |
155 | | // Create autogen target initializer |
156 | 0 | this->Initializers_.emplace_back( |
157 | 0 | cm::make_unique<cmQtAutoGenInitializer>( |
158 | 0 | this, target.get(), qtVersion.first, mocIsValid, uicIsValid, |
159 | 0 | rccIsValid, globalAutoGenTarget, globalAutoRccTarget)); |
160 | 0 | } |
161 | 0 | } |
162 | 0 | } |
163 | 0 | } |
164 | 0 | } |
165 | | |
166 | 0 | cmQtAutoGenGlobalInitializer::~cmQtAutoGenGlobalInitializer() = default; |
167 | | |
168 | | bool cmQtAutoGenGlobalInitializer::CachedFileExists(std::string const& path) |
169 | 0 | { |
170 | | // Most of the header paths probed for a source file do not exist. |
171 | | // A file whose name is not in the directory, compared without regard |
172 | | // to case, cannot exist. Names that are present, and names whose case |
173 | | // cannot be folded reliably, are checked on disk as before. So are |
174 | | // names Windows may resolve without a matching directory entry: short |
175 | | // 8.3 names, and names with a trailing dot or space. |
176 | | // |
177 | | // The listings are kept only while the per target initializers scan |
178 | | // their sources. Nothing creates files in the probed directories |
179 | | // meanwhile; AUTOGEN writes only to its own build directories, and |
180 | | // file(GENERATE) outputs are written later. |
181 | 0 | std::string const name = cmSystemTools::GetFilenameName(path); |
182 | 0 | bool const listable = !name.empty() && name.back() != '.' && |
183 | 0 | name.back() != ' ' && name.find('~') == std::string::npos && |
184 | 0 | std::all_of(name.begin(), name.end(), |
185 | 0 | [](char c) { return static_cast<unsigned char>(c) < 0x80; }); |
186 | 0 | if (listable) { |
187 | 0 | std::string const dir = cmSystemTools::GetFilenamePath(path); |
188 | 0 | auto it = this->DirectoryNames_.find(dir); |
189 | 0 | if (it == this->DirectoryNames_.end()) { |
190 | 0 | cmsys::Directory d; |
191 | 0 | if (d.Load(dir)) { |
192 | 0 | std::unordered_set<std::string> names; |
193 | 0 | unsigned long const n = d.GetNumberOfFiles(); |
194 | 0 | for (unsigned long i = 0; i < n; ++i) { |
195 | 0 | names.insert(cmSystemTools::LowerCase(d.GetFileName(i))); |
196 | 0 | } |
197 | 0 | it = this->DirectoryNames_.emplace(dir, std::move(names)).first; |
198 | 0 | } |
199 | 0 | } |
200 | 0 | if (it != this->DirectoryNames_.end() && |
201 | 0 | !it->second.count(cmSystemTools::LowerCase(name))) { |
202 | 0 | return false; |
203 | 0 | } |
204 | 0 | } |
205 | 0 | return cmSystemTools::FileExists(path); |
206 | 0 | } |
207 | | |
208 | | void cmQtAutoGenGlobalInitializer::GetOrCreateGlobalTarget( |
209 | | cmLocalGenerator* localGen, std::string const& name, |
210 | | std::string const& comment) |
211 | 0 | { |
212 | | // Test if the target already exists |
213 | 0 | if (!localGen->FindGeneratorTargetToUse(name)) { |
214 | 0 | cmMakefile const* makefile = localGen->GetMakefile(); |
215 | | |
216 | | // Create utility target |
217 | 0 | auto cc = cm::make_unique<cmCustomCommand>(); |
218 | 0 | cc->SetWorkingDirectory(makefile->GetHomeOutputDirectory()); |
219 | 0 | cc->SetEscapeOldStyle(false); |
220 | 0 | cc->SetComment(comment); |
221 | 0 | cmTarget* target = localGen->AddUtilityCommand(name, true, std::move(cc)); |
222 | 0 | localGen->AddGeneratorTarget( |
223 | 0 | cm::make_unique<cmGeneratorTarget>(target, localGen)); |
224 | | |
225 | | // Set FOLDER property in the target |
226 | 0 | { |
227 | 0 | cmValue folder = |
228 | 0 | makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER"); |
229 | 0 | if (folder) { |
230 | 0 | target->SetProperty("FOLDER", folder); |
231 | 0 | } |
232 | 0 | } |
233 | 0 | } |
234 | 0 | } |
235 | | |
236 | | void cmQtAutoGenGlobalInitializer::AddToGlobalAutoGen( |
237 | | cmLocalGenerator* localGen, std::string const& targetName) |
238 | 0 | { |
239 | 0 | auto const it = this->GlobalAutoGenTargets_.find(localGen); |
240 | 0 | if (it != this->GlobalAutoGenTargets_.end()) { |
241 | 0 | cmGeneratorTarget const* target = |
242 | 0 | localGen->FindGeneratorTargetToUse(it->second); |
243 | 0 | if (target) { |
244 | 0 | target->Target->AddUtility(targetName, false, localGen->GetMakefile()); |
245 | 0 | } |
246 | 0 | } |
247 | 0 | } |
248 | | |
249 | | void cmQtAutoGenGlobalInitializer::AddToGlobalAutoRcc( |
250 | | cmLocalGenerator* localGen, std::string const& targetName) |
251 | 0 | { |
252 | 0 | auto const it = this->GlobalAutoRccTargets_.find(localGen); |
253 | 0 | if (it != this->GlobalAutoRccTargets_.end()) { |
254 | 0 | cmGeneratorTarget const* target = |
255 | 0 | localGen->FindGeneratorTargetToUse(it->second); |
256 | 0 | if (target) { |
257 | 0 | target->Target->AddUtility(targetName, false, localGen->GetMakefile()); |
258 | 0 | } |
259 | 0 | } |
260 | 0 | } |
261 | | |
262 | | cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> |
263 | | cmQtAutoGenGlobalInitializer::GetCompilerFeatures( |
264 | | std::string const& generator, cmQtAutoGen::ConfigString const& executable, |
265 | | std::string& error, bool const isMultiConfig, bool const UseBetterGraph) |
266 | 0 | { |
267 | 0 | cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> res; |
268 | 0 | if (isMultiConfig && UseBetterGraph) { |
269 | 0 | for (auto const& config : executable.Config) { |
270 | 0 | auto const exe = config.second; |
271 | | // Check if we have cached features |
272 | 0 | { |
273 | 0 | auto it = this->CompilerFeatures_.Config[config.first].find(exe); |
274 | 0 | if (it != this->CompilerFeatures_.Config[config.first].end()) { |
275 | 0 | res.Config[config.first] = it->second; |
276 | 0 | continue; |
277 | 0 | } |
278 | 0 | } |
279 | | |
280 | | // Check if the executable exists |
281 | 0 | if (!cmSystemTools::FileExists(exe, true)) { |
282 | 0 | error = cmStrCat("The \"", generator, "\" executable ", |
283 | 0 | cmQtAutoGen::Quoted(exe), " does not exist."); |
284 | 0 | res.Config[config.first] = {}; |
285 | 0 | continue; |
286 | 0 | } |
287 | | |
288 | | // Test the executable |
289 | 0 | std::string stdOut; |
290 | 0 | { |
291 | 0 | std::string stdErr; |
292 | 0 | std::vector<std::string> command; |
293 | 0 | command.emplace_back(exe); |
294 | 0 | command.emplace_back("-h"); |
295 | 0 | int retVal = 0; |
296 | 0 | bool const runResult = cmSystemTools::RunSingleCommand( |
297 | 0 | command, &stdOut, &stdErr, &retVal, nullptr, |
298 | 0 | cmSystemTools::OUTPUT_NONE, cmDuration::zero(), |
299 | 0 | cmProcessOutput::Auto); |
300 | 0 | if (!runResult) { |
301 | 0 | error = cmStrCat("Test run of \"", generator, "\" executable ", |
302 | 0 | cmQtAutoGen::Quoted(exe), " failed.\n", |
303 | 0 | cmQtAutoGen::QuotedCommand(command), '\n', stdOut, |
304 | 0 | '\n', stdErr); |
305 | 0 | res.Config[config.first] = {}; |
306 | 0 | continue; |
307 | 0 | } |
308 | 0 | } |
309 | | |
310 | | // Create valid handle |
311 | 0 | res.Config[config.first] = |
312 | 0 | std::make_shared<cmQtAutoGen::CompilerFeatures>(); |
313 | 0 | res.Config[config.first]->HelpOutput = std::move(stdOut); |
314 | | |
315 | | // Register compiler features |
316 | 0 | this->CompilerFeatures_.Config[config.first].emplace( |
317 | 0 | exe, res.Config[config.first]); |
318 | 0 | } |
319 | 0 | return res; |
320 | 0 | } |
321 | | |
322 | | // Check if we have cached features |
323 | 0 | { |
324 | 0 | auto const it = this->CompilerFeatures_.Default.find(executable.Default); |
325 | 0 | if (it != this->CompilerFeatures_.Default.end()) { |
326 | 0 | res.Default = it->second; |
327 | 0 | return res; |
328 | 0 | } |
329 | 0 | } |
330 | | |
331 | | // Check if the executable exists |
332 | 0 | if (!cmSystemTools::FileExists(executable.Default, true)) { |
333 | 0 | error = |
334 | 0 | cmStrCat("The \"", generator, "\" executable ", |
335 | 0 | cmQtAutoGen::Quoted(executable.Default), " does not exist."); |
336 | 0 | return cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>(); |
337 | 0 | } |
338 | | |
339 | | // Test the executable |
340 | 0 | std::string stdOut; |
341 | 0 | { |
342 | 0 | std::string stdErr; |
343 | 0 | std::vector<std::string> command; |
344 | 0 | command.emplace_back(executable.Default); |
345 | 0 | command.emplace_back("-h"); |
346 | 0 | int retVal = 0; |
347 | 0 | bool const runResult = cmSystemTools::RunSingleCommand( |
348 | 0 | command, &stdOut, &stdErr, &retVal, nullptr, cmSystemTools::OUTPUT_NONE, |
349 | 0 | cmDuration::zero(), cmProcessOutput::Auto); |
350 | 0 | if (!runResult) { |
351 | 0 | error = cmStrCat("Test run of \"", generator, "\" executable ", |
352 | 0 | cmQtAutoGen::Quoted(executable.Default), " failed.\n", |
353 | 0 | cmQtAutoGen::QuotedCommand(command), '\n', stdOut, '\n', |
354 | 0 | stdErr); |
355 | 0 | return cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>(); |
356 | 0 | } |
357 | 0 | } |
358 | | |
359 | 0 | res.Default = std::make_shared<cmQtAutoGen::CompilerFeatures>(); |
360 | 0 | res.Default->HelpOutput = std::move(stdOut); |
361 | | |
362 | | // Register compiler features |
363 | 0 | this->CompilerFeatures_.Default.emplace(executable.Default, res.Default); |
364 | |
|
365 | 0 | return res; |
366 | 0 | } |
367 | | |
368 | | bool cmQtAutoGenGlobalInitializer::InitializeCustomTargets() |
369 | 0 | { |
370 | | // Initialize global autogen targets |
371 | 0 | { |
372 | 0 | std::string const comment = "Global AUTOGEN target"; |
373 | 0 | for (auto const& pair : this->GlobalAutoGenTargets_) { |
374 | 0 | this->GetOrCreateGlobalTarget(pair.first, pair.second, comment); |
375 | 0 | } |
376 | 0 | } |
377 | | // Initialize global autorcc targets |
378 | 0 | { |
379 | 0 | std::string const comment = "Global AUTORCC target"; |
380 | 0 | for (auto const& pair : this->GlobalAutoRccTargets_) { |
381 | 0 | this->GetOrCreateGlobalTarget(pair.first, pair.second, comment); |
382 | 0 | } |
383 | 0 | } |
384 | | // Initialize per target autogen targets |
385 | 0 | for (auto& initializer : this->Initializers_) { |
386 | 0 | if (!initializer->InitCustomTargets()) { |
387 | 0 | return false; |
388 | 0 | } |
389 | 0 | } |
390 | | // The directory listings are only valid while scanning sources. |
391 | 0 | this->DirectoryNames_.clear(); |
392 | 0 | return true; |
393 | 0 | } |
394 | | |
395 | | bool cmQtAutoGenGlobalInitializer::SetupCustomTargets() |
396 | 0 | { |
397 | 0 | for (auto& initializer : this->Initializers_) { |
398 | 0 | if (!initializer->SetupCustomTargets()) { |
399 | 0 | return false; |
400 | 0 | } |
401 | 0 | } |
402 | 0 | return true; |
403 | 0 | } |