cmGlobalXCodeGenerator.cxx 131 KB
Newer Older
1
2
3
/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Bill Hoffman's avatar
Bill Hoffman committed
4

5
6
  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.
Bill Hoffman's avatar
Bill Hoffman committed
7

8
9
10
11
  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/
Bill Hoffman's avatar
Bill Hoffman committed
12
13
14
15
#include "cmGlobalXCodeGenerator.h"
#include "cmLocalXCodeGenerator.h"
#include "cmMakefile.h"
#include "cmXCodeObject.h"
16
#include "cmXCode21Object.h"
Bill Hoffman's avatar
Bill Hoffman committed
17
18
#include "cmake.h"
#include "cmGeneratedFileStream.h"
19
#include "cmComputeLinkInformation.h"
Bill Hoffman's avatar
Bill Hoffman committed
20
#include "cmSourceFile.h"
21
#include "cmCustomCommandGenerator.h"
22
#include "cmGeneratorTarget.h"
23
#include "cmGlobalGeneratorFactory.h"
24

25
26
#include <cmsys/auto_ptr.hxx>

27
//----------------------------------------------------------------------------
28
29
#if defined(CMAKE_BUILD_WITH_CMAKE)
#include "cmXMLParser.h"
30
31
32
33
34
35

// parse the xml file storing the installed version of Xcode on
// the machine
class cmXcodeVersionParser : public cmXMLParser
{
public:
36
  cmXcodeVersionParser(): Version("1.5") {}
37
  void StartElement(const std::string&, const char**)
38
    {
Ken Martin's avatar
Ken Martin committed
39
      this->Data = "";
40
    }
41
  void EndElement(const std::string& name)
42
    {
43
      if(name == "key")
44
        {
Ken Martin's avatar
Ken Martin committed
45
        this->Key = this->Data;
46
        }
47
      else if(name == "string")
48
        {
Ken Martin's avatar
Ken Martin committed
49
        if(this->Key == "CFBundleShortVersionString")
50
          {
51
          this->Version = this->Data;
52
53
54
55
56
          }
        }
    }
  void CharacterDataHandler(const char* data, int length)
    {
Ken Martin's avatar
Ken Martin committed
57
      this->Data.append(data, length);
58
    }
59
  std::string Version;
60
61
  std::string Key;
  std::string Data;
62
};
63
#endif
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Builds either an object list or a space-separated string from the
// given inputs.
class cmGlobalXCodeGenerator::BuildObjectListOrString
{
  cmGlobalXCodeGenerator *Generator;
  cmXCodeObject *Group;
  bool Empty;
  std::string String;

public:
  BuildObjectListOrString(cmGlobalXCodeGenerator *gen, bool buildObjectList)
    : Generator(gen), Group(0), Empty(true)
    {
    if (buildObjectList)
      {
      this->Group = this->Generator->CreateObject(cmXCodeObject::OBJECT_LIST);
      }
    }

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

  void Add(const char *newString)
    {
    this->Empty = false;

    if (this->Group)
      {
      this->Group->AddObject(this->Generator->CreateString(newString));
      }
    else
      {
      this->String += newString;
      this->String += ' ';
      }
    }

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

  cmXCodeObject *CreateList()
    {
    if (this->Group)
      {
      return this->Group;
      }
    else
      {
      return this->Generator->CreateString(this->String.c_str());
      }
    }
};

116
117
118
class cmGlobalXCodeGenerator::Factory : public cmGlobalGeneratorFactory
{
public:
119
120
  virtual cmGlobalGenerator* CreateGlobalGenerator(
                                              const std::string& name) const;
121
122

  virtual void GetDocumentation(cmDocumentationEntry& entry) const {
123
    cmGlobalXCodeGenerator::GetDocumentation(entry); }
124
125
126

  virtual void GetGenerators(std::vector<std::string>& names) const {
    names.push_back(cmGlobalXCodeGenerator::GetActualName()); }
127
128
};

Bill Hoffman's avatar
Bill Hoffman committed
129
//----------------------------------------------------------------------------
130
cmGlobalXCodeGenerator::cmGlobalXCodeGenerator(std::string const& version)
Bill Hoffman's avatar
Bill Hoffman committed
131
{
132
133
134
135
136
137
138
  this->VersionString = version;

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

Ken Martin's avatar
Ken Martin committed
139
140
141
142
  this->FindMakeProgramFile = "CMakeFindXCode.cmake";
  this->RootObject = 0;
  this->MainGroupChildren = 0;
  this->SourcesGroupChildren = 0;
143
  this->ResourcesGroupChildren = 0;
Ken Martin's avatar
Ken Martin committed
144
145
  this->CurrentMakefile = 0;
  this->CurrentLocalGenerator = 0;
146
147
148
}

//----------------------------------------------------------------------------
149
150
151
152
153
154
155
cmGlobalGeneratorFactory* cmGlobalXCodeGenerator::NewFactory()
{
  return new Factory;
}

//----------------------------------------------------------------------------
cmGlobalGenerator* cmGlobalXCodeGenerator::Factory
156
::CreateGlobalGenerator(const std::string& name) const
157
{
158
  if (name != GetActualName())
159
    return 0;
160
#if defined(CMAKE_BUILD_WITH_CMAKE)
161
  cmXcodeVersionParser parser;
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  std::string versionFile;
  {
  std::string out;
  std::string::size_type pos;
  if(cmSystemTools::RunSingleCommand("xcode-select --print-path", &out, 0, 0,
                                     cmSystemTools::OUTPUT_NONE) &&
     (pos = out.find(".app/"), pos != out.npos))
    {
    versionFile = out.substr(0, pos+5)+"Contents/version.plist";
    }
  }
  if(!versionFile.empty() && cmSystemTools::FileExists(versionFile.c_str()))
    {
    parser.ParseFile(versionFile.c_str());
    }
  else if (cmSystemTools::FileExists(
             "/Applications/Xcode.app/Contents/version.plist"))
179
180
181
182
183
184
185
186
187
    {
    parser.ParseFile
      ("/Applications/Xcode.app/Contents/version.plist");
    }
  else
    {
    parser.ParseFile
      ("/Developer/Applications/Xcode.app/Contents/version.plist");
    }
188
189
190
  cmsys::auto_ptr<cmGlobalXCodeGenerator>
    gg(new cmGlobalXCodeGenerator(parser.Version));
  if (gg->XcodeVersion == 20)
191
192
193
    {
    cmSystemTools::Message("Xcode 2.0 not really supported by cmake, "
                           "using Xcode 15 generator\n");
194
    gg->XcodeVersion = 15;
195
    }
196
  return gg.release();
197
#else
198
  std::cerr << "CMake should be built with cmake to use Xcode, "
Ken Martin's avatar
Ken Martin committed
199
    "default to Xcode 1.5\n";
200
201
  return new cmGlobalXCodeGenerator;
#endif
Bill Hoffman's avatar
Bill Hoffman committed
202
203
}

204
205
206
207
208
//----------------------------------------------------------------------------
bool cmGlobalXCodeGenerator::SetGeneratorToolset(std::string const& ts)
{
  if(this->XcodeVersion >= 30)
    {
209
    this->GeneratorToolset = ts;
210
211
212
213
214
215
216
217
    return true;
    }
  else
    {
    return cmGlobalGenerator::SetGeneratorToolset(ts);
    }
}

Bill Hoffman's avatar
Bill Hoffman committed
218
//----------------------------------------------------------------------------
219
220
void cmGlobalXCodeGenerator::EnableLanguage(std::vector<std::string>const&
                                            lang,
Alexander Neundorf's avatar
   
Alexander Neundorf committed
221
                                            cmMakefile * mf, bool optional)
222
{
223
  mf->AddDefinition("XCODE","1");
224
  mf->AddDefinition("XCODE_VERSION", this->VersionString.c_str());
Ken Martin's avatar
Ken Martin committed
225
  if(this->XcodeVersion == 15)
226
227
228
229
    {
    }
  else
    {
230
231
232
233
234
235
236
237
238
239
    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.",
        cmCacheManager::STRING);
      }
240
    }
Bill Hoffman's avatar
Bill Hoffman committed
241
  mf->AddDefinition("CMAKE_GENERATOR_NO_COMPILER_ENV", "1");
242
  if(!this->GeneratorToolset.empty())
243
244
    {
    mf->AddDefinition("CMAKE_XCODE_PLATFORM_TOOLSET",
245
                      this->GeneratorToolset.c_str());
246
    }
Alexander Neundorf's avatar
   
Alexander Neundorf committed
247
  this->cmGlobalGenerator::EnableLanguage(lang, mf, optional);
248
    const char* osxArch =
249
      mf->GetDefinition("CMAKE_OSX_ARCHITECTURES");
250
  const char* sysroot =
251
252
253
254
255
256
257
      mf->GetDefinition("CMAKE_OSX_SYSROOT");
  if(osxArch && sysroot)
    {
    this->Architectures.clear();
    cmSystemTools::ExpandListArgument(std::string(osxArch),
                                      this->Architectures);
    }
Bill Hoffman's avatar
Bill Hoffman committed
258
259
260
}

//----------------------------------------------------------------------------
261
262
263
void
cmGlobalXCodeGenerator::GenerateBuildCommand(
  std::vector<std::string>& makeCommand,
264
  const std::string& makeProgram,
265
  const std::string& projectName,
266
  const std::string& /*projectDir*/,
267
  const std::string& targetName,
268
  const std::string& config,
269
270
  bool /*fast*/,
  std::vector<std::string> const& makeOptions)
Bill Hoffman's avatar
Bill Hoffman committed
271
{
272
  // now build the test
273
274
275
  makeCommand.push_back(
    this->SelectMakeProgram(makeProgram, "xcodebuild")
    );
276

277
278
279
  makeCommand.push_back("-project");
  std::string projectArg = projectName;
  projectArg += ".xcode";
Ken Martin's avatar
Ken Martin committed
280
  if(this->XcodeVersion > 20)
281
    {
282
    projectArg += "proj";
283
    }
284
  makeCommand.push_back(projectArg);
285

286
  bool clean = false;
287
288
  std::string realTarget = targetName;
  if ( realTarget == "clean" )
289
290
    {
    clean = true;
291
    realTarget = "ALL_BUILD";
292
    }
293
294
  if(clean)
    {
295
    makeCommand.push_back("clean");
296
297
298
    }
  else
    {
299
    makeCommand.push_back("build");
300
    }
301
  makeCommand.push_back("-target");
302
  if (!realTarget.empty())
303
    {
304
    makeCommand.push_back(realTarget);
305
    }
Bill Hoffman's avatar
Bill Hoffman committed
306
307
  else
    {
308
    makeCommand.push_back("ALL_BUILD");
Bill Hoffman's avatar
Bill Hoffman committed
309
    }
Ken Martin's avatar
Ken Martin committed
310
  if(this->XcodeVersion == 15)
311
    {
312
313
    makeCommand.push_back("-buildstyle");
    makeCommand.push_back("Development");
314
315
316
    }
  else
    {
317
    makeCommand.push_back("-configuration");
318
    makeCommand.push_back(!config.empty()?config:"Debug");
319
    }
320
321
  makeCommand.insert(makeCommand.end(),
                     makeOptions.begin(), makeOptions.end());
Bill Hoffman's avatar
Bill Hoffman committed
322
323
324
325
326
327
328
329
330
331
332
333
334
335
}

//----------------------------------------------------------------------------
///! Create a local generator appropriate to this Global Generator
cmLocalGenerator *cmGlobalXCodeGenerator::CreateLocalGenerator()
{
  cmLocalGenerator *lg = new cmLocalXCodeGenerator;
  lg->SetGlobalGenerator(this);
  return lg;
}

//----------------------------------------------------------------------------
void cmGlobalXCodeGenerator::Generate()
{
336
  std::map<std::string, std::vector<cmLocalGenerator*> >::iterator it;
337
338
  // make sure extra targets are added before calling
  // the parent generate which will call trace depends
Ken Martin's avatar
Ken Martin committed
339
  for(it = this->ProjectMap.begin(); it!= this->ProjectMap.end(); ++it)
340
    {
341
    cmLocalGenerator* root = it->second[0];
342
    this->SetGenerationRoot(root);
343
344
    // add ALL_BUILD, INSTALL, etc
    this->AddExtraTargets(root, it->second);
345
346
    }
  this->cmGlobalGenerator::Generate();
347
348
349
350
  if(cmSystemTools::GetErrorOccuredFlag())
    {
    return;
    }
351
  for(it = this->ProjectMap.begin(); it!= this->ProjectMap.end(); ++it)
352
    {
353
    cmLocalGenerator* root = it->second[0];
354
    this->SetGenerationRoot(root);
355
356
357
358
    // now create the project
    this->OutputXCodeProject(root, it->second);
    }
}
359

360
361
362
363
364
//----------------------------------------------------------------------------
void cmGlobalXCodeGenerator::SetGenerationRoot(cmLocalGenerator* root)
{
  this->CurrentProject = root->GetMakefile()->GetProjectName();
  this->SetCurrentLocalGenerator(root);
365
366
367
  cmSystemTools::SplitPath(this->CurrentMakefile->GetCurrentDirectory(),
                           this->ProjectSourceDirectoryComponents);
  cmSystemTools::SplitPath(this->CurrentMakefile->GetCurrentOutputDirectory(),
368
369
370
371
372
373
374
375
376
                           this->ProjectOutputDirectoryComponents);

  this->CurrentXCodeHackMakefile =
    root->GetMakefile()->GetCurrentOutputDirectory();
  this->CurrentXCodeHackMakefile += "/CMakeScripts";
  cmSystemTools::MakeDirectory(this->CurrentXCodeHackMakefile.c_str());
  this->CurrentXCodeHackMakefile += "/XCODE_DEPEND_HELPER.make";
}

377
378
379
380
381
//----------------------------------------------------------------------------
std::string
cmGlobalXCodeGenerator::PostBuildMakeTarget(std::string const& tName,
                                            std::string const& configName)
{
382
383
384
  std::string target = tName;
  cmSystemTools::ReplaceString(target, " ", "_");
  std::string out = "PostBuild." + target;
385
386
387
388
389
390
391
  if(this->XcodeVersion > 20)
    {
    out += "." + configName;
    }
  return out;
}

392
393
394
//----------------------------------------------------------------------------
#define CMAKE_CHECK_BUILD_SYSTEM_TARGET "ZERO_CHECK"

395
//----------------------------------------------------------------------------
396
397
void
cmGlobalXCodeGenerator::AddExtraTargets(cmLocalGenerator* root,
398
399
400
                                        std::vector<cmLocalGenerator*>& gens)
{
  cmMakefile* mf = root->GetMakefile();
401

402
  // Add ALL_BUILD
403
  const char* no_working_directory = 0;
404
  std::vector<std::string> no_depends;
Ken Martin's avatar
Ken Martin committed
405
  mf->AddUtilityCommand("ALL_BUILD", true, no_depends,
406
                        no_working_directory,
407
                        "echo", "Build all projects");
408
  cmTarget* allbuild = mf->FindTarget("ALL_BUILD");
409

410
411
412
413
  // Refer to the main build configuration file for easy editing.
  std::string listfile = mf->GetStartDirectory();
  listfile += "/";
  listfile += "CMakeLists.txt";
414
  allbuild->AddSourceCMP0049(listfile.c_str());
415

416
  // Add XCODE depend helper
417
  std::string dir = mf->GetCurrentOutputDirectory();
418
419
420
421
422
423
424
425
426
427
  cmCustomCommandLine makeHelper;
  if(this->XcodeVersion < 50)
    {
    makeHelper.push_back("make");
    makeHelper.push_back("-C");
    makeHelper.push_back(dir.c_str());
    makeHelper.push_back("-f");
    makeHelper.push_back(this->CurrentXCodeHackMakefile.c_str());
    makeHelper.push_back(""); // placeholder, see below
    }
428

429
430
431
432
433
434
435
436
437
438
439
440
  // Add ZERO_CHECK
  bool regenerate = !mf->IsOn("CMAKE_SUPPRESS_REGENERATION");
  if (regenerate)
    {
    this->CreateReRunCMakeFile(root, gens);
    std::string file = this->ConvertToRelativeForMake(
      this->CurrentReRunCMakeMakefile.c_str());
    cmSystemTools::ReplaceString(file, "\\ ", " ");
    mf->AddUtilityCommand(CMAKE_CHECK_BUILD_SYSTEM_TARGET, true, no_depends,
                          no_working_directory,
                          "make", "-f", file.c_str());
    }
441

442
443
444
445
446
  // now make the allbuild depend on all the non-utility targets
  // in the project
  for(std::vector<cmLocalGenerator*>::iterator i = gens.begin();
      i != gens.end(); ++i)
    {
447
    cmLocalGenerator* lg = *i;
448
449
450
451
    if(this->IsExcluded(root, *i))
      {
      continue;
      }
452

453
454
    cmTargets& tgts = lg->GetMakefile()->GetTargets();
    for(cmTargets::iterator l = tgts.begin(); l != tgts.end(); l++)
Bill Hoffman's avatar
Bill Hoffman committed
455
      {
456
      cmTarget& target = l->second;
457
458
459
460
461
462

      if (regenerate && (l->first != CMAKE_CHECK_BUILD_SYSTEM_TARGET))
        {
        target.AddUtility(CMAKE_CHECK_BUILD_SYSTEM_TARGET);
        }

463
      // make all exe, shared libs and modules
464
465
466
      // 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
467
468
      if(!makeHelper.empty() &&
         (target.GetType() == cmTarget::EXECUTABLE ||
469
470
// Nope - no post-build for OBJECT_LIRBRARY
//          target.GetType() == cmTarget::OBJECT_LIBRARY ||
471
          target.GetType() == cmTarget::STATIC_LIBRARY ||
472
473
          target.GetType() == cmTarget::SHARED_LIBRARY ||
          target.GetType() == cmTarget::MODULE_LIBRARY))
474
        {
475
        makeHelper[makeHelper.size()-1] = // fill placeholder
476
477
          this->PostBuildMakeTarget(target.GetName(), "$(CONFIGURATION)");
        cmCustomCommandLines commandLines;
478
        commandLines.push_back(makeHelper);
479
480
481
482
483
484
        lg->GetMakefile()->AddCustomCommandToTarget(target.GetName(),
                                                    no_depends,
                                                    commandLines,
                                                    cmTarget::POST_BUILD,
                                                    "Depend check for xcode",
                                                    dir.c_str());
485
        }
486

487
488
      if(target.GetType() != cmTarget::INTERFACE_LIBRARY
          && !target.GetPropertyAsBool("EXCLUDE_FROM_ALL"))
Bill Hoffman's avatar
Bill Hoffman committed
489
        {
490
        allbuild->AddUtility(target.GetName());
Bill Hoffman's avatar
Bill Hoffman committed
491
        }
492
493
494
495
496

      // Refer to the build configuration file for easy editing.
      listfile = lg->GetMakefile()->GetStartDirectory();
      listfile += "/";
      listfile += "CMakeLists.txt";
497
      target.AddSourceCMP0049(listfile.c_str());
Bill Hoffman's avatar
Bill Hoffman committed
498
      }
Bill Hoffman's avatar
Bill Hoffman committed
499
500
501
    }
}

502
//----------------------------------------------------------------------------
503
504
void cmGlobalXCodeGenerator::CreateReRunCMakeFile(
  cmLocalGenerator* root, std::vector<cmLocalGenerator*> const& gens)
