cmQtAutoGenInitializer.cxx 53.9 KB
Newer Older
1
2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmQtAutoGenInitializer.h"
4
#include "cmQtAutoGen.h"
5
#include "cmQtAutoGenGlobalInitializer.h"
6

7
#include "cmAlgorithms.h"
8
#include "cmCustomCommand.h"
9
#include "cmCustomCommandLines.h"
10
#include "cmDuration.h"
11
#include "cmFilePathChecksum.h"
12
#include "cmGeneratorTarget.h"
13
#include "cmGlobalGenerator.h"
14
#include "cmLinkItem.h"
15
#include "cmListFileCache.h"
16
17
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
18
#include "cmOutputConverter.h"
19
#include "cmPolicies.h"
20
#include "cmProcessOutput.h"
21
#include "cmSourceFile.h"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
22
#include "cmSourceGroup.h"
23
#include "cmState.h"
24
#include "cmStateTypes.h"
25
26
27
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmake.h"
28
#include "cmsys/FStream.hxx"
29
#include "cmsys/SystemInformation.hxx"
30

31
#include <algorithm>
32
#include <array>
33
#include <deque>
34
35
#include <map>
#include <set>
36
#include <sstream>
37
38
39
40
#include <string>
#include <utility>
#include <vector>

41
42
43
44
45
46
47
48
49
50
51
52
53
54
static std::size_t GetParallelCPUCount()
{
  static std::size_t count = 0;
  // Detect only on the first call
  if (count == 0) {
    cmsys::SystemInformation info;
    info.RunCPUCheck();
    count = info.GetNumberOfPhysicalCPU();
    count = std::max<std::size_t>(count, 1);
    count = std::min<std::size_t>(count, cmQtAutoGen::ParallelMax);
  }
  return count;
}

55
static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName,
56
                             cmQtAutoGen::GeneratorT genType)
57
{
Daniel Pfeifer's avatar
Daniel Pfeifer committed
58
  cmSourceGroup* sourceGroup = nullptr;
59
60
  // Acquire source group
  {
61
62
63
64
65
66
    std::string property;
    std::string groupName;
    {
      std::array<std::string, 2> props;
      // Use generator specific group name
      switch (genType) {
67
        case cmQtAutoGen::GeneratorT::MOC:
68
69
          props[0] = "AUTOMOC_SOURCE_GROUP";
          break;
70
        case cmQtAutoGen::GeneratorT::RCC:
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
          props[0] = "AUTORCC_SOURCE_GROUP";
          break;
        default:
          props[0] = "AUTOGEN_SOURCE_GROUP";
          break;
      }
      props[1] = "AUTOGEN_SOURCE_GROUP";
      for (std::string& prop : props) {
        const char* propName = makefile->GetState()->GetGlobalProperty(prop);
        if ((propName != nullptr) && (*propName != '\0')) {
          groupName = propName;
          property = std::move(prop);
          break;
        }
      }
86
87
    }
    // Generate a source group on demand
88
    if (!groupName.empty()) {
89
      sourceGroup = makefile->GetOrCreateSourceGroup(groupName);
Daniel Pfeifer's avatar
Daniel Pfeifer committed
90
      if (sourceGroup == nullptr) {
91
92
93
94
95
96
        std::ostringstream ost;
        ost << cmQtAutoGen::GeneratorNameUpper(genType);
        ost << ": " << property;
        ost << ": Could not find or create the source group ";
        ost << cmQtAutoGen::Quoted(groupName);
        cmSystemTools::Error(ost.str().c_str());
97
98
99
100
        return false;
      }
    }
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
101
  if (sourceGroup != nullptr) {
102
103
104
105
106
    sourceGroup->AddGroupFile(fileName);
  }
  return true;
}

107
static void AddCleanFile(cmMakefile* makefile, std::string const& fileName)
108
109
110
111
112
{
  makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", fileName.c_str(),
                           false);
}

113
114
115
116
117
118
static std::string FileProjectRelativePath(cmMakefile* makefile,
                                           std::string const& fileName)
{
  std::string res;
  {
    std::string pSource = cmSystemTools::RelativePath(
119
      makefile->GetCurrentSourceDirectory(), fileName);
120
    std::string pBinary = cmSystemTools::RelativePath(
121
      makefile->GetCurrentBinaryDirectory(), fileName);
122
123
124
125
126
127
128
129
130
131
132
    if (pSource.size() < pBinary.size()) {
      res = std::move(pSource);
    } else if (pBinary.size() < fileName.size()) {
      res = std::move(pBinary);
    } else {
      res = fileName;
    }
  }
  return res;
}

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/* @brief Tests if targetDepend is a STATIC_LIBRARY and if any of its
 * recursive STATIC_LIBRARY dependencies depends on targetOrigin
 * (STATIC_LIBRARY cycle).
 */
static bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin,
                               cmGeneratorTarget const* targetDepend,
                               std::string const& config)
{
  bool cycle = false;
  if ((targetOrigin->GetType() == cmStateEnums::STATIC_LIBRARY) &&
      (targetDepend->GetType() == cmStateEnums::STATIC_LIBRARY)) {
    std::set<cmGeneratorTarget const*> knownLibs;
    std::deque<cmGeneratorTarget const*> testLibs;

    // Insert initial static_library dependency
    knownLibs.insert(targetDepend);
    testLibs.push_back(targetDepend);

    while (!testLibs.empty()) {
      cmGeneratorTarget const* testTarget = testLibs.front();
      testLibs.pop_front();
      // Check if the test target is the origin target (cycle)
      if (testTarget == targetOrigin) {
        cycle = true;
        break;
      }
      // Collect all static_library dependencies from the test target
      cmLinkImplementationLibraries const* libs =
        testTarget->GetLinkImplementationLibraries(config);
      if (libs != nullptr) {
        for (cmLinkItem const& item : libs->Libraries) {
          cmGeneratorTarget const* depTarget = item.Target;
          if ((depTarget != nullptr) &&
              (depTarget->GetType() == cmStateEnums::STATIC_LIBRARY) &&
              knownLibs.insert(depTarget).second) {
            testLibs.push_back(depTarget);
          }
        }
      }
    }
  }
  return cycle;
}

177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
cmQtAutoGenInitializer::InfoWriter::InfoWriter(std::string const& filename)
{
  Ofs_.SetCopyIfDifferent(true);
  Ofs_.Open(filename, false, true);
}

template <class IT>
std::string cmQtAutoGenInitializer::InfoWriter::ListJoin(IT it_begin,
                                                         IT it_end)
{
  std::string res;
  for (IT it = it_begin; it != it_end; ++it) {
    if (it != it_begin) {
      res += ';';
    }
    for (const char* c = it->c_str(); *c; ++c) {
      if (*c == '"') {
        // Escape the double quote to avoid ending the argument.
        res += "\\\"";
      } else if (*c == '$') {
        // Escape the dollar to avoid expanding variables.
        res += "\\$";
      } else if (*c == '\\') {
        // Escape the backslash to avoid other escapes.
        res += "\\\\";
      } else if (*c == ';') {
        // Escape the semicolon to avoid list expansion.
        res += "\\;";
      } else {
        // Other characters will be parsed correctly.
        res += *c;
      }
    }
  }
  return res;
}

std::string cmQtAutoGenInitializer::InfoWriter::ConfigKey(
  const char* key, std::string const& config)
{
  std::string ckey = key;
  ckey += '_';
  ckey += config;
  return ckey;
}

void cmQtAutoGenInitializer::InfoWriter::Write(const char* key,
                                               std::string const& value)
{
  Ofs_ << "set(" << key << " " << cmOutputConverter::EscapeForCMake(value)
       << ")\n";
};

void cmQtAutoGenInitializer::InfoWriter::WriteUInt(const char* key,
                                                   unsigned int value)
{
  Ofs_ << "set(" << key << " " << value << ")\n";
};

template <class C>
void cmQtAutoGenInitializer::InfoWriter::WriteStrings(const char* key,
                                                      C const& container)
{
  Ofs_ << "set(" << key << " \""
       << ListJoin(container.begin(), container.end()) << "\")\n";
}

void cmQtAutoGenInitializer::InfoWriter::WriteConfig(
  const char* key, std::map<std::string, std::string> const& map)
{
  for (auto const& item : map) {
    Write(ConfigKey(key, item.first).c_str(), item.second);
  }
};

template <class C>
void cmQtAutoGenInitializer::InfoWriter::WriteConfigStrings(
  const char* key, std::map<std::string, C> const& map)
{
  for (auto const& item : map) {
    WriteStrings(ConfigKey(key, item.first).c_str(), item.second);
  }
}

void cmQtAutoGenInitializer::InfoWriter::WriteNestedLists(
  const char* key, std::vector<std::vector<std::string>> const& lists)
{
  std::vector<std::string> seplist;
  for (const std::vector<std::string>& list : lists) {
    std::string blist = "{";
    blist += ListJoin(list.begin(), list.end());
    blist += "}";
    seplist.push_back(std::move(blist));
  }
  Write(key, cmJoin(seplist, cmQtAutoGen::ListSep));
};

274
275
276
277
278
279
cmQtAutoGenInitializer::cmQtAutoGenInitializer(
  cmQtAutoGenGlobalInitializer* globalInitializer, cmGeneratorTarget* target,
  IntegerVersion const& qtVersion, bool mocEnabled, bool uicEnabled,
  bool rccEnabled, bool globalAutogenTarget, bool globalAutoRccTarget)
  : GlobalInitializer(globalInitializer)
  , Target(target)
280
  , QtVersion(qtVersion)
281
{
282
  AutogenTarget.GlobalTarget = globalAutogenTarget;
283
284
285
  Moc.Enabled = mocEnabled;
  Uic.Enabled = uicEnabled;
  Rcc.Enabled = rccEnabled;
286
  Rcc.GlobalTarget = globalAutoRccTarget;
287
}
288

289
bool cmQtAutoGenInitializer::InitCustomTargets()
290
291
292
293
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
  cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();
294

295
296
297
298
299
300
301
  // Configurations
  this->MultiConfig = globalGen->IsMultiConfig();
  this->ConfigDefault = makefile->GetConfigurations(this->ConfigsList);
  if (this->ConfigsList.empty()) {
    this->ConfigsList.push_back(this->ConfigDefault);
  }

302
  // Verbosity
303
304
305
306
307
  this->Verbosity = makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE");
  if (!this->Verbosity.empty()) {
    unsigned long iVerb = 0;
    if (!cmSystemTools::StringToULong(this->Verbosity.c_str(), &iVerb)) {
      // Non numeric verbosity
308
      this->Verbosity = cmSystemTools::IsOn(this->Verbosity) ? "1" : "0";
309
310
311
    }
  }

312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
  // Targets FOLDER
  {
    const char* folder =
      makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
    if (folder == nullptr) {
      folder =
        makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER");
    }
    // Inherit FOLDER property from target (#13688)
    if (folder == nullptr) {
      folder = this->Target->GetProperty("FOLDER");
    }
    if (folder != nullptr) {
      this->TargetsFolder = folder;
    }
327
328
  }

329
  // Common directories
330
331
332
  {
    // Collapsed current binary directory
    std::string const cbd = cmSystemTools::CollapseFullPath(
333
      std::string(), makefile->GetCurrentBinaryDirectory());
334

335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
    // Info directory
    this->Dir.Info = cbd;
    this->Dir.Info += makefile->GetCMakeInstance()->GetCMakeFilesDirectory();
    this->Dir.Info += '/';
    this->Dir.Info += this->Target->GetName();
    this->Dir.Info += "_autogen";
    this->Dir.Info += ".dir";
    cmSystemTools::ConvertToUnixSlashes(this->Dir.Info);

    // Build directory
    this->Dir.Build = this->Target->GetSafeProperty("AUTOGEN_BUILD_DIR");
    if (this->Dir.Build.empty()) {
      this->Dir.Build = cbd;
      this->Dir.Build += '/';
      this->Dir.Build += this->Target->GetName();
      this->Dir.Build += "_autogen";
351
    }
352
353
354
    cmSystemTools::ConvertToUnixSlashes(this->Dir.Build);
    // Cleanup build directory
    AddCleanFile(makefile, this->Dir.Build);
355
356

    // Working directory
357
358
    this->Dir.Work = cbd;
    cmSystemTools::ConvertToUnixSlashes(this->Dir.Work);
359
360

    // Include directory
361
362
    this->Dir.Include = this->Dir.Build;
    this->Dir.Include += "/include";
363
    if (this->MultiConfig) {
364
      this->Dir.Include += "_$<CONFIG>";
365
    }
366
    // Per config include directories
367
368
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
369
370
        std::string& dir = this->Dir.ConfigInclude[cfg];
        dir = this->Dir.Build;
371
372
373
374
        dir += "/include_";
        dir += cfg;
      }
    }
375
  }
376

377
  // Moc, Uic and _autogen target settings
378
379
380
381
382
  if (this->Moc.Enabled || this->Uic.Enabled) {
    // Init moc specific settings
    if (this->Moc.Enabled && !InitMoc()) {
      return false;
    }
383

384
385
386
387
388
    // Init uic specific settings
    if (this->Uic.Enabled && !InitUic()) {
      return false;
    }

389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
    // Autogen target name
    this->AutogenTarget.Name = this->Target->GetName();
    this->AutogenTarget.Name += "_autogen";

    // Autogen target parallel processing
    this->AutogenTarget.Parallel =
      this->Target->GetSafeProperty("AUTOGEN_PARALLEL");
    if (this->AutogenTarget.Parallel.empty() ||
        (this->AutogenTarget.Parallel == "AUTO")) {
      // Autodetect number of CPUs
      this->AutogenTarget.Parallel = std::to_string(GetParallelCPUCount());
    }

    // Autogen target info and settings files
    {
      this->AutogenTarget.InfoFile = this->Dir.Info;
      this->AutogenTarget.InfoFile += "/AutogenInfo.cmake";

      this->AutogenTarget.SettingsFile = this->Dir.Info;
      this->AutogenTarget.SettingsFile += "/AutogenOldSettings.txt";

      if (this->MultiConfig) {
        for (std::string const& cfg : this->ConfigsList) {
          std::string& filename = this->AutogenTarget.ConfigSettingsFile[cfg];
          filename =
            AppendFilenameSuffix(this->AutogenTarget.SettingsFile, "_" + cfg);
          AddCleanFile(makefile, filename);
        }
      } else {
        AddCleanFile(makefile, this->AutogenTarget.SettingsFile);
      }
    }

422
423
    // Autogen target: Compute user defined dependencies
    {
424
425
426
      this->AutogenTarget.DependOrigin =
        this->Target->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS");

427
428
429
430
431
432
433
434
435
      std::string const deps =
        this->Target->GetSafeProperty("AUTOGEN_TARGET_DEPENDS");
      if (!deps.empty()) {
        std::vector<std::string> extraDeps;
        cmSystemTools::ExpandListArgument(deps, extraDeps);
        for (std::string const& depName : extraDeps) {
          // Allow target and file dependencies
          auto* depTarget = makefile->FindTargetToUse(depName);
          if (depTarget != nullptr) {
436
            this->AutogenTarget.DependTargets.insert(depTarget);
437
          } else {
438
            this->AutogenTarget.DependFiles.insert(depName);
439
440
441
442
443
          }
        }
      }
    }
  }
