| # Copyright 2019 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Script to emit runtimes.json for Clang toolchain""" |
| |
| import argparse |
| import collections |
| import json |
| import os |
| import re |
| import subprocess |
| import sys |
| |
| |
| def read_build_id(readelf, filename): |
| p = subprocess.Popen([readelf, '-n', filename], |
| stdout=subprocess.PIPE, |
| env={'LC_ALL': 'C'}) |
| stdout, _ = p.communicate() |
| if p.returncode != 0: |
| raise Exception('failed to read notes') |
| match = re.search(r'Build ID: ([a-zA-Z0-9_-]+)', stdout) |
| if not match: |
| raise Exception('build ID missing') |
| return match.group(1) |
| |
| |
| def generate_entry(dirname, readelf, soname, filename): |
| build_id = read_build_id( |
| readelf, os.path.realpath(os.path.join(dirname, 'lib', filename))) |
| return { |
| 'soname': soname, |
| 'dist': filename, |
| 'debug': 'debug/.build-id/%s/%s.debug' % (build_id[0:2], build_id[2:]) |
| } |
| |
| |
| Target = collections.namedtuple('Target', ['triple', 'aliases']) |
| |
| |
| class TargetAction(argparse.Action): |
| |
| def __call__(self, parser, namespace, values, option_string=None): |
| targets = getattr(namespace, 'target') |
| targets.append(Target(triple=values, aliases=[])) |
| |
| |
| class AliasAction(argparse.Action): |
| |
| def __call__(self, parser, namespace, values, option_string=None): |
| targets = getattr(namespace, 'target') |
| if not targets: |
| raise argparse.ArgumentError(self, 'missing --target') |
| targets[-1].aliases.append(values) |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser(description=__doc__) |
| parser.add_argument('--dir', default=os.getcwd(), help='Clang directory') |
| parser.add_argument( |
| '--readelf', required=True, help='path to readelf utility') |
| parser.add_argument( |
| '--resource-dir', required=True, help='Clang resource directory') |
| parser.add_argument( |
| '--manifest', |
| action='store_const', |
| const=True, |
| default=False, |
| help='Emit old manifest format instead of JSON') |
| parser.add_argument( |
| '--target', |
| metavar='TRIPLE', |
| default=[], |
| action=TargetAction, |
| help='--target=TRIPLE values to support') |
| parser.add_argument( |
| '--alias', |
| metavar='TRIPLE', |
| action=AliasAction, |
| help='--target=TRIPLE alias, applies to preceding --target') |
| args = parser.parse_args() |
| |
| runtimes = [] |
| for target in args.target: |
| runtime_dir = '{resource_dir}/lib/{target}'.format( |
| resource_dir=args.resource_dir, target=target.triple) |
| cxx_lib_dir = '{target}/c++'.format(target=target.triple) |
| manifest = {} |
| for mode_ldflags in [[], ['-static-libstdc++']]: |
| for mode_cflags, mode_multilib, mode_runtimes in [ |
| ([], '', []), |
| (['-fsanitize=address'], 'asan/', ['libclang_rt.asan.so']), |
| (['-fsanitize=undefined'], '', ['libclang_rt.ubsan_standalone.so']), |
| ]: |
| runtime = [ |
| generate_entry(args.dir, args.readelf, soname, |
| runtime_dir + '/' + soname) |
| for soname in mode_runtimes |
| ] |
| for lib in runtime: |
| manifest['lib/' + lib['soname']] = lib['dist'] |
| if not mode_ldflags: |
| cxx_lib = [ |
| generate_entry(args.dir, args.readelf, soname, |
| cxx_lib_dir + '/' + mode_multilib + soname) |
| for soname in ['libc++.so.2', 'libc++abi.so.1', 'libunwind.so.1'] |
| ] |
| runtime.extend(cxx_lib) |
| for lib in cxx_lib: |
| manifest['lib/' + mode_multilib + lib['soname']] = lib['dist'] |
| |
| runtimes.append({ |
| 'target': [target.triple] + target.aliases, |
| 'cflags': mode_cflags, |
| 'ldflags': mode_ldflags, |
| 'runtime': runtime, |
| }) |
| |
| if args.manifest: |
| sys.stdout.write(''.join( |
| sorted('%s=%s\n' % x for x in manifest.iteritems()))) |
| else: |
| json.dump(runtimes, sys.stdout, indent=2, sort_keys=True) |
| |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |