From 533e84657e3a4b051d01a7ede156562308790162 Mon Sep 17 00:00:00 2001 From: Lumir Balhar Date: Wed, 1 Jul 2026 14:43:13 +0200 Subject: [PATCH] Include libpython .so files into scanned files --- required-symbols/check.sh | 9 +++++-- required-symbols/collect_symbols.py | 37 +++++++++++++++++++---------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/required-symbols/check.sh b/required-symbols/check.sh index afce753..ae328ca 100755 --- a/required-symbols/check.sh +++ b/required-symbols/check.sh @@ -30,11 +30,16 @@ if [ ${#SCAN_DIRS[@]} -eq 0 ]; then exit 1 fi +SCAN_ARGS=("${SCAN_DIRS[@]}") +for f in /usr/lib64/libpython${PYVER}*.so.*; do + [[ -f "$f" && ! -L "$f" ]] && SCAN_ARGS+=("$f") +done + WORK_DIR=$(mktemp -d) trap 'rm -rf "$WORK_DIR"' EXIT echo "=== Collecting PR build symbols ===" -python${PYVER} "$SCRIPT_DIR/collect_symbols.py" "${SCAN_DIRS[@]}" > "$WORK_DIR/new.json" +python${PYVER} "$SCRIPT_DIR/collect_symbols.py" "${SCAN_ARGS[@]}" > "$WORK_DIR/new.json" # Downgrade to the latest stable version in the distribution repos. # If no older version is available, there is nothing to compare against → skip. @@ -60,7 +65,7 @@ fi echo "" echo "=== Collecting stable version symbols ===" -python${PYVER} "$SCRIPT_DIR/collect_symbols.py" "${SCAN_DIRS[@]}" > "$WORK_DIR/old.json" +python${PYVER} "$SCRIPT_DIR/collect_symbols.py" "${SCAN_ARGS[@]}" > "$WORK_DIR/old.json" echo "" echo "=== Comparing PR build against stable ===" diff --git a/required-symbols/collect_symbols.py b/required-symbols/collect_symbols.py index 9550a39..983c223 100755 --- a/required-symbols/collect_symbols.py +++ b/required-symbols/collect_symbols.py @@ -1,11 +1,14 @@ #!/usr/bin/env python3 """ -Collect undefined non-boring external symbols from compiled Python extension modules. +Collect undefined non-boring external symbols from compiled Python extension modules +and libpython shared libraries. -Usage: collect_symbols.py [...] +Usage: collect_symbols.py [...] -Scans all *.cpython-*.so files found recursively, unions symbols across all -variants of each module (regular/debug/freethreading), and prints JSON to stdout. +Each path may be a directory (scanned recursively for *.cpython-*.so) or a file +(scanned directly; intended for libpython*.so.* passed explicitly from the caller). +Symbols are unioned across all variants of each module (regular/debug/freethreading), +and the result is printed as JSON to stdout. """ import json @@ -24,9 +27,11 @@ BORING = re.compile( def module_name(so: Path) -> str: - # Strip ABI tag (d=debug, t=freethreading, td=both) so variants of the same - # module merge together: _ssl.cpython-314td-x86_64-linux-gnu.so → _ssl - return re.sub(r"\.cpython-\d+[a-z]*-[^.]+\.so$", "", so.name) + # _ssl.cpython-314td-x86_64-linux-gnu.so → _ssl + if m := re.match(r"(.+?)\.cpython-", so.name): + return m.group(1) + # libpython3.14.so.1.0 → libpython3.14 + return re.sub(r"\.so.*$", "", so.name) def external_symbols(so: Path) -> list[str]: @@ -42,12 +47,18 @@ def external_symbols(so: Path) -> list[str]: modules: dict[str, set[str]] = {} for path_arg in sys.argv[1:]: - print(f"Scanning: {path_arg}", file=sys.stderr) - for so in sorted(Path(path_arg).rglob("*.cpython-*.so")): - if so.is_symlink(): - continue - if syms := external_symbols(so): - modules.setdefault(module_name(so), set()).update(syms) + p = Path(path_arg) + if p.is_file(): + print(f"Scanning: {p.name}", file=sys.stderr) + if syms := external_symbols(p): + modules.setdefault(module_name(p), set()).update(syms) + else: + print(f"Scanning: {path_arg}", file=sys.stderr) + for so in sorted(p.rglob("*.cpython-*.so")): + if so.is_symlink(): + continue + if syms := external_symbols(so): + modules.setdefault(module_name(so), set()).update(syms) total_symbols = sum(len(v) for v in modules.values()) print(f"Found {len(modules)} modules, {total_symbols} tracked symbols", file=sys.stderr)