Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 <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 "cmProcessOutput.h"
17
#include "cmQtAutoGen.h"
18
#include "cmQtAutoGenInitializer.h"
19
#include "cmState.h"
20
#include "cmStateTypes.h"
21
#include "cmStringAlgorithms.h"
22
#include "cmSystemTools.h"
23
#include "cmTarget.h"
24
#include "cmValue.h"
25
26
cmQtAutoGenGlobalInitializer::Keywords::Keywords()
27
0
  : AUTOMOC("AUTOMOC")
28
0
  , AUTOUIC("AUTOUIC")
29
0
  , AUTORCC("AUTORCC")
30
0
  , AUTOMOC_EXECUTABLE("AUTOMOC_EXECUTABLE")
31
0
  , AUTOUIC_EXECUTABLE("AUTOUIC_EXECUTABLE")
32
0
  , AUTORCC_EXECUTABLE("AUTORCC_EXECUTABLE")
33
0
  , SKIP_AUTOGEN("SKIP_AUTOGEN")
34
0
  , SKIP_AUTOMOC("SKIP_AUTOMOC")
35
0
  , SKIP_AUTOUIC("SKIP_AUTOUIC")
36
0
  , SKIP_AUTORCC("SKIP_AUTORCC")
37
0
  , AUTOUIC_OPTIONS("AUTOUIC_OPTIONS")
38
0
  , AUTORCC_OPTIONS("AUTORCC_OPTIONS")
39
0
  , qrc("qrc")
40
0
  , ui("ui")
41
0
{
42
0
}
43
44
cmQtAutoGenGlobalInitializer::cmQtAutoGenGlobalInitializer(
45
  std::vector<std::unique_ptr<cmLocalGenerator>> const& localGenerators)
46
0
{
47
0
  for (auto const& localGen : localGenerators) {
48
    // Detect global autogen and autorcc target names
49
0
    bool globalAutoGenTarget = false;
50
0
    bool globalAutoRccTarget = false;
51
0
    {
52
0
      cmMakefile const* makefile = localGen->GetMakefile();
53
      // Detect global autogen target name
54
0
      if (makefile->IsOn("CMAKE_GLOBAL_AUTOGEN_TARGET")) {
55
0
        std::string targetName =
56
0
          makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTOGEN_TARGET_NAME");
57
0
        if (targetName.empty()) {
58
0
          targetName = "autogen";
59
0
        }
60
0
        this->GlobalAutoGenTargets_.emplace(localGen.get(),
61
0
                                            std::move(targetName));
62
0
        globalAutoGenTarget = true;
63
0
      }
64
65
      // Detect global autorcc target name
66
0
      if (makefile->IsOn("CMAKE_GLOBAL_AUTORCC_TARGET")) {
67
0
        std::string targetName =
68
0
          makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTORCC_TARGET_NAME");
69
0
        if (targetName.empty()) {
70
0
          targetName = "autorcc";
71
0
        }
72
0
        this->GlobalAutoRccTargets_.emplace(localGen.get(),
73
0
                                            std::move(targetName));
74
0
        globalAutoRccTarget = true;
75
0
      }
76
0
    }
77
78
    // Find targets that require AUTOMOC/UIC/RCC processing
79
0
    for (auto const& target : localGen->GetGeneratorTargets()) {
80
      // Process only certain target types
81
0
      switch (target->GetType()) {
82
0
        case cmStateEnums::EXECUTABLE:
83
0
        case cmStateEnums::STATIC_LIBRARY:
84
0
        case cmStateEnums::SHARED_LIBRARY:
85
0
        case cmStateEnums::MODULE_LIBRARY:
86
0
        case cmStateEnums::OBJECT_LIBRARY:
87
          // Process target
88
0
          break;
89
0
        default:
90
          // Don't process target
91
0
          continue;
92
0
      }
93
0
      if (target->IsImported()) {
94
        // Don't process target
95
0
        continue;
96
0
      }
97
0
      std::set<std::string> const& languages =
98
0
        target->GetAllConfigCompileLanguages();
99
      // cmGeneratorTarget::GetAllConfigCompileLanguages caches the target's
100
      // sources. Clear it so that OBJECT library targets that are AUTOGEN
101
      // initialized after this target get their added mocs_compilation.cpp
102
      // source acknowledged by this target.
103
0
      target->ClearSourcesCache();
104
0
      if (languages.count("CSharp")) {
105
        // Don't process target if it's a CSharp target
106
0
        continue;
107
0
      }
108
109
0
      bool const moc = target->GetPropertyAsBool(this->kw().AUTOMOC);
110
0
      bool const uic = target->GetPropertyAsBool(this->kw().AUTOUIC);
111
0
      bool const rcc = target->GetPropertyAsBool(this->kw().AUTORCC);
112
0
      if (moc || uic || rcc) {
113
0
        std::string const& mocExec =
114
0
          target->GetSafeProperty(this->kw().AUTOMOC_EXECUTABLE);
115
0
        std::string const& uicExec =
116
0
          target->GetSafeProperty(this->kw().AUTOUIC_EXECUTABLE);
117
0
        std::string const& rccExec =
118
0
          target->GetSafeProperty(this->kw().AUTORCC_EXECUTABLE);
119
120
        // We support Qt4, Qt5 and Qt6
121
0
        auto const qtVersion =
122
0
          cmQtAutoGenInitializer::GetQtVersion(target.get(), mocExec);
123
0
        bool const validQt = (qtVersion.first.Major == 4) ||
124
0
          (qtVersion.first.Major == 5) || (qtVersion.first.Major == 6);
125
126
0
        bool const mocAvailable = (validQt || !mocExec.empty());
127
0
        bool const uicAvailable = (validQt || !uicExec.empty());
128
0
        bool const rccAvailable = (validQt || !rccExec.empty());
129
0
        bool const mocIsValid = (moc && mocAvailable);
130
0
        bool const uicIsValid = (uic && uicAvailable);
131
0
        bool const rccIsValid = (rcc && rccAvailable);
132
        // Disabled AUTOMOC/UIC/RCC warning
133
0
        bool const mocDisabled = (moc && !mocAvailable);
134
0
        bool const uicDisabled = (uic && !uicAvailable);
135
0
        bool const rccDisabled = (rcc && !rccAvailable);
136
0
        if (mocDisabled || uicDisabled || rccDisabled) {
137
0
          cmAlphaNum version = (qtVersion.second == 0)
138
0
            ? cmAlphaNum("<QTVERSION>")
139
0
            : cmAlphaNum(qtVersion.second);
140
0
          cmAlphaNum component = uicDisabled ? "Widgets" : "Core";
141
142
0
          std::string const msg = cmStrCat(
143
0
            "AUTOGEN: No valid Qt version found for target ",
144
0
            target->GetName(), ".  ",
145
0
            cmQtAutoGen::Tools(mocDisabled, uicDisabled, rccDisabled),
146
0
            " disabled.  Consider adding:\n  find_package(Qt", version,
147
0
            " COMPONENTS ", component, ")\nto your CMakeLists.txt file.");
148
0
          target->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, msg);
149
0
        }
150
0
        if (mocIsValid || uicIsValid || rccIsValid) {
151
          // Create autogen target initializer
152
0
          this->Initializers_.emplace_back(
153
0
            cm::make_unique<cmQtAutoGenInitializer>(
154
0
              this, target.get(), qtVersion.first, mocIsValid, uicIsValid,
155
0
              rccIsValid, globalAutoGenTarget, globalAutoRccTarget));
156
0
        }
157
0
      }
158
0
    }
159
0
  }
160
0
}
161
162
0
cmQtAutoGenGlobalInitializer::~cmQtAutoGenGlobalInitializer() = default;
163
164
void cmQtAutoGenGlobalInitializer::GetOrCreateGlobalTarget(
165
  cmLocalGenerator* localGen, std::string const& name,
166
  std::string const& comment)
