| #!/usr/bin/env python3 |
| |
| import argparse |
| import itertools |
| import json |
| import os |
| import sys |
| |
| if sys.version_info < (3, 11): |
| from pip._vendor import tomli |
| else: |
| import tomllib |
| |
| parser = argparse.ArgumentParser(description="generate wtf's modulemap from a machine-readable TAPI filelist") |
| parser.add_argument('tapi_filelist', type=argparse.FileType()) |
| parser.add_argument('--config', required=True) |
| parser.add_argument('--relative-to', help="path to WTF in build products or the SDK") |
| args = parser.parse_args() |
| |
| if sys.version_info < (3, 11): |
| # tomli post 1.2.0 requires 'rb'; |
| # tomli pre 1.1.0 requires text. |
| try: |
| config = tomli.load(open(args.config)) |
| except TypeError: |
| config = tomli.load(open(args.config, 'rb')) |
| else: |
| config = tomllib.load(open(args.config, 'rb')) |
| |
| config.setdefault('attributes', []) |
| config.setdefault('config-macros', []) |
| config.setdefault('one-submodule-per-header', False) |
| config.setdefault('requirements', []) |
| config.setdefault('textual-headers', []) |
| config.setdefault('module', {}) |
| |
| submodule_mappings = {path: module_name |
| for module_name, module in config['module'].items() |
| for path in module.get('headers', [])} |
| |
| filelist = json.load(args.tapi_filelist) |
| for header in filelist['headers']: |
| path_to_header = os.path.relpath(header['path'], args.relative_to) |
| if config.get('framework-module'): |
| path_to_header = (path_to_header.removeprefix('PrivateHeaders/') |
| .removeprefix('Headers/')) |
| if path_to_header not in submodule_mappings: |
| if config['one-submodule-per-header']: |
| default_submodule_name = os.path.splitext(os.path.basename(path_to_header))[0].replace('-', '') |
| submodule_mappings[path_to_header] = default_submodule_name |
| else: |
| submodule_mappings[path_to_header] = '' |
| |
| if config.get('framework-module'): |
| sys.stdout.write('framework ') |
| sys.stdout.write(f'module {config["module-name"]} ') |
| for attr in config['attributes']: |
| sys.stdout.write(f'[{attr}] ') |
| sys.stdout.write('{\n') |
| |
| if config['config-macros']: |
| sys.stdout.write(f' config_macros {", ".join(config["config-macros"])}\n') |
| |
| for module_name, paths in itertools.groupby( |
| sorted(submodule_mappings, key=submodule_mappings.__getitem__), |
| key=submodule_mappings.__getitem__): |
| # The top-level module's name will be the empty string "". |
| in_submodule = bool(module_name) |
| attrs = config['module'].get(module_name, {}).get('attributes', config['attributes']) |
| reqs = config['module'].get(module_name, {}).get('requirements', config['requirements']) |
| |
| if in_submodule: |
| sys.stdout.write(f' explicit module {module_name} ') |
| for attr in attrs: |
| sys.stdout.write(f'[{attr}] ') |
| sys.stdout.write('{\n') |
| indent = ' ' * 8 |
| else: |
| indent = ' ' * 4 |
| if reqs: |
| sys.stdout.write(f'{indent}requires {", ".join(reqs)}\n') |
| for path in paths: |
| sys.stdout.write(indent) |
| if path in config['textual-headers']: |
| sys.stdout.write('textual ') |
| sys.stdout.write(f'header "{path}"\n') |
| if in_submodule: |
| sys.stdout.write(' }\n') |
| |
| sys.stdout.write('''\ |
| export * |
| } |
| ''') |