mirror of
https://github.com/mudler/luet.git
synced 2025-09-06 09:41:05 +00:00
Introduce install --relaxed
It introduces a relaxed way to install packages with loose deps. Default installation now will by default prefer up-to-date packages during selection. Also: - Upgrade now it's used in install so it have to return the full system view also when there is nothing to upgrade - Avoid checking upgrade upfront if relaxed is on
This commit is contained in:
@@ -104,6 +104,7 @@ To force install a package:
|
|||||||
PreserveSystemEssentialData: true,
|
PreserveSystemEssentialData: true,
|
||||||
DownloadOnly: downloadOnly,
|
DownloadOnly: downloadOnly,
|
||||||
Ask: !yes,
|
Ask: !yes,
|
||||||
|
Relaxed: relax,
|
||||||
})
|
})
|
||||||
inst.Repositories(repos)
|
inst.Repositories(repos)
|
||||||
|
|
||||||
@@ -125,6 +126,8 @@ func init() {
|
|||||||
installCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
|
installCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
|
||||||
installCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
|
installCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
|
||||||
installCmd.Flags().Bool("nodeps", false, "Don't consider package dependencies (harmful!)")
|
installCmd.Flags().Bool("nodeps", false, "Don't consider package dependencies (harmful!)")
|
||||||
|
installCmd.Flags().Bool("relax", false, "Relax installation constraints")
|
||||||
|
|
||||||
installCmd.Flags().Bool("onlydeps", false, "Consider **only** package dependencies")
|
installCmd.Flags().Bool("onlydeps", false, "Consider **only** package dependencies")
|
||||||
installCmd.Flags().Bool("force", false, "Skip errors and keep going (potentially harmful)")
|
installCmd.Flags().Bool("force", false, "Skip errors and keep going (potentially harmful)")
|
||||||
installCmd.Flags().Bool("solver-concurrent", false, "Use concurrent solver (experimental)")
|
installCmd.Flags().Bool("solver-concurrent", false, "Use concurrent solver (experimental)")
|
||||||
|
@@ -50,6 +50,7 @@ type LuetInstallerOptions struct {
|
|||||||
SolverUpgrade, RemoveUnavailableOnUpgrade, UpgradeNewRevisions bool
|
SolverUpgrade, RemoveUnavailableOnUpgrade, UpgradeNewRevisions bool
|
||||||
Ask bool
|
Ask bool
|
||||||
DownloadOnly bool
|
DownloadOnly bool
|
||||||
|
Relaxed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type LuetInstaller struct {
|
type LuetInstaller struct {
|
||||||
@@ -513,7 +514,7 @@ func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s.Database.World()) > 0 {
|
if len(s.Database.World()) > 0 && !l.Options.Relaxed {
|
||||||
Info(":thinking: Checking for available upgrades")
|
Info(":thinking: Checking for available upgrades")
|
||||||
if err := l.checkAndUpgrade(syncedRepos, s); err != nil {
|
if err := l.checkAndUpgrade(syncedRepos, s); err != nil {
|
||||||
return errors.Wrap(err, "while checking upgrades before install")
|
return errors.Wrap(err, "while checking upgrades before install")
|
||||||
@@ -684,7 +685,12 @@ func (l *LuetInstaller) computeInstall(o Option, syncedRepos Repositories, cp pk
|
|||||||
|
|
||||||
if !o.NoDeps {
|
if !o.NoDeps {
|
||||||
solv := solver.NewResolver(solver.Options{Type: l.Options.SolverOptions.Implementation, Concurrency: l.Options.Concurrency}, s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
|
solv := solver.NewResolver(solver.Options{Type: l.Options.SolverOptions.Implementation, Concurrency: l.Options.Concurrency}, s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
|
||||||
|
|
||||||
|
if l.Options.Relaxed {
|
||||||
|
solution, err = solv.RelaxedInstall(p)
|
||||||
|
} else {
|
||||||
solution, err = solv.Install(p)
|
solution, err = solv.Install(p)
|
||||||
|
}
|
||||||
/// TODO: PackageAssertions needs to be a map[fingerprint]pack so lookup is in O(1)
|
/// TODO: PackageAssertions needs to be a map[fingerprint]pack so lookup is in O(1)
|
||||||
if err != nil && !o.Force {
|
if err != nil && !o.Force {
|
||||||
return toInstall, p, solution, allRepos, errors.Wrap(err, "Failed solving solution for package")
|
return toInstall, p, solution, allRepos, errors.Wrap(err, "Failed solving solution for package")
|
||||||
|
@@ -72,6 +72,7 @@ type Package interface {
|
|||||||
SetVersion(string)
|
SetVersion(string)
|
||||||
RequiresContains(PackageDatabase, Package) (bool, error)
|
RequiresContains(PackageDatabase, Package) (bool, error)
|
||||||
Matches(m Package) bool
|
Matches(m Package) bool
|
||||||
|
AtomMatches(m Package) bool
|
||||||
BumpBuildVersion() error
|
BumpBuildVersion() error
|
||||||
|
|
||||||
AddUse(use string)
|
AddUse(use string)
|
||||||
@@ -536,6 +537,13 @@ func (p *DefaultPackage) Matches(m Package) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *DefaultPackage) AtomMatches(m Package) bool {
|
||||||
|
if p.GetName() == m.GetName() && p.GetCategory() == m.GetCategory() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (p *DefaultPackage) Mark() Package {
|
func (p *DefaultPackage) Mark() Package {
|
||||||
marked := p.Clone()
|
marked := p.Clone()
|
||||||
marked.SetName("@@" + marked.GetName())
|
marked.SetName("@@" + marked.GetName())
|
||||||
@@ -795,37 +803,38 @@ func (pack *DefaultPackage) buildFormula(definitiondb PackageDatabase, db Packag
|
|||||||
required = requiredDef
|
required = requiredDef
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
var ALO, priorityConstraints, priorityALO []bf.Formula
|
var ALO []bf.Formula // , priorityConstraints, priorityALO []bf.Formula
|
||||||
|
|
||||||
// Try to prio best match
|
// Try to prio best match
|
||||||
// Force the solver to consider first our candidate (if does exists).
|
// Force the solver to consider first our candidate (if does exists).
|
||||||
// Then builds ALO and AMO for the requires.
|
// Then builds ALO and AMO for the requires.
|
||||||
c, candidateErr := definitiondb.FindPackageCandidate(requiredDef)
|
// c, candidateErr := definitiondb.FindPackageCandidate(requiredDef)
|
||||||
var C bf.Formula
|
// var C bf.Formula
|
||||||
if candidateErr == nil {
|
// if candidateErr == nil {
|
||||||
// We have a desired candidate, try to look a solution with that included first
|
// // We have a desired candidate, try to look a solution with that included first
|
||||||
for _, o := range packages {
|
// for _, o := range packages {
|
||||||
encodedB, err := o.Encode(db)
|
// encodedB, err := o.Encode(db)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
B := bf.Var(encodedB)
|
// B := bf.Var(encodedB)
|
||||||
if !o.Matches(c) {
|
// if !o.Matches(c) {
|
||||||
priorityConstraints = append(priorityConstraints, bf.Not(B))
|
// priorityConstraints = append(priorityConstraints, bf.Not(B))
|
||||||
priorityALO = append(priorityALO, B)
|
// priorityALO = append(priorityALO, B)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
encodedC, err := c.Encode(db)
|
// encodedC, err := c.Encode(db)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
C = bf.Var(encodedC)
|
// C = bf.Var(encodedC)
|
||||||
// Or the Candidate is true, or all the others might be not true
|
// // Or the Candidate is true, or all the others might be not true
|
||||||
// This forces the CDCL sat implementation to look first at a solution with C=true
|
// // This forces the CDCL sat implementation to look first at a solution with C=true
|
||||||
formulas = append(formulas, bf.Or(bf.Not(A), bf.Or(bf.And(C, bf.Or(priorityConstraints...)), bf.And(bf.Not(C), bf.Or(priorityALO...)))))
|
// //formulas = append(formulas, bf.Or(bf.Not(A), bf.Or(bf.And(C, bf.Or(priorityConstraints...)), bf.And(bf.Not(C), bf.Or(priorityALO...)))))
|
||||||
}
|
// formulas = append(formulas, bf.Or(C, bf.Or(priorityConstraints...)))
|
||||||
|
// }
|
||||||
|
|
||||||
// AMO - At most one
|
// AMO/ALO - At most/least one
|
||||||
for _, o := range packages {
|
for _, o := range packages {
|
||||||
encodedB, err := o.Encode(db)
|
encodedB, err := o.Encode(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -415,6 +415,7 @@ var _ = Describe("Decoder", func() {
|
|||||||
|
|
||||||
orderW, err := solution.Order(dbDefinitions, W.GetFingerPrint())
|
orderW, err := solution.Order(dbDefinitions, W.GetFingerPrint())
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(len(orderW) > 0).To(BeTrue())
|
||||||
Expect(orderW[0].Package.GetName()).To(Equal("X"))
|
Expect(orderW[0].Package.GetName()).To(Equal("X"))
|
||||||
Expect(orderW[1].Package.GetName()).To(Equal("Y"))
|
Expect(orderW[1].Package.GetName()).To(Equal("Y"))
|
||||||
Expect(orderW[2].Package.GetName()).To(Equal("Z"))
|
Expect(orderW[2].Package.GetName()).To(Equal("Z"))
|
||||||
|
@@ -550,6 +550,13 @@ func (s *Parallel) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAsse
|
|||||||
// Upgrade compute upgrades of the package against the world definition.
|
// Upgrade compute upgrades of the package against the world definition.
|
||||||
// It accepts two boolean indicating if it has to check for conflicts or try to attempt a full upgrade
|
// It accepts two boolean indicating if it has to check for conflicts or try to attempt a full upgrade
|
||||||
func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) {
|
func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) {
|
||||||
|
return s.upgrade(s.DefinitionDatabase, s.InstalledDatabase, checkconflicts, full)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upgrade compute upgrades of the package against the world definition.
|
||||||
|
// It accepts two boolean indicating if it has to check for conflicts or try to attempt a full upgrade
|
||||||
|
func (s *Parallel) upgrade(defDB pkg.PackageDatabase, installDB pkg.PackageDatabase, checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) {
|
||||||
|
|
||||||
// First get candidates that needs to be upgraded..
|
// First get candidates that needs to be upgraded..
|
||||||
|
|
||||||
@@ -557,7 +564,7 @@ func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAss
|
|||||||
toInstall := pkg.Packages{}
|
toInstall := pkg.Packages{}
|
||||||
|
|
||||||
// we do this in memory so we take into account of provides
|
// we do this in memory so we take into account of provides
|
||||||
universe, err := s.DefinitionDatabase.Copy()
|
universe, err := defDB.Copy()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.Wrap(err, "Could not copy def db")
|
return nil, nil, errors.Wrap(err, "Could not copy def db")
|
||||||
}
|
}
|
||||||
@@ -595,7 +602,7 @@ func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAss
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for _, p := range s.InstalledDatabase.World() {
|
for _, p := range installDB.World() {
|
||||||
all <- p
|
all <- p
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -604,7 +611,7 @@ func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAss
|
|||||||
close(results)
|
close(results)
|
||||||
wg2.Wait()
|
wg2.Wait()
|
||||||
|
|
||||||
s2 := &Parallel{Concurrency: s.Concurrency, InstalledDatabase: installedcopy, DefinitionDatabase: s.DefinitionDatabase, ParallelDatabase: pkg.NewInMemoryDatabase(false)}
|
s2 := &Parallel{Concurrency: s.Concurrency, InstalledDatabase: installedcopy, DefinitionDatabase: defDB, ParallelDatabase: pkg.NewInMemoryDatabase(false)}
|
||||||
s2.SetResolver(s.Resolver)
|
s2.SetResolver(s.Resolver)
|
||||||
if !full {
|
if !full {
|
||||||
ass := PackagesAssertions{}
|
ass := PackagesAssertions{}
|
||||||
@@ -855,6 +862,32 @@ func (s *Parallel) Install(c pkg.Packages) (PackagesAssertions, error) {
|
|||||||
}
|
}
|
||||||
return ass, nil
|
return ass, nil
|
||||||
}
|
}
|
||||||
|
assertions, err := s.Solve()
|
||||||
return s.Solve()
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.upgradeAssertions(assertions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Parallel) upgradeAssertions(assertions PackagesAssertions) (PackagesAssertions, error) {
|
||||||
|
|
||||||
|
systemAfterInstall := pkg.NewInMemoryDatabase(false)
|
||||||
|
|
||||||
|
for _, p := range assertions {
|
||||||
|
if p.Value {
|
||||||
|
systemAfterInstall.CreatePackage(p.Package)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, assertions, err := s.upgrade(s.DefinitionDatabase, systemAfterInstall, false, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// for _, u := range toUninstall {
|
||||||
|
// systemAfterInstall.RemovePackage()
|
||||||
|
|
||||||
|
// }
|
||||||
|
return assertions, nil
|
||||||
}
|
}
|
||||||
|
@@ -37,6 +37,8 @@ const (
|
|||||||
type PackageSolver interface {
|
type PackageSolver interface {
|
||||||
SetDefinitionDatabase(pkg.PackageDatabase)
|
SetDefinitionDatabase(pkg.PackageDatabase)
|
||||||
Install(p pkg.Packages) (PackagesAssertions, error)
|
Install(p pkg.Packages) (PackagesAssertions, error)
|
||||||
|
RelaxedInstall(p pkg.Packages) (PackagesAssertions, error)
|
||||||
|
|
||||||
Uninstall(checkconflicts, full bool, candidate ...pkg.Package) (pkg.Packages, error)
|
Uninstall(checkconflicts, full bool, candidate ...pkg.Package) (pkg.Packages, error)
|
||||||
ConflictsWithInstalled(p pkg.Package) (bool, error)
|
ConflictsWithInstalled(p pkg.Package) (bool, error)
|
||||||
ConflictsWith(p pkg.Package, ls pkg.Packages) (bool, error)
|
ConflictsWith(p pkg.Package, ls pkg.Packages) (bool, error)
|
||||||
@@ -51,6 +53,7 @@ type PackageSolver interface {
|
|||||||
SetResolver(PackageResolver)
|
SetResolver(PackageResolver)
|
||||||
|
|
||||||
Solve() (PackagesAssertions, error)
|
Solve() (PackagesAssertions, error)
|
||||||
|
// BestInstall(c pkg.Packages) (PackagesAssertions, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Solver is the default solver for luet
|
// Solver is the default solver for luet
|
||||||
@@ -493,34 +496,47 @@ func (s *Solver) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAssert
|
|||||||
return markedForRemoval, assertion, nil
|
return markedForRemoval, assertion, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) {
|
func inPackage(list []pkg.Package, p pkg.Package) bool {
|
||||||
|
for _, l := range list {
|
||||||
// First get candidates that needs to be upgraded..
|
if l.AtomMatches(p) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute upgrade between packages if specified, or all if none is specified
|
||||||
|
func (s *Solver) computeUpgrade(pps ...pkg.Package) func(defDB pkg.PackageDatabase, installDB pkg.PackageDatabase) (pkg.Packages, pkg.Packages, pkg.PackageDatabase) {
|
||||||
|
return func(defDB pkg.PackageDatabase, installDB pkg.PackageDatabase) (pkg.Packages, pkg.Packages, pkg.PackageDatabase) {
|
||||||
toUninstall := pkg.Packages{}
|
toUninstall := pkg.Packages{}
|
||||||
toInstall := pkg.Packages{}
|
toInstall := pkg.Packages{}
|
||||||
|
|
||||||
// we do this in memory so we take into account of provides, and its faster
|
// we do this in memory so we take into account of provides, and its faster
|
||||||
universe, err := s.DefinitionDatabase.Copy()
|
universe, _ := defDB.Copy()
|
||||||
if err != nil {
|
|
||||||
return nil, nil, errors.Wrap(err, "failed creating db copy")
|
|
||||||
}
|
|
||||||
|
|
||||||
installedcopy := pkg.NewInMemoryDatabase(false)
|
installedcopy := pkg.NewInMemoryDatabase(false)
|
||||||
|
|
||||||
for _, p := range s.InstalledDatabase.World() {
|
for _, p := range installDB.World() {
|
||||||
installedcopy.CreatePackage(p)
|
installedcopy.CreatePackage(p)
|
||||||
packages, err := universe.FindPackageVersions(p)
|
packages, err := universe.FindPackageVersions(p)
|
||||||
if err == nil && len(packages) != 0 {
|
if err == nil && len(packages) != 0 {
|
||||||
best := packages.Best(nil)
|
best := packages.Best(nil)
|
||||||
if !best.Matches(p) {
|
if !best.Matches(p) && len(pps) == 0 ||
|
||||||
|
len(pps) != 0 && inPackage(pps, p) {
|
||||||
toUninstall = append(toUninstall, p)
|
toUninstall = append(toUninstall, p)
|
||||||
toInstall = append(toInstall, best)
|
toInstall = append(toInstall, best)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return toUninstall, toInstall, installedcopy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s2 := NewSolver(Options{Type: SingleCoreSimple}, installedcopy, s.DefinitionDatabase, pkg.NewInMemoryDatabase(false))
|
func (s *Solver) upgrade(fn func(defDB pkg.PackageDatabase, installDB pkg.PackageDatabase) (pkg.Packages, pkg.Packages, pkg.PackageDatabase), defDB pkg.PackageDatabase, installDB pkg.PackageDatabase, checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) {
|
||||||
|
|
||||||
|
toUninstall, toInstall, installedcopy := fn(defDB, installDB)
|
||||||
|
|
||||||
|
s2 := NewSolver(Options{Type: SingleCoreSimple}, installedcopy, defDB, pkg.NewInMemoryDatabase(false))
|
||||||
s2.SetResolver(s.Resolver)
|
s2.SetResolver(s.Resolver)
|
||||||
if !full {
|
if !full {
|
||||||
ass := PackagesAssertions{}
|
ass := PackagesAssertions{}
|
||||||
@@ -541,15 +557,20 @@ func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAsser
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(toInstall) == 0 {
|
if len(toInstall) == 0 {
|
||||||
return toUninstall, PackagesAssertions{}, nil
|
ass := PackagesAssertions{}
|
||||||
|
for _, i := range installDB.World() {
|
||||||
|
ass = append(ass, PackageAssert{Package: i.(*pkg.DefaultPackage), Value: true})
|
||||||
|
}
|
||||||
|
return toUninstall, ass, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
assertions, err := s2.Install(toInstall.Unique())
|
assertions, err := s2.RelaxedInstall(toInstall.Unique())
|
||||||
|
|
||||||
return toUninstall, assertions, err
|
return toUninstall, assertions, err
|
||||||
// To that tree, ask to install the versions that should be upgraded, and try to solve
|
}
|
||||||
// Return the solution
|
|
||||||
|
|
||||||
|
func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) {
|
||||||
|
return s.upgrade(s.computeUpgrade(), s.DefinitionDatabase, s.InstalledDatabase, checkconflicts, full)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uninstall takes a candidate package and return a list of packages that would be removed
|
// Uninstall takes a candidate package and return a list of packages that would be removed
|
||||||
@@ -619,7 +640,7 @@ func (s *Solver) Uninstall(checkconflicts, full bool, packs ...pkg.Package) (pkg
|
|||||||
s2.SetResolver(s.Resolver)
|
s2.SetResolver(s.Resolver)
|
||||||
|
|
||||||
// Get the requirements to install the candidate
|
// Get the requirements to install the candidate
|
||||||
asserts, err := s2.Install(toRemove)
|
asserts, err := s2.RelaxedInstall(toRemove)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -729,7 +750,7 @@ func (s *Solver) Solve() (PackagesAssertions, error) {
|
|||||||
|
|
||||||
// Install given a list of packages, returns package assertions to indicate the packages that must be installed in the system in order
|
// 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
|
// to statisfy all the constraints
|
||||||
func (s *Solver) Install(c pkg.Packages) (PackagesAssertions, error) {
|
func (s *Solver) RelaxedInstall(c pkg.Packages) (PackagesAssertions, error) {
|
||||||
|
|
||||||
coll, err := s.getList(s.DefinitionDatabase, c)
|
coll, err := s.getList(s.DefinitionDatabase, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -749,6 +770,59 @@ func (s *Solver) Install(c pkg.Packages) (PackagesAssertions, error) {
|
|||||||
}
|
}
|
||||||
return ass, nil
|
return ass, nil
|
||||||
}
|
}
|
||||||
|
assertions, err := s.Solve()
|
||||||
return s.Solve()
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return assertions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install returns the assertions necessary in order to install the packages in
|
||||||
|
// a system.
|
||||||
|
// It calculates the best result possible, trying to maximize new packages.
|
||||||
|
func (s *Solver) Install(c pkg.Packages) (PackagesAssertions, error) {
|
||||||
|
assertions, err := s.RelaxedInstall(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
systemAfterInstall := pkg.NewInMemoryDatabase(false)
|
||||||
|
|
||||||
|
toUpgrade := pkg.Packages{}
|
||||||
|
|
||||||
|
for _, p := range c {
|
||||||
|
if p.GetVersion() == ">=0" || p.GetVersion() == ">0" {
|
||||||
|
toUpgrade = append(toUpgrade, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, p := range assertions {
|
||||||
|
if p.Value {
|
||||||
|
systemAfterInstall.CreatePackage(p.Package)
|
||||||
|
if !inPackage(c, p.Package) {
|
||||||
|
toUpgrade = append(toUpgrade, p.Package)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(toUpgrade) == 0 {
|
||||||
|
return assertions, nil
|
||||||
|
}
|
||||||
|
// do partial upgrade based on input.
|
||||||
|
// IF there is no version specified in the input, or >=0 is specified,
|
||||||
|
// then compute upgrade for those
|
||||||
|
_, newassertions, err := s.upgrade(s.computeUpgrade(toUpgrade...), s.DefinitionDatabase, systemAfterInstall, false, false)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: Emit warning.
|
||||||
|
// We were not able to compute upgrades (maybe for some pinned packages, or a conflict)
|
||||||
|
// so we return the relaxed result
|
||||||
|
return assertions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protect if we return no assertion at all
|
||||||
|
if len(newassertions) == 0 && len(assertions) > 0 {
|
||||||
|
return assertions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return newassertions, nil
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
package solver_test
|
package solver_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
pkg "github.com/mudler/luet/pkg/package"
|
pkg "github.com/mudler/luet/pkg/package"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
@@ -36,6 +38,143 @@ var _ = Describe("Solver", func() {
|
|||||||
dbDefinitions = pkg.NewInMemoryDatabase(false)
|
dbDefinitions = pkg.NewInMemoryDatabase(false)
|
||||||
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
|
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Context("Select of best available package", func() {
|
||||||
|
|
||||||
|
It("picks the best versions available for each package, excluding the ones manually specified while installing", func() {
|
||||||
|
|
||||||
|
B1 := pkg.NewPackage("B", "1.1", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
B2 := pkg.NewPackage("B", "1.2", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
B3 := pkg.NewPackage("B", "1.3", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
B4 := pkg.NewPackage("B", "1.4", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
|
||||||
|
A1 := pkg.NewPackage("A", "1.1", []*pkg.DefaultPackage{
|
||||||
|
pkg.NewPackage("B", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}),
|
||||||
|
}, []*pkg.DefaultPackage{})
|
||||||
|
A2 := pkg.NewPackage("A", "1.2", []*pkg.DefaultPackage{
|
||||||
|
pkg.NewPackage("B", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}),
|
||||||
|
}, []*pkg.DefaultPackage{})
|
||||||
|
|
||||||
|
D := pkg.NewPackage("D", "1.0", []*pkg.DefaultPackage{
|
||||||
|
pkg.NewPackage("A", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}),
|
||||||
|
}, []*pkg.DefaultPackage{})
|
||||||
|
|
||||||
|
C := pkg.NewPackage("C", "1", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{A1, A2, B1, B2, B3, B4, C, D} {
|
||||||
|
_, err := dbDefinitions.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{C} {
|
||||||
|
_, err := dbInstalled.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
|
||||||
|
|
||||||
|
solution, err := s.(*Solver).Install([]pkg.Package{D})
|
||||||
|
fmt.Println(solution)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(len(solution)).To(Equal(8))
|
||||||
|
|
||||||
|
// Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: A2, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B4, Value: true}))
|
||||||
|
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: A1, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B1, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B2, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B3, Value: false}))
|
||||||
|
|
||||||
|
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
|
||||||
|
|
||||||
|
solution, err = s.(*Solver).Install([]pkg.Package{D, B2})
|
||||||
|
fmt.Println(solution)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(len(solution)).To(Equal(8))
|
||||||
|
|
||||||
|
// Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: A2, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B2, Value: true}))
|
||||||
|
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: A1, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B1, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B4, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B3, Value: false}))
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
It("picks the best available excluding those manually input. In this case we the input is a selector >=0", func() {
|
||||||
|
|
||||||
|
B1 := pkg.NewPackage("B", "1.1", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
B2 := pkg.NewPackage("B", "1.2", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
B3 := pkg.NewPackage("B", "1.3", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
B4 := pkg.NewPackage("B", "1.4", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
|
||||||
|
A1 := pkg.NewPackage("A", "1.1", []*pkg.DefaultPackage{
|
||||||
|
pkg.NewPackage("B", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}),
|
||||||
|
}, []*pkg.DefaultPackage{})
|
||||||
|
A2 := pkg.NewPackage("A", "1.2", []*pkg.DefaultPackage{
|
||||||
|
pkg.NewPackage("B", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}),
|
||||||
|
}, []*pkg.DefaultPackage{})
|
||||||
|
|
||||||
|
D := pkg.NewPackage("D", "1.0", []*pkg.DefaultPackage{
|
||||||
|
pkg.NewPackage("A", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}),
|
||||||
|
}, []*pkg.DefaultPackage{})
|
||||||
|
|
||||||
|
C := pkg.NewPackage("C", "1", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{A1, A2, B1, B2, B3, B4, C, D} {
|
||||||
|
_, err := dbDefinitions.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range []pkg.Package{C} {
|
||||||
|
_, err := dbInstalled.CreatePackage(p)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
|
||||||
|
|
||||||
|
solution, err := s.(*Solver).Install([]pkg.Package{pkg.NewPackage("D", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})})
|
||||||
|
fmt.Println(solution)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(len(solution)).To(Equal(8))
|
||||||
|
|
||||||
|
// Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: A2, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B4, Value: true}))
|
||||||
|
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: A1, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B1, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B2, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B3, Value: false}))
|
||||||
|
|
||||||
|
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
|
||||||
|
|
||||||
|
solution, err = s.(*Solver).Install([]pkg.Package{pkg.NewPackage("D", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}), B2})
|
||||||
|
fmt.Println(solution)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(len(solution)).To(Equal(8))
|
||||||
|
|
||||||
|
// Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: A2, Value: true}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B2, Value: true}))
|
||||||
|
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: A1, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B1, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B4, Value: false}))
|
||||||
|
Expect(solution).To(ContainElement(PackageAssert{Package: B3, Value: false}))
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
Context("Simple set", func() {
|
Context("Simple set", func() {
|
||||||
It("Solves correctly if the selected package has no requirements or conflicts and we have nothing installed yet", func() {
|
It("Solves correctly if the selected package has no requirements or conflicts and we have nothing installed yet", func() {
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user