🎨 Port package to types

This refactors DefaultPackage into types.Package and gets rid of the
interface. This is a preceeding for a follow up where accessors will be
removed from the code.
It also does several cleanup, so we get rid also of some unneeded
dependencies.
This commit is contained in:
Ettore Di Giacinto
2022-01-06 23:57:56 +01:00
parent 9c61210b5b
commit ba0d625b5f
98 changed files with 2694 additions and 6525 deletions

View File

@@ -21,7 +21,8 @@ import (
"os"
"strconv"
pkg "github.com/mudler/luet/pkg/package"
types "github.com/mudler/luet/pkg/api/core/types"
pkg "github.com/mudler/luet/pkg/database"
"github.com/mudler/luet/tests/helpers"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -33,14 +34,14 @@ var _ = Describe("Solver Benchmarks", func() {
db := pkg.NewInMemoryDatabase(false)
dbInstalled := pkg.NewInMemoryDatabase(false)
dbDefinitions := pkg.NewInMemoryDatabase(false)
var s PackageSolver
var s types.PackageSolver
Context("Complex data sets", func() {
BeforeEach(func() {
db = pkg.NewInMemoryDatabase(false)
dbInstalled = pkg.NewInMemoryDatabase(false)
dbDefinitions = pkg.NewInMemoryDatabase(false)
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
s = NewSolver(types.SolverOptions{Type: types.SolverSingleCoreSimple}, dbInstalled, dbDefinitions, db)
if os.Getenv("BENCHMARK_TESTS") != "true" {
Skip("BENCHMARK_TESTS not enabled")
}
@@ -49,15 +50,15 @@ var _ = Describe("Solver Benchmarks", func() {
runtime := b.Time("runtime", func() {
for i := 0; i < 50000; i++ {
C := pkg.NewPackage("C"+strconv.Itoa(i), "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
E := pkg.NewPackage("E"+strconv.Itoa(i), "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
F := pkg.NewPackage("F"+strconv.Itoa(i), "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
G := pkg.NewPackage("G"+strconv.Itoa(i), "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H := pkg.NewPackage("H"+strconv.Itoa(i), "", []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D"+strconv.Itoa(i), "", []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B"+strconv.Itoa(i), "", []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A"+strconv.Itoa(i), "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
for _, p := range []pkg.Package{A, B, C, D, E, F, G} {
C := types.NewPackage("C"+strconv.Itoa(i), "", []*types.Package{}, []*types.Package{})
E := types.NewPackage("E"+strconv.Itoa(i), "", []*types.Package{}, []*types.Package{})
F := types.NewPackage("F"+strconv.Itoa(i), "", []*types.Package{}, []*types.Package{})
G := types.NewPackage("G"+strconv.Itoa(i), "", []*types.Package{}, []*types.Package{})
H := types.NewPackage("H"+strconv.Itoa(i), "", []*types.Package{G}, []*types.Package{})
D := types.NewPackage("D"+strconv.Itoa(i), "", []*types.Package{H}, []*types.Package{})
B := types.NewPackage("B"+strconv.Itoa(i), "", []*types.Package{D}, []*types.Package{})
A := types.NewPackage("A"+strconv.Itoa(i), "", []*types.Package{B}, []*types.Package{})
for _, p := range []*types.Package{A, B, C, D, E, F, G} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
@@ -66,22 +67,22 @@ var _ = Describe("Solver Benchmarks", func() {
}
for i := 0; i < 1; i++ {
C := pkg.NewPackage("C"+strconv.Itoa(i), "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
G := pkg.NewPackage("G"+strconv.Itoa(i), "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H := pkg.NewPackage("H"+strconv.Itoa(i), "", []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D"+strconv.Itoa(i), "", []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B"+strconv.Itoa(i), "", []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A"+strconv.Itoa(i), "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
C := types.NewPackage("C"+strconv.Itoa(i), "", []*types.Package{}, []*types.Package{})
G := types.NewPackage("G"+strconv.Itoa(i), "", []*types.Package{}, []*types.Package{})
H := types.NewPackage("H"+strconv.Itoa(i), "", []*types.Package{G}, []*types.Package{})
D := types.NewPackage("D"+strconv.Itoa(i), "", []*types.Package{H}, []*types.Package{})
B := types.NewPackage("B"+strconv.Itoa(i), "", []*types.Package{D}, []*types.Package{})
A := types.NewPackage("A"+strconv.Itoa(i), "", []*types.Package{B}, []*types.Package{})
solution, err := s.Install([]pkg.Package{A})
solution, err := s.Install([]*types.Package{A})
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(PackageAssert{Package: A, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: H, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: G, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: A, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: H, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: G, Value: true}))
}
})
@@ -94,7 +95,7 @@ var _ = Describe("Solver Benchmarks", func() {
db = pkg.NewInMemoryDatabase(false)
dbInstalled = pkg.NewInMemoryDatabase(false)
dbDefinitions = pkg.NewInMemoryDatabase(false)
s = NewSolver(Options{Type: SingleCoreSimple, Concurrency: 10}, dbInstalled, dbDefinitions, db)
s = NewSolver(types.SolverOptions{Type: types.SolverSingleCoreSimple, Concurrency: 10}, dbInstalled, dbDefinitions, db)
if os.Getenv("BENCHMARK_TESTS") != "true" {
Skip("BENCHMARK_TESTS not enabled")
}
@@ -102,15 +103,15 @@ var _ = Describe("Solver Benchmarks", func() {
Measure("it should be fast in resolution from a 50000 dataset", func(b Benchmarker) {
runtime := b.Time("runtime", func() {
for i := 0; i < 50000; i++ {
C := pkg.NewPackage("C"+strconv.Itoa(i), "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
E := pkg.NewPackage("E"+strconv.Itoa(i), "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
F := pkg.NewPackage("F"+strconv.Itoa(i), "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
G := pkg.NewPackage("G"+strconv.Itoa(i), "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H := pkg.NewPackage("H"+strconv.Itoa(i), "", []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D"+strconv.Itoa(i), "", []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B"+strconv.Itoa(i), "", []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A"+strconv.Itoa(i), "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
for _, p := range []pkg.Package{A, B, C, D, E, F, G} {
C := types.NewPackage("C"+strconv.Itoa(i), "", []*types.Package{}, []*types.Package{})
E := types.NewPackage("E"+strconv.Itoa(i), "", []*types.Package{}, []*types.Package{})
F := types.NewPackage("F"+strconv.Itoa(i), "", []*types.Package{}, []*types.Package{})
G := types.NewPackage("G"+strconv.Itoa(i), "", []*types.Package{}, []*types.Package{})
H := types.NewPackage("H"+strconv.Itoa(i), "", []*types.Package{G}, []*types.Package{})
D := types.NewPackage("D"+strconv.Itoa(i), "", []*types.Package{H}, []*types.Package{})
B := types.NewPackage("B"+strconv.Itoa(i), "", []*types.Package{D}, []*types.Package{})
A := types.NewPackage("A"+strconv.Itoa(i), "", []*types.Package{B}, []*types.Package{})
for _, p := range []*types.Package{A, B, C, D, E, F, G} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
@@ -118,22 +119,22 @@ var _ = Describe("Solver Benchmarks", func() {
Expect(err).ToNot(HaveOccurred())
}
for i := 0; i < 1; i++ {
C := pkg.NewPackage("C"+strconv.Itoa(i), "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
G := pkg.NewPackage("G"+strconv.Itoa(i), "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H := pkg.NewPackage("H"+strconv.Itoa(i), "", []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D"+strconv.Itoa(i), "", []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B"+strconv.Itoa(i), "", []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A"+strconv.Itoa(i), "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
C := types.NewPackage("C"+strconv.Itoa(i), "", []*types.Package{}, []*types.Package{})
G := types.NewPackage("G"+strconv.Itoa(i), "", []*types.Package{}, []*types.Package{})
H := types.NewPackage("H"+strconv.Itoa(i), "", []*types.Package{G}, []*types.Package{})
D := types.NewPackage("D"+strconv.Itoa(i), "", []*types.Package{H}, []*types.Package{})
B := types.NewPackage("B"+strconv.Itoa(i), "", []*types.Package{D}, []*types.Package{})
A := types.NewPackage("A"+strconv.Itoa(i), "", []*types.Package{B}, []*types.Package{})
solution, err := s.Install([]pkg.Package{A})
solution, err := s.Install([]*types.Package{A})
Expect(err).ToNot(HaveOccurred())
Expect(solution).To(ContainElement(PackageAssert{Package: A, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: H, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: G, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: A, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: H, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: G, Value: true}))
// Expect(len(solution)).To(Equal(6))
}
@@ -152,7 +153,7 @@ var _ = Describe("Solver Benchmarks", func() {
// dbInstalled = pkg.NewInMemoryDatabase(false)
dbDefinitions = pkg.NewInMemoryDatabase(false)
s = NewSolver(Options{Type: SingleCoreSimple, Concurrency: 100}, dbInstalled, dbDefinitions, db)
s = NewSolver(types.SolverOptions{Type: types.SolverSingleCoreSimple, Concurrency: 100}, dbInstalled, dbDefinitions, db)
if os.Getenv("BENCHMARK_TESTS") != "true" {
Skip("BENCHMARK_TESTS not enabled")
}
@@ -161,26 +162,26 @@ var _ = Describe("Solver Benchmarks", func() {
Measure("it should be fast in resolution from a 10000*8 dataset", func(b Benchmarker) {
runtime := b.Time("runtime", func() {
for i := 2; i < 10000; i++ {
C := pkg.NewPackage("C", strconv.Itoa(i), []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
E := pkg.NewPackage("E", strconv.Itoa(i), []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
F := pkg.NewPackage("F", strconv.Itoa(i), []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
G := pkg.NewPackage("G", strconv.Itoa(i), []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H := pkg.NewPackage("H", strconv.Itoa(i), []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", strconv.Itoa(i), []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", strconv.Itoa(i), []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A", strconv.Itoa(i), []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
for _, p := range []pkg.Package{A, B, C, D, E, F, G, H} {
C := types.NewPackage("C", strconv.Itoa(i), []*types.Package{}, []*types.Package{})
E := types.NewPackage("E", strconv.Itoa(i), []*types.Package{}, []*types.Package{})
F := types.NewPackage("F", strconv.Itoa(i), []*types.Package{}, []*types.Package{})
G := types.NewPackage("G", strconv.Itoa(i), []*types.Package{}, []*types.Package{})
H := types.NewPackage("H", strconv.Itoa(i), []*types.Package{G}, []*types.Package{})
D := types.NewPackage("D", strconv.Itoa(i), []*types.Package{H}, []*types.Package{})
B := types.NewPackage("B", strconv.Itoa(i), []*types.Package{D}, []*types.Package{})
A := types.NewPackage("A", strconv.Itoa(i), []*types.Package{B}, []*types.Package{})
for _, p := range []*types.Package{A, B, C, D, E, F, G, H} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
}
//C := pkg.NewPackage("C", "1", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
G := pkg.NewPackage("G", "1", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H := pkg.NewPackage("H", "1", []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", "1", []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", "1", []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A", "1", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
//C := types.NewPackage("C", "1", []*types.Package{}, []*types.Package{})
G := types.NewPackage("G", "1", []*types.Package{}, []*types.Package{})
H := types.NewPackage("H", "1", []*types.Package{G}, []*types.Package{})
D := types.NewPackage("D", "1", []*types.Package{H}, []*types.Package{})
B := types.NewPackage("B", "1", []*types.Package{D}, []*types.Package{})
A := types.NewPackage("A", "1", []*types.Package{B}, []*types.Package{})
_, err := dbInstalled.CreatePackage(A)
Expect(err).ToNot(HaveOccurred())
_, err = dbInstalled.CreatePackage(B)
@@ -198,12 +199,12 @@ var _ = Describe("Solver Benchmarks", func() {
Expect(packages).To(ContainElement(A))
G = pkg.NewPackage("G", "9999", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H = pkg.NewPackage("H", "9999", []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D = pkg.NewPackage("D", "9999", []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B = pkg.NewPackage("B", "9999", []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A = pkg.NewPackage("A", "9999", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
Expect(ass).To(ContainElement(PackageAssert{Package: A, Value: true}))
G = types.NewPackage("G", "9999", []*types.Package{}, []*types.Package{})
H = types.NewPackage("H", "9999", []*types.Package{G}, []*types.Package{})
D = types.NewPackage("D", "9999", []*types.Package{H}, []*types.Package{})
B = types.NewPackage("B", "9999", []*types.Package{D}, []*types.Package{})
A = types.NewPackage("A", "9999", []*types.Package{B}, []*types.Package{})
Expect(ass).To(ContainElement(types.PackageAssert{Package: A, Value: true}))
Expect(len(packages)).To(Equal(5))
// Expect(len(solution)).To(Equal(6))
@@ -216,15 +217,15 @@ var _ = Describe("Solver Benchmarks", func() {
Measure("it should be fast in installation with 12000 packages installed and 2000*8 available", func(b Benchmarker) {
runtime := b.Time("runtime", func() {
for i := 0; i < 2000; i++ {
C := pkg.NewPackage("C", strconv.Itoa(i), []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
E := pkg.NewPackage("E", strconv.Itoa(i), []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
F := pkg.NewPackage("F", strconv.Itoa(i), []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
G := pkg.NewPackage("G", strconv.Itoa(i), []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H := pkg.NewPackage("H", strconv.Itoa(i), []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", strconv.Itoa(i), []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", strconv.Itoa(i), []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A", strconv.Itoa(i), []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
for _, p := range []pkg.Package{A, B, C, D, E, F, G} {
C := types.NewPackage("C", strconv.Itoa(i), []*types.Package{}, []*types.Package{})
E := types.NewPackage("E", strconv.Itoa(i), []*types.Package{}, []*types.Package{})
F := types.NewPackage("F", strconv.Itoa(i), []*types.Package{}, []*types.Package{})
G := types.NewPackage("G", strconv.Itoa(i), []*types.Package{}, []*types.Package{})
H := types.NewPackage("H", strconv.Itoa(i), []*types.Package{G}, []*types.Package{})
D := types.NewPackage("D", strconv.Itoa(i), []*types.Package{H}, []*types.Package{})
B := types.NewPackage("B", strconv.Itoa(i), []*types.Package{D}, []*types.Package{})
A := types.NewPackage("A", strconv.Itoa(i), []*types.Package{B}, []*types.Package{})
for _, p := range []*types.Package{A, B, C, D, E, F, G} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
@@ -237,16 +238,16 @@ var _ = Describe("Solver Benchmarks", func() {
Expect(err).ToNot(HaveOccurred())
}
G := pkg.NewPackage("G", strconv.Itoa(50000), []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H := pkg.NewPackage("H", strconv.Itoa(50000), []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", strconv.Itoa(50000), []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", strconv.Itoa(50000), []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A", strconv.Itoa(50000), []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
G := types.NewPackage("G", strconv.Itoa(50000), []*types.Package{}, []*types.Package{})
H := types.NewPackage("H", strconv.Itoa(50000), []*types.Package{G}, []*types.Package{})
D := types.NewPackage("D", strconv.Itoa(50000), []*types.Package{H}, []*types.Package{})
B := types.NewPackage("B", strconv.Itoa(50000), []*types.Package{D}, []*types.Package{})
A := types.NewPackage("A", strconv.Itoa(50000), []*types.Package{B}, []*types.Package{})
ass, err := s.Install([]pkg.Package{A})
ass, err := s.Install([]*types.Package{A})
Expect(err).ToNot(HaveOccurred())
Expect(ass).To(ContainElement(PackageAssert{Package: pkg.NewPackage("A", "50000", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{}), Value: true}))
Expect(ass).To(ContainElement(types.PackageAssert{Package: types.NewPackage("A", "50000", []*types.Package{B}, []*types.Package{}), Value: true}))
//Expect(ass).To(Equal(5))
// Expect(len(solution)).To(Equal(6))
@@ -258,26 +259,26 @@ var _ = Describe("Solver Benchmarks", func() {
Measure("it should be fast in resolution from a 50000 dataset with upgrade universe", func(b Benchmarker) {
runtime := b.Time("runtime", func() {
for i := 0; i < 2; i++ {
C := pkg.NewPackage("C", strconv.Itoa(i), []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
E := pkg.NewPackage("E", strconv.Itoa(i), []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
F := pkg.NewPackage("F", strconv.Itoa(i), []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
G := pkg.NewPackage("G", strconv.Itoa(i), []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H := pkg.NewPackage("H", strconv.Itoa(i), []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", strconv.Itoa(i), []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", strconv.Itoa(i), []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A", strconv.Itoa(i), []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
for _, p := range []pkg.Package{A, B, C, D, E, F, G} {
C := types.NewPackage("C", strconv.Itoa(i), []*types.Package{}, []*types.Package{})
E := types.NewPackage("E", strconv.Itoa(i), []*types.Package{}, []*types.Package{})
F := types.NewPackage("F", strconv.Itoa(i), []*types.Package{}, []*types.Package{})
G := types.NewPackage("G", strconv.Itoa(i), []*types.Package{}, []*types.Package{})
H := types.NewPackage("H", strconv.Itoa(i), []*types.Package{G}, []*types.Package{})
D := types.NewPackage("D", strconv.Itoa(i), []*types.Package{H}, []*types.Package{})
B := types.NewPackage("B", strconv.Itoa(i), []*types.Package{D}, []*types.Package{})
A := types.NewPackage("A", strconv.Itoa(i), []*types.Package{B}, []*types.Package{})
for _, p := range []*types.Package{A, B, C, D, E, F, G} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
fmt.Println("Creating package, run", i)
}
G := pkg.NewPackage("G", "1", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H := pkg.NewPackage("H", "1", []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", "1", []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", "1", []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A", "1", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
G := types.NewPackage("G", "1", []*types.Package{}, []*types.Package{})
H := types.NewPackage("H", "1", []*types.Package{G}, []*types.Package{})
D := types.NewPackage("D", "1", []*types.Package{H}, []*types.Package{})
B := types.NewPackage("B", "1", []*types.Package{D}, []*types.Package{})
A := types.NewPackage("A", "1", []*types.Package{B}, []*types.Package{})
_, err := dbInstalled.CreatePackage(A)
Expect(err).ToNot(HaveOccurred())
fmt.Println("Upgrade starts")
@@ -285,8 +286,8 @@ var _ = Describe("Solver Benchmarks", func() {
packages, ass, err := s.UpgradeUniverse(true)
Expect(err).ToNot(HaveOccurred())
Expect(ass).To(ContainElement(PackageAssert{Package: pkg.NewPackage("A", "50000", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{}), Value: true}))
Expect(packages).To(ContainElement(pkg.NewPackage("A", "50000", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})))
Expect(ass).To(ContainElement(types.PackageAssert{Package: types.NewPackage("A", "50000", []*types.Package{B}, []*types.Package{}), Value: true}))
Expect(packages).To(ContainElement(types.NewPackage("A", "50000", []*types.Package{B}, []*types.Package{})))
Expect(packages).To(Equal(5))
// Expect(len(solution)).To(Equal(6))

View File

@@ -16,339 +16,19 @@
package solver
import (
"crypto/sha256"
"fmt"
"sort"
"unicode"
pkg "github.com/mudler/luet/pkg/package"
"github.com/mudler/topsort"
toposort "github.com/philopon/go-toposort"
"github.com/pkg/errors"
types "github.com/mudler/luet/pkg/api/core/types"
)
type PackagesAssertions []PackageAssert
type PackageHash struct {
BuildHash string
PackageHash string
}
// PackageAssert represent a package assertion.
// It is composed of a Package and a Value which is indicating the absence or not
// of the associated package state.
type PackageAssert struct {
Package *pkg.DefaultPackage
Value bool
Hash PackageHash
}
// DecodeModel decodes a model from the SAT solver to package assertions (PackageAssert)
func DecodeModel(model map[string]bool, db pkg.PackageDatabase) (PackagesAssertions, error) {
ass := make(PackagesAssertions, 0)
// DecodeModel decodes a model from the SAT solver to package assertions (types.PackageAssert)
func DecodeModel(model map[string]bool, db types.PackageDatabase) (types.PackagesAssertions, error) {
ass := make(types.PackagesAssertions, 0)
for k, v := range model {
a, err := pkg.DecodePackage(k, db)
a, err := types.DecodePackage(k, db)
if err != nil {
return nil, err
}
ass = append(ass, PackageAssert{Package: a.(*pkg.DefaultPackage), Value: v})
ass = append(ass, types.PackageAssert{Package: a, Value: v})
}
return ass, nil
}
func (a *PackageAssert) Explain() {
fmt.Println(a.ToString())
a.Package.Explain()
}
func (a *PackageAssert) String() string {
return a.ToString()
}
func (a *PackageAssert) ToString() string {
var msg string
if a.Value {
msg = "installed"
} else {
msg = "not installed"
}
return fmt.Sprintf("%s/%s %s %s", a.Package.GetCategory(), a.Package.GetName(), a.Package.GetVersion(), msg)
}
func (assertions PackagesAssertions) EnsureOrder() PackagesAssertions {
orderedAssertions := PackagesAssertions{}
unorderedAssertions := PackagesAssertions{}
fingerprints := []string{}
tmpMap := map[string]PackageAssert{}
for _, a := range assertions {
tmpMap[a.Package.GetFingerPrint()] = a
fingerprints = append(fingerprints, a.Package.GetFingerPrint())
unorderedAssertions = append(unorderedAssertions, a) // Build a list of the ones that must be ordered
if a.Value {
unorderedAssertions = append(unorderedAssertions, a) // Build a list of the ones that must be ordered
} else {
orderedAssertions = append(orderedAssertions, a) // Keep last the ones which are not meant to be installed
}
}
sort.Sort(unorderedAssertions)
// Build a topological graph
graph := toposort.NewGraph(len(unorderedAssertions))
graph.AddNodes(fingerprints...)
for _, a := range unorderedAssertions {
for _, req := range a.Package.GetRequires() {
graph.AddEdge(a.Package.GetFingerPrint(), req.GetFingerPrint())
}
}
result, ok := graph.Toposort()
if !ok {
panic("Cycle found")
}
for _, res := range result {
a, ok := tmpMap[res]
if !ok {
panic("fail")
// continue
}
orderedAssertions = append(orderedAssertions, a)
// orderedAssertions = append(PackagesAssertions{a}, orderedAssertions...) // push upfront
}
//helpers.ReverseAny(orderedAssertions)
return orderedAssertions
}
func (assertions PackagesAssertions) SearchByName(f string) *PackageAssert {
for _, a := range assertions {
if a.Value {
if a.Package.GetPackageName() == f {
return &a
}
}
}
return nil
}
func (assertions PackagesAssertions) Search(f string) *PackageAssert {
for _, a := range assertions {
if a.Value {
if a.Package.GetFingerPrint() == f {
return &a
}
}
}
return nil
}
func (assertions PackagesAssertions) ToDB() pkg.PackageDatabase {
db := pkg.NewInMemoryDatabase(false)
for _, a := range assertions {
if a.Value {
db.CreatePackage(a.Package)
}
}
return db
}
func (assertions PackagesAssertions) Order(definitiondb pkg.PackageDatabase, fingerprint string) (PackagesAssertions, error) {
orderedAssertions := PackagesAssertions{}
unorderedAssertions := PackagesAssertions{}
tmpMap := map[string]PackageAssert{}
graph := topsort.NewGraph()
for _, a := range assertions {
graph.AddNode(a.Package.GetFingerPrint())
tmpMap[a.Package.GetFingerPrint()] = a
unorderedAssertions = append(unorderedAssertions, a) // Build a list of the ones that must be ordered
}
sort.Sort(unorderedAssertions)
// Build a topological graph
for _, a := range unorderedAssertions {
currentPkg := a.Package
added := map[string]interface{}{}
REQUIRES:
for _, requiredDef := range currentPkg.GetRequires() {
if def, err := definitiondb.FindPackage(requiredDef); err == nil { // Provides: Get a chance of being override here
requiredDef = def.(*pkg.DefaultPackage)
}
// We cannot search for fingerprint, as we could have selector in versions.
// We know that the assertions are unique for packages, so look for a package with such name in the assertions
req := assertions.SearchByName(requiredDef.GetPackageName())
if req != nil {
requiredDef = req.Package
}
if _, ok := added[requiredDef.GetFingerPrint()]; ok {
continue REQUIRES
}
// Expand also here, as we need to order them (or instead the solver should give back the dep correctly?)
graph.AddEdge(currentPkg.GetFingerPrint(), requiredDef.GetFingerPrint())
added[requiredDef.GetFingerPrint()] = true
}
}
result, err := graph.TopSort(fingerprint)
if err != nil {
return nil, errors.Wrap(err, "fail on sorting "+fingerprint)
}
for _, res := range result {
a, ok := tmpMap[res]
if !ok {
//return nil, errors.New("fail looking for " + res)
// Since now we don't return the entire world as part of assertions
// if we don't find any reference must be because fingerprint we are analyzing (which is the one we are ordering against)
// is not part of the assertions, thus we can omit it from the result
continue
}
orderedAssertions = append(orderedAssertions, a)
// orderedAssertions = append(PackagesAssertions{a}, orderedAssertions...) // push upfront
}
//helpers.ReverseAny(orderedAssertions)
return orderedAssertions, nil
}
func (assertions PackagesAssertions) Explain() string {
var fingerprint string
for _, assertion := range assertions { // Always order them
fingerprint += assertion.ToString() + "\n"
}
return fingerprint
}
func (a PackagesAssertions) Len() int { return len(a) }
func (a PackagesAssertions) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a PackagesAssertions) Less(i, j int) bool {
iRunes := []rune(a[i].Package.GetName())
jRunes := []rune(a[j].Package.GetName())
max := len(iRunes)
if max > len(jRunes) {
max = len(jRunes)
}
for idx := 0; idx < max; idx++ {
ir := iRunes[idx]
jr := jRunes[idx]
lir := unicode.ToLower(ir)
ljr := unicode.ToLower(jr)
if lir != ljr {
return lir < ljr
}
// the lowercase runes are the same, so compare the original
if ir != jr {
return ir < jr
}
}
return false
}
func (a PackagesAssertions) TrueLen() int {
count := 0
for _, ass := range a {
if ass.Value {
count++
}
}
return count
}
// HashFrom computes the assertion hash From a given package. It drops it from the assertions
// and checks it's not the only one. if it's unique it marks it specially - so the hash
// which is generated is unique for the selected package
func (assertions PackagesAssertions) HashFrom(p pkg.Package) string {
return assertions.SaltedHashFrom(p, map[string]string{})
}
func (assertions PackagesAssertions) AssertionHash() string {
return assertions.SaltedAssertionHash(map[string]string{})
}
func (assertions PackagesAssertions) SaltedHashFrom(p pkg.Package, salts map[string]string) string {
var assertionhash string
// When we don't have any solution to hash for, we need to generate an UUID by ourselves
latestsolution := assertions.Drop(p)
if latestsolution.TrueLen() == 0 {
// Preserve the hash if supplied of marked packages
marked := p.Mark()
if markedHash, exists := salts[p.GetFingerPrint()]; exists {
salts[marked.GetFingerPrint()] = markedHash
}
assertionhash = assertions.Mark(p).SaltedAssertionHash(salts)
} else {
assertionhash = latestsolution.SaltedAssertionHash(salts)
}
return assertionhash
}
func (assertions PackagesAssertions) SaltedAssertionHash(salts map[string]string) string {
var fingerprint string
for _, assertion := range assertions { // Note: Always order them first!
if assertion.Value { // Tke into account only dependencies installed (get fingerprint of subgraph)
salt, exists := salts[assertion.Package.GetFingerPrint()]
if exists {
fingerprint += assertion.ToString() + salt + "\n"
} else {
fingerprint += assertion.ToString() + "\n"
}
}
}
hash := sha256.Sum256([]byte(fingerprint))
return fmt.Sprintf("%x", hash)
}
func (assertions PackagesAssertions) Drop(p pkg.Package) PackagesAssertions {
ass := PackagesAssertions{}
for _, a := range assertions {
if !a.Package.Matches(p) {
ass = append(ass, a)
}
}
return ass
}
// Cut returns an assertion list of installed (filter by Value) "cutted" until the package is found (included)
func (assertions PackagesAssertions) Cut(p pkg.Package) PackagesAssertions {
ass := PackagesAssertions{}
for _, a := range assertions {
if a.Value {
ass = append(ass, a)
if a.Package.Matches(p) {
break
}
}
}
return ass
}
// Mark returns a new assertion with the package marked
func (assertions PackagesAssertions) Mark(p pkg.Package) PackagesAssertions {
ass := PackagesAssertions{}
for _, a := range assertions {
if a.Package.Matches(p) {
marked := a.Package.Mark()
a = PackageAssert{Package: marked.(*pkg.DefaultPackage), Value: a.Value, Hash: a.Hash}
}
ass = append(ass, a)
}
return ass
}

View File

@@ -18,8 +18,8 @@ package solver_test
import (
"strconv"
pkg "github.com/mudler/luet/pkg/package"
"github.com/mudler/luet/pkg/solver"
"github.com/mudler/luet/pkg/api/core/types"
pkg "github.com/mudler/luet/pkg/database"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -30,13 +30,13 @@ var _ = Describe("Decoder", func() {
db := pkg.NewInMemoryDatabase(false)
dbInstalled := pkg.NewInMemoryDatabase(false)
dbDefinitions := pkg.NewInMemoryDatabase(false)
s := NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
s := NewSolver(types.SolverOptions{Type: types.SolverSingleCoreSimple}, dbInstalled, dbDefinitions, db)
BeforeEach(func() {
db = pkg.NewInMemoryDatabase(false)
dbInstalled = pkg.NewInMemoryDatabase(false)
dbDefinitions = pkg.NewInMemoryDatabase(false)
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
s = NewSolver(types.SolverOptions{Type: types.SolverSingleCoreSimple}, dbInstalled, dbDefinitions, db)
})
Context("Assertion ordering", func() {
@@ -44,32 +44,32 @@ var _ = Describe("Decoder", func() {
for index := 0; index < 300; index++ { // Just to make sure we don't have false positives
It("Orders them correctly #"+strconv.Itoa(index), func() {
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
E := pkg.NewPackage("E", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
F := pkg.NewPackage("F", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
G := pkg.NewPackage("G", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H := pkg.NewPackage("H", "", []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
C := types.NewPackage("C", "", []*types.Package{}, []*types.Package{})
E := types.NewPackage("E", "", []*types.Package{}, []*types.Package{})
F := types.NewPackage("F", "", []*types.Package{}, []*types.Package{})
G := types.NewPackage("G", "", []*types.Package{}, []*types.Package{})
H := types.NewPackage("H", "", []*types.Package{G}, []*types.Package{})
D := types.NewPackage("D", "", []*types.Package{H}, []*types.Package{})
B := types.NewPackage("B", "", []*types.Package{D}, []*types.Package{})
A := types.NewPackage("A", "", []*types.Package{B}, []*types.Package{})
for _, p := range []pkg.Package{A, B, C, D, E, F, G} {
for _, p := range []*types.Package{A, B, C, D, E, F, G} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{C} {
for _, p := range []*types.Package{C} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{A})
Expect(solution).To(ContainElement(PackageAssert{Package: A, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: H, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: G, Value: true}))
solution, err := s.Install([]*types.Package{A})
Expect(solution).To(ContainElement(types.PackageAssert{Package: A, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: H, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: G, Value: true}))
Expect(len(solution)).To(Equal(6))
Expect(err).ToNot(HaveOccurred())
@@ -95,32 +95,32 @@ var _ = Describe("Decoder", func() {
for index := 0; index < 300; index++ { // Just to make sure we don't have false positives
It("Doesn't order them correctly otherwise #"+strconv.Itoa(index), func() {
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
E := pkg.NewPackage("E", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
F := pkg.NewPackage("F", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
G := pkg.NewPackage("G", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H := pkg.NewPackage("H", "", []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
C := types.NewPackage("C", "", []*types.Package{}, []*types.Package{})
E := types.NewPackage("E", "", []*types.Package{}, []*types.Package{})
F := types.NewPackage("F", "", []*types.Package{}, []*types.Package{})
G := types.NewPackage("G", "", []*types.Package{}, []*types.Package{})
H := types.NewPackage("H", "", []*types.Package{G}, []*types.Package{})
D := types.NewPackage("D", "", []*types.Package{H}, []*types.Package{})
B := types.NewPackage("B", "", []*types.Package{D}, []*types.Package{})
A := types.NewPackage("A", "", []*types.Package{B}, []*types.Package{})
for _, p := range []pkg.Package{A, B, C, D, E, F, G} {
for _, p := range []*types.Package{A, B, C, D, E, F, G} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{C} {
for _, p := range []*types.Package{C} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{A})
Expect(solution).To(ContainElement(PackageAssert{Package: A, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: H, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: G, Value: true}))
solution, err := s.Install([]*types.Package{A})
Expect(solution).To(ContainElement(types.PackageAssert{Package: A, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: H, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: G, Value: true}))
Expect(len(solution)).To(Equal(6))
Expect(err).ToNot(HaveOccurred())
@@ -161,32 +161,32 @@ var _ = Describe("Decoder", func() {
Context("Assertion hashing", func() {
It("Hashes them, and could be used for comparison", func() {
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
E := pkg.NewPackage("E", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
F := pkg.NewPackage("F", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
G := pkg.NewPackage("G", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
H := pkg.NewPackage("H", "", []*pkg.DefaultPackage{G}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{H}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{})
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
C := types.NewPackage("C", "", []*types.Package{}, []*types.Package{})
E := types.NewPackage("E", "", []*types.Package{}, []*types.Package{})
F := types.NewPackage("F", "", []*types.Package{}, []*types.Package{})
G := types.NewPackage("G", "", []*types.Package{}, []*types.Package{})
H := types.NewPackage("H", "", []*types.Package{G}, []*types.Package{})
D := types.NewPackage("D", "", []*types.Package{H}, []*types.Package{})
B := types.NewPackage("B", "", []*types.Package{D}, []*types.Package{})
A := types.NewPackage("A", "", []*types.Package{B}, []*types.Package{})
for _, p := range []pkg.Package{A, B, C, D, E, F, G} {
for _, p := range []*types.Package{A, B, C, D, E, F, G} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{C} {
for _, p := range []*types.Package{C} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{A})
Expect(solution).To(ContainElement(PackageAssert{Package: A, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: H, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: G, Value: true}))
solution, err := s.Install([]*types.Package{A})
Expect(solution).To(ContainElement(types.PackageAssert{Package: A, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: H, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: G, Value: true}))
Expect(len(solution)).To(Equal(6))
Expect(err).ToNot(HaveOccurred())
@@ -200,12 +200,12 @@ var _ = Describe("Decoder", func() {
hash := solution.AssertionHash()
solution, err = s.Install([]pkg.Package{B})
Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: H, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: G, Value: true}))
solution, err = s.Install([]*types.Package{B})
Expect(solution).To(ContainElement(types.PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: H, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: G, Value: true}))
Expect(len(solution)).To(Equal(6))
Expect(err).ToNot(HaveOccurred())
@@ -226,24 +226,24 @@ var _ = Describe("Decoder", func() {
})
It("Hashes them, and could be used for comparison", func() {
X := pkg.NewPackage("X", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
Y := pkg.NewPackage("Y", "", []*pkg.DefaultPackage{X}, []*pkg.DefaultPackage{})
Z := pkg.NewPackage("Z", "", []*pkg.DefaultPackage{X}, []*pkg.DefaultPackage{})
X := types.NewPackage("X", "", []*types.Package{}, []*types.Package{})
Y := types.NewPackage("Y", "", []*types.Package{X}, []*types.Package{})
Z := types.NewPackage("Z", "", []*types.Package{X}, []*types.Package{})
for _, p := range []pkg.Package{X, Y, Z} {
for _, p := range []*types.Package{X, Y, Z} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{} {
for _, p := range []*types.Package{} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{Y})
solution, err := s.Install([]*types.Package{Y})
Expect(err).ToNot(HaveOccurred())
solution2, err := s.Install([]pkg.Package{Z})
solution2, err := s.Install([]*types.Package{Z})
Expect(err).ToNot(HaveOccurred())
orderY, err := solution.Order(dbDefinitions, Y.GetFingerPrint())
Expect(err).ToNot(HaveOccurred())
@@ -254,24 +254,24 @@ var _ = Describe("Decoder", func() {
It("Hashes them, Cuts them and could be used for comparison", func() {
X := pkg.NewPackage("X", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
Y := pkg.NewPackage("Y", "", []*pkg.DefaultPackage{X}, []*pkg.DefaultPackage{})
Z := pkg.NewPackage("Z", "", []*pkg.DefaultPackage{X}, []*pkg.DefaultPackage{})
X := types.NewPackage("X", "", []*types.Package{}, []*types.Package{})
Y := types.NewPackage("Y", "", []*types.Package{X}, []*types.Package{})
Z := types.NewPackage("Z", "", []*types.Package{X}, []*types.Package{})
for _, p := range []pkg.Package{X, Y, Z} {
for _, p := range []*types.Package{X, Y, Z} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{} {
for _, p := range []*types.Package{} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{Y})
solution, err := s.Install([]*types.Package{Y})
Expect(err).ToNot(HaveOccurred())
solution2, err := s.Install([]pkg.Package{Z})
solution2, err := s.Install([]*types.Package{Z})
Expect(err).ToNot(HaveOccurred())
orderY, err := solution.Order(dbDefinitions, Y.GetFingerPrint())
Expect(err).ToNot(HaveOccurred())
@@ -284,24 +284,24 @@ var _ = Describe("Decoder", func() {
It("HashFrom can be used equally", func() {
X := pkg.NewPackage("X", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
Y := pkg.NewPackage("Y", "", []*pkg.DefaultPackage{X}, []*pkg.DefaultPackage{})
Z := pkg.NewPackage("Z", "", []*pkg.DefaultPackage{X}, []*pkg.DefaultPackage{})
X := types.NewPackage("X", "", []*types.Package{}, []*types.Package{})
Y := types.NewPackage("Y", "", []*types.Package{X}, []*types.Package{})
Z := types.NewPackage("Z", "", []*types.Package{X}, []*types.Package{})
for _, p := range []pkg.Package{X, Y, Z} {
for _, p := range []*types.Package{X, Y, Z} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{} {
for _, p := range []*types.Package{} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{Y})
solution, err := s.Install([]*types.Package{Y})
Expect(err).ToNot(HaveOccurred())
solution2, err := s.Install([]pkg.Package{Z})
solution2, err := s.Install([]*types.Package{Z})
Expect(err).ToNot(HaveOccurred())
orderY, err := solution.Order(dbDefinitions, Y.GetFingerPrint())
Expect(err).ToNot(HaveOccurred())
@@ -314,27 +314,27 @@ var _ = Describe("Decoder", func() {
It("Unique hashes for single packages", func() {
X := pkg.NewPackage("X", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
F := pkg.NewPackage("F", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("X", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
X := types.NewPackage("X", "", []*types.Package{}, []*types.Package{})
F := types.NewPackage("F", "", []*types.Package{}, []*types.Package{})
D := types.NewPackage("X", "", []*types.Package{}, []*types.Package{})
for _, p := range []pkg.Package{X, F, D} {
for _, p := range []*types.Package{X, F, D} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{} {
for _, p := range []*types.Package{} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{X})
solution, err := s.Install([]*types.Package{X})
Expect(err).ToNot(HaveOccurred())
solution2, err := s.Install([]pkg.Package{F})
solution2, err := s.Install([]*types.Package{F})
Expect(err).ToNot(HaveOccurred())
solution3, err := s.Install([]pkg.Package{D})
solution3, err := s.Install([]*types.Package{D})
Expect(err).ToNot(HaveOccurred())
Expect(solution.AssertionHash()).ToNot(Equal(solution2.AssertionHash()))
@@ -343,41 +343,41 @@ var _ = Describe("Decoder", func() {
})
It("Unique hashes for empty assertions", func() {
empty := solver.PackagesAssertions{}
empty2 := solver.PackagesAssertions{}
empty := types.PackagesAssertions{}
empty2 := types.PackagesAssertions{}
Expect(empty.AssertionHash()).To(Equal(empty2.AssertionHash()))
})
It("Unique hashes for single packages with HashFrom", func() {
X := pkg.NewPackage("X", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
F := pkg.NewPackage("F", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("X", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
Y := pkg.NewPackage("Y", "", []*pkg.DefaultPackage{X}, []*pkg.DefaultPackage{})
X := types.NewPackage("X", "", []*types.Package{}, []*types.Package{})
F := types.NewPackage("F", "", []*types.Package{}, []*types.Package{})
D := types.NewPackage("X", "", []*types.Package{}, []*types.Package{})
Y := types.NewPackage("Y", "", []*types.Package{X}, []*types.Package{})
empty := solver.PackagesAssertions{}
empty := types.PackagesAssertions{}
for _, p := range []pkg.Package{X, F, D, Y} {
for _, p := range []*types.Package{X, F, D, Y} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{} {
for _, p := range []*types.Package{} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{X})
solution, err := s.Install([]*types.Package{X})
Expect(err).ToNot(HaveOccurred())
solution2, err := s.Install([]pkg.Package{F})
solution2, err := s.Install([]*types.Package{F})
Expect(err).ToNot(HaveOccurred())
solution3, err := s.Install([]pkg.Package{D})
solution3, err := s.Install([]*types.Package{D})
Expect(err).ToNot(HaveOccurred())
solution4, err := s.Install([]pkg.Package{Y})
solution4, err := s.Install([]*types.Package{Y})
Expect(err).ToNot(HaveOccurred())
Expect(solution.HashFrom(X)).ToNot(Equal(solution2.HashFrom(F)))
@@ -395,22 +395,22 @@ var _ = Describe("Decoder", func() {
It("Always same solution", func() {
X := pkg.NewPackage("X", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
Y := pkg.NewPackage("Y", "", []*pkg.DefaultPackage{X}, []*pkg.DefaultPackage{})
Z := pkg.NewPackage("Z", "", []*pkg.DefaultPackage{X}, []*pkg.DefaultPackage{})
W := pkg.NewPackage("W", "", []*pkg.DefaultPackage{Z, Y}, []*pkg.DefaultPackage{})
X := types.NewPackage("X", "", []*types.Package{}, []*types.Package{})
Y := types.NewPackage("Y", "", []*types.Package{X}, []*types.Package{})
Z := types.NewPackage("Z", "", []*types.Package{X}, []*types.Package{})
W := types.NewPackage("W", "", []*types.Package{Z, Y}, []*types.Package{})
for _, p := range []pkg.Package{X, Y, Z} {
for _, p := range []*types.Package{X, Y, Z} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{} {
for _, p := range []*types.Package{} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{W})
solution, err := s.Install([]*types.Package{W})
Expect(err).ToNot(HaveOccurred())
orderW, err := solution.Order(dbDefinitions, W.GetFingerPrint())

135
pkg/solver/explainer.go Normal file
View File

@@ -0,0 +1,135 @@
// Copyright © 2021-2022 Ettore Di Giacinto <mudler@mocaccino.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 (
"bufio"
"bytes"
"fmt"
"io"
"strings"
"github.com/crillab/gophersat/bf"
"github.com/crillab/gophersat/explain"
types "github.com/mudler/luet/pkg/api/core/types"
"github.com/pkg/errors"
)
type Explainer struct{}
func decodeDimacs(vars map[string]string, dimacs string) (string, error) {
res := ""
sc := bufio.NewScanner(bytes.NewBufferString(dimacs))
lines := strings.Split(dimacs, "\n")
linenum := 1
SCAN:
for sc.Scan() {
line := sc.Text()
fields := strings.Fields(line)
if len(fields) == 0 {
continue
}
switch fields[0] {
case "p":
continue SCAN
default:
for i := 0; i < len(fields)-1; i++ {
v := fields[i]
negative := false
if strings.HasPrefix(fields[i], "-") {
v = strings.TrimLeft(fields[i], "-")
negative = true
}
variable := vars[v]
if negative {
res += fmt.Sprintf("!(%s)", variable)
} else {
res += variable
}
if i != len(fields)-2 {
res += fmt.Sprintf(" or ")
}
}
if linenum != len(lines)-1 {
res += fmt.Sprintf(" and \n")
}
}
linenum++
}
if err := sc.Err(); err != nil {
return res, fmt.Errorf("could not parse problem: %v", err)
}
return res, nil
}
func parseVars(r io.Reader) (map[string]string, error) {
sc := bufio.NewScanner(r)
res := map[string]string{}
for sc.Scan() {
line := sc.Text()
fields := strings.Fields(line)
if len(fields) == 0 {
continue
}
switch fields[0] {
case "c":
data := strings.Split(fields[1], "=")
res[data[1]] = data[0]
default:
continue
}
}
if err := sc.Err(); err != nil {
return nil, fmt.Errorf("could not parse problem: %v", err)
}
return res, nil
}
// Solve tries to find the MUS (minimum unsat) formula from the original problem.
// it returns an error with the decoded dimacs
func (*Explainer) Solve(f bf.Formula, s types.PackageSolver) (types.PackagesAssertions, error) {
buf := bytes.NewBufferString("")
if err := bf.Dimacs(f, buf); err != nil {
return nil, errors.Wrap(err, "cannot extract dimacs from formula")
}
copy := *buf
pb, err := explain.ParseCNF(&copy)
if err != nil {
return nil, errors.Wrap(err, "could not parse problem")
}
pb2, err := pb.MUS()
if err != nil {
return nil, errors.Wrap(err, "could not extract subset")
}
variables, err := parseVars(buf)
if err != nil {
return nil, errors.Wrap(err, "could not parse variables")
}
res, err := decodeDimacs(variables, pb2.CNF())
if err != nil {
return nil, errors.Wrap(err, "could not parse dimacs")
}
return nil, fmt.Errorf("could not satisfy the constraints: \n%s", res)
}

View File

@@ -16,21 +16,16 @@
package solver
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
"strconv"
"strings"
"github.com/crillab/gophersat/bf"
"github.com/crillab/gophersat/explain"
"github.com/mudler/luet/pkg/api/core/types"
"github.com/mudler/luet/pkg/helpers"
"gopkg.in/yaml.v2"
"github.com/ecooper/qlearning"
pkg "github.com/mudler/luet/pkg/package"
"github.com/pkg/errors"
)
@@ -56,117 +51,6 @@ const (
QLearningResolverType = "qlearning"
)
// PackageResolver assists PackageSolver on unsat cases
type PackageResolver interface {
Solve(bf.Formula, PackageSolver) (PackagesAssertions, error)
}
type Explainer struct{}
func decodeDimacs(vars map[string]string, dimacs string) (string, error) {
res := ""
sc := bufio.NewScanner(bytes.NewBufferString(dimacs))
lines := strings.Split(dimacs, "\n")
linenum := 1
SCAN:
for sc.Scan() {
line := sc.Text()
fields := strings.Fields(line)
if len(fields) == 0 {
continue
}
switch fields[0] {
case "p":
continue SCAN
default:
for i := 0; i < len(fields)-1; i++ {
v := fields[i]
negative := false
if strings.HasPrefix(fields[i], "-") {
v = strings.TrimLeft(fields[i], "-")
negative = true
}
variable := vars[v]
if negative {
res += fmt.Sprintf("!(%s)", variable)
} else {
res += variable
}
if i != len(fields)-2 {
res += fmt.Sprintf(" or ")
}
}
if linenum != len(lines)-1 {
res += fmt.Sprintf(" and \n")
}
}
linenum++
}
if err := sc.Err(); err != nil {
return res, fmt.Errorf("could not parse problem: %v", err)
}
return res, nil
}
func parseVars(r io.Reader) (map[string]string, error) {
sc := bufio.NewScanner(r)
res := map[string]string{}
for sc.Scan() {
line := sc.Text()
fields := strings.Fields(line)
if len(fields) == 0 {
continue
}
switch fields[0] {
case "c":
data := strings.Split(fields[1], "=")
res[data[1]] = data[0]
default:
continue
}
}
if err := sc.Err(); err != nil {
return nil, fmt.Errorf("could not parse problem: %v", err)
}
return res, nil
}
// Solve tries to find the MUS (minimum unsat) formula from the original problem.
// it returns an error with the decoded dimacs
func (*Explainer) Solve(f bf.Formula, s PackageSolver) (PackagesAssertions, error) {
buf := bytes.NewBufferString("")
if err := bf.Dimacs(f, buf); err != nil {
return nil, errors.Wrap(err, "cannot extract dimacs from formula")
}
copy := *buf
pb, err := explain.ParseCNF(&copy)
if err != nil {
return nil, errors.Wrap(err, "could not parse problem")
}
pb2, err := pb.MUS()
if err != nil {
return nil, errors.Wrap(err, "could not extract subset")
}
variables, err := parseVars(buf)
if err != nil {
return nil, errors.Wrap(err, "could not parse variables")
}
res, err := decodeDimacs(variables, pb2.CNF())
if err != nil {
return nil, errors.Wrap(err, "could not parse dimacs")
}
return nil, fmt.Errorf("could not satisfy the constraints: \n%s", res)
}
type QLearningResolver struct {
Attempts int
@@ -176,24 +60,24 @@ type QLearningResolver struct {
Attempted map[string]bool
Solver PackageSolver
Solver types.PackageSolver
Formula bf.Formula
Targets pkg.Packages
Current pkg.Packages
Targets types.Packages
Current types.Packages
observedDelta int
observedDeltaChoice pkg.Packages
observedDeltaChoice types.Packages
Agent *qlearning.SimpleAgent
}
func SimpleQLearningSolver() PackageResolver {
func SimpleQLearningSolver() types.PackageResolver {
return NewQLearningResolver(DefaultLearningRate, DefaultDiscount, DefaultMaxAttempts, DefaultInitialObserved)
}
// Defaults LearningRate 0.7, Discount 1.0
func NewQLearningResolver(LearningRate, Discount float32, MaxAttempts, initialObservedDelta int) PackageResolver {
func NewQLearningResolver(LearningRate, Discount float32, MaxAttempts, initialObservedDelta int) types.PackageResolver {
return &QLearningResolver{
Agent: qlearning.NewSimpleAgent(LearningRate, Discount),
observedDelta: initialObservedDelta,
@@ -201,7 +85,7 @@ func NewQLearningResolver(LearningRate, Discount float32, MaxAttempts, initialOb
}
}
func (resolver *QLearningResolver) Solve(f bf.Formula, s PackageSolver) (PackagesAssertions, error) {
func (resolver *QLearningResolver) Solve(f bf.Formula, s types.PackageSolver) (types.PackagesAssertions, error) {
// Info("Using QLearning solver to resolve conflicts. Please be patient.")
resolver.Solver = s
@@ -276,10 +160,10 @@ func (resolver *QLearningResolver) IsComplete() int {
func (resolver *QLearningResolver) Try(c Choice) error {
pack := c.Package
packtoAdd := pkg.FromString(pack)
packtoAdd := types.PackageFromString(pack)
resolver.Attempted[pack+strconv.Itoa(int(c.Action))] = true // increase the count
s, _ := resolver.Solver.(*Solver)
var filtered pkg.Packages
var filtered types.Packages
switch c.Action {
case ActionAdded:

View File

@@ -16,7 +16,9 @@
package solver_test
import (
pkg "github.com/mudler/luet/pkg/package"
"github.com/mudler/luet/pkg/api/core/types"
pkg "github.com/mudler/luet/pkg/database"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -28,65 +30,65 @@ var _ = Describe("Resolver", func() {
db := pkg.NewInMemoryDatabase(false)
dbInstalled := pkg.NewInMemoryDatabase(false)
dbDefinitions := pkg.NewInMemoryDatabase(false)
s := NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
s := NewSolver(types.SolverOptions{Type: types.SolverSingleCoreSimple}, dbInstalled, dbDefinitions, db)
BeforeEach(func() {
db = pkg.NewInMemoryDatabase(false)
dbInstalled = pkg.NewInMemoryDatabase(false)
dbDefinitions = pkg.NewInMemoryDatabase(false)
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
s = NewSolver(types.SolverOptions{Type: types.SolverSingleCoreSimple}, dbInstalled, dbDefinitions, db)
})
Context("Conflict set", func() {
Context("Explainer", func() {
It("is unsolvable - as we something we ask to install conflict with system stuff", func() {
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C})
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
C := types.NewPackage("C", "", []*types.Package{}, []*types.Package{})
B := types.NewPackage("B", "", []*types.Package{}, []*types.Package{C})
A := types.NewPackage("A", "", []*types.Package{B}, []*types.Package{})
for _, p := range []pkg.Package{A, B, C} {
for _, p := range []*types.Package{A, B, C} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{C} {
for _, p := range []*types.Package{C} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{A})
solution, err := s.Install([]*types.Package{A})
Expect(len(solution)).To(Equal(0))
Expect(err).To(HaveOccurred())
})
It("succeeds to install D and F if explictly requested", func() {
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C})
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
E := pkg.NewPackage("E", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
F := pkg.NewPackage("F", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
C := types.NewPackage("C", "", []*types.Package{}, []*types.Package{})
B := types.NewPackage("B", "", []*types.Package{}, []*types.Package{C})
A := types.NewPackage("A", "", []*types.Package{B}, []*types.Package{})
D := types.NewPackage("D", "", []*types.Package{}, []*types.Package{})
E := types.NewPackage("E", "", []*types.Package{B}, []*types.Package{})
F := types.NewPackage("F", "", []*types.Package{}, []*types.Package{})
for _, p := range []pkg.Package{A, B, C, D, E, F} {
for _, p := range []*types.Package{A, B, C, D, E, F} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{C} {
for _, p := range []*types.Package{C} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{D, F}) // D and F should go as they have no deps. A/E should be filtered by QLearn
solution, err := s.Install([]*types.Package{D, F}) // D and F should go as they have no deps. A/E should be filtered by QLearn
Expect(err).ToNot(HaveOccurred())
Expect(len(solution)).To(Equal(6))
Expect(solution).ToNot(ContainElement(PackageAssert{Package: A, Value: true}))
Expect(solution).ToNot(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).ToNot(ContainElement(PackageAssert{Package: E, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: F, Value: true}))
Expect(solution).ToNot(ContainElement(types.PackageAssert{Package: A, Value: true}))
Expect(solution).ToNot(ContainElement(types.PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: D, Value: true}))
Expect(solution).ToNot(ContainElement(types.PackageAssert{Package: E, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: F, Value: true}))
})
@@ -94,82 +96,82 @@ var _ = Describe("Resolver", func() {
Context("QLearningResolver", func() {
It("will find out that we can install D by ignoring A", func() {
s.SetResolver(SimpleQLearningSolver())
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C})
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
C := types.NewPackage("C", "", []*types.Package{}, []*types.Package{})
B := types.NewPackage("B", "", []*types.Package{}, []*types.Package{C})
A := types.NewPackage("A", "", []*types.Package{B}, []*types.Package{})
D := types.NewPackage("D", "", []*types.Package{}, []*types.Package{})
for _, p := range []pkg.Package{A, B, C, D} {
for _, p := range []*types.Package{A, B, C, D} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{C} {
for _, p := range []*types.Package{C} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{A, D})
solution, err := s.Install([]*types.Package{A, D})
Expect(err).ToNot(HaveOccurred())
Expect(solution).ToNot(ContainElement(PackageAssert{Package: A, Value: true}))
Expect(solution).ToNot(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).ToNot(ContainElement(types.PackageAssert{Package: A, Value: true}))
Expect(solution).ToNot(ContainElement(types.PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: D, Value: true}))
Expect(len(solution)).To(Equal(4))
})
It("will find out that we can install D and F by ignoring E and A", func() {
s.SetResolver(SimpleQLearningSolver())
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C})
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
E := pkg.NewPackage("E", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
F := pkg.NewPackage("F", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
C := types.NewPackage("C", "", []*types.Package{}, []*types.Package{})
B := types.NewPackage("B", "", []*types.Package{}, []*types.Package{C})
A := types.NewPackage("A", "", []*types.Package{B}, []*types.Package{})
D := types.NewPackage("D", "", []*types.Package{}, []*types.Package{})
E := types.NewPackage("E", "", []*types.Package{B}, []*types.Package{})
F := types.NewPackage("F", "", []*types.Package{}, []*types.Package{})
for _, p := range []pkg.Package{A, B, C, D, E, F} {
for _, p := range []*types.Package{A, B, C, D, E, F} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{C} {
for _, p := range []*types.Package{C} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{A, D, E, F}) // D and F should go as they have no deps. A/E should be filtered by QLearn
solution, err := s.Install([]*types.Package{A, D, E, F}) // D and F should go as they have no deps. A/E should be filtered by QLearn
Expect(err).ToNot(HaveOccurred())
Expect(solution).ToNot(ContainElement(PackageAssert{Package: A, Value: true}))
Expect(solution).ToNot(ContainElement(PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true})) // Was already installed
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
Expect(solution).ToNot(ContainElement(PackageAssert{Package: E, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: F, Value: true}))
Expect(solution).ToNot(ContainElement(types.PackageAssert{Package: A, Value: true}))
Expect(solution).ToNot(ContainElement(types.PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: C, Value: true})) // Was already installed
Expect(solution).To(ContainElement(types.PackageAssert{Package: D, Value: true}))
Expect(solution).ToNot(ContainElement(types.PackageAssert{Package: E, Value: true}))
Expect(solution).To(ContainElement(types.PackageAssert{Package: F, Value: true}))
Expect(len(solution)).To(Equal(6))
})
})
Context("Explainer", func() {
It("cannot find a solution", func() {
C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C})
A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
C := types.NewPackage("C", "", []*types.Package{}, []*types.Package{})
B := types.NewPackage("B", "", []*types.Package{}, []*types.Package{C})
A := types.NewPackage("A", "", []*types.Package{B}, []*types.Package{})
D := types.NewPackage("D", "", []*types.Package{}, []*types.Package{})
for _, p := range []pkg.Package{A, B, C, D} {
for _, p := range []*types.Package{A, B, C, D} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{C} {
for _, p := range []*types.Package{C} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
solution, err := s.Install([]pkg.Package{A, D})
solution, err := s.Install([]*types.Package{A, D})
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal(`could not satisfy the constraints:
A-- and

View File

@@ -19,68 +19,52 @@ import (
//. "github.com/mudler/luet/pkg/logger"
"fmt"
"strings"
"github.com/pkg/errors"
"github.com/crillab/gophersat/bf"
pkg "github.com/mudler/luet/pkg/package"
"github.com/mudler/luet/pkg/api/core/types"
pkg "github.com/mudler/luet/pkg/database"
)
type SolverType int
const (
SingleCoreSimple = 0
)
// PackageSolver is an interface to a generic package solving algorithm
type PackageSolver interface {
SetDefinitionDatabase(pkg.PackageDatabase)
Install(p pkg.Packages) (PackagesAssertions, error)
RelaxedInstall(p pkg.Packages) (PackagesAssertions, error)
Uninstall(checkconflicts, full bool, candidate ...pkg.Package) (pkg.Packages, error)
ConflictsWithInstalled(p pkg.Package) (bool, error)
ConflictsWith(p pkg.Package, ls pkg.Packages) (bool, error)
Conflicts(pack pkg.Package, lsp pkg.Packages) (bool, error)
World() pkg.Packages
Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error)
UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAssertions, error)
UninstallUniverse(toremove pkg.Packages) (pkg.Packages, error)
SetResolver(PackageResolver)
Solve() (PackagesAssertions, error)
// BestInstall(c pkg.Packages) (PackagesAssertions, error)
}
var AvailableResolvers = strings.Join([]string{QLearningResolverType}, " ")
// Solver is the default solver for luet
type Solver struct {
DefinitionDatabase pkg.PackageDatabase
SolverDatabase pkg.PackageDatabase
Wanted pkg.Packages
InstalledDatabase pkg.PackageDatabase
DefinitionDatabase types.PackageDatabase
SolverDatabase types.PackageDatabase
Wanted types.Packages
InstalledDatabase types.PackageDatabase
Resolver PackageResolver
}
type Options struct {
Type SolverType `yaml:"type,omitempty"`
Concurrency int `yaml:"concurrency,omitempty"`
Resolver types.PackageResolver
}
// NewSolver accepts as argument two lists of packages, the first is the initial set,
// the second represent all the known packages.
func NewSolver(t Options, installed pkg.PackageDatabase, definitiondb pkg.PackageDatabase, solverdb pkg.PackageDatabase) PackageSolver {
func NewSolver(t types.SolverOptions, installed types.PackageDatabase, definitiondb types.PackageDatabase, solverdb types.PackageDatabase) types.PackageSolver {
return NewResolver(t, installed, definitiondb, solverdb, &Explainer{})
}
func NewSolverFromOptions(t types.LuetSolverOptions) types.PackageResolver {
switch t.Type {
case QLearningResolverType:
if t.LearnRate != 0.0 {
return NewQLearningResolver(t.LearnRate, t.Discount, t.MaxAttempts, 999999)
}
return SimpleQLearningSolver()
}
return &Explainer{}
}
// NewResolver accepts as argument two lists of packages, the first is the initial set,
// the second represent all the known packages.
// 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
func NewResolver(t types.SolverOptions, installed types.PackageDatabase, definitiondb types.PackageDatabase, solverdb types.PackageDatabase, re types.PackageResolver) types.PackageSolver {
var s types.PackageSolver
switch t.Type {
default:
s = &Solver{InstalledDatabase: installed, DefinitionDatabase: definitiondb, SolverDatabase: solverdb, Resolver: re}
@@ -91,20 +75,20 @@ func NewResolver(t Options, installed pkg.PackageDatabase, definitiondb pkg.Pack
// SetDefinitionDatabase is a setter for the definition Database
func (s *Solver) SetDefinitionDatabase(db pkg.PackageDatabase) {
func (s *Solver) SetDefinitionDatabase(db types.PackageDatabase) {
s.DefinitionDatabase = db
}
// SetResolver is a setter for the unsat resolver backend
func (s *Solver) SetResolver(r PackageResolver) {
func (s *Solver) SetResolver(r types.PackageResolver) {
s.Resolver = r
}
func (s *Solver) World() pkg.Packages {
func (s *Solver) World() types.Packages {
return s.DefinitionDatabase.World()
}
func (s *Solver) Installed() pkg.Packages {
func (s *Solver) Installed() types.Packages {
return s.InstalledDatabase.World()
}
@@ -131,7 +115,7 @@ func (s *Solver) noRulesInstalled() bool {
func (s *Solver) BuildInstalled() (bf.Formula, error) {
var formulas []bf.Formula
var packages pkg.Packages
var packages types.Packages
for _, p := range s.Installed() {
packages = append(packages, p)
for _, dep := range p.Related(s.InstalledDatabase) {
@@ -190,7 +174,7 @@ func (s *Solver) BuildPartialWorld(includeInstalled bool) (bf.Formula, error) {
formulas = append(formulas, solvable)
}
var packages pkg.Packages
var packages types.Packages
for _, p := range s.Wanted {
// packages = append(packages, p)
for _, dep := range p.Related(s.DefinitionDatabase) {
@@ -214,8 +198,8 @@ func (s *Solver) BuildPartialWorld(includeInstalled bool) (bf.Formula, error) {
return bf.True, nil
}
func (s *Solver) getList(db pkg.PackageDatabase, lsp pkg.Packages) (pkg.Packages, error) {
var ls pkg.Packages
func (s *Solver) getList(db types.PackageDatabase, lsp types.Packages) (types.Packages, error) {
var ls types.Packages
for _, pp := range lsp {
cp, err := db.FindPackage(pp)
@@ -235,7 +219,7 @@ func (s *Solver) getList(db pkg.PackageDatabase, lsp pkg.Packages) (pkg.Packages
// 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) {
func (s *Solver) Conflicts(pack *types.Package, lsp types.Packages) (bool, error) {
p, err := s.DefinitionDatabase.FindPackage(pack)
if err != nil {
p = pack
@@ -273,7 +257,7 @@ func (s *Solver) Conflicts(pack pkg.Package, lsp pkg.Packages) (bool, error) {
// 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)
func (s *Solver) ConflictsWith(pack pkg.Package, lsp pkg.Packages) (bool, error) {
func (s *Solver) ConflictsWith(pack *types.Package, lsp types.Packages) (bool, error) {
p, err := s.DefinitionDatabase.FindPackage(pack)
if err != nil {
p = pack //Relax search, otherwise we cannot compute solutions for packages not in definitions
@@ -329,7 +313,7 @@ func (s *Solver) ConflictsWith(pack pkg.Package, lsp pkg.Packages) (bool, error)
}
func (s *Solver) ConflictsWithInstalled(p pkg.Package) (bool, error) {
func (s *Solver) ConflictsWithInstalled(p *types.Package) (bool, error) {
return s.ConflictsWith(p, s.Installed())
}
@@ -339,7 +323,7 @@ func (s *Solver) ConflictsWithInstalled(p pkg.Package) (bool, error) {
// 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) {
func (s *Solver) UninstallUniverse(toremove types.Packages) (types.Packages, error) {
if s.noRulesInstalled() {
return s.getList(s.InstalledDatabase, toremove)
@@ -367,7 +351,7 @@ func (s *Solver) UninstallUniverse(toremove pkg.Packages) (pkg.Packages, error)
formulas = append(formulas, bf.And(bf.Not(P), r))
}
markedForRemoval := pkg.Packages{}
markedForRemoval := types.Packages{}
model := bf.Solve(bf.And(formulas...))
if model == nil {
return nil, errors.New("Failed finding a solution")
@@ -390,14 +374,14 @@ func (s *Solver) UninstallUniverse(toremove pkg.Packages) (pkg.Packages, error)
// 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) {
func (s *Solver) UpgradeUniverse(dropremoved bool) (types.Packages, types.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{}
replacements := map[pkg.Package]pkg.Package{}
notUptodate := types.Packages{}
removed := types.Packages{}
toUpgrade := types.Packages{}
replacements := map[*types.Package]*types.Package{}
// TODO: this is memory expensive, we need to optimize this
universe, err := s.DefinitionDatabase.Copy()
@@ -467,10 +451,10 @@ func (s *Solver) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAssert
//formulas = append(formulas, r)
markedForRemoval := pkg.Packages{}
markedForRemoval := types.Packages{}
if len(formulas) == 0 {
return pkg.Packages{}, PackagesAssertions{}, nil
return types.Packages{}, types.PackagesAssertions{}, nil
}
model := bf.Solve(bf.And(formulas...))
if model == nil {
@@ -493,7 +477,7 @@ func (s *Solver) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAssert
return markedForRemoval, assertion, nil
}
func inPackage(list []pkg.Package, p pkg.Package) bool {
func inPackage(list []*types.Package, p *types.Package) bool {
for _, l := range list {
if l.AtomMatches(p) {
return true
@@ -503,10 +487,10 @@ func inPackage(list []pkg.Package, p pkg.Package) bool {
}
// Compute upgrade between packages if specified, or all if none is specified
func (s *Solver) computeUpgrade(ppsToUpgrade, ppsToNotUpgrade []pkg.Package) func(defDB pkg.PackageDatabase, installDB pkg.PackageDatabase) (pkg.Packages, pkg.Packages, pkg.PackageDatabase, []pkg.Package) {
return func(defDB pkg.PackageDatabase, installDB pkg.PackageDatabase) (pkg.Packages, pkg.Packages, pkg.PackageDatabase, []pkg.Package) {
toUninstall := pkg.Packages{}
toInstall := pkg.Packages{}
func (s *Solver) computeUpgrade(ppsToUpgrade, ppsToNotUpgrade []*types.Package) func(defDB types.PackageDatabase, installDB types.PackageDatabase) (types.Packages, types.Packages, types.PackageDatabase, []*types.Package) {
return func(defDB types.PackageDatabase, installDB types.PackageDatabase) (types.Packages, types.Packages, types.PackageDatabase, []*types.Package) {
toUninstall := types.Packages{}
toInstall := types.Packages{}
// we do this in memory so we take into account of provides, and its faster
universe, _ := defDB.Copy()
@@ -535,15 +519,25 @@ func (s *Solver) computeUpgrade(ppsToUpgrade, ppsToNotUpgrade []pkg.Package) fun
}
}
func (s *Solver) upgrade(psToUpgrade, psToNotUpgrade pkg.Packages, fn func(defDB pkg.PackageDatabase, installDB pkg.PackageDatabase) (pkg.Packages, pkg.Packages, pkg.PackageDatabase, []pkg.Package), defDB pkg.PackageDatabase, installDB pkg.PackageDatabase, checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) {
func assertionToMemDB(assertions types.PackagesAssertions) types.PackageDatabase {
db := pkg.NewInMemoryDatabase(false)
for _, a := range assertions {
if a.Value {
db.CreatePackage(a.Package)
}
}
return db
}
func (s *Solver) upgrade(psToUpgrade, psToNotUpgrade types.Packages, fn func(defDB types.PackageDatabase, installDB types.PackageDatabase) (types.Packages, types.Packages, types.PackageDatabase, []*types.Package), defDB types.PackageDatabase, installDB types.PackageDatabase, checkconflicts, full bool) (types.Packages, types.PackagesAssertions, error) {
toUninstall, toInstall, installedcopy, packsToUpgrade := fn(defDB, installDB)
s2 := NewSolver(Options{Type: SingleCoreSimple}, installedcopy, defDB, pkg.NewInMemoryDatabase(false))
s2 := NewSolver(types.SolverOptions{Type: types.SolverSingleCoreSimple}, installedcopy, defDB, pkg.NewInMemoryDatabase(false))
s2.SetResolver(s.Resolver)
if !full {
ass := PackagesAssertions{}
ass := types.PackagesAssertions{}
for _, i := range toInstall {
ass = append(ass, PackageAssert{Package: i.(*pkg.DefaultPackage), Value: true})
ass = append(ass, types.PackageAssert{Package: i, Value: true})
}
}
// Then try to uninstall the versions in the system, and store that tree
@@ -559,23 +553,23 @@ func (s *Solver) upgrade(psToUpgrade, psToNotUpgrade pkg.Packages, fn func(defDB
}
if len(toInstall) == 0 {
ass := PackagesAssertions{}
ass := types.PackagesAssertions{}
for _, i := range installDB.World() {
ass = append(ass, PackageAssert{Package: i.(*pkg.DefaultPackage), Value: true})
ass = append(ass, types.PackageAssert{Package: i, Value: true})
}
return toUninstall, ass, nil
}
assertions, err := s2.RelaxedInstall(toInstall.Unique())
wantedSystem := assertions.ToDB()
wantedSystem := assertionToMemDB(assertions)
fn = s.computeUpgrade(pkg.Packages{}, pkg.Packages{})
fn = s.computeUpgrade(types.Packages{}, types.Packages{})
if len(packsToUpgrade) > 0 {
// If we have packages in input,
// compute what we are looking to upgrade.
// those are assertions minus packsToUpgrade
var selectedPackages []pkg.Package
var selectedPackages []*types.Package
for _, p := range assertions {
if p.Value && !inPackage(psToUpgrade, p.Package) {
@@ -593,25 +587,25 @@ func (s *Solver) upgrade(psToUpgrade, psToNotUpgrade pkg.Packages, fn func(defDB
return toUninstall, assertions, err
}
func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) {
func (s *Solver) Upgrade(checkconflicts, full bool) (types.Packages, types.PackagesAssertions, error) {
installedcopy := pkg.NewInMemoryDatabase(false)
err := s.InstalledDatabase.Clone(installedcopy)
if err != nil {
return nil, nil, err
}
return s.upgrade(pkg.Packages{}, pkg.Packages{}, s.computeUpgrade(pkg.Packages{}, pkg.Packages{}), s.DefinitionDatabase, installedcopy, checkconflicts, full)
return s.upgrade(types.Packages{}, types.Packages{}, s.computeUpgrade(types.Packages{}, types.Packages{}), s.DefinitionDatabase, installedcopy, checkconflicts, full)
}
// 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.
func (s *Solver) Uninstall(checkconflicts, full bool, packs ...pkg.Package) (pkg.Packages, error) {
func (s *Solver) Uninstall(checkconflicts, full bool, packs ...*types.Package) (types.Packages, error) {
if len(packs) == 0 {
return pkg.Packages{}, nil
return types.Packages{}, nil
}
var res pkg.Packages
var res types.Packages
toRemove := pkg.Packages{}
toRemove := types.Packages{}
for _, c := range packs {
candidate, err := s.InstalledDatabase.FindPackage(c)
@@ -633,7 +627,7 @@ func (s *Solver) Uninstall(checkconflicts, full bool, packs ...pkg.Package) (pkg
}
// Build a fake "Installed" - Candidate and its requires tree
var InstalledMinusCandidate pkg.Packages
var InstalledMinusCandidate types.Packages
// 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
@@ -666,7 +660,7 @@ func (s *Solver) Uninstall(checkconflicts, full bool, packs ...pkg.Package) (pkg
}
}
s2 := NewSolver(Options{Type: SingleCoreSimple}, pkg.NewInMemoryDatabase(false), s.InstalledDatabase, pkg.NewInMemoryDatabase(false))
s2 := NewSolver(types.SolverOptions{Type: types.SolverSingleCoreSimple}, pkg.NewInMemoryDatabase(false), s.InstalledDatabase, pkg.NewInMemoryDatabase(false))
s2.SetResolver(s.Resolver)
// Get the requirements to install the candidate
@@ -756,7 +750,7 @@ func (s *Solver) solve(f bf.Formula) (map[string]bool, bf.Formula, error) {
}
// Solve builds the formula given the current state and returns package assertions
func (s *Solver) Solve() (PackagesAssertions, error) {
func (s *Solver) Solve() (types.PackagesAssertions, error) {
var model map[string]bool
var err error
@@ -780,7 +774,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
// to statisfy all the constraints
func (s *Solver) RelaxedInstall(c pkg.Packages) (PackagesAssertions, error) {
func (s *Solver) RelaxedInstall(c types.Packages) (types.PackagesAssertions, error) {
coll, err := s.getList(s.DefinitionDatabase, c)
if err != nil {
@@ -790,13 +784,13 @@ func (s *Solver) RelaxedInstall(c pkg.Packages) (PackagesAssertions, error) {
s.Wanted = coll
if s.noRulesWorld() {
var ass PackagesAssertions
var ass types.PackagesAssertions
for _, p := range s.Installed() {
ass = append(ass, PackageAssert{Package: p.(*pkg.DefaultPackage), Value: true})
ass = append(ass, types.PackageAssert{Package: p, Value: true})
}
for _, p := range s.Wanted {
ass = append(ass, PackageAssert{Package: p.(*pkg.DefaultPackage), Value: true})
ass = append(ass, types.PackageAssert{Package: p, Value: true})
}
return ass, nil
}
@@ -811,7 +805,7 @@ func (s *Solver) RelaxedInstall(c pkg.Packages) (PackagesAssertions, error) {
// 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) {
func (s *Solver) Install(c types.Packages) (types.PackagesAssertions, error) {
assertions, err := s.RelaxedInstall(c)
if err != nil {
return nil, err
@@ -819,8 +813,8 @@ func (s *Solver) Install(c pkg.Packages) (PackagesAssertions, error) {
systemAfterInstall := pkg.NewInMemoryDatabase(false)
toUpgrade := pkg.Packages{}
toNotUpgrade := pkg.Packages{}
toUpgrade := types.Packages{}
toNotUpgrade := types.Packages{}
for _, p := range c {
if p.GetVersion() == ">=0" || p.GetVersion() == ">0" {
toUpgrade = append(toUpgrade, p)

File diff suppressed because it is too large Load Diff