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"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
@@ -60,7 +59,7 @@ func initConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
viper.SetConfigName(".luet") // name of config file (without extension)
|
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)
|
fmt.Println(">>> cfgFile: ", cfgFile)
|
||||||
viper.SetConfigFile(cfgFile)
|
viper.SetConfigFile(cfgFile)
|
||||||
configDir := path.Dir(cfgFile)
|
configDir := path.Dir(cfgFile)
|
||||||
@@ -80,8 +79,5 @@ func initConfig() {
|
|||||||
} else {
|
} else {
|
||||||
fmt.Println(err)
|
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)
|
Create([]byte) (string, error)
|
||||||
Retrieve(ID string) ([]byte, error)
|
Retrieve(ID string) ([]byte, error)
|
||||||
|
|
||||||
FindPackage(name, version string) (Package, error)
|
|
||||||
UpdatePackage(p Package) error
|
UpdatePackage(p Package) error
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,7 @@ package pkg
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
storm "github.com/asdine/storm"
|
storm "github.com/asdine/storm"
|
||||||
@@ -54,7 +55,7 @@ func (db *BoltDatabase) Retrieve(ID string) ([]byte, error) {
|
|||||||
return []byte{}, errors.New("Not implemented")
|
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{}
|
p := &DefaultPackage{}
|
||||||
bolt, err := storm.Open(db.Path)
|
bolt, err := storm.Open(db.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -62,7 +63,7 @@ func (db *BoltDatabase) FindPackage(name, version string) (Package, error) {
|
|||||||
}
|
}
|
||||||
defer bolt.Close()
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -142,3 +143,7 @@ func (db *BoltDatabase) CreatePackage(p Package) (string, error) {
|
|||||||
|
|
||||||
return strconv.Itoa(dp.ID), err
|
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
|
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")
|
return nil, errors.New("Not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,3 +120,8 @@ func (db *InMemoryDatabase) UpdatePackage(p Package) error {
|
|||||||
func (db *InMemoryDatabase) GetPackages() []string {
|
func (db *InMemoryDatabase) GetPackages() []string {
|
||||||
return []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"
|
"github.com/crillab/gophersat/bf"
|
||||||
version "github.com/hashicorp/go-version"
|
version "github.com/hashicorp/go-version"
|
||||||
|
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
|
|
||||||
|
"github.com/ghodss/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Package is a package interface (TBD)
|
// Package is a package interface (TBD)
|
||||||
@@ -50,30 +51,48 @@ type Package interface {
|
|||||||
AddUse(use string)
|
AddUse(use string)
|
||||||
RemoveUse(use string)
|
RemoveUse(use string)
|
||||||
GetUses() []string
|
GetUses() []string
|
||||||
|
|
||||||
|
Yaml() ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PackageSet interface {
|
type PackageSet interface {
|
||||||
GetPackages() []string //Ids
|
GetPackages() []string //Ids
|
||||||
CreatePackage(pkg Package) (string, error)
|
CreatePackage(pkg Package) (string, error)
|
||||||
GetPackage(ID string) (Package, error)
|
GetPackage(ID string) (Package, error)
|
||||||
|
Clean() error
|
||||||
|
FindPackage(Package) (Package, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tree interface {
|
type Tree interface {
|
||||||
GetPackageSet() PackageSet
|
GetPackageSet() PackageSet
|
||||||
Prelude() string // A tree might have a prelude to be able to consume a tree
|
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
|
// DefaultPackage represent a standard package definition
|
||||||
type DefaultPackage struct {
|
type DefaultPackage struct {
|
||||||
ID int `storm:"id,increment"` // primary key with auto increment
|
ID int `storm:"id,increment"` // primary key with auto increment
|
||||||
Name string
|
Name string `json:"name"` // Affects YAML field names too.
|
||||||
Version string
|
Version string `json:"version"` // Affects YAML field names too.
|
||||||
Category string
|
Category string `json:"category"` // Affects YAML field names too.
|
||||||
UseFlags []string
|
UseFlags []string `json:"use_flags"` // Affects YAML field names too.
|
||||||
State State
|
State State
|
||||||
PackageRequires []*DefaultPackage
|
PackageRequires []*DefaultPackage `json:"requires"` // Affects YAML field names too.
|
||||||
PackageConflicts []*DefaultPackage
|
PackageConflicts []*DefaultPackage `json:"conflicts"` // Affects YAML field names too.
|
||||||
IsSet bool
|
IsSet bool `json:"set"` // Affects YAML field names too.
|
||||||
}
|
}
|
||||||
|
|
||||||
// State represent the package state
|
// State represent the package state
|
||||||
@@ -117,6 +136,15 @@ func (p *DefaultPackage) Encode() (string, error) {
|
|||||||
return NewInMemoryDatabase().CreatePackage(p)
|
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 {
|
func (p *DefaultPackage) IsFlagged(b bool) Package {
|
||||||
p.IsSet = b
|
p.IsSet = b
|
||||||
return p
|
return p
|
||||||
|
@@ -94,8 +94,8 @@ var _ = Describe("Package", func() {
|
|||||||
f, err := a1.BuildFormula()
|
f, err := a1.BuildFormula()
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(len(f)).To(Equal(2))
|
Expect(len(f)).To(Equal(2))
|
||||||
Expect(f[0].String()).To(Equal("or(not(fd21d375), c85f382e)"))
|
Expect(f[0].String()).To(Equal("or(not(3dabcd83), dc3841f5)"))
|
||||||
Expect(f[1].String()).To(Equal("or(not(fd21d375), not(f512c160))"))
|
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)
|
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
|
Load(string) error // A tree might be loaded from a db (e.g. bolt) and written to folder
|
||||||
Tree() pkg.Tree // generates world
|
Tree() pkg.Tree // generates world
|
||||||
|
WithTree(pkg.Tree)
|
||||||
}
|
}
|
||||||
|
@@ -36,16 +36,11 @@ func NewGentooBuilder(e EbuildParser) tree.Parser {
|
|||||||
type GentooBuilder struct{ EbuildParser EbuildParser }
|
type GentooBuilder struct{ EbuildParser EbuildParser }
|
||||||
|
|
||||||
type GentooTree struct {
|
type GentooTree struct {
|
||||||
Packages pkg.PackageSet
|
*tree.DefaultTree
|
||||||
DBPath string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type EbuildParser interface {
|
type EbuildParser interface {
|
||||||
ScanEbuild(path string) ([]pkg.Package, error)
|
ScanEbuild(string, pkg.Tree) ([]pkg.Package, error)
|
||||||
}
|
|
||||||
|
|
||||||
func (gt *GentooTree) GetPackageSet() pkg.PackageSet {
|
|
||||||
return gt.Packages
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gt *GentooTree) Prelude() string {
|
func (gt *GentooTree) Prelude() string {
|
||||||
@@ -58,9 +53,8 @@ func (gb *GentooBuilder) Generate(dir string) (pkg.Tree, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//defer os.Remove(tmpfile.Name()) // clean up
|
tree := &GentooTree{DefaultTree: &tree.DefaultTree{Packages: pkg.NewBoltDatabase(tmpfile.Name())}}
|
||||||
|
// TODO: Handle cleaning after? Cleanup implemented in GetPackageSet().Clean()
|
||||||
tree := &GentooTree{Packages: pkg.NewBoltDatabase(tmpfile.Name()), DBPath: tmpfile.Name()}
|
|
||||||
|
|
||||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -70,7 +64,7 @@ func (gb *GentooBuilder) Generate(dir string) (pkg.Tree, error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if strings.Contains(info.Name(), "ebuild") {
|
if strings.Contains(info.Name(), "ebuild") {
|
||||||
pkgs, err := gb.EbuildParser.ScanEbuild(path)
|
pkgs, err := gb.EbuildParser.ScanEbuild(path, tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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.
|
// 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 := filepath.Base(path)
|
||||||
file = strings.Replace(file, ".ebuild", "", -1)
|
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 {
|
if err != nil {
|
||||||
return []pkg.Package{}, errors.New("Invalid regex")
|
return []pkg.Package{}, errors.New("Invalid regex")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
packageInfo := decodepackage.FindAllStringSubmatch(file, -1)
|
packageInfo := decodepackage.FindAllStringSubmatch(file, -1)
|
||||||
if len(packageInfo) != 1 || len(packageInfo[0]) != 12 {
|
if len(packageInfo) != 1 || len(packageInfo[0]) != 12 {
|
||||||
return []pkg.Package{}, errors.New("Failed decoding ebuild: " + path)
|
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 {
|
if err != nil {
|
||||||
// return []pkg.Package{}, err
|
// return []pkg.Package{}, err
|
||||||
}
|
}
|
||||||
//fmt.Println("Scanning", path)
|
fmt.Println("Scanning", path)
|
||||||
|
// TODO: Handle this a bit better
|
||||||
//fmt.Println(vars)
|
//fmt.Println(vars)
|
||||||
pack := &pkg.DefaultPackage{Name: packageInfo[0][2], Version: packageInfo[0][7]}
|
pack := &pkg.DefaultPackage{Name: packageInfo[0][2], Version: packageInfo[0][7]}
|
||||||
rdepend, ok := vars["RDEPEND"]
|
rdepend, ok := vars["RDEPEND"]
|
||||||
if ok {
|
if ok {
|
||||||
rdepends := strings.Split(rdepend.String(), "\n")
|
rdepends := strings.Split(rdepend.String(), "\n")
|
||||||
|
pack.PackageConflicts = []*pkg.DefaultPackage{}
|
||||||
pack.PackageRequires = []*pkg.DefaultPackage{}
|
pack.PackageRequires = []*pkg.DefaultPackage{}
|
||||||
for _, rr := range rdepends {
|
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.
|
//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{})
|
gb := NewGentooBuilder(&SimpleEbuildParser{})
|
||||||
tree, err := gb.Generate("../../../../tests/fixtures/overlay")
|
tree, err := gb.Generate("../../../../tests/fixtures/overlay")
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
defer func() {
|
||||||
|
Expect(tree.GetPackageSet().Clean()).ToNot(HaveOccurred())
|
||||||
|
}()
|
||||||
|
|
||||||
Expect(len(tree.GetPackageSet().GetPackages())).To(Equal(10))
|
Expect(len(tree.GetPackageSet().GetPackages())).To(Equal(10))
|
||||||
|
|
||||||
for _, pid := range tree.GetPackageSet().GetPackages() {
|
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