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

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
#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
cmQtAutoGenInitializer::cmQtAutoGenInitializer(cmGeneratorTarget* target,
                                               bool mocEnabled,
                                               bool uicEnabled,
                                               bool rccEnabled,
                                               IntegerVersion const& qtVersion)
182
  : Target(target)
183
  , QtVersion(qtVersion)
184
{
185 186 187
  Moc.Enabled = mocEnabled;
  Uic.Enabled = uicEnabled;
  Rcc.Enabled = rccEnabled;
188
}
189

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

415 416 417 418 419
  // Moc compile definitions
  {
    auto GetCompileDefinitions =
      [this, localGen](std::string const& cfg) -> std::string {
      std::set<std::string> defines;
420
      localGen->GetTargetDefines(this->Target, cfg, "CXX", defines);
421 422 423 424 425 426 427 428 429 430 431 432 433 434
      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);
        }
      }
    }
435
  }
436 437 438

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

  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);
455
      std::string const& srcDir = makefile->GetCurrentSourceDirectory();
456 457 458 459
      for (std::string& path : this->Uic.SearchPaths) {
        path = cmSystemTools::CollapseFullPath(path, srcDir);
      }
    }
460
  }
461 462 463 464 465 466 467
  // 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, ";");
    };
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 513
    // 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));
          }
        }
      }
514 515 516
    }
  }

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

522 523 524 525 526 527 528
  return true;
}

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

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

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

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

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

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

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

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

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

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

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

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

817 818
  return true;
}
819

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

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

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

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

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

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

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

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

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

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

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

  return true;
}

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