+def reverse_depends_check(removals, suites):
+ print "Checking reverse dependencies..."
+ components = Cnf.ValueList("Suite::%s::Components" % suites[0])
+ dep_problem = 0
+ p2c = {};
+ for architecture in Cnf.ValueList("Suite::%s::Architectures" % suites[0]):
+ if architecture in ["source", "all"]:
+ continue
+ deps = {};
+ virtual_packages = {};
+ for component in components:
+ filename = "%s/dists/%s/%s/binary-%s/Packages.gz" % (Cnf["Dir::Root"], suites[0], component, architecture)
+ # apt_pkg.ParseTagFile needs a real file handle and can't handle a GzipFile instance...
+ temp_filename = utils.temp_filename();
+ (result, output) = commands.getstatusoutput("gunzip -c %s > %s" % (filename, temp_filename));
+ if (result != 0):
+ utils.fubar("Gunzip invocation failed!\n%s\n" % (output), result);
+ packages = utils.open_file(temp_filename);
+ Packages = apt_pkg.ParseTagFile(packages)
+ while Packages.Step():
+ package = Packages.Section.Find("Package")
+ depends = Packages.Section.Find("Depends")
+ if depends:
+ deps[package] = depends
+ provides = Packages.Section.Find("Provides")
+ # Maintain a counter for each virtual package. If a
+ # Provides: exists, set the counter to 0 and count all
+ # provides by a package not in the list for removal.
+ # If the counter stays 0 at the end, we know that only
+ # the to-be-removed packages provided this virtual
+ # package.
+ if provides:
+ for virtual_pkg in provides.split(","):
+ virtual_pkg = virtual_pkg.strip()
+ if virtual_pkg == package: continue
+ if not virtual_packages.has_key(virtual_pkg):
+ virtual_packages[virtual_pkg] = 0
+ if package not in removals:
+ virtual_packages[virtual_pkg] += 1
+ p2c[package] = component;
+ packages.close()
+ os.unlink(temp_filename);
+
+ # If a virtual package is only provided by the to-be-removed
+ # packages, treat the virtual package as to-be-removed too.
+ for virtual_pkg in virtual_packages.keys():
+ if virtual_packages[virtual_pkg] == 0:
+ removals.append(virtual_pkg)
+
+ # Check binary dependencies (Depends)
+ for package in deps.keys():
+ if package in removals: continue
+ parsed_dep = []
+ try:
+ parsed_dep += apt_pkg.ParseDepends(deps[package])
+ except ValueError, e:
+ print "Error for package %s: %s" % (package, e)
+ for dep in parsed_dep:
+ # Check for partial breakage. If a package has a ORed
+ # dependency, there is only a dependency problem if all
+ # packages in the ORed depends will be removed.
+ unsat = 0
+ for dep_package, _, _ in dep:
+ if dep_package in removals:
+ unsat += 1
+ if unsat == len(dep):
+ component = p2c[package];
+ if component != "main":
+ what = "%s/%s" % (package, component);
+ else:
+ what = "** %s" % (package);
+ print "%s has an unsatisfied dependency on %s: %s" % (what, architecture, utils.pp_deps(dep));
+ dep_problem = 1
+
+ # Check source dependencies (Build-Depends and Build-Depends-Indep)
+ for component in components:
+ filename = "%s/dists/%s/%s/source/Sources.gz" % (Cnf["Dir::Root"], suites[0], component)
+ # apt_pkg.ParseTagFile needs a real file handle and can't handle a GzipFile instance...
+ temp_filename = utils.temp_filename();
+ result, output = commands.getstatusoutput("gunzip -c %s > %s" % (filename, temp_filename))
+ if result != 0:
+ sys.stderr.write("Gunzip invocation failed!\n%s\n" % (output))
+ sys.exit(result)
+ sources = utils.open_file(temp_filename, "r")
+ Sources = apt_pkg.ParseTagFile(sources)
+ while Sources.Step():
+ source = Sources.Section.Find("Package")
+ if source in removals: continue
+ parsed_dep = []
+ for build_dep_type in ["Build-Depends", "Build-Depends-Indep"]:
+ build_dep = Sources.Section.get(build_dep_type)
+ if build_dep:
+ # Remove [arch] information since we want to see breakage on all arches
+ build_dep = re_build_dep_arch.sub("", build_dep)
+ try:
+ parsed_dep += apt_pkg.ParseDepends(build_dep)
+ except ValueError, e:
+ print "Error for source %s: %s" % (source, e)
+ for dep in parsed_dep:
+ unsat = 0
+ for dep_package, _, _ in dep:
+ if dep_package in removals:
+ unsat += 1
+ if unsat == len(dep):
+ if component != "main":
+ source = "%s/%s" % (source, component);
+ else:
+ source = "** %s" % (source);
+ print "%s has an unsatisfied build-dependency: %s" % (source, utils.pp_deps(dep))
+ dep_problem = 1
+ sources.close()
+ os.unlink(temp_filename)
+
+ if dep_problem:
+ print "Dependency problem found."
+ if not Options["No-Action"]:
+ game_over()
+ else:
+ print "No dependency problem found."
+ print
+
+################################################################################
+