cmQtAutoGenInitializer.cxx 47.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 "cmQtAutoGen.h"
4
#include "cmQtAutoGenInitializer.h"
5

6
#include "cmAlgorithms.h"
7
#include "cmCustomCommand.h"
8
#include "cmCustomCommandLines.h"
9
#include "cmDuration.h"
10
#include "cmFilePathChecksum.h"
11
#include "cmGeneratorTarget.h"
12
#include "cmGlobalGenerator.h"
13
#include "cmLinkItem.h"
14
15
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
16
#include "cmOutputConverter.h"
17
#include "cmPolicies.h"
18
#include "cmProcessOutput.h"
19
#include "cmSourceFile.h"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
20
#include "cmSourceGroup.h"
21
#include "cmState.h"
22
#include "cmStateTypes.h"
23
24
#include "cmSystemTools.h"
#include "cmTarget.h"
25
#include "cm_sys_stat.h"
26
#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
inline static std::string GetSafeProperty(cmGeneratorTarget const* target,
                                          const char* key)
47
{
48
  return std::string(SafeString(target->GetProperty(key)));
49
50
}

51
52
53
54
55
56
inline static std::string GetSafeProperty(cmSourceFile const* sf,
                                          const char* key)
{
  return std::string(SafeString(sf->GetProperty(key)));
}

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
cmQtAutoGenInitializer::cmQtAutoGenInitializer(
194
195
196
197
198
199
  cmGeneratorTarget* target, bool mocEnabled, bool uicEnabled, bool rccEnabled,
  std::string const& qtVersionMajor)
  : Target(target)
  , MocEnabled(mocEnabled)
  , UicEnabled(uicEnabled)
  , RccEnabled(rccEnabled)
200
  , MultiConfig(false)
201
  , QtVersionMajor(qtVersionMajor)
202
{
203
204
  this->QtVersionMinor =
    cmQtAutoGenInitializer::GetQtMinorVersion(target, this->QtVersionMajor);
205
}
206

207
void cmQtAutoGenInitializer::InitCustomTargets()
208
209
210
211
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
  cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();
212

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

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

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

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

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

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

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

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

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

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

308
  // Add autogen includes directory to the origin target INCLUDE_DIRECTORIES
309
310
311
312
313
  if (this->MocEnabled || this->UicEnabled ||
      (this->RccEnabled && this->MultiConfig)) {
    std::string includeDir = this->DirBuild;
    includeDir += "/include";
    if (this->MultiConfig) {
314
      includeDir += "_$<CONFIG>";
315
    }
316
    this->Target->AddIncludeDirectory(includeDir, true);
317
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
  // 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,
359
        cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto);
360
361
362
363
364
365
366
367
368
      if (result && retVal == 0 &&
          rccStdOut.find("--list") != std::string::npos) {
        this->RccListOptions.push_back("--list");
      } else {
        this->RccListOptions.push_back("-list");
      }
    }
  }

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

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

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

560
    // Check if file name is unique
561
562
563
564
565
    for (Qrc& qrc : this->Qrcs) {
      qrc.Unique = true;
      for (Qrc const& qrc2 : this->Qrcs) {
        if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
          qrc.Unique = false;
566
567
          break;
        }
568
      }
569
    }
570
    // Path checksum and file names
571
    {
572
      cmFilePathChecksum const fpathCheckSum(makefile);
573
574
      for (Qrc& qrc : this->Qrcs) {
        qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile);
575
        // RCC output file name
576
        {
577
578
          std::string rccFile = this->DirBuild + "/";
          rccFile += qrc.PathChecksum;
579
          rccFile += "/qrc_";
580
          rccFile += qrc.QrcName;
581
          rccFile += ".cpp";
582
          qrc.RccFile = std::move(rccFile);
583
584
        }
        {
585
          std::string base = this->DirInfo;
586
          base += "/RCC";
587
588
589
          base += qrc.QrcName;
          if (!qrc.Unique) {
            base += qrc.PathChecksum;
590
          }
591
592
593
          qrc.InfoFile = base;
          qrc.InfoFile += "Info.cmake";
          qrc.SettingsFile = base;
594
          qrc.SettingsFile += "Settings.txt";
595
        }
596
597
598
      }
    }
    // RCC options
599
    for (Qrc& qrc : this->Qrcs) {
600
601
602
      // Target options
      std::vector<std::string> opts = optionsTarget;
      // Merge computed "-name XYZ" option
603
      {
604
        std::string name = qrc.QrcName;
605
606
        // Replace '-' with '_'. The former is not valid for symbol names.
        std::replace(name.begin(), name.end(), '-', '_');
607
        if (!qrc.Unique) {
608
          name += "_";
609
          name += qrc.PathChecksum;
610
        }
611
612
613
        std::vector<std::string> nameOpts;
        nameOpts.emplace_back("-name");
        nameOpts.emplace_back(std::move(name));
614
        RccMergeOptions(opts, nameOpts, QtV5);
615
      }
616
      // Merge file option
617
      RccMergeOptions(opts, qrc.Options, QtV5);
618
      qrc.Options = std::move(opts);
619
    }
620
    for (Qrc& qrc : this->Qrcs) {
621
      // Register file at target
622
      this->AddGeneratedSource(qrc.RccFile, GeneratorT::RCC);
623

624
625
      std::vector<std::string> ccOutput;
      ccOutput.push_back(qrc.RccFile);
626
      cmCustomCommandLines commandLines;
627
      {
628
629
630
631
        cmCustomCommandLine currentLine;
        currentLine.push_back(cmSystemTools::GetCMakeCommand());
        currentLine.push_back("-E");
        currentLine.push_back("cmake_autorcc");
632
        currentLine.push_back(qrc.InfoFile);
633
634
        currentLine.push_back("$<CONFIGURATION>");
        commandLines.push_back(std::move(currentLine));
635
      }
636
      std::string ccComment = "Automatic RCC for ";
637
      ccComment += FileProjectRelativePath(makefile, qrc.QrcFile);
638

639
      if (qrc.Generated) {
640
641
642
        // Create custom rcc target
        std::string ccName;
        {
643
          ccName = this->Target->GetName();
644
          ccName += "_arcc_";
645
646
          ccName += qrc.QrcName;
          if (!qrc.Unique) {
647
            ccName += "_";
648
            ccName += qrc.PathChecksum;
649
650
          }
          std::vector<std::string> ccDepends;
651
          // Add the .qrc and info file to the custom target dependencies
652
          ccDepends.push_back(qrc.QrcFile);
653
          ccDepends.push_back(qrc.InfoFile);
654
655

          cmTarget* autoRccTarget = makefile->AddUtilityCommand(
656
657
658
            ccName, cmMakefile::TargetOrigin::Generator, true,
            this->DirWork.c_str(), ccOutput, ccDepends, commandLines, false,
            ccComment.c_str());
659
660
661
662
663
          // Create autogen generator target
          localGen->AddGeneratorTarget(
            new cmGeneratorTarget(autoRccTarget, localGen));

          // Set FOLDER property in autogen target
664
665
          if (!this->AutogenFolder.empty()) {
            autoRccTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
666
667
668
          }
        }
        // Add autogen target to the origin target dependencies
669
        this->Target->Target->AddUtility(ccName, makefile);
670
      } else {
671
        // Create custom rcc command
672
        {
673
674
          std::vector<std::string> ccByproducts;
          std::vector<std::string> ccDepends;
675
          // Add the .qrc and info file to the custom command dependencies
676
          ccDepends.push_back(qrc.QrcFile);
677
          ccDepends.push_back(qrc.InfoFile);
678
679
680
681

          // Add the resource files to the dependencies
          {
            std::string error;
682
            if (RccListInputs(qrc.QrcFile, qrc.Resources, error)) {
683
              for (std::string const& fileName : qrc.Resources) {
684
685
686
687
688
                // Add resource file to the custom command dependencies
                ccDepends.push_back(fileName);
              }
            } else {
              cmSystemTools::Error(error.c_str());
689
            }
690
          }
691
692
693
          makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends,
                                             /*main_dependency*/ std::string(),
                                             commandLines, ccComment.c_str(),
694
                                             this->DirWork.c_str());
695
        }
696
        // Reconfigure when .qrc file changes
697
        makefile->AddCMakeDependFile(qrc.QrcFile);
698
699
700
701
      }
    }
  }

702
  // Create _autogen target
703
  if (this->MocEnabled || this->UicEnabled) {
704
705
706
    // Add user defined autogen target dependencies
    {
      std::string const deps =
707
        GetSafeProperty(this->Target, "AUTOGEN_TARGET_DEPENDS");
708
709
710
711
712
713
714
715
716
717
718
      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);
          }
719
        }
720
721
722
      }
    }

723
724
725
    // Compose target comment
    std::string autogenComment;
    {
726
727
728
      std::string tools;
      if (this->MocEnabled) {
        tools += "MOC";
729
      }
730
731
732
      if (this->UicEnabled) {
        if (!tools.empty()) {
          tools += " and ";
733
        }
734
        tools += "UIC";
735
      }
736
737
738
739
      autogenComment = "Automatic ";
      autogenComment += tools;
      autogenComment += " for target ";
      autogenComment += this->Target->GetName();
740
    }
741
742
743
744
745
746
747
748

    // Compose command lines
    cmCustomCommandLines commandLines;
    {
      cmCustomCommandLine currentLine;
      currentLine.push_back(cmSystemTools::GetCMakeCommand());
      currentLine.push_back("-E");
      currentLine.push_back("cmake_autogen");
749
      currentLine.push_back(this->AutogenInfoFile);
750
751
      currentLine.push_back("$<CONFIGURATION>");
      commandLines.push_back(std::move(currentLine));
752
753
    }

754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
    // 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) {
774
        this->Target->Target->AddUtility(depTarget->GetName(), makefile);
775
      }
776

777
778
779
780
781
782
783
784
785
      // 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(),
786
                         this->DirWork.c_str());
787
788
      cc.SetEscapeOldStyle(false);
      cc.SetEscapeAllowMakeVars(true);
789
      this->Target->Target->AddPreBuildCommand(cc);
790
    } else {
791

792
793
      // Add link library target dependencies to the autogen target
      // dependencies
794
795
796
797
798
799
800
801
802
803
804
805
806
807
      {
        // 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]++;
808
              }
809
810
811
            }
          }
        }
812
813
814
815
816
        for (auto const& item : commonTargets) {
          if (item.second == this->ConfigsList.size()) {
            autogenDependTargets.insert(item.first->Target);
          }
        }
817
818
      }

819
820
      // Create autogen target
      cmTarget* autogenTarget = makefile->AddUtilityCommand(
821
        this->AutogenTargetName, cmMakefile::TargetOrigin::Generator, true,
822
823
824
        this->DirWork.c_str(), /*byproducts=*/autogenProvides,
        std::vector<std::string>(autogenDependFiles.begin(),
                                 autogenDependFiles.end()),
825
        commandLines, false, autogenComment.c_str());
826
827
828
829
830
      // Create autogen generator target
      localGen->AddGeneratorTarget(
        new cmGeneratorTarget(autogenTarget, localGen));

      // Forward origin utilities to autogen target
831
      for (std::string const& depName : this->Target->Target->GetUtilities()) {
832
        autogenTarget->AddUtility(depName, makefile);
833
      }
834
835
836
      // Add additional autogen target dependencies to autogen target
      for (cmTarget* depTarget : autogenDependTargets) {
        autogenTarget->AddUtility(depTarget->GetName(), makefile);
837
      }
838
839

      // Set FOLDER property in autogen target
840
841
      if (!this->AutogenFolder.empty()) {
        autogenTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
842
      }
843

844
      // Add autogen target to the origin target dependencies
845
      this->Target->Target->AddUtility(this->AutogenTargetName, makefile);
846
    }
847
  }
848
849
}

850
void cmQtAutoGenInitializer::SetupCustomTargets()
851
{
852
  cmMakefile* makefile = this->Target->Target->GetMakefile();
853

854
  // Create info directory on demand
855
  if (!cmSystemTools::MakeDirectory(this->DirInfo)) {
856
    std::string emsg = ("Could not create directory: ");
857
    emsg += Quoted(this->DirInfo);
858
859
860
    cmSystemTools::Error(emsg.c_str());
  }

861
862
863
864
865
866
867
868
869
870
871
  // Configuration include directories
  std::string includeDir = "include";
  std::map<std::string, std::string> includeDirs;
  for (std::string const& cfg : this->ConfigsList) {
    std::string& dir = includeDirs[cfg];
    dir = "include_";
    dir += cfg;
  }

  auto OpenInfoFile = [](cmsys::ofstream& ofs,
                         std::string const& fileName) -> bool {
872
    // Ensure we have write permission
873
874
    if (cmSystemTools::FileExists(fileName)) {
      mode_t perm = 0;
875
#if defined(_WIN32) && !defined(__CYGWIN__)
876
      mode_t mode_write = S_IWRITE;
877
#else
878
      mode_t mode_write = S_IWUSR;
879
#endif
880
881
882
883
      cmSystemTools::GetPermissions(fileName, perm);
      if (!(perm & mode_write)) {
        cmSystemTools::SetPermissions(fileName, perm | mode_write);
      }
884
    }
885

886
887
    ofs.open(fileName.c_str(),
             (std::ios::out | std::ios::binary | std::ios::trunc));
888
889
890
    if (!ofs) {
      // File open error
      std::string error = "Internal CMake error when trying to open file: ";
891
      error += Quoted(fileName);
892
893
894
895
      error += " for writing.";
      cmSystemTools::Error(error.c_str());
    }
    return static_cast<bool>(ofs);
896
897
898
  };

  // Generate autogen target info file
899
  if (this->MocEnabled || this->UicEnabled) {
900
901
902
903
904
905
906
907
908
909
910
911
    if (this->MocEnabled) {
      this->SetupCustomTargetsMoc();
    }
    if (this->UicEnabled) {
      this->SetupCustomTargetsUic();
    }

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

914
    cmsys::ofstream ofs;
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
    if (OpenInfoFile(ofs, this->AutogenInfoFile)) {
      // Utility lambdas
      auto CWrite = [&ofs](const char* key, std::string const& value) {
        ofs << "set(" << key << " " << cmOutputConverter::EscapeForCMake(value)
            << ")\n";
      };
      auto CWriteList = [&CWrite](const char* key,
                                  std::vector<std::string> const& list) {
        CWrite(key, cmJoin(list, ";"));
      };
      auto CWriteNestedLists = [&CWrite](
        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 += cmJoin(list, ";");