cmQtAutoGenInitializer.cxx 52.2 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 "cmGeneratorTarget.h"
13
#include "cmGlobalGenerator.h"
14
#include "cmLinkItem.h"
15
#include "cmListFileCache.h"
16 17
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
18
#include "cmOutputConverter.h"
19
#include "cmPolicies.h"
20
#include "cmProcessOutput.h"
21
#include "cmSourceFile.h"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
22
#include "cmSourceGroup.h"
23
#include "cmState.h"
24
#include "cmStateTypes.h"
25 26 27
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmake.h"
28
#include "cmsys/FStream.hxx"
29
#include "cmsys/SystemInformation.hxx"
30

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

42 43 44
std::string GetQtExecutableTargetName(
  const cmQtAutoGen::IntegerVersion& qtVersion, std::string const& executable)
{
Tobias Hunger's avatar
Tobias Hunger committed
45 46 47
  if (qtVersion.Major == 6) {
    return ("Qt6::" + executable);
  }
48 49 50 51 52 53 54 55 56
  if (qtVersion.Major == 5) {
    return ("Qt5::" + executable);
  }
  if (qtVersion.Major == 4) {
    return ("Qt4::" + executable);
  }
  return ("");
}

57 58 59 60 61 62 63 64 65 66 67 68 69 70
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;
}

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

123
static void AddCleanFile(cmMakefile* makefile, std::string const& fileName)
124 125 126 127 128
{
  makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", fileName.c_str(),
                           false);
}

129 130 131 132 133 134
static std::string FileProjectRelativePath(cmMakefile* makefile,
                                           std::string const& fileName)
{
  std::string res;
  {
    std::string pSource = cmSystemTools::RelativePath(
135
      makefile->GetCurrentSourceDirectory(), fileName);
136
    std::string pBinary = cmSystemTools::RelativePath(
137
      makefile->GetCurrentBinaryDirectory(), fileName);
138 139 140 141 142 143 144 145 146 147 148
    if (pSource.size() < pBinary.size()) {
      res = std::move(pSource);
    } else if (pBinary.size() < fileName.size()) {
      res = std::move(pBinary);
    } else {
      res = fileName;
    }
  }
  return res;
}

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
/* @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;
}

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 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
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));
};

290 291 292 293 294 295
cmQtAutoGenInitializer::cmQtAutoGenInitializer(
  cmQtAutoGenGlobalInitializer* globalInitializer, cmGeneratorTarget* target,
  IntegerVersion const& qtVersion, bool mocEnabled, bool uicEnabled,
  bool rccEnabled, bool globalAutogenTarget, bool globalAutoRccTarget)
  : GlobalInitializer(globalInitializer)
  , Target(target)
296
  , QtVersion(qtVersion)
297
{
298
  AutogenTarget.GlobalTarget = globalAutogenTarget;
299 300 301
  Moc.Enabled = mocEnabled;
  Uic.Enabled = uicEnabled;
  Rcc.Enabled = rccEnabled;
302
  Rcc.GlobalTarget = globalAutoRccTarget;
303
}
304

305
bool cmQtAutoGenInitializer::InitCustomTargets()
306 307 308 309
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
  cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();
310

311 312 313 314 315 316 317
  // Configurations
  this->MultiConfig = globalGen->IsMultiConfig();
  this->ConfigDefault = makefile->GetConfigurations(this->ConfigsList);
  if (this->ConfigsList.empty()) {
    this->ConfigsList.push_back(this->ConfigDefault);
  }

318
  // Verbosity
319 320 321 322 323
  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
324
      this->Verbosity = cmSystemTools::IsOn(this->Verbosity) ? "1" : "0";
325 326 327
    }
  }

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
  // 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;
    }
343 344
  }

345
  // Common directories
346 347 348
  {
    // Collapsed current binary directory
    std::string const cbd = cmSystemTools::CollapseFullPath(
349
      std::string(), makefile->GetCurrentBinaryDirectory());
350

351 352
    // Info directory
    this->Dir.Info = cbd;
353
    this->Dir.Info += cmake::GetCMakeFilesDirectory();
354 355 356 357 358 359 360 361 362 363 364 365 366
    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";
367
    }
368 369 370
    cmSystemTools::ConvertToUnixSlashes(this->Dir.Build);
    // Cleanup build directory
    AddCleanFile(makefile, this->Dir.Build);
371 372

    // Working directory
373 374
    this->Dir.Work = cbd;
    cmSystemTools::ConvertToUnixSlashes(this->Dir.Work);
375 376

    // Include directory
377 378
    this->Dir.Include = this->Dir.Build;
    this->Dir.Include += "/include";
379
    if (this->MultiConfig) {
380
      this->Dir.Include += "_$<CONFIG>";
381
    }
382
    // Per config include directories
383 384
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
385 386
        std::string& dir = this->Dir.ConfigInclude[cfg];
        dir = this->Dir.Build;
387 388 389 390
        dir += "/include_";
        dir += cfg;
      }
    }
391
  }
392

393
  // Moc, Uic and _autogen target settings
394 395 396 397 398
  if (this->Moc.Enabled || this->Uic.Enabled) {
    // Init moc specific settings
    if (this->Moc.Enabled && !InitMoc()) {
      return false;
    }
399

400 401 402 403 404
    // Init uic specific settings
    if (this->Uic.Enabled && !InitUic()) {
      return false;
    }

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
    // 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);
      }
    }

438 439
    // Autogen target: Compute user defined dependencies
    {
440 441 442
      this->AutogenTarget.DependOrigin =
        this->Target->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS");

443 444 445 446 447 448 449 450 451
      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) {
452
            this->AutogenTarget.DependTargets.insert(depTarget);
453
          } else {
454
            this->AutogenTarget.DependFiles.insert(depName);
455 456 457 458 459
          }
        }
      }
    }
  }
460

461 462 463 464 465 466 467 468
  // 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)) {
469
    this->Target->AddIncludeDirectory(this->Dir.Include, true);
470 471 472 473 474 475 476 477 478 479 480
  }

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

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

482 483 484 485 486 487 488 489 490 491 492 493 494 495
  // 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
496
  this->Moc.MocsCompilation = this->Dir.Build;
497 498 499 500
  this->Moc.MocsCompilation += "/mocs_compilation.cpp";

  // Moc predefs command
  if (this->Target->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
501
      (this->QtVersion >= IntegerVersion(5, 8))) {
502 503 504 505 506
    this->Moc.PredefsCmd =
      makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND");
  }

  // Moc includes
507
  {
508 509
    // We need to disable this until we have all implicit includes available.
    // See issue #18669.
Tobias Hunger's avatar
Tobias Hunger committed
510
    // bool const appendImplicit = (this->QtVersion.Major >= 5);
511

512
    auto GetIncludeDirs =
513 514
      [this, localGen](std::string const& cfg) -> std::vector<std::string> {
      bool const appendImplicit = false;
515 516 517 518
      // 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;
519 520
      localGen->GetIncludeDirectories(dirs, this->Target, "CXX", cfg, false,
                                      appendImplicit);
521
      return dirs;
522 523 524 525 526
    };

    // Default configuration include directories
    this->Moc.Includes = GetIncludeDirs(this->ConfigDefault);
    // Other configuration settings
527
    if (this->MultiConfig) {
528
      for (std::string const& cfg : this->ConfigsList) {
529
        std::vector<std::string> dirs = GetIncludeDirs(cfg);
530 531 532
        if (dirs != this->Moc.Includes) {
          this->Moc.ConfigIncludes[cfg] = std::move(dirs);
        }
533
      }
534
    }
535 536
  }

537 538 539
  // Moc compile definitions
  {
    auto GetCompileDefinitions =
540
      [this, localGen](std::string const& cfg) -> std::set<std::string> {
541
      std::set<std::string> defines;
542
      localGen->GetTargetDefines(this->Target, cfg, "CXX", defines);
543 544 545 546 547 548
#ifdef _WIN32
      if (this->Moc.PredefsCmd.empty()) {
        // Add WIN32 definition if we don't have a moc_predefs.h
        defines.insert("WIN32");
      }
#endif
549
      return defines;
550 551 552 553 554 555 556
    };

    // Default configuration defines
    this->Moc.Defines = GetCompileDefinitions(this->ConfigDefault);
    // Other configuration defines
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
557
        std::set<std::string> defines = GetCompileDefinitions(cfg);
558 559 560 561 562
        if (defines != this->Moc.Defines) {
          this->Moc.ConfigDefines[cfg] = std::move(defines);
        }
      }
    }
563
  }
564 565

  // Moc executable
566
  return GetMocExecutable();
567 568 569 570 571 572 573 574 575 576 577 578
}

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);
579
      std::string const& srcDir = makefile->GetCurrentSourceDirectory();
580 581 582 583
      for (std::string& path : this->Uic.SearchPaths) {
        path = cmSystemTools::CollapseFullPath(path, srcDir);
      }
    }
584
  }
585 586
  // Uic target options
  {
587 588
    auto UicGetOpts =
      [this](std::string const& cfg) -> std::vector<std::string> {
589 590
      std::vector<std::string> opts;
      this->Target->GetAutoUicOptions(opts, cfg);
591
      return opts;
592
    };
593

594 595 596 597 598 599
    // Default settings
    this->Uic.Options = UicGetOpts(this->ConfigDefault);

    // Configuration specific settings
    if (this->MultiConfig) {
      for (std::string const& cfg : this->ConfigsList) {
600
        std::vector<std::string> options = UicGetOpts(cfg);
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
        if (options != this->Uic.Options) {
          this->Uic.ConfigOptions[cfg] = std::move(options);
        }
      }
    }
  }
  // .ui files skip and options
  {
    std::string const uiExt = "ui";
    std::string pathError;
    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).
      std::string const& fPath = sf->GetFullPath(&pathError);
      if (!pathError.empty()) {
        pathError.clear();
        continue;
      }
      if (sf->GetExtension() == uiExt) {
        std::string const absFile = cmSystemTools::GetRealPath(fPath);
        // Check if the .ui file should be skipped
        if (sf->GetPropertyAsBool("SKIP_AUTOUIC") ||
            sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
          this->Uic.Skip.insert(absFile);
        }
        // Check if the .ui file has uic options
        std::string const uicOpts = sf->GetSafeProperty("AUTOUIC_OPTIONS");
        if (!uicOpts.empty()) {
          // Check if file isn't skipped
          if (this->Uic.Skip.count(absFile) == 0) {
            this->Uic.FileFiles.push_back(absFile);
            std::vector<std::string> optsVec;
            cmSystemTools::ExpandListArgument(uicOpts, optsVec);
            this->Uic.FileOptions.push_back(std::move(optsVec));
          }
        }
      }
639 640 641
    }
  }

642
  // Uic executable
643
  return GetUicExecutable();
644 645 646 647
}

bool cmQtAutoGenInitializer::InitRcc()
{
648
  return GetRccExecutable();
649 650 651 652 653
}

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

655
  // Scan through target files
656
  {
657
    std::string const qrcExt = "qrc";
658
    std::vector<cmSourceFile*> srcFiles;
659
    this->Target->GetConfigCommonSourceFiles(srcFiles);
660
    for (cmSourceFile* sf : srcFiles) {
661 662 663 664
      if (sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
        continue;
      }
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
665 666
      std::string const& fPath = sf->GetFullPath();
      std::string const& ext = sf->GetExtension();
667
      // Register generated files that will be scanned by moc or uic
668
      if (this->Moc.Enabled || this->Uic.Enabled) {
669
        cmSystemTools::FileFormat const fileType =
670 671 672
          cmSystemTools::GetFileFormat(ext.c_str());
        if ((fileType == cmSystemTools::CXX_FILE_FORMAT) ||
            (fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
673
          std::string const absPath = cmSystemTools::GetRealPath(fPath);
674 675
          if ((this->Moc.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) ||
              (this->Uic.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) {
676 677 678 679
            // Register source
            const bool generated = sf->GetPropertyAsBool("GENERATED");
            if (fileType == cmSystemTools::HEADER_FILE_FORMAT) {
              if (generated) {
680
                this->AutogenTarget.HeadersGenerated.push_back(absPath);
681
              } else {
682
                this->AutogenTarget.Headers.push_back(absPath);
683 684 685
              }
            } else {
              if (generated) {
686
                this->AutogenTarget.SourcesGenerated.push_back(absPath);
687
              } else {
688
                this->AutogenTarget.Sources.push_back(absPath);
689
              }
690 691 692
            }
          }
        }
693 694
      }
      // Register rcc enabled files
695
      if (this->Rcc.Enabled && (ext == qrcExt) &&
696
          !sf->GetPropertyAsBool("SKIP_AUTORCC")) {
697 698
        // Register qrc file
        {
699 700 701 702 703
          Qrc qrc;
          qrc.QrcFile = cmSystemTools::GetRealPath(fPath);
          qrc.QrcName =
            cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
          qrc.Generated = sf->GetPropertyAsBool("GENERATED");
704 705
          // RCC options
          {
706
            std::string const opts = sf->GetSafeProperty("AUTORCC_OPTIONS");
707
            if (!opts.empty()) {
708
              cmSystemTools::ExpandListArgument(opts, qrc.Options);
709 710
            }
          }
711
          this->Rcc.Qrcs.push_back(std::move(qrc));
712
        }
713 714
      }
    }
715
  }
716 717 718 719 720 721
  // 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();

722
  if (this->Moc.Enabled || this->Uic.Enabled) {
723 724 725 726 727 728 729 730 731 732 733
    // Read skip files from makefile sources
    {
      std::string pathError;
      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).
        std::string const& fPath = sf->GetFullPath(&pathError);
        if (!pathError.empty()) {
          pathError.clear();
          continue;
734
        }
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
        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->Moc.Enabled &&
          (skipAll || sf->GetPropertyAsBool("SKIP_AUTOMOC"));
        const bool uicSkip = this->Uic.Enabled &&
          (skipAll || sf->GetPropertyAsBool("SKIP_AUTOUIC"));
        if (mocSkip || uicSkip) {
          std::string const absFile = cmSystemTools::GetRealPath(fPath);
          if (mocSkip) {
            this->Moc.Skip.insert(absFile);
          }
          if (uicSkip) {
            this->Uic.Skip.insert(absFile);
          }
754 755 756
        }
      }
    }
757

758
    // Process GENERATED sources and headers
759 760
    if (!this->AutogenTarget.SourcesGenerated.empty() ||
        !this->AutogenTarget.HeadersGenerated.empty()) {
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
      // 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;
779
      }
780 781 782

      if (policyAccept) {
        // Accept GENERATED sources
783 784 785 786
        for (std::string const& absFile :
             this->AutogenTarget.HeadersGenerated) {
          this->AutogenTarget.Headers.push_back(absFile);
          this->AutogenTarget.DependFiles.insert(absFile);
787
        }
788 789 790 791
        for (std::string const& absFile :
             this->AutogenTarget.SourcesGenerated) {
          this->AutogenTarget.Sources.push_back(absFile);
          this->AutogenTarget.DependFiles.insert(absFile);
792
        }
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
      } 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";
812 813
          for (const std::string& absFile :
               this->AutogenTarget.HeadersGenerated) {
814 815
            msg.append("  ").append(Quoted(absFile)).append("\n");
          }
816 817
          for (const std::string& absFile :
               this->AutogenTarget.SourcesGenerated) {
818 819 820 821 822 823 824 825 826 827 828 829 830
            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";
          makefile->IssueMessage(cmake::AUTHOR_WARNING, msg);
831
        }
832
      }
833
    }
834 835
    // Sort headers and sources
    if (this->Moc.Enabled || this->Uic.Enabled) {
836 837 838 839
      std::sort(this->AutogenTarget.Headers.begin(),
                this->AutogenTarget.Headers.end());
      std::sort(this->AutogenTarget.Sources.begin(),
                this->AutogenTarget.Sources.end());
840
    }
841
  }
842

843
  // Process qrc files
844
  if (!this->Rcc.Qrcs.empty()) {
Tobias Hunger's avatar
Tobias Hunger committed
845
    const bool modernQt = (this->QtVersion.Major >= 5);
846 847 848
    // Target rcc options
    std::vector<std::string> optionsTarget;
    cmSystemTools::ExpandListArgument(
849
      this->Target->GetSafeProperty("AUTORCC_OPTIONS"), optionsTarget);
850

851
    // Check if file name is unique
852
    for (Qrc& qrc : this->Rcc.Qrcs) {
853
      qrc.Unique = true;
854
      for (Qrc const& qrc2 : this->Rcc.Qrcs) {
855 856
        if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
          qrc.Unique = false;
857 858
          break;
        }
859
      }
860
    }
861
    // Path checksum and file names
862
    {
863
      cmFilePathChecksum const fpathCheckSum(makefile);
864
      for (Qrc& qrc : this->Rcc.Qrcs) {
865
        qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile);
866
        // RCC output file name
867
        {
868
          std::string rccFile = this->Dir.Build + "/";
869
          rccFile += qrc.PathChecksum;
870
          rccFile += "/qrc_";
871
          rccFile += qrc.QrcName;
872
          rccFile += ".cpp";
873
          qrc.RccFile = std::move(rccFile);
874 875
        }
        {
876
          std::string base = this->Dir.Info;
877
          base += "/RCC";
878 879 880
          base += qrc.QrcName;
          if (!qrc.Unique) {
            base += qrc.PathChecksum;
881
          }
882 883 884 885

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

886 887
          qrc.InfoFile = base;
          qrc.InfoFile += "Info.cmake";
888

889
          qrc.SettingsFile = base;
890
          qrc.SettingsFile += "Settings.txt";
891 892 893 894 895 896 897

          if (this->MultiConfig) {
            for (std::string const& cfg : this->ConfigsList) {
              qrc.ConfigSettingsFile[cfg] =
                AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg);
            }
          }
898
        }
899 900 901
      }
    }
    // RCC options
902
    for (Qrc& qrc : this->Rcc.Qrcs) {
903 904 905
      // Target options
      std::vector<std::string> opts = optionsTarget;
      // Merge computed "-name XYZ" option
906
      {
907
        std::string name = qrc.QrcName;
908 909
        // Replace '-' with '_'. The former is not valid for symbol names.
        std::replace(name.begin(), name.end(), '-', '_');
910
        if (!qrc.Unique) {
911
          name += "_";
912
          name += qrc.PathChecksum;
913
        }
914 915 916
        std::vector<std::string> nameOpts;
        nameOpts.emplace_back("-name");
        nameOpts.emplace_back(std::move(name));
Tobias Hunger's avatar
Tobias Hunger committed
917
        RccMergeOptions(opts, nameOpts, modernQt);
918
      }
919
      // Merge file option
Tobias Hunger's avatar
Tobias Hunger committed
920
      RccMergeOptions(opts, qrc.Options, modernQt);
921
      qrc.Options = std::move(opts);
922
    }
923
    // RCC resources
924
    for (Qrc& qrc : this->Rcc.Qrcs) {
925 926 927 928 929
      if (!qrc.Generated) {
        std::string error;
        if (!RccListInputs(qrc.QrcFile, qrc.Resources, error)) {
          cmSystemTools::Error(error.c_str());
          return false;
930
        }
931
      }
932 933
    }
  }
934

935 936
  return true;
}
937

938 939 940 941 942 943 944
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
945
  makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
946 947 948 949 950 951

  // Files provided by the autogen target
  std::vector<std::string> autogenProvides;
  if (this->Moc.Enabled) {
    this->AddGeneratedSource(this->Moc.MocsCompilation, GeneratorT::MOC);
    autogenProvides.push_back(this->Moc.MocsCompilation);
952 953
  }

954 955 956 957 958 959
  // Compose target comment
  std::string autogenComment;
  {
    std::string tools;
    if (this->Moc.Enabled) {
      tools += "MOC";
960
    }
961 962 963
    if (this->Uic.Enabled) {
      if (!tools.empty()) {
        tools += " and ";
964
      }
965
      tools += "UIC";
966
    }
967 968 969 970 971
    autogenComment = "Automatic ";
    autogenComment += tools;
    autogenComment += " for target ";
    autogenComment += this->Target->GetName();
  }
972

973 974 975 976 977 978 979
  // Compose command lines
  cmCustomCommandLines commandLines;
  {
    cmCustomCommandLine currentLine;
    currentLine.push_back(cmSystemTools::GetCMakeCommand());
    currentLine.push_back("-E");
    currentLine.push_back("cmake_autogen");
980
    currentLine.push_back(this->AutogenTarget.InfoFile);
981 982 983
    currentLine.push_back("$<CONFIGURATION>");
    commandLines.push_back(std::move(currentLine));
  }
984

985 986 987 988 989 990 991 992 993 994 995 996
  // 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
997
    if (!this->AutogenTarget.DependFiles.empty()) {