Merge pull request #275 from ratorx/master

Recursively remove dependencies when using yay -Yc
This commit is contained in:
Morgana 2018-04-09 17:01:00 +01:00 committed by GitHub
commit 13ef6f66ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 18 deletions

2
Gopkg.lock generated
View file

@ -5,7 +5,7 @@
branch = "master"
name = "github.com/jguer/go-alpm"
packages = ["."]
revision = "ec031c9cd5f6050edc3c2f23df2bff3bbb9511cc"
revision = "bc954af9b2ced79e4db54ce6ab3c6a24d769e98b"
[[projects]]
branch = "master"

View file

@ -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
}

4
cmd.go
View file

@ -323,8 +323,10 @@ func handleYay() (err error) {
//_, options, targets := cmdArgs.formatArgs()
if cmdArgs.existsArg("gendb") {
err = createDevelDB()
} 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()
}

View file

@ -313,26 +313,91 @@ 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
provides := make(map[string]stringSet)
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 {
addMapStringSet(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 pset, ok2 := provides[dep.Name]; ok2 {
for p := range pset {
if safePackages[p] == 0 {
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
}

View file

@ -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,