From 5f03a6a1ad9b5b1911bcabfde26591ba7e230077 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Tue, 13 May 2025 20:26:55 +0300 Subject: [PATCH] Redo dependency resolution and cache installed package information --- src/bpm/main.go | 4 +- src/bpmlib/dependencies.go | 164 ++++++++++++++++ src/bpmlib/general.go | 4 +- src/bpmlib/hooks.go | 2 +- src/bpmlib/installed_packages.go | 243 ++++++++++++++++++++++++ src/bpmlib/operations.go | 10 +- src/bpmlib/packages.go | 312 +------------------------------ 7 files changed, 420 insertions(+), 319 deletions(-) create mode 100644 src/bpmlib/dependencies.go create mode 100644 src/bpmlib/installed_packages.go diff --git a/src/bpm/main.go b/src/bpm/main.go index 88b581d..92b4dd8 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -547,7 +547,7 @@ func resolveCommand() { var pkgList []string for _, pkg := range pkgs { - if slices.ContainsFunc(bpmlib.GetPackageFiles(pkg, rootDir), func(entry *bpmlib.PackageFileEntry) bool { + if slices.ContainsFunc(bpmlib.GetPackage(pkg, rootDir).PkgFiles, func(entry *bpmlib.PackageFileEntry) bool { return entry.Path == absFile }) { pkgList = append(pkgList, pkg) @@ -593,7 +593,7 @@ func resolveCommand() { // Get direct runtime and make dependencies totalDepends := make([]string, 0) - for _, depend := range bpmpkg.PkgInfo.GetAllDependencies(true, false) { + for _, depend := range bpmpkg.PkgInfo.GetDependencies(true, false) { if !slices.Contains(totalDepends, depend) { totalDepends = append(totalDepends, depend) } diff --git a/src/bpmlib/dependencies.go b/src/bpmlib/dependencies.go new file mode 100644 index 0000000..d71a105 --- /dev/null +++ b/src/bpmlib/dependencies.go @@ -0,0 +1,164 @@ +package bpmlib + +import ( + "errors" + "fmt" + "slices" +) + +func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeOptionalDepends bool) []string { + allDepends := make([]string, 0) + allDepends = append(allDepends, pkgInfo.Depends...) + if includeMakeDepends { + allDepends = append(allDepends, pkgInfo.MakeDepends...) + } + if includeOptionalDepends { + allDepends = append(allDepends, pkgInfo.OptionalDepends...) + } + return allDepends +} + +func (pkgInfo *PackageInfo) GetAllDependencies(includeMakeDepends, includeOptionalDepends bool, rootDir string) (resolved []string) { + // Initialize slices + resolved = make([]string, 0) + unresolved := make([]string, 0) + + // Call unexported function + pkgInfo.getAllDependencies(&resolved, &unresolved, includeMakeDepends, includeOptionalDepends, rootDir) + + return resolved +} + +func (pkgInfo *PackageInfo) getAllDependencies(resolved *[]string, unresolved *[]string, includeMakeDepends, includeOptionalDepends bool, rootDir string) { + // Add current package name to unresolved slice + *unresolved = append(*unresolved, pkgInfo.Name) + + // Loop through all dependencies + for _, depend := range pkgInfo.GetDependencies(includeMakeDepends, includeOptionalDepends) { + if !slices.Contains(*resolved, depend) { + // Add current dependency to resolved slice when circular dependency is detected + if slices.Contains(*unresolved, depend) { + if !slices.Contains(*resolved, depend) { + *resolved = append(*resolved, depend) + } + continue + } + + var dependInfo *PackageInfo + + if isVirtual, p := IsVirtualPackage(depend, rootDir); isVirtual { + dependInfo = GetPackageInfo(p, rootDir) + } else { + dependInfo = GetPackageInfo(depend, rootDir) + } + + if dependInfo != nil { + dependInfo.getAllDependencies(resolved, unresolved, includeMakeDepends, includeOptionalDepends, rootDir) + } + } + } + if !slices.Contains(*resolved, pkgInfo.Name) { + *resolved = append(*resolved, pkgInfo.Name) + } + *unresolved = stringSliceRemove(*unresolved, pkgInfo.Name) +} + +func ResolvePackageDependenciesFromDatabases(pkgInfo *PackageInfo, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) (resolved []string, unresolved []string) { + // Initialize slices + resolved = make([]string, 0) + unresolved = make([]string, 0) + + // Call unexported function + resolvePackageDependenciesFromDatabase(&resolved, &unresolved, pkgInfo, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) + + return resolved, unresolved +} + +func resolvePackageDependenciesFromDatabase(resolved, unresolved *[]string, pkgInfo *PackageInfo, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) { + // Add current package name to unresolved slice + *unresolved = append(*unresolved, pkgInfo.Name) + + // Loop through all dependencies + for _, depend := range pkgInfo.GetDependencies(checkMake, checkOptional) { + if !slices.Contains(*resolved, depend) { + // Add current dependency to resolved slice when circular dependency is detected + if slices.Contains(*unresolved, depend) { + if verbose { + fmt.Printf("Circular dependency was detected (%s -> %s). Installing %s first\n", pkgInfo.Name, depend, depend) + } + if !slices.Contains(*resolved, depend) { + *resolved = append(*resolved, depend) + } + continue + } else if ignoreInstalled && IsPackageProvided(depend, rootDir) { + continue + } + var err error + var entry *RepositoryEntry + entry, _, err = GetRepositoryEntry(depend) + if err != nil { + if entry = ResolveVirtualPackage(depend); entry == nil { + if !slices.Contains(*unresolved, depend) { + *unresolved = append(*unresolved, depend) + } + continue + } + } + resolvePackageDependenciesFromDatabase(resolved, unresolved, entry.Info, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) + } + } + if !slices.Contains(*resolved, pkgInfo.Name) { + *resolved = append(*resolved, pkgInfo.Name) + } + *unresolved = stringSliceRemove(*unresolved, pkgInfo.Name) +} + +func GetPackageDependants(pkgName string, rootDir string) ([]string, error) { + ret := make([]string, 0) + + // Get BPM package + pkg := GetPackage(pkgName, rootDir) + if pkg == nil { + return nil, errors.New("package not found: " + pkgName) + } + + // Get installed package names + pkgs, err := GetInstalledPackages(rootDir) + if err != nil { + return nil, errors.New("could not get installed packages") + } + + // Loop through all installed packages + for _, installedPkgName := range pkgs { + // Get installed BPM package + installedPkg := GetPackage(installedPkgName, rootDir) + if installedPkg == nil { + return nil, errors.New("package not found: " + installedPkgName) + } + + // Skip iteration if comparing the same packages + if installedPkg.PkgInfo.Name == pkgName { + continue + } + + // Get installed package dependencies + dependencies := installedPkg.PkgInfo.GetDependencies(false, true) + + // Add installed package to list if its dependencies include pkgName + if slices.Contains(dependencies, pkgName) { + ret = append(ret, installedPkgName) + continue + } + + // 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.Contains(dependencies, vpkg) { + ret = append(ret, installedPkgName) + break + } + } + } + + return ret, nil +} diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index b63ec52..c41cac5 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -161,7 +161,7 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie // Do package cleanup if cleanupDependencies { - err := operation.Cleanup(verbose) + err := operation.Cleanup() if err != nil { return nil, fmt.Errorf("could not perform cleanup for operation: %s", err) } @@ -180,7 +180,7 @@ func CleanupPackages(rootDir string, verbose bool) (operation *BPMOperation, err } // Do package cleanup - err = operation.Cleanup(verbose) + err = operation.Cleanup() if err != nil { return nil, fmt.Errorf("could not perform cleanup for operation: %s", err) } diff --git a/src/bpmlib/hooks.go b/src/bpmlib/hooks.go index a9bbff4..cd966cd 100644 --- a/src/bpmlib/hooks.go +++ b/src/bpmlib/hooks.go @@ -99,7 +99,7 @@ func (hook *BPMHook) Execute(packageChanges map[string]string, verbose bool, roo // Get modified files slice modifiedFiles := make([]*PackageFileEntry, 0) for pkg := range packageChanges { - modifiedFiles = append(modifiedFiles, GetPackageFiles(pkg, rootDir)...) + modifiedFiles = append(modifiedFiles, GetPackage(pkg, rootDir).PkgFiles...) } // Check if any targets are met diff --git a/src/bpmlib/installed_packages.go b/src/bpmlib/installed_packages.go new file mode 100644 index 0000000..dc295f1 --- /dev/null +++ b/src/bpmlib/installed_packages.go @@ -0,0 +1,243 @@ +package bpmlib + +import ( + "errors" + "fmt" + "os" + "path" + "slices" + "strconv" + "strings" +) + +var localPackageInformation map[string]map[string]*BPMPackage = make(map[string]map[string]*BPMPackage) + +func initializeLocalPackageInformation(rootDir string) (err error) { + // Return if information is already initialized + if _, ok := localPackageInformation[rootDir]; ok { + return nil + } + + tempPackageInformation := make(map[string]*BPMPackage) + + // Get path to installed package information directory + installedDir := path.Join(rootDir, "var/lib/bpm/installed/") + + // Get directory content + items, err := os.ReadDir(installedDir) + if os.IsNotExist(err) { + localPackageInformation[rootDir] = make(map[string]*BPMPackage) + return nil + } + if err != nil { + return err + } + + // Loop through each subdirectory + for _, item := range items { + // Skip if not a directory + if !item.IsDir() { + continue + } + + // Read package info + infoData, err := os.ReadFile(path.Join(installedDir, item.Name(), "info")) + if err != nil { + return err + } + info, err := ReadPackageInfo(string(infoData)) + if err != nil { + return err + } + + // Read package files + files := getPackageFiles(info.Name, rootDir) + + // Add package to slice + tempPackageInformation[info.Name] = &BPMPackage{ + PkgInfo: info, + PkgFiles: files, + } + } + + localPackageInformation[rootDir] = tempPackageInformation + return nil +} + +func GetInstalledPackages(rootDir string) (ret []string, err error) { + // Initialize local package information + err = initializeLocalPackageInformation(rootDir) + if err != nil { + return nil, err + } + + // Loop through each package and add it to slice + for _, bpmpkg := range localPackageInformation[rootDir] { + ret = append(ret, bpmpkg.PkgInfo.Name) + } + + return ret, nil +} + +func IsPackageInstalled(pkg, rootDir string) bool { + // Initialize local package information + err := initializeLocalPackageInformation(rootDir) + if err != nil { + return false + } + + if _, ok := localPackageInformation[rootDir][pkg]; !ok { + return false + } + return true +} + +func GetPackageInfo(pkg string, rootDir string) *PackageInfo { + // Get BPM package + bpmpkg := GetPackage(pkg, rootDir) + + // Return nil if not found + if bpmpkg == nil { + return nil + } + + return bpmpkg.PkgInfo +} + +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 { + pkgs, err := GetInstalledPackages(rootDir) + if err != nil { + return false + } + for _, p := range pkgs { + if p == pkg { + return true + } + i := GetPackageInfo(p, rootDir) + if i == nil { + continue + } + if slices.Contains(i.Provides, pkg) { + return true + } + } + return false +} + +func GetPackage(pkg, rootDir string) *BPMPackage { + err := initializeLocalPackageInformation(rootDir) + if err != nil { + return nil + } + + bpmpkg := localPackageInformation[rootDir][pkg] + + return bpmpkg +} + +func GetAllPackageFiles(rootDir string, excludePackages ...string) (map[string][]*BPMPackage, error) { + ret := make(map[string][]*BPMPackage) + + pkgNames, err := GetInstalledPackages(rootDir) + if err != nil { + return nil, err + } + + for _, pkgName := range pkgNames { + if slices.Contains(excludePackages, pkgName) { + continue + } + bpmpkg := GetPackage(pkgName, rootDir) + if bpmpkg == nil { + return nil, errors.New(fmt.Sprintf("could not get BPM package (%s)", pkgName)) + } + for _, entry := range bpmpkg.PkgFiles { + if _, ok := ret[entry.Path]; ok { + ret[entry.Path] = append(ret[entry.Path], bpmpkg) + } else { + ret[entry.Path] = []*BPMPackage{bpmpkg} + } + } + } + + return ret, nil +} + +func getPackageFiles(pkg, rootDir string) []*PackageFileEntry { + var pkgFiles []*PackageFileEntry + installedDir := path.Join(rootDir, "var/lib/bpm/installed/") + pkgDir := path.Join(installedDir, pkg) + files := path.Join(pkgDir, "files") + if _, err := os.Stat(installedDir); os.IsNotExist(err) { + return nil + } + if _, err := os.Stat(pkgDir); os.IsNotExist(err) { + return nil + } + bs, err := os.ReadFile(files) + if err != nil { + return nil + } + + for _, line := range strings.Split(string(bs), "\n") { + if strings.TrimSpace(line) == "" { + continue + } + stringEntry := strings.Split(strings.TrimSpace(line), " ") + if len(stringEntry) < 5 { + pkgFiles = append(pkgFiles, &PackageFileEntry{ + Path: strings.TrimSuffix(line, "/"), + OctalPerms: 0, + UserID: 0, + GroupID: 0, + SizeInBytes: 0, + }) + continue + } + uid, err := strconv.ParseInt(stringEntry[len(stringEntry)-4], 0, 32) + if err != nil { + return nil + } + octalPerms, err := strconv.ParseInt(stringEntry[len(stringEntry)-3], 0, 32) + if err != nil { + return nil + } + gid, err := strconv.ParseInt(stringEntry[len(stringEntry)-2], 0, 32) + if err != nil { + return nil + } + size, err := strconv.ParseUint(stringEntry[len(stringEntry)-1], 0, 64) + if err != nil { + return nil + } + pkgFiles = append(pkgFiles, &PackageFileEntry{ + Path: strings.TrimSuffix(strings.Join(stringEntry[:len(stringEntry)-4], " "), "/"), + OctalPerms: uint32(octalPerms), + UserID: int(uid), + GroupID: int(gid), + SizeInBytes: size, + }) + } + + return pkgFiles +} diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index 0a3648b..0a50e41 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -139,7 +139,7 @@ func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, instal continue } - resolved, unresolved := pkgInfo.ResolveDependencies(&[]string{}, &[]string{}, pkgInfo.Type == "source", installOptionalDependencies, !reinstallDependencies, verbose, operation.RootDir) + resolved, unresolved := ResolvePackageDependenciesFromDatabases(pkgInfo, pkgInfo.Type == "source", installOptionalDependencies, !reinstallDependencies, verbose, operation.RootDir) operation.UnresolvedDepends = append(operation.UnresolvedDepends, unresolved...) @@ -174,7 +174,7 @@ func (operation *BPMOperation) RemoveNeededPackages() error { } for pkg, action := range removeActions { - dependants, err := action.BpmPackage.PkgInfo.GetDependants(operation.RootDir) + dependants, err := GetPackageDependants(action.BpmPackage.PkgInfo.Name, operation.RootDir) if err != nil { return errors.New("could not get dependant packages for package (" + pkg + ")") } @@ -192,7 +192,7 @@ func (operation *BPMOperation) RemoveNeededPackages() error { return nil } -func (operation *BPMOperation) Cleanup(verbose bool) error { +func (operation *BPMOperation) Cleanup() error { // Get all installed packages installedPackageNames, err := GetInstalledPackages(operation.RootDir) if err != nil { @@ -228,9 +228,9 @@ func (operation *BPMOperation) Cleanup(verbose bool) error { } keepPackages = append(keepPackages, pkg.Name) - resolved, _ := pkg.ResolveDependencies(&[]string{}, &[]string{}, false, true, false, verbose, operation.RootDir) + resolved := pkg.GetAllDependencies(false, true, operation.RootDir) for _, value := range resolved { - if !slices.Contains(keepPackages, value) && slices.Contains(installedPackageNames, value) { + if !slices.Contains(keepPackages, value) { keepPackages = append(keepPackages, value) } } diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index 503830b..6ea2b56 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -509,7 +509,7 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p appendArray("Make Dependencies", pkgInfo.MakeDepends) } appendArray("Optional dependencies", pkgInfo.OptionalDepends) - dependants, err := pkgInfo.GetDependants(rootDir) + dependants, err := GetPackageDependants(pkgInfo.Name, rootDir) if err == nil { appendArray("Dependant packages", dependants) } @@ -681,7 +681,7 @@ func installPackage(filename, rootDir string, verbose, force bool) error { // Check if package is installed and remove current files if packageInstalled { // Fetching and reversing package file entry list - fileEntries := GetPackageFiles(bpmpkg.PkgInfo.Name, rootDir) + fileEntries := GetPackage(bpmpkg.PkgInfo.Name, rootDir).PkgFiles sort.Slice(fileEntries, func(i, j int) bool { return fileEntries[i].Path < fileEntries[j].Path }) @@ -873,312 +873,6 @@ func installPackage(filename, rootDir string, verbose, force bool) error { return nil } -func (pkgInfo *PackageInfo) GetAllDependencies(checkMake, checkOptional bool) []string { - allDepends := make([]string, 0) - allDepends = append(allDepends, pkgInfo.Depends...) - if checkMake { - allDepends = append(allDepends, pkgInfo.MakeDepends...) - } - if checkOptional { - allDepends = append(allDepends, pkgInfo.OptionalDepends...) - } - return allDepends -} - -func (pkgInfo *PackageInfo) CheckDependencies(checkMake, checkOptional bool, rootDir string) []string { - var ret []string - for _, dependency := range pkgInfo.Depends { - if !IsPackageProvided(dependency, rootDir) { - ret = append(ret, dependency) - } - } - if checkMake { - for _, dependency := range pkgInfo.MakeDepends { - if !IsPackageProvided(dependency, rootDir) { - ret = append(ret, dependency) - } - } - } - if checkOptional { - for _, dependency := range pkgInfo.OptionalDepends { - if !IsPackageProvided(dependency, rootDir) { - ret = append(ret, dependency) - } - } - } - - return ret -} - -func (pkgInfo *PackageInfo) GetDependants(rootDir string) ([]string, error) { - ret := make([]string, 0) - - pkgs, err := GetInstalledPackages(rootDir) - if err != nil { - return nil, errors.New("could not get installed packages") - } - for _, pkg := range pkgs { - bpmpkg := GetPackage(pkg, rootDir) - if bpmpkg == nil { - return nil, errors.New("package not found: " + pkg) - } - if bpmpkg.PkgInfo.Name == pkgInfo.Name { - continue - } - - dependencies := bpmpkg.PkgInfo.GetAllDependencies(false, true) - - if slices.Contains(dependencies, pkgInfo.Name) { - ret = append(ret, pkg) - continue - } - for _, vpkg := range pkgInfo.Provides { - if slices.Contains(dependencies, vpkg) { - ret = append(ret, pkg) - break - } - } - } - - return ret, nil -} - -func (pkgInfo *PackageInfo) CheckConflicts(rootDir string) []string { - var ret []string - for _, conflict := range pkgInfo.Conflicts { - if IsPackageInstalled(conflict, rootDir) { - ret = append(ret, conflict) - } - } - return ret -} - -func (pkgInfo *PackageInfo) ResolveDependencies(resolved, unresolved *[]string, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) ([]string, []string) { - *unresolved = append(*unresolved, pkgInfo.Name) - for _, depend := range pkgInfo.GetAllDependencies(checkMake, checkOptional) { - depend = strings.TrimSpace(depend) - depend = strings.ToLower(depend) - if !slices.Contains(*resolved, depend) { - if slices.Contains(*unresolved, depend) { - if verbose { - fmt.Printf("Circular dependency was detected (%s -> %s). Installing %s first\n", pkgInfo.Name, depend, depend) - } - if !slices.Contains(*resolved, depend) { - *resolved = append(*resolved, depend) - } - continue - } else if ignoreInstalled && IsPackageProvided(depend, rootDir) { - continue - } - var err error - var entry *RepositoryEntry - entry, _, err = GetRepositoryEntry(depend) - if err != nil { - if entry = ResolveVirtualPackage(depend); entry == nil { - if !slices.Contains(*unresolved, depend) { - *unresolved = append(*unresolved, depend) - } - continue - } - } - entry.Info.ResolveDependencies(resolved, unresolved, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) - } - } - if !slices.Contains(*resolved, pkgInfo.Name) { - *resolved = append(*resolved, pkgInfo.Name) - } - *unresolved = stringSliceRemove(*unresolved, pkgInfo.Name) - return *resolved, *unresolved -} - -func IsPackageInstalled(pkg, rootDir string) bool { - installedDir := path.Join(rootDir, "var/lib/bpm/installed/") - pkgDir := path.Join(installedDir, pkg) - if _, err := os.Stat(pkgDir); err != nil { - return false - } - 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 { - pkgs, err := GetInstalledPackages(rootDir) - if err != nil { - return false - } - for _, p := range pkgs { - if p == pkg { - return true - } - i := GetPackageInfo(p, rootDir) - if i == nil { - continue - } - if slices.Contains(i.Provides, pkg) { - return true - } - } - return false -} - -func GetInstalledPackages(rootDir string) ([]string, error) { - installedDir := path.Join(rootDir, "var/lib/bpm/installed/") - items, err := os.ReadDir(installedDir) - if os.IsNotExist(err) { - return nil, nil - } - if err != nil { - return nil, err - } - var ret []string - for _, item := range items { - ret = append(ret, item.Name()) - } - return ret, nil -} - -func GetPackageFiles(pkg, rootDir string) []*PackageFileEntry { - var pkgFiles []*PackageFileEntry - installedDir := path.Join(rootDir, "var/lib/bpm/installed/") - pkgDir := path.Join(installedDir, pkg) - files := path.Join(pkgDir, "files") - if _, err := os.Stat(installedDir); os.IsNotExist(err) { - return nil - } - if _, err := os.Stat(pkgDir); os.IsNotExist(err) { - return nil - } - bs, err := os.ReadFile(files) - if err != nil { - return nil - } - - for _, line := range strings.Split(string(bs), "\n") { - if strings.TrimSpace(line) == "" { - continue - } - stringEntry := strings.Split(strings.TrimSpace(line), " ") - if len(stringEntry) < 5 { - pkgFiles = append(pkgFiles, &PackageFileEntry{ - Path: strings.TrimSuffix(line, "/"), - OctalPerms: 0, - UserID: 0, - GroupID: 0, - SizeInBytes: 0, - }) - continue - } - uid, err := strconv.ParseInt(stringEntry[len(stringEntry)-4], 0, 32) - if err != nil { - return nil - } - octalPerms, err := strconv.ParseInt(stringEntry[len(stringEntry)-3], 0, 32) - if err != nil { - return nil - } - gid, err := strconv.ParseInt(stringEntry[len(stringEntry)-2], 0, 32) - if err != nil { - return nil - } - size, err := strconv.ParseUint(stringEntry[len(stringEntry)-1], 0, 64) - if err != nil { - return nil - } - pkgFiles = append(pkgFiles, &PackageFileEntry{ - Path: strings.TrimSuffix(strings.Join(stringEntry[:len(stringEntry)-4], " "), "/"), - OctalPerms: uint32(octalPerms), - UserID: int(uid), - GroupID: int(gid), - SizeInBytes: size, - }) - } - - return pkgFiles -} - -func GetPackageInfo(pkg, rootDir string) *PackageInfo { - installedDir := path.Join(rootDir, "var/lib/bpm/installed/") - pkgDir := path.Join(installedDir, pkg) - files := path.Join(pkgDir, "info") - if _, err := os.Stat(installedDir); os.IsNotExist(err) { - return nil - } - if _, err := os.Stat(pkgDir); os.IsNotExist(err) { - return nil - } - file, err := os.Open(files) - if err != nil { - return nil - } - bs, err := io.ReadAll(file) - if err != nil { - return nil - } - info, err := ReadPackageInfo(string(bs)) - if err != nil { - return nil - } - return info -} - -func GetPackage(pkg, rootDir string) *BPMPackage { - if !IsPackageInstalled(pkg, rootDir) { - return nil - } - - return &BPMPackage{ - PkgInfo: GetPackageInfo(pkg, rootDir), - PkgFiles: GetPackageFiles(pkg, rootDir), - } -} - -func GetAllPackageFiles(rootDir string, excludePackages ...string) (map[string][]*BPMPackage, error) { - ret := make(map[string][]*BPMPackage) - - pkgNames, err := GetInstalledPackages(rootDir) - if err != nil { - return nil, err - } - - for _, pkgName := range pkgNames { - if slices.Contains(excludePackages, pkgName) { - continue - } - bpmpkg := GetPackage(pkgName, rootDir) - if bpmpkg == nil { - return nil, errors.New(fmt.Sprintf("could not get BPM package (%s)", pkgName)) - } - for _, entry := range bpmpkg.PkgFiles { - if _, ok := ret[entry.Path]; ok { - ret[entry.Path] = append(ret[entry.Path], bpmpkg) - } else { - ret[entry.Path] = []*BPMPackage{bpmpkg} - } - } - } - - return ret, nil -} - func removePackage(pkg string, verbose bool, rootDir string) error { installedDir := path.Join(rootDir, "var/lib/bpm/installed/") pkgDir := path.Join(installedDir, pkg) @@ -1206,7 +900,7 @@ func removePackage(pkg string, verbose bool, rootDir string) error { } // Fetching and reversing package file entry list - fileEntries := GetPackageFiles(pkg, rootDir) + fileEntries := GetPackage(pkg, rootDir).PkgFiles sort.Slice(fileEntries, func(i, j int) bool { return fileEntries[i].Path < fileEntries[j].Path })