| #!/usr/bin/env python3.8 |
| # Copyright 2018 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. |
| |
| """ |
| This script allows for the conversion between ids.txt and .build-id formats. |
| """ |
| |
| import os |
| import sys |
| import argparse |
| |
| # rel_to is expected to be absolute |
| def abs_path(path, rel_to): |
| if os.path.isabs(path): |
| return path |
| else: |
| return os.path.abspath(os.path.join(rel_to, path)) |
| |
| # rel_to is assumed to be absolute |
| def read_ids_txt(ids_path, rel_to): |
| with open(ids_path) as f: |
| return {build_id:abs_path(path, rel_to) for (build_id, path) in (x.split() for x in f.readlines())} |
| |
| # build_id_dir is assumed to be absolute |
| def read_build_id_dir(build_id_dir): |
| out = {} |
| for root, dirs, files in os.walk(build_id_dir): |
| if len(files) != 0 and len(dirs) != 0: |
| raise Exception("%s is not a valid .build-id directory" % build_id_dir) |
| for f in files: |
| suffix = ".debug" |
| if f.endswith(suffix): |
| out[os.path.basename(root) + f[:-len(suffix)]] = os.path.join(root, f) |
| return out |
| |
| def link(src, dst): |
| src = os.path.realpath(src) |
| if os.path.exists(dst): |
| os.remove(dst) |
| os.link(src, dst) |
| |
| def mkdir(path): |
| try: |
| os.makedirs(path) |
| except OSError as e: |
| if e.errno != os.errno.EEXIST: |
| raise e |
| |
| def touch(path): |
| if os.path.exists(path): |
| os.utime(path, None) |
| else: |
| with open(path, 'w'): |
| return |
| |
| def write_build_id_dir(build_id_dir, mods): |
| for build_id, path in mods.items(): |
| mkdir(os.path.join(build_id_dir, build_id[:2])) |
| link(path, os.path.join(build_id_dir, build_id[:2], build_id[2:] + ".debug")) |
| |
| # rel_to and path are assumed to be absolute |
| # if rel_to is None fix_path returns the absolute path. If rel_to |
| # is not None it turns the path into a relative path. |
| def fix_path(path, rel_to): |
| if rel_to is None: |
| return path |
| return os.path.relpath(path, rel_to) |
| |
| # rel_to is assumed to be an absolute path |
| def write_ids_txt(ids_path, rel_to, mods): |
| with open(ids_path, "w") as f: |
| for build_id, path in sorted(mods.items()): |
| path = fix_path(mods[build_id], rel_to) |
| f.write("%s %s\n" % (build_id, path)) |
| |
| def main(): |
| ids_fmt = "ids.txt" |
| build_id_fmt = ".build-id" |
| |
| parser = argparse.ArgumentParser(description="Convert between ids.txt and .build-id") |
| parser.add_argument("-O", "--output-format", help="Sets the output format.", |
| metavar="FMT", |
| choices=[ids_fmt, build_id_fmt]) |
| parser.add_argument("--ids-rel-to-in", |
| help="When reading ids.txt use paths relative to DIR", |
| metavar="DIR") |
| parser.add_argument("--ids-rel-to-out", |
| help="When writing ids.txt use paths relative to DIR", |
| metavar="DIR") |
| parser.add_argument("--stamp", |
| help="Touch STAMP after finishing", |
| metavar="STAMP") |
| parser.add_argument("--input", action="append", |
| help=".build-id directories or ids.txt files depending on the output format") |
| parser.add_argument("output") |
| |
| args = parser.parse_args() |
| |
| input_paths = map(os.path.abspath, args.input) |
| input_paths = list(filter(os.path.exists, input_paths)) # conventionally ignore empty inputs |
| input_dirs = list(map(os.path.isdir, input_paths)) |
| if len(input_dirs) > 0: |
| assert len(input_dirs) == len(input_paths), "input formats cannot be mixed" |
| in_fmt = build_id_fmt |
| else: |
| in_fmt = ids_fmt |
| |
| output_path = args.output |
| rel_to_in = os.path.abspath(args.ids_rel_to_in) if args.ids_rel_to_in is not None else None |
| rel_to_out = os.path.abspath(args.ids_rel_to_out) if args.ids_rel_to_out is not None else None |
| |
| mods = {} |
| for input_path in input_paths: |
| if in_fmt == ids_fmt: |
| if rel_to_in is None: |
| rel_to_in = os.path.abspath(os.path.dirname(input_path)) |
| mods.update(read_ids_txt(input_path, rel_to_in)) |
| else: |
| mods.update(read_build_id_dir(input_path)) |
| |
| if args.output_format == None: |
| if in_fmt == ids_fmt: |
| out_fmt = build_id_fmt |
| else: |
| out_fmt = ids_fmt |
| else: |
| out_fmt = args.output_format |
| |
| if out_fmt == ids_fmt: |
| write_ids_txt(output_path, rel_to_out, mods) |
| else: |
| write_build_id_dir(output_path, mods) |
| |
| if args.stamp is not None: |
| touch(args.stamp) |
| |
| if __name__ == "__main__": |
| main() |