1 # Copyright(c) 2009, Gentoo Foundation
3 # Licensed under the GNU General Public License, v2
7 """Display a direct dependency graph for a given package"""
9 from __future__ import print_function
11 __docformat__ = 'epytext'
18 from functools import partial
19 from getopt import gnu_getopt, GetoptError
23 import gentoolkit.pprinter as pp
24 from gentoolkit import errors
25 from gentoolkit.equery import format_options, mod_usage, CONFIG
26 from gentoolkit.keyword import determine_keyword
27 from gentoolkit.query import Query
42 "include_masked": True,
43 "show_progress": (not CONFIG['quiet'])
50 def print_help(with_description=True):
51 """Print description, usage and a detailed help message.
53 @type with_description: bool
54 @param with_description: if true, print module's __doc__ string
58 print(__doc__.strip())
60 print("Default depth is set to 1 (direct only). Use --depth=0 for no max.")
62 print(mod_usage(mod_name="depgraph"))
64 print(pp.command("options"))
65 print(format_options((
66 (" -h, --help", "display this help message"),
67 (" -A, --no-atom", "do not show dependency atom"),
68 (" -M, --no-mask", "do not show masking status"),
69 (" -U, --no-useflags", "do not show USE flags"),
70 (" -l, --linear", "do not format the graph by indenting dependencies"),
71 (" --depth=N", "limit dependency graph to specified depth")
75 def parse_module_options(module_opts):
76 """Parse module options and update QUERY_OPTS"""
78 opts = (x[0] for x in module_opts)
79 posargs = (x[1] for x in module_opts)
80 for opt, posarg in zip(opts, posargs):
81 if opt in ('-h', '--help'):
84 if opt in ('-A', '--no-atom'):
85 QUERY_OPTS["no_atom"] = True
86 if opt in ('-U', '--no-useflags'):
87 QUERY_OPTS["no_useflags"] = True
88 if opt in ('-M', '--no-mask'):
89 QUERY_OPTS["no_mask"] = True
90 if opt in ('-l', '--linear'):
91 QUERY_OPTS["no_indent"] = True
92 if opt in ('--depth'):
96 err = "Module option --depth requires integer (got '%s')"
97 sys.stderr.write(pp.error(err % posarg))
99 print_help(with_description=False)
101 QUERY_OPTS["depth"] = depth
104 def depgraph_printer(
114 """Display L{gentoolkit.dependencies.Dependencies.graph_depends} results.
117 @param depth: depth of indirection, used to calculate indent
118 @type pkg: L{gentoolkit.package.Package}
119 @param pkg: "best match" package matched by B{dep}
120 @type dep: L{gentoolkit.atom.Atom}
121 @param dep: dependency that matched B{pkg}
123 @param no_use: don't output USE flags
125 @param no_atom: don't output dep atom
126 @type no_indent: bool
127 @param no_indent: don't output indent based on B{depth}
128 @type initial_pkg: bool
129 @param initial_pkg: somewhat of a hack used to print the root package of
130 the graph with absolutely no indent
132 indent = '' if no_indent or initial_pkg else ' ' + (' ' * depth)
133 decorator = '[%3d] ' % depth if no_indent else '`-- '
139 if dep.operator == '=*':
140 atom += ' (=%s*)' % dep.cpv
142 atom += ' (%s%s)' % (dep.operator, dep.cpv)
143 if not no_use and dep is not None and dep.use:
144 use = ' [%s]' % ' '.join(
145 pp.useflag(x, enabled=True) for x in dep.use.tokens
147 except AttributeError:
148 # 'NoneType' object has no attribute 'atom'
150 if pkg and not no_mask:
151 mask = pkg.mask_status()
153 mask = [determine_keyword(portage.settings["ARCH"],
154 portage.settings["ACCEPT_KEYWORDS"],
155 pkg.environment('KEYWORDS'))]
156 mask = pp.masking(mask)
159 (indent, decorator, pp.cpv(str(pkg.cpv)), atom, mask, use)
161 except AttributeError:
162 # 'NoneType' object has no attribute 'cpv'
163 pp.uprint(''.join((indent, decorator, "(no match for %r)" % dep.atom)))
166 def make_depgraph(pkg, printer_fn):
167 """Create and display depgraph for each package."""
170 if CONFIG['verbose']:
171 pp.uprint(" * " + pp.subsection("dependency graph for ") +
172 pp.cpv(str(pkg.cpv)))
174 pp.uprint("%s:" % pkg.cpv)
176 # Print out the first package
177 printer_fn(0, pkg, None, initial_pkg=True)
179 deps = pkg.deps.graph_depends(
180 max_depth=QUERY_OPTS['depth'],
181 printer_fn=printer_fn,
182 # Use this to set this pkg as the graph's root; better way?
186 if CONFIG['verbose']:
187 pkgname = pp.cpv(str(pkg.cpv))
188 n_packages = pp.number(str(len(deps)))
189 max_seen = pp.number(str(max(x[0] for x in deps)))
190 info = "[ %s stats: packages (%s), max depth (%s) ]"
191 pp.uprint(info % (pkgname, n_packages, max_seen))
194 def main(input_args):
195 """Parse input and run the program"""
198 long_opts = ('help', 'no-atom', 'no-useflags', 'no-mask', 'depth=')
201 module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
202 except GetoptError as err:
203 sys.stderr.write(pp.error("Module %s" % err))
205 print_help(with_description=False)
208 parse_module_options(module_opts)
219 for query in (Query(x) for x in queries):
223 matches = query.smart_find(**QUERY_OPTS)
226 raise errors.GentoolkitNoMatches(query)
230 if CONFIG['verbose']:
233 no_atom=QUERY_OPTS['no_atom'],
234 no_indent=QUERY_OPTS['no_indent'],
235 no_use=QUERY_OPTS['no_useflags'],
236 no_mask=QUERY_OPTS['no_mask']
248 make_depgraph(pkg, printer)
252 # vim: set ts=4 sw=4 tw=79: