#!/usr/bin/python3 # # Check components of build dependencies and warn about universe/multiverse # ones, for a package destined for main/restricted # # Copyright (C) 2011 Canonical # # Authors: # Martin Pitt # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # pylint: disable=invalid-name # pylint: enable=invalid-name """Check if any of a package's build or binary dependencies are in universe or multiverse. Run this inside an unpacked source package """ import argparse import os.path import sys import apt def check_support(apt_cache, pkgname, alt=False): """Check if pkgname is in main or restricted. This prints messages if a package is not in main/restricted, or only partially (i. e. source in main, but binary in universe). """ if alt: prefix = " ... alternative " + pkgname else: prefix = " * " + pkgname prov_packages = apt_cache.get_providing_packages(pkgname) if pkgname in apt_cache: pkg = apt_cache[pkgname] # If this is a virtual package, iterate through the binary packages that # provide this, and ensure they are all in Main. Source packages in and of # themselves cannot provide virtual packages, only binary packages can. elif len(prov_packages) > 0: supported, unsupported = [], [] for pkg in prov_packages: candidate = pkg.candidate if candidate: section = candidate.section if section.startswith("universe") or section.startswith("multiverse"): unsupported.append(pkg.name) else: supported.append(pkg.name) if len(supported) > 0: msg = "is a virtual package, which is provided by the following " msg += "candidates in Main: " + " ".join(supported) print(prefix, msg) elif len(unsupported) > 0: msg = "is a virtual package, but is only provided by the " msg += "following non-Main candidates: " + " ".join(unsupported) print(prefix, msg, file=sys.stderr) return False else: msg = "is a virtual package that exists but is not provided by " msg += "package currently in the archive. Proceed with caution." print(prefix, msg, file=sys.stderr) return False else: print(prefix, "does not exist", file=sys.stderr) return False section = pkg.candidate.section if section.startswith("universe") or section.startswith("multiverse"): # check if the source package is in main and thus will only need binary # promotion source_records = apt.apt_pkg.SourceRecords() if not source_records.lookup(pkg.candidate.source_name): print("ERROR: Cannot lookup source package for", pkg.name, file=sys.stderr) print(prefix, "package is in", section.split("/")[0]) return False src = apt.apt_pkg.TagSection(source_records.record) if src["Section"].startswith("universe") or src["Section"].startswith("multiverse"): print(prefix, "binary and source package is in", section.split("/")[0]) return False print( prefix, "is in", section.split("/")[0] + ", but its source", pkg.candidate.source_name, "is already in main; file an ubuntu-archive bug for " "promoting the current preferred alternative", ) return True if alt: print(prefix, "is already in main; consider preferring it") return True def check_build_dependencies(apt_cache, control): print("Checking support status of build dependencies...") any_unsupported = False for field in ("Build-Depends", "Build-Depends-Indep"): if field not in control.section: continue for or_group in apt.apt_pkg.parse_src_depends(control.section[field]): pkgname = or_group[0][0] # debhelper-compat is expected to be a build dependency of every # package, so it is a red herring to display it in this report. # (src:debhelper is in Ubuntu Main anyway) if pkgname == "debhelper-compat": continue if not check_support(apt_cache, pkgname): # check non-preferred alternatives for altpkg in or_group[1:]: if check_support(apt_cache, altpkg[0], alt=True): break else: any_unsupported = True return any_unsupported def check_binary_dependencies(apt_cache, control): any_unsupported = False print("\nChecking support status of binary dependencies...") while True: try: next(control) except StopIteration: break for field in ("Depends", "Pre-Depends", "Recommends"): if field not in control.section: continue for or_group in apt.apt_pkg.parse_src_depends(control.section[field]): pkgname = or_group[0][0] if pkgname.startswith("$"): continue if not check_support(apt_cache, pkgname): # check non-preferred alternatives for altpkg in or_group[1:]: if check_support(apt_cache, altpkg[0], alt=True): break else: any_unsupported = True return any_unsupported def main(): parser = argparse.ArgumentParser(description=__doc__) parser.parse_args() apt_cache = apt.Cache() if not os.path.exists("debian/control"): print( "debian/control not found. You need to run this tool in a source package directory", file=sys.stderr, ) sys.exit(1) # get build dependencies from debian/control control = apt.apt_pkg.TagFile(open("debian/control", encoding="utf-8")) next(control) unsupported_build_deps = check_build_dependencies(apt_cache, control) unsupported_binary_deps = check_binary_dependencies(apt_cache, control) if unsupported_build_deps or unsupported_binary_deps: print( "\nPlease check https://wiki.ubuntu.com/MainInclusionProcess if " "this source package needs to get into in main/restricted, or " "reconsider if the package really needs above dependencies." ) else: print("All dependencies are supported in main or restricted.") if __name__ == "__main__": main()