소스 검색

Merge pull request #767 from Morganamilo/merge-dep-pool-order

Merge dep pool order and rework inner conflicts
Anna 6 년 전
부모
커밋
c2aeb4bc4e
7개의 변경된 파일951개의 추가작업 그리고 906개의 파일을 삭제
  1. 73 0
      dep.go
  2. 214 181
      depCheck.go
  3. 0 135
      depOrder.go
  4. 0 477
      depPool.go
  5. 627 0
      depSolver.go
  6. 37 43
      install.go
  7. 0 70
      print.go

+ 73 - 0
dep.go

@@ -8,6 +8,11 @@ import (
 	rpc "github.com/mikkeloscar/aur"
 )
 
+type missing struct {
+	Good    stringSet
+	Missing map[string][][]string
+}
+
 type providers struct {
 	lookfor string
 	Pkgs    []*rpc.Pkg
@@ -40,6 +45,74 @@ func (q providers) Swap(i, j int) {
 	q.Pkgs[i], q.Pkgs[j] = q.Pkgs[j], q.Pkgs[i]
 }
 
+type Base []*rpc.Pkg
+
+func (b Base) Pkgbase() string {
+	return b[0].PackageBase
+}
+
+func (b Base) Version() string {
+	return b[0].Version
+}
+
+func (b Base) URLPath() string {
+	return b[0].URLPath
+}
+
+func baseAppend(bases []Base, pkg *rpc.Pkg) []Base {
+	for i, base := range bases {
+		if base.Pkgbase() == pkg.PackageBase {
+			bases[i] = append(bases[i], pkg)
+			return bases
+		}
+	}
+
+	return append(bases, Base{pkg})
+}
+
+func baseFind(bases []Base, name string) *rpc.Pkg {
+	for _, base := range bases {
+		for _, pkg := range base {
+			if pkg.Name == name {
+				return pkg
+			}
+		}
+	}
+
+	return nil
+}
+
+type target struct {
+	Db      string
+	Name    string
+	Mod     string
+	Version string
+}
+
+func toTarget(pkg string) target {
+	db, dep := splitDbFromName(pkg)
+	name, mod, version := splitDep(dep)
+
+	return target{
+		db,
+		name,
+		mod,
+		version,
+	}
+}
+
+func (t target) DepString() string {
+	return t.Name + t.Mod + t.Version
+}
+
+func (t target) String() string {
+	if t.Db != "" {
+		return t.Db + "/" + t.DepString()
+	}
+
+	return t.DepString()
+}
+
 func splitDep(dep string) (string, string, string) {
 	mod := ""
 

+ 214 - 181
depCheck.go

@@ -6,41 +6,105 @@ import (
 	"sync"
 
 	alpm "github.com/jguer/go-alpm"
+	rpc "github.com/mikkeloscar/aur"
 )
 
-func (dp *depPool) checkInnerConflict(name string, conflict string, conflicts mapStringSet) {
-	for _, pkg := range dp.Aur {
-		if pkg.Name == name {
-			continue
-		}
+func (ds *depSolver) _checkMissing(dep string, stack []string, missing *missing) {
+	if missing.Good.get(dep) {
+		return
+	}
 
-		if satisfiesAur(conflict, pkg) {
-			conflicts.Add(name, pkg.Name)
+	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
 	}
 
-	for _, pkg := range dp.Repo {
-		if pkg.Name() == name {
-			continue
+	aurPkg := ds.findSatisfierAur(dep)
+	if aurPkg != nil {
+		missing.Good.set(dep)
+		for _, deps := range [3][]string{aurPkg.Depends, aurPkg.MakeDepends, aurPkg.CheckDepends} {
+			for _, aurDep := range deps {
+				if _, err := ds.LocalDb.PkgCache().FindSatisfier(aurDep); err == nil {
+					missing.Good.set(aurDep)
+					continue
+				}
+
+				ds._checkMissing(aurDep, append(stack, aurPkg.Name), missing)
+			}
 		}
 
-		if satisfiesRepo(conflict, pkg) {
-			conflicts.Add(name, pkg.Name())
+		return
+	}
+
+	repoPkg := ds.findSatisfierRepo(dep)
+	if repoPkg != nil {
+		missing.Good.set(dep)
+		repoPkg.Depends().ForEach(func(repoDep alpm.Depend) error {
+			if _, err := ds.LocalDb.PkgCache().FindSatisfier(repoDep.String()); err == nil {
+				missing.Good.set(repoDep.String())
+				return nil
+			}
+
+			ds._checkMissing(repoDep.String(), append(stack, repoPkg.Name()), missing)
+			return nil
+		})
+
+		return
+	}
+
+	missing.Missing[dep] = [][]string{stack}
+}
+
+func (ds *depSolver) CheckMissing() error {
+	missing := &missing{
+		make(stringSet),
+		make(map[string][][]string),
+	}
+
+	for _, target := range ds.Targets {
+		ds._checkMissing(target.DepString(), make([]string, 0), missing)
+	}
+
+	if len(missing.Missing) == 0 {
+		return nil
+	}
+
+	fmt.Println(bold(red(arrow+" Error: ")) + "Could not find all required packages:")
+	for dep, trees := range missing.Missing {
+		for _, tree := range trees {
+
+			fmt.Print("    ", cyan(dep))
+
+			if len(tree) == 0 {
+				fmt.Print(" (Target")
+			} else {
+				fmt.Print(" (Wanted by: ")
+				for n := 0; n < len(tree)-1; n++ {
+					fmt.Print(cyan(tree[n]), " -> ")
+				}
+				fmt.Print(cyan(tree[len(tree)-1]))
+			}
+
+			fmt.Println(")")
 		}
 	}
+
+	return fmt.Errorf("")
 }
 
-func (dp *depPool) checkForwardConflict(name string, conflict string, conflicts mapStringSet) {
-	dp.LocalDb.PkgCache().ForEach(func(pkg alpm.Package) error {
-		if pkg.Name() == name || dp.hasPackage(pkg.Name()) {
+func (ds *depSolver) checkForwardConflict(name string, conflict string, conflicts mapStringSet) {
+	ds.LocalDb.PkgCache().ForEach(func(pkg alpm.Package) error {
+		if pkg.Name() == name || ds.hasPackage(pkg.Name()) {
 			return nil
 		}
 
 		if satisfiesRepo(conflict, &pkg) {
 			n := pkg.Name()
-			if n != conflict {
-				n += " (" + conflict + ")"
-			}
 			conflicts.Add(name, n)
 		}
 
@@ -48,141 +112,203 @@ func (dp *depPool) checkForwardConflict(name string, conflict string, conflicts
 	})
 }
 
-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 + ")"
+func (ds *depSolver) checkReverseConflict(name string, conflict string, conflicts mapStringSet) {
+	for _, base := range ds.Aur {
+		for _, pkg := range base {
+			if pkg.Name == name {
+				continue
 			}
 
-			conflicts.Add(pkg.Name, name)
+			if satisfiesAur(conflict, pkg) {
+				conflicts.Add(pkg.Name, name)
+			}
 		}
 	}
 
-	for _, pkg := range dp.Repo {
+	for _, pkg := range ds.Repo {
 		if pkg.Name() == name {
 			continue
 		}
 
 		if satisfiesRepo(conflict, pkg) {
-			if name != conflict {
-				name += " (" + conflict + ")"
-			}
-
 			conflicts.Add(pkg.Name(), name)
 		}
 	}
 }
 
-func (dp *depPool) checkInnerConflicts(conflicts mapStringSet) {
-	for _, pkg := range dp.Aur {
-		for _, conflict := range pkg.Conflicts {
-			dp.checkInnerConflict(pkg.Name, conflict, conflicts)
+func (ds *depSolver) checkForwardConflicts(conflicts mapStringSet) {
+	for _, base := range ds.Aur {
+		for _, pkg := range base {
+			for _, conflict := range pkg.Conflicts {
+				ds.checkForwardConflict(pkg.Name, conflict, conflicts)
+			}
 		}
 	}
 
-	for _, pkg := range dp.Repo {
+	for _, pkg := range ds.Repo {
 		pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
-			dp.checkInnerConflict(pkg.Name(), conflict.String(), conflicts)
+			ds.checkForwardConflict(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)
+func (ds *depSolver) checkReverseConflicts(conflicts mapStringSet) {
+	ds.LocalDb.PkgCache().ForEach(func(pkg alpm.Package) error {
+		if ds.hasPackage(pkg.Name()) {
+			return nil
 		}
-	}
 
-	for _, pkg := range dp.Repo {
 		pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
-			dp.checkForwardConflict(pkg.Name(), conflict.String(), conflicts)
+			ds.checkReverseConflict(pkg.Name(), conflict.String(), conflicts)
 			return nil
 		})
-	}
+
+		return nil
+	})
 }
 
-func (dp *depPool) checkReverseConflicts(conflicts mapStringSet) {
-	dp.LocalDb.PkgCache().ForEach(func(pkg alpm.Package) error {
-		if dp.hasPackage(pkg.Name()) {
+func (ds *depSolver) checkInnerRepoConflicts(conflicts mapStringSet) {
+	for _, pkg := range ds.Repo {
+		pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
+			for _, innerpkg := range ds.Repo {
+				if pkg.Name() != innerpkg.Name() && satisfiesRepo(conflict.String(), innerpkg) {
+					conflicts.Add(pkg.Name(), innerpkg.Name())
+				}
+			}
+
 			return nil
+		})
+	}
+}
+
+func (ds *depSolver) checkInnerConflicts(conflicts mapStringSet) {
+	removed := make(stringSet)
+	//ds.checkInnerConflictRepoAur(conflicts)
+
+	for current, currbase := range ds.Aur {
+		for _, pkg := range currbase {
+			ds.checkInnerConflict(pkg, ds.Aur[:current], removed, conflicts)
 		}
+	}
+}
 
+// Check if anything conflicts with currpkg
+// If so add the conflict with currpkg being removed by the conflicting pkg
+func (ds *depSolver) checkInnerConflict(currpkg *rpc.Pkg, aur []Base, removed stringSet, conflicts mapStringSet) {
+	for _, base := range aur {
+		for _, pkg := range base {
+			for _, conflict := range pkg.Conflicts {
+				if !removed.get(pkg.Name) && satisfiesAur(conflict, currpkg) {
+					addInnerConflict(pkg.Name, currpkg.Name, removed, conflicts)
+				}
+			}
+		}
+	}
+	for _, pkg := range ds.Repo {
 		pkg.Conflicts().ForEach(func(conflict alpm.Depend) error {
-			dp.checkReverseConflict(pkg.Name(), conflict.String(), conflicts)
+			if !removed.get(pkg.Name()) && satisfiesAur(conflict.String(), currpkg) {
+				addInnerConflict(pkg.Name(), currpkg.Name, removed, conflicts)
+			}
 			return nil
 		})
+	}
 
-		return nil
-	})
+	for _, conflict := range currpkg.Conflicts {
+		for _, base := range aur {
+			for _, pkg := range base {
+				if !removed.get(pkg.Name) && satisfiesAur(conflict, pkg) {
+					addInnerConflict(pkg.Name, currpkg.Name, removed, conflicts)
+				}
+			}
+		}
+		for _, pkg := range ds.Repo {
+			if !removed.get(pkg.Name()) && satisfiesRepo(conflict, pkg) {
+				addInnerConflict(pkg.Name(), currpkg.Name, removed, conflicts)
+			}
+		}
+	}
+}
+
+func addInnerConflict(toRemove string, removedBy string, removed stringSet, conflicts mapStringSet) {
+	conflicts.Add(removedBy, toRemove)
+	removed.set(toRemove)
 }
 
-func (dp *depPool) CheckConflicts() (mapStringSet, error) {
+func (ds *depSolver) CheckConflicts() (mapStringSet, error) {
 	var wg sync.WaitGroup
 	innerConflicts := make(mapStringSet)
 	conflicts := make(mapStringSet)
-	wg.Add(2)
+	repoConflicts := make(mapStringSet)
+	wg.Add(3)
 
 	fmt.Println(bold(cyan("::") + bold(" Checking for conflicts...")))
 	go func() {
-		dp.checkForwardConflicts(conflicts)
-		dp.checkReverseConflicts(conflicts)
+		ds.checkForwardConflicts(conflicts)
+		ds.checkReverseConflicts(conflicts)
 		wg.Done()
 	}()
 
 	fmt.Println(bold(cyan("::") + bold(" Checking for inner conflicts...")))
 	go func() {
-		dp.checkInnerConflicts(innerConflicts)
+		ds.checkInnerConflicts(innerConflicts)
+		wg.Done()
+	}()
+	go func() {
+		ds.checkInnerRepoConflicts(repoConflicts)
 		wg.Done()
 	}()
 
 	wg.Wait()
 
-	if len(innerConflicts) != 0 {
-		fmt.Println()
-		fmt.Println(bold(red(arrow)), bold("Inner conflicts found:"))
-
-		for name, pkgs := range innerConflicts {
-			str := red(bold(smallArrow)) + " " + name + ":"
-			for pkg := range pkgs {
-				str += " " + cyan(pkg) + ","
+	formatConflicts := func(conflicts mapStringSet, inner bool, s string) {
+		if len(conflicts) != 0 {
+			fmt.Println()
+			if inner {
+				fmt.Println(bold(red(arrow)), bold("Inner conflicts found:"))
+			} else {
+				fmt.Println(bold(red(arrow)), bold("Package conflicts found:"))
 			}
-			str = strings.TrimSuffix(str, ",")
 
-			fmt.Println(str)
-		}
-
-	}
-
-	if len(conflicts) != 0 {
-		fmt.Println()
-		fmt.Println(bold(red(arrow)), bold("Package conflicts found:"))
+			printConflict := func(name string, pkgs stringSet) {
+				str := fmt.Sprintf(s, cyan(name))
+				for pkg := range pkgs {
+					str += " " + cyan(pkg) + ","
+				}
+				str = strings.TrimSuffix(str, ",")
 
-		for name, pkgs := range conflicts {
-			str := red(bold(smallArrow)) + " Installing " + cyan(name) + " will remove:"
-			for pkg := range pkgs {
-				str += " " + cyan(pkg) + ","
+				fmt.Println(str)
 			}
-			str = strings.TrimSuffix(str, ",")
 
-			fmt.Println(str)
+			for _, pkg := range ds.Repo {
+				if pkgs, ok := conflicts[pkg.Name()]; ok {
+					printConflict(pkg.Name(), pkgs)
+				}
+			}
+			for _, base := range ds.Aur {
+				for _, pkg := range base {
+					if pkgs, ok := conflicts[pkg.Name]; ok {
+						printConflict(pkg.Name, pkgs)
+					}
+				}
+			}
 		}
+	}
 
+	repoStr := red(bold(smallArrow)) + " %s Conflicts with:"
+	formatConflicts(repoConflicts, true, repoStr)
+
+	if len(repoConflicts) > 0 {
+		return nil, fmt.Errorf("Unavoidable conflicts, aborting")
 	}
 
-	// Add the inner conflicts to the conflicts
-	// These are used to decide what to pass --ask to (if set) or don't pass --noconfirm to
-	// As we have no idea what the order is yet we add every inner conflict to the slice
-	for name, pkgs := range innerConflicts {
-		conflicts[name] = make(stringSet)
-		for pkg := range pkgs {
-			conflicts[pkg] = make(stringSet)
+	str := red(bold(smallArrow)) + " Installing %s will remove:"
+	formatConflicts(conflicts, false, str)
+	formatConflicts(innerConflicts, true, str)
+
+	for name, c := range innerConflicts {
+		for cs, _ := range c {
+			conflicts.Add(name, cs)
 		}
 	}
 
@@ -200,96 +326,3 @@ func (dp *depPool) CheckConflicts() (mapStringSet, error) {
 
 	return conflicts, nil
 }
-
-type missing struct {
-	Good    stringSet
-	Missing map[string][][]string
-}
-
-func (dp *depPool) _checkMissing(dep string, stack []string, missing *missing) {
-	if missing.Good.get(dep) {
-		return
-	}
-
-	if trees, ok := missing.Missing[dep]; ok {
-		for _, tree := range trees {
-			if stringSliceEqual(tree, stack) {
-				return
-			}
-		}
-		missing.Missing[dep] = append(missing.Missing[dep], stack)
-		return
-	}
-
-	aurPkg := dp.findSatisfierAur(dep)
-	if aurPkg != nil {
-		missing.Good.set(dep)
-		for _, deps := range [3][]string{aurPkg.Depends, aurPkg.MakeDepends, aurPkg.CheckDepends} {
-			for _, aurDep := range deps {
-				if _, err := dp.LocalDb.PkgCache().FindSatisfier(aurDep); err == nil {
-					missing.Good.set(aurDep)
-					continue
-				}
-
-				dp._checkMissing(aurDep, append(stack, aurPkg.Name), missing)
-			}
-		}
-
-		return
-	}
-
-	repoPkg := dp.findSatisfierRepo(dep)
-	if repoPkg != nil {
-		missing.Good.set(dep)
-		repoPkg.Depends().ForEach(func(repoDep alpm.Depend) error {
-			if _, err := dp.LocalDb.PkgCache().FindSatisfier(repoDep.String()); err == nil {
-				missing.Good.set(repoDep.String())
-				return nil
-			}
-
-			dp._checkMissing(repoDep.String(), append(stack, repoPkg.Name()), missing)
-			return nil
-		})
-
-		return
-	}
-
-	missing.Missing[dep] = [][]string{stack}
-}
-
-func (dp *depPool) CheckMissing() error {
-	missing := &missing{
-		make(stringSet),
-		make(map[string][][]string),
-	}
-
-	for _, target := range dp.Targets {
-		dp._checkMissing(target.DepString(), make([]string, 0), missing)
-	}
-
-	if len(missing.Missing) == 0 {
-		return nil
-	}
-
-	fmt.Println(bold(red(arrow+" Error: ")) + "Could not find all required packages:")
-	for dep, trees := range missing.Missing {
-		for _, tree := range trees {
-
-			fmt.Print("    ", cyan(dep))
-
-			if len(tree) == 0 {
-				fmt.Print(" (Target")
-			} else {
-				fmt.Print(" (Wanted by: ")
-				for n := 0; n < len(tree)-1; n++ {
-					fmt.Print(cyan(tree[n]), " -> ")
-				}
-				fmt.Print(cyan(tree[len(tree)-1]))
-			}
-
-			fmt.Println(")")
-		}
-	}
-
-	return fmt.Errorf("")
-}

+ 0 - 135
depOrder.go

@@ -1,135 +0,0 @@
-package main
-
-import (
-	alpm "github.com/jguer/go-alpm"
-	rpc "github.com/mikkeloscar/aur"
-)
-
-type Base []*rpc.Pkg
-
-func (b Base) Pkgbase() string {
-	return b[0].PackageBase
-}
-
-func (b Base) Version() string {
-	return b[0].Version
-}
-
-func (b Base) URLPath() string {
-	return b[0].URLPath
-}
-
-type depOrder struct {
-	Aur     []Base
-	Repo    []*alpm.Package
-	Runtime stringSet
-}
-
-func makeDepOrder() *depOrder {
-	return &depOrder{
-		make([]Base, 0),
-		make([]*alpm.Package, 0),
-		make(stringSet),
-	}
-}
-
-func getDepOrder(dp *depPool) *depOrder {
-	do := makeDepOrder()
-
-	for _, target := range dp.Targets {
-		dep := target.DepString()
-		aurPkg := dp.Aur[dep]
-		if aurPkg != nil && pkgSatisfies(aurPkg.Name, aurPkg.Version, dep) {
-			do.orderPkgAur(aurPkg, dp, true)
-		}
-
-		aurPkg = dp.findSatisfierAur(dep)
-		if aurPkg != nil {
-			do.orderPkgAur(aurPkg, dp, true)
-		}
-
-		repoPkg := dp.findSatisfierRepo(dep)
-		if repoPkg != nil {
-			do.orderPkgRepo(repoPkg, dp, true)
-		}
-	}
-
-	return do
-}
-
-func (do *depOrder) orderPkgAur(pkg *rpc.Pkg, dp *depPool, runtime bool) {
-	if runtime {
-		do.Runtime.set(pkg.Name)
-	}
-	delete(dp.Aur, pkg.Name)
-
-	for i, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
-		for _, dep := range deps {
-			aurPkg := dp.findSatisfierAur(dep)
-			if aurPkg != nil {
-				do.orderPkgAur(aurPkg, dp, runtime && i == 0)
-			}
-
-			repoPkg := dp.findSatisfierRepo(dep)
-			if repoPkg != nil {
-				do.orderPkgRepo(repoPkg, dp, runtime && i == 0)
-			}
-		}
-	}
-
-	for i, base := range do.Aur {
-		if base.Pkgbase() == pkg.PackageBase {
-			do.Aur[i] = append(base, pkg)
-			return
-		}
-	}
-
-	do.Aur = append(do.Aur, Base{pkg})
-}
-
-func (do *depOrder) orderPkgRepo(pkg *alpm.Package, dp *depPool, runtime bool) {
-	if runtime {
-		do.Runtime.set(pkg.Name())
-	}
-	delete(dp.Repo, pkg.Name())
-
-	pkg.Depends().ForEach(func(dep alpm.Depend) (err error) {
-		repoPkg := dp.findSatisfierRepo(dep.String())
-		if repoPkg != nil {
-			do.orderPkgRepo(repoPkg, dp, runtime)
-		}
-
-		return nil
-	})
-
-	do.Repo = append(do.Repo, pkg)
-}
-
-func (do *depOrder) HasMake() bool {
-	lenAur := 0
-	for _, base := range do.Aur {
-		lenAur += len(base)
-	}
-
-	return len(do.Runtime) != lenAur+len(do.Repo)
-}
-
-func (do *depOrder) getMake() []string {
-	makeOnly := make([]string, 0, len(do.Aur)+len(do.Repo)-len(do.Runtime))
-
-	for _, base := range do.Aur {
-		for _, pkg := range base {
-			if !do.Runtime.get(pkg.Name) {
-				makeOnly = append(makeOnly, pkg.Name)
-			}
-		}
-	}
-
-	for _, pkg := range do.Repo {
-		if !do.Runtime.get(pkg.Name()) {
-			makeOnly = append(makeOnly, pkg.Name())
-		}
-	}
-
-	return makeOnly
-}

+ 0 - 477
depPool.go

@@ -1,477 +0,0 @@
-package main
-
-import (
-	"sort"
-	"strings"
-	"sync"
-
-	alpm "github.com/jguer/go-alpm"
-	rpc "github.com/mikkeloscar/aur"
-)
-
-type target struct {
-	Db      string
-	Name    string
-	Mod     string
-	Version string
-}
-
-func toTarget(pkg string) target {
-	db, dep := splitDbFromName(pkg)
-	name, mod, version := splitDep(dep)
-
-	return target{
-		db,
-		name,
-		mod,
-		version,
-	}
-}
-
-func (t target) DepString() string {
-	return t.Name + t.Mod + t.Version
-}
-
-func (t target) String() string {
-	if t.Db != "" {
-		return t.Db + "/" + t.DepString()
-	}
-
-	return t.DepString()
-}
-
-type depPool struct {
-	Targets  []target
-	Explicit stringSet
-	Repo     map[string]*alpm.Package
-	Aur      map[string]*rpc.Pkg
-	AurCache map[string]*rpc.Pkg
-	Groups   []string
-	LocalDb  *alpm.Db
-	SyncDb   alpm.DbList
-	Warnings *aurWarnings
-}
-
-func makeDepPool() (*depPool, error) {
-	localDb, err := alpmHandle.LocalDb()
-	if err != nil {
-		return nil, err
-	}
-	syncDb, err := alpmHandle.SyncDbs()
-	if err != nil {
-		return nil, err
-	}
-
-	dp := &depPool{
-		make([]target, 0),
-		make(stringSet),
-		make(map[string]*alpm.Package),
-		make(map[string]*rpc.Pkg),
-		make(map[string]*rpc.Pkg),
-		make([]string, 0),
-		localDb,
-		syncDb,
-		nil,
-	}
-
-	return dp, nil
-}
-
-// Includes db/ prefixes and group installs
-func (dp *depPool) ResolveTargets(pkgs []string) error {
-	// RPC requests are slow
-	// Combine as many AUR package requests as possible into a single RPC
-	// call
-	aurTargets := make(stringSet)
-
-	pkgs = removeInvalidTargets(pkgs)
-
-	for _, pkg := range pkgs {
-		var err error
-		target := toTarget(pkg)
-
-		// skip targets already satisfied
-		// even if the user enters db/pkg and aur/pkg the latter will
-		// still get skipped even if it's from a different database to
-		// the one specified
-		// this is how pacman behaves
-		if dp.hasPackage(target.DepString()) {
-			continue
-		}
-
-		var foundPkg *alpm.Package
-		var singleDb *alpm.Db
-
-		// aur/ prefix means we only check the aur
-		if target.Db == "aur" || mode == ModeAUR {
-			dp.Targets = append(dp.Targets, target)
-			aurTargets.set(target.DepString())
-			continue
-		}
-
-		// If there'ss a different priefix only look in that repo
-		if target.Db != "" {
-			singleDb, err = alpmHandle.SyncDbByName(target.Db)
-			if err != nil {
-				return err
-			}
-			foundPkg, err = singleDb.PkgCache().FindSatisfier(target.DepString())
-			//otherwise find it in any repo
-		} else {
-			foundPkg, err = dp.SyncDb.FindSatisfier(target.DepString())
-		}
-
-		if err == nil {
-			dp.Targets = append(dp.Targets, target)
-			dp.Explicit.set(foundPkg.Name())
-			dp.ResolveRepoDependency(foundPkg)
-			continue
-		} else {
-			//check for groups
-			//currently we don't resolve the packages in a group
-			//only check if the group exists
-			//would be better to check the groups from singleDb if
-			//the user specified a db but there's no easy way to do
-			//it without making alpm_lists so don't bother for now
-			//db/group is probably a rare use case
-			group, err := dp.SyncDb.PkgCachebyGroup(target.Name)
-			if err == nil {
-				dp.Groups = append(dp.Groups, target.String())
-				group.ForEach(func(pkg alpm.Package) error {
-					dp.Explicit.set(pkg.Name())
-					return nil
-				})
-				continue
-			}
-		}
-
-		//if there was no db prefix check the aur
-		if target.Db == "" {
-			aurTargets.set(target.DepString())
-		}
-
-		dp.Targets = append(dp.Targets, target)
-	}
-
-	if len(aurTargets) > 0 && (mode == ModeAny || mode == ModeAUR) {
-		return dp.resolveAURPackages(aurTargets, true)
-	}
-
-	return nil
-}
-
-// Pseudo provides finder.
-// Try to find provides by performing a search of the package name
-// This effectively performs -Ss on each package
-// then runs -Si on each result to cache the information.
-//
-// For example if you were to -S yay then yay -Ss would give:
-// yay-git yay-bin yay realyog pacui pacui-git ruby-yard
-// These packages will all be added to the cache in case they are needed later
-// Ofcouse only the first three packages provide yay, the rest are just false
-// positives.
-//
-// This method increases dependency resolve time
-func (dp *depPool) findProvides(pkgs stringSet) error {
-	var mux sync.Mutex
-	var wg sync.WaitGroup
-
-	doSearch := func(pkg string) {
-		defer wg.Done()
-		var err error
-		var results []rpc.Pkg
-
-		// Hack for a bigger search result, if the user wants
-		// java-envronment we can search for just java instead and get
-		// more hits.
-		words := strings.Split(pkg, "-")
-
-		for i := range words {
-			results, err = rpc.SearchByNameDesc(strings.Join(words[:i+1], "-"))
-			if err == nil {
-				break
-			}
-		}
-
-		if err != nil {
-			return
-		}
-
-		for _, result := range results {
-			mux.Lock()
-			if _, ok := dp.AurCache[result.Name]; !ok {
-				pkgs.set(result.Name)
-			}
-			mux.Unlock()
-		}
-	}
-
-	for pkg := range pkgs {
-		if _, err := dp.LocalDb.PkgByName(pkg); err == nil {
-			continue
-		}
-		wg.Add(1)
-		go doSearch(pkg)
-	}
-
-	wg.Wait()
-
-	return nil
-}
-
-func (dp *depPool) cacheAURPackages(_pkgs stringSet) error {
-	pkgs := _pkgs.copy()
-	query := make([]string, 0)
-
-	for pkg := range pkgs {
-		if _, ok := dp.AurCache[pkg]; ok {
-			pkgs.remove(pkg)
-		}
-	}
-
-	if len(pkgs) == 0 {
-		return nil
-	}
-
-	if config.Provides {
-		err := dp.findProvides(pkgs)
-		if err != nil {
-			return err
-		}
-	}
-
-	for pkg := range pkgs {
-		if _, ok := dp.AurCache[pkg]; !ok {
-			name, _, _ := splitDep(pkg)
-			query = append(query, name)
-		}
-	}
-
-	info, err := aurInfo(query, dp.Warnings)
-	if err != nil {
-		return err
-	}
-
-	for _, pkg := range info {
-		// Dump everything in cache just in case we need it later
-		dp.AurCache[pkg.Name] = pkg
-	}
-
-	return nil
-}
-
-func (dp *depPool) resolveAURPackages(pkgs stringSet, explicit bool) error {
-	newPackages := make(stringSet)
-	newAURPackages := make(stringSet)
-
-	err := dp.cacheAURPackages(pkgs)
-	if err != nil {
-		return err
-	}
-
-	if len(pkgs) == 0 {
-		return nil
-	}
-
-	for name := range pkgs {
-		_, ok := dp.Aur[name]
-		if ok {
-			continue
-		}
-
-		pkg := dp.findSatisfierAurCache(name)
-		if pkg == nil {
-			continue
-		}
-
-		if explicit {
-			dp.Explicit.set(pkg.Name)
-		}
-		dp.Aur[pkg.Name] = pkg
-
-		for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
-			for _, dep := range deps {
-				newPackages.set(dep)
-			}
-		}
-	}
-
-	for dep := range newPackages {
-		if dp.hasSatisfier(dep) {
-			continue
-		}
-
-		_, isInstalled := dp.LocalDb.PkgCache().FindSatisfier(dep) //has satisfier installed: skip
-		hm := hideMenus
-		hideMenus = isInstalled == nil
-		repoPkg, inRepos := dp.SyncDb.FindSatisfier(dep) //has satisfier in repo: fetch it
-		hideMenus = hm
-		if isInstalled == nil && (config.ReBuild != "tree" || inRepos == nil) {
-			continue
-		}
-
-		if inRepos == nil {
-			dp.ResolveRepoDependency(repoPkg)
-			continue
-		}
-
-		//assume it's in the aur
-		//ditch the versioning because the RPC can't handle it
-		newAURPackages.set(dep)
-
-	}
-
-	err = dp.resolveAURPackages(newAURPackages, false)
-	return err
-}
-
-func (dp *depPool) ResolveRepoDependency(pkg *alpm.Package) {
-	dp.Repo[pkg.Name()] = pkg
-
-	pkg.Depends().ForEach(func(dep alpm.Depend) (err error) {
-		//have satisfier in dep tree: skip
-		if dp.hasSatisfier(dep.String()) {
-			return
-		}
-
-		//has satisfier installed: skip
-		_, isInstalled := dp.LocalDb.PkgCache().FindSatisfier(dep.String())
-		if isInstalled == nil {
-			return
-		}
-
-		//has satisfier in repo: fetch it
-		repoPkg, inRepos := dp.SyncDb.FindSatisfier(dep.String())
-		if inRepos != nil {
-			return
-		}
-
-		dp.ResolveRepoDependency(repoPkg)
-
-		return nil
-	})
-}
-
-func getDepPool(pkgs []string, warnings *aurWarnings) (*depPool, error) {
-	dp, err := makeDepPool()
-	if err != nil {
-		return nil, err
-	}
-
-	dp.Warnings = warnings
-	err = dp.ResolveTargets(pkgs)
-
-	return dp, err
-}
-
-func (dp *depPool) findSatisfierAur(dep string) *rpc.Pkg {
-	for _, pkg := range dp.Aur {
-		if satisfiesAur(dep, pkg) {
-			return pkg
-		}
-	}
-
-	return nil
-}
-
-// This is mostly used to promote packages from the cache
-// to the Install list
-// Provide a pacman style provider menu if there's more than one candidate
-// This acts slightly differently from Pacman, It will give
-// a menu even if a package with a matching name exists. I believe this
-// method is better because most of the time you are choosing between
-// foo and foo-git.
-// Using Pacman's ways trying to install foo would never give you
-// a menu.
-// TODO: maybe intermix repo providers in the menu
-func (dp *depPool) findSatisfierAurCache(dep string) *rpc.Pkg {
-	depName, _, _ := splitDep(dep)
-	seen := make(stringSet)
-	providers := makeProviders(depName)
-
-	if _, err := dp.LocalDb.PkgByName(depName); err == nil {
-		if pkg, ok := dp.AurCache[dep]; ok && pkgSatisfies(pkg.Name, pkg.Version, dep) {
-			return pkg
-		}
-
-	}
-
-	if cmdArgs.op == "Y" || cmdArgs.op == "yay" {
-		for _, pkg := range dp.AurCache {
-			if pkgSatisfies(pkg.Name, pkg.Version, dep) {
-				for _, target := range dp.Targets {
-					if target.Name == pkg.Name {
-						return pkg
-					}
-				}
-			}
-		}
-	}
-
-	for _, pkg := range dp.AurCache {
-		if seen.get(pkg.Name) {
-			continue
-		}
-
-		if pkgSatisfies(pkg.Name, pkg.Version, dep) {
-			providers.Pkgs = append(providers.Pkgs, pkg)
-			seen.set(pkg.Name)
-			continue
-		}
-
-		for _, provide := range pkg.Provides {
-			if provideSatisfies(provide, dep) {
-				providers.Pkgs = append(providers.Pkgs, pkg)
-				seen.set(pkg.Name)
-				continue
-			}
-		}
-	}
-
-	if providers.Len() == 1 {
-		return providers.Pkgs[0]
-	}
-
-	if providers.Len() > 1 {
-		sort.Sort(providers)
-		return providerMenu(dep, providers)
-	}
-
-	return nil
-}
-
-func (dp *depPool) findSatisfierRepo(dep string) *alpm.Package {
-	for _, pkg := range dp.Repo {
-		if satisfiesRepo(dep, pkg) {
-			return pkg
-		}
-	}
-
-	return nil
-}
-
-func (dp *depPool) hasSatisfier(dep string) bool {
-	return dp.findSatisfierRepo(dep) != nil || dp.findSatisfierAur(dep) != nil
-}
-
-func (dp *depPool) hasPackage(name string) bool {
-	for _, pkg := range dp.Repo {
-		if pkg.Name() == name {
-			return true
-		}
-	}
-
-	for _, pkg := range dp.Aur {
-		if pkg.Name == name {
-			return true
-		}
-	}
-
-	for _, pkg := range dp.Groups {
-		if pkg == name {
-			return true
-		}
-	}
-
-	return false
-}

+ 627 - 0
depSolver.go

@@ -0,0 +1,627 @@
+package main
+
+import (
+	"sort"
+	"strings"
+	"sync"
+
+	alpm "github.com/jguer/go-alpm"
+	rpc "github.com/mikkeloscar/aur"
+)
+
+type depSolver struct {
+	Aur      []Base
+	Repo     []*alpm.Package
+	Runtime  stringSet
+	Targets  []target
+	Explicit stringSet
+	AurCache map[string]*rpc.Pkg
+	Groups   []string
+	LocalDb  *alpm.Db
+	SyncDb   alpm.DbList
+	Seen     stringSet
+	Warnings *aurWarnings
+}
+
+func makeDepSolver() (*depSolver, error) {
+	localDb, err := alpmHandle.LocalDb()
+	if err != nil {
+		return nil, err
+	}
+	syncDb, err := alpmHandle.SyncDbs()
+	if err != nil {
+		return nil, err
+	}
+
+	return &depSolver{
+		make([]Base, 0),
+		make([]*alpm.Package, 0),
+		make(stringSet),
+		make([]target, 0),
+		make(stringSet),
+		make(map[string]*rpc.Pkg),
+		make([]string, 0),
+		localDb,
+		syncDb,
+		make(stringSet),
+		nil,
+	}, nil
+}
+
+func getDepSolver(pkgs []string, warnings *aurWarnings) (*depSolver, error) {
+	ds, err := makeDepSolver()
+	if err != nil {
+		return nil, err
+	}
+
+	ds.Warnings = warnings
+	err = ds.resolveTargets(pkgs)
+	if err != nil {
+		return nil, err
+	}
+
+	ds.resolveRuntime()
+	return ds, err
+}
+
+// Includes db/ prefixes and group installs
+func (ds *depSolver) resolveTargets(pkgs []string) error {
+	// RPC requests are slow
+	// Combine as many AUR package requests as possible into a single RPC
+	// call
+	aurTargets := make([]string, 0)
+	pkgs = removeInvalidTargets(pkgs)
+
+	for _, pkg := range pkgs {
+		var err error
+		target := toTarget(pkg)
+
+		// skip targets already satisfied
+		// even if the user enters db/pkg and aur/pkg the latter will
+		// still get skipped even if it's from a different database to
+		// the one specified
+		// this is how pacman behaves
+		if ds.hasPackage(target.DepString()) {
+			continue
+		}
+
+		var foundPkg *alpm.Package
+		var singleDb *alpm.Db
+
+		// aur/ prefix means we only check the aur
+		if target.Db == "aur" || mode == ModeAUR {
+			ds.Targets = append(ds.Targets, target)
+			aurTargets = append(aurTargets, target.DepString())
+			continue
+		}
+
+		// If there'ss a different priefix only look in that repo
+		if target.Db != "" {
+			singleDb, err = alpmHandle.SyncDbByName(target.Db)
+			if err != nil {
+				return err
+			}
+			foundPkg, err = singleDb.PkgCache().FindSatisfier(target.DepString())
+			//otherwise find it in any repo
+		} else {
+			foundPkg, err = ds.SyncDb.FindSatisfier(target.DepString())
+		}
+
+		if err == nil {
+			ds.Targets = append(ds.Targets, target)
+			ds.Explicit.set(foundPkg.Name())
+			ds.ResolveRepoDependency(foundPkg)
+			continue
+		} else {
+			//check for groups
+			//currently we don't resolve the packages in a group
+			//only check if the group exists
+			//would be better to check the groups from singleDb if
+			//the user specified a db but there's no easy way to do
+			//it without making alpm_lists so don't bother for now
+			//db/group is probably a rare use case
+			group, err := ds.SyncDb.PkgCachebyGroup(target.Name)
+			if err == nil {
+				ds.Groups = append(ds.Groups, target.String())
+				group.ForEach(func(pkg alpm.Package) error {
+					ds.Explicit.set(pkg.Name())
+					return nil
+				})
+				continue
+			}
+		}
+
+		//if there was no db prefix check the aur
+		if target.Db == "" {
+			aurTargets = append(aurTargets, target.DepString())
+		}
+
+		ds.Targets = append(ds.Targets, target)
+	}
+
+	if len(aurTargets) > 0 && (mode == ModeAny || mode == ModeAUR) {
+		return ds.resolveAURPackages(aurTargets, true)
+	}
+
+	return nil
+}
+
+func (ds *depSolver) hasPackage(name string) bool {
+	for _, pkg := range ds.Repo {
+		if pkg.Name() == name {
+			return true
+		}
+	}
+
+	for _, base := range ds.Aur {
+		for _, pkg := range base {
+			if pkg.Name == name {
+				return true
+			}
+		}
+	}
+
+	for _, pkg := range ds.Groups {
+		if pkg == name {
+			return true
+		}
+	}
+
+	return false
+}
+
+func (ds *depSolver) findSatisfierAur(dep string) *rpc.Pkg {
+	for _, base := range ds.Aur {
+		for _, pkg := range base {
+			if satisfiesAur(dep, pkg) {
+				return pkg
+			}
+		}
+	}
+
+	return nil
+}
+
+func (ds *depSolver) findSatisfierRepo(dep string) *alpm.Package {
+	for _, pkg := range ds.Repo {
+		if satisfiesRepo(dep, pkg) {
+			return pkg
+		}
+	}
+
+	return nil
+}
+
+func (ds *depSolver) hasSatisfier(dep string) bool {
+	return ds.findSatisfierRepo(dep) != nil || ds.findSatisfierAur(dep) != nil
+}
+
+func (ds *depSolver) ResolveRepoDependency(pkg *alpm.Package) {
+	if ds.Seen.get(pkg.Name()) {
+		return
+	}
+	ds.Repo = append(ds.Repo, pkg)
+	ds.Seen.set(pkg.Name())
+
+	pkg.Depends().ForEach(func(dep alpm.Depend) (err error) {
+		//have satisfier in dep tree: skip
+		if ds.hasSatisfier(dep.String()) {
+			return
+		}
+
+		//has satisfier installed: skip
+		_, isInstalled := ds.LocalDb.PkgCache().FindSatisfier(dep.String())
+		if isInstalled == nil {
+			return
+		}
+
+		//has satisfier in repo: fetch it
+		repoPkg, inRepos := ds.SyncDb.FindSatisfier(dep.String())
+		if inRepos != nil {
+			return
+		}
+
+		ds.ResolveRepoDependency(repoPkg)
+		return nil
+	})
+}
+
+// This is mostly used to promote packages from the cache
+// to the Install list
+// Provide a pacman style provider menu if there's more than one candidate
+// This acts slightly differently from Pacman, It will give
+// a menu even if a package with a matching name exists. I believe this
+// method is better because most of the time you are choosing between
+// foo and foo-git.
+// Using Pacman's ways trying to install foo would never give you
+// a menu.
+// TODO: maybe intermix repo providers in the menu
+func (ds *depSolver) findSatisfierAurCache(dep string) *rpc.Pkg {
+	depName, _, _ := splitDep(dep)
+	seen := make(stringSet)
+	providers := makeProviders(depName)
+
+	if _, err := ds.LocalDb.PkgByName(depName); err == nil {
+		if pkg, ok := ds.AurCache[dep]; ok && pkgSatisfies(pkg.Name, pkg.Version, dep) {
+			return pkg
+		}
+
+	}
+
+	if cmdArgs.op == "Y" || cmdArgs.op == "yay" {
+		for _, pkg := range ds.AurCache {
+			if pkgSatisfies(pkg.Name, pkg.Version, dep) {
+				for _, target := range ds.Targets {
+					if target.Name == pkg.Name {
+						return pkg
+					}
+				}
+			}
+		}
+	}
+
+	for _, pkg := range ds.AurCache {
+		if seen.get(pkg.Name) {
+			continue
+		}
+
+		if pkgSatisfies(pkg.Name, pkg.Version, dep) {
+			providers.Pkgs = append(providers.Pkgs, pkg)
+			seen.set(pkg.Name)
+			continue
+		}
+
+		for _, provide := range pkg.Provides {
+			if provideSatisfies(provide, dep) {
+				providers.Pkgs = append(providers.Pkgs, pkg)
+				seen.set(pkg.Name)
+				continue
+			}
+		}
+	}
+
+	if providers.Len() == 1 {
+		return providers.Pkgs[0]
+	}
+
+	if providers.Len() > 1 {
+		sort.Sort(providers)
+		return providerMenu(dep, providers)
+	}
+
+	return nil
+}
+
+func (ds *depSolver) cacheAURPackages(_pkgs []string) error {
+	pkgs := sliceToStringSet(_pkgs)
+	query := make([]string, 0)
+
+	for pkg := range pkgs {
+		if _, ok := ds.AurCache[pkg]; ok {
+			pkgs.remove(pkg)
+		}
+	}
+
+	if len(pkgs) == 0 {
+		return nil
+	}
+
+	if config.Provides {
+		err := ds.findProvides(pkgs)
+		if err != nil {
+			return err
+		}
+	}
+
+	for pkg := range pkgs {
+		if _, ok := ds.AurCache[pkg]; !ok {
+			name, _, _ := splitDep(pkg)
+			query = append(query, name)
+		}
+	}
+
+	info, err := aurInfo(query, ds.Warnings)
+	if err != nil {
+		return err
+	}
+
+	for _, pkg := range info {
+		// Dump everything in cache just in case we need it later
+		ds.AurCache[pkg.Name] = pkg
+	}
+
+	return nil
+}
+
+// Pseudo provides finder.
+// Try to find provides by performing a search of the package name
+// This effectively performs -Ss on each package
+// then runs -Si on each result to cache the information.
+//
+// For example if you were to -S yay then yay -Ss would give:
+// yay-git yay-bin yay realyog pacui pacui-git ruby-yard
+// These packages will all be added to the cache in case they are needed later
+// Ofcouse only the first three packages provide yay, the rest are just false
+// positives.
+//
+// This method increases dependency resolve time
+func (ds *depSolver) findProvides(pkgs stringSet) error {
+	var mux sync.Mutex
+	var wg sync.WaitGroup
+
+	doSearch := func(pkg string) {
+		defer wg.Done()
+		var err error
+		var results []rpc.Pkg
+
+		// Hack for a bigger search result, if the user wants
+		// java-envronment we can search for just java instead and get
+		// more hits.
+		words := strings.Split(pkg, "-")
+
+		for i := range words {
+			results, err = rpc.SearchByNameDesc(strings.Join(words[:i+1], "-"))
+			if err == nil {
+				break
+			}
+		}
+
+		if err != nil {
+			return
+		}
+
+		for _, result := range results {
+			mux.Lock()
+			if _, ok := ds.AurCache[result.Name]; !ok {
+				pkgs.set(result.Name)
+			}
+			mux.Unlock()
+		}
+	}
+
+	for pkg := range pkgs {
+		if _, err := ds.LocalDb.PkgByName(pkg); err == nil {
+			continue
+		}
+		wg.Add(1)
+		go doSearch(pkg)
+	}
+
+	wg.Wait()
+
+	return nil
+}
+
+func (ds *depSolver) resolveAURPackages(pkgs []string, explicit bool) error {
+	newPackages := make(stringSet)
+	newAURPackages := make([]string, 0)
+	toAdd := make([]*rpc.Pkg, 0)
+
+	if len(pkgs) == 0 {
+		return nil
+	}
+
+	err := ds.cacheAURPackages(pkgs)
+	if err != nil {
+		return err
+	}
+
+	for _, name := range pkgs {
+		if ds.Seen.get(name) {
+			continue
+		}
+
+		pkg := ds.findSatisfierAurCache(name)
+		if pkg == nil {
+			continue
+		}
+
+		if explicit {
+			ds.Explicit.set(pkg.Name)
+		}
+
+		ds.Seen.set(pkg.Name)
+		toAdd = append(toAdd, pkg)
+
+		for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
+			for _, dep := range deps {
+				newPackages.set(dep)
+			}
+		}
+	}
+
+	for dep := range newPackages {
+		if ds.hasSatisfier(dep) {
+			continue
+		}
+
+		_, isInstalled := ds.LocalDb.PkgCache().FindSatisfier(dep) //has satisfier installed: skip
+		hm := hideMenus
+		hideMenus = isInstalled == nil
+		repoPkg, inRepos := ds.SyncDb.FindSatisfier(dep) //has satisfier in repo: fetch it
+		hideMenus = hm
+		if isInstalled == nil && (config.ReBuild != "tree" || inRepos == nil) {
+			continue
+		}
+
+		if inRepos == nil {
+			ds.ResolveRepoDependency(repoPkg)
+			continue
+		}
+
+		//assume it's in the aur
+		//ditch the versioning because the RPC can't handle it
+		newAURPackages = append(newAURPackages, dep)
+
+	}
+
+	err = ds.resolveAURPackages(newAURPackages, false)
+
+	for _, pkg := range toAdd {
+		if !ds.hasPackage(pkg.Name) {
+			ds.Aur = baseAppend(ds.Aur, pkg)
+		}
+	}
+
+	return err
+}
+
+func (ds *depSolver) Print() {
+	repo := ""
+	repoMake := ""
+	aur := ""
+	aurMake := ""
+
+	repoLen := 0
+	repoMakeLen := 0
+	aurLen := 0
+	aurMakeLen := 0
+
+	for _, pkg := range ds.Repo {
+		if ds.Runtime.get(pkg.Name()) {
+			repo += "  " + pkg.Name() + "-" + pkg.Version()
+			repoLen++
+		} else {
+			repoMake += "  " + pkg.Name() + "-" + pkg.Version()
+			repoMakeLen++
+		}
+	}
+
+	for _, base := range ds.Aur {
+		pkg := base.Pkgbase()
+		pkgStr := "  " + pkg + "-" + base[0].Version
+		pkgStrMake := pkgStr
+
+		push := false
+		pushMake := false
+
+		if len(base) > 1 || pkg != base[0].Name {
+			pkgStr += " ("
+			pkgStrMake += " ("
+
+			for _, split := range base {
+				if ds.Runtime.get(split.Name) {
+					pkgStr += split.Name + " "
+					aurLen++
+					push = true
+				} else {
+					pkgStrMake += split.Name + " "
+					aurMakeLen++
+					pushMake = true
+				}
+			}
+
+			pkgStr = pkgStr[:len(pkgStr)-1] + ")"
+			pkgStrMake = pkgStrMake[:len(pkgStrMake)-1] + ")"
+		} else if ds.Runtime.get(base[0].Name) {
+			aurLen++
+			push = true
+		} else {
+			aurMakeLen++
+			pushMake = true
+		}
+
+		if push {
+			aur += pkgStr
+		}
+		if pushMake {
+			aurMake += pkgStrMake
+		}
+	}
+
+	printDownloads("Repo", repoLen, repo)
+	printDownloads("Repo Make", repoMakeLen, repoMake)
+	printDownloads("Aur", aurLen, aur)
+	printDownloads("Aur Make", aurMakeLen, aurMake)
+}
+
+func (ds *depSolver) resolveRuntime() {
+	for _, pkg := range ds.Repo {
+		if ds.Explicit.get(pkg.Name()) {
+			ds.Runtime.set(pkg.Name())
+			ds.resolveRuntimeRepo(pkg)
+		}
+	}
+
+	for _, base := range ds.Aur {
+		for _, pkg := range base {
+			if ds.Explicit.get(pkg.Name) {
+				ds.Runtime.set(pkg.Name)
+				ds.resolveRuntimeAur(pkg)
+			}
+		}
+	}
+}
+
+func (ds *depSolver) resolveRuntimeRepo(pkg *alpm.Package) {
+	pkg.Depends().ForEach(func(dep alpm.Depend) (err error) {
+		for _, pkg := range ds.Repo {
+			if ds.Runtime.get(pkg.Name()) {
+				continue
+			}
+
+			if satisfiesRepo(dep.String(), pkg) {
+				ds.Runtime.set(pkg.Name())
+				ds.resolveRuntimeRepo(pkg)
+			}
+		}
+		return nil
+	})
+}
+
+func (ds *depSolver) resolveRuntimeAur(pkg *rpc.Pkg) {
+	for _, dep := range pkg.Depends {
+		for _, pkg := range ds.Repo {
+			if ds.Runtime.get(pkg.Name()) {
+				continue
+			}
+
+			if satisfiesRepo(dep, pkg) {
+				ds.Runtime.set(pkg.Name())
+				ds.resolveRuntimeRepo(pkg)
+			}
+		}
+
+		for _, base := range ds.Aur {
+			for _, pkg := range base {
+				if ds.Runtime.get(pkg.Name) {
+					continue
+				}
+
+				if satisfiesAur(dep, pkg) {
+					ds.Runtime.set(pkg.Name)
+					ds.resolveRuntimeAur(pkg)
+				}
+			}
+		}
+	}
+}
+
+func (ds *depSolver) HasMake() bool {
+	lenAur := 0
+	for _, base := range ds.Aur {
+		lenAur += len(base)
+	}
+
+	return len(ds.Runtime) != lenAur+len(ds.Repo)
+}
+
+func (ds *depSolver) getMake() []string {
+	makeOnly := make([]string, 0, len(ds.Aur)+len(ds.Repo)-len(ds.Runtime))
+
+	for _, base := range ds.Aur {
+		for _, pkg := range base {
+			if !ds.Runtime.get(pkg.Name) {
+				makeOnly = append(makeOnly, pkg.Name)
+			}
+		}
+	}
+
+	for _, pkg := range ds.Repo {
+		if !ds.Runtime.get(pkg.Name()) {
+			makeOnly = append(makeOnly, pkg.Name())
+		}
+	}
+
+	return makeOnly
+}

+ 37 - 43
install.go

@@ -17,7 +17,6 @@ import (
 func install(parser *arguments) error {
 	var err error
 	var incompatible stringSet
-	var do *depOrder
 
 	var aurUp upSlice
 	var repoUp upSlice
@@ -110,17 +109,17 @@ func install(parser *arguments) error {
 
 	targets := sliceToStringSet(parser.targets)
 
-	dp, err := getDepPool(requestTargets, warnings)
+	ds, err := getDepSolver(requestTargets, warnings)
 	if err != nil {
 		return err
 	}
 
-	err = dp.CheckMissing()
+	err = ds.CheckMissing()
 	if err != nil {
 		return err
 	}
 
-	if len(dp.Aur) == 0 {
+	if len(ds.Aur) == 0 {
 		if !config.CombinedUpgrade {
 			if parser.existsArg("u", "sysupgrade") {
 				fmt.Println(" there is nothing to do")
@@ -134,37 +133,32 @@ func install(parser *arguments) error {
 		return show(passToPacman(parser))
 	}
 
-	if len(dp.Aur) > 0 && 0 == os.Geteuid() {
+	if len(ds.Aur) > 0 && 0 == os.Geteuid() {
 		return fmt.Errorf(bold(red(arrow)) + " Refusing to install AUR Packages as root, Aborting.")
 	}
 
-	conflicts, err := dp.CheckConflicts()
+	conflicts, err := ds.CheckConflicts()
 	if err != nil {
 		return err
 	}
 
-	do = getDepOrder(dp)
-	if err != nil {
-		return err
-	}
-
-	for _, pkg := range do.Repo {
+	for _, pkg := range ds.Repo {
 		arguments.addTarget(pkg.DB().Name() + "/" + pkg.Name())
 	}
 
-	for _, pkg := range dp.Groups {
+	for _, pkg := range ds.Groups {
 		arguments.addTarget(pkg)
 	}
 
-	if len(do.Aur) == 0 && len(arguments.targets) == 0 && (!parser.existsArg("u", "sysupgrade") || mode == ModeAUR) {
+	if len(ds.Aur) == 0 && len(arguments.targets) == 0 && (!parser.existsArg("u", "sysupgrade") || mode == ModeAUR) {
 		fmt.Println(" there is nothing to do")
 		return nil
 	}
 
-	do.Print()
+	ds.Print()
 	fmt.Println()
 
-	if do.HasMake() {
+	if ds.HasMake() {
 		if config.RemoveMake == "yes" {
 			removeMake = true
 		} else if config.RemoveMake == "no" {
@@ -175,9 +169,9 @@ func install(parser *arguments) error {
 	}
 
 	if config.CleanMenu {
-		if anyExistInCache(do.Aur) {
-			askClean := pkgbuildNumberMenu(do.Aur, remoteNamesCache)
-			toClean, err := cleanNumberMenu(do.Aur, remoteNamesCache, askClean)
+		if anyExistInCache(ds.Aur) {
+			askClean := pkgbuildNumberMenu(ds.Aur, remoteNamesCache)
+			toClean, err := cleanNumberMenu(ds.Aur, remoteNamesCache, askClean)
 			if err != nil {
 				return err
 			}
@@ -186,8 +180,8 @@ func install(parser *arguments) error {
 		}
 	}
 
-	toSkip := pkgbuildsToSkip(do.Aur, targets)
-	cloned, err := downloadPkgbuilds(do.Aur, toSkip, config.BuildDir)
+	toSkip := pkgbuildsToSkip(ds.Aur, targets)
+	cloned, err := downloadPkgbuilds(ds.Aur, toSkip, config.BuildDir)
 	if err != nil {
 		return err
 	}
@@ -196,8 +190,8 @@ func install(parser *arguments) error {
 	var toEdit []Base
 
 	if config.DiffMenu {
-		pkgbuildNumberMenu(do.Aur, remoteNamesCache)
-		toDiff, err = diffNumberMenu(do.Aur, remoteNamesCache)
+		pkgbuildNumberMenu(ds.Aur, remoteNamesCache)
+		toDiff, err = diffNumberMenu(ds.Aur, remoteNamesCache)
 		if err != nil {
 			return err
 		}
@@ -220,19 +214,19 @@ func install(parser *arguments) error {
 		config.NoConfirm = oldValue
 	}
 
-	err = mergePkgbuilds(do.Aur)
+	err = mergePkgbuilds(ds.Aur)
 	if err != nil {
 		return err
 	}
 
-	srcinfos, err = parseSrcinfoFiles(do.Aur, true)
+	srcinfos, err = parseSrcinfoFiles(ds.Aur, true)
 	if err != nil {
 		return err
 	}
 
 	if config.EditMenu {
-		pkgbuildNumberMenu(do.Aur, remoteNamesCache)
-		toEdit, err = editNumberMenu(do.Aur, remoteNamesCache)
+		pkgbuildNumberMenu(ds.Aur, remoteNamesCache)
+		toEdit, err = editNumberMenu(ds.Aur, remoteNamesCache)
 		if err != nil {
 			return err
 		}
@@ -255,13 +249,13 @@ func install(parser *arguments) error {
 		config.NoConfirm = oldValue
 	}
 
-	incompatible, err = getIncompatible(do.Aur, srcinfos)
+	incompatible, err = getIncompatible(ds.Aur, srcinfos)
 	if err != nil {
 		return err
 	}
 
 	if config.PGPFetch {
-		err = checkPgpKeys(do.Aur, srcinfos)
+		err = checkPgpKeys(ds.Aur, srcinfos)
 		if err != nil {
 			return err
 		}
@@ -282,15 +276,15 @@ func install(parser *arguments) error {
 		expArguments := makeArguments()
 		expArguments.addArg("D", "asexplicit")
 
-		for _, pkg := range do.Repo {
-			if !dp.Explicit.get(pkg.Name()) && !localNamesCache.get(pkg.Name()) && !remoteNamesCache.get(pkg.Name()) {
+		for _, pkg := range ds.Repo {
+			if !ds.Explicit.get(pkg.Name()) && !localNamesCache.get(pkg.Name()) && !remoteNamesCache.get(pkg.Name()) {
 				depArguments.addTarget(pkg.Name())
 				continue
 			}
 
-			if parser.existsArg("asdeps", "asdep") && dp.Explicit.get(pkg.Name()) {
+			if parser.existsArg("asdeps", "asdep") && ds.Explicit.get(pkg.Name()) {
 				depArguments.addTarget(pkg.Name())
-			} else if parser.existsArg("asexp", "asexplicit") && dp.Explicit.get(pkg.Name()) {
+			} else if parser.existsArg("asexp", "asexplicit") && ds.Explicit.get(pkg.Name()) {
 				expArguments.addTarget(pkg.Name())
 			}
 		}
@@ -312,12 +306,12 @@ func install(parser *arguments) error {
 
 	go updateCompletion(false)
 
-	err = downloadPkgbuildsSources(do.Aur, incompatible)
+	err = downloadPkgbuildsSources(ds.Aur, incompatible)
 	if err != nil {
 		return err
 	}
 
-	err = buildInstallPkgbuilds(dp, do, srcinfos, parser, incompatible, conflicts)
+	err = buildInstallPkgbuilds(ds, srcinfos, parser, incompatible, conflicts)
 	if err != nil {
 		return err
 	}
@@ -326,7 +320,7 @@ func install(parser *arguments) error {
 		removeArguments := makeArguments()
 		removeArguments.addArg("R", "u")
 
-		for _, pkg := range do.getMake() {
+		for _, pkg := range ds.getMake() {
 			removeArguments.addTarget(pkg)
 		}
 
@@ -341,7 +335,7 @@ func install(parser *arguments) error {
 	}
 
 	if config.CleanAfter {
-		cleanAfter(do.Aur)
+		cleanAfter(ds.Aur)
 	}
 
 	return nil
@@ -900,8 +894,8 @@ func downloadPkgbuildsSources(bases []Base, incompatible stringSet) (err error)
 	return
 }
 
-func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc.Srcinfo, parser *arguments, incompatible stringSet, conflicts mapStringSet) error {
-	for _, base := range do.Aur {
+func buildInstallPkgbuilds(ds *depSolver, srcinfos map[string]*gosrc.Srcinfo, parser *arguments, incompatible stringSet, conflicts mapStringSet) error {
+	for _, base := range ds.Aur {
 		pkg := base.Pkgbase()
 		dir := filepath.Join(config.BuildDir, pkg)
 		built := true
@@ -927,7 +921,7 @@ func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc
 
 		isExplicit := false
 		for _, b := range base {
-			isExplicit = isExplicit || dp.Explicit.get(b.Name)
+			isExplicit = isExplicit || ds.Explicit.get(b.Name)
 		}
 		if config.ReBuild == "no" || (config.ReBuild == "yes" && !isExplicit) {
 			for _, split := range base {
@@ -950,7 +944,7 @@ func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc
 		if cmdArgs.existsArg("needed") {
 			installed := true
 			for _, split := range base {
-				if alpmpkg, err := dp.LocalDb.PkgByName(split.Name); err != nil || alpmpkg.Version() != version {
+				if alpmpkg, err := ds.LocalDb.PkgByName(split.Name); err != nil || alpmpkg.Version() != version {
 					installed = false
 				}
 			}
@@ -1034,11 +1028,11 @@ func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc
 			}
 
 			arguments.addTarget(pkgdest)
-			if !dp.Explicit.get(split.Name) && !localNamesCache.get(split.Name) && !remoteNamesCache.get(split.Name) {
+			if !ds.Explicit.get(split.Name) && !localNamesCache.get(split.Name) && !remoteNamesCache.get(split.Name) {
 				depArguments.addTarget(split.Name)
 			}
 
-			if dp.Explicit.get(split.Name) {
+			if ds.Explicit.get(split.Name) {
 				if parser.existsArg("asdeps", "asdep") {
 					depArguments.addTarget(split.Name)
 				} else if parser.existsArg("asexplicit", "asexp") {

+ 0 - 70
print.go

@@ -189,76 +189,6 @@ func (u upSlice) print() {
 	}
 }
 
-// printDownloadsFromRepo prints repository packages to be downloaded
-func (do *depOrder) Print() {
-	repo := ""
-	repoMake := ""
-	aur := ""
-	aurMake := ""
-
-	repoLen := 0
-	repoMakeLen := 0
-	aurLen := 0
-	aurMakeLen := 0
-
-	for _, pkg := range do.Repo {
-		if do.Runtime.get(pkg.Name()) {
-			repo += "  " + pkg.Name() + "-" + pkg.Version()
-			repoLen++
-		} else {
-			repoMake += "  " + pkg.Name() + "-" + pkg.Version()
-			repoMakeLen++
-		}
-	}
-
-	for _, base := range do.Aur {
-		pkg := base.Pkgbase()
-		pkgStr := "  " + pkg + "-" + base[0].Version
-		pkgStrMake := pkgStr
-
-		push := false
-		pushMake := false
-
-		if len(base) > 1 || pkg != base[0].Name {
-			pkgStr += " ("
-			pkgStrMake += " ("
-
-			for _, split := range base {
-				if do.Runtime.get(split.Name) {
-					pkgStr += split.Name + " "
-					aurLen++
-					push = true
-				} else {
-					pkgStrMake += split.Name + " "
-					aurMakeLen++
-					pushMake = true
-				}
-			}
-
-			pkgStr = pkgStr[:len(pkgStr)-1] + ")"
-			pkgStrMake = pkgStrMake[:len(pkgStrMake)-1] + ")"
-		} else if do.Runtime.get(base[0].Name) {
-			aurLen++
-			push = true
-		} else {
-			aurMakeLen++
-			pushMake = true
-		}
-
-		if push {
-			aur += pkgStr
-		}
-		if pushMake {
-			aurMake += pkgStrMake
-		}
-	}
-
-	printDownloads("Repo", repoLen, repo)
-	printDownloads("Repo Make", repoMakeLen, repoMake)
-	printDownloads("Aur", aurLen, aur)
-	printDownloads("Aur Make", aurMakeLen, aurMake)
-}
-
 func printDownloads(repoName string, length int, packages string) {
 	if length < 1 {
 		return