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