['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)
['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)
['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
['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()
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
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
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()
# 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
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)