# Copyright 2020 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import argparse import collections import json import os import subprocess import sys import tempfile class OrderedSet(collections.OrderedDict): def add(self, value): self[value] = True def compile_module(module, sources, settings, extras, tmpdir): output_file_map = {} if settings.whole_module_optimization: output_file_map[''] = { 'object': os.path.join(settings.object_dir, module + '.o'), 'dependencies': os.path.join(tmpdir, module + '.d'), } else: for source in sources: name, _ = os.path.splitext(os.path.basename(source)) output_file_map[source] = { 'object': os.path.join(settings.object_dir, name + '.o'), 'dependencies': os.path.join(tmpdir, name + '.d'), } for key in ('module_path', 'header_path', 'depfile'): path = getattr(settings, key) if os.path.exists(path): os.unlink(path) if key == 'module_path': for ext in '.swiftdoc', '.swiftsourceinfo': path = os.path.splitext(getattr(settings, key))[0] + ext if os.path.exists(path): os.unlink(path) directory = os.path.dirname(path) if not os.path.exists(directory): os.makedirs(directory) if not os.path.exists(settings.object_dir): os.makedirs(settings.object_dir) for key in output_file_map: path = output_file_map[key]['object'] if os.path.exists(path): os.unlink(path) output_file_map_path = os.path.join(tmpdir, module + '.json') with open(output_file_map_path, 'w') as output_file_map_file: output_file_map_file.write(json.dumps(output_file_map)) output_file_map_file.flush() extra_args = [] if settings.bridge_header: extra_args.extend([ '-import-objc-header', os.path.abspath(settings.bridge_header), ]) if settings.whole_module_optimization: extra_args.append('-whole-module-optimization') if settings.target: extra_args.extend([ '-target', settings.target, ]) if settings.sdk: extra_args.extend([ '-sdk', os.path.abspath(settings.sdk), ]) if settings.swift_version: extra_args.extend([ '-swift-version', settings.swift_version, ]) if settings.include_dirs: for include_dir in settings.include_dirs: extra_args.append('-I' + include_dir) process = subprocess.Popen([ 'swiftc', '-parse-as-library', '-module-name', module, '-emit-object', '-emit-dependencies', '-emit-module', '-emit-module-path', settings.module_path, '-emit-objc-header', '-emit-objc-header-path', settings.header_path, '-output-file-map', output_file_map_path, ] + extra_args + extras + sources, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) stdout, stderr = process.communicate() if process.returncode: sys.stdout.write(stdout) sys.stderr.write(stderr) sys.exit(process.returncode) depfile_content = collections.OrderedDict() for key in output_file_map: for line in open(output_file_map[key]['dependencies']): output, inputs = line.split(' : ', 2) _, ext = os.path.splitext(output) if ext == '.o': key = output else: key = os.path.splitext(settings.module_path)[0] + ext if key not in depfile_content: depfile_content[key] = OrderedSet() for path in inputs.split(): depfile_content[key].add(path) with open(settings.depfile, 'w') as depfile: for key in depfile_content: if not settings.depfile_filter or key in settings.depfile_filter: inputs = depfile_content[key] depfile.write('%s : %s\n' % (key, ' '.join(inputs))) def main(args): parser = argparse.ArgumentParser(add_help=False) parser.add_argument('-module-name', help='name of the Swift module') parser.add_argument('-include', '-I', action='append', dest='include_dirs', help='add directory to header search path') parser.add_argument('sources', nargs='+', help='Swift source file to compile') parser.add_argument('-whole-module-optimization', action='store_true', help='enable whole module optimization') parser.add_argument('-object-dir', help='path to the generated object files directory') parser.add_argument('-module-path', help='path to the generated module file') parser.add_argument('-header-path', help='path to the generated header file') parser.add_argument('-bridge-header', help='path to the Objective-C bridge header') parser.add_argument('-depfile', help='path to the generated depfile') parser.add_argument('-swift-version', help='version of Swift language to support') parser.add_argument('-depfile-filter', action='append', help='limit depfile to those files') parser.add_argument('-target', action='store', help='generate code for the given target ') parser.add_argument('-sdk', action='store', help='compile against sdk') parsed, extras = parser.parse_known_args(args) with tempfile.TemporaryDirectory() as tmpdir: compile_module(parsed.module_name, parsed.sources, parsed, extras, tmpdir) if __name__ == '__main__': sys.exit(main(sys.argv[1:]))