text: # Copyright 2014-2016 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  from . import backends
  from .. import build
  from .. import dependencies
  from .. import mesonlib
  from .. import mlog
  import uuid, os, operator
  
  from ..mesonlib import MesonException
  
  class XCodeBackend(backends.Backend):
      def __init__(self, build):
          super().__init__(build)
          self.name = 'xcode'
          self.project_uid = self.environment.coredata.lang_guids['default'].replace('-', '')[:24]
          self.project_conflist = self.gen_id()
          self.indent = '\t' # Recent versions of Xcode uses tabs
          self.indent_level = 0
          self.xcodetypemap = {'c': 'sourcecode.c.c',
                               'a': 'archive.ar',
                               'cc': 'sourcecode.cpp.cpp',
                               'cxx': 'sourcecode.cpp.cpp',
                               'cpp': 'sourcecode.cpp.cpp',
                               'c++': 'sourcecode.cpp.cpp',
                               'm': 'sourcecode.c.objc',
                               'mm': 'sourcecode.cpp.objcpp',
                               'h': 'sourcecode.c.h',
                               'hpp': 'sourcecode.cpp.h',
                               'hxx': 'sourcecode.cpp.h',
                               'hh': 'sourcecode.cpp.hh',
                               'inc': 'sourcecode.c.h',
                               'dylib': 'compiled.mach-o.dylib',
                               'o': 'compiled.mach-o.objfile',
                               's': 'sourcecode.asm',
                               'asm': 'sourcecode.asm',
                               }
          self.maingroup_id = self.gen_id()
          self.all_id = self.gen_id()
          self.all_buildconf_id = self.gen_id()
          self.buildtypes = ['debug']
          self.test_id = self.gen_id()
          self.test_command_id = self.gen_id()
          self.test_buildconf_id = self.gen_id()
  
      def gen_id(self):
          return str(uuid.uuid4()).upper().replace('-', '')[:24]
  
      def get_target_dir(self, target):
          dirname = os.path.join(target.get_subdir(), self.environment.coredata.get_builtin_option('buildtype'))
          os.makedirs(os.path.join(self.environment.get_build_dir(), dirname), exist_ok=True)
          return dirname
  
      def target_to_build_root(self, target):
          if self.get_target_dir(target) == '':
              return ''
          directories = os.path.normpath(self.get_target_dir(target)).split(os.sep)
          return os.sep.join(['..'] * len(directories))
  
      def write_line(self, text):
          self.ofile.write(self.indent * self.indent_level + text)
          if not text.endswith('\n'):
              self.ofile.write('\n')
  
      def generate(self, interp):
          self.interpreter = interp
          test_data = self.serialize_tests()[0]
          self.generate_filemap()
          self.generate_buildmap()
          self.generate_buildstylemap()
          self.generate_build_phase_map()
          self.generate_build_configuration_map()
          self.generate_build_configurationlist_map()
          self.generate_project_configurations_map()
          self.generate_buildall_configurations_map()
          self.generate_test_configurations_map()
          self.generate_native_target_map()
          self.generate_native_frameworks_map()
          self.generate_source_phase_map()
          self.generate_target_dependency_map()
          self.generate_pbxdep_map()
          self.generate_containerproxy_map()
          self.proj_dir = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.xcodeproj')
          os.makedirs(self.proj_dir, exist_ok=True)
          self.proj_file = os.path.join(self.proj_dir, 'project.pbxproj')
          with open(self.proj_file, 'w') as self.ofile:
              self.generate_prefix()
              self.generate_pbx_aggregate_target()
              self.generate_pbx_build_file()
              self.generate_pbx_build_style()
              self.generate_pbx_container_item_proxy()
              self.generate_pbx_file_reference()
              self.generate_pbx_frameworks_buildphase()
              self.generate_pbx_group()
              self.generate_pbx_native_target()
              self.generate_pbx_project()
              self.generate_pbx_shell_build_phase(test_data)
              self.generate_pbx_sources_build_phase()
              self.generate_pbx_target_dependency()
              self.generate_xc_build_configuration()
              self.generate_xc_configurationList()
              self.generate_suffix()
  
      def get_xcodetype(self, fname):
          xcodetype = self.xcodetypemap.get(fname.split('.')[-1].lower())
          if not xcodetype:
              xcodetype = 'sourcecode.unknown'
              mlog.warning('Unknown file type "%s" fallbacking to "%s". Xcode project might be malformed.' % (fname, xcodetype))
          return xcodetype
  
      def generate_filemap(self):
          self.filemap = {} # Key is source file relative to src root.
          self.target_filemap = {}
          for name, t in self.build.targets.items():
              for s in t.sources:
                  if isinstance(s, mesonlib.File):
                      s = os.path.join(s.subdir, s.fname)
                      self.filemap[s] = self.gen_id()
              for o in t.objects:
                  if isinstance(o, str):
                      o = os.path.join(t.subdir, o)
                      self.filemap[o] = self.gen_id()
              self.target_filemap[name] = self.gen_id()
  
      def generate_buildmap(self):
          self.buildmap = {}
          for t in self.build.targets.values():
              for s in t.sources:
                  s = os.path.join(s.subdir, s.fname)
                  self.buildmap[s] = self.gen_id()
              for o in t.objects:
                  o = os.path.join(t.subdir, o)
                  if isinstance(o, str):
                      self.buildmap[o] = self.gen_id()
  
      def generate_buildstylemap(self):
          self.buildstylemap = {'debug': self.gen_id()}
  
      def generate_build_phase_map(self):
          for tname, t in self.build.targets.items():
              # generate id for our own target-name
              t.buildphasemap = {}
              t.buildphasemap[tname] = self.gen_id()
              # each target can have it's own Frameworks/Sources/..., generate id's for those
              t.buildphasemap['Frameworks'] = self.gen_id()
              t.buildphasemap['Resources'] = self.gen_id()
              t.buildphasemap['Sources'] = self.gen_id()
  
      def generate_build_configuration_map(self):
          self.buildconfmap = {}
          for t in self.build.targets:
              bconfs = {'debug': self.gen_id()}
              self.buildconfmap[t] = bconfs
  
      def generate_project_configurations_map(self):
          self.project_configurations = {'debug': self.gen_id()}
  
      def generate_buildall_configurations_map(self):
          self.buildall_configurations = {'debug': self.gen_id()}
  
      def generate_test_configurations_map(self):
          self.test_configurations = {'debug': self.gen_id()}
  
      def generate_build_configurationlist_map(self):
          self.buildconflistmap = {}
          for t in self.build.targets:
              self.buildconflistmap[t] = self.gen_id()
  
      def generate_native_target_map(self):
          self.native_targets = {}
          for t in self.build.targets:
              self.native_targets[t] = self.gen_id()
  
      def generate_native_frameworks_map(self):
          self.native_frameworks = {}
          self.native_frameworks_fileref = {}
          for t in self.build.targets.values():
              for dep in t.get_external_deps():
                  if isinstance(dep, dependencies.AppleFrameworks):
                      for f in dep.frameworks:
                          self.native_frameworks[f] = self.gen_id()
                          self.native_frameworks_fileref[f] = self.gen_id()
  
      def generate_target_dependency_map(self):
          self.target_dependency_map = {}
          for tname, t in self.build.targets.items():
              for target in t.link_targets:
                  self.target_dependency_map[(tname, target.get_basename())] = self.gen_id()
  
      def generate_pbxdep_map(self):
          self.pbx_dep_map = {}
          for t in self.build.targets:
              self.pbx_dep_map[t] = self.gen_id()
  
      def generate_containerproxy_map(self):
          self.containerproxy_map = {}
          for t in self.build.targets:
              self.containerproxy_map[t] = self.gen_id()
  
      def generate_source_phase_map(self):
          self.source_phase = {}
          for t in self.build.targets:
              self.source_phase[t] = self.gen_id()
  
      def generate_pbx_aggregate_target(self):
          target_dependencies = list(map(lambda t: self.pbx_dep_map[t], self.build.targets))
          aggregated_targets = []
          aggregated_targets.append((self.all_id, 'ALL_BUILD', self.all_buildconf_id, [], target_dependencies))
          aggregated_targets.append((self.test_id, 'RUN_TESTS', self.test_buildconf_id, [self.test_command_id], []))
          # Sort objects by ID before writing
          sorted_aggregated_targets = sorted(aggregated_targets, key=operator.itemgetter(0))
          self.ofile.write('\n/* Begin PBXAggregateTarget section */\n')
          for t in sorted_aggregated_targets:
              name = t[1]
              buildconf_id = t[2]
              build_phases = t[3]
              dependencies = t[4]
              self.write_line('%s /* %s */ = {' % (t[0], name))
              self.indent_level += 1
              self.write_line('isa = PBXAggregateTarget;')
              self.write_line('buildConfigurationList = %s /* Build configuration list for PBXAggregateTarget "%s" */;' % (buildconf_id, name))
              self.write_line('buildPhases = (')
              self.indent_level += 1
              for bp in build_phases:
                  self.write_line('%s /* ShellScript */,' % bp)
              self.indent_level -= 1
              self.write_line(');')
              self.write_line('dependencies = (')
              self.indent_level += 1
              for td in dependencies:
                  self.write_line('%s /* PBXTargetDependency */,' % td)
              self.indent_level -= 1
              self.write_line(');')
              self.write_line('name = %s;' % name)
              self.write_line('productName = %s;' % name)
              self.indent_level -= 1
              self.write_line('};')
          self.ofile.write('/* End PBXAggregateTarget section */\n')
  
      def generate_pbx_build_file(self):
          self.ofile.write('\n/* Begin PBXBuildFile section */\n')
          templ = '%s /* %s */ = { isa = PBXBuildFile; fileRef = %s /* %s */; settings = { COMPILER_FLAGS = "%s"; }; };\n'
          otempl = '%s /* %s */ = { isa = PBXBuildFile; fileRef = %s /* %s */;};\n'
  
          for t in self.build.targets.values():
  
              for dep in t.get_external_deps():
                  if isinstance(dep, dependencies.AppleFrameworks):
                      for f in dep.frameworks:
                          self.write_line('%s /* %s.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = %s /* %s.framework */; };\n' % (self.native_frameworks[f], f, self.native_frameworks_fileref[f], f))
  
              for s in t.sources:
                  if isinstance(s, mesonlib.File):
                      s = s.fname
  
                  if isinstance(s, str):
                      s = os.path.join(t.subdir, s)
                      idval = self.buildmap[s]
                      fullpath = os.path.join(self.environment.get_source_dir(), s)
                      fileref = self.filemap[s]
                      fullpath2 = fullpath
                      compiler_args = ''
                      self.write_line(templ % (idval, fullpath, fileref, fullpath2, compiler_args))
              for o in t.objects:
                  o = os.path.join(t.subdir, o)
                  idval = self.buildmap[o]
                  fileref = self.filemap[o]
                  fullpath = os.path.join(self.environment.get_source_dir(), o)
                  fullpath2 = fullpath
                  self.write_line(otempl % (idval, fullpath, fileref, fullpath2))
          self.ofile.write('/* End PBXBuildFile section */\n')
  
      def generate_pbx_build_style(self):
          # FIXME: Xcode 9 and later does not uses PBXBuildStyle and it gets removed. Maybe we can remove this part.
          self.ofile.write('\n/* Begin PBXBuildStyle section */\n')
          for name, idval in self.buildstylemap.items():
              self.write_line('%s /* %s */ = {\n' % (idval, name))
              self.indent_level += 1
              self.write_line('isa = PBXBuildStyle;\n')
              self.write_line('buildSettings = {\n')
              self.indent_level += 1
              self.write_line('COPY_PHASE_STRIP = NO;\n')
              self.indent_level -= 1
              self.write_line('};\n')
              self.write_line('name = "%s";\n' % name)
              self.indent_level -= 1
              self.write_line('};\n')
          self.ofile.write('/* End PBXBuildStyle section */\n')
  
      def generate_pbx_container_item_proxy(self):
          self.ofile.write('\n/* Begin PBXContainerItemProxy section */\n')
          for t in self.build.targets:
              self.write_line('%s /* PBXContainerItemProxy */ = {' % self.containerproxy_map[t])
              self.indent_level += 1
              self.write_line('isa = PBXContainerItemProxy;')
              self.write_line('containerPortal = %s /* Project object */;' % self.project_uid)
              self.write_line('proxyType = 1;')
              self.write_line('remoteGlobalIDString = %s;' % self.native_targets[t])
              self.write_line('remoteInfo = "%s";' % t)
              self.indent_level -= 1
              self.write_line('};')
          self.ofile.write('/* End PBXContainerItemProxy section */\n')
  
      def generate_pbx_file_reference(self):
          self.ofile.write('\n/* Begin PBXFileReference section */\n')
          for t in self.build.targets.values():
              for dep in t.get_external_deps():
                  if isinstance(dep, dependencies.AppleFrameworks):
                      for f in dep.frameworks:
                          self.write_line('%s /* %s.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = %s.framework; path = System/Library/Frameworks/%s.framework; sourceTree = SDKROOT; };\n' % (self.native_frameworks_fileref[f], f, f, f))
          src_templ = '%s /* %s */ = { isa = PBXFileReference; explicitFileType = "%s"; fileEncoding = 4; name = "%s"; path = "%s"; sourceTree = SOURCE_ROOT; };\n'
          for fname, idval in self.filemap.items():
              fullpath = os.path.join(self.environment.get_source_dir(), fname)
              xcodetype = self.get_xcodetype(fname)
              name = os.path.basename(fname)
              path = fname
              self.write_line(src_templ % (idval, fullpath, xcodetype, name, path))
          target_templ = '%s /* %s */ = { isa = PBXFileReference; explicitFileType = "%s"; path = %s; refType = %d; sourceTree = BUILT_PRODUCTS_DIR; };\n'
          for tname, idval in self.target_filemap.items():
              t = self.build.targets[tname]
              fname = t.get_filename()
              reftype = 0
              if isinstance(t, build.Executable):
                  typestr = 'compiled.mach-o.executable'
                  path = fname
              elif isinstance(t, build.SharedLibrary):
                  typestr = self.get_xcodetype('dummy.dylib')
                  path = fname
              else:
                  typestr = self.get_xcodetype(fname)
                  path = '"%s"' % t.get_filename()
              self.write_line(target_templ % (idval, tname, typestr, path, reftype))
          self.ofile.write('/* End PBXFileReference section */\n')
  
      def generate_pbx_frameworks_buildphase(self):
          for tname, t in self.build.targets.items():
              self.ofile.write('\n/* Begin PBXFrameworksBuildPhase section */\n')
              self.write_line('%s /* %s */ = {\n' % (t.buildphasemap['Frameworks'], 'Frameworks'))
              self.indent_level += 1
              self.write_line('isa = PBXFrameworksBuildPhase;\n')
              self.write_line('buildActionMask = %s;\n' % (2147483647))
              self.write_line('files = (\n')
              self.indent_level += 1
              for dep in t.get_external_deps():
                  if isinstance(dep, dependencies.AppleFrameworks):
                      for f in dep.frameworks:
                          self.write_line('%s /* %s.framework in Frameworks */,\n' % (self.native_frameworks[f], f))
              self.indent_level -= 1
              self.write_line(');\n')
              self.write_line('runOnlyForDeploymentPostprocessing = 0;\n')
              self.indent_level -= 1
              self.write_line('};\n')
          self.ofile.write('/* End PBXFrameworksBuildPhase section */\n')
  
      def generate_pbx_group(self):
          groupmap = {}
          target_src_map = {}
          for t in self.build.targets:
              groupmap[t] = self.gen_id()
              target_src_map[t] = self.gen_id()
          self.ofile.write('\n/* Begin PBXGroup section */\n')
          sources_id = self.gen_id()
          resources_id = self.gen_id()
          products_id = self.gen_id()
          frameworks_id = self.gen_id()
          self.write_line('%s = {' % self.maingroup_id)
          self.indent_level += 1
          self.write_line('isa = PBXGroup;')
          self.write_line('children = (')
          self.indent_level += 1
          self.write_line('%s /* Sources */,' % sources_id)
          self.write_line('%s /* Resources */,' % resources_id)
          self.write_line('%s /* Products */,' % products_id)
          self.write_line('%s /* Frameworks */,' % frameworks_id)
          self.indent_level -= 1
          self.write_line(');')
          self.write_line('sourceTree = "<group>";')
          self.indent_level -= 1
          self.write_line('};')
  
          # Sources
          self.write_line('%s /* Sources */ = {' % sources_id)
          self.indent_level += 1
          self.write_line('isa = PBXGroup;')
          self.write_line('children = (')
          self.indent_level += 1
          for t in self.build.targets:
              self.write_line('%s /* %s */,' % (groupmap[t], t))
          self.indent_level -= 1
          self.write_line(');')
          self.write_line('name = Sources;')
          self.write_line('sourceTree = "<group>";')
          self.indent_level -= 1
          self.write_line('};')
  
          self.write_line('%s /* Resources */ = {' % resources_id)
          self.indent_level += 1
          self.write_line('isa = PBXGroup;')
          self.write_line('children = (')
          self.write_line(');')
          self.write_line('name = Resources;')
          self.write_line('sourceTree = "<group>";')
          self.indent_level -= 1
          self.write_line('};')
  
          self.write_line('%s /* Frameworks */ = {' % frameworks_id)
          self.indent_level += 1
          self.write_line('isa = PBXGroup;')
          self.write_line('children = (')
          # write frameworks
          self.indent_level += 1
  
          for t in self.build.targets.values():
              for dep in t.get_external_deps():
                  if isinstance(dep, dependencies.AppleFrameworks):
                      for f in dep.frameworks:
                          self.write_line('%s /* %s.framework */,\n' % (self.native_frameworks_fileref[f], f))
  
          self.indent_level -= 1
          self.write_line(');')
          self.write_line('name = Frameworks;')
          self.write_line('sourceTree = "<group>";')
          self.indent_level -= 1
          self.write_line('};')
  
          # Targets
          for t in self.build.targets:
              self.write_line('%s /* %s */ = {' % (groupmap[t], t))
              self.indent_level += 1
              self.write_line('isa = PBXGroup;')
              self.write_line('children = (')
              self.indent_level += 1
              self.write_line('%s /* Source files */,' % target_src_map[t])
              self.indent_level -= 1
              self.write_line(');')
              self.write_line('name = "%s";' % t)
              self.write_line('sourceTree = "<group>";')
              self.indent_level -= 1
              self.write_line('};')
              self.write_line('%s /* Source files */ = {' % target_src_map[t])
              self.indent_level += 1
              self.write_line('isa = PBXGroup;')
              self.write_line('children = (')
              self.indent_level += 1
              for s in self.build.targets[t].sources:
                  s = os.path.join(s.subdir, s.fname)
                  if isinstance(s, str):
                      self.write_line('%s /* %s */,' % (self.filemap[s], s))
              for o in self.build.targets[t].objects:
                  o = os.path.join(self.build.targets[t].subdir, o)
                  self.write_line('%s /* %s */,' % (self.filemap[o], o))
              self.indent_level -= 1
              self.write_line(');')
              self.write_line('name = "Source files";')
              self.write_line('sourceTree = "<group>";')
              self.indent_level -= 1
              self.write_line('};')
  
          # And finally products
          self.write_line('%s /* Products */ = {' % products_id)
          self.indent_level += 1
          self.write_line('isa = PBXGroup;')
          self.write_line('children = (')
          self.indent_level += 1
          for t in self.build.targets:
              self.write_line('%s /* %s */,' % (self.target_filemap[t], t))
          self.indent_level -= 1
          self.write_line(');')
          self.write_line('name = Products;')
          self.write_line('sourceTree = "<group>";')
          self.indent_level -= 1
          self.write_line('};')
          self.ofile.write('/* End PBXGroup section */\n')
  
      def generate_pbx_native_target(self):
          self.ofile.write('\n/* Begin PBXNativeTarget section */\n')
          for tname, idval in self.native_targets.items():
              t = self.build.targets[tname]
              self.write_line('%s /* %s */ = {' % (idval, tname))
              self.indent_level += 1
              self.write_line('isa = PBXNativeTarget;')
              self.write_line('buildConfigurationList = %s /* Build configuration list for PBXNativeTarget "%s" */;'
                              % (self.buildconflistmap[tname], tname))
              self.write_line('buildPhases = (')
              self.indent_level += 1
              for bpname, bpval in t.buildphasemap.items():
                  self.write_line('%s /* %s yyy */,' % (bpval, bpname))
              self.indent_level -= 1
              self.write_line(');')
              self.write_line('buildRules = (')
              self.write_line(');')
              self.write_line('dependencies = (')
              self.indent_level += 1
              for lt in self.build.targets[tname].link_targets:
                  # NOT DOCUMENTED, may need to make different links
                  # to same target have different targetdependency item.
                  idval = self.pbx_dep_map[lt.get_id()]
                  self.write_line('%s /* PBXTargetDependency */,' % idval)
              self.indent_level -= 1
              self.write_line(");")
              self.write_line('name = "%s";' % tname)
              self.write_line('productName = "%s";' % tname)
              self.write_line('productReference = %s /* %s */;' % (self.target_filemap[tname], tname))
              if isinstance(t, build.Executable):
                  typestr = 'com.apple.product-type.tool'
              elif isinstance(t, build.StaticLibrary):
                  typestr = 'com.apple.product-type.library.static'
              elif isinstance(t, build.SharedLibrary):
                  typestr = 'com.apple.product-type.library.dynamic'
              else:
                  raise MesonException('Unknown target type for %s' % tname)
              self.write_line('productType = "%s";' % typestr)
              self.indent_level -= 1
              self.write_line('};')
          self.ofile.write('/* End PBXNativeTarget section */\n')
  
      def generate_pbx_project(self):
          self.ofile.write('\n/* Begin PBXProject section */\n')
          self.write_line('%s /* Project object */ = {' % self.project_uid)
          self.indent_level += 1
          self.write_line('isa = PBXProject;')
          self.write_line('attributes = {')
          self.indent_level += 1
          self.write_line('BuildIndependentTargetsInParallel = YES;')
          self.indent_level -= 1
          self.write_line('};')
          conftempl = 'buildConfigurationList = %s /* Build configuration list for PBXProject "%s" */;'
          self.write_line(conftempl % (self.project_conflist, self.build.project_name))
          self.write_line('buildSettings = {')
          self.write_line('};')
          self.write_line('buildStyles = (')
          self.indent_level += 1
          for name, idval in self.buildstylemap.items():
              self.write_line('%s /* %s */,' % (idval, name))
          self.indent_level -= 1
          self.write_line(');')
          self.write_line('compatibilityVersion = "Xcode 3.2";')
          self.write_line('hasScannedForEncodings = 0;')
          self.write_line('mainGroup = %s;' % self.maingroup_id)
          self.write_line('projectDirPath = "%s";' % self.build_to_src)
          self.write_line('projectRoot = "";')
          self.write_line('targets = (')
          self.indent_level += 1
          self.write_line('%s /* ALL_BUILD */,' % self.all_id)
          self.write_line('%s /* RUN_TESTS */,' % self.test_id)
          for t in self.build.targets:
              self.write_line('%s /* %s */,' % (self.native_targets[t], t))
          self.indent_level -= 1
          self.write_line(');')
          self.indent_level -= 1
          self.write_line('};')
          self.ofile.write('/* End PBXProject section */\n')
  
      def generate_pbx_shell_build_phase(self, test_data):
          self.ofile.write('\n/* Begin PBXShellScriptBuildPhase section */\n')
          self.write_line('%s /* ShellScript */ = {' % self.test_command_id)
          self.indent_level += 1
          self.write_line('isa = PBXShellScriptBuildPhase;')
          self.write_line('buildActionMask = 2147483647;')
          self.write_line('files = (')
          self.write_line(');')
          self.write_line('inputPaths = (')
          self.write_line(');')
          self.write_line('outputPaths = (')
          self.write_line(');')
          self.write_line('runOnlyForDeploymentPostprocessing = 0;')
          self.write_line('shellPath = /bin/sh;')
          cmd = mesonlib.meson_command + ['test', test_data, '-C', self.environment.get_build_dir()]
          cmdstr = ' '.join(["'%s'" % i for i in cmd])
          self.write_line('shellScript = "%s";' % cmdstr)
          self.write_line('showEnvVarsInLog = 0;')
          self.indent_level -= 1
          self.write_line('};')
          self.ofile.write('/* End PBXShellScriptBuildPhase section */\n')
  
      def generate_pbx_sources_build_phase(self):
          self.ofile.write('\n/* Begin PBXSourcesBuildPhase section */\n')
          for name, phase_id in self.source_phase.items():
              t = self.build.targets[name]
              self.write_line('%s /* Sources */ = {' % (t.buildphasemap[name]))
              self.indent_level += 1
              self.write_line('isa = PBXSourcesBuildPhase;')
              self.write_line('buildActionMask = 2147483647;')
              self.write_line('files = (')
              self.indent_level += 1
              for s in self.build.targets[name].sources:
                  s = os.path.join(s.subdir, s.fname)
                  if not self.environment.is_header(s):
                      self.write_line('%s /* %s */,' % (self.buildmap[s], os.path.join(self.environment.get_source_dir(), s)))
              self.indent_level -= 1
              self.write_line(');')
              self.write_line('runOnlyForDeploymentPostprocessing = 0;')
              self.indent_level -= 1
              self.write_line('};')
          self.ofile.write('/* End PBXSourcesBuildPhase section */\n')
  
      def generate_pbx_target_dependency(self):
          targets = []
          for t in self.build.targets:
              idval = self.pbx_dep_map[t] # VERIFY: is this correct?
              targets.append((idval, self.native_targets[t], t, self.containerproxy_map[t]))
  
          # Sort object by ID
          sorted_targets = sorted(targets, key=operator.itemgetter(0))
          self.ofile.write('\n/* Begin PBXTargetDependency section */\n')
          for t in sorted_targets:
              self.write_line('%s /* PBXTargetDependency */ = {' % t[0])
              self.indent_level += 1
              self.write_line('isa = PBXTargetDependency;')
              self.write_line('target = %s /* %s */;' % (t[1], t[2]))
              self.write_line('targetProxy = %s /* PBXContainerItemProxy */;' % t[3])
              self.indent_level -= 1
              self.write_line('};')
          self.ofile.write('/* End PBXTargetDependency section */\n')
  
      def generate_xc_build_configuration(self):
          self.ofile.write('\n/* Begin XCBuildConfiguration section */\n')
          # First the setup for the toplevel project.
          for buildtype in self.buildtypes:
              self.write_line('%s /* %s */ = {' % (self.project_configurations[buildtype], buildtype))
              self.indent_level += 1
              self.write_line('isa = XCBuildConfiguration;')
              self.write_line('buildSettings = {')
              self.indent_level += 1
              self.write_line('ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";')
              self.write_line('ONLY_ACTIVE_ARCH = YES;')
              self.write_line('SDKROOT = "macosx";')
              self.write_line('SYMROOT = "%s/build";' % self.environment.get_build_dir())
              self.indent_level -= 1
              self.write_line('};')
              self.write_line('name = "%s";' % buildtype)
              self.indent_level -= 1
              self.write_line('};')
  
          # Then the all target.
          for buildtype in self.buildtypes:
              self.write_line('%s /* %s */ = {' % (self.buildall_configurations[buildtype], buildtype))
              self.indent_level += 1
              self.write_line('isa = XCBuildConfiguration;')
              self.write_line('buildSettings = {')
              self.indent_level += 1
              self.write_line('COMBINE_HIDPI_IMAGES = YES;')
              self.write_line('GCC_GENERATE_DEBUGGING_SYMBOLS = NO;')
              self.write_line('GCC_INLINES_ARE_PRIVATE_EXTERN = NO;')
              self.write_line('GCC_OPTIMIZATION_LEVEL = 0;')
              self.write_line('GCC_PREPROCESSOR_DEFINITIONS = "";')
              self.write_line('GCC_SYMBOLS_PRIVATE_EXTERN = NO;')
              self.write_line('INSTALL_PATH = "";')
              self.write_line('OTHER_CFLAGS = "  ";')
              self.write_line('OTHER_LDFLAGS = " ";')
              self.write_line('OTHER_REZFLAGS = "";')
              self.write_line('PRODUCT_NAME = ALL_BUILD;')
              self.write_line('SECTORDER_FLAGS = "";')
              self.write_line('SYMROOT = "%s";' % self.environment.get_build_dir())
              self.write_line('USE_HEADERMAP = NO;')
              self.write_build_setting_line('WARNING_CFLAGS', ['-Wmost', '-Wno-four-char-constants', '-Wno-unknown-pragmas'])
              self.indent_level -= 1
              self.write_line('};')
              self.write_line('name = "%s";' % buildtype)
              self.indent_level -= 1
              self.write_line('};')
  
          # Then the test target.
          for buildtype in self.buildtypes:
              self.write_line('%s /* %s */ = {' % (self.test_configurations[buildtype], buildtype))
              self.indent_level += 1
              self.write_line('isa = XCBuildConfiguration;')
              self.write_line('buildSettings = {')
              self.indent_level += 1
              self.write_line('COMBINE_HIDPI_IMAGES = YES;')
              self.write_line('GCC_GENERATE_DEBUGGING_SYMBOLS = NO;')
              self.write_line('GCC_INLINES_ARE_PRIVATE_EXTERN = NO;')
              self.write_line('GCC_OPTIMIZATION_LEVEL = 0;')
              self.write_line('GCC_PREPROCESSOR_DEFINITIONS = "";')
              self.write_line('GCC_SYMBOLS_PRIVATE_EXTERN = NO;')
              self.write_line('INSTALL_PATH = "";')
              self.write_line('OTHER_CFLAGS = "  ";')
              self.write_line('OTHER_LDFLAGS = " ";')
              self.write_line('OTHER_REZFLAGS = "";')
              self.write_line('PRODUCT_NAME = RUN_TESTS;')
              self.write_line('SECTORDER_FLAGS = "";')
              self.write_line('SYMROOT = "%s";' % self.environment.get_build_dir())
              self.write_line('USE_HEADERMAP = NO;')
              self.write_build_setting_line('WARNING_CFLAGS', ['-Wmost', '-Wno-four-char-constants', '-Wno-unknown-pragmas'])
              self.indent_level -= 1
              self.write_line('};')
              self.write_line('name = "%s";' % buildtype)
              self.indent_level -= 1
              self.write_line('};')
  
          # Now finally targets.
          langnamemap = {'c': 'C', 'cpp': 'CPLUSPLUS', 'objc': 'OBJC', 'objcpp': 'OBJCPLUSPLUS'}
          for target_name, target in self.build.targets.items():
              for buildtype in self.buildtypes:
                  dep_libs = []
                  links_dylib = False
                  headerdirs = []
                  for d in target.include_dirs:
                      for sd in d.incdirs:
                          cd = os.path.join(d.curdir, sd)
                          headerdirs.append(os.path.join(self.environment.get_source_dir(), cd))
                          headerdirs.append(os.path.join(self.environment.get_build_dir(), cd))
                  for l in target.link_targets:
                      abs_path = os.path.join(self.environment.get_build_dir(),
                                              l.subdir, buildtype, l.get_filename())
                      dep_libs.append("'%s'" % abs_path)
                      if isinstance(l, build.SharedLibrary):
                          links_dylib = True
                  if links_dylib:
                      dep_libs = ['-Wl,-search_paths_first', '-Wl,-headerpad_max_install_names'] + dep_libs
                  dylib_version = None
                  if isinstance(target, build.SharedLibrary):
                      ldargs = ['-dynamiclib', '-Wl,-headerpad_max_install_names'] + dep_libs
                      install_path = os.path.join(self.environment.get_build_dir(), target.subdir, buildtype)
                      dylib_version = target.soversion
                  else:
                      ldargs = dep_libs
                      install_path = ''
                  if dylib_version is not None:
                      product_name = target.get_basename() + '.' + dylib_version
                  else:
                      product_name = target.get_basename()
                  ldargs += target.link_args
                  for dep in target.get_external_deps():
                      ldargs += dep.get_link_args()
                  ldstr = ' '.join(ldargs)
                  valid = self.buildconfmap[target_name][buildtype]
                  langargs = {}
                  for lang in self.environment.coredata.compilers:
                      if lang not in langnamemap:
                          continue
                      # Add compile args added using add_project_arguments()
                      pargs = self.build.projects_args.get(target.subproject, {}).get(lang, [])
                      # Add compile args added using add_global_arguments()
                      # These override per-project arguments
                      gargs = self.build.global_args.get(lang, [])
                      targs = target.get_extra_args(lang)
                      args = pargs + gargs + targs
                      if len(args) > 0:
                          langargs[langnamemap[lang]] = args
                  symroot = os.path.join(self.environment.get_build_dir(), target.subdir)
                  self.write_line('%s /* %s */ = {' % (valid, buildtype))
                  self.indent_level += 1
                  self.write_line('isa = XCBuildConfiguration;')
                  self.write_line('buildSettings = {')
                  self.indent_level += 1
                  self.write_line('COMBINE_HIDPI_IMAGES = YES;')
                  if dylib_version is not None:
                      self.write_line('DYLIB_CURRENT_VERSION = "%s";' % dylib_version)
                  self.write_line('EXECUTABLE_PREFIX = "%s";' % target.prefix)
                  if target.suffix == '':
                      suffix = ''
                  else:
                      suffix = '.' + target.suffix
                  self.write_line('EXECUTABLE_SUFFIX = "%s";' % suffix)
                  self.write_line('GCC_GENERATE_DEBUGGING_SYMBOLS = YES;')
                  self.write_line('GCC_INLINES_ARE_PRIVATE_EXTERN = NO;')
                  self.write_line('GCC_OPTIMIZATION_LEVEL = 0;')
                  if target.has_pch:
                      # Xcode uses GCC_PREFIX_HEADER which only allows one file per target/executable. Precompiling various header files and
                      # applying a particular pch to each source file will require custom scripts (as a build phase) and build flags per each
                      # file. Since Xcode itself already discourages precompiled headers in favor of modules we don't try much harder here.
                      pchs = target.get_pch('c') + target.get_pch('cpp') + target.get_pch('objc') + target.get_pch('objcpp')
                      # Make sure to use headers (other backends require implementation files like *.c *.cpp, etc; these should not be used here)
                      pchs = [pch for pch in pchs if pch.endswith('.h') or pch.endswith('.hh') or pch.endswith('hpp')]
                      if pchs:
                          if len(pchs) > 1:
                              mlog.warning('Unsupported Xcode configuration: More than 1 precompiled header found "%s". Target "%s" might not compile correctly.' % (str(pchs), target.name))
                          relative_pch_path = os.path.join(target.get_subdir(), pchs[0]) # Path relative to target so it can be used with "$(PROJECT_DIR)"
                          self.write_line('GCC_PRECOMPILE_PREFIX_HEADER = YES;')
                          self.write_line('GCC_PREFIX_HEADER = "$(PROJECT_DIR)/%s";' % relative_pch_path)
                  self.write_line('GCC_PREPROCESSOR_DEFINITIONS = "";')
                  self.write_line('GCC_SYMBOLS_PRIVATE_EXTERN = NO;')
                  if len(headerdirs) > 0:
                      quotedh = ','.join(['"\\"%s\\""' % i for i in headerdirs])
                      self.write_line('HEADER_SEARCH_PATHS=(%s);' % quotedh)
                  self.write_line('INSTALL_PATH = "%s";' % install_path)
                  self.write_line('LIBRARY_SEARCH_PATHS = "";')
                  if isinstance(target, build.SharedLibrary):
                      self.write_line('LIBRARY_STYLE = DYNAMIC;')
                  for langname, args in langargs.items():
                      self.write_build_setting_line('OTHER_%sFLAGS' % langname, args)
                  self.write_line('OTHER_LDFLAGS = "%s";' % ldstr)
                  self.write_line('OTHER_REZFLAGS = "";')
                  self.write_line('PRODUCT_NAME = %s;' % product_name)
                  self.write_line('SECTORDER_FLAGS = "";')
                  self.write_line('SYMROOT = "%s";' % symroot)
                  self.write_build_setting_line('SYSTEM_HEADER_SEARCH_PATHS', [self.environment.get_build_dir()])
                  self.write_line('USE_HEADERMAP = NO;')
                  self.write_build_setting_line('WARNING_CFLAGS', ['-Wmost', '-Wno-four-char-constants', '-Wno-unknown-pragmas'])
                  self.indent_level -= 1
                  self.write_line('};')
                  self.write_line('name = %s;' % buildtype)
                  self.indent_level -= 1
                  self.write_line('};')
          self.ofile.write('/* End XCBuildConfiguration section */\n')
  
      def generate_xc_configurationList(self):
          # FIXME: sort items
          self.ofile.write('\n/* Begin XCConfigurationList section */\n')
          self.write_line('%s /* Build configuration list for PBXProject "%s" */ = {' % (self.project_conflist, self.build.project_name))
          self.indent_level += 1
          self.write_line('isa = XCConfigurationList;')
          self.write_line('buildConfigurations = (')
          self.indent_level += 1
          for buildtype in self.buildtypes:
              self.write_line('%s /* %s */,' % (self.project_configurations[buildtype], buildtype))
          self.indent_level -= 1
          self.write_line(');')
          self.write_line('defaultConfigurationIsVisible = 0;')
          self.write_line('defaultConfigurationName = debug;')
          self.indent_level -= 1
          self.write_line('};')
  
          # Now the all target
          self.write_line('%s /* Build configuration list for PBXAggregateTarget "ALL_BUILD" */ = {' % self.all_buildconf_id)
          self.indent_level += 1
          self.write_line('isa = XCConfigurationList;')
          self.write_line('buildConfigurations = (')
          self.indent_level += 1
          for buildtype in self.buildtypes:
              self.write_line('%s /* %s */,' % (self.buildall_configurations[buildtype], buildtype))
          self.indent_level -= 1
          self.write_line(');')
          self.write_line('defaultConfigurationIsVisible = 0;')
          self.write_line('defaultConfigurationName = debug;')
          self.indent_level -= 1
          self.write_line('};')
  
          # Test target
          self.write_line('%s /* Build configuration list for PBXAggregateTarget "ALL_BUILD" */ = {' % self.test_buildconf_id)
          self.indent_level += 1
          self.write_line('isa = XCConfigurationList;')
          self.write_line('buildConfigurations = (')
          self.indent_level += 1
          for buildtype in self.buildtypes:
              self.write_line('%s /* %s */,' % (self.test_configurations[buildtype], buildtype))
          self.indent_level -= 1
          self.write_line(');')
          self.write_line('defaultConfigurationIsVisible = 0;')
          self.write_line('defaultConfigurationName = debug;')
          self.indent_level -= 1
          self.write_line('};')
  
          for target_name in self.build.targets:
              listid = self.buildconflistmap[target_name]
              self.write_line('%s /* Build configuration list for PBXNativeTarget "%s" */ = {' % (listid, target_name))
              self.indent_level += 1
              self.write_line('isa = XCConfigurationList;')
              self.write_line('buildConfigurations = (')
              self.indent_level += 1
              typestr = 'debug'
              idval = self.buildconfmap[target_name][typestr]
              self.write_line('%s /* %s */,' % (idval, typestr))
              self.indent_level -= 1
              self.write_line(');')
              self.write_line('defaultConfigurationIsVisible = 0;')
              self.write_line('defaultConfigurationName = %s;' % typestr)
              self.indent_level -= 1
              self.write_line('};')
          self.ofile.write('/* End XCConfigurationList section */\n')
  
      def write_build_setting_line(self, flag_name, flag_values, explicit=False):
          if flag_values:
              if len(flag_values) == 1:
                  value = flag_values[0]
                  if (' ' in value):
                      # If path contains spaces surround it with double colon
                      self.write_line('%s = "\\"%s\\"";' % (flag_name, value))
                  else:
                      self.write_line('%s = "%s";' % (flag_name, value))
              else:
                  self.write_line('%s = (' % flag_name)
                  self.indent_level += 1
                  for value in flag_values:
                      if (' ' in value):
                          # If path contains spaces surround it with double colon
                          self.write_line('"\\"%s\\"",' % value)
                      else:
                          self.write_line('"%s",' % value)
                  self.indent_level -= 1
                  self.write_line(');')
          else:
              if explicit:
                  self.write_line('%s = "";' % flag_name)
  
      def generate_prefix(self):
          self.ofile.write('// !$*UTF8*$!\n{\n')
          self.indent_level += 1
          self.write_line('archiveVersion = 1;\n')
          self.write_line('classes = {\n')
          self.write_line('};\n')
          self.write_line('objectVersion = 46;\n')
          self.write_line('objects = {\n')
          self.indent_level += 1
  
      def generate_suffix(self):
          self.indent_level -= 1
          self.write_line('};\n')
          self.write_line('rootObject = ' + self.project_uid + ' /* Project object */;')
          self.indent_level -= 1
          self.write_line('}\n')
  