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

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

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

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

45
46
47
48
49
50
51
52
53
54
55
56
57
58
static std::size_t GetParallelCPUCount()
{
  static std::size_t count = 0;
  // Detect only on the first call
  if (count == 0) {
    cmsys::SystemInformation info;
    info.RunCPUCheck();
    count = info.GetNumberOfPhysicalCPU();
    count = std::max<std::size_t>(count, 1);
    count = std::min<std::size_t>(count, cmQtAutoGen::ParallelMax);
  }
  return count;
}

59
static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName,
60
                             cmQtAutoGen::GeneratorT genType)
61
{
Daniel Pfeifer's avatar
Daniel Pfeifer committed
62
  cmSourceGroup* sourceGroup = nullptr;
63
64
  // Acquire source group
  {
65
66
67
68
69
70
    std::string property;
    std::string groupName;
    {
      std::array<std::string, 2> props;
      // Use generator specific group name
      switch (genType) {
71
        case cmQtAutoGen::GeneratorT::MOC:
72
73
          props[0] = "AUTOMOC_SOURCE_GROUP";
          break;
74
        case cmQtAutoGen::GeneratorT::RCC:
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
          props[0] = "AUTORCC_SOURCE_GROUP";
          break;
        default:
          props[0] = "AUTOGEN_SOURCE_GROUP";
          break;
      }
      props[1] = "AUTOGEN_SOURCE_GROUP";
      for (std::string& prop : props) {
        const char* propName = makefile->GetState()->GetGlobalProperty(prop);
        if ((propName != nullptr) && (*propName != '\0')) {
          groupName = propName;
          property = std::move(prop);
          break;
        }
      }
90
91
    }
    // Generate a source group on demand
92
    if (!groupName.empty()) {
93
      sourceGroup = makefile->GetOrCreateSourceGroup(groupName);
Daniel Pfeifer's avatar
Daniel Pfeifer committed
94
      if (sourceGroup == nullptr) {
95
96
97
98
99
100
        std::ostringstream ost;
        ost << cmQtAutoGen::GeneratorNameUpper(genType);
        ost << ": " << property;
        ost << ": Could not find or create the source group ";
        ost << cmQtAutoGen::Quoted(groupName);
        cmSystemTools::Error(ost.str().c_str());
101
102
103
104
        return false;
      }
    }
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
105
  if (sourceGroup != nullptr) {
106
107
108
109
110
    sourceGroup->AddGroupFile(fileName);
  }
  return true;
}

111
static void AddCleanFile(cmMakefile* makefile, std::string const& fileName)
112
113
114
115
116
{
  makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", fileName.c_str(),
                           false);
}

117
118
119
120
121
122
static std::string FileProjectRelativePath(cmMakefile* makefile,
                                           std::string const& fileName)
{
  std::string res;
  {
    std::string pSource = cmSystemTools::RelativePath(
123
      makefile->GetCurrentSourceDirectory(), fileName);
124
    std::string pBinary = cmSystemTools::RelativePath(
125
      makefile->GetCurrentBinaryDirectory(), fileName);
126
127
128
129
130
131
132
133
134
135
136
    if (pSource.size() < pBinary.size()) {
      res = std::move(pSource);
    } else if (pBinary.size() < fileName.size()) {
      res = std::move(pBinary);
    } else {
      res = fileName;
    }
  }
  return res;
}

137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/* @brief Tests if targetDepend is a STATIC_LIBRARY and if any of its
 * recursive STATIC_LIBRARY dependencies depends on targetOrigin
 * (STATIC_LIBRARY cycle).
 */
static bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin,
                               cmGeneratorTarget const* targetDepend,
                               std::string const& config)
{
  bool cycle = false;
  if ((targetOrigin->GetType() == cmStateEnums::STATIC_LIBRARY) &&
      (targetDepend->GetType() == cmStateEnums::STATIC_LIBRARY)) {
    std::set<cmGeneratorTarget const*> knownLibs;
    std::deque<cmGeneratorTarget const*> testLibs;

    // Insert initial static_library dependency
    knownLibs.insert(targetDepend);
    testLibs.push_back(targetDepend);

    while (!testLibs.empty()) {
      cmGeneratorTarget const* testTarget = testLibs.front();
      testLibs.pop_front();
      // Check if the test target is the origin target (cycle)
      if (testTarget == targetOrigin) {
        cycle = true;
        break;
      }
      // Collect all static_library dependencies from the test target
      cmLinkImplementationLibraries const* libs =
        testTarget->GetLinkImplementationLibraries(config);
      if (libs != nullptr) {
        for (cmLinkItem const& item : libs->Libraries) {
          cmGeneratorTarget const* depTarget = item.Target;
          if ((depTarget != nullptr) &&
              (depTarget->GetType() == cmStateEnums::STATIC_LIBRARY) &&
              knownLibs.insert(depTarget).second) {
            testLibs.push_back(depTarget);
          }
        }
      }
    }
  }
  return cycle;
}

181
cmQtAutoGenInitializer::cmQtAutoGenInitializer(
182
183
184
  cmGeneratorTarget* target, bool mocEnabled, bool uicEnabled, bool rccEnabled,
  std::string const& qtVersionMajor)
  : Target(target)
185
  , MultiConfig(false)
186
  , QtVersionMajor(qtVersionMajor)
187
{
188
189
190
191
  Moc.Enabled = mocEnabled;
  Uic.Enabled = uicEnabled;
  Rcc.Enabled = rccEnabled;

192
193
  this->QtVersionMinor =
    cmQtAutoGenInitializer::GetQtMinorVersion(target, this->QtVersionMajor);
194
}
195

196
bool cmQtAutoGenInitializer::InitCustomTargets()
197
198
199
200
{
  cmMakefile* makefile = this->Target->Target->GetMakefile();
  cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
  cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();
201

202
203
204
205
206
207
208
209
210
211
212
213
214
  // Verbosity
  {
    this->Verbosity = makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE");
    if (!this->Verbosity.empty()) {
      unsigned long iVerb = 0;
      if (!cmSystemTools::StringToULong(this->Verbosity.c_str(), &iVerb)) {
        // Non numeric verbosity
        this->Verbosity =
          cmSystemTools::IsOn(this->Verbosity.c_str()) ? "1" : "0";
      }
    }
  }

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

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

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

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

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

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

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

280
281
282
  std::set<std::string> autogenDependFiles;
  std::set<cmTarget*> autogenDependTargets;
  std::vector<std::string> autogenProvides;
283

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

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

310
  // Add autogen includes directory to the origin target INCLUDE_DIRECTORIES
311
312
  if (this->Moc.Enabled || this->Uic.Enabled ||
      (this->Rcc.Enabled && this->MultiConfig)) {
313
314
315
    std::string includeDir = this->DirBuild;
    includeDir += "/include";
    if (this->MultiConfig) {
316
      includeDir += "_$<CONFIG>";
317
    }
318
    this->Target->AddIncludeDirectory(includeDir, true);
319
320
  }

321
  // Acquire rcc executable and features
322
  if (this->Rcc.Enabled) {
323
324
    if (!GetRccExecutable()) {
      return false;
325
326
327
    }
  }

328
  // Extract relevant source files
329
330
  std::vector<std::string> generatedSources;
  std::vector<std::string> generatedHeaders;
331
  {
332
    std::string const qrcExt = "qrc";
333
    std::vector<cmSourceFile*> srcFiles;
334
    this->Target->GetConfigCommonSourceFiles(srcFiles);
335
    for (cmSourceFile* sf : srcFiles) {
336
337
338
339
      if (sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
        continue;
      }
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
340
341
      std::string const& fPath = sf->GetFullPath();
      std::string const& ext = sf->GetExtension();
342
      // Register generated files that will be scanned by moc or uic
343
      if (this->Moc.Enabled || this->Uic.Enabled) {
344
        cmSystemTools::FileFormat const fileType =
345
346
347
          cmSystemTools::GetFileFormat(ext.c_str());
        if ((fileType == cmSystemTools::CXX_FILE_FORMAT) ||
            (fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
348
          std::string const absPath = cmSystemTools::GetRealPath(fPath);
349
350
          if ((this->Moc.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) ||
              (this->Uic.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) {
351
352
353
354
            // Register source
            const bool generated = sf->GetPropertyAsBool("GENERATED");
            if (fileType == cmSystemTools::HEADER_FILE_FORMAT) {
              if (generated) {
355
                generatedHeaders.push_back(absPath);
356
              } else {
357
                this->Headers.push_back(absPath);
358
359
360
              }
            } else {
              if (generated) {
361
                generatedSources.push_back(absPath);
362
              } else {
363
                this->Sources.push_back(absPath);
364
              }
365
366
367
            }
          }
        }
368
369
      }
      // Register rcc enabled files
370
      if (this->Rcc.Enabled && (ext == qrcExt) &&
371
          !sf->GetPropertyAsBool("SKIP_AUTORCC")) {
372
373
        // Register qrc file
        {
374
375
376
377
378
          Qrc qrc;
          qrc.QrcFile = cmSystemTools::GetRealPath(fPath);
          qrc.QrcName =
            cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
          qrc.Generated = sf->GetPropertyAsBool("GENERATED");
379
380
          // RCC options
          {
381
            std::string const opts = sf->GetSafeProperty("AUTORCC_OPTIONS");
382
            if (!opts.empty()) {
383
              cmSystemTools::ExpandListArgument(opts, qrc.Options);
384
385
            }
          }
386
          this->Rcc.Qrcs.push_back(std::move(qrc));
387
        }
388
389
390
391
392
393
      }
    }
    // 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.
394
395
396
    this->Target->ClearSourcesCache();
  }
  // Read skip files from makefile sources
397
  if (this->Moc.Enabled || this->Uic.Enabled) {
398
399
    std::string pathError;
    for (cmSourceFile* sf : makefile->GetSourceFiles()) {
400
      // sf->GetExtension() is only valid after sf->GetFullPath() ...
401
402
403
404
405
406
407
      // 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;
      }
408
409
410
411
412
413
414
      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");
415
416
417
418
      const bool mocSkip = this->Moc.Enabled &&
        (skipAll || sf->GetPropertyAsBool("SKIP_AUTOMOC"));
      const bool uicSkip = this->Uic.Enabled &&
        (skipAll || sf->GetPropertyAsBool("SKIP_AUTOUIC"));
419
420
421
      if (mocSkip || uicSkip) {
        std::string const absFile = cmSystemTools::GetRealPath(fPath);
        if (mocSkip) {
422
          this->Moc.Skip.insert(absFile);
423
424
        }
        if (uicSkip) {
425
          this->Uic.Skip.insert(absFile);
426
427
428
        }
      }
    }
429
  }
430

431
  // Process GENERATED sources and headers
432
  if (!generatedSources.empty() || !generatedHeaders.empty()) {
433
434
435
    // Check status of policy CMP0071
    bool policyAccept = false;
    bool policyWarn = false;
436
    cmPolicies::PolicyStatus const CMP0071_status =
437
      makefile->GetPolicyStatus(cmPolicies::CMP0071);
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
    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
455
      for (std::string const& absFile : generatedHeaders) {
456
        this->Headers.push_back(absFile);
457
        autogenDependFiles.insert(absFile);
458
      }
459
      for (std::string const& absFile : generatedSources) {
460
        this->Sources.push_back(absFile);
461
        autogenDependFiles.insert(absFile);
462
      }
463
464
465
466
467
468
    } else {
      if (policyWarn) {
        std::string msg;
        msg += cmPolicies::GetPolicyWarning(cmPolicies::CMP0071);
        msg += "\n";
        std::string tools;
469
        std::string property;
470
        if (this->Moc.Enabled && this->Uic.Enabled) {
471
472
          tools = "AUTOMOC and AUTOUIC";
          property = "SKIP_AUTOGEN";
473
        } else if (this->Moc.Enabled) {
474
475
          tools = "AUTOMOC";
          property = "SKIP_AUTOMOC";
476
        } else if (this->Uic.Enabled) {
477
478
          tools = "AUTOUIC";
          property = "SKIP_AUTOUIC";
479
        }
480
481
482
        msg += "For compatibility, CMake is excluding the GENERATED source "
               "file(s):\n";
        for (const std::string& absFile : generatedHeaders) {
483
          msg.append("  ").append(Quoted(absFile)).append("\n");
484
        }
485
        for (const std::string& absFile : generatedSources) {
486
          msg.append("  ").append(Quoted(absFile)).append("\n");
487
        }
488
489
490
491
492
493
494
495
496
497
        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";
498
        makefile->IssueMessage(cmake::AUTHOR_WARNING, msg);
499
      }
500
    }
501
502
503
    // Clear lists
    generatedSources.clear();
    generatedHeaders.clear();
504
  }
505
  // Sort headers and sources
506
  if (this->Moc.Enabled || this->Uic.Enabled) {
507
508
509
    std::sort(this->Headers.begin(), this->Headers.end());
    std::sort(this->Sources.begin(), this->Sources.end());
  }
510

511
  // Process qrc files
512
  if (!this->Rcc.Qrcs.empty()) {
513
    const bool QtV5 = (this->QtVersionMajor == "5");
514
515
516
    // Target rcc options
    std::vector<std::string> optionsTarget;
    cmSystemTools::ExpandListArgument(
517
      this->Target->GetSafeProperty("AUTORCC_OPTIONS"), optionsTarget);
518

519
    // Check if file name is unique
520
    for (Qrc& qrc : this->Rcc.Qrcs) {
521
      qrc.Unique = true;
522
      for (Qrc const& qrc2 : this->Rcc.Qrcs) {
523
524
        if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
          qrc.Unique = false;
525
526
          break;
        }
527
      }
528
    }
529
    // Path checksum and file names
530
    {
531
      cmFilePathChecksum const fpathCheckSum(makefile);
532
      for (Qrc& qrc : this->Rcc.Qrcs) {
533
        qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile);
534
        // RCC output file name
535
        {
536
537
          std::string rccFile = this->DirBuild + "/";
          rccFile += qrc.PathChecksum;
538
          rccFile += "/qrc_";
539
          rccFile += qrc.QrcName;
540
          rccFile += ".cpp";
541
          qrc.RccFile = std::move(rccFile);
542
543
        }
        {
544
          std::string base = this->DirInfo;
545
          base += "/RCC";
546
547
548
          base += qrc.QrcName;
          if (!qrc.Unique) {
            base += qrc.PathChecksum;
549
          }
550
551
552
553

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

554
555
          qrc.InfoFile = base;
          qrc.InfoFile += "Info.cmake";
556

557
          qrc.SettingsFile = base;
558
          qrc.SettingsFile += "Settings.txt";
559
        }
560
561
562
      }
    }
    // RCC options
563
    for (Qrc& qrc : this->Rcc.Qrcs) {
564
565
566
      // Target options
      std::vector<std::string> opts = optionsTarget;
      // Merge computed "-name XYZ" option
567
      {
568
        std::string name = qrc.QrcName;
569
570
        // Replace '-' with '_'. The former is not valid for symbol names.
        std::replace(name.begin(), name.end(), '-', '_');
571
        if (!qrc.Unique) {
572
          name += "_";
573
          name += qrc.PathChecksum;
574
        }
575
576
577
        std::vector<std::string> nameOpts;
        nameOpts.emplace_back("-name");
        nameOpts.emplace_back(std::move(name));
578
        RccMergeOptions(opts, nameOpts, QtV5);
579
      }
580
      // Merge file option
581
      RccMergeOptions(opts, qrc.Options, QtV5);
582
      qrc.Options = std::move(opts);
583
    }
584
    for (Qrc& qrc : this->Rcc.Qrcs) {
585
      // Register file at target
586
      this->AddGeneratedSource(qrc.RccFile, GeneratorT::RCC);
587

588
589
      std::vector<std::string> ccOutput;
      ccOutput.push_back(qrc.RccFile);
590

591
      cmCustomCommandLines commandLines;
592
593
594
595
596
597
598
599
600
601
602
603
      if (this->MultiConfig) {
        // Build for all configurations
        for (std::string const& config : this->ConfigsList) {
          cmCustomCommandLine currentLine;
          currentLine.push_back(cmSystemTools::GetCMakeCommand());
          currentLine.push_back("-E");
          currentLine.push_back("cmake_autorcc");
          currentLine.push_back(qrc.InfoFile);
          currentLine.push_back(config);
          commandLines.push_back(std::move(currentLine));
        }
      } else {
604
605
606
607
        cmCustomCommandLine currentLine;
        currentLine.push_back(cmSystemTools::GetCMakeCommand());
        currentLine.push_back("-E");
        currentLine.push_back("cmake_autorcc");
608
        currentLine.push_back(qrc.InfoFile);
609
        currentLine.push_back("$<CONFIG>");
610
        commandLines.push_back(std::move(currentLine));
611
      }
612
      std::string ccComment = "Automatic RCC for ";
613
      ccComment += FileProjectRelativePath(makefile, qrc.QrcFile);
614

615
      if (qrc.Generated) {
616
617
618
        // Create custom rcc target
        std::string ccName;
        {
619
          ccName = this->Target->GetName();
620
          ccName += "_arcc_";
621
622
          ccName += qrc.QrcName;
          if (!qrc.Unique) {
623
            ccName += "_";
624
            ccName += qrc.PathChecksum;
625
626
          }
          std::vector<std::string> ccDepends;
627
          // Add the .qrc and info file to the custom target dependencies
628
          ccDepends.push_back(qrc.QrcFile);
629
          ccDepends.push_back(qrc.InfoFile);
630
631

          cmTarget* autoRccTarget = makefile->AddUtilityCommand(
632
633
634
            ccName, cmMakefile::TargetOrigin::Generator, true,
            this->DirWork.c_str(), ccOutput, ccDepends, commandLines, false,
            ccComment.c_str());
635
636
637
638
639
          // Create autogen generator target
          localGen->AddGeneratorTarget(
            new cmGeneratorTarget(autoRccTarget, localGen));

          // Set FOLDER property in autogen target
640
641
          if (!this->AutogenFolder.empty()) {
            autoRccTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
642
643
644
          }
        }
        // Add autogen target to the origin target dependencies
645
        this->Target->Target->AddUtility(ccName, makefile);
646
      } else {
647
        // Create custom rcc command
648
        {
649
650
          std::vector<std::string> ccByproducts;
          std::vector<std::string> ccDepends;
651
          // Add the .qrc and info file to the custom command dependencies
652
          ccDepends.push_back(qrc.QrcFile);
653
          ccDepends.push_back(qrc.InfoFile);
654
655
656
657

          // Add the resource files to the dependencies
          {
            std::string error;
658
            if (RccListInputs(qrc.QrcFile, qrc.Resources, error)) {
659
              for (std::string const& fileName : qrc.Resources) {
660
661
662
663
664
                // Add resource file to the custom command dependencies
                ccDepends.push_back(fileName);
              }
            } else {
              cmSystemTools::Error(error.c_str());
665
              return false;
666
            }
667
          }
668
669
670
          makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends,
                                             /*main_dependency*/ std::string(),
                                             commandLines, ccComment.c_str(),
671
                                             this->DirWork.c_str());
672
        }
673
        // Reconfigure when .qrc file changes
674
        makefile->AddCMakeDependFile(qrc.QrcFile);
675
676
677
678
      }
    }
  }

679
  // Create _autogen target
680
  if (this->Moc.Enabled || this->Uic.Enabled) {
681
682
683
    // Add user defined autogen target dependencies
    {
      std::string const deps =
684
        this->Target->GetSafeProperty("AUTOGEN_TARGET_DEPENDS");
685
686
687
688
689
690
691
692
693
694
695
      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);
          }
696
        }
697
698
699
      }
    }

700
701
702
    // Compose target comment
    std::string autogenComment;
    {
703
      std::string tools;
704
      if (this->Moc.Enabled) {
705
        tools += "MOC";
706
      }
707
      if (this->Uic.Enabled) {
708
709
        if (!tools.empty()) {
          tools += " and ";
710
        }
711
        tools += "UIC";
712
      }
713
714
715
716
      autogenComment = "Automatic ";
      autogenComment += tools;
      autogenComment += " for target ";
      autogenComment += this->Target->GetName();
717
    }
718
719
720
721
722
723
724
725

    // Compose command lines
    cmCustomCommandLines commandLines;
    {
      cmCustomCommandLine currentLine;
      currentLine.push_back(cmSystemTools::GetCMakeCommand());
      currentLine.push_back("-E");
      currentLine.push_back("cmake_autogen");
726
      currentLine.push_back(this->AutogenInfoFile);
727
728
      currentLine.push_back("$<CONFIGURATION>");
      commandLines.push_back(std::move(currentLine));
729
730
    }

731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
    // 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) {
751
        this->Target->Target->AddUtility(depTarget->GetName(), makefile);
752
      }
753

754
755
756
757
758
759
760
761
762
      // 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(),
763
                         this->DirWork.c_str());
764
765
      cc.SetEscapeOldStyle(false);
      cc.SetEscapeAllowMakeVars(true);
766
      this->Target->Target->AddPreBuildCommand(cc);
767
    } else {
768

769
770
      // Add link library target dependencies to the autogen target
      // dependencies
771
772
773
774
775
776
777
778
779
780
781
782
783
784
      {
        // 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]++;
785
              }
786
787
788
            }
          }
        }
789
790
791
792
793
        for (auto const& item : commonTargets) {
          if (item.second == this->ConfigsList.size()) {
            autogenDependTargets.insert(item.first->Target);
          }
        }
794
795
      }

796
797
      // Create autogen target
      cmTarget* autogenTarget = makefile->AddUtilityCommand(
798
        this->AutogenTargetName, cmMakefile::TargetOrigin::Generator, true,
799
800
801
        this->DirWork.c_str(), /*byproducts=*/autogenProvides,
        std::vector<std::string>(autogenDependFiles.begin(),
                                 autogenDependFiles.end()),
802
        commandLines, false, autogenComment.c_str());
803
804
805
806
807
      // Create autogen generator target
      localGen->AddGeneratorTarget(
        new cmGeneratorTarget(autogenTarget, localGen));

      // Forward origin utilities to autogen target
808
      for (std::string const& depName : this->Target->Target->GetUtilities()) {
809
        autogenTarget->AddUtility(depName, makefile);
810
      }
811
812
813
      // Add additional autogen target dependencies to autogen target
      for (cmTarget* depTarget : autogenDependTargets) {
        autogenTarget->AddUtility(depTarget->GetName(), makefile);
814
      }
815
816

      // Set FOLDER property in autogen target
817
818
      if (!this->AutogenFolder.empty()) {
        autogenTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
819
      }
820

821
      // Add autogen target to the origin target dependencies
822
      this->Target->Target->AddUtility(this->AutogenTargetName, makefile);
