yay/depCheck.go
Joe Groocock 52bf777e67
Print errors on stderr
This makes a start on fixing https://github.com/Jguer/yay/issues/715
It is by no means a complete solution, but instead a start
2018-11-20 22:27:30 +00:00

330 lines
7.8 KiB
Go

package main
import (
"fmt"
"os"
"strings"
"sync"
alpm "github.com/jguer/go-alpm"
rpc "github.com/mikkeloscar/aur"
)
func (ds *depSolver) _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 := ds.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 := ds.LocalDb.PkgCache().FindSatisfier(aurDep); err == nil {
missing.Good.set(aurDep)
continue
}
ds._checkMissing(aurDep, append(stack, aurPkg.Name), missing)
}
}
return
}
repoPkg := ds.findSatisfierRepo(dep)
if repoPkg != nil {
missing.Good.set(dep)
repoPkg.Depends().ForEach(func(repoDep alpm.Depend) error {
if _, err := ds.LocalDb.PkgCache().FindSatisfier(repoDep.String()); err == nil {
missing.Good.set(repoDep.String())
return nil
}
ds._checkMissing(repoDep.String(), append(stack, repoPkg.Name()), missing)
return nil
})
return
}
missing.Missing[dep] = [][]string{stack}
}
func (ds *depSolver) CheckMissing() error {
missing := &missing{
make(stringSet),
make(map[string][][]string),
}
for _, target := range ds.Targets {
ds._checkMissing(target.DepString(), make([]string, 0), missing)
}
if len(missing.Missing) == 0 {
return nil
}
fmt.Fprintln(os.Stderr, bold(red(arrow+" Error: "))+"Could not find all required packages:")
for dep, trees := range missing.Missing {
for _, tree := range trees {
fmt.Fprint(os.Stderr, " ", cyan(dep))
if len(tree) == 0 {
fmt.Fprint(os.Stderr, " (Target")
} else {
fmt.Fprint(os.Stderr, " (Wanted by: ")
for n := 0; n < len(tree)-1; n++ {
fmt.Fprint(os.Stderr, cyan(tree[n]), " -> ")
}
fmt.Fprint(os.Stderr, cyan(tree[len(tree)-1]))
}
fmt.Fprintln(os.Stderr, ")")
}
}
return fmt.Errorf("")
}
func (ds *depSolver) checkForwardConflict(name string, conflict string, conflicts mapStringSet) {
ds.LocalDb.PkgCache().ForEach(func(pkg alpm.Package) error {
if pkg.Name() == name || ds.hasPackage(pkg.Name()) {
return nil
}
if satisfiesRepo(conflict, &pkg) {
n := pkg.Name()
conflicts.Add(name, n)
}
return nil
})
}
func (ds *depSolver) checkReverseConflict(name string, conflict string, conflicts mapStringSet) {
for _, base := range ds.Aur {
for _, pkg := range base {
if pkg.Name == name {
continue
}
if satisfiesAur(conflict, pkg) {
conflicts.Add(pkg.Name, name)
}
}
}
for _, pkg := range ds.Repo {
if pkg.Name() == name {
continue
}
if satisfiesRepo(conflict, pkg) {
conflicts.Add(pkg.Name(), name)
}
}
}
func (ds *depSolver) checkForwardConflicts(conflicts mapStringSet) {
for _, base := range ds.Aur {
for _, pkg := range base {
for _, conflict := range pkg.Conflicts {
ds.checkForwardConflict(pkg.Name, conflict, conflicts)
}
}
}
for _, pkg := range ds.Repo {
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
ds.checkForwardConflict(pkg.Name(), conflict.String(), conflicts)
return nil
})
}
}
func (ds *depSolver) checkReverseConflicts(conflicts mapStringSet) {
ds.LocalDb.PkgCache().ForEach(func(pkg alpm.Package) error {
if ds.hasPackage(pkg.Name()) {
return nil
}
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
ds.checkReverseConflict(pkg.Name(), conflict.String(), conflicts)
return nil
})
return nil
})
}
func (ds *depSolver) checkInnerRepoConflicts(conflicts mapStringSet) {
for _, pkg := range ds.Repo {
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
for _, innerpkg := range ds.Repo {
if pkg.Name() != innerpkg.Name() && satisfiesRepo(conflict.String(), innerpkg) {
conflicts.Add(pkg.Name(), innerpkg.Name())
}
}
return nil
})
}
}
func (ds *depSolver) checkInnerConflicts(conflicts mapStringSet) {
removed := make(stringSet)
//ds.checkInnerConflictRepoAur(conflicts)
for current, currbase := range ds.Aur {
for _, pkg := range currbase {
ds.checkInnerConflict(pkg, ds.Aur[:current], removed, conflicts)
}
}
}
// Check if anything conflicts with currpkg
// If so add the conflict with currpkg being removed by the conflicting pkg
func (ds *depSolver) checkInnerConflict(currpkg *rpc.Pkg, aur []Base, removed stringSet, conflicts mapStringSet) {
for _, base := range aur {
for _, pkg := range base {
for _, conflict := range pkg.Conflicts {
if !removed.get(pkg.Name) && satisfiesAur(conflict, currpkg) {
addInnerConflict(pkg.Name, currpkg.Name, removed, conflicts)
}
}
}
}
for _, pkg := range ds.Repo {
pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
if !removed.get(pkg.Name()) && satisfiesAur(conflict.String(), currpkg) {
addInnerConflict(pkg.Name(), currpkg.Name, removed, conflicts)
}
return nil
})
}
for _, conflict := range currpkg.Conflicts {
for _, base := range aur {
for _, pkg := range base {
if !removed.get(pkg.Name) && satisfiesAur(conflict, pkg) {
addInnerConflict(pkg.Name, currpkg.Name, removed, conflicts)
}
}
}
for _, pkg := range ds.Repo {
if !removed.get(pkg.Name()) && satisfiesRepo(conflict, pkg) {
addInnerConflict(pkg.Name(), currpkg.Name, removed, conflicts)
}
}
}
}
func addInnerConflict(toRemove string, removedBy string, removed stringSet, conflicts mapStringSet) {
conflicts.Add(removedBy, toRemove)
removed.set(toRemove)
}
func (ds *depSolver) CheckConflicts() (mapStringSet, error) {
var wg sync.WaitGroup
innerConflicts := make(mapStringSet)
conflicts := make(mapStringSet)
repoConflicts := make(mapStringSet)
wg.Add(3)
fmt.Println(bold(cyan("::") + bold(" Checking for conflicts...")))
go func() {
ds.checkForwardConflicts(conflicts)
ds.checkReverseConflicts(conflicts)
wg.Done()
}()
fmt.Println(bold(cyan("::") + bold(" Checking for inner conflicts...")))
go func() {
ds.checkInnerConflicts(innerConflicts)
wg.Done()
}()
go func() {
ds.checkInnerRepoConflicts(repoConflicts)
wg.Done()
}()
wg.Wait()
formatConflicts := func(conflicts mapStringSet, inner bool, s string) {
if len(conflicts) != 0 {
fmt.Fprintln(os.Stderr)
if inner {
fmt.Fprintln(os.Stderr, bold(red(arrow)), bold("Inner conflicts found:"))
} else {
fmt.Fprintln(os.Stderr, bold(red(arrow)), bold("Package conflicts found:"))
}
printConflict := func(name string, pkgs stringSet) {
str := fmt.Sprintf(s, cyan(name))
for pkg := range pkgs {
str += " " + cyan(pkg) + ","
}
str = strings.TrimSuffix(str, ",")
fmt.Fprintln(os.Stderr, str)
}
for _, pkg := range ds.Repo {
if pkgs, ok := conflicts[pkg.Name()]; ok {
printConflict(pkg.Name(), pkgs)
}
}
for _, base := range ds.Aur {
for _, pkg := range base {
if pkgs, ok := conflicts[pkg.Name]; ok {
printConflict(pkg.Name, pkgs)
}
}
}
}
}
repoStr := red(bold(smallArrow)) + " %s Conflicts with:"
formatConflicts(repoConflicts, true, repoStr)
if len(repoConflicts) > 0 {
return nil, fmt.Errorf("Unavoidable conflicts, aborting")
}
str := red(bold(smallArrow)) + " Installing %s will remove:"
formatConflicts(conflicts, false, str)
formatConflicts(innerConflicts, true, str)
for name, c := range innerConflicts {
for cs, _ := range c {
conflicts.Add(name, cs)
}
}
if len(conflicts) > 0 {
if !config.UseAsk {
if config.NoConfirm {
return nil, fmt.Errorf("Package conflicts can not be resolved with noconfirm, aborting")
}
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, bold(red(arrow)), bold("Conflicting packages will have to be confirmed manually"))
fmt.Fprintln(os.Stderr)
}
}
return conflicts, nil
}