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 250 251 252 253 254 255 256 257 258 259 260 261

    // Include directory
    this->DirInclude = this->DirBuild;
    this->DirInclude += "/include";
    if (this->MultiConfig) {
      this->DirInclude += "_$<CONFIG>";
    }
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
        std::string& dir = this->DirConfigInclude[cfg];
        dir = this->DirBuild;
        dir += "/include_";
        dir += cfg;
      }
    }
262
  }
263

264
  // Autogen files
265
  {
266 267
    this->AutogenInfoFile = this->DirInfo;
    this->AutogenInfoFile += "/AutogenInfo.cmake";
268

269
    this->AutogenSettingsFile = this->DirInfo;
270
    this->AutogenSettingsFile += "/AutogenOldSettings.txt";
271
  }
272

273 274 275 276 277 278 279 280 281 282
  // 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) {
283
      folder = this->Target->GetProperty("FOLDER");
284 285 286
    }
    if (folder != nullptr) {
      this->AutogenFolder = folder;
287 288
    }
  }
289

290 291 292
  std::set<std::string> autogenDependFiles;
  std::set<cmTarget*> autogenDependTargets;
  std::vector<std::string> autogenProvides;
293

294 295 296
  // Remove build directories on cleanup
  AddCleanFile(makefile, this->DirBuild);
  // Remove old settings on cleanup
297
  {
298 299
    std::string base = this->DirInfo;
    base += "/AutogenOldSettings";
300
    if (this->MultiConfig) {
301 302
      for (std::string const& cfg : this->ConfigsList) {
        std::string filename = base;
303
        filename += '_';
304 305 306
        filename += cfg;
        filename += ".cmake";
        AddCleanFile(makefile, filename);
307
      }
308 309
    } else {
      AddCleanFile(makefile, base.append(".cmake"));
310
    }
311 312
  }

313
  // Add moc compilation to generated files list
314
  if (this->Moc.Enabled) {
315 316 317
    std::string mocsComp = this->DirBuild + "/mocs_compilation.cpp";
    this->AddGeneratedSource(mocsComp, GeneratorT::MOC);
    autogenProvides.push_back(std::move(mocsComp));
318
  }
319

320
  // Add autogen includes directory to the origin target INCLUDE_DIRECTORIES
321 322
  if (this->Moc.Enabled || this->Uic.Enabled ||
      (this->Rcc.Enabled && this->MultiConfig)) {
323
    this->Target->AddIncludeDirectory(this->DirInclude, true);
324 325
  }

326
  // Acquire rcc executable and features
327
  if (this->Rcc.Enabled) {
328 329
    if (!GetRccExecutable()) {
      return false;
330 331 332
    }
  }

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

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

516
  // Process qrc files
517
  if (!this->Rcc.Qrcs.empty()) {
518
    const bool QtV5 = (this->QtVersionMajor == "5");
519 520 521
    // Target rcc options
    std::vector<std::string> optionsTarget;
    cmSystemTools::ExpandListArgument(
522
      this->Target->GetSafeProperty("AUTORCC_OPTIONS"), optionsTarget);
523

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

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

559 560
          qrc.InfoFile = base;
          qrc.InfoFile += "Info.cmake";
561

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

593 594
      std::vector<std::string> ccOutput;
      ccOutput.push_back(qrc.RccFile);
595

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

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

          cmTarget* autoRccTarget = makefile->AddUtilityCommand(
637 638 639
            ccName, cmMakefile::TargetOrigin::Generator, true,
            this->DirWork.c_str(), ccOutput, ccDepends, commandLines, false,
            ccComment.c_str());
640 641 642 643 644
          // Create autogen generator target
          localGen->AddGeneratorTarget(
            new cmGeneratorTarget(autoRccTarget, localGen));

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

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

684
  // Create _autogen target
685
  if (this->Moc.Enabled || this->Uic.Enabled) {
686 687 688
    // Add user defined autogen target dependencies
    {
      std::string const deps =
689
        this->Target->GetSafeProperty("AUTOGEN_TARGET_DEPENDS");
690 691 692 693 694 695 696 697 698 699 700
      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);
          }
701
        }
702 703 704
      }
    }

705 706 707
    // Compose target comment
    std::string autogenComment;
    {
708
      std::string tools;
709
      if (this->Moc.Enabled) {
710
        tools += "MOC";
711
      }
712
      if (this->Uic.Enabled) {
713 714
        if (!tools.empty()) {
          tools += " and ";
715
        }
716
        tools += "UIC";
717
      }
718 719 720 721
      autogenComment = "Automatic ";
      autogenComment += tools;
      autogenComment += " for target ";
      autogenComment += this->Target->GetName();
722
    }
723 724 725 726 727 728 729 730

    // Compose command lines
    cmCustomCommandLines commandLines;
    {
      cmCustomCommandLine currentLine;
      currentLine.push_back(cmSystemTools::GetCMakeCommand());
      currentLine.push_back("-E");
      currentLine.push_back("cmake_autogen");
731
      currentLine.push_back(this->AutogenInfoFile);
732 733
      currentLine.push_back("$<CONFIGURATION>");
      commandLines.push_back(std::move(currentLine));
734 735
    }

736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
    // 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) {
756
        this->Target->Target->AddUtility(depTarget->GetName(), makefile);
757
      }
758

759 760 761 762 763 764 765 766 767
      // 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(),
768
                         this->DirWork.c_str());
769 770
      cc.SetEscapeOldStyle(false);
      cc.SetEscapeAllowMakeVars(true);
771
      this->Target->Target->AddPreBuildCommand(cc);
772
    } else {
773

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

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

      // Forward origin utilities to autogen target
813
      for (std::string const& depName : this->Target->Target->GetUtilities()) {
814
        autogenTarget->AddUtility(depName, makefile);
815
      }
816 817 818
      // Add additional autogen target dependencies to autogen target
      for (cmTarget* depTarget : autogenDependTargets) {
        autogenTarget->AddUtility(depTarget->GetName(), makefile);
819
      }
820 821

      // Set FOLDER property in autogen target
822 823
      if (!this->AutogenFolder.empty()) {
        autogenTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
824
      }
825

826
      // Add autogen target to the origin target dependencies
827
      this->Target->Target->Ad