2017-08-02 19:24:03 +02:00
|
|
|
package main
|
|
|
|
|
2017-08-07 15:43:25 +02:00
|
|
|
import (
|
2018-03-02 12:32:35 +01:00
|
|
|
"strings"
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
alpm "github.com/jguer/go-alpm"
|
2018-01-20 23:37:10 +01:00
|
|
|
rpc "github.com/mikkeloscar/aur"
|
2017-08-07 15:43:25 +02:00
|
|
|
)
|
2017-08-04 11:26:53 +02:00
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
type depTree struct {
|
2018-02-18 00:35:54 +01:00
|
|
|
ToProcess stringSet
|
2018-01-20 23:37:10 +01:00
|
|
|
Repo map[string]*alpm.Package
|
|
|
|
Aur map[string]*rpc.Pkg
|
|
|
|
Missing stringSet
|
2018-01-17 22:48:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type depCatagories struct {
|
2018-01-20 23:37:10 +01:00
|
|
|
Repo []*alpm.Package
|
2018-02-16 18:18:59 +01:00
|
|
|
Aur []*rpc.Pkg
|
2018-02-15 22:23:34 +01:00
|
|
|
MakeOnly stringSet
|
2018-02-16 18:18:59 +01:00
|
|
|
Bases map[string][]*rpc.Pkg
|
2018-01-17 22:48:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func makeDepTree() *depTree {
|
|
|
|
dt := depTree{
|
2018-02-18 00:35:54 +01:00
|
|
|
make(stringSet),
|
2018-01-17 22:48:23 +01:00
|
|
|
make(map[string]*alpm.Package),
|
|
|
|
make(map[string]*rpc.Pkg),
|
|
|
|
make(stringSet),
|
2017-08-02 19:24:03 +02:00
|
|
|
}
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
return &dt
|
|
|
|
}
|
|
|
|
|
|
|
|
func makeDependCatagories() *depCatagories {
|
|
|
|
dc := depCatagories{
|
|
|
|
make([]*alpm.Package, 0),
|
|
|
|
make([]*rpc.Pkg, 0),
|
2018-02-15 22:23:34 +01:00
|
|
|
make(stringSet),
|
|
|
|
make(map[string][]*rpc.Pkg),
|
2017-08-02 19:24:03 +02:00
|
|
|
}
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
return &dc
|
|
|
|
}
|
|
|
|
|
2018-03-03 02:11:16 +01:00
|
|
|
//cut the version requirement from a dependency leaving just the name
|
2018-01-17 22:48:23 +01:00
|
|
|
func getNameFromDep(dep string) string {
|
|
|
|
return strings.FieldsFunc(dep, func(c rune) bool {
|
2018-02-22 22:05:39 +01:00
|
|
|
return c == '>' || c == '<' || c == '='
|
2018-01-17 22:48:23 +01:00
|
|
|
})[0]
|
|
|
|
}
|
|
|
|
|
2018-03-03 02:11:16 +01:00
|
|
|
//step two of dependency reloving. 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 not
|
|
|
|
//included.
|
|
|
|
//For each package we want we iterate down the tree until we hit the bottom.
|
|
|
|
//This is done recursivley 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 nothign so we add c to our list and move up
|
|
|
|
//
|
|
|
|
//The final install order would come out as debca
|
|
|
|
//
|
|
|
|
//Theres a little more to this, handling provide, multiple packages wanting the
|
|
|
|
//same dependencies and so on this is just the basic premise.
|
2018-01-17 22:48:23 +01:00
|
|
|
func getDepCatagories(pkgs []string, dt *depTree) (*depCatagories, error) {
|
|
|
|
dc := makeDependCatagories()
|
2018-02-15 22:23:34 +01:00
|
|
|
seen := make(stringSet)
|
2018-01-17 22:48:23 +01:00
|
|
|
|
2018-02-27 21:57:11 +01:00
|
|
|
for _, pkg := range dt.Aur {
|
|
|
|
_, ok := dc.Bases[pkg.PackageBase]
|
|
|
|
if !ok {
|
|
|
|
dc.Bases[pkg.PackageBase] = make([]*rpc.Pkg, 0)
|
|
|
|
}
|
|
|
|
dc.Bases[pkg.PackageBase] = append(dc.Bases[pkg.PackageBase], pkg)
|
|
|
|
}
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
for _, pkg := range pkgs {
|
|
|
|
dep := getNameFromDep(pkg)
|
|
|
|
alpmpkg, exists := dt.Repo[dep]
|
|
|
|
if exists {
|
|
|
|
repoDepCatagoriesRecursive(alpmpkg, dc, dt, false)
|
|
|
|
dc.Repo = append(dc.Repo, alpmpkg)
|
|
|
|
delete(dt.Repo, dep)
|
|
|
|
}
|
|
|
|
|
|
|
|
aurpkg, exists := dt.Aur[dep]
|
|
|
|
if exists {
|
2018-02-15 22:23:34 +01:00
|
|
|
depCatagoriesRecursive(aurpkg, dc, dt, false, seen)
|
|
|
|
if !seen.get(aurpkg.PackageBase) {
|
|
|
|
dc.Aur = append(dc.Aur, aurpkg)
|
|
|
|
seen.set(aurpkg.PackageBase)
|
|
|
|
}
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
delete(dt.Aur, dep)
|
|
|
|
}
|
2017-08-02 19:24:03 +02:00
|
|
|
}
|
|
|
|
|
2018-02-16 17:27:53 +01:00
|
|
|
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
|
|
|
|
})
|
|
|
|
}
|
2018-02-15 22:23:34 +01:00
|
|
|
|
2018-03-01 15:52:57 +01:00
|
|
|
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
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
return dc, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func repoDepCatagoriesRecursive(pkg *alpm.Package, dc *depCatagories, dt *depTree, isMake bool) {
|
|
|
|
pkg.Depends().ForEach(func(_dep alpm.Depend) error {
|
|
|
|
dep := _dep.Name
|
|
|
|
alpmpkg, exists := dt.Repo[dep]
|
|
|
|
if exists {
|
|
|
|
delete(dt.Repo, dep)
|
|
|
|
repoDepCatagoriesRecursive(alpmpkg, dc, dt, isMake)
|
2018-01-20 23:37:10 +01:00
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
if isMake {
|
2018-02-15 22:23:34 +01:00
|
|
|
dc.MakeOnly.set(alpmpkg.Name())
|
2018-01-17 22:48:23 +01:00
|
|
|
}
|
|
|
|
|
2018-02-15 22:23:34 +01:00
|
|
|
dc.Repo = append(dc.Repo, alpmpkg)
|
2017-08-02 19:24:03 +02:00
|
|
|
}
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-02-27 21:57:11 +01:00
|
|
|
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 _, _dep := range deps {
|
|
|
|
dep := getNameFromDep(_dep)
|
|
|
|
|
|
|
|
aurpkg, exists := dt.Aur[dep]
|
|
|
|
if exists {
|
|
|
|
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)
|
|
|
|
}
|
2018-02-15 22:23:34 +01:00
|
|
|
}
|
|
|
|
|
2018-02-27 21:57:11 +01:00
|
|
|
alpmpkg, exists := dt.Repo[dep]
|
|
|
|
if exists {
|
|
|
|
delete(dt.Repo, dep)
|
|
|
|
repoDepCatagoriesRecursive(alpmpkg, dc, dt, isMake)
|
2018-01-17 22:48:23 +01:00
|
|
|
|
2018-02-27 21:57:11 +01:00
|
|
|
if isMake {
|
|
|
|
dc.MakeOnly.set(alpmpkg.Name())
|
|
|
|
}
|
2018-01-17 22:48:23 +01:00
|
|
|
|
2018-02-27 21:57:11 +01:00
|
|
|
dc.Repo = append(dc.Repo, alpmpkg)
|
2018-01-17 22:48:23 +01:00
|
|
|
}
|
|
|
|
|
2017-08-02 19:24:03 +02:00
|
|
|
}
|
2018-02-27 21:57:11 +01:00
|
|
|
isMake = true
|
2017-08-02 19:24:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-03 02:11:16 +01:00
|
|
|
//This is step one for dependency resolving. pkgs is a slice of the packages you
|
|
|
|
//want to resolve the dependencioes 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 pre buiild.
|
|
|
|
//The return will be split into three catagories. Repo, Aur and Missing.
|
|
|
|
//The return is in no way ordered this step is is just aimed at gathering the
|
|
|
|
//packaghes we need.
|
|
|
|
//This has been designed to make the leat amount or rpc requests as possible.
|
|
|
|
//Web requests are probably going to be the bottleneck here so minimizing them
|
|
|
|
//provides a nice spead boost.
|
|
|
|
//
|
|
|
|
//Here is a visual expample of the request system.
|
|
|
|
//Remember only unsatisfied packages are requested, if a package is already
|
|
|
|
//installed we dont bother.
|
|
|
|
//
|
|
|
|
// a
|
|
|
|
// / \
|
|
|
|
// b c
|
|
|
|
// / \
|
|
|
|
// d e
|
|
|
|
//
|
|
|
|
//We see a so we send a request for a
|
|
|
|
//we see 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
|
|
|
|
//
|
|
|
|
//Thats 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.
|
2018-01-17 22:48:23 +01:00
|
|
|
func getDepTree(pkgs []string) (*depTree, error) {
|
|
|
|
dt := makeDepTree()
|
|
|
|
|
2017-10-18 04:38:19 +02:00
|
|
|
localDb, err := alpmHandle.LocalDb()
|
2017-08-02 19:24:03 +02:00
|
|
|
if err != nil {
|
2018-01-17 22:48:23 +01:00
|
|
|
return dt, err
|
2017-08-02 19:24:03 +02:00
|
|
|
}
|
2018-01-17 22:48:23 +01:00
|
|
|
syncDb, err := alpmHandle.SyncDbs()
|
2017-08-02 19:24:03 +02:00
|
|
|
if err != nil {
|
2018-01-17 22:48:23 +01:00
|
|
|
return dt, err
|
2017-08-02 19:24:03 +02:00
|
|
|
}
|
|
|
|
|
2018-01-20 23:37:10 +01:00
|
|
|
for _, pkg := range pkgs {
|
2018-01-17 22:48:23 +01:00
|
|
|
//if they explicitly asked for it still look for installed pkgs
|
|
|
|
/*installedPkg, isInstalled := localDb.PkgCache().FindSatisfier(pkg)
|
|
|
|
if isInstalled == nil {
|
|
|
|
dt.Repo[installedPkg.Name()] = installedPkg
|
|
|
|
continue
|
|
|
|
}//*/
|
2017-08-02 19:24:03 +02:00
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
//check the repos for a matching dep
|
|
|
|
repoPkg, inRepos := syncDb.FindSatisfier(pkg)
|
|
|
|
if inRepos == nil {
|
|
|
|
repoTreeRecursive(repoPkg, dt, localDb, syncDb)
|
2017-08-02 19:24:03 +02:00
|
|
|
continue
|
|
|
|
}
|
2018-01-20 23:37:10 +01:00
|
|
|
|
2018-02-27 21:37:52 +01:00
|
|
|
_, isGroup := syncDb.PkgCachebyGroup(pkg)
|
|
|
|
if isGroup == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-02-18 00:35:54 +01:00
|
|
|
dt.ToProcess.set(pkg)
|
2017-08-02 19:24:03 +02:00
|
|
|
}
|
|
|
|
|
2018-02-18 00:35:54 +01:00
|
|
|
err = depTreeRecursive(dt, localDb, syncDb, false)
|
2018-01-17 22:48:23 +01:00
|
|
|
|
|
|
|
return dt, err
|
2017-08-02 19:24:03 +02:00
|
|
|
}
|
2017-08-07 15:43:25 +02:00
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
//takes a repo package
|
|
|
|
//gives all of the non installed deps
|
|
|
|
//does again on each sub dep
|
2018-01-20 23:37:10 +01:00
|
|
|
func repoTreeRecursive(pkg *alpm.Package, dt *depTree, localDb *alpm.Db, syncDb alpm.DbList) (err error) {
|
2018-01-17 22:48:23 +01:00
|
|
|
_, exists := dt.Repo[pkg.Name()]
|
|
|
|
if exists {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
dt.Repo[pkg.Name()] = pkg
|
2018-03-01 15:30:16 +01:00
|
|
|
(*pkg).Provides().ForEach(func(dep alpm.Depend) (err error) {
|
2018-01-22 11:59:15 +01:00
|
|
|
dt.Repo[dep.Name] = pkg
|
|
|
|
return nil
|
2018-03-01 15:30:16 +01:00
|
|
|
})
|
2018-01-17 22:48:23 +01:00
|
|
|
|
|
|
|
(*pkg).Depends().ForEach(func(dep alpm.Depend) (err error) {
|
|
|
|
_, exists := dt.Repo[dep.Name]
|
|
|
|
if exists {
|
2017-08-07 15:43:25 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
_, isInstalled := localDb.PkgCache().FindSatisfier(dep.String())
|
|
|
|
if isInstalled == nil {
|
|
|
|
return
|
2018-01-20 23:37:10 +01:00
|
|
|
}
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
repoPkg, inRepos := syncDb.FindSatisfier(dep.String())
|
|
|
|
if inRepos == nil {
|
|
|
|
repoTreeRecursive(repoPkg, dt, localDb, syncDb)
|
|
|
|
return
|
2017-08-07 15:43:25 +02:00
|
|
|
}
|
2018-01-17 22:48:23 +01:00
|
|
|
|
2018-02-13 18:52:33 +01:00
|
|
|
dt.Missing.set(dep.String())
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
return
|
|
|
|
})
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func depTreeRecursive(dt *depTree, localDb *alpm.Db, syncDb alpm.DbList, isMake bool) (err error) {
|
2018-02-18 00:35:54 +01:00
|
|
|
if len(dt.ToProcess) == 0 {
|
|
|
|
return
|
|
|
|
}
|
2018-01-17 22:48:23 +01:00
|
|
|
|
2018-02-18 00:35:54 +01:00
|
|
|
nextProcess := make(stringSet)
|
|
|
|
currentProcess := make(stringSet)
|
2018-01-17 22:48:23 +01:00
|
|
|
//strip version conditions
|
2018-02-18 00:35:54 +01:00
|
|
|
for dep := range dt.ToProcess {
|
|
|
|
currentProcess.set(getNameFromDep(dep))
|
2018-01-17 22:48:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//assume toprocess only contains aur stuff we have not seen
|
2018-02-18 00:35:54 +01:00
|
|
|
info, err := aurInfo(currentProcess.toSlice())
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
//cache the results
|
|
|
|
for _, pkg := range info {
|
|
|
|
//copying to p fixes a bug
|
|
|
|
//would rather not copy but cant find another way to fix
|
|
|
|
p := pkg
|
|
|
|
dt.Aur[pkg.Name] = &p
|
|
|
|
|
2017-08-07 15:43:25 +02:00
|
|
|
}
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
//loop through to process and check if we now have
|
|
|
|
//each packaged cached
|
|
|
|
//if its not cached we assume its missing
|
2018-02-18 00:35:54 +01:00
|
|
|
for pkgName := range currentProcess {
|
2018-01-17 22:48:23 +01:00
|
|
|
pkg, exists := dt.Aur[pkgName]
|
|
|
|
|
2018-01-31 22:04:21 +01:00
|
|
|
//did not get it in the request
|
2018-01-17 22:48:23 +01:00
|
|
|
if !exists {
|
2018-02-18 00:35:54 +01:00
|
|
|
dt.Missing.set(pkgName)
|
2018-01-17 22:48:23 +01:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-02-18 00:35:54 +01:00
|
|
|
//for each dep and makedep
|
2018-02-19 18:53:03 +01:00
|
|
|
for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
|
2018-01-17 22:48:23 +01:00
|
|
|
for _, versionedDep := range deps {
|
|
|
|
dep := getNameFromDep(versionedDep)
|
|
|
|
|
|
|
|
_, exists = dt.Aur[dep]
|
|
|
|
//we have it cached so skip
|
|
|
|
if exists {
|
|
|
|
continue
|
|
|
|
}
|
2018-01-20 23:37:10 +01:00
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
_, exists = dt.Repo[dep]
|
|
|
|
//we have it cached so skip
|
|
|
|
if exists {
|
|
|
|
continue
|
|
|
|
}
|
2018-01-20 23:37:10 +01:00
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
_, exists = dt.Missing[dep]
|
2018-01-31 22:04:21 +01:00
|
|
|
//we know it does not resolve so skip
|
2018-01-17 22:48:23 +01:00
|
|
|
if exists {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
//check if already installed
|
|
|
|
_, isInstalled := localDb.PkgCache().FindSatisfier(versionedDep)
|
|
|
|
if isInstalled == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
//check the repos for a matching dep
|
|
|
|
repoPkg, inRepos := syncDb.FindSatisfier(versionedDep)
|
|
|
|
if inRepos == nil {
|
|
|
|
repoTreeRecursive(repoPkg, dt, localDb, syncDb)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-01-31 22:04:21 +01:00
|
|
|
//if all else fails add it to next search
|
2018-02-18 00:35:54 +01:00
|
|
|
nextProcess.set(versionedDep)
|
2018-01-17 22:48:23 +01:00
|
|
|
}
|
2017-08-07 15:43:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-17 22:48:23 +01:00
|
|
|
dt.ToProcess = nextProcess
|
|
|
|
depTreeRecursive(dt, localDb, syncDb, true)
|
2018-01-20 23:37:10 +01:00
|
|
|
|
2017-08-07 15:43:25 +02:00
|
|
|
return
|
|
|
|
}
|