mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-05-07 22:20:24 +00:00
286 lines
6.3 KiB
Go
286 lines
6.3 KiB
Go
package debian
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"github.com/ugorji/go/codec"
|
|
"sort"
|
|
)
|
|
|
|
// PackageList is list of unique (by key) packages
|
|
//
|
|
// It could be seen as repo snapshot, repo contents, result of filtering,
|
|
// merge, etc.
|
|
type PackageList struct {
|
|
packages map[string]*Package
|
|
}
|
|
|
|
// NewPackageList creates empty package list
|
|
func NewPackageList() *PackageList {
|
|
return &PackageList{packages: make(map[string]*Package, 1000)}
|
|
}
|
|
|
|
// Add appends package to package list, additionally checking for uniqueness
|
|
func (l *PackageList) Add(p *Package) error {
|
|
key := string(p.Key())
|
|
existing, ok := l.packages[key]
|
|
if ok {
|
|
if !existing.Equals(p) {
|
|
return fmt.Errorf("conflict in package %s: %#v != %#v", p, existing, p)
|
|
}
|
|
return nil
|
|
}
|
|
l.packages[key] = p
|
|
return nil
|
|
}
|
|
|
|
// ForEach calls handler for each package in list
|
|
func (l *PackageList) ForEach(handler func(*Package) error) error {
|
|
var err error
|
|
for _, p := range l.packages {
|
|
err = handler(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Len returns number of packages in the list
|
|
func (l *PackageList) Len() int {
|
|
return len(l.packages)
|
|
}
|
|
|
|
// Dependency options
|
|
const (
|
|
// DepFollowSource pulls source packages when required
|
|
DepFollowSource = 1 << iota
|
|
// DepFollowSuggests pulls from suggests
|
|
DepFollowSuggests
|
|
// DepFollowRecommends pulls from recommends
|
|
DepFollowRecommends
|
|
// DepFollowAllVariants follows all variants if depends on "a | b"
|
|
DepFollowAllVariants
|
|
)
|
|
|
|
// VerifyDependencies looks for missing dependencies in package list.
|
|
//
|
|
// Analysis would be peformed for each architecture, in specified sources
|
|
func (l *PackageList) VerifyDependencies(options int, architectures []string, sources *PackageIndexedList) ([]Dependency, error) {
|
|
missing := make([]Dependency, 0, 128)
|
|
|
|
for _, arch := range architectures {
|
|
cache := make(map[string]bool, 2048)
|
|
|
|
for _, p := range l.packages {
|
|
if !p.MatchesArchitecture(arch) {
|
|
continue
|
|
}
|
|
|
|
for _, dep := range p.GetDependencies(options) {
|
|
dep, err := parseDependency(dep)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to process package %s: %s", p, err)
|
|
}
|
|
|
|
dep.Architecture = arch
|
|
|
|
hash := dep.Hash()
|
|
_, ok := cache[hash]
|
|
if ok {
|
|
continue
|
|
}
|
|
|
|
if sources.Search(dep) == nil {
|
|
missing = append(missing, dep)
|
|
cache[hash] = false
|
|
} else {
|
|
cache[hash] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return missing, nil
|
|
}
|
|
|
|
// PackageIndexedList is a list of packages optimized for satisfying searches
|
|
type PackageIndexedList struct {
|
|
// List of packages, sorted by name internally
|
|
packages []*Package
|
|
// Map of packages for each virtual package
|
|
providesList map[string][]*Package
|
|
}
|
|
|
|
// Verify interface
|
|
var (
|
|
_ sort.Interface = &PackageIndexedList{}
|
|
)
|
|
|
|
// NewPackageIndexedList creates empty PackageIndexedList
|
|
func NewPackageIndexedList() *PackageIndexedList {
|
|
return &PackageIndexedList{
|
|
packages: make([]*Package, 0, 1024),
|
|
}
|
|
}
|
|
|
|
// Len returns number of refs
|
|
func (l *PackageIndexedList) Len() int {
|
|
return len(l.packages)
|
|
}
|
|
|
|
// Swap swaps two refs
|
|
func (l *PackageIndexedList) Swap(i, j int) {
|
|
l.packages[i], l.packages[j] = l.packages[j], l.packages[i]
|
|
}
|
|
|
|
// Compare compares two refs in lexographical order
|
|
func (l *PackageIndexedList) Less(i, j int) bool {
|
|
return l.packages[i].Name < l.packages[j].Name
|
|
}
|
|
|
|
// PrepareIndex prepares list for indexing
|
|
func (l *PackageIndexedList) PrepareIndex() {
|
|
sort.Sort(l)
|
|
|
|
l.providesList = make(map[string][]*Package, 128)
|
|
for _, p := range l.packages {
|
|
if p.Provides != "" {
|
|
l.providesList[p.Provides] = append(l.providesList[p.Provides], p)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Append adds more packages to the index
|
|
func (l *PackageIndexedList) Append(pl *PackageList) {
|
|
pp := make([]*Package, pl.Len())
|
|
i := 0
|
|
for _, p := range pl.packages {
|
|
pp[i] = p
|
|
i++
|
|
}
|
|
|
|
l.packages = append(l.packages, pp...)
|
|
}
|
|
|
|
// Search searches package index for specified package
|
|
func (l *PackageIndexedList) Search(dep Dependency) *Package {
|
|
if dep.Relation == VersionDontCare {
|
|
for _, p := range l.providesList[dep.Pkg] {
|
|
if p.MatchesArchitecture(dep.Architecture) {
|
|
return p
|
|
}
|
|
}
|
|
}
|
|
|
|
i := sort.Search(len(l.packages), func(j int) bool { return l.packages[j].Name >= dep.Pkg })
|
|
|
|
for i < len(l.packages) && l.packages[i].Name == dep.Pkg {
|
|
p := l.packages[i]
|
|
if p.MatchesArchitecture(dep.Architecture) {
|
|
if dep.Relation == VersionDontCare {
|
|
return p
|
|
}
|
|
|
|
r := CompareVersions(p.Version, dep.Version)
|
|
switch dep.Relation {
|
|
case VersionEqual:
|
|
if r == 0 {
|
|
return p
|
|
}
|
|
case VersionLess:
|
|
if r < 0 {
|
|
return p
|
|
}
|
|
case VersionGreater:
|
|
if r > 0 {
|
|
return p
|
|
}
|
|
case VersionLessOrEqual:
|
|
if r <= 0 {
|
|
return p
|
|
}
|
|
case VersionGreaterOrEqual:
|
|
if r >= 0 {
|
|
return p
|
|
}
|
|
}
|
|
}
|
|
i++
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// PackageRefList is a list of keys of packages, this is basis for snapshot
|
|
// and similar stuff
|
|
//
|
|
// Refs are sorted in lexicographical order
|
|
type PackageRefList struct {
|
|
// List of package keys
|
|
Refs [][]byte
|
|
}
|
|
|
|
// Verify interface
|
|
var (
|
|
_ sort.Interface = &PackageRefList{}
|
|
)
|
|
|
|
// NewPackageRefListFromPackageList creates PackageRefList from PackageList
|
|
func NewPackageRefListFromPackageList(list *PackageList) *PackageRefList {
|
|
reflist := &PackageRefList{}
|
|
reflist.Refs = make([][]byte, list.Len())
|
|
|
|
i := 0
|
|
for _, p := range list.packages {
|
|
reflist.Refs[i] = p.Key()
|
|
i++
|
|
}
|
|
|
|
sort.Sort(reflist)
|
|
|
|
return reflist
|
|
}
|
|
|
|
// Len returns number of refs
|
|
func (l *PackageRefList) Len() int {
|
|
return len(l.Refs)
|
|
}
|
|
|
|
// Swap swaps two refs
|
|
func (l *PackageRefList) Swap(i, j int) {
|
|
l.Refs[i], l.Refs[j] = l.Refs[j], l.Refs[i]
|
|
}
|
|
|
|
// Compare compares two refs in lexographical order
|
|
func (l *PackageRefList) Less(i, j int) bool {
|
|
return bytes.Compare(l.Refs[i], l.Refs[j]) < 0
|
|
}
|
|
|
|
// Encode does msgpack encoding of PackageRefList
|
|
func (l *PackageRefList) Encode() []byte {
|
|
var buf bytes.Buffer
|
|
|
|
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
|
|
encoder.Encode(l)
|
|
|
|
return buf.Bytes()
|
|
}
|
|
|
|
// Decode decodes msgpack representation into PackageRefLit
|
|
func (l *PackageRefList) Decode(input []byte) error {
|
|
decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
|
|
return decoder.Decode(l)
|
|
}
|
|
|
|
// ForEach calls handler for each package ref in list
|
|
func (l *PackageRefList) ForEach(handler func([]byte) error) error {
|
|
var err error
|
|
for _, p := range l.Refs {
|
|
err = handler(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return err
|
|
}
|