Adding option to create XCode project
Change-Id: I4d5792ff3bc3f8cc169ad3af3f7924344f55855c
diff --git a/waf-tools/xcode.py b/waf-tools/xcode.py
new file mode 100644
index 0000000..b3b8789
--- /dev/null
+++ b/waf-tools/xcode.py
@@ -0,0 +1,312 @@
+#! /usr/bin/env python
+# encoding: utf-8
+# XCode 3/XCode 4 generator for Waf
+# Nicolas Mercier 2011
+
+"""
+Usage:
+
+def options(opt):
+ opt.load('xcode')
+
+$ waf configure xcode
+"""
+
+# TODO: support iOS projects
+
+from waflib import Context, TaskGen, Build, Utils
+import os, sys, random, time
+
+HEADERS_GLOB = '**/(*.h|*.hpp|*.H|*.inl)'
+
+MAP_EXT = {
+ '.h' : "sourcecode.c.h",
+
+ '.hh': "sourcecode.cpp.h",
+ '.inl': "sourcecode.cpp.h",
+ '.hpp': "sourcecode.cpp.h",
+
+ '.c': "sourcecode.c.c",
+
+ '.m': "sourcecode.c.objc",
+
+ '.mm': "sourcecode.cpp.objcpp",
+
+ '.cc': "sourcecode.cpp.cpp",
+
+ '.cpp': "sourcecode.cpp.cpp",
+ '.C': "sourcecode.cpp.cpp",
+ '.cxx': "sourcecode.cpp.cpp",
+ '.c++': "sourcecode.cpp.cpp",
+
+ '.l': "sourcecode.lex", # luthor
+ '.ll': "sourcecode.lex",
+
+ '.y': "sourcecode.yacc",
+ '.yy': "sourcecode.yacc",
+
+ '.plist': "text.plist.xml",
+ ".nib": "wrapper.nib",
+ ".xib": "text.xib",
+}
+
+
+part1 = 0
+part2 = 10000
+part3 = 0
+id = 562000999
+def newid():
+ global id
+ id = id + 1
+ return "%04X%04X%04X%012d" % (0, 10000, 0, id)
+
+class XCodeNode:
+ def __init__(self):
+ self._id = newid()
+
+ def tostring(self, value):
+ if isinstance(value, dict):
+ result = "{\n"
+ for k,v in value.items():
+ result = result + "\t\t\t%s = %s;\n" % (k, self.tostring(v))
+ result = result + "\t\t}"
+ return result
+ elif isinstance(value, str):
+ return "\"%s\"" % value
+ elif isinstance(value, list):
+ result = "(\n"
+ for i in value:
+ result = result + "\t\t\t%s,\n" % self.tostring(i)
+ result = result + "\t\t)"
+ return result
+ elif isinstance(value, XCodeNode):
+ return value._id
+ else:
+ return str(value)
+
+ def write_recursive(self, value, file):
+ if isinstance(value, dict):
+ for k,v in value.items():
+ self.write_recursive(v, file)
+ elif isinstance(value, list):
+ for i in value:
+ self.write_recursive(i, file)
+ elif isinstance(value, XCodeNode):
+ value.write(file)
+
+ def write(self, file):
+ for attribute,value in self.__dict__.items():
+ if attribute[0] != '_':
+ self.write_recursive(value, file)
+
+ w = file.write
+ w("\t%s = {\n" % self._id)
+ w("\t\tisa = %s;\n" % self.__class__.__name__)
+ for attribute,value in self.__dict__.items():
+ if attribute[0] != '_':
+ w("\t\t%s = %s;\n" % (attribute, self.tostring(value)))
+ w("\t};\n\n")
+
+
+
+# Configurations
+class XCBuildConfiguration(XCodeNode):
+ def __init__(self, name, settings = {}, env=None):
+ XCodeNode.__init__(self)
+ self.baseConfigurationReference = ""
+ self.buildSettings = settings
+ self.name = name
+ if env and env.ARCH:
+ settings['ARCHS'] = " ".join(env.ARCH)
+
+
+class XCConfigurationList(XCodeNode):
+ def __init__(self, settings):
+ XCodeNode.__init__(self)
+ self.buildConfigurations = settings
+ self.defaultConfigurationIsVisible = 0
+ self.defaultConfigurationName = settings and settings[0].name or ""
+
+# Group/Files
+class PBXFileReference(XCodeNode):
+ def __init__(self, name, path, filetype = '', sourcetree = "SOURCE_ROOT"):
+ XCodeNode.__init__(self)
+ self.fileEncoding = 4
+ if not filetype:
+ _, ext = os.path.splitext(name)
+ filetype = MAP_EXT.get(ext, 'text')
+ self.lastKnownFileType = filetype
+ self.name = name
+ self.path = path
+ self.sourceTree = sourcetree
+
+class PBXGroup(XCodeNode):
+ def __init__(self, name, sourcetree = "<group>"):
+ XCodeNode.__init__(self)
+ self.children = []
+ self.name = name
+ self.sourceTree = sourcetree
+
+ def add(self, root, sources):
+ folders = {}
+ def folder(n):
+ if n == root:
+ return self
+ try:
+ return folders[n]
+ except KeyError:
+ f = PBXGroup(n.name)
+ p = folder(n.parent)
+ folders[n] = f
+ p.children.append(f)
+ return f
+ for s in sources:
+ f = folder(s.parent)
+ source = PBXFileReference(s.name, s.abspath())
+ f.children.append(source)
+
+
+# Targets
+class PBXLegacyTarget(XCodeNode):
+ def __init__(self, action, target=''):
+ XCodeNode.__init__(self)
+ self.buildConfigurationList = XCConfigurationList([XCBuildConfiguration('waf', {})])
+ if not target:
+ self.buildArgumentsString = "%s %s" % (sys.argv[0], action)
+ else:
+ self.buildArgumentsString = "%s %s --targets='%s'" % (sys.argv[0], action, target)
+ self.buildPhases = []
+ self.buildToolPath = sys.executable
+ self.buildWorkingDirectory = ""
+ self.dependencies = []
+ self.name = target or action
+ self.productName = target or action
+ self.passBuildSettingsInEnvironment = 0
+
+class PBXShellScriptBuildPhase(XCodeNode):
+ def __init__(self, action, target):
+ XCodeNode.__init__(self)
+ self.buildActionMask = 2147483647
+ self.files = []
+ self.inputPaths = []
+ self.outputPaths = []
+ self.runOnlyForDeploymentPostProcessing = 0
+ self.shellPath = "/bin/sh"
+ self.shellScript = "%s %s %s --targets='%s'" % (sys.executable, sys.argv[0], action, target)
+
+class PBXNativeTarget(XCodeNode):
+ def __init__(self, action, target, node, env):
+ XCodeNode.__init__(self)
+ conf = XCBuildConfiguration('waf', {'PRODUCT_NAME':target, 'CONFIGURATION_BUILD_DIR':node.parent.abspath()}, env)
+ self.buildConfigurationList = XCConfigurationList([conf])
+ self.buildPhases = [PBXShellScriptBuildPhase(action, target)]
+ self.buildRules = []
+ self.dependencies = []
+ self.name = target
+ self.productName = target
+ self.productType = "com.apple.product-type.application"
+ self.productReference = PBXFileReference(target, node.abspath(), 'wrapper.application', 'BUILT_PRODUCTS_DIR')
+
+# Root project object
+class PBXProject(XCodeNode):
+ def __init__(self, name, version):
+ XCodeNode.__init__(self)
+ self.buildConfigurationList = XCConfigurationList([XCBuildConfiguration('waf', {})])
+ self.compatibilityVersion = version[0]
+ self.hasScannedForEncodings = 1;
+ self.mainGroup = PBXGroup(name)
+ self.projectRoot = ""
+ self.projectDirPath = ""
+ self.targets = []
+ self._objectVersion = version[1]
+ self._output = PBXGroup('out')
+ self.mainGroup.children.append(self._output)
+
+ def write(self, file):
+ w = file.write
+ w("// !$*UTF8*$!\n")
+ w("{\n")
+ w("\tarchiveVersion = 1;\n")
+ w("\tclasses = {\n")
+ w("\t};\n")
+ w("\tobjectVersion = %d;\n" % self._objectVersion)
+ w("\tobjects = {\n\n")
+
+ XCodeNode.write(self, file)
+
+ w("\t};\n")
+ w("\trootObject = %s;\n" % self._id)
+ w("}\n")
+
+ def add_task_gen(self, tg):
+ if not getattr(tg, 'mac_app', False):
+ self.targets.append(PBXLegacyTarget('build', tg.name))
+ else:
+ target = PBXNativeTarget('build', tg.name, tg.link_task.outputs[0].change_ext('.app'), tg.env)
+ self.targets.append(target)
+ self._output.children.append(target.productReference)
+
+class xcode(Build.BuildContext):
+ cmd = 'xcode'
+ fun = 'build'
+
+ def collect_source(self, tg):
+ source_files = tg.to_nodes(getattr(tg, 'source', []))
+ plist_files = tg.to_nodes(getattr(tg, 'mac_plist', []))
+ resource_files = [tg.path.find_node(i) for i in Utils.to_list(getattr(tg, 'mac_resources', []))]
+ include_dirs = Utils.to_list(getattr(tg, 'includes', [])) + Utils.to_list(getattr(tg, 'export_dirs', []))
+ include_files = []
+ for x in include_dirs:
+ if not isinstance(x, str):
+ include_files.append(x)
+ continue
+ d = tg.path.find_node(x)
+ if d:
+ lst = [y for y in d.ant_glob(HEADERS_GLOB, flat=False)]
+ include_files.extend(lst)
+
+ # remove duplicates
+ source = list(set(source_files + plist_files + resource_files + include_files))
+ source.sort(key=lambda x: x.abspath())
+ return source
+
+ def execute(self):
+ """
+ Entry point
+ """
+ self.restore()
+ if not self.all_envs:
+ self.load_envs()
+ self.recurse([self.run_dir])
+
+ appname = getattr(Context.g_module, Context.APPNAME, os.path.basename(self.srcnode.abspath()))
+ p = PBXProject(appname, ('Xcode 3.2', 46))
+
+ for g in self.groups:
+ for tg in g:
+ if not isinstance(tg, TaskGen.task_gen):
+ continue
+
+ tg.post()
+
+ features = Utils.to_list(getattr(tg, 'features', ''))
+
+ group = PBXGroup(tg.name)
+ group.add(tg.path, self.collect_source(tg))
+ p.mainGroup.children.append(group)
+
+ if 'cprogram' or 'cxxprogram' in features:
+ p.add_task_gen(tg)
+
+
+ # targets that don't produce the executable but that you might want to run
+ p.targets.append(PBXLegacyTarget('configure'))
+ p.targets.append(PBXLegacyTarget('dist'))
+ p.targets.append(PBXLegacyTarget('install'))
+ p.targets.append(PBXLegacyTarget('check'))
+ node = self.srcnode.make_node('%s.xcodeproj' % appname)
+ node.mkdir()
+ node = node.make_node('project.pbxproj')
+ p.write(open(node.abspath(), 'w'))
+
+