cmGlobalXCodeGenerator.cxx 130 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.  */
Bill Hoffman's avatar
Bill Hoffman committed
3
#include "cmGlobalXCodeGenerator.h"
Brad King's avatar
Brad King committed
4

5
6
7
#include <cassert>
#include <cstdio>
#include <cstring>
Daniel Pfeifer's avatar
Daniel Pfeifer committed
8
9
10
#include <iomanip>
#include <sstream>

11
#include <cm/memory>
12

13
14
#include "cmsys/RegularExpression.hxx"

15
#include "cmAlgorithms.h"
16
#include "cmComputeLinkInformation.h"
17
#include "cmCustomCommand.h"
18
#include "cmCustomCommandGenerator.h"
19
#include "cmCustomCommandLines.h"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
20
#include "cmDocumentationEntry.h"
21
#include "cmGeneratedFileStream.h"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
22
#include "cmGeneratorExpression.h"
23
#include "cmGeneratorTarget.h"
24
#include "cmGlobalGeneratorFactory.h"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
25
#include "cmLocalGenerator.h"
26
27
#include "cmLocalXCodeGenerator.h"
#include "cmMakefile.h"
28
#include "cmMessageType.h"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
29
#include "cmOutputConverter.h"
30
#include "cmSourceFile.h"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
31
#include "cmSourceGroup.h"
32
#include "cmState.h"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
33
34
35
#include "cmStateTypes.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
36
37
#include "cmXCode21Object.h"
#include "cmXCodeObject.h"
38
#include "cmXCodeScheme.h"
39
#include "cmake.h"
40

Daniel Pfeifer's avatar
Daniel Pfeifer committed
41
struct cmLinkImplementation;
42

43
#if !defined(CMAKE_BOOTSTRAP) && defined(__APPLE__)
44
45
#  define HAVE_APPLICATION_SERVICES
#  include <ApplicationServices/ApplicationServices.h>
46
47
#endif

48
#if !defined(CMAKE_BOOTSTRAP)
49
#  include "cmXMLParser.h"
50
51
52
53
54
55

// parse the xml file storing the installed version of Xcode on
// the machine
class cmXcodeVersionParser : public cmXMLParser
{
public:
56
57
58
59
  cmXcodeVersionParser()
    : Version("1.5")
  {
  }
60
61
62
63
64
  void StartElement(const std::string&, const char**) override
  {
    this->Data = "";
  }
  void EndElement(const std::string& name) override
65
66
67
68
69
70
71
  {
    if (name == "key") {
      this->Key = this->Data;
    } else if (name == "string") {
      if (this->Key == "CFBundleShortVersionString") {
        this->Version = this->Data;
      }
72
    }
73
  }
74
  void CharacterDataHandler(const char* data, int length) override
75
76
77
  {
    this->Data.append(data, length);
  }
78
  std::string Version;
79
80
  std::string Key;
  std::string Data;
81
};
82
#endif
83

84
85
86
87
// Builds either an object list or a space-separated string from the
// given inputs.
class cmGlobalXCodeGenerator::BuildObjectListOrString
{
88
89
  cmGlobalXCodeGenerator* Generator;
  cmXCodeObject* Group;
90
91
92
93
  bool Empty;
  std::string String;

public:
94
95
  BuildObjectListOrString(cmGlobalXCodeGenerator* gen, bool buildObjectList)
    : Generator(gen)
Matthias Männich's avatar
Matthias Männich committed
96
    , Group(nullptr)
97
98
99
    , Empty(true)
  {
    if (buildObjectList) {
100
101
      this->Group = this->Generator->CreateObject(cmXCodeObject::OBJECT_LIST);
    }
102
  }
103
104
105

  bool IsEmpty() const { return this->Empty; }

106
  void Add(const std::string& newString)
107
  {
108
109
    this->Empty = false;

110
    if (this->Group) {
111
      this->Group->AddObject(this->Generator->CreateString(newString));
112
    } else {
113
114
115
      this->String += newString;
      this->String += ' ';
    }
116
  }
117

118
  const std::string& GetString() const { return this->String; }
119

120
121
122
  cmXCodeObject* CreateList()
  {
    if (this->Group) {
123
124
      return this->Group;
    }
125
    return this->Generator->CreateString(this->String);
126
  }
127
128
};

129
130
131
class cmGlobalXCodeGenerator::Factory : public cmGlobalGeneratorFactory
{
public:
132
  cmGlobalGenerator* CreateGlobalGenerator(const std::string& name,
133
                                           cmake* cm) const override;
134

135
  void GetDocumentation(cmDocumentationEntry& entry) const override
136
137
138
  {
    cmGlobalXCodeGenerator::GetDocumentation(entry);
  }
139

140
  std::vector<std::string> GetGeneratorNames() const override
141
  {
142
    std::vector<std::string> names;
143
    names.push_back(cmGlobalXCodeGenerator::GetActualName());
144
145
146
147
148
149
    return names;
  }

  std::vector<std::string> GetGeneratorNamesWithPlatform() const override
  {
    return std::vector<std::string>();
150
  }
151

152
153
  bool SupportsToolset() const override { return true; }
  bool SupportsPlatform() const override { return false; }
154
155
156
157
158

  std::vector<std::string> GetKnownPlatforms() const override
  {
    return std::vector<std::string>();
  }
159
160

  std::string GetDefaultPlatformName() const override { return std::string(); }
161
162
};

163
164
cmGlobalXCodeGenerator::cmGlobalXCodeGenerator(
  cmake* cm, std::string const& version_string, unsigned int version_number)
165
  : cmGlobalGenerator(cm)
Bill Hoffman's avatar
Bill Hoffman committed
166
{
167
168
  this->VersionString = version_string;
  this->XcodeVersion = version_number;
169

Matthias Männich's avatar
Matthias Männich committed
170
171
172
173
  this->RootObject = nullptr;
  this->MainGroupChildren = nullptr;
  this->CurrentMakefile = nullptr;
  this->CurrentLocalGenerator = nullptr;
174
  this->XcodeBuildCommandInitialized = false;
175

176
  this->ObjectDirArchDefault = "$(CURRENT_ARCH)";
177
  this->ObjectDirArch = this->ObjectDirArchDefault;
178

179
  cm->GetState()->SetIsGeneratorMultiConfig(true);
180
181
}

182
183
184
185
186
cmGlobalGeneratorFactory* cmGlobalXCodeGenerator::NewFactory()
{
  return new Factory;
}

187
188
cmGlobalGenerator* cmGlobalXCodeGenerator::Factory::CreateGlobalGenerator(
  const std::string& name, cmake* cm) const
189
{
190
  if (name != GetActualName()) {
Matthias Männich's avatar
Matthias Männich committed
191
    return nullptr;
192
  }
193
#if !defined(CMAKE_BOOTSTRAP)
194
  cmXcodeVersionParser parser;
195
196
  std::string versionFile;
  {
197
    std::string out;
198
199
200
201
202
203
204
205
    bool commandResult = cmSystemTools::RunSingleCommand(
      "xcode-select --print-path", &out, nullptr, nullptr, nullptr,
      cmSystemTools::OUTPUT_NONE);
    if (commandResult) {
      std::string::size_type pos = out.find(".app/");
      if (pos != std::string::npos) {
        versionFile = out.substr(0, pos + 5) + "Contents/version.plist";
      }
206
207
    }
  }
208
  if (!versionFile.empty() && cmSystemTools::FileExists(versionFile)) {
209
    parser.ParseFile(versionFile.c_str());
210
211
212
213
214
215
216
  } else if (cmSystemTools::FileExists(
               "/Applications/Xcode.app/Contents/version.plist")) {
    parser.ParseFile("/Applications/Xcode.app/Contents/version.plist");
  } else {
    parser.ParseFile(
      "/Developer/Applications/Xcode.app/Contents/version.plist");
  }
217
218
219
220
221
222
223
  std::string const& version_string = parser.Version;

  // Compute an integer form of the version number.
  unsigned int v[2] = { 0, 0 };
  sscanf(version_string.c_str(), "%u.%u", &v[0], &v[1]);
  unsigned int version_number = 10 * v[0] + v[1];

Gregor Jasny's avatar
Gregor Jasny committed
224
  if (version_number < 50) {
225
    cm->IssueMessage(MessageType::FATAL_ERROR,
226
                     "Xcode " + version_string + " not supported.");
Daniel Pfeifer's avatar
Daniel Pfeifer committed
227
    return nullptr;
228
229
  }

230
231
  auto gg = cm::make_unique<cmGlobalXCodeGenerator>(cm, version_string,
                                                    version_number);
232
  return gg.release();
233
#else
234
  std::cerr << "CMake should be built with cmake to use Xcode, "
235
               "default to Xcode 1.5\n";
236
  return new cmGlobalXCodeGenerator(cm);
237
#endif
Bill Hoffman's avatar
Bill Hoffman committed
238
239
}

