cmQtAutoGenInitializer.cxx 51.8 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

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

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

40 41 42 43 44 45 46 47 48 49 50 51 52 53
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;
}

54
static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName,
55
                             cmQtAutoGen::GeneratorT genType)
56
{
Daniel Pfeifer's avatar
Daniel Pfeifer committed
57
  cmSourceGroup* sourceGroup = nullptr;
58 59
  // Acquire source group
  {
60 61 62 63 64 65
    std::string property;
    std::string groupName;
    {
      std::array<std::string, 2> props;
      // Use generator specific group name
      switch (genType) {
66
        case cmQtAutoGen::GeneratorT::MOC:
67 68
          props[0] = "AUTOMOC_SOURCE_GROUP";
          break;
69
        case cmQtAutoGen::GeneratorT::RCC:
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
          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;
        }
      }
85 86
    }
    // Generate a source group on demand
87
    if (!groupName.empty()) {
88
      sourceGroup = makefile->GetOrCreateSourceGroup(groupName);
Daniel Pfeifer's avatar
Daniel Pfeifer committed
89
      if (sourceGroup == nullptr) {
90 91 92 93 94 95
        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());
96 97 98 99
        return false;
      }
    }
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
100
  if (sourceGroup != nullptr) {
101 102 103 104 105
    sourceGroup->AddGroupFile(fileName);
  }
  return true;
}

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

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

132 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
/* @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;
}

176 177 178 179 180
cmQtAutoGenInitializer::cmQtAutoGenInitializer(cmGeneratorTarget* target,
                                               bool mocEnabled,
                                               bool uicEnabled,
                                               bool rccEnabled,
                                               IntegerVersion const& qtVersion)
181
  : Target(target)
182
  , QtVersion(qtVersion)
183
{
184 185 186
  Moc.Enabled = mocEnabled;
  Uic.Enabled = uicEnabled;
  Rcc.Enabled = rccEnabled;
187
}
188

189
bool cmQtAutoGenInitializer::InitCustomTargets()
190 191 192 193
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
  cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();
194

195 196 197 198 199 200 201
  // Configurations
  this->MultiConfig = globalGen->IsMultiConfig();
  this->ConfigDefault = makefile->GetConfigurations(this->ConfigsList);
  if (this->ConfigsList.empty()) {
    this->ConfigsList.push_back(this->ConfigDefault);
  }

202
  // Verbosity
203 204 205 206 207
  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
208
      this->Verbosity = cmSystemTools::IsOn(this->Verbosity) ? "1" : "0";
209 210 211
    }
  }

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
  // 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;
    }
227 228
  }

229
  // Common directories
230 231 232
  {
    // Collapsed current binary directory
    std::string const cbd = cmSystemTools::CollapseFullPath(
233
      std::string(), makefile->GetCurrentBinaryDirectory());
234

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
    // 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";
251
    }
252 253 254
    cmSystemTools::ConvertToUnixSlashes(this->Dir.Build);
    // Cleanup build directory
    AddCleanFile(makefile, this->Dir.Build);
255 256

    // Working directory
257 258
    this->Dir.Work = cbd;
    cmSystemTools::ConvertToUnixSlashes(this->Dir.Work);
259 260

    // Include directory
261 262
    this->Dir.Include = this->Dir.Build;
    this->Dir.Include += "/include";
263
    if (this->MultiConfig) {
264
      this->Dir.Include += "_$<CONFIG>";
265
    }
266
    // Per config include directories
267 268
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
269 270
        std::string& dir = this->Dir.ConfigInclude[cfg];
        dir = this->Dir.Build;
271 272 273 274
        dir += "/include_";
        dir += cfg;
      }
    }
275
  }
276

277
  // Moc, Uic and _autogen target settings
278 279 280 281 282
  if (this->Moc.Enabled || this->Uic.Enabled) {
    // Init moc specific settings
    if (this->Moc.Enabled && !InitMoc()) {
      return false;
    }
283

284 285 286 287 288
    // Init uic specific settings
    if (this->Uic.Enabled && !InitUic()) {
      return false;
    }

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
    // 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);
      }
    }

322 323 324 325 326 327 328 329 330 331 332
    // Autogen target: Compute user defined dependencies
    {
      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) {
333
            this->AutogenTarget.DependTargets.insert(depTarget);
334
          } else {
335
            this->AutogenTarget.DependFiles.insert(depName);
336 337 338 339 340
          }
        }
      }
    }
  }
341

342 343 344 345 346 347 348 349
  // 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)) {
350
    this->Target->AddIncludeDirectory(this->Dir.Include, true);
351 352 353 354 355 356 357 358 359 360 361
  }

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

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

363 364 365 366 367 368 369 370 371 372 373 374 375 376
  // 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
377
  this->Moc.MocsCompilation = this->Dir.Build;
378 379 380 381
  this->Moc.MocsCompilation += "/mocs_compilation.cpp";

  // Moc predefs command
  if (this->Target->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
382
      (this->QtVersion >= IntegerVersion(5, 8))) {
383 384 385 386 387
    this->Moc.PredefsCmd =
      makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND");
  }

  // Moc includes
388
  {
389
    bool const appendImplicit = (this->QtVersion.Major == 5);
390 391
    auto GetIncludeDirs =
      [this, localGen, appendImplicit](std::string const& cfg) -> std::string {
392 393 394 395
      // 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;
396 397
      localGen->GetIncludeDirectories(dirs, this->Target, "CXX", cfg, false,
                                      appendImplicit);
398 399 400 401 402 403
      return cmJoin(dirs, ";");
    };

    // Default configuration include directories
    this->Moc.Includes = GetIncludeDirs(this->ConfigDefault);
    // Other configuration settings
404
    if (this->MultiConfig) {
405
      for (std::string const& cfg : this->ConfigsList) {
406 407 408 409
        std::string dirs = GetIncludeDirs(cfg);
        if (dirs != this->Moc.Includes) {
          this->Moc.ConfigIncludes[cfg] = std::move(dirs);
        }
410
      }
411
    }
412 413
  }

414 415 416 417 418
  // Moc compile definitions
  {
    auto GetCompileDefinitions =
      [this, localGen](std::string const& cfg) -> std::string {
      std::set<std::string> defines;
419
      localGen->GetTargetDefines(this->Target, cfg, "CXX", defines);
420 421 422 423 424 425 426 427 428 429 430 431 432 433
      return cmJoin(defines, ";");
    };

    // Default configuration defines
    this->Moc.Defines = GetCompileDefinitions(this->ConfigDefault);
    // Other configuration defines
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
        std::string defines = GetCompileDefinitions(cfg);
        if (defines != this->Moc.Defines) {
          this->Moc.ConfigDefines[cfg] = std::move(defines);
        }
      }
    }
434
  }
435 436 437

  // Moc executable
  if (!GetMocExecutable()) {
438 439
    return false;
  }
440 441 442 443 444 445 446 447 448 449 450 451 452 453

  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);
454
      std::string const& srcDir = makefile->GetCurrentSourceDirectory();
455 456 457 458
      for (std::string& path : this->Uic.SearchPaths) {
        path = cmSystemTools::CollapseFullPath(path, srcDir);
      }
    }
459
  }
460 461 462 463 464 465 466
  // Uic target options
  {
    auto UicGetOpts = [this](std::string const& cfg) -> std::string {
      std::vector<std::string> opts;
      this->Target->GetAutoUicOptions(opts, cfg);
      return cmJoin(opts, ";");
    };
467

468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
    // Default settings
    this->Uic.Options = UicGetOpts(this->ConfigDefault);

    // Configuration specific settings
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
        std::string options = UicGetOpts(cfg);
        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));
          }
        }
      }
513 514 515
    }
  }

516 517 518
  // Uic executable
  if (!GetUicExecutable()) {
    return false;
519 520
  }

521 522 523 524 525 526 527
  return true;
}

bool cmQtAutoGenInitializer::InitRcc()
{
  if (!GetRccExecutable()) {
    return false;
528
  }
529 530 531 532 533 534
  return true;
}

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

536
  // Scan through target files
537
  {
538
    std::string const qrcExt = "qrc";
539
    std::vector<cmSourceFile*> srcFiles;
540
    this->Target->GetConfigCommonSourceFiles(srcFiles);
541
    for (cmSourceFile* sf : srcFiles) {
542 543 544 545
      if (sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
        continue;
      }
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
546 547
      std::string const& fPath = sf->GetFullPath();
      std::string const& ext = sf->GetExtension();
548
      // Register generated files that will be scanned by moc or uic
549
      if (this->Moc.Enabled || this->Uic.Enabled) {
550
        cmSystemTools::FileFormat const fileType =
551 552 553
          cmSystemTools::GetFileFormat(ext.c_str());
        if ((fileType == cmSystemTools::CXX_FILE_FORMAT) ||
            (fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
554
          std::string const absPath = cmSystemTools::GetRealPath(fPath);
555 556
          if ((this->Moc.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) ||
              (this->Uic.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) {
557 558 559 560
            // Register source
            const bool generated = sf->GetPropertyAsBool("GENERATED");
            if (fileType == cmSystemTools::HEADER_FILE_FORMAT) {
              if (generated) {
561
                this->AutogenTarget.HeadersGenerated.push_back(absPath);
562
              } else {
563
                this->AutogenTarget.Headers.push_back(absPath);
564 565 566
              }
            } else {
              if (generated) {
567
                this->AutogenTarget.SourcesGenerated.push_back(absPath);
568
              } else {
569
                this->AutogenTarget.Sources.push_back(absPath);
570
              }
571 572 573
            }
          }
        }
574 575
      }
      // Register rcc enabled files
576
      if (this->Rcc.Enabled && (ext == qrcExt) &&
577
          !sf->GetPropertyAsBool("SKIP_AUTORCC")) {
578 579
        // Register qrc file
        {
580 581 582 583 584
          Qrc qrc;
          qrc.QrcFile = cmSystemTools::GetRealPath(fPath);
          qrc.QrcName =
            cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
          qrc.Generated = sf->GetPropertyAsBool("GENERATED");
585 586
          // RCC options
          {
587
            std::string const opts = sf->GetSafeProperty("AUTORCC_OPTIONS");
588
            if (!opts.empty()) {
589
              cmSystemTools::ExpandListArgument(opts, qrc.Options);
590 591
            }
          }
592
          this->Rcc.Qrcs.push_back(std::move(qrc));
593
        }
594 595
      }
    }
596
  }
597 598 599 600 601 602
  // 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();

603
  if (this->Moc.Enabled || this->Uic.Enabled) {
604 605 606 607 608 609 610 611 612 613 614
    // 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;
615
        }
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
        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);
          }
635 636 637
        }
      }
    }
638

639
    // Process GENERATED sources and headers
640 641
    if (!this->AutogenTarget.SourcesGenerated.empty() ||
        !this->AutogenTarget.HeadersGenerated.empty()) {
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
      // 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;
660
      }
661 662 663

      if (policyAccept) {
        // Accept GENERATED sources
664 665 666 667
        for (std::string const& absFile :
             this->AutogenTarget.HeadersGenerated) {
          this->AutogenTarget.Headers.push_back(absFile);
          this->AutogenTarget.DependFiles.insert(absFile);
668
        }
669 670 671 672
        for (std::string const& absFile :
             this->AutogenTarget.SourcesGenerated) {
          this->AutogenTarget.Sources.push_back(absFile);
          this->AutogenTarget.DependFiles.insert(absFile);
673
        }
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
      } 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";
693 694
          for (const std::string& absFile :
               this->AutogenTarget.HeadersGenerated) {
695 696
            msg.append("  ").append(Quoted(absFile)).append("\n");
          }
697 698
          for (const std::string& absFile :
               this->AutogenTarget.SourcesGenerated) {
699 700 701 702 703 704 705 706 707 708 709 710 711
            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);
712
        }
713
      }
714
    }
715 716
    // Sort headers and sources
    if (this->Moc.Enabled || this->Uic.Enabled) {
717 718 719 720
      std::sort(this->AutogenTarget.Headers.begin(),
                this->AutogenTarget.Headers.end());
      std::sort(this->AutogenTarget.Sources.begin(),
                this->AutogenTarget.Sources.end());
721
    }
722
  }
723

724
  // Process qrc files
725
  if (!this->Rcc.Qrcs.empty()) {
726
    const bool QtV5 = (this->QtVersion.Major == 5);
727 728 729
    // Target rcc options
    std::vector<std::string> optionsTarget;
    cmSystemTools::ExpandListArgument(
730
      this->Target->GetSafeProperty("AUTORCC_OPTIONS"), optionsTarget);
731

732
    // Check if file name is unique
733
    for (Qrc& qrc : this->Rcc.Qrcs) {
734
      qrc.Unique = true;
735
      for (Qrc const& qrc2 : this->Rcc.Qrcs) {
736 737
        if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
          qrc.Unique = false;
738 739
          break;
        }
740
      }
741
    }
742
    // Path checksum and file names
743
    {
744
      cmFilePathChecksum const fpathCheckSum(makefile);
745
      for (Qrc& qrc : this->Rcc.Qrcs) {
746
        qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile);
747
        // RCC output file name
748
        {
749
          std::string rccFile = this->Dir.Build + "/";
750
          rccFile += qrc.PathChecksum;
751
          rccFile += "/qrc_";
752
          rccFile += qrc.QrcName;
753
          rccFile += ".cpp";
754
          qrc.RccFile = std::move(rccFile);
755 756
        }
        {
757
          std::string base = this->Dir.Info;
758
          base += "/RCC";
759 760 761
          base += qrc.QrcName;
          if (!qrc.Unique) {
            base += qrc.PathChecksum;
762
          }
763 764 765 766

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

767 768
          qrc.InfoFile = base;
          qrc.InfoFile += "Info.cmake";
769

770
          qrc.SettingsFile = base;
771
          qrc.SettingsFile += "Settings.txt";
772 773 774 775 776 777 778

          if (this->MultiConfig) {
            for (std::string const& cfg : this->ConfigsList) {
              qrc.ConfigSettingsFile[cfg] =
                AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg);
            }
          }
779
        }
780 781 782
      }
    }
    // RCC options
783
    for (Qrc& qrc : this->Rcc.Qrcs) {
784 785 786
      // Target options
      std::vector<std::string> opts = optionsTarget;
      // Merge computed "-name XYZ" option
787
      {
788
        std::string name = qrc.QrcName;
789 790
        // Replace '-' with '_'. The former is not valid for symbol names.
        std::replace(name.begin(), name.end(), '-', '_');
791
        if (!qrc.Unique) {
792
          name += "_";
793
          name += qrc.PathChecksum;
794
        }
795 796 797
        std::vector<std::string> nameOpts;
        nameOpts.emplace_back("-name");
        nameOpts.emplace_back(std::move(name));
798
        RccMergeOptions(opts, nameOpts, QtV5);
799
      }
800
      // Merge file option
801
      RccMergeOptions(opts, qrc.Options, QtV5);
802
      qrc.Options = std::move(opts);
803
    }
804
    // RCC resources
805
    for (Qrc& qrc : this->Rcc.Qrcs) {
806 807 808 809 810
      if (!qrc.Generated) {
        std::string error;
        if (!RccListInputs(qrc.QrcFile, qrc.Resources, error)) {
          cmSystemTools::Error(error.c_str());
          return false;
811
        }
812
      }
813 814
    }
  }
815

816 817
  return true;
}
818

819 820 821 822 823 824 825
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
826
  makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
827 828 829 830 831 832

  // 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);
833 834
  }

835 836 837 838 839 840
  // Compose target comment
  std::string autogenComment;
  {
    std::string tools;
    if (this->Moc.Enabled) {
      tools += "MOC";
841
    }
842 843 844
    if (this->Uic.Enabled) {
      if (!tools.empty()) {
        tools += " and ";
845
      }
846
      tools += "UIC";
847
    }
848 849 850 851 852
    autogenComment = "Automatic ";
    autogenComment += tools;
    autogenComment += " for target ";
    autogenComment += this->Target->GetName();
  }
853

854 855 856 857 858 859 860
  // Compose command lines
  cmCustomCommandLines commandLines;
  {
    cmCustomCommandLine currentLine;
    currentLine.push_back(cmSystemTools::GetCMakeCommand());
    currentLine.push_back("-E");
    currentLine.push_back("cmake_autogen");
861
    currentLine.push_back(this->AutogenTarget.InfoFile);
862 863 864
    currentLine.push_back("$<CONFIGURATION>");
    commandLines.push_back(std::move(currentLine));
  }
865

866 867 868 869 870 871 872 873 874 875 876 877
  // 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
878
    if (!this->AutogenTarget.DependFiles.empty()) {
879
      usePRE_BUILD = false;
880
    }
881 882 883 884
  }
  // Create the autogen target/command
  if (usePRE_BUILD) {
    // Add additional autogen target dependencies to origin target
885
    for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
886
      this->Target->Target->AddUtility(depTarget->GetName(), makefile);
887
    }
888

889 890 891 892 893 894 895 896 897
    // 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(),
898
                       this->Dir.Work.c_str());
899 900 901 902
    cc.SetEscapeOldStyle(false);
    cc.SetEscapeAllowMakeVars(true);
    this->Target->Target->AddPreBuildCommand(cc);
  } else {
903

904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
    // Add link library target dependencies to the autogen target
    // dependencies
    {
      // 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]++;
920 921 922
            }
          }
        }
923
      }
924 925
      for (auto const& item : commonTargets) {
        if (item.second == this->ConfigsList.size()) {
926
          this->AutogenTarget.DependTargets.insert(item.first->Target);
927
        }
928
      }
929
    }
930

931 932
    // Create autogen target
    cmTarget* autogenTarget = makefile->AddUtilityCommand(
933 934 935 936
      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()),
937 938 939 940 941 942 943 944 945 946
      commandLines, false, autogenComment.c_str());
    // Create autogen generator target
    localGen->AddGeneratorTarget(
      new cmGeneratorTarget(autogenTarget, localGen));

    // Forward origin utilities to autogen target
    for (std::string const& depName : this->Target->Target->GetUtilities()) {
      autogenTarget->AddUtility(depName, makefile);
    }
    // Add additional autogen target dependencies to autogen target
947
    for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
948
      autogenTarget->AddUtility(depTarget->GetName(), makefile);
949 950
    }

951
    // Set FOLDER property in autogen target
952 953
    if (!this->TargetsFolder.empty()) {
      autogenTarget->SetProperty("FOLDER", this->TargetsFolder.c_str());
954 955
    }

956
    // Add autogen target to the origin target dependencies
957
    this->Target->Target->AddUtility(this->AutogenTarget.Name, makefile);
958 959 960 961 962
  }

  return true;
}

963
bool cmQtAutoGenInitializer::InitRccTargets()
964 965
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
966
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
967

968 969 970 971 972
  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);