Skip to main content
Glama
make_modulemap.py6.59 kB
#!/usr/bin/env python3 # Copyright (c) Meta Platforms, Inc. and affiliates. # # This source code is licensed under both the MIT license found in the # LICENSE-MIT file in the root directory of this source tree and the Apache # License, Version 2.0 found in the LICENSE-APACHE file in the root directory # of this source tree. import argparse import os import re from io import TextIOWrapper from typing import Dict, FrozenSet, Iterable, List _RESERVED_KEYWORDS: FrozenSet[str] = frozenset( [ "config_macros", "conflict", "exclude", "explicit", "extern", "export_as", "export", "framework", "header", "link", "module", "private", "requires", "textual", "umbrella", "use", ] ) class Module: def __init__( self, name: str, mark_headers_private: bool, is_framework: bool ) -> None: self.name: str = name self.headers: List[str] = [] self.mark_headers_private: bool = mark_headers_private self.submodules: Dict[str, Module] = {} self.is_framework: bool = is_framework def add_header(self, src: str) -> None: self.headers.append(src) def get_submodule( self, name: str, mark_headers_private: bool, is_framework: bool ) -> "Module": if name not in self.submodules: self.submodules[name] = Module(name, mark_headers_private, is_framework) return self.submodules[name] def render(self, f: TextIOWrapper, path_prefix: str, indent: int = 0) -> None: space = " " * indent name = self.name if name in _RESERVED_KEYWORDS: name = f"{name}_" if self.is_framework: f.write(f"{space}framework module {name} {{\n") else: f.write(f"{space}module {name} {{\n") submodule_names = set() for submodule_name in sorted(self.submodules.keys()): submodule = self.submodules[submodule_name] # remove any extensions for readability sanitized_name = os.path.splitext(submodule_name)[0] # module names can only be ascii or _ sanitized_name = re.sub(r"[^A-Za-z0-9_]", "_", sanitized_name) if sanitized_name[0].isdigit(): sanitized_name = "_" + sanitized_name # avoid any collisions with other files while sanitized_name in submodule_names: sanitized_name += "_" submodule_names.add(sanitized_name) submodule.name = sanitized_name submodule.render(f, path_prefix, indent + 4) header_space = " " * (indent + 4) prefix = "private " if self.mark_headers_private else "" for h in sorted(self.headers): f.write(f'{header_space}{prefix}header "{os.path.join(path_prefix, h)}"\n') if self.headers: f.write(f"{header_space}export *\n") f.write(f"{space}}}\n") def _write_single_module( f: TextIOWrapper, name: str, headers: Iterable[str], path_prefix: str, mark_headers_private: bool, is_framework: bool, ) -> None: module = Module(name, mark_headers_private, is_framework) for h in headers: module.add_header(h) module.render(f, path_prefix) def _write_submodules( f: TextIOWrapper, name: str, headers: Iterable[str], path_prefix: str, mark_headers_private: bool, is_framework: bool, ) -> None: # Create a tree of nested modules, one for each path component. root_module = Module(name, mark_headers_private, is_framework) for h in headers: module = root_module for i, component in enumerate(h.split(os.sep)): if i == 0 and component == name: # The common case is we have a single header path prefix that matches the module name. # In this case we add the headers directly to the root module. pass else: module = module.get_submodule( component, mark_headers_private, is_framework ) module.add_header(h) root_module.render(f, path_prefix) def _write_swift_header(f: TextIOWrapper, name: str, swift_header_path: str) -> None: f.write( f""" module {name}.Swift {{ header "{swift_header_path}" requires objc }} """ ) def main() -> None: parser = argparse.ArgumentParser() parser.add_argument( "--output", required=True, help="The path to write the modulemap to" ) parser.add_argument("--name", required=True, help="The name of the module") parser.add_argument( "--swift-header", help="If this is a mixed module extend with this Swift header" ) parser.add_argument( "--use-submodules", action="store_true", help="If set produce a modulemap with per-header submodules", ) parser.add_argument( "--symlink-tree", required=True, ) parser.add_argument( "--mark-headers-private", help="This doesn't apply to --swift-header.", action="store_true", ) parser.add_argument( "--framework", help="This modulemap is for embedding in a framework.", action="store_true", ) parser.add_argument( "mappings", nargs="*", default=[], help="A list of import paths" ) args = parser.parse_args() output_dir = os.path.dirname(args.output) if args.framework: path_prefix = "" else: path_prefix = os.path.relpath(args.symlink_tree, output_dir) with open(args.output, "w") as f: if args.use_submodules: _write_submodules( f, args.name, args.mappings, path_prefix, args.mark_headers_private, args.framework, ) else: _write_single_module( f, args.name, args.mappings, path_prefix, args.mark_headers_private, args.framework, ) if args.swift_header: swift_header_name = os.path.relpath(args.swift_header, output_dir) _write_swift_header( f, args.name, os.path.join( "swift-extended_symlink_tree", args.name, str(swift_header_name), ), ) if __name__ == "__main__": main()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server