240
bool cmGlobalXCodeGenerator::FindMakeProgram(cmMakefile* mf)
241
242
243
244
{
  // The Xcode generator knows how to lookup its build tool
  // directly instead of needing a helper module to do it, so we
  // do not actually need to put CMAKE_MAKE_PROGRAM into the cache.
245
  if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
246
    mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetXcodeBuildCommand());
247
  }
248
  return true;
249
250
}

251
252
std::string const& cmGlobalXCodeGenerator::GetXcodeBuildCommand()
{
253
  if (!this->XcodeBuildCommandInitialized) {
254
255
    this->XcodeBuildCommandInitialized = true;
    this->XcodeBuildCommand = this->FindXcodeBuildCommand();
256
  }
257
258
259
260
261
  return this->XcodeBuildCommand;
}

std::string cmGlobalXCodeGenerator::FindXcodeBuildCommand()
{
Gregor Jasny's avatar
Gregor Jasny committed
262
263
264
  std::string makeProgram = cmSystemTools::FindProgram("xcodebuild");
  if (makeProgram.empty()) {
    makeProgram = "xcodebuild";
265
  }
Gregor Jasny's avatar
Gregor Jasny committed
266
  return makeProgram;
267
268
}

269
270
bool cmGlobalXCodeGenerator::SetGeneratorToolset(std::string const& ts,
                                                 cmMakefile* mf)
271
{
272
  if (ts.find_first_of(",=") != std::string::npos) {
273
274
275
276
277
278
279
280
281
    std::ostringstream e;
    /* clang-format off */
    e <<
      "Generator\n"
      "  " << this->GetName() << "\n"
      "does not recognize the toolset\n"
      "  " << ts << "\n"
      "that was specified.";
    /* clang-format on */
282
    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
283
284
285
286
    return false;
  }
  this->GeneratorToolset = ts;
  if (!this->GeneratorToolset.empty()) {
287
    mf->AddDefinition("CMAKE_XCODE_PLATFORM_TOOLSET", this->GeneratorToolset);
288
  }
289
  return true;
290
291
}

292
293
void cmGlobalXCodeGenerator::EnableLanguage(
  std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
294
{
295
  mf->AddDefinition("XCODE", "1");
296
  mf->AddDefinition("XCODE_VERSION", this->VersionString);
297
298
299
300
301
302
303
  if (!mf->GetDefinition("CMAKE_CONFIGURATION_TYPES")) {
    mf->AddCacheDefinition(
      "CMAKE_CONFIGURATION_TYPES", "Debug;Release;MinSizeRel;RelWithDebInfo",
      "Semicolon separated list of supported configuration types, "
      "only supports Debug, Release, MinSizeRel, and RelWithDebInfo, "
      "anything else will be ignored.",
      cmStateEnums::STRING);
304
  }
Bill Hoffman's avatar
Bill Hoffman committed
305
  mf->AddDefinition("CMAKE_GENERATOR_NO_COMPILER_ENV", "1");
Alexander Neundorf's avatar
   
Alexander Neundorf committed
306
  this->cmGlobalGenerator::EnableLanguage(lang, mf, optional);
307
  this->ComputeArchitectures(mf);
Bill Hoffman's avatar
Bill Hoffman committed
308
309
}

310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
bool cmGlobalXCodeGenerator::Open(const std::string& bindir,
                                  const std::string& projectName, bool dryRun)
{
  bool ret = false;

#ifdef HAVE_APPLICATION_SERVICES
  std::string url = bindir + "/" + projectName + ".xcodeproj";

  if (dryRun) {
    return cmSystemTools::FileExists(url, false);
  }

  CFStringRef cfStr = CFStringCreateWithCString(
    kCFAllocatorDefault, url.c_str(), kCFStringEncodingUTF8);
  if (cfStr) {
    CFURLRef cfUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, cfStr,
                                                   kCFURLPOSIXPathStyle, true);
    if (cfUrl) {
      OSStatus err = LSOpenCFURLRef(cfUrl, nullptr);
      ret = err == noErr;
      CFRelease(cfUrl);
    }
    CFRelease(cfStr);
  }
#endif

  return ret;
}

339
340
341
342
343
344
345
std::vector<cmGlobalGenerator::GeneratedMakeCommand>
cmGlobalXCodeGenerator::GenerateBuildCommand(
  const std::string& makeProgram, const std::string& projectName,
  const std::string& /*projectDir*/,
  std::vector<std::string> const& targetNames, const std::string& config,
  bool /*fast*/, int jobs, bool /*verbose*/,
  std::vector<std::string> const& makeOptions)
Bill Hoffman's avatar
Bill Hoffman committed
346
{
347
  GeneratedMakeCommand makeCommand;
348
  // now build the test
349
  makeCommand.Add(
350
    this->SelectMakeProgram(makeProgram, this->GetXcodeBuildCommand()));
351

352
  if (!projectName.empty()) {
353
    makeCommand.Add("-project");
354
    std::string projectArg = cmStrCat(projectName, ".xcodeproj");
355
    makeCommand.Add(projectArg);
356
  }
wahikihiki's avatar
wahikihiki committed
357
  if (cmContains(targetNames, "clean")) {
358
359
360
361
362
363
364
365
366
367
368
369
370
371
    makeCommand.Add("clean");
    makeCommand.Add("-target", "ALL_BUILD");
  } else {
    makeCommand.Add("build");
    if (targetNames.empty() ||
        ((targetNames.size() == 1) && targetNames.front().empty())) {
      makeCommand.Add("-target", "ALL_BUILD");
    } else {
      for (const auto& tname : targetNames) {
        if (!tname.empty()) {
          makeCommand.Add("-target", tname);
        }
      }
    }
372
  }
373

374
  makeCommand.Add("-configuration", (config.empty() ? "Debug" : config));
375
376

  if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
377
    makeCommand.Add("-jobs");
378
    if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
379
      makeCommand.Add(std::to_string(jobs));
380
381
382
    }
  }

383
  if (this->XcodeVersion >= 70) {
384
    makeCommand.Add("-hideShellScriptEnvironment");
385
  }
386
  makeCommand.Add(makeOptions.begin(), makeOptions.end());
387
  return { std::move(makeCommand) };
Bill Hoffman's avatar
Bill Hoffman committed
388
389
}

390
//! Create a local generator appropriate to this Global Generator
391
cmLocalGenerator* cmGlobalXCodeGenerator::CreateLocalGenerator(cmMakefile* mf)
Bill Hoffman's avatar
Bill Hoffman committed
392
{
393
  return new cmLocalXCodeGenerator(this, mf);
Bill Hoffman's avatar
Bill Hoffman committed
394
395
}

396
397
void cmGlobalXCodeGenerator::AddExtraIDETargets()
{
398
399
  // make sure extra targets are added before calling
  // the parent generate which will call trace depends
400
401
  for (auto keyVal : this->ProjectMap) {
    cmLocalGenerator* root = keyVal.second[0];
402
    this->SetGenerationRoot(root);
403
    // add ALL_BUILD, INSTALL, etc
404
    this->AddExtraTargets(root, keyVal.second);
405
  }
406
407
}

408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
void cmGlobalXCodeGenerator::ComputeTargetOrder()
{
  size_t index = 0;
  auto const& lgens = this->GetLocalGenerators();
  for (cmLocalGenerator* lgen : lgens) {
    auto const& targets = lgen->GetGeneratorTargets();
    for (cmGeneratorTarget const* gt : targets) {
      this->ComputeTargetOrder(gt, index);
    }
  }
  assert(index == this->TargetOrderIndex.size());
}

void cmGlobalXCodeGenerator::ComputeTargetOrder(cmGeneratorTarget const* gt,
                                                size_t& index)
{
  std::map<cmGeneratorTarget const*, size_t>::value_type value(gt, 0);
  auto insertion = this->TargetOrderIndex.insert(value);
  if (!insertion.second) {
    return;
  }
  auto entry = insertion.first;

  auto& deps = this->GetTargetDirectDepends(gt);
  for (auto& d : deps) {
    this->ComputeTargetOrder(d, index);
  }

  entry->second = index++;
}

439
440
void cmGlobalXCodeGenerator::Generate()
{
441
  this->cmGlobalGenerator::Generate();
442
  if (cmSystemTools::GetErrorOccuredFlag()) {
443
    return;
444
  }
445
446
447

  this->ComputeTargetOrder();

448
449
  for (auto keyVal : this->ProjectMap) {
    cmLocalGenerator* root = keyVal.second[0];
450
451
452
453
454
455
456
457
458
459
460

    bool generateTopLevelProjectOnly =
      root->GetMakefile()->IsOn("CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY");

    if (generateTopLevelProjectOnly) {
      cmStateSnapshot snp = root->GetStateSnapshot();
      if (snp.GetBuildsystemDirectoryParent().IsValid()) {
        continue;
      }
    }

461
    this->SetGenerationRoot(root);
462
    // now create the project
463
    this->OutputXCodeProject(root, keyVal.second);
464
  }
465
}
466

467
468
void cmGlobalXCodeGenerator::SetGenerationRoot(cmLocalGenerator* root)
{
469
  this->CurrentProject = root->GetProjectName();
470
  this->SetCurrentLocalGenerator(root);
471
  cmSystemTools::SplitPath(
472
473
    this->CurrentLocalGenerator->GetCurrentSourceDirectory(),
    this->ProjectSourceDirectoryComponents);
474
  cmSystemTools::SplitPath(
475
476
    this->CurrentLocalGenerator->GetCurrentBinaryDirectory(),
    this->ProjectOutputDirectoryComponents);
477

478
479
  this->CurrentXCodeHackMakefile =
    cmStrCat(root->GetCurrentBinaryDirectory(), "/CMakeScripts");
480
  cmSystemTools::MakeDirectory(this->CurrentXCodeHackMakefile);
481
482
483
  this->CurrentXCodeHackMakefile += "/XCODE_DEPEND_HELPER.make";
}

484
485
std::string cmGlobalXCodeGenerator::PostBuildMakeTarget(
  std::string const& tName, std::string const& configName)
486
{
487
  std::string target = tName;
488
  std::replace(target.begin(), target.end(), ' ', '_');
489
  std::string out = cmStrCat("PostBuild.", target, '.', configName);
490
491
492
  return out;
}

493
#define CMAKE_CHECK_BUILD_SYSTEM_TARGET "ZERO_CHECK"
494
#define OBJECT_LIBRARY_ARTIFACT_DIR std::string()
495

496
497
void cmGlobalXCodeGenerator::AddExtraTargets(
  cmLocalGenerator* root, std::vector<cmLocalGenerator*>& gens)
498
499
{
  cmMakefile* mf = root->GetMakefile();
500

Matthias Männich's avatar
Matthias Männich committed
501
  const char* no_working_directory = nullptr;
502
  std::vector<std::string> no_byproducts;
503
  std::vector<std::string> no_depends;
504
505

  // Add ALL_BUILD
506
  cmTarget* allbuild = mf->AddUtilityCommand(
507
    "ALL_BUILD", cmCommandOrigin::Generator, true, no_working_directory,
508
509
    no_byproducts, no_depends,
    cmMakeSingleCommandLine({ "echo", "Build all projects" }));
510

511
  cmGeneratorTarget* allBuildGt = new cmGeneratorTarget(allbuild, root);
512
  root->AddGeneratorTarget(allBuildGt);
513

514
  // Add XCODE depend helper
515
  std::string dir = root->GetCurrentBinaryDirectory();
516
517
518
  cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
    { "make", "-C", dir, "-f", this->CurrentXCodeHackMakefile,
      "OBJDIR=$(OBJDIR)", /* placeholder, see below */ "" });
519

520
  // Add ZERO_CHECK
521
  bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION");
522
523
524
525
526
  bool generateTopLevelProjectOnly =
    mf->IsOn("CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY");
  bool isTopLevel =
    !root->GetStateSnapshot().GetBuildsystemDirectoryParent().IsValid();
  if (regenerate && (isTopLevel || !generateTopLevelProjectOnly)) {
527
    this->CreateReRunCMakeFile(root, gens);
528
    std::string file =
529
      this->ConvertToRelativeForMake(this->CurrentReRunCMakeMakefile);
530
    cmSystemTools::ReplaceString(file, "\\ ", " ");
531
    cmTarget* check = mf->AddUtilityCommand(
532
      CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmCommandOrigin::Generator, true,
533
      no_working_directory, no_byproducts, no_depends,
534
      cmMakeSingleCommandLine({ "make", "-f", file }));
535

536
    cmGeneratorTarget* checkGt = new cmGeneratorTarget(check, root);
537
    root->AddGeneratorTarget(checkGt);
538
  }
539

540
541
  // now make the allbuild depend on all the non-utility targets
  // in the project
542
  for (auto& gen : gens) {
543
    for (auto target : gen->GetGeneratorTargets()) {
544
      if (target->GetType() == cmStateEnums::GLOBAL_TARGET) {
545
        continue;
546
      }
547

548
549
      if (regenerate &&
          (target->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET)) {
550
        target->Target->AddUtility(CMAKE_CHECK_BUILD_SYSTEM_TARGET);
551
      }
552

553
      // make all exe, shared libs and modules
554
555
556
      // run the depend check makefile as a post build rule
      // this will make sure that when the next target is built
      // things are up-to-date
Gregor Jasny's avatar
Gregor Jasny committed
557
      if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
558
        commandLines.front().back() = // fill placeholder
559
          this->PostBuildMakeTarget(target->GetName(), "$(CONFIGURATION)");
560
        gen->GetMakefile()->AddCustomCommandToTarget(
561
          target->GetName(), no_byproducts, no_depends, commandLines,
562
563
564
          cmCustomCommandType::POST_BUILD, "Depend check for xcode",
          dir.c_str(), true, false, "", "", false,
          cmObjectLibraryCommands::Accept);
565
      }
566

567
      if (!this->IsExcluded(gens[0], target)) {
568
        allbuild->AddUtility(target->GetName());
569
      }
Bill Hoffman's avatar
Bill Hoffman committed
570
    }
