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

6
#include "cmAlgorithms.h"
7
#include "cmCustomCommand.h"
8
#include "cmCustomCommandLines.h"
9
#include "cmDuration.h"
10
#include "cmFilePathChecksum.h"
11
#include "cmGeneratedFileStream.h"
12
#include "cmGeneratorTarget.h"
13
#include "cmGlobalGenerator.h"
14
#include "cmLinkItem.h"
15 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
void 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 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
  // Acquire rcc executable and features
  if (this->RccEnabled) {
    {
      std::string err;
      if (this->QtVersionMajor == "5") {
        cmGeneratorTarget* tgt =
          localGen->FindGeneratorTargetToUse("Qt5::rcc");
        if (tgt != nullptr) {
          this->RccExecutable = SafeString(tgt->ImportedGetLocation(""));
        } else {
          err = "AUTORCC: Qt5::rcc target not found";
        }
      } else if (QtVersionMajor == "4") {
        cmGeneratorTarget* tgt =
          localGen->FindGeneratorTargetToUse("Qt4::rcc");
        if (tgt != nullptr) {
          this->RccExecutable = SafeString(tgt->ImportedGetLocation(""));
        } else {
          err = "AUTORCC: Qt4::rcc target not found";
        }
      } else {
        err = "The AUTORCC feature supports only Qt 4 and Qt 5";
      }
      if (!err.empty()) {
        err += " (";
        err += this->Target->GetName();
        err += ")";
        cmSystemTools::Error(err.c_str());
      }
    }
    // Detect if rcc supports (-)-list
    if (!this->RccExecutable.empty() && (this->QtVersionMajor == "5")) {
      std::vector<std::string> command;
      command.push_back(this->RccExecutable);
      command.push_back("--help");
      std::string rccStdOut;
      std::string rccStdErr;
      int retVal = 0;
      bool result = cmSystemTools::RunSingleCommand(
        command, &rccStdOut, &rccStdErr, &retVal, nullptr,
360
        cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto);
361 362 363 364 365 366 367 368 369
      if (result && retVal == 0 &&
          rccStdOut.find("--list") != std::string::npos) {
        this->RccListOptions.push_back("--list");
      } else {
        this->RccListOptions.push_back("-list");
      }
    }
  }

370
  // Extract relevant source files
371 372
  std::vector<std::string> generatedSources;
  std::vector<std::string> generatedHeaders;
373
  {
374
    std::string const qrcExt = "qrc";
375
    std::vector<cmSourceFile*> srcFiles;
376
    this->Target->GetConfigCommonSourceFiles(srcFiles);
377
    for (cmSourceFile* sf : srcFiles) {
378 379 380 381
      if (sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
        continue;
      }
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
382 383
      std::string const& fPath = sf->GetFullPath();
      std::string const& ext = sf->GetExtension();
384
      // Register generated files that will be scanned by moc or uic
385
      if (this->MocEnabled || this->UicEnabled) {
386
        cmSystemTools::FileFormat const fileType =
387 388 389
          cmSystemTools::GetFileFormat(ext.c_str());
        if ((fileType == cmSystemTools::CXX_FILE_FORMAT) ||
            (fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
390
          std::string const absPath = cmSystemTools::GetRealPath(fPath);
391 392
          if ((this->MocEnabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) ||
              (this->UicEnabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) {
393 394 395 396
            // Register source
            const bool generated = sf->GetPropertyAsBool("GENERATED");
            if (fileType == cmSystemTools::HEADER_FILE_FORMAT) {
              if (generated) {
397
                generatedHeaders.push_back(absPath);
398
              } else {
399
                this->Headers.push_back(absPath);
400 401 402
              }
            } else {
              if (generated) {
403
                generatedSources.push_back(absPath);
404
              } else {
405
                this->Sources.push_back(absPath);
406
              }
407 408 409
            }
          }
        }
410 411
      }
      // Register rcc enabled files
412
      if (this->RccEnabled && (ext == qrcExt) &&
413
          !sf->GetPropertyAsBool("SKIP_AUTORCC")) {
414 415
        // Register qrc file
        {
416 417 418 419 420
          Qrc qrc;
          qrc.QrcFile = cmSystemTools::GetRealPath(fPath);
          qrc.QrcName =
            cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
          qrc.Generated = sf->GetPropertyAsBool("GENERATED");
421 422
          // RCC options
          {
423
            std::string const opts = sf->GetSafeProperty("AUTORCC_OPTIONS");
424
            if (!opts.empty()) {
425
              cmSystemTools::ExpandListArgument(opts, qrc.Options);
426 427
            }
          }
428
          this->Qrcs.push_back(std::move(qrc));
429
        }
430 431 432 433 434 435
      }
    }
    // 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.
436 437 438 439
    this->Target->ClearSourcesCache();
  }
  // Read skip files from makefile sources
  if (this->MocEnabled || this->UicEnabled) {
440 441
    std::string pathError;
    for (cmSourceFile* sf : makefile->GetSourceFiles()) {
442
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
443 444 445 446 447 448 449
      // 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;
      }
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
      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);
        }
      }
    }
471
  }
472

473
  // Process GENERATED sources and headers
474
  if (!generatedSources.empty() || !generatedHeaders.empty()) {
475 476 477
    // Check status of policy CMP0071
    bool policyAccept = false;
    bool policyWarn = false;
478
    cmPolicies::PolicyStatus const CMP0071_status =
479
      makefile->GetPolicyStatus(cmPolicies::CMP0071);
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
    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
497
      for (std::string const& absFile : generatedHeaders) {
498
        this->Headers.push_back(absFile);
499
        autogenDependFiles.insert(absFile);
500
      }
501
      for (std::string const& absFile : generatedSources) {
502
        this->Sources.push_back(absFile);
503
        autogenDependFiles.insert(absFile);
504
      }
505 506 507 508 509 510
    } else {
      if (policyWarn) {
        std::string msg;
        msg += cmPolicies::GetPolicyWarning(cmPolicies::CMP0071);
        msg += "\n";
        std::string tools;
511
        std::string property;
512
        if (this->MocEnabled && this->UicEnabled) {
513 514
          tools = "AUTOMOC and AUTOUIC";
          property = "SKIP_AUTOGEN";
515
        } else if (this->MocEnabled) {
516 517
          tools = "AUTOMOC";
          property = "SKIP_AUTOMOC";
518
        } else if (this->UicEnabled) {
519 520
          tools = "AUTOUIC";
          property = "SKIP_AUTOUIC";
521
        }
522 523 524
        msg += "For compatibility, CMake is excluding the GENERATED source "
               "file(s):\n";
        for (const std::string& absFile : generatedHeaders) {
525
          msg.append("  ").append(Quoted(absFile)).append("\n");
526
        }
527
        for (const std::string& absFile : generatedSources) {
528
          msg.append("  ").append(Quoted(absFile)).append("\n");
529
        }
530 531 532 533 534 535 536 537 538 539
        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";
540
        makefile->IssueMessage(cmake::AUTHOR_WARNING, msg);
541
      }
542
    }
543 544 545
    // Clear lists
    generatedSources.clear();
    generatedHeaders.clear();
546
  }
547
  // Sort headers and sources
548 549 550 551
  if (this->MocEnabled || this->UicEnabled) {
    std::sort(this->Headers.begin(), this->Headers.end());
    std::sort(this->Sources.begin(), this->Sources.end());
  }
552

553
  // Process qrc files
554 555
  if (!this->Qrcs.empty()) {
    const bool QtV5 = (this->QtVersionMajor == "5");
556 557 558
    // Target rcc options
    std::vector<std::string> optionsTarget;
    cmSystemTools::ExpandListArgument(
559
      this->Target->GetSafeProperty("AUTORCC_OPTIONS"), optionsTarget);
560

561
    // Check if file name is unique
562 563 564 565 566
    for (Qrc& qrc : this->Qrcs) {
      qrc.Unique = true;
      for (Qrc const& qrc2 : this->Qrcs) {
        if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
          qrc.Unique = false;
567 568
          break;
        }
569
      }
570
    }
571
    // Path checksum and file names