505
506
{
  cmMakefile* mf = root->GetMakefile();
507
508
509
510
511
512
513
514
  std::vector<std::string> lfiles;
  for(std::vector<cmLocalGenerator*>::const_iterator gi = gens.begin();
      gi != gens.end(); ++gi)
    {
    std::vector<std::string> const& lf = (*gi)->GetMakefile()->GetListFiles();
    lfiles.insert(lfiles.end(), lf.begin(), lf.end());
    }

515
  // sort the array
516
517
  std::sort(lfiles.begin(), lfiles.end(), std::less<std::string>());
  std::vector<std::string>::iterator new_end =
518
519
    std::unique(lfiles.begin(), lfiles.end());
  lfiles.erase(new_end, lfiles.end());
520
  this->CurrentReRunCMakeMakefile = mf->GetStartOutputDirectory();
Ken Martin's avatar
Ken Martin committed
521
522
523
  this->CurrentReRunCMakeMakefile += "/CMakeScripts";
  cmSystemTools::MakeDirectory(this->CurrentReRunCMakeMakefile.c_str());
  this->CurrentReRunCMakeMakefile += "/ReRunCMake.make";
Ken Martin's avatar
Ken Martin committed
524
525
  cmGeneratedFileStream makefileStream
    (this->CurrentReRunCMakeMakefile.c_str());
526
  makefileStream.SetCopyIfDifferent(true);
527
  makefileStream << "# Generated by CMake, DO NOT EDIT\n";
528
529
530
531
532
533
  std::string checkCache = mf->GetHomeOutputDirectory();
  checkCache += "/";
  checkCache += cmake::GetCMakeFilesDirectoryPostSlash();
  checkCache += "cmake.check_cache";
  makefileStream << this->ConvertToRelativeForMake(checkCache.c_str())
                 << ": ";
534
535
536
  for(std::vector<std::string>::const_iterator i = lfiles.begin();
      i !=  lfiles.end(); ++i)
    {
Andy Cedilnik's avatar
Andy Cedilnik committed
537
    makefileStream << "\\\n" << this->ConvertToRelativeForMake(i->c_str());
538
539
    }
  std::string cmake = mf->GetRequiredDefinition("CMAKE_COMMAND");
540
  makefileStream << "\n\t" << this->ConvertToRelativeForMake(cmake.c_str())
541
542
543
544
545
546
                 << " -H" << this->ConvertToRelativeForMake(
                   mf->GetHomeDirectory())
                 << " -B" << this->ConvertToRelativeForMake(
                   mf->GetHomeOutputDirectory()) << "\n";
}

Bill Hoffman's avatar
Bill Hoffman committed
547
548
549
//----------------------------------------------------------------------------
void cmGlobalXCodeGenerator::ClearXCodeObjects()
{
Ken Martin's avatar
Ken Martin committed
550
551
  this->TargetDoneSet.clear();
  for(unsigned int i = 0; i < this->XCodeObjects.size(); ++i)
Bill Hoffman's avatar
Bill Hoffman committed
552
    {
Ken Martin's avatar
Ken Martin committed
553
    delete this->XCodeObjects[i];
Bill Hoffman's avatar
Bill Hoffman committed
554
    }
Ken Martin's avatar
Ken Martin committed
555
  this->XCodeObjects.clear();
556
  this->XCodeObjectIDs.clear();
Ken Martin's avatar
Ken Martin committed
557
558
559
  this->GroupMap.clear();
  this->GroupNameMap.clear();
  this->TargetGroup.clear();
560
  this->FileRefs.clear();
Bill Hoffman's avatar
Bill Hoffman committed
561
562
}

563
564
565
566
567
//----------------------------------------------------------------------------
void cmGlobalXCodeGenerator::addObject(cmXCodeObject *obj)
{
  if(obj->GetType() == cmXCodeObject::OBJECT)
    {
568
    std::string id = obj->GetId();
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583

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

    this->XCodeObjectIDs.insert(id);
    }

  this->XCodeObjects.push_back(obj);
}

Bill Hoffman's avatar
Bill Hoffman committed
584
//----------------------------------------------------------------------------
585
cmXCodeObject*
586
cmGlobalXCodeGenerator::CreateObject(cmXCodeObject::PBXType ptype)
Bill Hoffman's avatar
Bill Hoffman committed
587
{
588
  cmXCodeObject* obj;
Ken Martin's avatar
Ken Martin committed
589
  if(this->XcodeVersion == 15)
590
591
592
593
594
595
596
    {
    obj = new cmXCodeObject(ptype, cmXCodeObject::OBJECT);
    }
  else
    {
    obj = new cmXCode21Object(ptype, cmXCodeObject::OBJECT);
    }
597
  this->addObject(obj);
Bill Hoffman's avatar
Bill Hoffman committed
598
599
600
  return obj;
}

Bill Hoffman's avatar
Bill Hoffman committed
601
//----------------------------------------------------------------------------
602
cmXCodeObject*
603
cmGlobalXCodeGenerator::CreateObject(cmXCodeObject::Type type)
604
{
Bill Hoffman's avatar
Bill Hoffman committed
605
  cmXCodeObject* obj = new cmXCodeObject(cmXCodeObject::None, type);
606
  this->addObject(obj);
Bill Hoffman's avatar
Bill Hoffman committed
607
608
609
  return obj;
}

610
//----------------------------------------------------------------------------
611
cmXCodeObject*
612
cmGlobalXCodeGenerator::CreateString(const std::string& s)
Bill Hoffman's avatar
Bill Hoffman committed
613
614
{
  cmXCodeObject* obj = this->CreateObject(cmXCodeObject::STRING);
615
616
617
  obj->SetString(s);
  return obj;
}
618
619

//----------------------------------------------------------------------------
Ken Martin's avatar
Ken Martin committed
620
621
cmXCodeObject* cmGlobalXCodeGenerator
::CreateObjectReference(cmXCodeObject* ref)
Bill Hoffman's avatar
Bill Hoffman committed
622
623
624
625
626
{
  cmXCodeObject* obj = this->CreateObject(cmXCodeObject::OBJECT_REF);
  obj->SetObject(ref);
  return obj;
}
627

628
//----------------------------------------------------------------------------
629
std::string
630
GetGroupMapKeyFromPath(cmTarget& cmtarget, const std::string& fullpath)
631
{
632
  std::string key(cmtarget.GetName());
633
  key += "-";
634
  key += fullpath;
635
636
637
  return key;
}

638
//----------------------------------------------------------------------------
639
std::string
640
641
642
643
644
645
646
647
648
649
GetGroupMapKey(cmTarget& cmtarget, cmSourceFile* sf)
{
  return GetGroupMapKeyFromPath(cmtarget, sf->GetFullPath());
}

//----------------------------------------------------------------------------
cmXCodeObject*
cmGlobalXCodeGenerator::CreateXCodeSourceFileFromPath(
  const std::string &fullpath,
  cmTarget& cmtarget,
650
651
  const std::string &lang,
  cmSourceFile* sf)
652
653
654
655
656
{
  // Using a map and the full path guarantees that we will always get the same
  // fileRef object for any given full path.
  //
  cmXCodeObject* fileRef =
657
    this->CreateXCodeFileReferenceFromPath(fullpath, cmtarget, lang, sf);
658
659
660
661
662
663
664
665

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

  return buildFile;
}

666
//----------------------------------------------------------------------------
667
668
cmXCodeObject*
cmGlobalXCodeGenerator::CreateXCodeSourceFile(cmLocalGenerator* lg,
669
670
                                              cmSourceFile* sf,
                                              cmTarget& cmtarget)
Bill Hoffman's avatar
Bill Hoffman committed
671
{
672
  // Add flags from target and source file properties.
Bill Hoffman's avatar
Bill Hoffman committed
673
  std::string flags;
674
675
676
677
678
679
680
  const char* srcfmt = sf->GetProperty("Fortran_FORMAT");
  switch(this->CurrentLocalGenerator->GetFortranFormat(srcfmt))
    {
    case cmLocalGenerator::FortranFormatFixed: flags="-fixed "+flags; break;
    case cmLocalGenerator::FortranFormatFree: flags="-free "+flags; break;
    default: break;
    }
Bill Hoffman's avatar
Bill Hoffman committed
681
  lg->AppendFlags(flags, sf->GetProperty("COMPILE_FLAGS"));
682

683
  // Add per-source definitions.
684
685
686
687
688
689
690
691
692
693
694
  BuildObjectListOrString flagsBuild(this, false);
  this->AppendDefines(flagsBuild,
                      sf->GetProperty("COMPILE_DEFINITIONS"), true);
  if (!flagsBuild.IsEmpty())
    {
    if (flags.size())
      {
      flags += ' ';
      }
    flags += flagsBuild.GetString();
    }
695

696
  std::string lang =
697
    this->CurrentLocalGenerator->GetSourceFileLanguage(*sf);
698

699
  cmXCodeObject* buildFile =
700
    this->CreateXCodeSourceFileFromPath(sf->GetFullPath(), cmtarget, lang, sf);
701
  cmXCodeObject* fileRef = buildFile->GetObject("fileRef")->GetObject();
702

703
  cmXCodeObject* settings =
Ken Martin's avatar
Ken Martin committed
704
    this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
Bill Hoffman's avatar
Bill Hoffman committed
705
  settings->AddAttribute("COMPILER_FLAGS", this->CreateString(flags.c_str()));
706
707
708

  // Is this a resource file in this target? Add it to the resources group...
  //
709
710
711
  cmGeneratorTarget::SourceFileFlags tsFlags =
            this->GetGeneratorTarget(&cmtarget)->GetTargetSourceFileFlags(sf);
  bool isResource = tsFlags.Type == cmGeneratorTarget::SourceFileTypeResource;
712
713
714
715

  // Is this a "private" or "public" framework header file?
  // Set the ATTRIBUTES attribute appropriately...
  //
716
  if(cmtarget.IsFrameworkOnApple())
717
    {
718
    if(tsFlags.Type == cmGeneratorTarget::SourceFileTypePrivateHeader)
719
720
721
722
723
724
      {
      cmXCodeObject* attrs = this->CreateObject(cmXCodeObject::OBJECT_LIST);
      attrs->AddObject(this->CreateString("Private"));
      settings->AddAttribute("ATTRIBUTES", attrs);
      isResource = true;
      }
725
    else if(tsFlags.Type == cmGeneratorTarget::SourceFileTypePublicHeader)
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
      {
      cmXCodeObject* attrs = this->CreateObject(cmXCodeObject::OBJECT_LIST);
      attrs->AddObject(this->CreateString("Public"));
      settings->AddAttribute("ATTRIBUTES", attrs);
      isResource = true;
      }
    }

  // Add the fileRef to the top level Resources group/folder if it is not
  // already there.
  //
  if(isResource && this->ResourcesGroupChildren &&
    !this->ResourcesGroupChildren->HasObject(fileRef))
    {
    this->ResourcesGroupChildren->AddObject(fileRef);
    }

Bill Hoffman's avatar
Bill Hoffman committed
743
  buildFile->AddAttribute("settings", settings);
744
745
746
747
  return buildFile;
}

//----------------------------------------------------------------------------
748
749
std::string
GetSourcecodeValueFromFileExtension(const std::string& _ext,
Ruslan Baratov's avatar
Ruslan Baratov committed
750
751
                                    const std::string& lang,
                                    bool& keepLastKnownFileType)
752
{
753
  std::string ext = cmSystemTools::LowerCase(_ext);
Bill Hoffman's avatar
Bill Hoffman committed
754
  std::string sourcecode = "sourcecode";
755

756
  if(ext == "o")
Bill Hoffman's avatar
Bill Hoffman committed
757
    {
758
    sourcecode = "compiled.mach-o.objfile";
Bill Hoffman's avatar
Bill Hoffman committed
759
    }
760
761
  else if(ext == "xib")
    {
Ruslan Baratov's avatar
Ruslan Baratov committed
762
    keepLastKnownFileType = true;
763
764
    sourcecode = "file.xib";
    }
765
766
  else if(ext == "storyboard")
    {
Ruslan Baratov's avatar
Ruslan Baratov committed
767
    keepLastKnownFileType = true;
768
769
    sourcecode = "file.storyboard";
    }
770
  else if(ext == "mm")
771
772
773
    {
    sourcecode += ".cpp.objcpp";
    }
774
775
  else if(ext == "m")
    {
776
    sourcecode += ".c.objc";
777
    }
778
779
780
781
  else if(ext == "plist")
    {
    sourcecode += ".text.plist";
    }
782
  else if(ext == "h")
Bill Hoffman's avatar
Bill Hoffman committed
783
    {
784
785
    sourcecode += ".c.h";
    }
786
  else if(ext == "hxx" || ext == "hpp" || ext == "txx"
787
    || ext == "pch" || ext == "hh")
788
789
    {
    sourcecode += ".cpp.h";
Bill Hoffman's avatar
Bill Hoffman committed
790
    }
791
  else if(ext == "png" || ext == "gif" || ext == "jpg")
Bill Hoffman's avatar
Bill Hoffman committed
792
    {
Ruslan Baratov's avatar
Ruslan Baratov committed
793
    keepLastKnownFileType = true;
794
    sourcecode = "image";
Bill Hoffman's avatar
Bill Hoffman committed
795
    }
796
  else if(ext == "txt")
797
    {
798
    sourcecode += ".text";
799
    }
800
  else if(lang == "CXX")
801
    {
802
    sourcecode += ".cpp.cpp";
803
    }
804
  else if(lang == "C")
805
    {
806
    sourcecode += ".c.c";
807
    }
808
  else if(lang == "Fortran")
809
    {
810
    sourcecode += ".fortran.f90";
811
    }
812
813
814
815
  else if(lang == "ASM")
    {
    sourcecode += ".asm";
    }
816
817
818
819
820
  //else
  //  {
  //  // Already specialized above or we leave sourcecode == "sourcecode"
  //  // which is probably the most correct choice. Extensionless headers,
  //  // for example... Or file types unknown to Xcode that do not map to a
821
  //  // valid explicitFileType value.
822
823
  //  }

824
825
826
827
828
829
830
831
  return sourcecode;
}

//----------------------------------------------------------------------------
cmXCodeObject*
cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath(
  const std::string &fullpath,
  cmTarget& cmtarget,
832
833
  const std::string &lang,
  cmSourceFile* sf)
834
835
836
837
838
839
840
841
842
843
{
  std::string fname = fullpath;
  cmXCodeObject* fileRef = this->FileRefs[fname];
  if(!fileRef)
    {
    fileRef = this->CreateObject(cmXCodeObject::PBXFileReference);
    std::string comment = fname;
    fileRef->SetComment(fname.c_str());
    this->FileRefs[fname] = fileRef;
    }
844
  std::string key = GetGroupMapKeyFromPath(cmtarget, fullpath);
845
846
847
848
849
850
851
852
  cmXCodeObject* group = this->GroupMap[key];
  cmXCodeObject* children = group->GetObject("children");
  if (!children->HasObject(fileRef))
    {
    children->AddObject(fileRef);
    }
  fileRef->AddAttribute("fileEncoding", this->CreateString("4"));

853
854
  bool useLastKnownFileType = false;
  std::string fileType;
855
856
857
858
859
860
861
862
863
864
865
866
867
  if(sf)
    {
    if(const char* e = sf->GetProperty("XCODE_EXPLICIT_FILE_TYPE"))
      {
      fileType = e;
      }
    else if(const char* l = sf->GetProperty("XCODE_LAST_KNOWN_FILE_TYPE"))
      {
      useLastKnownFileType = true;
      fileType = l;
      }
    }
  if(fileType.empty())
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
    {
    // If fullpath references a directory, then we need to specify
    // lastKnownFileType as folder in order for Xcode to be able to
    // open the contents of the folder.
    // (Xcode 4.6 does not like explicitFileType=folder).
    if(cmSystemTools::FileIsDirectory(fullpath.c_str()))
      {
      fileType = "folder";
      useLastKnownFileType = true;
      }
    else
      {
      // Compute the extension without leading '.'.
      std::string ext = cmSystemTools::GetFilenameLastExtension(fullpath);
      if(!ext.empty())
        {
        ext = ext.substr(1);
        }
886

887
888
889
      fileType = GetSourcecodeValueFromFileExtension(
        ext, lang, useLastKnownFileType);
      }
890
    }
891

892
893
894
895
  fileRef->AddAttribute(useLastKnownFileType? "lastKnownFileType"
                                            : "explicitFileType",
                        this->CreateString(fileType));

896
  // Store the file path relative to the top of the source tree.
897
  std::string path = this->RelativeToSource(fullpath.c_str());
898
899
900
901
  std::string name = cmSystemTools::GetFilenameName(path.c_str());
  const char* sourceTree = (cmSystemTools::FileIsFullPath(path.c_str())?
                            "<absolute>" : "SOURCE_ROOT");
  fileRef->AddAttribute("name", this->CreateString(name.c_str()));
902
  fileRef->AddAttribute("path", this->CreateString(path.c_str()));
903
  fileRef->AddAttribute("sourceTree", this->CreateString(sourceTree));
Ken Martin's avatar
Ken Martin committed
904
  if(this->XcodeVersion == 15)
905
906
907
    {
    fileRef->AddAttribute("refType", this->CreateString("4"));
    }
908
  return fileRef;
Bill Hoffman's avatar
Bill Hoffman committed
909
910
}

911
//----------------------------------------------------------------------------
912
913
914
915
cmXCodeObject*
cmGlobalXCodeGenerator::CreateXCodeFileReference(cmSourceFile* sf,
                                                 cmTarget& cmtarget)
{
916
  std::string lang =
917
918
919
    this->CurrentLocalGenerator->GetSourceFileLanguage(*sf);

  return this->CreateXCodeFileReferenceFromPath(
920
    sf->GetFullPath(), cmtarget, lang, sf);
921
922
}

923
924
925
926
//----------------------------------------------------------------------------
bool cmGlobalXCodeGenerator::SpecialTargetEmitted(std::string const& tname)
{
  if(tname == "ALL_BUILD" || tname == "XCODE_DEPEND_HELPER" ||
927
928
     tname == "install" || tname == "package" || tname == "RUN_TESTS" ||
     tname == CMAKE_CHECK_BUILD_SYSTEM_TARGET )
929
    {
Ken Martin's avatar
Ken Martin committed
930
    if(this->TargetDoneSet.find(tname) != this->TargetDoneSet.end())
931
932
933
      {
      return true;
      }
Ken Martin's avatar
Ken Martin committed
934
    this->TargetDoneSet.insert(tname);
935
936
937
938
939
    return false;
    }
  return false;
}

940
//----------------------------------------------------------------------------
941
942
void cmGlobalXCodeGenerator::SetCurrentLocalGenerator(cmLocalGenerator* gen)
{
Ken Martin's avatar
Ken Martin committed
943
944
  this->CurrentLocalGenerator = gen;
  this->CurrentMakefile = gen->GetMakefile();
945
  std::string outdir =
Ken Martin's avatar
Ken Martin committed
946
    cmSystemTools::CollapseFullPath(this->CurrentMakefile->
947
                                    GetCurrentOutputDirectory());
948
  cmSystemTools::SplitPath(outdir.c_str(),
Ken Martin's avatar
Ken Martin committed
949
                           this->CurrentOutputDirectoryComponents);
950
951

  // Select the current set of configuration types.
Ken Martin's avatar
Ken Martin committed
952
  this->CurrentConfigurationTypes.clear();
953
  this->CurrentMakefile->GetConfigurations(this->CurrentConfigurationTypes);
Ken Martin's avatar
Ken Martin committed
954
  if(this->CurrentConfigurationTypes.empty())
955
    {
956
    this->CurrentConfigurationTypes.push_back("");
957
    }
958
959
}

Alexander Chehovsky's avatar
Alexander Chehovsky committed
960
961
962
963
964
965
966
967
968
//----------------------------------------------------------------------------
struct cmSourceFilePathCompare
{
  bool operator()(cmSourceFile* l, cmSourceFile* r)
  {
    return l->GetFullPath() < r->GetFullPath();
  }
};

Bill Hoffman's avatar
Bill Hoffman committed
969
//----------------------------------------------------------------------------
970
void
971
cmGlobalXCodeGenerator::CreateXCodeTargets(cmLocalGenerator* gen,
972
                                           std::vector<cmXCodeObject*>&
Bill Hoffman's avatar
Bill Hoffman committed
973
                                           targets)
Bill Hoffman's avatar
Bill Hoffman committed
974
{
975
  this->SetCurrentLocalGenerator(gen);
Ken Martin's avatar
Ken Martin committed
976
  cmTargets &tgts = this->CurrentMakefile->GetTargets();
Bill Hoffman's avatar
Bill Hoffman committed
977
  for(cmTargets::iterator l = tgts.begin(); l != tgts.end(); l++)
David Cole's avatar