cmQtAutoGenInitializer.cxx 52.1 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
    // Autogen target: Compute user defined dependencies
    {
325 326 327
      this->AutogenTarget.DependOrigin =
        this->Target->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS");

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

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

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

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

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

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

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

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

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

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

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

520 521 522
  // Uic executable
  if (!GetUicExecutable()) {
    return false;
523 524
  }

525 526 527 528 529 530 531
  return true;
}

bool cmQtAutoGenInitializer::InitRcc()
{
  if (!GetRccExecutable()) {
    return false;
532
  }
533 534 535 536 537 538
  return true;
}

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

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

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

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

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

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

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

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

771 772
          qrc.InfoFile = base;
          qrc.InfoFile += "Info.cmake";
773

774
          qrc.SettingsFile = base;
775
          qrc.SettingsFile += "Settings.txt";
776 777 778 779 780 781 782

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

820 821
  return true;
}
822

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

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

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

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

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

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

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

935 936
    // Create autogen target
    cmTarget* autogenTarget = makefile->AddUtilityCommand(
937 938 939 940
      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()),
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
947 948 949 950
    if (this->AutogenTarget.DependOrigin) {
      for (BT<std::string> const& depName : this->Target->GetUtilities()) {
        autogenTarget->AddUtility(depName.Value, makefile);
      }
951 952
    }
    // Add additional autogen target dependencies to autogen target
953
    for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
954
      autogenTarget->AddUtility(depTarget->GetName(), makefile);
955 956
    }

957
    // Set FOLDER property in autogen target
958 959
    if (!this->TargetsFolder.empty()) {
      autogenTarget->SetProperty("FOLDER", this->TargetsFolder.c_str());
960 961
    }

962
    // Add autogen target to the origin target dependencies
963
    this->Target->Target->AddUtility(this->AutogenTarget.Name, makefile);
964 965 966 967 968
  }