mirror of
https://github.com/EnumeratedDev/bpm.git
synced 2026-09-16 02:26:12 +00:00
248 lines
7.1 KiB
Go
248 lines
7.1 KiB
Go
package bpmlib
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
func (pkgInfo *PackageInfo) GetPackageDependants(rootDir string, skipMultipleProviders bool) (dependants []string) {
|
|
// Get installed package names
|
|
pkgs, ok := localPackageInformation[rootDir]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
// Loop through all installed packages
|
|
for _, installedPkg := range pkgs {
|
|
// Skip iteration if comparing the same packages
|
|
if installedPkg.Name == pkgInfo.Name {
|
|
continue
|
|
}
|
|
|
|
// Add installed package to list if its dependencies include pkgName
|
|
if slices.ContainsFunc(installedPkg.Depends, func(n string) bool {
|
|
n, _, _ = SplitPkgNameAndVersion(n)
|
|
return n == pkgInfo.Name
|
|
}) {
|
|
dependants = append(dependants, installedPkg.Name)
|
|
continue
|
|
}
|
|
|
|
// Add installed package to list if its runtime dependencies include pkgName
|
|
if slices.ContainsFunc(installedPkg.RuntimeDepends, func(n string) bool {
|
|
n, _, _ = SplitPkgNameAndVersion(n)
|
|
return n == pkgInfo.Name
|
|
}) {
|
|
dependants = append(dependants, installedPkg.Name)
|
|
continue
|
|
}
|
|
|
|
// Loop through each virtual package
|
|
for _, vpkg := range pkgInfo.Provides {
|
|
if skipMultipleProviders && len(GetVirtualPackageInfo(vpkg, rootDir)) > 1 {
|
|
continue
|
|
}
|
|
|
|
// Add installed package to list if its dependencies contain a provided virtual package
|
|
if slices.ContainsFunc(installedPkg.Depends, func(n string) bool {
|
|
n, _, _ = SplitPkgNameAndVersion(n)
|
|
return n == vpkg
|
|
}) {
|
|
dependants = append(dependants, installedPkg.Name)
|
|
break
|
|
}
|
|
|
|
// Add installed package to list if its runtime dependencies contain a provided virtual package
|
|
if slices.ContainsFunc(installedPkg.RuntimeDepends, func(n string) bool {
|
|
n, _, _ = SplitPkgNameAndVersion(n)
|
|
return n == vpkg
|
|
}) {
|
|
dependants = append(dependants, installedPkg.Name)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return dependants
|
|
}
|
|
|
|
func (pkgInfo *PackageInfo) GetPackageOptionalDependants(rootDir string) (dependants []string) {
|
|
// Get installed package names
|
|
pkgs, ok := localPackageInformation[rootDir]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
// Loop through all installed packages
|
|
for _, installedPkg := range pkgs {
|
|
// Skip iteration if comparing the same packages
|
|
if installedPkg.Name == pkgInfo.Name {
|
|
continue
|
|
}
|
|
|
|
// Add installed package to list if its optional dependencies include pkgName
|
|
if slices.ContainsFunc(installedPkg.OptionalDepends, func(n string) bool {
|
|
// 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)
|
|
continue
|
|
}
|
|
|
|
// Loop through each virtual package
|
|
for _, vpkg := range pkgInfo.Provides {
|
|
// Add installed package to list if its optional dependencies contain a provided virtual package
|
|
if slices.ContainsFunc(installedPkg.OptionalDepends, func(n string) bool {
|
|
// Remove optional dependency comment
|
|
n = strings.SplitN(n, ":", 2)[0]
|
|
|
|
// Remove required version
|
|
n, _, _ = SplitPkgNameAndVersion(n)
|
|
|
|
return n == vpkg
|
|
}) {
|
|
dependants = append(dependants, installedPkg.Name)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return dependants
|
|
}
|
|
|
|
type ResolvedPackage struct {
|
|
DatabaseEntry *BPMDatabaseEntry
|
|
InstallationReason InstallationReason
|
|
}
|
|
|
|
func ResolveDependencies(pkgInfo *PackageInfo, resolvedVirtualPackages map[string]string, includeRuntimeDepends 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 {
|
|
// Split dependency name and required version
|
|
dependName, _, _ := SplitPkgNameAndVersion(depend)
|
|
|
|
// Ignore if package is already installed
|
|
if IsPackageInstalled(dependName, rootDir) && EvaluateDependency(depend, GetPackageInfo(dependName, rootDir).Version) {
|
|
continue
|
|
} else if providers := GetVirtualPackageInfo(dependName, rootDir); len(providers) > 0 {
|
|
continue
|
|
}
|
|
|
|
// Find database entry for dependency
|
|
var dependEntry *BPMDatabaseEntry
|
|
if resolvedVpkg, ok := resolvedVirtualPackages[dependName]; ok {
|
|
dependEntry, _, _ = GetDatabaseEntry(resolvedVpkg)
|
|
} else if entry, _, _ := GetDatabaseEntry(dependName); entry != nil {
|
|
dependEntry = entry
|
|
} else if providers := GetDatabaseVirtualPackageEntry(dependName); len(providers) > 0 {
|
|
dependEntry = providers[0]
|
|
}
|
|
|
|
if dependEntry == nil {
|
|
unresolved = append(unresolved, depend)
|
|
continue
|
|
}
|
|
|
|
// Ensure entry has required version
|
|
if !EvaluateDependency(depend, dependEntry.Info.Version) {
|
|
unresolved = append(unresolved, depend)
|
|
continue
|
|
}
|
|
|
|
// Skip ignored packages in config
|
|
if slices.Contains(MainBPMConfig.IgnorePackages, dependEntry.Info.Name) {
|
|
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 includeRuntimeDepends {
|
|
checkDependencies(pkgInfo.RuntimeDepends, InstallationReasonDependency)
|
|
}
|
|
if pkgInfo.Type == "source" {
|
|
checkDependencies(pkgInfo.MakeDepends, InstallationReasonMakeDependency)
|
|
checkDependencies(pkgInfo.CheckDepends, InstallationReasonMakeDependency)
|
|
}
|
|
}
|
|
|
|
dfs(pkgInfo)
|
|
|
|
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
|
|
}
|
|
}
|