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

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
cmQtAutoGenInitializer::cmQtAutoGenInitializer(
177 178 179
  cmGeneratorTarget* target, bool mocEnabled, bool uicEnabled, bool rccEnabled,
  std::string const& qtVersionMajor)
  : Target(target)
180
  , MultiConfig(false)
181
  , QtVersionMajor(qtVersionMajor)
182
{
183 184 185 186
  Moc.Enabled = mocEnabled;
  Uic.Enabled = uicEnabled;
  Rcc.Enabled = rccEnabled;

187 188
  this->QtVersionMinor =
    cmQtAutoGenInitializer::GetQtMinorVersion(target, this->QtVersionMajor);
189
}
190

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

197 198 199 200 201 202 203 204 205 206 207 208 209
  // Verbosity
  {
    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
        this->Verbosity =
          cmSystemTools::IsOn(this->Verbosity.c_str()) ? "1" : "0";
      }
    }
  }

210
  // Configurations
211
  this->MultiConfig = globalGen->IsMultiConfig();
212 213 214
  this->ConfigDefault = makefile->GetConfigurations(this->ConfigsList);
  if (this->ConfigsList.empty()) {
    this->ConfigsList.push_back(this->ConfigDefault);
215
  }
216 217 218 219 220 221 222 223 224 225 226 227 228 229

  // Autogen target name
  this->AutogenTargetName = this->Target->GetName();
  this->AutogenTargetName += "_autogen";

  // Autogen directories
  {
    // Collapsed current binary directory
    std::string const cbd = cmSystemTools::CollapseFullPath(
      "", makefile->GetCurrentBinaryDirectory());

    // Autogen info dir
    this->DirInfo = cbd;
    this->DirInfo += makefile->GetCMakeInstance()->GetCMakeFilesDirectory();
230
    this->DirInfo += '/';
231 232 233 234 235
    this->DirInfo += this->AutogenTargetName;
    this->DirInfo += ".dir";
    cmSystemTools::ConvertToUnixSlashes(this->DirInfo);

    // Autogen build dir
236
    this->DirBuild = this->Target->GetSafeProperty("AUTOGEN_BUILD_DIR");
237 238
    if (this->DirBuild.empty()) {
      this->DirBuild = cbd;
239
      this->DirBuild += '/';
240
      this->DirBuild += this->AutogenTargetName;
241
    }
242 243 244 245 246
    cmSystemTools::ConvertToUnixSlashes(this->DirBuild);

    // Working directory
    this->DirWork = cbd;
    cmSystemTools::ConvertToUnixSlashes(this->DirWork);
247
  }
248

249
  // Autogen files
250
  {
251 252
    this->AutogenInfoFile = this->DirInfo;
    this->AutogenInfoFile += "/AutogenInfo.cmake";
253

254
    this->AutogenSettingsFile = this->DirInfo;
255
    this->AutogenSettingsFile += "/AutogenOldSettings.txt";
256
  }
257

258 259 260 261 262 263 264 265 266 267
  // Autogen target FOLDER property
  {
    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) {
268
      folder = this->Target->GetProperty("FOLDER");
269 270 271
    }
    if (folder != nullptr) {
      this->AutogenFolder = folder;
272 273
    }
  }
274

275 276 277
  std::set<std::string> autogenDependFiles;
  std::set<cmTarget*> autogenDependTargets;
  std::vector<std::string> autogenProvides;
278

279 280 281
  // Remove build directories on cleanup
  AddCleanFile(makefile, this->DirBuild);
  // Remove old settings on cleanup
282
  {
283 284
    std::string base = this->DirInfo;
    base += "/AutogenOldSettings";
285
    if (this->MultiConfig) {
286 287
      for (std::string const& cfg : this->ConfigsList) {
        std::string filename = base;
288
        filename += '_';
289 290 291
        filename += cfg;
        filename += ".cmake";
        AddCleanFile(makefile, filename);
292
      }
293 294
    } else {
      AddCleanFile(makefile, base.append(".cmake"));
295
    }
296 297
  }

298
  // Add moc compilation to generated files list
299
  if (this->Moc.Enabled) {
300 301 302
    std::string mocsComp = this->DirBuild + "/mocs_compilation.cpp";
    this->AddGeneratedSource(mocsComp, GeneratorT::MOC);
    autogenProvides.push_back(std::move(mocsComp));
303
  }
