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