Add Tree parser with bolt

Also add Tree builder (Recipe) which can recompose trees.
This commit is contained in:
Ettore Di Giacinto
2019-10-30 17:52:57 +01:00
committed by Ettore Di Giacinto
parent 9d0dc601b7
commit a5ceafca26
16 changed files with 574 additions and 78 deletions

79
cmd/convert.go Normal file
View File

@@ -0,0 +1,79 @@
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/>.
package cmd
import (
"fmt"
"log"
"os"
tree "github.com/mudler/luet/pkg/tree"
"github.com/mudler/luet/pkg/tree/builder/gentoo"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var convertCmd = &cobra.Command{
Use: "convert",
Short: "convert other package manager tree into luet",
Long: `Parses external PM and produces a luet parsable tree`,
Run: func(cmd *cobra.Command, args []string) {
t := viper.GetString("type")
if len(args) != 2 {
log.Fatalln("Incorrect number of arguments")
}
input := args[0]
output := args[1]
fmt.Println("Converting trees from" + input + " [" + t + "]")
var builder tree.Parser
switch t {
case "gentoo":
builder = gentoo.NewGentooBuilder(&gentoo.SimpleEbuildParser{})
default: // dup
builder = gentoo.NewGentooBuilder(&gentoo.SimpleEbuildParser{})
}
packageTree, err := builder.Generate(input)
if err != nil {
fmt.Println("Error: " + err.Error())
os.Exit(1)
}
defer packageTree.GetPackageSet().Clean()
fmt.Println("Tree generated")
generalRecipe := tree.NewGeneralRecipe()
fmt.Println("Saving generated tree to " + output)
generalRecipe.WithTree(packageTree)
err = generalRecipe.Save(output)
if err != nil {
fmt.Println("Error: " + err.Error())
os.Exit(1)
}
},
}
func init() {
convertCmd.Flags().String("type", "gentoo", "source type")
viper.BindPFlag("type", convertCmd.Flags().Lookup("type"))
RootCmd.AddCommand(convertCmd)
}

View File

@@ -1,41 +0,0 @@
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/>.
package cmd
import (
"log"
"github.com/spf13/cobra"
)
var importCmd = &cobra.Command{
Use: "import",
Short: "imports other package manager tree into luet",
Long: `Parses external PM and produces a luet parsable tree`,
Run: func(cmd *cobra.Command, args []string) {
//Output := viper.GetString("output")
if len(args) == 0 {
log.Fatalln("Insufficient arguments")
}
//input := args[0]
},
}
func init() {
RootCmd.AddCommand(importCmd)
}

104
cmd/query.go Normal file
View File

@@ -0,0 +1,104 @@
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/>.
package cmd
import (
"fmt"
"log"
"os"
pkg "github.com/mudler/luet/pkg/package"
"github.com/mudler/luet/pkg/solver"
tree "github.com/mudler/luet/pkg/tree"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var queryCmd = &cobra.Command{
Use: "query install <pkg>",
Short: "query other package manager tree into luet",
Long: `Parses external PM and produces a luet parsable tree`,
Run: func(cmd *cobra.Command, args []string) {
input := viper.GetString("input")
if len(args) != 3 {
log.Fatalln("Incorrect number of arguments")
}
generalRecipe := tree.NewGeneralRecipe()
fmt.Println("Loading generated tree from " + input)
err := generalRecipe.Load(input)
if err != nil {
fmt.Println("Error: " + err.Error())
os.Exit(1)
}
defer generalRecipe.Tree().GetPackageSet().Clean()
t := args[0]
v := args[1]
version := args[2]
switch t {
case "install":
// XXX: pack needs to be the same which is present in world.
// Tree caches generated world when using FindPackage
pack, err := generalRecipe.Tree().FindPackage(&pkg.DefaultPackage{Name: v, Version: version})
if err != nil {
fmt.Println("Error: " + err.Error())
os.Exit(1)
}
fmt.Println("Install query from " + input + " [" + v + "]")
world, err := generalRecipe.Tree().World()
if err != nil {
fmt.Println("Error: " + err.Error())
os.Exit(1)
}
fmt.Println(">>> World")
for _, packss := range world {
fmt.Println(packss)
}
s := solver.NewSolver([]pkg.Package{}, world)
solution, err := s.Install([]pkg.Package{pack})
if err != nil {
fmt.Println("Error: " + err.Error())
os.Exit(1)
}
fmt.Println(">>> Solution")
for _, assertion := range solution {
fmt.Println(assertion.Package, assertion.Value)
for _, req := range assertion.Package.GetRequires() {
fmt.Println("\t-> ", req)
}
for _, con := range assertion.Package.GetConflicts() {
fmt.Println("\t!! ", con)
}
}
}
},
}
func init() {
queryCmd.Flags().String("input", "", "source folder")
viper.BindPFlag("input", queryCmd.Flags().Lookup("input"))
RootCmd.AddCommand(queryCmd)
}

View File

@@ -22,7 +22,6 @@ import (
"path"
"path/filepath"
"github.com/fsnotify/fsnotify"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
@@ -60,7 +59,7 @@ func initConfig() {
}
viper.SetConfigName(".luet") // name of config file (without extension)
if cfgFile != "" { // enable ability to specify config file via flag
if cfgFile != "" { // enable ability to specify config file via flag
fmt.Println(">>> cfgFile: ", cfgFile)
viper.SetConfigFile(cfgFile)
configDir := path.Dir(cfgFile)
@@ -80,8 +79,5 @@ func initConfig() {
} else {
fmt.Println(err)
}
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
})
}

View File

@@ -26,6 +26,5 @@ type PackageDatabase interface {
Create([]byte) (string, error)
Retrieve(ID string) ([]byte, error)
FindPackage(name, version string) (Package, error)
UpdatePackage(p Package) error
}

View File

@@ -17,6 +17,7 @@ package pkg
import (
"errors"
"os"
"strconv"
storm "github.com/asdine/storm"
@@ -54,7 +55,7 @@ func (db *BoltDatabase) Retrieve(ID string) ([]byte, error) {
return []byte{}, errors.New("Not implemented")
}
func (db *BoltDatabase) FindPackage(name, version string) (Package, error) {
func (db *BoltDatabase) FindPackage(tofind Package) (Package, error) {
p := &DefaultPackage{}
bolt, err := storm.Open(db.Path)
if err != nil {
@@ -62,7 +63,7 @@ func (db *BoltDatabase) FindPackage(name, version string) (Package, error) {
}
defer bolt.Close()
err = bolt.Select(q.Eq("Name", name), q.Eq("Version", version)).Limit(1).First(p)
err = bolt.Select(q.Eq("Name", tofind.GetName()), q.Eq("Category", tofind.GetCategory()), q.Eq("Version", tofind.GetVersion())).Limit(1).First(p)
if err != nil {
return nil, err
}
@@ -142,3 +143,7 @@ func (db *BoltDatabase) CreatePackage(p Package) (string, error) {
return strconv.Itoa(dp.ID), err
}
func (db *BoltDatabase) Clean() error {
return os.RemoveAll(db.Path)
}

View File

@@ -109,7 +109,7 @@ func (db *InMemoryDatabase) CreatePackage(p Package) (string, error) {
return ID, nil
}
func (db *InMemoryDatabase) FindPackage(name, version string) (Package, error) {
func (db *InMemoryDatabase) FindPackage(p Package) (Package, error) {
return nil, errors.New("Not implemented")
}
@@ -120,3 +120,8 @@ func (db *InMemoryDatabase) UpdatePackage(p Package) error {
func (db *InMemoryDatabase) GetPackages() []string {
return []string{}
}
func (db *InMemoryDatabase) Clean() error {
db.Database = map[string]string{}
return nil
}

View File

@@ -20,8 +20,9 @@ import (
"github.com/crillab/gophersat/bf"
version "github.com/hashicorp/go-version"
"github.com/jinzhu/copier"
"github.com/ghodss/yaml"
)
// Package is a package interface (TBD)
@@ -50,30 +51,48 @@ type Package interface {
AddUse(use string)
RemoveUse(use string)
GetUses() []string
Yaml() ([]byte, error)
}
type PackageSet interface {
GetPackages() []string //Ids
CreatePackage(pkg Package) (string, error)
GetPackage(ID string) (Package, error)
Clean() error
FindPackage(Package) (Package, error)
}
type Tree interface {
GetPackageSet() PackageSet
Prelude() string // A tree might have a prelude to be able to consume a tree
SetPackageSet(s PackageSet)
World() ([]Package, error)
FindPackage(Package) (Package, error)
}
// >> Unmarshallers
// DefaultPackageFromYaml decodes a package from yaml bytes
func DefaultPackageFromYaml(source []byte) (DefaultPackage, error) {
var pkg DefaultPackage
err := yaml.Unmarshal(source, &pkg)
if err != nil {
return pkg, err
}
return pkg, nil
}
// DefaultPackage represent a standard package definition
type DefaultPackage struct {
ID int `storm:"id,increment"` // primary key with auto increment
Name string
Version string
Category string
UseFlags []string
ID int `storm:"id,increment"` // primary key with auto increment
Name string `json:"name"` // Affects YAML field names too.
Version string `json:"version"` // Affects YAML field names too.
Category string `json:"category"` // Affects YAML field names too.
UseFlags []string `json:"use_flags"` // Affects YAML field names too.
State State
PackageRequires []*DefaultPackage
PackageConflicts []*DefaultPackage
IsSet bool
PackageRequires []*DefaultPackage `json:"requires"` // Affects YAML field names too.
PackageConflicts []*DefaultPackage `json:"conflicts"` // Affects YAML field names too.
IsSet bool `json:"set"` // Affects YAML field names too.
}
// State represent the package state
@@ -117,6 +136,15 @@ func (p *DefaultPackage) Encode() (string, error) {
return NewInMemoryDatabase().CreatePackage(p)
}
func (p *DefaultPackage) Yaml() ([]byte, error) {
y, err := yaml.Marshal(p)
if err != nil {
return []byte{}, err
}
return y, nil
}
func (p *DefaultPackage) IsFlagged(b bool) Package {
p.IsSet = b
return p

View File

@@ -94,8 +94,8 @@ var _ = Describe("Package", func() {
f, err := a1.BuildFormula()
Expect(err).ToNot(HaveOccurred())
Expect(len(f)).To(Equal(2))
Expect(f[0].String()).To(Equal("or(not(fd21d375), c85f382e)"))
Expect(f[1].String()).To(Equal("or(not(fd21d375), not(f512c160))"))
Expect(f[0].String()).To(Equal("or(not(3dabcd83), dc3841f5)"))
Expect(f[1].String()).To(Equal("or(not(3dabcd83), not(5f881536))"))
})
})

View File

@@ -24,4 +24,5 @@ type Builder interface {
Save(string) error // A tree might be saved to a folder structure (human editable)
Load(string) error // A tree might be loaded from a db (e.g. bolt) and written to folder
Tree() pkg.Tree // generates world
WithTree(pkg.Tree)
}

View File

@@ -36,16 +36,11 @@ func NewGentooBuilder(e EbuildParser) tree.Parser {
type GentooBuilder struct{ EbuildParser EbuildParser }
type GentooTree struct {
Packages pkg.PackageSet
DBPath string
*tree.DefaultTree
}
type EbuildParser interface {
ScanEbuild(path string) ([]pkg.Package, error)
}
func (gt *GentooTree) GetPackageSet() pkg.PackageSet {
return gt.Packages
ScanEbuild(string, pkg.Tree) ([]pkg.Package, error)
}
func (gt *GentooTree) Prelude() string {
@@ -58,9 +53,8 @@ func (gb *GentooBuilder) Generate(dir string) (pkg.Tree, error) {
return nil, err
}
//defer os.Remove(tmpfile.Name()) // clean up
tree := &GentooTree{Packages: pkg.NewBoltDatabase(tmpfile.Name()), DBPath: tmpfile.Name()}
tree := &GentooTree{DefaultTree: &tree.DefaultTree{Packages: pkg.NewBoltDatabase(tmpfile.Name())}}
// TODO: Handle cleaning after? Cleanup implemented in GetPackageSet().Clean()
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
@@ -70,7 +64,7 @@ func (gb *GentooBuilder) Generate(dir string) (pkg.Tree, error) {
return nil
}
if strings.Contains(info.Name(), "ebuild") {
pkgs, err := gb.EbuildParser.ScanEbuild(path)
pkgs, err := gb.EbuildParser.ScanEbuild(path, tree)
if err != nil {
return err
}

View File

@@ -52,16 +52,17 @@ func SourceFile(ctx context.Context, path string) (map[string]expand.Variable, e
}
// ScanEbuild returns a list of packages (always one with SimpleEbuildParser) decoded from an ebuild.
func (ep *SimpleEbuildParser) ScanEbuild(path string) ([]pkg.Package, error) {
func (ep *SimpleEbuildParser) ScanEbuild(path string, tree pkg.Tree) ([]pkg.Package, error) {
file := filepath.Base(path)
file = strings.Replace(file, ".ebuild", "", -1)
decodepackage, err := regexp.Compile(`^([<>]?=?)((([^\/]+)\/)?(?U)(\S+))(-(\d+(\.\d+)*[a-z]?(_(alpha|beta|pre|rc|p)\d*)*(-r\d+)?))?$`)
decodepackage, err := regexp.Compile(`^([<>]?\~?=?)((([^\/]+)\/)?(?U)(\S+))(-(\d+(\.\d+)*[a-z]?(_(alpha|beta|pre|rc|p)\d*)*(-r\d+)?))?$`)
if err != nil {
return []pkg.Package{}, errors.New("Invalid regex")
}
packageInfo := decodepackage.FindAllStringSubmatch(file, -1)
if len(packageInfo) != 1 || len(packageInfo[0]) != 12 {
return []pkg.Package{}, errors.New("Failed decoding ebuild: " + path)
@@ -71,18 +72,55 @@ func (ep *SimpleEbuildParser) ScanEbuild(path string) ([]pkg.Package, error) {
if err != nil {
// return []pkg.Package{}, err
}
//fmt.Println("Scanning", path)
fmt.Println("Scanning", path)
// TODO: Handle this a bit better
//fmt.Println(vars)
pack := &pkg.DefaultPackage{Name: packageInfo[0][2], Version: packageInfo[0][7]}
rdepend, ok := vars["RDEPEND"]
if ok {
rdepends := strings.Split(rdepend.String(), "\n")
pack.PackageConflicts = []*pkg.DefaultPackage{}
pack.PackageRequires = []*pkg.DefaultPackage{}
for _, rr := range rdepends {
rr = strings.TrimSpace(rr)
conflicts := false
if strings.HasPrefix(rr, "~") {
rr = rr[1:]
}
if strings.HasPrefix(rr, "!") {
rr = rr[1:]
conflicts = true
}
if strings.HasSuffix(rr, "-") {
rr = rr[0 : len(rr)-1]
}
deppackageInfo := decodepackage.FindAllStringSubmatch(rr, -1)
if len(deppackageInfo) != 1 || len(deppackageInfo[0]) != 12 {
continue
}
//TODO: Resolve to db or create a new one.
pack.PackageRequires = append(pack.PackageRequires, &pkg.DefaultPackage{Name: rr})
dep := &pkg.DefaultPackage{Name: deppackageInfo[0][2], Version: deppackageInfo[0][7]}
foundPackage, err := tree.GetPackageSet().FindPackage(dep)
if err != nil {
_, err := tree.GetPackageSet().CreatePackage(dep)
if err != nil {
panic(err)
}
foundPackage = dep
}
found, ok := foundPackage.(*pkg.DefaultPackage)
if !ok {
panic("Simpleparser should deal only with DefaultPackages")
}
if conflicts {
pack.PackageConflicts = append(pack.PackageConflicts, found)
} else {
pack.PackageRequires = append(pack.PackageRequires, found)
}
}
}

View File

@@ -29,6 +29,10 @@ var _ = Describe("GentooBuilder", func() {
gb := NewGentooBuilder(&SimpleEbuildParser{})
tree, err := gb.Generate("../../../../tests/fixtures/overlay")
Expect(err).ToNot(HaveOccurred())
defer func() {
Expect(tree.GetPackageSet().Clean()).ToNot(HaveOccurred())
}()
Expect(len(tree.GetPackageSet().GetPackages())).To(Equal(10))
for _, pid := range tree.GetPackageSet().GetPackages() {

97
pkg/tree/recipes.go Normal file
View File

@@ -0,0 +1,97 @@
// Recipe is a builder imeplementation.
// It reads a Tree and spit it in human readable form (YAML), called recipe,
// It also loads a tree (recipe) from a YAML (to a db, e.g. BoltDB), allowing to query it
// with the solver, using the package object.
package tree
import (
"io/ioutil"
"os"
"path/filepath"
pkg "github.com/mudler/luet/pkg/package"
)
const DefinitionFile = "definition.yaml"
func NewGeneralRecipe() Builder { return &Recipe{} }
// Recipe is the "general" reciper for Trees
type Recipe struct {
PackageTree pkg.Tree
}
func (r *Recipe) Save(path string) error {
for _, pid := range r.PackageTree.GetPackageSet().GetPackages() {
p, err := r.PackageTree.GetPackageSet().GetPackage(pid)
if err != nil {
return err
}
dir := filepath.Join(path, p.GetName(), p.GetVersion())
os.MkdirAll(dir, os.ModePerm)
data, err := p.Yaml()
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(dir, DefinitionFile), data, 0644)
if err != nil {
return err
}
}
return nil
}
func (r *Recipe) Load(path string) error {
if r.Tree() == nil {
r.PackageTree = NewDefaultTree()
}
tmpfile, err := ioutil.TempFile("", "boltdb")
if err != nil {
return err
}
r.Tree().SetPackageSet(pkg.NewBoltDatabase(tmpfile.Name()))
// TODO: Handle cleaning after? Cleanup implemented in GetPackageSet().Clean()
// the function that handles each file or dir
var ff = func(currentpath string, info os.FileInfo, err error) error {
if info.Name() != DefinitionFile {
return nil // Skip with no errors
}
dat, err := ioutil.ReadFile(currentpath)
if err != nil {
return err
}
pack, err := pkg.DefaultPackageFromYaml(dat)
if err != nil {
return err
}
_, err = r.Tree().GetPackageSet().CreatePackage(&pack)
if err != nil {
return err
}
// first thing to do, check error. and decide what to do about it
if err != nil {
return err
}
return nil
}
err = filepath.Walk(path, ff)
if err != nil {
return err
}
return nil
}
func (r *Recipe) Tree() pkg.Tree { return r.PackageTree }
func (r *Recipe) WithTree(t pkg.Tree) { r.PackageTree = t }

125
pkg/tree/recipes_test.go Normal file
View File

@@ -0,0 +1,125 @@
// Recipe is a builder imeplementation.
// It reads a Tree and spit it in human readable form (YAML), called recipe,
// It also loads a tree (recipe) from a YAML (to a db, e.g. BoltDB), allowing to query it
// with the solver, using the package object.
package tree_test
import (
"io/ioutil"
"os"
pkg "github.com/mudler/luet/pkg/package"
"github.com/mudler/luet/pkg/solver"
gentoo "github.com/mudler/luet/pkg/tree/builder/gentoo"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/mudler/luet/pkg/tree"
)
type FakeParser struct {
}
var _ = Describe("Recipe", func() {
Context("Tree generation and storing", func() {
It("parses and writes a tree", func() {
tmpdir, err := ioutil.TempDir("", "tree")
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(tmpdir) // clean up
gb := gentoo.NewGentooBuilder(&gentoo.SimpleEbuildParser{})
tree, err := gb.Generate("../../tests/fixtures/overlay")
Expect(err).ToNot(HaveOccurred())
defer func() {
Expect(tree.GetPackageSet().Clean()).ToNot(HaveOccurred())
}()
Expect(len(tree.GetPackageSet().GetPackages())).To(Equal(10))
generalRecipe := NewGeneralRecipe()
generalRecipe.WithTree(tree)
err = generalRecipe.Save(tmpdir)
Expect(err).ToNot(HaveOccurred())
})
})
Context("Reloading trees", func() {
It("writes and reads back the same tree", func() {
tmpdir, err := ioutil.TempDir("", "tree")
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(tmpdir) // clean up
gb := gentoo.NewGentooBuilder(&gentoo.SimpleEbuildParser{})
tree, err := gb.Generate("../../tests/fixtures/overlay")
Expect(err).ToNot(HaveOccurred())
defer func() {
Expect(tree.GetPackageSet().Clean()).ToNot(HaveOccurred())
}()
Expect(len(tree.GetPackageSet().GetPackages())).To(Equal(10))
generalRecipe := NewGeneralRecipe()
generalRecipe.WithTree(tree)
err = generalRecipe.Save(tmpdir)
Expect(err).ToNot(HaveOccurred())
generalRecipe.WithTree(nil)
Expect(generalRecipe.Tree()).To(BeNil())
err = generalRecipe.Load(tmpdir)
Expect(err).ToNot(HaveOccurred())
Expect(generalRecipe.Tree()).ToNot(BeNil()) // It should be populated back at this point
Expect(len(generalRecipe.Tree().GetPackageSet().GetPackages())).To(Equal(10))
for _, pid := range tree.GetPackageSet().GetPackages() {
p, err := tree.GetPackageSet().GetPackage(pid)
Expect(err).ToNot(HaveOccurred())
Expect(p.GetName()).To(ContainSubstring("pinentry"))
Expect(p.GetVersion()).To(ContainSubstring("1."))
}
})
})
Context("Simple solving with the fixture tree", func() {
It("writes and reads back the same tree", func() {
tmpdir, err := ioutil.TempDir("", "tree")
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(tmpdir) // clean up
gb := gentoo.NewGentooBuilder(&gentoo.SimpleEbuildParser{})
tree, err := gb.Generate("../../tests/fixtures/overlay")
Expect(err).ToNot(HaveOccurred())
defer func() {
Expect(tree.GetPackageSet().Clean()).ToNot(HaveOccurred())
}()
Expect(len(tree.GetPackageSet().GetPackages())).To(Equal(10))
pack, err := tree.FindPackage(&pkg.DefaultPackage{Name: "pinentry", Version: "1.0.0-r2"})
Expect(err).ToNot(HaveOccurred())
world, err := tree.World()
Expect(err).ToNot(HaveOccurred())
s := solver.NewSolver([]pkg.Package{}, world)
solution, err := s.Install([]pkg.Package{pack})
Expect(err).ToNot(HaveOccurred())
Expect(len(solution)).To(Equal(3))
// for _, assertion := range solution {
// fmt.Println(assertion.Package, assertion.Value)
// for _, req := range assertion.Package.GetRequires() {
// fmt.Println("\t-> ", req)
// }
// for _, con := range assertion.Package.GetConflicts() {
// fmt.Println("\t!! ", con)
// }
// }
})
})
})

62
pkg/tree/tree.go Normal file
View File

@@ -0,0 +1,62 @@
// Recipe is a builder imeplementation.
// It reads a Tree and spit it in human readable form (YAML), called recipe,
// It also loads a tree (recipe) from a YAML (to a db, e.g. BoltDB), allowing to query it
// with the solver, using the package object.
package tree
import (
"errors"
pkg "github.com/mudler/luet/pkg/package"
)
func NewDefaultTree() pkg.Tree { return &DefaultTree{} }
type DefaultTree struct {
Packages pkg.PackageSet
CacheWorld []pkg.Package
}
func (gt *DefaultTree) GetPackageSet() pkg.PackageSet {
return gt.Packages
}
func (gt *DefaultTree) Prelude() string {
return ""
}
func (gt *DefaultTree) SetPackageSet(s pkg.PackageSet) {
gt.Packages = s
}
func (gt *DefaultTree) World() ([]pkg.Package, error) {
if len(gt.CacheWorld) > 0 {
return gt.CacheWorld, nil
}
packages := []pkg.Package{}
for _, pid := range gt.GetPackageSet().GetPackages() {
p, err := gt.GetPackageSet().GetPackage(pid)
if err != nil {
return packages, err
}
packages = append(packages, p)
}
gt.CacheWorld = packages
return packages, nil
}
// FIXME: Dup in Packageset
func (gt *DefaultTree) FindPackage(pack pkg.Package) (pkg.Package, error) {
packages, err := gt.World()
if err != nil {
return nil, err
}
for _, pid := range packages {
if pack.GetFingerPrint() == pid.GetFingerPrint() {
return pid, nil
}
}
return nil, errors.New("No package found")
}