572
    {
573
      cmFilePathChecksum const fpathCheckSum(makefile);
574 575
      for (Qrc& qrc : this->Qrcs) {
        qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile);
576
        // RCC output file name
577
        {
578 579
          std::string rccFile = this->DirBuild + "/";
          rccFile += qrc.PathChecksum;
580
          rccFile += "/qrc_";
581
          rccFile += qrc.QrcName;
582
          rccFile += ".cpp";
583
          qrc.RccFile = std::move(rccFile);
584 585
        }
        {
586
          std::string base = this->DirInfo;
587
          base += "/RCC";
588 589 590
          base += qrc.QrcName;
          if (!qrc.Unique) {
            base += qrc.PathChecksum;
591
          }
592 593 594 595

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

596 597
          qrc.InfoFile = base;
          qrc.InfoFile += "Info.cmake";
598

599
          qrc.SettingsFile = base;
600
          qrc.SettingsFile += "Settings.txt";
601
        }
602 603 604
      }
    }
    // RCC options
605
    for (Qrc& qrc : this->Qrcs) {
606 607 608
      // Target options
      std::vector<std::string> opts = optionsTarget;
      // Merge computed "-name XYZ" option
609
      {
610
        std::string name = qrc.QrcName;
611 612
        // Replace '-' with '_'. The former is not valid for symbol names.
        std::replace(name.begin(), name.end(), '-', '_');
613
        if (!qrc.Unique) {
614
          name += "_";
615
          name += qrc.PathChecksum;
616
        }
617 618 619
        std::vector<std::string> nameOpts;
        nameOpts.emplace_back("-name");
        nameOpts.emplace_back(std::move(name));
620
        RccMergeOptions(opts, nameOpts, QtV5);
621
      }
622
      // Merge file option
623
      RccMergeOptions(opts, qrc.Options, QtV5);
624
      qrc.Options = std::move(opts);
625
    }
626
    for (Qrc& qrc : this->Qrcs) {
627
      // Register file at target
628
      this->AddGeneratedSource(qrc.RccFile, GeneratorT::RCC);
629

630 631
      std::vector<std::string> ccOutput;
      ccOutput.push_back(qrc.RccFile);
632

633
      cmCustomCommandLines commandLines;
634 635 636 637 638 639 640 641 642 643 644 645
      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 {
646 647 648 649
        cmCustomCommandLine currentLine;
        currentLine.push_back(cmSystemTools::GetCMakeCommand());
        currentLine.push_back("-E");
        currentLine.push_back("cmake_autorcc");
650
        currentLine.push_back(qrc.InfoFile);
651
        currentLine.push_back("$<CONFIG>");
652
        commandLines.push_back(std::move(currentLine));
653
      }
654
      std::string ccComment = "Automatic RCC for ";
655
      ccComment += FileProjectRelativePath(makefile, qrc.QrcFile);
656

657
      if (qrc.Generated) {
658 659 660
        // Create custom rcc target
        std::string ccName;
        {
661
          ccName = this->Target->GetName();
662
          ccName += "_arcc_";
663 664
          ccName += qrc.QrcName;
          if (!qrc.Unique) {
665
            ccName += "_";
666
            ccName += qrc.PathChecksum;
667 668
          }
          std::vector<std::string> ccDepends;
669
          // Add the .qrc and info file to the custom target dependencies
670
          ccDepends.push_back(qrc.QrcFile);
671
          ccDepends.push_back(qrc.InfoFile);
672 673

          cmTarget* autoRccTarget = makefile->AddUtilityCommand(
674 675 676
            ccName, cmMakefile::TargetOrigin::Generator, true,
            this->DirWork.c_str(), ccOutput, ccDepends, commandLines, false,
            ccComment.c_str());
677 678 679 680 681
          // Create autogen generator target
          localGen->AddGeneratorTarget(
            new cmGeneratorTarget(autoRccTarget, localGen));

          // Set FOLDER property in autogen target
682 683
          if (!this->AutogenFolder.empty()) {
            autoRccTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
684 685 686
          }
        }
        // Add autogen target to the origin target dependencies
687
        this->Target->Target->AddUtility(ccName, makefile);
688
      } else {
689
        // Create custom rcc command
690
        {
691 692
          std::vector<std::string> ccByproducts;
          std::vector<std::string> ccDepends;
693
          // Add the .qrc and info file to the custom command dependencies
694
          ccDepends.push_back(qrc.QrcFile);
695
          ccDepends.push_back(qrc.InfoFile);
696 697 698 699

          // Add the resource files to the dependencies
          {
            std::string error;
700
            if (RccListInputs(qrc.QrcFile, qrc.Resources, error)) {
701
              for (std::string const& fileName : qrc.Resources) {
702 703 704 705 706
                // Add resource file to the custom command dependencies
                ccDepends.push_back(fileName);
              }
            } else {
              cmSystemTools::Error(error.c_str());
707
            }
708
          }
709 710 711
          makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends,
                                             /*main_dependency*/ std::string(),
                                             commandLines, ccComment.c_str(),
712
                                             this->DirWork.c_str());
713
        }
714
        // Reconfigure when .qrc file changes
715
        makefile->AddCMakeDependFile(qrc.QrcFile);
716 717 718 719
      }
    }
  }

720
  // Create _autogen target
721
  if (this->MocEnabled || this->UicEnabled) {
722 723 724
    // Add user defined autogen target dependencies
    {
      std::string const deps =
725
        this->Target->GetSafeProperty("AUTOGEN_TARGET_DEPENDS");
726 727 728 729 730 731 732 733 734 735 736
      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);
          }
737
        }
738 739 740
      }
    }

741 742 743
    // Compose target comment
    std::string autogenComment;
    {
744 745 746
      std::string tools;
      if (this->MocEnabled) {
        tools += "MOC";
747
      }
748 749 750
      if (this->UicEnabled) {
        if (!tools.empty()) {
          tools += " and ";
751
        }
752
        tools += "UIC";
753
      }
754 755 756 757
      autogenComment = "Automatic ";
      autogenComment += tools;
      autogenComment += " for target ";
      autogenComment += this->Target->GetName();
758
    }
759 760 761 762 763 764 765 766

    // Compose command lines
    cmCustomCommandLines commandLines;
    {
      cmCustomCommandLine currentLine;
      currentLine.push_back(cmSystemTools::GetCMakeCommand());
      currentLine.push_back("-E");
      currentLine.push_back("cmake_autogen");
767
      currentLine.push_back(this->AutogenInfoFile);
768 769
      currentLine.push_back("$<CONFIGURATION>");
      commandLines.push_back(std::move(currentLine));
770 771
    }

772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
    // 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) {
792
        this->Target->Target->AddUtility(depTarget->GetName(), makefile);
793
      }
794

795 796 797 798 799 800 801 802 803
      // 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(),
804
                         this->DirWork.c_str());
805 806
      cc.SetEscapeOldStyle(false);
      cc.SetEscapeAllowMakeVars(true);
807
      this->Target->Target->AddPreBuildCommand(cc);
808
    } else {
809

810 811
      // Add link library target dependencies to the autogen target
      // dependencies
812 813 814 815 816 817 818 819 820 821 822 823 824 825
      {
        // 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]++;
826
              }
827 828 829
            }
          }
        }
830 831 832 833 834
        for (auto const& item : commonTargets) {
          if (item.second == this->ConfigsList.size()) {
            autogenDependTargets.insert(item.first->Target);
          }
        }
835 836
      }

837 838
      // Create autogen target
      cmTarget* autogenTarget = makefile->AddUtilityCommand(
839
        this->AutogenTargetName, cmMakefile::TargetOrigin::Generator, true,
840 841 842
        this->DirWork.c_str(), /*byproducts=*/autogenProvides,
        std::vector<std::string>(autogenDependFiles.begin(),
                                 autogenDependFiles.end()),
843
        commandLines, false, autogenComment.c_str());
844 845 846 847 848
      // Create autogen generator target
      localGen->AddGeneratorTarget(
        new cmGeneratorTarget(autogenTarget, localGen));

      // Forward origin utilities to autogen target
849
      for (std::string const& depName : this->Target->Target->GetUtilities()) {
850
        autogenTarget->AddUtility(depName, makefile);
851
      }
852 853 854
      // Add additional autogen target dependencies to autogen target
      for (cmTarget* depTarget : autogenDependTargets) {
        autogenTarget->AddUtility(depTarget->GetName(), makefile);
855
      }
856 857

      // Set FOLDER property in autogen target
858 859
      if (!this->AutogenFolder.empty()) {
        autogenTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
860
      }
861

862
      // Add autogen target to the origin target dependencies
863
      this->Target->Target->AddUtility(this->AutogenTargetName, makefile);
864
    }
865
  }
Stephen Kelly's avatar