1#!/usr/bin/python3 2 3 4import argparse 5import collections 6import json 7import os 8import subprocess 9import sys 10import tempfile 11 12 13class OrderedSet(collections.OrderedDict): 14 15 def add(self, value): 16 self[value] = True 17 18 19def compile_module(module, sources, settings, extras, tmpdir): 20 output_file_map = {} 21 if settings.whole_module_optimization: 22 output_file_map[''] = { 23 'object': os.path.join(settings.object_dir, module + '.o'), 24 'dependencies': os.path.join(tmpdir, module + '.d'), 25 } 26 else: 27 for source in sources: 28 name, _ = os.path.splitext(os.path.basename(source)) 29 output_file_map[source] = { 30 'object': os.path.join(settings.object_dir, name + '.o'), 31 'dependencies': os.path.join(tmpdir, name + '.d'), 32 } 33 34 for key in ('module_path', 'header_path', 'depfile'): 35 path = getattr(settings, key) 36 if os.path.exists(path): 37 os.unlink(path) 38 if key == 'module_path': 39 for ext in '.swiftdoc', '.swiftsourceinfo': 40 path = os.path.splitext(getattr(settings, key))[0] + ext 41 if os.path.exists(path): 42 os.unlink(path) 43 directory = os.path.dirname(path) 44 if not os.path.exists(directory): 45 os.makedirs(directory) 46 47 if not os.path.exists(settings.object_dir): 48 os.makedirs(settings.object_dir) 49 50 for key in output_file_map: 51 path = output_file_map[key]['object'] 52 if os.path.exists(path): 53 os.unlink(path) 54 55 output_file_map_path = os.path.join(tmpdir, module + '.json') 56 with open(output_file_map_path, 'w') as output_file_map_file: 57 output_file_map_file.write(json.dumps(output_file_map)) 58 output_file_map_file.flush() 59 60 extra_args = [] 61 if settings.bridge_header: 62 extra_args.extend([ 63 '-import-objc-header', 64 os.path.abspath(settings.bridge_header), 65 ]) 66 67 if settings.whole_module_optimization: 68 extra_args.append('-whole-module-optimization') 69 70 if settings.target: 71 extra_args.extend([ 72 '-target', 73 settings.target, 74 ]) 75 76 if settings.sdk: 77 extra_args.extend([ 78 '-sdk', 79 os.path.abspath(settings.sdk), 80 ]) 81 82 if settings.include_dirs: 83 for include_dir in settings.include_dirs: 84 extra_args.append('-I' + include_dir) 85 86 process = subprocess.Popen( 87 ['swiftc', 88 '-parse-as-library', 89 '-module-name', 90 module, 91 '-emit-object', 92 '-emit-dependencies', 93 '-emit-module', 94 '-emit-module-path', 95 settings.module_path, 96 '-emit-objc-header', 97 '-emit-objc-header-path', 98 settings.header_path, 99 '-output-file-map', 100 output_file_map_path, 101 ] + extra_args + extras + sources, 102 stdout=subprocess.PIPE, stderr=subprocess.PIPE, 103 universal_newlines=True) 104 105 stdout, stderr = process.communicate() 106 if process.returncode: 107 sys.stdout.write(stdout) 108 sys.stderr.write(stderr) 109 sys.exit(process.returncode) 110 111 112 depfile_content = collections.OrderedDict() 113 for key in output_file_map: 114 for line in open(output_file_map[key]['dependencies']): 115 output, inputs = line.split(' : ', 2) 116 _, ext = os.path.splitext(output) 117 if ext == '.o': 118 key = output 119 else: 120 key = os.path.splitext(settings.module_path)[0] + ext 121 if key not in depfile_content: 122 depfile_content[key] = OrderedSet() 123 for path in inputs.split(): 124 depfile_content[key].add(path) 125 126 with open(settings.depfile, 'w') as depfile: 127 for key in depfile_content: 128 if not settings.depfile_filter or key in settings.depfile_filter: 129 inputs = depfile_content[key] 130 depfile.write('%s : %s\n' % (key, ' '.join(inputs))) 131 132 133def main(args): 134 parser = argparse.ArgumentParser(add_help=False) 135 parser.add_argument( 136 '--module-name', 137 help='name of the Swift module') 138 parser.add_argument( 139 '--include', '-I', action='append', dest='include_dirs', 140 help='add directory to header search path') 141 parser.add_argument( 142 'sources', nargs='+', 143 help='Swift source file to compile') 144 parser.add_argument( 145 '--whole-module-optimization', action='store_true', 146 help='enable whole module optimization') 147 parser.add_argument( 148 '--object-dir', '-o', 149 help='path to the generated object files directory') 150 parser.add_argument( 151 '--module-path', '-m', 152 help='path to the generated module file') 153 parser.add_argument( 154 '--header-path', '-h', 155 help='path to the generated header file') 156 parser.add_argument( 157 '--bridge-header', '-b', 158 help='path to the Objective-C bridge header') 159 parser.add_argument( 160 '--depfile', '-d', 161 help='path to the generated depfile') 162 parser.add_argument( 163 '--depfile-filter', action='append', 164 help='limit depfile to those files') 165 parser.add_argument( 166 '--target', action='store', 167 help='generate code for the given target <triple>') 168 parser.add_argument( 169 '--sdk', action='store', 170 help='compile against sdk') 171 172 parsed, extras = parser.parse_known_args(args) 173 with tempfile.TemporaryDirectory() as tmpdir: 174 compile_module( 175 parsed.module_name, 176 parsed.sources, 177 parsed, 178 extras, 179 tmpdir) 180 181 182if __name__ == '__main__': 183 sys.exit(main(sys.argv[1:])) 184