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

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

40
inline static const char* SafeString(const char* value)
41
{
Daniel Pfeifer's avatar
Daniel Pfeifer committed
42
  return (value != nullptr) ? value : "";
43
44
}

45
46
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
594

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

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

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

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

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

          cmTarget* autoRccTarget = makefile->AddUtilityCommand(
661
662
663
            ccName, cmMakefile::TargetOrigin::Generator, true,
            this->DirWork.c_str(), ccOutput, ccDepends, commandLines, false,
            ccComment.c_str());
664
665
666
667
668
          // Create autogen generator target
          localGen->AddGeneratorTarget(
            new cmGeneratorTarget(autoRccTarget, localGen));

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

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

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

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

    // Compose command lines
    cmCustomCommandLines commandLines;
    {
      cmCustomCommandLine currentLine;
      currentLine.push_back(cmSystemTools::GetCMakeCommand());
      currentLine.push_back("-E");
      currentLine.push_back("cmake_autogen");
754
      currentLine.push_back(this->AutogenInfoFile);
755
756
      currentLine.push_back("$<CONFIGURATION>");
      commandLines.push_back(std::move(currentLine));
757
758
    }

759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
    // 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) {
779
        this->Target->Target->AddUtility(depTarget->GetName(), makefile);
780
      }
781

782
783
784
785
786
787
788
789
790
      // 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(),
791
                         this->DirWork.c_str());
792
793
      cc.SetEscapeOldStyle(false);
      cc.SetEscapeAllowMakeVars(true);
794
      this->Target->Target->AddPreBuildCommand(cc);
795
    } else {
796

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

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

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

      // Set FOLDER property in autogen target
845
846
      if (!this->AutogenFolder.empty()) {
        autogenTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
847
      }
848

849
      // Add autogen target to the origin target dependencies
850
      this->Target->Target->AddUtility(this->AutogenTargetName, makefile);
851
    }
852
  }
853
854
}

855
void cmQtAutoGenInitializer::SetupCustomTargets()
856
{
857
  cmMakefile* makefile = this->Target->Target->GetMakefile();
858

859
  // Create info directory on demand
860
  if (!cmSystemTools::MakeDirectory(this->DirInfo)) {
861
    std::string emsg = ("Could not create directory: ");
862
    emsg += Quoted(this->DirInfo);
863
864
865
    cmSystemTools::Error(emsg.c_str());
  }

866
867
868
869
870
871
872
873
874
  // 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;
  }

875
  // Generate autogen target info file
876
  if (this->MocEnabled || this->UicEnabled) {
877
878
879
880
881
882
883
884
885
886
887
888
    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());
889
    }
890

891
892
893
894
    cmGeneratedFileStream ofs;
    ofs.SetCopyIfDifferent(true);
    ofs.Open(this->AutogenInfoFile.c_str(), false, true);
    if (ofs) {
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
      // 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, ";");
          blist += "}";
          seplist.push_back(std::move(blist));
        }
        CWrite(key, cmJoin(seplist, cmQtAutoGen::ListSep));
      };
      auto CWriteSet = [&CWrite](const char* key,
                                 std::set<std::string> const& list) {
        CWrite(key, cmJoin(list, ";"));
      };
      auto CWriteMap = [&ofs](const char* key,
                              std::map<std::string, std::string> const& map) {
921
        for (auto const& item : map) {
922
923
924
925
          ofs << "set(" << key << "_" << item.first << " "
              << cmOutputConverter::EscapeForCMake(item.second) << ")\n";
        }
      };
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
      auto MfDef = [makefile](const char* key) {
        return std::string(makefile->GetSafeDefinition(key));
      };

      // Write
      ofs << "# Meta\n";
      CWrite("AM_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE");
      CWrite("AM_PARALLEL", this->Parallel);

      ofs << "# Directories\n";
      CWrite("AM_CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
      CWrite("AM_CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
      CWrite("AM_CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR"));
      CWrite("AM_CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR"));
      CWrite("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE",
             MfDef("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"));
      CWrite("AM_BUILD_DIR", this->DirBuild);
      if (this->MultiConfig) {
        CWriteMap("AM_INCLUDE_DIR", includeDirs);
      } else {
        CWrite("AM_INCLUDE_DIR", includeDir);
      }

      ofs << "# Files\n";
      CWriteList("AM_SOURCES", this->Sources);
      CWriteList("AM_HEADERS", this->Headers);
      if (this->MultiConfig) {
953
954
        std::map<std::string, std::string> settingsFiles;
        for (std::string const& cfg : this->ConfigsList) {
955
956
          settingsFiles[cfg] =
            AppendFilenameSuffix(this->AutogenSettingsFile, "_" + cfg);