From 5109fc6250746987efce665c4d7ec87768a526f3 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Wed, 10 Oct 2018 22:13:00 +0100 Subject: [PATCH 01/11] Move target code to dep.go --- dep.go | 31 +++++++++++++++++++++++++++++++ depPool.go | 31 ------------------------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/dep.go b/dep.go index b3fae560..5f1eebc1 100644 --- a/dep.go +++ b/dep.go @@ -40,6 +40,37 @@ func (q providers) Swap(i, j int) { q.Pkgs[i], q.Pkgs[j] = q.Pkgs[j], q.Pkgs[i] } +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() +} + func splitDep(dep string) (string, string, string) { mod := "" diff --git a/depPool.go b/depPool.go index 231cecd9..58b92f68 100644 --- a/depPool.go +++ b/depPool.go @@ -9,37 +9,6 @@ import ( 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 From 75d01f32b47d95653821b58aa34f7a71cd91af8b Mon Sep 17 00:00:00 2001 From: morganamilo Date: Wed, 10 Oct 2018 22:27:42 +0100 Subject: [PATCH 02/11] Move Base code to dep.go --- dep.go | 14 ++++++++++++++ depOrder.go | 14 -------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/dep.go b/dep.go index 5f1eebc1..11788694 100644 --- a/dep.go +++ b/dep.go @@ -40,6 +40,20 @@ func (q providers) Swap(i, j int) { q.Pkgs[i], q.Pkgs[j] = q.Pkgs[j], q.Pkgs[i] } +type Base []*rpc.Pkg + +func (b Base) Pkgbase() string { + return b[0].PackageBase +} + +func (b Base) Version() string { + return b[0].Version +} + +func (b Base) URLPath() string { + return b[0].URLPath +} + type target struct { Db string Name string diff --git a/depOrder.go b/depOrder.go index e2d3f9a1..833248bc 100644 --- a/depOrder.go +++ b/depOrder.go @@ -5,20 +5,6 @@ import ( rpc "github.com/mikkeloscar/aur" ) -type Base []*rpc.Pkg - -func (b Base) Pkgbase() string { - return b[0].PackageBase -} - -func (b Base) Version() string { - return b[0].Version -} - -func (b Base) URLPath() string { - return b[0].URLPath -} - type depOrder struct { Aur []Base Repo []*alpm.Package From 0efc31a881d7b322216d70ea8fb7411a7a112be4 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Thu, 11 Oct 2018 00:14:33 +0100 Subject: [PATCH 03/11] Add helper functions for []Base --- dep.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/dep.go b/dep.go index 11788694..e155b2d3 100644 --- a/dep.go +++ b/dep.go @@ -54,6 +54,29 @@ func (b Base) URLPath() string { return b[0].URLPath } +func baseAppend(bases []Base, pkg *rpc.Pkg) []Base { + for i, base := range bases { + if base.Pkgbase() == pkg.PackageBase { + bases[i] = append(bases[i], pkg) + return bases + } + } + + return append(bases, Base{pkg}) +} + +func baseFind(bases []Base, name string) *rpc.Pkg { + for _, base := range bases { + for _, pkg := range base { + if pkg.Name == name { + return pkg + } + } + } + + return nil +} + type target struct { Db string Name string From 704e8406d141730468b0f3343c2bb8a0cb45fe9b Mon Sep 17 00:00:00 2001 From: morganamilo Date: Thu, 11 Oct 2018 00:15:06 +0100 Subject: [PATCH 04/11] Merge depOrder with depSolver merge both dependency resolving parts into depSolver. The name was choosen simply to not colide with the current code. depSolver works but it not implemented in the install process and is missing conflict checking. --- depSolver.go | 716 +++++++++++++++++++++++++++++++++++++++++++++++++++ install.go | 8 + 2 files changed, 724 insertions(+) create mode 100644 depSolver.go diff --git a/depSolver.go b/depSolver.go new file mode 100644 index 00000000..9e479368 --- /dev/null +++ b/depSolver.go @@ -0,0 +1,716 @@ +package main + +import ( + "fmt" + "sort" + "strings" + "sync" + + alpm "github.com/jguer/go-alpm" + rpc "github.com/mikkeloscar/aur" +) + +type depSolver struct { + Aur []Base + Repo []*alpm.Package + Runtime stringSet + Targets []target + Explicit stringSet + AurCache map[string]*rpc.Pkg + Groups []string + LocalDb *alpm.Db + SyncDb alpm.DbList + Seen stringSet + Warnings *aurWarnings +} + +func makeDepSolver() (*depSolver, error) { + localDb, err := alpmHandle.LocalDb() + if err != nil { + return nil, err + } + syncDb, err := alpmHandle.SyncDbs() + if err != nil { + return nil, err + } + + return &depSolver{ + make([]Base, 0), + make([]*alpm.Package, 0), + make(stringSet), + make([]target, 0), + make(stringSet), + make(map[string]*rpc.Pkg), + make([]string, 0), + localDb, + syncDb, + make(stringSet), + nil, + }, nil +} + +func getDepSolver(pkgs []string, warnings *aurWarnings) (*depSolver, error) { + ds, err := makeDepSolver() + if err != nil { + return nil, err + } + + ds.Warnings = warnings + err = ds.resolveTargets(pkgs) + if err != nil { + return nil, err + } + + ds.resolveRuntime() + return ds, err +} + +// Includes db/ prefixes and group installs +func (ds *depSolver) resolveTargets(pkgs []string) error { + // RPC requests are slow + // Combine as many AUR package requests as possible into a single RPC + // call + aurTargets := make(stringSet) + pkgs = removeInvalidTargets(pkgs) + + 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 skipped even if it's from a different database to + // the one specified + // this is how pacman behaves + if ds.hasPackage(target.DepString()) { + continue + } + + var foundPkg *alpm.Package + var singleDb *alpm.Db + + // aur/ prefix means we only check the aur + if target.Db == "aur" || mode == ModeAUR { + ds.Targets = append(ds.Targets, target) + aurTargets.set(target.DepString()) + continue + } + + // If there'ss 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 = ds.SyncDb.FindSatisfier(target.DepString()) + } + + if err == nil { + ds.Targets = append(ds.Targets, target) + ds.Explicit.set(foundPkg.Name()) + ds.ResolveRepoDependency(foundPkg) + continue + } else { + //check for groups + //currently we don't 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 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 + group, err := ds.SyncDb.PkgCachebyGroup(target.Name) + if err == nil { + ds.Groups = append(ds.Groups, target.String()) + group.ForEach(func(pkg alpm.Package) error { + ds.Explicit.set(pkg.Name()) + return nil + }) + continue + } + } + + //if there was no db prefix check the aur + if target.Db == "" { + aurTargets.set(target.DepString()) + } + + ds.Targets = append(ds.Targets, target) + } + + if len(aurTargets) > 0 && (mode == ModeAny || mode == ModeAUR) { + return ds.resolveAURPackages(aurTargets, true) + } + + return nil +} + +func (ds *depSolver) hasPackage(name string) bool { + for _, pkg := range ds.Repo { + if pkg.Name() == name { + return true + } + } + + for _, base := range ds.Aur { + for _, pkg := range base { + if pkg.Name == name { + return true + } + } + } + + for _, pkg := range ds.Groups { + if pkg == name { + return true + } + } + + return false +} + +func (ds *depSolver) findSatisfierAur(dep string) *rpc.Pkg { + for _, base := range ds.Aur { + for _, pkg := range base { + if satisfiesAur(dep, pkg) { + return pkg + } + } + } + + return nil +} + +func (ds *depSolver) findSatisfierRepo(dep string) *alpm.Package { + for _, pkg := range ds.Repo { + if satisfiesRepo(dep, pkg) { + return pkg + } + } + + return nil +} + +func (ds *depSolver) hasSatisfier(dep string) bool { + return ds.findSatisfierRepo(dep) != nil || ds.findSatisfierAur(dep) != nil +} + +func (ds *depSolver) ResolveRepoDependency(pkg *alpm.Package) { + if ds.Seen.get(pkg.Name()) { + return + } + ds.Repo = append(ds.Repo, pkg) + ds.Seen.set(pkg.Name()) + + pkg.Depends().ForEach(func(dep alpm.Depend) (err error) { + //have satisfier in dep tree: skip + if ds.hasSatisfier(dep.String()) { + return + } + + //has satisfier installed: skip + _, isInstalled := ds.LocalDb.PkgCache().FindSatisfier(dep.String()) + if isInstalled == nil { + return + } + + //has satisfier in repo: fetch it + repoPkg, inRepos := ds.SyncDb.FindSatisfier(dep.String()) + if inRepos != nil { + return + } + + ds.ResolveRepoDependency(repoPkg) + return nil + }) +} + +// This is mostly used to promote packages from the cache +// to the Install list +// Provide a pacman style provider menu if there's more than one candidate +// This acts slightly differently 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. +// TODO: maybe intermix repo providers in the menu +func (ds *depSolver) findSatisfierAurCache(dep string) *rpc.Pkg { + depName, _, _ := splitDep(dep) + seen := make(stringSet) + providers := makeProviders(depName) + + if _, err := ds.LocalDb.PkgByName(depName); err == nil { + if pkg, ok := ds.AurCache[dep]; ok && pkgSatisfies(pkg.Name, pkg.Version, dep) { + return pkg + } + + } + + if cmdArgs.op == "Y" || cmdArgs.op == "yay" { + for _, pkg := range ds.AurCache { + if pkgSatisfies(pkg.Name, pkg.Version, dep) { + for _, target := range ds.Targets { + if target.Name == pkg.Name { + return pkg + } + } + } + } + } + + for _, pkg := range ds.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 (ds *depSolver) cacheAURPackages(_pkgs stringSet) error { + pkgs := _pkgs.copy() + query := make([]string, 0) + + for pkg := range pkgs { + if _, ok := ds.AurCache[pkg]; ok { + pkgs.remove(pkg) + } + } + + if len(pkgs) == 0 { + return nil + } + + if config.Provides { + err := ds.findProvides(pkgs) + if err != nil { + return err + } + } + + for pkg := range pkgs { + if _, ok := ds.AurCache[pkg]; !ok { + name, _, _ := splitDep(pkg) + query = append(query, name) + } + } + + info, err := aurInfo(query, ds.Warnings) + if err != nil { + return err + } + + for _, pkg := range info { + // Dump everything in cache just in case we need it later + ds.AurCache[pkg.Name] = pkg + } + + 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 in case 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 (ds *depSolver) 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 := ds.AurCache[result.Name]; !ok { + pkgs.set(result.Name) + } + mux.Unlock() + } + } + + for pkg := range pkgs { + if _, err := ds.LocalDb.PkgByName(pkg); err == nil { + continue + } + wg.Add(1) + go doSearch(pkg) + } + + wg.Wait() + + return nil +} + +func (ds *depSolver) resolveAURPackages(pkgs stringSet, explicit bool) error { + newPackages := make(stringSet) + newAURPackages := make(stringSet) + toAdd := make([]*rpc.Pkg, 0) + + if len(pkgs) == 0 { + return nil + } + + err := ds.cacheAURPackages(pkgs) + if err != nil { + return err + } + + for name := range pkgs { + if ds.Seen.get(name) { + continue + } + + pkg := ds.findSatisfierAurCache(name) + if pkg == nil { + continue + } + + if explicit { + ds.Explicit.set(pkg.Name) + } + + ds.Seen.set(pkg.Name) + toAdd = append(toAdd, pkg) + + for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} { + for _, dep := range deps { + newPackages.set(dep) + } + } + } + + for dep := range newPackages { + if ds.hasSatisfier(dep) { + continue + } + + _, isInstalled := ds.LocalDb.PkgCache().FindSatisfier(dep) //has satisfier installed: skip + hm := hideMenus + hideMenus = isInstalled == nil + repoPkg, inRepos := ds.SyncDb.FindSatisfier(dep) //has satisfier in repo: fetch it + hideMenus = hm + if isInstalled == nil && (config.ReBuild != "tree" || inRepos == nil) { + continue + } + + if inRepos == nil { + ds.ResolveRepoDependency(repoPkg) + continue + } + + //assume it's in the aur + //ditch the versioning because the RPC can't handle it + newAURPackages.set(dep) + + } + + err = ds.resolveAURPackages(newAURPackages, false) + + for _, pkg := range toAdd { + if !ds.hasPackage(pkg.Name) { + ds.Aur = baseAppend(ds.Aur, pkg) + } + } + + return err +} + +func (ds *depSolver) Print() { + repo := "" + repoMake := "" + aur := "" + aurMake := "" + + repoLen := 0 + repoMakeLen := 0 + aurLen := 0 + aurMakeLen := 0 + + for _, pkg := range ds.Repo { + if ds.Runtime.get(pkg.Name()) { + repo += " " + pkg.Name() + "-" + pkg.Version() + repoLen++ + } else { + repoMake += " " + pkg.Name() + "-" + pkg.Version() + repoMakeLen++ + } + } + + for _, base := range ds.Aur { + pkg := base.Pkgbase() + pkgStr := " " + pkg + "-" + base[0].Version + pkgStrMake := pkgStr + + push := false + pushMake := false + + if len(base) > 1 || pkg != base[0].Name { + pkgStr += " (" + pkgStrMake += " (" + + for _, split := range base { + if ds.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 ds.Runtime.get(base[0].Name) { + aurLen++ + push = true + } else { + aurMakeLen++ + pushMake = true + } + + if push { + aur += pkgStr + } + if pushMake { + aurMake += pkgStrMake + } + } + + printDownloads("Repo", repoLen, repo) + printDownloads("Repo Make", repoMakeLen, repoMake) + printDownloads("Aur", aurLen, aur) + printDownloads("Aur Make", aurMakeLen, aurMake) +} + +func (ds *depSolver) resolveRuntime() { + for _, pkg := range ds.Repo { + if ds.Explicit.get(pkg.Name()) { + ds.Runtime.set(pkg.Name()) + ds.resolveRuntimeRepo(pkg) + } + } + + for _, base := range ds.Aur { + for _, pkg := range base { + if ds.Explicit.get(pkg.Name) { + ds.Runtime.set(pkg.Name) + ds.resolveRuntimeAur(pkg) + } + } + } +} + +func (ds *depSolver) resolveRuntimeRepo(pkg *alpm.Package) { + pkg.Depends().ForEach(func(dep alpm.Depend) (err error) { + for _, pkg := range ds.Repo { + if ds.Runtime.get(pkg.Name()) { + continue + } + + if satisfiesRepo(dep.String(), pkg) { + ds.Runtime.set(pkg.Name()) + ds.resolveRuntimeRepo(pkg) + } + } + return nil + }) +} + +func (ds *depSolver) resolveRuntimeAur(pkg *rpc.Pkg) { + for _, dep := range pkg.Depends { + for _, pkg := range ds.Repo { + if ds.Runtime.get(pkg.Name()) { + continue + } + + if satisfiesRepo(dep, pkg) { + ds.Runtime.set(pkg.Name()) + ds.resolveRuntimeRepo(pkg) + } + } + + for _, base := range ds.Aur { + for _, pkg := range base { + if ds.Runtime.get(pkg.Name) { + continue + } + + if satisfiesAur(dep, pkg) { + ds.Runtime.set(pkg.Name) + ds.resolveRuntimeAur(pkg) + } + } + } + } +} + +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.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("") +} + +func (ds *depSolver) HasMake() bool { + lenAur := 0 + for _, base := range ds.Aur { + lenAur += len(base) + } + + return len(ds.Runtime) != lenAur+len(ds.Repo) +} + +func (ds *depSolver) getMake() []string { + makeOnly := make([]string, 0, len(ds.Aur)+len(ds.Repo)-len(ds.Runtime)) + + for _, base := range ds.Aur { + for _, pkg := range base { + if !ds.Runtime.get(pkg.Name) { + makeOnly = append(makeOnly, pkg.Name) + } + } + } + + for _, pkg := range ds.Repo { + if !ds.Runtime.get(pkg.Name()) { + makeOnly = append(makeOnly, pkg.Name()) + } + } + + return makeOnly +} diff --git a/install.go b/install.go index c147029f..4e035791 100644 --- a/install.go +++ b/install.go @@ -115,6 +115,14 @@ func install(parser *arguments) error { return err } + ds, err := getDepSolver(requestTargets, warnings) + if err != nil { + return err + } + + ds.Print() + fmt.Println(ds.Runtime) + err = dp.CheckMissing() if err != nil { return err From 4ff36fbd4b42193614509ff0d9aa792aad3358e6 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Thu, 11 Oct 2018 17:26:22 +0100 Subject: [PATCH 05/11] Use depSolver in install --- depSolver.go | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++ install.go | 86 +++++++++------------- 2 files changed, 237 insertions(+), 50 deletions(-) diff --git a/depSolver.go b/depSolver.go index 9e479368..9fd4ba35 100644 --- a/depSolver.go +++ b/depSolver.go @@ -714,3 +714,204 @@ func (ds *depSolver) getMake() []string { return makeOnly } + +func (ds *depSolver) checkInnerConflict(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(name, pkg.Name) + } + } + } + + for _, pkg := range ds.Repo { + if pkg.Name() == name { + continue + } + + if satisfiesRepo(conflict, pkg) { + conflicts.Add(name, pkg.Name()) + } + } +} + +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() + if n != conflict { + n += " (" + conflict + ")" + } + 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) { + if name != conflict { + name += " (" + conflict + ")" + } + + conflicts.Add(pkg.Name, name) + } + } + } + + for _, pkg := range ds.Repo { + if pkg.Name() == name { + continue + } + + if satisfiesRepo(conflict, pkg) { + if name != conflict { + name += " (" + conflict + ")" + } + + conflicts.Add(pkg.Name(), name) + } + } +} + +func (ds *depSolver) checkInnerConflicts(conflicts mapStringSet) { + for _, base := range ds.Aur { + for _, pkg := range base { + for _, conflict := range pkg.Conflicts { + ds.checkInnerConflict(pkg.Name, conflict, conflicts) + } + } + } + + for _, pkg := range ds.Repo { + pkg.Conflicts().ForEach(func(conflict alpm.Depend) error { + ds.checkInnerConflict(pkg.Name(), conflict.String(), conflicts) + return nil + }) + } +} + +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) CheckConflicts() (mapStringSet, error) { + var wg sync.WaitGroup + innerConflicts := make(mapStringSet) + conflicts := make(mapStringSet) + wg.Add(2) + + 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() + }() + + 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) + } + + } + + 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) + } + + } + + // Add the inner conflicts to the conflicts + // These are used to decide what to pass --ask to (if set) or don't pass --noconfirm to + // As we have no idea what the order is yet we add every inner conflict to the slice + for name, pkgs := range innerConflicts { + conflicts[name] = make(stringSet) + for pkg := range pkgs { + conflicts[pkg] = make(stringSet) + } + } + + if len(conflicts) > 0 { + if !config.UseAsk { + if config.NoConfirm { + return nil, fmt.Errorf("Package conflicts can not be resolved with noconfirm, aborting") + } + + fmt.Println() + fmt.Println(bold(red(arrow)), bold("Conflicting packages will have to be confirmed manually")) + fmt.Println() + } + } + + return conflicts, nil +} diff --git a/install.go b/install.go index 4e035791..d6d65a13 100644 --- a/install.go +++ b/install.go @@ -17,7 +17,6 @@ import ( func install(parser *arguments) error { var err error var incompatible stringSet - var do *depOrder var aurUp upSlice var repoUp upSlice @@ -110,25 +109,17 @@ func install(parser *arguments) error { targets := sliceToStringSet(parser.targets) - dp, err := getDepPool(requestTargets, warnings) - if err != nil { - return err - } - ds, err := getDepSolver(requestTargets, warnings) if err != nil { return err } - ds.Print() - fmt.Println(ds.Runtime) - - err = dp.CheckMissing() + err = ds.CheckMissing() if err != nil { return err } - if len(dp.Aur) == 0 { + if len(ds.Aur) == 0 { if !config.CombinedUpgrade { if parser.existsArg("u", "sysupgrade") { fmt.Println(" there is nothing to do") @@ -142,37 +133,32 @@ func install(parser *arguments) error { return show(passToPacman(parser)) } - if len(dp.Aur) > 0 && 0 == os.Geteuid() { + if len(ds.Aur) > 0 && 0 == os.Geteuid() { return fmt.Errorf(bold(red(arrow)) + " Refusing to install AUR Packages as root, Aborting.") } - conflicts, err := dp.CheckConflicts() + conflicts, err := ds.CheckConflicts() if err != nil { return err } - do = getDepOrder(dp) - if err != nil { - return err - } - - for _, pkg := range do.Repo { + for _, pkg := range ds.Repo { arguments.addTarget(pkg.DB().Name() + "/" + pkg.Name()) } - for _, pkg := range dp.Groups { + for _, pkg := range ds.Groups { arguments.addTarget(pkg) } - if len(do.Aur) == 0 && len(arguments.targets) == 0 && (!parser.existsArg("u", "sysupgrade") || mode == ModeAUR) { + if len(ds.Aur) == 0 && len(arguments.targets) == 0 && (!parser.existsArg("u", "sysupgrade") || mode == ModeAUR) { fmt.Println(" there is nothing to do") return nil } - do.Print() + ds.Print() fmt.Println() - if do.HasMake() { + if ds.HasMake() { if config.RemoveMake == "yes" { removeMake = true } else if config.RemoveMake == "no" { @@ -183,9 +169,9 @@ func install(parser *arguments) error { } if config.CleanMenu { - if anyExistInCache(do.Aur) { - askClean := pkgbuildNumberMenu(do.Aur, remoteNamesCache) - toClean, err := cleanNumberMenu(do.Aur, remoteNamesCache, askClean) + if anyExistInCache(ds.Aur) { + askClean := pkgbuildNumberMenu(ds.Aur, remoteNamesCache) + toClean, err := cleanNumberMenu(ds.Aur, remoteNamesCache, askClean) if err != nil { return err } @@ -194,8 +180,8 @@ func install(parser *arguments) error { } } - toSkip := pkgbuildsToSkip(do.Aur, targets) - cloned, err := downloadPkgbuilds(do.Aur, toSkip, config.BuildDir) + toSkip := pkgbuildsToSkip(ds.Aur, targets) + cloned, err := downloadPkgbuilds(ds.Aur, toSkip, config.BuildDir) if err != nil { return err } @@ -204,8 +190,8 @@ func install(parser *arguments) error { var toEdit []Base if config.DiffMenu { - pkgbuildNumberMenu(do.Aur, remoteNamesCache) - toDiff, err = diffNumberMenu(do.Aur, remoteNamesCache) + pkgbuildNumberMenu(ds.Aur, remoteNamesCache) + toDiff, err = diffNumberMenu(ds.Aur, remoteNamesCache) if err != nil { return err } @@ -228,19 +214,19 @@ func install(parser *arguments) error { config.NoConfirm = oldValue } - err = mergePkgbuilds(do.Aur) + err = mergePkgbuilds(ds.Aur) if err != nil { return err } - srcinfos, err = parseSrcinfoFiles(do.Aur, true) + srcinfos, err = parseSrcinfoFiles(ds.Aur, true) if err != nil { return err } if config.EditMenu { - pkgbuildNumberMenu(do.Aur, remoteNamesCache) - toEdit, err = editNumberMenu(do.Aur, remoteNamesCache) + pkgbuildNumberMenu(ds.Aur, remoteNamesCache) + toEdit, err = editNumberMenu(ds.Aur, remoteNamesCache) if err != nil { return err } @@ -263,13 +249,13 @@ func install(parser *arguments) error { config.NoConfirm = oldValue } - incompatible, err = getIncompatible(do.Aur, srcinfos) + incompatible, err = getIncompatible(ds.Aur, srcinfos) if err != nil { return err } if config.PGPFetch { - err = checkPgpKeys(do.Aur, srcinfos) + err = checkPgpKeys(ds.Aur, srcinfos) if err != nil { return err } @@ -290,15 +276,15 @@ func install(parser *arguments) error { expArguments := makeArguments() expArguments.addArg("D", "asexplicit") - for _, pkg := range do.Repo { - if !dp.Explicit.get(pkg.Name()) && !localNamesCache.get(pkg.Name()) && !remoteNamesCache.get(pkg.Name()) { + for _, pkg := range ds.Repo { + if !ds.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()) { + if parser.existsArg("asdeps", "asdep") && ds.Explicit.get(pkg.Name()) { depArguments.addTarget(pkg.Name()) - } else if parser.existsArg("asexp", "asexplicit") && dp.Explicit.get(pkg.Name()) { + } else if parser.existsArg("asexp", "asexplicit") && ds.Explicit.get(pkg.Name()) { expArguments.addTarget(pkg.Name()) } } @@ -320,12 +306,12 @@ func install(parser *arguments) error { go updateCompletion(false) - err = downloadPkgbuildsSources(do.Aur, incompatible) + err = downloadPkgbuildsSources(ds.Aur, incompatible) if err != nil { return err } - err = buildInstallPkgbuilds(dp, do, srcinfos, parser, incompatible, conflicts) + err = buildInstallPkgbuilds(ds, srcinfos, parser, incompatible, conflicts) if err != nil { return err } @@ -334,7 +320,7 @@ func install(parser *arguments) error { removeArguments := makeArguments() removeArguments.addArg("R", "u") - for _, pkg := range do.getMake() { + for _, pkg := range ds.getMake() { removeArguments.addTarget(pkg) } @@ -349,7 +335,7 @@ func install(parser *arguments) error { } if config.CleanAfter { - cleanAfter(do.Aur) + cleanAfter(ds.Aur) } return nil @@ -906,8 +892,8 @@ func downloadPkgbuildsSources(bases []Base, incompatible stringSet) (err error) return } -func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc.Srcinfo, parser *arguments, incompatible stringSet, conflicts mapStringSet) error { - for _, base := range do.Aur { +func buildInstallPkgbuilds(ds *depSolver, srcinfos map[string]*gosrc.Srcinfo, parser *arguments, incompatible stringSet, conflicts mapStringSet) error { + for _, base := range ds.Aur { pkg := base.Pkgbase() dir := filepath.Join(config.BuildDir, pkg) built := true @@ -933,7 +919,7 @@ func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc isExplicit := false for _, b := range base { - isExplicit = isExplicit || dp.Explicit.get(b.Name) + isExplicit = isExplicit || ds.Explicit.get(b.Name) } if config.ReBuild == "no" || (config.ReBuild == "yes" && !isExplicit) { for _, split := range base { @@ -956,7 +942,7 @@ func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc if cmdArgs.existsArg("needed") { installed := true for _, split := range base { - if alpmpkg, err := dp.LocalDb.PkgByName(split.Name); err != nil || alpmpkg.Version() != version { + if alpmpkg, err := ds.LocalDb.PkgByName(split.Name); err != nil || alpmpkg.Version() != version { installed = false } } @@ -1040,11 +1026,11 @@ func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc } arguments.addTarget(pkgdest) - if !dp.Explicit.get(split.Name) && !localNamesCache.get(split.Name) && !remoteNamesCache.get(split.Name) { + if !ds.Explicit.get(split.Name) && !localNamesCache.get(split.Name) && !remoteNamesCache.get(split.Name) { depArguments.addTarget(split.Name) } - if dp.Explicit.get(split.Name) { + if ds.Explicit.get(split.Name) { if parser.existsArg("asdeps", "asdep") { depArguments.addTarget(split.Name) } else if parser.existsArg("asexplicit", "asexp") { From a83f8fb9e81441c79edddb92aacd1a3211b40d56 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Thu, 11 Oct 2018 17:28:11 +0100 Subject: [PATCH 06/11] Remove depPool and depOrder code --- dep.go | 5 + depCheck.go | 295 ---------------------------------- depOrder.go | 121 -------------- depPool.go | 446 ---------------------------------------------------- print.go | 70 --------- 5 files changed, 5 insertions(+), 932 deletions(-) delete mode 100644 depCheck.go delete mode 100644 depOrder.go delete mode 100644 depPool.go diff --git a/dep.go b/dep.go index e155b2d3..d37aee44 100644 --- a/dep.go +++ b/dep.go @@ -8,6 +8,11 @@ import ( rpc "github.com/mikkeloscar/aur" ) +type missing struct { + Good stringSet + Missing map[string][][]string +} + type providers struct { lookfor string Pkgs []*rpc.Pkg diff --git a/depCheck.go b/depCheck.go deleted file mode 100644 index 0a3d26bb..00000000 --- a/depCheck.go +++ /dev/null @@ -1,295 +0,0 @@ -package main - -import ( - "fmt" - "strings" - "sync" - - alpm "github.com/jguer/go-alpm" -) - -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() (mapStringSet, 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) - } - - } - - 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) - } - - } - - // Add the inner conflicts to the conflicts - // These are used to decide what to pass --ask to (if set) or don't pass --noconfirm to - // As we have no idea what the order is yet we add every inner conflict to the slice - for name, pkgs := range innerConflicts { - conflicts[name] = make(stringSet) - for pkg := range pkgs { - conflicts[pkg] = make(stringSet) - } - } - - if len(conflicts) > 0 { - if !config.UseAsk { - if config.NoConfirm { - return nil, fmt.Errorf("Package conflicts can not be resolved with noconfirm, aborting") - } - - fmt.Println() - fmt.Println(bold(red(arrow)), bold("Conflicting packages will have to be confirmed manually")) - fmt.Println() - } - } - - return conflicts, 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("") -} diff --git a/depOrder.go b/depOrder.go deleted file mode 100644 index 833248bc..00000000 --- a/depOrder.go +++ /dev/null @@ -1,121 +0,0 @@ -package main - -import ( - alpm "github.com/jguer/go-alpm" - rpc "github.com/mikkeloscar/aur" -) - -type depOrder struct { - Aur []Base - Repo []*alpm.Package - Runtime stringSet -} - -func makeDepOrder() *depOrder { - return &depOrder{ - make([]Base, 0), - make([]*alpm.Package, 0), - make(stringSet), - } -} - -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) - } - } - } - - for i, base := range do.Aur { - if base.Pkgbase() == pkg.PackageBase { - do.Aur[i] = append(base, pkg) - return - } - } - - do.Aur = append(do.Aur, Base{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.Aur { - 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.Aur { - 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 -} diff --git a/depPool.go b/depPool.go deleted file mode 100644 index 58b92f68..00000000 --- a/depPool.go +++ /dev/null @@ -1,446 +0,0 @@ -package main - -import ( - "sort" - "strings" - "sync" - - alpm "github.com/jguer/go-alpm" - rpc "github.com/mikkeloscar/aur" -) - -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) - - pkgs = removeInvalidTargets(pkgs) - - 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 skipped 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" || mode == ModeAUR { - dp.Targets = append(dp.Targets, target) - aurTargets.set(target.DepString()) - continue - } - - // If there'ss 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 don't 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 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 - 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 && (mode == ModeAny || mode == ModeAUR) { - 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 in case 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 - } - - _, isInstalled := dp.LocalDb.PkgCache().FindSatisfier(dep) //has satisfier installed: skip - hm := hideMenus - hideMenus = isInstalled == nil - repoPkg, inRepos := dp.SyncDb.FindSatisfier(dep) //has satisfier in repo: fetch it - hideMenus = hm - if isInstalled == nil && (config.ReBuild != "tree" || inRepos == nil) { - continue - } - - if inRepos == nil { - dp.ResolveRepoDependency(repoPkg) - continue - } - - //assume it's in the aur - //ditch the versioning because the RPC can't 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 there's more than one candidate -// This acts slightly differently 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. -// 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 - } - - } - - if cmdArgs.op == "Y" || cmdArgs.op == "yay" { - for _, pkg := range dp.AurCache { - if pkgSatisfies(pkg.Name, pkg.Version, dep) { - for _, target := range dp.Targets { - if target.Name == pkg.Name { - return pkg - } - } - } - } - } - - 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 -} diff --git a/print.go b/print.go index c47e52a4..9df7d609 100644 --- a/print.go +++ b/print.go @@ -189,76 +189,6 @@ func (u upSlice) print() { } } -// printDownloadsFromRepo prints repository packages to be downloaded -func (do *depOrder) Print() { - repo := "" - repoMake := "" - aur := "" - aurMake := "" - - repoLen := 0 - repoMakeLen := 0 - aurLen := 0 - aurMakeLen := 0 - - 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 _, base := range do.Aur { - pkg := base.Pkgbase() - pkgStr := " " + pkg + "-" + base[0].Version - pkgStrMake := pkgStr - - push := false - pushMake := false - - if len(base) > 1 || pkg != base[0].Name { - pkgStr += " (" - pkgStrMake += " (" - - for _, split := range base { - 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 do.Runtime.get(base[0].Name) { - aurLen++ - push = true - } else { - aurMakeLen++ - pushMake = true - } - - if push { - aur += pkgStr - } - if pushMake { - aurMake += pkgStrMake - } - } - - printDownloads("Repo", repoLen, repo) - printDownloads("Repo Make", repoMakeLen, repoMake) - printDownloads("Aur", aurLen, aur) - printDownloads("Aur Make", aurMakeLen, aurMake) -} - func printDownloads(repoName string, length int, packages string) { if length < 1 { return From ff4f076d579119dc67cb7745fc9d9747b4a333ea Mon Sep 17 00:00:00 2001 From: morganamilo Date: Thu, 11 Oct 2018 18:55:23 +0100 Subject: [PATCH 07/11] Move depSolver checks back to depCheck.go --- depCheck.go | 298 +++++++++++++++++++++++++++++++++++++++++++++++++++ depSolver.go | 290 ------------------------------------------------- 2 files changed, 298 insertions(+), 290 deletions(-) create mode 100644 depCheck.go diff --git a/depCheck.go b/depCheck.go new file mode 100644 index 00000000..6a12cd63 --- /dev/null +++ b/depCheck.go @@ -0,0 +1,298 @@ +package main + +import ( + "fmt" + "strings" + "sync" + + alpm "github.com/jguer/go-alpm" +) + +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.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("") +} + +func (ds *depSolver) checkInnerConflict(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(name, pkg.Name) + } + } + } + + for _, pkg := range ds.Repo { + if pkg.Name() == name { + continue + } + + if satisfiesRepo(conflict, pkg) { + conflicts.Add(name, pkg.Name()) + } + } +} + +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() + if n != conflict { + n += " (" + conflict + ")" + } + 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) { + if name != conflict { + name += " (" + conflict + ")" + } + + conflicts.Add(pkg.Name, name) + } + } + } + + for _, pkg := range ds.Repo { + if pkg.Name() == name { + continue + } + + if satisfiesRepo(conflict, pkg) { + if name != conflict { + name += " (" + conflict + ")" + } + + conflicts.Add(pkg.Name(), name) + } + } +} + +func (ds *depSolver) checkInnerConflicts(conflicts mapStringSet) { + for _, base := range ds.Aur { + for _, pkg := range base { + for _, conflict := range pkg.Conflicts { + ds.checkInnerConflict(pkg.Name, conflict, conflicts) + } + } + } + + for _, pkg := range ds.Repo { + pkg.Conflicts().ForEach(func(conflict alpm.Depend) error { + ds.checkInnerConflict(pkg.Name(), conflict.String(), conflicts) + return nil + }) + } +} + +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) CheckConflicts() (mapStringSet, error) { + var wg sync.WaitGroup + innerConflicts := make(mapStringSet) + conflicts := make(mapStringSet) + wg.Add(2) + + 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() + }() + + 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) + } + + } + + 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) + } + + } + + // Add the inner conflicts to the conflicts + // These are used to decide what to pass --ask to (if set) or don't pass --noconfirm to + // As we have no idea what the order is yet we add every inner conflict to the slice + for name, pkgs := range innerConflicts { + conflicts[name] = make(stringSet) + for pkg := range pkgs { + conflicts[pkg] = make(stringSet) + } + } + + if len(conflicts) > 0 { + if !config.UseAsk { + if config.NoConfirm { + return nil, fmt.Errorf("Package conflicts can not be resolved with noconfirm, aborting") + } + + fmt.Println() + fmt.Println(bold(red(arrow)), bold("Conflicting packages will have to be confirmed manually")) + fmt.Println() + } + } + + return conflicts, nil +} diff --git a/depSolver.go b/depSolver.go index 9fd4ba35..8f714410 100644 --- a/depSolver.go +++ b/depSolver.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "sort" "strings" "sync" @@ -598,94 +597,6 @@ func (ds *depSolver) resolveRuntimeAur(pkg *rpc.Pkg) { } } -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.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("") -} - func (ds *depSolver) HasMake() bool { lenAur := 0 for _, base := range ds.Aur { @@ -714,204 +625,3 @@ func (ds *depSolver) getMake() []string { return makeOnly } - -func (ds *depSolver) checkInnerConflict(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(name, pkg.Name) - } - } - } - - for _, pkg := range ds.Repo { - if pkg.Name() == name { - continue - } - - if satisfiesRepo(conflict, pkg) { - conflicts.Add(name, pkg.Name()) - } - } -} - -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() - if n != conflict { - n += " (" + conflict + ")" - } - 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) { - if name != conflict { - name += " (" + conflict + ")" - } - - conflicts.Add(pkg.Name, name) - } - } - } - - for _, pkg := range ds.Repo { - if pkg.Name() == name { - continue - } - - if satisfiesRepo(conflict, pkg) { - if name != conflict { - name += " (" + conflict + ")" - } - - conflicts.Add(pkg.Name(), name) - } - } -} - -func (ds *depSolver) checkInnerConflicts(conflicts mapStringSet) { - for _, base := range ds.Aur { - for _, pkg := range base { - for _, conflict := range pkg.Conflicts { - ds.checkInnerConflict(pkg.Name, conflict, conflicts) - } - } - } - - for _, pkg := range ds.Repo { - pkg.Conflicts().ForEach(func(conflict alpm.Depend) error { - ds.checkInnerConflict(pkg.Name(), conflict.String(), conflicts) - return nil - }) - } -} - -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) CheckConflicts() (mapStringSet, error) { - var wg sync.WaitGroup - innerConflicts := make(mapStringSet) - conflicts := make(mapStringSet) - wg.Add(2) - - 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() - }() - - 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) - } - - } - - 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) - } - - } - - // Add the inner conflicts to the conflicts - // These are used to decide what to pass --ask to (if set) or don't pass --noconfirm to - // As we have no idea what the order is yet we add every inner conflict to the slice - for name, pkgs := range innerConflicts { - conflicts[name] = make(stringSet) - for pkg := range pkgs { - conflicts[pkg] = make(stringSet) - } - } - - if len(conflicts) > 0 { - if !config.UseAsk { - if config.NoConfirm { - return nil, fmt.Errorf("Package conflicts can not be resolved with noconfirm, aborting") - } - - fmt.Println() - fmt.Println(bold(red(arrow)), bold("Conflicting packages will have to be confirmed manually")) - fmt.Println() - } - } - - return conflicts, nil -} From 5080e400e115fcf1ad03c757e27bfeba428ee734 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Fri, 12 Oct 2018 21:49:32 +0100 Subject: [PATCH 08/11] Remove printing conflict name in conflicts Currently when printing conflicts the package name is printed and then name of the conflict if it differs from the package name. For example if yay-git is installed and you are installing yay-bin: yay-bin conflicts with yay yay-git provides yay so the conflict is printed as "yay-bin (yay)" This is not too bad but some packages have many provides and conflicts that ends up cluttering the output. For example if gvim is installed and you are installing vim: "gvim, gvim (vim-minimal), gvim (vim)" is printed Just printing the package name should be enough information. If not the user can always -Si either package. --- depCheck.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/depCheck.go b/depCheck.go index 6a12cd63..cbf82030 100644 --- a/depCheck.go +++ b/depCheck.go @@ -128,9 +128,6 @@ func (ds *depSolver) checkForwardConflict(name string, conflict string, conflict if satisfiesRepo(conflict, &pkg) { n := pkg.Name() - if n != conflict { - n += " (" + conflict + ")" - } conflicts.Add(name, n) } @@ -146,10 +143,6 @@ func (ds *depSolver) checkReverseConflict(name string, conflict string, conflict } if satisfiesAur(conflict, pkg) { - if name != conflict { - name += " (" + conflict + ")" - } - conflicts.Add(pkg.Name, name) } } @@ -161,10 +154,6 @@ func (ds *depSolver) checkReverseConflict(name string, conflict string, conflict } if satisfiesRepo(conflict, pkg) { - if name != conflict { - name += " (" + conflict + ")" - } - conflicts.Add(pkg.Name(), name) } } From f6486979947f178d1e22ebe5d0be66f49f0dd0e6 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Fri, 12 Oct 2018 01:12:25 +0100 Subject: [PATCH 09/11] Rework inner conflicts Inner conflicts now check against the previously installed aur packages For example when installing yay, yay-bin and yay-git yay will be checked the previously insalled packages (nothing) yay-bin will be checked the previously insalled packages and see that it conflicts with yay and warn you that yay will be removed yay-git will be checked the previously insalled packages and see that it conflicts with yay but see that it has already been removed by yay-bin so does nothing. Then it will see that it conflicts with yay-bin and warn you that yay-bin will be removed This example is unlikley to happen unless the user enters `yay -S yay yay-bin yay-git`. But where this does happen is packages which have some sort of bootsrapping process such as ffmpeg-libfdk_aac. If two repo packages conflict then we abort. This is because they will be installed at the same time and we never swap repo packages mid AUR install. --- depCheck.go | 174 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 101 insertions(+), 73 deletions(-) diff --git a/depCheck.go b/depCheck.go index cbf82030..2016b3ea 100644 --- a/depCheck.go +++ b/depCheck.go @@ -6,6 +6,7 @@ import ( "sync" alpm "github.com/jguer/go-alpm" + rpc "github.com/mikkeloscar/aur" ) func (ds *depSolver) _checkMissing(dep string, stack []string, missing *missing) { @@ -96,30 +97,6 @@ func (ds *depSolver) CheckMissing() error { return fmt.Errorf("") } -func (ds *depSolver) checkInnerConflict(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(name, pkg.Name) - } - } - } - - for _, pkg := range ds.Repo { - if pkg.Name() == name { - continue - } - - if satisfiesRepo(conflict, pkg) { - conflicts.Add(name, pkg.Name()) - } - } -} - 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()) { @@ -159,23 +136,6 @@ func (ds *depSolver) checkReverseConflict(name string, conflict string, conflict } } -func (ds *depSolver) checkInnerConflicts(conflicts mapStringSet) { - for _, base := range ds.Aur { - for _, pkg := range base { - for _, conflict := range pkg.Conflicts { - ds.checkInnerConflict(pkg.Name, conflict, conflicts) - } - } - } - - for _, pkg := range ds.Repo { - pkg.Conflicts().ForEach(func(conflict alpm.Depend) error { - ds.checkInnerConflict(pkg.Name(), conflict.String(), conflicts) - return nil - }) - } -} - func (ds *depSolver) checkForwardConflicts(conflicts mapStringSet) { for _, base := range ds.Aur { for _, pkg := range base { @@ -208,11 +168,79 @@ func (ds *depSolver) checkReverseConflicts(conflicts mapStringSet) { }) } +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) - wg.Add(2) + repoConflicts := make(mapStringSet) + wg.Add(3) fmt.Println(bold(cyan("::") + bold(" Checking for conflicts..."))) go func() { @@ -226,48 +254,48 @@ func (ds *depSolver) CheckConflicts() (mapStringSet, error) { ds.checkInnerConflicts(innerConflicts) wg.Done() }() + go func() { + ds.checkInnerRepoConflicts(repoConflicts) + 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) + "," + formatConflicts := func(conflicts mapStringSet, inner bool, s string) { + if len(conflicts) != 0 { + fmt.Println() + if inner { + fmt.Println(bold(red(arrow)), bold("Inner conflicts found:")) + } else { + fmt.Println(bold(red(arrow)), bold("Package conflicts found:")) } - str = strings.TrimSuffix(str, ",") - fmt.Println(str) + for name, pkgs := range conflicts { + str := fmt.Sprintf(s, cyan(name)) + for pkg := range pkgs { + str += " " + cyan(pkg) + "," + } + str = strings.TrimSuffix(str, ",") + + fmt.Println(str) + } } - } - 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) - } + repoStr := red(bold(smallArrow)) + " %s Conflicts with:" + formatConflicts(repoConflicts, true, repoStr) + if len(repoConflicts) > 0 { + return nil, fmt.Errorf("Unavoidable conflicts, aborting") } - // Add the inner conflicts to the conflicts - // These are used to decide what to pass --ask to (if set) or don't pass --noconfirm to - // As we have no idea what the order is yet we add every inner conflict to the slice - for name, pkgs := range innerConflicts { - conflicts[name] = make(stringSet) - for pkg := range pkgs { - conflicts[pkg] = make(stringSet) + 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) } } From 0bfba1f24b90e0a7601111de3f905cf961d47709 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Sat, 13 Oct 2018 01:09:43 +0100 Subject: [PATCH 10/11] Use slices for AUR dep resolving This ensures AUR packages are always installed in the order specified by the user. In the case there are any inner conflicts packages will be removed in a predictable way. --- depSolver.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/depSolver.go b/depSolver.go index 8f714410..100f296e 100644 --- a/depSolver.go +++ b/depSolver.go @@ -69,7 +69,7 @@ func (ds *depSolver) resolveTargets(pkgs []string) error { // RPC requests are slow // Combine as many AUR package requests as possible into a single RPC // call - aurTargets := make(stringSet) + aurTargets := make([]string, 0) pkgs = removeInvalidTargets(pkgs) for _, pkg := range pkgs { @@ -91,7 +91,7 @@ func (ds *depSolver) resolveTargets(pkgs []string) error { // aur/ prefix means we only check the aur if target.Db == "aur" || mode == ModeAUR { ds.Targets = append(ds.Targets, target) - aurTargets.set(target.DepString()) + aurTargets = append(aurTargets, target.DepString()) continue } @@ -133,7 +133,7 @@ func (ds *depSolver) resolveTargets(pkgs []string) error { //if there was no db prefix check the aur if target.Db == "" { - aurTargets.set(target.DepString()) + aurTargets = append(aurTargets, target.DepString()) } ds.Targets = append(ds.Targets, target) @@ -292,8 +292,8 @@ func (ds *depSolver) findSatisfierAurCache(dep string) *rpc.Pkg { return nil } -func (ds *depSolver) cacheAURPackages(_pkgs stringSet) error { - pkgs := _pkgs.copy() +func (ds *depSolver) cacheAURPackages(_pkgs []string) error { + pkgs := sliceToStringSet(_pkgs) query := make([]string, 0) for pkg := range pkgs { @@ -392,9 +392,9 @@ func (ds *depSolver) findProvides(pkgs stringSet) error { return nil } -func (ds *depSolver) resolveAURPackages(pkgs stringSet, explicit bool) error { +func (ds *depSolver) resolveAURPackages(pkgs []string, explicit bool) error { newPackages := make(stringSet) - newAURPackages := make(stringSet) + newAURPackages := make([]string, 0) toAdd := make([]*rpc.Pkg, 0) if len(pkgs) == 0 { @@ -406,7 +406,7 @@ func (ds *depSolver) resolveAURPackages(pkgs stringSet, explicit bool) error { return err } - for name := range pkgs { + for _, name := range pkgs { if ds.Seen.get(name) { continue } @@ -451,7 +451,7 @@ func (ds *depSolver) resolveAURPackages(pkgs stringSet, explicit bool) error { //assume it's in the aur //ditch the versioning because the RPC can't handle it - newAURPackages.set(dep) + newAURPackages = append(newAURPackages, dep) } From 7702a88ebbd92fb7334af441265adda73a3b1d2d Mon Sep 17 00:00:00 2001 From: morganamilo Date: Sat, 13 Oct 2018 01:16:50 +0100 Subject: [PATCH 11/11] Print conflicts in order of install This is usefull for inner conflicts, you can follow when packages will be swapped out with others. --- depCheck.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/depCheck.go b/depCheck.go index 2016b3ea..169e4446 100644 --- a/depCheck.go +++ b/depCheck.go @@ -270,7 +270,7 @@ func (ds *depSolver) CheckConflicts() (mapStringSet, error) { fmt.Println(bold(red(arrow)), bold("Package conflicts found:")) } - for name, pkgs := range conflicts { + printConflict := func(name string, pkgs stringSet) { str := fmt.Sprintf(s, cyan(name)) for pkg := range pkgs { str += " " + cyan(pkg) + "," @@ -279,6 +279,19 @@ func (ds *depSolver) CheckConflicts() (mapStringSet, error) { fmt.Println(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) + } + } + } } }