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
inline static const char* SafeString(const char* value)
41
{
Daniel Pfeifer's avatar
Daniel Pfeifer committed
42
  return (value != nullptr) ? value : "";
43 44
}

45 46 47 48 49 50 51 52 53 54 55 56 57 58
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;
}

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

111
static void AddCleanFile(cmMakefile* makefile, std::string const& fileName)
112 113 114 115 116
{
  makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", fileName.c_str(),
                           false);
}

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

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 177 178 179 180
/* @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;
}

181
cmQtAutoGenInitializer::cmQtAutoGenInitializer(
182 183 184 185 186 187
  cmGeneratorTarget* target, bool mocEnabled, bool uicEnabled, bool rccEnabled,
  std::string const& qtVersionMajor)
  : Target(target)
  , MocEnabled(mocEnabled)
  , UicEnabled(uicEnabled)
  , RccEnabled(rccEnabled)
188
  , MultiConfig(false)
189
  , QtVersionMajor(qtVersionMajor)
190
{
191 192
  this->QtVersionMinor =
    cmQtAutoGenInitializer::GetQtMinorVersion(target, this->QtVersionMajor);
193
}
194

195
bool cmQtAutoGenInitializer::InitCustomTargets()
196 197 198 199
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
  cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();
200

201 202 203 204 205 206 207 208 209 210 211 212 213
  // 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";
      }
    }
  }

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

  // 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();
234
    this->DirInfo += '/';
235 236 237 238 239
    this->DirInfo += this->AutogenTargetName;
    this->DirInfo += ".dir";
    cmSystemTools::ConvertToUnixSlashes(this->DirInfo);

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

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

253
  // Autogen files
254
  {
255 256
    this->AutogenInfoFile = this->DirInfo;
    this->AutogenInfoFile += "/AutogenInfo.cmake";
257

258
    this->AutogenSettingsFile = this->DirInfo;
259
    this->AutogenSettingsFile += "/AutogenOldSettings.txt";
260
  }
261

262 263 264 265 266 267 268 269 270 271
  // 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) {
272
      folder = this->Target->GetProperty("FOLDER");
273 274 275
    }
    if (folder != nullptr) {
      this->AutogenFolder = folder;
276 277
    }
  }
278

279 280 281
  std::set<std::string> autogenDependFiles;
  std::set<cmTarget*> autogenDependTargets;
  std::vector<std::string> autogenProvides;
282

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

302
  // Add moc compilation to generated files list
303
  if (this->MocEnabled) {
304 305 306
    std::string mocsComp = this->DirBuild + "/mocs_compilation.cpp";
    this->AddGeneratedSource(mocsComp, GeneratorT::MOC);
    autogenProvides.push_back(std::move(mocsComp));
307
  }
308

309
  // Add autogen includes directory to the origin target INCLUDE_DIRECTORIES
310 311 312 313 314
  if (this->MocEnabled || this->UicEnabled ||
      (this->RccEnabled && this->MultiConfig)) {
    std::string includeDir = this->DirBuild;
    includeDir += "/include";
    if (this->MultiConfig) {
315
      includeDir += "_$<CONFIG>";
316
    }
317
    this->Target->AddIncludeDirectory(includeDir, true);
318 319
  }

320 321
  // Acquire rcc executable and features
  if (this->RccEnabled) {
322 323
    if (!GetRccExecutable()) {
      return false;
324 325 326
    }
  }

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

430
  // Process GENERATED sources and headers
431
  if (!generatedSources.empty() || !generatedHeaders.empty()) {
432 433 434
    // Check status of policy CMP0071
    bool policyAccept = false;
    bool policyWarn = false;
435
    cmPolicies::PolicyStatus const CMP0071_status =
436
      makefile->GetPolicyStatus(cmPolicies::CMP0071);
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
    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
454
      for (std::string const& absFile : generatedHeaders) {
455
        this->Headers.push_back(absFile);
456
        autogenDependFiles.insert(absFile);
457
      }
458
      for (std::string const& absFile : generatedSources) {
459
        this->Sources.push_back(absFile);
460
        autogenDependFiles.insert(absFile);
461
      }
462 463 464 465 466 467
    } else {
      if (policyWarn) {
        std::string msg;
        msg += cmPolicies::GetPolicyWarning(cmPolicies::CMP0071);
        msg += "\n";
        std::string tools;
468
        std::string property;
469
        if (this->MocEnabled && this->UicEnabled) {
470 471
          tools = "AUTOMOC and AUTOUIC";
          property = "SKIP_AUTOGEN";
472
        } else if (this->MocEnabled) {
473 474
          tools = "AUTOMOC";
          property = "SKIP_AUTOMOC";
475
        } else if (this->UicEnabled) {
476 477
          tools = "AUTOUIC";
          property = "SKIP_AUTOUIC";
478
        }
479 480 481
        msg += "For compatibility, CMake is excluding the GENERATED source "
               "file(s):\n";
        for (const std::string& absFile : generatedHeaders) {
482
          msg.append("  ").append(Quoted(absFile)).append("\n");
483
        }
484
        for (const std::string& absFile : generatedSources) {
485
          msg.append("  ").append(Quoted(absFile)).append("\n");
486
        }
487 488 489 490 491 492 493 494 495 496
        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";
497
        makefile->IssueMessage(cmake::AUTHOR_WARNING, msg);
498
      }
499
    }
500 501 502
    // Clear lists
    generatedSources.clear();
    generatedHeaders.clear();
503
  }
504
  // Sort headers and sources
505 506 507 508
  if (this->MocEnabled || this->UicEnabled) {
    std::sort(this->Headers.begin(), this->Headers.end());
    std::sort(this->Sources.begin(), this->Sources.end());
  }
509

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

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

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

553 554
          qrc.InfoFile = base;
          qrc.InfoFile += "Info.cmake";
555

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

587 588
      std::vector<std::string> ccOutput;
      ccOutput.push_back(qrc.RccFile);
589

590
      cmCustomCommandLines commandLines;
591 592 593 594 595 596 597 598 599 600 601 602
      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 {
603 604 605 606
        cmCustomCommandLine currentLine;
        currentLine.push_back(cmSystemTools::GetCMakeCommand());
        currentLine.push_back("-E");
        currentLine.push_back("cmake_autorcc");
607
        currentLine.push_back(qrc.InfoFile);
608
        currentLine.push_back("$<CONFIG>");
609
        commandLines.push_back(std::move(currentLine));
610
      }
611
      std::string ccComment = "Automatic RCC for ";
612
      ccComment += FileProjectRelativePath(makefile, qrc.QrcFile);
613

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

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

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

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

678
  // Create _autogen target
679
  if (this->MocEnabled || this->UicEnabled) {
680 681 682
    // Add user defined autogen target dependencies
    {
      std::string const deps =
683
        this->Target->GetSafeProperty("AUTOGEN_TARGET_DEPENDS");
684 685 686 687 688 689 690 691 692 693 694
      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);
          }
695
        }
696 697 698
      }
    }

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

    // Compose command lines
    cmCustomCommandLines commandLines;
    {
      cmCustomCommandLine currentLine;
      currentLine.push_back(cmSystemTools::GetCMakeCommand());
      currentLine.push_back("-E");
      currentLine.push_back("cmake_autogen");
725
      currentLine.push_back(this->AutogenInfoFile);
726 727
      currentLine.push_back("$<CONFIGURATION>");
      commandLines.push_back(std::move(currentLine));
728 729
    }

730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
    // 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) {
750
        this->Target->Target->AddUtility(depTarget->GetName(), makefile);
751
      }
752

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

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

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

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

      // Set FOLDER property in autogen target
816 817
      if (!this->AutogenFolder.empty()) {
        autogenTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
818
      }
819

820
      // Add autogen target to the origin target dependencies
821
      this->Target->Target->AddUtility(this->AutogenTargetName, makefile);
822
    }
823
  }
824 825

  return true;
826 827
}

828
bool cmQtAutoGenInitializer::SetupCustomTargets()
829
{
830
  cmMakefile* makefile = this->Target->Target->GetMakefile();
831

832
  // Create info directory on demand
833
  if (!cmSystemTools::MakeDirectory(this->DirInfo)) {
834
    std::string emsg = ("AutoGen: Could not create directory: ");
835