1. Alexey Petruchik
  2. SCons

Commits

Alexey Petruchik  committed 1f8b07b

refactoring Xcode project generation

  • Participants
  • Parent commits c662af6
  • Branches default

Comments (0)

Files changed (1)

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

View file
  • Ignore whitespace
         return ext in ['.c', '.cpp', '.cc', '.m', '.mm', '.cxx']
 
 class PBXLegacyTarget(XcodeNode):
-    def __init__(self, context, description, buildSettings = {}):
+    def __init__(self, context, description, buildSettings, buildtarget):
         XcodeNode.__init__(self, context, description)
 
-        self.buildArgumentsString = '"--xcode=${ACTION}"'
+        self.buildArgumentsString = '"--xcode=${ACTION} %s"' % buildtarget
+        self.buildWorkingDirectory = os.getcwd()
         self.buildConfigurationList = XCConfigurationList(context, 'Build configuration list for PBXLegacyTarget "%s"' % description, buildSettings)
         self.buildPhases = []
         self.buildToolPath = 'scons'
         self.productType = '"com.apple.product-type.tool"'
 
 class PBXProject(XcodeNode):
-    def __init__(self, context, description, project_name, buildSettings = {}):
+    def __init__(self, context, description, project_name, buildSettings, buildtarget):
         XcodeNode.__init__(self, context, description)
 
-        self.targets = [PBXLegacyTarget(context, project_name.split('.')[0], buildSettings)]
+        self.targets = [PBXLegacyTarget(context, project_name, buildSettings, buildtarget)]
         if context.generate_codenav_targets:
-            self.targets.append(PBXNativeTarget(context, project_name.split('.')[0] + '-codenav', buildSettings))
+            self.targets.append(PBXNativeTarget(context, project_name + '-codenav', buildSettings))
 
-        self.buildConfigurationList = XCConfigurationList(context, 'Build configuration list for PBXProject "%s"' % project_name, 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._subgroups = {}
 
-    def add_source(self, source, executable = False):
-        l = source.split(os.path.sep)
+    # 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)))
 
-        currGroup = self
-        path = ''
-        while len(l) > 1:
-            dir_name = l.pop(0)
-            path = os.path.join(path, dir_name)
+        return self._subgroups[subgroup_name]
 
-            if not currGroup._subgroups.has_key(dir_name):
-                newGroup = PBXGroup(self._context, dir_name)
-                currGroup._subgroups[dir_name] = newGroup
-                currGroup.children.append(newGroup)
-            
-            currGroup = currGroup._subgroups[dir_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)
 
-        p = PBXFileReference(self._context, source, executable)
-        currGroup.children.append(p)
-        return p
+        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):
         self.defaultConfigurationName = 'Release'
 
 def XcodeProjectAction(target, source, env):
-    project_name = str(target[0].srcnode())
-    print 'Generating Xcode project \"%s\"' % project_name
+    project_filename = str(target[0].srcnode())
+    project_name = os.path.basename(project_filename).split('.')[0]
 
-    # Create folder hierarchy
-    path = ''
-    for p in project_name.split(os.path.sep):
-        path = os.path.join(path, p)
-        try:
-            os.mkdir(path)
-        except:
-            pass
-
-    # Generate xxx.xcodeproj/project/project.xcworkspace/contents.xcworkspacedata
-    os.mkdir(os.path.join(project_name, 'project.xcworkspace'))
-    fw = open(os.path.join(project_name, 'project.xcworkspace', 'contents.xcworkspacedata'), 'w')
-    fw.write(contents_xcworkspacedata % project_name)
-
-    fp = open(os.path.join(project_name, 'project.pbxproj'), 'w')
-
-    fp.write(project_pbxproj_header)
+    print 'Generating Xcode project \"%s\"' % project_filename
+    print env.get('buildtarget', [])
 
     def custom_sort(x):
         if isinstance(x, PBXGroup):
 
     context = XcodeProjectBuildContext()
     context.generate_codenav_targets = env.get('generate_codenav_targets', 1)
-    context.project_dir = os.path.dirname(project_name)
+    context.project_dir = os.path.dirname(project_filename)
 
     include_search_path = map(os.path.abspath, env.get('CPPPATH', []))
 
         'PRODUCT_NAME': '"$(TARGET_NAME)"'
     }
 
-    for i in env.get('CPPPATH', []):
-        print os.path.abspath(i)
+    # Create project
+    project = PBXProject(context, 'Project object', project_name, buildSettings, env.get('buildtarget', ''))
+    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)
 
-    p = PBXProject(context, 'Project object', project_name, buildSettings)
-    
-    for i in env['srcs']:
-        p.mainGroup.add_source(i)
-
-    for i in env['incs']:
-        p.mainGroup.add_source(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():
-                p.targets[1].buildPhases[0].files.append(PBXBuildFile(context, f))
+                project.targets[1].buildPhases[0].files.append(PBXBuildFile(context, f))
 
+    # Add executable reference
     pp = PBXFileReference(context, 'scons-xcode', True)
-    p.targets[0].productReference = pp
-    p.targets[1].productReference = pp
+    project.targets[0].productReference = pp
+    project.targets[1].productReference = pp
 
-    context.print_section(fp, PBXFileReference)
-    context.print_section(fp, PBXGroup)
-    context.print_section(fp, PBXSourcesBuildPhase)
-    context.print_section(fp, PBXLegacyTarget)
-    context.print_section(fp, PBXNativeTarget)
-    context.print_section(fp, PBXProject)
-    context.print_section(fp, PBXBuildFile)
-    context.print_section(fp, XCBuildConfiguration)
-    context.print_section(fp, XCConfigurationList)
+    # 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
 
-    fp.write(project_pbxproj_footer % p.tostring(p))
+    # 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())