#!/usr/bin/env python import argparse import libcxx.header_information import os import pathlib import re import sys import typing def IWYU_mapping(header: str) -> typing.Optional[typing.List[str]]: ignore = [ "__debug_utils/.+", "__fwd/get[.]h", "__pstl/.+", "__support/.+", "__utility/private_constructor_tag.h", ] if any(re.match(pattern, header) for pattern in ignore): return None elif header == "__bits": return ["bits"] elif header in ("__bit_reference", "__fwd/bit_reference.h"): return ["bitset", "vector"] elif re.match("__configuration/.+", header) or header == "__config": return ["version"] elif header == "__hash_table": return ["unordered_map", "unordered_set"] elif header == "__locale": return ["locale"] elif re.match("__locale_dir/.+", header): return ["locale"] elif re.match("__math/.+", header): return ["cmath"] elif header == "__node_handle": return ["map", "set", "unordered_map", "unordered_set"] elif header == "__split_buffer": return ["deque", "vector"] elif re.match("(__thread/support[.]h)|(__thread/support/.+)", header): return ["atomic", "mutex", "semaphore", "thread"] elif header == "__tree": return ["map", "set"] elif header == "__fwd/byte.h": return ["cstddef"] elif header == "__fwd/pair.h": return ["utility"] elif header == "__fwd/subrange.h": return ["ranges"] elif re.match("__fwd/(fstream|ios|istream|ostream|sstream|streambuf)[.]h", header): return ["iosfwd"] # Handle remaining forward declaration headers elif re.match("__fwd/(.+)[.]h", header): return [re.match("__fwd/(.+)[.]h", header).group(1)] # Handle detail headers for things like <__algorithm/foo.h> elif re.match("__(.+?)/.+", header): return [re.match("__(.+?)/.+", header).group(1)] else: return None def main(argv: typing.List[str]): parser = argparse.ArgumentParser() parser.add_argument( "-o", help="File to output the IWYU mappings into", type=argparse.FileType("w"), required=True, dest="output", ) args = parser.parse_args(argv) mappings = [] # Pairs of (header, public_header) for header in libcxx.header_information.all_headers: public_headers = IWYU_mapping(header) if public_headers is not None: mappings.extend((header, public) for public in public_headers) # Validate that we only have valid public header names -- otherwise the mapping above # needs to be updated. for header, public in mappings: if public not in libcxx.header_information.public_headers: raise RuntimeError(f"{header}: Header {public} is not a valid header") args.output.write("[\n") for header, public in sorted(mappings): args.output.write( f' {{ include: [ "<{header}>", "private", "<{public}>", "public" ] }},\n' ) args.output.write("]\n") if __name__ == "__main__": main(sys.argv[1:])