From 8a0bade6cd5ee2cbda847dcac862838119f14013 Mon Sep 17 00:00:00 2001 From: Reeto Chatterjee Date: Fri, 23 Mar 2018 23:36:45 +0000 Subject: [PATCH 1/5] Update vendored go-alpm --- vendor/github.com/jguer/go-alpm/package.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/vendor/github.com/jguer/go-alpm/package.go b/vendor/github.com/jguer/go-alpm/package.go index dd342ccc..ef589dd0 100644 --- a/vendor/github.com/jguer/go-alpm/package.go +++ b/vendor/github.com/jguer/go-alpm/package.go @@ -282,6 +282,21 @@ func (pkg Package) ComputeRequiredBy() []string { return requiredby } +// ComputeOptionalFor returns the names of packages that optionally require the given package +func (pkg Package) ComputeOptionalFor() []string { + result := C.alpm_pkg_compute_optionalfor(pkg.pmpkg) + optionalfor := make([]string, 0) + for i := (*list)(unsafe.Pointer(result)); i != nil; i = i.Next { + defer C.free(unsafe.Pointer(i)) + if i.Data != nil { + defer C.free(unsafe.Pointer(i.Data)) + name := C.GoString((*C.char)(unsafe.Pointer(i.Data))) + optionalfor = append(optionalfor, name) + } + } + return optionalfor +} + // NewVersion checks if there is a new version of the package in the Synced DBs. func (pkg Package) NewVersion(l DbList) *Package { ptr := C.alpm_sync_newversion(pkg.pmpkg, From 1b704a869d57a606170a0e575295d96f7d32be02 Mon Sep 17 00:00:00 2001 From: Reeto Chatterjee Date: Fri, 23 Mar 2018 23:45:46 +0000 Subject: [PATCH 2/5] Add recursive removal of packages --- clean.go | 10 ++----- cmd.go | 4 ++- query.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 76 insertions(+), 17 deletions(-) diff --git a/clean.go b/clean.go index 3fb23355..4773004a 100644 --- a/clean.go +++ b/clean.go @@ -20,16 +20,13 @@ func removeVCSPackage(pkgs []string) { } // CleanDependencies removes all dangling dependencies in system -func cleanDependencies() error { - hanging, err := hangingPackages() +func cleanDependencies(removeOptional bool) error { + hanging, err := hangingPackages(removeOptional) if err != nil { return err } if len(hanging) != 0 { - if !continueTask("Confirm Removal?", "nN") { - return nil - } err = cleanRemove(hanging) } @@ -42,12 +39,9 @@ func cleanRemove(pkgNames []string) (err error) { return nil } - oldvalue := config.NoConfirm - config.NoConfirm = true arguments := makeArguments() arguments.addArg("R") arguments.addTarget(pkgNames...) err = passToPacman(arguments) - config.NoConfirm = oldvalue return err } diff --git a/cmd.go b/cmd.go index 526eabed..f6d5cf8a 100644 --- a/cmd.go +++ b/cmd.go @@ -291,8 +291,10 @@ func handleYay() (err error) { if err != nil { return } + } else if cmdArgs.existsDouble("c") { + err = cleanDependencies(true) } else if cmdArgs.existsArg("c", "clean") { - err = cleanDependencies() + err = cleanDependencies(false) } else if len(cmdArgs.targets) > 0 { err = handleYogurt() } diff --git a/query.go b/query.go index 80b2209c..75ef91c2 100644 --- a/query.go +++ b/query.go @@ -274,26 +274,89 @@ func packageSlices(toCheck []string) (aur []string, repo []string, err error) { // HangingPackages returns a list of packages installed as deps // and unneeded by the system -func hangingPackages() (hanging []string, err error) { +// removeOptional decides whether optional dependencies are counted or not +func hangingPackages(removeOptional bool) (hanging []string, err error) { localDb, err := alpmHandle.LocalDb() if err != nil { return } - f := func(pkg alpm.Package) error { - if pkg.Reason() != alpm.PkgReasonDepend { + // safePackages represents every package in the system in one of 3 states + // State = 0 - Remove package from the system + // State = 1 - Keep package in the system; need to iterate over dependencies + // State = 2 - Keep package and have iterated over dependencies + safePackages := make(map[string]uint8) + // provides stores a mapping from the provides name back to the original package name + // Assumption - multiple installed packages don't provide the same dependency + provides := make(map[string]string) + packages := localDb.PkgCache() + + // Mark explicit dependencies and enumerate the provides list + setupResources := func(pkg alpm.Package) error { + if pkg.Reason() == alpm.PkgReasonExplicit { + safePackages[pkg.Name()] = 1 + } else { + safePackages[pkg.Name()] = 0 + } + + pkg.Provides().ForEach(func(dep alpm.Depend) error { + provides[dep.Name] = pkg.Name() + return nil + }) + return nil + } + packages.ForEach(setupResources) + + iterateAgain := true + processDependencies := func(pkg alpm.Package) error { + if state, _ := safePackages[pkg.Name()]; state == 0 || state == 2 { return nil } - requiredby := pkg.ComputeRequiredBy() - if len(requiredby) == 0 { - hanging = append(hanging, pkg.Name()) - fmt.Println(pkg.Name() + ": " + magenta(human(pkg.ISize()))) + safePackages[pkg.Name()] = 2 + + // Update state for dependencies + markDependencies := func(dep alpm.Depend) error { + // Don't assume a dependency is installed + state, ok := safePackages[dep.Name] + if !ok { + // Check if dep is a provides rather than actual package name + if p, ok2 := provides[dep.Name]; ok2 { + iterateAgain = true + safePackages[p] = 1 + } + + return nil + } + + if state == 0 { + iterateAgain = true + safePackages[dep.Name] = 1 + } + return nil + } + + pkg.Depends().ForEach(markDependencies) + if !removeOptional { + pkg.OptionalDepends().ForEach(markDependencies) } return nil } - err = localDb.PkgCache().ForEach(f) + for iterateAgain { + iterateAgain = false + packages.ForEach(processDependencies) + } + + // Build list of packages to be removed + packages.ForEach(func(pkg alpm.Package) error { + if safePackages[pkg.Name()] == 0 { + hanging = append(hanging, pkg.Name()) + } + return nil + }) + + return } From 21df6b1d57e0b48dd655f989ac71fd7c74acccf0 Mon Sep 17 00:00:00 2001 From: Reeto Chatterjee Date: Sat, 24 Mar 2018 00:04:28 +0000 Subject: [PATCH 3/5] Update deps properly and lint --- Gopkg.lock | 2 +- query.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 626b49f3..45405dc4 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -5,7 +5,7 @@ branch = "master" name = "github.com/jguer/go-alpm" packages = ["."] - revision = "ec031c9cd5f6050edc3c2f23df2bff3bbb9511cc" + revision = "bc954af9b2ced79e4db54ce6ab3c6a24d769e98b" [[projects]] branch = "master" diff --git a/query.go b/query.go index 75ef91c2..759f94b4 100644 --- a/query.go +++ b/query.go @@ -356,7 +356,6 @@ func hangingPackages(removeOptional bool) (hanging []string, err error) { return nil }) - return } From 51f7b147775cf4be6f07a226e99755e0bbcaa2e9 Mon Sep 17 00:00:00 2001 From: Reeto Chatterjee Date: Sat, 24 Mar 2018 12:37:31 +0000 Subject: [PATCH 4/5] Remove assumption of unique provides --- query.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/query.go b/query.go index 759f94b4..dc69df29 100644 --- a/query.go +++ b/query.go @@ -287,8 +287,7 @@ func hangingPackages(removeOptional bool) (hanging []string, err error) { // State = 2 - Keep package and have iterated over dependencies safePackages := make(map[string]uint8) // provides stores a mapping from the provides name back to the original package name - // Assumption - multiple installed packages don't provide the same dependency - provides := make(map[string]string) + provides := make(map[string]stringSet) packages := localDb.PkgCache() // Mark explicit dependencies and enumerate the provides list @@ -300,7 +299,13 @@ func hangingPackages(removeOptional bool) (hanging []string, err error) { } pkg.Provides().ForEach(func(dep alpm.Depend) error { - provides[dep.Name] = pkg.Name() + if deps, ok := provides[dep.Name]; ok { + deps.set(pkg.Name()) + } else { + ss := make(stringSet) + ss.set(pkg.Name()) + provides[dep.Name] = ss + } return nil }) return nil @@ -321,9 +326,13 @@ func hangingPackages(removeOptional bool) (hanging []string, err error) { state, ok := safePackages[dep.Name] if !ok { // Check if dep is a provides rather than actual package name - if p, ok2 := provides[dep.Name]; ok2 { - iterateAgain = true - safePackages[p] = 1 + if pset, ok2 := provides[dep.Name]; ok2 { + for p := range pset { + if safePackages[p] == 0 { + iterateAgain = true + safePackages[p] = 1 + } + } } return nil From b103a34f3bee2d499e1da423d384271cec5b5d59 Mon Sep 17 00:00:00 2001 From: Reeto Chatterjee Date: Sat, 24 Mar 2018 18:31:55 +0000 Subject: [PATCH 5/5] Use exisiting util function --- query.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/query.go b/query.go index dc69df29..c9ab2234 100644 --- a/query.go +++ b/query.go @@ -299,13 +299,7 @@ func hangingPackages(removeOptional bool) (hanging []string, err error) { } pkg.Provides().ForEach(func(dep alpm.Depend) error { - if deps, ok := provides[dep.Name]; ok { - deps.set(pkg.Name()) - } else { - ss := make(stringSet) - ss.set(pkg.Name()) - provides[dep.Name] = ss - } + addMapStringSet(provides, dep.Name, pkg.Name()) return nil }) return nil