Commits

Alexey Petruchik committed 897b381

initial XcodeProj arguments validation

  • Participants
  • Parent commits 6a5bfce

Comments (0)

Files changed (4)

File src/engine/MANIFEST.in

 SCons/Tool/wix.py
 SCons/Tool/yacc.py
 SCons/Tool/zip.py
-SCons/Tool/xcodeproj.py
+SCons/Tool/xcode.py
 SCons/Util.py
 SCons/Variables/__init__.py
 SCons/Variables/BoolVariable.py

File src/engine/SCons/Tool/__init__.py

         assemblers = ['as']
         fortran_compilers = ['gfortran', 'f95', 'f90', 'g77']
         ars = ['ar']
-        other_plat_tools = ['xcodeproj']
+        other_plat_tools = ['xcode']
     else:
         "prefer GNU tools on all other platforms"
         linkers = ['gnulink', 'mslink', 'ilink']

File src/engine/SCons/Tool/xcode.py

+"""SCons.Tool.xcodeproj
+
+Tool-specific initialization for Xcode project files.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+
+import SCons.Builder
+import SCons.Node.FS
+
+contents_xcworkspacedata = """\
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:%s">
+   </FileRef>
+</Workspace>
+"""
+
+project_pbxproj_header = """\
+// !$*UTF8*$!
+{
+    archiveVersion = 1;
+    classes = {
+    };
+    objectVersion = 46;
+    objects = {
+
+"""
+
+project_pbxproj_footer = """\
+    };
+    rootObject = %s;
+}
+"""
+
+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",
+    "": "",
+}
+
+class XcodeProjectBuildContext(object):
+    def __init__(self):
+        self.id = 562000999
+        self.nodes = {}
+        self.generate_codenav_targets = True
+
+    def newid(self):
+        self.id = self.id + 1
+        return "%04X%04X%04X%012d" % (0, 10000, 0, self.id)
+
+    def regNode(self, node):
+        type_name = node.__class__.__name__
+
+        if not self.nodes.has_key(type_name):
+            self.nodes[type_name] = []
+
+        self.nodes[type_name].append(node)
+
+    def print_section(self, file, t):
+        if self.nodes.has_key(t.__name__):
+            file.write('/* Begin %s section */\n' % t.__name__)
+            for n in self.nodes[t.__name__]:
+                n.write(file)
+            file.write('/* End %s section */\n\n' % t.__name__)
+
+class XcodeNode(object):
+    def __init__(self, context, description):
+        context.regNode(self)
+
+        self._context = context
+        self._id = context.newid()
+        self._name = description
+        
+        self.isa = self.__class__.__name__
+
+    def tostring(self, value):
+        if isinstance(value, list):
+            result = '(\n'
+            for i in value:
+                result = result + '\t\t\t\t%s,\n' % self.tostring(i)
+            result = result + '\t\t\t)'
+            return result
+        elif isinstance(value, dict):
+            result = '{\n'
+            for k, v in value.iteritems():
+                result = result + ('\t\t\t\t%s = %s;\n' % (self.tostring(k), self.tostring(v)))
+            result = result + '\t\t\t}'
+            return result
+        elif isinstance(value, XcodeNode):
+            return value._id + ' /* ' + value._name + ' */' if value._name != '' else value._id
+        else:
+            return str(value)
+
+    def write(self, f):
+        if self._name != '':
+            f.write('\t\t%s /* %s */ = {\n' % (self._id, self._name))
+        else:
+            f.write('\t\t%s = {\n' % (self._id))
+
+        for attribute,value in self.__dict__.items():
+            if attribute[0] != '_':
+                f.write('\t\t\t%s = %s;\n' % (attribute, self.tostring(value)))
+
+        f.write('\t\t};\n')
+
+class PBXFileReference(XcodeNode):
+    def __init__(self, context, path, executable = False):
+        XcodeNode.__init__(self, context, os.path.basename(path))
+
+        if not executable:
+            self.fileEncoding = 4
+            self.name = self._name
+            self.path = os.path.relpath(path, context.project_dir)
+            self.sourceTree = '"<group>"'
+
+            _, filetype = os.path.splitext(self._name)
+            self.lastKnownFileType = MAP_EXT[filetype]
+        else:
+            self.explicitFileType = '"compiled.mach-o.executable"'
+            self.includeInIndex = 0
+            self.path = path
+            self.sourceTree = 'BUILT_PRODUCTS_DIR'
+
+    def is_header_file(self):
+        _, ext = os.path.splitext(self._name)
+        return ext in ['.h', '.hh', '.hpp']
+
+    def is_inline_file(self):
+        _, ext = os.path.splitext(self._name)
+        return ext in ['.inl']
+
+    def is_source_file(self):
+        _, ext = os.path.splitext(self._name)
+        return ext in ['.c', '.cpp', '.cc', '.m', '.mm', '.cxx']
+
+class PBXLegacyTarget(XcodeNode):
+    def __init__(self, context, description, buildSettings):
+        XcodeNode.__init__(self, context, description)
+
+        self.buildArgumentsString = '"--xcode=${ACTION} ${SCONS_BUILD_TARGET}"'
+        self.buildWorkingDirectory = os.getcwd()
+        self.buildConfigurationList = XCConfigurationList(context, 'Build configuration list for PBXLegacyTarget "%s"' % description, buildSettings)
+        self.buildPhases = []
+        self.buildToolPath = 'scons'
+        self.dependencies = []
+        self.productName = '"%s"' % description
+        self.name = '"%s"' % description
+        self.passBuildSettingsInEnvironment = 1
+        self.productType = '"com.apple.product-type.tool"'
+        #self.productType = '"com.apple.product-type.library.static"'
+        #self.productType = '"com.apple.product-type.library.dynamic"'
+
+class PBXNativeTarget(XcodeNode):
+    def __init__(self, context, description, buildSettings = {}):
+        XcodeNode.__init__(self, context, description)
+
+        self.buildConfigurationList = XCConfigurationList(context, 'Build configuration list for PBXNativeTarget "%s"' % description, buildSettings)
+        self.buildPhases = [PBXSourcesBuildPhase(context, 'Sources')]
+        self.buildRules = []
+        self.dependencies = []
+        self.name = self._name
+        self.productName = self._name
+        self.productType = '"com.apple.product-type.tool"'
+
+class PBXProject(XcodeNode):
+    def __init__(self, context, description, project_name, buildSettings):
+        XcodeNode.__init__(self, context, description)
+
+        self.targets = [PBXLegacyTarget(context, project_name, buildSettings)]
+        if context.generate_codenav_targets:
+            self.targets.append(PBXNativeTarget(context, project_name + '-codenav', buildSettings))
+
+        self.buildConfigurationList = XCConfigurationList(context, 'Build configuration list for PBXProject "%s.xcodeproj"' % project_name, buildSettings)
+        self.compatibilityVersion = '"Xcode 3.2"'
+        self.projectDirPath = '""';
+        self.projectRoot = '""'
+        self.mainGroup = PBXGroup(context, '')
+
+class PBXGroup(XcodeNode):
+    def __init__(self, context, description):
+        XcodeNode.__init__(self, context, description)
+
+        self.sourceTree = '"<group>"'
+        self.children = []
+
+        if description != '':
+            self.name = description
+
+        self._subgroups = {}
+
+    # Find subgroup by name and create it if needed
+    def subgroup_by_name(self, subgroup_name):
+        if not self._subgroups.has_key(subgroup_name):
+            self.children.append(self._subgroups.setdefault(subgroup_name, PBXGroup(self._context, subgroup_name)))
+
+        return self._subgroups[subgroup_name]
+
+    # Add source file to group creating all hierarchy of subgroups if needed
+    def add_source(self, project_path, full_path, executable = False):
+        file_ref = PBXFileReference(self._context, full_path, executable)
+
+        group = reduce(lambda g, n: g.subgroup_by_name(n), project_path.split(os.path.sep)[:-1], self)
+        group.children.append(file_ref)
+
+        return file_ref
+
+class PBXSourcesBuildPhase(XcodeNode):
+    def __init__(self, context, description):
+        XcodeNode.__init__(self, context, description)
+        self.buildActionMask = 2147483647
+        self.runOnlyForDeploymentPostprocessing = 0
+        self.files = []
+
+class PBXBuildFile(XcodeNode):
+    def __init__(self, context, ref):
+        XcodeNode.__init__(self, context, ref._name)
+        self.fileRef = ref
+
+class XCBuildConfiguration(XcodeNode):
+    def __init__(self, context, description, buildSettings):
+        XcodeNode.__init__(self, context, description)
+
+        self.buildSettings = buildSettings
+        self.name = self._name
+
+class XCConfigurationList(XcodeNode):
+    def __init__(self, context, description, buildSettings = {}):
+        XcodeNode.__init__(self, context, description)
+
+        self.buildConfigurations = map(lambda key: XCBuildConfiguration(context, key, buildSettings[key]), buildSettings)
+        self.defaultConfigurationIsVisible = 0
+        self.defaultConfigurationName = buildSettings.keys()[0]
+
+def XcodeProjectAction(target, source, env):
+    project_filename = str(target[0].srcnode())
+    project_name = os.path.basename(project_filename).split('.')[0]
+
+    print 'Generating Xcode project \"%s\"' % project_filename
+
+    def custom_sort(x):
+        if isinstance(x, PBXGroup):
+            return '0' + x._name
+        elif isinstance(x, PBXFileReference):
+            if x.is_header_file(): return '1' + x._name
+            if x.is_inline_file(): return '2' + x._name
+            if x.is_source_file(): return '3' + x._name
+            return '4' + x._name
+        else:
+            return '5' + x._name
+
+    context = XcodeProjectBuildContext()
+    context.generate_codenav_targets = env.get('generate_codenav_targets', 1)
+    context.project_dir = os.path.dirname(project_filename)
+
+    # Get param from environment. Ensure it is a string or a node or a list of strings or nodes. Makes strings lists
+    def get_param(env, name, default_value):
+        param = env.get(name, default_value)
+        result = []
+
+        if SCons.Util.is_String(param):
+            result.append(param)
+        elif SCons.Util.is_List(param):
+            for i in param:
+                if SCons.Util.is_String(i):
+                    result.append(i)
+                else:
+                    try:
+                        result.append(str(i))
+                    except:
+                        raise SCons.Errors.InternalError("'%s' must be a string, a node, a list of strings or nodes" % name)
+        else:
+            raise SCons.Errors.InternalError("'%s' must be a string, a node, a list of strings or nodes" % name)
+
+        return result
+
+    configuration = get_param(env, 'configuration', 'Default')
+    buildtarget = get_param(env, 'buildtarget', '')
+    runfile = get_param(env, 'runfile', '')
+
+    # Validation
+    if len(configuration) != len(buildtarget):
+        raise SCons.Errors.InternalError("Sizes of 'configuration' and 'buildtarget' lists must be the same.")
+    if len(configuration) != len(runfile):
+        raise SCons.Errors.InternalError("Sizes of 'configuration' and 'runfile' lists must be the same.")
+
+    include_search_path = map(os.path.abspath, env.get('CPPPATH', []))
+
+    configurationBuildSettings = {}
+    for i in range(len(configuration)):
+        bs = {
+            'BUILT_PRODUCTS_DIR': os.path.dirname(os.path.abspath(runfile[i])),
+            'ARCHS': '"$(ARCHS_STANDARD_64_BIT)"',
+            'HEADER_SEARCH_PATHS': include_search_path,
+            'USER_HEADER_SEARCH_PATHS': include_search_path,
+            'PRODUCT_NAME': '"$(TARGET_NAME)"',
+            'SCONS_BUILD_TARGET': buildtarget[i],
+        }
+        configurationBuildSettings[configuration[i]] = bs
+
+
+
+    print configuration
+    print map(str, buildtarget)
+    print runfile
+
+    # buildSettings = {
+    #     'BUILT_PRODUCTS_DIR': os.path.dirname(os.path.abspath(env.get('runfile', ''))),
+    #     'ARCHS': '"$(ARCHS_STANDARD_64_BIT)"',
+    #     'HEADER_SEARCH_PATHS': include_search_path,
+    #     'USER_HEADER_SEARCH_PATHS': include_search_path,
+    #     'PRODUCT_NAME': '"$(TARGET_NAME)"',
+    #     'SCONS_BUILD_TARGET': env.get('buildtarget', ''),
+    # }
+
+    # Create project
+    project = PBXProject(context, 'Project object', project_name,configurationBuildSettings)# buildSettings)
+    sourcesGroup = project.mainGroup.subgroup_by_name(project_name)
+    
+    # Add all source files to project's source group
+    files = env['srcs'] = env['srcs'] + env['incs']
+    cp = os.path.commonprefix(files)
+    for i in files:
+        sourcesGroup.add_source(i[len(cp):], i)
+
+    # Sort files in groups
+    # TODO: Alphabetically? Headers than sources?
+    for g in context.nodes['PBXGroup']:
+        g.children.sort(key = custom_sort)
+
+    # Generate fake native Xcode targets for code navigation to work
+    if context.generate_codenav_targets:
+        for f in context.nodes['PBXFileReference']:
+            if f.is_source_file():
+                project.targets[1].buildPhases[0].files.append(PBXBuildFile(context, f))
+
+    # Add executable reference
+    pp = PBXFileReference(context, 'scons-xcode', True)
+    project.targets[0].productReference = pp
+    project.targets[1].productReference = pp
+
+    # Create folder hierarchy
+    path = ''
+    for dir_name in project_filename.split(os.path.sep):
+        path = os.path.join(path, dir_name)
+        try:
+            os.mkdir(path)
+        except:
+            pass
+
+    # Write down project.workspace file
+    os.mkdir(os.path.join(project_filename, 'project.xcworkspace'))
+    fw = open(os.path.join(project_filename, 'project.xcworkspace', 'contents.xcworkspacedata'), 'w')
+    fw.write(contents_xcworkspacedata % project_filename)
+
+    # Write down .pbxproj file
+    fp = open(os.path.join(project_filename, 'project.pbxproj'), 'w')
+    fp.write(project_pbxproj_header)
+
+    for i in [PBXFileReference, PBXGroup, PBXSourcesBuildPhase, PBXLegacyTarget, PBXNativeTarget, PBXProject, PBXBuildFile, XCBuildConfiguration, XCConfigurationList]:
+        context.print_section(fp, i)
+
+    fp.write(project_pbxproj_footer % project.tostring(project))
+
+def XcodeProjectEmitter(target, source, env):
+    return target, source
+
+projectBuilder = SCons.Builder.Builder(
+    #emitter = XcodeProjectEmitter,
+    action = SCons.Action.Action(XcodeProjectAction, None),
+    suffix = '.xcodeproj',
+    target_factory = SCons.Node.FS.Dir
+)
+
+def generate(env):
+    """Add Builders and construction variables for Xcode project files to an Environment."""
+
+    try:
+        env['BUILDERS']['XcodeProject']
+    except KeyError:
+        env['BUILDERS']['XcodeProject'] = projectBuilder
+    
+def exists(env):
+    return True
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:

File src/engine/SCons/Tool/xcodeproj.py

-"""SCons.Tool.xcodeproj
-
-Tool-specific initialization for Xcode project files.
-
-There normally shouldn't be any need to import this module directly.
-It will usually be imported through the generic SCons.Tool.Tool()
-selection method.
-
-"""
-
-#
-# __COPYRIGHT__
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be included
-# in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
-# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
-# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-
-import os
-
-import SCons.Builder
-import SCons.Node.FS
-
-contents_xcworkspacedata = """\
-<?xml version="1.0" encoding="UTF-8"?>
-<Workspace
-   version = "1.0">
-   <FileRef
-      location = "self:%s">
-   </FileRef>
-</Workspace>
-"""
-
-project_pbxproj_header = """\
-// !$*UTF8*$!
-{
-    archiveVersion = 1;
-    classes = {
-    };
-    objectVersion = 46;
-    objects = {
-
-"""
-
-project_pbxproj_footer = """\
-    };
-    rootObject = %s;
-}
-"""
-
-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",
-    "": "",
-}
-
-class XcodeProjectBuildContext(object):
-    def __init__(self):
-        self.id = 562000999
-        self.nodes = {}
-        self.generate_codenav_targets = True
-
-    def newid(self):
-        self.id = self.id + 1
-        return "%04X%04X%04X%012d" % (0, 10000, 0, self.id)
-
-    def regNode(self, node):
-        type_name = node.__class__.__name__
-
-        if not self.nodes.has_key(type_name):
-            self.nodes[type_name] = []
-
-        self.nodes[type_name].append(node)
-
-    def print_section(self, file, t):
-        if self.nodes.has_key(t.__name__):
-            file.write('/* Begin %s section */\n' % t.__name__)
-            for n in self.nodes[t.__name__]:
-                n.write(file)
-            file.write('/* End %s section */\n\n' % t.__name__)
-
-class XcodeNode(object):
-    def __init__(self, context, description):
-        context.regNode(self)
-
-        self._context = context
-        self._id = context.newid()
-        self._name = description
-        
-        self.isa = self.__class__.__name__
-
-    def tostring(self, value):
-        if isinstance(value, list):
-            result = '(\n'
-            for i in value:
-                result = result + '\t\t\t\t%s,\n' % self.tostring(i)
-            result = result + '\t\t\t)'
-            return result
-        elif isinstance(value, dict):
-            result = '{\n'
-            for k, v in value.iteritems():
-                result = result + ('\t\t\t\t%s = %s;\n' % (self.tostring(k), self.tostring(v)))
-            result = result + '\t\t\t}'
-            return result
-        elif isinstance(value, XcodeNode):
-            return value._id + ' /* ' + value._name + ' */' if value._name != '' else value._id
-        else:
-            return str(value)
-
-    def write(self, f):
-        if self._name != '':
-            f.write('\t\t%s /* %s */ = {\n' % (self._id, self._name))
-        else:
-            f.write('\t\t%s = {\n' % (self._id))
-
-        for attribute,value in self.__dict__.items():
-            if attribute[0] != '_':
-                f.write('\t\t\t%s = %s;\n' % (attribute, self.tostring(value)))
-
-        f.write('\t\t};\n')
-
-class PBXFileReference(XcodeNode):
-    def __init__(self, context, path, executable = False):
-        XcodeNode.__init__(self, context, os.path.basename(path))
-
-        if not executable:
-            self.fileEncoding = 4
-            self.name = self._name
-            self.path = os.path.relpath(path, context.project_dir)
-            self.sourceTree = '"<group>"'
-
-            _, filetype = os.path.splitext(self._name)
-            self.lastKnownFileType = MAP_EXT[filetype]
-        else:
-            self.explicitFileType = '"compiled.mach-o.executable"'
-            self.includeInIndex = 0
-            self.path = path
-            self.sourceTree = 'BUILT_PRODUCTS_DIR'
-
-    def is_header_file(self):
-        _, ext = os.path.splitext(self._name)
-        return ext in ['.h', '.hh', '.hpp']
-
-    def is_inline_file(self):
-        _, ext = os.path.splitext(self._name)
-        return ext in ['.inl']
-
-    def is_source_file(self):
-        _, ext = os.path.splitext(self._name)
-        return ext in ['.c', '.cpp', '.cc', '.m', '.mm', '.cxx']
-
-class PBXLegacyTarget(XcodeNode):
-    def __init__(self, context, description, buildSettings):
-        XcodeNode.__init__(self, context, description)
-
-        self.buildArgumentsString = '"--xcode=${ACTION} ${SCONS_BUILD_TARGET}"'
-        self.buildWorkingDirectory = os.getcwd()
-        self.buildConfigurationList = XCConfigurationList(context, 'Build configuration list for PBXLegacyTarget "%s"' % description, buildSettings)
-        self.buildPhases = []
-        self.buildToolPath = 'scons'
-        self.dependencies = []
-        self.productName = '"%s"' % description
-        self.name = '"%s"' % description
-        self.passBuildSettingsInEnvironment = 1
-        self.productType = '"com.apple.product-type.tool"'
-        #self.productType = '"com.apple.product-type.library.static"'
-        #self.productType = '"com.apple.product-type.library.dynamic"'
-
-class PBXNativeTarget(XcodeNode):
-    def __init__(self, context, description, buildSettings = {}):
-        XcodeNode.__init__(self, context, description)
-
-        self.buildConfigurationList = XCConfigurationList(context, 'Build configuration list for PBXNativeTarget "%s"' % description, buildSettings)
-        self.buildPhases = [PBXSourcesBuildPhase(context, 'Sources')]
-        self.buildRules = []
-        self.dependencies = []
-        self.name = self._name
-        self.productName = self._name
-        self.productType = '"com.apple.product-type.tool"'
-
-class PBXProject(XcodeNode):
-    def __init__(self, context, description, project_name, buildSettings):
-        XcodeNode.__init__(self, context, description)
-
-        self.targets = [PBXLegacyTarget(context, project_name, buildSettings)]
-        if context.generate_codenav_targets:
-            self.targets.append(PBXNativeTarget(context, project_name + '-codenav', buildSettings))
-
-        self.buildConfigurationList = XCConfigurationList(context, 'Build configuration list for PBXProject "%s.xcodeproj"' % project_name, buildSettings)
-        self.compatibilityVersion = '"Xcode 3.2"'
-        self.projectDirPath = '""';
-        self.projectRoot = '""'
-        self.mainGroup = PBXGroup(context, '')
-
-class PBXGroup(XcodeNode):
-    def __init__(self, context, description):
-        XcodeNode.__init__(self, context, description)
-
-        self.sourceTree = '"<group>"'
-        self.children = []
-
-        if description != '':
-            self.name = description
-
-        self._subgroups = {}
-
-    # Find subgroup by name and create it if needed
-    def subgroup_by_name(self, subgroup_name):
-        if not self._subgroups.has_key(subgroup_name):
-            self.children.append(self._subgroups.setdefault(subgroup_name, PBXGroup(self._context, subgroup_name)))
-
-        return self._subgroups[subgroup_name]
-
-    # Add source file to group creating all hierarchy of subgroups if needed
-    def add_source(self, project_path, full_path, executable = False):
-        file_ref = PBXFileReference(self._context, full_path, executable)
-
-        group = reduce(lambda g, n: g.subgroup_by_name(n), project_path.split(os.path.sep)[:-1], self)
-        group.children.append(file_ref)
-
-        return file_ref
-
-class PBXSourcesBuildPhase(XcodeNode):
-    def __init__(self, context, description):
-        XcodeNode.__init__(self, context, description)
-        self.buildActionMask = 2147483647
-        self.runOnlyForDeploymentPostprocessing = 0
-        self.files = []
-
-class PBXBuildFile(XcodeNode):
-    def __init__(self, context, ref):
-        XcodeNode.__init__(self, context, ref._name)
-        self.fileRef = ref
-
-class XCBuildConfiguration(XcodeNode):
-    def __init__(self, context, description, buildSettings):
-        XcodeNode.__init__(self, context, description)
-
-        self.buildSettings = buildSettings
-        self.name = self._name
-
-class XCConfigurationList(XcodeNode):
-    def __init__(self, context, description, buildSettings = {}):
-        XcodeNode.__init__(self, context, description)
-
-        self.buildConfigurations = map(lambda key: XCBuildConfiguration(context, key, buildSettings[key]), buildSettings)
-        self.defaultConfigurationIsVisible = 0
-        self.defaultConfigurationName = buildSettings.keys()[0]
-
-def XcodeProjectAction(target, source, env):
-    project_filename = str(target[0].srcnode())
-    project_name = os.path.basename(project_filename).split('.')[0]
-
-    print 'Generating Xcode project \"%s\"' % project_filename
-
-    def custom_sort(x):
-        if isinstance(x, PBXGroup):
-            return '0' + x._name
-        elif isinstance(x, PBXFileReference):
-            if x.is_header_file(): return '1' + x._name
-            if x.is_inline_file(): return '2' + x._name
-            if x.is_source_file(): return '3' + x._name
-            return '4' + x._name
-        else:
-            return '5' + x._name
-
-    context = XcodeProjectBuildContext()
-    context.generate_codenav_targets = env.get('generate_codenav_targets', 1)
-    context.project_dir = os.path.dirname(project_filename)
-
-    configurations = env.get('configurations', ['Default'])
-    buildtarget = env.get('buildtarget', [''])
-    runfile = env.get('runfile', [''])
-
-    include_search_path = map(os.path.abspath, env.get('CPPPATH', []))
-
-    # TODO: Check configuration, buildtarget and runfile must have same length
-
-    configurationBuildSettings = {}
-    for i in range(len(configurations)):
-        bs = {
-            'BUILT_PRODUCTS_DIR': os.path.dirname(os.path.abspath(runfile[i])),
-            'ARCHS': '"$(ARCHS_STANDARD_64_BIT)"',
-            'HEADER_SEARCH_PATHS': include_search_path,
-            'USER_HEADER_SEARCH_PATHS': include_search_path,
-            'PRODUCT_NAME': '"$(TARGET_NAME)"',
-            'SCONS_BUILD_TARGET': buildtarget[i]
-        }
-        configurationBuildSettings[configurations[i]] = bs
-
-
-
-    print configurations
-    print map(str, buildtarget)
-    print runfile
-
-    # buildSettings = {
-    #     'BUILT_PRODUCTS_DIR': os.path.dirname(os.path.abspath(env.get('runfile', ''))),
-    #     'ARCHS': '"$(ARCHS_STANDARD_64_BIT)"',
-    #     'HEADER_SEARCH_PATHS': include_search_path,
-    #     'USER_HEADER_SEARCH_PATHS': include_search_path,
-    #     'PRODUCT_NAME': '"$(TARGET_NAME)"',
-    #     'SCONS_BUILD_TARGET': env.get('buildtarget', ''),
-    # }
-
-    # Create project
-    project = PBXProject(context, 'Project object', project_name,configurationBuildSettings)# buildSettings)
-    sourcesGroup = project.mainGroup.subgroup_by_name(project_name)
-    
-    # Add all source files to project's source group
-    files = env['srcs'] = env['srcs'] + env['incs']
-    cp = os.path.commonprefix(files)
-    for i in files:
-        sourcesGroup.add_source(i[len(cp):], i)
-
-    # Sort files in groups
-    # TODO: Alphabetically? Headers than sources?
-    for g in context.nodes['PBXGroup']:
-        g.children.sort(key = custom_sort)
-
-    # Generate fake native Xcode targets for code navigation to work
-    if context.generate_codenav_targets:
-        for f in context.nodes['PBXFileReference']:
-            if f.is_source_file():
-                project.targets[1].buildPhases[0].files.append(PBXBuildFile(context, f))
-
-    # Add executable reference
-    pp = PBXFileReference(context, 'scons-xcode', True)
-    project.targets[0].productReference = pp
-    project.targets[1].productReference = pp
-
-    # Create folder hierarchy
-    path = ''
-    for dir_name in project_filename.split(os.path.sep):
-        path = os.path.join(path, dir_name)
-        try:
-            os.mkdir(path)
-        except:
-            pass
-
-    # Write down project.workspace file
-    os.mkdir(os.path.join(project_filename, 'project.xcworkspace'))
-    fw = open(os.path.join(project_filename, 'project.xcworkspace', 'contents.xcworkspacedata'), 'w')
-    fw.write(contents_xcworkspacedata % project_filename)
-
-    # Write down .pbxproj file
-    fp = open(os.path.join(project_filename, 'project.pbxproj'), 'w')
-    fp.write(project_pbxproj_header)
-
-    for i in [PBXFileReference, PBXGroup, PBXSourcesBuildPhase, PBXLegacyTarget, PBXNativeTarget, PBXProject, PBXBuildFile, XCBuildConfiguration, XCConfigurationList]:
-        context.print_section(fp, i)
-
-    fp.write(project_pbxproj_footer % project.tostring(project))
-
-def XcodeProjectEmitter(target, source, env):
-    project_name = str(target[0].srcnode())
-    target.append(os.path.join(project_name, 'project.pbxproj'))
-
-    if env.get('generate_workspace', 1):
-        target.append(os.path.join(project_name, 'project.xcworkspace'))
-        target.append(os.path.join(project_name, 'project.xcworkspace', 'contents.xcworkspacedata'))
-
-    return target, source
-
-projectBuilder = SCons.Builder.Builder(
-#    emitter = XcodeProjectEmitter,
-    action = SCons.Action.Action(XcodeProjectAction, None),
-    suffix = '.xcodeproj',
-    target_factory = SCons.Node.FS.Dir
-)
-
-def generate(env):
-    """Add Builders and construction variables for Xcode project files to an Environment."""
-
-    try:
-        env['BUILDERS']['XcodeProject']
-    except KeyError:
-        env['BUILDERS']['XcodeProject'] = projectBuilder
-    
-def exists(env):
-    return True
-
-# Local Variables:
-# tab-width:4
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=4 shiftwidth=4: