cmQtAutoGenInitializer.cxx 54.3 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
    // Info directory
    this->Dir.Info = cbd;
337
    this->Dir.Info += cmake::GetCMakeFilesDirectory();
338
339
340
341
342
343
344
345
346
347
348
349
350
    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
525
526
527
528
529
#ifdef _WIN32
      if (this->Moc.PredefsCmd.empty()) {
        // Add WIN32 definition if we don't have a moc_predefs.h
        defines.insert("WIN32");
      }
#endif
530
      return defines;
531
532
533
534
535
536
537
    };

    // Default configuration defines
    this->Moc.Defines = GetCompileDefinitions(this->ConfigDefault);
    // Other configuration defines
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
538
        std::set<std::string> defines = GetCompileDefinitions(cfg);
539
540
541
542
543
        if (defines != this->Moc.Defines) {
          this->Moc.ConfigDefines[cfg] = std::move(defines);
        }
      }
    }
544
  }
545
546
547

  // Moc executable
  if (!GetMocExecutable()) {
548
549
    return false;
  }
550
551
552
553
554
555
556
557
558
559
560
561
562
563

  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);
564
      std::string const& srcDir = makefile->GetCurrentSourceDirectory();
565
566
567
568
      for (std::string& path : this->Uic.SearchPaths) {
        path = cmSystemTools::CollapseFullPath(path, srcDir);
      }
    }
569
  }
570
571
  // Uic target options
  {
572
573
    auto UicGetOpts =
      [this](std::string const& cfg) -> std::vector<std::string> {
574
575
      std::vector<std::string> opts;
      this->Target->GetAutoUicOptions(opts, cfg);
576
      return opts;
577
    };
578

579
580
581
582
583
584
    // Default settings
    this->Uic.Options = UicGetOpts(this->ConfigDefault);

    // Configuration specific settings
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
585
        std::vector<std::string> options = UicGetOpts(cfg);
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
618
619
620
621
622
623
        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));
          }
        }
      }
624
625
626
    }
  }

627
628
629
  // Uic executable
  if (!GetUicExecutable()) {
    return false;
630
631
  }

632
633
634
635
636
637
638
  return true;
}

bool cmQtAutoGenInitializer::InitRcc()
{
  if (!GetRccExecutable()) {
    return false;
639
  }
640
641
642
643
644
645
  return true;
}

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

647
  // Scan through target files
648
  {
649
    std::string const qrcExt = "qrc";
650
    std::vector<cmSourceFile*> srcFiles;
651
    this->Target->GetConfigCommonSourceFiles(srcFiles);
652
    for (cmSourceFile* sf : srcFiles) {
653
654
655
656
      if (sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
        continue;
      }
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
657
658
      std::string const& fPath = sf->GetFullPath();
      std::string const& ext = sf->GetExtension();
659
      // Register generated files that will be scanned by moc or uic
660
      if (this->Moc.Enabled || this->Uic.Enabled) {
661
        cmSystemTools::FileFormat const fileType =
662
663
664
          cmSystemTools::GetFileFormat(ext.c_str());
        if ((fileType == cmSystemTools::CXX_FILE_FORMAT) ||
            (fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
665
          std::string const absPath = cmSystemTools::GetRealPath(fPath);
666
667
          if ((this->Moc.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) ||
              (this->Uic.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) {
668
669
670
671
            // Register source
            const bool generated = sf->GetPropertyAsBool("GENERATED");
            if (fileType == cmSystemTools::HEADER_FILE_FORMAT) {
              if (generated) {
672
                this->AutogenTarget.HeadersGenerated.push_back(absPath);
673
              } else {
674
                this->AutogenTarget.Headers.push_back(absPath);
675
676
677
              }
            } else {
              if (generated) {
678
                this->AutogenTarget.SourcesGenerated.push_back(absPath);
679
              } else {
680
                this->AutogenTarget.Sources.push_back(absPath);
681
              }
682
683
684
            }
          }
        }
685
686
      }
      // Register rcc enabled files
687
      if (this->Rcc.Enabled && (ext == qrcExt) &&
688
          !sf->GetPropertyAsBool("SKIP_AUTORCC")) {
689
690
        // Register qrc file
        {
691
692
693
694
695
          Qrc qrc;
          qrc.QrcFile = cmSystemTools::GetRealPath(fPath);
          qrc.QrcName =
            cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
          qrc.Generated = sf->GetPropertyAsBool("GENERATED");
696
697
          // RCC options
          {
698
            std::string const opts = sf->GetSafeProperty("AUTORCC_OPTIONS");
699
            if (!opts.empty()) {
700
              cmSystemTools::ExpandListArgument(opts, qrc.Options);
701
702
            }
          }
703
          this->Rcc.Qrcs.push_back(std::move(qrc));
704
        }
705
706
      }
    }
707
  }
708
709
710
711
712
713
  // 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();

714
  if (this->Moc.Enabled || this->Uic.Enabled) {
715
716
717
718
719
720
721
722
723
724
725
    // 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;
726
        }
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
        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);
          }
746
747
748
        }
      }
    }
749

750
    // Process GENERATED sources and headers
751
752
    if (!this->AutogenTarget.SourcesGenerated.empty() ||
        !this->AutogenTarget.HeadersGenerated.empty()) {
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
      // 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;
771
      }
772
773
774

      if (policyAccept) {
        // Accept GENERATED sources
775
776
777
778
        for (std::string const& absFile :
             this->AutogenTarget.HeadersGenerated) {
          this->AutogenTarget.Headers.push_back(absFile);
          this->AutogenTarget.DependFiles.insert(absFile);
779
        }
780
781
782
783
        for (std::string const& absFile :
             this->AutogenTarget.SourcesGenerated) {
          this->AutogenTarget.Sources.push_back(absFile);
          this->AutogenTarget.DependFiles.insert(absFile);
784
        }
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
      } 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";
804
805
          for (const std::string& absFile :
               this->AutogenTarget.HeadersGenerated) {
806
807
            msg.append("  ").append(Quoted(absFile)).append("\n");
          }
808
809
          for (const std::string& absFile :
               this->AutogenTarget.SourcesGenerated) {
810
811
812
813
814
815
816
817
818
819
820
821
822
            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);
823
        }
824
      }
825
    }
826
827
    // Sort headers and sources
    if (this->Moc.Enabled || this->Uic.Enabled) {
828
829
830
831
      std::sort(this->AutogenTarget.Headers.begin(),
                this->AutogenTarget.Headers.end());
      std::sort(this->AutogenTarget.Sources.begin(),
                this->AutogenTarget.Sources.end());
832
    }
833
  }
834

835
  // Process qrc files
836
  if (!this->Rcc.Qrcs.empty()) {
837
    const bool QtV5 = (this->QtVersion.Major == 5);
838
839
840
    // Target rcc options
    std::vector<std::string> optionsTarget;
    cmSystemTools::ExpandListArgument(
841
      this->Target->GetSafeProperty("AUTORCC_OPTIONS"), optionsTarget);
842

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

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

878
879
          qrc.InfoFile = base;
          qrc.InfoFile += "Info.cmake";
880

881
          qrc.SettingsFile = base;
882
          qrc.SettingsFile += "Settings.txt";
883
884
885
886
887
888
889

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

927
928
  return true;
}
929

930
931
932
933
934
935
936
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
937
  makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
938
939
940
941
942
943

  // 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);
944
945
  }

946
947
948
949
950
951
  // Compose target comment
  std::string autogenComment;
  {
    std::string tools;
    if (this->Moc.Enabled) {
      tools += "MOC";
952
    }
953
954
955
    if (this->Uic.Enabled) {
      if (!tools.empty()) {
        tools += " and ";
956
      }
957
      tools += "UIC";
958
    }
959
960
961
962
963
    autogenComment = "Automatic ";
    autogenComment += tools;
    autogenComment += " for target ";
    autogenComment += this->Target->GetName();
  }
964

965
966
967
968
969
970
971
  // Compose command lines
  cmCustomCommandLines commandLines;
  {
    cmCustomCommandLine currentLine;
    currentLine.push_back(cmSystemTools::GetCMakeCommand());
    currentLine.push_back("-E");
    currentLine.push_back("cmake_autogen");
972
    currentLine.push_back(this->AutogenTarget.InfoFile);
973
974
975
    currentLine.push_back("$<CONFIGURATION>");
    commandLines.push_back(std::move(currentLine));
  }
976

977
978
979
980
981
982
983
984
985
986
987
988
  // 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
989
    if (!this->AutogenTarget.DependFiles.empty()) {
990
      usePRE_BUILD = false;
991
    }
992
993
994
995
    // Cannot use PRE_BUILD when a global autogen target is in place
    if (AutogenTarget.GlobalTarget) {
      usePRE_BUILD = false;
    }
996
997
998
999
  }
  // Create the autogen target/command
  if (usePRE_BUILD) {
    // Add additional autogen target dependencies to origin target
1000
    for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
1001
      this->Target->Target->AddUtility(depTarget->GetName(), makefile);
1002
    }
1003

1004
1005
1006
1007
1008
1009
1010
1011
1012
    // 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(),
1013
                       this->Dir.Work.c_str());
1014
1015
1016
1017
    cc.SetEscapeOldStyle(false);
    cc.SetEscapeAllowMakeVars(true);
    this->Target->Target->AddPreBuildCommand(cc);
  } else {
1018

1019
1020
    // Add link library target dependencies to the autogen target
    // dependencies
1021
    if (this->AutogenTarget.DependOrigin) {
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
      // 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]++;
1035
1036
1037
            }
          }
        }
1038
      }
1039
1040
      for (auto const& item : commonTargets) {
        if (item.second == this->ConfigsList.size()) {
1041
          this->AutogenTarget.DependTargets.insert(item.first->Target);
1042
        }
1043
      }
1044
    }
1045

1046
1047
    // Create autogen target
    cmTarget* autogenTarget = makefile->AddUtilityCommand(
1048
1049
1050
1051
      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()),
1052
1053
1054
1055
1056
1057
      commandLines, false, autogenComment.c_str());
    // Create autogen generator target
    localGen->AddGeneratorTarget(
      new cmGeneratorTarget(autogenTarget, localGen));

    // Forward origin utilities to autogen target
1058
1059
1060
1061
    if (this->AutogenTarget.DependOrigin) {
      for (BT<std::string> const& depName : this->Target->GetUtilities()) {
        autogenTarget->AddUtility(depName.Value, makefile);
      }
1062
1063
    }
    // Add additional autogen target dependencies to autogen target
1064
    for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
1065
      autogenTarget->AddUtility(depTarget->GetName(), makefile);
1066
1067
    }

1068
    // Set FOLDER property in autogen target
1069
1070
    if (!this->TargetsFolder.empty()) {
      autogenTarget->SetProperty("FOLDER", this->TargetsFolder.c_str());
1071
1072
    }

1073
    // Add autogen target to the origin target dependencies
1074
    this->Target->Target->AddUtility(this->AutogenTarget.Name, makefile);
1075
1076
1077
1078
1079
1080

    // Add autogen target to the global autogen target dependencies
    if (this->AutogenTarget.GlobalTarget) {
      this->GlobalInitializer->AddToGlobalAutoGen(localGen,
                                                  this->AutogenTarget.Name);
    }
1081
1082
1083
1084
1085
  }

  return true;
}

1086
bool cmQtAutoGenInitializer::InitRccTargets()
1087
1088
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
1089
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
1090