mirror of
https://github.com/mudler/luet.git
synced 2025-09-12 21:33:31 +00:00
Add Tree parser with bolt
Also add Tree builder (Recipe) which can recompose trees.
This commit is contained in:
committed by
Ettore Di Giacinto
parent
9d0dc601b7
commit
a5ceafca26
79
cmd/convert.go
Normal file
79
cmd/convert.go
Normal 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)
|
||||
}
|
@@ -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
104
cmd/query.go
Normal 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)
|
||||
}
|
@@ -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)
|
||||
})
|
||||
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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))"))
|
||||
})
|
||||
})
|
||||
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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
97
pkg/tree/recipes.go
Normal 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
125
pkg/tree/recipes_test.go
Normal 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
62
pkg/tree/tree.go
Normal 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")
|
||||
}
|
Reference in New Issue
Block a user