444

445
446
447
448
449
450
451
452
  // Init rcc specific settings
  if (this->Rcc.Enabled && !InitRcc()) {
    return false;
  }

  // Add autogen include directory to the origin target INCLUDE_DIRECTORIES
  if (this->Moc.Enabled || this->Uic.Enabled ||
      (this->Rcc.Enabled && this->MultiConfig)) {
453
    this->Target->AddIncludeDirectory(this->Dir.Include, true);
454
455
456
457
458
459
460
461
462
463
464
  }

  // Scan files
  if (!this->InitScanFiles()) {
    return false;
  }

  // Create autogen target
  if ((this->Moc.Enabled || this->Uic.Enabled) && !this->InitAutogenTarget()) {
    return false;
  }
465

466
467
468
469
470
471
472
473
474
475
476
477
478
479
  // Create rcc targets
  if (this->Rcc.Enabled && !this->InitRccTargets()) {
    return false;
  }

  return true;
}

bool cmQtAutoGenInitializer::InitMoc()
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();

  // Mocs compilation file
480
  this->Moc.MocsCompilation = this->Dir.Build;
481
482
483
484
  this->Moc.MocsCompilation += "/mocs_compilation.cpp";

  // Moc predefs command
  if (this->Target->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
485
      (this->QtVersion >= IntegerVersion(5, 8))) {
486
487
488
489
490
    this->Moc.PredefsCmd =
      makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND");
  }

  // Moc includes
491
  {
492
    bool const appendImplicit = (this->QtVersion.Major == 5);
493
    auto GetIncludeDirs =
494
495
      [this, localGen,
       appendImplicit](std::string const& cfg) -> std::vector<std::string> {
496
497
498
499
      // Get the include dirs for this target, without stripping the implicit
      // include dirs off, see
      // https://gitlab.kitware.com/cmake/cmake/issues/13667
      std::vector<std::string> dirs;
500
501
      localGen->GetIncludeDirectories(dirs, this->Target, "CXX", cfg, false,
                                      appendImplicit);
502
      return dirs;
503
504
505
506
507
    };

    // Default configuration include directories
    this->Moc.Includes = GetIncludeDirs(this->ConfigDefault);
    // Other configuration settings
508
    if (this->MultiConfig) {
509
      for (std::string const& cfg : this->ConfigsList) {
510
        std::vector<std::string> dirs = GetIncludeDirs(cfg);
511
512
513
        if (dirs != this->Moc.Includes) {
          this->Moc.ConfigIncludes[cfg] = std::move(dirs);
        }
514
      }
515
    }
516
517
  }

518
519
520
  // Moc compile definitions
  {
    auto GetCompileDefinitions =
521
      [this, localGen](std::string const& cfg) -> std::set<std::string> {
522
      std::set<std::string> defines;
523
      localGen->GetTargetDefines(this->Target, cfg, "CXX", defines);
524
      return defines;
525
526
527
528
529
530
531
    };

    // Default configuration defines
    this->Moc.Defines = GetCompileDefinitions(this->ConfigDefault);
    // Other configuration defines
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
532
        std::set<std::string> defines = GetCompileDefinitions(cfg);
533
534
535
536
537
        if (defines != this->Moc.Defines) {
          this->Moc.ConfigDefines[cfg] = std::move(defines);
        }
      }
    }
538
  }
539
540
541

  // Moc executable
  if (!GetMocExecutable()) {
542
543
    return false;
  }
544
545
546
547
548
549
550
551
552
553
554
555
556
557

  return true;
}

bool cmQtAutoGenInitializer::InitUic()
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();

  // Uic search paths
  {
    std::string const usp =
      this->Target->GetSafeProperty("AUTOUIC_SEARCH_PATHS");
    if (!usp.empty()) {
      cmSystemTools::ExpandListArgument(usp, this->Uic.SearchPaths);
558
      std::string const& srcDir = makefile->GetCurrentSourceDirectory();
559
560
561
562
      for (std::string& path : this->Uic.SearchPaths) {
        path = cmSystemTools::CollapseFullPath(path, srcDir);
      }
    }
563
  }
