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], ['nm', '--dynamic', '--defined-only', '--extern-only', library_path],
capture_output=True, capture_output=True,
text=True, text=True,
check=True check=True,
) )
except subprocess.CalledProcessError as e: 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) sys.exit(1)
except FileNotFoundError: except FileNotFoundError:
print("Error: 'nm' command not found. Please install binutils.", file=sys.stderr) 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], ['nm', '--dynamic', '--undefined-only', executable_path],
capture_output=True, capture_output=True,
text=True, text=True,
check=True check=True,
) )
except subprocess.CalledProcessError as e: 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() return set()
except FileNotFoundError: except FileNotFoundError:
print("Error: 'nm' command not found. Please install binutils.", file=sys.stderr) 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], ['ldd', executable_path],
capture_output=True, capture_output=True,
text=True, text=True,
check=True check=True,
) )
except (subprocess.CalledProcessError, FileNotFoundError): except (subprocess.CalledProcessError, FileNotFoundError):
# If ldd fails or doesn't exist, we'll skip the verification # 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], ['objdump', '-R', library_path],
capture_output=True, capture_output=True,
text=True, text=True,
check=True check=True,
) )
except subprocess.CalledProcessError as e: 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() return set()
except FileNotFoundError: except FileNotFoundError:
print("Warning: 'objdump' command not found. Internal references won't be detected.", print(
file=sys.stderr) "Warning: 'objdump' command not found. Internal references won't be detected.",
file=sys.stderr,
)
return set() return set()
internal_refs = 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) exported_symbols = get_exported_symbols(library_path)
if not exported_symbols: 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() return set(), set(), set()
# Collect all symbols used by the executables # 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: for exe_path in executable_paths:
# Optionally verify linkage # Optionally verify linkage
if verify_linkage and not verify_executable_links_library(exe_path, library_name): 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}", print(f'Warning: {exe_path} does not appear to link against {library_name}', file=sys.stderr)
file=sys.stderr)
undefined_symbols = get_undefined_symbols(exe_path) undefined_symbols = get_undefined_symbols(exe_path)
# Only count symbols that are actually exported by our library # 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(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Find unused exported symbols in a shared library' description='Find unused exported symbols in a shared library',
) )
parser.add_argument( parser.add_argument(
'library', 'library',
help='Path to the shared library to analyze' help='Path to the shared library to analyze',
) )
parser.add_argument( parser.add_argument(
'executables', 'executables',
nargs='+', nargs='+',
help='Paths to executables that link against the library' help='Paths to executables that link against the library',
) )
parser.add_argument( parser.add_argument(
'--no-verify-linkage', '--no-verify-linkage',
action='store_true', 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( parser.add_argument(
'--show-used', '--show-used',
action='store_true', action='store_true',
help='Also show used symbols' help='Also show used symbols',
) )
parser.add_argument( parser.add_argument(
'--stats-only', '--stats-only',
action='store_true', action='store_true',
help='Only show statistics, not individual symbols' help='Only show statistics, not individual symbols',
) )
args = parser.parse_args() args = parser.parse_args()
@@ -241,7 +242,7 @@ def main():
# Verify library exists # Verify library exists
library_path = Path(args.library) library_path = Path(args.library)
if not library_path.exists(): 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) sys.exit(1)
# Verify executables exist # Verify executables exist
@@ -249,47 +250,45 @@ def main():
for exe in args.executables: for exe in args.executables:
exe_path = Path(exe) exe_path = Path(exe)
if not exe_path.exists(): 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: else:
executable_paths.append(str(exe_path)) executable_paths.append(str(exe_path))
if not executable_paths: 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) sys.exit(1)
# Analyze symbols # Analyze symbols
unused, exported, used = find_unused_symbols( unused, exported, used = find_unused_symbols(
str(library_path), str(library_path), executable_paths, verify_linkage=not args.no_verify_linkage
executable_paths,
verify_linkage=not args.no_verify_linkage
) )
# Print results # Print results
print(f"Analysis of {library_path.name}") print(f'Analysis of {library_path.name}')
print("=" * 70) print('=' * 70)
print(f"Total exported symbols: {len(exported)}") print(f'Total exported symbols: {len(exported)}')
print(f" (excluding public API symbols starting with 'sd_')") print(" (excluding public API symbols starting with 'sd_')")
print(f"Used symbols: {len(used)}") print(f'Used symbols: {len(used)}')
print(f"Unused symbols: {len(unused)}") print(f'Unused symbols: {len(unused)}')
print(f"Usage rate: {len(used)/len(exported)*100:.1f}%" if exported else "N/A") print(f'Usage rate: {len(used) / len(exported) * 100:.1f}%' if exported else 'N/A')
print() print()
if not args.stats_only: if not args.stats_only:
if unused: if unused:
print("Unused symbols:") print('Unused symbols:')
print("-" * 70) print('-' * 70)
for symbol in sorted(unused): for symbol in sorted(unused):
print(f" {symbol}") print(f' {symbol}')
print() print()
else: else:
print("All exported symbols are used!") print('All exported symbols are used!')
print() print()
if args.show_used and used: if args.show_used and used:
print("Used symbols:") print('Used symbols:')
print("-" * 70) print('-' * 70)
for symbol in sorted(used): for symbol in sorted(used):
print(f" {symbol}") print(f' {symbol}')
print() print()
# Exit with non-zero if there are unused symbols (useful for CI) # Exit with non-zero if there are unused symbols (useful for CI)