2019-06-04 19:25:17 +00:00
|
|
|
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
|
2018-09-21 21:29:50 +00:00
|
|
|
//
|
|
|
|
// 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 pkg
|
|
|
|
|
|
|
|
import (
|
2019-11-09 13:59:20 +00:00
|
|
|
"encoding/json"
|
2019-06-14 15:40:24 +00:00
|
|
|
"fmt"
|
2019-11-04 11:36:31 +00:00
|
|
|
"path/filepath"
|
2019-06-14 15:40:24 +00:00
|
|
|
|
2018-09-21 21:29:50 +00:00
|
|
|
"github.com/crillab/gophersat/bf"
|
2019-06-11 16:03:50 +00:00
|
|
|
version "github.com/hashicorp/go-version"
|
2018-09-21 21:29:50 +00:00
|
|
|
"github.com/jinzhu/copier"
|
2019-10-30 16:52:57 +00:00
|
|
|
|
|
|
|
"github.com/ghodss/yaml"
|
2018-09-21 21:29:50 +00:00
|
|
|
)
|
|
|
|
|
2019-06-11 16:03:50 +00:00
|
|
|
// Package is a package interface (TBD)
|
|
|
|
// FIXME: Currently some of the methods are returning DefaultPackages due to JSON serialization of the package
|
2018-09-21 21:29:50 +00:00
|
|
|
type Package interface {
|
2019-11-15 23:38:07 +00:00
|
|
|
Encode(PackageDatabase) (string, error)
|
2019-06-12 18:57:39 +00:00
|
|
|
|
2019-11-15 23:38:07 +00:00
|
|
|
BuildFormula(PackageDatabase) ([]bf.Formula, error)
|
2018-09-21 21:29:50 +00:00
|
|
|
IsFlagged(bool) Package
|
2019-06-04 19:25:17 +00:00
|
|
|
Flagged() bool
|
2019-06-05 15:51:24 +00:00
|
|
|
GetFingerPrint() string
|
2019-06-04 19:25:17 +00:00
|
|
|
Requires([]*DefaultPackage) Package
|
|
|
|
Conflicts([]*DefaultPackage) Package
|
2019-11-14 22:44:22 +00:00
|
|
|
Revdeps(world *[]Package) []Package
|
2019-06-05 15:51:24 +00:00
|
|
|
|
|
|
|
GetRequires() []*DefaultPackage
|
|
|
|
GetConflicts() []*DefaultPackage
|
2019-11-14 22:20:28 +00:00
|
|
|
Expand(*[]Package) ([]Package, error)
|
2019-06-14 15:40:24 +00:00
|
|
|
SetCategory(string)
|
2019-06-11 16:03:50 +00:00
|
|
|
|
|
|
|
GetName() string
|
2019-06-14 15:40:24 +00:00
|
|
|
GetCategory() string
|
|
|
|
|
2019-06-11 16:03:50 +00:00
|
|
|
GetVersion() string
|
|
|
|
RequiresContains(Package) bool
|
2019-11-15 17:04:46 +00:00
|
|
|
Matches(m Package) bool
|
2019-06-12 19:32:15 +00:00
|
|
|
|
|
|
|
AddUse(use string)
|
|
|
|
RemoveUse(use string)
|
|
|
|
GetUses() []string
|
2019-10-30 16:52:57 +00:00
|
|
|
|
|
|
|
Yaml() ([]byte, error)
|
2019-10-31 11:34:28 +00:00
|
|
|
Explain()
|
2019-11-04 11:35:21 +00:00
|
|
|
|
|
|
|
SetPath(string)
|
|
|
|
GetPath() string
|
2019-11-04 11:36:31 +00:00
|
|
|
Rel(string) string
|
2018-09-21 21:29:50 +00:00
|
|
|
}
|
|
|
|
|
2019-07-27 09:29:39 +00:00
|
|
|
type Tree interface {
|
2019-11-15 23:38:07 +00:00
|
|
|
GetPackageSet() PackageDatabase
|
2019-10-28 16:12:29 +00:00
|
|
|
Prelude() string // A tree might have a prelude to be able to consume a tree
|
2019-11-15 23:38:07 +00:00
|
|
|
SetPackageSet(s PackageDatabase)
|
2019-10-30 16:52:57 +00:00
|
|
|
World() ([]Package, error)
|
|
|
|
FindPackage(Package) (Package, error)
|
2019-11-12 07:48:07 +00:00
|
|
|
ResolveDeps(int) error
|
2019-10-30 16:52:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// >> 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
|
2019-07-27 09:29:39 +00:00
|
|
|
}
|
|
|
|
|
2019-06-11 16:03:50 +00:00
|
|
|
// DefaultPackage represent a standard package definition
|
2018-09-21 21:29:50 +00:00
|
|
|
type DefaultPackage struct {
|
2019-11-04 10:11:33 +00:00
|
|
|
ID int `json:"-" 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.
|
2018-09-21 21:29:50 +00:00
|
|
|
State State
|
2019-10-30 16:52:57 +00:00
|
|
|
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.
|
2019-11-04 11:35:21 +00:00
|
|
|
|
2019-11-04 16:14:32 +00:00
|
|
|
// TODO: Annotations?
|
|
|
|
|
|
|
|
// Path is set only internally when tree is loaded from disk
|
2019-11-10 09:48:07 +00:00
|
|
|
Path string `json:"path,omitempty"`
|
2018-09-21 21:29:50 +00:00
|
|
|
}
|
|
|
|
|
2019-06-11 16:03:50 +00:00
|
|
|
// State represent the package state
|
2018-09-21 21:29:50 +00:00
|
|
|
type State string
|
|
|
|
|
2019-06-11 16:03:50 +00:00
|
|
|
// NewPackage returns a new package
|
2019-06-04 19:25:17 +00:00
|
|
|
func NewPackage(name, version string, requires []*DefaultPackage, conflicts []*DefaultPackage) *DefaultPackage {
|
2018-09-21 21:29:50 +00:00
|
|
|
return &DefaultPackage{Name: name, Version: version, PackageRequires: requires, PackageConflicts: conflicts}
|
|
|
|
}
|
2019-06-11 16:03:50 +00:00
|
|
|
|
2019-11-09 13:59:20 +00:00
|
|
|
func (p *DefaultPackage) String() string {
|
|
|
|
b, err := json.Marshal(p)
|
|
|
|
if err != nil {
|
2019-11-10 21:47:33 +00:00
|
|
|
return fmt.Sprintf("{ id: \"%d\", name: \"%s\" }", p.ID, p.Name)
|
2019-11-09 13:59:20 +00:00
|
|
|
}
|
|
|
|
return fmt.Sprintf("%s", string(b))
|
|
|
|
}
|
|
|
|
|
2019-06-11 16:03:50 +00:00
|
|
|
// GetFingerPrint returns a UUID of the package.
|
|
|
|
// FIXME: this needs to be unique, now just name is generalized
|
2019-06-05 15:51:24 +00:00
|
|
|
func (p *DefaultPackage) GetFingerPrint() string {
|
2019-06-14 15:40:24 +00:00
|
|
|
return fmt.Sprintf("%s-%s-%s", p.Name, p.Category, p.Version)
|
2019-06-05 15:51:24 +00:00
|
|
|
}
|
2018-09-21 21:29:50 +00:00
|
|
|
|
2019-11-04 11:35:21 +00:00
|
|
|
// GetPath returns the path where the definition file was found
|
|
|
|
func (p *DefaultPackage) GetPath() string {
|
|
|
|
return p.Path
|
|
|
|
}
|
|
|
|
|
2019-11-04 11:36:31 +00:00
|
|
|
func (p *DefaultPackage) Rel(s string) string {
|
|
|
|
return filepath.Join(p.GetPath(), s)
|
|
|
|
}
|
|
|
|
|
2019-11-04 11:35:21 +00:00
|
|
|
func (p *DefaultPackage) SetPath(s string) {
|
|
|
|
p.Path = s
|
|
|
|
}
|
|
|
|
|
2019-06-11 16:03:50 +00:00
|
|
|
// AddUse adds a use to a package
|
2018-09-21 21:29:50 +00:00
|
|
|
func (p *DefaultPackage) AddUse(use string) {
|
|
|
|
for _, v := range p.UseFlags {
|
|
|
|
if v == use {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p.UseFlags = append(p.UseFlags, use)
|
|
|
|
}
|
|
|
|
|
2019-06-11 16:03:50 +00:00
|
|
|
// RemoveUse removes a use to a package
|
2018-09-21 21:29:50 +00:00
|
|
|
func (p *DefaultPackage) RemoveUse(use string) {
|
|
|
|
|
|
|
|
for i := len(p.UseFlags) - 1; i >= 0; i-- {
|
|
|
|
if p.UseFlags[i] == use {
|
|
|
|
p.UseFlags = append(p.UseFlags[:i], p.UseFlags[i+1:]...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-06-11 16:03:50 +00:00
|
|
|
// Encode encodes the package to string.
|
|
|
|
// It returns an ID which can be used to retrieve the package later on.
|
2019-11-15 23:38:07 +00:00
|
|
|
func (p *DefaultPackage) Encode(db PackageDatabase) (string, error) {
|
|
|
|
return db.CreatePackage(p)
|
2018-09-21 21:29:50 +00:00
|
|
|
}
|
|
|
|
|
2019-10-30 16:52:57 +00:00
|
|
|
func (p *DefaultPackage) Yaml() ([]byte, error) {
|
|
|
|
y, err := yaml.Marshal(p)
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return []byte{}, err
|
|
|
|
}
|
|
|
|
return y, nil
|
|
|
|
}
|
|
|
|
|
2018-09-21 21:29:50 +00:00
|
|
|
func (p *DefaultPackage) IsFlagged(b bool) Package {
|
|
|
|
p.IsSet = b
|
|
|
|
return p
|
|
|
|
}
|
2019-06-12 18:57:39 +00:00
|
|
|
|
2019-06-04 19:25:17 +00:00
|
|
|
func (p *DefaultPackage) Flagged() bool {
|
|
|
|
return p.IsSet
|
|
|
|
}
|
2019-06-12 18:57:39 +00:00
|
|
|
|
2019-06-11 16:03:50 +00:00
|
|
|
func (p *DefaultPackage) GetName() string {
|
|
|
|
return p.Name
|
|
|
|
}
|
2019-06-12 18:57:39 +00:00
|
|
|
|
2019-06-11 16:03:50 +00:00
|
|
|
func (p *DefaultPackage) GetVersion() string {
|
|
|
|
return p.Version
|
|
|
|
}
|
2019-06-12 18:57:39 +00:00
|
|
|
|
2019-06-14 15:40:24 +00:00
|
|
|
func (p *DefaultPackage) GetCategory() string {
|
|
|
|
return p.Category
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *DefaultPackage) SetCategory(s string) {
|
|
|
|
p.Category = s
|
|
|
|
}
|
2019-06-12 19:32:15 +00:00
|
|
|
func (p *DefaultPackage) GetUses() []string {
|
|
|
|
return p.UseFlags
|
|
|
|
}
|
|
|
|
|
2019-06-05 15:51:24 +00:00
|
|
|
func (p *DefaultPackage) GetRequires() []*DefaultPackage {
|
|
|
|
return p.PackageRequires
|
|
|
|
}
|
|
|
|
func (p *DefaultPackage) GetConflicts() []*DefaultPackage {
|
|
|
|
return p.PackageConflicts
|
|
|
|
}
|
2019-06-04 19:25:17 +00:00
|
|
|
func (p *DefaultPackage) Requires(req []*DefaultPackage) Package {
|
2018-09-21 21:29:50 +00:00
|
|
|
p.PackageRequires = req
|
|
|
|
return p
|
|
|
|
}
|
2019-06-04 19:25:17 +00:00
|
|
|
func (p *DefaultPackage) Conflicts(req []*DefaultPackage) Package {
|
2018-09-21 21:29:50 +00:00
|
|
|
p.PackageConflicts = req
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
func (p *DefaultPackage) Clone() Package {
|
|
|
|
new := &DefaultPackage{}
|
|
|
|
copier.Copy(&new, &p)
|
|
|
|
return new
|
|
|
|
}
|
|
|
|
|
2019-11-15 17:04:46 +00:00
|
|
|
func (p *DefaultPackage) Matches(m Package) bool {
|
|
|
|
if p.GetFingerPrint() == m.GetFingerPrint() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-11-14 22:20:28 +00:00
|
|
|
func (p *DefaultPackage) Expand(world *[]Package) ([]Package, error) {
|
2019-06-11 16:03:50 +00:00
|
|
|
|
|
|
|
var versionsInWorld []Package
|
2019-11-14 22:20:28 +00:00
|
|
|
for _, w := range *world {
|
2019-11-16 13:58:50 +00:00
|
|
|
if w.GetName() != p.GetName() || w.GetCategory() != p.GetCategory() {
|
2019-06-11 16:47:07 +00:00
|
|
|
continue
|
|
|
|
}
|
2019-06-11 16:03:50 +00:00
|
|
|
|
2019-06-11 16:47:07 +00:00
|
|
|
v, err := version.NewVersion(w.GetVersion())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
constraints, err := version.NewConstraint(p.GetVersion())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if constraints.Check(v) {
|
|
|
|
versionsInWorld = append(versionsInWorld, w)
|
2019-06-11 16:03:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return versionsInWorld, nil
|
|
|
|
}
|
|
|
|
|
2019-11-14 22:44:22 +00:00
|
|
|
func (p *DefaultPackage) Revdeps(world *[]Package) []Package {
|
|
|
|
var versionsInWorld []Package
|
|
|
|
for _, w := range *world {
|
2019-11-15 17:04:46 +00:00
|
|
|
if w.Matches(p) {
|
2019-11-14 22:44:22 +00:00
|
|
|
continue
|
|
|
|
}
|
2019-11-15 17:04:46 +00:00
|
|
|
for _, re := range w.GetRequires() {
|
|
|
|
if re.Matches(p) {
|
2019-11-14 22:44:22 +00:00
|
|
|
versionsInWorld = append(versionsInWorld, w)
|
|
|
|
versionsInWorld = append(versionsInWorld, w.Revdeps(world)...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return versionsInWorld
|
|
|
|
}
|
|
|
|
|
2019-11-15 23:38:07 +00:00
|
|
|
func DecodePackage(ID string, db PackageDatabase) (Package, error) {
|
|
|
|
return db.GetPackage(ID)
|
2018-09-21 21:29:50 +00:00
|
|
|
}
|
|
|
|
|
2019-06-05 16:40:33 +00:00
|
|
|
func NormalizeFlagged(p Package) {
|
|
|
|
for _, r := range p.GetRequires() {
|
|
|
|
r.IsFlagged(true)
|
|
|
|
NormalizeFlagged(r)
|
|
|
|
}
|
|
|
|
for _, r := range p.GetConflicts() {
|
|
|
|
r.IsFlagged(true)
|
|
|
|
NormalizeFlagged(r)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-11 16:03:50 +00:00
|
|
|
func (p *DefaultPackage) RequiresContains(s Package) bool {
|
|
|
|
for _, re := range p.GetRequires() {
|
2019-11-15 17:04:46 +00:00
|
|
|
if re.Matches(s) {
|
2019-06-11 16:03:50 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if re.RequiresContains(s) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-11-15 23:38:07 +00:00
|
|
|
func (p *DefaultPackage) BuildFormula(db PackageDatabase) ([]bf.Formula, error) {
|
|
|
|
encodedA, err := p.IsFlagged(true).Encode(db)
|
2018-09-21 21:29:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-06-05 16:40:33 +00:00
|
|
|
NormalizeFlagged(p)
|
2018-09-21 21:29:50 +00:00
|
|
|
|
|
|
|
A := bf.Var(encodedA)
|
|
|
|
|
2019-06-04 19:57:13 +00:00
|
|
|
var formulas []bf.Formula
|
2018-09-21 21:29:50 +00:00
|
|
|
|
|
|
|
for _, required := range p.PackageRequires {
|
2019-11-15 23:38:07 +00:00
|
|
|
encodedB, err := required.Encode(db)
|
2018-09-21 21:29:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
B := bf.Var(encodedB)
|
2019-06-04 19:25:17 +00:00
|
|
|
formulas = append(formulas, bf.Or(bf.Not(A), B))
|
2018-09-21 21:29:50 +00:00
|
|
|
|
2019-11-15 23:38:07 +00:00
|
|
|
f, err := required.BuildFormula(db)
|
2019-06-03 21:23:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
formulas = append(formulas, f...)
|
|
|
|
|
2018-09-21 21:29:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, required := range p.PackageConflicts {
|
2019-11-15 23:38:07 +00:00
|
|
|
encodedB, err := required.Encode(db)
|
2018-09-21 21:29:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
B := bf.Var(encodedB)
|
2019-06-04 19:25:17 +00:00
|
|
|
formulas = append(formulas, bf.Or(bf.Not(A),
|
|
|
|
bf.Not(B)))
|
2019-06-03 21:23:02 +00:00
|
|
|
|
2019-11-15 23:38:07 +00:00
|
|
|
f, err := required.BuildFormula(db)
|
2019-06-03 21:23:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
formulas = append(formulas, f...)
|
2018-09-21 21:29:50 +00:00
|
|
|
}
|
2019-06-04 19:25:17 +00:00
|
|
|
return formulas, nil
|
2018-09-21 21:29:50 +00:00
|
|
|
}
|
2019-10-31 11:34:28 +00:00
|
|
|
|
|
|
|
func (p *DefaultPackage) Explain() {
|
|
|
|
|
|
|
|
fmt.Println("====================")
|
|
|
|
fmt.Println("Name: ", p.GetName())
|
|
|
|
fmt.Println("Category: ", p.GetCategory())
|
|
|
|
fmt.Println("Version: ", p.GetVersion())
|
|
|
|
fmt.Println("Installed: ", p.IsSet)
|
|
|
|
|
|
|
|
for _, req := range p.GetRequires() {
|
|
|
|
fmt.Println("\t-> ", req)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, con := range p.GetConflicts() {
|
|
|
|
fmt.Println("\t!! ", con)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println("====================")
|
|
|
|
|
|
|
|
}
|