diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index a7b12c74..00a864e1 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -468,7 +468,7 @@ func (cs *LuetCompiler) ComputeDepTree(p CompilationSpec) (solver.PackagesAssert nthsolution := dependencies.Cut(assertion.Package) assertion.Hash = solver.PackageHash{ - BuildHash: nthsolution.Drop(assertion.Package).AssertionHash(), + BuildHash: nthsolution.HashFrom(assertion.Package), PackageHash: nthsolution.AssertionHash(), } assertions = append(assertions, assertion) diff --git a/pkg/package/package.go b/pkg/package/package.go index 415ddc3f..5f68ec4e 100644 --- a/pkg/package/package.go +++ b/pkg/package/package.go @@ -59,6 +59,7 @@ type Package interface { SetCategory(string) GetName() string + SetName(string) GetCategory() string GetVersion() string @@ -98,6 +99,8 @@ type Package interface { String() string HumanReadableString() string HashFingerprint() string + + Clone() Package } type Tree interface { @@ -337,6 +340,11 @@ func (p *DefaultPackage) GetCategory() string { func (p *DefaultPackage) SetCategory(s string) { p.Category = s } + +func (p *DefaultPackage) SetName(s string) { + p.Name = s +} + func (p *DefaultPackage) GetUses() []string { return p.UseFlags } diff --git a/pkg/solver/decoder.go b/pkg/solver/decoder.go index 05118c4d..17bd0ab8 100644 --- a/pkg/solver/decoder.go +++ b/pkg/solver/decoder.go @@ -251,6 +251,34 @@ func (a PackagesAssertions) Less(i, j int) bool { } +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 { + + 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 { + assertionhash = assertions.Mark(p).AssertionHash() + } else { + assertionhash = latestsolution.AssertionHash() + } + return assertionhash +} + func (assertions PackagesAssertions) AssertionHash() string { var fingerprint string for _, assertion := range assertions { // Note: Always order them first! @@ -287,3 +315,18 @@ func (assertions PackagesAssertions) Cut(p pkg.Package) PackagesAssertions { } 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.Clone() + marked.SetName("@@" + marked.GetName()) + a = PackageAssert{Package: marked.(*pkg.DefaultPackage), Value: a.Value, Hash: a.Hash} + } + ass = append(ass, a) + } + return ass +} diff --git a/pkg/solver/decoder_test.go b/pkg/solver/decoder_test.go index c888b6d7..175f86cb 100644 --- a/pkg/solver/decoder_test.go +++ b/pkg/solver/decoder_test.go @@ -19,6 +19,7 @@ import ( "strconv" pkg "github.com/mudler/luet/pkg/package" + "github.com/mudler/luet/pkg/solver" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -283,5 +284,111 @@ var _ = Describe("Decoder", func() { Expect(orderY.Cut(Y).Drop(Y).AssertionHash()).To(Equal(orderZ.Cut(Z).Drop(Z).AssertionHash())) }) + 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{}) + + for _, p := range []pkg.Package{X, Y, Z} { + _, err := dbDefinitions.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + for _, p := range []pkg.Package{} { + _, err := dbInstalled.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + solution, err := s.Install([]pkg.Package{Y}) + Expect(err).ToNot(HaveOccurred()) + + solution2, err := s.Install([]pkg.Package{Z}) + Expect(err).ToNot(HaveOccurred()) + orderY, err := solution.Order(dbDefinitions, Y.GetFingerPrint()) + Expect(err).ToNot(HaveOccurred()) + orderZ, err := solution2.Order(dbDefinitions, Z.GetFingerPrint()) + Expect(err).ToNot(HaveOccurred()) + Expect(orderY.Cut(Y).Drop(Y)).To(Equal(orderZ.Cut(Z).Drop(Z))) + + Expect(orderY.Cut(Y).HashFrom(Y)).To(Equal(orderZ.Cut(Z).HashFrom(Z))) + }) + + 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{}) + + for _, p := range []pkg.Package{X, F, D} { + _, err := dbDefinitions.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + for _, p := range []pkg.Package{} { + _, err := dbInstalled.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + solution, err := s.Install([]pkg.Package{X}) + Expect(err).ToNot(HaveOccurred()) + + solution2, err := s.Install([]pkg.Package{F}) + Expect(err).ToNot(HaveOccurred()) + + solution3, err := s.Install([]pkg.Package{D}) + Expect(err).ToNot(HaveOccurred()) + + Expect(solution.AssertionHash()).ToNot(Equal(solution2.AssertionHash())) + Expect(solution3.AssertionHash()).To(Equal(solution.AssertionHash())) + + }) + + It("Unique hashes for empty assertions", func() { + empty := solver.PackagesAssertions{} + empty2 := solver.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{}) + + empty := solver.PackagesAssertions{} + + for _, p := range []pkg.Package{X, F, D, Y} { + _, err := dbDefinitions.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + for _, p := range []pkg.Package{} { + _, err := dbInstalled.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + solution, err := s.Install([]pkg.Package{X}) + Expect(err).ToNot(HaveOccurred()) + + solution2, err := s.Install([]pkg.Package{F}) + Expect(err).ToNot(HaveOccurred()) + + solution3, err := s.Install([]pkg.Package{D}) + Expect(err).ToNot(HaveOccurred()) + + solution4, err := s.Install([]pkg.Package{Y}) + Expect(err).ToNot(HaveOccurred()) + + Expect(solution.HashFrom(X)).ToNot(Equal(solution2.HashFrom(F))) + Expect(solution3.HashFrom(D)).To(Equal(solution.HashFrom(X))) + + Expect(empty.AssertionHash()).ToNot(Equal(solution3.HashFrom(D))) + Expect(empty.AssertionHash()).ToNot(Equal(solution2.HashFrom(F))) + + Expect(solution4.Drop(Y).AssertionHash()).To(Equal(solution4.HashFrom(Y))) + }) }) })