823
    }
824
  }
825
826

  return true;
827
828
}

829
bool cmQtAutoGenInitializer::SetupCustomTargets()
830
{
831
  cmMakefile* makefile = this->Target->Target->GetMakefile();
832

833
  // Create info directory on demand
834
  if (!cmSystemTools::MakeDirectory(this->DirInfo)) {
835
    std::string emsg = ("AutoGen: Could not create directory: ");
836
    emsg += Quoted(this->DirInfo);
837
    cmSystemTools::Error(emsg.c_str());
838
    return false;
839
840
  }

841
842
843
844
845
846
847
848
849
  // 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;
  }

850
  // Generate autogen target info file
851
852
  if (this->Moc.Enabled || this->Uic.Enabled) {
    if (this->Moc.Enabled) {
853
854
      this->SetupCustomTargetsMoc();
    }
855
    if (this->Uic.Enabled) {
856
857
858
859
      this->SetupCustomTargetsUic();
    }

    // Parallel processing
860
    this->Parallel = this->Target->GetSafeProperty("AUTOGEN_PARALLEL");
861
862
863
    if (this->Parallel.empty() || (this->Parallel == "AUTO")) {
      // Autodetect number of CPUs
      this->Parallel = std::to_string(GetParallelCPUCount());
864
    }
865

866
867
868
869
    cmGeneratedFileStream ofs;
    ofs.SetCopyIfDifferent(true);
    ofs.Open(this->AutogenInfoFile.c_str(), false, true);
    if (ofs) {
870
871
872
873
874
875
876
877
878
      // 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, ";"));
      };
879
880
881
882
883
884
885
886
887
888
889
890
      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));
        };
891
892
893
894
895
896
      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) {
897
        for (auto const& item : map) {
898
899
900
901
          ofs << "set(" << key << "_" << item.first << " "
              << cmOutputConverter::EscapeForCMake(item.second) << ")\n";
        }
      };
902
903
904
905
906
907
908
909
      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);
910
      CWrite("AM_VERBOSITY", this->Verbosity);
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929

      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) {
930
931
        std::map<std::string, std::string> settingsFiles;
        for (std::string const& cfg : this->ConfigsList) {
932
933
          settingsFiles[cfg] =
            AppendFilenameSuffix(this->AutogenSettingsFile, "_" + cfg);
934
        }
935
936
937
938
939
940
941
        CWriteMap("AM_SETTINGS_FILE", settingsFiles);
      } else {
        CWrite("AM_SETTINGS_FILE", this->AutogenSettingsFile);
      }

      ofs << "# Qt\n";
      CWrite("AM_QT_VERSION_MAJOR", this->QtVersionMajor);
942
943
      CWrite("AM_QT_MOC_EXECUTABLE", this->Moc.Executable);
      CWrite("AM_QT_UIC_EXECUTABLE", this->Uic.Executable);
944

945
      if (this->Moc.Enabled) {
946
        ofs << "# MOC settings\n";
947
948
949
950
951
        CWriteSet("AM_MOC_SKIP", this->Moc.Skip);
        CWrite("AM_MOC_DEFINITIONS", this->Moc.Defines);
        CWriteMap("AM_MOC_DEFINITIONS", this->Moc.ConfigDefines);
        CWrite("AM_MOC_INCLUDES", this->Moc.Includes);
        CWriteMap("AM_MOC_INCLUDES", this->Moc.ConfigIncludes);
952
        CWrite("AM_MOC_OPTIONS",
953
               this->Target->GetSafeProperty("AUTOMOC_MOC_OPTIONS"));
954
955
        CWrite("AM_MOC_RELAXED_MODE", MfDef("CMAKE_AUTOMOC_RELAXED_MODE"));
        CWrite("AM_MOC_MACRO_NAMES",
956
               this->Target->GetSafeProperty("AUTOMOC_MACRO_NAMES"));