167
0
{
168
  // Test if the target already exists
169
0
  if (!localGen->FindGeneratorTargetToUse(name)) {
170
0
    cmMakefile const* makefile = localGen->GetMakefile();
171
172
    // Create utility target
173
0
    auto cc = cm::make_unique<cmCustomCommand>();
174
0
    cc->SetWorkingDirectory(makefile->GetHomeOutputDirectory().c_str());
175
0
    cc->SetEscapeOldStyle(false);
176
0
    cc->SetComment(comment.c_str());
177
0
    cmTarget* target = localGen->AddUtilityCommand(name, true, std::move(cc));
178
0
    localGen->AddGeneratorTarget(
179
0
      cm::make_unique<cmGeneratorTarget>(target, localGen));
180
181
    // Set FOLDER property in the target
182
0
    {
183
0
      cmValue folder =
184
0
        makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER");
185
0
      if (folder) {
186
0
        target->SetProperty("FOLDER", folder);
187
0
      }
188
0
    }
189
0
  }
190
0
}
191
192
void cmQtAutoGenGlobalInitializer::AddToGlobalAutoGen(
193
  cmLocalGenerator* localGen, std::string const& targetName)
194
0
{
195
0
  auto const it = this->GlobalAutoGenTargets_.find(localGen);
196
0
  if (it != this->GlobalAutoGenTargets_.end()) {
197
0
    cmGeneratorTarget const* target =
198
0
      localGen->FindGeneratorTargetToUse(it->second);
199
0
    if (target) {
200
0
      target->Target->AddUtility(targetName, false, localGen->GetMakefile());
201
0
    }
202
0
  }
203
0
}
204
205
void cmQtAutoGenGlobalInitializer::AddToGlobalAutoRcc(
206
  cmLocalGenerator* localGen, std::string const& targetName)
207
0
{
208
0
  auto const it = this->GlobalAutoRccTargets_.find(localGen);
209
0
  if (it != this->GlobalAutoRccTargets_.end()) {
210
0
    cmGeneratorTarget const* target =
211
0
      localGen->FindGeneratorTargetToUse(it->second);
212
0
    if (target) {
213
0
      target->Target->AddUtility(targetName, false, localGen->GetMakefile());
214
0
    }
215
0
  }
216
0
}
217
218
cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>
219
cmQtAutoGenGlobalInitializer::GetCompilerFeatures(
220
  std::string const& generator, cmQtAutoGen::ConfigString const& executable,
221
  std::string& error, bool const isMultiConfig, bool const UseBetterGraph)
222
0
{
223
0
  cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> res;
224
0
  if (isMultiConfig && UseBetterGraph) {
225
0
    for (auto const& config : executable.Config) {
226
0
      auto const exe = config.second;
227
      // Check if we have cached features
228
0
      {
229
0
        auto it = this->CompilerFeatures_.Config[config.first].find(exe);
230
0
        if (it != this->CompilerFeatures_.Config[config.first].end()) {
231
0
          res.Config[config.first] = it->second;
232
0
          continue;
233
0
        }
234
0
      }
235
236
      // Check if the executable exists
237
0
      if (!cmSystemTools::FileExists(exe, true)) {
238
0
        error = cmStrCat("The \"", generator, "\" executable ",
239
0
                         cmQtAutoGen::Quoted(exe), " does not exist.");
240
0
        res.Config[config.first] = {};
241
0
        continue;
242
0
      }
243
244
      // Test the executable
245
0
      std::string stdOut;
246
0
      {
247
0
        std::string stdErr;
248
0
        std::vector<std::string> command;
249
0
        command.emplace_back(exe);
250
0
        command.emplace_back("-h");
251
0
        int retVal = 0;
252
0
        bool const runResult = cmSystemTools::RunSingleCommand(
253
0
          command, &stdOut, &stdErr, &retVal, nullptr,
254
0
          cmSystemTools::OUTPUT_NONE, cmDuration::zero(),
255
0
          cmProcessOutput::Auto);
256
0
        if (!runResult) {
257
0
          error = cmStrCat("Test run of \"", generator, "\" executable ",
258
0
                           cmQtAutoGen::Quoted(exe), " failed.\n",
259
0
                           cmQtAutoGen::QuotedCommand(command), '\n', stdOut,
260
0
                           '\n', stdErr);
261
0
          res.Config[config.first] = {};
262
0
          continue;
263
0
        }
264
0
      }
265
266
      // Create valid handle
267
0
      res.Config[config.first] =
268
0
        std::make_shared<cmQtAutoGen::CompilerFeatures>();
269
0
      res.Config[config.first]->HelpOutput = std::move(stdOut);
270
271
      // Register compiler features
272
0
      this->CompilerFeatures_.Config[config.first].emplace(
273
0
        exe, res.Config[config.first]);
274
0
    }
275
0
    return res;
276
0
  }
277
278
  // Check if we have cached features
279
0
  {
280
0
    auto const it = this->CompilerFeatures_.Default.find(executable.Default);
281
0
    if (it != this->CompilerFeatures_.Default.end()) {
282
0
      res.Default = it->second;
283
0
      return res;
284
0
    }
285
0
  }
286
287
  // Check if the executable exists
288
0
  if (!cmSystemTools::FileExists(executable.Default, true)) {
289
0
    error =
290
0
      cmStrCat("The \"", generator, "\" executable ",
291
0
               cmQtAutoGen::Quoted(executable.Default), " does not exist.");
292
0
    return cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>();
293
0
  }
294
295
  // Test the executable
296
0
  std::string stdOut;
297
0
  {
298
0
    std::string stdErr;
299
0
    std::vector<std::string> command;
300
0
    command.emplace_back(executable.Default);
301
0
    command.emplace_back("-h");
302
0
    int retVal = 0;
303
0
    bool const runResult = cmSystemTools::RunSingleCommand(
304
0
      command, &stdOut, &stdErr, &retVal, nullptr, cmSystemTools::OUTPUT_NONE,
305
0
      cmDuration::zero(), cmProcessOutput::Auto);
306
0
    if (!runResult) {
307
0
      error = cmStrCat("Test run of \"", generator, "\" executable ",
308
0
                       cmQtAutoGen::Quoted(executable.Default), " failed.\n",
309
0
                       cmQtAutoGen::QuotedCommand(command), '\n', stdOut, '\n',
310
0
                       stdErr);
311
0
      return cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>();
312
0
    }
313
0
  }
314
315
0
  res.Default = std::make_shared<cmQtAutoGen::CompilerFeatures>();
316
0
  res.Default->HelpOutput = std::move(stdOut);
317
318
  // Register compiler features
319
0
  this->CompilerFeatures_.Default.emplace(executable.Default, res.Default);
320
321
0
  return res;
322
0
}
323
324
bool cmQtAutoGenGlobalInitializer::InitializeCustomTargets()
325
0
{
326
  // Initialize global autogen targets
327
0
  {
328
0
    std::string const comment = "Global AUTOGEN target";
329
0
    for (auto const& pair : this->GlobalAutoGenTargets_) {
330
0
      this->GetOrCreateGlobalTarget(pair.first, pair.second, comment);
331
0
    }
332
0
  }
333
  // Initialize global autorcc targets
334
0
  {
335
0
    std::string const comment = "Global AUTORCC target";
336
0
    for (auto const& pair : this->GlobalAutoRccTargets_) {
337
0
      this->GetOrCreateGlobalTarget(pair.first, pair.second, comment);
338
0
    }
339
0
  }
340
  // Initialize per target autogen targets
341
0
  for (auto& initializer : this->Initializers_) {
342
0
    if (!initializer->InitCustomTargets()) {
343
0
      return false;
344
0
    }
345
0
  }
346
0
  return true;
347
0
}
348
349
bool cmQtAutoGenGlobalInitializer::SetupCustomTargets()
350
0
{
351
0
  for (auto& initializer : this->Initializers_) {
352
0
    if (!initializer->SetupCustomTargets()) {
353
0
      return false;
354
0
    }
355
0
  }
356
0
  return true;
357
0
}