Compare commits

...

2 Commits

Author SHA1 Message Date
87c492a30c Improve virtual package handling 2025-04-04 21:40:21 +03:00
e94b2a8816 Add replaces field to package information 2025-03-15 11:20:33 +02:00
5 changed files with 139 additions and 50 deletions

42
main.go
View File

@ -105,9 +105,13 @@ func resolveCommand() {
var info *utils.PackageInfo var info *utils.PackageInfo
isFile := false isFile := false
if showRepoInfo { if showRepoInfo {
entry, _, err := utils.GetRepositoryEntry(pkg) var err error
var entry *utils.RepositoryEntry
entry, _, err = utils.GetRepositoryEntry(pkg)
if err != nil { if err != nil {
log.Fatalf("Error: could not find package (%s) in any repository\n", pkg) if entry = utils.ResolveVirtualPackage(pkg); entry == nil {
log.Fatalf("Error: could not find package (%s) in any repository\n", pkg)
}
} }
info = entry.Info info = entry.Info
} else if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() { } else if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() {
@ -118,7 +122,11 @@ func resolveCommand() {
info = bpmpkg.PkgInfo info = bpmpkg.PkgInfo
isFile = true isFile = true
} else { } else {
info = utils.GetPackageInfo(pkg, rootDir) if isVirtual, p := utils.IsVirtualPackage(pkg, rootDir); isVirtual {
info = utils.GetPackageInfo(p, rootDir)
} else {
info = utils.GetPackageInfo(pkg, rootDir)
}
} }
if info == nil { if info == nil {
log.Fatalf("Error: package (%s) is not installed\n", pkg) log.Fatalf("Error: package (%s) is not installed\n", pkg)
@ -236,8 +244,18 @@ func resolveCommand() {
BpmPackage: bpmpkg, BpmPackage: bpmpkg,
}) })
} else { } else {
entry, _, err := utils.GetRepositoryEntry(pkg) var entry *utils.RepositoryEntry
if err != nil {
if e, _, err := utils.GetRepositoryEntry(pkg); err == nil {
entry = e
} else if isVirtual, p := utils.IsVirtualPackage(pkg, rootDir); isVirtual {
entry, _, err = utils.GetRepositoryEntry(p)
if err != nil {
log.Fatalf("Error: could not find package (%s) in any repository\n", p)
}
} else if e := utils.ResolveVirtualPackage(pkg); e != nil {
entry = e
} else {
log.Fatalf("Error: could not find package (%s) in any repository\n", pkg) log.Fatalf("Error: could not find package (%s) in any repository\n", pkg)
} }
if !reinstall && utils.IsPackageInstalled(entry.Info.Name, rootDir) && utils.GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() { if !reinstall && utils.IsPackageInstalled(entry.Info.Name, rootDir) && utils.GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() {
@ -263,6 +281,9 @@ func resolveCommand() {
} }
} }
// Replace obsolete packages
operation.ReplaceObsoletePackages()
// Check for conflicts // Check for conflicts
conflicts, err := operation.CheckForConflicts() conflicts, err := operation.CheckForConflicts()
if err != nil { if err != nil {
@ -343,10 +364,14 @@ func resolveCommand() {
if slices.Contains(utils.BPMConfig.IgnorePackages, pkg) { if slices.Contains(utils.BPMConfig.IgnorePackages, pkg) {
continue continue
} }
entry, _, err := utils.GetRepositoryEntry(pkg) var entry *utils.RepositoryEntry
if err != nil { // Check if installed package can be replaced and install that instead
if e := utils.FindReplacement(pkg); e != nil {
entry = e
} else if entry, _, err = utils.GetRepositoryEntry(pkg); err != nil {
continue continue
} }
installedInfo := utils.GetPackageInfo(pkg, rootDir) installedInfo := utils.GetPackageInfo(pkg, rootDir)
if installedInfo == nil { if installedInfo == nil {
log.Fatalf("Error: could not get package info for (%s)\n", pkg) log.Fatalf("Error: could not get package info for (%s)\n", pkg)
@ -374,6 +399,9 @@ func resolveCommand() {
} }
} }
// Replace obsolete packages
operation.ReplaceObsoletePackages()
// Show operation summary // Show operation summary
operation.ShowOperationSummary() operation.ShowOperationSummary()

View File

@ -42,6 +42,7 @@ func ReadConfig() {
} }
for _, repo := range BPMConfig.Repositories { for _, repo := range BPMConfig.Repositories {
repo.Entries = make(map[string]*RepositoryEntry) repo.Entries = make(map[string]*RepositoryEntry)
repo.VirtualPackages = make(map[string][]string)
err := repo.ReadLocalDatabase() err := repo.ReadLocalDatabase()
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

View File

@ -228,6 +228,30 @@ func (operation *BPMOperation) Cleanup(verbose bool) error {
return nil return nil
} }
func (operation *BPMOperation) ReplaceObsoletePackages() {
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.RepositoryEntry.Info
} else {
continue
}
for _, r := range pkgInfo.Replaces {
if bpmpkg := GetPackage(r, operation.RootDir); bpmpkg != nil && !operation.ActionsContainPackage(bpmpkg.PkgInfo.Name) {
operation.InsertActionAt(0, &RemovePackageAction{
BpmPackage: bpmpkg,
})
}
}
}
}
func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error) { func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error) {
conflicts := make(map[string][]string) conflicts := make(map[string][]string)
installedPackages, err := GetInstalledPackages(operation.RootDir) installedPackages, err := GetInstalledPackages(operation.RootDir)
@ -256,9 +280,12 @@ func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error)
} else if value.GetActionType() == "remove" { } else if value.GetActionType() == "remove" {
action := value.(*RemovePackageAction) action := value.(*RemovePackageAction)
pkgInfo := action.BpmPackage.PkgInfo pkgInfo := action.BpmPackage.PkgInfo
slices.DeleteFunc(allPackages, func(info *PackageInfo) bool { for i := len(allPackages) - 1; i >= 0; i-- {
return info.Name == pkgInfo.Name info := allPackages[i]
}) if info.Name == pkgInfo.Name {
allPackages = append(allPackages[:i], allPackages[i+1:]...)
}
}
} }
} }

