mirror of
https://github.com/rust-lang/rust.git
synced 2025-10-27 19:16:36 +00:00
Rather than collecting a list of file names in `libm-test/build.rs`, just use a script to parse rustdoc's JSON output.
118 lines
2.9 KiB
Python
Executable File
118 lines
2.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Create a text file listing all public API. This can be used to ensure that all
|
|
functions are covered by our macros.
|
|
"""
|
|
|
|
import json
|
|
import subprocess as sp
|
|
import sys
|
|
import difflib
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
ETC_DIR = Path(__file__).parent
|
|
|
|
|
|
def get_rustdoc_json() -> dict[Any, Any]:
|
|
"""Get rustdoc's JSON output for the `libm` crate."""
|
|
|
|
librs_path = ETC_DIR.joinpath("../src/lib.rs")
|
|
j = sp.check_output(
|
|
[
|
|
"rustdoc",
|
|
librs_path,
|
|
"--edition=2021",
|
|
"--output-format=json",
|
|
"-Zunstable-options",
|
|
"-o-",
|
|
],
|
|
text=True,
|
|
)
|
|
j = json.loads(j)
|
|
return j
|
|
|
|
|
|
def list_public_functions() -> list[str]:
|
|
"""Get a list of public functions from rustdoc JSON output.
|
|
|
|
Note that this only finds functions that are reexported in `lib.rs`, this will
|
|
need to be adjusted if we need to account for functions that are defined there.
|
|
"""
|
|
names = []
|
|
index: dict[str, dict[str, Any]] = get_rustdoc_json()["index"]
|
|
for item in index.values():
|
|
# Find public items
|
|
if item["visibility"] != "public":
|
|
continue
|
|
|
|
# Find only reexports
|
|
if "use" not in item["inner"].keys():
|
|
continue
|
|
|
|
# Locate the item that is reexported
|
|
id = item["inner"]["use"]["id"]
|
|
srcitem = index.get(str(id))
|
|
|
|
# External crate
|
|
if srcitem is None:
|
|
continue
|
|
|
|
# Skip if not a function
|
|
if "function" not in srcitem["inner"].keys():
|
|
continue
|
|
|
|
names.append(srcitem["name"])
|
|
|
|
names.sort()
|
|
return names
|
|
|
|
|
|
def diff_and_exit(actual: str, expected: str):
|
|
"""If the two strings are different, print a diff between them and then exit
|
|
with an error.
|
|
"""
|
|
if actual == expected:
|
|
print("output matches expected; success")
|
|
return
|
|
|
|
a = [f"{line}\n" for line in actual.splitlines()]
|
|
b = [f"{line}\n" for line in expected.splitlines()]
|
|
|
|
diff = difflib.unified_diff(a, b, "actual", "expected")
|
|
sys.stdout.writelines(diff)
|
|
print("mismatched function list")
|
|
exit(1)
|
|
|
|
|
|
def main():
|
|
"""By default overwrite the file. If `--check` is passed, print a diff instead and
|
|
error if the files are different.
|
|
"""
|
|
match sys.argv:
|
|
case [_]:
|
|
check = False
|
|
case [_, "--check"]:
|
|
check = True
|
|
case _:
|
|
print("unrecognized arguments")
|
|
exit(1)
|
|
|
|
names = list_public_functions()
|
|
output = "# autogenerated by update-api-list.py\n"
|
|
for name in names:
|
|
output += f"{name}\n"
|
|
|
|
out_file = ETC_DIR.joinpath("function-list.txt")
|
|
|
|
if check:
|
|
with open(out_file, "r") as f:
|
|
current = f.read()
|
|
diff_and_exit(current, output)
|
|
else:
|
|
with open(out_file, "w") as f:
|
|
f.write(output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|