571
  }
Bill Hoffman's avatar
Bill Hoffman committed
572
573
}

574
575
void cmGlobalXCodeGenerator::CreateReRunCMakeFile(
  cmLocalGenerator* root, std::vector<cmLocalGenerator*> const& gens)
576
{
577
  std::vector<std::string> lfiles;
578
  for (auto gen : gens) {
579
    cmAppend(lfiles, gen->GetMakefile()->GetListFiles());
580
  }
581

582
  // sort the array
wahikihiki's avatar
wahikihiki committed
583
584
  std::sort(lfiles.begin(), lfiles.end());
  lfiles.erase(std::unique(lfiles.begin(), lfiles.end()), lfiles.end());
585
586
587
588
589
590

  cmake* cm = this->GetCMakeInstance();
  if (cm->DoWriteGlobVerifyTarget()) {
    lfiles.emplace_back(cm->GetGlobVerifyStamp());
  }

591
592
  this->CurrentReRunCMakeMakefile =
    cmStrCat(root->GetCurrentBinaryDirectory(), "/CMakeScripts");
593
  cmSystemTools::MakeDirectory(this->CurrentReRunCMakeMakefile);
Ken Martin's avatar
Ken Martin committed
594
  this->CurrentReRunCMakeMakefile += "/ReRunCMake.make";
595
  cmGeneratedFileStream makefileStream(this->CurrentReRunCMakeMakefile);
596
  makefileStream.SetCopyIfDifferent(true);
597
598
  makefileStream << "# Generated by CMake, DO NOT EDIT\n\n";

599
  makefileStream << "TARGETS:= \n";
600
601
602
603
  makefileStream << "empty:= \n";
  makefileStream << "space:= $(empty) $(empty)\n";
  makefileStream << "spaceplus:= $(empty)\\ $(empty)\n\n";

604
  for (const auto& lfile : lfiles) {
605
    makefileStream << "TARGETS += $(subst $(space),$(spaceplus),$(wildcard "
606
                   << this->ConvertToRelativeForMake(lfile) << "))\n";
607
  }
608
  makefileStream << "\n";
609

610
611
  std::string checkCache =
    cmStrCat(root->GetBinaryDirectory(), "/CMakeFiles/cmake.check_cache");
612

613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
  if (cm->DoWriteGlobVerifyTarget()) {
    makefileStream << ".NOTPARALLEL:\n\n";
    makefileStream << ".PHONY: all VERIFY_GLOBS\n\n";
    makefileStream << "all: VERIFY_GLOBS "
                   << this->ConvertToRelativeForMake(checkCache) << "\n\n";
    makefileStream << "VERIFY_GLOBS:\n";
    makefileStream << "\t"
                   << this->ConvertToRelativeForMake(
                        cmSystemTools::GetCMakeCommand())
                   << " -P "
                   << this->ConvertToRelativeForMake(cm->GetGlobVerifyScript())
                   << "\n\n";
  }

  makefileStream << this->ConvertToRelativeForMake(checkCache)
628
                 << ": $(TARGETS)\n";
629
630
  makefileStream << "\t"
                 << this->ConvertToRelativeForMake(
631
                      cmSystemTools::GetCMakeCommand())
632
633
634
635
636
                 << " -H"
                 << this->ConvertToRelativeForMake(root->GetSourceDirectory())
                 << " -B"
                 << this->ConvertToRelativeForMake(root->GetBinaryDirectory())
                 << "\n";
637
638
}

Gregor Jasny's avatar
Gregor Jasny committed
639
640
641
642
643
644
645
646
647
648
649
static bool objectIdLessThan(cmXCodeObject* l, cmXCodeObject* r)
{
  return l->GetId() < r->GetId();
}

void cmGlobalXCodeGenerator::SortXCodeObjects()
{
  std::sort(this->XCodeObjects.begin(), this->XCodeObjects.end(),
            objectIdLessThan);
}

Bill Hoffman's avatar
Bill Hoffman committed
650
651
void cmGlobalXCodeGenerator::ClearXCodeObjects()
{
Ken Martin's avatar
Ken Martin committed
652
  this->TargetDoneSet.clear();
653
654
  for (auto& obj : this->XCodeObjects) {
    delete obj;
655
  }
Ken Martin's avatar
Ken Martin committed
656
  this->XCodeObjects.clear();
657
  this->XCodeObjectIDs.clear();
658
  this->XCodeObjectMap.clear();
Ken Martin's avatar
Ken Martin committed
659
660
661
  this->GroupMap.clear();
  this->GroupNameMap.clear();
  this->TargetGroup.clear();
662
  this->FileRefs.clear();
Bill Hoffman's avatar
Bill Hoffman committed
663
664
}

665
void cmGlobalXCodeGenerator::addObject(cmXCodeObject* obj)
666
{
667
  if (obj->GetType() == cmXCodeObject::OBJECT) {
668
    const std::string& id = obj->GetId();
669
670
671

    // If this is a duplicate id, it's an error:
    //
672
    if (this->XCodeObjectIDs.count(id)) {
673
674
      cmSystemTools::Error(
        "Xcode generator: duplicate object ids not allowed");
675
    }
676
677

    this->XCodeObjectIDs.insert(id);
678
  }
679
680
681
682

  this->XCodeObjects.push_back(obj);
}

683
684
cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(
  cmXCodeObject::PBXType ptype)
Bill Hoffman's avatar
Bill Hoffman committed
685
{
686
  cmXCodeObject* obj = new cmXCode21Object(ptype, cmXCodeObject::OBJECT);
687
  this->addObject(obj);
Bill Hoffman's avatar
Bill Hoffman committed
688
689
690
  return obj;
}

691
cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(cmXCodeObject::Type type)
692
{
Bill Hoffman's avatar
Bill Hoffman committed
693
  cmXCodeObject* obj = new cmXCodeObject(cmXCodeObject::None, type);
694
  this->addObject(obj);
Bill Hoffman's avatar
Bill Hoffman committed
695
696
697
  return obj;
}

698
cmXCodeObject* cmGlobalXCodeGenerator::CreateString(const std::string& s)
Bill Hoffman's avatar
Bill Hoffman committed
699
700
{
  cmXCodeObject* obj = this->CreateObject(cmXCodeObject::STRING);
701
702
703
  obj->SetString(s);
  return obj;
}
704

705
706
cmXCodeObject* cmGlobalXCodeGenerator::CreateObjectReference(
  cmXCodeObject* ref)
Bill Hoffman's avatar
Bill Hoffman committed
707
708
709
710
711
{
  cmXCodeObject* obj = this->CreateObject(cmXCodeObject::OBJECT_REF);
  obj->SetObject(ref);
  return obj;
}
712

713
cmXCodeObject* cmGlobalXCodeGenerator::CreateFlatClone(cmXCodeObject* orig)
714
715
716
717
718
719
{
  cmXCodeObject* obj = this->CreateObject(orig->GetType());
  obj->CopyAttributes(orig);
  return obj;
}

720
721
std::string GetGroupMapKeyFromPath(cmGeneratorTarget* target,
                                   const std::string& fullpath)
722
{
723
  std::string key(target->GetName());
724
  key += "-";
725
  key += fullpath;
726
727
728
  return key;
}

729
730
731
cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFileFromPath(
  const std::string& fullpath, cmGeneratorTarget* target,
  const std::string& lang, cmSourceFile* sf)
732
733
734
735
736
{
  // Using a map and the full path guarantees that we will always get the same
  // fileRef object for any given full path.
  //
  cmXCodeObject* fileRef =
737
    this->CreateXCodeFileReferenceFromPath(fullpath, target, lang, sf);
738
739
740
741
742
743
744
745

  cmXCodeObject* buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile);
  buildFile->SetComment(fileRef->GetComment());
  buildFile->AddAttribute("fileRef", this->CreateObjectReference(fileRef));

  return buildFile;
}

