cmQtAutoGenInitializer.cxx 53.5 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 "cmGeneratorExpression.h"
13
#include "cmGeneratorTarget.h"
14
#include "cmGlobalGenerator.h"
15
#include "cmLinkItem.h"
16
#include "cmListFileCache.h"
17
18
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
19
#include "cmMessageType.h"
20
#include "cmOutputConverter.h"
21
#include "cmPolicies.h"
22
#include "cmProcessOutput.h"
23
#include "cmSourceFile.h"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
24
#include "cmSourceGroup.h"
25
#include "cmState.h"
26
#include "cmStateTypes.h"
27
28
#include "cmSystemTools.h"
#include "cmTarget.h"
29
#include "cmsys/FStream.hxx"
30
#include "cmsys/SystemInformation.hxx"
31

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

43
44
45
std::string GetQtExecutableTargetName(
  const cmQtAutoGen::IntegerVersion& qtVersion, std::string const& executable)
{
Tobias Hunger's avatar
Tobias Hunger committed
46
47
48
  if (qtVersion.Major == 6) {
    return ("Qt6::" + executable);
  }
49
50
51
52
53
54
55
56
57
  if (qtVersion.Major == 5) {
    return ("Qt5::" + executable);
  }
  if (qtVersion.Major == 4) {
    return ("Qt4::" + executable);
  }
  return ("");
}

58
59
60
61
62
63
64
65
66
67
68
69
70
71
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;
}

72
static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName,
73
                             cmQtAutoGen::GeneratorT genType)
74
{
Daniel Pfeifer's avatar
Daniel Pfeifer committed
75
  cmSourceGroup* sourceGroup = nullptr;
76
77
  // Acquire source group
  {
78
79
80
81
82
83
    std::string property;
    std::string groupName;
    {
      std::array<std::string, 2> props;
      // Use generator specific group name
      switch (genType) {
84
        case cmQtAutoGen::GeneratorT::MOC:
85
86
          props[0] = "AUTOMOC_SOURCE_GROUP";
          break;
87
        case cmQtAutoGen::GeneratorT::RCC:
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
          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;
        }
      }
103
104
    }
    // Generate a source group on demand
105
    if (!groupName.empty()) {
106
      sourceGroup = makefile->GetOrCreateSourceGroup(groupName);
Daniel Pfeifer's avatar
Daniel Pfeifer committed
107
      if (sourceGroup == nullptr) {
108
109
110
111
112
        std::ostringstream ost;
        ost << cmQtAutoGen::GeneratorNameUpper(genType);
        ost << ": " << property;
        ost << ": Could not find or create the source group ";
        ost << cmQtAutoGen::Quoted(groupName);
113
        cmSystemTools::Error(ost.str());
114
115
116
117
        return false;
      }
    }
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
118
  if (sourceGroup != nullptr) {
119
120
121
122
123
    sourceGroup->AddGroupFile(fileName);
  }
  return true;
}

124
static void AddCleanFile(cmMakefile* makefile, std::string const& fileName)
125
126
127
128
129
{
  makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", fileName.c_str(),
                           false);
}

130
131
132
133
134
135
static std::string FileProjectRelativePath(cmMakefile* makefile,
                                           std::string const& fileName)
{
  std::string res;
  {
    std::string pSource = cmSystemTools::RelativePath(
136
      makefile->GetCurrentSourceDirectory(), fileName);
137
    std::string pBinary = cmSystemTools::RelativePath(
138
      makefile->GetCurrentBinaryDirectory(), fileName);
139
140
141
142
143
144
145
146
147
148
149
    if (pSource.size() < pBinary.size()) {
      res = std::move(pSource);
    } else if (pBinary.size() < fileName.size()) {
      res = std::move(pBinary);
    } else {
      res = fileName;
    }
  }
  return res;
}

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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/* @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;
}

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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
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));
};

291
292
293
294
295
296
cmQtAutoGenInitializer::cmQtAutoGenInitializer(
  cmQtAutoGenGlobalInitializer* globalInitializer, cmGeneratorTarget* target,
  IntegerVersion const& qtVersion, bool mocEnabled, bool uicEnabled,
  bool rccEnabled, bool globalAutogenTarget, bool globalAutoRccTarget)
  : GlobalInitializer(globalInitializer)
  , Target(target)
297
  , QtVersion(qtVersion)
298
{
299
  AutogenTarget.GlobalTarget = globalAutogenTarget;
300
301
302
  Moc.Enabled = mocEnabled;
  Uic.Enabled = uicEnabled;
  Rcc.Enabled = rccEnabled;
303
  Rcc.GlobalTarget = globalAutoRccTarget;
304
}
305

306
bool cmQtAutoGenInitializer::InitCustomTargets()
307
308
309
310
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
  cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();
311

312
313
314
315
316
317
318
  // Configurations
  this->MultiConfig = globalGen->IsMultiConfig();
  this->ConfigDefault = makefile->GetConfigurations(this->ConfigsList);
  if (this->ConfigsList.empty()) {
    this->ConfigsList.push_back(this->ConfigDefault);
  }

319
  // Verbosity
320
321
322
323
324
  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
325
      this->Verbosity = cmSystemTools::IsOn(this->Verbosity) ? "1" : "0";
326
327
328
    }
  }

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
  // 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;
    }
344
345
  }

346
  // Common directories
347
348
349
  {
    // Collapsed current binary directory
    std::string const cbd = cmSystemTools::CollapseFullPath(
350
      std::string(), makefile->GetCurrentBinaryDirectory());
351

352
353
    // Info directory
    this->Dir.Info = cbd;
354
    this->Dir.Info += "/CMakeFiles";
355
356
357
358
359
360
361
362
363
364
365
366
367
    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";
368
    }
369
370
371
    cmSystemTools::ConvertToUnixSlashes(this->Dir.Build);
    // Cleanup build directory
    AddCleanFile(makefile, this->Dir.Build);
372
373

    // Working directory
374
375
    this->Dir.Work = cbd;
    cmSystemTools::ConvertToUnixSlashes(this->Dir.Work);
376
377

    // Include directory
378
379
    this->Dir.Include = this->Dir.Build;
    this->Dir.Include += "/include";
380
    if (this->MultiConfig) {
381
      this->Dir.Include += "_$<CONFIG>";
382
    }
383
    // Per config include directories
384
385
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
386
387
        std::string& dir = this->Dir.ConfigInclude[cfg];
        dir = this->Dir.Build;
388
389
390
391
        dir += "/include_";
        dir += cfg;
      }
    }
392
  }
393

394
  // Moc, Uic and _autogen target settings
395
396
397
398
399
  if (this->Moc.Enabled || this->Uic.Enabled) {
    // Init moc specific settings
    if (this->Moc.Enabled && !InitMoc()) {
      return false;
    }
400

401
    // Init uic specific settings
402
403
404
405
406
407
408
409
410
411
    if (this->Uic.Enabled) {
      if (InitUic()) {
        auto* uicTarget = makefile->FindTargetToUse(
          GetQtExecutableTargetName(this->QtVersion, "uic"));
        if (uicTarget != nullptr) {
          this->AutogenTarget.DependTargets.insert(uicTarget);
        }
      } else {
        return false;
      }
412
413
    }

414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
    // 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);
      }
    }

447
448
    // Autogen target: Compute user defined dependencies
    {
449
450
451
      this->AutogenTarget.DependOrigin =
        this->Target->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS");

452
453
454
455
456
457
      auto* mocTarget = makefile->FindTargetToUse(
        GetQtExecutableTargetName(this->QtVersion, "moc"));
      if (mocTarget != nullptr) {
        this->AutogenTarget.DependTargets.insert(mocTarget);
      }

458
459
460
461
462
463
464
465
466
      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) {
467
            this->AutogenTarget.DependTargets.insert(depTarget);
468
          } else {
469
            this->AutogenTarget.DependFiles.insert(depName);
470
471
472
473
474
          }
        }
      }
    }
  }
475

476
477
478
479
480
481
482
483
  // 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)) {
484
    this->Target->AddIncludeDirectory(this->Dir.Include, true);
485
486
487
488
489
490
491
492
493
494
495
  }

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

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

497
498
499
500
501
502
503
504
505
506
507
508
509
510
  // 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
511
  this->Moc.MocsCompilation = this->Dir.Build;
512
513
514
515
  this->Moc.MocsCompilation += "/mocs_compilation.cpp";

  // Moc predefs command
  if (this->Target->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
516
      (this->QtVersion >= IntegerVersion(5, 8))) {
517
518
519
520
521
    this->Moc.PredefsCmd =
      makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND");
  }

  // Moc includes
522
  {
523
    bool const appendImplicit = (this->QtVersion.Major >= 5);
524
    auto GetIncludeDirs =
525
526
      [this, localGen,
       appendImplicit](std::string const& cfg) -> std::vector<std::string> {
527
528
529
530
      // 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;
531
532
      localGen->GetIncludeDirectoriesImplicit(dirs, this->Target, "CXX", cfg,
                                              false, appendImplicit);
533
      return dirs;
534
535
536
537
538
    };

    // Default configuration include directories
    this->Moc.Includes = GetIncludeDirs(this->ConfigDefault);
    // Other configuration settings
539
    if (this->MultiConfig) {
540
      for (std::string const& cfg : this->ConfigsList) {
541
        std::vector<std::string> dirs = GetIncludeDirs(cfg);
542
543
544
        if (dirs != this->Moc.Includes) {
          this->Moc.ConfigIncludes[cfg] = std::move(dirs);
        }
545
      }
546
    }
547
548
  }

549
550
551
  // Moc compile definitions
  {
    auto GetCompileDefinitions =
552
      [this, localGen](std::string const& cfg) -> std::set<std::string> {
553
      std::set<std::string> defines;
554
      localGen->GetTargetDefines(this->Target, cfg, "CXX", defines);
555
556
557
558
559
560
#ifdef _WIN32
      if (this->Moc.PredefsCmd.empty()) {
        // Add WIN32 definition if we don't have a moc_predefs.h
        defines.insert("WIN32");
      }
#endif
561
      return defines;
562
563
564
565
566
567
568
    };

    // Default configuration defines
    this->Moc.Defines = GetCompileDefinitions(this->ConfigDefault);
    // Other configuration defines
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
569
        std::set<std::string> defines = GetCompileDefinitions(cfg);
570
571
572
573
574
        if (defines != this->Moc.Defines) {
          this->Moc.ConfigDefines[cfg] = std::move(defines);
        }
      }
    }
575
  }
576
577

  // Moc executable
578
  return GetMocExecutable();
579
580
581
582
583
584
585
586
587
588
589
590
}

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);
591
      std::string const& srcDir = makefile->GetCurrentSourceDirectory();
592
593
594
595
      for (std::string& path : this->Uic.SearchPaths) {
        path = cmSystemTools::CollapseFullPath(path, srcDir);
      }
    }
596
  }
597
598
  // Uic target options
  {
599
600
    auto UicGetOpts =
      [this](std::string const& cfg) -> std::vector<std::string> {
601
602
      std::vector<std::string> opts;
      this->Target->GetAutoUicOptions(opts, cfg);
603
      return opts;
604
    };
605

606
607
608
609
610
611
    // Default settings
    this->Uic.Options = UicGetOpts(this->ConfigDefault);

    // Configuration specific settings
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
612
        std::vector<std::string> options = UicGetOpts(cfg);
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
        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));
          }
        }
      }
651
652
653
    }
  }

654
  // Uic executable
655
  return GetUicExecutable();
656
657
658
659
}

bool cmQtAutoGenInitializer::InitRcc()
{
660
  return GetRccExecutable();
661
662
663
664
665
}

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

667
  // Scan through target files
668
  {
669
    std::string const qrcExt = "qrc";
670
    std::vector<cmSourceFile*> srcFiles;
671
    this->Target->GetConfigCommonSourceFiles(srcFiles);
672
    for (cmSourceFile* sf : srcFiles) {
673
674
675
676
      if (sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
        continue;
      }
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
677
678
      std::string const& fPath = sf->GetFullPath();
      std::string const& ext = sf->GetExtension();
679
      // Register generated files that will be scanned by moc or uic
680
      if (this->Moc.Enabled || this->Uic.Enabled) {
681
        cmSystemTools::FileFormat const fileType =
682
683
684
          cmSystemTools::GetFileFormat(ext.c_str());
        if ((fileType == cmSystemTools::CXX_FILE_FORMAT) ||
            (fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
685
          std::string const absPath = cmSystemTools::GetRealPath(fPath);
686
687
          if ((this->Moc.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) ||
              (this->Uic.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) {
688
            // Register source
689
            const bool generated = sf->GetIsGenerated();
690
691
            if (fileType == cmSystemTools::HEADER_FILE_FORMAT) {
              if (generated) {
692
                this->AutogenTarget.HeadersGenerated.push_back(absPath);
693
              } else {
694
                this->AutogenTarget.Headers.push_back(absPath);
695
696
697
              }
            } else {
              if (generated) {
698
                this->AutogenTarget.SourcesGenerated.push_back(absPath);
699
              } else {
700
                this->AutogenTarget.Sources.push_back(absPath);
701
              }
702
703
704
            }
          }
        }
705
706
      }
      // Register rcc enabled files
707
      if (this->Rcc.Enabled && (ext == qrcExt) &&
708
          !sf->GetPropertyAsBool("SKIP_AUTORCC")) {
709
710
        // Register qrc file
        {
711
712
713
714
          Qrc qrc;
          qrc.QrcFile = cmSystemTools::GetRealPath(fPath);
          qrc.QrcName =
            cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
715
          qrc.Generated = sf->GetIsGenerated();
716
717
          // RCC options
          {
718
            std::string const opts = sf->GetSafeProperty("AUTORCC_OPTIONS");
719
            if (!opts.empty()) {
720
              cmSystemTools::ExpandListArgument(opts, qrc.Options);
721
722
            }
          }
723
          this->Rcc.Qrcs.push_back(std::move(qrc));
724
        }
725
726
      }
    }
727
  }
728
729
730
731
732
733
  // 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();

734
  if (this->Moc.Enabled || this->Uic.Enabled) {
735
736
737
738
739
740
741
742
743
744
745
    // 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;
746
        }
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
        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);
          }
766
767
768
        }
      }
    }
769

770
    // Process GENERATED sources and headers
771
772
    if (!this->AutogenTarget.SourcesGenerated.empty() ||
        !this->AutogenTarget.HeadersGenerated.empty()) {
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
      // 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;
791
      }
792
793
794

      if (policyAccept) {
        // Accept GENERATED sources
795
796
797
798
        for (std::string const& absFile :
             this->AutogenTarget.HeadersGenerated) {
          this->AutogenTarget.Headers.push_back(absFile);
          this->AutogenTarget.DependFiles.insert(absFile);
799
        }
800
801
802
803
        for (std::string const& absFile :
             this->AutogenTarget.SourcesGenerated) {
          this->AutogenTarget.Sources.push_back(absFile);
          this->AutogenTarget.DependFiles.insert(absFile);
804
        }
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
      } 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";
824
825
          for (const std::string& absFile :
               this->AutogenTarget.HeadersGenerated) {
826
827
            msg.append("  ").append(Quoted(absFile)).append("\n");
          }
828
829
          for (const std::string& absFile :
               this->AutogenTarget.SourcesGenerated) {
830
831
832
833
834
835
836
837
838
839
840
841
            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";
842
          makefile->IssueMessage(MessageType::AUTHOR_WARNING, msg);
843
        }
844
      }
845
    }
846
847
    // Sort headers and sources
    if (this->Moc.Enabled || this->Uic.Enabled) {
848
849
850
851
      std::sort(this->AutogenTarget.Headers.begin(),
                this->AutogenTarget.Headers.end());
      std::sort(this->AutogenTarget.Sources.begin(),
                this->AutogenTarget.Sources.end());
852
    }
853
  }
854

855
  // Process qrc files
856
  if (!this->Rcc.Qrcs.empty()) {
Tobias Hunger's avatar
Tobias Hunger committed
857
    const bool modernQt = (this->QtVersion.Major >= 5);
858
859
860
    // Target rcc options
    std::vector<std::string> optionsTarget;
    cmSystemTools::ExpandListArgument(
861
      this->Target->GetSafeProperty("AUTORCC_OPTIONS"), optionsTarget);
862

863
    // Check if file name is unique
864
    for (Qrc& qrc : this->Rcc.Qrcs) {
865
      qrc.Unique = true;
866
      for (Qrc const& qrc2 : this->Rcc.Qrcs) {
867
868
        if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
          qrc.Unique = false;
869
870
          break;
        }
871
      }
872
    }
873
    // Path checksum and file names
874
    {
875
      cmFilePathChecksum const fpathCheckSum(makefile);
876
      for (Qrc& qrc : this->Rcc.Qrcs) {
877
        qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile);
878
        // RCC output file name
879
        {
880
          std::string rccFile = this->Dir.Build + "/";
881
          rccFile += qrc.PathChecksum;
882
          rccFile += "/qrc_";
883
          rccFile += qrc.QrcName;
884
          rccFile += ".cpp";
885
          qrc.RccFile = std::move(rccFile);
886
887
        }
        {
888
          std::string base = this->Dir.Info;
889
          base += "/RCC";
890
891
892
          base += qrc.QrcName;
          if (!qrc.Unique) {
            base += qrc.PathChecksum;
893
          }
894
895
896
897

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

898
899
          qrc.InfoFile = base;
          qrc.InfoFile += "Info.cmake";
900

901
          qrc.SettingsFile = base;
902
          qrc.SettingsFile += "Settings.txt";
903
904
905
906
907
908
909

          if (this->MultiConfig) {
            for (std::string const& cfg : this->ConfigsList) {
              qrc.ConfigSettingsFile[cfg] =
                AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg);
            }
          }
910
        }
911
912
913
      }
    }
    // RCC options
914
    for (Qrc& qrc : this->Rcc.Qrcs) {
915
916
917
      // Target options
      std::vector<std::string> opts = optionsTarget;
      // Merge computed "-name XYZ" option
918
      {
919
        std::string name = qrc.QrcName;
920
921
        // Replace '-' with '_'. The former is not valid for symbol names.
        std::replace(name.begin(), name.end(), '-', '_');
922
        if (!qrc.Unique) {
923
          name += "_";
924
          name += qrc.PathChecksum;
925
        }
926
927
928
        std::vector<std::string> nameOpts;
        nameOpts.emplace_back("-name");
        nameOpts.emplace_back(std::move(name));
Tobias Hunger's avatar
Tobias Hunger committed
929
        RccMergeOptions(opts, nameOpts, modernQt);
930
      }
931
      // Merge file option
Tobias Hunger's avatar
Tobias Hunger committed
932
      RccMergeOptions(opts, qrc.Options, modernQt);
933
      qrc.Options = std::move(opts);
934
    }
935
    // RCC resources
936
    for (Qrc& qrc : this->Rcc.Qrcs) {
937
938
939
      if (!qrc.Generated) {
        std::string error;
        if (!RccListInputs(qrc.QrcFile, qrc.Resources, error)) {
940
          cmSystemTools::Error(error);
941
          return false;
942
        }
943
      }
944
945
    }
  }
946

947
948
  return true;
}
949

950
951
952
953
954
955
956
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
957
  makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
958
959
960
961

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

966
967
968
969
970
971
  // Compose target comment
  std::string autogenComment;
  {
    std::string tools;
    if (this->Moc.Enabled) {
      tools += "MOC";
972
    }
973
974
975
    if (this->Uic.Enabled) {
      if (!tools.empty()) {
        tools += " and ";
976
      }
977
      tools += "UIC";
978
    }
979
980
981
982
983
    autogenComment = "Automatic ";
    autogenComment += tools;
    autogenComment += " for target ";
    autogenComment += this->Target->GetName();
  }
984

985
986
987
988
989
990
991
  // Compose command lines
  cmCustomCommandLines commandLines;
  {
    cmCustomCommandLine currentLine;
    currentLine.push_back(cmSystemTools::GetCMakeCommand());
    currentLine.push_back("-E");
    currentLine.push_back("cmake_autogen");
992
    currentLine.push_back(this->AutogenTarget.InfoFile);
993
994
995
    currentLine.push_back("$<CONFIGURATION>");
    commandLines.push_back(std::move(currentLine));
  }
996

997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
  // 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
1009
    if (!this->AutogenTarget.DependFiles.empty()) {
1010
      usePRE_BUILD = false;
1011
    }
1012
1013
1014
1015
    // Cannot use PRE_BUILD when a global autogen target is in place
    if (AutogenTarget.GlobalTarget) {
      usePRE_BUILD = false;
    }
1016
1017
1018
1019
  }
  // Create the autogen target/command
  if (usePRE_BUILD) {
    // Add additional autogen target dependencies to origin target
1020
    for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
1021
      this->Target->Target->AddUtility(depTarget->GetName(), makefile);
1022
    }
1023

1024
1025
1026
1027
1028
1029
1030
1031
1032
    // 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(),
1033
                       this->Dir.Work.c_str());
1034
1035
1036
1037
    cc.SetEscapeOldStyle(false);
    cc.SetEscapeAllowMakeVars(true);
    this->Target->Target->AddPreBuildCommand(cc);
  } else {
1038

1039
1040
    // Add link library target dependencies to the autogen target
    // dependencies
1041
    if (this->AutogenTarget.DependOrigin) {
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
      // 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]++;
1055
1056
1057
            }
          }
        }
1058
      }
1059
1060
      for (auto const& item : commonTargets) {
        if (item.second == this->ConfigsList.size()) {
1061
          this->AutogenTarget.DependTargets.insert(item.first->Target);
1062
        }
1063
      }
1064
    }
1065

1066
1067
    // Create autogen target
    cmTarget* autogenTarget = makefile->AddUtilityCommand(
1068
1069
1070
1071
      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()),
1072
1073
1074
1075
1076
1077
      commandLines, false, autogenComment.c_str());
    // Create autogen generator target
    localGen->AddGeneratorTarget(
      new cmGeneratorTarget(autogenTarget, localGen));

    // Forward origin utilities to autogen target