mirror of
https://github.com/Jguer/yay
synced 2024-08-31 21:58:39 +00:00
Merge pull request #401 from Morganamilo/deps3
Dependency system rewrite
This commit is contained in:
commit
c36bfc1237
4
cmd.go
4
cmd.go
|
@ -296,6 +296,10 @@ func handleConfig(option, value string) bool {
|
|||
config.SudoLoop = true
|
||||
case "nosudoloop":
|
||||
config.SudoLoop = false
|
||||
case "provides":
|
||||
config.Provides = true
|
||||
case "noprovides":
|
||||
config.Provides = false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ type Configuration struct {
|
|||
Devel bool `json:"devel"`
|
||||
CleanAfter bool `json:"cleanAfter"`
|
||||
GitClone bool `json:"gitclone"`
|
||||
Provides bool `json:"provides"`
|
||||
}
|
||||
|
||||
var version = "5.688"
|
||||
|
@ -152,6 +153,7 @@ func defaultSettings(config *Configuration) {
|
|||
config.AnswerEdit = ""
|
||||
config.AnswerUpgrade = ""
|
||||
config.GitClone = true
|
||||
config.Provides = true
|
||||
}
|
||||
|
||||
// Editor returns the preferred system editor.
|
||||
|
|
354
conflicts.go
354
conflicts.go
|
@ -1,354 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
alpm "github.com/jguer/go-alpm"
|
||||
gopkg "github.com/mikkeloscar/gopkgbuild"
|
||||
)
|
||||
|
||||
// Checks a single conflict against every other to be installed package's
|
||||
// name and its provides.
|
||||
func checkInnerConflict(name string, conflict string, conflicts mapStringSet, dc *depCatagories) {
|
||||
deps, err := gopkg.ParseDeps([]string{conflict})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dep := deps[0]
|
||||
|
||||
for _, pkg := range dc.Aur {
|
||||
if name == pkg.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
version, err := gopkg.NewCompleteVersion(pkg.Version)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if dep.Name == pkg.Name && version.Satisfies(dep) {
|
||||
conflicts.Add(name, pkg.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, provide := range pkg.Provides {
|
||||
// Provides are not versioned unless explicitly defined as
|
||||
// such. If a conflict is versioned but a provide is
|
||||
// not it can not conflict.
|
||||
if (dep.MaxVer != nil || dep.MinVer != nil) && !strings.ContainsAny(provide, "><=") {
|
||||
continue
|
||||
}
|
||||
|
||||
var version *gopkg.CompleteVersion
|
||||
var err error
|
||||
|
||||
pname, pversion := splitNameFromDep(provide)
|
||||
|
||||
if dep.Name != pname {
|
||||
continue
|
||||
}
|
||||
|
||||
if pversion != "" {
|
||||
version, err = gopkg.NewCompleteVersion(provide)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if version != nil && version.Satisfies(dep) {
|
||||
conflicts.Add(name, pkg.Name)
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dc.Repo {
|
||||
if name == pkg.Name() {
|
||||
continue
|
||||
}
|
||||
|
||||
version, err := gopkg.NewCompleteVersion(pkg.Version())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if dep.Name == pkg.Name() && version.Satisfies(dep) {
|
||||
conflicts.Add(name, pkg.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
pkg.Provides().ForEach(func(provide alpm.Depend) error {
|
||||
// Provides are not versioned unless explicitly defined as
|
||||
// such. If a conflict is versioned but a provide is
|
||||
// not it can not conflict.
|
||||
if (dep.MaxVer != nil || dep.MinVer != nil) && provide.Mod == alpm.DepModAny {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dep.Name != pkg.Name() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if provide.Mod == alpm.DepModAny {
|
||||
conflicts.Add(name, pkg.Name())
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
|
||||
version, err := gopkg.NewCompleteVersion(provide.Version)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if version.Satisfies(dep) {
|
||||
conflicts.Add(name, pkg.Name())
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Checks every to be installed package's conflicts against every other to be
|
||||
// installed package and its provides.
|
||||
func checkForInnerConflicts(dc *depCatagories) mapStringSet {
|
||||
conflicts := make(mapStringSet)
|
||||
|
||||
for _, pkg := range dc.Aur {
|
||||
for _, cpkg := range pkg.Conflicts {
|
||||
checkInnerConflict(pkg.Name, cpkg, conflicts, dc)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dc.Repo {
|
||||
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
|
||||
checkInnerConflict(pkg.Name(), conflict.String(), conflicts, dc)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return conflicts
|
||||
}
|
||||
|
||||
// Checks a provide or packagename from a to be installed package
|
||||
// against every already installed package's conflicts
|
||||
func checkReverseConflict(name string, provide string, conflicts mapStringSet) error {
|
||||
var version *gopkg.CompleteVersion
|
||||
var err error
|
||||
|
||||
localDb, err := alpmHandle.LocalDb()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pname, pversion := splitNameFromDep(provide)
|
||||
if pversion != "" {
|
||||
version, err = gopkg.NewCompleteVersion(pversion)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
localDb.PkgCache().ForEach(func(pkg alpm.Package) error {
|
||||
if name == pkg.Name() {
|
||||
return nil
|
||||
}
|
||||
|
||||
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
|
||||
deps, err := gopkg.ParseDeps([]string{conflict.String()})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
dep := deps[0]
|
||||
// Provides are not versioned unless explicitly defined as
|
||||
// such. If a conflict is versioned but a provide is
|
||||
// not it can not conflict.
|
||||
if (dep.MaxVer != nil || dep.MinVer != nil) && version == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dep.Name != pname {
|
||||
return nil
|
||||
}
|
||||
|
||||
if version == nil || version.Satisfies(dep) {
|
||||
// Todo
|
||||
conflicts.Add(name, pkg.Name()+" ("+provide+")")
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checks the conflict of a to be installed package against the package name and
|
||||
// provides of every installed package.
|
||||
func checkConflict(name string, conflict string, conflicts mapStringSet) error {
|
||||
localDb, err := alpmHandle.LocalDb()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deps, err := gopkg.ParseDeps([]string{conflict})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
dep := deps[0]
|
||||
|
||||
localDb.PkgCache().ForEach(func(pkg alpm.Package) error {
|
||||
if name == pkg.Name() {
|
||||
return nil
|
||||
}
|
||||
|
||||
version, err := gopkg.NewCompleteVersion(pkg.Version())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dep.Name == pkg.Name() && version.Satisfies(dep) {
|
||||
conflicts.Add(name, pkg.Name())
|
||||
return nil
|
||||
}
|
||||
|
||||
pkg.Provides().ForEach(func(provide alpm.Depend) error {
|
||||
if dep.Name != provide.Name {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Provides aren't version unless explicitly defined as
|
||||
// such. If a conflict is versioned but a provide is
|
||||
// not it can not conflict.
|
||||
if (dep.MaxVer != nil || dep.MinVer != nil) && provide.Mod == alpm.DepModAny {
|
||||
return nil
|
||||
}
|
||||
|
||||
if provide.Mod == alpm.DepModAny {
|
||||
conflicts.Add(name, pkg.Name()+" ("+provide.Name+")")
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
|
||||
version, err := gopkg.NewCompleteVersion(provide.Version)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if version.Satisfies(dep) {
|
||||
conflicts.Add(name, pkg.Name()+" ("+provide.Name+")")
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checks every to be installed package's conflicts against the names and
|
||||
// provides of every already installed package and checks every to be installed
|
||||
// package's name and provides against every already installed package.
|
||||
func checkForConflicts(dc *depCatagories) (mapStringSet, error) {
|
||||
conflicts := make(mapStringSet)
|
||||
|
||||
for _, pkg := range dc.Aur {
|
||||
for _, cpkg := range pkg.Conflicts {
|
||||
checkConflict(pkg.Name, cpkg, conflicts)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dc.Repo {
|
||||
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
|
||||
checkConflict(pkg.Name(), conflict.String(), conflicts)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
for _, pkg := range dc.Aur {
|
||||
checkReverseConflict(pkg.Name, pkg.Name, conflicts)
|
||||
for _, ppkg := range pkg.Provides {
|
||||
checkReverseConflict(pkg.Name, ppkg, conflicts)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dc.Repo {
|
||||
checkReverseConflict(pkg.Name(), pkg.Name(), conflicts)
|
||||
pkg.Provides().ForEach(func(provide alpm.Depend) error {
|
||||
checkReverseConflict(pkg.Name(), provide.String(), conflicts)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return conflicts, nil
|
||||
}
|
||||
|
||||
// Combiles checkForConflicts() and checkForInnerConflicts() in parallel and
|
||||
// does some printing.
|
||||
func checkForAllConflicts(dc *depCatagories) error {
|
||||
var err error
|
||||
var conflicts mapStringSet
|
||||
var innerConflicts mapStringSet
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
|
||||
fmt.Println(bold(cyan("::") + bold(" Checking for conflicts...")))
|
||||
go func() {
|
||||
conflicts, err = checkForConflicts(dc)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
fmt.Println(bold(cyan("::") + bold(" Checking for inner conflicts...")))
|
||||
go func() {
|
||||
innerConflicts = checkForInnerConflicts(dc)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(innerConflicts) != 0 {
|
||||
fmt.Println()
|
||||
fmt.Println(bold(red(arrow)), bold("Inner conflicts found:"))
|
||||
|
||||
for name, pkgs := range innerConflicts {
|
||||
str := red(bold(smallArrow)) + " " + name + ":"
|
||||
for pkg := range pkgs {
|
||||
str += " " + cyan(pkg)
|
||||
}
|
||||
|
||||
fmt.Println(str)
|
||||
}
|
||||
|
||||
return fmt.Errorf("Unresolvable package conflicts, aborting")
|
||||
}
|
||||
|
||||
if len(conflicts) != 0 {
|
||||
fmt.Println()
|
||||
fmt.Println(bold(red(arrow)), bold("Package conflicts found:"))
|
||||
for name, pkgs := range conflicts {
|
||||
str := red(bold(smallArrow)) + " Installing " + cyan(name) + " will remove:"
|
||||
for pkg := range pkgs {
|
||||
str += " " + cyan(pkg)
|
||||
}
|
||||
|
||||
fmt.Println(str)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
170
dep.go
Normal file
170
dep.go
Normal file
|
@ -0,0 +1,170 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
alpm "github.com/jguer/go-alpm"
|
||||
rpc "github.com/mikkeloscar/aur"
|
||||
)
|
||||
|
||||
type providers struct {
|
||||
lookfor string
|
||||
Pkgs []*rpc.Pkg
|
||||
}
|
||||
|
||||
func makeProviders(name string) providers {
|
||||
return providers{
|
||||
name,
|
||||
make([]*rpc.Pkg, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (q providers) Len() int {
|
||||
return len(q.Pkgs)
|
||||
}
|
||||
|
||||
func (q providers) Less(i, j int) bool {
|
||||
if q.lookfor == q.Pkgs[i].Name {
|
||||
return true
|
||||
}
|
||||
|
||||
if q.lookfor == q.Pkgs[j].Name {
|
||||
return false
|
||||
}
|
||||
|
||||
return lessRunes([]rune(q.Pkgs[i].Name), []rune(q.Pkgs[j].Name))
|
||||
}
|
||||
|
||||
func (q providers) Swap(i, j int) {
|
||||
q.Pkgs[i], q.Pkgs[j] = q.Pkgs[j], q.Pkgs[i]
|
||||
}
|
||||
|
||||
func splitDep(dep string) (string, string, string) {
|
||||
mod := ""
|
||||
|
||||
split := strings.FieldsFunc(dep, func(c rune) bool {
|
||||
match := c == '>' || c == '<' || c == '='
|
||||
|
||||
if match {
|
||||
mod += string(c)
|
||||
}
|
||||
|
||||
return match
|
||||
})
|
||||
|
||||
if len(split) == 1 {
|
||||
return split[0], "", ""
|
||||
}
|
||||
|
||||
return split[0], mod, split[1]
|
||||
}
|
||||
|
||||
func pkgSatisfies(name, version, dep string) bool {
|
||||
depName, depMod, depVersion := splitDep(dep)
|
||||
|
||||
if depName != name {
|
||||
return false
|
||||
}
|
||||
|
||||
return verSatisfies(version, depMod, depVersion)
|
||||
}
|
||||
|
||||
func provideSatisfies(provide, dep string) bool {
|
||||
depName, depMod, depVersion := splitDep(dep)
|
||||
provideName, provideMod, provideVersion := splitDep(provide)
|
||||
|
||||
if provideName != depName {
|
||||
return false
|
||||
}
|
||||
|
||||
// Unversioned provieds can not satisfy a versioned dep
|
||||
if provideMod == "" && depMod != "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return verSatisfies(provideVersion, depMod, depVersion)
|
||||
}
|
||||
|
||||
func verSatisfies(ver1, mod, ver2 string) bool {
|
||||
switch mod {
|
||||
case "=":
|
||||
return alpm.VerCmp(ver1, ver2) == 0
|
||||
case "<":
|
||||
return alpm.VerCmp(ver1, ver2) < 0
|
||||
case "<=":
|
||||
return alpm.VerCmp(ver1, ver2) <= 0
|
||||
case ">":
|
||||
return alpm.VerCmp(ver1, ver2) > 0
|
||||
case ">=":
|
||||
return alpm.VerCmp(ver1, ver2) >= 0
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func satisfiesAur(dep string, pkg *rpc.Pkg) bool {
|
||||
if pkgSatisfies(pkg.Name, pkg.Version, dep) {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, provide := range pkg.Provides {
|
||||
if provideSatisfies(provide, dep) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func satisfiesRepo(dep string, pkg *alpm.Package) bool {
|
||||
if pkgSatisfies(pkg.Name(), pkg.Version(), dep) {
|
||||
return true
|
||||
}
|
||||
|
||||
if pkg.Provides().ForEach(func(provide alpm.Depend) error {
|
||||
if provideSatisfies(provide.String(), dep) {
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
|
||||
return nil
|
||||
}) != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
//split apart db/package to db and package
|
||||
func splitDbFromName(pkg string) (string, string) {
|
||||
split := strings.SplitN(pkg, "/", 2)
|
||||
|
||||
if len(split) == 2 {
|
||||
return split[0], split[1]
|
||||
}
|
||||
return "", split[0]
|
||||
}
|
||||
|
||||
func getBases(pkgs map[string]*rpc.Pkg) map[string][]*rpc.Pkg {
|
||||
bases := make(map[string][]*rpc.Pkg)
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
_, ok := bases[pkg.PackageBase]
|
||||
if !ok {
|
||||
bases[pkg.PackageBase] = make([]*rpc.Pkg, 0)
|
||||
}
|
||||
bases[pkg.PackageBase] = append(bases[pkg.PackageBase], pkg)
|
||||
}
|
||||
|
||||
return bases
|
||||
}
|
||||
|
||||
func isDevelName(name string) bool {
|
||||
for _, suffix := range []string{"git", "svn", "hg", "bzr", "nightly"} {
|
||||
if strings.HasSuffix(name, "-"+suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Contains(name, "-always-")
|
||||
}
|
275
depCheck.go
Normal file
275
depCheck.go
Normal file
|
@ -0,0 +1,275 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
alpm "github.com/jguer/go-alpm"
|
||||
// gopkg "github.com/mikkeloscar/gopkgbuild"
|
||||
)
|
||||
|
||||
func (dp *depPool) checkInnerConflict(name string, conflict string, conflicts mapStringSet) {
|
||||
for _, pkg := range dp.Aur {
|
||||
if pkg.Name == name {
|
||||
continue
|
||||
}
|
||||
|
||||
if satisfiesAur(conflict, pkg) {
|
||||
conflicts.Add(name, pkg.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dp.Repo {
|
||||
if pkg.Name() == name {
|
||||
continue
|
||||
}
|
||||
|
||||
if satisfiesRepo(conflict, pkg) {
|
||||
conflicts.Add(name, pkg.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dp *depPool) checkForwardConflict(name string, conflict string, conflicts mapStringSet) {
|
||||
dp.LocalDb.PkgCache().ForEach(func(pkg alpm.Package) error {
|
||||
if pkg.Name() == name || dp.hasPackage(pkg.Name()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if satisfiesRepo(conflict, &pkg) {
|
||||
n := pkg.Name()
|
||||
if n != conflict {
|
||||
n += " (" + conflict + ")"
|
||||
}
|
||||
conflicts.Add(name, n)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (dp *depPool) checkReverseConflict(name string, conflict string, conflicts mapStringSet) {
|
||||
for _, pkg := range dp.Aur {
|
||||
if pkg.Name == name {
|
||||
continue
|
||||
}
|
||||
|
||||
if satisfiesAur(conflict, pkg) {
|
||||
if name != conflict {
|
||||
name += " (" + conflict + ")"
|
||||
}
|
||||
|
||||
conflicts.Add(pkg.Name, name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dp.Repo {
|
||||
if pkg.Name() == name {
|
||||
continue
|
||||
}
|
||||
|
||||
if satisfiesRepo(conflict, pkg) {
|
||||
if name != conflict {
|
||||
name += " (" + conflict + ")"
|
||||
}
|
||||
|
||||
conflicts.Add(pkg.Name(), name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dp *depPool) checkInnerConflicts(conflicts mapStringSet) {
|
||||
for _, pkg := range dp.Aur {
|
||||
for _, conflict := range pkg.Conflicts {
|
||||
dp.checkInnerConflict(pkg.Name, conflict, conflicts)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dp.Repo {
|
||||
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
|
||||
dp.checkInnerConflict(pkg.Name(), conflict.String(), conflicts)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (dp *depPool) checkForwardConflicts(conflicts mapStringSet) {
|
||||
for _, pkg := range dp.Aur {
|
||||
for _, conflict := range pkg.Conflicts {
|
||||
dp.checkForwardConflict(pkg.Name, conflict, conflicts)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dp.Repo {
|
||||
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
|
||||
dp.checkForwardConflict(pkg.Name(), conflict.String(), conflicts)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (dp *depPool) checkReverseConflicts(conflicts mapStringSet) {
|
||||
dp.LocalDb.PkgCache().ForEach(func(pkg alpm.Package) error {
|
||||
if dp.hasPackage(pkg.Name()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
|
||||
dp.checkReverseConflict(pkg.Name(), conflict.String(), conflicts)
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (dp *depPool) CheckConflicts() error {
|
||||
var wg sync.WaitGroup
|
||||
innerConflicts := make(mapStringSet)
|
||||
conflicts := make(mapStringSet)
|
||||
wg.Add(2)
|
||||
|
||||
fmt.Println(bold(cyan("::") + bold(" Checking for conflicts...")))
|
||||
go func() {
|
||||
dp.checkForwardConflicts(conflicts)
|
||||
dp.checkReverseConflicts(conflicts)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
fmt.Println(bold(cyan("::") + bold(" Checking for inner conflicts...")))
|
||||
go func() {
|
||||
dp.checkInnerConflicts(innerConflicts)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if len(innerConflicts) != 0 {
|
||||
fmt.Println()
|
||||
fmt.Println(bold(red(arrow)), bold("Inner conflicts found:"))
|
||||
|
||||
for name, pkgs := range innerConflicts {
|
||||
str := red(bold(smallArrow)) + " " + name + ":"
|
||||
for pkg := range pkgs {
|
||||
str += " " + cyan(pkg) + ","
|
||||
}
|
||||
str = strings.TrimSuffix(str, ",")
|
||||
|
||||
fmt.Println(str)
|
||||
}
|
||||
|
||||
return fmt.Errorf("Unresolvable package conflicts, aborting")
|
||||
}
|
||||
|
||||
if len(conflicts) != 0 {
|
||||
fmt.Println()
|
||||
fmt.Println(bold(red(arrow)), bold("Package conflicts found:"))
|
||||
for name, pkgs := range conflicts {
|
||||
str := red(bold(smallArrow)) + " Installing " + cyan(name) + " will remove:"
|
||||
for pkg := range pkgs {
|
||||
str += " " + cyan(pkg) + ","
|
||||
}
|
||||
str = strings.TrimSuffix(str, ",")
|
||||
|
||||
fmt.Println(str)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type missing struct {
|
||||
Good stringSet
|
||||
Missing map[string][][]string
|
||||
}
|
||||
|
||||
func (dp *depPool) _checkMissing(dep string, stack []string, missing *missing) {
|
||||
if missing.Good.get(dep) {
|
||||
return
|
||||
}
|
||||
|
||||
if trees, ok := missing.Missing[dep]; ok {
|
||||
for _, tree := range trees {
|
||||
if stringSliceEqual(tree, stack) {
|
||||
return
|
||||
}
|
||||
}
|
||||
missing.Missing[dep] = append(missing.Missing[dep], stack)
|
||||
return
|
||||
}
|
||||
|
||||
aurPkg := dp.findSatisfierAur(dep)
|
||||
if aurPkg != nil {
|
||||
missing.Good.set(dep)
|
||||
for _, deps := range [3][]string{aurPkg.Depends, aurPkg.MakeDepends, aurPkg.CheckDepends} {
|
||||
for _, aurDep := range deps {
|
||||
if _, err := dp.LocalDb.PkgCache().FindSatisfier(aurDep); err == nil {
|
||||
missing.Good.set(aurDep)
|
||||
continue
|
||||
}
|
||||
|
||||
dp._checkMissing(aurDep, append(stack, aurPkg.Name), missing)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
repoPkg := dp.findSatisfierRepo(dep)
|
||||
if repoPkg != nil {
|
||||
missing.Good.set(dep)
|
||||
repoPkg.Depends().ForEach(func(repoDep alpm.Depend) error {
|
||||
if _, err := dp.LocalDb.PkgCache().FindSatisfier(repoDep.String()); err == nil {
|
||||
missing.Good.set(repoDep.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
dp._checkMissing(repoDep.String(), append(stack, repoPkg.Name()), missing)
|
||||
return nil
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
missing.Missing[dep] = [][]string{stack}
|
||||
}
|
||||
|
||||
func (dp *depPool) CheckMissing() error {
|
||||
missing := &missing{
|
||||
make(stringSet),
|
||||
make(map[string][][]string),
|
||||
}
|
||||
|
||||
for _, target := range dp.Targets {
|
||||
dp._checkMissing(target.DepString(), make([]string, 0), missing)
|
||||
}
|
||||
|
||||
if len(missing.Missing) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println(bold(red(arrow+" Error: ")) + "Could not find all required packages:")
|
||||
for dep, trees := range missing.Missing {
|
||||
for _, tree := range trees {
|
||||
|
||||
fmt.Print(" ", cyan(dep))
|
||||
|
||||
if len(tree) == 0 {
|
||||
fmt.Print(" (Target")
|
||||
} else {
|
||||
fmt.Print(" (Wanted by: ")
|
||||
for n := 0; n < len(tree)-1; n++ {
|
||||
fmt.Print(cyan(tree[n]), " -> ")
|
||||
}
|
||||
fmt.Print(cyan(tree[len(tree)-1]))
|
||||
}
|
||||
|
||||
fmt.Println(")")
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("")
|
||||
}
|
120
depOrder.go
Normal file
120
depOrder.go
Normal file
|
@ -0,0 +1,120 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
alpm "github.com/jguer/go-alpm"
|
||||
rpc "github.com/mikkeloscar/aur"
|
||||
)
|
||||
|
||||
type depOrder struct {
|
||||
Aur []*rpc.Pkg
|
||||
Repo []*alpm.Package
|
||||
Runtime stringSet
|
||||
Bases map[string][]*rpc.Pkg
|
||||
}
|
||||
|
||||
func makeDepOrder() *depOrder {
|
||||
return &depOrder{
|
||||
make([]*rpc.Pkg, 0),
|
||||
make([]*alpm.Package, 0),
|
||||
make(stringSet),
|
||||
make(map[string][]*rpc.Pkg),
|
||||
}
|
||||
}
|
||||
|
||||
func getDepOrder(dp *depPool) *depOrder {
|
||||
do := makeDepOrder()
|
||||
|
||||
for _, target := range dp.Targets {
|
||||
dep := target.DepString()
|
||||
aurPkg := dp.Aur[dep]
|
||||
if aurPkg != nil && pkgSatisfies(aurPkg.Name, aurPkg.Version, dep) {
|
||||
do.orderPkgAur(aurPkg, dp, true)
|
||||
}
|
||||
|
||||
aurPkg = dp.findSatisfierAur(dep)
|
||||
if aurPkg != nil {
|
||||
do.orderPkgAur(aurPkg, dp, true)
|
||||
}
|
||||
|
||||
repoPkg := dp.findSatisfierRepo(dep)
|
||||
if repoPkg != nil {
|
||||
do.orderPkgRepo(repoPkg, dp, true)
|
||||
}
|
||||
}
|
||||
|
||||
return do
|
||||
}
|
||||
|
||||
func (do *depOrder) orderPkgAur(pkg *rpc.Pkg, dp *depPool, runtime bool) {
|
||||
if runtime {
|
||||
do.Runtime.set(pkg.Name)
|
||||
}
|
||||
delete(dp.Aur, pkg.Name)
|
||||
|
||||
for i, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
|
||||
for _, dep := range deps {
|
||||
aurPkg := dp.findSatisfierAur(dep)
|
||||
if aurPkg != nil {
|
||||
do.orderPkgAur(aurPkg, dp, runtime && i == 0)
|
||||
}
|
||||
|
||||
repoPkg := dp.findSatisfierRepo(dep)
|
||||
if repoPkg != nil {
|
||||
do.orderPkgRepo(repoPkg, dp, runtime && i == 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := do.Bases[pkg.PackageBase]; !ok {
|
||||
do.Aur = append(do.Aur, pkg)
|
||||
do.Bases[pkg.PackageBase] = make([]*rpc.Pkg, 0)
|
||||
}
|
||||
do.Bases[pkg.PackageBase] = append(do.Bases[pkg.PackageBase], pkg)
|
||||
}
|
||||
|
||||
func (do *depOrder) orderPkgRepo(pkg *alpm.Package, dp *depPool, runtime bool) {
|
||||
if runtime {
|
||||
do.Runtime.set(pkg.Name())
|
||||
}
|
||||
delete(dp.Repo, pkg.Name())
|
||||
|
||||
pkg.Depends().ForEach(func(dep alpm.Depend) (err error) {
|
||||
repoPkg := dp.findSatisfierRepo(dep.String())
|
||||
if repoPkg != nil {
|
||||
do.orderPkgRepo(repoPkg, dp, runtime)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
do.Repo = append(do.Repo, pkg)
|
||||
}
|
||||
|
||||
func (do *depOrder) HasMake() bool {
|
||||
lenAur := 0
|
||||
for _, base := range do.Bases {
|
||||
lenAur += len(base)
|
||||
}
|
||||
|
||||
return len(do.Runtime) != lenAur+len(do.Repo)
|
||||
}
|
||||
|
||||
func (do *depOrder) getMake() []string {
|
||||
makeOnly := make([]string, 0, len(do.Aur)+len(do.Repo)-len(do.Runtime))
|
||||
|
||||
for _, base := range do.Bases {
|
||||
for _, pkg := range base {
|
||||
if !do.Runtime.get(pkg.Name) {
|
||||
makeOnly = append(makeOnly, pkg.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range do.Repo {
|
||||
if !do.Runtime.get(pkg.Name()) {
|
||||
makeOnly = append(makeOnly, pkg.Name())
|
||||
}
|
||||
}
|
||||
|
||||
return makeOnly
|
||||
}
|
482
depPool.go
Normal file
482
depPool.go
Normal file
|
@ -0,0 +1,482 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
alpm "github.com/jguer/go-alpm"
|
||||
rpc "github.com/mikkeloscar/aur"
|
||||
)
|
||||
|
||||
type target struct {
|
||||
Db string
|
||||
Name string
|
||||
Mod string
|
||||
Version string
|
||||
}
|
||||
|
||||
func toTarget(pkg string) target {
|
||||
db, dep := splitDbFromName(pkg)
|
||||
name, mod, version := splitDep(dep)
|
||||
|
||||
return target{
|
||||
db,
|
||||
name,
|
||||
mod,
|
||||
version,
|
||||
}
|
||||
}
|
||||
|
||||
func (t target) DepString() string {
|
||||
return t.Name + t.Mod + t.Version
|
||||
}
|
||||
|
||||
func (t target) String() string {
|
||||
if t.Db != "" {
|
||||
return t.Db + "/" + t.DepString()
|
||||
}
|
||||
|
||||
return t.DepString()
|
||||
}
|
||||
|
||||
type depPool struct {
|
||||
Targets []target
|
||||
Explicit stringSet
|
||||
Repo map[string]*alpm.Package
|
||||
Aur map[string]*rpc.Pkg
|
||||
AurCache map[string]*rpc.Pkg
|
||||
Groups []string
|
||||
LocalDb *alpm.Db
|
||||
SyncDb alpm.DbList
|
||||
Warnings *aurWarnings
|
||||
}
|
||||
|
||||
func makeDepPool() (*depPool, error) {
|
||||
localDb, err := alpmHandle.LocalDb()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
syncDb, err := alpmHandle.SyncDbs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dp := &depPool{
|
||||
make([]target, 0),
|
||||
make(stringSet),
|
||||
make(map[string]*alpm.Package),
|
||||
make(map[string]*rpc.Pkg),
|
||||
make(map[string]*rpc.Pkg),
|
||||
make([]string, 0),
|
||||
localDb,
|
||||
syncDb,
|
||||
nil,
|
||||
}
|
||||
|
||||
return dp, nil
|
||||
}
|
||||
|
||||
// Includes db/ prefixes and group installs
|
||||
func (dp *depPool) ResolveTargets(pkgs []string) error {
|
||||
// RPC requests are slow
|
||||
// Combine as many AUR package requests as possible into a single RPC
|
||||
// call
|
||||
aurTargets := make(stringSet)
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
var err error
|
||||
target := toTarget(pkg)
|
||||
|
||||
// skip targets already satisfied
|
||||
// even if the user enters db/pkg and aur/pkg the latter will
|
||||
// still get skiped even if it's from a different database to
|
||||
// the one specified
|
||||
// this is how pacman behaves
|
||||
if dp.hasPackage(target.DepString()) {
|
||||
continue
|
||||
}
|
||||
|
||||
var foundPkg *alpm.Package
|
||||
var singleDb *alpm.Db
|
||||
|
||||
// aur/ prefix means we only check the aur
|
||||
if target.Db == "aur" {
|
||||
dp.Targets = append(dp.Targets, target)
|
||||
aurTargets.set(target.DepString())
|
||||
continue
|
||||
}
|
||||
|
||||
// if theres a different priefix only look in that repo
|
||||
if target.Db != "" {
|
||||
singleDb, err = alpmHandle.SyncDbByName(target.Db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
foundPkg, err = singleDb.PkgCache().FindSatisfier(target.DepString())
|
||||
//otherwise find it in any repo
|
||||
} else {
|
||||
foundPkg, err = dp.SyncDb.FindSatisfier(target.DepString())
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
dp.Targets = append(dp.Targets, target)
|
||||
dp.Explicit.set(foundPkg.Name())
|
||||
dp.ResolveRepoDependency(foundPkg)
|
||||
continue
|
||||
} else {
|
||||
//check for groups
|
||||
//currently we dont resolve the packages in a group
|
||||
//only check if the group exists
|
||||
//would be better to check the groups from singleDb if
|
||||
//the user specified a db but theres no easy way to do
|
||||
//it without making alpm_lists so dont bother for now
|
||||
//db/group is probably a rare use case
|
||||
group, err := dp.SyncDb.PkgCachebyGroup(target.Name)
|
||||
if err == nil {
|
||||
dp.Groups = append(dp.Groups, target.String())
|
||||
group.ForEach(func(pkg alpm.Package) error {
|
||||
dp.Explicit.set(pkg.Name())
|
||||
return nil
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
//if there was no db prefix check the aur
|
||||
if target.Db == "" {
|
||||
aurTargets.set(target.DepString())
|
||||
}
|
||||
|
||||
dp.Targets = append(dp.Targets, target)
|
||||
}
|
||||
|
||||
if len(aurTargets) > 0 {
|
||||
return dp.resolveAURPackages(aurTargets, true)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pseudo provides finder.
|
||||
// Try to find provides by performing a search of the package name
|
||||
// This effectively performs -Ss on each package
|
||||
// then runs -Si on each result to cache the information.
|
||||
//
|
||||
// For example if you were to -S yay then yay -Ss would give:
|
||||
// yay-git yay-bin yay realyog pacui pacui-git ruby-yard
|
||||
// These packages will all be added to the cache incase they are needed later
|
||||
// Ofcouse only the first three packages provide yay, the rest are just false
|
||||
// positives.
|
||||
//
|
||||
// This method increases dependency resolve time
|
||||
func (dp *depPool) findProvides(pkgs stringSet) error {
|
||||
var mux sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
|
||||
doSearch := func(pkg string) {
|
||||
defer wg.Done()
|
||||
var err error
|
||||
var results []rpc.Pkg
|
||||
|
||||
// Hack for a bigger search result, if the user wants
|
||||
// java-envronment we can search for just java instead and get
|
||||
// more hits.
|
||||
words := strings.Split(pkg, "-")
|
||||
|
||||
for i := range words {
|
||||
results, err = rpc.SearchByNameDesc(strings.Join(words[:i+1], "-"))
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, result := range results {
|
||||
mux.Lock()
|
||||
if _, ok := dp.AurCache[result.Name]; !ok {
|
||||
pkgs.set(result.Name)
|
||||
}
|
||||
mux.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
for pkg := range pkgs {
|
||||
if _, err := dp.LocalDb.PkgByName(pkg); err == nil {
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go doSearch(pkg)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dp *depPool) cacheAURPackages(_pkgs stringSet) error {
|
||||
pkgs := _pkgs.copy()
|
||||
query := make([]string, 0)
|
||||
|
||||
for pkg := range pkgs {
|
||||
if _, ok := dp.AurCache[pkg]; ok {
|
||||
pkgs.remove(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
if len(pkgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if config.Provides {
|
||||
err := dp.findProvides(pkgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for pkg := range pkgs {
|
||||
if _, ok := dp.AurCache[pkg]; !ok {
|
||||
name, _, _ := splitDep(pkg)
|
||||
query = append(query, name)
|
||||
}
|
||||
}
|
||||
|
||||
info, err := aurInfo(query, dp.Warnings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pkg := range info {
|
||||
// Dump everything in cache just in case we need it later
|
||||
dp.AurCache[pkg.Name] = pkg
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dp *depPool) resolveAURPackages(pkgs stringSet, explicit bool) error {
|
||||
newPackages := make(stringSet)
|
||||
newAURPackages := make(stringSet)
|
||||
|
||||
err := dp.cacheAURPackages(pkgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(pkgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for name := range pkgs {
|
||||
_, ok := dp.Aur[name]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
|
||||
pkg := dp.findSatisfierAurCache(name)
|
||||
if pkg == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if explicit {
|
||||
dp.Explicit.set(pkg.Name)
|
||||
}
|
||||
dp.Aur[pkg.Name] = pkg
|
||||
|
||||
for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
|
||||
for _, dep := range deps {
|
||||
newPackages.set(dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for dep := range newPackages {
|
||||
if dp.hasSatisfier(dep) {
|
||||
continue
|
||||
}
|
||||
|
||||
//has satisfier installed: skip
|
||||
_, isInstalled := dp.LocalDb.PkgCache().FindSatisfier(dep)
|
||||
if isInstalled == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
//has satisfier in repo: fetch it
|
||||
repoPkg, inRepos := dp.SyncDb.FindSatisfier(dep)
|
||||
if inRepos == nil {
|
||||
dp.ResolveRepoDependency(repoPkg)
|
||||
continue
|
||||
}
|
||||
|
||||
//assume it's in the aur
|
||||
//ditch the versioning because the RPC cant handle it
|
||||
newAURPackages.set(dep)
|
||||
|
||||
}
|
||||
|
||||
err = dp.resolveAURPackages(newAURPackages, false)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (dp *depPool) ResolveRepoDependency(pkg *alpm.Package) {
|
||||
dp.Repo[pkg.Name()] = pkg
|
||||
|
||||
pkg.Depends().ForEach(func(dep alpm.Depend) (err error) {
|
||||
//have satisfier in dep tree: skip
|
||||
if dp.hasSatisfier(dep.String()) {
|
||||
return
|
||||
}
|
||||
|
||||
//has satisfier installed: skip
|
||||
_, isInstalled := dp.LocalDb.PkgCache().FindSatisfier(dep.String())
|
||||
if isInstalled == nil {
|
||||
return
|
||||
}
|
||||
|
||||
//has satisfier in repo: fetch it
|
||||
repoPkg, inRepos := dp.SyncDb.FindSatisfier(dep.String())
|
||||
if inRepos != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dp.ResolveRepoDependency(repoPkg)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func getDepPool(pkgs []string, warnings *aurWarnings) (*depPool, error) {
|
||||
dp, err := makeDepPool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dp.Warnings = warnings
|
||||
err = dp.ResolveTargets(pkgs)
|
||||
|
||||
return dp, err
|
||||
}
|
||||
|
||||
func (dp *depPool) findSatisfierAur(dep string) *rpc.Pkg {
|
||||
for _, pkg := range dp.Aur {
|
||||
if satisfiesAur(dep, pkg) {
|
||||
return pkg
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is mostly used to promote packages from the cache
|
||||
// to the Install list
|
||||
// Provide a pacman style provider menu if theres more than one candidate
|
||||
// TODO: maybe intermix repo providers in the menu
|
||||
func (dp *depPool) findSatisfierAurCache(dep string) *rpc.Pkg {
|
||||
depName, _, _ := splitDep(dep)
|
||||
seen := make(stringSet)
|
||||
providers := makeProviders(depName)
|
||||
|
||||
if _, err := dp.LocalDb.PkgByName(depName); err == nil {
|
||||
if pkg, ok := dp.AurCache[dep]; ok && pkgSatisfies(pkg.Name, pkg.Version, dep) {
|
||||
return pkg
|
||||
}
|
||||
}
|
||||
|
||||
//this version prioratizes name over provides
|
||||
//if theres a direct match for a package return
|
||||
//that instead of using the menu
|
||||
//
|
||||
//providers := make(rpcPkgs, 0)
|
||||
//for _, pkg := range dp.AurCache {
|
||||
// if pkgSatisfies(pkg.Name, pkg.Version, dep) {
|
||||
// return pkg
|
||||
// }
|
||||
//}
|
||||
|
||||
//for _, pkg := range dp.AurCache {
|
||||
// for _, provide := range pkg.Provides {
|
||||
// if provideSatisfies(provide, dep) {
|
||||
// providers = append(providers, pkg)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// This version acts slightly differenly from Pacman, It will give
|
||||
// a menu even if a package with a matching name exists. I believe this
|
||||
// method is better because most of the time you are choosing between
|
||||
// foo and foo-git.
|
||||
// Using Pacman's ways trying to install foo would never give you
|
||||
// a menu.
|
||||
|
||||
for _, pkg := range dp.AurCache {
|
||||
if seen.get(pkg.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
if pkgSatisfies(pkg.Name, pkg.Version, dep) {
|
||||
providers.Pkgs = append(providers.Pkgs, pkg)
|
||||
seen.set(pkg.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, provide := range pkg.Provides {
|
||||
if provideSatisfies(provide, dep) {
|
||||
providers.Pkgs = append(providers.Pkgs, pkg)
|
||||
seen.set(pkg.Name)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if providers.Len() == 1 {
|
||||
return providers.Pkgs[0]
|
||||
}
|
||||
|
||||
if providers.Len() > 1 {
|
||||
sort.Sort(providers)
|
||||
return providerMenu(dep, providers)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dp *depPool) findSatisfierRepo(dep string) *alpm.Package {
|
||||
for _, pkg := range dp.Repo {
|
||||
if satisfiesRepo(dep, pkg) {
|
||||
return pkg
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dp *depPool) hasSatisfier(dep string) bool {
|
||||
return dp.findSatisfierRepo(dep) != nil || dp.findSatisfierAur(dep) != nil
|
||||
}
|
||||
|
||||
func (dp *depPool) hasPackage(name string) bool {
|
||||
for _, pkg := range dp.Repo {
|
||||
if pkg.Name() == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dp.Aur {
|
||||
if pkg.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dp.Groups {
|
||||
if pkg == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
650
dependencies.go
650
dependencies.go
|
@ -1,650 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
alpm "github.com/jguer/go-alpm"
|
||||
rpc "github.com/mikkeloscar/aur"
|
||||
gopkg "github.com/mikkeloscar/gopkgbuild"
|
||||
)
|
||||
|
||||
type depTree struct {
|
||||
ToProcess stringSet
|
||||
Repo map[string]*alpm.Package
|
||||
Aur map[string]*rpc.Pkg
|
||||
Missing stringSet
|
||||
Groups stringSet
|
||||
Provides map[string]string
|
||||
Warnings *aurWarnings
|
||||
}
|
||||
|
||||
type depCatagories struct {
|
||||
Repo []*alpm.Package
|
||||
Aur []*rpc.Pkg
|
||||
MakeOnly stringSet
|
||||
Bases map[string][]*rpc.Pkg
|
||||
}
|
||||
|
||||
func makeDepTree() *depTree {
|
||||
dt := depTree{
|
||||
make(stringSet),
|
||||
make(map[string]*alpm.Package),
|
||||
make(map[string]*rpc.Pkg),
|
||||
make(stringSet),
|
||||
make(stringSet),
|
||||
make(map[string]string),
|
||||
&aurWarnings{},
|
||||
}
|
||||
|
||||
return &dt
|
||||
}
|
||||
|
||||
func makeDependCatagories() *depCatagories {
|
||||
dc := depCatagories{
|
||||
make([]*alpm.Package, 0),
|
||||
make([]*rpc.Pkg, 0),
|
||||
make(stringSet),
|
||||
make(map[string][]*rpc.Pkg),
|
||||
}
|
||||
|
||||
return &dc
|
||||
}
|
||||
|
||||
// Cut the version requirement from a dependency leaving just the name.
|
||||
func splitNameFromDep(dep string) (string, string) {
|
||||
split := strings.FieldsFunc(dep, func(c rune) bool {
|
||||
return c == '>' || c == '<' || c == '='
|
||||
})
|
||||
|
||||
if len(split) == 1 {
|
||||
return split[0], ""
|
||||
}
|
||||
|
||||
return split[0], split[1]
|
||||
}
|
||||
|
||||
//split apart db/package to db and package
|
||||
func splitDbFromName(pkg string) (string, string) {
|
||||
split := strings.SplitN(pkg, "/", 2)
|
||||
|
||||
if len(split) == 2 {
|
||||
return split[0], split[1]
|
||||
}
|
||||
return "", split[0]
|
||||
}
|
||||
|
||||
func isDevelName(name string) bool {
|
||||
for _, suffix := range []string{"git", "svn", "hg", "bzr", "nightly"} {
|
||||
if strings.HasSuffix(name, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Contains(name, "-always-")
|
||||
}
|
||||
|
||||
func getBases(pkgs map[string]*rpc.Pkg) map[string][]*rpc.Pkg {
|
||||
bases := make(map[string][]*rpc.Pkg)
|
||||
|
||||
nextpkg:
|
||||
for _, pkg := range pkgs {
|
||||
for _, base := range bases[pkg.PackageBase] {
|
||||
if base == pkg {
|
||||
continue nextpkg
|
||||
}
|
||||
}
|
||||
|
||||
_, ok := bases[pkg.PackageBase]
|
||||
if !ok {
|
||||
bases[pkg.PackageBase] = make([]*rpc.Pkg, 0)
|
||||
}
|
||||
bases[pkg.PackageBase] = append(bases[pkg.PackageBase], pkg)
|
||||
}
|
||||
|
||||
return bases
|
||||
}
|
||||
|
||||
func aurFindProvider(name string, dt *depTree) (string, *rpc.Pkg) {
|
||||
dep, _ := splitNameFromDep(name)
|
||||
aurpkg, exists := dt.Aur[dep]
|
||||
|
||||
if exists {
|
||||
return dep, aurpkg
|
||||
}
|
||||
|
||||
dep, exists = dt.Provides[dep]
|
||||
if exists {
|
||||
aurpkg, exists = dt.Aur[dep]
|
||||
if exists {
|
||||
return dep, aurpkg
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
|
||||
}
|
||||
|
||||
func repoFindProvider(name string, dt *depTree) (string, *alpm.Package) {
|
||||
dep, _ := splitNameFromDep(name)
|
||||
alpmpkg, exists := dt.Repo[dep]
|
||||
|
||||
if exists {
|
||||
return dep, alpmpkg
|
||||
}
|
||||
|
||||
dep, exists = dt.Provides[dep]
|
||||
if exists {
|
||||
alpmpkg, exists = dt.Repo[dep]
|
||||
if exists {
|
||||
return dep, alpmpkg
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
|
||||
}
|
||||
|
||||
// Step two of dependency resolving. We already have all the information on the
|
||||
// packages we need, now it's just about ordering them correctly.
|
||||
// pkgs is a list of targets, the packages we want to install. Dependencies are
|
||||
// not included.
|
||||
// For each package we want we iterate down the tree until we hit the bottom.
|
||||
// This is done recursively for each branch.
|
||||
// The start of the tree is defined as the package we want.
|
||||
// When we hit the bottom of the branch we know thats the first package
|
||||
// we need to install so we add it to the start of the to install
|
||||
// list (dc.Aur and dc.Repo).
|
||||
// We work our way up until there is another branch to go down and do it all
|
||||
// again.
|
||||
//
|
||||
// Here is a visual example:
|
||||
//
|
||||
// a
|
||||
// / \
|
||||
// b c
|
||||
// / \
|
||||
// d e
|
||||
//
|
||||
// We see a and it needs b and c
|
||||
// We see b and it needs d and e
|
||||
// We see d - it needs nothing so we add d to our list and move up
|
||||
// We see e - it needs nothing so we add e to our list and move up
|
||||
// We see c - it needs nothing so we add c to our list and move up
|
||||
//
|
||||
// The final install order would come out as debca
|
||||
//
|
||||
// There is a little more to this, handling provides, multiple packages wanting the
|
||||
// same dependencies, etc. This is just the basic premise.
|
||||
func getDepCatagories(pkgs []string, dt *depTree) (*depCatagories, error) {
|
||||
dc := makeDependCatagories()
|
||||
seen := make(stringSet)
|
||||
|
||||
dc.Bases = getBases(dt.Aur)
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
dep, alpmpkg := repoFindProvider(pkg, dt)
|
||||
if alpmpkg != nil {
|
||||
repoDepCatagoriesRecursive(alpmpkg, dc, dt, false)
|
||||
dc.Repo = append(dc.Repo, alpmpkg)
|
||||
delete(dt.Repo, dep)
|
||||
}
|
||||
|
||||
dep, aurpkg := aurFindProvider(pkg, dt)
|
||||
if aurpkg != nil {
|
||||
depCatagoriesRecursive(aurpkg, dc, dt, false, seen)
|
||||
if !seen.get(aurpkg.PackageBase) {
|
||||
dc.Aur = append(dc.Aur, aurpkg)
|
||||
seen.set(aurpkg.PackageBase)
|
||||
}
|
||||
|
||||
delete(dt.Aur, dep)
|
||||
}
|
||||
}
|
||||
|
||||
for _, base := range dc.Bases {
|
||||
for _, pkg := range base {
|
||||
for _, dep := range pkg.Depends {
|
||||
dc.MakeOnly.remove(dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dc.Repo {
|
||||
pkg.Depends().ForEach(func(_dep alpm.Depend) error {
|
||||
dep := _dep.Name
|
||||
dc.MakeOnly.remove(dep)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
dc.MakeOnly.remove(pkg)
|
||||
}
|
||||
|
||||
dupes := make(map[*alpm.Package]struct{})
|
||||
filteredRepo := make([]*alpm.Package, 0)
|
||||
|
||||
for _, pkg := range dc.Repo {
|
||||
_, ok := dupes[pkg]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
dupes[pkg] = struct{}{}
|
||||
filteredRepo = append(filteredRepo, pkg)
|
||||
}
|
||||
|
||||
dc.Repo = filteredRepo
|
||||
|
||||
return dc, nil
|
||||
}
|
||||
|
||||
func repoDepCatagoriesRecursive(pkg *alpm.Package, dc *depCatagories, dt *depTree, isMake bool) {
|
||||
pkg.Depends().ForEach(func(_dep alpm.Depend) error {
|
||||
dep, alpmpkg := repoFindProvider(_dep.Name, dt)
|
||||
if alpmpkg != nil {
|
||||
delete(dt.Repo, dep)
|
||||
repoDepCatagoriesRecursive(alpmpkg, dc, dt, isMake)
|
||||
|
||||
if isMake {
|
||||
dc.MakeOnly.set(alpmpkg.Name())
|
||||
}
|
||||
|
||||
dc.Repo = append(dc.Repo, alpmpkg)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func depCatagoriesRecursive(_pkg *rpc.Pkg, dc *depCatagories, dt *depTree, isMake bool, seen stringSet) {
|
||||
for _, pkg := range dc.Bases[_pkg.PackageBase] {
|
||||
for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
|
||||
for _, pkg := range deps {
|
||||
dep, aurpkg := aurFindProvider(pkg, dt)
|
||||
if aurpkg != nil {
|
||||
delete(dt.Aur, dep)
|
||||
depCatagoriesRecursive(aurpkg, dc, dt, isMake, seen)
|
||||
|
||||
if !seen.get(aurpkg.PackageBase) {
|
||||
dc.Aur = append(dc.Aur, aurpkg)
|
||||
seen.set(aurpkg.PackageBase)
|
||||
}
|
||||
|
||||
if isMake {
|
||||
dc.MakeOnly.set(aurpkg.Name)
|
||||
}
|
||||
}
|
||||
|
||||
dep, alpmpkg := repoFindProvider(pkg, dt)
|
||||
if alpmpkg != nil {
|
||||
delete(dt.Repo, dep)
|
||||
repoDepCatagoriesRecursive(alpmpkg, dc, dt, isMake)
|
||||
|
||||
if isMake {
|
||||
dc.MakeOnly.set(alpmpkg.Name())
|
||||
}
|
||||
|
||||
dc.Repo = append(dc.Repo, alpmpkg)
|
||||
}
|
||||
|
||||
}
|
||||
isMake = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is step one for dependency resolving. pkgs is a slice of the packages you
|
||||
// want to resolve the dependencies for. They can be a mix of aur and repo
|
||||
// dependencies. All unmet dependencies will be resolved.
|
||||
//
|
||||
// For Aur dependencies depends, makedepends and checkdepends are resolved but
|
||||
// for repo packages only depends are resolved as they are prebuilt.
|
||||
// The return will be split into three categories: Repo, Aur and Missing.
|
||||
// The return is in no way ordered. This step is is just aimed at gathering the
|
||||
// packages we need.
|
||||
//
|
||||
// This has been designed to make the least amount of rpc requests as possible.
|
||||
// Web requests are probably going to be the bottleneck here so minimizing them
|
||||
// provides a nice speed boost.
|
||||
//
|
||||
// Here is a visual expample of the request system.
|
||||
// Remember only unsatisfied packages are requested, if a package is already
|
||||
// installed we don't bother.
|
||||
//
|
||||
// a
|
||||
// / \
|
||||
// b c
|
||||
// / \
|
||||
// d e
|
||||
//
|
||||
// We see a so we send a request for a
|
||||
// We see a wants b and c so we send a request for b and c
|
||||
// We see d and e so we send a request for d and e
|
||||
//
|
||||
// That's 5 packages in 3 requests. The amount of requests needed should always be
|
||||
// the same as the height of the tree.
|
||||
// The example does not really do this justice, In the real world where packages
|
||||
// have 10+ dependencies each this is a very nice optimization.
|
||||
func getDepTree(pkgs []string, warnings *aurWarnings) (*depTree, error) {
|
||||
dt := makeDepTree()
|
||||
dt.Warnings = warnings
|
||||
|
||||
localDb, err := alpmHandle.LocalDb()
|
||||
if err != nil {
|
||||
return dt, err
|
||||
}
|
||||
syncDb, err := alpmHandle.SyncDbs()
|
||||
if err != nil {
|
||||
return dt, err
|
||||
}
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
db, name := splitDbFromName(pkg)
|
||||
var foundPkg *alpm.Package
|
||||
var singleDb *alpm.Db
|
||||
|
||||
if db == "aur" {
|
||||
dt.ToProcess.set(name)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check the repos for a matching dep
|
||||
if db != "" {
|
||||
singleDb, err = alpmHandle.SyncDbByName(db)
|
||||
if err != nil {
|
||||
return dt, err
|
||||
}
|
||||
foundPkg, err = singleDb.PkgCache().FindSatisfier(name)
|
||||
} else {
|
||||
foundPkg, err = syncDb.FindSatisfier(name)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
repoTreeRecursive(foundPkg, dt, localDb, syncDb)
|
||||
continue
|
||||
} else {
|
||||
//would be better to check the groups from singleDb if
|
||||
//the user specified a db but there's no easy way to do
|
||||
//it without making alpm_lists so don't bother for now
|
||||
//db/group is probably a rare use case
|
||||
_, err := syncDb.PkgCachebyGroup(name)
|
||||
|
||||
if err == nil {
|
||||
dt.Groups.set(pkg)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if db == "" {
|
||||
dt.ToProcess.set(name)
|
||||
} else {
|
||||
dt.Missing.set(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
if len(dt.ToProcess) > 0 {
|
||||
fmt.Println(bold(cyan("::") + bold(" Querying AUR...")))
|
||||
}
|
||||
|
||||
err = depTreeRecursive(dt, localDb, syncDb, false)
|
||||
if err != nil {
|
||||
return dt, err
|
||||
}
|
||||
|
||||
if !cmdArgs.existsArg("d", "nodeps") {
|
||||
err = checkVersions(dt)
|
||||
}
|
||||
|
||||
dt.Warnings.print()
|
||||
|
||||
return dt, err
|
||||
}
|
||||
|
||||
// Takes a repo package,
|
||||
// gives all of the non installed deps,
|
||||
// repeats on each sub dep.
|
||||
func repoTreeRecursive(pkg *alpm.Package, dt *depTree, localDb *alpm.Db, syncDb alpm.DbList) (err error) {
|
||||
_, exists := dt.Repo[pkg.Name()]
|
||||
if exists {
|
||||
return
|
||||
}
|
||||
|
||||
_, exists = dt.Provides[pkg.Name()]
|
||||
if exists {
|
||||
return
|
||||
}
|
||||
|
||||
dt.Repo[pkg.Name()] = pkg
|
||||
(*pkg).Provides().ForEach(func(dep alpm.Depend) (err error) {
|
||||
dt.Provides[dep.Name] = pkg.Name()
|
||||
return nil
|
||||
})
|
||||
|
||||
(*pkg).Depends().ForEach(func(dep alpm.Depend) (err error) {
|
||||
_, exists := dt.Repo[dep.Name]
|
||||
if exists {
|
||||
return
|
||||
}
|
||||
|
||||
_, isInstalled := localDb.PkgCache().FindSatisfier(dep.String())
|
||||
if isInstalled == nil {
|
||||
return
|
||||
}
|
||||
|
||||
repoPkg, inRepos := syncDb.FindSatisfier(dep.String())
|
||||
if inRepos == nil {
|
||||
repoTreeRecursive(repoPkg, dt, localDb, syncDb)
|
||||
return
|
||||
}
|
||||
|
||||
dt.Missing.set(dep.String())
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func depTreeRecursive(dt *depTree, localDb *alpm.Db, syncDb alpm.DbList, isMake bool) (err error) {
|
||||
if len(dt.ToProcess) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
nextProcess := make(stringSet)
|
||||
currentProcess := make(stringSet)
|
||||
// Strip version conditions
|
||||
for _dep := range dt.ToProcess {
|
||||
dep, _ := splitNameFromDep(_dep)
|
||||
currentProcess.set(dep)
|
||||
}
|
||||
|
||||
// Assume toprocess only contains aur stuff we have not seen
|
||||
info, err := aurInfo(currentProcess.toSlice(), dt.Warnings)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Cache the results
|
||||
for _, pkg := range info {
|
||||
dt.Aur[pkg.Name] = pkg
|
||||
|
||||
for _, provide := range pkg.Provides {
|
||||
name, _ := splitNameFromDep(provide)
|
||||
dt.Provides[name] = pkg.Name
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through to process and check if we now have
|
||||
// each packaged cached.
|
||||
// If not cached, we assume it is missing.
|
||||
for pkgName := range currentProcess {
|
||||
pkg, exists := dt.Aur[pkgName]
|
||||
|
||||
// Did not get it in the request.
|
||||
if !exists {
|
||||
dt.Missing.set(pkgName)
|
||||
continue
|
||||
}
|
||||
|
||||
// for each dep and makedep
|
||||
for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
|
||||
for _, versionedDep := range deps {
|
||||
dep, _ := splitNameFromDep(versionedDep)
|
||||
|
||||
_, exists = dt.Aur[dep]
|
||||
// We have it cached so skip.
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
|
||||
_, exists = dt.Provides[dep]
|
||||
// We have it cached so skip.
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
|
||||
_, exists = dt.Repo[dep]
|
||||
// We have it cached so skip.
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
|
||||
_, exists = dt.Missing[dep]
|
||||
// We know it does not resolve so skip.
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if already installed.
|
||||
_, isInstalled := localDb.PkgCache().FindSatisfier(versionedDep)
|
||||
if isInstalled == nil && config.ReBuild != "tree" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check the repos for a matching dep.
|
||||
repoPkg, inRepos := syncDb.FindSatisfier(versionedDep)
|
||||
if inRepos == nil {
|
||||
if isInstalled == nil && config.ReBuild == "tree" {
|
||||
continue
|
||||
}
|
||||
|
||||
repoTreeRecursive(repoPkg, dt, localDb, syncDb)
|
||||
continue
|
||||
}
|
||||
|
||||
// If all else fails add it to next search.
|
||||
nextProcess.set(versionedDep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dt.ToProcess = nextProcess
|
||||
depTreeRecursive(dt, localDb, syncDb, true)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func checkVersions(dt *depTree) error {
|
||||
has := make(mapStringSlice)
|
||||
allDeps := make([]*gopkg.Dependency, 0)
|
||||
|
||||
localDb, err := alpmHandle.LocalDb()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pkg := range dt.Aur {
|
||||
for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
|
||||
for _, dep := range deps {
|
||||
_, _dep := splitNameFromDep(dep)
|
||||
if _dep != "" {
|
||||
deps, _ := gopkg.ParseDeps([]string{dep})
|
||||
if deps[0] != nil {
|
||||
allDeps = append(allDeps, deps[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
has.Add(pkg.Name, pkg.Version)
|
||||
|
||||
if !isDevelName(pkg.Name) {
|
||||
for _, name := range pkg.Provides {
|
||||
_name, _ver := splitNameFromDep(name)
|
||||
if _ver != "" {
|
||||
has.Add(_name, _ver)
|
||||
} else {
|
||||
delete(has, _name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dt.Repo {
|
||||
pkg.Depends().ForEach(func(dep alpm.Depend) error {
|
||||
if dep.Mod != alpm.DepModAny {
|
||||
deps, _ := gopkg.ParseDeps([]string{dep.String()})
|
||||
if deps[0] != nil {
|
||||
allDeps = append(allDeps, deps[0])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
has.Add(pkg.Name(), pkg.Version())
|
||||
|
||||
pkg.Provides().ForEach(func(dep alpm.Depend) error {
|
||||
if dep.Mod != alpm.DepModAny {
|
||||
has.Add(dep.Name, dep.Version)
|
||||
} else {
|
||||
delete(has, dep.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
localDb.PkgCache().ForEach(func(pkg alpm.Package) error {
|
||||
pkg.Provides().ForEach(func(dep alpm.Depend) error {
|
||||
if dep.Mod != alpm.DepModAny {
|
||||
has.Add(dep.Name, dep.Version)
|
||||
} else {
|
||||
delete(has, dep.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
for _, dep := range allDeps {
|
||||
satisfied := false
|
||||
verStrs, ok := has[dep.Name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, verStr := range verStrs {
|
||||
version, err := gopkg.NewCompleteVersion(verStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if version.Satisfies(dep) {
|
||||
satisfied = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !satisfied {
|
||||
dt.Missing.set(dep.String())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
169
install.go
169
install.go
|
@ -18,7 +18,7 @@ func install(parser *arguments) error {
|
|||
requestTargets := parser.targets.toSlice()
|
||||
var err error
|
||||
var incompatible stringSet
|
||||
var dc *depCatagories
|
||||
var do *depOrder
|
||||
var toClean []*rpc.Pkg
|
||||
var toEdit []*rpc.Pkg
|
||||
|
||||
|
@ -41,6 +41,14 @@ func install(parser *arguments) error {
|
|||
remoteNamesCache := sliceToStringSet(remoteNames)
|
||||
localNamesCache := sliceToStringSet(localNames)
|
||||
|
||||
//create the arguments to pass for the repo install
|
||||
arguments := parser.copy()
|
||||
arguments.delArg("y", "refresh")
|
||||
arguments.delArg("asdeps", "asdep")
|
||||
arguments.delArg("asexplicit", "asexp")
|
||||
arguments.op = "S"
|
||||
arguments.targets = make(stringSet)
|
||||
|
||||
//if we are doing -u also request all packages needing update
|
||||
if parser.existsArg("u", "sysupgrade") {
|
||||
aurUp, repoUp, err = upList(warnings)
|
||||
|
@ -48,61 +56,13 @@ func install(parser *arguments) error {
|
|||
return err
|
||||
}
|
||||
|
||||
for _, up := range aurUp {
|
||||
requestTargets = append(requestTargets, "aur/"+up.Name)
|
||||
}
|
||||
warnings.print()
|
||||
|
||||
for _, up := range repoUp {
|
||||
requestTargets = append(requestTargets, up.Name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//if len(aurTargets) > 0 || parser.existsArg("u", "sysupgrade") && len(remoteNames) > 0 {
|
||||
// fmt.Println(bold(cyan("::") + " Querying AUR..."))
|
||||
//}
|
||||
dt, err := getDepTree(requestTargets, warnings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Deptree will handle db/pkg prefixes. Now they can be striped from the
|
||||
// targets.
|
||||
for pkg := range parser.targets {
|
||||
_, name := splitDbFromName(pkg)
|
||||
parser.targets.remove(pkg)
|
||||
parser.targets.set(name)
|
||||
}
|
||||
|
||||
for i, pkg := range requestTargets {
|
||||
_, name := splitDbFromName(pkg)
|
||||
requestTargets[i] = name
|
||||
}
|
||||
|
||||
if len(dt.Missing) > 0 {
|
||||
str := bold(red(arrow+" Error: ")) + "Could not find all required packages:"
|
||||
|
||||
for name := range dt.Missing {
|
||||
str += "\n " + name
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s", str)
|
||||
}
|
||||
|
||||
//create the arguments to pass for the repo install
|
||||
arguments := parser.copy()
|
||||
arguments.delArg("y", "refresh")
|
||||
arguments.op = "S"
|
||||
arguments.targets = make(stringSet)
|
||||
|
||||
if parser.existsArg("u", "sysupgrade") {
|
||||
ignore, aurUp, err := upgradePkgs(aurUp, repoUp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
requestTargets = parser.targets.toSlice()
|
||||
|
||||
for _, up := range repoUp {
|
||||
if !ignore.get(up.Name) {
|
||||
requestTargets = append(requestTargets, up.Name)
|
||||
|
@ -131,61 +91,65 @@ func install(parser *arguments) error {
|
|||
}
|
||||
}
|
||||
|
||||
hasAur := false
|
||||
for pkg := range parser.targets {
|
||||
_, ok := dt.Aur[pkg]
|
||||
if ok {
|
||||
hasAur = true
|
||||
}
|
||||
dp, err := getDepPool(requestTargets, warnings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = dp.CheckMissing()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = dp.CheckConflicts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hasAur := len(dp.Aur) > 0
|
||||
|
||||
if hasAur && 0 == os.Geteuid() {
|
||||
return fmt.Errorf(bold(red(arrow)) + " Refusing to install AUR Packages as root, Aborting.")
|
||||
}
|
||||
|
||||
dc, err = getDepCatagories(requestTargets, dt)
|
||||
do = getDepOrder(dp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pkg := range dc.Repo {
|
||||
for _, pkg := range do.Repo {
|
||||
arguments.addTarget(pkg.DB().Name() + "/" + pkg.Name())
|
||||
}
|
||||
|
||||
for pkg := range dt.Groups {
|
||||
for _, pkg := range dp.Groups {
|
||||
arguments.addTarget(pkg)
|
||||
}
|
||||
|
||||
if len(dc.Aur) == 0 && len(arguments.targets) == 0 && !parser.existsArg("u", "sysupgrade") {
|
||||
if len(do.Aur) == 0 && len(arguments.targets) == 0 && !parser.existsArg("u", "sysupgrade") {
|
||||
fmt.Println("There is nothing to do")
|
||||
return nil
|
||||
}
|
||||
|
||||
if hasAur {
|
||||
hasAur = len(dc.Aur) != 0
|
||||
hasAur = len(do.Aur) != 0
|
||||
|
||||
err = checkForAllConflicts(dc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printDepCatagories(dc)
|
||||
do.Print()
|
||||
fmt.Println()
|
||||
|
||||
if len(dc.MakeOnly) > 0 {
|
||||
if do.HasMake() {
|
||||
if !continueTask("Remove make dependencies after install?", "yY") {
|
||||
removeMake = true
|
||||
}
|
||||
}
|
||||
|
||||
toClean, toEdit, err = cleanEditNumberMenu(dc.Aur, dc.Bases, remoteNamesCache)
|
||||
toClean, toEdit, err = cleanEditNumberMenu(do.Aur, do.Bases, remoteNamesCache)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cleanBuilds(toClean)
|
||||
|
||||
err = downloadPkgBuilds(dc.Aur, parser.targets, dc.Bases)
|
||||
err = downloadPkgBuilds(do.Aur, parser.targets, do.Bases)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -205,17 +169,17 @@ func install(parser *arguments) error {
|
|||
}
|
||||
|
||||
//initial srcinfo parse before pkgver() bump
|
||||
err = parseSRCINFOFiles(dc.Aur, srcinfosStale, dc.Bases)
|
||||
err = parseSRCINFOFiles(do.Aur, srcinfosStale, do.Bases)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
incompatible, err = getIncompatible(dc.Aur, srcinfosStale, dc.Bases)
|
||||
incompatible, err = getIncompatible(do.Aur, srcinfosStale, do.Bases)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = checkPgpKeys(dc.Aur, dc.Bases, srcinfosStale)
|
||||
err = checkPgpKeys(do.Aur, do.Bases, srcinfosStale)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -229,10 +193,19 @@ func install(parser *arguments) error {
|
|||
|
||||
depArguments := makeArguments()
|
||||
depArguments.addArg("D", "asdeps")
|
||||
expArguments := makeArguments()
|
||||
expArguments.addArg("D", "asexplicit")
|
||||
|
||||
for _, pkg := range dc.Repo {
|
||||
if !parser.targets.get(pkg.Name()) && !localNamesCache.get(pkg.Name()) && !remoteNamesCache.get(pkg.Name()) {
|
||||
for _, pkg := range do.Repo {
|
||||
if !dp.Explicit.get(pkg.Name()) && !localNamesCache.get(pkg.Name()) && !remoteNamesCache.get(pkg.Name()) {
|
||||
depArguments.addTarget(pkg.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
if parser.existsArg("asdeps", "asdep") && dp.Explicit.get(pkg.Name()) {
|
||||
depArguments.addTarget(pkg.Name())
|
||||
} else if parser.existsArg("asexp", "asexplicit") && dp.Explicit.get(pkg.Name()) {
|
||||
expArguments.addTarget(pkg.Name())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,6 +215,13 @@ func install(parser *arguments) error {
|
|||
return fmt.Errorf("%s%s", stderr, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(expArguments.targets) > 0 {
|
||||
_, stderr, err := passToPacmanCapture(expArguments)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s%s", stderr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hasAur {
|
||||
|
@ -250,25 +230,21 @@ func install(parser *arguments) error {
|
|||
uask := alpm.QuestionType(ask) | alpm.QuestionTypeConflictPkg
|
||||
cmdArgs.globals["ask"] = fmt.Sprint(uask)
|
||||
|
||||
err = downloadPkgBuildsSources(dc.Aur, dc.Bases, incompatible)
|
||||
err = downloadPkgBuildsSources(do.Aur, do.Bases, incompatible)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = buildInstallPkgBuilds(dc.Aur, srcinfosStale, parser.targets, parser, dc.Bases, incompatible)
|
||||
err = buildInstallPkgBuilds(dp, do, srcinfosStale, parser, incompatible)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dc.MakeOnly) > 0 {
|
||||
if !removeMake {
|
||||
return nil
|
||||
}
|
||||
|
||||
if removeMake {
|
||||
removeArguments := makeArguments()
|
||||
removeArguments.addArg("R", "u")
|
||||
|
||||
for pkg := range dc.MakeOnly {
|
||||
for _, pkg := range do.getMake() {
|
||||
removeArguments.addTarget(pkg)
|
||||
}
|
||||
|
||||
|
@ -283,7 +259,7 @@ func install(parser *arguments) error {
|
|||
}
|
||||
|
||||
if config.CleanAfter {
|
||||
clean(dc.Aur)
|
||||
clean(do.Aur)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -594,13 +570,13 @@ func downloadPkgBuildsSources(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg, inco
|
|||
return
|
||||
}
|
||||
|
||||
func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD, targets stringSet, parser *arguments, bases map[string][]*rpc.Pkg, incompatible stringSet) error {
|
||||
func buildInstallPkgBuilds(dp *depPool, do *depOrder, srcinfos map[string]*gopkg.PKGBUILD, parser *arguments, incompatible stringSet) error {
|
||||
arch, err := alpmHandle.Arch()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
for _, pkg := range do.Aur {
|
||||
dir := filepath.Join(config.BuildDir, pkg.PackageBase)
|
||||
built := true
|
||||
|
||||
|
@ -623,8 +599,8 @@ func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD,
|
|||
return err
|
||||
}
|
||||
|
||||
if config.ReBuild == "no" || (config.ReBuild == "yes" && !targets.get(pkg.Name)) {
|
||||
for _, split := range bases[pkg.PackageBase] {
|
||||
if config.ReBuild == "no" || (config.ReBuild == "yes" && !dp.Explicit.get(pkg.Name)) {
|
||||
for _, split := range do.Bases[pkg.PackageBase] {
|
||||
file, err := completeFileName(dir, split.Name+"-"+version+"-"+arch+".pkg")
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -674,6 +650,8 @@ func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD,
|
|||
|
||||
depArguments := makeArguments()
|
||||
depArguments.addArg("D", "asdeps")
|
||||
expArguments := makeArguments()
|
||||
expArguments.addArg("D", "asexplicit")
|
||||
|
||||
//remotenames: names of all non repo packages on the system
|
||||
_, _, localNames, remoteNames, err := filterPackages()
|
||||
|
@ -686,7 +664,7 @@ func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD,
|
|||
remoteNamesCache := sliceToStringSet(remoteNames)
|
||||
localNamesCache := sliceToStringSet(localNames)
|
||||
|
||||
for _, split := range bases[pkg.PackageBase] {
|
||||
for _, split := range do.Bases[pkg.PackageBase] {
|
||||
file, err := completeFileName(dir, split.Name+"-"+version+"-"+arch+".pkg")
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -704,10 +682,17 @@ func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD,
|
|||
}
|
||||
|
||||
arguments.addTarget(file)
|
||||
//if !targets.get(split.Name) {
|
||||
if !targets.get(split.Name) && !localNamesCache.get(split.Name) && !remoteNamesCache.get(split.Name) {
|
||||
if !dp.Explicit.get(split.Name) && !localNamesCache.get(split.Name) && !remoteNamesCache.get(split.Name) {
|
||||
depArguments.addTarget(split.Name)
|
||||
}
|
||||
|
||||
if dp.Explicit.get(split.Name) {
|
||||
if parser.existsArg("asdeps", "asdep") {
|
||||
depArguments.addTarget(split.Name)
|
||||
} else if parser.existsArg("asexplicit", "asexp") {
|
||||
expArguments.addTarget(split.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
oldConfirm := config.NoConfirm
|
||||
|
@ -717,7 +702,7 @@ func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD,
|
|||
return err
|
||||
}
|
||||
|
||||
for _, pkg := range bases[pkg.PackageBase] {
|
||||
for _, pkg := range do.Bases[pkg.PackageBase] {
|
||||
updateVCSData(pkg.Name, srcinfo.Source)
|
||||
}
|
||||
|
||||
|
|
10
parser.go
10
parser.go
|
@ -41,6 +41,16 @@ func (set stringSet) toSlice() []string {
|
|||
return slice
|
||||
}
|
||||
|
||||
func (set stringSet) copy() stringSet {
|
||||
newSet := make(stringSet)
|
||||
|
||||
for str := range set {
|
||||
newSet.set(str)
|
||||
}
|
||||
|
||||
return newSet
|
||||
}
|
||||
|
||||
func sliceToStringSet(in []string) stringSet {
|
||||
set := make(stringSet)
|
||||
|
||||
|
|
95
print.go
95
print.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
|
@ -188,7 +189,7 @@ func (u upSlice) print() {
|
|||
}
|
||||
|
||||
// printDownloadsFromRepo prints repository packages to be downloaded
|
||||
func printDepCatagories(dc *depCatagories) {
|
||||
func (do *depOrder) Print() {
|
||||
repo := ""
|
||||
repoMake := ""
|
||||
aur := ""
|
||||
|
@ -199,47 +200,47 @@ func printDepCatagories(dc *depCatagories) {
|
|||
aurLen := 0
|
||||
aurMakeLen := 0
|
||||
|
||||
for _, pkg := range dc.Repo {
|
||||
if dc.MakeOnly.get(pkg.Name()) {
|
||||
repoMake += " " + pkg.Name() + "-" + pkg.Version()
|
||||
repoMakeLen++
|
||||
} else {
|
||||
for _, pkg := range do.Repo {
|
||||
if do.Runtime.get(pkg.Name()) {
|
||||
repo += " " + pkg.Name() + "-" + pkg.Version()
|
||||
repoLen++
|
||||
} else {
|
||||
repoMake += " " + pkg.Name() + "-" + pkg.Version()
|
||||
repoMakeLen++
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dc.Aur {
|
||||
for _, pkg := range do.Aur {
|
||||
pkgStr := " " + pkg.PackageBase + "-" + pkg.Version
|
||||
pkgStrMake := pkgStr
|
||||
|
||||
push := false
|
||||
pushMake := false
|
||||
|
||||
if len(dc.Bases[pkg.PackageBase]) > 1 || pkg.PackageBase != pkg.Name {
|
||||
if len(do.Bases[pkg.PackageBase]) > 1 || pkg.PackageBase != pkg.Name {
|
||||
pkgStr += " ("
|
||||
pkgStrMake += " ("
|
||||
|
||||
for _, split := range dc.Bases[pkg.PackageBase] {
|
||||
if dc.MakeOnly.get(split.Name) {
|
||||
pkgStrMake += split.Name + " "
|
||||
aurMakeLen++
|
||||
pushMake = true
|
||||
} else {
|
||||
for _, split := range do.Bases[pkg.PackageBase] {
|
||||
if do.Runtime.get(split.Name) {
|
||||
pkgStr += split.Name + " "
|
||||
aurLen++
|
||||
push = true
|
||||
} else {
|
||||
pkgStrMake += split.Name + " "
|
||||
aurMakeLen++
|
||||
pushMake = true
|
||||
}
|
||||
}
|
||||
|
||||
pkgStr = pkgStr[:len(pkgStr)-1] + ")"
|
||||
pkgStrMake = pkgStrMake[:len(pkgStrMake)-1] + ")"
|
||||
} else if dc.MakeOnly.get(pkg.Name) {
|
||||
aurMakeLen++
|
||||
pushMake = true
|
||||
} else {
|
||||
} else if do.Runtime.get(pkg.Name) {
|
||||
aurLen++
|
||||
push = true
|
||||
} else {
|
||||
aurMakeLen++
|
||||
pushMake = true
|
||||
}
|
||||
|
||||
if push {
|
||||
|
@ -572,3 +573,61 @@ func colourHash(name string) (output string) {
|
|||
}
|
||||
return fmt.Sprintf("\x1b[%dm%s\x1b[0m", hash%6+31, name)
|
||||
}
|
||||
|
||||
func providerMenu(dep string, providers providers) *rpc.Pkg {
|
||||
size := providers.Len()
|
||||
|
||||
fmt.Print(bold(cyan(":: ")))
|
||||
str := bold(fmt.Sprintf(bold("There are %d providers available for %s:"), size, dep))
|
||||
|
||||
size = 1
|
||||
str += bold(cyan("\n:: ")) + bold("Repository AUR\n ")
|
||||
|
||||
for _, pkg := range providers.Pkgs {
|
||||
str += fmt.Sprintf("%d) %s ", size, pkg.Name)
|
||||
size++
|
||||
}
|
||||
|
||||
fmt.Println(str)
|
||||
|
||||
for {
|
||||
fmt.Print("\nEnter a number (default=1): ")
|
||||
|
||||
if config.NoConfirm {
|
||||
fmt.Println()
|
||||
break
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
numberBuf, overflow, err := reader.ReadLine()
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
break
|
||||
}
|
||||
|
||||
if overflow {
|
||||
fmt.Println("Input too long")
|
||||
continue
|
||||
}
|
||||
|
||||
if string(numberBuf) == "" {
|
||||
return providers.Pkgs[0]
|
||||
}
|
||||
|
||||
num, err := strconv.Atoi(string(numberBuf))
|
||||
if err != nil {
|
||||
fmt.Printf("%s invalid number: %s\n", red("error:"), string(numberBuf))
|
||||
continue
|
||||
}
|
||||
|
||||
if num < 1 || num > size {
|
||||
fmt.Printf("%s invalid value: %d is not between %d and %d\n", red("error:"), num, 1, size)
|
||||
continue
|
||||
}
|
||||
|
||||
return providers.Pkgs[num-1]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
31
utils.go
31
utils.go
|
@ -7,7 +7,6 @@ import (
|
|||
"unicode"
|
||||
)
|
||||
|
||||
type mapStringSlice map[string][]string
|
||||
type mapStringSet map[string]stringSet
|
||||
|
||||
type intRange struct {
|
||||
|
@ -60,14 +59,6 @@ func (mss mapStringSet) Add(n string, v string) {
|
|||
mss[n].set(v)
|
||||
}
|
||||
|
||||
func (mss mapStringSlice) Add(n string, v string) {
|
||||
_, ok := mss[n]
|
||||
if !ok {
|
||||
mss[n] = make([]string, 0, 1)
|
||||
}
|
||||
mss[n] = append(mss[n], v)
|
||||
}
|
||||
|
||||
func completeFileName(dir, name string) (string, error) {
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
|
@ -112,3 +103,25 @@ func lessRunes(iRunes, jRunes []rune) bool {
|
|||
|
||||
return len(iRunes) < len(jRunes)
|
||||
}
|
||||
|
||||
func stringSliceEqual(a, b []string) bool {
|
||||
if a == nil && b == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue