waf

FORK: waf with some random patches
git clone https://git.neptards.moe/neptards/waf.git
Log | Files | Refs | README

xcode6.py (24215B)


      1 #! /usr/bin/env python
      2 # encoding: utf-8
      3 # XCode 3/XCode 4/XCode 6/Xcode 7 generator for Waf
      4 # Based on work by Nicolas Mercier 2011
      5 # Extended by Simon Warg 2015, https://github.com/mimon
      6 # XCode project file format based on http://www.monobjc.net/xcode-project-file-format.html
      7 
      8 """
      9 See playground/xcode6/ for usage examples.
     10 
     11 """
     12 
     13 from waflib import Context, TaskGen, Build, Utils, Errors, Logs
     14 import os, sys
     15 
     16 # FIXME too few extensions
     17 XCODE_EXTS = ['.c', '.cpp', '.m', '.mm']
     18 
     19 HEADERS_GLOB = '**/(*.h|*.hpp|*.H|*.inl)'
     20 
     21 MAP_EXT = {
     22 	'': "folder",
     23 	'.h' :  "sourcecode.c.h",
     24 
     25 	'.hh':  "sourcecode.cpp.h",
     26 	'.inl': "sourcecode.cpp.h",
     27 	'.hpp': "sourcecode.cpp.h",
     28 
     29 	'.c':   "sourcecode.c.c",
     30 
     31 	'.m':   "sourcecode.c.objc",
     32 
     33 	'.mm':  "sourcecode.cpp.objcpp",
     34 
     35 	'.cc':  "sourcecode.cpp.cpp",
     36 
     37 	'.cpp': "sourcecode.cpp.cpp",
     38 	'.C':   "sourcecode.cpp.cpp",
     39 	'.cxx': "sourcecode.cpp.cpp",
     40 	'.c++': "sourcecode.cpp.cpp",
     41 
     42 	'.l':   "sourcecode.lex", # luthor
     43 	'.ll':  "sourcecode.lex",
     44 
     45 	'.y':   "sourcecode.yacc",
     46 	'.yy':  "sourcecode.yacc",
     47 
     48 	'.plist': "text.plist.xml",
     49 	".nib":   "wrapper.nib",
     50 	".xib":   "text.xib",
     51 }
     52 
     53 # Used in PBXNativeTarget elements
     54 PRODUCT_TYPE_APPLICATION = 'com.apple.product-type.application'
     55 PRODUCT_TYPE_FRAMEWORK = 'com.apple.product-type.framework'
     56 PRODUCT_TYPE_EXECUTABLE = 'com.apple.product-type.tool'
     57 PRODUCT_TYPE_LIB_STATIC = 'com.apple.product-type.library.static'
     58 PRODUCT_TYPE_LIB_DYNAMIC = 'com.apple.product-type.library.dynamic'
     59 PRODUCT_TYPE_EXTENSION = 'com.apple.product-type.kernel-extension'
     60 PRODUCT_TYPE_IOKIT = 'com.apple.product-type.kernel-extension.iokit'
     61 
     62 # Used in PBXFileReference elements
     63 FILE_TYPE_APPLICATION = 'wrapper.cfbundle'
     64 FILE_TYPE_FRAMEWORK = 'wrapper.framework'
     65 FILE_TYPE_LIB_DYNAMIC = 'compiled.mach-o.dylib'
     66 FILE_TYPE_LIB_STATIC = 'archive.ar'
     67 FILE_TYPE_EXECUTABLE = 'compiled.mach-o.executable'
     68 
     69 # Tuple packs of the above
     70 TARGET_TYPE_FRAMEWORK = (PRODUCT_TYPE_FRAMEWORK, FILE_TYPE_FRAMEWORK, '.framework')
     71 TARGET_TYPE_APPLICATION = (PRODUCT_TYPE_APPLICATION, FILE_TYPE_APPLICATION, '.app')
     72 TARGET_TYPE_DYNAMIC_LIB = (PRODUCT_TYPE_LIB_DYNAMIC, FILE_TYPE_LIB_DYNAMIC, '.dylib')
     73 TARGET_TYPE_STATIC_LIB = (PRODUCT_TYPE_LIB_STATIC, FILE_TYPE_LIB_STATIC, '.a')
     74 TARGET_TYPE_EXECUTABLE = (PRODUCT_TYPE_EXECUTABLE, FILE_TYPE_EXECUTABLE, '')
     75 
     76 # Maps target type string to its data
     77 TARGET_TYPES = {
     78 	'framework': TARGET_TYPE_FRAMEWORK,
     79 	'app': TARGET_TYPE_APPLICATION,
     80 	'dylib': TARGET_TYPE_DYNAMIC_LIB,
     81 	'stlib': TARGET_TYPE_STATIC_LIB,
     82 	'exe' :TARGET_TYPE_EXECUTABLE,
     83 }
     84 
     85 def delete_invalid_values(dct):
     86 	""" Deletes entries that are dictionaries or sets """
     87 	for k, v in list(dct.items()):
     88 		if isinstance(v, dict) or isinstance(v, set):
     89 			del dct[k]
     90 	return dct
     91 
     92 """
     93 Configuration of the global project settings. Sets an environment variable 'PROJ_CONFIGURATION'
     94 which is a dictionary of configuration name and buildsettings pair.
     95 E.g.:
     96 env.PROJ_CONFIGURATION = {
     97 	'Debug': {
     98 		'ARCHS': 'x86',
     99 		...
    100 	}
    101 	'Release': {
    102 		'ARCHS': x86_64'
    103 		...
    104 	}
    105 }
    106 The user can define a completely customized dictionary in configure() stage. Otherwise a default Debug/Release will be created
    107 based on env variable
    108 """
    109 def configure(self):
    110 	if not self.env.PROJ_CONFIGURATION:
    111 		self.to_log("A default project configuration was created since no custom one was given in the configure(conf) stage. Define your custom project settings by adding PROJ_CONFIGURATION to env. The env.PROJ_CONFIGURATION must be a dictionary with at least one key, where each key is the configuration name, and the value is a dictionary of key/value settings.\n")
    112 
    113 	# Check for any added config files added by the tool 'c_config'.
    114 	if 'cfg_files' in self.env:
    115 		self.env.INCLUDES = Utils.to_list(self.env.INCLUDES) + [os.path.abspath(os.path.dirname(f)) for f in self.env.cfg_files]
    116 
    117 	# Create default project configuration?
    118 	if 'PROJ_CONFIGURATION' not in self.env:
    119 		defaults = delete_invalid_values(self.env.get_merged_dict())
    120 		self.env.PROJ_CONFIGURATION = {
    121 			"Debug": defaults,
    122 			"Release": defaults,
    123 		}
    124 
    125 	# Some build settings are required to be present by XCode. We will supply default values
    126 	# if user hasn't defined any.
    127 	defaults_required = [('PRODUCT_NAME', '$(TARGET_NAME)')]
    128 	for cfgname,settings in self.env.PROJ_CONFIGURATION.items():
    129 		for default_var, default_val in defaults_required:
    130 			if default_var not in settings:
    131 				settings[default_var] = default_val
    132 
    133 	# Error check customization
    134 	if not isinstance(self.env.PROJ_CONFIGURATION, dict):
    135 		raise Errors.ConfigurationError("The env.PROJ_CONFIGURATION must be a dictionary with at least one key, where each key is the configuration name, and the value is a dictionary of key/value settings.")
    136 
    137 part1 = 0
    138 part2 = 10000
    139 part3 = 0
    140 id = 562000999
    141 def newid():
    142 	global id
    143 	id += 1
    144 	return "%04X%04X%04X%012d" % (0, 10000, 0, id)
    145 
    146 """
    147 Represents a tree node in the XCode project plist file format.
    148 When written to a file, all attributes of XCodeNode are stringified together with
    149 its value. However, attributes starting with an underscore _ are ignored
    150 during that process and allows you to store arbitrary values that are not supposed
    151 to be written out.
    152 """
    153 class XCodeNode(object):
    154 	def __init__(self):
    155 		self._id = newid()
    156 		self._been_written = False
    157 
    158 	def tostring(self, value):
    159 		if isinstance(value, dict):
    160 			result = "{\n"
    161 			for k,v in value.items():
    162 				result = result + "\t\t\t%s = %s;\n" % (k, self.tostring(v))
    163 			result = result + "\t\t}"
    164 			return result
    165 		elif isinstance(value, str):
    166 			return '"%s"' % value.replace('"', '\\\\\\"')
    167 		elif isinstance(value, list):
    168 			result = "(\n"
    169 			for i in value:
    170 				result = result + "\t\t\t\t%s,\n" % self.tostring(i)
    171 			result = result + "\t\t\t)"
    172 			return result
    173 		elif isinstance(value, XCodeNode):
    174 			return value._id
    175 		else:
    176 			return str(value)
    177 
    178 	def write_recursive(self, value, file):
    179 		if isinstance(value, dict):
    180 			for k,v in value.items():
    181 				self.write_recursive(v, file)
    182 		elif isinstance(value, list):
    183 			for i in value:
    184 				self.write_recursive(i, file)
    185 		elif isinstance(value, XCodeNode):
    186 			value.write(file)
    187 
    188 	def write(self, file):
    189 		if not self._been_written:
    190 			self._been_written = True
    191 			for attribute,value in self.__dict__.items():
    192 				if attribute[0] != '_':
    193 					self.write_recursive(value, file)
    194 			w = file.write
    195 			w("\t%s = {\n" % self._id)
    196 			w("\t\tisa = %s;\n" % self.__class__.__name__)
    197 			for attribute,value in self.__dict__.items():
    198 				if attribute[0] != '_':
    199 					w("\t\t%s = %s;\n" % (attribute, self.tostring(value)))
    200 			w("\t};\n\n")
    201 
    202 # Configurations
    203 class XCBuildConfiguration(XCodeNode):
    204 	def __init__(self, name, settings = {}, env=None):
    205 		XCodeNode.__init__(self)
    206 		self.baseConfigurationReference = ""
    207 		self.buildSettings = settings
    208 		self.name = name
    209 		if env and env.ARCH:
    210 			settings['ARCHS'] = " ".join(env.ARCH)
    211 
    212 
    213 class XCConfigurationList(XCodeNode):
    214 	def __init__(self, configlst):
    215 		""" :param configlst: list of XCConfigurationList """
    216 		XCodeNode.__init__(self)
    217 		self.buildConfigurations = configlst
    218 		self.defaultConfigurationIsVisible = 0
    219 		self.defaultConfigurationName = configlst and configlst[0].name or ""
    220 
    221 # Group/Files
    222 class PBXFileReference(XCodeNode):
    223 	def __init__(self, name, path, filetype = '', sourcetree = "SOURCE_ROOT"):
    224 
    225 		XCodeNode.__init__(self)
    226 		self.fileEncoding = 4
    227 		if not filetype:
    228 			_, ext = os.path.splitext(name)
    229 			filetype = MAP_EXT.get(ext, 'text')
    230 		self.lastKnownFileType = filetype
    231 		self.explicitFileType = filetype
    232 		self.name = name
    233 		self.path = path
    234 		self.sourceTree = sourcetree
    235 
    236 	def __hash__(self):
    237 		return (self.path+self.name).__hash__()
    238 
    239 	def __eq__(self, other):
    240 		return (self.path, self.name) == (other.path, other.name)
    241 
    242 class PBXBuildFile(XCodeNode):
    243 	""" This element indicate a file reference that is used in a PBXBuildPhase (either as an include or resource). """
    244 	def __init__(self, fileRef, settings={}):
    245 		XCodeNode.__init__(self)
    246 
    247 		# fileRef is a reference to a PBXFileReference object
    248 		self.fileRef = fileRef
    249 
    250 		# A map of key/value pairs for additional settings.
    251 		self.settings = settings
    252 
    253 	def __hash__(self):
    254 		return (self.fileRef).__hash__()
    255 
    256 	def __eq__(self, other):
    257 		return self.fileRef == other.fileRef
    258 
    259 class PBXGroup(XCodeNode):
    260 	def __init__(self, name, sourcetree = 'SOURCE_TREE'):
    261 		XCodeNode.__init__(self)
    262 		self.children = []
    263 		self.name = name
    264 		self.sourceTree = sourcetree
    265 
    266 		# Maintain a lookup table for all PBXFileReferences
    267 		# that are contained in this group.
    268 		self._filerefs = {}
    269 
    270 	def add(self, sources):
    271 		"""
    272 		Add a list of PBXFileReferences to this group
    273 
    274 		:param sources: list of PBXFileReferences objects
    275 		"""
    276 		self._filerefs.update(dict(zip(sources, sources)))
    277 		self.children.extend(sources)
    278 
    279 	def get_sub_groups(self):
    280 		"""
    281 		Returns all child PBXGroup objects contained in this group
    282 		"""
    283 		return list(filter(lambda x: isinstance(x, PBXGroup), self.children))
    284 
    285 	def find_fileref(self, fileref):
    286 		"""
    287 		Recursively search this group for an existing PBXFileReference. Returns None
    288 		if none were found.
    289 
    290 		The reason you'd want to reuse existing PBXFileReferences from a PBXGroup is that XCode doesn't like PBXFileReferences that aren't part of a PBXGroup hierarchy.
    291 		If it isn't, the consequence is that certain UI features like 'Reveal in Finder'
    292 		stops working.
    293 		"""
    294 		if fileref in self._filerefs:
    295 			return self._filerefs[fileref]
    296 		elif self.children:
    297 			for childgroup in self.get_sub_groups():
    298 				f = childgroup.find_fileref(fileref)
    299 				if f:
    300 					return f
    301 		return None
    302 
    303 class PBXContainerItemProxy(XCodeNode):
    304 	""" This is the element for to decorate a target item. """
    305 	def __init__(self, containerPortal, remoteGlobalIDString, remoteInfo='', proxyType=1):
    306 		XCodeNode.__init__(self)
    307 		self.containerPortal = containerPortal # PBXProject
    308 		self.remoteGlobalIDString = remoteGlobalIDString # PBXNativeTarget
    309 		self.remoteInfo = remoteInfo # Target name
    310 		self.proxyType = proxyType
    311 
    312 class PBXTargetDependency(XCodeNode):
    313 	""" This is the element for referencing other target through content proxies. """
    314 	def __init__(self, native_target, proxy):
    315 		XCodeNode.__init__(self)
    316 		self.target = native_target
    317 		self.targetProxy = proxy
    318 
    319 class PBXFrameworksBuildPhase(XCodeNode):
    320 	""" This is the element for the framework link build phase, i.e. linking to frameworks """
    321 	def __init__(self, pbxbuildfiles):
    322 		XCodeNode.__init__(self)
    323 		self.buildActionMask = 2147483647
    324 		self.runOnlyForDeploymentPostprocessing = 0
    325 		self.files = pbxbuildfiles #List of PBXBuildFile (.o, .framework, .dylib)
    326 
    327 class PBXHeadersBuildPhase(XCodeNode):
    328 	""" This is the element for adding header files to be packaged into the .framework """
    329 	def __init__(self, pbxbuildfiles):
    330 		XCodeNode.__init__(self)
    331 		self.buildActionMask = 2147483647
    332 		self.runOnlyForDeploymentPostprocessing = 0
    333 		self.files = pbxbuildfiles #List of PBXBuildFile (.o, .framework, .dylib)
    334 
    335 class PBXCopyFilesBuildPhase(XCodeNode):
    336 	"""
    337 	Represents the PBXCopyFilesBuildPhase section. PBXBuildFile
    338 	can be added to this node to copy files after build is done.
    339 	"""
    340 	def __init__(self, pbxbuildfiles, dstpath, dstSubpathSpec=0, *args, **kwargs):
    341 			XCodeNode.__init__(self)
    342 			self.files = pbxbuildfiles
    343 			self.dstPath = dstpath
    344 			self.dstSubfolderSpec = dstSubpathSpec
    345 
    346 class PBXSourcesBuildPhase(XCodeNode):
    347 	""" Represents the 'Compile Sources' build phase in a Xcode target """
    348 	def __init__(self, buildfiles):
    349 		XCodeNode.__init__(self)
    350 		self.files = buildfiles # List of PBXBuildFile objects
    351 
    352 class PBXLegacyTarget(XCodeNode):
    353 	def __init__(self, action, target=''):
    354 		XCodeNode.__init__(self)
    355 		self.buildConfigurationList = XCConfigurationList([XCBuildConfiguration('waf', {})])
    356 		if not target:
    357 			self.buildArgumentsString = "%s %s" % (sys.argv[0], action)
    358 		else:
    359 			self.buildArgumentsString = "%s %s --targets=%s" % (sys.argv[0], action, target)
    360 		self.buildPhases = []
    361 		self.buildToolPath = sys.executable
    362 		self.buildWorkingDirectory = ""
    363 		self.dependencies = []
    364 		self.name = target or action
    365 		self.productName = target or action
    366 		self.passBuildSettingsInEnvironment = 0
    367 
    368 class PBXShellScriptBuildPhase(XCodeNode):
    369 	def __init__(self, action, target):
    370 		XCodeNode.__init__(self)
    371 		self.buildActionMask = 2147483647
    372 		self.files = []
    373 		self.inputPaths = []
    374 		self.outputPaths = []
    375 		self.runOnlyForDeploymentPostProcessing = 0
    376 		self.shellPath = "/bin/sh"
    377 		self.shellScript = "%s %s %s --targets=%s" % (sys.executable, sys.argv[0], action, target)
    378 
    379 class PBXNativeTarget(XCodeNode):
    380 	""" Represents a target in XCode, e.g. App, DyLib, Framework etc. """
    381 	def __init__(self, target, node, target_type=TARGET_TYPE_APPLICATION, configlist=[], buildphases=[]):
    382 		XCodeNode.__init__(self)
    383 		product_type = target_type[0]
    384 		file_type = target_type[1]
    385 
    386 		self.buildConfigurationList = XCConfigurationList(configlist)
    387 		self.buildPhases = buildphases
    388 		self.buildRules = []
    389 		self.dependencies = []
    390 		self.name = target
    391 		self.productName = target
    392 		self.productType = product_type # See TARGET_TYPE_ tuples constants
    393 		self.productReference = PBXFileReference(node.name, node.abspath(), file_type, '')
    394 
    395 	def add_configuration(self, cf):
    396 		""" :type cf: XCBuildConfiguration """
    397 		self.buildConfigurationList.buildConfigurations.append(cf)
    398 
    399 	def add_build_phase(self, phase):
    400 		# Some build phase types may appear only once. If a phase type already exists, then merge them.
    401 		if ( (phase.__class__ == PBXFrameworksBuildPhase)
    402 			or (phase.__class__ == PBXSourcesBuildPhase) ):
    403 			for b in self.buildPhases:
    404 				if b.__class__ == phase.__class__:
    405 					b.files.extend(phase.files)
    406 					return
    407 		self.buildPhases.append(phase)
    408 
    409 	def add_dependency(self, depnd):
    410 		self.dependencies.append(depnd)
    411 
    412 # Root project object
    413 class PBXProject(XCodeNode):
    414 	def __init__(self, name, version, env):
    415 		XCodeNode.__init__(self)
    416 
    417 		if not isinstance(env.PROJ_CONFIGURATION, dict):
    418 			raise Errors.WafError("Error: env.PROJ_CONFIGURATION must be a dictionary. This is done for you if you do not define one yourself. However, did you load the xcode module at the end of your wscript configure() ?")
    419 
    420 		# Retrieve project configuration
    421 		configurations = []
    422 		for config_name, settings in env.PROJ_CONFIGURATION.items():
    423 			cf = XCBuildConfiguration(config_name, settings)
    424 			configurations.append(cf)
    425 
    426 		self.buildConfigurationList = XCConfigurationList(configurations)
    427 		self.compatibilityVersion = version[0]
    428 		self.hasScannedForEncodings = 1
    429 		self.mainGroup = PBXGroup(name)
    430 		self.projectRoot = ""
    431 		self.projectDirPath = ""
    432 		self.targets = []
    433 		self._objectVersion = version[1]
    434 
    435 	def create_target_dependency(self, target, name):
    436 		""" : param target : PXBNativeTarget """
    437 		proxy = PBXContainerItemProxy(self, target, name)
    438 		dependency = PBXTargetDependency(target, proxy)
    439 		return dependency
    440 
    441 	def write(self, file):
    442 
    443 		# Make sure this is written only once
    444 		if self._been_written:
    445 			return
    446 
    447 		w = file.write
    448 		w("// !$*UTF8*$!\n")
    449 		w("{\n")
    450 		w("\tarchiveVersion = 1;\n")
    451 		w("\tclasses = {\n")
    452 		w("\t};\n")
    453 		w("\tobjectVersion = %d;\n" % self._objectVersion)
    454 		w("\tobjects = {\n\n")
    455 
    456 		XCodeNode.write(self, file)
    457 
    458 		w("\t};\n")
    459 		w("\trootObject = %s;\n" % self._id)
    460 		w("}\n")
    461 
    462 	def add_target(self, target):
    463 		self.targets.append(target)
    464 
    465 	def get_target(self, name):
    466 		""" Get a reference to PBXNativeTarget if it exists """
    467 		for t in self.targets:
    468 			if t.name == name:
    469 				return t
    470 		return None
    471 
    472 @TaskGen.feature('c', 'cxx')
    473 @TaskGen.after('propagate_uselib_vars', 'apply_incpaths')
    474 def process_xcode(self):
    475 	bld = self.bld
    476 	try:
    477 		p = bld.project
    478 	except AttributeError:
    479 		return
    480 
    481 	if not hasattr(self, 'target_type'):
    482 		return
    483 
    484 	products_group = bld.products_group
    485 
    486 	target_group = PBXGroup(self.name)
    487 	p.mainGroup.children.append(target_group)
    488 
    489 	# Determine what type to build - framework, app bundle etc.
    490 	target_type = getattr(self, 'target_type', 'app')
    491 	if target_type not in TARGET_TYPES:
    492 		raise Errors.WafError("Target type '%s' does not exists. Available options are '%s'. In target '%s'" % (target_type, "', '".join(TARGET_TYPES.keys()), self.name))
    493 	else:
    494 		target_type = TARGET_TYPES[target_type]
    495 	file_ext = target_type[2]
    496 
    497 	# Create the output node
    498 	target_node = self.path.find_or_declare(self.name+file_ext)
    499 	target = PBXNativeTarget(self.name, target_node, target_type, [], [])
    500 
    501 	products_group.children.append(target.productReference)
    502 
    503 	# Pull source files from the 'source' attribute and assign them to a UI group.
    504 	# Use a default UI group named 'Source' unless the user
    505 	# provides a 'group_files' dictionary to customize the UI grouping.
    506 	sources = getattr(self, 'source', [])
    507 	if hasattr(self, 'group_files'):
    508 		group_files = getattr(self, 'group_files', [])
    509 		for grpname,files in group_files.items():
    510 			group = bld.create_group(grpname, files)
    511 			target_group.children.append(group)
    512 	else:
    513 		group = bld.create_group('Source', sources)
    514 		target_group.children.append(group)
    515 
    516 	# Create a PBXFileReference for each source file.
    517 	# If the source file already exists as a PBXFileReference in any of the UI groups, then
    518 	# reuse that PBXFileReference object (XCode does not like it if we don't reuse)
    519 	for idx, path in enumerate(sources):
    520 		fileref = PBXFileReference(path.name, path.abspath())
    521 		existing_fileref = target_group.find_fileref(fileref)
    522 		if existing_fileref:
    523 			sources[idx] = existing_fileref
    524 		else:
    525 			sources[idx] = fileref
    526 
    527 	# If the 'source' attribute contains any file extension that XCode can't work with,
    528 	# then remove it. The allowed file extensions are defined in XCODE_EXTS.
    529 	is_valid_file_extension = lambda file: os.path.splitext(file.path)[1] in XCODE_EXTS
    530 	sources = list(filter(is_valid_file_extension, sources))
    531 
    532 	buildfiles = [bld.unique_buildfile(PBXBuildFile(x)) for x in sources]
    533 	target.add_build_phase(PBXSourcesBuildPhase(buildfiles))
    534 
    535 	# Check if any framework to link against is some other target we've made
    536 	libs = getattr(self, 'tmp_use_seen', [])
    537 	for lib in libs:
    538 		use_target = p.get_target(lib)
    539 		if use_target:
    540 			# Create an XCode dependency so that XCode knows to build the other target before this target
    541 			dependency = p.create_target_dependency(use_target, use_target.name)
    542 			target.add_dependency(dependency)
    543 
    544 			buildphase = PBXFrameworksBuildPhase([PBXBuildFile(use_target.productReference)])
    545 			target.add_build_phase(buildphase)
    546 			if lib in self.env.LIB:
    547 				self.env.LIB = list(filter(lambda x: x != lib, self.env.LIB))
    548 
    549 	# If 'export_headers' is present, add files to the Headers build phase in xcode.
    550 	# These are files that'll get packed into the Framework for instance.
    551 	exp_hdrs = getattr(self, 'export_headers', [])
    552 	hdrs = bld.as_nodes(Utils.to_list(exp_hdrs))
    553 	files = [p.mainGroup.find_fileref(PBXFileReference(n.name, n.abspath())) for n in hdrs]
    554 	files = [PBXBuildFile(f, {'ATTRIBUTES': ('Public',)}) for f in files]
    555 	buildphase = PBXHeadersBuildPhase(files)
    556 	target.add_build_phase(buildphase)
    557 
    558 	# Merge frameworks and libs into one list, and prefix the frameworks
    559 	frameworks = Utils.to_list(self.env.FRAMEWORK)
    560 	frameworks = ' '.join(['-framework %s' % (f.split('.framework')[0]) for f in frameworks])
    561 
    562 	libs = Utils.to_list(self.env.STLIB) + Utils.to_list(self.env.LIB)
    563 	libs = ' '.join(bld.env['STLIB_ST'] % t for t in libs)
    564 
    565 	# Override target specific build settings
    566 	bldsettings = {
    567 		'HEADER_SEARCH_PATHS': ['$(inherited)'] + self.env['INCPATHS'],
    568 		'LIBRARY_SEARCH_PATHS': ['$(inherited)'] + Utils.to_list(self.env.LIBPATH) + Utils.to_list(self.env.STLIBPATH) + Utils.to_list(self.env.LIBDIR),
    569 		'FRAMEWORK_SEARCH_PATHS': ['$(inherited)'] + Utils.to_list(self.env.FRAMEWORKPATH),
    570 		'OTHER_LDFLAGS': libs + ' ' + frameworks + ' ' + ' '.join(bld.env['LINKFLAGS']),
    571 		'OTHER_CPLUSPLUSFLAGS': Utils.to_list(self.env['CXXFLAGS']),
    572 		'OTHER_CFLAGS': Utils.to_list(self.env['CFLAGS']),
    573 		'INSTALL_PATH': [],
    574 		'GCC_PREPROCESSOR_DEFINITIONS': self.env['DEFINES']
    575 	}
    576 
    577 	# Install path
    578 	installpaths = Utils.to_list(getattr(self, 'install', []))
    579 	prodbuildfile = PBXBuildFile(target.productReference)
    580 	for instpath in installpaths:
    581 		bldsettings['INSTALL_PATH'].append(instpath)
    582 		target.add_build_phase(PBXCopyFilesBuildPhase([prodbuildfile], instpath))
    583 
    584 	if not bldsettings['INSTALL_PATH']:
    585 		del bldsettings['INSTALL_PATH']
    586 
    587 	# Create build settings which can override the project settings. Defaults to none if user
    588 	# did not pass argument. This will be filled up with target specific
    589 	# search paths, libs to link etc.
    590 	settings = getattr(self, 'settings', {})
    591 
    592 	# The keys represents different build configuration, e.g. Debug, Release and so on..
    593 	# Insert our generated build settings to all configuration names
    594 	keys = set(settings.keys()) | set(bld.env.PROJ_CONFIGURATION.keys())
    595 	for k in keys:
    596 		if k in settings:
    597 			settings[k].update(bldsettings)
    598 		else:
    599 			settings[k] = bldsettings
    600 
    601 	for k,v in settings.items():
    602 		target.add_configuration(XCBuildConfiguration(k, v))
    603 
    604 	p.add_target(target)
    605 
    606 
    607 class xcode(Build.BuildContext):
    608 	cmd = 'xcode6'
    609 	fun = 'build'
    610 
    611 	def as_nodes(self, files):
    612 		""" Returns a list of waflib.Nodes from a list of string of file paths """
    613 		nodes = []
    614 		for x in files:
    615 			if not isinstance(x, str):
    616 				d = x
    617 			else:
    618 				d = self.srcnode.find_node(x)
    619 				if not d:
    620 					raise Errors.WafError('File \'%s\' was not found' % x)
    621 			nodes.append(d)
    622 		return nodes
    623 
    624 	def create_group(self, name, files):
    625 		"""
    626 		Returns a new PBXGroup containing the files (paths) passed in the files arg
    627 		:type files: string
    628 		"""
    629 		group = PBXGroup(name)
    630 		"""
    631 		Do not use unique file reference here, since XCode seem to allow only one file reference
    632 		to be referenced by a group.
    633 		"""
    634 		files_ = []
    635 		for d in self.as_nodes(Utils.to_list(files)):
    636 			fileref = PBXFileReference(d.name, d.abspath())
    637 			files_.append(fileref)
    638 		group.add(files_)
    639 		return group
    640 
    641 	def unique_buildfile(self, buildfile):
    642 		"""
    643 		Returns a unique buildfile, possibly an existing one.
    644 		Use this after you've constructed a PBXBuildFile to make sure there is
    645 		only one PBXBuildFile for the same file in the same project.
    646 		"""
    647 		try:
    648 			build_files = self.build_files
    649 		except AttributeError:
    650 			build_files = self.build_files = {}
    651 
    652 		if buildfile not in build_files:
    653 			build_files[buildfile] = buildfile
    654 		return build_files[buildfile]
    655 
    656 	def execute(self):
    657 		"""
    658 		Entry point
    659 		"""
    660 		self.restore()
    661 		if not self.all_envs:
    662 			self.load_envs()
    663 		self.recurse([self.run_dir])
    664 
    665 		appname = getattr(Context.g_module, Context.APPNAME, os.path.basename(self.srcnode.abspath()))
    666 
    667 		p = PBXProject(appname, ('Xcode 3.2', 46), self.env)
    668 
    669 		# If we don't create a Products group, then
    670 		# XCode will create one, which entails that
    671 		# we'll start to see duplicate files in the UI
    672 		# for some reason.
    673 		products_group = PBXGroup('Products')
    674 		p.mainGroup.children.append(products_group)
    675 
    676 		self.project = p
    677 		self.products_group = products_group
    678 
    679 		# post all task generators
    680 		# the process_xcode method above will be called for each target
    681 		if self.targets and self.targets != '*':
    682 			(self._min_grp, self._exact_tg) = self.get_targets()
    683 
    684 		self.current_group = 0
    685 		while self.current_group < len(self.groups):
    686 			self.post_group()
    687 			self.current_group += 1
    688 
    689 		node = self.bldnode.make_node('%s.xcodeproj' % appname)
    690 		node.mkdir()
    691 		node = node.make_node('project.pbxproj')
    692 		with open(node.abspath(), 'w') as f:
    693 			p.write(f)
    694 		Logs.pprint('GREEN', 'Wrote %r' % node.abspath())
    695 
    696 def bind_fun(tgtype):
    697 	def fun(self, *k, **kw):
    698 		tgtype = fun.__name__
    699 		if tgtype == 'shlib' or tgtype == 'dylib':
    700 			features = 'cxx cxxshlib'
    701 			tgtype = 'dylib'
    702 		elif tgtype == 'framework':
    703 			features = 'cxx cxxshlib'
    704 			tgtype = 'framework'
    705 		elif tgtype == 'program':
    706 			features = 'cxx cxxprogram'
    707 			tgtype = 'exe'
    708 		elif tgtype == 'app':
    709 			features = 'cxx cxxprogram'
    710 			tgtype = 'app'
    711 		elif tgtype == 'stlib':
    712 			features = 'cxx cxxstlib'
    713 			tgtype = 'stlib'
    714 		lst = kw['features'] = Utils.to_list(kw.get('features', []))
    715 		for x in features.split():
    716 			if not x in kw['features']:
    717 				lst.append(x)
    718 
    719 		kw['target_type'] = tgtype
    720 		return self(*k, **kw)
    721 	fun.__name__ = tgtype
    722 	setattr(Build.BuildContext, tgtype, fun)
    723 	return fun
    724 
    725 for xx in 'app framework dylib shlib stlib program'.split():
    726 	bind_fun(xx)
    727