Add package compilation flags

This commit is contained in:
2026-06-27 17:22:30 +03:00
parent 6f524ab6fa
commit 80382c849b
8 changed files with 768 additions and 249 deletions
+41 -28
View File
@@ -239,21 +239,20 @@ func showPackageInfo() {
}
for n, pkg := range packages {
if showDatabaseInfo {
// Split package name and required version
pkgName, _, _ := bpmlib.SplitPkgNameAndVersion(pkg)
entry := bpmlib.ResolveDatabaseEntry(pkgName, rootDir)
if entry == nil {
log.Printf("Error: could not find package (%s) in any database\n", pkg)
// Deconstruct package string
d, err := bpmlib.DeconstructPackageString(pkg)
if err != nil {
log.Printf("Error: could not deconstruct package string: %s\n", err)
exitCode = 1
return
}
if !bpmlib.EvaluateDependency(pkg, entry.Info.Version) {
if showDatabaseInfo {
entry := bpmlib.ResolveDatabaseEntry(d, rootDir)
if entry == nil {
log.Printf("Error: could not find package (%s) in any database\n", pkg)
exitCode = 1
continue
return
}
if n != 0 {
@@ -266,8 +265,8 @@ func showPackageInfo() {
var bpmpkg *bpmlib.BPMPackage
isFile := false
if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() {
bpmpkg, err = bpmlib.ReadPackage(pkg)
if stat, err := os.Stat(d.PkgName); err == nil && !stat.IsDir() {
bpmpkg, err = bpmlib.ReadPackage(d.PkgName)
if err != nil {
log.Printf("Error: could not read package: %s\n", err)
exitCode = 1
@@ -275,13 +274,10 @@ func showPackageInfo() {
}
isFile = true
} else {
// Split package name and required version
pkgName, _, _ := bpmlib.SplitPkgNameAndVersion(pkg)
if providers := bpmlib.GetVirtualPackageInfo(pkgName, rootDir); len(providers) > 0 {
if providers := bpmlib.GetVirtualPackageInfo(d.PkgName, rootDir); len(providers) > 0 {
bpmpkg = bpmlib.GetPackage(providers[0].Name, rootDir)
} else {
bpmpkg = bpmlib.GetPackage(pkgName, rootDir)
bpmpkg = bpmlib.GetPackage(d.PkgName, rootDir)
}
}
@@ -291,7 +287,7 @@ func showPackageInfo() {
return
}
if !bpmlib.EvaluateDependency(pkg, bpmpkg.PkgInfo.Version) {
if !bpmlib.EvaluatePackageString(bpmpkg.PkgInfo, d) {
log.Printf("Error: package (%s) is not installed\n", pkg)
exitCode = 1
return
@@ -301,7 +297,7 @@ func showPackageInfo() {
fmt.Println()
}
if isFile {
abs, err := filepath.Abs(pkg)
abs, err := filepath.Abs(d.PkgName)
if err != nil {
log.Printf("Error: could not get absolute path of file (%s)\n", abs)
exitCode = 1
@@ -632,7 +628,7 @@ func installPackages() {
// Create installation operation
operation, err := bpmlib.InstallPackages(rootDir, ir, reinstallPackages, installRuntime, force, !skipChecks, verbose, packages...)
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
if errors.As(err, &bpmlib.PackageNotResolvedErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
log.Printf("Error: %s", err)
exitCode = 1
return
@@ -787,7 +783,7 @@ func removePackages() {
// Create remove operation
operation, err := bpmlib.RemovePackages(rootDir, force, cleanupPackages, packages...)
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
if errors.As(err, &bpmlib.PackageNotResolvedErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
log.Printf("Error: %s", err)
exitCode = 1
return
@@ -931,7 +927,7 @@ func doCleanup() {
// Create cleanup operation
operation, err := bpmlib.CleanupPackages(cleanupMakeDepends, rootDir)
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
if errors.As(err, &bpmlib.PackageNotResolvedErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
log.Printf("Error: %s", err)
exitCode = 1
return
@@ -1095,7 +1091,7 @@ func updatePackages() {
// Create update operation
operation, err := bpmlib.UpdatePackages(rootDir, !noSync, allowDowngrades, force, !skipChecks, verbose)
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
if errors.As(err, &bpmlib.PackageNotResolvedErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
log.Printf("Error: %s", err)
exitCode = 1
return
@@ -1300,8 +1296,16 @@ func compilePackage() {
// Compile packages
for _, sourcePackage := range sourcePackages {
if _, err := os.Stat(sourcePackage); os.IsNotExist(err) {
log.Printf("Error: file (%s) does not exist!", sourcePackage)
// Deconstruct package string
d, err := bpmlib.DeconstructPackageString(sourcePackage)
if err != nil {
log.Printf("Error: could not deconstruct package string: %s\n", err)
exitCode = 1
return
}
if _, err := os.Stat(d.PkgName); os.IsNotExist(err) {
log.Printf("Error: file (%s) does not exist!", d.PkgName)
exitCode = 1
return
}
@@ -1321,6 +1325,12 @@ func compilePackage() {
return
}
if !bpmlib.EvaluatePackageString(bpmpkg.PkgInfo, d) {
log.Printf("Error: could not evaluate package: %s\n", sourcePackage)
exitCode = 1
return
}
// Get common, make and check dependencies
totalDepends := make([]string, 0)
totalDepends = append(totalDepends, bpmpkg.PkgInfo.Depends...)
@@ -1350,10 +1360,13 @@ func compilePackage() {
// Ignore packages that can't be found in any databases
if force {
unmetDepends = slices.DeleteFunc(unmetDepends, func(s string) bool {
if bpmlib.ResolveDatabaseEntry(s, rootDir) == nil {
return true
}
// Deconstruct package string
d, err := bpmlib.DeconstructPackageString(s)
if err != nil {
return false
}
return bpmlib.ResolveDatabaseEntry(d, rootDir) == nil
})
}
@@ -1485,7 +1498,7 @@ func compilePackage() {
return
}
outputBpmPackages, err := bpmlib.CompileSourcePackage(sourcePackage, outputDirectory, compilationJobs, skipChecks, keepCompilationFiles, verbose)
outputBpmPackages, err := bpmlib.CompileSourcePackage(sourcePackage, outputDirectory, d.Flags, compilationJobs, skipChecks, keepCompilationFiles, verbose)
if err != nil {
// Remove unused packages
cleanupFunc()
+35 -1
View File
@@ -24,7 +24,7 @@ import (
var rootCompilationUID = "65534"
var rootCompilationGID = "65534"
func CompileSourcePackage(archiveFilename, outputDirectory string, compilationJobs int, skipChecks, keepCompilationFiles, verbose bool) (outputBpmPackages map[string]string, err error) {
func CompileSourcePackage(archiveFilename, outputDirectory string, flags map[string]string, compilationJobs int, skipChecks, keepCompilationFiles, verbose bool) (outputBpmPackages map[string]string, err error) {
// Set compilation jobs
if compilationJobs <= 0 || compilationJobs > runtime.NumCPU() {
compilationJobs = runtime.NumCPU()
@@ -146,6 +146,15 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, compilationJo
env = append(env, "BPM_PKG_URL="+bpmpkg.PkgInfo.Url)
env = append(env, "BPM_PKG_ARCH="+bpmpkg.PkgInfo.OutputArch)
env = append(env, "BPM_JOBS="+strconv.Itoa(compilationJobs))
for _, flag := range bpmpkg.PkgInfo.Flags {
if value, ok := flags[flag.Name]; ok {
env = append(env, "BPM_PKG_FLAG_"+strings.ToUpper(flag.Name)+"="+value)
fmt.Printf("Package flag: %s=%s\n", flag.Name, value)
} else {
env = append(env, "BPM_PKG_FLAG_"+strings.ToUpper(flag.Name)+"="+flag.DefaultValue)
fmt.Printf("Package flag: %s=%s\n", flag.Name, flag.DefaultValue)
}
}
env = append(env, CompilationBPMConfig.CompilationEnvironment...)
// Set common flags used for limiting job count
@@ -381,6 +390,31 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, compilationJo
pkgInfo.SplitPackages = nil
pkgInfo.Downloads = nil
// Set built flag values and add their dependencies
for i, flag := range pkgInfo.Flags {
if value, ok := flags[flag.Name]; ok {
flag.BuiltValue = value
} else {
flag.BuiltValue = flag.DefaultValue
}
acceptedValueIndex := slices.IndexFunc(flag.AcceptedValues, func(acceptedValue PackageAcceptedValue) bool {
return acceptedValue.Value == flag.BuiltValue
})
if acceptedValueIndex < 0 {
return nil, fmt.Errorf("flag value not accepted: %s=%s", flag.Name, flag.BuiltValue)
}
acceptedValue := flag.AcceptedValues[acceptedValueIndex]
pkgInfo.Flags[i] = flag
pkgInfo.Depends = append(pkgInfo.Depends, acceptedValue.Depends...)
pkgInfo.RuntimeDepends = append(pkgInfo.RuntimeDepends, acceptedValue.RuntimeDepends...)
pkgInfo.OptionalDepends = append(pkgInfo.OptionalDepends, acceptedValue.OptionalDepends...)
pkgInfo.MakeDepends = append(pkgInfo.MakeDepends, acceptedValue.MakeDepends...)
pkgInfo.CheckDepends = append(pkgInfo.CheckDepends, acceptedValue.CheckDepends...)
}
// Marshal package info
pkgInfoBytes, err := yaml.Marshal(pkgInfo)
if err != nil {
+143 -49
View File
@@ -42,7 +42,7 @@ type BPMDatabaseEntry struct {
Database *BPMDatabase
}
var BPMDatabases = make(map[string]*BPMDatabase)
var BPMDatabases = make([]*BPMDatabase, 0)
func (db *BPMDatabase) ContainsPackage(pkg string) bool {
_, ok := db.Entries[pkg]
@@ -135,7 +135,7 @@ func (db *configDatabase) ReadLocalDatabase() error {
}
}
BPMDatabases[db.Name] = database
BPMDatabases = append(BPMDatabases, database)
return nil
}
@@ -207,61 +207,76 @@ func ReadLocalDatabaseFiles() (err error) {
return nil
}
func ResolveDatabaseEntry(pkg string, rootDir string) *BPMDatabaseEntry {
entry := GetDatabaseEntry(pkg)
if entry != nil {
return entry
}
func ResolveDatabaseEntry(d DeconstructedPackageString, rootDir string) *BPMDatabaseEntry {
results := SearchDatabaseEntries(d.PkgName)
for _, result := range results {
installedProviders := GetVirtualPackageInfo(pkg, rootDir)
if len(installedProviders) > 0 {
entry := GetDatabaseEntry(installedProviders[0].Name)
if entry != nil {
return entry
if EvaluatePackageString(result.Info, d) {
return result
}
}
databaseProviders := GetDatabaseVirtualPackageEntry(pkg)
if len(databaseProviders) > 0 {
entry := GetDatabaseEntry(databaseProviders[0].Info.Name)
if entry != nil {
return entry
installedProviders := GetVirtualPackageInfo(d.PkgName, rootDir)
for _, provider := range installedProviders {
results := SearchDatabaseEntries(provider.Name)
for _, result := range results {
if EvaluatePackageString(result.Info, d) {
return result
}
}
}
databaseProviders := SearchDatabaseVirtualPackageProviders(d.PkgName)
for _, provider := range databaseProviders {
results := SearchDatabaseEntries(provider.Info.Name)
for _, result := range results {
if EvaluatePackageString(result.Info, d) {
return result
}
}
}
return nil
}
func GetDatabaseEntry(pkg string) *BPMDatabaseEntry {
func SearchDatabaseEntries(pkg string) (results []*BPMDatabaseEntry) {
split := strings.Split(pkg, "/")
if len(split) == 1 {
pkgName := strings.TrimSpace(split[0])
if pkgName == "" {
return nil
return results
}
for _, db := range BPMDatabases {
if db.ContainsPackage(pkgName) {
return db.Entries[pkgName]
results = append(results, db.Entries[pkgName])
}
}
return nil
return results
} else if len(split) == 2 {
dbName := strings.TrimSpace(split[0])
pkgName := strings.TrimSpace(split[1])
if dbName == "" || pkgName == "" {
return nil
}
db := BPMDatabases[dbName]
if db == nil || !db.ContainsPackage(pkgName) {
return nil
}
return db.Entries[pkgName]
} else {
return nil
return results
}
dbIndex := slices.IndexFunc(BPMDatabases, func(db *BPMDatabase) bool {
return db.Name == dbName
})
if dbIndex < 0 {
return results
}
func GetDatabaseVirtualPackageEntry(vpkg string) (providers []*BPMDatabaseEntry) {
db := BPMDatabases[dbIndex]
if !db.ContainsPackage(pkgName) {
return results
}
results = append(results, db.Entries[pkgName])
}
return results
}
func SearchDatabaseVirtualPackageProviders(vpkg string) (providers []*BPMDatabaseEntry) {
for _, db := range BPMDatabases {
providers = append(providers, db.VirtualPackages[vpkg]...)
}
@@ -336,8 +351,12 @@ func (entry *BPMDatabaseEntry) GetEntryDependants() (dependants []string) {
// Add installed package to list if its dependencies include pkgName
if slices.ContainsFunc(e.Info.Depends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == entry.Info.Name
d, err := DeconstructPackageString(n)
if err != nil {
return false
}
return d.PkgName == entry.Info.Name
}) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], db.Name)
continue
@@ -345,8 +364,12 @@ func (entry *BPMDatabaseEntry) GetEntryDependants() (dependants []string) {
// Add installed package to list if its runtime dependencies include pkgName
if slices.ContainsFunc(e.Info.RuntimeDepends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == entry.Info.Name
d, err := DeconstructPackageString(n)
if err != nil {
return false
}
return d.PkgName == entry.Info.Name
}) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], db.Name)
continue
@@ -356,8 +379,12 @@ func (entry *BPMDatabaseEntry) GetEntryDependants() (dependants []string) {
for _, vpkg := range entry.Info.Provides {
// Add installed package to list if its dependencies contain a provided virtual package
if slices.ContainsFunc(e.Info.Depends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == vpkg
d, err := DeconstructPackageString(n)
if err != nil {
return false
}
return d.PkgName == vpkg
}) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], db.Name)
break
@@ -365,8 +392,12 @@ func (entry *BPMDatabaseEntry) GetEntryDependants() (dependants []string) {
// Add installed package to list if its runtime dependencies contain a provided virtual package
if slices.ContainsFunc(e.Info.RuntimeDepends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == vpkg
d, err := DeconstructPackageString(n)
if err != nil {
return false
}
return d.PkgName == vpkg
}) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], db.Name)
break
@@ -399,13 +430,13 @@ func (entry *BPMDatabaseEntry) GetEntryOptionalDependants() (dependants []string
for _, db := range BPMDatabases {
for _, e := range db.Entries {
if slices.ContainsFunc(e.Info.OptionalDepends, func(n string) bool {
// Remove optional dependency comment
n = strings.SplitN(n, ":", 2)[0]
// Deconstruct package string
d, err := DeconstructPackageString(n)
if err != nil {
return false
}
// Remove required version
n, _, _ = SplitPkgNameAndVersion(n)
return n == entry.Info.Name
return d.PkgName == entry.Info.Name
}) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], e.Database.Name)
}
@@ -436,8 +467,13 @@ func (entry *BPMDatabaseEntry) GetEntryMakeDependants() (dependants []string) {
for _, db := range BPMDatabases {
for _, e := range db.Entries {
if slices.ContainsFunc(e.Info.MakeDepends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == entry.Info.Name
// Deconstruct package string
d, err := DeconstructPackageString(n)
if err != nil {
return false
}
return d.PkgName == entry.Info.Name
}) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], e.Database.Name)
}
@@ -498,7 +534,7 @@ func (entry *BPMDatabaseEntry) CreateReadableInfo(rootDir string, showBytes bool
builder.WriteString(" - " + val)
// Show virtual package providers
if providers := GetDatabaseVirtualPackageEntry(val); len(providers) > 0 {
if providers := SearchDatabaseVirtualPackageProviders(val); len(providers) > 0 {
builder.WriteString(" (")
for i, vpkg := range providers {
if i == len(providers)-1 {
@@ -528,6 +564,64 @@ func (entry *BPMDatabaseEntry) CreateReadableInfo(rootDir string, showBytes bool
}
builder.WriteString("Type: " + entry.Info.Type + "\n")
// Flags
if entry.Info.Type == "binary" {
var flags []string
for _, flag := range entry.Info.Flags {
flags = append(flags, fmt.Sprintf("%s=%s", flag.Name, flag.BuiltValue))
}
builderWriteArray("Built with flags:", flags, false)
} else {
if len(entry.Info.Flags) > 0 {
builder.WriteString("Available flags (")
builder.WriteString(strconv.Itoa(len(entry.Info.Flags)))
builder.WriteString("):\n")
for _, flag := range entry.Info.Flags {
builder.WriteString(" - Flag: ")
builder.WriteString(flag.Name)
builder.WriteRune('\n')
if flag.DefaultValue != "" {
builder.WriteString(" Default value: ")
builder.WriteString(flag.DefaultValue)
builder.WriteRune('\n')
}
if len(flag.AcceptedValues) > 0 {
builder.WriteString(" Accepted values (")
builder.WriteString(strconv.Itoa(len(flag.AcceptedValues)))
builder.WriteString("):\n")
for i, acceptedValue := range flag.AcceptedValues {
writeValueDepends := func(text string, depends []string) {
if len(depends) > 0 {
builder.WriteString(" ")
builder.WriteString(text)
builder.WriteString(" (")
builder.WriteString(strconv.Itoa(len(depends)))
builder.WriteString("): ")
for _, depend := range depends {
if i != 0 {
builder.WriteString(", ")
}
builder.WriteString(depend)
}
builder.WriteRune('\n')
}
}
builder.WriteString(" - Value: ")
builder.WriteString(acceptedValue.Value)
builder.WriteRune('\n')
writeValueDepends("Dependencies", acceptedValue.Depends)
writeValueDepends("Make Dependencies", acceptedValue.MakeDepends)
writeValueDepends("Check Dependencies", acceptedValue.CheckDepends)
writeValueDepends("Runtime Dependencies", acceptedValue.RuntimeDepends)
writeValueDepends("Optional Dependencies", acceptedValue.OptionalDepends)
}
}
}
}
}
// Dependencies
builderWriteDependencyArray("Dependencies", entry.Info.Depends)
if entry.Info.Type == "source" {
@@ -546,7 +640,7 @@ func (entry *BPMDatabaseEntry) CreateReadableInfo(rootDir string, showBytes bool
}
// Show virtual package providers
if providers := GetDatabaseVirtualPackageEntry(dependSplit[0]); len(providers) > 0 {
if providers := SearchDatabaseVirtualPackageProviders(dependSplit[0]); len(providers) > 0 {
builder.WriteString(" (")
for i, vpkg := range providers {
if i == len(providers)-1 {
+264 -90
View File
@@ -1,6 +1,8 @@
package bpmlib
import (
"fmt"
"maps"
"slices"
"strings"
)
@@ -21,8 +23,13 @@ func (pkgInfo *PackageInfo) GetPackageDependants(rootDir string, skipMultiplePro
// Add installed package to list if its dependencies include pkgName
if slices.ContainsFunc(installedPkg.Depends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == pkgInfo.Name
// Deconstruct package string
d, err := DeconstructPackageString(n)
if err != nil {
return false
}
return d.PkgName == pkgInfo.Name
}) {
dependants = append(dependants, installedPkg.Name)
continue
@@ -30,8 +37,13 @@ func (pkgInfo *PackageInfo) GetPackageDependants(rootDir string, skipMultiplePro
// Add installed package to list if its runtime dependencies include pkgName
if slices.ContainsFunc(installedPkg.RuntimeDepends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == pkgInfo.Name
// Deconstruct package string
d, err := DeconstructPackageString(n)
if err != nil {
return false
}
return d.PkgName == pkgInfo.Name
}) {
dependants = append(dependants, installedPkg.Name)
continue
@@ -45,8 +57,13 @@ func (pkgInfo *PackageInfo) GetPackageDependants(rootDir string, skipMultiplePro
// Add installed package to list if its dependencies contain a provided virtual package
if slices.ContainsFunc(installedPkg.Depends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == vpkg
// Deconstruct package string
d, err := DeconstructPackageString(n)
if err != nil {
return false
}
return d.PkgName == vpkg
}) {
dependants = append(dependants, installedPkg.Name)
break
@@ -54,8 +71,13 @@ func (pkgInfo *PackageInfo) GetPackageDependants(rootDir string, skipMultiplePro
// Add installed package to list if its runtime dependencies contain a provided virtual package
if slices.ContainsFunc(installedPkg.RuntimeDepends, func(n string) bool {
n, _, _ = SplitPkgNameAndVersion(n)
return n == vpkg
// Deconstruct package string
d, err := DeconstructPackageString(n)
if err != nil {
return false
}
return d.PkgName == vpkg
}) {
dependants = append(dependants, installedPkg.Name)
break
@@ -82,13 +104,13 @@ 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 {
// Remove optional dependency comment
n = strings.SplitN(n, ":", 2)[0]
// Deconstruct package string
d, err := DeconstructPackageString(n)
if err != nil {
return false
}
// Remove required version
n, _, _ = SplitPkgNameAndVersion(n)
return n == pkgInfo.Name
return d.PkgName == pkgInfo.Name
}) {
dependants = append(dependants, installedPkg.Name)
continue
@@ -98,13 +120,13 @@ 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 {
// Remove optional dependency comment
n = strings.SplitN(n, ":", 2)[0]
// Deconstruct package string
d, err := DeconstructPackageString(n)
if err != nil {
return false
}
// Remove required version
n, _, _ = SplitPkgNameAndVersion(n)
return n == vpkg
return d.PkgName == vpkg
}) {
dependants = append(dependants, installedPkg.Name)
break
@@ -117,49 +139,74 @@ func (pkgInfo *PackageInfo) GetPackageOptionalDependants(rootDir string) (depend
type ResolvedPackage struct {
DatabaseEntry *BPMDatabaseEntry
Flags map[string]string
InstallationReason InstallationReason
}
func ResolveDependencies(pkgInfo *PackageInfo, resolvedVirtualPackages map[string]string, includeRuntimeDepends bool, rootDir string) (resolved []ResolvedPackage, unresolved []string) {
func ResolveDependencies(pkgInfo *PackageInfo, flags, resolvedVirtualPackages map[string]string, includeRuntimeDepends bool, rootDir string) (resolved []ResolvedPackage, unresolved map[string]string) {
unresolved = make(map[string]string)
visited := make([]string, 0)
var dfs func(resolvedPkg *PackageInfo)
dfs = func(pkgInfo *PackageInfo) {
var dfs func(resolvedPkg *PackageInfo, flags map[string]string)
dfs = func(pkgInfo *PackageInfo, flags map[string]string) {
checkDependencies := func(dependencies []string, installationReason InstallationReason) {
for _, depend := range dependencies {
// Split dependency name and required version
dependName, _, _ := SplitPkgNameAndVersion(depend)
// Ignore if package is already installed
if IsPackageInstalled(dependName, rootDir) && EvaluateDependency(depend, GetPackageInfo(dependName, rootDir).Version) {
continue
} else if providers := GetVirtualPackageInfo(dependName, rootDir); len(providers) > 0 {
// Deconstruct package string
d, err := DeconstructPackageString(depend)
if err != nil {
unresolved[depend] = "could not deconstruct package string: " + err.Error()
continue
}
// Check resolved virtual packages
if resolvedPkg, ok := resolvedVirtualPackages[dependName]; ok {
dependName = resolvedPkg
if resolvedPkg, ok := resolvedVirtualPackages[d.PkgName]; ok {
d.PkgName = resolvedPkg
}
// Find database entry for dependency
dependEntry := ResolveDatabaseEntry(dependName, rootDir)
dependEntry := ResolveDatabaseEntry(d, rootDir)
if dependEntry == nil {
unresolved = append(unresolved, depend)
continue
}
// Ensure entry has required version
if !EvaluateDependency(depend, dependEntry.Info.Version) {
unresolved = append(unresolved, depend)
unresolved[depend] = "could not find in any database"
continue
}
d.PkgName = dependEntry.Info.Name
// Skip ignored packages in config
if rootDir == "/" && slices.Contains(MainBPMConfig.IgnorePackages, dependEntry.Info.Name) {
continue
}
// Add user defined package flags and resolve entry again if package is already installed
if installedInfo := GetPackageInfo(dependEntry.Info.Name, rootDir); installedInfo != nil {
d.Flags, err = CombineFlags(getPackageLocalInfo(dependEntry.Info.Name, rootDir).Flags, d.Flags)
if err != nil {
unresolved[depend] = "could not combine flags: " + err.Error()
continue
}
dependEntry = ResolveDatabaseEntry(d, rootDir)
if dependEntry == nil {
unresolved[depend] = "could not find in any database"
continue
}
shouldIgnore := true
// Skip if no update/downgrade is available
if installedInfo.GetFullVersion() != dependEntry.Info.GetFullVersion() {
shouldIgnore = false
}
// Skip if no new package flags
if !maps.Equal(getPackageLocalInfo(dependEntry.Info.Name, rootDir).Flags, d.Flags) {
shouldIgnore = false
}
if shouldIgnore {
continue
}
}
// Resolve virtual packages
for _, vpkg := range dependEntry.Info.Provides {
if _, ok := resolvedVirtualPackages[vpkg]; !ok {
@@ -169,8 +216,8 @@ func ResolveDependencies(pkgInfo *PackageInfo, resolvedVirtualPackages map[strin
// Resolve entry dependencies
if !slices.Contains(visited, dependEntry.Info.Name) {
dfs(dependEntry.Info)
resolved = append(resolved, ResolvedPackage{DatabaseEntry: dependEntry, InstallationReason: installationReason})
dfs(dependEntry.Info, d.Flags)
resolved = append(resolved, ResolvedPackage{DatabaseEntry: dependEntry, Flags: d.Flags, InstallationReason: installationReason})
}
}
}
@@ -184,69 +231,196 @@ func ResolveDependencies(pkgInfo *PackageInfo, resolvedVirtualPackages map[strin
if pkgInfo.Type == "source" {
checkDependencies(pkgInfo.MakeDepends, InstallationReasonMakeDependency)
checkDependencies(pkgInfo.CheckDepends, InstallationReasonMakeDependency)
// Resolve flag-specific dependencies
for _, flag := range pkgInfo.Flags {
acceptedValueIndex := slices.IndexFunc(flag.AcceptedValues, func(acceptedValue PackageAcceptedValue) bool {
if value, ok := flags[flag.Name]; ok {
return acceptedValue.Value == value
} else {
return acceptedValue.Value == flag.DefaultValue
}
})
if acceptedValueIndex < 0 {
continue
}
acceptedValue := flag.AcceptedValues[acceptedValueIndex]
checkDependencies(acceptedValue.Depends, InstallationReasonDependency)
checkDependencies(acceptedValue.RuntimeDepends, InstallationReasonDependency)
checkDependencies(acceptedValue.MakeDepends, InstallationReasonMakeDependency)
checkDependencies(acceptedValue.CheckDepends, InstallationReasonMakeDependency)
}
}
}
dfs(pkgInfo)
dfs(pkgInfo, flags)
return resolved, unresolved
}
func SplitPkgNameAndVersion(pkg string) (string, string, string) {
if strings.Contains(pkg, ">=") {
pkgSplit := strings.SplitN(pkg, ">=", 2)
pkgName := pkgSplit[0]
pkgVersion := pkgSplit[1]
return pkgName, ">=", pkgVersion
} else if strings.Contains(pkg, ">") {
pkgSplit := strings.SplitN(pkg, ">", 2)
pkgName := pkgSplit[0]
pkgVersion := pkgSplit[1]
return pkgName, ">", pkgVersion
} else if strings.Contains(pkg, "<=") {
pkgSplit := strings.SplitN(pkg, "<=", 2)
pkgName := pkgSplit[0]
pkgVersion := pkgSplit[1]
return pkgName, "<=", pkgVersion
} else if strings.Contains(pkg, "<") {
pkgSplit := strings.SplitN(pkg, "<", 2)
pkgName := pkgSplit[0]
pkgVersion := pkgSplit[1]
return pkgName, "<", pkgVersion
} else if strings.Contains(pkg, "=") {
pkgSplit := strings.SplitN(pkg, "=", 2)
pkgName := pkgSplit[0]
pkgVersion := pkgSplit[1]
return pkgName, "=", pkgVersion
type DeconstructedPackageString struct {
PkgName string
Description string
Flags map[string]string
RequiredVersionOperator string
RequiredVersion string
}
return pkg, "", ""
func DeconstructPackageString(pkg string) (ret DeconstructedPackageString, err error) {
// Initialize values
ret.Flags = make(map[string]string)
// Get dependency description
if i := strings.IndexRune(pkg, ':'); i > 0 {
ret.Description = pkg[i+1:]
pkg = pkg[0:i]
}
func EvaluateDependency(pkg, matchVersion string) bool {
_, comparisonSymbol, pkgVersion := SplitPkgNameAndVersion(pkg)
if left := strings.IndexRune(pkg, '['); left > 0 {
if right := strings.IndexRune(pkg, ']'); right > left {
flagsStr := pkg[left+1 : right]
pkg = pkg[0:left] + pkg[right+1:]
switch comparisonSymbol {
case ">=":
return CompareVersions(matchVersion, pkgVersion) >= 0
case ">":
return CompareVersions(matchVersion, pkgVersion) > 0
case "<=":
return CompareVersions(matchVersion, pkgVersion) <= 0
case "<":
return CompareVersions(matchVersion, pkgVersion) < 0
case "=":
if cutPkgVersion, ok := strings.CutSuffix(pkgVersion, "*"); ok {
return strings.HasPrefix(matchVersion, cutPkgVersion)
if flagsStr != "" {
for flag := range strings.SplitSeq(flagsStr, ",") {
flagSplit := strings.SplitN(flag, "=", 2)
if len(flagSplit) != 2 {
return ret, fmt.Errorf("could not parse flag: %s", flag)
}
ret.Flags[flagSplit[0]] = flagSplit[1]
}
}
} else {
return CompareVersions(matchVersion, pkgVersion) == 0
return ret, fmt.Errorf("could not find closing square bracket for set flags")
}
}
if strings.Contains(pkg, ">=") {
ret.RequiredVersionOperator = ">="
} else if strings.Contains(pkg, ">") {
ret.RequiredVersionOperator = ">"
} else if strings.Contains(pkg, "<=") {
ret.RequiredVersionOperator = "<="
} else if strings.Contains(pkg, "<") {
ret.RequiredVersionOperator = "<"
} else if strings.Contains(pkg, "=") {
ret.RequiredVersionOperator = "="
}
if ret.RequiredVersionOperator != "" {
pkgSplit := strings.SplitN(pkg, ret.RequiredVersionOperator, 2)
if len(pkgSplit) != 2 {
return ret, fmt.Errorf("could not parse required version: %s", pkg)
}
pkg = pkgSplit[0]
ret.RequiredVersion = pkgSplit[1]
}
ret.PkgName = pkg
return ret, nil
}
func EvaluatePackageString(pkgInfo *PackageInfo, match DeconstructedPackageString) bool {
// Validate flags
for flag, value := range match.Flags {
pkgFlagIndex := slices.IndexFunc(pkgInfo.Flags, func(f PackageFlag) bool {
return f.Name == flag
})
if pkgFlagIndex < 0 {
return false
}
pkgFlag := pkgInfo.Flags[pkgFlagIndex]
if pkgInfo.Type == "binary" {
if pkgFlag.BuiltValue != value {
return false
}
}
if len(pkgFlag.AcceptedValues) == 0 {
continue
}
if !slices.ContainsFunc(pkgFlag.AcceptedValues, func(acceptedFlag PackageAcceptedValue) bool {
return acceptedFlag.Value == value
}) {
return false
}
}
// Validate version
switch match.RequiredVersionOperator {
case ">=":
return CompareVersions(match.RequiredVersion, pkgInfo.Version) >= 0
case ">":
return CompareVersions(match.RequiredVersion, pkgInfo.Version) > 0
case "<=":
return CompareVersions(match.RequiredVersion, pkgInfo.Version) <= 0
case "<":
return CompareVersions(match.RequiredVersion, pkgInfo.Version) < 0
case "=":
if cutPkgVersion, ok := strings.CutSuffix(match.RequiredVersion, "*"); ok {
return strings.HasPrefix(pkgInfo.Version, cutPkgVersion)
} else {
return CompareVersions(pkgInfo.Version, match.RequiredVersion) == 0
}
default:
return true
}
}
func GetBuiltFlags(pkg, rootDir string) map[string]string {
builtFlags := make(map[string]string)
pkgInfo := GetPackageInfo(pkg, rootDir)
if pkgInfo == nil {
return nil
}
for _, flag := range pkgInfo.Flags {
builtFlags[flag.Name] = flag.BuiltValue
}
return builtFlags
}
func RemoveInvalidFlags(pkgInfo *PackageInfo, userFlags map[string]string) (ret map[string]string) {
ret = make(map[string]string)
for userFlag, userValue := range userFlags {
flagIndex := slices.IndexFunc(pkgInfo.Flags, func(flag PackageFlag) bool {
return flag.Name == userFlag
})
if flagIndex < 0 {
continue
}
flag := pkgInfo.Flags[flagIndex]
if len(flag.AcceptedValues) > 0 && !slices.ContainsFunc(flag.AcceptedValues, func(acceptedValue PackageAcceptedValue) bool {
return acceptedValue.Value == userValue
}) {
continue
}
ret[userFlag] = userValue
}
return ret
}
func CombineFlags(flags, newFlags map[string]string) (map[string]string, error) {
ret := make(map[string]string)
maps.Copy(ret, flags)
for flag, value := range newFlags {
if oldValue, ok := flags[flag]; ok && value != oldValue {
return ret, fmt.Errorf("flag already defined: %s=%s/%s", flag, oldValue, value)
}
ret[flag] = value
}
return ret, nil
}
+11 -12
View File
@@ -2,26 +2,25 @@ package bpmlib
import (
"fmt"
"maps"
"slices"
"strings"
)
type PackageNotFoundErr struct {
packages []string
type PackageNotResolvedErr struct {
packages map[string]string
}
func (e PackageNotFoundErr) Error() string {
slices.Sort(e.packages)
return "The following packages were not found in any databases: " + strings.Join(e.packages, ", ")
func (e PackageNotResolvedErr) Error() (ret string) {
ret = "The following packages could not be resolved:"
keys := slices.Collect(maps.Keys(e.packages))
slices.Sort(keys)
for _, key := range keys {
ret += "\n " + key + ": " + e.packages[key]
}
type DependencyNotFoundErr struct {
dependencies []string
}
func (e DependencyNotFoundErr) Error() string {
slices.Sort(e.dependencies)
return "The following dependencies were not found in any databases: " + strings.Join(e.dependencies, ", ")
return ret
}
type PackageConflictErr struct {
+123 -30
View File
@@ -16,7 +16,7 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
// Setup operation struct
operation = &BPMOperation{
Actions: make([]OperationAction, 0),
UnresolvedDepends: make([]string, 0),
UnresolvedDepends: make(map[string]string, 0),
ModifiedFiles: make(map[string]string),
RunChecks: runChecks,
RootDir: rootDir,
@@ -27,21 +27,58 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
packages = removeDuplicates(packages)
// Resolve packages
pkgsNotFound := make([]string, 0)
unresolvedPackages := make(map[string]string)
resolvedVirtualPackages := make(map[string]string)
for _, pkg := range packages {
if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() {
bpmpkg, err := ReadPackage(pkg)
d, err := DeconstructPackageString(pkg)
if err != nil {
return nil, fmt.Errorf("could not deconstruct package string: %s", err)
}
if stat, err := os.Stat(d.PkgName); err == nil && !stat.IsDir() {
bpmpkg, err := ReadPackage(d.PkgName)
if err != nil {
return nil, fmt.Errorf("could not read package: %s", err)
}
if bpmpkg.PkgInfo.Type == "source" && bpmpkg.PkgInfo.IsSplitPackage() {
// Add user defined package flags
if !reinstallPackages && IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) {
d.Flags, err = CombineFlags(getPackageLocalInfo(bpmpkg.PkgInfo.Name, rootDir).Flags, d.Flags)
if err != nil {
return nil, fmt.Errorf("could not combine flags for package (%s): %s", bpmpkg.PkgInfo.Name, err)
}
}
if !EvaluatePackageString(bpmpkg.PkgInfo, d) {
unresolvedPackages[pkg] = "could not evaluate package"
continue
}
if bpmpkg.PkgInfo.IsSplitPackage() {
for _, splitPkg := range bpmpkg.PkgInfo.SplitPackages {
if !reinstallPackages && IsPackageInstalled(splitPkg.Name, rootDir) && GetPackageInfo(splitPkg.Name, rootDir).GetFullVersion() == splitPkg.GetFullVersion() {
continue
}
// Check if installation can be ignored
if !reinstallPackages && IsPackageInstalled(splitPkg.Name, rootDir) {
shouldIgnore := true
// Skip if no update/downgrade is available
if GetPackageInfo(splitPkg.Name, rootDir).GetFullVersion() != splitPkg.GetFullVersion() {
shouldIgnore = false
}
// Skip if no new package flags
if !maps.Equal(getPackageLocalInfo(splitPkg.Name, rootDir).Flags, d.Flags) {
shouldIgnore = false
}
if shouldIgnore {
continue
}
}
// Set package installation reason
installationReason := forceInstallationReason
if installationReason == InstallationReasonUnknown {
@@ -52,9 +89,17 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
}
}
// Resolve virtual packages
for _, vpkg := range splitPkg.Provides {
if _, ok := resolvedVirtualPackages[vpkg]; !ok {
resolvedVirtualPackages[vpkg] = splitPkg.Name
}
}
operation.Actions = append(operation.Actions, &InstallPackageAction{
File: pkg,
InstallationReason: installationReason,
Flags: d.Flags,
BpmPackage: bpmpkg,
SplitPackageToInstall: splitPkg.Name,
})
@@ -62,9 +107,24 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
continue
}
if !reinstallPackages && IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() {
// Check if installation can be ignored
if !reinstallPackages && IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) {
shouldIgnore := true
// Skip if no update/downgrade is available
if GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() != bpmpkg.PkgInfo.GetFullVersion() {
shouldIgnore = false
}
// Skip if no new package flags
if !maps.Equal(getPackageLocalInfo(bpmpkg.PkgInfo.Name, rootDir).Flags, d.Flags) {
shouldIgnore = false
}
if shouldIgnore {
continue
}
}
// Set package installation reason
installationReason := forceInstallationReason
@@ -86,31 +146,55 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
operation.Actions = append(operation.Actions, &InstallPackageAction{
File: pkg,
InstallationReason: installationReason,
Flags: d.Flags,
BpmPackage: bpmpkg,
})
} else {
// Split package name and required version
pkgName, _, _ := SplitPkgNameAndVersion(pkg)
// Check resolved virtual packages
if resolvedPkg, ok := resolvedVirtualPackages[pkgName]; ok {
pkgName = resolvedPkg
if resolvedPkg, ok := resolvedVirtualPackages[d.PkgName]; ok {
d.PkgName = resolvedPkg
}
entry := ResolveDatabaseEntry(pkgName, rootDir)
entry := ResolveDatabaseEntry(d, rootDir)
if entry == nil {
pkgsNotFound = append(pkgsNotFound, pkg)
unresolvedPackages[pkg] = "could not find in any database"
continue
}
if !EvaluateDependency(pkg, entry.Info.Version) {
pkgsNotFound = append(pkgsNotFound, pkg)
continue
pkgInfo := GetPackageInfo(entry.Info.Name, rootDir)
// Add user defined package flags and resolve entry again if package is already installed
if !reinstallPackages && pkgInfo != nil {
d.Flags, err = CombineFlags(getPackageLocalInfo(entry.Info.Name, rootDir).Flags, d.Flags)
if err != nil {
return nil, fmt.Errorf("could not combine flags for package (%s): %s", entry.Info.Name, err)
}
if !reinstallPackages && IsPackageInstalled(entry.Info.Name, rootDir) && GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() {
entry = ResolveDatabaseEntry(d, rootDir)
if entry == nil {
unresolvedPackages[pkg] = "could not find in any database"
continue
}
}
// Check if installation can be ignored
if !reinstallPackages && pkgInfo != nil {
shouldIgnore := true
// Skip if no update/downgrade is available
if GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() != entry.Info.GetFullVersion() {
shouldIgnore = false
}
// Skip if no new package flags
if !maps.Equal(getPackageLocalInfo(entry.Info.Name, rootDir).Flags, d.Flags) {
shouldIgnore = false
}
if shouldIgnore {
continue
}
}
// Set package installation reason
installationReason := forceInstallationReason
@@ -131,23 +215,24 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
operation.Actions = append(operation.Actions, &FetchPackageAction{
InstallationReason: installationReason,
Flags: d.Flags,
DatabaseEntry: entry,
})
}
}
// Return error if not all packages are found
if len(pkgsNotFound) != 0 {
return nil, PackageNotFoundErr{pkgsNotFound}
if len(unresolvedPackages) != 0 {
return nil, PackageNotResolvedErr{unresolvedPackages}
}
// Resolve dependencies
operation.ResolveDependencies(installRuntimeDependencies)
if len(operation.UnresolvedDepends) != 0 {
if !forceInstallation {
return nil, DependencyNotFoundErr{operation.UnresolvedDepends}
return nil, PackageNotResolvedErr{operation.UnresolvedDepends}
} else if verbose {
log.Printf("Warning: %s", DependencyNotFoundErr{operation.UnresolvedDepends})
log.Printf("Warning: %s", PackageNotResolvedErr{operation.UnresolvedDepends})
}
}
@@ -197,7 +282,7 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
func RemovePackages(rootDir string, force, cleanupDependencies bool, packages ...string) (operation *BPMOperation, err error) {
operation = &BPMOperation{
Actions: make([]OperationAction, 0),
UnresolvedDepends: make([]string, 0),
UnresolvedDepends: make(map[string]string),
ModifiedFiles: make(map[string]string),
RootDir: rootDir,
compiledPackages: make(map[string]string),
@@ -275,7 +360,7 @@ func RemovePackages(rootDir string, force, cleanupDependencies bool, packages ..
func CleanupPackages(cleanupMakeDepends bool, rootDir string) (operation *BPMOperation, err error) {
operation = &BPMOperation{
Actions: make([]OperationAction, 0),
UnresolvedDepends: make([]string, 0),
UnresolvedDepends: make(map[string]string),
ModifiedFiles: make(map[string]string),
RootDir: rootDir,
compiledPackages: make(map[string]string),
@@ -403,7 +488,7 @@ func UpdatePackages(rootDir string, syncDatabase, allowDowngrades, forceInstalla
operation = &BPMOperation{
Actions: make([]OperationAction, 0),
UnresolvedDepends: make([]string, 0),
UnresolvedDepends: make(map[string]string),
ModifiedFiles: make(map[string]string),
RunChecks: runChecks,
RootDir: rootDir,
@@ -417,16 +502,21 @@ func UpdatePackages(rootDir string, syncDatabase, allowDowngrades, forceInstalla
continue
}
// Get installed package built flags
flags := getPackageLocalInfo(pkg, rootDir).Flags
var entry *BPMDatabaseEntry
// Check if installed package can be replaced and install that instead
if e := FindReplacement(pkg); e != nil {
entry = e
} else if entry = ResolveDatabaseEntry(pkg, rootDir); entry == nil {
} else if entry = ResolveDatabaseEntry(DeconstructedPackageString{PkgName: pkg, Flags: flags}, rootDir); entry == nil {
continue
}
installedInfo := GetPackageInfo(pkg, rootDir)
if installedInfo == nil {
// Remove invalid flags
flags = RemoveInvalidFlags(entry.Info, flags)
if installedInfo := GetPackageInfo(pkg, rootDir); installedInfo == nil {
return nil, fmt.Errorf("could not get package info for package (%s)", pkg)
} else {
comparison := CompareVersions(entry.Info.GetFullVersion(), installedInfo.GetFullVersion())
@@ -438,8 +528,11 @@ func UpdatePackages(rootDir string, syncDatabase, allowDowngrades, forceInstalla
}
}
bpmpkg := GetPackage(pkg, rootDir)
operation.Actions = append(operation.Actions, &FetchPackageAction{
InstallationReason: GetPackage(pkg, rootDir).LocalInfo.GetInstallationReason(),
InstallationReason: bpmpkg.LocalInfo.GetInstallationReason(),
Flags: flags,
DatabaseEntry: entry,
})
}
@@ -450,9 +543,9 @@ func UpdatePackages(rootDir string, syncDatabase, allowDowngrades, forceInstalla
operation.ResolveDependencies(true)
if len(operation.UnresolvedDepends) != 0 {
if !forceInstallation {
return nil, DependencyNotFoundErr{operation.UnresolvedDepends}
return nil, PackageNotResolvedErr{operation.UnresolvedDepends}
} else if verbose {
log.Printf("Warning: %s", DependencyNotFoundErr{operation.UnresolvedDepends})
log.Printf("Warning: %s", PackageNotResolvedErr{operation.UnresolvedDepends})
}
}
+61 -32
View File
@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"log"
"maps"
"os"
"path"
"slices"
@@ -13,7 +14,7 @@ import (
type BPMOperation struct {
Actions []OperationAction
UnresolvedDepends []string
UnresolvedDepends map[string]string
ModifiedFiles map[string]string
CompilationJobs int
RunChecks bool
@@ -91,26 +92,29 @@ func (operation *BPMOperation) ResolveDependencies(installRuntimeDepends bool) {
newActions := make([]OperationAction, 0)
for _, value := range operation.Actions {
var pkgInfo *PackageInfo
var flags map[string]string
if value.GetActionType() == "install" {
action := value.(*InstallPackageAction)
pkgInfo = action.BpmPackage.PkgInfo
flags = action.Flags
} else if value.GetActionType() == "fetch" {
action := value.(*FetchPackageAction)
pkgInfo = action.DatabaseEntry.Info
flags = action.Flags
} else {
continue
}
resolved, unresolved := ResolveDependencies(pkgInfo, resolvedVirtualPackages, installRuntimeDepends, operation.RootDir)
resolved, unresolved := ResolveDependencies(pkgInfo, flags, resolvedVirtualPackages, installRuntimeDepends, operation.RootDir)
// Append unresolved dependencies
operation.UnresolvedDepends = append(operation.UnresolvedDepends, unresolved...)
operation.UnresolvedDepends = removeDuplicates(operation.UnresolvedDepends)
// Copy unresolved dependencies
maps.Copy(operation.UnresolvedDepends, unresolved)
for _, resolvedPkg := range resolved {
if ActionSliceIndex(newActions, resolvedPkg.DatabaseEntry.Info.Name) == -1 { // Dependency not in actions slice
var action OperationAction = &FetchPackageAction{
InstallationReason: resolvedPkg.InstallationReason,
Flags: resolvedPkg.Flags,
DatabaseEntry: resolvedPkg.DatabaseEntry,
}
newActions = append(newActions, action)
@@ -192,15 +196,18 @@ func (operation *BPMOperation) Cleanup(cleanupMakeDepends bool) error {
// Loop through all dependencies
for _, depend := range depends {
// Remove required version
depend, _, _ = SplitPkgNameAndVersion(depend)
// Deconstruct package string
d, err := DeconstructPackageString(depend)
if err != nil {
return fmt.Errorf("could not deconstruct package string: %s", err)
}
// Resolve dependency
var dependPkgInfo *PackageInfo
if providers := GetVirtualPackageInfo(depend, operation.RootDir); len(providers) > 0 {
if providers := GetVirtualPackageInfo(d.PkgName, operation.RootDir); len(providers) > 0 {
dependPkgInfo = providers[0]
} else {
dependPkgInfo = GetPackageInfo(depend, operation.RootDir)
dependPkgInfo = GetPackageInfo(d.PkgName, operation.RootDir)
}
if dependPkgInfo == nil {
continue
@@ -225,7 +232,7 @@ func (operation *BPMOperation) Cleanup(cleanupMakeDepends bool) error {
if !slices.Contains(visited, pkg) {
bpmpkg := GetPackage(pkg, operation.RootDir)
if bpmpkg == nil {
return errors.New("Error: could not find installed package (" + pkg + ")")
return fmt.Errorf("could not find installed package (%s)", pkg)
}
operation.Actions = append(operation.Actions, &RemovePackageAction{BpmPackage: bpmpkg})
}
@@ -358,15 +365,30 @@ func (operation *BPMOperation) ShowOperationSummary() {
for _, value := range operation.Actions {
var pkgInfo *PackageInfo
var flagsStr string
var installationReason = InstallationReasonUnknown
if value.GetActionType() == "install" {
installationReason = value.(*InstallPackageAction).InstallationReason
pkgInfo = value.(*InstallPackageAction).BpmPackage.PkgInfo
if len(value.(*InstallPackageAction).Flags) > 0 {
flagsSlice := make([]string, 0)
for flag, value := range value.(*InstallPackageAction).Flags {
flagsSlice = append(flagsSlice, flag+"="+value)
}
flagsStr = "[" + strings.Join(flagsSlice, ",") + "]"
}
if value.(*InstallPackageAction).SplitPackageToInstall != "" {
pkgInfo = pkgInfo.GetSplitPackageInfo(value.(*InstallPackageAction).SplitPackageToInstall)
}
} else if value.GetActionType() == "fetch" {
installationReason = value.(*FetchPackageAction).InstallationReason
if len(value.(*FetchPackageAction).Flags) > 0 {
flagsSlice := make([]string, 0)
for flag, value := range value.(*FetchPackageAction).Flags {
flagsSlice = append(flagsSlice, flag+"="+value)
}
flagsStr = "[" + strings.Join(flagsSlice, ",") + "]"
}
pkgInfo = value.(*FetchPackageAction).DatabaseEntry.Info
} else {
pkgInfo = value.(*RemovePackageAction).BpmPackage.PkgInfo
@@ -388,15 +410,15 @@ func (operation *BPMOperation) ShowOperationSummary() {
installedInfo := GetPackageInfo(pkgInfo.Name, operation.RootDir)
if installedInfo == nil {
fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%t\n", pkgInfo.Name, pkgInfo.GetFullVersion(), "Install", installationReasonStr, pkgInfo.Type == "source")
fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%t\n", pkgInfo.Name+flagsStr, pkgInfo.GetFullVersion(), "Install", installationReasonStr, pkgInfo.Type == "source")
} else {
comparison := CompareVersions(pkgInfo.GetFullVersion(), installedInfo.GetFullVersion())
if comparison < 0 {
fmt.Fprintf(writer, "%s\t%s -> %s\t%s\t%s\t%t\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), "Downgrade", installationReasonStr, pkgInfo.Type == "source")
fmt.Fprintf(writer, "%s\t%s -> %s\t%s\t%s\t%t\n", pkgInfo.Name+flagsStr, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), "Downgrade", installationReasonStr, pkgInfo.Type == "source")
} else if comparison > 0 {
fmt.Fprintf(writer, "%s\t%s -> %s\t%s\t%s\t%t\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), "Upgrade", installationReasonStr, pkgInfo.Type == "source")
fmt.Fprintf(writer, "%s\t%s -> %s\t%s\t%s\t%t\n", pkgInfo.Name+flagsStr, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), "Upgrade", installationReasonStr, pkgInfo.Type == "source")
} else {
fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%t\n", pkgInfo.Name, pkgInfo.GetFullVersion(), "Reinstall", installationReasonStr, pkgInfo.Type == "source")
fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%t\n", pkgInfo.Name+flagsStr, pkgInfo.GetFullVersion(), "Reinstall", installationReasonStr, pkgInfo.Type == "source")
}
}
}
@@ -462,12 +484,14 @@ func (operation *BPMOperation) GetOptionalDependencies() (optionalDepends map[st
}
for _, depend := range pkgInfo.OptionalDepends {
// Get optional dependency name
dependSplit := strings.SplitN(depend, ":", 2)
dependName, _, _ := SplitPkgNameAndVersion(dependSplit[0])
// Deconstruct package string
d, err := DeconstructPackageString(depend)
if err != nil {
continue
}
// Skip if dependency is already installed
if IsPackageInstalled(dependName, operation.RootDir) || len(GetVirtualPackageInfo(dependName, operation.RootDir)) > 0 {
if IsPackageInstalled(d.PkgName, operation.RootDir) || len(GetVirtualPackageInfo(d.PkgName, operation.RootDir)) > 0 {
continue
}
@@ -484,9 +508,9 @@ func (operation *BPMOperation) GetOptionalDependencies() (optionalDepends map[st
return false
}
if pkgInfo.Name == dependName {
if pkgInfo.Name == d.PkgName {
return true
} else if slices.Contains(pkgInfo.Provides, dependName) {
} else if slices.Contains(pkgInfo.Provides, d.PkgName) {
return true
}
@@ -497,21 +521,21 @@ func (operation *BPMOperation) GetOptionalDependencies() (optionalDepends map[st
// Skip if not a new dependency of the package
if installedPkg := GetPackage(pkgInfo.Name, operation.RootDir); installedPkg != nil && slices.ContainsFunc(installedPkg.PkgInfo.OptionalDepends, func(n string) bool {
// Remove optional dependency comment
n = strings.SplitN(n, ":", 2)[0]
// Deconstruct package string
d2, err := DeconstructPackageString(depend)
if err != nil {
return false
}
// Remove required version
n, _, _ = SplitPkgNameAndVersion(n)
return n == dependName
return d2.PkgName == d.PkgName
}) {
continue
}
if len(dependSplit) == 2 {
optionalDepends[pkgInfo.Name] = append(optionalDepends[pkgInfo.Name], fmt.Sprintf("%s (%s)", dependSplit[0], dependSplit[1]))
if d.Description != "" {
optionalDepends[pkgInfo.Name] = append(optionalDepends[pkgInfo.Name], fmt.Sprintf("%s (%s)", d.PkgName, d.Description))
} else {
optionalDepends[pkgInfo.Name] = append(optionalDepends[pkgInfo.Name], dependName)
optionalDepends[pkgInfo.Name] = append(optionalDepends[pkgInfo.Name], d.PkgName)
}
}
}
@@ -648,6 +672,7 @@ func (operation *BPMOperation) FetchPackages() (err error) {
operation.Actions[i] = &InstallPackageAction{
File: fetchedPackages[entry.Filepath],
InstallationReason: action.(*FetchPackageAction).InstallationReason,
Flags: action.(*FetchPackageAction).Flags,
BpmPackage: bpmpkg,
SplitPackageToInstall: entry.Info.Name,
}
@@ -655,6 +680,7 @@ func (operation *BPMOperation) FetchPackages() (err error) {
operation.Actions[i] = &InstallPackageAction{
File: fetchedPackages[entry.Filepath],
InstallationReason: action.(*FetchPackageAction).InstallationReason,
Flags: action.(*FetchPackageAction).Flags,
BpmPackage: bpmpkg,
}
}
@@ -735,6 +761,7 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
} else if action.GetActionType() == "install" {
value := action.(*InstallPackageAction)
fileToInstall := value.File
compilationFlags := value.Flags
bpmpkg := value.BpmPackage
var err error
@@ -759,7 +786,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, !operation.RunChecks, false, verbose)
outputBpmPackages, err := CompileSourcePackage(value.File, compiledDir, compilationFlags, operation.CompilationJobs, !operation.RunChecks, false, verbose)
if err != nil {
return fmt.Errorf("could not compile source package (%s): %s\n", value.File, err)
}
@@ -779,9 +806,9 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
}
if value.InstallationReason != InstallationReasonManual {
err = installPackage(fileToInstall, value.InstallationReason, operation.RootDir, verbose, true)
err = installPackage(fileToInstall, value.InstallationReason, value.Flags, operation.RootDir, verbose, true)
} else {
err = installPackage(fileToInstall, value.InstallationReason, operation.RootDir, verbose, force)
err = installPackage(fileToInstall, value.InstallationReason, value.Flags, operation.RootDir, verbose, force)
}
if err != nil {
return fmt.Errorf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err)
@@ -799,6 +826,7 @@ type OperationAction interface {
type InstallPackageAction struct {
File string
InstallationReason InstallationReason
Flags map[string]string
SplitPackageToInstall string
BpmPackage *BPMPackage
}
@@ -809,6 +837,7 @@ func (action *InstallPackageAction) GetActionType() string {
type FetchPackageAction struct {
InstallationReason InstallationReason
Flags map[string]string
DatabaseEntry *BPMDatabaseEntry
}
+84 -1
View File
@@ -49,10 +49,27 @@ type PackageInfo struct {
Replaces []string `yaml:"replaces,omitempty"`
Provides []string `yaml:"provides,omitempty"`
Options []string `yaml:"options,omitempty"`
Flags []PackageFlag `yaml:"flags,omitempty"`
Downloads []PackageDownload `yaml:"downloads,omitempty"`
SplitPackages []*PackageInfo `yaml:"split_packages,omitempty"`
}
type PackageFlag struct {
Name string `yaml:"name"`
DefaultValue string `yaml:"default_value"`
BuiltValue string `yaml:"built_value"`
AcceptedValues []PackageAcceptedValue `yaml:"accepted_values,omitempty"`
}
type PackageAcceptedValue struct {
Value string `yaml:"value"`
Depends []string `yaml:"depends,omitempty"`
RuntimeDepends []string `yaml:"runtime_depends,omitempty"`
OptionalDepends []string `yaml:"optional_depends,omitempty"`
MakeDepends []string `yaml:"make_depends,omitempty"`
CheckDepends []string `yaml:"check_depends,omitempty"`
}
type PackageDownload struct {
Url string `yaml:"url"`
Type string `yaml:"type,omitempty"`
@@ -80,6 +97,7 @@ type PackageFileEntry struct {
type PackageLocalInfo struct {
InstallationReason string `yaml:"installation_reason"`
Flags map[string]string `yaml:"flags"`
InstalledOn int64 `yaml:"installed_on"`
LastUpdatedOn int64 `yaml:"last_updated_on"`
}
@@ -595,6 +613,70 @@ func (pkgInfo *PackageInfo) CreateReadableInfo(rootDir string) string {
}
builder.WriteString("Type: " + pkgInfo.Type + "\n")
// Flags
if pkgInfo.Type == "binary" {
var flags []string
for _, flag := range pkgInfo.Flags {
if pkgInfo != GetPackageInfo(pkgInfo.Name, rootDir) {
flags = append(flags, fmt.Sprintf("%s=%s", flag.Name, flag.BuiltValue))
} else if _, ok := getPackageLocalInfo(pkgInfo.Name, rootDir).Flags[flag.Name]; ok {
flags = append(flags, fmt.Sprintf("%s=%s (User defined)", flag.Name, flag.BuiltValue))
} else {
flags = append(flags, fmt.Sprintf("%s=%s (Default value)", flag.Name, flag.BuiltValue))
}
}
builderWriteArray("Built with flags:", flags, false)
} else {
if len(pkgInfo.Flags) > 0 {
builder.WriteString("Available flags (")
builder.WriteString(strconv.Itoa(len(pkgInfo.Flags)))
builder.WriteString("):\n")
for _, flag := range pkgInfo.Flags {
builder.WriteString(" - Flag: ")
builder.WriteString(flag.Name)
builder.WriteRune('\n')
if flag.DefaultValue != "" {
builder.WriteString(" Default value: ")
builder.WriteString(flag.DefaultValue)
builder.WriteRune('\n')
}
if len(flag.AcceptedValues) > 0 {
builder.WriteString(" Accepted values (")
builder.WriteString(strconv.Itoa(len(flag.AcceptedValues)))
builder.WriteString("):\n")
for i, acceptedValue := range flag.AcceptedValues {
writeValueDepends := func(text string, depends []string) {
if len(depends) > 0 {
builder.WriteString(" ")
builder.WriteString(text)
builder.WriteString(" (")
builder.WriteString(strconv.Itoa(len(depends)))
builder.WriteString("): ")
for _, depend := range depends {
if i != 0 {
builder.WriteString(", ")
}
builder.WriteString(depend)
}
builder.WriteRune('\n')
}
}
builder.WriteString(" - Value: ")
builder.WriteString(acceptedValue.Value)
builder.WriteRune('\n')
writeValueDepends("Dependencies", acceptedValue.Depends)
writeValueDepends("Make Dependencies", acceptedValue.MakeDepends)
writeValueDepends("Check Dependencies", acceptedValue.CheckDepends)
writeValueDepends("Runtime Dependencies", acceptedValue.RuntimeDepends)
writeValueDepends("Optional Dependencies", acceptedValue.OptionalDepends)
}
}
}
}
}
// Dependencies
builderWriteDependencyArray("Dependencies", pkgInfo.Depends)
if pkgInfo.Type == "source" {
@@ -870,7 +952,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
return nil
}
func installPackage(filename string, installationReason InstallationReason, rootDir string, verbose, force bool) error {
func installPackage(filename string, installationReason InstallationReason, flags map[string]string, rootDir string, verbose, force bool) error {
if _, err := os.Stat(filename); os.IsNotExist(err) {
return err
}
@@ -1074,6 +1156,7 @@ func installPackage(filename string, installationReason InstallationReason, root
}
localInfo.LastUpdatedOn = time.Now().Unix()
localInfo.InstallationReason = string(installationReason)
localInfo.Flags = flags
SetPackageLocalInfo(bpmpkg.PkgInfo.Name, localInfo, rootDir)