2018-09-21 21:29:50 +00:00
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
//
// 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; either version 2 of the License, or
// (at your option) any later version.
//
// 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, see <http://www.gnu.org/licenses/>.
package solver
import (
2019-11-29 18:01:41 +00:00
//. "github.com/mudler/luet/pkg/logger"
2020-05-03 10:16:45 +00:00
"fmt"
2019-11-29 18:01:41 +00:00
"github.com/pkg/errors"
2020-02-11 13:58:17 +00:00
"github.com/crillab/gophersat/bf"
2019-06-05 17:13:09 +00:00
pkg "github.com/mudler/luet/pkg/package"
2018-09-21 21:29:50 +00:00
)
2020-10-25 17:43:35 +00:00
type SolverType int
const (
SingleCoreSimple = 0
ParallelSimple = iota
)
2019-06-11 16:03:50 +00:00
// PackageSolver is an interface to a generic package solving algorithm
2018-09-21 21:29:50 +00:00
type PackageSolver interface {
2019-11-29 18:01:41 +00:00
SetDefinitionDatabase ( pkg . PackageDatabase )
2020-04-04 12:29:08 +00:00
Install ( p pkg . Packages ) ( PackagesAssertions , error )
2020-11-19 15:25:51 +00:00
Uninstall ( checkconflicts , full bool , candidate ... pkg . Package ) ( pkg . Packages , error )
2019-06-11 16:03:50 +00:00
ConflictsWithInstalled ( p pkg . Package ) ( bool , error )
2020-04-04 12:29:08 +00:00
ConflictsWith ( p pkg . Package , ls pkg . Packages ) ( bool , error )
2020-05-02 15:05:26 +00:00
Conflicts ( pack pkg . Package , lsp pkg . Packages ) ( bool , error )
2020-04-04 12:29:08 +00:00
World ( ) pkg . Packages
2020-05-20 21:24:32 +00:00
Upgrade ( checkconflicts , full bool ) ( pkg . Packages , PackagesAssertions , error )
2020-02-10 08:41:09 +00:00
2020-05-22 18:45:28 +00:00
UpgradeUniverse ( dropremoved bool ) ( pkg . Packages , PackagesAssertions , error )
UninstallUniverse ( toremove pkg . Packages ) ( pkg . Packages , error )
2020-02-10 08:41:09 +00:00
SetResolver ( PackageResolver )
2020-02-10 16:16:35 +00:00
Solve ( ) ( PackagesAssertions , error )
2018-09-21 21:29:50 +00:00
}
2019-06-11 16:03:50 +00:00
// Solver is the default solver for luet
2018-09-21 21:29:50 +00:00
type Solver struct {
2019-11-29 18:01:41 +00:00
DefinitionDatabase pkg . PackageDatabase
SolverDatabase pkg . PackageDatabase
2020-04-04 12:29:08 +00:00
Wanted pkg . Packages
2019-11-29 18:01:41 +00:00
InstalledDatabase pkg . PackageDatabase
2020-02-10 08:41:09 +00:00
Resolver PackageResolver
2018-09-21 21:29:50 +00:00
}
2020-10-25 17:43:35 +00:00
type Options struct {
Type SolverType
Concurrency int
}
2019-06-11 16:03:50 +00:00
// NewSolver accepts as argument two lists of packages, the first is the initial set,
// the second represent all the known packages.
2020-10-25 17:43:35 +00:00
func NewSolver ( t Options , installed pkg . PackageDatabase , definitiondb pkg . PackageDatabase , solverdb pkg . PackageDatabase ) PackageSolver {
return NewResolver ( t , installed , definitiondb , solverdb , & DummyPackageResolver { } )
2020-02-12 10:22:23 +00:00
}
2020-10-25 17:43:35 +00:00
// NewResolver accepts as argument two lists of packages, the first is the initial set,
2020-02-12 10:22:23 +00:00
// the second represent all the known packages.
2020-10-25 17:43:35 +00:00
// Using constructors as in the future we foresee warmups for hot-restore solver cache
func NewResolver ( t Options , installed pkg . PackageDatabase , definitiondb pkg . PackageDatabase , solverdb pkg . PackageDatabase , re PackageResolver ) PackageSolver {
var s PackageSolver
switch t . Type {
case SingleCoreSimple :
s = & Solver { InstalledDatabase : installed , DefinitionDatabase : definitiondb , SolverDatabase : solverdb , Resolver : re }
case ParallelSimple :
s = & Parallel { InstalledDatabase : installed , DefinitionDatabase : definitiondb , ParallelDatabase : solverdb , Resolver : re , Concurrency : t . Concurrency }
}
return s
2018-09-21 21:29:50 +00:00
}
2020-02-10 08:41:09 +00:00
// SetDefinitionDatabase is a setter for the definition Database
2019-06-11 16:03:50 +00:00
2019-11-29 18:01:41 +00:00
func ( s * Solver ) SetDefinitionDatabase ( db pkg . PackageDatabase ) {
s . DefinitionDatabase = db
}
2020-02-10 08:41:09 +00:00
// SetResolver is a setter for the unsat resolver backend
func ( s * Solver ) SetResolver ( r PackageResolver ) {
s . Resolver = r
}
2020-04-04 12:29:08 +00:00
func ( s * Solver ) World ( ) pkg . Packages {
2019-11-29 18:01:51 +00:00
return s . DefinitionDatabase . World ( )
2019-11-29 18:01:41 +00:00
}
2020-04-04 12:29:08 +00:00
func ( s * Solver ) Installed ( ) pkg . Packages {
2019-11-29 18:01:51 +00:00
return s . InstalledDatabase . World ( )
2019-06-04 19:25:17 +00:00
}
2019-06-05 15:51:24 +00:00
func ( s * Solver ) noRulesWorld ( ) bool {
2019-11-29 18:01:41 +00:00
for _ , p := range s . World ( ) {
2019-06-05 15:51:24 +00:00
if len ( p . GetConflicts ( ) ) != 0 || len ( p . GetRequires ( ) ) != 0 {
return false
}
}
return true
}
2020-05-22 18:45:28 +00:00
func ( s * Solver ) noRulesInstalled ( ) bool {
for _ , p := range s . Installed ( ) {
if len ( p . GetConflicts ( ) ) != 0 || len ( p . GetRequires ( ) ) != 0 {
return false
}
}
return true
}
2019-06-11 16:03:50 +00:00
func ( s * Solver ) BuildInstalled ( ) ( bf . Formula , error ) {
var formulas [ ] bf . Formula
2020-10-30 18:12:12 +00:00
var packages pkg . Packages
2019-11-29 18:01:41 +00:00
for _ , p := range s . Installed ( ) {
2020-10-30 18:12:12 +00:00
packages = append ( packages , p )
for _ , dep := range p . Related ( s . DefinitionDatabase ) {
packages = append ( packages , dep )
}
}
for _ , p := range packages {
2019-11-29 18:01:41 +00:00
solvable , err := p . BuildFormula ( s . DefinitionDatabase , s . SolverDatabase )
2019-06-11 16:03:50 +00:00
if err != nil {
return nil , err
}
//f = bf.And(f, solvable)
formulas = append ( formulas , solvable ... )
}
return bf . And ( formulas ... ) , nil
}
// BuildWorld builds the formula which olds the requirements from the package definitions
// which are available (global state)
func ( s * Solver ) BuildWorld ( includeInstalled bool ) ( bf . Formula , error ) {
2018-09-21 21:29:50 +00:00
var formulas [ ] bf . Formula
2019-06-11 16:03:50 +00:00
// NOTE: This block should be enabled in case of very old systems with outdated world sets
if includeInstalled {
solvable , err := s . BuildInstalled ( )
if err != nil {
return nil , err
}
//f = bf.And(f, solvable)
formulas = append ( formulas , solvable )
}
2019-11-29 18:01:41 +00:00
for _ , p := range s . World ( ) {
2020-10-30 18:12:12 +00:00
2019-11-29 18:01:41 +00:00
solvable , err := p . BuildFormula ( s . DefinitionDatabase , s . SolverDatabase )
2018-09-21 21:29:50 +00:00
if err != nil {
return nil , err
}
2019-06-04 19:25:17 +00:00
formulas = append ( formulas , solvable ... )
2018-09-21 21:29:50 +00:00
}
2019-06-04 19:25:17 +00:00
return bf . And ( formulas ... ) , nil
}
2018-09-21 21:29:50 +00:00
2020-10-30 18:12:12 +00:00
// BuildWorld builds the formula which olds the requirements from the package definitions
// which are available (global state)
func ( s * Solver ) BuildPartialWorld ( includeInstalled bool ) ( bf . Formula , error ) {
var formulas [ ] bf . Formula
// NOTE: This block shouldf be enabled in case of very old systems with outdated world sets
if includeInstalled {
solvable , err := s . BuildInstalled ( )
if err != nil {
return nil , err
}
//f = bf.And(f, solvable)
formulas = append ( formulas , solvable )
}
var packages pkg . Packages
for _ , p := range s . Wanted {
// packages = append(packages, p)
for _ , dep := range p . Related ( s . DefinitionDatabase ) {
packages = append ( packages , dep )
}
}
for _ , p := range packages {
solvable , err := p . BuildFormula ( s . DefinitionDatabase , s . SolverDatabase )
if err != nil {
return nil , err
}
formulas = append ( formulas , solvable ... )
}
if len ( formulas ) != 0 {
return bf . And ( formulas ... ) , nil
}
return bf . True , nil
}
2020-04-04 12:29:08 +00:00
func ( s * Solver ) getList ( db pkg . PackageDatabase , lsp pkg . Packages ) ( pkg . Packages , error ) {
var ls pkg . Packages
2019-11-29 18:01:55 +00:00
2019-11-29 18:01:41 +00:00
for _ , pp := range lsp {
cp , err := db . FindPackage ( pp )
if err != nil {
2019-12-06 15:28:42 +00:00
packages , err := pp . Expand ( db )
2019-11-29 18:01:55 +00:00
// Expand, and relax search - if not found pick the same one
if err != nil || len ( packages ) == 0 {
cp = pp
} else {
2020-04-04 13:33:14 +00:00
cp = packages . Best ( nil )
2019-11-29 18:01:55 +00:00
}
2019-11-29 18:01:41 +00:00
}
ls = append ( ls , cp )
}
return ls , nil
}
2020-05-02 15:05:26 +00:00
// Conflicts acts like ConflictsWith, but uses package's reverse dependencies to
// determine if it conflicts with the given set
func ( s * Solver ) Conflicts ( pack pkg . Package , lsp pkg . Packages ) ( bool , error ) {
p , err := s . DefinitionDatabase . FindPackage ( pack )
if err != nil {
p = pack
}
ls , err := s . getList ( s . DefinitionDatabase , lsp )
if err != nil {
return false , errors . Wrap ( err , "Package not found in definition db" )
}
if s . noRulesWorld ( ) {
return false , nil
}
temporarySet := pkg . NewInMemoryDatabase ( false )
for _ , p := range ls {
temporarySet . CreatePackage ( p )
}
visited := make ( map [ string ] interface { } )
revdeps := p . ExpandedRevdeps ( temporarySet , visited )
2020-05-03 10:16:45 +00:00
var revdepsErr error
for _ , r := range revdeps {
if revdepsErr == nil {
revdepsErr = errors . New ( "" )
}
revdepsErr = errors . New ( fmt . Sprintf ( "%s\n%s" , revdepsErr . Error ( ) , r . HumanReadableString ( ) ) )
}
return len ( revdeps ) != 0 , revdepsErr
2020-05-02 15:05:26 +00:00
}
// ConflictsWith return true if a package is part of the requirement set of a list of package
// return false otherwise (and thus it is NOT relevant to the given list)
2020-04-04 12:29:08 +00:00
func ( s * Solver ) ConflictsWith ( pack pkg . Package , lsp pkg . Packages ) ( bool , error ) {
2019-11-29 18:01:41 +00:00
p , err := s . DefinitionDatabase . FindPackage ( pack )
if err != nil {
p = pack //Relax search, otherwise we cannot compute solutions for packages not in definitions
// return false, errors.Wrap(err, "Package not found in definition db")
}
ls , err := s . getList ( s . DefinitionDatabase , lsp )
if err != nil {
return false , errors . Wrap ( err , "Package not found in definition db" )
}
2019-06-11 16:03:50 +00:00
var formulas [ ] bf . Formula
if s . noRulesWorld ( ) {
return false , nil
}
2019-11-29 18:01:41 +00:00
encodedP , err := p . Encode ( s . SolverDatabase )
2019-06-11 16:03:50 +00:00
if err != nil {
return false , err
}
P := bf . Var ( encodedP )
r , err := s . BuildWorld ( false )
if err != nil {
return false , err
}
formulas = append ( formulas , bf . And ( bf . Not ( P ) , r ) )
for _ , i := range ls {
2019-11-15 17:04:46 +00:00
if i . Matches ( p ) {
2019-06-11 16:03:50 +00:00
continue
}
// XXX: Skip check on any of its requires ? ( Drop to avoid removing system packages when selecting an uninstall)
// if i.RequiresContains(p) {
// fmt.Println("Requires found")
// continue
// }
2019-11-29 18:01:12 +00:00
encodedI , err := i . Encode ( s . SolverDatabase )
2019-06-11 16:03:50 +00:00
if err != nil {
return false , err
}
I := bf . Var ( encodedI )
formulas = append ( formulas , bf . And ( I , r ) )
}
model := bf . Solve ( bf . And ( formulas ... ) )
if model == nil {
return true , nil
}
return false , nil
}
func ( s * Solver ) ConflictsWithInstalled ( p pkg . Package ) ( bool , error ) {
2019-11-29 18:01:41 +00:00
return s . ConflictsWith ( p , s . Installed ( ) )
2019-06-11 16:03:50 +00:00
}
2020-05-22 18:45:28 +00:00
// UninstallUniverse takes a list of candidate package and return a list of packages that would be removed
// in order to purge the candidate. Uses the solver to check constraints and nothing else
//
// It can be compared to the counterpart Uninstall as this method acts like a uninstall --full
// it removes all the packages and its deps. taking also in consideration other packages that might have
// revdeps
func ( s * Solver ) UninstallUniverse ( toremove pkg . Packages ) ( pkg . Packages , error ) {
if s . noRulesInstalled ( ) {
return s . getList ( s . InstalledDatabase , toremove )
}
// resolve to packages from the db
toRemove , err := s . getList ( s . InstalledDatabase , toremove )
if err != nil {
return nil , errors . Wrap ( err , "Package not found in definition db" )
}
var formulas [ ] bf . Formula
r , err := s . BuildInstalled ( )
if err != nil {
return nil , errors . Wrap ( err , "Package not found in definition db" )
}
// SAT encode the clauses against the world
for _ , p := range toRemove . Unique ( ) {
encodedP , err := p . Encode ( s . InstalledDatabase )
if err != nil {
return nil , errors . Wrap ( err , "Package not found in definition db" )
}
P := bf . Var ( encodedP )
formulas = append ( formulas , bf . And ( bf . Not ( P ) , r ) )
}
markedForRemoval := pkg . Packages { }
model := bf . Solve ( bf . And ( formulas ... ) )
if model == nil {
return nil , errors . New ( "Failed finding a solution" )
}
assertion , err := DecodeModel ( model , s . InstalledDatabase )
if err != nil {
return nil , errors . Wrap ( err , "while decoding model from solution" )
}
for _ , a := range assertion {
if ! a . Value {
if p , err := s . InstalledDatabase . FindPackage ( a . Package ) ; err == nil {
markedForRemoval = append ( markedForRemoval , p )
}
}
}
return markedForRemoval , nil
}
// UpgradeUniverse mark packages for removal and returns a solution. It considers
// the Universe db as authoritative
// See also on the subject: https://arxiv.org/pdf/1007.1021.pdf
func ( s * Solver ) UpgradeUniverse ( dropremoved bool ) ( pkg . Packages , PackagesAssertions , error ) {
// we first figure out which aren't up-to-date
// which has to be removed
// and which needs to be upgraded
notUptodate := pkg . Packages { }
removed := pkg . Packages { }
toUpgrade := pkg . Packages { }
// TODO: this is memory expensive, we need to optimize this
universe := pkg . NewInMemoryDatabase ( false )
for _ , p := range s . DefinitionDatabase . World ( ) {
universe . CreatePackage ( p )
}
for _ , p := range s . Installed ( ) {
universe . CreatePackage ( p )
}
// Grab all the installed ones, see if they are eligible for update
for _ , p := range s . Installed ( ) {
available , err := universe . FindPackageVersions ( p )
if err != nil {
removed = append ( removed , p )
}
if len ( available ) == 0 {
continue
}
bestmatch := available . Best ( nil )
// Found a better version available
if ! bestmatch . Matches ( p ) {
notUptodate = append ( notUptodate , p )
toUpgrade = append ( toUpgrade , bestmatch )
}
}
var formulas [ ] bf . Formula
// Build constraints for the whole defdb
r , err := s . BuildWorld ( true )
if err != nil {
return nil , nil , errors . Wrap ( err , "couldn't build world constraints" )
}
// Treat removed packages from universe as marked for deletion
if dropremoved {
2020-10-30 18:12:12 +00:00
notUptodate = append ( notUptodate , removed ... )
2020-05-22 18:45:28 +00:00
}
// SAT encode the clauses against the world
2020-10-30 18:12:12 +00:00
for _ , p := range notUptodate . Unique ( ) {
2020-05-22 18:45:28 +00:00
encodedP , err := p . Encode ( universe )
if err != nil {
return nil , nil , errors . Wrap ( err , "couldn't encode package" )
}
P := bf . Var ( encodedP )
formulas = append ( formulas , bf . And ( bf . Not ( P ) , r ) )
}
2020-10-30 18:12:12 +00:00
for _ , p := range toUpgrade {
2020-05-22 18:45:28 +00:00
encodedP , err := p . Encode ( universe )
if err != nil {
return nil , nil , errors . Wrap ( err , "couldn't encode package" )
}
P := bf . Var ( encodedP )
formulas = append ( formulas , bf . And ( P , r ) )
}
markedForRemoval := pkg . Packages { }
2020-10-30 18:12:12 +00:00
if len ( formulas ) == 0 {
return pkg . Packages { } , PackagesAssertions { } , nil
}
2020-05-22 18:45:28 +00:00
model := bf . Solve ( bf . And ( formulas ... ) )
if model == nil {
return nil , nil , errors . New ( "Failed finding a solution" )
}
assertion , err := DecodeModel ( model , universe )
if err != nil {
return nil , nil , errors . Wrap ( err , "while decoding model from solution" )
}
for _ , a := range assertion {
if ! a . Value {
if p , err := s . InstalledDatabase . FindPackage ( a . Package ) ; err == nil {
markedForRemoval = append ( markedForRemoval , p )
}
}
}
return markedForRemoval , assertion , nil
}
2020-05-20 21:24:32 +00:00
func ( s * Solver ) Upgrade ( checkconflicts , full bool ) ( pkg . Packages , PackagesAssertions , error ) {
2019-11-29 18:01:56 +00:00
// First get candidates that needs to be upgraded..
2020-04-04 12:29:08 +00:00
toUninstall := pkg . Packages { }
toInstall := pkg . Packages { }
2019-11-29 18:01:56 +00:00
2020-11-05 19:52:02 +00:00
// we do this in memory so we take into account of provides
universe := pkg . NewInMemoryDatabase ( false )
for _ , p := range s . DefinitionDatabase . World ( ) {
universe . CreatePackage ( p )
}
2019-11-29 18:01:56 +00:00
installedcopy := pkg . NewInMemoryDatabase ( false )
for _ , p := range s . InstalledDatabase . World ( ) {
installedcopy . CreatePackage ( p )
2020-11-05 19:52:02 +00:00
packages , err := universe . FindPackageVersions ( p )
if err == nil && len ( packages ) != 0 {
2020-04-04 13:33:14 +00:00
best := packages . Best ( nil )
2020-11-05 19:52:02 +00:00
if ! best . Matches ( p ) {
2019-11-29 18:01:56 +00:00
toUninstall = append ( toUninstall , p )
toInstall = append ( toInstall , best )
}
}
}
2020-05-20 20:46:47 +00:00
2020-10-25 17:43:35 +00:00
s2 := NewSolver ( Options { Type : SingleCoreSimple } , installedcopy , s . DefinitionDatabase , pkg . NewInMemoryDatabase ( false ) )
2020-02-10 16:16:35 +00:00
s2 . SetResolver ( s . Resolver )
2020-05-20 21:24:32 +00:00
if ! full {
ass := PackagesAssertions { }
for _ , i := range toInstall {
ass = append ( ass , PackageAssert { Package : i . ( * pkg . DefaultPackage ) , Value : true } )
}
}
2019-11-29 18:01:56 +00:00
// Then try to uninstall the versions in the system, and store that tree
2020-11-19 15:25:51 +00:00
r , err := s . Uninstall ( checkconflicts , false , toUninstall ... )
if err != nil {
return nil , nil , errors . Wrap ( err , "Could not compute upgrade - couldn't uninstall candidates " )
}
for _ , z := range r {
err = installedcopy . RemovePackage ( z )
2019-11-29 18:01:56 +00:00
if err != nil {
2020-11-19 15:25:51 +00:00
return nil , nil , errors . Wrap ( err , "Could not compute upgrade - couldn't remove copy of package targetted for removal" )
2019-11-29 18:01:56 +00:00
}
}
2020-11-19 15:25:51 +00:00
2020-10-30 18:12:12 +00:00
if len ( toInstall ) == 0 {
return toUninstall , PackagesAssertions { } , nil
}
2020-11-19 15:25:51 +00:00
assertions , err := s2 . Install ( toInstall )
return toUninstall , assertions , err
2019-11-29 18:01:56 +00:00
// To that tree, ask to install the versions that should be upgraded, and try to solve
// Return the solution
}
2019-06-11 16:03:50 +00:00
// Uninstall takes a candidate package and return a list of packages that would be removed
// in order to purge the candidate. Returns error if unsat.
2020-11-19 15:25:51 +00:00
func ( s * Solver ) Uninstall ( checkconflicts , full bool , packs ... pkg . Package ) ( pkg . Packages , error ) {
if len ( packs ) == 0 {
return pkg . Packages { } , nil
}
2020-04-04 12:29:08 +00:00
var res pkg . Packages
2019-11-29 18:01:55 +00:00
2020-11-19 15:25:51 +00:00
toRemove := pkg . Packages { }
for _ , c := range packs {
candidate , err := s . InstalledDatabase . FindPackage ( c )
if err != nil {
// return nil, errors.Wrap(err, "Couldn't find required package in db definition")
packages , err := c . Expand ( s . InstalledDatabase )
// Info("Expanded", packages, err)
if err != nil || len ( packages ) == 0 {
candidate = c
} else {
candidate = packages . Best ( nil )
}
//Relax search, otherwise we cannot compute solutions for packages not in definitions
// return nil, errors.Wrap(err, "Package not found between installed")
2019-11-29 18:01:55 +00:00
}
2020-11-19 15:25:51 +00:00
toRemove = append ( toRemove , candidate )
2019-11-29 18:01:41 +00:00
}
2020-11-19 15:25:51 +00:00
2019-06-11 16:03:50 +00:00
// Build a fake "Installed" - Candidate and its requires tree
2020-04-04 12:29:08 +00:00
var InstalledMinusCandidate pkg . Packages
2019-11-29 18:01:41 +00:00
2020-05-03 10:16:45 +00:00
// We are asked to not perform a full uninstall (checking all the possible requires that could
// be removed). Let's only check if we can remove the selected package
if ! full && checkconflicts {
2020-11-19 15:25:51 +00:00
for _ , candidate := range toRemove {
if conflicts , err := s . Conflicts ( candidate , s . Installed ( ) ) ; conflicts {
return nil , err
}
2020-05-03 10:16:45 +00:00
}
2020-11-19 15:25:51 +00:00
return toRemove , nil
2020-05-03 10:16:45 +00:00
}
2019-11-29 18:01:41 +00:00
// TODO: Can be optimized
for _ , i := range s . Installed ( ) {
2020-11-19 15:25:51 +00:00
matched := false
for _ , candidate := range toRemove {
if ! i . Matches ( candidate ) {
contains , err := candidate . RequiresContains ( s . SolverDatabase , i )
if err != nil {
return nil , errors . Wrap ( err , "Failed getting installed list" )
}
if ! contains {
matched = true
}
2019-11-29 18:01:41 +00:00
}
2019-06-11 16:03:50 +00:00
}
2020-11-19 15:25:51 +00:00
if matched {
InstalledMinusCandidate = append ( InstalledMinusCandidate , i )
}
2019-06-11 16:03:50 +00:00
}
2020-10-25 17:43:35 +00:00
s2 := NewSolver ( Options { Type : SingleCoreSimple } , pkg . NewInMemoryDatabase ( false ) , s . DefinitionDatabase , pkg . NewInMemoryDatabase ( false ) )
2020-02-27 17:26:48 +00:00
s2 . SetResolver ( s . Resolver )
2019-06-11 16:03:50 +00:00
// Get the requirements to install the candidate
2020-11-19 15:25:51 +00:00
asserts , err := s2 . Install ( toRemove )
2019-06-05 15:51:24 +00:00
if err != nil {
return nil , err
}
for _ , a := range asserts {
2019-11-29 18:01:41 +00:00
if a . Value {
2020-02-27 17:26:48 +00:00
if ! checkconflicts {
2020-08-02 09:31:23 +00:00
res = append ( res , a . Package )
2020-02-27 17:26:48 +00:00
continue
}
2019-06-11 16:03:50 +00:00
c , err := s . ConflictsWithInstalled ( a . Package )
if err != nil {
return nil , err
}
2019-06-11 16:47:07 +00:00
// If doesn't conflict with installed we just consider it for removal and look for the next one
if ! c {
2020-08-02 09:31:23 +00:00
res = append ( res , a . Package )
2019-06-11 16:47:07 +00:00
continue
}
// If does conflicts, give it another chance by checking conflicts if in case we didn't installed our candidate and all the required packages in the system
c , err = s . ConflictsWith ( a . Package , InstalledMinusCandidate )
if err != nil {
return nil , err
}
if ! c {
2020-08-02 09:31:23 +00:00
res = append ( res , a . Package )
2019-06-11 16:03:50 +00:00
}
2019-06-05 15:51:24 +00:00
}
}
return res , nil
}
2019-06-11 16:03:50 +00:00
// BuildFormula builds the main solving formula that is evaluated by the sat solver.
2019-06-04 19:25:17 +00:00
func ( s * Solver ) BuildFormula ( ) ( bf . Formula , error ) {
var formulas [ ] bf . Formula
2020-10-30 18:12:12 +00:00
r , err := s . BuildPartialWorld ( false )
2019-06-04 19:25:17 +00:00
if err != nil {
return nil , err
}
2020-10-30 18:12:12 +00:00
2019-06-04 19:25:17 +00:00
for _ , wanted := range s . Wanted {
2019-11-29 18:01:12 +00:00
encodedW , err := wanted . Encode ( s . SolverDatabase )
2018-09-21 21:29:50 +00:00
if err != nil {
return nil , err
}
2019-06-04 19:25:17 +00:00
W := bf . Var ( encodedW )
2020-10-30 18:12:12 +00:00
// allW = append(allW, W)
2019-11-29 18:01:41 +00:00
installedWorld := s . Installed ( )
//TODO:Optimize
if len ( installedWorld ) == 0 {
2019-06-05 15:51:24 +00:00
formulas = append ( formulas , W ) //bf.And(bf.True, W))
2019-06-04 20:05:52 +00:00
continue
}
2019-06-05 15:51:24 +00:00
2019-11-29 18:01:41 +00:00
for _ , installed := range installedWorld {
2019-11-29 18:01:12 +00:00
encodedI , err := installed . Encode ( s . SolverDatabase )
2019-06-04 19:25:17 +00:00
if err != nil {
return nil , err
}
I := bf . Var ( encodedI )
formulas = append ( formulas , bf . And ( W , I ) )
}
2018-09-21 21:29:50 +00:00
}
2019-06-05 15:51:24 +00:00
2020-10-30 18:12:12 +00:00
formulas = append ( formulas , r )
2018-09-21 21:29:50 +00:00
return bf . And ( formulas ... ) , nil
}
func ( s * Solver ) solve ( f bf . Formula ) ( map [ string ] bool , bf . Formula , error ) {
model := bf . Solve ( f )
if model == nil {
return model , f , errors . New ( "Unsolvable" )
}
2019-06-04 19:25:17 +00:00
2018-09-21 21:29:50 +00:00
return model , f , nil
}
2019-06-11 16:03:50 +00:00
// Solve builds the formula given the current state and returns package assertions
2019-11-11 10:02:32 +00:00
func ( s * Solver ) Solve ( ) ( PackagesAssertions , error ) {
2020-02-10 08:41:09 +00:00
var model map [ string ] bool
var err error
2018-09-21 21:29:50 +00:00
f , err := s . BuildFormula ( )
2019-06-04 20:22:45 +00:00
2018-09-21 21:29:50 +00:00
if err != nil {
2019-06-04 20:22:45 +00:00
return nil , err
2018-09-21 21:29:50 +00:00
}
2020-02-10 08:41:09 +00:00
model , _ , err = s . solve ( f )
if err != nil && s . Resolver != nil {
2020-02-10 16:16:35 +00:00
return s . Resolver . Solve ( f , s )
2020-02-10 08:41:09 +00:00
}
2018-09-21 21:29:50 +00:00
if err != nil {
2019-06-04 19:25:17 +00:00
return nil , err
2018-09-21 21:29:50 +00:00
}
2019-06-04 19:25:17 +00:00
2019-11-29 18:01:12 +00:00
return DecodeModel ( model , s . SolverDatabase )
2018-09-21 21:29:50 +00:00
}
2019-06-04 20:22:45 +00:00
2019-06-11 16:03:50 +00:00
// Install given a list of packages, returns package assertions to indicate the packages that must be installed in the system in order
// to statisfy all the constraints
2020-04-04 12:29:08 +00:00
func ( s * Solver ) Install ( c pkg . Packages ) ( PackagesAssertions , error ) {
2019-11-29 18:01:41 +00:00
coll , err := s . getList ( s . DefinitionDatabase , c )
if err != nil {
return nil , errors . Wrap ( err , "Packages not found in definition db" )
2019-06-04 20:22:45 +00:00
}
2019-11-29 18:01:41 +00:00
2019-06-04 20:22:45 +00:00
s . Wanted = coll
2019-06-05 15:51:24 +00:00
if s . noRulesWorld ( ) {
2019-11-11 10:02:32 +00:00
var ass PackagesAssertions
2019-11-29 18:01:41 +00:00
for _ , p := range s . Installed ( ) {
ass = append ( ass , PackageAssert { Package : p . ( * pkg . DefaultPackage ) , Value : true } )
2019-06-05 15:51:24 +00:00
}
for _ , p := range s . Wanted {
2019-11-29 18:01:41 +00:00
ass = append ( ass , PackageAssert { Package : p . ( * pkg . DefaultPackage ) , Value : true } )
2019-06-05 15:51:24 +00:00
}
return ass , nil
}
2019-06-04 20:22:45 +00:00
return s . Solve ( )
}