Now Uninstall takes multiple packages

This commit is contained in:
Ettore Di Giacinto
2020-11-19 16:25:51 +01:00
parent 287098f101
commit f2df3faee5
5 changed files with 150 additions and 91 deletions

View File

@@ -700,7 +700,7 @@ func (l *LuetInstaller) Uninstall(p pkg.Package, s *System) error {
return errors.Wrap(err, "Could not solve the uninstall constraints. Tip: try with --solver-type qlearning or with --force, or by removing packages excluding their dependencies with --nodeps") return errors.Wrap(err, "Could not solve the uninstall constraints. Tip: try with --solver-type qlearning or with --force, or by removing packages excluding their dependencies with --nodeps")
} }
} else { } else {
solution, err = solv.Uninstall(p, checkConflicts, full) solution, err = solv.Uninstall(checkConflicts, full, p)
if err != nil && !l.Options.Force { if err != nil && !l.Options.Force {
return errors.Wrap(err, "Could not solve the uninstall constraints. Tip: try with --solver-type qlearning or with --force, or by removing packages excluding their dependencies with --nodeps") return errors.Wrap(err, "Could not solve the uninstall constraints. Tip: try with --solver-type qlearning or with --force, or by removing packages excluding their dependencies with --nodeps")
} }

View File

@@ -615,10 +615,9 @@ func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAss
} }
// Then try to uninstall the versions in the system, and store that tree // Then try to uninstall the versions in the system, and store that tree
for _, p := range toUninstall { r, err := s.Uninstall(checkconflicts, false, toUninstall...)
r, err := s.Uninstall(p, checkconflicts, false)
if err != nil { if err != nil {
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't uninstall selected candidate "+p.GetFingerPrint()) return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't uninstall candidates ")
} }
for _, z := range r { for _, z := range r {
err = installedcopy.RemovePackage(z) err = installedcopy.RemovePackage(z)
@@ -626,12 +625,12 @@ func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAss
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't remove copy of package targetted for removal") return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't remove copy of package targetted for removal")
} }
} }
}
if len(toInstall) == 0 { if len(toInstall) == 0 {
return toUninstall, PackagesAssertions{}, nil return toUninstall, PackagesAssertions{}, nil
} }
r, e := s2.Install(toInstall) assertions, e := s2.Install(toInstall)
return toUninstall, r, e return toUninstall, assertions, e
// To that tree, ask to install the versions that should be upgraded, and try to solve // To that tree, ask to install the versions that should be upgraded, and try to solve
// Return the solution // Return the solution
@@ -639,8 +638,14 @@ func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAss
// 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
// in order to purge the candidate. Returns error if unsat. // in order to purge the candidate. Returns error if unsat.
func (s *Parallel) Uninstall(c pkg.Package, checkconflicts, full bool) (pkg.Packages, error) { func (s *Parallel) Uninstall(checkconflicts, full bool, packs ...pkg.Package) (pkg.Packages, error) {
if len(packs) == 0 {
return pkg.Packages{}, nil
}
var res pkg.Packages var res pkg.Packages
toRemove := pkg.Packages{}
for _, c := range packs {
candidate, err := s.InstalledDatabase.FindPackage(c) candidate, err := s.InstalledDatabase.FindPackage(c)
if err != nil { if err != nil {
@@ -655,36 +660,47 @@ func (s *Parallel) Uninstall(c pkg.Package, checkconflicts, full bool) (pkg.Pack
//Relax search, otherwise we cannot compute solutions for packages not in definitions //Relax search, otherwise we cannot compute solutions for packages not in definitions
// return nil, errors.Wrap(err, "Package not found between installed") // return nil, errors.Wrap(err, "Package not found between installed")
} }
toRemove = append(toRemove, candidate)
}
// Build a fake "Installed" - Candidate and its requires tree // Build a fake "Installed" - Candidate and its requires tree
var InstalledMinusCandidate pkg.Packages var InstalledMinusCandidate pkg.Packages
// We are asked to not perform a full uninstall (checking all the possible requires that could // 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 // be removed). Let's only check if we can remove the selected package
if !full && checkconflicts { if !full && checkconflicts {
for _, candidate := range toRemove {
if conflicts, err := s.Conflicts(candidate, s.Installed()); conflicts { if conflicts, err := s.Conflicts(candidate, s.Installed()); conflicts {
return nil, err return nil, err
} else {
return pkg.Packages{candidate}, nil
} }
} }
return toRemove, nil
}
// TODO: Can be optimized // TODO: Can be optimized
for _, i := range s.Installed() { for _, i := range s.Installed() {
matched := false
for _, candidate := range toRemove {
if !i.Matches(candidate) { if !i.Matches(candidate) {
contains, err := candidate.RequiresContains(s.ParallelDatabase, i) contains, err := candidate.RequiresContains(s.ParallelDatabase, i)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "Failed getting installed list") return nil, errors.Wrap(err, "Failed getting installed list")
} }
if !contains { if !contains {
InstalledMinusCandidate = append(InstalledMinusCandidate, i) matched = true
} }
}
}
if matched {
InstalledMinusCandidate = append(InstalledMinusCandidate, i)
} }
} }
s2 := &Parallel{Concurrency: s.Concurrency, InstalledDatabase: pkg.NewInMemoryDatabase(false), DefinitionDatabase: s.DefinitionDatabase, ParallelDatabase: pkg.NewInMemoryDatabase(false)} s2 := &Parallel{Concurrency: s.Concurrency, InstalledDatabase: pkg.NewInMemoryDatabase(false), DefinitionDatabase: s.DefinitionDatabase, ParallelDatabase: pkg.NewInMemoryDatabase(false)}
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(pkg.Packages{candidate}) asserts, err := s2.Install(toRemove)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -593,7 +593,7 @@ var _ = Describe("Parallel", func() {
} }
s = &Parallel{InstalledDatabase: dbInstalled, Concurrency: 4, DefinitionDatabase: dbDefinitions, ParallelDatabase: db} s = &Parallel{InstalledDatabase: dbInstalled, Concurrency: 4, DefinitionDatabase: dbDefinitions, ParallelDatabase: db}
solution, err := s.Uninstall(A, true, true) solution, err := s.Uninstall(true, true, A)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -619,7 +619,7 @@ var _ = Describe("Parallel", func() {
} }
s = &Parallel{InstalledDatabase: dbInstalled, Concurrency: 4, DefinitionDatabase: dbDefinitions, ParallelDatabase: db} s = &Parallel{InstalledDatabase: dbInstalled, Concurrency: 4, DefinitionDatabase: dbDefinitions, ParallelDatabase: db}
solution, err := s.Uninstall(&pkg.DefaultPackage{Name: "A", Version: ">1.0"}, true, true) solution, err := s.Uninstall(true, true, &pkg.DefaultPackage{Name: "A", Version: ">1.0"})
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -643,7 +643,7 @@ var _ = Describe("Parallel", func() {
_, err := dbInstalled.CreatePackage(p) _, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
solution, err := s.Uninstall(A, true, true) solution, err := s.Uninstall(true, true, A)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -667,7 +667,7 @@ var _ = Describe("Parallel", func() {
_, err := dbInstalled.CreatePackage(p) _, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
solution, err := s.Uninstall(A, true, true) solution, err := s.Uninstall(true, true, A)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -690,7 +690,7 @@ var _ = Describe("Parallel", func() {
_, err := dbInstalled.CreatePackage(p) _, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
solution, err := s.Uninstall(A, true, true) solution, err := s.Uninstall(true, true, A)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -699,6 +699,31 @@ var _ = Describe("Parallel", func() {
Expect(len(solution)).To(Equal(1)) Expect(len(solution)).To(Equal(1))
}) })
It("Uninstalls multiple complex packages correctly, even if shared deps are required by system packages", func() {
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
for _, p := range []pkg.Package{A, B, C, D} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{A, B, C, D} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Uninstall(true, true, A, C)
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(C))
Expect(solution).To(ContainElement(A))
Expect(solution).ToNot(ContainElement(B))
Expect(len(solution)).To(Equal(2))
})
It("Uninstalls complex packages in world correctly", func() { It("Uninstalls complex packages in world correctly", func() {
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
@@ -715,7 +740,7 @@ var _ = Describe("Parallel", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
solution, err := s.Uninstall(A, true, true) solution, err := s.Uninstall(true, true, A)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -741,7 +766,7 @@ var _ = Describe("Parallel", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
solution, err := s.Uninstall(A, true, true) solution, err := s.Uninstall(true, true, A)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -1070,7 +1095,7 @@ var _ = Describe("Parallel", func() {
} }
val, err := s.Conflicts(D, dbInstalled.World()) val, err := s.Conflicts(D, dbInstalled.World())
Expect(err.Error()).To(Equal("\n/A-\n/B-")) Expect(err.Error()).To(Or(Equal("\n/A-\n/B-"), Equal("\n/B-\n/A-")))
Expect(val).To(BeTrue()) Expect(val).To(BeTrue())
}) })

View File

@@ -37,7 +37,7 @@ 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)
Uninstall(candidate pkg.Package, checkconflicts, full bool) (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)
Conflicts(pack pkg.Package, lsp pkg.Packages) (bool, error) Conflicts(pack pkg.Package, lsp pkg.Packages) (bool, error)
@@ -518,10 +518,9 @@ func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAsser
} }
} }
// Then try to uninstall the versions in the system, and store that tree // Then try to uninstall the versions in the system, and store that tree
for _, p := range toUninstall { r, err := s.Uninstall(checkconflicts, false, toUninstall...)
r, err := s.Uninstall(p, checkconflicts, false)
if err != nil { if err != nil {
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't uninstall selected candidate "+p.GetFingerPrint()) return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't uninstall candidates ")
} }
for _, z := range r { for _, z := range r {
err = installedcopy.RemovePackage(z) err = installedcopy.RemovePackage(z)
@@ -529,12 +528,12 @@ func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAsser
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't remove copy of package targetted for removal") return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't remove copy of package targetted for removal")
} }
} }
}
if len(toInstall) == 0 { if len(toInstall) == 0 {
return toUninstall, PackagesAssertions{}, nil return toUninstall, PackagesAssertions{}, nil
} }
r, e := s2.Install(toInstall) assertions, err := s2.Install(toInstall)
return toUninstall, r, e return toUninstall, assertions, err
// To that tree, ask to install the versions that should be upgraded, and try to solve // To that tree, ask to install the versions that should be upgraded, and try to solve
// Return the solution // Return the solution
@@ -542,8 +541,15 @@ func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAsser
// 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
// in order to purge the candidate. Returns error if unsat. // in order to purge the candidate. Returns error if unsat.
func (s *Solver) Uninstall(c pkg.Package, checkconflicts, full bool) (pkg.Packages, error) { func (s *Solver) Uninstall(checkconflicts, full bool, packs ...pkg.Package) (pkg.Packages, error) {
if len(packs) == 0 {
return pkg.Packages{}, nil
}
var res pkg.Packages var res pkg.Packages
toRemove := pkg.Packages{}
for _, c := range packs {
candidate, err := s.InstalledDatabase.FindPackage(c) candidate, err := s.InstalledDatabase.FindPackage(c)
if err != nil { if err != nil {
@@ -558,36 +564,48 @@ func (s *Solver) Uninstall(c pkg.Package, checkconflicts, full bool) (pkg.Packag
//Relax search, otherwise we cannot compute solutions for packages not in definitions //Relax search, otherwise we cannot compute solutions for packages not in definitions
// return nil, errors.Wrap(err, "Package not found between installed") // return nil, errors.Wrap(err, "Package not found between installed")
} }
toRemove = append(toRemove, candidate)
}
// Build a fake "Installed" - Candidate and its requires tree // Build a fake "Installed" - Candidate and its requires tree
var InstalledMinusCandidate pkg.Packages var InstalledMinusCandidate pkg.Packages
// We are asked to not perform a full uninstall (checking all the possible requires that could // 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 // be removed). Let's only check if we can remove the selected package
if !full && checkconflicts { if !full && checkconflicts {
for _, candidate := range toRemove {
if conflicts, err := s.Conflicts(candidate, s.Installed()); conflicts { if conflicts, err := s.Conflicts(candidate, s.Installed()); conflicts {
return nil, err return nil, err
} else {
return pkg.Packages{candidate}, nil
} }
} }
return toRemove, nil
}
// TODO: Can be optimized // TODO: Can be optimized
for _, i := range s.Installed() { for _, i := range s.Installed() {
matched := false
for _, candidate := range toRemove {
if !i.Matches(candidate) { if !i.Matches(candidate) {
contains, err := candidate.RequiresContains(s.SolverDatabase, i) contains, err := candidate.RequiresContains(s.SolverDatabase, i)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "Failed getting installed list") return nil, errors.Wrap(err, "Failed getting installed list")
} }
if !contains { if !contains {
InstalledMinusCandidate = append(InstalledMinusCandidate, i) matched = true
} }
}
}
if matched {
InstalledMinusCandidate = append(InstalledMinusCandidate, i)
} }
} }
s2 := NewSolver(Options{Type: SingleCoreSimple}, pkg.NewInMemoryDatabase(false), s.DefinitionDatabase, pkg.NewInMemoryDatabase(false)) s2 := NewSolver(Options{Type: SingleCoreSimple}, pkg.NewInMemoryDatabase(false), s.DefinitionDatabase, pkg.NewInMemoryDatabase(false))
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(pkg.Packages{candidate}) asserts, err := s2.Install(toRemove)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -593,7 +593,7 @@ var _ = Describe("Solver", func() {
} }
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db) s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
solution, err := s.Uninstall(A, true, true) solution, err := s.Uninstall(true, true, A)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -619,7 +619,7 @@ var _ = Describe("Solver", func() {
} }
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db) s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
solution, err := s.Uninstall(&pkg.DefaultPackage{Name: "A", Version: ">1.0"}, true, true) solution, err := s.Uninstall(true, true, &pkg.DefaultPackage{Name: "A", Version: ">1.0"})
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -643,7 +643,7 @@ var _ = Describe("Solver", func() {
_, err := dbInstalled.CreatePackage(p) _, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
solution, err := s.Uninstall(A, true, true) solution, err := s.Uninstall(true, true, A)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -667,7 +667,7 @@ var _ = Describe("Solver", func() {
_, err := dbInstalled.CreatePackage(p) _, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
solution, err := s.Uninstall(A, true, true) solution, err := s.Uninstall(true, true, A)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -690,7 +690,7 @@ var _ = Describe("Solver", func() {
_, err := dbInstalled.CreatePackage(p) _, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
solution, err := s.Uninstall(A, true, true) solution, err := s.Uninstall(true, true, A)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -715,7 +715,7 @@ var _ = Describe("Solver", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
solution, err := s.Uninstall(A, true, true) solution, err := s.Uninstall(true, true, A)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -741,7 +741,7 @@ var _ = Describe("Solver", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
solution, err := s.Uninstall(A, true, true) solution, err := s.Uninstall(true, true, A)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(A)) Expect(solution).To(ContainElement(A))
@@ -1070,7 +1070,7 @@ var _ = Describe("Solver", func() {
} }
val, err := s.Conflicts(D, dbInstalled.World()) val, err := s.Conflicts(D, dbInstalled.World())
Expect(err.Error()).To(Equal("\n/A-\n/B-")) Expect(err.Error()).To(Or(Equal("\n/A-\n/B-"), Equal("\n/B-\n/A-")))
Expect(val).To(BeTrue()) Expect(val).To(BeTrue())
}) })