746
747
748
749
750
751
class XCodeGeneratorExpressionInterpreter
  : public cmGeneratorExpressionInterpreter
{
public:
  XCodeGeneratorExpressionInterpreter(cmSourceFile* sourceFile,
                                      cmLocalGenerator* localGenerator,
752
                                      cmGeneratorTarget* headTarget,
753
                                      const std::string& lang)
754
755
    : cmGeneratorExpressionInterpreter(
        localGenerator, "NO-PER-CONFIG-SUPPORT-IN-XCODE", headTarget, lang)
756
757
758
759
    , SourceFile(sourceFile)
  {
  }

wahikihiki's avatar
wahikihiki committed
760
761
762
763
764
  XCodeGeneratorExpressionInterpreter(
    XCodeGeneratorExpressionInterpreter const&) = delete;
  XCodeGeneratorExpressionInterpreter& operator=(
    XCodeGeneratorExpressionInterpreter const&) = delete;

765
766
  using cmGeneratorExpressionInterpreter::Evaluate;

767
768
  const std::string& Evaluate(const char* expression,
                              const std::string& property)
769
  {
770
    const std::string& processed =
771
      this->cmGeneratorExpressionInterpreter::Evaluate(expression, property);
772
    if (this->CompiledGeneratorExpression->GetHadContextSensitiveCondition()) {
773
774
775
776
777
778
      std::ostringstream e;
      /* clang-format off */
      e <<
          "Xcode does not support per-config per-source " << property << ":\n"
          "  " << expression << "\n"
          "specified for source:\n"
779
          "  " << this->SourceFile->ResolveFullPath() << "\n";
780
      /* clang-format on */
781
      this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str());
782
783
784
785
786
787
788
789
790
    }

    return processed;
  }

private:
  cmSourceFile* SourceFile = nullptr;
};

791
792
cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile(
  cmLocalGenerator* lg, cmSourceFile* sf, cmGeneratorTarget* gtgt)
Bill Hoffman's avatar
Bill Hoffman committed
793
{
794
795
796
  std::string lang = this->CurrentLocalGenerator->GetSourceFileLanguage(*sf);

  XCodeGeneratorExpressionInterpreter genexInterpreter(sf, lg, gtgt, lang);
797

798
  // Add flags from target and source file properties.
Bill Hoffman's avatar
Bill Hoffman committed
799
  std::string flags;
800
  const char* srcfmt = sf->GetProperty("Fortran_FORMAT");
801
  switch (cmOutputConverter::GetFortranFormat(srcfmt)) {
802
    case cmOutputConverter::FortranFormatFixed:
803
804
      flags = "-fixed " + flags;
      break;
805
    case cmOutputConverter::FortranFormatFree:
806
807
808
809
810
      flags = "-free " + flags;
      break;
    default:
      break;
  }
811
812
813
  const std::string COMPILE_FLAGS("COMPILE_FLAGS");
  if (const char* cflags = sf->GetProperty(COMPILE_FLAGS)) {
    lg->AppendFlags(flags, genexInterpreter.Evaluate(cflags, COMPILE_FLAGS));
814
  }
815
816
817
818
819
  const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
  if (const char* coptions = sf->GetProperty(COMPILE_OPTIONS)) {
    lg->AppendCompileOptions(
      flags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS));
  }
820

821
  // Add per-source definitions.
822
  BuildObjectListOrString flagsBuild(this, false);
823
824
  const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
  if (const char* compile_defs = sf->GetProperty(COMPILE_DEFINITIONS)) {
825
    this->AppendDefines(
826
827
      flagsBuild,
      genexInterpreter.Evaluate(compile_defs, COMPILE_DEFINITIONS).c_str(),
828
      true);
829
  }
830
831
832
833
834

  if (sf->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) {
    this->AppendDefines(flagsBuild, "CMAKE_SKIP_PRECOMPILE_HEADERS", true);
  }

835
836
  if (!flagsBuild.IsEmpty()) {
    if (!flags.empty()) {
837
838
      flags += ' ';
    }
839
840
    flags += flagsBuild.GetString();
  }
841

842
843
844
845
846
847
848
849
850
851
  // Add per-source include directories.
  std::vector<std::string> includes;
  const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
  if (const char* cincludes = sf->GetProperty(INCLUDE_DIRECTORIES)) {
    lg->AppendIncludeDirectories(
      includes, genexInterpreter.Evaluate(cincludes, INCLUDE_DIRECTORIES),
      *sf);
  }
  lg->AppendFlags(flags, lg->GetIncludeFlags(includes, gtgt, lang, true));

852
  cmXCodeObject* buildFile =
853
    this->CreateXCodeSourceFileFromPath(sf->ResolveFullPath(), gtgt, lang, sf);
854

855
  cmXCodeObject* settings = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
856
857
  settings->AddAttributeIfNotEmpty("COMPILER_FLAGS",
                                   this->CreateString(flags));
858

859
  cmGeneratorTarget::SourceFileFlags tsFlags =
860
    gtgt->GetTargetSourceFileFlags(sf);
861

862
863
  cmXCodeObject* attrs = this->CreateObject(cmXCodeObject::OBJECT_LIST);

864
865
866
  // Is this a "private" or "public" framework header file?
  // Set the ATTRIBUTES attribute appropriately...
  //
867
868
  if (gtgt->IsFrameworkOnApple()) {
    if