From 76ad8e77c5a1b56ecd18eb8636350c7ee16baca3 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sat, 7 Feb 2026 14:52:14 +0200 Subject: [PATCH] Greatly simplify dependency resolution --- src/bpmlib/dependencies.go | 60 ++++++++++++++++++++++++++++++++++++++ src/bpmlib/general.go | 10 ++----- src/bpmlib/operations.go | 53 +++++++++++++++++++++++---------- 3 files changed, 99 insertions(+), 24 deletions(-) diff --git a/src/bpmlib/dependencies.go b/src/bpmlib/dependencies.go index 7c9bff2..38eb5d8 100644 --- a/src/bpmlib/dependencies.go +++ b/src/bpmlib/dependencies.go @@ -318,3 +318,63 @@ func (pkgInfo *PackageInfo) GetPackageOptionalDependants(rootDir string) (depend return dependants } + +type ResolvedPackage struct { + DatabaseEntry *BPMDatabaseEntry + InstallationReason InstallationReason +} + +func ResolveDependencies(pkgInfo *PackageInfo, resolvedVirtualPackages map[string]string, includeOptionalDepends bool, rootDir string) (resolved []ResolvedPackage, unresolved []string) { + visited := make([]string, 0) + + var dfs func(resolvedPkg *PackageInfo) + dfs = func(pkgInfo *PackageInfo) { + checkDependencies := func(dependencies []string, installationReason InstallationReason) { + for _, depend := range dependencies { + // Ignore if package is already installed + if IsPackageInstalled(depend, rootDir) { + continue + } else if providers := GetVirtualPackageInfo(depend, rootDir); len(providers) > 0 { + continue + } + + // Find database entry for dependency + var dependEntry *BPMDatabaseEntry + if resolvedVpkg, ok := resolvedVirtualPackages[depend]; ok { + dependEntry, _, _ = GetDatabaseEntry(resolvedVpkg) + } else if entry, _, _ := GetDatabaseEntry(depend); entry != nil { + dependEntry = entry + } else if providers := GetDatabaseVirtualPackageEntry(depend); len(providers) > 0 { + dependEntry = providers[0] + } + + if dependEntry == nil { + unresolved = append(unresolved, depend) + continue + } + + if !slices.Contains(visited, dependEntry.Info.Name) { + dfs(dependEntry.Info) + resolved = append(resolved, ResolvedPackage{DatabaseEntry: dependEntry, InstallationReason: installationReason}) + } + } + } + + visited = append(visited, pkgInfo.Name) + + checkDependencies(pkgInfo.Depends, InstallationReasonDependency) + if pkgInfo.Type == "binary" { + checkDependencies(pkgInfo.RuntimeDepends, InstallationReasonDependency) + } else if pkgInfo.Type == "source" { + checkDependencies(pkgInfo.MakeDepends, InstallationReasonMakeDependency) + checkDependencies(pkgInfo.CheckDepends, InstallationReasonMakeDependency) + } + if includeOptionalDepends { + checkDependencies(pkgInfo.OptionalDepends, InstallationReasonManual) + } + } + + dfs(pkgInfo) + + return resolved, unresolved +} diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index 4f149de..72001cc 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -132,10 +132,7 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason, } // Resolve dependencies - err = operation.ResolveDependencies(reinstallMethod == ReinstallMethodAll, installRuntimeDependencies, installOptionalDependencies, verbose) - if err != nil { - return nil, fmt.Errorf("could not resolve dependencies: %s", err) - } + operation.ResolveDependencies(reinstallMethod == ReinstallMethodAll, installOptionalDependencies) if len(operation.UnresolvedDepends) != 0 { if !forceInstallation { return nil, DependencyNotFoundErr{operation.UnresolvedDepends} @@ -431,10 +428,7 @@ func UpdatePackages(rootDir string, syncDatabase bool, allowDowngrades bool, ins } // Check for new dependencies in updated packages - err = operation.ResolveDependencies(false, true, installOptionalDependencies, verbose) - if err != nil { - return nil, fmt.Errorf("could not resolve dependencies: %s", err) - } + operation.ResolveDependencies(false, installOptionalDependencies) if len(operation.UnresolvedDepends) != 0 { if !forceInstallation { return nil, DependencyNotFoundErr{operation.UnresolvedDepends} diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index 70b5f3c..9563e13 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -131,9 +131,9 @@ func (operation *BPMOperation) GetFinalActionSize(rootDir string) int64 { return ret } -func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, installRuntimeDependencies, installOptionalDependencies, verbose bool) error { - pos := 0 - resolvedVirtualPkgs := make(map[string]string, 0) +func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, installOptionalDependencies bool) { + // Discover resolved virtual packages + resolvedVirtualPackages := make(map[string]string) for _, value := range slices.Clone(operation.Actions) { var pkgInfo *PackageInfo if value.GetActionType() == "install" { @@ -143,34 +143,55 @@ func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, instal action := value.(*FetchPackageAction) pkgInfo = action.DatabaseEntry.Info } else { - pos++ continue } - resolved, unresolved := ResolveAllPackageDependenciesFromDatabases(pkgInfo, resolvedVirtualPkgs, pkgInfo.Type == "source", pkgInfo.Type == "source" && operation.RunChecks, installRuntimeDependencies, installOptionalDependencies, !reinstallDependencies, verbose, operation.RootDir) + for _, vpkg := range pkgInfo.Provides { + if _, ok := resolvedVirtualPackages[vpkg]; !ok { + resolvedVirtualPackages[vpkg] = pkgInfo.Name + } + } + } + // Discover all dependencies + pos := 0 + for _, value := range slices.Clone(operation.Actions) { + var pkgInfo *PackageInfo + if value.GetActionType() == "install" { + action := value.(*InstallPackageAction) + pkgInfo = action.BpmPackage.PkgInfo + } else if value.GetActionType() == "fetch" { + action := value.(*FetchPackageAction) + pkgInfo = action.DatabaseEntry.Info + } else { + continue + } + + resolved, unresolved := ResolveDependencies(pkgInfo, resolvedVirtualPackages, installOptionalDependencies, operation.RootDir) + + // Append unresolved dependencies operation.UnresolvedDepends = append(operation.UnresolvedDepends, unresolved...) + operation.UnresolvedDepends = removeDuplicates(operation.UnresolvedDepends) for _, resolvedPkg := range resolved { - if !operation.ActionsContainPackage(resolvedPkg.PkgName) && resolvedPkg.PkgName != pkgInfo.Name { - if !reinstallDependencies && IsPackageInstalled(resolvedPkg.PkgName, operation.RootDir) { - continue - } - entry, _, err := GetDatabaseEntry(resolvedPkg.PkgName) - if err != nil { - return errors.New("could not get database entry for package (" + resolvedPkg.PkgName + ")") - } + if !operation.ActionsContainPackage(resolvedPkg.DatabaseEntry.Info.Name) && resolvedPkg.DatabaseEntry.Info.Name != pkgInfo.Name { operation.InsertActionAt(pos, &FetchPackageAction{ InstallationReason: resolvedPkg.InstallationReason, - DatabaseEntry: entry, + DatabaseEntry: resolvedPkg.DatabaseEntry, }) + + for _, vpkg := range resolvedPkg.DatabaseEntry.Info.Provides { + if _, ok := resolvedVirtualPackages[vpkg]; !ok { + resolvedVirtualPackages[vpkg] = resolvedPkg.DatabaseEntry.Info.Name + } + } + pos++ } } + pos++ } - - return nil } func (operation *BPMOperation) RemoveNeededPackages() error {