Gentoo Archives: gentoo-commits

From: Mike Frysinger <vapier@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/pax-utils:master commit in: /
Date: Wed, 28 Sep 2022 07:43:00
Message-Id: 1664350937.1ddedd87363c65d6b910fe32da0f1764ba1329a9.vapier@gentoo
1 commit: 1ddedd87363c65d6b910fe32da0f1764ba1329a9
2 Author: Mike Frysinger <vapier <AT> chromium <DOT> org>
3 AuthorDate: Wed Sep 28 07:39:56 2022 +0000
4 Commit: Mike Frysinger <vapier <AT> gentoo <DOT> org>
5 CommitDate: Wed Sep 28 07:42:17 2022 +0000
6 URL: https://gitweb.gentoo.org/proj/pax-utils.git/commit/?id=1ddedd87
7
8 lddtree: reformat with black
9
10 Largely this is just single quotes -> double quotes.
11
12 Signed-off-by: Mike Frysinger <vapier <AT> gentoo.org>
13
14 lddtree.py | 503 ++++++++++++++++++++++++++++++++++++-------------------------
15 1 file changed, 294 insertions(+), 209 deletions(-)
16
17 diff --git a/lddtree.py b/lddtree.py
18 index d894505..e851ac1 100755
19 --- a/lddtree.py
20 +++ b/lddtree.py
21 @@ -50,7 +50,7 @@ import shutil
22 import sys
23 from typing import Any, Iterable, Optional, Union
24
25 -assert sys.version_info >= (3, 6), f'Python 3.6+ required, but found {sys.version}'
26 +assert sys.version_info >= (3, 6), f"Python 3.6+ required, but found {sys.version}"
27
28 try:
29 import argcomplete
30 @@ -63,12 +63,12 @@ from elftools.elf.elffile import ELFFile
31
32 def warn(msg: Any, prefix: Optional[str] = "warning") -> None:
33 """Write |msg| to stderr with a |prefix| before it"""
34 - print('%s: %s: %s' % (os.path.basename(sys.argv[0]), prefix, msg), file=sys.stderr)
35 + print("%s: %s: %s" % (os.path.basename(sys.argv[0]), prefix, msg), file=sys.stderr)
36
37
38 def err(msg: Any, status: Optional[int] = 1) -> None:
39 """Write |msg| to stderr and exit with |status|"""
40 - warn(msg, prefix='error')
41 + warn(msg, prefix="error")
42 sys.exit(status)
43
44
45 @@ -82,7 +82,7 @@ def bstr(buf: Union[bytes, str]) -> str:
46 """Decode the byte string into a string"""
47 if isinstance(buf, str):
48 return buf
49 - return buf.decode('utf-8')
50 + return buf.decode("utf-8")
51
52
53 def normpath(path: str) -> str:
54 @@ -93,7 +93,7 @@ def normpath(path: str) -> str:
55 //..// -> //
56 //..//..// -> ///
57 """
58 - return os.path.normpath(path).replace('//', '/')
59 + return os.path.normpath(path).replace("//", "/")
60
61
62 @functools.lru_cache(maxsize=None)
63 @@ -115,9 +115,9 @@ def readlink(path: str, root: str, prefixed: Optional[bool] = False) -> str:
64 Returns:
65 A fully resolved symlink path
66 """
67 - root = root.rstrip('/')
68 + root = root.rstrip("/")
69 if prefixed:
70 - path = path[len(root):]
71 + path = path[len(root) :]
72
73 while os.path.islink(root + path):
74 path = os.path.join(os.path.dirname(path), os.readlink(root + path))
75 @@ -137,9 +137,9 @@ def interp_supports_argv0(interp: str) -> bool:
76
77 Starting with glibc-2.33, the ldso supports --argv0 to override argv[0].
78 """
79 - with open(interp, 'rb') as fp:
80 + with open(interp, "rb") as fp:
81 with mmap.mmap(fp.fileno(), 0, prot=mmap.PROT_READ) as mm:
82 - return mm.find(b'--argv0') >= 0
83 + return mm.find(b"--argv0") >= 0
84
85
86 def GenerateLdsoWrapper(
87 @@ -165,12 +165,11 @@ def GenerateLdsoWrapper(
88 # Add ldso interpreter dir to end of libpaths as a fallback library path.
89 libpaths = dedupe(list(libpaths) + [interp_dir])
90 replacements = {
91 - 'interp': os.path.join(os.path.relpath(interp_dir, basedir),
92 - interp_name),
93 + "interp": os.path.join(os.path.relpath(interp_dir, basedir), interp_name),
94 "libpaths": ":".join(
95 "${basedir}/" + os.path.relpath(p, basedir) for p in libpaths
96 ),
97 - 'argv0_arg': '--argv0 "$0"' if interp_supports_argv0(root + interp) else '',
98 + "argv0_arg": '--argv0 "$0"' if interp_supports_argv0(root + interp) else "",
99 }
100 wrapper = """#!/bin/sh
101 if ! base=$(realpath "$0" 2>/dev/null); then
102 @@ -190,8 +189,8 @@ exec \\
103 "$@"
104 """
105 wrappath = root + path
106 - os.rename(wrappath, wrappath + '.elf')
107 - with open(wrappath, 'w', encoding='utf-8') as f:
108 + os.rename(wrappath, wrappath + ".elf")
109 + with open(wrappath, "w", encoding="utf-8") as f:
110 f.write(wrapper % replacements)
111 os.chmod(wrappath, 0o0755)
112
113 @@ -223,17 +222,17 @@ def ParseLdPaths(
114 cwd = os.getcwd()
115
116 ldpaths = []
117 - for ldpath in str_ldpaths.split(':'):
118 + for ldpath in str_ldpaths.split(":"):
119 # Expand placeholders first.
120 - if '$ORIGIN' in ldpath:
121 - ldpath = ldpath.replace('$ORIGIN', os.path.dirname(path))
122 - elif '${ORIGIN}' in ldpath:
123 - ldpath = ldpath.replace('${ORIGIN}', os.path.dirname(path))
124 + if "$ORIGIN" in ldpath:
125 + ldpath = ldpath.replace("$ORIGIN", os.path.dirname(path))
126 + elif "${ORIGIN}" in ldpath:
127 + ldpath = ldpath.replace("${ORIGIN}", os.path.dirname(path))
128
129 # Expand relative paths if needed. These don't make sense in general,
130 # but that doesn't stop people from using them. As such, root prefix
131 # doesn't make sense with it either.
132 - if not ldpath.startswith('/'):
133 + if not ldpath.startswith("/"):
134 # NB: The ldso treats "" paths as cwd too.
135 ldpath = os.path.join(cwd, ldpath)
136 else:
137 @@ -265,27 +264,29 @@ def ParseLdSoConf(
138 """
139 paths = []
140
141 - dbg_pfx = '' if _first else ' '
142 + dbg_pfx = "" if _first else " "
143 try:
144 dbg(debug, f"{dbg_pfx}ParseLdSoConf({ldso_conf})")
145 - with open(ldso_conf, encoding='utf-8') as f:
146 + with open(ldso_conf, encoding="utf-8") as f:
147 for line in f.readlines():
148 - line = line.split('#', 1)[0].strip()
149 + line = line.split("#", 1)[0].strip()
150 if not line:
151 continue
152 - if line.startswith('include '):
153 + if line.startswith("include "):
154 line = line[8:]
155 - if line[0] == '/':
156 - line = root + line.lstrip('/')
157 + if line[0] == "/":
158 + line = root + line.lstrip("/")
159 else:
160 - line = os.path.dirname(ldso_conf) + '/' + line
161 + line = os.path.dirname(ldso_conf) + "/" + line
162 dbg(debug, dbg_pfx, "glob:", line)
163 # ldconfig in glibc uses glob() which returns entries sorted according
164 # to LC_COLLATE. Further, ldconfig does not reset that but respects
165 # the active env settings (which might be a mistake). Python does not
166 # sort its results by default though, so do it ourselves.
167 for path in sorted(glob.glob(line)):
168 - paths += ParseLdSoConf(path, root=root, debug=debug, _first=False)
169 + paths += ParseLdSoConf(
170 + path, root=root, debug=debug, _first=False
171 + )
172 else:
173 paths += [normpath(root + line)]
174 except IOError as e:
175 @@ -320,25 +321,26 @@ def LoadLdpaths(
176 dict containing library paths to search
177 """
178 ldpaths: dict[str, list[str]] = {
179 - 'conf': [],
180 - 'env': [],
181 - 'interp': [],
182 + "conf": [],
183 + "env": [],
184 + "interp": [],
185 }
186
187 # Load up $LD_LIBRARY_PATH.
188 - ldpaths['env'] = []
189 - env_ldpath = os.environ.get('LD_LIBRARY_PATH')
190 + ldpaths["env"] = []
191 + env_ldpath = os.environ.get("LD_LIBRARY_PATH")
192 if not env_ldpath is None:
193 - if root != '/':
194 - warn('ignoring LD_LIBRARY_PATH due to ROOT usage')
195 + if root != "/":
196 + warn("ignoring LD_LIBRARY_PATH due to ROOT usage")
197 else:
198 # XXX: If this contains $ORIGIN, we probably have to parse this
199 # on a per-ELF basis so it can get turned into the right thing.
200 - ldpaths['env'] = ParseLdPaths(env_ldpath, cwd=cwd, path='')
201 + ldpaths["env"] = ParseLdPaths(env_ldpath, cwd=cwd, path="")
202
203 # Load up /etc/ld.so.conf.
204 - ldpaths['conf'] = ParseLdSoConf(root + prefix + '/etc/ld.so.conf', root=root,
205 - debug=debug)
206 + ldpaths["conf"] = ParseLdSoConf(
207 + root + prefix + "/etc/ld.so.conf", root=root, debug=debug
208 + )
209
210 return ldpaths
211
212 @@ -356,14 +358,16 @@ def CompatibleELFs(elf1: ELFFile, elf2: ELFFile) -> bool:
213 Returns:
214 True if compatible, False otherwise
215 """
216 - osabis = frozenset([e.header['e_ident']['EI_OSABI'] for e in (elf1, elf2)])
217 + osabis = frozenset([e.header["e_ident"]["EI_OSABI"] for e in (elf1, elf2)])
218 compat_sets = (
219 frozenset(f"ELFOSABI_{x}" for x in ("NONE", "SYSV", "GNU", "LINUX")),
220 )
221 - return ((len(osabis) == 1 or any(osabis.issubset(x) for x in compat_sets)) and
222 - elf1.elfclass == elf2.elfclass and
223 - elf1.little_endian == elf2.little_endian and
224 - elf1.header['e_machine'] == elf2.header['e_machine'])
225 + return (
226 + (len(osabis) == 1 or any(osabis.issubset(x) for x in compat_sets))
227 + and elf1.elfclass == elf2.elfclass
228 + and elf1.little_endian == elf2.little_endian
229 + and elf1.header["e_machine"] == elf2.header["e_machine"]
230 + )
231
232
233 def FindLib(
234 @@ -393,10 +397,10 @@ def FindLib(
235 if path != target:
236 dbg(debug, " checking:", path, "->", target)
237 else:
238 - dbg(debug, ' checking:', path)
239 + dbg(debug, " checking:", path)
240
241 if os.path.exists(target):
242 - with open(target, 'rb') as f:
243 + with open(target, "rb") as f:
244 try:
245 libelf = ELFFile(f)
246 if CompatibleELFs(elf, libelf):
247 @@ -456,18 +460,18 @@ def ParseELF(
248 _all_libs = {}
249 ldpaths = ldpaths.copy()
250 ret = {
251 - 'interp': None,
252 - 'path': path if display is None else display,
253 - 'realpath': path,
254 - 'needed': [],
255 - 'rpath': [],
256 - 'runpath': [],
257 - 'libs': _all_libs,
258 + "interp": None,
259 + "path": path if display is None else display,
260 + "realpath": path,
261 + "needed": [],
262 + "rpath": [],
263 + "runpath": [],
264 + "libs": _all_libs,
265 }
266
267 dbg(debug, f"ParseELF({path})")
268
269 - with open(path, 'rb') as f:
270 + with open(path, "rb") as f:
271 try:
272 elf = ELFFile(f)
273 except exceptions.ELFParseError:
274 @@ -477,17 +481,17 @@ def ParseELF(
275 # If this is the first ELF, extract the interpreter.
276 if _first:
277 for segment in elf.iter_segments():
278 - if segment.header.p_type != 'PT_INTERP':
279 + if segment.header.p_type != "PT_INTERP":
280 continue
281
282 interp = bstr(segment.get_interp_name())
283 - dbg(debug, ' interp =', interp)
284 - ret['interp'] = normpath(root + interp)
285 - real_interp = readlink(ret['interp'], root, prefixed=True)
286 - ret['libs'][os.path.basename(interp)] = {
287 - 'path': ret['interp'],
288 - 'realpath': real_interp,
289 - 'needed': [],
290 + dbg(debug, " interp =", interp)
291 + ret["interp"] = normpath(root + interp)
292 + real_interp = readlink(ret["interp"], root, prefixed=True)
293 + ret["libs"][os.path.basename(interp)] = {
294 + "path": ret["interp"],
295 + "realpath": real_interp,
296 + "needed": [],
297 }
298 # XXX: Could read it and scan for /lib paths.
299 # If the interp is a symlink, lets follow it on the assumption that it
300 @@ -498,12 +502,16 @@ def ParseELF(
301 # ld64.so.1 is really a symlink to ../lib64/ld64.so.1. In the multiarch
302 # setup, it'll be /lib/ld64.so.1 -> /lib/s390x-linux-gnu/ld64.so.1.
303 # That is why we use |real_interp| here instead of |interp|.
304 - ldpaths['interp'] = [
305 + ldpaths["interp"] = [
306 os.path.dirname(real_interp),
307 - normpath(root + prefix + '/usr/' + os.path.dirname(
308 - real_interp)[len(root) + len(prefix):]),
309 + normpath(
310 + root
311 + + prefix
312 + + "/usr/"
313 + + os.path.dirname(real_interp)[len(root) + len(prefix) :]
314 + ),
315 ]
316 - dbg(debug, ' ldpaths[interp] =', ldpaths['interp'])
317 + dbg(debug, " ldpaths[interp] =", ldpaths["interp"])
318 break
319
320 # Parse the ELF's dynamic tags.
321 @@ -511,15 +519,17 @@ def ParseELF(
322 rpaths = []
323 runpaths = []
324 for segment in elf.iter_segments():
325 - if segment.header.p_type != 'PT_DYNAMIC':
326 + if segment.header.p_type != "PT_DYNAMIC":
327 continue
328
329 for t in segment.iter_tags():
330 - if t.entry.d_tag == 'DT_RPATH':
331 + if t.entry.d_tag == "DT_RPATH":
332 rpaths = ParseLdPaths(bstr(t.rpath), root=root, cwd=cwd, path=path)
333 - elif t.entry.d_tag == 'DT_RUNPATH':
334 - runpaths = ParseLdPaths(bstr(t.runpath), root=root, cwd=cwd, path=path)
335 - elif t.entry.d_tag == 'DT_NEEDED':
336 + elif t.entry.d_tag == "DT_RUNPATH":
337 + runpaths = ParseLdPaths(
338 + bstr(t.runpath), root=root, cwd=cwd, path=path
339 + )
340 + elif t.entry.d_tag == "DT_NEEDED":
341 libs.append(bstr(t.needed))
342 if runpaths:
343 # If both RPATH and RUNPATH are set, only the latter is used.
344 @@ -531,13 +541,13 @@ def ParseELF(
345 if _first:
346 # Propagate the rpaths used by the main ELF since those will be
347 # used at runtime to locate things.
348 - ldpaths['rpath'] = rpaths
349 - ldpaths['runpath'] = runpaths
350 - dbg(debug, ' ldpaths[rpath] =', rpaths)
351 - dbg(debug, ' ldpaths[runpath] =', runpaths)
352 - ret['rpath'] = rpaths
353 - ret['runpath'] = runpaths
354 - ret['needed'] = libs
355 + ldpaths["rpath"] = rpaths
356 + ldpaths["runpath"] = runpaths
357 + dbg(debug, " ldpaths[rpath] =", rpaths)
358 + dbg(debug, " ldpaths[runpath] =", runpaths)
359 + ret["rpath"] = rpaths
360 + ret["runpath"] = runpaths
361 + ret["needed"] = libs
362
363 # Search for the libs this ELF uses.
364 all_ldpaths = None
365 @@ -546,29 +556,42 @@ def ParseELF(
366 continue
367 if all_ldpaths is None:
368 all_ldpaths = (
369 - rpaths + ldpaths['rpath'] +
370 - ldpaths['env'] +
371 - runpaths + ldpaths['runpath'] +
372 - ldpaths['conf'] +
373 - ldpaths['interp']
374 + rpaths
375 + + ldpaths["rpath"]
376 + + ldpaths["env"]
377 + + runpaths
378 + + ldpaths["runpath"]
379 + + ldpaths["conf"]
380 + + ldpaths["interp"]
381 )
382 realpath, fullpath = FindLib(elf, lib, all_ldpaths, root, debug=debug)
383 _all_libs[lib] = {
384 - 'realpath': realpath,
385 - 'path': fullpath,
386 - 'needed': [],
387 + "realpath": realpath,
388 + "path": fullpath,
389 + "needed": [],
390 }
391 if realpath is not None:
392 try:
393 - lret = ParseELF(realpath, root, cwd, prefix, ldpaths, display=fullpath,
394 - debug=debug, _first=False, _all_libs=_all_libs)
395 + lret = ParseELF(
396 + realpath,
397 + root,
398 + cwd,
399 + prefix,
400 + ldpaths,
401 + display=fullpath,
402 + debug=debug,
403 + _first=False,
404 + _all_libs=_all_libs,
405 + )
406 except exceptions.ELFError as e:
407 warn(f"{realpath}: {e}")
408 - _all_libs[lib]['needed'] = lret['needed']
409 + _all_libs[lib]["needed"] = lret["needed"]
410
411 del elf
412
413 return ret
414 +
415 +
416 # pylint: enable=dangerous-default-value
417
418
419 @@ -579,9 +602,10 @@ class _NormalizePathAction(argparse.Action):
420
421 def _ActionShow(options: argparse.Namespace, elf: dict):
422 """Show the dependency tree for this ELF"""
423 +
424 def _show(lib, depth):
425 chain_libs.append(lib)
426 - fullpath = elf['libs'][lib]['path']
427 + fullpath = elf["libs"][lib]["path"]
428 if options.list:
429 print(fullpath or lib)
430 else:
431 @@ -602,10 +626,10 @@ def _ActionShow(options: argparse.Namespace, elf: dict):
432 _show(nlib, depth + 1)
433 chain_libs.pop()
434
435 - shown_libs = set(elf['needed'])
436 - new_libs = elf['needed'][:]
437 + shown_libs = set(elf["needed"])
438 + new_libs = elf["needed"][:]
439 chain_libs: list[str] = []
440 - interp = elf['interp']
441 + interp = elf["interp"]
442 if interp:
443 lib = os.path.basename(interp)
444 shown_libs.add(lib)
445 @@ -617,7 +641,7 @@ def _ActionShow(options: argparse.Namespace, elf: dict):
446 if not options.all and options.list and lib in new_libs:
447 new_libs.remove(lib)
448 if options.list:
449 - print(elf['path'])
450 + print(elf["path"])
451 if not interp is None:
452 print(interp)
453 else:
454 @@ -628,17 +652,17 @@ def _ActionShow(options: argparse.Namespace, elf: dict):
455
456 def _ActionCopy(options: argparse.Namespace, elf: dict):
457 """Copy the ELF and its dependencies to a destination tree"""
458 +
459 def _StripRoot(path: str) -> str:
460 - return path[len(options.root) - 1:]
461 + return path[len(options.root) - 1 :]
462
463 - def _copy(realsrc, src, striproot=True, wrapit=False, libpaths=(),
464 - outdir=None):
465 + def _copy(realsrc, src, striproot=True, wrapit=False, libpaths=(), outdir=None):
466 if realsrc is None:
467 return
468
469 if wrapit:
470 # Static ELFs don't need to be wrapped.
471 - if not elf['interp']:
472 + if not elf["interp"]:
473 wrapit = False
474
475 striproot = _StripRoot if striproot else lambda x: x
476 @@ -651,11 +675,10 @@ def _ActionCopy(options: argparse.Namespace, elf: dict):
477
478 try:
479 # See if they're the same file.
480 - nstat = os.stat(dst + ('.elf' if wrapit else ''))
481 + nstat = os.stat(dst + (".elf" if wrapit else ""))
482 ostat = os.stat(realsrc)
483 - for field in ('mode', 'mtime', 'size'):
484 - if getattr(ostat, 'st_' + field) != \
485 - getattr(nstat, 'st_' + field):
486 + for field in ("mode", "mtime", "size"):
487 + if getattr(ostat, "st_" + field) != getattr(nstat, "st_" + field):
488 break
489 else:
490 return
491 @@ -684,9 +707,9 @@ def _ActionCopy(options: argparse.Namespace, elf: dict):
492 print("generate wrapper", dst)
493
494 if options.libdir:
495 - interp = os.path.join(options.libdir, os.path.basename(elf['interp']))
496 + interp = os.path.join(options.libdir, os.path.basename(elf["interp"]))
497 else:
498 - interp = _StripRoot(elf['interp'])
499 + interp = _StripRoot(elf["interp"])
500 GenerateLdsoWrapper(options.dest, subdst, interp, libpaths)
501
502 # XXX: We should automatically import libgcc_s.so whenever libpthread.so
503 @@ -695,99 +718,153 @@ def _ActionCopy(options: argparse.Namespace, elf: dict):
504 # the libnsl.so and libnss_*.so libraries, as well as an open ended list
505 # for known libs that get loaded (e.g. curl will dlopen(libresolv)).
506 uniq_libpaths = set()
507 - for lib in elf['libs']:
508 - libdata = elf['libs'][lib]
509 - path = libdata['realpath']
510 + for lib in elf["libs"]:
511 + libdata = elf["libs"][lib]
512 + path = libdata["realpath"]
513 if path is None:
514 warn("could not locate library:", lib)
515 continue
516 if not options.libdir:
517 uniq_libpaths.add(_StripRoot(os.path.dirname(path)))
518 - _copy(path, libdata['path'], outdir=options.libdir)
519 + _copy(path, libdata["path"], outdir=options.libdir)
520
521 if not options.libdir:
522 libpaths = list(uniq_libpaths)
523 - if elf['runpath']:
524 - libpaths = elf['runpath'] + libpaths
525 + if elf["runpath"]:
526 + libpaths = elf["runpath"] + libpaths
527 else:
528 - libpaths = elf['rpath'] + libpaths
529 + libpaths = elf["rpath"] + libpaths
530 else:
531 uniq_libpaths.add(options.libdir)
532 libpaths = list(uniq_libpaths)
533
534 # We don't bother to copy this as ParseElf adds the interp to the 'libs',
535 # so it was already copied in the libs loop above.
536 - #_copy(elf['interp'], outdir=options.libdir)
537 - _copy(elf['realpath'], elf['path'], striproot=options.auto_root,
538 - wrapit=options.generate_wrappers, libpaths=libpaths,
539 - outdir=options.bindir)
540 + # _copy(elf['interp'], outdir=options.libdir)
541 + _copy(
542 + elf["realpath"],
543 + elf["path"],
544 + striproot=options.auto_root,
545 + wrapit=options.generate_wrappers,
546 + libpaths=libpaths,
547 + outdir=options.bindir,
548 + )
549
550
551 def GetParser() -> argparse.ArgumentParser:
552 """Get a CLI parser."""
553 parser = argparse.ArgumentParser(
554 - description=__doc__,
555 - formatter_class=argparse.RawDescriptionHelpFormatter)
556 - parser.add_argument('-a', '--all',
557 - action='store_true', default=False,
558 - help='Show all duplicated dependencies')
559 - parser.add_argument('-l', '--list',
560 - action='store_true', default=False,
561 - help='Display output in a simple list (easy for copying)')
562 - parser.add_argument('-x', '--debug',
563 - action='store_true', default=False,
564 - help='Run with debugging')
565 - parser.add_argument('-v', '--verbose',
566 - action='store_true', default=False,
567 - help='Be verbose')
568 - parser.add_argument('--skip-non-elfs',
569 - action='store_true', default=False,
570 - help='Skip plain (non-ELF) files instead of warning')
571 - parser.add_argument('--skip-missing',
572 - action='store_true', default=False,
573 - help='Skip missing files instead of failing')
574 - parser.add_argument('-V', '--version',
575 - action='version',
576 - version='lddtree by Mike Frysinger <vapier@g.o>',
577 - help='Show version information')
578 - parser.add_argument('path', nargs='+')
579 -
580 - group = parser.add_argument_group('Path options')
581 - group.add_argument('-R', '--root',
582 - default=os.environ.get('ROOT', ''), type=str,
583 - action=_NormalizePathAction,
584 - help='Search for all files/dependencies in ROOT')
585 - group.add_argument('--no-auto-root',
586 - dest='auto_root', action='store_false', default=True,
587 - help='Do not automatically prefix input ELFs with ROOT')
588 - group.add_argument('-C', '--cwd',
589 - default=os.getcwd(), type=str, action=_NormalizePathAction,
590 - help='Path to resolve relative paths against')
591 - group.add_argument('-P', '--prefix',
592 - default=os.environ.get(
593 - 'EPREFIX', '@GENTOO_PORTAGE_EPREFIX@'), type=str,
594 - action=_NormalizePathAction,
595 - help='Specify EPREFIX for binaries (for Gentoo Prefix)')
596 -
597 - group = parser.add_argument_group('Copying options')
598 - group.add_argument('--copy-to-tree',
599 - dest='dest', default=None, type=str,
600 - action=_NormalizePathAction,
601 - help='Copy all files to the specified tree')
602 - group.add_argument('--bindir',
603 - default=None, type=str,
604 - action=_NormalizePathAction,
605 - help='Dir to store all ELFs specified on the command line')
606 - group.add_argument('--libdir',
607 - default=None, type=str,
608 - action=_NormalizePathAction,
609 - help='Dir to store all ELF libs')
610 - group.add_argument('--generate-wrappers',
611 - action='store_true', default=False,
612 - help='Wrap executable ELFs with scripts for local ldso')
613 - group.add_argument('--copy-non-elfs',
614 - action='store_true', default=False,
615 - help='Copy over plain (non-ELF) files instead of warn+ignore')
616 + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
617 + )
618 + parser.add_argument(
619 + "-a",
620 + "--all",
621 + action="store_true",
622 + default=False,
623 + help="Show all duplicated dependencies",
624 + )
625 + parser.add_argument(
626 + "-l",
627 + "--list",
628 + action="store_true",
629 + default=False,
630 + help="Display output in a simple list (easy for copying)",
631 + )
632 + parser.add_argument(
633 + "-x", "--debug", action="store_true", default=False, help="Run with debugging"
634 + )
635 + parser.add_argument(
636 + "-v", "--verbose", action="store_true", default=False, help="Be verbose"
637 + )
638 + parser.add_argument(
639 + "--skip-non-elfs",
640 + action="store_true",
641 + default=False,
642 + help="Skip plain (non-ELF) files instead of warning",
643 + )
644 + parser.add_argument(
645 + "--skip-missing",
646 + action="store_true",
647 + default=False,
648 + help="Skip missing files instead of failing",
649 + )
650 + parser.add_argument(
651 + "-V",
652 + "--version",
653 + action="version",
654 + version="lddtree by Mike Frysinger <vapier@g.o>",
655 + help="Show version information",
656 + )
657 + parser.add_argument("path", nargs="+")
658 +
659 + group = parser.add_argument_group("Path options")
660 + group.add_argument(
661 + "-R",
662 + "--root",
663 + default=os.environ.get("ROOT", ""),
664 + type=str,
665 + action=_NormalizePathAction,
666 + help="Search for all files/dependencies in ROOT",
667 + )
668 + group.add_argument(
669 + "--no-auto-root",
670 + dest="auto_root",
671 + action="store_false",
672 + default=True,
673 + help="Do not automatically prefix input ELFs with ROOT",
674 + )
675 + group.add_argument(
676 + "-C",
677 + "--cwd",
678 + default=os.getcwd(),
679 + type=str,
680 + action=_NormalizePathAction,
681 + help="Path to resolve relative paths against",
682 + )
683 + group.add_argument(
684 + "-P",
685 + "--prefix",
686 + default=os.environ.get("EPREFIX", "@GENTOO_PORTAGE_EPREFIX@"),
687 + type=str,
688 + action=_NormalizePathAction,
689 + help="Specify EPREFIX for binaries (for Gentoo Prefix)",
690 + )
691 +
692 + group = parser.add_argument_group("Copying options")
693 + group.add_argument(
694 + "--copy-to-tree",
695 + dest="dest",
696 + default=None,
697 + type=str,
698 + action=_NormalizePathAction,
699 + help="Copy all files to the specified tree",
700 + )
701 + group.add_argument(
702 + "--bindir",
703 + default=None,
704 + type=str,
705 + action=_NormalizePathAction,
706 + help="Dir to store all ELFs specified on the command line",
707 + )
708 + group.add_argument(
709 + "--libdir",
710 + default=None,
711 + type=str,
712 + action=_NormalizePathAction,
713 + help="Dir to store all ELF libs",
714 + )
715 + group.add_argument(
716 + "--generate-wrappers",
717 + action="store_true",
718 + default=False,
719 + help="Wrap executable ELFs with scripts for local ldso",
720 + )
721 + group.add_argument(
722 + "--copy-non-elfs",
723 + action="store_true",
724 + default=False,
725 + help="Copy over plain (non-ELF) files instead of warn+ignore",
726 + )
727
728 if argcomplete is not None:
729 argcomplete.autocomplete(parser)
730 @@ -800,41 +877,42 @@ def main(argv: list[str]) -> Optional[int]:
731 options = parser.parse_args(argv)
732 paths = options.path
733
734 - if options.root != '/':
735 - options.root += '/'
736 - if options.prefix == '@''GENTOO_PORTAGE_EPREFIX''@':
737 - options.prefix = ''
738 + if options.root != "/":
739 + options.root += "/"
740 + if options.prefix == "@" "GENTOO_PORTAGE_EPREFIX" "@":
741 + options.prefix = ""
742
743 - if options.bindir and options.bindir[0] != '/':
744 - parser.error('--bindir accepts absolute paths only')
745 - if options.libdir and options.libdir[0] != '/':
746 - parser.error('--libdir accepts absolute paths only')
747 + if options.bindir and options.bindir[0] != "/":
748 + parser.error("--bindir accepts absolute paths only")
749 + if options.libdir and options.libdir[0] != "/":
750 + parser.error("--libdir accepts absolute paths only")
751
752 if options.skip_non_elfs and options.copy_non_elfs:
753 - parser.error('pick one handler for non-ELFs: skip or copy')
754 + parser.error("pick one handler for non-ELFs: skip or copy")
755
756 - dbg(options.debug, 'root =', options.root)
757 - dbg(options.debug, 'cwd =', options.cwd)
758 + dbg(options.debug, "root =", options.root)
759 + dbg(options.debug, "cwd =", options.cwd)
760 if options.dest:
761 - dbg(options.debug, 'dest =', options.dest)
762 + dbg(options.debug, "dest =", options.dest)
763 if not paths:
764 - err('missing ELF files to scan')
765 + err("missing ELF files to scan")
766
767 - ldpaths = LoadLdpaths(options.root, cwd=options.cwd, prefix=options.prefix,
768 - debug=options.debug)
769 - dbg(options.debug, 'ldpaths[conf] =', ldpaths['conf'])
770 - dbg(options.debug, 'ldpaths[env] =', ldpaths['env'])
771 + ldpaths = LoadLdpaths(
772 + options.root, cwd=options.cwd, prefix=options.prefix, debug=options.debug
773 + )
774 + dbg(options.debug, "ldpaths[conf] =", ldpaths["conf"])
775 + dbg(options.debug, "ldpaths[env] =", ldpaths["env"])
776
777 # Process all the files specified.
778 ret = 0
779 for path in paths:
780 - dbg(options.debug, 'argv[x] =', path)
781 + dbg(options.debug, "argv[x] =", path)
782 # Only auto-prefix the path if the ELF is absolute.
783 # If it's a relative path, the user most likely wants
784 # the local path.
785 - if options.auto_root and path.startswith('/'):
786 - path = options.root + path.lstrip('/')
787 - dbg(options.debug, ' +auto-root =', path)
788 + if options.auto_root and path.startswith("/"):
789 + path = options.root + path.lstrip("/")
790 + dbg(options.debug, " +auto-root =", path)
791
792 matched = False
793 for p in glob.iglob(path):
794 @@ -844,20 +922,27 @@ def main(argv: list[str]) -> Optional[int]:
795 # $ lddtree --root $PWD/root /bin/sh
796 # First we'd turn /bin/sh into $PWD/root/bin/sh, then we want to resolve
797 # the symlink to $PWD/root/bin/bash rather than a plain /bin/bash.
798 - dbg(options.debug, ' globbed =', p)
799 - if not path.startswith('/'):
800 + dbg(options.debug, " globbed =", p)
801 + if not path.startswith("/"):
802 realpath = os.path.realpath(path)
803 elif options.auto_root:
804 realpath = readlink(p, options.root, prefixed=True)
805 else:
806 realpath = path
807 if path != realpath:
808 - dbg(options.debug, ' resolved =', realpath)
809 + dbg(options.debug, " resolved =", realpath)
810
811 matched = True
812 try:
813 - elf = ParseELF(realpath, options.root, options.cwd, options.prefix, ldpaths,
814 - display=p, debug=options.debug)
815 + elf = ParseELF(
816 + realpath,
817 + options.root,
818 + options.cwd,
819 + options.prefix,
820 + ldpaths,
821 + display=p,
822 + debug=options.debug,
823 + )
824 except exceptions.ELFError as e:
825 if options.skip_non_elfs:
826 continue
827 @@ -865,12 +950,12 @@ def main(argv: list[str]) -> Optional[int]:
828 if options.dest is not None and options.copy_non_elfs:
829 if os.path.exists(p):
830 elf = {
831 - 'interp': None,
832 - 'libs': [],
833 - 'runpath': [],
834 - 'rpath': [],
835 - 'path': p,
836 - 'realpath': realpath,
837 + "interp": None,
838 + "libs": [],
839 + "runpath": [],
840 + "rpath": [],
841 + "path": p,
842 + "realpath": realpath,
843 }
844 _ActionCopy(options, elf)
845 continue
846 @@ -895,5 +980,5 @@ def main(argv: list[str]) -> Optional[int]:
847 return ret
848
849
850 -if __name__ == '__main__':
851 +if __name__ == "__main__":
852 sys.exit(main(sys.argv[1:]))