View File

@ -39,6 +39,7 @@ type PackageInfo struct {
MakeDepends []string `yaml:"make_depends,omitempty"` MakeDepends []string `yaml:"make_depends,omitempty"`
OptionalDepends []string `yaml:"optional_depends,omitempty"` OptionalDepends []string `yaml:"optional_depends,omitempty"`
Conflicts []string `yaml:"conflicts,omitempty"` Conflicts []string `yaml:"conflicts,omitempty"`
Replaces []string `yaml:"replaces,omitempty"`
Provides []string `yaml:"provides,omitempty"` Provides []string `yaml:"provides,omitempty"`
} }
@ -406,6 +407,7 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
MakeDepends: make([]string, 0), MakeDepends: make([]string, 0),
OptionalDepends: make([]string, 0), OptionalDepends: make([]string, 0),
Conflicts: make([]string, 0), Conflicts: make([]string, 0),
Replaces: make([]string, 0),
Provides: make([]string, 0), Provides: make([]string, 0),
} }
err := yaml.Unmarshal([]byte(contents), &pkgInfo) err := yaml.Unmarshal([]byte(contents), &pkgInfo)
@ -468,7 +470,7 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p
} }
appendArray("Conflicting packages", pkgInfo.Conflicts) appendArray("Conflicting packages", pkgInfo.Conflicts)
appendArray("Provided packages", pkgInfo.Provides) appendArray("Provided packages", pkgInfo.Provides)
appendArray("Replaces packages", pkgInfo.Replaces)
} }
ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir))) ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir)))
return strings.Join(ret, "\n") return strings.Join(ret, "\n")
@ -1338,15 +1340,19 @@ func (pkgInfo *PackageInfo) ResolveDependencies(resolved, unresolved *[]string,
*resolved = append(*resolved, depend) *resolved = append(*resolved, depend)
} }
continue continue
} else if ignoreInstalled && IsPackageInstalled(depend, rootDir) { } else if ignoreInstalled && IsPackageProvided(depend, rootDir) {
continue continue
} }
entry, _, err := GetRepositoryEntry(depend) var err error
var entry *RepositoryEntry
entry, _, err = GetRepositoryEntry(depend)
if err != nil { if err != nil {
if !slices.Contains(*unresolved, depend) { if entry = ResolveVirtualPackage(depend); entry == nil {
*unresolved = append(*unresolved, depend) if !slices.Contains(*unresolved, depend) {
*unresolved = append(*unresolved, depend)
}
continue
} }
continue
} }
entry.Info.ResolveDependencies(resolved, unresolved, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) entry.Info.ResolveDependencies(resolved, unresolved, checkMake, checkOptional, ignoreInstalled, verbose, rootDir)
} }
@ -1367,6 +1373,26 @@ func IsPackageInstalled(pkg, rootDir string) bool {
return true return true
} }
func IsVirtualPackage(pkg, rootDir string) (bool, string) {
pkgs, err := GetInstalledPackages(rootDir)
if err != nil {
return false, ""
}
for _, p := range pkgs {
if p == pkg {
return false, ""
}
i := GetPackageInfo(p, rootDir)
if i == nil {
continue
}
if slices.Contains(i.Provides, pkg) {
return true, p
}
}
return false, ""
}
func IsPackageProvided(pkg, rootDir string) bool { func IsPackageProvided(pkg, rootDir string) bool {
pkgs, err := GetInstalledPackages(rootDir) pkgs, err := GetInstalledPackages(rootDir)
if err != nil { if err != nil {

View File

@ -8,24 +8,23 @@ import (
"net/url" "net/url"
"os" "os"
"path" "path"
"sort"
"strings" "strings"
) )
type Repository struct { type Repository struct {
Name string `yaml:"name"` Name string `yaml:"name"`
Source string `yaml:"source"` Source string `yaml:"source"`
Disabled *bool `yaml:"disabled"` Disabled *bool `yaml:"disabled"`
Entries map[string]*RepositoryEntry Entries map[string]*RepositoryEntry
VirtualPackages map[string][]string
} }
type RepositoryEntry struct { type RepositoryEntry struct {
Info *PackageInfo `yaml:"info"` Info *PackageInfo `yaml:"info"`
Download string `yaml:"download"` Download string `yaml:"download"`
DownloadSize uint64 `yaml:"download_size"` DownloadSize uint64 `yaml:"download_size"`
InstalledSize uint64 `yaml:"installed_size"` InstalledSize uint64 `yaml:"installed_size"`
IsVirtualPackage bool `yaml:"-"` Repository *Repository
Repository *Repository
} }
func (repo *Repository) ContainsPackage(pkg string) bool { func (repo *Repository) ContainsPackage(pkg string) bool {
@ -44,8 +43,6 @@ func (repo *Repository) ReadLocalDatabase() error {
return err return err
} }
virtualPackages := make(map[string][]string)
data := string(bytes) data := string(bytes)
for _, b := range strings.Split(data, "---") { for _, b := range strings.Split(data, "---") {
entry := RepositoryEntry{ entry := RepositoryEntry{
@ -65,11 +62,10 @@ func (repo *Repository) ReadLocalDatabase() error {
Conflicts: make([]string, 0), Conflicts: make([]string, 0),
Provides: make([]string, 0), Provides: make([]string, 0),
}, },
Download: "", Download: "",
DownloadSize: 0, DownloadSize: 0,
InstalledSize: 0, InstalledSize: 0,
IsVirtualPackage: false, Repository: repo,
Repository: repo,
} }
err := yaml.Unmarshal([]byte(b), &entry) err := yaml.Unmarshal([]byte(b), &entry)
if err != nil { if err != nil {
@ -77,26 +73,11 @@ func (repo *Repository) ReadLocalDatabase() error {
} }
for _, p := range entry.Info.Provides { for _, p := range entry.Info.Provides {
virtualPackages[p] = append(virtualPackages[p], entry.Info.Name) repo.VirtualPackages[p] = append(repo.VirtualPackages[p], entry.Info.Name)
} }
repo.Entries[entry.Info.Name] = &entry repo.Entries[entry.Info.Name] = &entry
} }
for key, value := range virtualPackages {
if _, ok := repo.Entries[key]; ok {
continue
}
sort.Strings(value)
entry := RepositoryEntry{
Info: repo.Entries[value[0]].Info,
Download: repo.Entries[value[0]].Download,
DownloadSize: repo.Entries[value[0]].DownloadSize,
InstalledSize: repo.Entries[value[0]].InstalledSize,
IsVirtualPackage: true,
Repository: repo,
}
repo.Entries[key] = &entry
}
return nil return nil
} }
@ -167,6 +148,32 @@ func GetRepositoryEntry(str string) (*RepositoryEntry, *Repository, error) {
} }
} }
func FindReplacement(pkg string) *RepositoryEntry {
for _, repo := range BPMConfig.Repositories {
for _, entry := range repo.Entries {
for _, replaced := range entry.Info.Replaces {
if replaced == pkg {
return entry
}
}
}
}
return nil
}
func ResolveVirtualPackage(vpkg string) *RepositoryEntry {
for _, repo := range BPMConfig.Repositories {
if v, ok := repo.VirtualPackages[vpkg]; ok {
for _, pkg := range v {
return repo.Entries[pkg]
}
}
}
return nil
}
func (repo *Repository) FetchPackage(pkg string) (string, error) { func (repo *Repository) FetchPackage(pkg string) (string, error) {
if !repo.ContainsPackage(pkg) { if !repo.ContainsPackage(pkg) {
return "", errors.New("could not fetch package '" + pkg + "'") return "", errors.New("could not fetch package '" + pkg + "'")