Skip to content

Commit 4baa72a

Browse files
tiranclaude
andcommitted
cli: human-readable YAML-like output with --symbols flag
Replace pprint with a YAML-like formatter for ELFInfo output. Add --symbols flag to include sorted exported and imported dynamic symbols. None-valued fields and the marker field are omitted. Multiple entries are separated by "---". Example: ``` $ elfdeps /usr/lib64/libc.so.6 filename: /usr/lib64/libc.so.6 requires: - ld-linux-x86-64.so.2(GLIBC_2.3)(64bit) provides: - libc.so.6(GLIBC_2.34)(64bit) machine: EM_X86_64 is_dso: true is_exec: false soname: libc.so.6 ``` Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9930548 commit 4baa72a

2 files changed

Lines changed: 66 additions & 3 deletions

File tree

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,34 @@ libc.so.6(GLIBC_PRIVATE)(64bit)
2929
libc.so.6()(64bit)
3030
```
3131

32+
`elfdeps` can also inspect archives like Python wheels and extract symbols:
33+
34+
```shell-session
35+
$ elfdeps --symbols torchaudio-2.11.0-cp312-cp312-manylinux_2_28_x86_64.whl
36+
filename: torchaudio/lib/_torchaudio.abi3.so
37+
requires:
38+
- libgcc_s.so.1(GCC_3.0)(64bit)
39+
- libc.so.6(GLIBC_2.2.5)(64bit)
40+
- ...
41+
provides: []
42+
machine: EM_X86_64
43+
is_dso: true
44+
is_exec: true
45+
got_debug: false
46+
got_hash: false
47+
got_gnuhash: true
48+
soname: _torchaudio.abi3.so
49+
exported_symbols:
50+
- _ZN10torchaudio12cuda_versionEv
51+
- _ZN10torchaudio18is_align_availableEv
52+
- ...
53+
imported_symbols:
54+
- ...
55+
- aoti_torch_abi_version
56+
- aoti_torch_delete_library_object
57+
- ...
58+
```
59+
3260
## RPM
3361

3462
In Fedora-based distributions, RPM packages provide and require virtual packages with ELF sonames and versions. The package manager can install virtual provides.

src/elfdeps/__main__.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# SPDX-License-Identifier: Apache-2.0
22

33
import argparse
4+
import dataclasses
45
import logging
56
import pathlib
6-
import pprint
77
import stat
88
import tarfile
99
import zipfile
@@ -67,6 +67,38 @@
6767
dest="unique",
6868
help="Remove duplicate entries",
6969
)
70+
parser.add_argument(
71+
"--symbols",
72+
action="store_true",
73+
dest="symbols",
74+
help="Include exported and imported dynamic symbols",
75+
)
76+
77+
78+
def _format_elfinfo(info: _elfdeps.ELFInfo) -> str:
79+
"""Format ELFInfo as human-readable YAML-like output."""
80+
lines: list[str] = []
81+
for field in dataclasses.fields(info):
82+
if field.name == "marker":
83+
continue
84+
value = getattr(info, field.name)
85+
if isinstance(value, bool):
86+
lines.append(f"{field.name}: {str(value).lower()}")
87+
elif isinstance(value, list):
88+
if not value:
89+
lines.append(f"{field.name}: []")
90+
else:
91+
lines.append(f"{field.name}:")
92+
_sort = field.name in ("exported_symbols", "imported_symbols")
93+
items = sorted(value) if _sort else value
94+
for item in items:
95+
lines.append(f" - {item}")
96+
elif value is None:
97+
# skip fields that are None when not requested (e.g. symbols)
98+
continue
99+
else:
100+
lines.append(f"{field.name}: {value}")
101+
return "\n".join(lines)
70102

71103

72104
def main(argv: list[str] | None = None) -> None:
@@ -77,6 +109,7 @@ def main(argv: list[str] | None = None) -> None:
77109
filter_soname=args.filter_soname,
78110
require_interp=args.require_interp,
79111
unique=args.unique,
112+
include_symbols=args.symbols,
80113
)
81114
logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
82115
filename: pathlib.Path = args.filename
@@ -105,8 +138,10 @@ def main(argv: list[str] | None = None) -> None:
105138
for r in sorted(requires):
106139
print(r)
107140
else:
108-
for info in sorted(infos):
109-
pprint.pprint(info)
141+
for i, info in enumerate(sorted(infos)):
142+
if i > 0:
143+
print("---")
144+
print(_format_elfinfo(info))
110145

111146

112147
if __name__ == "__main__":

0 commit comments

Comments
 (0)