blob: 768c67b6d872c2646d123fd0d6bd5c5158616e56 [file] [log] [blame] [edit]
#!/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()