find-unused-library-symbols: apply "ruff format" and "ruff check --fix"

This commit is contained in:
Yu Watanabe
2026-05-18 02:44:47 +09:00
parent 2ab056cb7e
commit 30c87278d4
+37 -38
View File
@@ -39,10 +39,10 @@ def get_exported_symbols(library_path):
['nm', '--dynamic', '--defined-only', '--extern-only', library_path],
capture_output=True,
text=True,
check=True
check=True,
)
except subprocess.CalledProcessError as e:
print(f"Error: Failed to run nm on {library_path}: {e}", file=sys.stderr)
print(f'Error: Failed to run nm on {library_path}: {e}', file=sys.stderr)
sys.exit(1)
except FileNotFoundError:
print("Error: 'nm' command not found. Please install binutils.", file=sys.stderr)
@@ -79,10 +79,10 @@ def get_undefined_symbols(executable_path):
['nm', '--dynamic', '--undefined-only', executable_path],
capture_output=True,
text=True,
check=True
check=True,
)
except subprocess.CalledProcessError as e:
print(f"Warning: Failed to run nm on {executable_path}: {e}", file=sys.stderr)
print(f'Warning: Failed to run nm on {executable_path}: {e}', file=sys.stderr)
return set()
except FileNotFoundError:
print("Error: 'nm' command not found. Please install binutils.", file=sys.stderr)
@@ -112,7 +112,7 @@ def verify_executable_links_library(executable_path, library_name):
['ldd', executable_path],
capture_output=True,
text=True,
check=True
check=True,
)
except (subprocess.CalledProcessError, FileNotFoundError):
# If ldd fails or doesn't exist, we'll skip the verification
@@ -137,14 +137,16 @@ def get_library_internal_references(library_path, exported_symbols):
['objdump', '-R', library_path],
capture_output=True,
text=True,
check=True
check=True,
)
except subprocess.CalledProcessError as e:
print(f"Warning: Failed to run objdump on {library_path}: {e}", file=sys.stderr)
print(f'Warning: Failed to run objdump on {library_path}: {e}', file=sys.stderr)
return set()
except FileNotFoundError:
print("Warning: 'objdump' command not found. Internal references won't be detected.",
file=sys.stderr)
print(
"Warning: 'objdump' command not found. Internal references won't be detected.",
file=sys.stderr,
)
return set()
internal_refs = set()
@@ -181,7 +183,7 @@ def find_unused_symbols(library_path, executable_paths, verify_linkage=True):
exported_symbols = get_exported_symbols(library_path)
if not exported_symbols:
print(f"Warning: No exported symbols found in {library_path}", file=sys.stderr)
print(f'Warning: No exported symbols found in {library_path}', file=sys.stderr)
return set(), set(), set()
# Collect all symbols used by the executables
@@ -194,8 +196,7 @@ def find_unused_symbols(library_path, executable_paths, verify_linkage=True):
for exe_path in executable_paths:
# Optionally verify linkage
if verify_linkage and not verify_executable_links_library(exe_path, library_name):
print(f"Warning: {exe_path} does not appear to link against {library_name}",
file=sys.stderr)
print(f'Warning: {exe_path} does not appear to link against {library_name}', file=sys.stderr)
undefined_symbols = get_undefined_symbols(exe_path)
# Only count symbols that are actually exported by our library
@@ -209,31 +210,31 @@ def find_unused_symbols(library_path, executable_paths, verify_linkage=True):
def main():
parser = argparse.ArgumentParser(
description='Find unused exported symbols in a shared library'
description='Find unused exported symbols in a shared library',
)
parser.add_argument(
'library',
help='Path to the shared library to analyze'
help='Path to the shared library to analyze',
)
parser.add_argument(
'executables',
nargs='+',
help='Paths to executables that link against the library'
help='Paths to executables that link against the library',
)
parser.add_argument(
'--no-verify-linkage',
action='store_true',
help='Skip verification that executables actually link against the library'
help='Skip verification that executables actually link against the library',
)
parser.add_argument(
'--show-used',
action='store_true',
help='Also show used symbols'
help='Also show used symbols',
)
parser.add_argument(
'--stats-only',
action='store_true',
help='Only show statistics, not individual symbols'
help='Only show statistics, not individual symbols',
)
args = parser.parse_args()
@@ -241,7 +242,7 @@ def main():
# Verify library exists
library_path = Path(args.library)
if not library_path.exists():
print(f"Error: Library not found: {library_path}", file=sys.stderr)
print(f'Error: Library not found: {library_path}', file=sys.stderr)
sys.exit(1)
# Verify executables exist
@@ -249,47 +250,45 @@ def main():
for exe in args.executables:
exe_path = Path(exe)
if not exe_path.exists():
print(f"Warning: Executable not found: {exe_path}", file=sys.stderr)
print(f'Warning: Executable not found: {exe_path}', file=sys.stderr)
else:
executable_paths.append(str(exe_path))
if not executable_paths:
print("Error: No valid executables provided", file=sys.stderr)
print('Error: No valid executables provided', file=sys.stderr)
sys.exit(1)
# Analyze symbols
unused, exported, used = find_unused_symbols(
str(library_path),
executable_paths,
verify_linkage=not args.no_verify_linkage
str(library_path), executable_paths, verify_linkage=not args.no_verify_linkage
)
# Print results
print(f"Analysis of {library_path.name}")
print("=" * 70)
print(f"Total exported symbols: {len(exported)}")
print(f" (excluding public API symbols starting with 'sd_')")
print(f"Used symbols: {len(used)}")
print(f"Unused symbols: {len(unused)}")
print(f"Usage rate: {len(used)/len(exported)*100:.1f}%" if exported else "N/A")
print(f'Analysis of {library_path.name}')
print('=' * 70)
print(f'Total exported symbols: {len(exported)}')
print(" (excluding public API symbols starting with 'sd_')")
print(f'Used symbols: {len(used)}')
print(f'Unused symbols: {len(unused)}')
print(f'Usage rate: {len(used) / len(exported) * 100:.1f}%' if exported else 'N/A')
print()
if not args.stats_only:
if unused:
print("Unused symbols:")
print("-" * 70)
print('Unused symbols:')
print('-' * 70)
for symbol in sorted(unused):
print(f" {symbol}")
print(f' {symbol}')
print()
else:
print("All exported symbols are used!")
print('All exported symbols are used!')
print()
if args.show_used and used:
print("Used symbols:")
print("-" * 70)
print('Used symbols:')
print('-' * 70)
for symbol in sorted(used):
print(f" {symbol}")
print(f' {symbol}')
print()
# Exit with non-zero if there are unused symbols (useful for CI)