From b2d3eb5c4924230ddb4f1dcf3a891cd610320463 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Tue, 8 May 2018 05:06:25 +0100 Subject: [PATCH 01/20] Add new dependency code to replace the old code This is a rewrite of the dependency system, It aims to be cleaner written, with a better idea of what is needed from the start, meaning less new code being hacked on for things that were not thought about. This version also aims to use as many small functions as possible, for cleaner code and better testing. Added dep.go: general dependency functions Added depPool.go: Replacement of depTree, dependencies were never ordered so a tree did not really make sense. Instead the term pool makes more sense. Added depOrder.go: Replacement of depCatagories, This simply orders the dependencies, dependencies are still catagorized as repo and AUR but I believe this to be a better name Added depCheck.go: Replaces conflicts.go and also contains the missing dependency code This version is mostly the same as the old version with a few improvments: Missing packages will print the full dependency tree Versioned dependency checking errors should be fixed Make depends should be calculated properly Experimental AUR provide searcher This code has been added along side the old code for testing and is not currently used by the install process. Once the install process is moved to use this code, the old code will be removed. --- dep.go | 104 ++++++++++++ depCheck.go | 264 +++++++++++++++++++++++++++++ depOrder.go | 138 +++++++++++++++ depPool.go | 477 ++++++++++++++++++++++++++++++++++++++++++++++++++++ parser.go | 10 ++ print.go | 59 +++++++ utils.go | 22 +++ 7 files changed, 1074 insertions(+) create mode 100644 dep.go create mode 100644 depCheck.go create mode 100644 depOrder.go create mode 100644 depPool.go diff --git a/dep.go b/dep.go new file mode 100644 index 00000000..2958ef71 --- /dev/null +++ b/dep.go @@ -0,0 +1,104 @@ +package main + +import ( + "fmt" + "strings" + + alpm "github.com/jguer/go-alpm" + rpc "github.com/mikkeloscar/aur" +) + +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 +} diff --git a/depCheck.go b/depCheck.go new file mode 100644 index 00000000..de6a4635 --- /dev/null +++ b/depCheck.go @@ -0,0 +1,264 @@ +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 { + 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 { + 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 _, err := dp.LocalDb.PkgCache().FindSatisfier(dep); err == nil { + missing.Good.set(dep) + return + } + + 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 { + 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 { + 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), " (Tree: ") + + if len(tree) == 0 { + fmt.Print(cyan("Target ")) + } else { + for _, pkg := range tree { + fmt.Print(cyan(pkg), " -> ") + } + } + + fmt.Println(")") + } + } + + return fmt.Errorf("") +} diff --git a/depOrder.go b/depOrder.go new file mode 100644 index 00000000..acb00ad6 --- /dev/null +++ b/depOrder.go @@ -0,0 +1,138 @@ +package main + +import ( + // "fmt" + "strconv" + // "strings" + // "sync" + + alpm "github.com/jguer/go-alpm" + rpc "github.com/mikkeloscar/aur" + //gopkg "github.com/mikkeloscar/gopkgbuild" +) + +type depOrder struct { + Aur []*rpc.Pkg + Repo []*alpm.Package + Missing []string + Runtime stringSet +} + +func (do *depOrder) String() string { + str := "" + str += "\n" + red("Repo") + " (" + strconv.Itoa(len(do.Repo)) + ") :" + for _, pkg := range do.Repo { + if do.Runtime.get(pkg.Name()) { + str += " " + pkg.Name() + } + } + + str += "\n" + red("Aur") + " (" + strconv.Itoa(len(do.Aur)) + ") :" + for _, pkg := range do.Aur { + if do.Runtime.get(pkg.Name) { + str += " " + pkg.Name + } + + } + + str += "\n" + red("Repo Make") + " (" + strconv.Itoa(len(do.Repo)) + ") :" + for _, pkg := range do.Repo { + if !do.Runtime.get(pkg.Name()) { + str += " " + pkg.Name() + } + } + + str += "\n" + red("Aur Make") + " (" + strconv.Itoa(len(do.Aur)) + ") :" + for _, pkg := range do.Aur { + if !do.Runtime.get(pkg.Name) { + str += " " + pkg.Name + } + + } + + return str +} + +func makeDepOrder() *depOrder { + return &depOrder{ + make([]*rpc.Pkg, 0), + make([]*alpm.Package, 0), + make([]string, 0), + make(stringSet), + } +} + +func getDepOrder(dp *depPool) *depOrder { + do := makeDepOrder() + + for _, target := range dp.Targets { + dep := target.DepString() + 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) + } + do.Aur = append(do.Aur, pkg) + delete(dp.Aur, pkg.Name) + + for _, 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) + } + + repoPkg := dp.findSatisfierRepo(dep) + if repoPkg != nil { + do.orderPkgRepo(repoPkg, dp, runtime) + } + + runtime = false + } + } +} + +func (do *depOrder) orderPkgRepo(pkg *alpm.Package, dp *depPool, runtime bool) { + if runtime { + do.Runtime.set(pkg.Name()) + } + do.Repo = append(do.Repo, pkg) + 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 + }) +} + +func (do *depOrder) getMakeOnlyRepo() stringSet { + makeOnly := make(stringSet) + + for _, pkg := range do.Repo { + if !do.Runtime.get(pkg.Name()) { + makeOnly.set(pkg.Name()) + } + } + + return makeOnly +} + +func (do *depOrder) checkMissing() { +} diff --git a/depPool.go b/depPool.go new file mode 100644 index 00000000..db6442f7 --- /dev/null +++ b/depPool.go @@ -0,0 +1,477 @@ +package main + +import ( + "fmt" + "strconv" + "strings" + "sync" + + alpm "github.com/jguer/go-alpm" + rpc "github.com/mikkeloscar/aur" + //gopkg "github.com/mikkeloscar/gopkgbuild" +) + +const PROVIDES = false + +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 + 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(map[string]*alpm.Package), + make(map[string]*rpc.Pkg), + make(map[string]*rpc.Pkg), + make([]string, 0), + localDb, + syncDb, + &aurWarnings{}, + } + + return dp, nil +} + +func (dp *depPool) String() string { + str := "" + str += "\n" + red("Targets") + " (" + strconv.Itoa(len(dp.Targets)) + ") :" + for _, pkg := range dp.Targets { + str += " " + pkg.String() + } + + str += "\n" + red("Repo") + " (" + strconv.Itoa(len(dp.Repo)) + ") :" + for pkg := range dp.Repo { + str += " " + pkg + } + + str += "\n" + red("Aur") + " (" + strconv.Itoa(len(dp.Aur)) + ") :" + for pkg := range dp.Aur { + str += " " + pkg + } + + str += "\n" + red("Aur Cache") + " (" + strconv.Itoa(len(dp.AurCache)) + ") :" + for pkg := range dp.AurCache { + str += " " + pkg + } + + str += "\n" + red("Groups") + " (" + strconv.Itoa(len(dp.Groups)) + ") :" + for _, pkg := range dp.Groups { + str += " " + pkg + } + + return str +} + +// Includes db/ prefixes and group installs +func (dp *depPool) ResolveTargets(pkgs []string) error { + for _, pkg := range pkgs { + target := toTarget(pkg) + dp.Targets = append(dp.Targets, target) + } + + // RPC requests are slow + // Combine as many AUR package requests as possible into a single RPC + // call + aurTargets := make(stringSet) + var err error + //repo := make([]*alpm.Package, 0) + + for _, target := range dp.Targets { + + // 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.hasSatisfier(target.DepString()) { + fmt.Println("Skipping target", target) + continue + } + + var foundPkg *alpm.Package + var singleDb *alpm.Db + + // aur/ prefix means we only check the aur + if target.Db == "aur" { + 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.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 + _, err := dp.SyncDb.PkgCachebyGroup(target.Name) + + if err == nil { + dp.Groups = append(dp.Groups, target.String()) + continue + } + } + + //if there was no db prefix check the aur + if target.Db == "" { + aurTargets.set(target.DepString()) + } + } + + if len(aurTargets) > 0 { + err = dp.resolveAURPackages(aurTargets) + } + + 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 { + 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 + } + + //TODO: config option, maybe --deepsearh but aurman uses that flag for + //something else already which might be confusing + //maybe --provides + if 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) 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 + } + + 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) + + 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 (dp *depPool) queryAUR(pkgs []string) error { + _, err := aurInfo(pkgs, dp.Warnings) + if err != nil { + return err + } + + return nil +} + +func getDepPool(pkgs []string) (*depPool, error) { + dp, err := makeDepPool() + if err != nil { + return nil, err + } + + 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 { + //try to match providers + providers := make([]*rpc.Pkg, 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) + } + } + } + + if len(providers) == 1 { + return providers[0] + } + + if len(providers) > 1 { + 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/parser.go b/parser.go index 7a6787a1..c384a79d 100644 --- a/parser.go +++ b/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) diff --git a/print.go b/print.go index 11ff9f32..5b7dd546 100644 --- a/print.go +++ b/print.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "bytes" "encoding/xml" "fmt" @@ -566,3 +567,61 @@ func colourHash(name string) (output string) { } return fmt.Sprintf("\x1b[%dm%s\x1b[0m", hash%6+31, name) } + +func providerMenu(dep string, providers []*rpc.Pkg) *rpc.Pkg { + size := len(providers) + + 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 { + 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[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[num-1] + } + + return nil +} diff --git a/utils.go b/utils.go index 2439981f..260b2769 100644 --- a/utils.go +++ b/utils.go @@ -112,3 +112,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 +} From b585cec123527052f7be768426db4e7066579cba Mon Sep 17 00:00:00 2001 From: morganamilo Date: Tue, 8 May 2018 05:23:00 +0100 Subject: [PATCH 02/20] Add Basses to depOrder This should act exaclty like depCatagories.Basses --- depOrder.go | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/depOrder.go b/depOrder.go index acb00ad6..0ce10a31 100644 --- a/depOrder.go +++ b/depOrder.go @@ -14,8 +14,8 @@ import ( type depOrder struct { Aur []*rpc.Pkg Repo []*alpm.Package - Missing []string Runtime stringSet + Bases map[string][]*rpc.Pkg } func (do *depOrder) String() string { @@ -57,8 +57,8 @@ func makeDepOrder() *depOrder { return &depOrder{ make([]*rpc.Pkg, 0), make([]*alpm.Package, 0), - make([]string, 0), make(stringSet), + make(map[string][]*rpc.Pkg, 0), } } @@ -78,6 +78,8 @@ func getDepOrder(dp *depPool) *depOrder { } } + do.getBases() + return do } @@ -122,17 +124,12 @@ func (do *depOrder) orderPkgRepo(pkg *alpm.Package, dp *depPool, runtime bool) { }) } -func (do *depOrder) getMakeOnlyRepo() stringSet { - makeOnly := make(stringSet) - - for _, pkg := range do.Repo { - if !do.Runtime.get(pkg.Name()) { - makeOnly.set(pkg.Name()) +func (do *depOrder) getBases() { + for _, pkg := range do.Aur { + if _, ok := do.Bases[pkg.PackageBase]; !ok { + do.Bases[pkg.PackageBase] = make([]*rpc.Pkg, 0) } + + do.Bases[pkg.PackageBase] = append(do.Bases[pkg.PackageBase], pkg) } - - return makeOnly -} - -func (do *depOrder) checkMissing() { } From 2e7a022b7c92917cdb87dee484bca272a5a3be9f Mon Sep 17 00:00:00 2001 From: morganamilo Date: Tue, 8 May 2018 05:41:04 +0100 Subject: [PATCH 03/20] Use new dependency code in install() The old depTree and depCatagories has been changed to depPool and depOrder in the install function. --- depOrder.go | 18 +++++++++++++ install.go | 73 +++++++++++++++++++++++------------------------------ print.go | 36 +++++++++++++------------- 3 files changed, 67 insertions(+), 60 deletions(-) diff --git a/depOrder.go b/depOrder.go index 0ce10a31..0a91756f 100644 --- a/depOrder.go +++ b/depOrder.go @@ -133,3 +133,21 @@ func (do *depOrder) getBases() { do.Bases[pkg.PackageBase] = append(do.Bases[pkg.PackageBase], pkg) } } + +func (do *depOrder) HasMake() bool { + return len(do.Runtime) != len(do.Aur)+len(do.Repo) +} + +func (do *depOrder) getMake() []string { + makeOnly := make([]string, 0, len(do.Aur)+len(do.Repo)-len(do.Runtime)) + + for _, pkg := range do.Aur { + makeOnly = append(makeOnly, pkg.Name) + } + + for _, pkg := range do.Repo { + makeOnly = append(makeOnly, pkg.Name()) + } + + return makeOnly +} diff --git a/install.go b/install.go index af09391f..bd7381f9 100644 --- a/install.go +++ b/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 @@ -61,7 +61,17 @@ func install(parser *arguments) error { //if len(aurTargets) > 0 || parser.existsArg("u", "sysupgrade") && len(remoteNames) > 0 { // fmt.Println(bold(cyan("::") + " Querying AUR...")) //} - dt, err := getDepTree(requestTargets, warnings) + dp, err := getDepPool(cmdArgs.targets.toSlice()) + if err != nil { + return err + } + + err = dp.CheckMissing() + if err != nil { + return err + } + + err = dp.CheckConflicts() if err != nil { return err } @@ -79,16 +89,6 @@ func install(parser *arguments) error { 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") @@ -131,61 +131,50 @@ func install(parser *arguments) error { } } - hasAur := false - for pkg := range parser.targets { - _, ok := dt.Aur[pkg] - if ok { - hasAur = true - } - } + 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 +194,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 } @@ -230,7 +219,7 @@ func install(parser *arguments) error { depArguments := makeArguments() depArguments.addArg("D", "asdeps") - for _, pkg := range dc.Repo { + for _, pkg := range do.Repo { if !parser.targets.get(pkg.Name()) && !localNamesCache.get(pkg.Name()) && !remoteNamesCache.get(pkg.Name()) { depArguments.addTarget(pkg.Name()) } @@ -250,17 +239,17 @@ 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(do.Aur, srcinfosStale, parser.targets, parser, do.Bases, incompatible) if err != nil { return err } - if len(dc.MakeOnly) > 0 { + if do.HasMake() { if !removeMake { return nil } @@ -268,7 +257,7 @@ func install(parser *arguments) error { removeArguments := makeArguments() removeArguments.addArg("R", "u") - for pkg := range dc.MakeOnly { + for _, pkg := range do.getMake() { removeArguments.addTarget(pkg) } @@ -283,7 +272,7 @@ func install(parser *arguments) error { } if config.CleanAfter { - clean(dc.Aur) + clean(do.Aur) } return nil diff --git a/print.go b/print.go index 5b7dd546..19a20db1 100644 --- a/print.go +++ b/print.go @@ -189,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 := "" @@ -200,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 { From c019a2cc0240220c28927f0608334b51b2de4cd0 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Tue, 8 May 2018 05:50:52 +0100 Subject: [PATCH 04/20] Remove old dependency code --- conflicts.go | 354 -------------------------- dep.go | 25 ++ dependencies.go | 650 ------------------------------------------------ 3 files changed, 25 insertions(+), 1004 deletions(-) delete mode 100644 conflicts.go delete mode 100644 dependencies.go diff --git a/conflicts.go b/conflicts.go deleted file mode 100644 index 9f03b945..00000000 --- a/conflicts.go +++ /dev/null @@ -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 -} diff --git a/dep.go b/dep.go index 2958ef71..e604c3f1 100644 --- a/dep.go +++ b/dep.go @@ -102,3 +102,28 @@ func satisfiesRepo(dep string, pkg *alpm.Package) bool { 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 +} + diff --git a/dependencies.go b/dependencies.go deleted file mode 100644 index b795673b..00000000 --- a/dependencies.go +++ /dev/null @@ -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 -} From 671f836ec5aecc454aa11b5fac5591d2ceb8ca25 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Tue, 8 May 2018 19:42:19 +0100 Subject: [PATCH 05/20] Calculate makeDeps properly MakeOnly would be set to true when moving from normal deps to make deps But would incorrectly stay set to true when moving to the deps of the following packages. depOrder.Aur now only holds one package from each base like depCatagories does. --- dep.go | 1 - depOrder.go | 36 +++++++++++++++++++++++++----------- install.go | 6 +----- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/dep.go b/dep.go index e604c3f1..c921c0da 100644 --- a/dep.go +++ b/dep.go @@ -126,4 +126,3 @@ func getBases(pkgs map[string]*rpc.Pkg) map[string][]*rpc.Pkg { return bases } - diff --git a/depOrder.go b/depOrder.go index 0a91756f..8c0cffda 100644 --- a/depOrder.go +++ b/depOrder.go @@ -78,7 +78,7 @@ func getDepOrder(dp *depPool) *depOrder { } } - do.getBases() + //do.getBases() return do } @@ -87,22 +87,25 @@ func (do *depOrder) orderPkgAur(pkg *rpc.Pkg, dp *depPool, runtime bool) { if runtime { do.Runtime.set(pkg.Name) } - do.Aur = append(do.Aur, pkg) + 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) + delete(dp.Aur, pkg.Name) - for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} { + 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) + do.orderPkgAur(aurPkg, dp, runtime && i == 0) } repoPkg := dp.findSatisfierRepo(dep) if repoPkg != nil { - do.orderPkgRepo(repoPkg, dp, runtime) + do.orderPkgRepo(repoPkg, dp, runtime && i == 0) } - - runtime = false } } } @@ -135,18 +138,29 @@ func (do *depOrder) getBases() { } func (do *depOrder) HasMake() bool { - return len(do.Runtime) != len(do.Aur)+len(do.Repo) + 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 _, pkg := range do.Aur { - makeOnly = append(makeOnly, pkg.Name) + 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 { - makeOnly = append(makeOnly, pkg.Name()) + if !do.Runtime.get(pkg.Name()) { + makeOnly = append(makeOnly, pkg.Name()) + } } return makeOnly diff --git a/install.go b/install.go index bd7381f9..6ada45af 100644 --- a/install.go +++ b/install.go @@ -249,11 +249,7 @@ func install(parser *arguments) error { return err } - if do.HasMake() { - if !removeMake { - return nil - } - + if removeMake { removeArguments := makeArguments() removeArguments.addArg("R", "u") From 8c77ad061e65cae1a8ef36b62e95922fc770657c Mon Sep 17 00:00:00 2001 From: morganamilo Date: Tue, 8 May 2018 19:53:03 +0100 Subject: [PATCH 06/20] Clean up unused code --- depOrder.go | 55 +---------------------------------------------------- depPool.go | 45 ++----------------------------------------- utils.go | 9 --------- 3 files changed, 3 insertions(+), 106 deletions(-) diff --git a/depOrder.go b/depOrder.go index 8c0cffda..6fccbafd 100644 --- a/depOrder.go +++ b/depOrder.go @@ -1,14 +1,8 @@ package main import ( - // "fmt" - "strconv" - // "strings" - // "sync" - alpm "github.com/jguer/go-alpm" rpc "github.com/mikkeloscar/aur" - //gopkg "github.com/mikkeloscar/gopkgbuild" ) type depOrder struct { @@ -18,47 +12,12 @@ type depOrder struct { Bases map[string][]*rpc.Pkg } -func (do *depOrder) String() string { - str := "" - str += "\n" + red("Repo") + " (" + strconv.Itoa(len(do.Repo)) + ") :" - for _, pkg := range do.Repo { - if do.Runtime.get(pkg.Name()) { - str += " " + pkg.Name() - } - } - - str += "\n" + red("Aur") + " (" + strconv.Itoa(len(do.Aur)) + ") :" - for _, pkg := range do.Aur { - if do.Runtime.get(pkg.Name) { - str += " " + pkg.Name - } - - } - - str += "\n" + red("Repo Make") + " (" + strconv.Itoa(len(do.Repo)) + ") :" - for _, pkg := range do.Repo { - if !do.Runtime.get(pkg.Name()) { - str += " " + pkg.Name() - } - } - - str += "\n" + red("Aur Make") + " (" + strconv.Itoa(len(do.Aur)) + ") :" - for _, pkg := range do.Aur { - if !do.Runtime.get(pkg.Name) { - str += " " + pkg.Name - } - - } - - return str -} - func makeDepOrder() *depOrder { return &depOrder{ make([]*rpc.Pkg, 0), make([]*alpm.Package, 0), make(stringSet), - make(map[string][]*rpc.Pkg, 0), + make(map[string][]*rpc.Pkg), } } @@ -78,8 +37,6 @@ func getDepOrder(dp *depPool) *depOrder { } } - //do.getBases() - return do } @@ -127,16 +84,6 @@ func (do *depOrder) orderPkgRepo(pkg *alpm.Package, dp *depPool, runtime bool) { }) } -func (do *depOrder) getBases() { - for _, pkg := range do.Aur { - if _, ok := do.Bases[pkg.PackageBase]; !ok { - do.Bases[pkg.PackageBase] = make([]*rpc.Pkg, 0) - } - - do.Bases[pkg.PackageBase] = append(do.Bases[pkg.PackageBase], pkg) - } -} - func (do *depOrder) HasMake() bool { lenAur := 0 for _, base := range do.Bases { diff --git a/depPool.go b/depPool.go index db6442f7..b520b05c 100644 --- a/depPool.go +++ b/depPool.go @@ -2,13 +2,11 @@ package main import ( "fmt" - "strconv" "strings" "sync" alpm "github.com/jguer/go-alpm" rpc "github.com/mikkeloscar/aur" - //gopkg "github.com/mikkeloscar/gopkgbuild" ) const PROVIDES = false @@ -79,36 +77,6 @@ func makeDepPool() (*depPool, error) { return dp, nil } -func (dp *depPool) String() string { - str := "" - str += "\n" + red("Targets") + " (" + strconv.Itoa(len(dp.Targets)) + ") :" - for _, pkg := range dp.Targets { - str += " " + pkg.String() - } - - str += "\n" + red("Repo") + " (" + strconv.Itoa(len(dp.Repo)) + ") :" - for pkg := range dp.Repo { - str += " " + pkg - } - - str += "\n" + red("Aur") + " (" + strconv.Itoa(len(dp.Aur)) + ") :" - for pkg := range dp.Aur { - str += " " + pkg - } - - str += "\n" + red("Aur Cache") + " (" + strconv.Itoa(len(dp.AurCache)) + ") :" - for pkg := range dp.AurCache { - str += " " + pkg - } - - str += "\n" + red("Groups") + " (" + strconv.Itoa(len(dp.Groups)) + ") :" - for _, pkg := range dp.Groups { - str += " " + pkg - } - - return str -} - // Includes db/ prefixes and group installs func (dp *depPool) ResolveTargets(pkgs []string) error { for _, pkg := range pkgs { @@ -185,7 +153,7 @@ func (dp *depPool) ResolveTargets(pkgs []string) error { err = dp.resolveAURPackages(aurTargets) } - return nil + return err } // Pseudo provides finder. @@ -375,16 +343,6 @@ func (dp *depPool) ResolveRepoDependency(pkg *alpm.Package) { return nil }) - -} - -func (dp *depPool) queryAUR(pkgs []string) error { - _, err := aurInfo(pkgs, dp.Warnings) - if err != nil { - return err - } - - return nil } func getDepPool(pkgs []string) (*depPool, error) { @@ -475,3 +433,4 @@ func (dp *depPool) hasPackage(name string) bool { return false } + diff --git a/utils.go b/utils.go index 260b2769..f1760677 100644 --- a/utils.go +++ b/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 { From 7b558ba47ca5cfec42934bf5ca9ae232431d885e Mon Sep 17 00:00:00 2001 From: morganamilo Date: Tue, 8 May 2018 22:54:28 +0100 Subject: [PATCH 07/20] Fix check for missing dependencies We dont check the dependencies of things already installed becuase, well, they're already installed. But we should check the dependencies of targets even if they are installed because they get reinstalled. --- depCheck.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/depCheck.go b/depCheck.go index de6a4635..47e762ff 100644 --- a/depCheck.go +++ b/depCheck.go @@ -183,11 +183,6 @@ type missing struct { } func (dp *depPool) _checkMissing(dep string, stack []string, missing *missing) { - if _, err := dp.LocalDb.PkgCache().FindSatisfier(dep); err == nil { - missing.Good.set(dep) - return - } - if missing.Good.get(dep) { return } @@ -207,6 +202,11 @@ func (dp *depPool) _checkMissing(dep string, stack []string, missing *missing) { 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) } } @@ -218,6 +218,11 @@ func (dp *depPool) _checkMissing(dep string, stack []string, missing *missing) { 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 }) From 6423fb55b55b27dbb090e22f46621cc88effdf36 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Thu, 10 May 2018 01:50:19 +0100 Subject: [PATCH 08/20] Fix build order being reversed --- depOrder.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/depOrder.go b/depOrder.go index 6fccbafd..bb3a5273 100644 --- a/depOrder.go +++ b/depOrder.go @@ -26,7 +26,12 @@ func getDepOrder(dp *depPool) *depOrder { for _, target := range dp.Targets { dep := target.DepString() - aurPkg := dp.findSatisfierAur(dep) + 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) } @@ -44,12 +49,6 @@ func (do *depOrder) orderPkgAur(pkg *rpc.Pkg, dp *depPool, runtime bool) { if runtime { do.Runtime.set(pkg.Name) } - 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) - delete(dp.Aur, pkg.Name) for i, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} { @@ -65,13 +64,18 @@ func (do *depOrder) orderPkgAur(pkg *rpc.Pkg, dp *depPool, runtime bool) { } } } + + 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()) } - do.Repo = append(do.Repo, pkg) delete(dp.Repo, pkg.Name()) pkg.Depends().ForEach(func(dep alpm.Depend) (err error) { @@ -82,6 +86,8 @@ func (do *depOrder) orderPkgRepo(pkg *alpm.Package, dp *depPool, runtime bool) { return nil }) + + do.Repo = append(do.Repo, pkg) } func (do *depOrder) HasMake() bool { From b76d0854187b99f1d0e1140adabdf5657017dcfd Mon Sep 17 00:00:00 2001 From: morganamilo Date: Thu, 10 May 2018 02:22:21 +0100 Subject: [PATCH 09/20] Improve provider menu Sort the provider menu alphabetically. Always ensure direct matches show up first. This ensures hitting enter for the default value will always be the same package that the user/dependency requested if an exact match exists. If a package is already installed pick that instead of providing a menu. Ensure duplicates do not show up in the menu. --- dep.go | 32 +++++++++++++++++++++++++++ depPool.go | 63 ++++++++++++++++++++++++++++++++++++++++++++---------- install.go | 2 +- print.go | 10 ++++----- 4 files changed, 90 insertions(+), 17 deletions(-) diff --git a/dep.go b/dep.go index c921c0da..72795247 100644 --- a/dep.go +++ b/dep.go @@ -8,6 +8,38 @@ import ( 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 := "" diff --git a/depPool.go b/depPool.go index b520b05c..2c19fdc9 100644 --- a/depPool.go +++ b/depPool.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "sort" "strings" "sync" @@ -9,7 +10,7 @@ import ( rpc "github.com/mikkeloscar/aur" ) -const PROVIDES = false +const PROVIDES = true type target struct { Db string @@ -98,7 +99,7 @@ func (dp *depPool) ResolveTargets(pkgs []string) error { // still get skiped even if it's from a different database to // the one specified // this is how pacman behaves - if dp.hasSatisfier(target.DepString()) { + if dp.hasPackage(target.DepString()) { fmt.Println("Skipping target", target) continue } @@ -371,27 +372,68 @@ func (dp *depPool) findSatisfierAur(dep string) *rpc.Pkg { // 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 { - //try to match providers - providers := make([]*rpc.Pkg, 0) - for _, pkg := range dp.AurCache { - if pkgSatisfies(pkg.Name, pkg.Version, dep) { + 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 = append(providers, pkg) + providers.Pkgs = append(providers.Pkgs, pkg) + seen.set(pkg.Name) + continue } } } - if len(providers) == 1 { - return providers[0] + if providers.Len() == 1 { + return providers.Pkgs[0] } - if len(providers) > 1 { + if providers.Len() > 1 { + sort.Sort(providers) return providerMenu(dep, providers) } @@ -433,4 +475,3 @@ func (dp *depPool) hasPackage(name string) bool { return false } - diff --git a/install.go b/install.go index 6ada45af..f527aa53 100644 --- a/install.go +++ b/install.go @@ -61,7 +61,7 @@ func install(parser *arguments) error { //if len(aurTargets) > 0 || parser.existsArg("u", "sysupgrade") && len(remoteNames) > 0 { // fmt.Println(bold(cyan("::") + " Querying AUR...")) //} - dp, err := getDepPool(cmdArgs.targets.toSlice()) + dp, err := getDepPool(requestTargets) if err != nil { return err } diff --git a/print.go b/print.go index 19a20db1..87676091 100644 --- a/print.go +++ b/print.go @@ -568,8 +568,8 @@ func colourHash(name string) (output string) { return fmt.Sprintf("\x1b[%dm%s\x1b[0m", hash%6+31, name) } -func providerMenu(dep string, providers []*rpc.Pkg) *rpc.Pkg { - size := len(providers) +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)) @@ -577,7 +577,7 @@ func providerMenu(dep string, providers []*rpc.Pkg) *rpc.Pkg { size = 1 str += bold(cyan("\n:: ")) + bold("Repository AUR\n ") - for _, pkg := range providers { + for _, pkg := range providers.Pkgs { str += fmt.Sprintf("%d) %s ", size, pkg.Name) size++ } @@ -606,7 +606,7 @@ func providerMenu(dep string, providers []*rpc.Pkg) *rpc.Pkg { } if string(numberBuf) == "" { - return providers[0] + return providers.Pkgs[0] } num, err := strconv.Atoi(string(numberBuf)) @@ -620,7 +620,7 @@ func providerMenu(dep string, providers []*rpc.Pkg) *rpc.Pkg { continue } - return providers[num-1] + return providers.Pkgs[num-1] } return nil From d51205194ee3f8a74e3f209616aa0bd3fa1fb45f Mon Sep 17 00:00:00 2001 From: morganamilo Date: Thu, 10 May 2018 14:36:38 +0100 Subject: [PATCH 10/20] Support skiping updates and showing warnings The warnings were moved down to after the upgrade menu, mainly because it is a lot easier to do this way, it may get moved back if it can be done in a non hacky way, --- depPool.go | 9 ++++----- install.go | 48 +++++++++++++++++------------------------------- 2 files changed, 21 insertions(+), 36 deletions(-) diff --git a/depPool.go b/depPool.go index 2c19fdc9..64bf815a 100644 --- a/depPool.go +++ b/depPool.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "sort" "strings" "sync" @@ -72,7 +71,7 @@ func makeDepPool() (*depPool, error) { make([]string, 0), localDb, syncDb, - &aurWarnings{}, + nil, } return dp, nil @@ -100,7 +99,6 @@ func (dp *depPool) ResolveTargets(pkgs []string) error { // the one specified // this is how pacman behaves if dp.hasPackage(target.DepString()) { - fmt.Println("Skipping target", target) continue } @@ -346,12 +344,13 @@ func (dp *depPool) ResolveRepoDependency(pkg *alpm.Package) { }) } -func getDepPool(pkgs []string) (*depPool, error) { +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 @@ -413,7 +412,7 @@ func (dp *depPool) findSatisfierAurCache(dep string) *rpc.Pkg { continue } - if pkgSatisfies(pkg.Name, pkg.Version, dep){ + if pkgSatisfies(pkg.Name, pkg.Version, dep) { providers.Pkgs = append(providers.Pkgs, pkg) seen.set(pkg.Name) continue diff --git a/install.go b/install.go index f527aa53..9fd505e4 100644 --- a/install.go +++ b/install.go @@ -58,37 +58,6 @@ func install(parser *arguments) error { } - //if len(aurTargets) > 0 || parser.existsArg("u", "sysupgrade") && len(remoteNames) > 0 { - // fmt.Println(bold(cyan("::") + " Querying AUR...")) - //} - dp, err := getDepPool(requestTargets) - if err != nil { - return err - } - - err = dp.CheckMissing() - if err != nil { - return err - } - - err = dp.CheckConflicts() - 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 - } - //create the arguments to pass for the repo install arguments := parser.copy() arguments.delArg("y", "refresh") @@ -131,6 +100,23 @@ func install(parser *arguments) error { } } + dp, err := getDepPool(requestTargets, warnings) + if err != nil { + return err + } + + warnings.print() + + 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() { From 6e990e4dc505c8fa9683d31dd98d6f325de45a48 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Sat, 19 May 2018 05:30:21 +0100 Subject: [PATCH 11/20] Don't conflict with pkgs that are in the depPool When upgrading multiple packages that are already installed we should not conflict with already installed packages that are in the depPool. This is because the version in the depTree is going to replace the currently installed version of the package and may have different conflicts. If there is a conflict between stuff in the depPool these should be handled by the innerConflicts check anyway. --- depCheck.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/depCheck.go b/depCheck.go index 47e762ff..9c14ed17 100644 --- a/depCheck.go +++ b/depCheck.go @@ -33,7 +33,7 @@ func (dp *depPool) checkInnerConflict(name string, conflict string, conflicts ma func (dp *depPool) checkForwardConflict(name string, conflict string, conflicts mapStringSet) { dp.LocalDb.PkgCache().ForEach(func(pkg alpm.Package) error { - if pkg.Name() == name { + if pkg.Name() == name || dp.hasPackage(pkg.Name()) { return nil } @@ -111,6 +111,10 @@ func (dp *depPool) checkForwardConflicts(conflicts mapStringSet) { 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 From 6df8c58e00a0969961e271beb216a50c5b9f2b7c Mon Sep 17 00:00:00 2001 From: morganamilo Date: Sat, 19 May 2018 06:07:58 +0100 Subject: [PATCH 12/20] Re add isDevelName() --- dep.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dep.go b/dep.go index 72795247..3af9cb59 100644 --- a/dep.go +++ b/dep.go @@ -158,3 +158,13 @@ func getBases(pkgs map[string]*rpc.Pkg) map[string][]*rpc.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-") +} From 33d056e9ac32c6aada49c6ea63408efb908df775 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Tue, 29 May 2018 16:47:43 +0100 Subject: [PATCH 13/20] Split Targets to Targets and Explicit Targets are used for tracking wether a package should be marked as explicitly installed or as a dependency. This is not ideal because you can have a target such as java-environment that resolves to a different package. Therefore Targets are now used only for the initial dependency resolving and checking for missing dependencies. The Explicit set is now used to mark what packages are explicit, seperate from the targets. --- depPool.go | 12 +++++++++--- install.go | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/depPool.go b/depPool.go index 64bf815a..ad899ee2 100644 --- a/depPool.go +++ b/depPool.go @@ -44,6 +44,7 @@ func (t target) String() string { type depPool struct { Targets []target + Explicit stringSet Repo map[string]*alpm.Package Aur map[string]*rpc.Pkg AurCache map[string]*rpc.Pkg @@ -65,6 +66,7 @@ func makeDepPool() (*depPool, error) { dp := &depPool{ make([]target, 0), + make(stringSet), make(map[string]*alpm.Package), make(map[string]*rpc.Pkg), make(map[string]*rpc.Pkg), @@ -124,6 +126,7 @@ func (dp *depPool) ResolveTargets(pkgs []string) error { } if err == nil { + dp.Explicit.set(foundPkg.Name()) dp.ResolveRepoDependency(foundPkg) continue } else { @@ -149,7 +152,7 @@ func (dp *depPool) ResolveTargets(pkgs []string) error { } if len(aurTargets) > 0 { - err = dp.resolveAURPackages(aurTargets) + err = dp.resolveAURPackages(aurTargets, true) } return err @@ -255,7 +258,7 @@ func (dp *depPool) cacheAURPackages(_pkgs stringSet) error { return nil } -func (dp *depPool) resolveAURPackages(pkgs stringSet) error { +func (dp *depPool) resolveAURPackages(pkgs stringSet, explicit bool) error { newPackages := make(stringSet) newAURPackages := make(stringSet) @@ -279,6 +282,9 @@ func (dp *depPool) resolveAURPackages(pkgs stringSet) error { continue } + if explicit { + dp.Explicit.set(pkg.Name) + } dp.Aur[pkg.Name] = pkg for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} { @@ -312,7 +318,7 @@ func (dp *depPool) resolveAURPackages(pkgs stringSet) error { } - err = dp.resolveAURPackages(newAURPackages) + err = dp.resolveAURPackages(newAURPackages, false) return err } diff --git a/install.go b/install.go index 9fd505e4..810dc601 100644 --- a/install.go +++ b/install.go @@ -206,7 +206,7 @@ func install(parser *arguments) error { depArguments.addArg("D", "asdeps") for _, pkg := range do.Repo { - if !parser.targets.get(pkg.Name()) && !localNamesCache.get(pkg.Name()) && !remoteNamesCache.get(pkg.Name()) { + if !dp.Explicit.get(pkg.Name()) && !localNamesCache.get(pkg.Name()) && !remoteNamesCache.get(pkg.Name()) { depArguments.addTarget(pkg.Name()) } } @@ -230,7 +230,7 @@ func install(parser *arguments) error { return err } - err = buildInstallPkgBuilds(do.Aur, srcinfosStale, parser.targets, parser, do.Bases, incompatible) + err = buildInstallPkgBuilds(do.Aur, srcinfosStale, dp.Explicit, parser, do.Bases, incompatible) if err != nil { return err } From c464af6b9bee6f62e7f3d69614957690cbc11cee Mon Sep 17 00:00:00 2001 From: morganamilo Date: Tue, 29 May 2018 17:39:38 +0100 Subject: [PATCH 14/20] Implement config option for provider searching --- cmd.go | 4 ++++ config.go | 2 ++ depPool.go | 7 +------ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cmd.go b/cmd.go index 26865c05..1441f3b5 100644 --- a/cmd.go +++ b/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 } diff --git a/config.go b/config.go index 00a9c8f4..105b397a 100644 --- a/config.go +++ b/config.go @@ -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. diff --git a/depPool.go b/depPool.go index ad899ee2..0e082e6b 100644 --- a/depPool.go +++ b/depPool.go @@ -9,8 +9,6 @@ import ( rpc "github.com/mikkeloscar/aur" ) -const PROVIDES = true - type target struct { Db string Name string @@ -228,10 +226,7 @@ func (dp *depPool) cacheAURPackages(_pkgs stringSet) error { return nil } - //TODO: config option, maybe --deepsearh but aurman uses that flag for - //something else already which might be confusing - //maybe --provides - if PROVIDES { + if config.Provides { err := dp.findProvides(pkgs) if err != nil { return err From b140e66f6ad7f981b6c592ccd87c03bbe3d8dfc3 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Tue, 29 May 2018 22:35:58 +0100 Subject: [PATCH 15/20] Improve --asdeps and --asexplicit handling Correctly handle --asdeps and --asexplicit for both repo packages and aur packages. --- dep.go | 2 +- install.go | 54 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/dep.go b/dep.go index 3af9cb59..3328331f 100644 --- a/dep.go +++ b/dep.go @@ -161,7 +161,7 @@ func getBases(pkgs map[string]*rpc.Pkg) map[string][]*rpc.Pkg { func isDevelName(name string) bool { for _, suffix := range []string{"git", "svn", "hg", "bzr", "nightly"} { - if strings.HasSuffix(name, "-" + suffix) { + if strings.HasSuffix(name, "-"+suffix) { return true } } diff --git a/install.go b/install.go index 810dc601..43aa2dcf 100644 --- a/install.go +++ b/install.go @@ -47,20 +47,13 @@ func install(parser *arguments) error { if err != nil { return err } - - for _, up := range aurUp { - requestTargets = append(requestTargets, "aur/"+up.Name) - } - - for _, up := range repoUp { - requestTargets = append(requestTargets, up.Name) - } - } //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) @@ -204,10 +197,19 @@ func install(parser *arguments) error { depArguments := makeArguments() depArguments.addArg("D", "asdeps") + 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()) { 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()) } } @@ -217,6 +219,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 { @@ -230,7 +239,7 @@ func install(parser *arguments) error { return err } - err = buildInstallPkgBuilds(do.Aur, srcinfosStale, dp.Explicit, parser, do.Bases, incompatible) + err = buildInstallPkgBuilds(dp, do, srcinfosStale, parser, incompatible) if err != nil { return err } @@ -565,13 +574,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 @@ -594,8 +603,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 @@ -645,6 +654,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() @@ -657,7 +668,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 @@ -675,10 +686,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 @@ -688,7 +706,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) } From e76f978d635262583a220351aea4bb24867459bc Mon Sep 17 00:00:00 2001 From: morganamilo Date: Tue, 29 May 2018 23:39:02 +0100 Subject: [PATCH 16/20] Only show warnings for pkgs that are being updated The previous warning system would show warnings recursivley for all packages being resolved. While I like this, other have complained at it being overly verbose. Either way the main purpose of this is to allow warnings to be printed before the upgrade menu shows. This is mostly just to get a usable warning system. This may change if a better solution is found. --- install.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.go b/install.go index 43aa2dcf..5886a54a 100644 --- a/install.go +++ b/install.go @@ -47,6 +47,8 @@ func install(parser *arguments) error { if err != nil { return err } + + warnings.print() } //create the arguments to pass for the repo install @@ -98,8 +100,6 @@ func install(parser *arguments) error { return err } - warnings.print() - err = dp.CheckMissing() if err != nil { return err From 7a1e2de6cd96b172095ccc3a6a59e8dee2c1fb44 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Wed, 30 May 2018 03:21:17 +0100 Subject: [PATCH 17/20] Simplfy upgrade skipping --- install.go | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/install.go b/install.go index 5886a54a..ccd64143 100644 --- a/install.go +++ b/install.go @@ -41,16 +41,6 @@ func install(parser *arguments) error { remoteNamesCache := sliceToStringSet(remoteNames) localNamesCache := sliceToStringSet(localNames) - //if we are doing -u also request all packages needing update - if parser.existsArg("u", "sysupgrade") { - aurUp, repoUp, err = upList(warnings) - if err != nil { - return err - } - - warnings.print() - } - //create the arguments to pass for the repo install arguments := parser.copy() arguments.delArg("y", "refresh") @@ -59,13 +49,19 @@ func install(parser *arguments) error { arguments.op = "S" arguments.targets = make(stringSet) + //if we are doing -u also request all packages needing update if parser.existsArg("u", "sysupgrade") { - ignore, aurUp, err := upgradePkgs(aurUp, repoUp) + aurUp, repoUp, err = upList(warnings) if err != nil { return err } - requestTargets = parser.targets.toSlice() + warnings.print() + + ignore, aurUp, err := upgradePkgs(aurUp, repoUp) + if err != nil { + return err + } for _, up := range repoUp { if !ignore.get(up.Name) { From 53227082a5df236a500efa6b143004e58218302d Mon Sep 17 00:00:00 2001 From: morganamilo Date: Wed, 30 May 2018 03:31:21 +0100 Subject: [PATCH 18/20] Skip provider searching for already installed pkgs We do not offer a provider menu for already installed packages. For example if you already have yay installed then yay -S yay will just reinstall yay. No prompt will be given for yay-git or yay-bin. Therefore it makes sense to disable provider searching for these packages. Expecially because it is the slowest part of dependency resolving and many already installed packages are going to be resolved when using -u. --- depPool.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/depPool.go b/depPool.go index 0e082e6b..aede9404 100644 --- a/depPool.go +++ b/depPool.go @@ -203,6 +203,9 @@ func (dp *depPool) findProvides(pkgs stringSet) error { } for pkg := range pkgs { + if _, err := dp.LocalDb.PkgByName(pkg); err == nil { + continue + } wg.Add(1) go doSearch(pkg) } From 943b2f931a15d1dbb8cdeb79e1bb435343b325f1 Mon Sep 17 00:00:00 2001 From: morganamilo Date: Wed, 30 May 2018 04:06:29 +0100 Subject: [PATCH 19/20] Tweak missing packages text --- depCheck.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/depCheck.go b/depCheck.go index 9c14ed17..08e9a632 100644 --- a/depCheck.go +++ b/depCheck.go @@ -255,14 +255,16 @@ func (dp *depPool) CheckMissing() error { for dep, trees := range missing.Missing { for _, tree := range trees { - fmt.Print(" "+cyan(dep), " (Tree: ") + fmt.Print(" ", cyan(dep)) if len(tree) == 0 { - fmt.Print(cyan("Target ")) + fmt.Print(" (Target") } else { - for _, pkg := range tree { - fmt.Print(cyan(pkg), " -> ") + 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(")") From 13754722fd92b82909a64c6ec0c2b7e155fe57ab Mon Sep 17 00:00:00 2001 From: morganamilo Date: Wed, 30 May 2018 04:41:59 +0100 Subject: [PATCH 20/20] Fix group install Using --asdeps with a group install does not work, this seems like a rare use case so ignoring for now. --- depPool.go | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/depPool.go b/depPool.go index aede9404..0ccc27de 100644 --- a/depPool.go +++ b/depPool.go @@ -79,19 +79,14 @@ func makeDepPool() (*depPool, error) { // Includes db/ prefixes and group installs func (dp *depPool) ResolveTargets(pkgs []string) error { - for _, pkg := range pkgs { - target := toTarget(pkg) - dp.Targets = append(dp.Targets, target) - } - // RPC requests are slow // Combine as many AUR package requests as possible into a single RPC // call aurTargets := make(stringSet) - var err error - //repo := make([]*alpm.Package, 0) - for _, target := range dp.Targets { + 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 @@ -107,6 +102,7 @@ func (dp *depPool) ResolveTargets(pkgs []string) error { // aur/ prefix means we only check the aur if target.Db == "aur" { + dp.Targets = append(dp.Targets, target) aurTargets.set(target.DepString()) continue } @@ -124,6 +120,7 @@ func (dp *depPool) ResolveTargets(pkgs []string) error { } if err == nil { + dp.Targets = append(dp.Targets, target) dp.Explicit.set(foundPkg.Name()) dp.ResolveRepoDependency(foundPkg) continue @@ -135,10 +132,13 @@ func (dp *depPool) ResolveTargets(pkgs []string) error { //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 - _, err := dp.SyncDb.PkgCachebyGroup(target.Name) - + 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 } } @@ -147,13 +147,15 @@ func (dp *depPool) ResolveTargets(pkgs []string) error { if target.Db == "" { aurTargets.set(target.DepString()) } + + dp.Targets = append(dp.Targets, target) } if len(aurTargets) > 0 { - err = dp.resolveAURPackages(aurTargets, true) + return dp.resolveAURPackages(aurTargets, true) } - return err + return nil } // Pseudo provides finder.