mirror of
https://git.launchpad.net/ubuntu-dev-tools
synced 2025-03-12 15:41:09 +00:00
251 lines
7.7 KiB
Python
Executable File
251 lines
7.7 KiB
Python
Executable File
#!/usr/bin/python3
|
|
#
|
|
# Copyright (C) 2011, Stefano Rivera <stefanor@ubuntu.com>
|
|
#
|
|
# Permission to use, copy, modify, and/or distribute this software for any
|
|
# purpose with or without fee is hereby granted, provided that the above
|
|
# copyright notice and this permission notice appear in all copies.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
# pylint: disable=invalid-name
|
|
# pylint: enable=invalid-name
|
|
|
|
import argparse
|
|
import sys
|
|
|
|
from distro_info import DistroDataOutdated
|
|
|
|
from ubuntutools import getLogger
|
|
from ubuntutools.misc import codename_to_distribution, system_distribution, vendor_to_distroinfo
|
|
from ubuntutools.rdepends import RDependsException, query_rdepends
|
|
|
|
Logger = getLogger()
|
|
|
|
DEFAULT_MAX_DEPTH = 10 # We want avoid any infinite loop...
|
|
|
|
|
|
def main():
|
|
system_distro_info = vendor_to_distroinfo(system_distribution())()
|
|
try:
|
|
default_release = system_distro_info.devel()
|
|
except DistroDataOutdated as e:
|
|
Logger.warning(e)
|
|
default_release = "unstable"
|
|
|
|
description = (
|
|
"List reverse-dependencies of package. "
|
|
"If the package name is prefixed with src: then the "
|
|
"reverse-dependencies of all the binary packages that "
|
|
"the specified source package builds will be listed."
|
|
)
|
|
|
|
parser = argparse.ArgumentParser(description=description)
|
|
parser.add_argument(
|
|
"-r",
|
|
"--release",
|
|
default=default_release,
|
|
help="Query dependencies in RELEASE. Default: %s" % default_release,
|
|
)
|
|
parser.add_argument(
|
|
"-R",
|
|
"--without-recommends",
|
|
action="store_false",
|
|
dest="recommends",
|
|
help="Only consider Depends relationships, not Recommends",
|
|
)
|
|
parser.add_argument(
|
|
"-s", "--with-suggests", action="store_true", help="Also consider Suggests relationships"
|
|
)
|
|
parser.add_argument(
|
|
"-b",
|
|
"--build-depends",
|
|
action="store_true",
|
|
help="Query build dependencies (synonym for --arch=source)",
|
|
)
|
|
parser.add_argument(
|
|
"-a", "--arch", default="any", help="Query dependencies in ARCH. Default: any"
|
|
)
|
|
parser.add_argument(
|
|
"-c",
|
|
"--component",
|
|
action="append",
|
|
help="Only consider reverse-dependencies in COMPONENT. "
|
|
"Can be specified multiple times. Default: all",
|
|
)
|
|
parser.add_argument(
|
|
"-l", "--list", action="store_true", help="Display a simple, machine-readable list"
|
|
)
|
|
parser.add_argument(
|
|
"-u",
|
|
"--service-url",
|
|
metavar="URL",
|
|
dest="server",
|
|
default=None,
|
|
help="Reverse Dependencies webservice URL. Default: UbuntuWire",
|
|
)
|
|
parser.add_argument(
|
|
"-x",
|
|
"--recursive",
|
|
action="store_true",
|
|
help="Consider to find reverse dependencies recursively.",
|
|
)
|
|
parser.add_argument(
|
|
"-d",
|
|
"--recursive-depth",
|
|
type=int,
|
|
default=DEFAULT_MAX_DEPTH,
|
|
help="If recusive, you can specify the depth.",
|
|
)
|
|
parser.add_argument("package")
|
|
|
|
options = parser.parse_args()
|
|
|
|
opts = {}
|
|
if options.server is not None:
|
|
opts["server"] = options.server
|
|
|
|
# Convert unstable/testing aliases to codenames:
|
|
distribution = codename_to_distribution(options.release)
|
|
if not distribution:
|
|
parser.error("Unknown release codename %s" % options.release)
|
|
distro_info = vendor_to_distroinfo(distribution)()
|
|
try:
|
|
options.release = distro_info.codename(options.release, default=options.release)
|
|
except DistroDataOutdated:
|
|
# We already logged a warning
|
|
pass
|
|
|
|
if options.build_depends:
|
|
options.arch = "source"
|
|
|
|
if options.arch == "source":
|
|
fields = [
|
|
"Reverse-Build-Depends",
|
|
"Reverse-Build-Depends-Indep",
|
|
"Reverse-Build-Depends-Arch",
|
|
"Reverse-Testsuite-Triggers",
|
|
]
|
|
else:
|
|
fields = ["Reverse-Depends"]
|
|
if options.recommends:
|
|
fields.append("Reverse-Recommends")
|
|
if options.with_suggests:
|
|
fields.append("Reverse-Suggests")
|
|
|
|
def build_results(package, result, fields, component, recursive):
|
|
try:
|
|
data = query_rdepends(package, options.release, options.arch, **opts)
|
|
except RDependsException as e:
|
|
Logger.error(str(e))
|
|
sys.exit(1)
|
|
if not data:
|
|
return
|
|
|
|
if fields:
|
|
data = {k: v for k, v in data.items() if k in fields}
|
|
if component:
|
|
data = {
|
|
k: [rdep for rdep in v if rdep["Component"] in component] for k, v in data.items()
|
|
}
|
|
data = {k: v for k, v in data.items() if v}
|
|
|
|
result[package] = data
|
|
|
|
if recursive > 0:
|
|
for rdeps in result[package].values():
|
|
for rdep in rdeps:
|
|
build_results(rdep["Package"], result, fields, component, recursive - 1)
|
|
|
|
result = {}
|
|
build_results(
|
|
options.package,
|
|
result,
|
|
fields,
|
|
options.component,
|
|
options.recursive and options.recursive_depth or 0,
|
|
)
|
|
|
|
if options.list:
|
|
display_consise(result)
|
|
else:
|
|
display_verbose(options.package, result)
|
|
|
|
|
|
def display_verbose(package, values):
|
|
if not values:
|
|
Logger.info("No reverse dependencies found")
|
|
return
|
|
|
|
def log_field(field):
|
|
Logger.info(field)
|
|
Logger.info("=" * len(field))
|
|
|
|
def log_package(values, package, arch, dependency, offset=0):
|
|
line = " " * offset + "* %s" % package
|
|
if all_archs and set(arch) != all_archs:
|
|
line += " [%s]" % " ".join(sorted(arch))
|
|
if dependency:
|
|
if len(line) < 30:
|
|
line += " " * (30 - len(line))
|
|
line += " (for %s)" % dependency
|
|
Logger.info(line)
|
|
data = values.get(package)
|
|
if data:
|
|
offset = offset + 1
|
|
for rdeps in data.values():
|
|
for rdep in rdeps:
|
|
log_package(
|
|
values,
|
|
rdep["Package"],
|
|
rdep.get("Architectures", all_archs),
|
|
rdep.get("Dependency"),
|
|
offset,
|
|
)
|
|
|
|
all_archs = set()
|
|
# This isn't accurate, but we make up for it by displaying what we found
|
|
for data in values.values():
|
|
for rdeps in data.values():
|
|
for rdep in rdeps:
|
|
if "Architectures" in rdep:
|
|
all_archs.update(rdep["Architectures"])
|
|
|
|
for field, rdeps in values[package].items():
|
|
Logger.info(field)
|
|
rdeps.sort(key=lambda x: x["Package"])
|
|
for rdep in rdeps:
|
|
log_package(
|
|
values,
|
|
rdep["Package"],
|
|
rdep.get("Architectures", all_archs),
|
|
rdep.get("Dependency"),
|
|
)
|
|
Logger.info("")
|
|
|
|
if all_archs:
|
|
Logger.info(
|
|
"Packages without architectures listed are "
|
|
"reverse-dependencies in: %s" % ", ".join(sorted(list(all_archs)))
|
|
)
|
|
|
|
|
|
def display_consise(values):
|
|
result = set()
|
|
for data in values.values():
|
|
for rdeps in data.values():
|
|
for rdep in rdeps:
|
|
result.add(rdep["Package"])
|
|
|
|
Logger.info("\n".join(sorted(list(result))))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|