564
565
  // Uic target options
  {
566
567
    auto UicGetOpts =
      [this](std::string const& cfg) -> std::vector<std::string> {
568
569
      std::vector<std::string> opts;
      this->Target->GetAutoUicOptions(opts, cfg);
570
      return opts;
571
    };
572

573
574
575
576
577
578
    // Default settings
    this->Uic.Options = UicGetOpts(this->ConfigDefault);

    // Configuration specific settings
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
579
        std::vector<std::string> options = UicGetOpts(cfg);
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
        if (options != this->Uic.Options) {
          this->Uic.ConfigOptions[cfg] = std::move(options);
        }
      }
    }
  }
  // .ui files skip and options
  {
    std::string const uiExt = "ui";
    std::string pathError;
    for (cmSourceFile* sf : makefile->GetSourceFiles()) {
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
      // Since we're iterating over source files that might be not in the
      // target we need to check for path errors (not existing files).
      std::string const& fPath = sf->GetFullPath(&pathError);
      if (!pathError.empty()) {
        pathError.clear();
        continue;
      }
      if (sf->GetExtension() == uiExt) {
        std::string const absFile = cmSystemTools::GetRealPath(fPath);
        // Check if the .ui file should be skipped
        if (sf->GetPropertyAsBool("SKIP_AUTOUIC") ||
            sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
          this->Uic.Skip.insert(absFile);
        }
        // Check if the .ui file has uic options
        std::string const uicOpts = sf->GetSafeProperty("AUTOUIC_OPTIONS");
        if (!uicOpts.empty()) {
          // Check if file isn't skipped
          if (this->Uic.Skip.count(absFile) == 0) {
            this->Uic.FileFiles.push_back(absFile);
            std::vector<std::string> optsVec;
            cmSystemTools::ExpandListArgument(uicOpts, optsVec);
            this->Uic.FileOptions.push_back(std::move(optsVec));
          }
        }
      }
618
619
620
    }
  }

621
622
623
  // Uic executable
  if (!GetUicExecutable()) {
    return false;
624
625
  }

626
627
628
629
630
631
632
  return true;
}

bool cmQtAutoGenInitializer::InitRcc()
{
  if (!GetRccExecutable()) {
    return false;
633
  }
634
635
636
637
638
639
  return true;
}

bool cmQtAutoGenInitializer::InitScanFiles()
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
640

641
  // Scan through target files
642
  {
643
    std::string const qrcExt = "qrc";
644
    std::vector<cmSourceFile*> srcFiles;
645
    this->Target->GetConfigCommonSourceFiles(srcFiles);
646
    for (cmSourceFile* sf : srcFiles) {
647
648
649
650
      if (sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
        continue;
      }
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
651
652
      std::string const& fPath = sf->GetFullPath();
      std::string const& ext = sf->GetExtension();
653
      // Register generated files that will be scanned by moc or uic
654
      if (this->Moc.Enabled || this->Uic.Enabled) {
655
        cmSystemTools::FileFormat const fileType =
656
657
658
          cmSystemTools::GetFileFormat(ext.c_str());
        if ((fileType == cmSystemTools::CXX_FILE_FORMAT) ||
            (fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
659
          std::string const absPath = cmSystemTools::GetRealPath(fPath);
660
661
          if ((this->Moc.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) ||
              (this->Uic.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) {
662
663
664
665
            // Register source
            const bool generated = sf->GetPropertyAsBool("GENERATED");
            if (fileType == cmSystemTools::HEADER_FILE_FORMAT) {
              if (generated) {
666
                this->AutogenTarget.HeadersGenerated.push_back(absPath);
667
              } else {
668
                this->AutogenTarget.Headers.push_back(absPath);
669
670
671
              }
            } else {
              if (generated) {
672
                this->AutogenTarget.SourcesGenerated.push_back(absPath);
673
              } else {
674
                this->AutogenTarget.Sources.push_back(absPath);
675
              }
676
677
678
            }
          }
        }
679
680
      }
      // Register rcc enabled files
681
      if (this->Rcc.Enabled && (ext == qrcExt) &&
682
          !sf->GetPropertyAsBool("SKIP_AUTORCC")) {
683
684
        // Register qrc file
        {
685
686
687
688
689
          Qrc qrc;
          qrc.QrcFile = cmSystemTools::GetRealPath(fPath);
          qrc.QrcName =
            cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
          qrc.Generated = sf->GetPropertyAsBool("GENERATED");
690
691
          // RCC options
          {
692
            std::string const opts = sf->GetSafeProperty("AUTORCC_OPTIONS");
693
            if (!opts.empty()) {
694
              cmSystemTools::ExpandListArgument(opts, qrc.Options);
695
696
            }
          }
697
          this->Rcc.Qrcs.push_back(std::move(qrc));
698
        }
699
700
      }
    }
701
  }
702
703
704
705
706
707
  // cmGeneratorTarget::GetConfigCommonSourceFiles computes the target's
  // sources meta data cache. Clear it so that OBJECT library targets that
  // are AUTOGEN initialized after this target get their added
  // mocs_compilation.cpp source acknowledged by this target.
  this->Target->ClearSourcesCache();

708
  if (this->Moc.Enabled || this->Uic.Enabled) {
709
710
711
712
713
714
715
716
717
718
719
    // Read skip files from makefile sources
    {
      std::string pathError;
      for (cmSourceFile* sf : makefile->GetSourceFiles()) {
        // sf->GetExtension() is only valid after sf->GetFullPath() ...
        // Since we're iterating over source files that might be not in the
        // target we need to check for path errors (not existing files).
        std::string const& fPath = sf->GetFullPath(&pathError);
        if (!pathError.empty()) {
          pathError.clear();
          continue;
720
        }
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
        cmSystemTools::FileFormat const fileType =
          cmSystemTools::GetFileFormat(sf->GetExtension().c_str());
        if (!(fileType == cmSystemTools::CXX_FILE_FORMAT) &&
            !(fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
          continue;
        }
        const bool skipAll = sf->GetPropertyAsBool("SKIP_AUTOGEN");
        const bool mocSkip = this->Moc.Enabled &&
          (skipAll || sf->GetPropertyAsBool("SKIP_AUTOMOC"));
        const bool uicSkip = this->Uic.Enabled &&
          (skipAll || sf->GetPropertyAsBool("SKIP_AUTOUIC"));
        if (mocSkip || uicSkip) {
          std::string const absFile = cmSystemTools::GetRealPath(fPath);
          if (mocSkip) {
            this->Moc.Skip.insert(absFile);
          }
          if (uicSkip) {
            this->Uic.Skip.insert(absFile);
          }
740
741
742
        }
      }
    }
743

744
    // Process GENERATED sources and headers
745
746
    if (!this->AutogenTarget.SourcesGenerated.empty() ||
        !this->AutogenTarget.HeadersGenerated.empty()) {
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
      // Check status of policy CMP0071
      bool policyAccept = false;
      bool policyWarn = false;
      cmPolicies::PolicyStatus const CMP0071_status =
        makefile->GetPolicyStatus(cmPolicies::CMP0071);
      switch (CMP0071_status) {
        case cmPolicies::WARN:
          policyWarn = true;
          CM_FALLTHROUGH;
        case cmPolicies::OLD:
          // Ignore GENERATED file
          break;
        case cmPolicies::REQUIRED_IF_USED:
        case cmPolicies::REQUIRED_ALWAYS:
        case cmPolicies::NEW:
          // Process GENERATED file
          policyAccept = true;
          break;
765
      }
766
767
768

      if (policyAccept) {
        // Accept GENERATED sources
769
770
771
772
        for (std::string const& absFile :
             this->AutogenTarget.HeadersGenerated) {
          this->AutogenTarget.Headers.push_back(absFile);
          this->AutogenTarget.DependFiles.insert(absFile);
773
        }
774
775
776
777
        for (std::string const& absFile :
             this->AutogenTarget.SourcesGenerated) {
          this->AutogenTarget.Sources.push_back(absFile);
          this->AutogenTarget.DependFiles.insert(absFile);
778
        }
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
      } else {
        if (policyWarn) {
          std::string msg;
          msg += cmPolicies::GetPolicyWarning(cmPolicies::CMP0071);
          msg += "\n";
          std::string tools;
          std::string property;
          if (this->Moc.Enabled && this->Uic.Enabled) {
            tools = "AUTOMOC and AUTOUIC";
            property = "SKIP_AUTOGEN";
          } else if (this->Moc.Enabled) {
            tools = "AUTOMOC";
            property = "SKIP_AUTOMOC";
          } else if (this->Uic.Enabled) {
            tools = "AUTOUIC";
            property = "SKIP_AUTOUIC";
          }
          msg += "For compatibility, CMake is excluding the GENERATED source "
                 "file(s):\n";
798
799
          for (const std::string& absFile :
               this->AutogenTarget.HeadersGenerated) {
800
801
            msg.append("  ").append(Quoted(absFile)).append("\n");
          }
802
803
          for (const std::string& absFile :
               this->AutogenTarget.SourcesGenerated) {
804
805
806
807
808
809
810
811
812
813
814
815
816
            msg.append("  ").append(Quoted(absFile)).append("\n");
          }
          msg += "from processing by ";
          msg += tools;
          msg +=
            ". If any of the files should be processed, set CMP0071 to NEW. "
            "If any of the files should not be processed, "
            "explicitly exclude them by setting the source file property ";
          msg += property;
          msg += ":\n  set_property(SOURCE file.h PROPERTY ";
          msg += property;
          msg += " ON)\n";
          makefile->IssueMessage(cmake::AUTHOR_WARNING, msg);
817
        }
818
      }
819
    }
820
821
    // Sort headers and sources
    if (this->Moc.Enabled || this->Uic.Enabled) {
822
823
824
825
      std::sort(this->AutogenTarget.Headers.begin(),
                this->AutogenTarget.Headers.end());
      std::sort(this->AutogenTarget.Sources.begin(),
                this->AutogenTarget.Sources.end());
826
    }
827
  }
828

829
  // Process qrc files
830
  if (!this->Rcc.Qrcs.empty()) {
831
    const bool QtV5 = (this->QtVersion.Major == 5);
832
833
834
    // Target rcc options
    std::vector<std::string> optionsTarget;
    cmSystemTools::ExpandListArgument(
835
      this->Target->GetSafeProperty("AUTORCC_OPTIONS"), optionsTarget);
836

837
    // Check if file name is unique
838
    for (Qrc& qrc : this->Rcc.Qrcs) {
839
      qrc.Unique = true;
840
      for (Qrc const& qrc2 : this->Rcc.Qrcs) {
841
842
        if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
          qrc.Unique = false;
843
844
          break;
        }
845
      }
846
    }
847
    // Path checksum and file names
848
    {
849
      cmFilePathChecksum const fpathCheckSum(makefile);
850
      for (Qrc& qrc : this->Rcc.Qrcs) {
851
        qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile);
852
        // RCC output file name
853
        {
854
          std::string rccFile = this->Dir.Build + "/";
855
          rccFile += qrc.PathChecksum;
856
          rccFile += "/qrc_";
857
          rccFile += qrc.QrcName;
858
          rccFile += ".cpp";
859
          qrc.RccFile = std::move(rccFile);
860
861
        }
        {
862
          std::string base = this->Dir.Info;
863
          base += "/RCC";
864
865
866
          base += qrc.QrcName;
          if (!qrc.Unique) {
            base += qrc.PathChecksum;
867
          }
868
869
870
871

          qrc.LockFile = base;
          qrc.LockFile += ".lock";

872
873
          qrc.InfoFile = base;
          qrc.InfoFile += "Info.cmake";
874

875
          qrc.SettingsFile = base;
876
          qrc.SettingsFile += "Settings.txt";
877
878
879
880
881
882
883

          if (this->MultiConfig) {
            for (std::string const& cfg : this->ConfigsList) {
              qrc.ConfigSettingsFile[cfg] =
                AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg);
            }
          }
884
        }
885
886
887
      }
    }
    // RCC options
888
    for (Qrc& qrc : this->Rcc.Qrcs) {
889
890
891
      // Target options
      std::vector<std::string> opts = optionsTarget;
      // Merge computed "-name XYZ" option
892
      {
893
        std::string name = qrc.QrcName;
894
895
        // Replace '-' with '_'. The former is not valid for symbol names.
        std::replace(name.begin(), name.end(), '-', '_');
896
        if (!qrc.Unique) {
897
          name += "_";
898
          name += qrc.PathChecksum;
899
        }
900
901
902
        std::vector<std::string> nameOpts;
        nameOpts.emplace_back("-name");
        nameOpts.emplace_back(std::move(name));
903
        RccMergeOptions(opts, nameOpts, QtV5);
904
      }
905
      // Merge file option
906
      RccMergeOptions(opts, qrc.Options, QtV5);
907
      qrc.Options = std::move(opts);
908
    }
909
    // RCC resources
910
    for (Qrc& qrc : this->Rcc.Qrcs) {
911
912
913
914
915
      if (!qrc.Generated) {
        std::string error;
        if (!RccListInputs(qrc.QrcFile, qrc.Resources, error)) {
          cmSystemTools::Error(error.c_str());
          return false;
916
        }
917
      }
918
919
    }
  }
920

921
922
  return true;
}
923

924
925
926
927
928
929
930
bool cmQtAutoGenInitializer::InitAutogenTarget()
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
  cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();

  // Register info file as generated by CMake
931
  makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
932
933
934
935
936
937

  // Files provided by the autogen target
  std::vector<std::string> autogenProvides;
  if (this->Moc.Enabled) {
    this->AddGeneratedSource(this->Moc.MocsCompilation, GeneratorT::MOC);
    autogenProvides.push_back(this->Moc.MocsCompilation);
938
939
  }

940
941
942
943
944
945
  // Compose target comment
  std::string autogenComment;
  {
    std::string tools;
    if (this->Moc.Enabled) {
      tools += "MOC";
946
    }
947
948
949
    if (this->Uic.Enabled) {
      if (!tools.empty()) {
        tools += " and ";
950
      }
951
      tools += "UIC";
952
    }
953
954
955
956
957
    autogenComment = "Automatic ";
    autogenComment += tools;
    autogenComment += " for target ";
    autogenComment += this->Target->GetName();
  }
958

959
960
961
962
963
964
965
  // Compose command lines
  cmCustomCommandLines commandLines;
  {
    cmCustomCommandLine currentLine;
    currentLine.push_back(cmSystemTools::GetCMakeCommand());
    currentLine.push_back("-E");
    currentLine.push_back("cmake_autogen");
966
    currentLine.push_back(this->AutogenTarget.InfoFile);
967
968
969
    currentLine.push_back("$<CONFIGURATION>");
    commandLines.push_back(std::move(currentLine));
  }
970

971
972
973
974
975
976
977
978
979
980
981
982
  // Use PRE_BUILD on demand
  bool usePRE_BUILD = false;
  if (globalGen->GetName().find("Visual Studio") != std::string::npos) {
    // Under VS use a PRE_BUILD event instead of a separate target to
    // reduce the number of targets loaded into the IDE.
    // This also works around a VS 11 bug that may skip updating the target:
    //  https://connect.microsoft.com/VisualStudio/feedback/details/769495
    usePRE_BUILD = true;
  }
  // Disable PRE_BUILD in some cases
  if (usePRE_BUILD) {
    // Cannot use PRE_BUILD with file depends
983
    if (!this->AutogenTarget.DependFiles.empty()) {
984
      usePRE_BUILD = false;
985
    }
986
987
988
989
    // Cannot use PRE_BUILD when a global autogen target is in place
    if (AutogenTarget.GlobalTarget) {
      usePRE_BUILD = false;
    }
990
991
992
993
  }
  // Create the autogen target/command
  if (usePRE_BUILD) {
    // Add additional autogen target dependencies to origin target
994
    for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
995
      this->Target->Target->AddUtility(depTarget->GetName(), makefile);
996
    }
997

998
999
1000
1001
1002
1003
1004
1005
1006
    // Add the pre-build command directly to bypass the OBJECT_LIBRARY
    // rejection in cmMakefile::AddCustomCommandToTarget because we know
    // PRE_BUILD will work for an OBJECT_LIBRARY in this specific case.
    //
    // PRE_BUILD does not support file dependencies!
    const std::vector<std::string> no_output;
    const std::vector<std::string> no_deps;
    cmCustomCommand cc(makefile, no_output, autogenProvides, no_deps,
                       commandLines, autogenComment.c_str(),
1007
                       this->Dir.Work.c_str());
1008
1009
1010
1011
    cc.SetEscapeOldStyle(false);
    cc.SetEscapeAllowMakeVars(true);
    this->Target->Target->AddPreBuildCommand(cc);
  } else {
1012

1013
1014
    // Add link library target dependencies to the autogen target
    // dependencies
1015
    if (this->AutogenTarget.DependOrigin) {
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
      // add_dependencies/addUtility do not support generator expressions.
      // We depend only on the libraries found in all configs therefore.
      std::map<cmGeneratorTarget const*, std::size_t> commonTargets;
      for (std::string const& config : this->ConfigsList) {
        cmLinkImplementationLibraries const* libs =
          this->Target->GetLinkImplementationLibraries(config);
        if (libs != nullptr) {
          for (cmLinkItem const& item : libs->Libraries) {
            cmGeneratorTarget const* libTarget = item.Target;
            if ((libTarget != nullptr) &&
                !StaticLibraryCycle(this->Target, libTarget, config)) {
              // Increment target config count
              commonTargets[libTarget]++;
1029
1030
1031
            }
          }
        }
1032
      }
1033
1034
      for (auto const& item : commonTargets) {
        if (item.second == this->ConfigsList.size()) {
1035
          this->AutogenTarget.DependTargets.insert(item.first->Target);
1036
        }
1037
      }
1038
    }
1039

1040
1041
    // Create autogen target
    cmTarget* autogenTarget = makefile->AddUtilityCommand(
1042
1043
1044
1045
      this->AutogenTarget.Name, cmMakefile::TargetOrigin::Generator, true,
      this->Dir.Work.c_str(), /*byproducts=*/autogenProvides,
      std::vector<std::string>(this->AutogenTarget.DependFiles.begin(),
                               this->AutogenTarget.DependFiles.end()),
1046
1047
1048
1049
1050
1051
      commandLines, false, autogenComment.c_str());
    // Create autogen generator target
    localGen->AddGeneratorTarget(
      new cmGeneratorTarget(autogenTarget, localGen));

    // Forward origin utilities to autogen target
1052
1053
1054
1055
    if (this->AutogenTarget.DependOrigin) {
      for (BT<std::string> const& depName : this->Target->GetUtilities()) {
        autogenTarget->AddUtility(depName.Value, makefile);
      }
1056
1057
    }
    // Add additional autogen target dependencies to autogen target
1058
    for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
1059
      autogenTarget->AddUtility(depTarget->GetName(), makefile);
1060
1061
    }

1062
    // Set FOLDER property in autogen target
1063
1064
    if (!this->TargetsFolder.empty()) {
      autogenTarget->SetProperty("FOLDER", this->TargetsFolder.c_str());
1065
1066
    }

1067
    // Add autogen target to the origin target dependencies
1068
    this->Target->Target->AddUtility(this->AutogenTarget.Name, makefile);
1069
1070
1071
1072
1073
1074

    // Add autogen target to the global autogen target dependencies
    if (this->AutogenTarget.GlobalTarget) {
      this->GlobalInitializer->AddToGlobalAutoGen(localGen,
                                                  this->AutogenTarget.Name);
    }
1075
1076
1077
1078
1079
  }

  return true;
}

1080
bool cmQtAutoGenInitializer::InitRccTargets()
1081
1082
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
1083
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
1084

1085
1086
1087
1088
1089
  for (Qrc const& qrc : this->Rcc.Qrcs) {
    // Register info file as generated by CMake
    makefile->AddCMakeOutputFile(qrc.InfoFile);
    // Register file at target
    this->AddGeneratedSource(qrc.RccFile, GeneratorT::RCC);
1090

1091
1092
    std::vector<std</