cmQtAutoGenInitializer.cxx 52.7 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
#include "cmQtAutoGenGlobalInitializer.h"
6

7
#include "cmAlgorithms.h"
8
#include "cmCustomCommand.h"
9
#include "cmCustomCommandLines.h"
10
#include "cmDuration.h"
11
#include "cmFilePathChecksum.h"
12
#include "cmGeneratorExpression.h"
13
#include "cmGeneratorTarget.h"
14
#include "cmGlobalGenerator.h"
15
#include "cmLinkItem.h"
16
#include "cmListFileCache.h"
17 18
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
19
#include "cmMessageType.h"
20
#include "cmOutputConverter.h"
21
#include "cmPolicies.h"
22
#include "cmProcessOutput.h"
23
#include "cmSourceFile.h"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
24
#include "cmSourceGroup.h"
25
#include "cmState.h"
26
#include "cmStateTypes.h"
27 28
#include "cmSystemTools.h"
#include "cmTarget.h"
29
#include "cmsys/FStream.hxx"
30
#include "cmsys/SystemInformation.hxx"
31

32
#include <algorithm>
33
#include <array>
34
#include <deque>
35 36
#include <map>
#include <set>
37
#include <sstream>
38 39 40 41
#include <string>
#include <utility>
#include <vector>

42 43 44 45 46 47 48 49 50 51 52 53 54 55
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;
}

56
static void AddCleanFile(cmMakefile* makefile, std::string const& fileName)
57 58 59 60 61
{
  makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", fileName.c_str(),
                           false);
}

62 63 64 65 66 67
static std::string FileProjectRelativePath(cmMakefile* makefile,
                                           std::string const& fileName)
{
  std::string res;
  {
    std::string pSource = cmSystemTools::RelativePath(
68
      makefile->GetCurrentSourceDirectory(), fileName);
69
    std::string pBinary = cmSystemTools::RelativePath(
70
      makefile->GetCurrentBinaryDirectory(), fileName);
71 72 73 74 75 76 77 78 79 80 81
    if (pSource.size() < pBinary.size()) {
      res = std::move(pSource);
    } else if (pBinary.size() < fileName.size()) {
      res = std::move(pBinary);
    } else {
      res = fileName;
    }
  }
  return res;
}

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
/* @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;
}

126 127 128 129 130 131 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 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
cmQtAutoGenInitializer::InfoWriter::InfoWriter(std::string const& filename)
{
  Ofs_.SetCopyIfDifferent(true);
  Ofs_.Open(filename, false, true);
}

template <class IT>
std::string cmQtAutoGenInitializer::InfoWriter::ListJoin(IT it_begin,
                                                         IT it_end)
{
  std::string res;
  for (IT it = it_begin; it != it_end; ++it) {
    if (it != it_begin) {
      res += ';';
    }
    for (const char* c = it->c_str(); *c; ++c) {
      if (*c == '"') {
        // Escape the double quote to avoid ending the argument.
        res += "\\\"";
      } else if (*c == '$') {
        // Escape the dollar to avoid expanding variables.
        res += "\\$";
      } else if (*c == '\\') {
        // Escape the backslash to avoid other escapes.
        res += "\\\\";
      } else if (*c == ';') {
        // Escape the semicolon to avoid list expansion.
        res += "\\;";
      } else {
        // Other characters will be parsed correctly.
        res += *c;
      }
    }
  }
  return res;
}

std::string cmQtAutoGenInitializer::InfoWriter::ConfigKey(
  const char* key, std::string const& config)
{
  std::string ckey = key;
  ckey += '_';
  ckey += config;
  return ckey;
}

void cmQtAutoGenInitializer::InfoWriter::Write(const char* key,
                                               std::string const& value)
{
  Ofs_ << "set(" << key << " " << cmOutputConverter::EscapeForCMake(value)
       << ")\n";
};

void cmQtAutoGenInitializer::InfoWriter::WriteUInt(const char* key,
                                                   unsigned int value)
{
  Ofs_ << "set(" << key << " " << value << ")\n";
};

template <class C>
void cmQtAutoGenInitializer::InfoWriter::WriteStrings(const char* key,
                                                      C const& container)
{
  Ofs_ << "set(" << key << " \""
       << ListJoin(container.begin(), container.end()) << "\")\n";
}

void cmQtAutoGenInitializer::InfoWriter::WriteConfig(
  const char* key, std::map<std::string, std::string> const& map)
{
  for (auto const& item : map) {
    Write(ConfigKey(key, item.first).c_str(), item.second);
  }
};

template <class C>
void cmQtAutoGenInitializer::InfoWriter::WriteConfigStrings(
  const char* key, std::map<std::string, C> const& map)
{
  for (auto const& item : map) {
    WriteStrings(ConfigKey(key, item.first).c_str(), item.second);
  }
}

void cmQtAutoGenInitializer::InfoWriter::WriteNestedLists(
  const char* key, std::vector<std::vector<std::string>> const& lists)
{
  std::vector<std::string> seplist;
  for (const std::vector<std::string>& list : lists) {
    std::string blist = "{";
    blist += ListJoin(list.begin(), list.end());
    blist += "}";
    seplist.push_back(std::move(blist));
  }
  Write(key, cmJoin(seplist, cmQtAutoGen::ListSep));
};

223 224 225 226 227 228
cmQtAutoGenInitializer::cmQtAutoGenInitializer(
  cmQtAutoGenGlobalInitializer* globalInitializer, cmGeneratorTarget* target,
  IntegerVersion const& qtVersion, bool mocEnabled, bool uicEnabled,
  bool rccEnabled, bool globalAutogenTarget, bool globalAutoRccTarget)
  : GlobalInitializer(globalInitializer)
  , Target(target)
229
  , QtVersion(qtVersion)
230
{
231
  AutogenTarget.GlobalTarget = globalAutogenTarget;
232 233 234
  Moc.Enabled = mocEnabled;
  Uic.Enabled = uicEnabled;
  Rcc.Enabled = rccEnabled;
235
  Rcc.GlobalTarget = globalAutoRccTarget;
236
}
237

238
bool cmQtAutoGenInitializer::InitCustomTargets()
239 240 241 242
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
  cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();
243

244 245 246 247 248 249 250
  // Configurations
  this->MultiConfig = globalGen->IsMultiConfig();
  this->ConfigDefault = makefile->GetConfigurations(this->ConfigsList);
  if (this->ConfigsList.empty()) {
    this->ConfigsList.push_back(this->ConfigDefault);
  }

251
  // Verbosity
252 253 254 255 256
  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
257
      this->Verbosity = cmSystemTools::IsOn(this->Verbosity) ? "1" : "0";
258 259 260
    }
  }

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
  // Targets FOLDER
  {
    const char* folder =
      makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
    if (folder == nullptr) {
      folder =
        makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER");
    }
    // Inherit FOLDER property from target (#13688)
    if (folder == nullptr) {
      folder = this->Target->GetProperty("FOLDER");
    }
    if (folder != nullptr) {
      this->TargetsFolder = folder;
    }
276 277
  }

278
  // Common directories
279 280 281
  {
    // Collapsed current binary directory
    std::string const cbd = cmSystemTools::CollapseFullPath(
282
      std::string(), makefile->GetCurrentBinaryDirectory());
283

284 285
    // Info directory
    this->Dir.Info = cbd;
286
    this->Dir.Info += "/CMakeFiles";
287 288 289 290 291 292 293 294 295 296 297 298 299
    this->Dir.Info += '/';
    this->Dir.Info += this->Target->GetName();
    this->Dir.Info += "_autogen";
    this->Dir.Info += ".dir";
    cmSystemTools::ConvertToUnixSlashes(this->Dir.Info);

    // Build directory
    this->Dir.Build = this->Target->GetSafeProperty("AUTOGEN_BUILD_DIR");
    if (this->Dir.Build.empty()) {
      this->Dir.Build = cbd;
      this->Dir.Build += '/';
      this->Dir.Build += this->Target->GetName();
      this->Dir.Build += "_autogen";
300
    }
301 302 303
    cmSystemTools::ConvertToUnixSlashes(this->Dir.Build);
    // Cleanup build directory
    AddCleanFile(makefile, this->Dir.Build);
304 305

    // Working directory
306 307
    this->Dir.Work = cbd;
    cmSystemTools::ConvertToUnixSlashes(this->Dir.Work);
308 309

    // Include directory
310 311
    this->Dir.Include = this->Dir.Build;
    this->Dir.Include += "/include";
312
    if (this->MultiConfig) {
313
      this->Dir.Include += "_$<CONFIG>";
314
    }
315
    // Per config include directories
316 317
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
318 319
        std::string& dir = this->Dir.ConfigInclude[cfg];
        dir = this->Dir.Build;
320 321 322 323
        dir += "/include_";
        dir += cfg;
      }
    }
324
  }
325

326
  // Moc, Uic and _autogen target settings
327 328 329 330 331
  if (this->Moc.Enabled || this->Uic.Enabled) {
    // Init moc specific settings
    if (this->Moc.Enabled && !InitMoc()) {
      return false;
    }
332

333
    // Init uic specific settings
334 335
    if (this->Uic.Enabled && !InitUic()) {
      return false;
336 337
    }

338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
    // Autogen target name
    this->AutogenTarget.Name = this->Target->GetName();
    this->AutogenTarget.Name += "_autogen";

    // Autogen target parallel processing
    this->AutogenTarget.Parallel =
      this->Target->GetSafeProperty("AUTOGEN_PARALLEL");
    if (this->AutogenTarget.Parallel.empty() ||
        (this->AutogenTarget.Parallel == "AUTO")) {
      // Autodetect number of CPUs
      this->AutogenTarget.Parallel = std::to_string(GetParallelCPUCount());
    }

    // Autogen target info and settings files
    {
      this->AutogenTarget.InfoFile = this->Dir.Info;
      this->AutogenTarget.InfoFile += "/AutogenInfo.cmake";

      this->AutogenTarget.SettingsFile = this->Dir.Info;
      this->AutogenTarget.SettingsFile += "/AutogenOldSettings.txt";

      if (this->MultiConfig) {
        for (std::string const& cfg : this->ConfigsList) {
          std::string& filename = this->AutogenTarget.ConfigSettingsFile[cfg];
          filename =
            AppendFilenameSuffix(this->AutogenTarget.SettingsFile, "_" + cfg);
          AddCleanFile(makefile, filename);
        }
      } else {
        AddCleanFile(makefile, this->AutogenTarget.SettingsFile);
      }
    }

371 372
    // Autogen target: Compute user defined dependencies
    {
373 374 375
      this->AutogenTarget.DependOrigin =
        this->Target->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS");

376 377 378 379 380 381 382 383 384
      std::string const deps =
        this->Target->GetSafeProperty("AUTOGEN_TARGET_DEPENDS");
      if (!deps.empty()) {
        std::vector<std::string> extraDeps;
        cmSystemTools::ExpandListArgument(deps, extraDeps);
        for (std::string const& depName : extraDeps) {
          // Allow target and file dependencies
          auto* depTarget = makefile->FindTargetToUse(depName);
          if (depTarget != nullptr) {
385
            this->AutogenTarget.DependTargets.insert(depTarget);
386
          } else {
387
            this->AutogenTarget.DependFiles.insert(depName);
388 389 390 391 392
          }
        }
      }
    }
  }
393

394 395 396 397 398 399 400 401
  // Init rcc specific settings
  if (this->Rcc.Enabled && !InitRcc()) {
    return false;
  }

  // Add autogen include directory to the origin target INCLUDE_DIRECTORIES
  if (this->Moc.Enabled || this->Uic.Enabled ||
      (this->Rcc.Enabled && this->MultiConfig)) {
402
    this->Target->AddIncludeDirectory(this->Dir.Include, true);
403 404 405 406 407 408 409 410 411 412 413
  }

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

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

415 416 417 418 419 420 421 422 423 424 425 426 427 428
  // Create rcc targets
  if (this->Rcc.Enabled && !this->InitRccTargets()) {
    return false;
  }

  return true;
}

bool cmQtAutoGenInitializer::InitMoc()
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();

  // Mocs compilation file
429
  this->Moc.MocsCompilation = this->Dir.Build;
430 431 432 433
  this->Moc.MocsCompilation += "/mocs_compilation.cpp";

  // Moc predefs command
  if (this->Target->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
434
      (this->QtVersion >= IntegerVersion(5, 8))) {
435 436 437 438 439
    this->Moc.PredefsCmd =
      makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND");
  }

  // Moc includes
440
  {
441
    bool const appendImplicit = (this->QtVersion.Major >= 5);
442
    auto GetIncludeDirs =
443 444
      [this, localGen,
       appendImplicit](std::string const& cfg) -> std::vector<std::string> {
445 446 447 448
      // Get the include dirs for this target, without stripping the implicit
      // include dirs off, see
      // https://gitlab.kitware.com/cmake/cmake/issues/13667
      std::vector<std::string> dirs;
449 450
      localGen->GetIncludeDirectoriesImplicit(dirs, this->Target, "CXX", cfg,
                                              false, appendImplicit);
451
      return dirs;
452 453 454 455 456
    };

    // Default configuration include directories
    this->Moc.Includes = GetIncludeDirs(this->ConfigDefault);
    // Other configuration settings
457
    if (this->MultiConfig) {
458
      for (std::string const& cfg : this->ConfigsList) {
459
        std::vector<std::string> dirs = GetIncludeDirs(cfg);
460 461 462
        if (dirs != this->Moc.Includes) {
          this->Moc.ConfigIncludes[cfg] = std::move(dirs);
        }
463
      }
464
    }
465 466
  }

467 468 469
  // Moc compile definitions
  {
    auto GetCompileDefinitions =
470
      [this, localGen](std::string const& cfg) -> std::set<std::string> {
471
      std::set<std::string> defines;
472
      localGen->GetTargetDefines(this->Target, cfg, "CXX", defines);
473 474 475 476 477 478
#ifdef _WIN32
      if (this->Moc.PredefsCmd.empty()) {
        // Add WIN32 definition if we don't have a moc_predefs.h
        defines.insert("WIN32");
      }
#endif
479
      return defines;
480 481 482 483 484 485 486
    };

    // Default configuration defines
    this->Moc.Defines = GetCompileDefinitions(this->ConfigDefault);
    // Other configuration defines
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
487
        std::set<std::string> defines = GetCompileDefinitions(cfg);
488 489 490 491 492
        if (defines != this->Moc.Defines) {
          this->Moc.ConfigDefines[cfg] = std::move(defines);
        }
      }
    }
493
  }
494 495

  // Moc executable
496 497 498 499 500 501 502 503 504 505 506 507
  {
    if (!this->GetQtExecutable(this->Moc, "moc", false, nullptr)) {
      return false;
    }
    // Let the _autogen target depend on the moc executable
    if (this->Moc.ExecutableTarget != nullptr) {
      this->AutogenTarget.DependTargets.insert(
        this->Moc.ExecutableTarget->Target);
    }
  }

  return true;
508 509 510 511 512 513 514 515 516 517 518 519
}

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

  // Uic search paths
  {
    std::string const usp =
      this->Target->GetSafeProperty("AUTOUIC_SEARCH_PATHS");
    if (!usp.empty()) {
      cmSystemTools::ExpandListArgument(usp, this->Uic.SearchPaths);
520
      std::string const& srcDir = makefile->GetCurrentSourceDirectory();
521 522 523 524
      for (std::string& path : this->Uic.SearchPaths) {
        path = cmSystemTools::CollapseFullPath(path, srcDir);
      }
    }
525
  }
526 527
  // Uic target options
  {
528 529
    auto UicGetOpts =
      [this](std::string const& cfg) -> std::vector<std::string> {
530 531
      std::vector<std::string> opts;
      this->Target->GetAutoUicOptions(opts, cfg);
532
      return opts;
533
    };
534

535 536 537 538 539 540
    // Default settings
    this->Uic.Options = UicGetOpts(this->ConfigDefault);

    // Configuration specific settings
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
541
        std::vector<std::string> options = UicGetOpts(cfg);
542 543 544 545 546 547
        if (options != this->Uic.Options) {
          this->Uic.ConfigOptions[cfg] = std::move(options);
        }
      }
    }
  }
548

549
  // Uic executable
550 551 552 553 554 555 556 557 558 559 560 561
  {
    if (!this->GetQtExecutable(this->Uic, "uic", true, nullptr)) {
      return false;
    }
    // Let the _autogen target depend on the uic executable
    if (this->Uic.ExecutableTarget != nullptr) {
      this->AutogenTarget.DependTargets.insert(
        this->Uic.ExecutableTarget->Target);
    }
  }

  return true;
562 563 564 565
}

bool cmQtAutoGenInitializer::InitRcc()
{
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
  // Rcc executable
  {
    std::string stdOut;
    if (!this->GetQtExecutable(this->Rcc, "rcc", false, &stdOut)) {
      return false;
    }
    // Evaluate test output
    if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) {
      if (stdOut.find("--list") != std::string::npos) {
        this->Rcc.ListOptions.emplace_back("--list");
      } else {
        this->Rcc.ListOptions.emplace_back("-list");
      }
    }
  }

  return true;
583 584 585 586 587
}

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

589 590 591 592 593
  // String constants
  std::string const SKIP_AUTOGEN_str = "SKIP_AUTOGEN";
  std::string const SKIP_AUTOMOC_str = "SKIP_AUTOMOC";
  std::string const SKIP_AUTOUIC_str = "SKIP_AUTOUIC";

594
  // Scan through target files
595
  {
596 597 598 599 600 601
    // String constants
    std::string const qrc_str = "qrc";
    std::string const SKIP_AUTORCC_str = "SKIP_AUTORCC";
    std::string const AUTORCC_OPTIONS_str = "AUTORCC_OPTIONS";

    // Scan through target files
602
    std::vector<cmSourceFile*> srcFiles;
603
    this->Target->GetConfigCommonSourceFiles(srcFiles);
604
    for (cmSourceFile* sf : srcFiles) {
605
      if (sf->GetPropertyAsBool(SKIP_AUTOGEN_str)) {
606 607
        continue;
      }
608

609
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
610 611
      std::string const& fPath = sf->GetFullPath();
      std::string const& ext = sf->GetExtension();
612

613
      // Register generated files that will be scanned by moc or uic
614
      if (this->Moc.Enabled || this->Uic.Enabled) {
615
        cmSystemTools::FileFormat const fileType =
616
          cmSystemTools::GetFileFormat(ext);
617 618
        if ((fileType == cmSystemTools::CXX_FILE_FORMAT) ||
            (fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
619
          std::string const absPath = cmSystemTools::GetRealPath(fPath);
620 621 622 623
          if ((this->Moc.Enabled &&
               !sf->GetPropertyAsBool(SKIP_AUTOMOC_str)) ||
              (this->Uic.Enabled &&
               !sf->GetPropertyAsBool(SKIP_AUTOUIC_str))) {
624
            // Register source
625
            const bool generated = sf->GetIsGenerated();
626 627
            if (fileType == cmSystemTools::HEADER_FILE_FORMAT) {
              if (generated) {
628
                this->AutogenTarget.HeadersGenerated.push_back(absPath);
629
              } else {
630
                this->AutogenTarget.Headers.push_back(absPath);
631 632 633
              }
            } else {
              if (generated) {
634
                this->AutogenTarget.SourcesGenerated.push_back(absPath);
635
              } else {
636
                this->AutogenTarget.Sources.push_back(absPath);
637
              }
638 639 640
            }
          }
        }
641 642
      }
      // Register rcc enabled files
643 644 645
      if (this->Rcc.Enabled) {
        if ((ext == qrc_str) && !sf->GetPropertyAsBool(SKIP_AUTORCC_str)) {
          // Register qrc file
646 647 648 649
          Qrc qrc;
          qrc.QrcFile = cmSystemTools::GetRealPath(fPath);
          qrc.QrcName =
            cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
650
          qrc.Generated = sf->GetIsGenerated();
651 652
          // RCC options
          {
653
            std::string const opts = sf->GetSafeProperty(AUTORCC_OPTIONS_str);
654
            if (!opts.empty()) {
655
              cmSystemTools::ExpandListArgument(opts, qrc.Options);
656 657
            }
          }
658
          this->Rcc.Qrcs.push_back(std::move(qrc));
659
        }
660 661
      }
    }
662
  }
663 664 665 666 667 668
  // cmGeneratorTarget::GetConfigCommonSourceFiles computes the target's
  // sources meta data cache. Clear it so that OBJECT library targets that
  // are AUTOGEN initialized after this target get their added
  // mocs_compilation.cpp source acknowledged by this target.
  this->Target->ClearSourcesCache();

669 670 671 672
  // Scan through all source files in the makefile to extract moc and uic
  // parameters.  Historically we support non target source file parameters.
  // The reason is that their file names might be discovered from source files
  // at generation time.
673
  if (this->Moc.Enabled || this->Uic.Enabled) {
674 675 676 677 678 679 680 681
    // String constants
    std::string const ui_str = "ui";
    std::string const AUTOUIC_OPTIONS_str = "AUTOUIC_OPTIONS";

    for (cmSourceFile* sf : makefile->GetSourceFiles()) {
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
      // Since we're iterating over source files that might be not in the
      // target we need to check for path errors (not existing files).
682
      std::string pathError;
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706
      std::string const& fullPath = sf->GetFullPath(&pathError);
      if (!pathError.empty() || fullPath.empty()) {
        continue;
      }

      // Check file type
      auto const fileType = cmSystemTools::GetFileFormat(sf->GetExtension());
      bool const isSource = (fileType == cmSystemTools::CXX_FILE_FORMAT) ||
        (fileType == cmSystemTools::HEADER_FILE_FORMAT);
      bool const isUi = (this->Moc.Enabled && sf->GetExtension() == ui_str);

      // Process only certain file types
      if (isSource || isUi) {
        std::string const absFile = cmSystemTools::GetRealPath(fullPath);
        // Acquire file properties
        bool const skipAUTOGEN = sf->GetPropertyAsBool(SKIP_AUTOGEN_str);
        bool const skipMoc = (this->Moc.Enabled && isSource) &&
          (skipAUTOGEN || sf->GetPropertyAsBool(SKIP_AUTOMOC_str));
        bool const skipUic = this->Uic.Enabled &&
          (skipAUTOGEN || sf->GetPropertyAsBool(SKIP_AUTOUIC_str));

        // Register moc and uic skipped file
        if (skipMoc) {
          this->Moc.Skip.insert(absFile);
707
        }
708 709
        if (skipUic) {
          this->Uic.Skip.insert(absFile);
710
        }
711 712 713 714 715 716 717 718 719

        // Check if the .ui file has uic options
        if (isUi && !skipUic) {
          std::string const uicOpts = sf->GetSafeProperty(AUTOUIC_OPTIONS_str);
          if (!uicOpts.empty()) {
            this->Uic.FileFiles.push_back(absFile);
            std::vector<std::string> optsVec;
            cmSystemTools::ExpandListArgument(uicOpts, optsVec);
            this->Uic.FileOptions.push_back(std::move(optsVec));
720
          }
721 722 723
        }
      }
    }
724
  }
725

726 727
  // Process GENERATED sources and headers
  if (this->Moc.Enabled || this->Uic.Enabled) {
728 729
    if (!this->AutogenTarget.SourcesGenerated.empty() ||
        !this->AutogenTarget.HeadersGenerated.empty()) {
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
      // Check status of policy CMP0071
      bool policyAccept = false;
      bool policyWarn = false;
      cmPolicies::PolicyStatus const CMP0071_status =
        makefile->GetPolicyStatus(cmPolicies::CMP0071);
      switch (CMP0071_status) {
        case cmPolicies::WARN:
          policyWarn = true;
          CM_FALLTHROUGH;
        case cmPolicies::OLD:
          // Ignore GENERATED file
          break;
        case cmPolicies::REQUIRED_IF_USED:
        case cmPolicies::REQUIRED_ALWAYS:
        case cmPolicies::NEW:
          // Process GENERATED file
          policyAccept = true;
          break;
748
      }
749 750 751

      if (policyAccept) {
        // Accept GENERATED sources
752 753 754 755
        for (std::string const& absFile :
             this->AutogenTarget.HeadersGenerated) {
          this->AutogenTarget.Headers.push_back(absFile);
          this->AutogenTarget.DependFiles.insert(absFile);
756
        }
757 758 759 760
        for (std::string const& absFile :
             this->AutogenTarget.SourcesGenerated) {
          this->AutogenTarget.Sources.push_back(absFile);
          this->AutogenTarget.DependFiles.insert(absFile);
761
        }
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
      } else {
        if (policyWarn) {
          std::string msg;
          msg += cmPolicies::GetPolicyWarning(cmPolicies::CMP0071);
          msg += "\n";
          std::string tools;
          std::string property;
          if (this->Moc.Enabled && this->Uic.Enabled) {
            tools = "AUTOMOC and AUTOUIC";
            property = "SKIP_AUTOGEN";
          } else if (this->Moc.Enabled) {
            tools = "AUTOMOC";
            property = "SKIP_AUTOMOC";
          } else if (this->Uic.Enabled) {
            tools = "AUTOUIC";
            property = "SKIP_AUTOUIC";
          }
          msg += "For compatibility, CMake is excluding the GENERATED source "
                 "file(s):\n";
781 782
          for (const std::string& absFile :
               this->AutogenTarget.HeadersGenerated) {
783 784
            msg.append("  ").append(Quoted(absFile)).append("\n");
          }
785 786
          for (const std::string& absFile :
               this->AutogenTarget.SourcesGenerated) {
787 788 789 790 791 792 793 794 795 796 797 798
            msg.append("  ").append(Quoted(absFile)).append("\n");
          }
          msg += "from processing by ";
          msg += tools;
          msg +=
            ". If any of the files should be processed, set CMP0071 to NEW. "
            "If any of the files should not be processed, "
            "explicitly exclude them by setting the source file property ";
          msg += property;
          msg += ":\n  set_property(SOURCE file.h PROPERTY ";
          msg += property;
          msg += " ON)\n";
799
          makefile->IssueMessage(MessageType::AUTHOR_WARNING, msg);
800
        }
801
      }
802
    }
803 804 805 806 807 808 809 810
  }

  // Sort headers and sources
  if (this->Moc.Enabled || this->Uic.Enabled) {
    std::sort(this->AutogenTarget.Headers.begin(),
              this->AutogenTarget.Headers.end());
    std::sort(this->AutogenTarget.Sources.begin(),
              this->AutogenTarget.Sources.end());
811
  }
812

813
  // Process qrc files
814
  if (!this->Rcc.Qrcs.empty()) {
Tobias Hunger's avatar
Tobias Hunger committed
815
    const bool modernQt = (this->QtVersion.Major >= 5);
816 817 818
    // Target rcc options
    std::vector<std::string> optionsTarget;
    cmSystemTools::ExpandListArgument(
819
      this->Target->GetSafeProperty("AUTORCC_OPTIONS"), optionsTarget);
820

821
    // Check if file name is unique
822
    for (Qrc& qrc : this->Rcc.Qrcs) {
823
      qrc.Unique = true;
824
      for (Qrc const& qrc2 : this->Rcc.Qrcs) {
825 826
        if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
          qrc.Unique = false;
827 828
          break;
        }
829
      }
830
    }
831
    // Path checksum and file names
832
    {
833
      cmFilePathChecksum const fpathCheckSum(makefile);
834
      for (Qrc& qrc : this->Rcc.Qrcs) {
835
        qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile);
836
        // RCC output file name
837
        {
838
          std::string rccFile = this->Dir.Build + "/";
839
          rccFile += qrc.PathChecksum;
840
          rccFile += "/qrc_";
841
          rccFile += qrc.QrcName;
842
          rccFile += ".cpp";
843
          qrc.RccFile = std::move(rccFile);
844 845
        }
        {
846
          std::string base = this->Dir.Info;
847
          base += "/RCC";
848 849 850
          base += qrc.QrcName;
          if (!qrc.Unique) {
            base += qrc.PathChecksum;
851
          }
852 853 854 855

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

856 857
          qrc.InfoFile = base;
          qrc.InfoFile += "Info.cmake";
858

859
          qrc.SettingsFile = base;
860
          qrc.SettingsFile += "Settings.txt";
861 862 863 864 865 866 867

          if (this->MultiConfig) {
            for (std::string const& cfg : this->ConfigsList) {
              qrc.ConfigSettingsFile[cfg] =
                AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg);
            }
          }
868
        }
869 870 871
      }
    }
    // RCC options
872
    for (Qrc& qrc : this->Rcc.Qrcs) {
873 874 875
      // Target options
      std::vector<std::string> opts = optionsTarget;
      // Merge computed "-name XYZ" option
876
      {
877
        std::string name = qrc.QrcName;
878 879
        // Replace '-' with '_'. The former is not valid for symbol names.
        std::replace(name.begin(), name.end(), '-', '_');
880
        if (!qrc.Unique) {
881
          name += "_";
882
          name += qrc.PathChecksum;
883
        }
884 885 886
        std::vector<std::string> nameOpts;
        nameOpts.emplace_back("-name");
        nameOpts.emplace_back(std::move(name));
Tobias Hunger's avatar
Tobias Hunger committed
887
        RccMergeOptions(opts, nameOpts, modernQt);
888
      }
889
      // Merge file option
Tobias Hunger's avatar
Tobias Hunger committed
890
      RccMergeOptions(opts, qrc.Options, modernQt);
891
      qrc.Options = std::move(opts);
892
    }
893
    // RCC resources
894
    for (Qrc& qrc : this->Rcc.Qrcs) {
895 896 897
      if (!qrc.Generated) {
        std::string error;
        if (!RccListInputs(qrc.QrcFile, qrc.Resources, error)) {
898
          cmSystemTools::Error(error);
899
          return false;
900
        }
901
      }
902 903
    }
  }
904

905 906
  return true;
}
907

908 909 910 911 912 913 914
bool cmQtAutoGenInitializer::InitAutogenTarget()
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
  cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();

  // Register info file as generated by CMake
915
  makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
916 917 918 919

  // Files provided by the autogen target
  std::vector<std::string> autogenProvides;
  if (this->Moc.Enabled) {
920
    this->AddGeneratedSource(this->Moc.MocsCompilation, this->Moc, true);
921
    autogenProvides.push_back(this->Moc.MocsCompilation);
922 923
  }

924 925 926 927 928 929
  // Compose target comment
  std::string autogenComment;
  {
    std::string tools;
    if (this->Moc.Enabled) {
      tools += "MOC";
930
    }
931 932 933
    if (this->Uic.Enabled) {
      if (!tools.empty()) {
        tools += " and ";
934
      }
935
      tools += "UIC";
936
    }
937 938 939 940 941
    autogenComment = "Automatic ";
    autogenComment += tools;
    autogenComment += " for target ";
    autogenComment += this->Target->GetName();
  }
942

943 944 945 946 947 948 949
  // Compose command lines
  cmCustomCommandLines commandLines;
  {
    cmCustomCommandLine currentLine;
    currentLine.push_back(cmSystemTools::GetCMakeCommand());
    currentLine.push_back("-E");
    currentLine.push_back("cmake_autogen");
950
    currentLine.push_back(this->AutogenTarget.InfoFile);
951 952 953
    currentLine.push_back("$<CONFIGURATION>");
    commandLines.push_back(std::move(currentLine));
  }
954

955 956 957 958 959 960 961 962 963 964 965 966
  // 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
967
    if (!this->AutogenTarget.DependFiles.empty()) {
968
      usePRE_BUILD = false;
969
    }
970 971 972 973
    // Cannot use PRE_BUILD when a global autogen target is in place
    if (AutogenTarget.GlobalTarget) {
      usePRE_BUILD = false;
    }
974 975 976 977
  }
  // Create the autogen target/command
  if (usePRE_BUILD) {
    // Add additional autogen target dependencies to origin target
978
    for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
979
      this->Target->Target->AddUtility(depTarget->GetName(), makefile);
980
    }
981

982 983 984