llvm-capstone/libcxx/utils/generate_iwyu_mapping.py
Louis Dionne 127c390fc4
[libc++] Rewrite the IWYU generation (#78295)
This simplifies the IWYU generation script by treating everything as a
file, instead of dealing with directories and files separately.

This has the downside that the `libcxx.imp` file is a lot larger than it
used to be, however we now have the flexibility of mapping files under
detail directories to different public headers. For example, this allows
us to map <__fwd/subrange.h> to <ranges> but <__fwd/pair.h> to
<utility>.

This patch also adds basic validation to ensure that we never map a
header to a public header that doesn't exist. We may still be missing
some mappings or we may be mapping to incorrect headers, but we won't be
mapping to headers that downright don't exist.

Fixes #63346
2024-01-16 13:45:05 -05:00

75 lines
2.5 KiB
Python

#!/usr/bin/env python
import libcxx.header_information
import os
import pathlib
import re
import typing
def IWYU_mapping(header: str) -> typing.Optional[typing.List[str]]:
ignore = [
"__debug_utils/.+",
"__fwd/get[.]h",
"__support/.+",
]
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 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 header == "__threading_support":
return ["atomic", "mutex", "semaphore", "thread"]
elif header == "__tree":
return ["map", "set"]
elif header == "__fwd/hash.h":
return ["functional"]
elif header == "__fwd/pair.h":
return ["utility"]
elif header == "__fwd/subrange.h":
return ["ranges"]
# 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():
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")
with open(libcxx.header_information.include / "libcxx.imp", "w") as f:
f.write("[\n")
for header, public in sorted(mappings):
f.write(
f' {{ include: [ "<{header}>", "private", "<{public}>", "public" ] }},\n'
)
f.write("]\n")
if __name__ == "__main__":
main()