Add dependency version requirement resolution

This commit is contained in:
2026-05-09 19:06:20 +03:00
parent a03c9ad9ce
commit a4e20b7d88
5 changed files with 172 additions and 35 deletions
+15 -2
View File
@@ -311,6 +311,7 @@ func (entry *BPMDatabaseEntry) GetEntryDependants() (dependants []string) {
// Add installed package to list if its dependencies include pkgName // Add installed package to list if its dependencies include pkgName
if slices.ContainsFunc(e.Info.Depends, func(n string) bool { if slices.ContainsFunc(e.Info.Depends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == entry.Info.Name return n == entry.Info.Name
}) { }) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], db.Name) dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], db.Name)
@@ -319,6 +320,7 @@ func (entry *BPMDatabaseEntry) GetEntryDependants() (dependants []string) {
// Add installed package to list if its runtime dependencies include pkgName // Add installed package to list if its runtime dependencies include pkgName
if slices.ContainsFunc(e.Info.RuntimeDepends, func(n string) bool { if slices.ContainsFunc(e.Info.RuntimeDepends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == entry.Info.Name return n == entry.Info.Name
}) { }) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], db.Name) dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], db.Name)
@@ -329,6 +331,7 @@ func (entry *BPMDatabaseEntry) GetEntryDependants() (dependants []string) {
for _, vpkg := range entry.Info.Provides { for _, vpkg := range entry.Info.Provides {
// Add installed package to list if its dependencies contain a provided virtual package // Add installed package to list if its dependencies contain a provided virtual package
if slices.ContainsFunc(e.Info.Depends, func(n string) bool { if slices.ContainsFunc(e.Info.Depends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == vpkg return n == vpkg
}) { }) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], db.Name) dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], db.Name)
@@ -337,6 +340,7 @@ func (entry *BPMDatabaseEntry) GetEntryDependants() (dependants []string) {
// Add installed package to list if its runtime dependencies contain a provided virtual package // Add installed package to list if its runtime dependencies contain a provided virtual package
if slices.ContainsFunc(e.Info.RuntimeDepends, func(n string) bool { if slices.ContainsFunc(e.Info.RuntimeDepends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == vpkg return n == vpkg
}) { }) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], db.Name) dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], db.Name)
@@ -370,7 +374,13 @@ func (entry *BPMDatabaseEntry) GetEntryOptionalDependants() (dependants []string
for _, db := range BPMDatabases { for _, db := range BPMDatabases {
for _, e := range db.Entries { for _, e := range db.Entries {
if slices.ContainsFunc(e.Info.OptionalDepends, func(n string) bool { if slices.ContainsFunc(e.Info.OptionalDepends, func(n string) bool {
return strings.SplitN(n, ":", 2)[0] == entry.Info.Name // Remove optional dependency comment
n = strings.SplitN(n, ":", 2)[0]
// Remove required version
n, _, _ = SplitPkgNameAndVersion(n)
return n == entry.Info.Name
}) { }) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], e.Database.Name) dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], e.Database.Name)
} }
@@ -400,7 +410,10 @@ func (entry *BPMDatabaseEntry) GetEntryMakeDependants() (dependants []string) {
dependantsMap := make(map[string][]string) dependantsMap := make(map[string][]string)
for _, db := range BPMDatabases { for _, db := range BPMDatabases {
for _, e := range db.Entries { for _, e := range db.Entries {
if slices.Contains(e.Info.MakeDepends, entry.Info.Name) { if slices.ContainsFunc(e.Info.MakeDepends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == entry.Info.Name
}) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], e.Database.Name) dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], e.Database.Name)
} }
} }
+91 -7
View File
@@ -21,6 +21,7 @@ func (pkgInfo *PackageInfo) GetPackageDependants(rootDir string, skipMultiplePro
// Add installed package to list if its dependencies include pkgName // Add installed package to list if its dependencies include pkgName
if slices.ContainsFunc(installedPkg.Depends, func(n string) bool { if slices.ContainsFunc(installedPkg.Depends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == pkgInfo.Name return n == pkgInfo.Name
}) { }) {
dependants = append(dependants, installedPkg.Name) dependants = append(dependants, installedPkg.Name)
@@ -29,6 +30,7 @@ func (pkgInfo *PackageInfo) GetPackageDependants(rootDir string, skipMultiplePro
// Add installed package to list if its runtime dependencies include pkgName // Add installed package to list if its runtime dependencies include pkgName
if slices.ContainsFunc(installedPkg.RuntimeDepends, func(n string) bool { if slices.ContainsFunc(installedPkg.RuntimeDepends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == pkgInfo.Name return n == pkgInfo.Name
}) { }) {
dependants = append(dependants, installedPkg.Name) dependants = append(dependants, installedPkg.Name)
@@ -43,6 +45,7 @@ func (pkgInfo *PackageInfo) GetPackageDependants(rootDir string, skipMultiplePro
// Add installed package to list if its dependencies contain a provided virtual package // Add installed package to list if its dependencies contain a provided virtual package
if slices.ContainsFunc(installedPkg.Depends, func(n string) bool { if slices.ContainsFunc(installedPkg.Depends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == vpkg return n == vpkg
}) { }) {
dependants = append(dependants, installedPkg.Name) dependants = append(dependants, installedPkg.Name)
@@ -51,6 +54,7 @@ func (pkgInfo *PackageInfo) GetPackageDependants(rootDir string, skipMultiplePro
// Add installed package to list if its runtime dependencies contain a provided virtual package // Add installed package to list if its runtime dependencies contain a provided virtual package
if slices.ContainsFunc(installedPkg.RuntimeDepends, func(n string) bool { if slices.ContainsFunc(installedPkg.RuntimeDepends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == vpkg return n == vpkg
}) { }) {
dependants = append(dependants, installedPkg.Name) dependants = append(dependants, installedPkg.Name)
@@ -78,7 +82,13 @@ func (pkgInfo *PackageInfo) GetPackageOptionalDependants(rootDir string) (depend
// Add installed package to list if its optional dependencies include pkgName // Add installed package to list if its optional dependencies include pkgName
if slices.ContainsFunc(installedPkg.OptionalDepends, func(n string) bool { if slices.ContainsFunc(installedPkg.OptionalDepends, func(n string) bool {
return strings.SplitN(n, ":", 2)[0] == pkgInfo.Name // Remove optional dependency comment
n = strings.SplitN(n, ":", 2)[0]
// Remove required version
n, _, _ = SplitPkgNameAndVersion(n)
return n == pkgInfo.Name
}) { }) {
dependants = append(dependants, installedPkg.Name) dependants = append(dependants, installedPkg.Name)
continue continue
@@ -88,7 +98,13 @@ func (pkgInfo *PackageInfo) GetPackageOptionalDependants(rootDir string) (depend
for _, vpkg := range pkgInfo.Provides { for _, vpkg := range pkgInfo.Provides {
// Add installed package to list if its optional dependencies contain a provided virtual package // Add installed package to list if its optional dependencies contain a provided virtual package
if slices.ContainsFunc(installedPkg.OptionalDepends, func(n string) bool { if slices.ContainsFunc(installedPkg.OptionalDepends, func(n string) bool {
return strings.SplitN(n, ":", 2)[0] == vpkg // Remove optional dependency comment
n = strings.SplitN(n, ":", 2)[0]
// Remove required version
n, _, _ = SplitPkgNameAndVersion(n)
return n == vpkg
}) { }) {
dependants = append(dependants, installedPkg.Name) dependants = append(dependants, installedPkg.Name)
break break
@@ -111,20 +127,23 @@ func ResolveDependencies(pkgInfo *PackageInfo, resolvedVirtualPackages map[strin
dfs = func(pkgInfo *PackageInfo) { dfs = func(pkgInfo *PackageInfo) {
checkDependencies := func(dependencies []string, installationReason InstallationReason) { checkDependencies := func(dependencies []string, installationReason InstallationReason) {
for _, depend := range dependencies { for _, depend := range dependencies {
// Split dependency name and required version
dependName, _, _ := SplitPkgNameAndVersion(depend)
// Ignore if package is already installed // Ignore if package is already installed
if IsPackageInstalled(depend, rootDir) { if IsPackageInstalled(dependName, rootDir) && EvaluateDependency(depend, GetPackageInfo(dependName, rootDir).Version) {
continue continue
} else if providers := GetVirtualPackageInfo(depend, rootDir); len(providers) > 0 { } else if providers := GetVirtualPackageInfo(dependName, rootDir); len(providers) > 0 {
continue continue
} }
// Find database entry for dependency // Find database entry for dependency
var dependEntry *BPMDatabaseEntry var dependEntry *BPMDatabaseEntry
if resolvedVpkg, ok := resolvedVirtualPackages[depend]; ok { if resolvedVpkg, ok := resolvedVirtualPackages[dependName]; ok {
dependEntry, _, _ = GetDatabaseEntry(resolvedVpkg) dependEntry, _, _ = GetDatabaseEntry(resolvedVpkg)
} else if entry, _, _ := GetDatabaseEntry(depend); entry != nil { } else if entry, _, _ := GetDatabaseEntry(dependName); entry != nil {
dependEntry = entry dependEntry = entry
} else if providers := GetDatabaseVirtualPackageEntry(depend); len(providers) > 0 { } else if providers := GetDatabaseVirtualPackageEntry(dependName); len(providers) > 0 {
dependEntry = providers[0] dependEntry = providers[0]
} }
@@ -133,6 +152,12 @@ func ResolveDependencies(pkgInfo *PackageInfo, resolvedVirtualPackages map[strin
continue continue
} }
// Ensure entry has required version
if !EvaluateDependency(depend, dependEntry.Info.Version) {
unresolved = append(unresolved, depend)
continue
}
// Skip ignored packages in config // Skip ignored packages in config
if slices.Contains(MainBPMConfig.IgnorePackages, dependEntry.Info.Name) { if slices.Contains(MainBPMConfig.IgnorePackages, dependEntry.Info.Name) {
continue continue
@@ -161,3 +186,62 @@ func ResolveDependencies(pkgInfo *PackageInfo, resolvedVirtualPackages map[strin
return resolved, unresolved return resolved, unresolved
} }
func SplitPkgNameAndVersion(pkg string) (string, string, string) {
if strings.Contains(pkg, ">=") {
pkgSplit := strings.SplitN(pkg, ">=", 2)
pkgName := pkgSplit[0]
pkgVersion := pkgSplit[1]
return pkgName, ">=", pkgVersion
} else if strings.Contains(pkg, ">") {
pkgSplit := strings.SplitN(pkg, ">", 2)
pkgName := pkgSplit[0]
pkgVersion := pkgSplit[1]
return pkgName, ">", pkgVersion
} else if strings.Contains(pkg, "<=") {
pkgSplit := strings.SplitN(pkg, "<=", 2)
pkgName := pkgSplit[0]
pkgVersion := pkgSplit[1]
return pkgName, "<=", pkgVersion
} else if strings.Contains(pkg, "<") {
pkgSplit := strings.SplitN(pkg, "<", 2)
pkgName := pkgSplit[0]
pkgVersion := pkgSplit[1]
return pkgName, "<", pkgVersion
} else if strings.Contains(pkg, "=") {
pkgSplit := strings.SplitN(pkg, "=", 2)
pkgName := pkgSplit[0]
pkgVersion := pkgSplit[1]
return pkgName, "=", pkgVersion
}
return pkg, "", ""
}
func EvaluateDependency(pkg, matchVersion string) bool {
_, comparisonSymbol, pkgVersion := SplitPkgNameAndVersion(pkg)
switch comparisonSymbol {
case ">=":
return CompareVersions(matchVersion, pkgVersion) >= 0
case ">":
return CompareVersions(matchVersion, pkgVersion) > 0
case "<=":
return CompareVersions(matchVersion, pkgVersion) <= 0
case "<":
return CompareVersions(matchVersion, pkgVersion) < 0
case "=":
if cutPkgVersion, ok := strings.CutSuffix(pkgVersion, "*"); ok {
return strings.HasPrefix(matchVersion, cutPkgVersion)
} else {
return CompareVersions(matchVersion, pkgVersion) == 0
}
default:
return true
}
}
+50 -22
View File
@@ -81,22 +81,31 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
BpmPackage: bpmpkg, BpmPackage: bpmpkg,
}) })
} else { } else {
// Split package name and required version
pkgName, _, _ := SplitPkgNameAndVersion(pkg)
var entry *BPMDatabaseEntry var entry *BPMDatabaseEntry
if e, _, err := GetDatabaseEntry(pkg); err == nil { if e, _, err := GetDatabaseEntry(pkgName); err == nil {
entry = e entry = e
} else if providers := GetVirtualPackageInfo(pkg, rootDir); len(providers) > 0 { } else if providers := GetVirtualPackageInfo(pkgName, rootDir); len(providers) > 0 {
entry, _, err = GetDatabaseEntry(providers[0].Name) entry, _, err = GetDatabaseEntry(providers[0].Name)
if err != nil { if err != nil {
pkgsNotFound = append(pkgsNotFound, pkg) pkgsNotFound = append(pkgsNotFound, pkg)
continue continue
} }
} else if providers := GetDatabaseVirtualPackageEntry(pkg); len(providers) > 0 { } else if providers := GetDatabaseVirtualPackageEntry(pkgName); len(providers) > 0 {
entry = providers[0] entry = providers[0]
} else { } else {
pkgsNotFound = append(pkgsNotFound, pkg) pkgsNotFound = append(pkgsNotFound, pkg)
continue continue
} }
if !EvaluateDependency(pkg, entry.Info.Version) {
pkgsNotFound = append(pkgsNotFound, pkg)
continue
}
if !reinstallPackages && IsPackageInstalled(entry.Info.Name, rootDir) && GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() { if !reinstallPackages && IsPackageInstalled(entry.Info.Name, rootDir) && GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() {
continue continue
} }
@@ -419,72 +428,91 @@ func UpdatePackages(rootDir string, syncDatabase, allowDowngrades, forceInstalla
} }
// Check for missing dependencies // Check for missing dependencies
for _, dep := range entry.Info.Depends { for _, depend := range entry.Info.Depends {
if IsPackageInstalled(dep, rootDir) { // Split package name and required version
dependName, _, _ := SplitPkgNameAndVersion(depend)
if IsPackageInstalled(dependName, rootDir) && EvaluateDependency(depend, GetPackageInfo(dependName, rootDir).Version) {
continue continue
} }
if len(GetVirtualPackageInfo(dep, rootDir)) > 0 { if len(GetVirtualPackageInfo(dependName, rootDir)) > 0 {
continue continue
} }
// Find database entry for missing dependency // Find database entry for missing dependency
depEntry, _, err := GetDatabaseEntry(dep) dependEntry, _, err := GetDatabaseEntry(dependName)
if err != nil { if err != nil {
providers := GetDatabaseVirtualPackageEntry(dep) providers := GetDatabaseVirtualPackageEntry(dependName)
if len(providers) == 0 { if len(providers) == 0 {
pkgsNotFound = append(pkgsNotFound, dep) pkgsNotFound = append(pkgsNotFound, depend)
continue continue
} }
depEntry = providers[0] dependEntry = providers[0]
} }
// Skip dependency if action already exists // Skip dependency if action already exists
if operation.ActionsContainPackage(depEntry.Info.Name) { if operation.ActionsContainPackage(dependEntry.Info.Name) {
continue continue
} }
// Skip dependency if ignored in config // Skip dependency if ignored in config
if slices.Contains(MainBPMConfig.IgnorePackages, depEntry.Info.Name) { if slices.Contains(MainBPMConfig.IgnorePackages, dependEntry.Info.Name) {
continue
}
// Ensure entry has required version
if !EvaluateDependency(depend, dependEntry.Info.Version) {
pkgsNotFound = append(pkgsNotFound, depend)
continue continue
} }
// Fetch dependency // Fetch dependency
operation.AppendAction(&FetchPackageAction{ operation.AppendAction(&FetchPackageAction{
InstallationReason: InstallationReasonDependency, InstallationReason: InstallationReasonDependency,
DatabaseEntry: depEntry, DatabaseEntry: dependEntry,
}) })
} }
// Check for missing runtime dependencies // Check for missing runtime dependencies
for _, dep := range entry.Info.RuntimeDepends { for _, depend := range entry.Info.RuntimeDepends {
if IsPackageInstalled(dep, rootDir) { // Split package name and required version
dependName, _, _ := SplitPkgNameAndVersion(depend)
if IsPackageInstalled(dependName, rootDir) && EvaluateDependency(depend, GetPackageInfo(dependName, rootDir).Version) {
continue continue
} }
if len(GetVirtualPackageInfo(dep, rootDir)) > 0 { if len(GetVirtualPackageInfo(dependName, rootDir)) > 0 {
continue continue
} }
// Find database entry for missing dependency // Find database entry for missing dependency
depEntry, _, err := GetDatabaseEntry(dep) dependEntry, _, err := GetDatabaseEntry(dependName)
if err != nil { if err != nil {
providers := GetDatabaseVirtualPackageEntry(dep) providers := GetDatabaseVirtualPackageEntry(dependName)
if len(providers) == 0 { if len(providers) == 0 {
pkgsNotFound = append(pkgsNotFound, dep) pkgsNotFound = append(pkgsNotFound, depend)
continue continue
} }
depEntry = providers[0] dependEntry = providers[0]
} }
// Skip dependency if ignored in config // Skip dependency if ignored in config
if slices.Contains(MainBPMConfig.IgnorePackages, depEntry.Info.Name) { if slices.Contains(MainBPMConfig.IgnorePackages, dependEntry.Info.Name) {
continue
}
// Ensure entry has required version
if !EvaluateDependency(depend, dependEntry.Info.Version) {
pkgsNotFound = append(pkgsNotFound, depend)
continue continue
} }
// Fetch dependency // Fetch dependency
operation.AppendAction(&FetchPackageAction{ operation.AppendAction(&FetchPackageAction{
InstallationReason: InstallationReasonDependency, InstallationReason: InstallationReasonDependency,
DatabaseEntry: depEntry, DatabaseEntry: dependEntry,
}) })
} }
} }
+2 -1
View File
@@ -3,7 +3,6 @@ package bpmlib
import ( import (
"errors" "errors"
"fmt" "fmt"
"gopkg.in/yaml.v3"
"os" "os"
"os/exec" "os/exec"
"path" "path"
@@ -11,6 +10,8 @@ import (
"slices" "slices"
"strings" "strings"
"syscall" "syscall"
"gopkg.in/yaml.v3"
) )
type BPMHook struct { type BPMHook struct {
+14 -3
View File
@@ -250,6 +250,9 @@ func (operation *BPMOperation) Cleanup(cleanupMakeDepends bool) error {
// Loop through all dependencies // Loop through all dependencies
for _, depend := range depends { for _, depend := range depends {
// Remove required version
depend, _, _ = SplitPkgNameAndVersion(depend)
// Resolve dependency // Resolve dependency
var dependPkgInfo *PackageInfo var dependPkgInfo *PackageInfo
if providers := GetVirtualPackageInfo(depend, operation.RootDir); len(providers) > 0 { if providers := GetVirtualPackageInfo(depend, operation.RootDir); len(providers) > 0 {
@@ -516,16 +519,24 @@ func (operation *BPMOperation) GetOptionalDependencies() (optionalDepends map[st
} }
for _, depend := range pkgInfo.OptionalDepends { for _, depend := range pkgInfo.OptionalDepends {
// Get optional dependency name
dependSplit := strings.SplitN(depend, ":", 2) dependSplit := strings.SplitN(depend, ":", 2)
dependName, _, _ := SplitPkgNameAndVersion(dependSplit[0])
// Skip if dependency is already installed // Skip if dependency is already installed
if IsPackageInstalled(dependSplit[0], operation.RootDir) { if IsPackageInstalled(dependName, operation.RootDir) {
continue continue
} }
// Skip if not a new dependency of the package // Skip if not a new dependency of the package
if installedPkg := GetPackage(pkgInfo.Name, operation.RootDir); installedPkg != nil && slices.ContainsFunc(installedPkg.PkgInfo.OptionalDepends, func(n string) bool { if installedPkg := GetPackage(pkgInfo.Name, operation.RootDir); installedPkg != nil && slices.ContainsFunc(installedPkg.PkgInfo.OptionalDepends, func(n string) bool {
return strings.SplitN(n, ":", 2)[0] == dependSplit[0] // Remove optional dependency comment
n = strings.SplitN(n, ":", 2)[0]
// Remove required version
n, _, _ = SplitPkgNameAndVersion(n)
return n == dependName
}) { }) {
continue continue
} }
@@ -533,7 +544,7 @@ func (operation *BPMOperation) GetOptionalDependencies() (optionalDepends map[st
if len(dependSplit) == 2 { if len(dependSplit) == 2 {
optionalDepends[pkgInfo.Name] = append(optionalDepends[pkgInfo.Name], fmt.Sprintf("%s (%s)", dependSplit[0], dependSplit[1])) optionalDepends[pkgInfo.Name] = append(optionalDepends[pkgInfo.Name], fmt.Sprintf("%s (%s)", dependSplit[0], dependSplit[1]))
} else { } else {
optionalDepends[pkgInfo.Name] = append(optionalDepends[pkgInfo.Name], dependSplit[0]) optionalDepends[pkgInfo.Name] = append(optionalDepends[pkgInfo.Name], dependName)
} }
} }
} }