resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmQtAutoGenGlobalInitializer.cxx
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 "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"
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
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
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
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     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));
182
183     // Set FOLDER property in the target
184     {
185       cmValue folder =
186         makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER");
187       if (folder) {
188         target->SetProperty("FOLDER", folder);
189       }
190     }
191   }
192 }
193
194 void cmQtAutoGenGlobalInitializer::AddToGlobalAutoGen(
195   cmLocalGenerator* localGen, std::string const& targetName)
196 {
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());
202     }
203   }
204 }
205
206 void cmQtAutoGenGlobalInitializer::AddToGlobalAutoRcc(
207   cmLocalGenerator* localGen, std::string const& targetName)
208 {
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());
214     }
215   }
216 }
217
218 cmQtAutoGen::CompilerFeaturesHandle
219 cmQtAutoGenGlobalInitializer::GetCompilerFeatures(
220   std::string const& generator, std::string const& executable,
221   std::string& error)
222 {
223   // Check if we have cached features
224   {
225     auto it = this->CompilerFeatures_.find(executable);
226     if (it != this->CompilerFeatures_.end()) {
227       return it->second;
228     }
229   }
230
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();
236   }
237
238   // Test the executable
239   std::string stdOut;
240   {
241     std::string stdErr;
242     std::vector<std::string> command;
243     command.emplace_back(executable);
244     command.emplace_back("-h");
245     int retVal = 0;
246     const bool runResult = cmSystemTools::RunSingleCommand(
247       command, &stdOut, &stdErr, &retVal, nullptr, cmSystemTools::OUTPUT_NONE,
248       cmDuration::zero(), cmProcessOutput::Auto);
249     if (!runResult) {
250       error = cmStrCat("Test run of \"", generator, "\" executable ",
251                        cmQtAutoGen::Quoted(executable), " failed.\n",
252                        cmQtAutoGen::QuotedCommand(command), '\n', stdOut, '\n',
253                        stdErr);
254       return cmQtAutoGen::CompilerFeaturesHandle();
255     }
256   }
257
258   // Create valid handle
259   cmQtAutoGen::CompilerFeaturesHandle res =
260     std::make_shared<cmQtAutoGen::CompilerFeatures>();
261   res->HelpOutput = std::move(stdOut);
262
263   // Register compiler features
264   this->CompilerFeatures_.emplace(executable, res);
265
266   return res;
267 }
268
269 bool cmQtAutoGenGlobalInitializer::generate()
270 {
271   return (this->InitializeCustomTargets() && this->SetupCustomTargets());
272 }
273
274 bool cmQtAutoGenGlobalInitializer::InitializeCustomTargets()
275 {
276   // Initialize global autogen targets
277   {
278     std::string const comment = "Global AUTOGEN target";
279     for (auto const& pair : this->GlobalAutoGenTargets_) {
280       this->GetOrCreateGlobalTarget(pair.first, pair.second, comment);
281     }
282   }
283   // Initialize global autorcc targets
284   {
285     std::string const comment = "Global AUTORCC target";
286     for (auto const& pair : this->GlobalAutoRccTargets_) {
287       this->GetOrCreateGlobalTarget(pair.first, pair.second, comment);
288     }
289   }
290   // Initialize per target autogen targets
291   for (auto& initializer : this->Initializers_) {
292     if (!initializer->InitCustomTargets()) {
293       return false;
294     }
295   }
296   return true;
297 }
298
299 bool cmQtAutoGenGlobalInitializer::SetupCustomTargets()
300 {
301   for (auto& initializer : this->Initializers_) {
302     if (!initializer->SetupCustomTargets()) {
303       return false;
304     }
305   }
306   return true;
307 }