| #!/usr/bin/env python3 |
| |
| import argparse |
| import collections |
| import yaml |
| import os |
| import tempfile |
| |
| parser = argparse.ArgumentParser(description='subset of strip(1) for manipulating symbol tables in text-based API stubs') |
| parser.add_argument('input', type=argparse.FileType('r'), nargs='+', help='TBDs to strip') |
| group = parser.add_mutually_exclusive_group() |
| group.add_argument('-s', type=argparse.FileType('r'), dest='save_list', help='save the symbols listed in the file given') |
| group.add_argument('-R', type=argparse.FileType('r'), dest='remove_list', help='remove the symbols listed in the file given') |
| |
| OPTS = parser.parse_args() |
| SELECTED_SYMBOLS = set() |
| STRIP_SELECTED = bool(OPTS.remove_list) |
| |
| fd = OPTS.save_list or OPTS.remove_list |
| if fd: |
| for line in fd: |
| line = line.strip() |
| if line and not line.startswith('#'): |
| SELECTED_SYMBOLS.add(line) |
| |
| # FIXME: handle JSON-based TBD format (v5) |
| class TBD(yaml.YAMLObject): |
| yaml_tag = '!tapi-tbd' |
| |
| def __repr__(self): |
| return 'TBD(version={tbd-version}, {install-name})'.format_map(self.__dict__) |
| |
| @classmethod |
| def to_yaml(cls, dumper, data): |
| dumper.sort_keys = False |
| mapping = super().to_yaml(dumper, data) |
| # Selectively enable flow layout to mimic LLVM formatting. |
| def walk_and_set_flow_style(node): |
| if isinstance(node, yaml.nodes.SequenceNode): |
| only_scalar = True |
| for n in node.value: |
| only_scalar &= walk_and_set_flow_style(n) |
| if only_scalar: |
| node.flow_style = True |
| return False |
| elif isinstance(node, yaml.nodes.MappingNode): |
| for k, v in node.value: |
| walk_and_set_flow_style(v) |
| return False |
| else: |
| node.flow_style = True |
| return True |
| walk_and_set_flow_style(mapping) |
| return mapping |
| |
| for tbd_fd in OPTS.input: |
| updated_tbds = [] |
| for tbd in yaml.unsafe_load_all(tbd_fd): |
| new_exports = [] |
| while getattr(tbd, 'exports', None): |
| export_list = tbd.exports.pop() |
| write_back = False |
| |
| for key, predicate in ( |
| # field name # whether value represents a selected symbol |
| ('symbols', lambda x: x in SELECTED_SYMBOLS), |
| ('weak-symbols', lambda x: x in SELECTED_SYMBOLS), |
| ('objc-classes', lambda x: f'_OBJC_CLASS_$_{x}' in SELECTED_SYMBOLS or f'_OBJC_METACLASS_$_{x}' in SELECTED_SYMBOLS), |
| ('objc-ivars', lambda x: f'_OBJC_IVAR_$_{x}' in SELECTED_SYMBOLS), |
| ): |
| if not key in export_list: |
| continue |
| filtered = [name for name in export_list[key] if predicate(name) ^ STRIP_SELECTED] |
| if filtered: |
| export_list[key] = filtered |
| write_back = True |
| else: |
| del export_list[key] |
| |
| if write_back: |
| new_exports.append(export_list) |
| tbd.exports = new_exports |
| if hasattr(tbd, 'allowable-clients'): |
| # Stub TBDs in WebKit aren't part of a real SDK, no need to care |
| # about who links against them. |
| delattr(tbd, 'allowable-clients') |
| updated_tbds.append(tbd) |
| |
| output_dir, output_basename = os.path.split(tbd_fd.name) |
| output_fd, output_name = tempfile.mkstemp(prefix=f'.{output_basename}', dir=output_dir) |
| output_fd = os.fdopen(output_fd, 'w') |
| yaml.dump_all(updated_tbds, output_fd, width=120, indent=8, explicit_start=True, explicit_end=True) |
| os.replace(output_name, tbd_fd.name) |