304

305
  // Add autogen includes directory to the origin target INCLUDE_DIRECTORIES
306 307
  if (this->Moc.Enabled || this->Uic.Enabled ||
      (this->Rcc.Enabled && this->MultiConfig)) {
308 309 310
    std::string includeDir = this->DirBuild;
    includeDir += "/include";
    if (this->MultiConfig) {
311
      includeDir += "_$<CONFIG>";
312
    }
313
    this->Target->AddIncludeDirectory(includeDir, true);
314 315
  }

316
  // Acquire rcc executable and features
317
  if (this->Rcc.Enabled) {
318 319
    if (!GetRccExecutable()) {
      return false;
320 321 322
    }
  }

323
  // Extract relevant source files
324 325
  std::vector<std::string> generatedSources;
  std::vector<std::string> generatedHeaders;
326
  {
327
    std::string const qrcExt = "qrc";
328
    std::vector<cmSourceFile*> srcFiles;
329
    this->Target->GetConfigCommonSourceFiles(srcFiles);
330
    for (cmSourceFile* sf : srcFiles) {
331 332 333 334
      if (sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
        continue;
      }
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
335 336
      std::string const& fPath = sf->GetFullPath();
      std::string const& ext = sf->GetExtension();
337
      // Register generated files that will be scanned by moc or uic
338
      if (this->Moc.Enabled || this->Uic.Enabled) {
339
        cmSystemTools::FileFormat const fileType =
340 341 342
          cmSystemTools::GetFileFormat(ext.c_str());
        if ((fileType == cmSystemTools::CXX_FILE_FORMAT) ||
            (fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
343
          std::string const absPath = cmSystemTools::GetRealPath(fPath);
344 345
          if ((this->Moc.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) ||
              (this->Uic.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) {
346 347 348 349
            // Register source
            const bool generated = sf->GetPropertyAsBool("GENERATED");
            if (fileType == cmSystemTools::HEADER_FILE_FORMAT) {
              if (generated) {
350
                generatedHeaders.push_back(absPath);
351
              } else {
352
                this->Headers.push_back(absPath);
353 354 355
              }
            } else {
              if (generated) {
356
                generatedSources.push_back(absPath);
357
              } else {
358
                this->Sources.push_back(absPath);
359
              }
360 361 362
            }
          }
        }
363 364
      }
      // Register rcc enabled files
365
      if (this->Rcc.Enabled && (ext == qrcExt) &&
366
          !sf->GetPropertyAsBool("SKIP_AUTORCC")) {
367 368
        // Register qrc file
        {
369 370 371 372 373
          Qrc qrc;
          qrc.QrcFile = cmSystemTools::GetRealPath(fPath);
          qrc.QrcName =
            cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
          qrc.Generated = sf->GetPropertyAsBool("GENERATED");
374 375
          // RCC options
          {
376
            std::string const opts = sf->GetSafeProperty("AUTORCC_OPTIONS");
377
            if (!opts.empty()) {
378
              cmSystemTools::ExpandListArgument(opts, qrc.Options);
379 380
            }
          }
381
          this->Rcc.Qrcs.push_back(std::move(qrc));
382
        }
383 384 385 386 387 388
      }
    }
    // 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.
389 390 391
    this->Target->ClearSourcesCache();
  }
  // Read skip files from makefile sources
392
  if (this->Moc.Enabled || this->Uic.Enabled) {
393 394
    std::string pathError;
    for (cmSourceFile* sf : makefile->GetSourceFiles()) {
395
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
396 397 398 399 400 401 402
      // 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;
      }
403 404 405 406 407 408 409
      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");
410 411 412 413
      const bool mocSkip = this->Moc.Enabled &&
        (skipAll || sf->GetPropertyAsBool("SKIP_AUTOMOC"));
      const bool uicSkip = this->Uic.Enabled &&
        (skipAll || sf->GetPropertyAsBool("SKIP_AUTOUIC"));
414 415 416
      if (mocSkip || uicSkip) {
        std::string const absFile = cmSystemTools::GetRealPath(fPath);
        if (mocSkip) {
417
          this->Moc.Skip.insert(absFile);
418 419
        }
        if (uicSkip) {
420
          this->Uic.Skip.insert(absFile);
421 422 423
        }
      }
    }
424
  }
425

426
  // Process GENERATED sources and headers
427
  if (!generatedSources.empty() || !generatedHeaders.empty()) {
428 429 430
    // Check status of policy CMP0071
    bool policyAccept = false;
    bool policyWarn = false;
431
    cmPolicies::PolicyStatus const CMP0071_status =
432
      makefile->GetPolicyStatus(cmPolicies::CMP0071);
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
    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;
    }

    if (policyAccept) {
      // Accept GENERATED sources
450
      for (std::string const& absFile : generatedHeaders) {
451
        this->Headers.push_back(absFile);
452
        autogenDependFiles.insert(absFile);
453
      }
454
      for (std::string const& absFile : generatedSources) {
455
        this->Sources.push_back(absFile);
456
        autogenDependFiles.insert(absFile);
457
      }
458 459 460 461 462 463
    } else {
      if (policyWarn) {
        std::string msg;
        msg += cmPolicies::GetPolicyWarning(cmPolicies::CMP0071);
        msg += "\n";
        std::string tools;
464
        std::string property;
465
        if (this->Moc.Enabled && this->Uic.Enabled) {
466 467
          tools = "AUTOMOC and AUTOUIC";
          property = "SKIP_AUTOGEN";
468
        } else if (this->Moc.Enabled) {
469 470
          tools = "AUTOMOC";
          property = "SKIP_AUTOMOC";
471
        } else if (this->Uic.Enabled) {
472 473
          tools = "AUTOUIC";
          property = "SKIP_AUTOUIC";
474
        }
475 476 477
        msg += "For compatibility, CMake is excluding the GENERATED source "
               "file(s):\n";
        for (const std::string& absFile : generatedHeaders) {
478
          msg.append("  ").append(Quoted(absFile)).append("\n");
479
        }
480
        for (const std::string& absFile : generatedSources) {
481
          msg.append("  ").append(Quoted(absFile)).append("\n");
482
        }
483 484 485 486 487 488 489 490 491 492
        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";
493
        makefile->IssueMessage(cmake::AUTHOR_WARNING, msg);
494
      }
495
    }
496 497 498
    // Clear lists
    generatedSources.clear();
    generatedHeaders.clear();
499
  }
500
  // Sort headers and sources
501
  if (this->Moc.Enabled || this->Uic.Enabled) {
502 503 504
    std::sort(this->Headers.begin(), this->Headers.end());
    std::sort(this->Sources.begin(), this->Sources.end());
  }
505

506
  // Process qrc files
507
  if (!this->Rcc.Qrcs.empty()) {
508
    const bool QtV5 = (this->QtVersionMajor == "5");
509 510 511
    // Target rcc options
    std::vector<std::string> optionsTarget;
    cmSystemTools::ExpandListArgument(
512
      this->Target->GetSafeProperty("AUTORCC_OPTIONS"), optionsTarget);
513

514
    // Check if file name is unique
515
    for (Qrc& qrc : this->Rcc.Qrcs) {
516
      qrc.Unique = true;
517
      for (Qrc const& qrc2 : this->Rcc.Qrcs) {
518 519
        if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
          qrc.Unique = false;
520 521
          break;
        }
522
      }
523
    }
524
    // Path checksum and file names
525
    {
526
      cmFilePathChecksum const fpathCheckSum(makefile);
527
      for (Qrc& qrc : this->Rcc.Qrcs) {
528
        qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile);
529
        // RCC output file name
530
        {
531 532
          std::string rccFile = this->DirBuild + "/";
          rccFile += qrc.PathChecksum;
533
          rccFile += "/qrc_";
534
          rccFile += qrc.QrcName;
535
          rccFile += ".cpp";
536
          qrc.RccFile = std::move(rccFile);
537 538
        }
        {
539
          std::string base = this->DirInfo;
540
          base += "/RCC";
541 542 543
          base += qrc.QrcName;
          if (!qrc.Unique) {
            base += qrc.PathChecksum;
544
          }
545 546 547 548

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

549 550
          qrc.InfoFile = base;
          qrc.InfoFile += "Info.cmake";
551

552
          qrc.SettingsFile = base;
553
          qrc.SettingsFile += "Settings.txt";
554
        }
555 556 557
      }
    }
    // RCC options
558
    for (Qrc& qrc : this->Rcc.Qrcs) {
559 560 561
      // Target options
      std::vector<std::string> opts = optionsTarget;
      // Merge computed "-name XYZ" option
562
      {
563
        std::string name = qrc.QrcName;
564 565
        // Replace '-' with '_'. The former is not valid for symbol names.
        std::replace(name.begin(), name.end(), '-', '_');
566
        if (!qrc.Unique) {
567
          name += "_";
568
          name += qrc.PathChecksum;
569
        }
570 571 572
        std::vector<std::string> nameOpts;
        nameOpts.emplace_back("-name");
        nameOpts.emplace_back(std::move(name));
573
        RccMergeOptions(opts, nameOpts, QtV5);
574
      }
575
      // Merge file option
576
      RccMergeOptions(opts, qrc.Options, QtV5);
577
      qrc.Options = std::move(opts);
578
    }
579
    for (Qrc& qrc : this->Rcc.Qrcs) {
580
      // Register file at target
581
      this->AddGeneratedSource(qrc.RccFile, GeneratorT::RCC);
582

583 584
      std::vector<std::string> ccOutput;
      ccOutput.push_back(qrc.RccFile);
585

586
      cmCustomCommandLines commandLines;
587 588 589 590 591 592 593 594 595 596 597 598
      if (this->MultiConfig) {
        // Build for all configurations
        for (std::string const& config : this->ConfigsList) {
          cmCustomCommandLine currentLine;
          currentLine.push_back(cmSystemTools::GetCMakeCommand());
          currentLine.push_back("-E");
          currentLine.push_back("cmake_autorcc");
          currentLine.push_back(qrc.InfoFile);
          currentLine.push_back(config);
          commandLines.push_back(std::move(currentLine));
        }
      } else {
599 600 601 602
        cmCustomCommandLine currentLine;
        currentLine.push_back(cmSystemTools::GetCMakeCommand());
        currentLine.push_back("-E");
        currentLine.push_back("cmake_autorcc");
603
        currentLine.push_back(qrc.InfoFile);
604
        currentLine.push_back("$<CONFIG>");
605
        commandLines.push_back(std::move(currentLine));
606
      }
607
      std::string ccComment = "Automatic RCC for ";
608
      ccComment += FileProjectRelativePath(makefile, qrc.QrcFile);
609

610
      if (qrc.Generated) {
611 612 613
        // Create custom rcc target
        std::string ccName;
        {
614
          ccName = this->Target->GetName();
615
          ccName += "_arcc_";
616 617
          ccName += qrc.QrcName;
          if (!qrc.Unique) {
618
            ccName += "_";
619
            ccName += qrc.PathChecksum;
620 621
          }
          std::vector<std::string> ccDepends;
622
          // Add the .qrc and info file to the custom target dependencies
623
          ccDepends.push_back(qrc.QrcFile);
624
          ccDepends.push_back(qrc.InfoFile);
625 626

          cmTarget* autoRccTarget = makefile->AddUtilityCommand(
627 628 629
            ccName, cmMakefile::TargetOrigin::Generator, true,
            this->DirWork.c_str(), ccOutput, ccDepends, commandLines, false,
            ccComment.c_str());
630 631 632 633 634
          // Create autogen generator target
          localGen->AddGeneratorTarget(
            new cmGeneratorTarget(autoRccTarget, localGen));

          // Set FOLDER property in autogen target
635 636
          if (!this->AutogenFolder.empty()) {
            autoRccTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
637 638 639
          }
        }
        // Add autogen target to the origin target dependencies
640
        this->Target->Target->AddUtility(ccName, makefile);
641
      } else {
642
        // Create custom rcc command
643
        {
644 645
          std::vector<std::string> ccByproducts;
          std::vector<std::string> ccDepends;
646
          // Add the .qrc and info file to the custom command dependencies
647
          ccDepends.push_back(qrc.QrcFile);
648
          ccDepends.push_back(qrc.InfoFile);
649 650 651 652

          // Add the resource files to the dependencies
          {
            std::string error;
653
            if (RccListInputs(qrc.QrcFile, qrc.Resources, error)) {
654
              for (std::string const& fileName : qrc.Resources) {
655 656 657 658 659
                // Add resource file to the custom command dependencies
                ccDepends.push_back(fileName);
              }
            } else {
              cmSystemTools::Error(error.c_str());
660
              return false;
661
            }
662
          }
663 664 665
          makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends,
                                             /*main_dependency*/ std::string(),
                                             commandLines, ccComment.c_str(),
666
                                             this->DirWork.c_str());
667
        }
668
        // Reconfigure when .qrc file changes
669
        makefile->AddCMakeDependFile(qrc.QrcFile);
670 671 672 673
      }
    }
  }

674
  // Create _autogen target
675
  if (this->Moc.Enabled || this->Uic.Enabled) {
676 677 678
    // Add user defined autogen target dependencies
    {
      std::string const deps =
679
        this->Target->GetSafeProperty("AUTOGEN_TARGET_DEPENDS");
680 681 682 683 684 685 686 687 688 689 690
      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) {
            autogenDependTargets.insert(depTarget);
          } else {
            autogenDependFiles.insert(depName);
          }
691
        }
692 693 694
      }
    }

695 696 697
    // Compose target comment
    std::string autogenComment;
    {
698
      std::string tools;
699
      if (this->Moc.Enabled) {
700
        tools += "MOC";
701
      }
702
      if (this->Uic.Enabled) {
703 704
        if (!tools.empty()) {
          tools += " and ";
705
        }
706
        tools += "UIC";
707
      }
708 709 710 711
      autogenComment = "Automatic ";
      autogenComment += tools;
      autogenComment += " for target ";
      autogenComment += this->Target->GetName();
712
    }
713 714 715 716 717 718 719 720

    // Compose command lines
    cmCustomCommandLines commandLines;
    {
      cmCustomCommandLine currentLine;
      currentLine.push_back(cmSystemTools::GetCMakeCommand());
      currentLine.push_back("-E");
      currentLine.push_back("cmake_autogen");
721
      currentLine.push_back(this->AutogenInfoFile);
722 723
      currentLine.push_back("$<CONFIGURATION>");
      commandLines.push_back(std::move(currentLine));
724 725
    }

726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
    // 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
      if (!autogenDependFiles.empty()) {
        usePRE_BUILD = false;
      }
    }
    // Create the autogen target/command
    if (usePRE_BUILD) {
      // Add additional autogen target dependencies to origin target
      for (cmTarget* depTarget : autogenDependTargets) {
746
        this->Target->Target->AddUtility(depTarget->GetName(), makefile);
747
      }
748

749 750 751 752 753 754 755 756 757
      // 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(),
758
                         this->DirWork.c_str());
759 760
      cc.SetEscapeOldStyle(false);
      cc.SetEscapeAllowMakeVars(true);
761
      this->Target->Target->AddPreBuildCommand(cc);
762
    } else {
763

764 765
      // Add link library target dependencies to the autogen target
      // dependencies
766 767 768 769 770 771 772 773 774 775 776 777 778 779
      {
        // 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]++;
780
              }
781 782 783
            }
          }
        }
784 785 786 787 788
        for (auto const& item : commonTargets) {
          if (item.second == this->ConfigsList.size()) {
            autogenDependTargets.insert(item.first->Target);
          }
        }
789 790
      }

791 792
      // Create autogen target
      cmTarget* autogenTarget = makefile->AddUtilityCommand(
793
        this->AutogenTargetName, cmMakefile::TargetOrigin::Generator, true,
794 795 796
        this->DirWork.c_str(), /*byproducts=*/autogenProvides,
        std::vector<std::string>(autogenDependFiles.begin(),
                                 autogenDependFiles.end()),
797
        commandLines, false, autogenComment.c_str());
798 799 800 801 802
      // Create autogen generator target
      localGen->AddGeneratorTarget(
        new cmGeneratorTarget(autogenTarget, localGen));

      // Forward origin utilities to autogen target
803
      for (std::string const& depName : this->Target->Target->GetUtilities()) {
804
        autogenTarget->AddUtility(depName, makefile);
805
      }
806 807 808
      // Add additional autogen target dependencies to autogen target
      for (cmTarget* depTarget : autogenDependTargets) {
        autogenTarget->AddUtility(depTarget->GetName(), makefile);
809
      }
810 811

      // Set FOLDER property in autogen target
812 813
      if (!this->AutogenFolder.empty()) {
        autogenTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
814
      }
815

816
      // Add autogen target to the origin target dependencies
817
      this->Target->Target->AddUtility(this->AutogenTargetName, makefile);