18 Commits
Author SHA1 Message Date
EnumDev ad89650dc4 Sort packages by name correctly in 'list' subcommand 2026-02-05 18:08:34 +02:00
EnumDev 3a7adb27ab Replace 'human-readable' flag with 'show-bytes' 2026-02-05 18:02:02 +02:00
EnumDev f6aa01339e Show array length in readable info 2026-02-05 17:57:11 +02:00
EnumDev a29de2e52d Show virtual package providers in readable info 2026-02-05 17:52:40 +02:00
EnumDev 619335bbe6 Replace ResolveVirtualPackage function with GetDatabaseVirtualPackageEntry 2026-02-05 14:54:08 +02:00
EnumDev 8579c688d7 Use BFS for finding unused dependencies 2026-02-05 14:22:16 +02:00
EnumDev 48cec35f0c Cache installed virtual packages 2026-02-05 13:21:50 +02:00
EnumDev e19d64bf04 Replace an instance of GetPackage with GetPackageInfo 2026-02-04 14:50:25 +02:00
EnumDev ee80640153 Move installation reason to package local info 2026-02-04 14:48:52 +02:00
EnumDev e4d94e378f Move bpm locks to var/cache/bpm 2026-02-04 13:03:52 +02:00
EnumDev b37360ed82 Add package installation and last update dates 2026-02-03 21:25:33 +02:00
EnumDev ba0d288e45 Add check dependencies 2026-01-28 18:50:25 +02:00
EnumDev c1fa9d9474 Allow skipping tests during compilation in 'install' and 'update' subcommands 2026-01-28 13:02:37 +02:00
EnumDev 0742c81f2f Fix file ownership issues with package compilation as the root user 2026-01-28 12:33:26 +02:00
EnumDev dd20574470 More improvements for the readable info functions 2026-01-27 12:47:25 +02:00
EnumDev b3dc6e3e34 Add optional dependency descriptions 2026-01-26 13:25:25 +02:00
EnumDev 0a06808303 Improve readable info functions 2026-01-25 19:55:09 +02:00
EnumDev 9f8a20c671 Show arrays in multiple lines in readable info 2026-01-25 19:23:39 +02:00
9 changed files with 707 additions and 252 deletions
+129 -20
View File
@@ -13,6 +13,7 @@ import (
"slices"
"sort"
"strings"
"time"
"github.com/EnumeratedDev/bpm/src/bpmlib"
@@ -54,7 +55,7 @@ func main() {
currentFlagSet = flag.NewFlagSet("query", flag.ExitOnError)
currentFlagSet.StringP("root", "R", "/", "Operate on specified root directory")
currentFlagSet.BoolP("database", "d", false, "Show package information from remote databases")
currentFlagSet.BoolP("human-readable", "h", false, "Show package installed size in a human readable format")
currentFlagSet.BoolP("show-bytes", "b", false, "Show package installed size in bytes")
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Show information on the specified packages", os.Args[2:])
showPackageInfo()
@@ -70,7 +71,7 @@ func main() {
currentFlagSet.Bool("make-depends", false, "Show packages installed as make dependencies")
currentFlagSet.String("sort", "", "Sort listed packages by 'name' or 'size")
currentFlagSet.Bool("reverse", false, "Reverse the order in which packages are listed")
currentFlagSet.BoolP("human-readable", "h", false, "Show package installed size in a human readable format")
currentFlagSet.BoolP("show-bytes", "b", false, "Show package installed size in bytes")
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "List packages", os.Args[2:])
showPackageList()
@@ -93,6 +94,7 @@ func main() {
currentFlagSet.BoolP("reinstall", "r", false, "Reinstall the specified packages")
currentFlagSet.BoolP("reinstall-all", "a", false, "Reinstall the specified packages and their dependencies")
currentFlagSet.IntP("jobs", "j", bpmlib.CompilationBPMConfig.CompilationJobs, "Set the amount of concurrent processes to use for source package compilation")
currentFlagSet.BoolP("skip-checks", "s", false, "Skip the check function in source.sh scripts")
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Install the specified packages", os.Args[2:])
installPackages()
@@ -142,6 +144,7 @@ func main() {
currentFlagSet.BoolP("no-sync", "n", false, "Do not sync databases")
currentFlagSet.Bool("allow-downgrades", false, "Allow package downgrades")
currentFlagSet.BoolP("optional", "o", false, "Install all optional dependencies")
currentFlagSet.BoolP("skip-checks", "s", false, "Skip the check function in source.sh scripts")
currentFlagSet.IntP("jobs", "j", bpmlib.CompilationBPMConfig.CompilationJobs, "Set the amount of concurrent processes to use for source package compilation")
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Update installed packages", os.Args[2:])
@@ -174,6 +177,18 @@ func main() {
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Compare two version numbers", os.Args[2:])
compareVersions()
case "upgrade-persistent-data":
currentFlagSet = flag.NewFlagSet("upgrade-persistent-data", flag.ExitOnError)
currentFlagSet.StringP("root", "R", "/", "Operate on specified root directory")
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Upgrade BPM's persistent data directory contents", os.Args[2:])
rootDir, _ := currentFlagSet.GetString("root")
err = bpmlib.UpgradePersistentData(rootDir)
if err != nil {
log.Printf("Error: could not upgrade persistent data directory: %s", err)
exitCode = 1
return
}
default:
printUsage()
exitCode = 1
@@ -188,7 +203,15 @@ func showPackageInfo() {
// Get flags
rootDir, _ := currentFlagSet.GetString("root")
showDatabaseInfo, _ := currentFlagSet.GetBool("database")
showHumanReadableSize, _ := currentFlagSet.GetBool("human-readable")
showBytes, _ := currentFlagSet.GetBool("show-bytes")
// Initialize installed packages map
err := bpmlib.InitializeLocalPackageInformation(rootDir)
if err != nil {
log.Printf("Error: %s", err)
exitCode = 1
return
}
// Get packages
packages := currentFlagSet.Args()
@@ -198,7 +221,7 @@ func showPackageInfo() {
}
// Read local databases
err := bpmlib.ReadLocalDatabaseFiles()
err = bpmlib.ReadLocalDatabaseFiles()
if err != nil {
log.Printf("Error: could not read local databases: %s", err)
exitCode = 1
@@ -211,7 +234,9 @@ func showPackageInfo() {
var entry *bpmlib.BPMDatabaseEntry
entry, _, err = bpmlib.GetDatabaseEntry(pkg)
if err != nil {
if entry = bpmlib.ResolveVirtualPackage(pkg); entry == nil {
if providers := bpmlib.GetDatabaseVirtualPackageEntry(pkg); len(providers) > 0 {
entry = providers[0]
} else {
log.Printf("Error: could not find package (%s) in any database\n", pkg)
exitCode = 1
return
@@ -221,7 +246,7 @@ func showPackageInfo() {
if n != 0 {
fmt.Println()
}
fmt.Println(entry.CreateReadableInfo(rootDir, showHumanReadableSize))
fmt.Println(entry.CreateReadableInfo(rootDir, showBytes))
return
}
@@ -237,8 +262,8 @@ func showPackageInfo() {
}
isFile = true
} else {
if isVirtual, p := bpmlib.IsVirtualPackage(pkg, rootDir); isVirtual {
bpmpkg = bpmlib.GetPackage(p, rootDir)
if providers := bpmlib.GetVirtualPackageInfo(pkg, rootDir); len(providers) > 0 {
bpmpkg = bpmlib.GetPackage(providers[0].Name, rootDir)
} else {
bpmpkg = bpmlib.GetPackage(pkg, rootDir)
}
@@ -262,12 +287,20 @@ func showPackageInfo() {
}
fmt.Println(bpmpkg.PkgInfo.CreateReadableInfo(rootDir))
if bpmpkg.PkgInfo.Type == "binary" {
if !showHumanReadableSize {
if showBytes {
fmt.Printf("Installed size: %d\n", bpmpkg.GetInstalledSize())
} else {
fmt.Printf("Installed size: %s\n", bpmlib.BytesToHumanReadable(bpmpkg.GetInstalledSize()))
}
}
if bpmlib.IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) {
format := "02/01/2006 15:04"
installedOnDate := time.Unix(bpmpkg.LocalInfo.InstalledOn, 0).Format(format)
lastUpdatedOn := time.Unix(bpmpkg.LocalInfo.LastUpdatedOn, 0).Format(format)
fmt.Printf("Installed on: %s\n", installedOnDate)
fmt.Printf("Last updated on: %s\n", lastUpdatedOn)
}
}
}
@@ -282,7 +315,7 @@ func showPackageList() {
showMakeDepends, _ := currentFlagSet.GetBool("make-depends")
sortPackages, _ := currentFlagSet.GetString("sort")
reversePackages, _ := currentFlagSet.GetBool("reverse")
showHumanReadableSize, _ := currentFlagSet.GetBool("human-readable")
showBytes, _ := currentFlagSet.GetBool("show-bytes")
if !isFlagSet(currentFlagSet, "manual") && !isFlagSet(currentFlagSet, "depends") && !isFlagSet(currentFlagSet, "make-depends") {
showManual = true
@@ -290,8 +323,16 @@ func showPackageList() {
showMakeDepends = true
}
// Initialize installed packages map
err := bpmlib.InitializeLocalPackageInformation(rootDir)
if err != nil {
log.Printf("Error: %s", err)
exitCode = 1
return
}
// Read local databases
err := bpmlib.ReadLocalDatabaseFiles()
err = bpmlib.ReadLocalDatabaseFiles()
if err != nil {
log.Printf("Error: could not read local databases: %s", err)
exitCode = 1
@@ -329,6 +370,15 @@ func showPackageList() {
switch sortPackages {
case "", "name":
slices.SortFunc(installedPackages, func(a, b struct {
pkgInfo bpmlib.PackageInfo
installedSize int64
}) int {
return strings.Compare(a.pkgInfo.Name, b.pkgInfo.Name)
})
slices.SortFunc(databaseEntries, func(a, b *bpmlib.BPMDatabaseEntry) int {
return strings.Compare(a.Info.Name, b.Info.Name)
})
case "size":
slices.SortFunc(installedPackages, func(a, b struct {
pkgInfo bpmlib.PackageInfo
@@ -363,7 +413,7 @@ func showPackageList() {
}
} else {
for _, pkg := range installedPackages {
installationReason := bpmlib.GetInstallationReason(pkg.pkgInfo.Name, rootDir)
installationReason := bpmlib.GetPackage(pkg.pkgInfo.Name, rootDir).LocalInfo.GetInstallationReason()
if installationReason == bpmlib.InstallationReasonManual && !showManual {
continue
} else if installationReason == bpmlib.InstallationReasonDependency && !showDepends {
@@ -387,7 +437,7 @@ func showPackageList() {
if n != 0 {
fmt.Println()
}
fmt.Println(entry.CreateReadableInfo(rootDir, showHumanReadableSize))
fmt.Println(entry.CreateReadableInfo(rootDir, showBytes))
}
} else {
if len(installedPackages) == 0 {
@@ -395,7 +445,7 @@ func showPackageList() {
return
}
for n, pkg := range installedPackages {
installationReason := bpmlib.GetInstallationReason(pkg.pkgInfo.Name, rootDir)
installationReason := bpmlib.GetPackage(pkg.pkgInfo.Name, rootDir).LocalInfo.GetInstallationReason()
if installationReason == bpmlib.InstallationReasonManual && !showManual {
continue
} else if installationReason == bpmlib.InstallationReasonDependency && !showDepends {
@@ -412,12 +462,20 @@ func showPackageList() {
fmt.Println(pkg.pkgInfo.CreateReadableInfo(rootDir))
if pkg.pkgInfo.Type == "binary" {
if !showHumanReadableSize {
if showBytes {
fmt.Printf("Installed size: %d\n", pkg.installedSize)
} else {
fmt.Printf("Installed size: %s\n", bpmlib.BytesToHumanReadable(pkg.installedSize))
}
}
localInfo := bpmlib.GetPackage(pkg.pkgInfo.Name, rootDir).LocalInfo
format := "02/01/2006 15:04"
installedOnDate := time.Unix(localInfo.InstalledOn, 0).Format(format)
lastUpdatedOn := time.Unix(localInfo.LastUpdatedOn, 0).Format(format)
fmt.Printf("Installed on: %s\n", installedOnDate)
fmt.Printf("Last updated on: %s\n", lastUpdatedOn)
}
}
}
@@ -489,6 +547,7 @@ func installPackages() {
installationReason, _ := currentFlagSet.GetString("installation-reason")
reinstall, _ := currentFlagSet.GetBool("reinstall")
reinstallAll, _ := currentFlagSet.GetBool("reinstall-all")
skipChecks, _ := currentFlagSet.GetBool("skip-checks")
compilationJobs, _ := currentFlagSet.GetInt("jobs")
// Get packages
@@ -540,6 +599,14 @@ func installPackages() {
}
defer fileLock.Unlock()
// Initialize installed packages map
err = bpmlib.InitializeLocalPackageInformation(rootDir)
if err != nil {
log.Printf("Error: %s", err)
exitCode = 1
return
}
// Read local databases
err = bpmlib.ReadLocalDatabaseFiles()
if err != nil {
@@ -549,7 +616,7 @@ func installPackages() {
}
// Create installation operation
operation, err := bpmlib.InstallPackages(rootDir, ir, reinstallMethod, installRuntime, installOptional, force, verbose, packages...)
operation, err := bpmlib.InstallPackages(rootDir, ir, reinstallMethod, installRuntime, installOptional, force, !skipChecks, verbose, packages...)
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
log.Printf("Error: %s", err)
exitCode = 1
@@ -673,6 +740,14 @@ func removePackages() {
}
defer fileLock.Unlock()
// Initialize installed packages map
err = bpmlib.InitializeLocalPackageInformation(rootDir)
if err != nil {
log.Printf("Error: %s", err)
exitCode = 1
return
}
// Read local databases
err = bpmlib.ReadLocalDatabaseFiles()
if err != nil {
@@ -786,6 +861,14 @@ func doCleanup() {
}
defer fileLock.Unlock()
// Initialize installed packages map
err = bpmlib.InitializeLocalPackageInformation(rootDir)
if err != nil {
log.Printf("Error: %s", err)
exitCode = 1
return
}
err = bpmlib.CleanupCache(rootDir, cleanupCompilationFiles, cleanupBinaryPackages, cleanupFetchedPackages, verbose)
if err != nil {
log.Printf("Error: could not complete cache cleanup: %s", err)
@@ -907,6 +990,7 @@ func updatePackages() {
noSync, _ := currentFlagSet.GetBool("no-sync")
allowDowngrades, _ := currentFlagSet.GetBool("allow-downgrades")
installOptional, _ := currentFlagSet.GetBool("optional")
skipChecks, _ := currentFlagSet.GetBool("skip-checks")
compilationJobs, _ := currentFlagSet.GetInt("jobs")
// Check for required permissions
@@ -925,6 +1009,14 @@ func updatePackages() {
}
defer fileLock.Unlock()
// Initialize installed packages map
err = bpmlib.InitializeLocalPackageInformation(rootDir)
if err != nil {
log.Printf("Error: %s", err)
exitCode = 1
return
}
// Read local databases if no sync
if noSync {
err := bpmlib.ReadLocalDatabaseFiles()
@@ -945,7 +1037,7 @@ func updatePackages() {
}
// Create update operation
operation, err := bpmlib.UpdatePackages(rootDir, !noSync, allowDowngrades, installOptional, force, verbose)
operation, err := bpmlib.UpdatePackages(rootDir, !noSync, allowDowngrades, installOptional, force, !skipChecks, verbose)
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
log.Printf("Error: %s", err)
exitCode = 1
@@ -1046,6 +1138,14 @@ func getFileOwner() {
// Get flags
rootDir, _ := currentFlagSet.GetString("root")
// Initialize installed packages map
err := bpmlib.InitializeLocalPackageInformation(rootDir)
if err != nil {
log.Printf("Error: %s", err)
exitCode = 1
return
}
// Get files
files := currentFlagSet.Args()
if len(files) == 0 {
@@ -1139,6 +1239,14 @@ func compilePackage() {
outputFd, _ := currentFlagSet.GetInt("output-fd")
compilationJobs, _ := currentFlagSet.GetInt("jobs")
// Initialize installed packages map
err := bpmlib.InitializeLocalPackageInformation(rootDir)
if err != nil {
log.Printf("Error: %s", err)
exitCode = 1
return
}
// Get files
sourcePackages := currentFlagSet.Args()
if len(sourcePackages) == 0 {
@@ -1147,7 +1255,7 @@ func compilePackage() {
}
// Read local databases
err := bpmlib.ReadLocalDatabaseFiles()
err = bpmlib.ReadLocalDatabaseFiles()
if err != nil {
log.Printf("Error: could not read local databases: %s", err)
exitCode = 1
@@ -1179,7 +1287,7 @@ func compilePackage() {
// Get direct common and make dependencies
totalDepends := make([]string, 0)
for _, depend := range bpmpkg.PkgInfo.GetDependencies(true, false, false) {
for _, depend := range bpmpkg.PkgInfo.GetDependencies(true, !skipChecks, false, false) {
if !slices.Contains(totalDepends, depend.PkgName) {
totalDepends = append(totalDepends, depend.PkgName)
}
@@ -1196,7 +1304,7 @@ func compilePackage() {
for i := len(unmetDepends) - 1; i >= 0; i-- {
if slices.Contains(installedPackages, unmetDepends[i]) {
unmetDepends = append(unmetDepends[:i], unmetDepends[i+1:]...)
} else if ok, _ := bpmlib.IsVirtualPackage(unmetDepends[i], rootDir); ok {
} else if providers := bpmlib.GetVirtualPackageInfo(unmetDepends[i], rootDir); len(providers) > 0 {
unmetDepends = append(unmetDepends[:i], unmetDepends[i+1:]...)
}
}
@@ -1388,6 +1496,7 @@ func printUsage() {
fmt.Println(" o, owner Show what packages own the specified paths")
fmt.Println(" c, compile Compile source packages and convert them to binary ones")
fmt.Println(" p, vercmp Compare package version numbers")
}
func setupFlagsAndHelp(flagset *flag.FlagSet, usage, desc string, args []string) {
+37
View File
@@ -456,6 +456,24 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, compilationJo
}
func downloadPackageFiles(pkgInfo *PackageInfo, tempDirectory string, verbose bool) error {
// Get UID and GID to use for compilation
var uid, gid int
if os.Getuid() == 0 {
_uid, err := strconv.ParseInt(rootCompilationUID, 10, 32)
if err != nil {
return fmt.Errorf("could not convert UID '%s' to int", rootCompilationUID)
}
_gid, err := strconv.ParseInt(rootCompilationGID, 10, 32)
if err != nil {
return fmt.Errorf("could not convert GID '%s' to int", rootCompilationGID)
}
uid = int(_uid)
gid = int(_gid)
} else {
uid = os.Getuid()
gid = os.Getgid()
}
for _, download := range pkgInfo.Downloads {
// Replace variables
replaceVars := func(s string) string {
@@ -534,12 +552,22 @@ func downloadPackageFiles(pkgInfo *PackageInfo, tempDirectory string, verbose bo
fmt.Println("Skipping checksum checking...")
}
// Change downloaded file ownership
err = os.Chown(filepath, uid, gid)
if err != nil {
return err
}
if !download.NoExtract && (strings.Contains(filepath, ".tar") || strings.HasSuffix(filepath, ".tgz")) {
cmd := exec.Command("tar", "xf", filepath, "--strip-components="+strconv.Itoa(download.ExtractStripComponents))
if verbose {
cmd.Args[1] = "xvf"
}
cmd.Dir = tempDirectory
if os.Getuid() == 0 {
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
}
if extractTo != "" {
err := os.MkdirAll(extractTo, 0755)
if err != nil {
@@ -557,6 +585,11 @@ func downloadPackageFiles(pkgInfo *PackageInfo, tempDirectory string, verbose bo
}
} else if !download.NoExtract && strings.HasSuffix(filepath, ".zip") {
cmd := exec.Command("unzip", filepath)
cmd.Dir = tempDirectory
if os.Getuid() == 0 {
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
}
if extractTo != "" {
err := os.MkdirAll(extractTo, 0755)
if err != nil {
@@ -601,6 +634,10 @@ func downloadPackageFiles(pkgInfo *PackageInfo, tempDirectory string, verbose bo
cmd := exec.Command("git", "clone", "--depth=1", downloadUrl)
cmd.Dir = tempDirectory
if os.Getuid() == 0 {
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
}
if gitBranch != "" {
cmd.Args = slices.Insert(cmd.Args, len(cmd.Args)-1, "--branch="+gitBranch)
}
+116 -55
View File
@@ -20,7 +20,7 @@ import (
type BPMDatabase struct {
DatabaseVersion int `yaml:"database_version"`
Entries map[string]*BPMDatabaseEntry `yaml:"entries"`
VirtualPackages map[string][]string
VirtualPackages map[string][]*BPMDatabaseEntry
Name string
Source string
}
@@ -59,7 +59,7 @@ func (db *configDatabase) ReadLocalDatabase() error {
}
// Initialize struct values
database.VirtualPackages = make(map[string][]string)
database.VirtualPackages = make(map[string][]*BPMDatabaseEntry)
database.Name = db.Name
database.Source = db.Source
@@ -105,13 +105,13 @@ func (db *configDatabase) ReadLocalDatabase() error {
// Add virtual packages to database
for _, p := range splitPkg.Provides {
database.VirtualPackages[p] = append(database.VirtualPackages[p], splitPkg.Name)
database.VirtualPackages[p] = append(database.VirtualPackages[p], database.Entries[splitPkg.Name])
}
}
} else {
// Add virtual packages to database
for _, p := range entry.Info.Provides {
database.VirtualPackages[p] = append(database.VirtualPackages[p], entry.Info.Name)
database.VirtualPackages[p] = append(database.VirtualPackages[p], entry)
}
}
}
@@ -231,15 +231,12 @@ func FindReplacement(pkg string) *BPMDatabaseEntry {
return nil
}
func ResolveVirtualPackage(vpkg string) *BPMDatabaseEntry {
func GetDatabaseVirtualPackageEntry(vpkg string) (providers []*BPMDatabaseEntry) {
for _, db := range BPMDatabases {
if v, ok := db.VirtualPackages[vpkg]; ok {
slices.Sort(v)
return db.Entries[v[0]]
}
providers = append(providers, db.VirtualPackages[vpkg]...)
}
return nil
return providers
}
func (db *BPMDatabase) FetchPackage(pkg string) (string, error) {
@@ -297,7 +294,9 @@ func (entry *BPMDatabaseEntry) GetEntryOptionalDependants() (dependants []string
dependantsMap := make(map[string][]string)
for _, db := range BPMDatabases {
for _, e := range db.Entries {
if slices.Contains(e.Info.OptionalDepends, entry.Info.Name) {
if slices.ContainsFunc(e.Info.OptionalDepends, func(n string) bool {
return strings.SplitN(n, ":", 2)[0] == entry.Info.Name
}) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], e.Database.Name)
}
}
@@ -322,65 +321,124 @@ func (entry *BPMDatabaseEntry) GetEntryOptionalDependants() (dependants []string
return dependants
}
func (entry *BPMDatabaseEntry) CreateReadableInfo(rootDir string, humanReadableSize bool) string {
ret := make([]string, 0)
appendArray := func(label string, array []string, sort bool) {
func (entry *BPMDatabaseEntry) CreateReadableInfo(rootDir string, showBytes bool) string {
builder := strings.Builder{}
builderWriteStringNotEmpty := func(label string, value string) {
if value != "" {
builder.WriteString(label + ": " + value + "\n")
}
}
builderWriteArray := func(label string, array []string, sort bool) {
if len(array) == 0 {
return
}
// Sort array
if sort {
// Sort array
slices.Sort(array)
}
ret = append(ret, fmt.Sprintf("%s: %s", label, strings.Join(array, ", ")))
builder.WriteString(label + " (" + strconv.Itoa(len(array)) + "):\n")
for _, val := range array {
builder.WriteString(" - " + val + "\n")
}
}
builderWriteDependencyArray := func(label string, depends []string) {
if len(depends) == 0 {
return
}
// Sort array
slices.Sort(depends)
builder.WriteString(label + " (" + strconv.Itoa(len(depends)) + "):\n")
for _, val := range depends {
builder.WriteString(" - " + val)
// Show virtual package providers
if providers := GetDatabaseVirtualPackageEntry(val); len(providers) > 0 {
builder.WriteString(" (")
for i, vpkg := range providers {
if i == len(providers)-1 {
builder.WriteString(vpkg.Info.Name)
} else {
builder.WriteString(vpkg.Info.Name + ", ")
}
}
builder.WriteString(")")
}
builder.WriteString("\n")
}
}
ret = append(ret, "Name: "+entry.Info.Name)
ret = append(ret, "Database: "+entry.Database.Name)
ret = append(ret, "Description: "+entry.Info.Description)
ret = append(ret, "Version: "+entry.Info.GetFullVersion())
if entry.Info.Url != "" {
ret = append(ret, "URL: "+entry.Info.Url)
}
if entry.Info.License != "" {
ret = append(ret, "License: "+entry.Info.License)
}
appendArray("Maintainers", entry.Info.Maintainers, false)
ret = append(ret, "Architecture: "+entry.Info.Arch)
// Main information
builder.WriteString("Name: " + entry.Info.Name + "\n")
builder.WriteString("Database: " + entry.Database.Name + "\n")
builder.WriteString("Description: " + entry.Info.Description + "\n")
builder.WriteString("Version: " + entry.Info.GetFullVersion() + "\n")
builderWriteStringNotEmpty("URL", entry.Info.Url)
builderWriteStringNotEmpty("License", entry.Info.License)
builderWriteArray("Maintainers", entry.Info.Maintainers, false)
builder.WriteString("Architecture: " + entry.Info.Arch + "\n")
if entry.Info.Type == "source" && entry.Info.OutputArch != "" && entry.Info.OutputArch != GetArch() {
ret = append(ret, "Output architecture: "+entry.Info.OutputArch)
builder.WriteString("Output architecture: " + entry.Info.OutputArch + "\n")
}
ret = append(ret, "Type: "+entry.Info.Type)
appendArray("Dependencies", entry.Info.Depends, true)
if entry.Info.Type == "source" {
appendArray("Make Dependencies", entry.Info.MakeDepends, true)
}
appendArray("Runtime dependencies", entry.Info.RuntimeDepends, true)
appendArray("Optional dependencies", entry.Info.OptionalDepends, true)
dependants := entry.GetEntryDependants()
if len(dependants) > 0 {
appendArray("Dependant packages", dependants, false)
}
optionalDependants := entry.GetEntryOptionalDependants()
if len(optionalDependants) > 0 {
appendArray("Optionally dependant packages", optionalDependants, false)
}
appendArray("Conflicting packages", entry.Info.Conflicts, true)
appendArray("Provided packages", entry.Info.Provides, true)
appendArray("Replaces packages", entry.Info.Replaces, true)
builder.WriteString("Type: " + entry.Info.Type + "\n")
// Dependencies
builderWriteDependencyArray("Dependencies", entry.Info.Depends)
if entry.Info.Type == "source" {
builderWriteDependencyArray("Make dependencies", entry.Info.MakeDepends)
builderWriteDependencyArray("Check dependencies", entry.Info.CheckDepends)
}
builderWriteDependencyArray("Runtime dependencies", entry.Info.RuntimeDepends)
if len(entry.Info.OptionalDepends) > 0 {
builder.WriteString("Optional dependencies: (" + strconv.Itoa(len(entry.Info.OptionalDepends)) + ")\n")
for _, depend := range entry.Info.OptionalDepends {
dependSplit := strings.SplitN(depend, ":", 2)
if len(dependSplit) == 2 {
builder.WriteString(fmt.Sprintf(" - %s (%s)", dependSplit[0], dependSplit[1]))
} else {
builder.WriteString(" - " + dependSplit[0])
}
// Show virtual package providers
if providers := GetDatabaseVirtualPackageEntry(dependSplit[0]); len(providers) > 0 {
builder.WriteString(" (")
for i, vpkg := range providers {
if i == len(providers)-1 {
builder.WriteString(vpkg.Info.Name)
} else {
builder.WriteString(vpkg.Info.Name + ", ")
}
}
builder.WriteString(")")
}
builder.WriteString("\n")
}
}
builderWriteArray("Dependant packages", entry.GetEntryDependants(), true)
builderWriteArray("Optionally dependant packages", entry.GetEntryOptionalDependants(), true)
// Other package relations
builderWriteArray("Conflicting packages", entry.Info.Conflicts, true)
builderWriteArray("Provided packages", entry.Info.Provides, true)
builderWriteArray("Replaces packages", entry.Info.Replaces, true)
// Split packages
if entry.Info.Type == "source" && len(entry.Info.SplitPackages) != 0 {
splitPkgs := make([]string, len(entry.Info.SplitPackages))
for i, splitPkgInfo := range entry.Info.SplitPackages {
splitPkgs[i] = splitPkgInfo.Name
}
appendArray("Split Packages", splitPkgs, true)
builderWriteArray("Split packages", splitPkgs, true)
}
// Installation reason
if rootDir != "" && IsPackageInstalled(entry.Info.Name, rootDir) {
installationReason := GetInstallationReason(entry.Info.Name, rootDir)
installationReason := GetPackage(entry.Info.Name, rootDir).LocalInfo.GetInstallationReason()
var installationReasonString string
switch installationReason {
case InstallationReasonManual:
@@ -392,17 +450,20 @@ func (entry *BPMDatabaseEntry) CreateReadableInfo(rootDir string, humanReadableS
default:
installationReasonString = "Unknown"
}
ret = append(ret, "Installation Reason: "+installationReasonString)
builder.WriteString("Installation reason: " + installationReasonString + "\n")
}
// Installed size
if entry.Info.Type == "binary" {
installedSize := entry.InstalledSize
var installedSizeStr string
if humanReadableSize {
installedSizeStr = BytesToHumanReadable(installedSize)
} else {
if showBytes {
installedSizeStr = strconv.FormatInt(installedSize, 10)
} else {
installedSizeStr = BytesToHumanReadable(installedSize)
}
ret = append(ret, "Installed size: "+installedSizeStr)
builder.WriteString("Installed size: " + installedSizeStr + "\n")
}
return strings.Join(ret, "\n")
return strings.TrimSpace(builder.String())
}
+32 -17
View File
@@ -3,6 +3,7 @@ package bpmlib
import (
"fmt"
"slices"
"strings"
)
type pkgInstallationReason struct {
@@ -10,7 +11,7 @@ type pkgInstallationReason struct {
InstallationReason InstallationReason
}
func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeRuntimeDepends, includeOptionalDepends bool) []pkgInstallationReason {
func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeCheckDepends, includeRuntimeDepends, includeOptionalDepends bool) []pkgInstallationReason {
allDepends := make([]pkgInstallationReason, 0)
for _, depend := range pkgInfo.Depends {
@@ -25,6 +26,7 @@ func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeRuntimeDe
}
if includeOptionalDepends {
for _, depend := range pkgInfo.OptionalDepends {
depend = strings.SplitN(depend, ":", 2)[0]
if !slices.ContainsFunc(allDepends, func(p pkgInstallationReason) bool {
return p.PkgName == depend
}) {
@@ -59,6 +61,18 @@ func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeRuntimeDe
}
}
}
if includeCheckDepends {
for _, depend := range pkgInfo.CheckDepends {
if !slices.ContainsFunc(allDepends, func(p pkgInstallationReason) bool {
return p.PkgName == depend
}) {
allDepends = append(allDepends, pkgInstallationReason{
PkgName: depend,
InstallationReason: InstallationReasonMakeDependency,
})
}
}
}
// Skip ignored packages
allDepends = slices.DeleteFunc(allDepends, func(depend pkgInstallationReason) bool {
@@ -68,27 +82,27 @@ func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeRuntimeDe
return allDepends
}
func (pkgInfo *PackageInfo) GetDependenciesRecursive(includeRuntimeDepends, includeMakeDepends bool, rootDir string) (resolved []string) {
func (pkgInfo *PackageInfo) GetDependenciesRecursive(includeRuntimeDepends, includeCheckDepends, includeMakeDepends bool, rootDir string) (resolved []string) {
// Initialize slices
resolved = make([]string, 0)
unresolved := make([]string, 0)
// Call unexported function
pkgInfo.getDependenciesRecursive(&resolved, &unresolved, includeRuntimeDepends, includeMakeDepends, rootDir)
pkgInfo.getDependenciesRecursive(&resolved, &unresolved, includeRuntimeDepends, includeMakeDepends, includeCheckDepends, rootDir)
return resolved
}
func (pkgInfo *PackageInfo) getDependenciesRecursive(resolved *[]string, unresolved *[]string, includeRuntimeDepends, includeMakeDepends bool, rootDir string) {
func (pkgInfo *PackageInfo) getDependenciesRecursive(resolved *[]string, unresolved *[]string, includeRuntimeDepends, includeMakeDepends, includeCheckDepends bool, rootDir string) {
// Add current package name to unresolved slice
*unresolved = append(*unresolved, pkgInfo.Name)
// Loop through all dependencies
for _, pkgIR := range pkgInfo.GetDependencies(includeMakeDepends, includeRuntimeDepends, false) {
for _, pkgIR := range pkgInfo.GetDependencies(includeMakeDepends, includeCheckDepends, includeRuntimeDepends, false) {
depend := pkgIR.PkgName
if isVirtual, p := IsVirtualPackage(depend, rootDir); isVirtual {
depend = p
if providers := GetVirtualPackageInfo(depend, rootDir); len(providers) > 0 {
depend = providers[0].Name
}
if !slices.Contains(*resolved, depend) {
@@ -103,7 +117,7 @@ func (pkgInfo *PackageInfo) getDependenciesRecursive(resolved *[]string, unresol
dependInfo := GetPackageInfo(depend, rootDir)
if dependInfo != nil {
dependInfo.getDependenciesRecursive(resolved, unresolved, includeRuntimeDepends, includeMakeDepends, rootDir)
dependInfo.getDependenciesRecursive(resolved, unresolved, includeRuntimeDepends, includeMakeDepends, includeCheckDepends, rootDir)
}
}
}
@@ -113,7 +127,7 @@ func (pkgInfo *PackageInfo) getDependenciesRecursive(resolved *[]string, unresol
*unresolved = stringSliceRemove(*unresolved, pkgInfo.Name)
}
func ResolveAllPackageDependenciesFromDatabases(pkgInfo *PackageInfo, resolvedVirtualPkgs map[string]string, checkMake, checkRuntime, checkOptional, ignoreInstalled, verbose bool, rootDir string) (resolved []pkgInstallationReason, unresolved []string) {
func ResolveAllPackageDependenciesFromDatabases(pkgInfo *PackageInfo, resolvedVirtualPkgs map[string]string, checkMake, checkCheck, checkRuntime, checkOptional, ignoreInstalled, verbose bool, rootDir string) (resolved []pkgInstallationReason, unresolved []string) {
// Initialize slices and maps
resolved = make([]pkgInstallationReason, 0)
unresolved = make([]string, 0)
@@ -122,7 +136,7 @@ func ResolveAllPackageDependenciesFromDatabases(pkgInfo *PackageInfo, resolvedVi
}
// Call unexported function
resolvePackageDependenciesFromDatabase(&resolved, &unresolved, resolvedVirtualPkgs, pkgInfo, checkMake, checkRuntime, checkOptional, ignoreInstalled, verbose, rootDir)
resolvePackageDependenciesFromDatabase(&resolved, &unresolved, resolvedVirtualPkgs, pkgInfo, checkMake, checkCheck, checkRuntime, checkOptional, ignoreInstalled, verbose, rootDir)
// Remove main package from unresolved slice
unresolved = stringSliceRemove(unresolved, pkgInfo.Name)
@@ -130,7 +144,7 @@ func ResolveAllPackageDependenciesFromDatabases(pkgInfo *PackageInfo, resolvedVi
return resolved, unresolved
}
func resolvePackageDependenciesFromDatabase(resolved *[]pkgInstallationReason, unresolved *[]string, resolvedVirtualPkgs map[string]string, pkgInfo *PackageInfo, checkMake, checkRuntime, checkOptional, ignoreInstalled, verbose bool, rootDir string) {
func resolvePackageDependenciesFromDatabase(resolved *[]pkgInstallationReason, unresolved *[]string, resolvedVirtualPkgs map[string]string, pkgInfo *PackageInfo, checkMake, checkCheck, checkRuntime, checkOptional, ignoreInstalled, verbose bool, rootDir string) {
// Add current package name to unresolved slice
*unresolved = append(*unresolved, pkgInfo.Name)
@@ -141,7 +155,7 @@ func resolvePackageDependenciesFromDatabase(resolved *[]pkgInstallationReason, u
}
// Loop through all dependencies
for _, pkgIR := range pkgInfo.GetDependencies(pkgInfo.Type == "source", checkRuntime, checkOptional) {
for _, pkgIR := range pkgInfo.GetDependencies(pkgInfo.Type == "source", pkgInfo.Type == "source" && checkCheck, checkRuntime, checkOptional) {
// Skip dependency if it has already been resolved
if slices.ContainsFunc(*resolved, func(p pkgInstallationReason) bool {
return p.PkgName == pkgIR.PkgName
@@ -163,7 +177,7 @@ func resolvePackageDependenciesFromDatabase(resolved *[]pkgInstallationReason, u
}
// Skip dependency if it is already installed or provided
if ignoreInstalled && IsPackageProvided(pkgIR.PkgName, rootDir) {
if providers := GetVirtualPackageInfo(pkgIR.PkgName, rootDir); ignoreInstalled && len(providers) > 0 {
continue
}
@@ -187,8 +201,9 @@ func resolvePackageDependenciesFromDatabase(resolved *[]pkgInstallationReason, u
*unresolved = stringSliceRemove(*unresolved, resolvedVirtualPkg)
continue
} else if entry = ResolveVirtualPackage(pkgIR.PkgName); entry != nil {
} else if providers := GetDatabaseVirtualPackageEntry(pkgIR.PkgName); len(providers) > 0 {
// Virtual package found in database
entry = providers[0]
} else {
// Virtual package not found
if !slices.Contains(*unresolved, pkgIR.PkgName) {
@@ -199,7 +214,7 @@ func resolvePackageDependenciesFromDatabase(resolved *[]pkgInstallationReason, u
}
// Resolve the dependencies of this dependency
resolvePackageDependenciesFromDatabase(resolved, unresolved, resolvedVirtualPkgs, entry.Info, checkMake, checkRuntime, false, ignoreInstalled, verbose, rootDir)
resolvePackageDependenciesFromDatabase(resolved, unresolved, resolvedVirtualPkgs, entry.Info, checkMake, checkCheck, checkRuntime, false, ignoreInstalled, verbose, rootDir)
// Move dependency from the unresolved slice to the resolved slice
if !slices.ContainsFunc(*resolved, func(p pkgInstallationReason) bool {
@@ -283,7 +298,7 @@ func (pkgInfo *PackageInfo) GetPackageOptionalDependants(rootDir string) (depend
// Add installed package to list if its optional dependencies include pkgName
if slices.ContainsFunc(installedPkg.OptionalDepends, func(n string) bool {
return n == pkgInfo.Name
return strings.SplitN(n, ":", 2)[0] == pkgInfo.Name
}) {
dependants = append(dependants, installedPkg.Name)
continue
@@ -293,7 +308,7 @@ func (pkgInfo *PackageInfo) GetPackageOptionalDependants(rootDir string) (depend
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 {
return n == vpkg
return strings.SplitN(n, ":", 2)[0] == vpkg
}) {
dependants = append(dependants, installedPkg.Name)
break
+14 -12
View File
@@ -20,12 +20,13 @@ const (
)
// InstallPackages installs the specified packages into the given root directory by fetching them from databases or directly from local bpm archives
func InstallPackages(rootDir string, forceInstallationReason InstallationReason, reinstallMethod ReinstallMethod, installRuntimeDependencies, installOptionalDependencies, forceInstallation, verbose bool, packages ...string) (operation *BPMOperation, err error) {
func InstallPackages(rootDir string, forceInstallationReason InstallationReason, reinstallMethod ReinstallMethod, installRuntimeDependencies, installOptionalDependencies, forceInstallation, runChecks bool, verbose bool, packages ...string) (operation *BPMOperation, err error) {
// Setup operation struct
operation = &BPMOperation{
Actions: make([]OperationAction, 0),
UnresolvedDepends: make([]string, 0),
Changes: make(map[string]string),
RunChecks: runChecks,
RootDir: rootDir,
compiledPackages: make(map[string]string),
}
@@ -52,7 +53,7 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
installationReason := forceInstallationReason
if installationReason == InstallationReasonUnknown {
if IsPackageInstalled(splitPkg.Name, rootDir) {
installationReason = GetInstallationReason(splitPkg.Name, rootDir)
installationReason = GetPackage(splitPkg.Name, rootDir).LocalInfo.GetInstallationReason()
} else {
installationReason = InstallationReasonManual
}
@@ -76,7 +77,7 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
installationReason := forceInstallationReason
if installationReason == InstallationReasonUnknown {
if IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) {
installationReason = GetInstallationReason(bpmpkg.PkgInfo.Name, rootDir)
installationReason = GetPackage(bpmpkg.PkgInfo.Name, rootDir).LocalInfo.GetInstallationReason()
} else {
installationReason = InstallationReasonManual
}
@@ -92,14 +93,14 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
if e, _, err := GetDatabaseEntry(pkg); err == nil {
entry = e
} else if isVirtual, p := IsVirtualPackage(pkg, rootDir); isVirtual {
entry, _, err = GetDatabaseEntry(p)
} else if providers := GetVirtualPackageInfo(pkg, rootDir); len(providers) > 0 {
entry, _, err = GetDatabaseEntry(providers[0].Name)
if err != nil {
pkgsNotFound = append(pkgsNotFound, pkg)
continue
}
} else if e := ResolveVirtualPackage(pkg); e != nil {
entry = e
} else if providers := GetDatabaseVirtualPackageEntry(pkg); len(providers) > 0 {
entry = providers[0]
} else {
pkgsNotFound = append(pkgsNotFound, pkg)
continue
@@ -112,7 +113,7 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
installationReason := forceInstallationReason
if installationReason == InstallationReasonUnknown {
if IsPackageInstalled(entry.Info.Name, rootDir) {
installationReason = GetInstallationReason(entry.Info.Name, rootDir)
installationReason = GetPackage(entry.Info.Name, rootDir).LocalInfo.GetInstallationReason()
} else {
installationReason = InstallationReasonManual
}
@@ -201,8 +202,8 @@ func RemovePackages(rootDir string, force, cleanupDependencies bool, packages ..
// Search for packages
for _, pkg := range packages {
bpmpkg := GetPackage(pkg, rootDir)
if isVirutal, vpkg := IsVirtualPackage(pkg, rootDir); isVirutal {
bpmpkg = GetPackage(vpkg, rootDir)
if providers := GetVirtualPackageInfo(pkg, rootDir); len(providers) > 0 {
bpmpkg = GetPackage(providers[0].Name, rootDir)
}
if bpmpkg == nil {
continue
@@ -365,7 +366,7 @@ func CleanupCache(rootDir string, cleanupCompilationFiles, cleanupCompiledPackag
}
// UpdatePackages fetches the newest versions of all installed packages from
func UpdatePackages(rootDir string, syncDatabase bool, allowDowngrades bool, installOptionalDependencies, forceInstallation, verbose bool) (operation *BPMOperation, err error) {
func UpdatePackages(rootDir string, syncDatabase bool, allowDowngrades bool, installOptionalDependencies, forceInstallation, runChecks, verbose bool) (operation *BPMOperation, err error) {
// Sync databases
if syncDatabase {
err := SyncDatabase(verbose)
@@ -397,6 +398,7 @@ func UpdatePackages(rootDir string, syncDatabase bool, allowDowngrades bool, ins
Actions: make([]OperationAction, 0),
UnresolvedDepends: make([]string, 0),
Changes: make(map[string]string),
RunChecks: runChecks,
RootDir: rootDir,
compiledPackages: make(map[string]string),
}
@@ -421,7 +423,7 @@ func UpdatePackages(rootDir string, syncDatabase bool, allowDowngrades bool, ins
comparison := CompareVersions(entry.Info.GetFullVersion(), installedInfo.GetFullVersion())
if (!allowDowngrades && comparison > 0) || (allowDowngrades && comparison != 0) {
operation.AppendAction(&FetchPackageAction{
InstallationReason: GetInstallationReason(pkg, rootDir),
InstallationReason: GetPackage(pkg, rootDir).LocalInfo.GetInstallationReason(),
DatabaseEntry: entry,
})
}
+167 -44
View File
@@ -7,20 +7,42 @@ import (
"slices"
"strconv"
"strings"
"gopkg.in/yaml.v3"
)
var localPackageInformation map[string]map[string]*PackageInfo = make(map[string]map[string]*PackageInfo)
var persistentDataVersion int = 1
func initializeLocalPackageInformation(rootDir string) (err error) {
var localPackageInformation map[string]map[string]*PackageInfo = make(map[string]map[string]*PackageInfo)
var installedVirtualPackages map[string]map[string][]*PackageInfo = make(map[string]map[string][]*PackageInfo)
func InitializeLocalPackageInformation(rootDir string) (err error) {
// Return if information is already initialized
if _, ok := localPackageInformation[rootDir]; ok {
return nil
}
tempPackageInformation := make(map[string]*PackageInfo)
tempInstalledVirtualPackages := make(map[string][]*PackageInfo)
// Get path to installed package information directory
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
// Get paths
persistentDataDir := path.Join(rootDir, "var/lib/bpm")
installedDir := path.Join(persistentDataDir, "installed")
// Ensure persistent data directory is up-to-date
if _, err := os.Stat(persistentDataDir); err == nil {
data, err := os.ReadFile(path.Join(persistentDataDir, ".version"))
if err != nil {
return fmt.Errorf("persistent data is not up-to-date! Please run 'bpm upgrade-persistent-data' first")
}
currentPersistentDataVersion, err := strconv.Atoi(strings.TrimSpace(string(data)))
if err != nil {
return fmt.Errorf("persistent data is not up-to-date! Please run 'bpm upgrade-persistent-data' first")
}
if currentPersistentDataVersion != persistentDataVersion {
return fmt.Errorf("persistent data is not up-to-date! Please run 'bpm upgrade-persistent-data' first")
}
}
// Get directory content
items, err := os.ReadDir(installedDir)
@@ -51,15 +73,21 @@ func initializeLocalPackageInformation(rootDir string) (err error) {
// Add package to slice
tempPackageInformation[info.Name] = info
// Add virtual packages
for _, vpkg := range info.Provides {
tempInstalledVirtualPackages[vpkg] = append(tempInstalledVirtualPackages[vpkg], info)
}
}
localPackageInformation[rootDir] = tempPackageInformation
installedVirtualPackages[rootDir] = tempInstalledVirtualPackages
return nil
}
func GetInstalledPackages(rootDir string) (ret []string, err error) {
// Initialize local package information
err = initializeLocalPackageInformation(rootDir)
err = InitializeLocalPackageInformation(rootDir)
if err != nil {
return nil, err
}
@@ -77,7 +105,7 @@ func GetInstalledPackages(rootDir string) (ret []string, err error) {
func IsPackageInstalled(pkg, rootDir string) bool {
// Initialize local package information
err := initializeLocalPackageInformation(rootDir)
err := InitializeLocalPackageInformation(rootDir)
if err != nil {
return false
}
@@ -88,48 +116,17 @@ func IsPackageInstalled(pkg, rootDir string) bool {
return true
}
func IsVirtualPackage(pkg, rootDir string) (bool, string) {
pkgs, err := GetInstalledPackages(rootDir)
func GetVirtualPackageInfo(vpkg, rootDir string) []*PackageInfo {
err := InitializeLocalPackageInformation(rootDir)
if err != nil {
return false, ""
return nil
}
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
return installedVirtualPackages[rootDir][vpkg]
}
func GetPackageInfo(pkg string, rootDir string) *PackageInfo {
err := initializeLocalPackageInformation(rootDir)
err := InitializeLocalPackageInformation(rootDir)
if err != nil {
return nil
}
@@ -144,10 +141,12 @@ func GetPackage(pkg, rootDir string) *BPMPackage {
}
files := getPackageFiles(pkgInfo.Name, rootDir)
localInfo := getPackageLocalInfo(pkgInfo.Name, rootDir)
return &BPMPackage{
PkgInfo: pkgInfo,
PkgFiles: files,
PkgInfo: pkgInfo,
PkgFiles: files,
LocalInfo: localInfo,
}
}
@@ -234,3 +233,127 @@ func getPackageFiles(pkg, rootDir string) []*PackageFileEntry {
return pkgFiles
}
func getPackageLocalInfo(pkg, rootDir string) PackageLocalInfo {
localInfo := PackageLocalInfo{}
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
pkgDir := path.Join(installedDir, pkg)
localInfoFile := path.Join(path.Join(pkgDir, "local"))
if _, err := os.Stat(localInfoFile); os.IsNotExist(err) {
return localInfo
}
file, err := os.Open(localInfoFile)
if err != nil {
return localInfo
}
defer file.Close()
err = yaml.NewDecoder(file).Decode(&localInfo)
if err != nil {
return localInfo
}
return localInfo
}
func SetPackageLocalInfo(pkg string, localInfo PackageLocalInfo, rootDir string) error {
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
pkgDir := path.Join(installedDir, pkg)
localFile, err := os.OpenFile(path.Join(pkgDir, "local"), os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return err
}
defer localFile.Close()
err = yaml.NewEncoder(localFile).Encode(localInfo)
if err != nil {
return err
}
return nil
}
func UpgradePersistentData(rootDir string) error {
persistentDataDir := path.Join(rootDir, "var/lib/bpm")
// Create persistent data directory
os.MkdirAll(persistentDataDir, 0755)
// Upgrade installed package directories
dirEntries, err := os.ReadDir(path.Join(persistentDataDir, "installed"))
if err != nil && !os.IsNotExist(err) {
return err
} else if err == nil {
for _, entry := range dirEntries {
pkgDir := path.Join(persistentDataDir, "installed", entry.Name())
// Generate default local package information file
if _, err := os.Stat(path.Join(pkgDir, "local")); err != nil && !os.IsNotExist(err) {
return err
} else if os.IsNotExist(err) {
fmt.Printf("Generating local package information for package (%s)\n", entry.Name())
out, err := yaml.Marshal(PackageLocalInfo{
InstallationReason: "unknown",
InstalledOn: 0,
LastUpdatedOn: 0,
})
if err != nil {
return err
}
err = os.WriteFile(path.Join(pkgDir, "local"), out, 0644)
if err != nil {
return err
}
}
// Move installation reason to local package information file
if installationReason, err := os.ReadFile(path.Join(pkgDir, "installation_reason")); err != nil && !os.IsNotExist(err) {
return err
} else if err == nil {
fmt.Printf("Moving installation reason to local package information for package (%s)\n", entry.Name())
data, err := os.ReadFile(path.Join(pkgDir, "local"))
if err != nil {
return err
}
localInfo := &PackageLocalInfo{}
err = yaml.Unmarshal(data, localInfo)
if err != nil {
return err
}
localInfo.InstallationReason = strings.TrimSpace(string(installationReason))
out, err := yaml.Marshal(localInfo)
if err != nil {
return err
}
err = os.WriteFile(path.Join(pkgDir, "local"), out, 0644)
if err != nil {
return err
}
err = os.Remove(path.Join(pkgDir, "installation_reason"))
if err != nil {
return err
}
}
}
}
// Set persistent data version number
err = os.WriteFile(path.Join(persistentDataDir, ".version"), []byte(strconv.Itoa(persistentDataVersion)), 0644)
if err != nil {
return err
}
return nil
}
+67 -31
View File
@@ -16,6 +16,7 @@ type BPMOperation struct {
UnresolvedDepends []string
Changes map[string]string
CompilationJobs int
RunChecks bool
RootDir string
compiledPackages map[string]string
@@ -146,7 +147,7 @@ func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, instal
continue
}
resolved, unresolved := ResolveAllPackageDependenciesFromDatabases(pkgInfo, resolvedVirtualPkgs, pkgInfo.Type == "source", installRuntimeDependencies, installOptionalDependencies, !reinstallDependencies, verbose, operation.RootDir)
resolved, unresolved := ResolveAllPackageDependenciesFromDatabases(pkgInfo, resolvedVirtualPkgs, pkgInfo.Type == "source", pkgInfo.Type == "source" && operation.RunChecks, installRuntimeDependencies, installOptionalDependencies, !reinstallDependencies, verbose, operation.RootDir)
operation.UnresolvedDepends = append(operation.UnresolvedDepends, unresolved...)
@@ -204,11 +205,11 @@ func (operation *BPMOperation) Cleanup(cleanupMakeDepends bool) error {
}
installedPackages := make([]*PackageInfo, len(installedPackageNames))
for i, value := range installedPackageNames {
bpmpkg := GetPackage(value, operation.RootDir)
if bpmpkg == nil {
pkgInfo := GetPackageInfo(value, operation.RootDir)
if pkgInfo == nil {
return errors.New("could not find installed package (" + value + ")")
}
installedPackages[i] = bpmpkg.PkgInfo
installedPackages[i] = pkgInfo
}
// Get packages to remove
@@ -219,34 +220,67 @@ func (operation *BPMOperation) Cleanup(cleanupMakeDepends bool) error {
}
}
// Get manually installed packages, resolve all their dependencies and add them to the keepPackages slice
keepPackages := make([]string, 0)
for _, pkg := range slices.Clone(installedPackages) {
if GetInstallationReason(pkg.Name, operation.RootDir) != InstallationReasonManual {
// Run BFS on all manually installed packages
visited := make([]string, 0)
for _, pkg := range installedPackages {
if getPackageLocalInfo(pkg.Name, operation.RootDir).GetInstallationReason() != InstallationReasonManual {
continue
}
// Do not resolve dependencies or add package to keepPackages slice if package removal action exists for it
if _, ok := removeActions[pkg.Name]; ok {
continue
}
queue := make([]*PackageInfo, 0)
keepPackages = append(keepPackages, pkg.Name)
resolved := pkg.GetDependenciesRecursive(true, !cleanupMakeDepends, operation.RootDir)
for _, value := range resolved {
if !slices.Contains(keepPackages, value) && !slices.Contains(MainBPMConfig.IgnorePackages, value) {
keepPackages = append(keepPackages, value)
queue = append(queue, pkg)
for len(queue) > 0 {
v := queue[len(queue)-1]
queue = queue[:len(queue)-1]
// Skip package if it's to be removed
if _, ok := removeActions[v.Name]; ok {
continue
}
// Mark package as visited
visited = append(visited, v.Name)
// Get all package dependencies
depends := v.Depends
depends = append(depends, v.RuntimeDepends...)
if cleanupMakeDepends {
depends = append(depends, v.MakeDepends...)
depends = append(depends, v.CheckDepends...)
}
// Loop through all dependencies
for _, depend := range depends {
// Resolve dependency
var dependPkgInfo *PackageInfo
if providers := GetVirtualPackageInfo(depend, operation.RootDir); len(providers) > 0 {
dependPkgInfo = providers[0]
} else {
dependPkgInfo = GetPackageInfo(depend, operation.RootDir)
}
if dependPkgInfo == nil {
continue
}
// Mark dependency as visited and add it to the queue
if !slices.Contains(visited, dependPkgInfo.Name) {
visited = append(visited, dependPkgInfo.Name)
queue = append(queue, dependPkgInfo)
}
}
}
}
// Get all installed packages that are not in the keepPackages slice and add them to the BPM operation
// Remove all packages that were not discovered after running BFS
for _, pkg := range installedPackageNames {
// Do not add package removal action if there already is one
if _, ok := removeActions[pkg]; ok {
continue
}
if !slices.Contains(keepPackages, pkg) {
if !slices.Contains(visited, pkg) {
bpmpkg := GetPackage(pkg, operation.RootDir)
if bpmpkg == nil {
return errors.New("Error: could not find installed package (" + pkg + ")")
@@ -485,17 +519,25 @@ func (operation *BPMOperation) GetOptionalDependencies() (optionalDepends map[st
}
for _, depend := range pkgInfo.OptionalDepends {
dependSplit := strings.SplitN(depend, ":", 2)
// Skip if dependency is already installed
if IsPackageInstalled(depend, operation.RootDir) {
if IsPackageInstalled(dependSplit[0], operation.RootDir) {
continue
}
// Skip if not a new dependency of the package
if installedPkg := GetPackage(pkgInfo.Name, operation.RootDir); installedPkg != nil && slices.Contains(installedPkg.PkgInfo.OptionalDepends, depend) {
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]
}) {
continue
}
optionalDepends[pkgInfo.Name] = append(optionalDepends[pkgInfo.Name], depend)
if len(dependSplit) == 2 {
optionalDepends[pkgInfo.Name] = append(optionalDepends[pkgInfo.Name], fmt.Sprintf("%s (%s)", dependSplit[0], dependSplit[1]))
} else {
optionalDepends[pkgInfo.Name] = append(optionalDepends[pkgInfo.Name], dependSplit[0])
}
}
}
@@ -672,7 +714,7 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
// Compile source package if not compiled already
if _, ok := operation.compiledPackages[pkgNameToInstall]; !ok {
outputBpmPackages, err := CompileSourcePackage(value.File, compiledDir, operation.CompilationJobs, false, false, verbose)
outputBpmPackages, err := CompileSourcePackage(value.File, compiledDir, operation.CompilationJobs, !operation.RunChecks, false, verbose)
if err != nil {
return fmt.Errorf("could not compile source package (%s): %s\n", value.File, err)
}
@@ -692,19 +734,13 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
}
if value.InstallationReason != InstallationReasonManual {
err = installPackage(fileToInstall, operation.RootDir, verbose, true)
err = installPackage(fileToInstall, value.InstallationReason, operation.RootDir, verbose, true)
} else {
err = installPackage(fileToInstall, operation.RootDir, verbose, force)
err = installPackage(fileToInstall, value.InstallationReason, operation.RootDir, verbose, force)
}
if err != nil {
return fmt.Errorf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err)
}
// Set installed package's installation reason
err = SetInstallationReason(bpmpkg.PkgInfo.Name, value.InstallationReason, operation.RootDir)
if err != nil {
return fmt.Errorf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err)
}
}
}
fmt.Println("Operation complete!")
+141 -69
View File
@@ -16,13 +16,15 @@ import (
"strconv"
"strings"
"syscall"
"time"
"gopkg.in/yaml.v3"
)
type BPMPackage struct {
PkgInfo *PackageInfo
PkgFiles []*PackageFileEntry
PkgInfo *PackageInfo
PkgFiles []*PackageFileEntry
LocalInfo PackageLocalInfo
}
type PackageInfo struct {
@@ -41,6 +43,7 @@ type PackageInfo struct {
RuntimeDepends []string `yaml:"runtime_depends,omitempty"`
OptionalDepends []string `yaml:"optional_depends,omitempty"`
MakeDepends []string `yaml:"make_depends,omitempty"`
CheckDepends []string `yaml:"check_depends,omitempty"`
Conflicts []string `yaml:"conflicts,omitempty"`
Replaces []string `yaml:"replaces,omitempty"`
Provides []string `yaml:"provides,omitempty"`
@@ -74,6 +77,12 @@ type PackageFileEntry struct {
SizeInBytes int64
}
type PackageLocalInfo struct {
InstallationReason string `yaml:"installation_reason"`
InstalledOn int64 `yaml:"installed_on"`
LastUpdatedOn int64 `yaml:"last_updated_on"`
}
func (pkg *BPMPackage) GetInstalledSize() int64 {
var totalSize int64 = 0
for _, entry := range pkg.PkgFiles {
@@ -122,35 +131,17 @@ const (
InstallationReasonUnknown InstallationReason = "unknown"
)
func GetInstallationReason(pkg, rootDir string) InstallationReason {
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
pkgDir := path.Join(installedDir, pkg)
if stat, err := os.Stat(path.Join(pkgDir, "installation_reason")); err != nil || stat.IsDir() {
func (localInfo PackageLocalInfo) GetInstallationReason() InstallationReason {
switch localInfo.InstallationReason {
case "manual":
return InstallationReasonManual
}
b, err := os.ReadFile(path.Join(pkgDir, "installation_reason"))
if err != nil {
case "dependency":
return InstallationReasonDependency
case "make_dependency":
return InstallationReasonMakeDependency
default:
return InstallationReasonUnknown
}
reason := strings.TrimSpace(string(b))
if reason == "manual" {
return InstallationReasonManual
} else if reason == "dependency" {
return InstallationReasonDependency
} else if reason == "make_dependency" {
return InstallationReasonMakeDependency
}
return InstallationReasonUnknown
}
func SetInstallationReason(pkg string, reason InstallationReason, rootDir string) error {
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
pkgDir := path.Join(installedDir, pkg)
err := os.WriteFile(path.Join(pkgDir, "installation_reason"), []byte(reason), 0644)
if err != nil {
return err
}
return nil
}
func GetPackageInfoRaw(filename string) (string, error) {
@@ -540,59 +531,122 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
}
func (pkgInfo *PackageInfo) CreateReadableInfo(rootDir string) string {
ret := make([]string, 0)
appendArray := func(label string, array []string) {
builder := strings.Builder{}
builderWriteStringNotEmpty := func(label string, value string) {
if value != "" {
builder.WriteString(label + ": " + value + "\n")
}
}
builderWriteArray := func(label string, array []string, sort bool) {
if len(array) == 0 {
return
}
ret = append(ret, fmt.Sprintf("%s: %s", label, strings.Join(array, ", ")))
// Sort array
if sort {
slices.Sort(array)
}
builder.WriteString(label + " (" + strconv.Itoa(len(array)) + "):\n")
for _, val := range array {
builder.WriteString(" - " + val + "\n")
}
}
builderWriteDependencyArray := func(label string, depends []string) {
if len(depends) == 0 {
return
}
// Sort array
slices.Sort(depends)
builder.WriteString(label + " (" + strconv.Itoa(len(depends)) + "):\n")
for _, val := range depends {
builder.WriteString(" - " + val)
// Show virtual package providers
if providers := GetVirtualPackageInfo(val, rootDir); len(providers) > 0 {
builder.WriteString(" (")
for i, vpkg := range providers {
if i == len(providers)-1 {
builder.WriteString(vpkg.Name)
} else {
builder.WriteString(vpkg.Name + ", ")
}
}
builder.WriteString(")")
}
builder.WriteString("\n")
}
}
ret = append(ret, "Name: "+pkgInfo.Name)
ret = append(ret, "Description: "+pkgInfo.Description)
ret = append(ret, "Version: "+pkgInfo.GetFullVersion())
if pkgInfo.Url != "" {
ret = append(ret, "URL: "+pkgInfo.Url)
}
if pkgInfo.License != "" {
ret = append(ret, "License: "+pkgInfo.License)
}
appendArray("Maintainers", pkgInfo.Maintainers)
ret = append(ret, "Architecture: "+pkgInfo.Arch)
// Main information
builder.WriteString("Name: " + pkgInfo.Name + "\n")
builder.WriteString("Description: " + pkgInfo.Description + "\n")
builder.WriteString("Version: " + pkgInfo.GetFullVersion() + "\n")
builderWriteStringNotEmpty("URL", pkgInfo.Url)
builderWriteStringNotEmpty("License", pkgInfo.License)
builderWriteArray("Maintainers", pkgInfo.Maintainers, false)
builder.WriteString("Architecture: " + pkgInfo.Arch + "\n")
if pkgInfo.Type == "source" && pkgInfo.OutputArch != "" && pkgInfo.OutputArch != GetArch() {
ret = append(ret, "Output architecture: "+pkgInfo.Arch)
builder.WriteString("Output architecture: " + pkgInfo.OutputArch + "\n")
}
ret = append(ret, "Type: "+pkgInfo.Type)
appendArray("Dependencies", pkgInfo.Depends)
if pkgInfo.Type == "source" {
appendArray("Runtime Dependencies", pkgInfo.RuntimeDepends)
appendArray("Make Dependencies", pkgInfo.MakeDepends)
}
appendArray("Runtime dependencies", pkgInfo.RuntimeDepends)
appendArray("Optional dependencies", pkgInfo.OptionalDepends)
dependants := pkgInfo.GetPackageDependants(rootDir)
if len(dependants) > 0 {
appendArray("Dependant packages", dependants)
}
optionalDependants := pkgInfo.GetPackageOptionalDependants(rootDir)
if len(optionalDependants) > 0 {
appendArray("Optionally dependant packages", optionalDependants)
}
appendArray("Conflicting packages", pkgInfo.Conflicts)
appendArray("Provided packages", pkgInfo.Provides)
appendArray("Replaces packages", pkgInfo.Replaces)
builder.WriteString("Type: " + pkgInfo.Type + "\n")
// Dependencies
builderWriteDependencyArray("Dependencies", pkgInfo.Depends)
if pkgInfo.Type == "source" {
builderWriteDependencyArray("Make dependencies", pkgInfo.MakeDepends)
builderWriteDependencyArray("Check dependencies", pkgInfo.CheckDepends)
}
builderWriteDependencyArray("Runtime dependencies", pkgInfo.RuntimeDepends)
if len(pkgInfo.OptionalDepends) > 0 {
builder.WriteString("Optional dependencies (" + strconv.Itoa(len(pkgInfo.OptionalDepends)) + "):\n")
for _, depend := range pkgInfo.OptionalDepends {
dependSplit := strings.SplitN(depend, ":", 2)
if len(dependSplit) == 2 {
builder.WriteString(fmt.Sprintf(" - %s (%s)", dependSplit[0], dependSplit[1]))
} else {
builder.WriteString(" - " + dependSplit[0])
}
// Show virtual package providers
if providers := GetVirtualPackageInfo(dependSplit[0], rootDir); len(providers) > 0 {
builder.WriteString(" (")
for i, vpkg := range providers {
if i == len(providers)-1 {
builder.WriteString(vpkg.Name)
} else {
builder.WriteString(vpkg.Name + ", ")
}
}
builder.WriteString(")")
}
builder.WriteString("\n")
}
}
builderWriteArray("Dependant packages", pkgInfo.GetPackageDependants(rootDir), true)
builderWriteArray("Optionally dependant packages", pkgInfo.GetPackageOptionalDependants(rootDir), true)
// Other package relations
builderWriteArray("Conflicting packages", pkgInfo.Conflicts, true)
builderWriteArray("Provided packages", pkgInfo.Provides, true)
builderWriteArray("Replaces packages", pkgInfo.Replaces, true)
// Split packages
if pkgInfo.Type == "source" && len(pkgInfo.SplitPackages) != 0 {
splitPkgs := make([]string, len(pkgInfo.SplitPackages))
for i, splitPkgInfo := range pkgInfo.SplitPackages {
splitPkgs[i] = splitPkgInfo.Name
}
appendArray("Split Packages", splitPkgs)
builderWriteArray("Split packages", splitPkgs, true)
}
// Installation reason
if rootDir != "" && IsPackageInstalled(pkgInfo.Name, rootDir) {
installationReason := GetInstallationReason(pkgInfo.Name, rootDir)
installationReason := GetPackage(pkgInfo.Name, rootDir).LocalInfo.GetInstallationReason()
var installationReasonString string
switch installationReason {
case InstallationReasonManual:
@@ -604,10 +658,10 @@ func (pkgInfo *PackageInfo) CreateReadableInfo(rootDir string) string {
default:
installationReasonString = "Unknown"
}
ret = append(ret, "Installation Reason: "+installationReasonString)
builder.WriteString("Installation reason: " + installationReasonString + "\n")
}
return strings.Join(ret, "\n")
return strings.TrimSpace(builder.String())
}
func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) error {
@@ -771,7 +825,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
return nil
}
func installPackage(filename, rootDir string, verbose, force bool) error {
func installPackage(filename string, installationReason InstallationReason, rootDir string, verbose, force bool) error {
if _, err := os.Stat(filename); os.IsNotExist(err) {
return err
}
@@ -955,6 +1009,16 @@ func installPackage(filename, rootDir string, verbose, force bool) error {
return err
}
// Write local package information
localInfo := getPackageLocalInfo(bpmpkg.PkgInfo.Name, rootDir)
if !packageInstalled {
localInfo.InstalledOn = time.Now().Unix()
}
localInfo.LastUpdatedOn = time.Now().Unix()
localInfo.InstallationReason = string(installationReason)
SetPackageLocalInfo(bpmpkg.PkgInfo.Name, localInfo, rootDir)
// Save remove package scripts
packageScripts, err := ReadPackageScripts(filename)
if err != nil {
@@ -994,8 +1058,16 @@ func installPackage(filename, rootDir string, verbose, force bool) error {
}
}
// Write persistent data version number
if _, err := os.Stat(path.Join(rootDir, "var/lib/bpm/.version")); err != nil {
err = os.WriteFile(path.Join(rootDir, "var/lib/bpm/.version"), []byte(strconv.Itoa(persistentDataVersion)), 0644)
if err != nil {
return err
}
}
// Ensure local package information has been initialized for rootDir
err = initializeLocalPackageInformation(rootDir)
err = InitializeLocalPackageInformation(rootDir)
if err != nil {
return err
}
@@ -1115,7 +1187,7 @@ func removePackage(pkg string, verbose bool, rootDir string) error {
}
// Ensure local package information has been initialized for rootDir
err = initializeLocalPackageInformation(rootDir)
err = InitializeLocalPackageInformation(rootDir)
if err != nil {
return err
}
+4 -4
View File
@@ -19,14 +19,14 @@ type BPMLock struct {
}
func LockBPM(rootDir string) (*BPMLock, error) {
// Create parent directories if they don't already exist
err := os.MkdirAll(path.Join(rootDir, "/var/lib/bpm"), 0755)
// Create cache directory if it doesn't already exist
err := os.MkdirAll(path.Join(rootDir, "/var/cache/bpm"), 0755)
if err != nil {
return nil, err
}
// Create file
f, err := os.Create(path.Join(rootDir, "var/lib/bpm/bpm.lock"))
f, err := os.Create(path.Join(rootDir, "var/cache/bpm/bpm.lock"))
if err != nil {
return nil, err
}
@@ -37,7 +37,7 @@ func LockBPM(rootDir string) (*BPMLock, error) {
return nil, err
}
return &BPMLock{f, path.Join(rootDir, "var/lib/bpm/bpm.lock")}, nil
return &BPMLock{f, path.Join(rootDir, "var/cache/bpm/bpm.lock")}, nil
}
func (lock *BPMLock) Unlock() error {