5 Commits
6 changed files with 65 additions and 24 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ ROOT_COMPILATION_GID ?= 65534
build:
mkdir -p build
cd src/bpm; $(GO) build -ldflags "-w -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationUID=$(ROOT_COMPILATION_UID)' -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationGID=$(ROOT_COMPILATION_GID)'" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm
cd src/bpm; $(GO) build $(GOFLAGS) -ldflags "-w -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationUID=$(ROOT_COMPILATION_UID)' -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationGID=$(ROOT_COMPILATION_GID)'" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm
install: build/bpm config/
# Create directory
+9 -3
View File
@@ -92,7 +92,6 @@ func main() {
currentFlagSet.BoolP("verbose", "v", false, "Show additional information about the current operation")
currentFlagSet.BoolP("force", "f", false, "Bypass warnings during package removal")
currentFlagSet.BoolP("yes", "y", false, "Enter 'yes' in all prompts")
currentFlagSet.BoolP("unused", "u", false, "Remove packages only if they are not required as dependencies")
currentFlagSet.BoolP("cleanup", "n", false, "Additionally remove all unused dependencies")
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Remove the specified packages", os.Args[2:])
@@ -496,7 +495,6 @@ func removePackages() {
verbose, _ := currentFlagSet.GetBool("verbose")
force, _ := currentFlagSet.GetBool("force")
yesAll, _ := currentFlagSet.GetBool("yes")
removeUnused, _ := currentFlagSet.GetBool("unused")
cleanupPackages, _ := currentFlagSet.GetBool("cleanup")
// Get packages
@@ -527,8 +525,16 @@ func removePackages() {
}
// Create remove operation
operation, err := bpmlib.RemovePackages(rootDir, removeUnused, cleanupPackages, packages...)
operation, err := bpmlib.RemovePackages(rootDir, force, cleanupPackages, packages...)
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
log.Printf("Error: %s", err)
exitCode = 1
return
} else if errors.As(err, &bpmlib.PackageRemovalDependencyErr{}) {
for pkg, dependants := range err.(bpmlib.PackageRemovalDependencyErr).RequiredPackages {
fmt.Printf("The following packages depend on package (%s): %s\n", pkg, strings.Join(dependants, ", "))
}
log.Printf("Error: %s", err)
exitCode = 1
return
+3 -3
View File
@@ -6,6 +6,7 @@ import (
"net/url"
"os"
"path"
"slices"
"strings"
"gopkg.in/yaml.v3"
@@ -217,9 +218,8 @@ func FindReplacement(pkg string) *BPMDatabaseEntry {
func ResolveVirtualPackage(vpkg string) *BPMDatabaseEntry {
for _, db := range BPMDatabases {
if v, ok := db.VirtualPackages[vpkg]; ok {
for _, pkg := range v {
return db.Entries[pkg]
}
slices.Sort(v)
return db.Entries[v[0]]
}
}
+4 -7
View File
@@ -198,12 +198,9 @@ func GetPackageDependants(pkgName string, rootDir string) ([]string, error) {
continue
}
// Get installed package dependencies
dependencies := installedPkg.PkgInfo.GetDependencies(false, true)
// Add installed package to list if its dependencies include pkgName
if slices.ContainsFunc(dependencies, func(p pkgInstallationReason) bool {
return p.PkgName == pkgName
if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool {
return n == pkgName
}) {
ret = append(ret, installedPkgName)
continue
@@ -212,8 +209,8 @@ func GetPackageDependants(pkgName string, rootDir string) ([]string, error) {
// Loop through each virtual package
for _, vpkg := range pkg.PkgInfo.Provides {
// Add installed package to list if its dependencies contain a provided virtual package
if slices.ContainsFunc(dependencies, func(p pkgInstallationReason) bool {
return p.PkgName == vpkg
if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool {
return n == vpkg
}) {
ret = append(ret, installedPkgName)
break
+8
View File
@@ -40,3 +40,11 @@ type PackageScriptErr struct {
func (e PackageScriptErr) Error() string {
return fmt.Sprintf("could not execute package script (%s) for package (%s): %s", e.packageScript, e.packageName, e.err)
}
type PackageRemovalDependencyErr struct {
RequiredPackages map[string][]string
}
func (e PackageRemovalDependencyErr) Error() string {
return "removing these package would break other installed packages"
}
+40 -10
View File
@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"log"
"maps"
"os"
"path"
"slices"
@@ -186,7 +187,7 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
}
// RemovePackages removes the specified packages from the given root directory
func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencies bool, packages ...string) (operation *BPMOperation, err error) {
func RemovePackages(rootDir string, force, cleanupDependencies bool, packages ...string) (operation *BPMOperation, err error) {
operation = &BPMOperation{
Actions: make([]OperationAction, 0),
UnresolvedDepends: make([]string, 0),
@@ -198,27 +199,56 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie
// Search for packages
for _, pkg := range packages {
bpmpkg := GetPackage(pkg, rootDir)
if isVirutal, vpkg := IsVirtualPackage(pkg, rootDir); isVirutal {
bpmpkg = GetPackage(vpkg, rootDir)
}
if bpmpkg == nil {
continue
}
operation.AppendAction(&RemovePackageAction{BpmPackage: bpmpkg})
}
// Do not remove packages which other packages depend on
if removeUnusedPackagesOnly {
err := operation.RemoveNeededPackages()
if err != nil {
return nil, fmt.Errorf("could not skip needed packages: %s", err)
}
}
// Do package cleanup
if cleanupDependencies {
err := operation.Cleanup(true)
err := operation.Cleanup(MainBPMConfig.CleanupMakeDependencies)
if err != nil {
return nil, fmt.Errorf("could not perform cleanup for operation: %s", err)
}
}
// Return error if other packages depend on removed ones
if !force {
// Get packages and their dependants
packageDepndants := make(map[string][]string, 0)
for _, action := range operation.Actions {
dependants, err := GetPackageDependants(action.(*RemovePackageAction).BpmPackage.PkgInfo.Name, rootDir)
if err != nil {
return nil, fmt.Errorf("could not get package dependants: %s", err)
}
packageDepndants[action.(*RemovePackageAction).BpmPackage.PkgInfo.Name] = dependants
}
// Remove dependant packages from map if they are to be removed by this operation
for pkg, required := range packageDepndants {
required = slices.DeleteFunc(required, func(pkgName string) bool {
_, ok := packageDepndants[pkgName]
return ok
})
packageDepndants[pkg] = required
}
// Remove empty keys from map
maps.DeleteFunc(packageDepndants, func(pkg string, required []string) bool {
return len(required) == 0
})
// Return error
if len(packageDepndants) != 0 {
return nil, PackageRemovalDependencyErr{RequiredPackages: packageDepndants}
}
}
return operation, nil
}