Coverage Report

Created: 2026-10-28 06:38

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 <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
}