def solve(self, specs, installed=[], update_deps=True, returnall=False, guess=True, minimal_hint=False): try: stdoutlog.info("Solving package specifications: ") res = self.explicit(specs) if res is not None: return res # If update_deps=True, set the target package in MatchSpec so that # the solver can minimize the version change. If update_deps=False, # fix the version and build so that no change is possible. len0 = len(specs) specs = list(map(MatchSpec, specs)) snames = {s.name for s in specs} for pkg in installed: name, version, build = self.package_triple(pkg) if pkg not in self.index: self.index[pkg] = { 'name':name, 'version':version, 'build':build, 'build_number':0 } self.groups.setdefault(name,[]).append(pkg) if name in snames: continue if update_deps: spec = MatchSpec(name, target=pkg) else: spec = MatchSpec('%s %s %s'%(name,version,build)) specs.append(spec) snames.add(spec) dotlog.debug("Solving for %s" % specs) try: dists, specs = self.get_dists(specs) except NoPackagesFound: raise # Clear out our caches to reduce memory usage before the solve self.find_matches_.clear() self.ms_depends_.clear() # Check if satisfiable dotlog.debug('Checking satisfiability') groups = build_groups(dists) m, v, w = self.build_vw(groups) clauses = list(self.gen_clauses(v, groups, specs)) solution = sat(clauses) if not solution: if guess: if minimal_hint: stderrlog.info('\nError: Unsatisfiable package ' 'specifications.\nGenerating minimal hint: \n') sys.exit(self.minimal_unsatisfiable_subset(clauses, v, w)) else: stderrlog.info('\nError: Unsatisfiable package ' 'specifications.\nGenerating hint: \n') sys.exit(self.guess_bad_solve(specs)) raise RuntimeError("Unsatisfiable package specifications") eq_version = self.generate_version_eq(v, groups, specs[:len0], majoronly=True) clauses, solution, obj1 = optimize(eq_version, clauses, solution) dotlog.debug('Requested version metric: %d'%obj1) eq_features, n0 = self.generate_feature_eq(v, groups, specs) clauses, solution, obj = optimize(eq_features, clauses, solution) dotlog.debug('Feature count metric: %d'%(obj+n0)) eq_version2 = self.generate_version_eq(v, groups, specs, majoronly=False) clauses, solution, obj2 = optimize(eq_version2, clauses, solution) dotlog.debug('Total version metric: %d'%obj2) eq_version3 = self.generate_package_count(v, groups, specs) clauses, solution, obj3 = optimize(eq_version3, clauses, solution) dotlog.debug('Weak dependency metric: %d'%obj3) dotlog.debug('Final metrics: (%d,%d,%d,%d)'%( (n0+evaluate_eq(eq_features,solution), evaluate_eq(eq_version,solution), evaluate_eq(eq_version2,solution), evaluate_eq(eq_version3,solution)))) solution = [s for s in solution if 0 < s <= m] dotlog.debug('Looking for alternate solutions') solutions = [solution] nsol = 1 while True: nclause = tuple(-q for q in solution if 0 < q <= m) clauses.append(nclause) solution = sat(clauses) if solution is None: break solution = [s for s in solution if 0 < s <= m] nsol += 1 if nsol > 10: dotlog.debug('Too many solutions; terminating') break solutions.append(solution) psolutions = [set(w[lit] for lit in sol if 0 < lit <= m and '@' not in w[lit]) for sol in solutions] if nsol > 1: stdoutlog.info( '\nWarning: %s possible package resolutions (only showing differing packages):\n' % ('>10' if nsol > 10 else nsol)) common = set.intersection(*psolutions) for sol in psolutions: stdoutlog.info('\t%s,\n' % sorted(sol - common)) if obj1 > 0 or obj2 > 0 or (obj3 > 0 and any(i>1 for i,_ in eq_version3)): log.debug("Older versions in the solution(s):") for sol in solutions: v = ([(i, w[j]) for i, j in eq_version if j in sol] + [(i, w[j]) for i, j in eq_version2 if j in sol] + [(i, w[j]) for i, j in eq_version3 if i>1 and j in sol]) log.debug(v) stdoutlog.info('\n') return list(map(sorted, psolutions)) if returnall else sorted(psolutions[0]) except: stdoutlog.info('\n') raise