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: Fri, 16 Apr 2021 00:48:52
Message-Id: 1618528224.9a9adbf228320fdc135ad1457bb8bf586c71279c.vapier@gentoo
1 commit: 9a9adbf228320fdc135ad1457bb8bf586c71279c
2 Author: Mike Frysinger <vapier <AT> chromium <DOT> org>
3 AuthorDate: Thu Mar 26 18:32:57 2020 +0000
4 Commit: Mike Frysinger <vapier <AT> gentoo <DOT> org>
5 CommitDate: Thu Apr 15 23:10:24 2021 +0000
6 URL: https://gitweb.gentoo.org/proj/pax-utils.git/commit/?id=9a9adbf2
7
8 pylintrc: adjust python code to 4 space indent
9
10 This aligns with the latest Google/PEP standards.
11 This doesn't add any pylint warnings as we've been disabling long
12 lines in here for a long time. We don't do any other reformatting
13 to try and cut down on git log/diff noise.
14
15 Looking at this with --word-diff=color shows only whitespace changes.
16
17 Signed-off-by: Mike Frysinger <vapier <AT> gentoo.org>
18
19 .pylintrc | 2 +-
20 lddtree.py | 1270 ++++++++++++++++++++++++++++++------------------------------
21 pylint | 46 +--
22 3 files changed, 659 insertions(+), 659 deletions(-)
23
24 diff --git a/.pylintrc b/.pylintrc
25 index cf1379d..577641f 100644
26 --- a/.pylintrc
27 +++ b/.pylintrc
28 @@ -36,7 +36,7 @@ score=no
29
30 [FORMAT]
31 max-line-length=80
32 -indent-string=' '
33 +indent-string = ' '
34
35 [BASIC]
36 bad-functions=
37
38 diff --git a/lddtree.py b/lddtree.py
39 index 141195b..cdb3f1c 100755
40 --- a/lddtree.py
41 +++ b/lddtree.py
42 @@ -55,107 +55,107 @@ from elftools.common import exceptions
43
44
45 def warn(msg, prefix='warning'):
46 - """Write |msg| to stderr with a |prefix| before it"""
47 - print('%s: %s: %s' % (os.path.basename(sys.argv[0]), prefix, msg), file=sys.stderr)
48 + """Write |msg| to stderr with a |prefix| before it"""
49 + print('%s: %s: %s' % (os.path.basename(sys.argv[0]), prefix, msg), file=sys.stderr)
50
51
52 def err(msg, status=1):
53 - """Write |msg| to stderr and exit with |status|"""
54 - warn(msg, prefix='error')
55 - sys.exit(status)
56 + """Write |msg| to stderr and exit with |status|"""
57 + warn(msg, prefix='error')
58 + sys.exit(status)
59
60
61 def dbg(debug, *args, **kwargs):
62 - """Pass |args| and |kwargs| to print() when |debug| is True"""
63 - if debug:
64 - print(*args, **kwargs)
65 + """Pass |args| and |kwargs| to print() when |debug| is True"""
66 + if debug:
67 + print(*args, **kwargs)
68
69
70 def bstr(buf):
71 - """Decode the byte string into a string"""
72 - if isinstance(buf, str):
73 - return buf
74 - return buf.decode('utf-8')
75 + """Decode the byte string into a string"""
76 + if isinstance(buf, str):
77 + return buf
78 + return buf.decode('utf-8')
79
80
81 def normpath(path):
82 - """Normalize a path
83 + """Normalize a path
84
85 - Python's os.path.normpath() doesn't handle some cases:
86 - // -> //
87 - //..// -> //
88 - //..//..// -> ///
89 - """
90 - return os.path.normpath(path).replace('//', '/')
91 + Python's os.path.normpath() doesn't handle some cases:
92 + // -> //
93 + //..// -> //
94 + //..//..// -> ///
95 + """
96 + return os.path.normpath(path).replace('//', '/')
97
98
99 def readlink(path, root, prefixed=False):
100 - """Like os.readlink(), but relative to a |root|
101 + """Like os.readlink(), but relative to a |root|
102
103 - This does not currently handle the pathological case:
104 - /lib/foo.so -> ../../../../../../../foo.so
105 - This relies on the .. entries in / to point to itself.
106 + This does not currently handle the pathological case:
107 + /lib/foo.so -> ../../../../../../../foo.so
108 + This relies on the .. entries in / to point to itself.
109
110 - Args:
111 - path: The symlink to read
112 - root: The path to use for resolving absolute symlinks
113 - prefixed: When False, the |path| must not have |root| prefixed to it, nor
114 - will the return value have |root| prefixed. When True, |path|
115 - must have |root| prefixed, and the return value will have |root|
116 - added.
117 + Args:
118 + path: The symlink to read
119 + root: The path to use for resolving absolute symlinks
120 + prefixed: When False, the |path| must not have |root| prefixed to it, nor
121 + will the return value have |root| prefixed. When True, |path|
122 + must have |root| prefixed, and the return value will have |root|
123 + added.
124
125 - Returns:
126 - A fully resolved symlink path
127 - """
128 - root = root.rstrip('/')
129 - if prefixed:
130 - path = path[len(root):]
131 + Returns:
132 + A fully resolved symlink path
133 + """
134 + root = root.rstrip('/')
135 + if prefixed:
136 + path = path[len(root):]
137
138 - while os.path.islink(root + path):
139 - path = os.path.join(os.path.dirname(path), os.readlink(root + path))
140 + while os.path.islink(root + path):
141 + path = os.path.join(os.path.dirname(path), os.readlink(root + path))
142
143 - return normpath((root + path) if prefixed else path)
144 + return normpath((root + path) if prefixed else path)
145
146
147 def makedirs(path):
148 - """Like os.makedirs(), but ignore EEXIST errors"""
149 - try:
150 - os.makedirs(path)
151 - except OSError as e:
152 - if e.errno != errno.EEXIST:
153 - raise
154 + """Like os.makedirs(), but ignore EEXIST errors"""
155 + try:
156 + os.makedirs(path)
157 + except OSError as e:
158 + if e.errno != errno.EEXIST:
159 + raise
160
161
162 def dedupe(items):
163 - """Remove all duplicates from |items| (keeping order)"""
164 - seen = {}
165 - return [seen.setdefault(x, x) for x in items if x not in seen]
166 + """Remove all duplicates from |items| (keeping order)"""
167 + seen = {}
168 + return [seen.setdefault(x, x) for x in items if x not in seen]
169
170
171 def GenerateLdsoWrapper(root, path, interp, libpaths=()):
172 - """Generate a shell script wrapper which uses local ldso to run the ELF
173 -
174 - Since we cannot rely on the host glibc (or other libraries), we need to
175 - execute the local packaged ldso directly and tell it where to find our
176 - copies of libraries.
177 -
178 - Args:
179 - root: The root tree to generate scripts inside of
180 - path: The full path (inside |root|) to the program to wrap
181 - interp: The ldso interpreter that we need to execute
182 - libpaths: Extra lib paths to search for libraries
183 - """
184 - basedir = os.path.dirname(path)
185 - interp_dir, interp_name = os.path.split(interp)
186 - # Add ldso interpreter dir to end of libpaths as a fallback library path.
187 - libpaths = dedupe(list(libpaths) + [interp_dir])
188 - replacements = {
189 - 'interp': os.path.join(os.path.relpath(interp_dir, basedir),
190 - interp_name),
191 - 'libpaths': ':'.join(['${basedir}/' + os.path.relpath(p, basedir)
192 - for p in libpaths]),
193 - }
194 - wrapper = """#!/bin/sh
195 + """Generate a shell script wrapper which uses local ldso to run the ELF
196 +
197 + Since we cannot rely on the host glibc (or other libraries), we need to
198 + execute the local packaged ldso directly and tell it where to find our
199 + copies of libraries.
200 +
201 + Args:
202 + root: The root tree to generate scripts inside of
203 + path: The full path (inside |root|) to the program to wrap
204 + interp: The ldso interpreter that we need to execute
205 + libpaths: Extra lib paths to search for libraries
206 + """
207 + basedir = os.path.dirname(path)
208 + interp_dir, interp_name = os.path.split(interp)
209 + # Add ldso interpreter dir to end of libpaths as a fallback library path.
210 + libpaths = dedupe(list(libpaths) + [interp_dir])
211 + replacements = {
212 + 'interp': os.path.join(os.path.relpath(interp_dir, basedir),
213 + interp_name),
214 + 'libpaths': ':'.join(['${basedir}/' + os.path.relpath(p, basedir)
215 + for p in libpaths]),
216 + }
217 + wrapper = """#!/bin/sh
218 if ! base=$(realpath "$0" 2>/dev/null); then
219 case $0 in
220 /*) base=$0;;
221 @@ -171,635 +171,635 @@ exec \
222 "${base}.elf" \
223 "$@"
224 """
225 - wrappath = root + path
226 - os.rename(wrappath, wrappath + '.elf')
227 - with open(wrappath, 'w') as f:
228 - f.write(wrapper % replacements)
229 - os.chmod(wrappath, 0o0755)
230 + wrappath = root + path
231 + os.rename(wrappath, wrappath + '.elf')
232 + with open(wrappath, 'w') as f:
233 + f.write(wrapper % replacements)
234 + os.chmod(wrappath, 0o0755)
235
236
237 def ParseLdPaths(str_ldpaths, root='', path=None):
238 - """Parse the colon-delimited list of paths and apply ldso rules to each
239 -
240 - Note the special handling as dictated by the ldso:
241 - - Empty paths are equivalent to $PWD
242 - - $ORIGIN is expanded to the path of the given file
243 - - (TODO) $LIB and friends
244 -
245 - Args:
246 - str_ldpaths: A colon-delimited string of paths
247 - root: The path to prepend to all paths found
248 - path: The object actively being parsed (used for $ORIGIN)
249 -
250 - Returns:
251 - list of processed paths
252 - """
253 - ldpaths = []
254 - for ldpath in str_ldpaths.split(':'):
255 - if not ldpath:
256 - # The ldso treats "" paths as $PWD.
257 - ldpath = os.getcwd()
258 - elif '$ORIGIN' in ldpath:
259 - ldpath = ldpath.replace('$ORIGIN', os.path.dirname(path))
260 - else:
261 - ldpath = root + ldpath
262 - ldpaths.append(normpath(ldpath))
263 - return dedupe(ldpaths)
264 + """Parse the colon-delimited list of paths and apply ldso rules to each
265 +
266 + Note the special handling as dictated by the ldso:
267 + - Empty paths are equivalent to $PWD
268 + - $ORIGIN is expanded to the path of the given file
269 + - (TODO) $LIB and friends
270 +
271 + Args:
272 + str_ldpaths: A colon-delimited string of paths
273 + root: The path to prepend to all paths found
274 + path: The object actively being parsed (used for $ORIGIN)
275 +
276 + Returns:
277 + list of processed paths
278 + """
279 + ldpaths = []
280 + for ldpath in str_ldpaths.split(':'):
281 + if not ldpath:
282 + # The ldso treats "" paths as $PWD.
283 + ldpath = os.getcwd()
284 + elif '$ORIGIN' in ldpath:
285 + ldpath = ldpath.replace('$ORIGIN', os.path.dirname(path))
286 + else:
287 + ldpath = root + ldpath
288 + ldpaths.append(normpath(ldpath))
289 + return dedupe(ldpaths)
290
291
292 def ParseLdSoConf(ldso_conf, root='/', debug=False, _first=True):
293 - """Load all the paths from a given ldso config file
294 -
295 - This should handle comments, whitespace, and "include" statements.
296 -
297 - Args:
298 - ldso_conf: The file to scan
299 - root: The path to prepend to all paths found
300 - debug: Enable debug output
301 - _first: Recursive use only; is this the first ELF ?
302 -
303 - Returns:
304 - list of paths found
305 - """
306 - paths = []
307 -
308 - dbg_pfx = '' if _first else ' '
309 - try:
310 - dbg(debug, '%sParseLdSoConf(%s)' % (dbg_pfx, ldso_conf))
311 - with open(ldso_conf) as f:
312 - for line in f.readlines():
313 - line = line.split('#', 1)[0].strip()
314 - if not line:
315 - continue
316 - if line.startswith('include '):
317 - line = line[8:]
318 - if line[0] == '/':
319 - line = root + line.lstrip('/')
320 - else:
321 - line = os.path.dirname(ldso_conf) + '/' + line
322 - dbg(debug, '%s glob: %s' % (dbg_pfx, line))
323 - # ldconfig in glibc uses glob() which returns entries sorted according
324 - # to LC_COLLATE. Further, ldconfig does not reset that but respects
325 - # the active env settings (which might be a mistake). Python does not
326 - # sort its results by default though, so do it ourselves.
327 - for path in sorted(glob.glob(line)):
328 - paths += ParseLdSoConf(path, root=root, debug=debug, _first=False)
329 - else:
330 - paths += [normpath(root + line)]
331 - except IOError as e:
332 - if e.errno != errno.ENOENT:
333 - warn(e)
334 + """Load all the paths from a given ldso config file
335 +
336 + This should handle comments, whitespace, and "include" statements.
337
338 - if _first:
339 - # XXX: Load paths from ldso itself.
340 - # Remove duplicate entries to speed things up.
341 - paths = dedupe(paths)
342 + Args:
343 + ldso_conf: The file to scan
344 + root: The path to prepend to all paths found
345 + debug: Enable debug output
346 + _first: Recursive use only; is this the first ELF ?
347
348 - return paths
349 + Returns:
350 + list of paths found
351 + """
352 + paths = []
353 +
354 + dbg_pfx = '' if _first else ' '
355 + try:
356 + dbg(debug, '%sParseLdSoConf(%s)' % (dbg_pfx, ldso_conf))
357 + with open(ldso_conf) as f:
358 + for line in f.readlines():
359 + line = line.split('#', 1)[0].strip()
360 + if not line:
361 + continue
362 + if line.startswith('include '):
363 + line = line[8:]
364 + if line[0] == '/':
365 + line = root + line.lstrip('/')
366 + else:
367 + line = os.path.dirname(ldso_conf) + '/' + line
368 + dbg(debug, '%s glob: %s' % (dbg_pfx, line))
369 + # ldconfig in glibc uses glob() which returns entries sorted according
370 + # to LC_COLLATE. Further, ldconfig does not reset that but respects
371 + # the active env settings (which might be a mistake). Python does not
372 + # sort its results by default though, so do it ourselves.
373 + for path in sorted(glob.glob(line)):
374 + paths += ParseLdSoConf(path, root=root, debug=debug, _first=False)
375 + else:
376 + paths += [normpath(root + line)]
377 + except IOError as e:
378 + if e.errno != errno.ENOENT:
379 + warn(e)
380 +
381 + if _first:
382 + # XXX: Load paths from ldso itself.
383 + # Remove duplicate entries to speed things up.
384 + paths = dedupe(paths)
385 +
386 + return paths
387
388
389 def LoadLdpaths(root='/', prefix='', debug=False):
390 - """Load linker paths from common locations
391 -
392 - This parses the ld.so.conf and LD_LIBRARY_PATH env var.
393 -
394 - Args:
395 - root: The root tree to prepend to paths
396 - prefix: The path under |root| to search
397 - debug: Enable debug output
398 -
399 - Returns:
400 - dict containing library paths to search
401 - """
402 - ldpaths = {
403 - 'conf': [],
404 - 'env': [],
405 - 'interp': [],
406 - }
407 -
408 - # Load up $LD_LIBRARY_PATH.
409 - ldpaths['env'] = []
410 - env_ldpath = os.environ.get('LD_LIBRARY_PATH')
411 - if not env_ldpath is None:
412 - if root != '/':
413 - warn('ignoring LD_LIBRARY_PATH due to ROOT usage')
414 - else:
415 - # XXX: If this contains $ORIGIN, we probably have to parse this
416 - # on a per-ELF basis so it can get turned into the right thing.
417 - ldpaths['env'] = ParseLdPaths(env_ldpath, path='')
418 + """Load linker paths from common locations
419 +
420 + This parses the ld.so.conf and LD_LIBRARY_PATH env var.
421 +
422 + Args:
423 + root: The root tree to prepend to paths
424 + prefix: The path under |root| to search
425 + debug: Enable debug output
426 +
427 + Returns:
428 + dict containing library paths to search
429 + """
430 + ldpaths = {
431 + 'conf': [],
432 + 'env': [],
433 + 'interp': [],
434 + }
435
436 - # Load up /etc/ld.so.conf.
437 - ldpaths['conf'] = ParseLdSoConf(root + prefix + '/etc/ld.so.conf', root=root,
438 - debug=debug)
439 + # Load up $LD_LIBRARY_PATH.
440 + ldpaths['env'] = []
441 + env_ldpath = os.environ.get('LD_LIBRARY_PATH')
442 + if not env_ldpath is None:
443 + if root != '/':
444 + warn('ignoring LD_LIBRARY_PATH due to ROOT usage')
445 + else:
446 + # XXX: If this contains $ORIGIN, we probably have to parse this
447 + # on a per-ELF basis so it can get turned into the right thing.
448 + ldpaths['env'] = ParseLdPaths(env_ldpath, path='')
449 +
450 + # Load up /etc/ld.so.conf.
451 + ldpaths['conf'] = ParseLdSoConf(root + prefix + '/etc/ld.so.conf', root=root,
452 + debug=debug)
453
454 - return ldpaths
455 + return ldpaths
456
457
458 def CompatibleELFs(elf1, elf2):
459 - """See if two ELFs are compatible
460 + """See if two ELFs are compatible
461
462 - This compares the aspects of the ELF to see if they're compatible:
463 - bit size, endianness, machine type, and operating system.
464 + This compares the aspects of the ELF to see if they're compatible:
465 + bit size, endianness, machine type, and operating system.
466
467 - Args:
468 - elf1: an ELFFile object
469 - elf2: an ELFFile object
470 + Args:
471 + elf1: an ELFFile object
472 + elf2: an ELFFile object
473
474 - Returns:
475 - True if compatible, False otherwise
476 - """
477 - osabis = frozenset([e.header['e_ident']['EI_OSABI'] for e in (elf1, elf2)])
478 - compat_sets = (
479 - frozenset('ELFOSABI_%s' % x for x in ('NONE', 'SYSV', 'GNU', 'LINUX',)),
480 - )
481 - return ((len(osabis) == 1 or any(osabis.issubset(x) for x in compat_sets)) and
482 - elf1.elfclass == elf2.elfclass and
483 - elf1.little_endian == elf2.little_endian and
484 - elf1.header['e_machine'] == elf2.header['e_machine'])
485 + Returns:
486 + True if compatible, False otherwise
487 + """
488 + osabis = frozenset([e.header['e_ident']['EI_OSABI'] for e in (elf1, elf2)])
489 + compat_sets = (
490 + frozenset('ELFOSABI_%s' % x for x in ('NONE', 'SYSV', 'GNU', 'LINUX',)),
491 + )
492 + return ((len(osabis) == 1 or any(osabis.issubset(x) for x in compat_sets)) and
493 + elf1.elfclass == elf2.elfclass and
494 + elf1.little_endian == elf2.little_endian and
495 + elf1.header['e_machine'] == elf2.header['e_machine'])
496
497
498 def FindLib(elf, lib, ldpaths, root='/', debug=False):
499 - """Try to locate a |lib| that is compatible to |elf| in the given |ldpaths|
500 -
501 - Args:
502 - elf: The elf which the library should be compatible with (ELF wise)
503 - lib: The library (basename) to search for
504 - ldpaths: A list of paths to search
505 - root: The root path to resolve symlinks
506 - debug: Enable debug output
507 -
508 - Returns:
509 - Tuple of the full path to the desired library and the real path to it
510 - """
511 - dbg(debug, ' FindLib(%s)' % lib)
512 -
513 - for ldpath in ldpaths:
514 - path = os.path.join(ldpath, lib)
515 - target = readlink(path, root, prefixed=True)
516 - if path != target:
517 - dbg(debug, ' checking: %s -> %s' % (path, target))
518 - else:
519 - dbg(debug, ' checking:', path)
520 + """Try to locate a |lib| that is compatible to |elf| in the given |ldpaths|
521 +
522 + Args:
523 + elf: The elf which the library should be compatible with (ELF wise)
524 + lib: The library (basename) to search for
525 + ldpaths: A list of paths to search
526 + root: The root path to resolve symlinks
527 + debug: Enable debug output
528 +
529 + Returns:
530 + Tuple of the full path to the desired library and the real path to it
531 + """
532 + dbg(debug, ' FindLib(%s)' % lib)
533 +
534 + for ldpath in ldpaths:
535 + path = os.path.join(ldpath, lib)
536 + target = readlink(path, root, prefixed=True)
537 + if path != target:
538 + dbg(debug, ' checking: %s -> %s' % (path, target))
539 + else:
540 + dbg(debug, ' checking:', path)
541
542 - if os.path.exists(target):
543 - with open(target, 'rb') as f:
544 - try:
545 - libelf = ELFFile(f)
546 - if CompatibleELFs(elf, libelf):
547 - return (target, path)
548 - except exceptions.ELFError as e:
549 - warn('%s: %s' % (target, e))
550 + if os.path.exists(target):
551 + with open(target, 'rb') as f:
552 + try:
553 + libelf = ELFFile(f)
554 + if CompatibleELFs(elf, libelf):
555 + return (target, path)
556 + except exceptions.ELFError as e:
557 + warn('%s: %s' % (target, e))
558
559 - return (None, None)
560 + return (None, None)
561
562
563 # We abuse the _all_libs state. We probably shouldn't, but we do currently.
564 # pylint: disable=dangerous-default-value
565 def ParseELF(path, root='/', prefix='', ldpaths={'conf':[], 'env':[], 'interp':[]},
566 display=None, debug=False, _first=True, _all_libs={}):
567 - """Parse the ELF dependency tree of the specified file
568 + """Parse the ELF dependency tree of the specified file
569
570 - Args:
571 - path: The ELF to scan
572 - root: The root tree to prepend to paths; this applies to interp and rpaths
573 + Args:
574 + path: The ELF to scan
575 + root: The root tree to prepend to paths; this applies to interp and rpaths
576 only as |path| and |ldpaths| are expected to be prefixed already
577 - prefix: The path under |root| to search
578 - ldpaths: dict containing library paths to search; should have the keys:
579 - conf, env, interp
580 - display: The path to show rather than |path|
581 - debug: Enable debug output
582 - _first: Recursive use only; is this the first ELF ?
583 - _all_libs: Recursive use only; dict of all libs we've seen
584 -
585 - Returns:
586 - a dict containing information about all the ELFs; e.g.
587 - {
588 - 'interp': '/lib64/ld-linux.so.2',
589 - 'needed': ['libc.so.6', 'libcurl.so.4',],
590 - 'libs': {
591 - 'libc.so.6': {
592 - 'path': '/lib64/libc.so.6',
593 - 'needed': [],
594 - },
595 - 'libcurl.so.4': {
596 - 'path': '/usr/lib64/libcurl.so.4',
597 - 'needed': ['libc.so.6', 'librt.so.1',],
598 + prefix: The path under |root| to search
599 + ldpaths: dict containing library paths to search; should have the keys:
600 + conf, env, interp
601 + display: The path to show rather than |path|
602 + debug: Enable debug output
603 + _first: Recursive use only; is this the first ELF ?
604 + _all_libs: Recursive use only; dict of all libs we've seen
605 +
606 + Returns:
607 + a dict containing information about all the ELFs; e.g.
608 + {
609 + 'interp': '/lib64/ld-linux.so.2',
610 + 'needed': ['libc.so.6', 'libcurl.so.4',],
611 + 'libs': {
612 + 'libc.so.6': {
613 + 'path': '/lib64/libc.so.6',
614 + 'needed': [],
615 + },
616 + 'libcurl.so.4': {
617 + 'path': '/usr/lib64/libcurl.so.4',
618 + 'needed': ['libc.so.6', 'librt.so.1',],
619 + },
620 },
621 - },
622 - }
623 - """
624 - if _first:
625 - _all_libs = {}
626 - ldpaths = ldpaths.copy()
627 - ret = {
628 - 'interp': None,
629 - 'path': path if display is None else display,
630 - 'realpath': path,
631 - 'needed': [],
632 - 'rpath': [],
633 - 'runpath': [],
634 - 'libs': _all_libs,
635 - }
636 -
637 - dbg(debug, 'ParseELF(%s)' % path)
638 -
639 - with open(path, 'rb') as f:
640 - elf = ELFFile(f)
641 -
642 - # If this is the first ELF, extract the interpreter.
643 + }
644 + """
645 if _first:
646 - for segment in elf.iter_segments():
647 - if segment.header.p_type != 'PT_INTERP':
648 - continue
649 -
650 - interp = bstr(segment.get_interp_name())
651 - dbg(debug, ' interp =', interp)
652 - ret['interp'] = normpath(root + interp)
653 - real_interp = readlink(ret['interp'], root, prefixed=True)
654 - ret['libs'][os.path.basename(interp)] = {
655 - 'path': ret['interp'],
656 - 'realpath': real_interp,
657 - 'needed': [],
658 - }
659 - # XXX: Could read it and scan for /lib paths.
660 - # If the interp is a symlink, lets follow it on the assumption that it
661 - # is in this path purely for ABI reasons, and the distro is using a
662 - # different (probably more correct) path. This can come up in some
663 - # multilib situations like s390x where /lib64/ contains all the native
664 - # libraries, but /lib/ld64.so.1 is the interp hardcoded in gcc, so the
665 - # ld64.so.1 is really a symlink to ../lib64/ld64.so.1. In the multiarch
666 - # setup, it'll be /lib/ld64.so.1 -> /lib/s390x-linux-gnu/ld64.so.1.
667 - # That is why we use |real_interp| here instead of |interp|.
668 - ldpaths['interp'] = [
669 - os.path.dirname(real_interp),
670 - normpath(root + prefix + '/usr/' + os.path.dirname(
671 - real_interp)[len(root) + len(prefix):]),
672 - ]
673 - dbg(debug, ' ldpaths[interp] =', ldpaths['interp'])
674 - break
675 -
676 - # Parse the ELF's dynamic tags.
677 - libs = []
678 - rpaths = []
679 - runpaths = []
680 - for segment in elf.iter_segments():
681 - if segment.header.p_type != 'PT_DYNAMIC':
682 - continue
683 -
684 - for t in segment.iter_tags():
685 - if t.entry.d_tag == 'DT_RPATH':
686 - rpaths = ParseLdPaths(bstr(t.rpath), root=root, path=path)
687 - elif t.entry.d_tag == 'DT_RUNPATH':
688 - runpaths = ParseLdPaths(bstr(t.runpath), root=root, path=path)
689 - elif t.entry.d_tag == 'DT_NEEDED':
690 - libs.append(bstr(t.needed))
691 - if runpaths:
692 - # If both RPATH and RUNPATH are set, only the latter is used.
693 - rpaths = []
694 + _all_libs = {}
695 + ldpaths = ldpaths.copy()
696 + ret = {
697 + 'interp': None,
698 + 'path': path if display is None else display,
699 + 'realpath': path,
700 + 'needed': [],
701 + 'rpath': [],
702 + 'runpath': [],
703 + 'libs': _all_libs,
704 + }
705
706 - # XXX: We assume there is only one PT_DYNAMIC. This is
707 - # probably fine since the runtime ldso does the same.
708 - break
709 - if _first:
710 - # Propagate the rpaths used by the main ELF since those will be
711 - # used at runtime to locate things.
712 - ldpaths['rpath'] = rpaths
713 - ldpaths['runpath'] = runpaths
714 - dbg(debug, ' ldpaths[rpath] =', rpaths)
715 - dbg(debug, ' ldpaths[runpath] =', runpaths)
716 - ret['rpath'] = rpaths
717 - ret['runpath'] = runpaths
718 - ret['needed'] = libs
719 -
720 - # Search for the libs this ELF uses.
721 - all_ldpaths = None
722 - for lib in libs:
723 - if lib in _all_libs:
724 - continue
725 - if all_ldpaths is None:
726 - all_ldpaths = rpaths + ldpaths['rpath'] + ldpaths['env'] + runpaths + ldpaths['runpath'] + ldpaths['conf'] + ldpaths['interp']
727 - realpath, fullpath = FindLib(elf, lib, all_ldpaths, root, debug=debug)
728 - _all_libs[lib] = {
729 - 'realpath': realpath,
730 - 'path': fullpath,
731 - 'needed': [],
732 - }
733 - if fullpath:
734 - try:
735 - lret = ParseELF(realpath, root, prefix, ldpaths, display=fullpath,
736 - debug=debug, _first=False, _all_libs=_all_libs)
737 - except exceptions.ELFError as e:
738 - warn('%s: %s' % (realpath, e))
739 - _all_libs[lib]['needed'] = lret['needed']
740 + dbg(debug, 'ParseELF(%s)' % path)
741 +
742 + with open(path, 'rb') as f:
743 + elf = ELFFile(f)
744 +
745 + # If this is the first ELF, extract the interpreter.
746 + if _first:
747 + for segment in elf.iter_segments():
748 + if segment.header.p_type != 'PT_INTERP':
749 + continue
750 +
751 + interp = bstr(segment.get_interp_name())
752 + dbg(debug, ' interp =', interp)
753 + ret['interp'] = normpath(root + interp)
754 + real_interp = readlink(ret['interp'], root, prefixed=True)
755 + ret['libs'][os.path.basename(interp)] = {
756 + 'path': ret['interp'],
757 + 'realpath': real_interp,
758 + 'needed': [],
759 + }
760 + # XXX: Could read it and scan for /lib paths.
761 + # If the interp is a symlink, lets follow it on the assumption that it
762 + # is in this path purely for ABI reasons, and the distro is using a
763 + # different (probably more correct) path. This can come up in some
764 + # multilib situations like s390x where /lib64/ contains all the native
765 + # libraries, but /lib/ld64.so.1 is the interp hardcoded in gcc, so the
766 + # ld64.so.1 is really a symlink to ../lib64/ld64.so.1. In the multiarch
767 + # setup, it'll be /lib/ld64.so.1 -> /lib/s390x-linux-gnu/ld64.so.1.
768 + # That is why we use |real_interp| here instead of |interp|.
769 + ldpaths['interp'] = [
770 + os.path.dirname(real_interp),
771 + normpath(root + prefix + '/usr/' + os.path.dirname(
772 + real_interp)[len(root) + len(prefix):]),
773 + ]
774 + dbg(debug, ' ldpaths[interp] =', ldpaths['interp'])
775 + break
776 +
777 + # Parse the ELF's dynamic tags.
778 + libs = []
779 + rpaths = []
780 + runpaths = []
781 + for segment in elf.iter_segments():
782 + if segment.header.p_type != 'PT_DYNAMIC':
783 + continue
784 +
785 + for t in segment.iter_tags():
786 + if t.entry.d_tag == 'DT_RPATH':
787 + rpaths = ParseLdPaths(bstr(t.rpath), root=root, path=path)
788 + elif t.entry.d_tag == 'DT_RUNPATH':
789 + runpaths = ParseLdPaths(bstr(t.runpath), root=root, path=path)
790 + elif t.entry.d_tag == 'DT_NEEDED':
791 + libs.append(bstr(t.needed))
792 + if runpaths:
793 + # If both RPATH and RUNPATH are set, only the latter is used.
794 + rpaths = []
795 +
796 + # XXX: We assume there is only one PT_DYNAMIC. This is
797 + # probably fine since the runtime ldso does the same.
798 + break
799 + if _first:
800 + # Propagate the rpaths used by the main ELF since those will be
801 + # used at runtime to locate things.
802 + ldpaths['rpath'] = rpaths
803 + ldpaths['runpath'] = runpaths
804 + dbg(debug, ' ldpaths[rpath] =', rpaths)
805 + dbg(debug, ' ldpaths[runpath] =', runpaths)
806 + ret['rpath'] = rpaths
807 + ret['runpath'] = runpaths
808 + ret['needed'] = libs
809 +
810 + # Search for the libs this ELF uses.
811 + all_ldpaths = None
812 + for lib in libs:
813 + if lib in _all_libs:
814 + continue
815 + if all_ldpaths is None:
816 + all_ldpaths = rpaths + ldpaths['rpath'] + ldpaths['env'] + runpaths + ldpaths['runpath'] + ldpaths['conf'] + ldpaths['interp']
817 + realpath, fullpath = FindLib(elf, lib, all_ldpaths, root, debug=debug)
818 + _all_libs[lib] = {
819 + 'realpath': realpath,
820 + 'path': fullpath,
821 + 'needed': [],
822 + }
823 + if fullpath:
824 + try:
825 + lret = ParseELF(realpath, root, prefix, ldpaths, display=fullpath,
826 + debug=debug, _first=False, _all_libs=_all_libs)
827 + except exceptions.ELFError as e:
828 + warn('%s: %s' % (realpath, e))
829 + _all_libs[lib]['needed'] = lret['needed']
830
831 - del elf
832 + del elf
833
834 - return ret
835 + return ret
836 # pylint: enable=dangerous-default-value
837
838
839 class _NormalizePathAction(argparse.Action):
840 - def __call__(self, parser, namespace, values, option_string=None):
841 - setattr(namespace, self.dest, normpath(values))
842 + def __call__(self, parser, namespace, values, option_string=None):
843 + setattr(namespace, self.dest, normpath(values))
844
845
846 def _ActionShow(options, elf):
847 - """Show the dependency tree for this ELF"""
848 - def _show(lib, depth):
849 - chain_libs.append(lib)
850 - fullpath = elf['libs'][lib]['path']
851 + """Show the dependency tree for this ELF"""
852 + def _show(lib, depth):
853 + chain_libs.append(lib)
854 + fullpath = elf['libs'][lib]['path']
855 + if options.list:
856 + print(fullpath or lib)
857 + else:
858 + print('%s%s => %s' % (' ' * depth, lib, fullpath))
859 +
860 + new_libs = []
861 + for lib in elf['libs'][lib]['needed']:
862 + if lib in chain_libs:
863 + if not options.list:
864 + print('%s%s => !!! circular loop !!!' % (' ' * depth, lib))
865 + continue
866 + if options.all or not lib in shown_libs:
867 + shown_libs.add(lib)
868 + new_libs.append(lib)
869 +
870 + for lib in new_libs:
871 + _show(lib, depth + 1)
872 + chain_libs.pop()
873 +
874 + shown_libs = set(elf['needed'])
875 + new_libs = elf['needed'][:]
876 + chain_libs = []
877 + interp = elf['interp']
878 + if interp:
879 + lib = os.path.basename(interp)
880 + shown_libs.add(lib)
881 + # If we are in non-list mode, then we want to show the "duplicate" interp
882 + # lines -- first the header (interp=>xxx), and then the DT_NEEDED line to
883 + # show that the ELF is directly linked against the interp.
884 + # If we're in list mode though, we only want to show the interp once.
885 + # Unless of course we have the --all flag active, then we show everything.
886 + if not options.all and options.list and lib in new_libs:
887 + new_libs.remove(lib)
888 if options.list:
889 - print(fullpath or lib)
890 + print(elf['path'])
891 + if not interp is None:
892 + print(interp)
893 else:
894 - print('%s%s => %s' % (' ' * depth, lib, fullpath))
895 -
896 - new_libs = []
897 - for lib in elf['libs'][lib]['needed']:
898 - if lib in chain_libs:
899 - if not options.list:
900 - print('%s%s => !!! circular loop !!!' % (' ' * depth, lib))
901 - continue
902 - if options.all or not lib in shown_libs:
903 - shown_libs.add(lib)
904 - new_libs.append(lib)
905 -
906 + print('%s (interpreter => %s)' % (elf['path'], interp))
907 for lib in new_libs:
908 - _show(lib, depth + 1)
909 - chain_libs.pop()
910 -
911 - shown_libs = set(elf['needed'])
912 - new_libs = elf['needed'][:]
913 - chain_libs = []
914 - interp = elf['interp']
915 - if interp:
916 - lib = os.path.basename(interp)
917 - shown_libs.add(lib)
918 - # If we are in non-list mode, then we want to show the "duplicate" interp
919 - # lines -- first the header (interp=>xxx), and then the DT_NEEDED line to
920 - # show that the ELF is directly linked against the interp.
921 - # If we're in list mode though, we only want to show the interp once.
922 - # Unless of course we have the --all flag active, then we show everything.
923 - if not options.all and options.list and lib in new_libs:
924 - new_libs.remove(lib)
925 - if options.list:
926 - print(elf['path'])
927 - if not interp is None:
928 - print(interp)
929 - else:
930 - print('%s (interpreter => %s)' % (elf['path'], interp))
931 - for lib in new_libs:
932 - _show(lib, 1)
933 + _show(lib, 1)
934
935
936 def _ActionCopy(options, elf):
937 - """Copy the ELF and its dependencies to a destination tree"""
938 - def _StripRoot(path):
939 - return path[len(options.root) - 1:]
940 -
941 - def _copy(realsrc, src, striproot=True, wrapit=False, libpaths=(),
942 - outdir=None):
943 - if realsrc is None:
944 - return
945 + """Copy the ELF and its dependencies to a destination tree"""
946 + def _StripRoot(path):
947 + return path[len(options.root) - 1:]
948
949 - if wrapit:
950 - # Static ELFs don't need to be wrapped.
951 - if not elf['interp']:
952 - wrapit = False
953 + def _copy(realsrc, src, striproot=True, wrapit=False, libpaths=(),
954 + outdir=None):
955 + if realsrc is None:
956 + return
957
958 - striproot = _StripRoot if striproot else lambda x: x
959 + if wrapit:
960 + # Static ELFs don't need to be wrapped.
961 + if not elf['interp']:
962 + wrapit = False
963
964 - if outdir:
965 - subdst = os.path.join(outdir, os.path.basename(src))
966 - else:
967 - subdst = striproot(src)
968 - dst = options.dest + subdst
969 + striproot = _StripRoot if striproot else lambda x: x
970
971 - try:
972 - # See if they're the same file.
973 - nstat = os.stat(dst + ('.elf' if wrapit else ''))
974 - ostat = os.stat(realsrc)
975 - for field in ('mode', 'mtime', 'size'):
976 - if getattr(ostat, 'st_' + field) != \
977 - getattr(nstat, 'st_' + field):
978 - break
979 - else:
980 - return
981 - except OSError as e:
982 - if e.errno != errno.ENOENT:
983 - raise
984 + if outdir:
985 + subdst = os.path.join(outdir, os.path.basename(src))
986 + else:
987 + subdst = striproot(src)
988 + dst = options.dest + subdst
989
990 - if options.verbose:
991 - print('%s -> %s' % (src, dst))
992 + try:
993 + # See if they're the same file.
994 + nstat = os.stat(dst + ('.elf' if wrapit else ''))
995 + ostat = os.stat(realsrc)
996 + for field in ('mode', 'mtime', 'size'):
997 + if getattr(ostat, 'st_' + field) != \
998 + getattr(nstat, 'st_' + field):
999 + break
1000 + else:
1001 + return
1002 + except OSError as e:
1003 + if e.errno != errno.ENOENT:
1004 + raise
1005 +
1006 + if options.verbose:
1007 + print('%s -> %s' % (src, dst))
1008 +
1009 + makedirs(os.path.dirname(dst))
1010 + try:
1011 + shutil.copy2(realsrc, dst)
1012 + except IOError:
1013 + os.unlink(dst)
1014 + shutil.copy2(realsrc, dst)
1015 +
1016 + if wrapit:
1017 + if options.verbose:
1018 + print('generate wrapper %s' % (dst,))
1019 +
1020 + if options.libdir:
1021 + interp = os.path.join(options.libdir, os.path.basename(elf['interp']))
1022 + else:
1023 + interp = _StripRoot(elf['interp'])
1024 + GenerateLdsoWrapper(options.dest, subdst, interp, libpaths)
1025 +
1026 + # XXX: We should automatically import libgcc_s.so whenever libpthread.so
1027 + # is copied over (since we know it can be dlopen-ed by NPTL at runtime).
1028 + # Similarly, we should provide an option for automatically copying over
1029 + # the libnsl.so and libnss_*.so libraries, as well as an open ended list
1030 + # for known libs that get loaded (e.g. curl will dlopen(libresolv)).
1031 + uniq_libpaths = set()
1032 + for lib in elf['libs']:
1033 + libdata = elf['libs'][lib]
1034 + path = libdata['realpath']
1035 + if path is None:
1036 + warn('could not locate library: %s' % lib)
1037 + continue
1038 + if not options.libdir:
1039 + uniq_libpaths.add(_StripRoot(os.path.dirname(path)))
1040 + _copy(path, libdata['path'], outdir=options.libdir)
1041
1042 - makedirs(os.path.dirname(dst))
1043 - try:
1044 - shutil.copy2(realsrc, dst)
1045 - except IOError:
1046 - os.unlink(dst)
1047 - shutil.copy2(realsrc, dst)
1048 -
1049 - if wrapit:
1050 - if options.verbose:
1051 - print('generate wrapper %s' % (dst,))
1052 -
1053 - if options.libdir:
1054 - interp = os.path.join(options.libdir, os.path.basename(elf['interp']))
1055 - else:
1056 - interp = _StripRoot(elf['interp'])
1057 - GenerateLdsoWrapper(options.dest, subdst, interp, libpaths)
1058 -
1059 - # XXX: We should automatically import libgcc_s.so whenever libpthread.so
1060 - # is copied over (since we know it can be dlopen-ed by NPTL at runtime).
1061 - # Similarly, we should provide an option for automatically copying over
1062 - # the libnsl.so and libnss_*.so libraries, as well as an open ended list
1063 - # for known libs that get loaded (e.g. curl will dlopen(libresolv)).
1064 - uniq_libpaths = set()
1065 - for lib in elf['libs']:
1066 - libdata = elf['libs'][lib]
1067 - path = libdata['realpath']
1068 - if path is None:
1069 - warn('could not locate library: %s' % lib)
1070 - continue
1071 if not options.libdir:
1072 - uniq_libpaths.add(_StripRoot(os.path.dirname(path)))
1073 - _copy(path, libdata['path'], outdir=options.libdir)
1074 -
1075 - if not options.libdir:
1076 - libpaths = list(uniq_libpaths)
1077 - if elf['runpath']:
1078 - libpaths = elf['runpath'] + libpaths
1079 + libpaths = list(uniq_libpaths)
1080 + if elf['runpath']:
1081 + libpaths = elf['runpath'] + libpaths
1082 + else:
1083 + libpaths = elf['rpath'] + libpaths
1084 else:
1085 - libpaths = elf['rpath'] + libpaths
1086 - else:
1087 - uniq_libpaths.add(options.libdir)
1088 - libpaths = list(uniq_libpaths)
1089 + uniq_libpaths.add(options.libdir)
1090 + libpaths = list(uniq_libpaths)
1091
1092 - # We don't bother to copy this as ParseElf adds the interp to the 'libs',
1093 - # so it was already copied in the libs loop above.
1094 - #_copy(elf['interp'], outdir=options.libdir)
1095 - _copy(elf['realpath'], elf['path'], striproot=options.auto_root,
1096 - wrapit=options.generate_wrappers, libpaths=libpaths,
1097 - outdir=options.bindir)
1098 + # We don't bother to copy this as ParseElf adds the interp to the 'libs',
1099 + # so it was already copied in the libs loop above.
1100 + #_copy(elf['interp'], outdir=options.libdir)
1101 + _copy(elf['realpath'], elf['path'], striproot=options.auto_root,
1102 + wrapit=options.generate_wrappers, libpaths=libpaths,
1103 + outdir=options.bindir)
1104
1105
1106 def GetParser():
1107 - """Get a CLI parser."""
1108 - parser = argparse.ArgumentParser(
1109 - description=__doc__,
1110 - formatter_class=argparse.RawDescriptionHelpFormatter)
1111 - parser.add_argument('-a', '--all',
1112 - action='store_true', default=False,
1113 - help='Show all duplicated dependencies')
1114 - parser.add_argument('-R', '--root',
1115 - default=os.environ.get('ROOT', ''), type=str,
1116 - action=_NormalizePathAction,
1117 - help='Search for all files/dependencies in ROOT')
1118 - parser.add_argument('-P', '--prefix',
1119 - default=os.environ.get(
1120 - 'EPREFIX', '@GENTOO_PORTAGE_EPREFIX@'), type=str,
1121 - action=_NormalizePathAction,
1122 - help='Specify EPREFIX for binaries (for Gentoo Prefix)')
1123 - parser.add_argument('--no-auto-root',
1124 - dest='auto_root', action='store_false', default=True,
1125 - help='Do not automatically prefix input ELFs with ROOT')
1126 - parser.add_argument('-l', '--list',
1127 - action='store_true', default=False,
1128 - help='Display output in a simple list (easy for copying)')
1129 - parser.add_argument('-x', '--debug',
1130 - action='store_true', default=False,
1131 - help='Run with debugging')
1132 - parser.add_argument('-v', '--verbose',
1133 - action='store_true', default=False,
1134 - help='Be verbose')
1135 - parser.add_argument('--skip-non-elfs',
1136 - action='store_true', default=False,
1137 - help='Skip plain (non-ELF) files instead of warning')
1138 - parser.add_argument('-V', '--version',
1139 - action='version',
1140 - version='lddtree by Mike Frysinger <vapier@g.o>',
1141 - help='Show version information')
1142 - parser.add_argument('path', nargs='+')
1143 -
1144 - group = parser.add_argument_group('Copying options')
1145 - group.add_argument('--copy-to-tree',
1146 - dest='dest', default=None, type=str,
1147 - action=_NormalizePathAction,
1148 - help='Copy all files to the specified tree')
1149 - group.add_argument('--bindir',
1150 - default=None, type=str,
1151 - action=_NormalizePathAction,
1152 - help='Dir to store all ELFs specified on the command line')
1153 - group.add_argument('--libdir',
1154 - default=None, type=str,
1155 - action=_NormalizePathAction,
1156 - help='Dir to store all ELF libs')
1157 - group.add_argument('--generate-wrappers',
1158 - action='store_true', default=False,
1159 - help='Wrap executable ELFs with scripts for local ldso')
1160 - group.add_argument('--copy-non-elfs',
1161 - action='store_true', default=False,
1162 - help='Copy over plain (non-ELF) files instead of warn+ignore')
1163 -
1164 - return parser
1165 + """Get a CLI parser."""
1166 + parser = argparse.ArgumentParser(
1167 + description=__doc__,
1168 + formatter_class=argparse.RawDescriptionHelpFormatter)
1169 + parser.add_argument('-a', '--all',
1170 + action='store_true', default=False,
1171 + help='Show all duplicated dependencies')
1172 + parser.add_argument('-R', '--root',
1173 + default=os.environ.get('ROOT', ''), type=str,
1174 + action=_NormalizePathAction,
1175 + help='Search for all files/dependencies in ROOT')
1176 + parser.add_argument('-P', '--prefix',
1177 + default=os.environ.get(
1178 + 'EPREFIX', '@GENTOO_PORTAGE_EPREFIX@'), type=str,
1179 + action=_NormalizePathAction,
1180 + help='Specify EPREFIX for binaries (for Gentoo Prefix)')
1181 + parser.add_argument('--no-auto-root',
1182 + dest='auto_root', action='store_false', default=True,
1183 + help='Do not automatically prefix input ELFs with ROOT')
1184 + parser.add_argument('-l', '--list',
1185 + action='store_true', default=False,
1186 + help='Display output in a simple list (easy for copying)')
1187 + parser.add_argument('-x', '--debug',
1188 + action='store_true', default=False,
1189 + help='Run with debugging')
1190 + parser.add_argument('-v', '--verbose',
1191 + action='store_true', default=False,
1192 + help='Be verbose')
1193 + parser.add_argument('--skip-non-elfs',
1194 + action='store_true', default=False,
1195 + help='Skip plain (non-ELF) files instead of warning')
1196 + parser.add_argument('-V', '--version',
1197 + action='version',
1198 + version='lddtree by Mike Frysinger <vapier@g.o>',
1199 + help='Show version information')
1200 + parser.add_argument('path', nargs='+')
1201 +
1202 + group = parser.add_argument_group('Copying options')
1203 + group.add_argument('--copy-to-tree',
1204 + dest='dest', default=None, type=str,
1205 + action=_NormalizePathAction,
1206 + help='Copy all files to the specified tree')
1207 + group.add_argument('--bindir',
1208 + default=None, type=str,
1209 + action=_NormalizePathAction,
1210 + help='Dir to store all ELFs specified on the command line')
1211 + group.add_argument('--libdir',
1212 + default=None, type=str,
1213 + action=_NormalizePathAction,
1214 + help='Dir to store all ELF libs')
1215 + group.add_argument('--generate-wrappers',
1216 + action='store_true', default=False,
1217 + help='Wrap executable ELFs with scripts for local ldso')
1218 + group.add_argument('--copy-non-elfs',
1219 + action='store_true', default=False,
1220 + help='Copy over plain (non-ELF) files instead of warn+ignore')
1221 +
1222 + return parser
1223
1224
1225 def main(argv):
1226 - """The main entry point!"""
1227 - parser = GetParser()
1228 - options = parser.parse_args(argv)
1229 - paths = options.path
1230 -
1231 - if options.root != '/':
1232 - options.root += '/'
1233 - if options.prefix == '@''GENTOO_PORTAGE_EPREFIX''@':
1234 - options.prefix = ''
1235 -
1236 - if options.bindir and options.bindir[0] != '/':
1237 - parser.error('--bindir accepts absolute paths only')
1238 - if options.libdir and options.libdir[0] != '/':
1239 - parser.error('--libdir accepts absolute paths only')
1240 -
1241 - if options.skip_non_elfs and options.copy_non_elfs:
1242 - parser.error('pick one handler for non-ELFs: skip or copy')
1243 -
1244 - dbg(options.debug, 'root =', options.root)
1245 - if options.dest:
1246 - dbg(options.debug, 'dest =', options.dest)
1247 - if not paths:
1248 - err('missing ELF files to scan')
1249 -
1250 - ldpaths = LoadLdpaths(options.root, options.prefix, debug=options.debug)
1251 - dbg(options.debug, 'ldpaths[conf] =', ldpaths['conf'])
1252 - dbg(options.debug, 'ldpaths[env] =', ldpaths['env'])
1253 -
1254 - # Process all the files specified.
1255 - ret = 0
1256 - for path in paths:
1257 - dbg(options.debug, 'argv[x] =', path)
1258 - # Only auto-prefix the path if the ELF is absolute.
1259 - # If it's a relative path, the user most likely wants
1260 - # the local path.
1261 - if options.auto_root and path.startswith('/'):
1262 - path = options.root + path.lstrip('/')
1263 - dbg(options.debug, ' +auto-root =', path)
1264 -
1265 - matched = False
1266 - for p in glob.iglob(path):
1267 - # Once we've processed the globs, resolve the symlink. This way you can
1268 - # operate on a path that is an absolute symlink itself. e.g.:
1269 - # $ ln -sf /bin/bash $PWD/root/bin/sh
1270 - # $ lddtree --root $PWD/root /bin/sh
1271 - # First we'd turn /bin/sh into $PWD/root/bin/sh, then we want to resolve
1272 - # the symlink to $PWD/root/bin/bash rather than a plain /bin/bash.
1273 - dbg(options.debug, ' globbed =', p)
1274 - if not path.startswith('/'):
1275 - realpath = os.path.realpath(path)
1276 - elif options.auto_root:
1277 - realpath = readlink(p, options.root, prefixed=True)
1278 - else:
1279 - realpath = path
1280 - if path != realpath:
1281 - dbg(options.debug, ' resolved =', realpath)
1282 -
1283 - matched = True
1284 - try:
1285 - elf = ParseELF(realpath, options.root, options.prefix, ldpaths,
1286 - display=p, debug=options.debug)
1287 - except exceptions.ELFError as e:
1288 - if options.skip_non_elfs:
1289 - continue
1290 - # XXX: Ugly. Should unify with _Action* somehow.
1291 - if options.dest is not None and options.copy_non_elfs:
1292 - if os.path.exists(p):
1293 - elf = {
1294 - 'interp': None,
1295 - 'libs': [],
1296 - 'runpath': [],
1297 - 'rpath': [],
1298 - 'path': p,
1299 - 'realpath': realpath,
1300 - }
1301 - _ActionCopy(options, elf)
1302 - continue
1303 - ret = 1
1304 - warn('%s: %s' % (p, e))
1305 - continue
1306 - except IOError as e:
1307 - ret = 1
1308 - warn('%s: %s' % (p, e))
1309 - continue
1310 -
1311 - if options.dest is None:
1312 - _ActionShow(options, elf)
1313 - else:
1314 - _ActionCopy(options, elf)
1315 -
1316 - if not matched:
1317 - ret = 1
1318 - warn('%s: did not match any paths' % (path,))
1319 -
1320 - return ret
1321 + """The main entry point!"""
1322 + parser = GetParser()
1323 + options = parser.parse_args(argv)
1324 + paths = options.path
1325 +
1326 + if options.root != '/':
1327 + options.root += '/'
1328 + if options.prefix == '@''GENTOO_PORTAGE_EPREFIX''@':
1329 + options.prefix = ''
1330 +
1331 + if options.bindir and options.bindir[0] != '/':
1332 + parser.error('--bindir accepts absolute paths only')
1333 + if options.libdir and options.libdir[0] != '/':
1334 + parser.error('--libdir accepts absolute paths only')
1335 +
1336 + if options.skip_non_elfs and options.copy_non_elfs:
1337 + parser.error('pick one handler for non-ELFs: skip or copy')
1338 +
1339 + dbg(options.debug, 'root =', options.root)
1340 + if options.dest:
1341 + dbg(options.debug, 'dest =', options.dest)
1342 + if not paths:
1343 + err('missing ELF files to scan')
1344 +
1345 + ldpaths = LoadLdpaths(options.root, options.prefix, debug=options.debug)
1346 + dbg(options.debug, 'ldpaths[conf] =', ldpaths['conf'])
1347 + dbg(options.debug, 'ldpaths[env] =', ldpaths['env'])
1348 +
1349 + # Process all the files specified.
1350 + ret = 0
1351 + for path in paths:
1352 + dbg(options.debug, 'argv[x] =', path)
1353 + # Only auto-prefix the path if the ELF is absolute.
1354 + # If it's a relative path, the user most likely wants
1355 + # the local path.
1356 + if options.auto_root and path.startswith('/'):
1357 + path = options.root + path.lstrip('/')
1358 + dbg(options.debug, ' +auto-root =', path)
1359 +
1360 + matched = False
1361 + for p in glob.iglob(path):
1362 + # Once we've processed the globs, resolve the symlink. This way you can
1363 + # operate on a path that is an absolute symlink itself. e.g.:
1364 + # $ ln -sf /bin/bash $PWD/root/bin/sh
1365 + # $ lddtree --root $PWD/root /bin/sh
1366 + # First we'd turn /bin/sh into $PWD/root/bin/sh, then we want to resolve
1367 + # the symlink to $PWD/root/bin/bash rather than a plain /bin/bash.
1368 + dbg(options.debug, ' globbed =', p)
1369 + if not path.startswith('/'):
1370 + realpath = os.path.realpath(path)
1371 + elif options.auto_root:
1372 + realpath = readlink(p, options.root, prefixed=True)
1373 + else:
1374 + realpath = path
1375 + if path != realpath:
1376 + dbg(options.debug, ' resolved =', realpath)
1377 +
1378 + matched = True
1379 + try:
1380 + elf = ParseELF(realpath, options.root, options.prefix, ldpaths,
1381 + display=p, debug=options.debug)
1382 + except exceptions.ELFError as e:
1383 + if options.skip_non_elfs:
1384 + continue
1385 + # XXX: Ugly. Should unify with _Action* somehow.
1386 + if options.dest is not None and options.copy_non_elfs:
1387 + if os.path.exists(p):
1388 + elf = {
1389 + 'interp': None,
1390 + 'libs': [],
1391 + 'runpath': [],
1392 + 'rpath': [],
1393 + 'path': p,
1394 + 'realpath': realpath,
1395 + }
1396 + _ActionCopy(options, elf)
1397 + continue
1398 + ret = 1
1399 + warn('%s: %s' % (p, e))
1400 + continue
1401 + except IOError as e:
1402 + ret = 1
1403 + warn('%s: %s' % (p, e))
1404 + continue
1405 +
1406 + if options.dest is None:
1407 + _ActionShow(options, elf)
1408 + else:
1409 + _ActionCopy(options, elf)
1410 +
1411 + if not matched:
1412 + ret = 1
1413 + warn('%s: did not match any paths' % (path,))
1414 +
1415 + return ret
1416
1417
1418 if __name__ == '__main__':
1419 - sys.exit(main(sys.argv[1:]))
1420 + sys.exit(main(sys.argv[1:]))
1421
1422 diff --git a/pylint b/pylint
1423 index 965537d..38d77a2 100755
1424 --- a/pylint
1425 +++ b/pylint
1426 @@ -12,38 +12,38 @@ import sys
1427
1428
1429 def find_all_modules(source_root):
1430 - """Locate all python modules in the tree for scanning"""
1431 - ret = []
1432 + """Locate all python modules in the tree for scanning"""
1433 + ret = []
1434
1435 - for root, _dirs, files in os.walk(source_root, topdown=False):
1436 - # Add all of the .py modules in the tree.
1437 - ret += [os.path.join(root, x) for x in files if x.endswith('.py')]
1438 + for root, _dirs, files in os.walk(source_root, topdown=False):
1439 + # Add all of the .py modules in the tree.
1440 + ret += [os.path.join(root, x) for x in files if x.endswith('.py')]
1441
1442 - # Add the main scripts that don't end in .py.
1443 - ret += [os.path.join(source_root, x) for x in ('pylint',)]
1444 + # Add the main scripts that don't end in .py.
1445 + ret += [os.path.join(source_root, x) for x in ('pylint',)]
1446
1447 - return ret
1448 + return ret
1449
1450
1451 def main(argv):
1452 - """The main entry point"""
1453 - source_root = os.path.dirname(os.path.realpath(__file__))
1454 + """The main entry point"""
1455 + source_root = os.path.dirname(os.path.realpath(__file__))
1456
1457 - if not argv:
1458 - argv = find_all_modules(source_root)
1459 + if not argv:
1460 + argv = find_all_modules(source_root)
1461
1462 - pympath = source_root
1463 - pythonpath = os.environ.get('PYTHONPATH')
1464 - if pythonpath is None:
1465 - pythonpath = pympath
1466 - else:
1467 - pythonpath = pympath + ':' + pythonpath
1468 - os.environ['PYTHONPATH'] = pythonpath
1469 + pympath = source_root
1470 + pythonpath = os.environ.get('PYTHONPATH')
1471 + if pythonpath is None:
1472 + pythonpath = pympath
1473 + else:
1474 + pythonpath = pympath + ':' + pythonpath
1475 + os.environ['PYTHONPATH'] = pythonpath
1476
1477 - pylintrc = os.path.join(source_root, '.pylintrc')
1478 - cmd = ['pylint', '--rcfile', pylintrc]
1479 - os.execvp(cmd[0], cmd + argv)
1480 + pylintrc = os.path.join(source_root, '.pylintrc')
1481 + cmd = ['pylint', '--rcfile', pylintrc]
1482 + os.execvp(cmd[0], cmd + argv)
1483
1484
1485 if __name__ == '__main__':
1486 - sys.exit(main(sys.argv[1:]))
1487 + sys.exit(main(sys.argv[1:]))