├── .gitignore ├── README.markdown └── source ├── codeblocks.py ├── codelite.py ├── debug.py ├── generate.py ├── makefile.py ├── project.py ├── project_object.py ├── project_parser.py ├── project_path.py ├── project_writer.py ├── visualc.py └── xcode.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | 4 | Generates project files for numerous compilers, IDE:s and makefiles from a XML definition file. 5 | 6 | Command line 7 | ------------ 8 | 9 | generate.py 10 | -i 11 | -p 12 | -g 13 | [-n optional override to project name] 14 | [-d optional path to data] 15 | [-r optional path to resource files] 16 | 17 | * **-i** *xml_definition_file*. The definition file to use. See below. 18 | * **-p** *platform_name*. mac_os_x, ios, windows or linux. 19 | * **-g** *generator*. visualc, xcode, makefile, codelite or codeblocks. 20 | * **-n** *project_name*. The name to use for the generated files. 21 | * **-d** *path_to_data*. Data that should be included in the build. Mostly for iphone (iOS) platforms. 22 | * **-r** *resource_files*. Resources, such as icons and plist files. 23 | 24 | XML tags 25 | -------- 26 | * *target*. Either an executable or library. 27 | * *dependency*. Includes another XML-definition to the target. Either merges it in or depends on the library it produces. 28 | * *source*. Includes all found (source) files in the specified directory. Optionally can include all sub directories recursively. 29 | * *header*. Search path for include files. 30 | * *define*. Adds a preprocessor define. 31 | * *library-path*. Paths to library files. 32 | * *configuration*. Defines a configuration. Defaults are "debug", "release", "internal" and "distribution". 33 | * *platform*. Platform-specific options are placed inside this tag scope. 34 | 35 | 36 | Sample XML file 37 | --------------- 38 | 39 | ```xml 40 | 41 | 42 | 43 |
44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | ``` 71 | 72 | -------------------------------------------------------------------------------- /source/codeblocks.py: -------------------------------------------------------------------------------- 1 | import project_writer 2 | import os 3 | import project_path 4 | import re 5 | 6 | class CodeBlocksNode(object): 7 | def __init__(self): 8 | self.children = [] 9 | 10 | class CodeBlocks_project_file(CodeBlocksNode): 11 | def __init__(self): 12 | super(CodeBlocks_project_file, self).__init__() 13 | 14 | class FileVersion(CodeBlocksNode): 15 | def __init__(self, major, minor): 16 | super(FileVersion, self).__init__() 17 | self.major = major 18 | self.minor = minor 19 | 20 | class Target(CodeBlocksNode): 21 | def __init__(self, title): 22 | super(Target, self).__init__() 23 | self.title = title 24 | 25 | class Compiler(CodeBlocksNode): 26 | def __init__(self): 27 | super(Compiler, self).__init__() 28 | pass 29 | 30 | class Linker(CodeBlocksNode): 31 | def __init__(self): 32 | super(Linker, self).__init__() 33 | pass 34 | 35 | class Option(CodeBlocksNode): 36 | def __init__(self): 37 | super(Option, self).__init__() 38 | pass 39 | 40 | class Unit(CodeBlocksNode): 41 | def __init__(self): 42 | super(Unit, self).__init__() 43 | 44 | class Extensions(CodeBlocksNode): 45 | def __init__(self): 46 | super(Extensions, self).__init__() 47 | 48 | 49 | class code_completion(CodeBlocksNode): 50 | def __init__(self): 51 | super(code_completion, self).__init__() 52 | 53 | class debugger(CodeBlocksNode): 54 | def __init__(self): 55 | super(debugger, self).__init__() 56 | 57 | class Add(CodeBlocksNode): 58 | def __init__(self): 59 | super(Add, self).__init__() 60 | pass 61 | 62 | class Build(CodeBlocksNode): 63 | def __init__(self): 64 | super(Build, self).__init__() 65 | 66 | class Project(CodeBlocksNode): 67 | def __init__(self, project): 68 | super(Project, self).__init__() 69 | self.generate(project) 70 | 71 | def add_output_path(self, parent, configuration_name, project_name): 72 | output = Option() 73 | output.output = "bin/" + configuration_name + "/" + project_name 74 | output.prefix_auto = "1" 75 | output.extension_auto = "1" 76 | parent.append(output) 77 | 78 | def add_object_output_path(self, parent, configuration_name): 79 | object_output = Option() 80 | object_output.object_output = "obj/" + configuration_name 81 | parent.append(object_output) 82 | 83 | def add_target(self, parent, project, configuration_name, formal_configuration_name): 84 | target = Target(configuration_name) 85 | self.add_output_path(target.children, configuration_name, project.name()) 86 | self.add_object_output_path(target.children, configuration_name) 87 | target_type = Option() 88 | target_type.type = "1" 89 | compiler = Compiler() 90 | target.children.append(compiler) 91 | parent.append(target) 92 | 93 | defines = project.configurations[formal_configuration_name].defines 94 | for define in defines: 95 | add_define = Add() 96 | add_define.option = "-D" + define 97 | compiler.children.append(add_define) 98 | 99 | return target, compiler 100 | 101 | def add_debug_target(self, parent, project): 102 | target, compiler = self.add_target(parent, project, "Debug", "debug") 103 | add = Add() 104 | add.option = "-g" 105 | compiler.children.append(add) 106 | 107 | def add_release_target(self, parent, project): 108 | target, compiler = self.add_target(parent, project, "Release", "release") 109 | add = Add() 110 | add.option = "-O2" 111 | compiler.children.append(add) 112 | linker = Linker() 113 | add = Add() 114 | add.option = "-s" 115 | linker.children.append(add) 116 | target.children.append(linker) 117 | 118 | def add_build(self, parent, project): 119 | build = Build() 120 | self.add_debug_target(build.children, project) 121 | self.add_release_target(build.children, project) 122 | parent.append(build) 123 | 124 | def add_global_compiler_options(self, parent, project): 125 | compiler = Compiler() 126 | add = Add() 127 | add.option = "-Wall" 128 | compiler.children.append(add) 129 | 130 | for include_path in project.settings.include_paths(): 131 | add = Add() 132 | add.directory = include_path 133 | compiler.children.append(add) 134 | 135 | for define in project.settings.defines: 136 | add_define = Add() 137 | add_define.option = "-D" + define 138 | compiler.children.append(add_define) 139 | 140 | parent.append(compiler) 141 | 142 | def add_global_linker_options(self, parent, project): 143 | linker = Linker() 144 | for library_file_name in project.settings.library_filenames: 145 | add = Add() 146 | add.library = library_file_name 147 | linker.children.append(add) 148 | 149 | parent.append(linker) 150 | 151 | def add_units(self, parent, project): 152 | for filename in project.settings.source_filenames(): 153 | unit = Unit() 154 | unit.filename = filename 155 | parent.append(unit) 156 | 157 | def add_extensions(self, parent): 158 | extensions = Extensions() 159 | extensions.children.append(code_completion()) 160 | extensions.children.append(debugger()) 161 | parent.append(extensions) 162 | 163 | def add_global_project_options(self, parent): 164 | title = Option() 165 | title.title = "Tyran" 166 | parent.append(title) 167 | 168 | pch = Option() 169 | pch.pchmode = "2" 170 | parent.append(pch) 171 | 172 | compiler = Option() 173 | compiler.compiler = "gcc" 174 | parent.append(compiler) 175 | 176 | def generate(self, project_definition): 177 | self.add_global_project_options(self.children) 178 | self.add_build(self.children, project_definition) 179 | self.add_global_compiler_options(self.children, project_definition) 180 | self.add_global_linker_options(self.children, project_definition) 181 | self.add_units(self.children, project_definition) 182 | self.add_extensions(self.children) 183 | 184 | class CodeBlocks: 185 | def __init__(self, project, source_root, platform): 186 | self.output = None 187 | self.project = project 188 | self.source_root = source_root 189 | self.generate_tree() 190 | 191 | def generate_tree(self): 192 | self.root = CodeBlocks_project_file() 193 | major = "1" 194 | minor = "6" 195 | self.root.children.append(FileVersion(major, minor)) 196 | self.root.children.append(Project(self.project)) 197 | 198 | def output_name_value(self, name, value): 199 | return " " + name + "=\"" + value + "\"" 200 | 201 | def output_scope_start(self, node): 202 | s = "<" + node.__class__.__name__ 203 | 204 | for name in sorted(iter(node.__dict__)): 205 | if name == "children": 206 | continue 207 | value = node.__dict__[name] 208 | s = s + self.output_name_value(name, value) 209 | 210 | if len(node.children) > 0: 211 | s = s + ">" 212 | self.output.output(s); 213 | self.output.increase_tabs() 214 | self.output_node_children(node.children) 215 | self.output.decrease_tabs() 216 | self.output_scope_end(node) 217 | else: 218 | s = s + "/>" 219 | self.output.output(s); 220 | 221 | def output_scope_end(self, node): 222 | self.output.output("") 223 | 224 | def output_value(self, node): 225 | if isinstance(node, CodeBlocksNode): 226 | self.output_scope_start(node) 227 | 228 | def output_node_children(self, children): 229 | for child in children: 230 | # exclude_properties = exclude_object.__dict__.keys() 231 | 232 | #if name in exclude_properties: 233 | # continue 234 | 235 | self.output_value(child) 236 | 237 | def write(self, creator, name): 238 | self.output = creator.create_file(name + ".project") 239 | self.output.output('') 240 | self.output_value(self.root) 241 | self.output.close() 242 | 243 | def close(self, output): 244 | pass 245 | 246 | def change_short_name_for_file_references(self, output): 247 | pass 248 | -------------------------------------------------------------------------------- /source/codelite.py: -------------------------------------------------------------------------------- 1 | import project_writer 2 | import os 3 | import project_path 4 | import re 5 | 6 | class CodeLiteNode(object): 7 | def __init__(self): 8 | self.children = [] 9 | 10 | class Settings(CodeLiteNode): 11 | def __init__(self): 12 | super(Settings, self).__init__() 13 | self.Type = "Executable" 14 | 15 | class GlobalSettings(CodeLiteNode): 16 | def __init__(self): 17 | super(GlobalSettings, self).__init__() 18 | 19 | class Compiler(CodeLiteNode): 20 | def __init__(self): 21 | super(Compiler, self).__init__() 22 | self.Options = "" 23 | self.C_Options = "" 24 | self.Required = "yes" 25 | self.PreCompiledHeader = "" 26 | pass 27 | 28 | class IncludePath(CodeLiteNode): 29 | def __init__(self, path): 30 | super(IncludePath, self).__init__() 31 | self.Value = path 32 | pass 33 | 34 | class Linker(CodeLiteNode): 35 | def __init__(self): 36 | super(Linker, self).__init__() 37 | self.Options = "" 38 | pass 39 | 40 | class LibraryPath(CodeLiteNode): 41 | def __init__(self, path): 42 | super(LibraryPath, self).__init__() 43 | self.Value = path 44 | 45 | class Library(CodeLiteNode): 46 | def __init__(self, filename): 47 | super(Library, self).__init__() 48 | self.Value = filename 49 | 50 | class Configuration(CodeLiteNode): 51 | def __init__(self, name): 52 | super(Configuration, self).__init__() 53 | self.Name = name 54 | self.CompilerType = "gnu g++" 55 | self.DebuggerType = "GNU gdb debugger" 56 | self.Type = "" 57 | self.BuildCmpWithGlobalSettings = "append" 58 | self.BuildLnkWithGlobalSettings = "append" 59 | self.BuildResWithGlobalSettings = "append" 60 | 61 | class General(CodeLiteNode): 62 | def __init__(self, configuration_name): 63 | super(General, self).__init__() 64 | self.OutputFile = "$(IntermediateDirectory)/$(ProjectName)" 65 | self.IntermediatedDirectory = "./" + configuration_name 66 | self.Command = "./$(ProjectName)" 67 | self.CommandArguments = "" 68 | self.WorkingDirectory = "$(IntermediateDirectory)" 69 | self.PauseExecWhenProcTerminates = "yes" 70 | 71 | class VirtualDirectory(CodeLiteNode): 72 | def __init__(self, name): 73 | super(VirtualDirectory, self).__init__() 74 | self.Name = name 75 | 76 | class File(CodeLiteNode): 77 | def __init__(self, name): 78 | super(File, self).__init__() 79 | self.Name = name 80 | pass 81 | 82 | class Project(CodeLiteNode): 83 | def __init__(self, project_name, config_name, inside_config): 84 | super(Project, self).__init__() 85 | self.Name = project_name 86 | if inside_config: 87 | self.ConfigName = config_name 88 | else: 89 | self.Path = project_name + ".project" 90 | self.Active = "Yes" 91 | 92 | class WorkspaceConfiguration(CodeLiteNode): 93 | def __init__(self, project_name, name, selected): 94 | super(WorkspaceConfiguration, self).__init__() 95 | self.Name = name 96 | self.Selected = "yes" if selected else "no" 97 | project = Project(project_name, name, True) 98 | self.children.append(project) 99 | 100 | class BuildMatrix(CodeLiteNode): 101 | def __init__(self, project): 102 | super(BuildMatrix, self).__init__() 103 | first_config = True 104 | print("CONFIGS:" + str(project.configurations)) 105 | for name in project.configurations.keys(): 106 | config = project.configurations[name] 107 | workspace_config = WorkspaceConfiguration(project.name(), config.name, first_config) 108 | self.children.append(workspace_config) 109 | first_config = False 110 | 111 | class CodeLite_Workspace(CodeLiteNode): 112 | def __init__(self, project): 113 | super(CodeLite_Workspace, self).__init__() 114 | self.Name = project.name() 115 | self.Database = "./" + self.Name + ".tags" 116 | 117 | codelite_project = Project(project.name(), "", False) 118 | self.children.append(codelite_project) 119 | 120 | build_matrix = BuildMatrix(project) 121 | self.children.append(build_matrix) 122 | 123 | class CodeLite_Project(CodeLiteNode): 124 | def __init__(self, project): 125 | super(CodeLite_Project, self).__init__() 126 | self.Name = project.name() 127 | self.InternalType = "Console" 128 | self.generate(project) 129 | 130 | def generate(self, project_definition): 131 | settings = self.add_global_settings(self.children, project_definition.settings) 132 | self.common_compiler_options = "-Wall" 133 | for name in project_definition.configurations.keys(): 134 | config = project_definition.configurations[name] 135 | if name == "debug": 136 | options_to_add = "-g" 137 | else: 138 | options_to_add = "" 139 | self.add_configuration(settings.children, config, config.name, config.name, options_to_add) 140 | self.add_files(self.children, project_definition) 141 | 142 | def add_global_settings(self, parent, project): 143 | settings = Settings() 144 | parent.append(settings) 145 | 146 | global_settings = GlobalSettings() 147 | settings.children.append(global_settings) 148 | define_options_string = "" 149 | 150 | for define in project.defines: 151 | define_options_string += " -D" + define 152 | 153 | compiler = Compiler() 154 | global_settings.children.append(compiler) 155 | compiler.Options = define_options_string 156 | compiler.C_Options = define_options_string + " -std=c99" 157 | 158 | for include_path in project.include_paths(): 159 | include_path_object = IncludePath(include_path) 160 | compiler.children.append(include_path_object) 161 | 162 | linker = Linker() 163 | global_settings.children.append(linker) 164 | 165 | for library_file_name in project.library_search_paths: 166 | library_path = LibraryPath(library_file_name) 167 | linker.children.append(library_path) 168 | 169 | for library_file_name in project.library_filenames: 170 | library_file = Library(library_file_name) 171 | linker.children.append(library_file) 172 | 173 | return settings 174 | 175 | def add_configuration(self, parent, settings, configuration_name, readable_name, options_to_add): 176 | config = Configuration(readable_name) 177 | 178 | compiler = Compiler() 179 | config.children.append(compiler) 180 | 181 | defines = settings.defines 182 | define_configuration_string = "" 183 | for define in defines: 184 | define_configuration_string += " -D" + define 185 | 186 | compiler.Options = self.common_compiler_options + " " + define_configuration_string + " " + options_to_add 187 | compiler.C_Options = self.common_compiler_options + " " + define_configuration_string + " " + options_to_add 188 | 189 | linker = Linker() 190 | config.children.append(linker) 191 | 192 | general = General(configuration_name) 193 | config.children.append(general) 194 | parent.append(config) 195 | 196 | def add_files(self, parent, project): 197 | vdir = VirtualDirectory("Code") 198 | parent.append(vdir) 199 | for filename in project.settings.source_filenames(): 200 | f = File(filename) 201 | vdir.children.append(f) 202 | 203 | class CodeLite: 204 | def __init__(self, project, source_root, platform): 205 | self.output = None 206 | self.project = project 207 | self.source_root = source_root 208 | self.generate_tree(project) 209 | 210 | def generate_tree(self, project): 211 | self.root = CodeLite_Project(project) 212 | self.workspace = CodeLite_Workspace(project) 213 | 214 | def output_name_value(self, name, value): 215 | return " " + name + "=\"" + value + "\"" 216 | 217 | def output_scope_start(self, node): 218 | s = "<" + node.__class__.__name__ 219 | 220 | for name in sorted(iter(node.__dict__.keys())): 221 | if name == "children": 222 | continue 223 | value = node.__dict__[name] 224 | s = s + self.output_name_value(name, value) 225 | 226 | if len(node.children) > 0: 227 | s = s + ">" 228 | self.output.output(s); 229 | self.output.increase_tabs() 230 | self.output_node_children(node.children) 231 | self.output.decrease_tabs() 232 | self.output_scope_end(node) 233 | else: 234 | s = s + "/>" 235 | self.output.output(s); 236 | 237 | def output_scope_end(self, node): 238 | self.output.output("") 239 | 240 | def output_value(self, node): 241 | if isinstance(node, CodeLiteNode): 242 | self.output_scope_start(node) 243 | 244 | def output_node_children(self, children): 245 | for child in children: 246 | # exclude_properties = exclude_object.__dict__.keys() 247 | 248 | #if name in exclude_properties: 249 | # continue 250 | 251 | self.output_value(child) 252 | 253 | def write(self, creator, name): 254 | self.output = creator.create_file(name + ".project") 255 | self.output.output('') 256 | self.output_value(self.root) 257 | self.output.close() 258 | 259 | self.output = creator.create_file(name + ".workspace") 260 | self.output.output('') 261 | self.output_value(self.workspace) 262 | self.output.close() 263 | 264 | def change_short_name_for_file_references(self, output): 265 | pass 266 | -------------------------------------------------------------------------------- /source/debug.py: -------------------------------------------------------------------------------- 1 | def show_attributes(attributes): 2 | for i in range(attributes.length): 3 | item = attributes.item(i) 4 | print item.name + "='" + item.value + "'", 5 | 6 | def show_nodes(node, tabs): 7 | for x in node.childNodes: 8 | for tab in range(0, tabs): 9 | print " ", 10 | 11 | if x.localName != None: 12 | print "<" + x.localName, 13 | show_attributes(x.attributes) 14 | print " />" 15 | 16 | show_nodes(x, tabs+1) 17 | -------------------------------------------------------------------------------- /source/generate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from xml.dom import minidom 3 | import xcode 4 | import project_writer 5 | import project 6 | 7 | import project_parser 8 | import project_path 9 | import makefile 10 | import visualc 11 | import codeblocks 12 | import codelite 13 | 14 | import sys 15 | import os 16 | 17 | def usage(): 18 | print("generate -i project_definition -p platform -g project_format\nproject_definition: The xml-file that defines the project\nplatform: iphone, mac_os_x, windows or linux\nproject_format: makefile, visualc, xcode, codelite, codeblocks") 19 | sys.exit(0) 20 | 21 | try: 22 | import getopt 23 | optlist, list = getopt.getopt(sys.argv[1:], 'p:i:g:d:n:r:o:', "") 24 | except getopt.GetoptError: 25 | usage() 26 | print("called exception") 27 | sys.exit(1) 28 | 29 | class Options: 30 | def __init__(self): 31 | self.platform_string = "" 32 | self.input_filename = "" 33 | self.generator_name = "" 34 | self.data_path = "" 35 | self.resource_path = "" 36 | self.project_name = "" 37 | self.target_path = "" 38 | 39 | options = Options() 40 | 41 | for option, value in optlist: 42 | if option == "-p": 43 | options.platform_string = value 44 | elif option == "-i": 45 | options.input_filename = value 46 | elif option == "-g": 47 | options.generator_name = value 48 | elif option == "-d": 49 | options.data_path = os.path.abspath(value) 50 | elif option == "-r": 51 | options.resource_path = os.path.abspath(value) 52 | elif option == "-n": 53 | options.project_name = value 54 | elif option == "-o": 55 | options.target_path = os.path.abspath(value) 56 | 57 | import os 58 | import platform 59 | 60 | def touch(filename): 61 | if not os.path.exists(filename): 62 | try: 63 | os.makedirs(os.path.dirname(filename)) 64 | except os.error as e: 65 | import errno 66 | if e.errno != errno.EEXIST: 67 | raise 68 | open(filename, 'w').close() 69 | 70 | def create_project(filename, platform_string, data_path, resource_path): 71 | target_project = project.Project(platform_string) 72 | parser = project_parser.Parser() 73 | node = minidom.parse(filename) 74 | 75 | source_root = os.path.abspath(os.path.dirname(filename)) + "/" 76 | parser.parse(node, target_project, source_root, platform_string) 77 | if target_project.target_type != "library": 78 | if data_path != None: 79 | resource_root = os.path.normpath(data_path) + "/" 80 | # print("data:", resource_root) 81 | target_project.settings.add_resource_directory(resource_root, False, []) 82 | 83 | resource_root = os.path.normpath(resource_path) + "/" + platform_string + "/" 84 | # print("Resource:", resource_root) 85 | target_project.settings.add_resource_directory(resource_root, False, []) 86 | 87 | 88 | return target_project 89 | 90 | def load_project(filename, platform_string, data_path, resource_path): 91 | target_project = create_project(filename, platform_string, data_path, resource_path) 92 | for dependency in target_project.dependencies(): 93 | if not dependency.merge: 94 | raise "Not supported!!" 95 | 96 | p = load_project(dependency.filename, platform_string, data_path, resource_path) 97 | target_project.merge(p) 98 | 99 | return target_project 100 | 101 | def get_class( kls ): 102 | parts = kls.split('.') 103 | module = ".".join(parts[:-1]) 104 | m = __import__( module ) 105 | for comp in parts[1:]: 106 | m = getattr(m, comp) 107 | return m 108 | 109 | if options.project_name == "": 110 | print("Must specify project name") 111 | exit(-1) 112 | 113 | target_project = load_project(options.input_filename, options.platform_string, options.data_path, options.resource_path) 114 | 115 | if options.project_name != "": 116 | target_project.set_name(options.project_name) 117 | 118 | source_root = os.path.abspath(os.path.dirname(options.input_filename)) + "/" 119 | source_root = source_root.replace("\\", "/") 120 | 121 | build_dir = options.target_path + "/" + options.platform_string + "/" 122 | build_dir = build_dir.replace("\\", "/") 123 | 124 | target_filename_prefix = build_dir 125 | project_file_name_prefix = options.project_name 126 | 127 | if options.generator_name == "xcode": 128 | generator_name = "xcode.Xcode" 129 | elif options.generator_name == "makefile": 130 | generator_name = "makefile.Makefile" 131 | elif options.generator_name == "visualc": 132 | generator_name = "visualc.VisualC" 133 | elif options.generator_name == "codeblocks": 134 | generator_name = "codeblocks.CodeBlocks" 135 | elif options.generator_name == "codelite": 136 | generator_name = "codelite.CodeLite" 137 | 138 | generator = get_class(generator_name)(target_project, source_root, options.platform_string) 139 | 140 | if options.platform_string == "ios" or options.platform_string == "mac_os_x": 141 | touch(build_dir + target_project.name() + "_Prefix.pch") 142 | 143 | creator = project_writer.ProjectFileCreator(target_filename_prefix) 144 | generator.write(creator, project_file_name_prefix) 145 | -------------------------------------------------------------------------------- /source/makefile.py: -------------------------------------------------------------------------------- 1 | import project_writer 2 | import os 3 | import project_path 4 | import re 5 | 6 | class Makefile: 7 | def __init__(self, project, source_root, platform): 8 | self.output = None 9 | self.project = project 10 | self.source_root = source_root 11 | 12 | def output_variable(self, variable, content): 13 | self.output.output(variable + " = " + content) 14 | self.output.output("") 15 | 16 | def output_variable_list(self, variable, contents): 17 | s = "" 18 | for content in contents: 19 | s = s + " " + content 20 | 21 | self.output_variable(variable, s) 22 | 23 | def output_target_dependency(self, target, dependency): 24 | self.output.output(target + ": " + dependency) 25 | self.output.increase_tabs() 26 | 27 | def output_target_dependencies(self, target, dependencies, commands): 28 | self.output.output("") 29 | s = "" 30 | for dependency in dependencies: 31 | s = s + " " + dependency 32 | 33 | self.output_target_dependency(target, s) 34 | for command in commands: 35 | self.output_command(command) 36 | self.output.decrease_tabs() 37 | 38 | def output_command(self, command): 39 | self.output.output(command) 40 | 41 | def change_extension(self, filename, new_extension): 42 | return file_without_extension + "." + new_extension 43 | 44 | def write(self, creator, name): 45 | self.output = creator.create_file("/Makefile") 46 | 47 | complete_sources = self.project.settings.source_filenames() 48 | objects = [] 49 | sources = [] 50 | 51 | for source in complete_sources: 52 | path = project_path.Path(source) 53 | relative_source = path.relative(self.source_root) 54 | file_without_extension, extension = os.path.splitext(relative_source) 55 | if extension == ".cpp" or extension == ".c": 56 | sources.append(relative_source) 57 | objects.append(file_without_extension + ".o") 58 | 59 | includes = self.project.settings.include_paths() 60 | include_string = " -I" + " -I".join(includes) 61 | 62 | define_string = "-D" + " -D".join(self.project.configurations["debug"].defines + self.project.settings.defines) 63 | 64 | if self.project.settings.library_filenames: 65 | link_string = "-l" + " -l".join(self.project.settings.library_filenames) 66 | else: 67 | link_string = ""; 68 | 69 | if self.project.settings.framework_names: 70 | link_string += "-framework " + " -framework ".join(self.project.settings.framework_names) 71 | 72 | compiler_executable = self.project.settings.compiler_executable or "g++" 73 | self.output_variable_list("cc", [compiler_executable]) 74 | self.output_variable_list("c", [ compiler_executable, "-x c"]) 75 | 76 | compiler_flags = self.project.settings.compiler_flags + ["-Wall", "-Wextra -Werror -std=c99 -pedantic"] 77 | flags_array = ["-c", define_string, include_string] 78 | flags_array.extend(compiler_flags) 79 | self.output_variable_list("cflags", flags_array) 80 | 81 | linker_flags = self.project.settings.linker_flags or ""; 82 | self.output_variable_list("ldflags", [linker_flags, link_string]) 83 | 84 | self.output_variable_list("sources", sources) 85 | self.output_variable_list("objects", objects) 86 | self.output_variable_list("executable", [self.project.target_name + ".out"]) 87 | 88 | self.output_target_dependencies("all", [self.project.target_name], []) 89 | self.output_target_dependencies("clean", "", ["@rm -f $(objects)", "@echo clean done!"]) 90 | self.output_target_dependencies(self.project.target_name, ["$(sources) $(executable)"], ["@echo 'done'"]) 91 | self.output_target_dependencies("-include $(objects:.o=.d)", [], []) 92 | self.output_target_dependencies("$(executable)", ["$(objects)"], ["@echo Linking $@", "@$(cc) $(objects) $(ldflags)"]) 93 | self.output_target_dependencies(".c.o", [], ["echo Compiling c $@", "@$(c) $(cflags) $< -o $@"]) 94 | self.output_target_dependencies(".cpp.o", [], ["echo Compiling c++ $@", "@$(cc) $(cflags) $< -o $@"]) 95 | self.output_target_dependencies("depend", [], ["makedepend -f " + self.output.target_path + "Makefile -- $(cflags) -- $(sources) -I" + include_string]) 96 | self.close() 97 | self.output.close() 98 | 99 | def close(self): 100 | self.output.output("# DO NOT DELETE") 101 | pass 102 | -------------------------------------------------------------------------------- /source/project.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | import project_path 4 | 5 | class SourceFileNode: 6 | def __init__(self): 7 | self.filenames = [] 8 | 9 | def extend(self, other): 10 | self.filenames.extend(other.filenames) 11 | 12 | def search_recursive(self, dor, extensions, exclude_basenames): 13 | # print("BaseName rec:", dor) 14 | for path, dirs, files in os.walk(dor): 15 | for filename in files: 16 | filename = os.path.abspath(os.path.join(path, filename)) 17 | filename = filename.replace("\\", "/") 18 | 19 | basename = os.path.basename(filename) 20 | 21 | if basename in exclude_basenames: 22 | continue 23 | extension = os.path.splitext(filename)[1][1:] 24 | if extension in extensions: 25 | complete_path = filename 26 | self.filenames.append(complete_path) 27 | 28 | def search_directory_only(self, dor, extensions, exclude_basenames): 29 | # print("BaseName:", dor + "*") 30 | filenames = glob.glob(dor + "*") 31 | for filename in filenames: 32 | filename = filename.replace("\\", "/") 33 | basename = os.path.basename(filename) 34 | 35 | # print("filename:", filename, "BaseName:", basename) 36 | if basename in exclude_basenames: 37 | continue 38 | 39 | extension = os.path.splitext(filename)[1][1:] 40 | if extension in extensions: 41 | complete_path = filename 42 | self.filenames.append(complete_path) 43 | 44 | self.filenames.sort() 45 | 46 | def source_filenames(self): 47 | return self.filenames 48 | 49 | class Dependency: 50 | def __init__(self, filename, merge): 51 | self.filename = filename 52 | self.merge = merge 53 | 54 | class Define: 55 | def __init__(self, name): 56 | self.name = name 57 | 58 | class Settings(object): 59 | def __init__(self): 60 | self.defines = [] 61 | self.header_paths = [] 62 | self.root_source_files = SourceFileNode() 63 | self.library_search_paths = [] 64 | self.library_filenames = [] 65 | self.framework_names = [] 66 | self.framework_search_paths = [] 67 | self.root_resource_files = SourceFileNode() 68 | self.compiler_executable = None 69 | self.compiler_flags = [] 70 | self.linker_flags = [] 71 | 72 | def extend(self, setting): 73 | self.defines.extend(setting.defines) 74 | self.header_paths.extend(setting.header_paths) 75 | self.root_source_files.extend(setting.root_source_files) 76 | self.library_search_paths.extend(setting.library_search_paths) 77 | self.library_filenames.extend(setting.library_filenames) 78 | self.framework_names.extend(setting.framework_names) 79 | self.framework_search_paths.extend(setting.framework_search_paths) 80 | self.root_resource_files.extend(setting.root_resource_files) 81 | self.compiler_executable = self.compiler_executable or setting.compiler_executable 82 | self.compiler_flags.extend(setting.compiler_flags) 83 | self.linker_flags.extend(setting.linker_flags) 84 | 85 | def add_define(self, name): 86 | self.defines.append(name) 87 | 88 | def add_header_directory(self, path): 89 | self.header_paths.append(path) 90 | header_extensions = ["h", "hpp"] 91 | empty_exclude_list = [] 92 | self.root_source_files.search_recursive(path, header_extensions, empty_exclude_list) 93 | 94 | def add_source_directory(self, path, recursive, exclude_list): 95 | extensions = ["cpp", "c", "h", "pch", "xib", "m", "mm"] 96 | if recursive: 97 | self.root_source_files.search_recursive(path, extensions, exclude_list) 98 | else: 99 | self.root_source_files.search_directory_only(path, extensions, exclude_list) 100 | 101 | def add_resource_directory(self, path, recursive, exclude_list): 102 | extensions = ["png", "xib", "storyboard", "oes", "oeb", "oec", "jpg", "ogg", "icns", "plist"] 103 | if recursive: 104 | self.root_resource_files.search_recursive(path, extensions, exclude_list) 105 | else: 106 | self.root_resource_files.search_directory_only(path, extensions, exclude_list) 107 | 108 | def resource_filenames(self): 109 | return self.root_resource_files.source_filenames() 110 | 111 | def source_filenames(self): 112 | return self.root_source_files.source_filenames() 113 | 114 | def add_library_filename(self, library_filename): 115 | if library_filename[-10:] == ".framework": 116 | self.framework_names.append(library_filename[:-10]) 117 | else: 118 | self.library_filenames.append(library_filename) 119 | 120 | def add_library_search_path(self, path): 121 | self.library_search_paths.append(path) 122 | 123 | def add_framework_search_path(self, path): 124 | self.framework_search_paths.append(path) 125 | 126 | def set_compiler(self, compiler, flags): 127 | self.compiler_executable = compiler 128 | self.compiler_flags = flags.split(" ") 129 | 130 | def set_linker(self, linker, flags): 131 | self.linker_executable = linker 132 | self.linker_flags = flags 133 | 134 | def include_paths(self): 135 | return self.header_paths 136 | 137 | class Configuration(Settings): 138 | def __init__(self, name): 139 | super(Configuration, self).__init__() 140 | self.name = name 141 | 142 | class Project: 143 | def __init__(self, platform_string): 144 | self.platform_string = platform_string 145 | self.target_name = None 146 | self.dependency_projects = [] 147 | self.configurations = {} 148 | self.settings = Settings() 149 | 150 | def add_configuration(self, name): 151 | config = Configuration(name) 152 | self.configurations[name] = config 153 | return config 154 | 155 | def configuration(self, name): 156 | if name in self.configurations: 157 | return self.configurations[name] 158 | else: 159 | return self.add_configuration(name) 160 | 161 | def settings(self): 162 | return self.settings 163 | 164 | def merge(self, p): 165 | self.settings.extend(p.settings) 166 | assert(self.platform_string == p.platform_string) 167 | self.configurations = p.configurations 168 | 169 | def add_dependency(self, filename, merge): 170 | self.dependency_projects.append(Dependency(filename, merge)) 171 | 172 | def dependencies(self): 173 | return self.dependency_projects 174 | 175 | def set_name(self, name): 176 | self.target_name = name 177 | 178 | def set_target_type(self, target_type): 179 | self.target_type = target_type 180 | 181 | def name(self): 182 | return self.target_name 183 | -------------------------------------------------------------------------------- /source/project_object.py: -------------------------------------------------------------------------------- 1 | import project_writer 2 | import os 3 | 4 | def add_quotation_marks_when_needed(value): 5 | value = '"' + value + '"' 6 | return value 7 | 8 | class WriterAttribute(project_writer.ProjectWriter): 9 | def __init__(self, keyword, value): 10 | project_writer.ProjectWriter.__init__(self) 11 | self.keyword = keyword 12 | self.value = value 13 | 14 | def write(self, output): 15 | keyword = self.keyword 16 | value = add_quotation_marks_when_needed(self.value) 17 | output.output_no_lf(keyword + " = " + value + " ") 18 | 19 | class WriterScope(project_writer.ProjectWriter): 20 | def __init__(self, keyword): 21 | project_writer.ProjectWriter.__init__(self) 22 | self.keyword = keyword 23 | 24 | def write(self, output): 25 | output.output("<" + str(self.keyword) + ">") 26 | output.increase_tabs() 27 | 28 | def close(self, output): 29 | project_writer.ProjectWriter.close(self, output) 30 | output.decrease_tabs() 31 | output.output( "<" + str(self.keyword) + " />" ) 32 | 33 | class WriterList(project_writer.ProjectWriter): 34 | def __init__(self, ids): 35 | project_writer.ProjectWriter.__init__(self) 36 | self.ids = ids 37 | 38 | def write(self, output): 39 | for id_object in self.ids: 40 | output_value(id_object, output) 41 | 42 | def close(self, output): 43 | pass 44 | 45 | class WriterDictionary(project_writer.ProjectWriter): 46 | def __init__(self, dictionary): 47 | project_writer.ProjectWriter.__init__(self) 48 | self.dictionary = dictionary 49 | 50 | def write(self, output): 51 | project_writer.ProjectWriter.write(self, output) 52 | for name in sorted(self.dictionary.keys()): 53 | value = self.dictionary[name] 54 | output.output("<" + name + ">" + value + "") 55 | 56 | def output_value(value, output): 57 | if isinstance(value, list): 58 | write_object = WriterList(value) 59 | elif isinstance(value, dict): 60 | write_object = WriterDictionary(value) 61 | else: 62 | write_object = value 63 | 64 | write_object.write(output) 65 | write_object.close(output) 66 | 67 | class WriterObject(project_writer.ProjectWriter): 68 | def write_all_attributes(self, output, exclude_object = None): 69 | for name in sorted(self.__dict__.keys()): 70 | if exclude_object != None: 71 | exclude_properties = exclude_object.__dict__.keys() 72 | 73 | if name in exclude_properties: 74 | continue 75 | 76 | value = self.__dict__[name] 77 | output_value( value, output) 78 | 79 | def add_properties(self): 80 | s = "" 81 | for name in sorted(self.__dict__.keys()): 82 | value = self.__dict__[name] 83 | if not isinstance(value, list) and not isinstance(value, dict): 84 | s = s + " " + name + '="' + value + '" ' 85 | return s 86 | 87 | def write_all_lists(self, output): 88 | for name in sorted(self.__dict__.keys()): 89 | value = self.__dict__[name] 90 | if isinstance(value, list): 91 | output_value(value, output) 92 | 93 | def write_all_dictionaries(self, output): 94 | for name in sorted(self.__dict__.keys()): 95 | value = self.__dict__[name] 96 | if isinstance(value, dict): 97 | output_value(value, output) 98 | 99 | def name(self): 100 | return self.__class__.__name__ 101 | 102 | def has_children(self): 103 | for name in sorted(self.__dict__.keys()): 104 | value = self.__dict__[name] 105 | if isinstance(value, dict) or isinstance(value, list): 106 | return True 107 | return False 108 | 109 | def write(self, output): 110 | s = "<" + self.name() 111 | s = s + self.add_properties() 112 | if self.has_children(): 113 | s = s + ">" 114 | output.output(s) 115 | output.increase_tabs() 116 | self.write_all_lists(output) 117 | self.write_all_dictionaries(output) 118 | else: 119 | s = s + "/>" 120 | output.output(s) 121 | 122 | def close(self, output): 123 | if self.has_children(): 124 | s = "" 125 | output.decrease_tabs() 126 | output.output(s) 127 | -------------------------------------------------------------------------------- /source/project_parser.py: -------------------------------------------------------------------------------- 1 | import project_path 2 | import os 3 | 4 | class Target: 5 | def __init__(self): 6 | self.name = "" 7 | self.type = "" 8 | 9 | class Source: 10 | def __init__(self): 11 | self.directory = "" 12 | self.recursive = False 13 | self.exclude = "" 14 | self.compile_as = "" 15 | 16 | class Resource: 17 | def __init__(self): 18 | self.directory = "" 19 | self.recursive = False 20 | self.exclude = "" 21 | 22 | class Platform: 23 | def __init__(self): 24 | self.name = "" 25 | 26 | class Configuration: 27 | def __init__(self): 28 | self.name = "" 29 | self.define = "" 30 | 31 | class Header: 32 | def __init__(self): 33 | self.directory = "" 34 | 35 | class Library: 36 | def __init__(self): 37 | self.filename = "" 38 | 39 | class LibraryPath: 40 | def __init__(self): 41 | self.directory = "" 42 | 43 | class FrameworkPath: 44 | def __init__(self): 45 | self.directory = "" 46 | 47 | class Compiler: 48 | def __init__(self): 49 | self.flags = "" 50 | self.compiler = "" 51 | 52 | class Linker: 53 | def __init__(self): 54 | self.flags = "" 55 | self.linker = "" 56 | 57 | class Dependency: 58 | def __init__(self): 59 | self.filename = "" 60 | self.merge = False 61 | 62 | class Definer: 63 | def __init__(self): 64 | self.name = "" 65 | 66 | class Parser: 67 | def parse(self, node, project, root_directory, platform_string): 68 | self.project = project 69 | self.root_directory = root_directory 70 | self.platform_string = platform_string 71 | 72 | for sub_node in node.childNodes: 73 | if sub_node.localName == "target": 74 | target = Target() 75 | self.parse_object(target, sub_node) 76 | self.project.set_name(target.name) 77 | data_dir = root_directory 78 | 79 | self.project.set_target_type(target.type) 80 | self.parse_target_sub_nodes(sub_node, self.project.settings) 81 | 82 | def parse_target_sub_nodes(self, node, settings): 83 | for sub_node in node.childNodes: 84 | if sub_node.localName == "source": 85 | source = Source() 86 | self.parse_object(source, sub_node) 87 | settings.add_source_directory(source.directory, source.recursive, source.exclude) 88 | 89 | elif sub_node.localName == "dependency": 90 | dependency = Dependency() 91 | self.parse_object(dependency, sub_node) 92 | self.project.add_dependency(dependency.filename, dependency.merge) 93 | 94 | elif sub_node.localName == "header": 95 | header = Header() 96 | self.parse_object(header, sub_node) 97 | directory = header.directory.replace("\\", "/") 98 | settings.add_header_directory(directory) 99 | 100 | elif sub_node.localName == "library": 101 | library = Library() 102 | self.parse_object(library, sub_node) 103 | settings.add_library_filename(library.filename) 104 | extension = os.path.splitext(library.filename)[1][1:] 105 | if extension == "framework": 106 | if library.filename[0:3] == "../": 107 | library.filename = self.convert_path(library.filename) 108 | directory = os.path.dirname(library.filename[0:-1]) 109 | settings.add_framework_search_path(directory) 110 | 111 | 112 | elif sub_node.localName == "library-path": 113 | library_path = LibraryPath() 114 | self.parse_object(library_path, sub_node) 115 | settings.add_library_search_path(library_path.directory) 116 | 117 | elif sub_node.localName == "compiler": 118 | compiler = Compiler() 119 | self.parse_object(compiler, sub_node) 120 | settings.set_compiler(compiler.program, compiler.flags) 121 | 122 | elif sub_node.localName == "linker": 123 | linker = Linker() 124 | self.parse_object(linker, sub_node) 125 | print("LINKER: ", linker.program, linker.flags) 126 | settings.set_linker(linker.program, linker.flags) 127 | 128 | elif sub_node.localName == "platform": 129 | platform = Platform() 130 | self.parse_object(platform, sub_node) 131 | if platform.name == self.platform_string: 132 | # print("We should parse platform:", self.platform_string) 133 | self.parse_target_sub_nodes(sub_node, settings) 134 | #print("********** platform:", self.platform_string) 135 | 136 | elif sub_node.localName == "define": 137 | d = Definer() 138 | self.parse_object(d, sub_node) 139 | settings.add_define(d.name) 140 | 141 | elif sub_node.localName == "configuration": 142 | c = Configuration() 143 | self.parse_object(c, sub_node) 144 | sub_config = self.project.configuration(c.name) 145 | self.parse_target_sub_nodes(sub_node, sub_config) 146 | 147 | def convert_path(self, path): 148 | new_path = project_path.Path(self.root_directory).join(path) 149 | new_path = new_path.replace("\\", "/") 150 | return new_path 151 | 152 | def parse_object(self, o, node): 153 | attributes = node.attributes 154 | for i in range(attributes.length): 155 | item = attributes.item(i) 156 | if "directory" in item.name: 157 | before = item.value 158 | item.value = self.convert_path(item.value) 159 | elif item.value != "" and item.value[0] == "[": 160 | value = item.value[1:-1] 161 | value_list = value.split() 162 | value = "".join(value_list) 163 | item.value = value.split(",") 164 | setattr(o, item.name, item.value) 165 | -------------------------------------------------------------------------------- /source/project_path.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | class Path: 4 | def __init__(self, path_string): 5 | self.path_string = path_string 6 | 7 | def _relpath(self, target, base=os.curdir): 8 | if not os.path.exists(target): 9 | raise OSError('Target does not exist: '+target) 10 | 11 | if not os.path.isdir(base): 12 | raise OSError( 'Base is not a directory or does not exist: '+base) 13 | 14 | base_list = (os.path.abspath(base)).split(os.sep) 15 | target_list = (os.path.abspath(target)).split(os.sep) 16 | 17 | if os.name in ['nt','dos','os2'] and base_list[0] != target_list[0]: 18 | raise OSError('Target is on a different drive to base. Target: '+target_list[0].upper()+', base: '+base_list[0].upper()) 19 | 20 | for i in range(min(len(base_list), len(target_list))): 21 | if base_list[i] != target_list[i]: break 22 | else: 23 | i+=1 24 | 25 | rel_list = [os.pardir] * (len(base_list)-i) + target_list[i:] 26 | return os.path.join(*rel_list) 27 | 28 | def join(self, path_string): 29 | new_path = os.path.normpath(os.path.join(self.path_string, path_string)) + "/" 30 | return new_path 31 | 32 | def relative(self, start_path_string): 33 | new_path = self._relpath(self.path_string, start_path_string) 34 | new_path = new_path.replace("\\", "/") 35 | return new_path 36 | -------------------------------------------------------------------------------- /source/project_writer.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | class ProjectFileCreator: 4 | def __init__(self, prefix): 5 | self.prefix = prefix 6 | 7 | def create_file(self, name): 8 | return ProjectFileOutput(self.prefix + name) 9 | 10 | class ProjectOutput: 11 | def __init__(self): 12 | self.tab_count = 0 13 | 14 | def output(self, s): 15 | self.write(self.__output_tabs() + s + "\n") 16 | 17 | def output_no_lf(self, s): 18 | self.write(s) 19 | 20 | def increase_tabs(self): 21 | self.tab_count += 1 22 | 23 | def decrease_tabs(self): 24 | self.tab_count -= 1 25 | 26 | def __output_tabs(self): 27 | s = "" 28 | for x in range(0, self.tab_count): 29 | s = s + "\t" 30 | return s 31 | 32 | class ProjectFileOutput(ProjectOutput): 33 | def __init__(self, filename): 34 | ProjectOutput.__init__(self) 35 | import errno 36 | try: 37 | directory = os.path.dirname(filename) 38 | print("Making directories:" + directory) 39 | os.makedirs(directory) 40 | except os.error as e: 41 | if e.errno != errno.EEXIST: 42 | raise 43 | self.file = open(filename, 'w') 44 | self.target_path = os.path.dirname(filename) + "/" 45 | print("Writing to file:", self.target_path) 46 | 47 | def close(self): 48 | self.file.close() 49 | 50 | def write(self, s): 51 | self.file.write(s) 52 | 53 | class ProjectWriter: 54 | def __init__(self): 55 | self.stack = [] 56 | 57 | def write(self, output): 58 | pass 59 | 60 | def close(self, output): 61 | while len(self.stack) > 0: 62 | o = self.stack.pop() 63 | o.close(output) 64 | 65 | def push(self, object, output): 66 | self.stack.append(object) 67 | object.write(output) 68 | -------------------------------------------------------------------------------- /source/visualc.py: -------------------------------------------------------------------------------- 1 | import project_object 2 | import os 3 | import project_path 4 | 5 | class VisualC(): 6 | def __init__(self, project, source_root, platform): 7 | self.source_project = project 8 | 9 | def write(self, creator, name): 10 | output = creator.create_file(name + ".vcxproj") 11 | self.project = Project(self.source_project, output.target_path) 12 | self.document = Document(self.project) 13 | self.document.write(output) 14 | self.document.close(output) 15 | output.close() 16 | 17 | class ClInclude(project_object.WriterObject): 18 | def __init__(self, relative_file_path): 19 | self.Include = relative_file_path 20 | 21 | class ClCompile(project_object.WriterObject): 22 | def __init__(self, relative_file_path): 23 | self.Include = relative_file_path 24 | 25 | class CompileAs(project_object.WriterObject): 26 | def __init__(self, compile_as): 27 | self.Include = relative_file_path 28 | 29 | 30 | class ClCompileDefinition(project_object.WriterObject): 31 | def __init__(self, attribs): 32 | project_object.WriterObject.__init__(self) 33 | self.attribs = attribs 34 | 35 | def name(self): 36 | return "ClCompile" 37 | 38 | class Document(project_object.WriterObject): 39 | def __init__(self, projects): 40 | self.Project = projects 41 | 42 | def write(self, output): 43 | output.output('') 44 | project_object.WriterObject.write(self.Project, output) 45 | 46 | def close(self, output): 47 | project_object.WriterObject.close(self.Project, output) 48 | 49 | class Import(project_object.WriterObject): 50 | pass 51 | 52 | class ItemDefinitionGroup(project_object.WriterObject): 53 | def __init__(self, condition, compile_options, link_options): 54 | self.Condition = condition 55 | self.compile = [ClCompileDefinition(compile_options), ] 56 | self.link = [Link(link_options), ] 57 | 58 | class ItemGroup(project_object.WriterObject): 59 | pass 60 | 61 | class ImportGroup(project_object.WriterObject): 62 | def __init__(self, label): 63 | self.Label = label 64 | 65 | class Link(project_object.WriterObject): 66 | def __init__(self, options): 67 | self.attribs = options 68 | 69 | class ProjectConfiguration(project_object.WriterObject): 70 | def __init__(self, include_configuration): 71 | self.Include = include_configuration 72 | 73 | class PropertyGroup(project_object.WriterObject): 74 | pass 75 | 76 | class Project(project_object.WriterObject): 77 | def __init__(self, project, write_path): 78 | self.DefaultTargets = "Build" 79 | self.ToolsVersion = "4.0" 80 | self.target_path = "" 81 | self.xmlns = "http://schemas.microsoft.com/developer/msbuild/2003" 82 | 83 | project_configurations_item_group = ItemGroup() 84 | project_configurations_item_group.Label = "ProjectConfigurations" 85 | 86 | debug_configuration = ProjectConfiguration("Debug|Win32") 87 | debug_configuration.attribs = {"Configuration": "Debug", "Platform": "Win32" } 88 | 89 | release_configuration = ProjectConfiguration("Release|Win32") 90 | release_configuration.attribs = {"Configuration": "Release", "Platform": "Win32" } 91 | 92 | project_configurations_item_group.configurations = [debug_configuration, release_configuration] 93 | 94 | source_files = project.settings.root_source_files.source_filenames() 95 | 96 | files_to_compile_item_group = ItemGroup() 97 | files_to_compile_item_group.files = [] 98 | 99 | files_to_include_item_group = ItemGroup() 100 | files_to_include_item_group.files = [] 101 | 102 | for filename in source_files: 103 | extension = os.path.splitext(filename)[1][1:] 104 | relative_filename = project_path.Path(filename).relative(write_path) 105 | if extension == "h" or extension == "hpp" or extension == "inc": 106 | files_to_include_item_group.files.append(ClInclude(relative_filename)) 107 | elif extension == "cpp": 108 | files_to_compile_item_group.files.append(ClCompile(relative_filename)) 109 | elif extension == "c": 110 | c = ClCompile(relative_filename) 111 | c.other = {"CompileAs": "CompileAsCpp" } 112 | files_to_compile_item_group.files.append(c) 113 | 114 | globals_property_group = PropertyGroup() 115 | globals_property_group.Label = "Globals" 116 | globals_property_group.attribs = {"ProjectGuid": "{95F04BB7-67C7-4BD5-9BF4-79E24D45023E}", "Keyword": "Win32Proj", "RootNamespace": "mergeblob"} 117 | 118 | default_props_import = Import() 119 | default_props_import.Project = "$(VCTargetsPath)\Microsoft.Cpp.Default.props" 120 | 121 | debug_configuration_property_group = PropertyGroup() 122 | debug_configuration_property_group.Condition = "'$(Configuration)|$(Platform)'=='Debug|Win32'" 123 | debug_configuration_property_group.Label = "Configuration" 124 | debug_configuration_property_group.attribs = {"ConfigurationType": "Application", "UseDebugLibraries": "true", "PlatformToolset": "v110", "CharacterSet": "NotSet"} 125 | 126 | release_configuration_property_group = PropertyGroup() 127 | release_configuration_property_group.Condition = "'$(Configuration)|$(Platform)'=='Release|Win32'" 128 | release_configuration_property_group.Label = "Configuration" 129 | release_configuration_property_group.attribs = {"ConfigurationType": "Application", "UseDebugLibraries": "false", "PlatformToolset": "v110", "CharacterSet": "NotSet"} 130 | 131 | self.item_groups = [project_configurations_item_group, files_to_compile_item_group, files_to_include_item_group, globals_property_group, default_props_import, debug_configuration_property_group, release_configuration_property_group] 132 | 133 | user_props_import = Import() 134 | user_props_import.Project ="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" 135 | user_props_import.Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" 136 | user_props_import.Label="LocalAppDataPlatform" 137 | 138 | extension_settings_import_group = ImportGroup("ExtensionSettings") 139 | props_import = Import() 140 | props_import.Project="$(VCTargetsPath)\Microsoft.Cpp.props" 141 | debug_property_sheets = ImportGroup("PropertySheets") 142 | debug_property_sheets.Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" 143 | debug_property_sheets.imports= [user_props_import, ] 144 | 145 | release_property_sheets = ImportGroup("PropertySheets") 146 | release_property_sheets.Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" 147 | release_property_sheets.imports= [user_props_import, ] 148 | 149 | 150 | user_macros_property_group = PropertyGroup() 151 | user_macros_property_group.Label="UserMacros" 152 | 153 | debug_link_property_group = PropertyGroup() 154 | debug_link_property_group.Condition ="'$(Configuration)|$(Platform)'=='Debug|Win32'" 155 | debug_link_property_group.attribs = {"LinkIncremental": "true"} 156 | 157 | 158 | release_link_property_group = PropertyGroup() 159 | release_link_property_group.Condition ="'$(Configuration)|$(Platform)'=='Release|Win32'" 160 | release_link_property_group.attribs = {"LinkIncremental": "false"} 161 | 162 | include_paths = [] 163 | for header_path in project.settings.include_paths(): 164 | header_path = project_path.Path(header_path).relative(write_path) 165 | include_paths.append(header_path) 166 | 167 | include_paths = ";".join(include_paths) 168 | 169 | additionalIncludeDirectories = include_paths + ";%(AdditionalIncludeDirectories)" 170 | 171 | library_paths = [] 172 | for library_filename in project.settings.library_filenames: 173 | library_paths.append(library_filename) 174 | 175 | library_path_string = ";".join(library_paths) 176 | additionalDependencies = library_path_string + ";%(AdditionalDependencies)" 177 | project_specific_defines_string = ";".join(project.settings.defines) 178 | 179 | debug_defines = ";".join(project.settings.defines + project.configurations["debug"].defines + ["WIN32", "_DEBUG", "_CONSOLE", "%(PreprocessorDefinitions)"]) 180 | release_defines = ";".join(project.settings.defines + project.configurations["release"].defines + ["WIN32", "NDEBUG", "_CONSOLE", "%(PreprocessorDefinitions)"]) 181 | 182 | library_dir = ";".join(project.settings.library_search_paths) 183 | 184 | debug_compile_options = {"PrecompiledHeader": "", "WarningLevel": "Level4", "Optimization": "Disabled", "FunctionLevelLinking": "true", "IntrinsicFunctions": "true", "PreprocessorDefinitions": debug_defines, "AdditionalIncludeDirectories": additionalIncludeDirectories} 185 | debug_link_options = {"SubSystem": "Console", "GenerateDebugInformation": "true", "AdditionalDependencies": additionalDependencies, "AdditionalLibraryDirectories": library_dir + ";%(AdditionalLibraryDirectories)" } 186 | debug_compile_item_definition_group = ItemDefinitionGroup("'$(Configuration)|$(Platform)'=='Debug|Win32'", debug_compile_options, debug_link_options) 187 | 188 | release_compile_options = {"PrecompiledHeader": "", "WarningLevel": "Level4", "Optimization": "MaxSpeed", "FunctionLevelLinking": "true", "IntrinsicFunctions": "true", "PreprocessorDefinitions": release_defines, "AdditionalIncludeDirectories": additionalIncludeDirectories} 189 | release_link_options = {"SubSystem": "Console", "GenerateDebugInformation": "true", "EnableCOMDATFolding":"true", "OptimizeReferences": "true", "AdditionalDependencies": additionalDependencies, "AdditionalLibraryDirectories": library_dir + ";%(AdditionalLibraryDirectories)"} 190 | release_compile_item_definition_group = ItemDefinitionGroup("'$(Configuration)|$(Platform)'=='Release|Win32'", release_compile_options, release_link_options) 191 | 192 | import_targets = Import() 193 | import_targets.Project = "$(VCTargetsPath)\Microsoft.Cpp.targets" 194 | extension_targets_import_group = ImportGroup("ExtensionTargets") 195 | 196 | self.item_groups2 = [extension_targets_import_group, props_import, extension_settings_import_group, debug_property_sheets, release_property_sheets, user_macros_property_group, debug_link_property_group, release_link_property_group, debug_compile_item_definition_group, release_compile_item_definition_group, import_targets, extension_targets_import_group] 197 | -------------------------------------------------------------------------------- /source/xcode.py: -------------------------------------------------------------------------------- 1 | import project_writer 2 | import os 3 | import project_path 4 | import re 5 | 6 | class XcodeId(): 7 | def __init__(self, id_string): 8 | self.id_string = id_string 9 | assert(len(self.id_string) == 24) 10 | 11 | def __str__(self): 12 | return self.id_string; 13 | 14 | class XcodeIdHolder(): 15 | def __init__(self): 16 | self.id = 0 17 | 18 | def generate_id(self): 19 | self.id += 1 20 | id_string = "%024X" % self.id 21 | id = XcodeId(id_string) 22 | return id 23 | 24 | class XcodeObjectCreator(): 25 | def __init__(self): 26 | self.id_holder = XcodeIdHolder() 27 | 28 | def create(self, cls, *args): 29 | xcode_id = self.id_holder.generate_id() 30 | new_object = cls(xcode_id, *args) 31 | return new_object 32 | 33 | class XcodeWriterScope(project_writer.ProjectWriter): 34 | def __init__(self, keyword): 35 | project_writer.ProjectWriter.__init__(self) 36 | self.keyword = keyword 37 | 38 | def write(self, output): 39 | output.output(str(self.keyword) + " = {") 40 | output.increase_tabs() 41 | 42 | def close(self, output): 43 | project_writer.ProjectWriter.close(self, output) 44 | output.decrease_tabs() 45 | output.output( "};" ) 46 | 47 | class XcodeWriterLine(project_writer.ProjectWriter): 48 | def __init__(self, keyword, value): 49 | project_writer.ProjectWriter.__init__(self) 50 | self.keyword = keyword 51 | self.value = value 52 | 53 | def write(self, output): 54 | value = add_quotation_marks_when_needed(self.value) 55 | keyword = add_quotation_marks_when_needed(self.keyword) 56 | output.output(keyword + " = " + value + ";" ) 57 | 58 | class XcodeWriterCollection(project_writer.ProjectWriter): 59 | def __init__(self, keyword, ids): 60 | project_writer.ProjectWriter.__init__(self) 61 | self.keyword = keyword 62 | self.ids = ids 63 | 64 | def write(self, output): 65 | s = self.keyword + " = (" 66 | output.output(s) 67 | output.increase_tabs() 68 | for id_object in self.ids: 69 | output.output( add_quotation_marks_when_needed(id_object) + ",") 70 | 71 | def close(self, output): 72 | output.decrease_tabs() 73 | output.output( ");") 74 | 75 | class XcodeWriterDictionary(XcodeWriterScope): 76 | def __init__(self, keyword, dictionary): 77 | XcodeWriterScope.__init__(self, keyword) 78 | self.dictionary = dictionary 79 | 80 | def write(self, output): 81 | XcodeWriterScope.write(self, output) 82 | for name in sorted(self.dictionary.iterkeys()): 83 | value = self.dictionary[name] 84 | output_value(name, value, output) 85 | 86 | class XcodeProjectSectionObjectWriter(XcodeWriterScope): 87 | def __init__(self, keyword, section_object): 88 | XcodeWriterScope.__init__(self, keyword) 89 | self.section_object = section_object 90 | 91 | def write(self, output): 92 | XcodeWriterScope.write(self, output) 93 | self.section_object.write(output) 94 | 95 | def close(self, output): 96 | self.section_object.close(output) 97 | XcodeWriterScope.close(self, output) 98 | 99 | class XcodeWriterDocument(project_writer.ProjectWriter): 100 | def write(self, output): 101 | project_writer.ProjectWriter.write(self, output) 102 | output.output("// !$*UTF8*$!\n{"); 103 | output.increase_tabs(); 104 | 105 | def close(self, output): 106 | project_writer.ProjectWriter.close(self, output) 107 | output.decrease_tabs(); 108 | output.output("}"); 109 | 110 | class XcodeReference(project_writer.ProjectWriter): 111 | def __init__(self, xcode_id, comment): 112 | project_writer.ProjectWriter.__init__(self) 113 | 114 | self.xcode_id = xcode_id 115 | self.comment = comment 116 | 117 | def __str__(self): 118 | return str(self.xcode_id) + " /* " + self.comment + " */ "; 119 | 120 | def output_value(name, value, output): 121 | if isinstance(value, list): 122 | write_object = XcodeWriterCollection(name, value) 123 | elif isinstance(value, dict): 124 | write_object = XcodeWriterDictionary(name, value) 125 | elif isinstance(value, XcodeProjectSectionObject): 126 | write_object = XcodeProjectSectionObjectWriter(name, value) 127 | else: 128 | write_object = XcodeWriterLine(name, value) 129 | 130 | write_object.write(output) 131 | write_object.close(output) 132 | 133 | def add_quotation_marks_when_needed(value): 134 | if isinstance(value, XcodeReference): 135 | return str(value) 136 | 137 | 138 | 139 | value = str(value) 140 | all_characters_are_representable = re.match('[a-zA-Z./_0-9]*', value) 141 | if value == "" or (not all_characters_are_representable) or all_characters_are_representable.group(0) != value: 142 | value = '"' + value + '"' 143 | return value 144 | 145 | class XcodeProjectObjectRaw(XcodeReference): 146 | def write_all_attributes(self, output, exclude_object = None): 147 | if not exclude_object: 148 | exclude_object = XcodeProjectObject(None, None) 149 | 150 | for name in sorted(self.__dict__.iterkeys()): 151 | exclude_properties = exclude_object.__dict__.keys() 152 | 153 | if name in exclude_properties: 154 | continue 155 | 156 | value = self.__dict__[name] 157 | output_value(name, value, output) 158 | 159 | def write(self, output): 160 | self.write_all_attributes(output) 161 | 162 | class XcodeProjectObject(XcodeProjectObjectRaw): 163 | def __init__(self, xcode_id, comment): 164 | XcodeProjectObjectRaw.__init__(self, xcode_id, comment) 165 | 166 | def write(self, output): 167 | self.push(XcodeWriterScope(self), output) 168 | XcodeWriterLine("isa", self.__class__.__name__).write(output) 169 | self.write_all_attributes(output) 170 | 171 | class PBXBuildFile(XcodeProjectObject): 172 | def __init__(self, xcode_id, file_ref): 173 | self.fileRef = file_ref 174 | comment = os.path.basename(file_ref.path) 175 | 176 | XcodeProjectObject.__init__(self, xcode_id, comment) 177 | 178 | def change_target_path(self, target_path): 179 | pass 180 | 181 | class BuildPhase(XcodeProjectObject): 182 | def __init__(self, xcode_id, comment, file_references): 183 | self.files = file_references 184 | self.buildActionMask = 2147483647 185 | 186 | self.runOnlyForDeploymentPostprocessing = 0 187 | XcodeProjectObject.__init__(self, xcode_id, comment) 188 | 189 | class PBXFrameworksBuildPhase(BuildPhase): 190 | def __init__(self, xcode_id, file_references): 191 | comment = "Frameworks build phase" 192 | BuildPhase.__init__(self, xcode_id, comment, file_references) 193 | 194 | class PBXHeadersBuildPhase(BuildPhase): 195 | def __init__(self, xcode_id, file_references): 196 | comment = "Headers build phase" 197 | BuildPhase.__init__(self, xcode_id, comment, file_references) 198 | 199 | class PBXSourcesBuildPhase(BuildPhase): 200 | def __init__(self, xcode_id, file_references): 201 | comment = "Sources build phase" 202 | BuildPhase.__init__(self, xcode_id, comment, file_references) 203 | 204 | class PBXResourcesBuildPhase(BuildPhase): 205 | def __init__(self, xcode_id, file_references): 206 | comment = "Resources build phase" 207 | BuildPhase.__init__(self, xcode_id, comment, file_references) 208 | 209 | class PBXGroup(XcodeProjectObject): 210 | def __init__(self, xcode_id, name, children): 211 | self.children = children 212 | self.name = name 213 | 214 | self.sourceTree = "" 215 | comment = name 216 | XcodeProjectObject.__init__(self, xcode_id, comment) 217 | 218 | def append_child(self, child): 219 | self.children.append(child) 220 | 221 | def find(self, name): 222 | for group in self.children: 223 | if isinstance(group, PBXGroup): 224 | try: 225 | if group.name == name: 226 | return group 227 | except: 228 | pass 229 | return None 230 | 231 | class FilePath: 232 | def __init__(self, file_reference): 233 | self.file_reference = file_reference 234 | 235 | def __str__(self): 236 | return self.file_reference.path 237 | 238 | class FilePaths: 239 | def __init__(self, file_references): 240 | self.paths = [] 241 | for file_reference in file_references: 242 | self.paths.append(FilePath(file_reference)) 243 | 244 | def __str__(self): 245 | paths = [] 246 | for path in self.paths: 247 | paths.append(str(path)) 248 | 249 | return " ".join(paths) 250 | 251 | 252 | class PBXFileReference(XcodeProjectObject): 253 | def __init__(self, xcode_id, path): 254 | extension = os.path.splitext(path)[1][1:] 255 | if extension != "app": 256 | self.fileEncoding = 4 257 | 258 | if extension == "cpp": 259 | self.lastKnownFileType = "sourcecode.cpp.cpp" 260 | self.sourceTree = "SOURCE_ROOT" 261 | elif extension == "c": 262 | self.lastKnownFileType = "sourcecode.c.c" 263 | self.sourceTree = "SOURCE_ROOT" 264 | elif extension == "h" or extension == "pch": 265 | self.lastKnownFileType = "sourcecode.c.h" 266 | self.sourceTree = "SOURCE_ROOT" 267 | elif extension == "a": 268 | self.explicitFileType = "archive.ar" 269 | self.sourceTree = "" 270 | elif extension == "framework": 271 | self.lastKnownFileType = "wrapper.framework" 272 | if path[0:3] == "../": 273 | self.sourceTree = "" 274 | else: 275 | self.sourceTree = "SDKROOT" 276 | elif extension == "dylib": 277 | self.lastKnownFileType = "wrapper.framework" 278 | self.sourceTree = "SDKROOT" 279 | elif extension == "plist": 280 | self.lastKnownFileType = "text.plist.xml" 281 | self.sourceTree = "" 282 | self.plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist" 283 | elif extension == "ogg": 284 | self.lastKnownFileType = "audio" 285 | self.sourceTree = "SOURCE_ROOT" 286 | elif extension == "m": 287 | self.lastKnownFileType = "sourcecode.c.objc" 288 | self.sourceTree = "SOURCE_ROOT" 289 | elif extension == "mm": 290 | self.lastKnownFileType = "sourcecode.cpp.objcpp" 291 | self.sourceTree = "SOURCE_ROOT" 292 | elif extension == "xib": 293 | self.lastKnownFileType = "file.xib" 294 | self.sourceTree = "SOURCE_ROOT" 295 | elif extension == "storyboard": 296 | self.lastKnownFileType = "file.storyboard" 297 | self.sourceTree = "SOURCE_ROOT" 298 | elif extension == "oes": 299 | self.lastKnownFileType = "text" 300 | self.sourceTree = "SOURCE_ROOT" 301 | elif extension == "oec": 302 | self.lastKnownFileType = "text" 303 | self.sourceTree = "SOURCE_ROOT" 304 | elif extension == "oeb": 305 | self.lastKnownFileType = "text" 306 | self.sourceTree = "SOURCE_ROOT" 307 | elif extension == "png": 308 | self.lastKnownFileType = "image.png" 309 | self.sourceTree = "SOURCE_ROOT" 310 | elif extension == "icns": 311 | self.lastKnownFileType = "image.icns" 312 | self.sourceTree = "SOURCE_ROOT" 313 | elif extension == "app": 314 | self.explicitFileType = "wrapper.application" 315 | self.sourceTree = "BUILT_PRODUCTS_DIR" 316 | self.includeInIndex = 0 317 | # else: 318 | # raise Exception("Unknown extension:" + extension) 319 | 320 | self.path = path 321 | comment = os.path.basename(path) 322 | XcodeProjectObject.__init__(self, xcode_id, comment) 323 | 324 | def change_target_path(self, target_path): 325 | extension = os.path.splitext(self.path)[1][1:] 326 | if self.path[0:7] != "System/" and extension != "dylib" and extension != "app" and os.path.dirname(self.path) != "": 327 | relative_filename = project_path.Path(self.path).relative(target_path) 328 | self.path = relative_filename 329 | 330 | class PBXNativeTarget(XcodeProjectObject): 331 | def __init__(self, xcode_id, name, product_file_reference, configuration_list, build_phases, product_type): 332 | self.buildConfigurationList = configuration_list 333 | self.buildPhases = build_phases 334 | self.buildRules = [] 335 | self.dependencies = [] 336 | self.name = name 337 | self.productName = name 338 | self.productReference = product_file_reference 339 | if product_type == "library": 340 | self.productType = "com.apple.product-type.library.static" 341 | else: 342 | self.productType = "com.apple.product-type.application" 343 | comment = name 344 | XcodeProjectObject.__init__(self, xcode_id, comment) 345 | 346 | class PBXProject(XcodeProjectObject): 347 | def __init__(self, xcode_id, build_configuration_list, main_group, product_ref_group, target_list): 348 | self.buildConfigurationList = build_configuration_list 349 | self.compatibilityVersion = "Xcode 3.2"; 350 | self.hasScannedForEncodings = 1; 351 | self.mainGroup = main_group 352 | self.productRefGroup = product_ref_group # Not present in Applications!? 353 | self.projectDirPath = "" 354 | self.projectRoot = "" 355 | self.targets = target_list 356 | comment = "Project" 357 | XcodeProjectObject.__init__(self, xcode_id, comment) 358 | 359 | class XCBuildConfiguration(XcodeProjectObject): 360 | def __init__(self, xcode_id, name, build_settings, usage): 361 | self.buildSettings = build_settings 362 | self.name = name; 363 | comment = name + " build settings " + usage 364 | XcodeProjectObject.__init__(self, xcode_id, comment) 365 | 366 | class XCConfigurationList(XcodeProjectObject): 367 | def __init__(self, xcode_id, build_configurations, default_configuration_name, usage): 368 | self.buildConfigurations = build_configurations 369 | self.defaultConfigurationIsVisible = 0 370 | self.defaultConfigurationName = default_configuration_name 371 | comment = "Build configuration list for " + usage 372 | XcodeProjectObject.__init__(self, xcode_id, comment) 373 | 374 | class XcodeSection(project_writer.ProjectWriter): 375 | def __init__(self, section_name): 376 | project_writer.ProjectWriter.__init__(self) 377 | self.section_name = section_name 378 | 379 | def write(self, output): 380 | output.output("") 381 | output.output("/* Begin " + self.section_name + " section */") 382 | 383 | def close(self, output): 384 | output.output("/* End " + self.section_name + " section */") 385 | output.output("") 386 | project_writer.ProjectWriter.close(self, output) 387 | 388 | class XcodeProjectSectionObject(project_writer.ProjectWriter): 389 | def write(self, output): 390 | project_writer.ProjectWriter.write(self, output) 391 | self.write_all_attributes(output) 392 | 393 | def write_all_attributes(self, output, exclude_object=project_writer.ProjectWriter()): 394 | section_infos = {} 395 | for name, value in self.__dict__.iteritems(): 396 | exclude_properties = exclude_object.__dict__.keys() 397 | 398 | if name in exclude_properties: 399 | continue 400 | 401 | value = self.__dict__[name] 402 | if isinstance(value, list): 403 | section_name = value[0].__class__.__name__ 404 | else: 405 | section_name = value.__class__.__name__ 406 | 407 | if value == None: 408 | raise Exception("self." + name + " has illegal value!") 409 | section_infos[section_name] = value 410 | 411 | for section_name in sorted(section_infos.iterkeys()): 412 | value = section_infos[section_name] 413 | section = XcodeSection(section_name) 414 | section.write(output) 415 | if isinstance(value, list): 416 | for o in value: 417 | o.write(output) 418 | o.close(output) 419 | else: 420 | if not value: 421 | raise Exception("section:" + section_name + " in object:" + self.__class__.__name__) 422 | else: 423 | value.write(output) 424 | value.close(output) 425 | section.close(output) 426 | 427 | def split_directories(path): 428 | directories = path.split("/") 429 | directories = directories[:-1] 430 | return directories 431 | 432 | def create_directory_groups(object_factory, parent_group, source_root, absolute_filename): 433 | relative_filename = project_path.Path(absolute_filename).relative(source_root) 434 | 435 | absolute_directory = source_root 436 | 437 | directories = split_directories(relative_filename) 438 | for directory in directories: 439 | absolute_directory = os.path.normpath(os.path.join(absolute_directory, directory)) 440 | if directory == ".." or directory == ".": 441 | continue 442 | 443 | child_group = parent_group.find(directory) 444 | if not child_group: 445 | child_group = object_factory.create(PBXGroup, directory, []) 446 | parent_group.append_child(child_group) 447 | parent_group = child_group 448 | return parent_group 449 | 450 | class XcodeDefaultGroups: 451 | def __init__(self, creator, name): 452 | self.classes = creator.create(PBXGroup, "Classes", []) 453 | self.frameworks = creator.create(PBXGroup, "Frameworks", []) 454 | self.products = creator.create(PBXGroup, "Products", []) 455 | self.resources = creator.create(PBXGroup, "Resources", []) 456 | self.root = creator.create(PBXGroup, name, [self.classes, self.frameworks, self.products]) 457 | 458 | def flatten_groups(self): 459 | groups = [] 460 | self.add_group(groups, self.root) 461 | return groups 462 | 463 | def root_group(self): 464 | return self.root 465 | 466 | def add_group(self, groups, group): 467 | for child_group in group.children: 468 | if isinstance(child_group, PBXGroup): 469 | self.add_group(groups, child_group) 470 | 471 | groups.append(group) 472 | 473 | class XcodeDefaultGroupsForApplication(XcodeDefaultGroups): 474 | def __init__(self, creator, name): 475 | XcodeDefaultGroups.__init__(self, creator, name) 476 | self.resources = creator.create(PBXGroup, "Resources", []) 477 | self.libraries = creator.create(PBXGroup, "Libraries", []) 478 | self.root.append_child(self.resources) 479 | self.root.append_child(self.libraries) 480 | 481 | class XcodeObjects(XcodeProjectSectionObject): 482 | def __init__(self, project, source_root, platform): 483 | object_factory = XcodeObjectCreator() 484 | 485 | self.convert_library_names(project) 486 | 487 | if project.target_type == "library": 488 | default_groups = XcodeDefaultGroups(object_factory, project.name()) 489 | else: 490 | default_groups = XcodeDefaultGroupsForApplication(object_factory, project.name()) 491 | 492 | self.source_file_references = [] 493 | self.build_files = [] 494 | self.build_configurations = [] 495 | self.configuration_lists = [] 496 | 497 | extensions = ["cpp", "c", "h", "pch", "xib", "storyboard", "m", "mm"] 498 | self.generate_build_files(source_root, project.settings.source_filenames(), extensions, object_factory, default_groups.classes) 499 | extensions = ["plist"] 500 | self.generate_file_references(project.settings.source_filenames(), extensions, object_factory, default_groups.resources) 501 | extensions = None 502 | self.generate_build_files(source_root, project.settings.resource_filenames(), extensions, object_factory, default_groups.resources) 503 | 504 | if project.target_type == "library": 505 | project.library_filenames.append("Foundation.framework") # not sure if all libraries need Foundation? 506 | library_build_files = [] 507 | else: 508 | library_build_files = self.create_build_files_for_extension(object_factory, source_root, default_groups.libraries, project.library_filenames, ["a"], "") 509 | 510 | framework_names = [] 511 | for name in project.settings.framework_names: 512 | framework_names.append(name + ".framework") 513 | 514 | 515 | 516 | framework_build_files = self.create_build_files_for_extension(object_factory, source_root, default_groups.frameworks, framework_names, ["framework"], "System/Library/Frameworks/") 517 | 518 | dylib_build_files = self.create_build_files_for_extension(object_factory, source_root, default_groups.frameworks, project.library_filenames, ["dylib"], "System/Library/Frameworks/") 519 | 520 | all_framework_like_files = framework_build_files + dylib_build_files + library_build_files 521 | 522 | self.frameworks_build_phase = object_factory.create(PBXFrameworksBuildPhase, all_framework_like_files) 523 | source_build_files = self.all_source_build_files(project) 524 | self.sources_build_phase = object_factory.create(PBXSourcesBuildPhase, source_build_files) 525 | self.headers_build_phase = object_factory.create(PBXHeadersBuildPhase, self.all_header_build_files(project)) 526 | self.resources_build_phase = object_factory.create(PBXResourcesBuildPhase, self.all_resource_build_files(project)) 527 | 528 | self.groups = default_groups.flatten_groups() 529 | 530 | self.target_product = self.product(object_factory, default_groups.products, project.name(), project.target_type, project.settings.library_search_paths, project.settings.framework_search_paths) 531 | if project.target_type == "library": 532 | header_paths = self.create_header_paths(object_factory, project.settings.header_paths) 533 | self.project = self.create_project_for_library(object_factory, project.name(), default_groups.root_group(), default_groups.products, self.target_product, header_paths, project.settings.defines,platform) 534 | else: 535 | 536 | header_paths = self.create_header_paths(object_factory, project.settings.header_paths) 537 | self.project = self.create_project_for_application(object_factory, project.name(), default_groups.root_group(), default_groups.products, self.target_product, header_paths, project.settings.defines, project.configurations, platform) 538 | 539 | XcodeProjectSectionObject.__init__(self) 540 | 541 | def create_header_paths(self, object_factory, header_paths): 542 | file_references = [] 543 | for header_path in header_paths: 544 | file_reference = self.create_invisible_file_reference(object_factory, header_path) 545 | file_references.append(file_reference) 546 | 547 | return FilePaths(file_references) 548 | 549 | 550 | def convert_library_names(self, project): 551 | new_filenames = [] 552 | for filename in project.settings.library_filenames: 553 | extension = os.path.splitext(filename)[1][1:] 554 | new_filenames.append(filename) 555 | 556 | project.library_filenames = new_filenames 557 | 558 | def create_build_files_for_extension(self, object_factory, source_root, root_group, filenames, extensions, path_prefix): 559 | build_files = [] 560 | for filename in filenames: 561 | extension = os.path.splitext(filename)[1][1:] 562 | if extension in extensions: 563 | if extension == "framework" and filename[0:3] == "../": 564 | use_filename = filename 565 | else: 566 | use_filename = path_prefix + filename 567 | build_file = self.create_build_file(object_factory, source_root, use_filename, root_group) 568 | build_files.append(build_file) 569 | 570 | return build_files 571 | 572 | def change_target_path_for_file_references(self, target_path): 573 | for file_reference in self.source_file_references: 574 | file_reference.change_target_path(target_path) 575 | 576 | for file_reference in self.build_files: 577 | file_reference.change_target_path(target_path) 578 | 579 | def change_short_name_for_file_references(self, source_path): 580 | for file_reference in self.source_file_references: 581 | file_reference.change_short_name(source_path) 582 | 583 | def create_project_for_library(self, object_factory, name, root_group, products_group, target_product, header_paths, defines, platform): 584 | build_configuration_list = self.create_project_configuration_list_for_library(object_factory, name, header_paths, defines,platform) 585 | return object_factory.create(PBXProject, build_configuration_list, root_group, products_group, [target_product]) 586 | 587 | def create_project_for_application(self, object_factory, name, root_group, products_group, target_product, header_paths, defines, configurations, platform): 588 | build_configuration_list = self.create_project_configuration_list_for_application(object_factory, name, header_paths, defines, configurations, platform) 589 | return object_factory.create(PBXProject, build_configuration_list, root_group, products_group, [target_product]) 590 | 591 | def create_build_configuration(self, object_creator, name, build_settings, comment): 592 | configuration = object_creator.create(XCBuildConfiguration, name, build_settings, comment) 593 | self.build_configurations.append(configuration) 594 | return configuration 595 | 596 | def create_common_target_build_settings_for_application(self, name, plist_filename, library_search_paths, framework_search_paths): 597 | build_settings = { 598 | "ALWAYS_SEARCH_USER_PATHS": "NO", 599 | "GCC_PRECOMPILE_PREFIX_HEADER": "YES", 600 | "GCC_PREFIX_HEADER": name + "_Prefix.pch", 601 | "GCC_THUMB_SUPPORT": "NO", 602 | "USE_HEADERMAP": "NO", 603 | "INFOPLIST_FILE": plist_filename, 604 | "LIBRARY_SEARCH_PATHS": library_search_paths, 605 | # "FRAMEWORK_SEARCH_PATHS": framework_search_paths, 606 | "PRODUCT_NAME": name 607 | } 608 | return build_settings 609 | 610 | def create_target_debug_configuration_for_application(self, object_creator, name, plist_filename, library_search_paths, framework_search_paths): 611 | build_settings = self.create_common_target_build_settings_for_application(name, plist_filename, library_search_paths, framework_search_paths) 612 | build_settings["COPY_PHASE_STRIP"] = "NO" 613 | build_settings["GCC_DYNAMIC_NO_PIC"] = "NO" 614 | build_settings["GCC_OPTIMIZATION_LEVEL"] = 0 615 | return self.create_build_configuration(object_creator, "Debug", build_settings, "target") 616 | 617 | def create_target_release_configuration_for_application(self, object_creator, name, plist_filename, library_search_paths, framework_search_paths): 618 | build_settings = self.create_common_target_build_settings_for_application(name, plist_filename, library_search_paths, framework_search_paths) 619 | build_settings["COPY_PHASE_STRIP"] = "YES" 620 | return self.create_build_configuration(object_creator, "Release", build_settings, "target") 621 | 622 | def create_target_adhoc_configuration_for_application(self, object_creator, name, plist_filename, library_search_paths, framework_search_paths): 623 | build_settings = self.create_common_target_build_settings_for_application(name, plist_filename, library_search_paths, framework_search_paths) 624 | build_settings["COPY_PHASE_STRIP"] = "YES" 625 | return self.create_build_configuration(object_creator, "AdHoc", build_settings, "target") 626 | 627 | def create_target_distribution_configuration_for_application(self, object_creator, name, plist_filename, library_search_paths, framework_search_paths): 628 | build_settings = self.create_common_target_build_settings_for_application(name, plist_filename, library_search_paths, framework_search_paths) 629 | build_settings["COPY_PHASE_STRIP"] = "YES" 630 | return self.create_build_configuration(object_creator, "AppStore", build_settings, "target") 631 | 632 | def create_target_build_configurations_for_application(self, factory, name, plist_filename, library_search_paths, framework_search_paths): 633 | build_configurations = [ 634 | self.create_target_debug_configuration_for_application(factory, name, plist_filename, library_search_paths, framework_search_paths), 635 | self.create_target_release_configuration_for_application(factory, name, plist_filename, library_search_paths, framework_search_paths), 636 | self.create_target_adhoc_configuration_for_application(factory, name, plist_filename, library_search_paths, framework_search_paths), 637 | self.create_target_distribution_configuration_for_application(factory, name, plist_filename, library_search_paths, framework_search_paths), 638 | ] 639 | 640 | return build_configurations 641 | 642 | def create_common_target_build_settings_for_library(self, name): 643 | build_settings = { 644 | "ALWAYS_SEARCH_USER_PATHS": "NO", 645 | "ARCHS": "$(ARCHS_STANDARD_32_BIT)", 646 | "COPY_PHASE_STRIP": "NO", 647 | "DSTROOT": "/tmp/" + name + ".dst", 648 | "GCC_MODEL_TUNING": "G5", 649 | "GCC_OPTIMIZATION_LEVEL": 0, 650 | "GCC_PRECOMPILE_PREFIX_HEADER": "YES", 651 | "GCC_PREFIX_HEADER": name + "_Prefix.pch", 652 | "INSTALL_PATH": "/usr/local/lib", 653 | "PRODUCT_NAME": name 654 | } 655 | return build_settings 656 | 657 | def create_target_debug_configuration_for_library(self, object_creator, name): 658 | build_settings = self.create_common_target_build_settings_for_library(name) 659 | build_settings.update( { 660 | "COPY_PHASE_STRIP": "NO", 661 | "GCC_DYNAMIC_NO_PIC": "NO", 662 | "GCC_ENABLE_FIX_AND_CONTINUE": "YES", 663 | "GCC_OPTIMIZATION_LEVEL": 0, 664 | } ) 665 | 666 | return self.create_build_configuration(object_creator, "Debug", build_settings, "target") 667 | 668 | def create_target_release_configuration_for_library(self, object_creator, name): 669 | build_settings = self.create_common_target_build_settings_for_library(name) 670 | return self.create_build_configuration(object_creator, "Release", build_settings, "target") 671 | 672 | def create_target_build_configurations_for_library(self, factory, name): 673 | build_configurations = [ 674 | self.create_target_debug_configuration_for_library(factory, name), 675 | self.create_target_release_configuration_for_library(factory, name) 676 | ] 677 | 678 | return build_configurations 679 | 680 | def create_common_project_build_settings(self, header_paths, defines, platform): 681 | if platform == "ios": 682 | build_settings = { 683 | "ARCHS": "$(ARCHS_STANDARD_32_BIT)", 684 | "GCC_C_LANGUAGE_STANDARD": "c99", 685 | "GCC_WARN_ABOUT_RETURN_TYPE": "YES", 686 | "GCC_WARN_UNUSED_VARIABLE": "YES", 687 | "GCC_THUMB_SUPPORT": "NO", 688 | "COMPRESS_PNG_FILES": "NO", 689 | "SDKROOT": "iphoneos", 690 | "IPHONEOS_DEPLOYMENT_TARGET": 5.1, 691 | "HEADER_SEARCH_PATHS": header_paths, 692 | "TARGETED_DEVICE_FAMILY": "1,2" 693 | } 694 | elif platform == "mac_os_x": 695 | build_settings = { 696 | "ARCHS": "$(ARCHS_STANDARD_32_BIT)", 697 | "GCC_C_LANGUAGE_STANDARD": "c99", 698 | "GCC_WARN_ABOUT_RETURN_TYPE": "YES", 699 | "GCC_WARN_UNUSED_VARIABLE": "YES", 700 | "GCC_THUMB_SUPPORT": "NO", 701 | "COMPRESS_PNG_FILES": "NO", 702 | "SDKROOT": "macosx", 703 | "HEADER_SEARCH_PATHS": header_paths 704 | } 705 | 706 | build_settings["GCC_PREPROCESSOR_DEFINITIONS"] = defines 707 | 708 | return build_settings 709 | 710 | def create_project_build_settings_for_application(self, header_paths, defines, platform): 711 | build_settings = self.create_common_project_build_settings(header_paths, defines, platform) 712 | build_settings["CODE_SIGN_IDENTITY[sdk=iphoneos*]"] = "iPhone Developer" 713 | return build_settings 714 | 715 | def create_project_release_configuration_for_application(self, object_creator, name, header_paths, defines, platform): 716 | build_settings = self.create_project_build_settings_for_application(header_paths, defines, platform) 717 | return self.create_build_configuration(object_creator, "Release", build_settings, "project") 718 | 719 | def create_project_adhoc_configuration_for_application(self, object_creator, name, header_paths, defines, platform): 720 | build_settings = self.create_project_build_settings_for_application(header_paths, defines, platform) 721 | build_settings["CODE_SIGN_IDENTITY[sdk=iphoneos*]"] = "iPhone Distribution" 722 | build_settings["VALIDATE_PRODUCT"] = "YES" 723 | return self.create_build_configuration(object_creator, "AdHoc", build_settings, "project") 724 | 725 | def create_project_distribution_configuration_for_application(self, object_creator, name, header_paths, defines, platform): 726 | build_settings = self.create_project_build_settings_for_application(header_paths, defines, platform) 727 | build_settings["CODE_SIGN_IDENTITY[sdk=iphoneos*]"] = "iPhone Distribution" 728 | build_settings["VALIDATE_PRODUCT"] = "YES" 729 | return self.create_build_configuration(object_creator, "AppStore", build_settings, "project") 730 | 731 | def create_project_debug_configuration_for_application(self, object_creator, name, header_paths, defines, platform): 732 | build_settings = self.create_project_build_settings_for_application(header_paths, defines, platform) 733 | build_settings["GCC_PREPROCESSOR_DEFINITIONS"] = build_settings["GCC_PREPROCESSOR_DEFINITIONS"] 734 | build_settings["GCC_OPTIMIZATION_LEVEL"] = "0" 735 | bc = self.create_build_configuration(object_creator, "Debug", build_settings, "project") 736 | return bc 737 | 738 | def create_project_build_configurations_for_application(self, factory, name, header_paths, defines, configurations, platform): 739 | build_configurations = [ 740 | self.create_project_debug_configuration_for_application(factory, name, header_paths, defines + configurations["debug"].defines, platform), 741 | self.create_project_release_configuration_for_application(factory, name, header_paths, defines + configurations["release"].defines, platform), 742 | self.create_project_adhoc_configuration_for_application(factory, name, header_paths, defines + configurations["adhoc"].defines, platform), 743 | self.create_project_distribution_configuration_for_application(factory, name, header_paths, defines + configurations["distribution"].defines, platform), 744 | ] 745 | 746 | return build_configurations 747 | 748 | def create_project_build_settings_for_library(self, header_paths, defines,platform): 749 | build_settings = self.create_common_project_build_settings(header_paths, defines,platform) 750 | build_settings["OTHER_LDFLAGS"] = "-ObjC" 751 | return build_settings 752 | 753 | def create_project_release_configuration_for_library(self, object_creator, name, header_paths, defines, platform): 754 | build_settings = self.create_project_build_settings_for_library(header_paths, defines,platform) 755 | return self.create_build_configuration(object_creator, "Release", build_settings, "project") 756 | 757 | def create_project_debug_configuration_for_library(self, object_creator, name, header_paths, defines,platform): 758 | build_settings = self.create_project_build_settings_for_library(header_paths, defines,platform) 759 | build_settings["GCC_OPTIMIZATION_LEVEL"] = 0 760 | return self.create_build_configuration(object_creator, "Debug", build_settings, "project") 761 | 762 | def create_project_build_configurations_for_library(self, factory, name, header_paths, defines,platform): 763 | build_configurations = [ 764 | self.create_project_debug_configuration_for_library(factory, name, header_paths, defines,platform), 765 | self.create_project_release_configuration_for_library(factory, name, header_paths, defines,platform) 766 | ] 767 | 768 | return build_configurations 769 | 770 | def create_configuration_list(self, object_creator, build_configurations, usage): 771 | default_configuration_name = "Release" 772 | configuration_list = object_creator.create(XCConfigurationList, build_configurations, default_configuration_name, usage) 773 | self.configuration_lists.append(configuration_list) 774 | return configuration_list 775 | 776 | def create_target_configuration_list_for_application(self, object_creator, name, plist_filename, library_search_paths, framework_search_paths): 777 | target_build_configurations = self.create_target_build_configurations_for_application(object_creator, name, plist_filename, library_search_paths, framework_search_paths) 778 | configuration_list = self.create_configuration_list(object_creator, target_build_configurations, "target") 779 | return configuration_list 780 | 781 | def create_project_configuration_list_for_application(self, object_creator, name, header_paths, defines, configurations, platform): 782 | project_build_configurations = self.create_project_build_configurations_for_application(object_creator, name, header_paths, defines, configurations, platform) 783 | configuration_list = self.create_configuration_list(object_creator, project_build_configurations, "project") 784 | return configuration_list 785 | 786 | def create_target_configuration_list_for_library(self, object_creator, name): 787 | target_build_configurations = self.create_target_build_configurations_for_library(object_creator, name) 788 | configuration_list = self.create_configuration_list(object_creator, target_build_configurations, "target") 789 | return configuration_list 790 | 791 | def create_project_configuration_list_for_library(self, object_creator, name, header_paths, defines,platform): 792 | project_build_configurations = self.create_project_build_configurations_for_library(object_creator, name, header_paths, defines,platform) 793 | configuration_list = self.create_configuration_list(object_creator, project_build_configurations, "project") 794 | return configuration_list 795 | 796 | def product(self, object_factory, products_group, name, product_type, library_search_paths, framework_search_paths): 797 | if product_type == "library": 798 | target_filename = "lib" + name + ".a" 799 | else: 800 | target_filename = name + ".app" 801 | 802 | product_file_reference = self.create_file_reference(object_factory, products_group, target_filename) 803 | build_phases = [self.headers_build_phase, self.resources_build_phase, self.sources_build_phase, self.frameworks_build_phase] 804 | if product_type == "library": 805 | target_configuration_list = self.create_target_configuration_list_for_library(object_factory, name) 806 | else: 807 | extensions = self.file_references_with_extensions(["plist"]) 808 | if len(extensions) != 0: 809 | plist_filename = self.file_references_with_extensions(["plist"])[0] 810 | plist_file_path = FilePath(plist_filename) 811 | else: 812 | plist_file_path = '' 813 | target_configuration_list = self.create_target_configuration_list_for_application(object_factory, name, plist_file_path, library_search_paths, framework_search_paths) 814 | 815 | 816 | return object_factory.create(PBXNativeTarget, name, product_file_reference, target_configuration_list, build_phases, product_type) 817 | 818 | def all_source_build_files(self, project): 819 | return self.build_files_with_extensions(["cpp", "c", "m", "mm"]) 820 | 821 | def all_header_build_files(self, project): 822 | return self.build_files_with_extensions(["pch", "h"]) 823 | 824 | def all_resource_build_files(self, project): 825 | return self.build_files_with_extensions(["xib", "storyboard", "png", "ogg", "oes", "oeb", "oec", "icns", "fnt"]) 826 | 827 | def all_resource_file_references(self): 828 | return self.file_references_with_extensions(["plist", "xib", "storyboard", "png", "ogg", "oes", "oeb", "oec", "icns", "fnt"]) 829 | 830 | def file_references_with_extensions(self, extensions_to_match): 831 | matching_file_references = [] 832 | for file_reference in self.source_file_references: 833 | filename = file_reference.path 834 | extension = os.path.splitext(filename)[1][1:] 835 | if extension in extensions_to_match: 836 | matching_file_references.append(file_reference) 837 | return matching_file_references 838 | 839 | def build_files_with_extensions(self, extensions_to_match): 840 | matching_build_files = [] 841 | for build_file in self.build_files: 842 | filename = build_file.fileRef.path 843 | extension = os.path.splitext(filename)[1][1:] 844 | if extension in extensions_to_match: 845 | matching_build_files.append(build_file) 846 | return matching_build_files 847 | 848 | def create_file_reference(self, object_factory, parent_group, filename): 849 | filename_reference = self.create_invisible_file_reference(object_factory, filename) 850 | parent_group.append_child(filename_reference) 851 | return filename_reference 852 | 853 | def create_invisible_file_reference(self, object_factory, filename): 854 | filename_reference = object_factory.create(PBXFileReference, filename) 855 | filename_reference.name = os.path.basename(filename) 856 | self.source_file_references.append(filename_reference) 857 | return filename_reference 858 | 859 | 860 | def create_build_file(self, object_factory, source_root, filename, root_group): 861 | parent_group = root_group 862 | 863 | extension = os.path.splitext(filename)[1][1:] 864 | if extension != "framework" and extension != "dylib": 865 | parent_group = create_directory_groups(object_factory, root_group, source_root, filename) 866 | 867 | filename_reference = self.create_file_reference(object_factory, parent_group, filename) 868 | build_file = object_factory.create(PBXBuildFile, filename_reference) 869 | self.build_files.append(build_file) 870 | 871 | return build_file 872 | 873 | def generate_build_files(self, source_root, source_filenames, extensions, object_factory, root_group): 874 | for filename in source_filenames: 875 | extension = os.path.splitext(filename)[1][1:] 876 | if not extensions or (extension in extensions): 877 | self.create_build_file(object_factory, source_root, filename, root_group) 878 | 879 | def generate_file_references(self, source_filenames, extensions, object_factory, root_group): 880 | for filename in source_filenames: 881 | extension = os.path.splitext(filename)[1][1:] 882 | if extension in extensions: 883 | self.create_file_reference(object_factory, root_group, filename) 884 | 885 | def create_default_groups(self): 886 | pass 887 | 888 | class Xcode(XcodeProjectObjectRaw): 889 | def __init__(self, project, source_root, platform): 890 | self.archiveVersion = 1 891 | self.classes = {} 892 | self.objectVersion = 46 893 | self.objects = XcodeObjects(project, source_root, platform) 894 | self.rootObject = self.objects.project 895 | comment = "Xcode" 896 | XcodeProjectObjectRaw.__init__(self, project, comment) 897 | 898 | def write(self, creator, name): 899 | output = creator.create_file(name + ".xcodeproj/project.pbxproj") 900 | pos = output.target_path.rfind("/", 0, len(output.target_path)-1) 901 | build_path = output.target_path[:pos] 902 | self.objects.change_target_path_for_file_references(build_path) 903 | document = XcodeWriterDocument() 904 | self.push(document, output) 905 | 906 | XcodeProjectObjectRaw.write(self, output) 907 | document.close(output) 908 | output.close() 909 | --------------------------------------------------------------------------------