WIP: Readd source package compilation functionality #11
6
Makefile
6
Makefile
@ -6,9 +6,13 @@ SYSCONFDIR := $(PREFIX)/etc
|
|||||||
# Compilers and tools
|
# Compilers and tools
|
||||||
GO ?= $(shell which go)
|
GO ?= $(shell which go)
|
||||||
|
|
||||||
|
# Build-time variables
|
||||||
|
ROOT_COMPILATION_UID ?= 65534
|
||||||
|
ROOT_COMPILATION_GID ?= 65534
|
||||||
|
|
||||||
build:
|
build:
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
cd src/bpm; $(GO) build -ldflags "-w" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm
|
cd src/bpm; $(GO) build -ldflags "-w -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationUID=$(ROOT_COMPILATION_UID)' -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationGID=$(ROOT_COMPILATION_GID)'" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm
|
||||||
|
|
||||||
install: build/bpm config/
|
install: build/bpm config/
|
||||||
# Create directories
|
# Create directories
|
||||||
|
@ -30,7 +30,7 @@ You are able to install bpm packages by typing the following:
|
|||||||
```sh
|
```sh
|
||||||
bpm install /path/to/package.bpm
|
bpm install /path/to/package.bpm
|
||||||
```
|
```
|
||||||
You can also use the package name directly if using repositories
|
You can also use the package name directly if using databases
|
||||||
```sh
|
```sh
|
||||||
bpm install package_name
|
bpm install package_name
|
||||||
```
|
```
|
||||||
@ -45,12 +45,12 @@ You can remove an installed package by typing the following
|
|||||||
bpm remove package_name
|
bpm remove package_name
|
||||||
```
|
```
|
||||||
|
|
||||||
To remove all unused dependencies try using the cleanup command
|
To remove all unused dependencies and clean cached files try using the cleanup command
|
||||||
```sh
|
```sh
|
||||||
bpm cleanup
|
bpm cleanup
|
||||||
```
|
```
|
||||||
|
|
||||||
If using repositories, all packages can be updated using this simple command
|
If using databases, all packages can be updated using this simple command
|
||||||
```sh
|
```sh
|
||||||
bpm update
|
bpm update
|
||||||
```
|
```
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
ignore_packages: []
|
ignore_packages: []
|
||||||
repositories:
|
privilege_escalator_cmd: "sudo"
|
||||||
- name: example-repository
|
compilation_env: []
|
||||||
source: https://my-repo.xyz/
|
cleanup_make_dependencies: true
|
||||||
|
databases:
|
||||||
|
- name: example-database
|
||||||
|
source: https://my-database.xyz/
|
||||||
disabled: true
|
disabled: true
|
371
src/bpm/main.go
371
src/bpm/main.go
@ -8,6 +8,8 @@ import (
|
|||||||
"git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib"
|
"git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
@ -37,7 +39,15 @@ var installationReason = ""
|
|||||||
var nosync = true
|
var nosync = true
|
||||||
var removeUnused = false
|
var removeUnused = false
|
||||||
var doCleanup = false
|
var doCleanup = false
|
||||||
var showRepoInfo = false
|
var showDatabaseInfo = false
|
||||||
|
var installSrcPkgDepends = false
|
||||||
|
var skipChecks = false
|
||||||
|
var outputDirectory = ""
|
||||||
|
var cleanupDependencies = false
|
||||||
|
var cleanupMakeDependencies = false
|
||||||
|
var cleanupCompilationFiles = false
|
||||||
|
var cleanupCompiledPackages = false
|
||||||
|
var cleanupFetchedPackages = false
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err := bpmlib.ReadConfig()
|
err := bpmlib.ReadConfig()
|
||||||
@ -62,6 +72,7 @@ const (
|
|||||||
remove
|
remove
|
||||||
cleanup
|
cleanup
|
||||||
file
|
file
|
||||||
|
compile
|
||||||
)
|
)
|
||||||
|
|
||||||
func getCommandType() commandType {
|
func getCommandType() commandType {
|
||||||
@ -86,6 +97,8 @@ func getCommandType() commandType {
|
|||||||
return cleanup
|
return cleanup
|
||||||
case "file":
|
case "file":
|
||||||
return file
|
return file
|
||||||
|
case "compile":
|
||||||
|
return compile
|
||||||
default:
|
default:
|
||||||
return help
|
return help
|
||||||
}
|
}
|
||||||
@ -104,7 +117,7 @@ func resolveCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read local databases
|
// Read local databases
|
||||||
err := bpmlib.ReadLocalDatabases()
|
err := bpmlib.ReadLocalDatabaseFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
log.Fatalf("Error: could not read local databases: %s", err)
|
||||||
}
|
}
|
||||||
@ -112,13 +125,14 @@ func resolveCommand() {
|
|||||||
for n, pkg := range packages {
|
for n, pkg := range packages {
|
||||||
var info *bpmlib.PackageInfo
|
var info *bpmlib.PackageInfo
|
||||||
isFile := false
|
isFile := false
|
||||||
if showRepoInfo {
|
showInstallationReason := false
|
||||||
|
if showDatabaseInfo {
|
||||||
var err error
|
var err error
|
||||||
var entry *bpmlib.RepositoryEntry
|
var entry *bpmlib.BPMDatabaseEntry
|
||||||
entry, _, err = bpmlib.GetRepositoryEntry(pkg)
|
entry, _, err = bpmlib.GetDatabaseEntry(pkg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if entry = bpmlib.ResolveVirtualPackage(pkg); entry == nil {
|
if entry = bpmlib.ResolveVirtualPackage(pkg); entry == nil {
|
||||||
log.Fatalf("Error: could not find package (%s) in any repository\n", pkg)
|
log.Fatalf("Error: could not find package (%s) in any database\n", pkg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info = entry.Info
|
info = entry.Info
|
||||||
@ -135,6 +149,7 @@ func resolveCommand() {
|
|||||||
} else {
|
} else {
|
||||||
info = bpmlib.GetPackageInfo(pkg, rootDir)
|
info = bpmlib.GetPackageInfo(pkg, rootDir)
|
||||||
}
|
}
|
||||||
|
showInstallationReason = true
|
||||||
}
|
}
|
||||||
if info == nil {
|
if info == nil {
|
||||||
log.Fatalf("Error: package (%s) is not installed\n", pkg)
|
log.Fatalf("Error: package (%s) is not installed\n", pkg)
|
||||||
@ -149,11 +164,11 @@ func resolveCommand() {
|
|||||||
}
|
}
|
||||||
fmt.Println("File: " + abs)
|
fmt.Println("File: " + abs)
|
||||||
}
|
}
|
||||||
fmt.Println(bpmlib.CreateReadableInfo(true, true, true, info, rootDir))
|
fmt.Println(bpmlib.CreateReadableInfo(true, true, true, showInstallationReason, info, rootDir))
|
||||||
}
|
}
|
||||||
case list:
|
case list:
|
||||||
// Read local databases
|
// Read local databases
|
||||||
err := bpmlib.ReadLocalDatabases()
|
err := bpmlib.ReadLocalDatabaseFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
log.Fatalf("Error: could not read local databases: %s", err)
|
||||||
}
|
}
|
||||||
@ -183,7 +198,7 @@ func resolveCommand() {
|
|||||||
if n != 0 {
|
if n != 0 {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
fmt.Println(bpmlib.CreateReadableInfo(true, true, true, info, rootDir))
|
fmt.Println(bpmlib.CreateReadableInfo(true, true, true, true, info, rootDir))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case search:
|
case search:
|
||||||
@ -193,7 +208,7 @@ func resolveCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read local databases
|
// Read local databases
|
||||||
err := bpmlib.ReadLocalDatabases()
|
err := bpmlib.ReadLocalDatabaseFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
log.Fatalf("Error: could not read local databases: %s", err)
|
||||||
}
|
}
|
||||||
@ -201,8 +216,8 @@ func resolveCommand() {
|
|||||||
for i, term := range searchTerms {
|
for i, term := range searchTerms {
|
||||||
nameResults := make([]*bpmlib.PackageInfo, 0)
|
nameResults := make([]*bpmlib.PackageInfo, 0)
|
||||||
descResults := make([]*bpmlib.PackageInfo, 0)
|
descResults := make([]*bpmlib.PackageInfo, 0)
|
||||||
for _, repo := range bpmlib.BPMConfig.Repositories {
|
for _, db := range bpmlib.BPMConfig.Databases {
|
||||||
for _, entry := range repo.Entries {
|
for _, entry := range db.Entries {
|
||||||
if strings.Contains(entry.Info.Name, term) {
|
if strings.Contains(entry.Info.Name, term) {
|
||||||
nameResults = append(nameResults, entry.Info)
|
nameResults = append(nameResults, entry.Info)
|
||||||
} else if strings.Contains(entry.Info.Description, term) {
|
} else if strings.Contains(entry.Info.Description, term) {
|
||||||
@ -235,12 +250,14 @@ func resolveCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if installationReason argument is valid
|
// Check if installationReason argument is valid
|
||||||
ir := bpmlib.InstallationReasonUnknown
|
ir := bpmlib.InstallationReasonManual
|
||||||
switch installationReason {
|
switch installationReason {
|
||||||
case "manual":
|
case "manual":
|
||||||
ir = bpmlib.InstallationReasonManual
|
ir = bpmlib.InstallationReasonManual
|
||||||
case "dependency":
|
case "dependency":
|
||||||
ir = bpmlib.InstallationReasonDependency
|
ir = bpmlib.InstallationReasonDependency
|
||||||
|
case "make-dependency":
|
||||||
|
ir = bpmlib.InstallationReasonMakeDependency
|
||||||
case "":
|
case "":
|
||||||
default:
|
default:
|
||||||
log.Fatalf("Error: %s is not a valid installation reason", installationReason)
|
log.Fatalf("Error: %s is not a valid installation reason", installationReason)
|
||||||
@ -257,7 +274,7 @@ func resolveCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read local databases
|
// Read local databases
|
||||||
err := bpmlib.ReadLocalDatabases()
|
err := bpmlib.ReadLocalDatabaseFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
log.Fatalf("Error: could not read local databases: %s", err)
|
||||||
}
|
}
|
||||||
@ -315,7 +332,7 @@ func resolveCommand() {
|
|||||||
|
|
||||||
// Read local databases if no sync
|
// Read local databases if no sync
|
||||||
if nosync {
|
if nosync {
|
||||||
err := bpmlib.ReadLocalDatabases()
|
err := bpmlib.ReadLocalDatabaseFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
log.Fatalf("Error: could not read local databases: %s", err)
|
||||||
}
|
}
|
||||||
@ -397,13 +414,13 @@ func resolveCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read local databases
|
// Read local databases
|
||||||
err := bpmlib.ReadLocalDatabases()
|
err := bpmlib.ReadLocalDatabaseFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
log.Fatalf("Error: could not read local databases: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create remove operation
|
// Create remove operation
|
||||||
operation, err := bpmlib.RemovePackages(rootDir, removeUnused, doCleanup, verbose, subcommandArgs...)
|
operation, err := bpmlib.RemovePackages(rootDir, removeUnused, doCleanup, subcommandArgs...)
|
||||||
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
|
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
|
||||||
log.Fatalf("Error: %s", err)
|
log.Fatalf("Error: %s", err)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@ -448,51 +465,58 @@ func resolveCommand() {
|
|||||||
log.Fatalf("Error: this subcommand needs to be run with superuser permissions")
|
log.Fatalf("Error: this subcommand needs to be run with superuser permissions")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read local databases
|
err := bpmlib.CleanupCache(rootDir, cleanupCompilationFiles, cleanupCompiledPackages, cleanupFetchedPackages, verbose)
|
||||||
err := bpmlib.ReadLocalDatabases()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
log.Fatalf("Error: could not complete cache cleanup: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create cleanup operation
|
if cleanupDependencies || cleanupMakeDependencies {
|
||||||
operation, err := bpmlib.CleanupPackages(rootDir, verbose)
|
// Read local databases
|
||||||
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
|
err := bpmlib.ReadLocalDatabaseFiles()
|
||||||
log.Fatalf("Error: %s", err)
|
if err != nil {
|
||||||
} else if err != nil {
|
log.Fatalf("Error: could not read local databases: %s", err)
|
||||||
log.Fatalf("Error: could not setup operation: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit if operation contains no actions
|
|
||||||
if len(operation.Actions) == 0 {
|
|
||||||
fmt.Println("No action needs to be taken")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show operation summary
|
|
||||||
operation.ShowOperationSummary()
|
|
||||||
|
|
||||||
// Confirmation Prompt
|
|
||||||
if !yesAll {
|
|
||||||
fmt.Printf("Are you sure you wish to remove all %d packages? [y\\N] ", len(operation.Actions))
|
|
||||||
reader := bufio.NewReader(os.Stdin)
|
|
||||||
text, _ := reader.ReadString('\n')
|
|
||||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
|
||||||
fmt.Println("Cancelling package removal...")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Execute operation
|
// Create cleanup operation
|
||||||
err = operation.Execute(verbose, force)
|
operation, err := bpmlib.CleanupPackages(cleanupMakeDependencies, rootDir)
|
||||||
if err != nil {
|
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
|
||||||
log.Fatalf("Error: could not complete operation: %s\n", err)
|
log.Fatalf("Error: %s", err)
|
||||||
}
|
} else if err != nil {
|
||||||
|
log.Fatalf("Error: could not setup operation: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Executing hooks
|
// Exit if operation contains no actions
|
||||||
fmt.Println("Running hooks...")
|
if len(operation.Actions) == 0 {
|
||||||
err = operation.RunHooks(verbose)
|
fmt.Println("No action needs to be taken")
|
||||||
if err != nil {
|
return
|
||||||
log.Fatalf("Error: could not run hooks: %s\n", err)
|
}
|
||||||
|
|
||||||
|
// Show operation summary
|
||||||
|
operation.ShowOperationSummary()
|
||||||
|
|
||||||
|
// Confirmation Prompt
|
||||||
|
if !yesAll {
|
||||||
|
fmt.Printf("Are you sure you wish to remove all %d packages? [y\\N] ", len(operation.Actions))
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
text, _ := reader.ReadString('\n')
|
||||||
|
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
||||||
|
fmt.Println("Cancelling package removal...")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute operation
|
||||||
|
err = operation.Execute(verbose, force)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not complete operation: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Executing hooks
|
||||||
|
fmt.Println("Running hooks...")
|
||||||
|
err = operation.RunHooks(verbose)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not run hooks: %s\n", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case file:
|
case file:
|
||||||
files := subcommandArgs
|
files := subcommandArgs
|
||||||
@ -528,7 +552,7 @@ func resolveCommand() {
|
|||||||
|
|
||||||
var pkgList []string
|
var pkgList []string
|
||||||
for _, pkg := range pkgs {
|
for _, pkg := range pkgs {
|
||||||
if slices.ContainsFunc(bpmlib.GetPackageFiles(pkg, rootDir), func(entry *bpmlib.PackageFileEntry) bool {
|
if slices.ContainsFunc(bpmlib.GetPackage(pkg, rootDir).PkgFiles, func(entry *bpmlib.PackageFileEntry) bool {
|
||||||
return entry.Path == absFile
|
return entry.Path == absFile
|
||||||
}) {
|
}) {
|
||||||
pkgList = append(pkgList, pkg)
|
pkgList = append(pkgList, pkg)
|
||||||
@ -543,6 +567,169 @@ func resolveCommand() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case compile:
|
||||||
|
if len(subcommandArgs) == 0 {
|
||||||
|
fmt.Println("No source packages were given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read local databases
|
||||||
|
err := bpmlib.ReadLocalDatabaseFiles()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not read local databases: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile packages
|
||||||
|
for _, sourcePackage := range subcommandArgs {
|
||||||
|
if _, err := os.Stat(sourcePackage); os.IsNotExist(err) {
|
||||||
|
log.Fatalf("Error: file (%s) does not exist!", sourcePackage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read archive
|
||||||
|
bpmpkg, err := bpmlib.ReadPackage(sourcePackage)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not read package (%s): %s", sourcePackage, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure archive is source BPM package
|
||||||
|
if bpmpkg.PkgInfo.Type != "source" {
|
||||||
|
log.Fatalf("Error: cannot compile a non-source package!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get direct runtime and make dependencies
|
||||||
|
totalDepends := make([]string, 0)
|
||||||
|
for depend := range bpmpkg.PkgInfo.GetDependencies(true, false) {
|
||||||
|
if !slices.Contains(totalDepends, depend) {
|
||||||
|
totalDepends = append(totalDepends, depend)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get unmet dependencies
|
||||||
|
unmetDepends := slices.Clone(totalDepends)
|
||||||
|
installedPackages, err := bpmlib.GetInstalledPackages("/")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not get installed packages: %s\n", err)
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
unmetDepends = append(unmetDepends[:i], unmetDepends[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install missing source package dependencies
|
||||||
|
if installSrcPkgDepends && len(unmetDepends) > 0 {
|
||||||
|
// Get path to current executable
|
||||||
|
executable, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not get path to executable: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run 'bpm install' using the set privilege escalator command
|
||||||
|
args := []string{executable, "install", "--installation-reason=make-dependency"}
|
||||||
|
args = append(args, unmetDepends...)
|
||||||
|
cmd := exec.Command(bpmlib.BPMConfig.PrivilegeEscalatorCmd, args...)
|
||||||
|
if yesAll {
|
||||||
|
cmd.Args = slices.Insert(cmd.Args, 3, "-y")
|
||||||
|
}
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
if verbose {
|
||||||
|
fmt.Println("Running command: " + cmd.String())
|
||||||
|
}
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: dependency installation command failed: %s\n", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Ensure the required dependencies are installed
|
||||||
|
if len(unmetDepends) != 0 {
|
||||||
|
log.Fatalf("Error: could not resolve dependencies: the following dependencies were not found in any databases: " + strings.Join(unmetDepends, ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current working directory
|
||||||
|
workdir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not get working directory: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user home directory
|
||||||
|
homedir, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not get user home directory: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim output directory
|
||||||
|
outputDirectory = strings.TrimSpace(outputDirectory)
|
||||||
|
if outputDirectory != "/" {
|
||||||
|
outputDirectory = strings.TrimSuffix(outputDirectory, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set output directory if empty
|
||||||
|
if outputDirectory == "" {
|
||||||
|
outputDirectory = workdir
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace first tilde with user home directory
|
||||||
|
if strings.Split(outputDirectory, "/")[0] == "~" {
|
||||||
|
outputDirectory = strings.Replace(outputDirectory, "~", homedir, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepend current working directory to output directory if not an absolute path
|
||||||
|
if outputDirectory != "" && !strings.HasPrefix(outputDirectory, "/") {
|
||||||
|
outputDirectory = filepath.Join(workdir, outputDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean path
|
||||||
|
path.Clean(outputDirectory)
|
||||||
|
|
||||||
|
// Ensure output directory exists and is a directory
|
||||||
|
stat, err := os.Stat(outputDirectory)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not stat output directory (%s): %s", outputDirectory, err)
|
||||||
|
}
|
||||||
|
if !stat.IsDir() {
|
||||||
|
log.Fatalf("Error: output directory (%s) is not a directory", outputDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
outputBpmPackages, err := bpmlib.CompileSourcePackage(sourcePackage, outputDirectory, skipChecks)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not compile source package (%s): %s", sourcePackage, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range outputBpmPackages {
|
||||||
|
fmt.Printf("Package (%s) was successfully compiled! Binary package generated at: %s\n", k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove unused packages
|
||||||
|
if installSrcPkgDepends && len(unmetDepends) > 0 {
|
||||||
|
// Get path to current executable
|
||||||
|
executable, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not get path to executable: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run 'bpm cleanup' using the set privilege escalator command
|
||||||
|
cmd := exec.Command(bpmlib.BPMConfig.PrivilegeEscalatorCmd, executable, "cleanup")
|
||||||
|
if yesAll {
|
||||||
|
cmd.Args = slices.Insert(cmd.Args, 3, "-y")
|
||||||
|
}
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
if verbose {
|
||||||
|
fmt.Println("Running command: " + cmd.String())
|
||||||
|
}
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: dependency cleanup command failed: %s\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
printHelp()
|
printHelp()
|
||||||
}
|
}
|
||||||
@ -554,25 +741,24 @@ func printHelp() {
|
|||||||
fmt.Println("-> flags will be read if passed right after the subcommand otherwise they will be read as subcommand arguments")
|
fmt.Println("-> flags will be read if passed right after the subcommand otherwise they will be read as subcommand arguments")
|
||||||
fmt.Println("\033[1m---- Command List ----\033[0m")
|
fmt.Println("\033[1m---- Command List ----\033[0m")
|
||||||
fmt.Println("-> bpm version | shows information on the installed version of bpm")
|
fmt.Println("-> bpm version | shows information on the installed version of bpm")
|
||||||
fmt.Println("-> bpm info [-R, --repos] <packages...> | shows information on an installed package")
|
fmt.Println("-> bpm info [-R, --databases] <packages...> | shows information on an installed package")
|
||||||
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
||||||
fmt.Println(" --repos show information on package in repository")
|
fmt.Println(" --databases show information on package in configured databases")
|
||||||
fmt.Println("-> bpm list [-R, -c, -n] | lists all installed packages")
|
fmt.Println("-> bpm list [-R, -c, -n] | lists all installed packages")
|
||||||
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
||||||
fmt.Println(" -c lists the amount of installed packages")
|
fmt.Println(" -c lists the amount of installed packages")
|
||||||
fmt.Println(" -n lists only the names of installed packages")
|
fmt.Println(" -n lists only the names of installed packages")
|
||||||
fmt.Println("-> bpm search <search terms...> | Searches for packages through declared repositories")
|
fmt.Println("-> bpm search <search terms...> | Searches for packages through configured databases")
|
||||||
fmt.Println("-> bpm install [-R, -v, -y, -f, -o, -c, -b, -k, --reinstall, --reinstall-all, --no-optional, --installation-reason] <packages...> | installs the following files")
|
fmt.Println("-> bpm install [-R, -v, -y, -f, --reinstall, --reinstall-all, --no-optional, --installation-reason] <packages...> | installs the following files")
|
||||||
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
||||||
fmt.Println(" -v Show additional information about what BPM is doing")
|
fmt.Println(" -v Show additional information about what BPM is doing")
|
||||||
fmt.Println(" -y skips the confirmation prompt")
|
fmt.Println(" -y skips the confirmation prompt")
|
||||||
fmt.Println(" -f skips dependency, conflict and architecture checking")
|
fmt.Println(" -f skips dependency, conflict and architecture checking")
|
||||||
fmt.Println(" -k keeps the compilation directory created by BPM after source package installation")
|
|
||||||
fmt.Println(" --reinstall Reinstalls packages even if they do not have a newer version available")
|
fmt.Println(" --reinstall Reinstalls packages even if they do not have a newer version available")
|
||||||
fmt.Println(" --reinstall-all Same as --reinstall but also reinstalls dependencies")
|
fmt.Println(" --reinstall-all Same as --reinstall but also reinstalls dependencies")
|
||||||
fmt.Println(" --no-optional Prevents installation of optional dependencies")
|
fmt.Println(" --no-optional Prevents installation of optional dependencies")
|
||||||
fmt.Println(" --installation-reason=<manual/dependency> sets the installation reason for all newly installed packages")
|
fmt.Println(" --installation-reason=<manual/dependency> sets the installation reason for all newly installed packages")
|
||||||
fmt.Println("-> bpm update [-R, -v, -y, -f, --reinstall, --no-sync] | updates all packages that are available in the repositories")
|
fmt.Println("-> bpm update [-R, -v, -y, -f, --reinstall, --no-sync] | updates all packages that are available in the configured databases")
|
||||||
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
||||||
fmt.Println(" -v Show additional information about what BPM is doing")
|
fmt.Println(" -v Show additional information about what BPM is doing")
|
||||||
fmt.Println(" -y skips the confirmation prompt")
|
fmt.Println(" -y skips the confirmation prompt")
|
||||||
@ -589,12 +775,24 @@ func printHelp() {
|
|||||||
fmt.Println(" -y skips the confirmation prompt")
|
fmt.Println(" -y skips the confirmation prompt")
|
||||||
fmt.Println(" --unused removes only packages that aren't required as dependencies by other packages")
|
fmt.Println(" --unused removes only packages that aren't required as dependencies by other packages")
|
||||||
fmt.Println(" --cleanup performs a dependency cleanup")
|
fmt.Println(" --cleanup performs a dependency cleanup")
|
||||||
fmt.Println("-> bpm cleanup [-R, -v, -y] | remove all unused dependency packages")
|
fmt.Println("-> bpm cleanup [-R, -v, -y, --depends, --compilation-files, --compiled-pkgs, --fetched-pkgs] | remove all unused dependencies and cache directories")
|
||||||
fmt.Println(" -v Show additional information about what BPM is doing")
|
fmt.Println(" -v Show additional information about what BPM is doing")
|
||||||
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
||||||
fmt.Println(" -y skips the confirmation prompt")
|
fmt.Println(" -y skips the confirmation prompt")
|
||||||
|
fmt.Println(" --depends performs a dependency cleanup")
|
||||||
|
fmt.Println(" --make-depends performs a make dependency cleanup")
|
||||||
|
fmt.Println(" --compilation-files performs a cleanup of compilation files")
|
||||||
|
fmt.Println(" --compiled-pkgs performs a cleanup of compilation compiled binary packages")
|
||||||
|
fmt.Println(" --fetched-pkgs performs a cleanup of fetched packages from databases")
|
||||||
fmt.Println("-> bpm file [-R] <files...> | shows what packages the following packages are managed by")
|
fmt.Println("-> bpm file [-R] <files...> | shows what packages the following packages are managed by")
|
||||||
fmt.Println(" -R=<root_path> lets you define the root path which will be used")
|
fmt.Println(" -R=<root_path> lets you define the root path which will be used")
|
||||||
|
fmt.Println("-> bpm compile [-d, -s, -o] <source packages...> | Compile source BPM package")
|
||||||
|
fmt.Println(" -v Show additional information about what BPM is doing")
|
||||||
|
fmt.Println(" -d installs required dependencies for package compilation")
|
||||||
|
fmt.Println(" -s skips the check function in source.sh scripts")
|
||||||
|
fmt.Println(" -o sets output directory")
|
||||||
|
fmt.Println(" -y skips the confirmation prompt")
|
||||||
|
|
||||||
fmt.Println("\033[1m----------------\033[0m")
|
fmt.Println("\033[1m----------------\033[0m")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,7 +806,7 @@ func resolveFlags() {
|
|||||||
// Info flags
|
// Info flags
|
||||||
infoFlagSet := flag.NewFlagSet("Info flags", flag.ExitOnError)
|
infoFlagSet := flag.NewFlagSet("Info flags", flag.ExitOnError)
|
||||||
infoFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
infoFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
||||||
infoFlagSet.BoolVar(&showRepoInfo, "repos", false, "Show information on package in repository")
|
infoFlagSet.BoolVar(&showDatabaseInfo, "databases", false, "Show information on package in configured databases")
|
||||||
infoFlagSet.Usage = printHelp
|
infoFlagSet.Usage = printHelp
|
||||||
// Install flags
|
// Install flags
|
||||||
installFlagSet := flag.NewFlagSet("Install flags", flag.ExitOnError)
|
installFlagSet := flag.NewFlagSet("Install flags", flag.ExitOnError)
|
||||||
@ -648,11 +846,35 @@ func resolveFlags() {
|
|||||||
cleanupFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
cleanupFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
||||||
cleanupFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing")
|
cleanupFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing")
|
||||||
cleanupFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
|
cleanupFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
|
||||||
|
cleanupFlagSet.BoolVar(&cleanupDependencies, "depends", false, "Perform a dependency cleanup")
|
||||||
|
cleanupFlagSet.BoolVar(&cleanupMakeDependencies, "make-depends", false, "Perform a make dependency cleanup")
|
||||||
|
cleanupFlagSet.BoolVar(&cleanupCompilationFiles, "compilation-files", false, "Perform a cleanup of compilation files")
|
||||||
|
cleanupFlagSet.BoolVar(&cleanupCompiledPackages, "compiled-pkgs", false, "Perform a cleanup of compilation compiled binary packages")
|
||||||
|
cleanupFlagSet.BoolVar(&cleanupFetchedPackages, "fetched-pkgs", false, "Perform a cleanup of fetched packages from databases")
|
||||||
cleanupFlagSet.Usage = printHelp
|
cleanupFlagSet.Usage = printHelp
|
||||||
// File flags
|
// File flags
|
||||||
fileFlagSet := flag.NewFlagSet("Remove flags", flag.ExitOnError)
|
fileFlagSet := flag.NewFlagSet("Remove flags", flag.ExitOnError)
|
||||||
fileFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
fileFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
||||||
fileFlagSet.Usage = printHelp
|
fileFlagSet.Usage = printHelp
|
||||||
|
// Compile flags
|
||||||
|
compileFlagSet := flag.NewFlagSet("Compile flags", flag.ExitOnError)
|
||||||
|
compileFlagSet.BoolVar(&installSrcPkgDepends, "d", false, "Install required dependencies for package compilation")
|
||||||
|
compileFlagSet.BoolVar(&skipChecks, "s", false, "Skip the check function in source.sh scripts")
|
||||||
|
compileFlagSet.StringVar(&outputDirectory, "o", "", "Set output directory")
|
||||||
|
compileFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing")
|
||||||
|
compileFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
|
||||||
|
compileFlagSet.Usage = printHelp
|
||||||
|
|
||||||
|
isFlagSet := func(flagSet *flag.FlagSet, name string) bool {
|
||||||
|
found := false
|
||||||
|
flagSet.Visit(func(f *flag.Flag) {
|
||||||
|
if f.Name == name {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
if len(os.Args[1:]) <= 0 {
|
if len(os.Args[1:]) <= 0 {
|
||||||
subcommand = "help"
|
subcommand = "help"
|
||||||
} else {
|
} else {
|
||||||
@ -694,12 +916,31 @@ func resolveFlags() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
subcommandArgs = removeFlagSet.Args()
|
subcommandArgs = removeFlagSet.Args()
|
||||||
|
} else if getCommandType() == cleanup {
|
||||||
|
err := cleanupFlagSet.Parse(subcommandArgs)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !isFlagSet(cleanupFlagSet, "depends") && !isFlagSet(cleanupFlagSet, "make-depends") && !isFlagSet(cleanupFlagSet, "compilation-files") && !isFlagSet(cleanupFlagSet, "compiled-pkgs") && !isFlagSet(cleanupFlagSet, "fetched-pkgs") {
|
||||||
|
cleanupDependencies = true
|
||||||
|
cleanupMakeDependencies = bpmlib.BPMConfig.CleanupMakeDependencies
|
||||||
|
cleanupCompilationFiles = true
|
||||||
|
cleanupCompiledPackages = true
|
||||||
|
cleanupFetchedPackages = true
|
||||||
|
}
|
||||||
|
subcommandArgs = cleanupFlagSet.Args()
|
||||||
} else if getCommandType() == file {
|
} else if getCommandType() == file {
|
||||||
err := fileFlagSet.Parse(subcommandArgs)
|
err := fileFlagSet.Parse(subcommandArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
subcommandArgs = fileFlagSet.Args()
|
subcommandArgs = fileFlagSet.Args()
|
||||||
|
} else if getCommandType() == compile {
|
||||||
|
err := compileFlagSet.Parse(subcommandArgs)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
subcommandArgs = compileFlagSet.Args()
|
||||||
}
|
}
|
||||||
if reinstallAll {
|
if reinstallAll {
|
||||||
reinstall = true
|
reinstall = true
|
||||||
|
382
src/bpmlib/compilation.go
Normal file
382
src/bpmlib/compilation.go
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
package bpmlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var rootCompilationUID = "65534"
|
||||||
|
var rootCompilationGID = "65534"
|
||||||
|
|
||||||
|
func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bool) (outputBpmPackages map[string]string, err error) {
|
||||||
|
// Initialize map
|
||||||
|
outputBpmPackages = make(map[string]string)
|
||||||
|
|
||||||
|
// Read BPM archive
|
||||||
|
bpmpkg, err := ReadPackage(archiveFilename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure package type is 'source'
|
||||||
|
if bpmpkg.PkgInfo.Type != "source" {
|
||||||
|
return nil, errors.New("cannot compile a non-source package")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read compilation options file in current directory
|
||||||
|
compilationOptions, err := readCompilationOptionsFile()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get HOME directory
|
||||||
|
homeDir, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 nil, fmt.Errorf("could not convert UID '%s' to int", rootCompilationUID)
|
||||||
|
}
|
||||||
|
_gid, err := strconv.ParseInt(rootCompilationGID, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not convert GID '%s' to int", rootCompilationGID)
|
||||||
|
}
|
||||||
|
uid = int(_uid)
|
||||||
|
gid = int(_gid)
|
||||||
|
} else {
|
||||||
|
uid = os.Getuid()
|
||||||
|
gid = os.Getgid()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set temporary directory
|
||||||
|
var tempDirectory string
|
||||||
|
if os.Getuid() == 0 {
|
||||||
|
tempDirectory = path.Join("/var/cache/bpm/compilation/", bpmpkg.PkgInfo.Name)
|
||||||
|
} else {
|
||||||
|
tempDirectory = path.Join(homeDir, ".cache/bpm/compilation/", bpmpkg.PkgInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure temporary directory does not exist
|
||||||
|
if _, err := os.Stat(tempDirectory); err == nil {
|
||||||
|
err := os.RemoveAll(tempDirectory)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create temporary directory
|
||||||
|
err = os.MkdirAll(tempDirectory, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change temporary directory owner
|
||||||
|
err = os.Chown(tempDirectory, uid, gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract source.sh file
|
||||||
|
err = extractTarballFile(archiveFilename, "source.sh", tempDirectory, uid, gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get package scripts and extract them
|
||||||
|
packageScripts := getPackageScripts(archiveFilename)
|
||||||
|
for _, script := range packageScripts {
|
||||||
|
err = extractTarballFile(archiveFilename, script, tempDirectory, uid, gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract source files
|
||||||
|
err = extractTarballDirectory(archiveFilename, "source-files", tempDirectory, uid, gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create source directory
|
||||||
|
err = os.Mkdir(path.Join(tempDirectory, "source"), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change source directory owner
|
||||||
|
err = os.Chown(path.Join(tempDirectory, "source"), uid, gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup environment for commands
|
||||||
|
env := os.Environ()
|
||||||
|
env = append(env, "HOME="+tempDirectory)
|
||||||
|
env = append(env, "BPM_WORKDIR="+tempDirectory)
|
||||||
|
env = append(env, "BPM_SOURCE="+path.Join(tempDirectory, "source"))
|
||||||
|
env = append(env, "BPM_OUTPUT="+path.Join(tempDirectory, "output"))
|
||||||
|
env = append(env, "BPM_PKG_NAME="+bpmpkg.PkgInfo.Name)
|
||||||
|
env = append(env, "BPM_PKG_VERSION="+bpmpkg.PkgInfo.Version)
|
||||||
|
env = append(env, "BPM_PKG_REVISION="+strconv.Itoa(bpmpkg.PkgInfo.Revision))
|
||||||
|
// Check for architecture override in compilation options
|
||||||
|
if val, ok := compilationOptions["ARCH"]; ok {
|
||||||
|
env = append(env, "BPM_PKG_ARCH="+val)
|
||||||
|
} else {
|
||||||
|
env = append(env, "BPM_PKG_ARCH="+GetArch())
|
||||||
|
}
|
||||||
|
env = append(env, BPMConfig.CompilationEnvironment...)
|
||||||
|
|
||||||
|
// Execute prepare and build functions in source.sh script
|
||||||
|
cmd := exec.Command("bash", "-c",
|
||||||
|
"set -a\n"+ // Source and export functions and variables in source.sh script
|
||||||
|
". \"${BPM_WORKDIR}\"/source.sh\n"+
|
||||||
|
"set +a\n"+
|
||||||
|
"[[ $(type -t prepare) == \"function\" ]] && { echo \"Running prepare() function\"; bash -e -c 'cd \"$BPM_WORKDIR\" && prepare' || exit 1; }\n"+ // Run prepare() function if it exists
|
||||||
|
"[[ $(type -t build) == \"function\" ]] && { echo \"Running build() function\"; bash -e -c 'cd \"$BPM_SOURCE\" && build' || exit 1; }\n"+ // Run build() function if it exists
|
||||||
|
"exit 0")
|
||||||
|
cmd.Dir = tempDirectory
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Env = env
|
||||||
|
if os.Getuid() == 0 {
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
|
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
|
||||||
|
}
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute check function in source.sh script if not skipping checks
|
||||||
|
if !skipChecks {
|
||||||
|
cmd = exec.Command("bash", "-c",
|
||||||
|
"set -a\n"+ // Source and export functions and variables in source.sh script
|
||||||
|
". \"${BPM_WORKDIR}\"/source.sh\n"+
|
||||||
|
"set +a\n"+
|
||||||
|
"[[ $(type -t check) == \"function\" ]] && { echo \"Running check() function\"; bash -e -c 'cd \"$BPM_SOURCE\" && check' || exit 1; }\n"+ // Run check() function if it exists
|
||||||
|
"exit 0")
|
||||||
|
cmd.Dir = tempDirectory
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Env = env
|
||||||
|
if os.Getuid() == 0 {
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
|
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
|
||||||
|
}
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all packages to compile
|
||||||
|
packagesToCompile := bpmpkg.PkgInfo.SplitPackages
|
||||||
|
if !bpmpkg.PkgInfo.IsSplitPackage() {
|
||||||
|
packagesToCompile = append(packagesToCompile, bpmpkg.PkgInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile each package
|
||||||
|
for _, pkg := range packagesToCompile {
|
||||||
|
// Get package function name
|
||||||
|
packageFunctionName := "package"
|
||||||
|
if bpmpkg.PkgInfo.IsSplitPackage() {
|
||||||
|
packageFunctionName = "package_" + pkg.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove output directory if it already exists
|
||||||
|
if _, err := os.Stat(path.Join(tempDirectory, "output")); err == nil {
|
||||||
|
err := os.RemoveAll(path.Join(tempDirectory, "output"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new output directory
|
||||||
|
err = os.Mkdir(path.Join(tempDirectory, "output"), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change output directory owner
|
||||||
|
err = os.Chown(path.Join(tempDirectory, "output"), uid, gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute package function in source.sh script and generate package file list
|
||||||
|
cmd = exec.Command("bash", "-c",
|
||||||
|
"set -a\n"+ // Source and export functions and variables in source.sh script
|
||||||
|
". \"${BPM_WORKDIR}\"/source.sh\n"+
|
||||||
|
"set +a\n"+
|
||||||
|
"echo \"Running "+packageFunctionName+"() function\"\n"+
|
||||||
|
"( cd \"$BPM_SOURCE\" && fakeroot -s \"$BPM_WORKDIR\"/fakeroot_file bash -e -c '"+packageFunctionName+"' ) || exit 1\n"+ // Run package() function
|
||||||
|
"fakeroot -i \"$BPM_WORKDIR\"/fakeroot_file find \"$BPM_OUTPUT\" -mindepth 1 -printf \"%P %#m %U %G %s\\n\" > \"$BPM_WORKDIR\"/pkg.files") // Create package file list
|
||||||
|
cmd.Dir = tempDirectory
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Env = env
|
||||||
|
if os.Getuid() == 0 {
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
|
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
|
||||||
|
}
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create gzip-compressed archive for the package files
|
||||||
|
cmd = exec.Command("bash", "-c", "find output -printf \"%P\\n\" | fakeroot -i \"$BPM_WORKDIR\"/fakeroot_file tar -czf files.tar.gz --no-recursion -C output -T -")
|
||||||
|
cmd.Dir = tempDirectory
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Env = env
|
||||||
|
if os.Getuid() == 0 {
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
|
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
|
||||||
|
}
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("files.tar.gz archive could not be created: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone source package info
|
||||||
|
pkgInfo := *pkg
|
||||||
|
|
||||||
|
// Set package type to binary
|
||||||
|
pkgInfo.Type = "binary"
|
||||||
|
|
||||||
|
// Set package architecture
|
||||||
|
if val, ok := compilationOptions["ARCH"]; ok {
|
||||||
|
pkgInfo.Arch = val
|
||||||
|
} else {
|
||||||
|
pkgInfo.Arch = GetArch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove split package field
|
||||||
|
pkgInfo.SplitPackages = nil
|
||||||
|
|
||||||
|
// Marshal package info
|
||||||
|
pkgInfoBytes, err := yaml.Marshal(pkgInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pkgInfoBytes = append(pkgInfoBytes, '\n')
|
||||||
|
|
||||||
|
// Create pkg.info file
|
||||||
|
err = os.WriteFile(path.Join(tempDirectory, "pkg.info"), pkgInfoBytes, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change pkg.info file owner
|
||||||
|
err = os.Chown(path.Join(tempDirectory, "pkg.info"), uid, gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get files to include in BPM archive
|
||||||
|
bpmArchiveFiles := make([]string, 0)
|
||||||
|
bpmArchiveFiles = append(bpmArchiveFiles, "pkg.info", "pkg.files", "files.tar.gz") // Base files
|
||||||
|
bpmArchiveFiles = append(bpmArchiveFiles, packageScripts...) // Package scripts
|
||||||
|
|
||||||
|
// Create final BPM archive
|
||||||
|
cmd = exec.Command("bash", "-c", "tar -cf final-archive.bpm --owner=0 --group=0 -C \"$BPM_WORKDIR\" "+strings.Join(bpmArchiveFiles, " "))
|
||||||
|
cmd.Dir = tempDirectory
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
currentDir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cmd.Env = append(env, "CURRENT_DIR="+currentDir)
|
||||||
|
if os.Getuid() == 0 {
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
|
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
|
||||||
|
}
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("BPM archive could not be created: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove pkg.info file
|
||||||
|
err = os.Remove(path.Join(tempDirectory, "pkg.info"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set output filename
|
||||||
|
outputFilename := path.Join(outputDirectory, fmt.Sprintf("%s-%s-%d-%s.bpm", pkgInfo.Name, pkgInfo.Version, pkgInfo.Revision, pkgInfo.Arch))
|
||||||
|
|
||||||
|
// Move final BPM archive
|
||||||
|
err = os.Rename(path.Join(tempDirectory, "final-archive.bpm"), outputFilename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set final BPM archive owner
|
||||||
|
err = os.Chown(outputFilename, os.Getuid(), os.Getgid())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
outputBpmPackages[pkgInfo.Name] = outputFilename
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputBpmPackages, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCompilationOptionsFile() (options map[string]string, err error) {
|
||||||
|
// Initialize options map
|
||||||
|
options = make(map[string]string)
|
||||||
|
|
||||||
|
// Check if file compilation options file exists
|
||||||
|
stat, err := os.Stat(".compilation-options")
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure it is a regular file
|
||||||
|
if !stat.Mode().IsRegular() {
|
||||||
|
return nil, fmt.Errorf("%s is not a regular file", stat.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read file data
|
||||||
|
data, err := os.ReadFile(stat.Name())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range strings.Split(string(data), "\n") {
|
||||||
|
// Trim line
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
|
||||||
|
// Skip empty lines
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split line
|
||||||
|
split := strings.SplitN(line, "=", 2)
|
||||||
|
|
||||||
|
// Throw error if line isn't valid
|
||||||
|
if len(split) < 2 {
|
||||||
|
return nil, fmt.Errorf("invalid line in compilation-options file: '%s'", line)
|
||||||
|
}
|
||||||
|
|
||||||
|
options[split[0]] = split[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return options, nil
|
||||||
|
}
|
@ -6,8 +6,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type BPMConfigStruct struct {
|
type BPMConfigStruct struct {
|
||||||
IgnorePackages []string `yaml:"ignore_packages"`
|
IgnorePackages []string `yaml:"ignore_packages"`
|
||||||
Repositories []*Repository `yaml:"repositories"`
|
PrivilegeEscalatorCmd string `yaml:"privilege_escalator_cmd"`
|
||||||
|
CompilationEnvironment []string `yaml:"compilation_env"`
|
||||||
|
CleanupMakeDependencies bool `yaml:"cleanup_make_dependencies"`
|
||||||
|
Databases []*BPMDatabase `yaml:"databases"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var BPMConfig BPMConfigStruct
|
var BPMConfig BPMConfigStruct
|
||||||
@ -22,15 +25,17 @@ func ReadConfig() (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
BPMConfig = BPMConfigStruct{}
|
BPMConfig = BPMConfigStruct{
|
||||||
|
CleanupMakeDependencies: true,
|
||||||
|
}
|
||||||
err = yaml.Unmarshal(bytes, &BPMConfig)
|
err = yaml.Unmarshal(bytes, &BPMConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := len(BPMConfig.Repositories) - 1; i >= 0; i-- {
|
for i := len(BPMConfig.Databases) - 1; i >= 0; i-- {
|
||||||
if BPMConfig.Repositories[i].Disabled != nil && *BPMConfig.Repositories[i].Disabled {
|
if BPMConfig.Databases[i].Disabled != nil && *BPMConfig.Databases[i].Disabled {
|
||||||
BPMConfig.Repositories = append(BPMConfig.Repositories[:i], BPMConfig.Repositories[i+1:]...)
|
BPMConfig.Databases = append(BPMConfig.Databases[:i], BPMConfig.Databases[i+1:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
281
src/bpmlib/databases.go
Normal file
281
src/bpmlib/databases.go
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
package bpmlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BPMDatabase struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
Source string `yaml:"source"`
|
||||||
|
Disabled *bool `yaml:"disabled"`
|
||||||
|
Entries map[string]*BPMDatabaseEntry
|
||||||
|
VirtualPackages map[string][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BPMDatabaseEntry struct {
|
||||||
|
Info *PackageInfo `yaml:"info"`
|
||||||
|
Download string `yaml:"download"`
|
||||||
|
DownloadSize uint64 `yaml:"download_size"`
|
||||||
|
InstalledSize uint64 `yaml:"installed_size"`
|
||||||
|
Database *BPMDatabase
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *BPMDatabase) ContainsPackage(pkg string) bool {
|
||||||
|
_, ok := db.Entries[pkg]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *BPMDatabase) ReadLocalDatabase() error {
|
||||||
|
dbFile := "/var/lib/bpm/databases/" + db.Name + ".bpmdb"
|
||||||
|
if _, err := os.Stat(dbFile); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := os.ReadFile(dbFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data := string(bytes)
|
||||||
|
for _, b := range strings.Split(data, "---") {
|
||||||
|
entry := BPMDatabaseEntry{
|
||||||
|
Info: &PackageInfo{
|
||||||
|
Name: "",
|
||||||
|
Description: "",
|
||||||
|
Version: "",
|
||||||
|
Revision: 1,
|
||||||
|
Url: "",
|
||||||
|
License: "",
|
||||||
|
Arch: "",
|
||||||
|
Type: "",
|
||||||
|
Keep: make([]string, 0),
|
||||||
|
Depends: make([]string, 0),
|
||||||
|
MakeDepends: make([]string, 0),
|
||||||
|
OptionalDepends: make([]string, 0),
|
||||||
|
Conflicts: make([]string, 0),
|
||||||
|
Provides: make([]string, 0),
|
||||||
|
},
|
||||||
|
Download: "",
|
||||||
|
DownloadSize: 0,
|
||||||
|
InstalledSize: 0,
|
||||||
|
Database: db,
|
||||||
|
}
|
||||||
|
err := yaml.Unmarshal([]byte(b), &entry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create database entries
|
||||||
|
if entry.Info.IsSplitPackage() {
|
||||||
|
for _, splitPkg := range entry.Info.SplitPackages {
|
||||||
|
// Turn split package into json data
|
||||||
|
splitPkgJson, err := yaml.Marshal(splitPkg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone all main package fields onto split package
|
||||||
|
splitPkgClone := *entry.Info
|
||||||
|
|
||||||
|
// Set split package field of split package to nil
|
||||||
|
splitPkgClone.SplitPackages = nil
|
||||||
|
|
||||||
|
// Unmarshal json data back to struct
|
||||||
|
err = yaml.Unmarshal(splitPkgJson, &splitPkgClone)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force set split package version, revision and URL
|
||||||
|
splitPkgClone.Version = entry.Info.Version
|
||||||
|
splitPkgClone.Revision = entry.Info.Revision
|
||||||
|
splitPkgClone.Url = entry.Info.Url
|
||||||
|
|
||||||
|
// Create entry for split package
|
||||||
|
db.Entries[splitPkg.Name] = &BPMDatabaseEntry{
|
||||||
|
Info: &splitPkgClone,
|
||||||
|
Download: entry.Download,
|
||||||
|
DownloadSize: entry.DownloadSize,
|
||||||
|
InstalledSize: 0,
|
||||||
|
Database: db,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add virtual packages to database
|
||||||
|
for _, p := range splitPkg.Provides {
|
||||||
|
db.VirtualPackages[p] = append(db.VirtualPackages[p], splitPkg.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Create entry for package
|
||||||
|
db.Entries[entry.Info.Name] = &entry
|
||||||
|
|
||||||
|
// Add virtual packages to database
|
||||||
|
for _, p := range entry.Info.Provides {
|
||||||
|
db.VirtualPackages[p] = append(db.VirtualPackages[p], entry.Info.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *BPMDatabase) SyncLocalDatabaseFile() error {
|
||||||
|
dbFile := "/var/lib/bpm/databases/" + db.Name + ".bpmdb"
|
||||||
|
|
||||||
|
// Get URL to database
|
||||||
|
u, err := url.JoinPath(db.Source, "database.bpmdb")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve data from URL
|
||||||
|
resp, err := http.Get(u)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Load data into byte buffer
|
||||||
|
buffer, err := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
// Unmarshal data to ensure it is a valid BPM database
|
||||||
|
err = yaml.Unmarshal(buffer, &BPMDatabase{})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not decode database: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create parent directories to database file
|
||||||
|
err = os.MkdirAll(path.Dir(dbFile), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create file and save database data
|
||||||
|
out, err := os.Create(dbFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
_, err = out.Write(buffer)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadLocalDatabaseFiles() (err error) {
|
||||||
|
for _, db := range BPMConfig.Databases {
|
||||||
|
// Initialize struct values
|
||||||
|
db.Entries = make(map[string]*BPMDatabaseEntry)
|
||||||
|
db.VirtualPackages = make(map[string][]string)
|
||||||
|
|
||||||
|
// Read database
|
||||||
|
err = db.ReadLocalDatabase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDatabase(name string) *BPMDatabase {
|
||||||
|
for _, db := range BPMConfig.Databases {
|
||||||
|
if db.Name == name {
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDatabaseEntry(str string) (*BPMDatabaseEntry, *BPMDatabase, error) {
|
||||||
|
split := strings.Split(str, "/")
|
||||||
|
if len(split) == 1 {
|
||||||
|
pkgName := strings.TrimSpace(split[0])
|
||||||
|
if pkgName == "" {
|
||||||
|
return nil, nil, errors.New("could not find database entry for this package")
|
||||||
|
}
|
||||||
|
for _, db := range BPMConfig.Databases {
|
||||||
|
if db.ContainsPackage(pkgName) {
|
||||||
|
return db.Entries[pkgName], db, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil, errors.New("could not find database entry for this package")
|
||||||
|
} else if len(split) == 2 {
|
||||||
|
dbName := strings.TrimSpace(split[0])
|
||||||
|
pkgName := strings.TrimSpace(split[1])
|
||||||
|
if dbName == "" || pkgName == "" {
|
||||||
|
return nil, nil, errors.New("could not find database entry for this package")
|
||||||
|
}
|
||||||
|
db := GetDatabase(dbName)
|
||||||
|
if db == nil || !db.ContainsPackage(pkgName) {
|
||||||
|
return nil, nil, errors.New("could not find database entry for this package")
|
||||||
|
}
|
||||||
|
return db.Entries[pkgName], db, nil
|
||||||
|
} else {
|
||||||
|
return nil, nil, errors.New("could not find database entry for this package")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindReplacement(pkg string) *BPMDatabaseEntry {
|
||||||
|
for _, db := range BPMConfig.Databases {
|
||||||
|
for _, entry := range db.Entries {
|
||||||
|
for _, replaced := range entry.Info.Replaces {
|
||||||
|
if replaced == pkg {
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveVirtualPackage(vpkg string) *BPMDatabaseEntry {
|
||||||
|
for _, db := range BPMConfig.Databases {
|
||||||
|
if v, ok := db.VirtualPackages[vpkg]; ok {
|
||||||
|
for _, pkg := range v {
|
||||||
|
return db.Entries[pkg]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *BPMDatabase) FetchPackage(pkg string) (string, error) {
|
||||||
|
if !db.ContainsPackage(pkg) {
|
||||||
|
return "", errors.New("could not fetch package '" + pkg + "'")
|
||||||
|
}
|
||||||
|
entry := db.Entries[pkg]
|
||||||
|
URL, err := url.JoinPath(db.Source, entry.Download)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
resp, err := http.Get(URL)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
err = os.MkdirAll("/var/cache/bpm/fetched/", 0755)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
out, err := os.Create("/var/cache/bpm/fetched/" + path.Base(entry.Download))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(out, resp.Body)
|
||||||
|
return "/var/cache/bpm/fetched/" + path.Base(entry.Download), nil
|
||||||
|
}
|
173
src/bpmlib/dependencies.go
Normal file
173
src/bpmlib/dependencies.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
package bpmlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeOptionalDepends bool) map[string]InstallationReason {
|
||||||
|
allDepends := make(map[string]InstallationReason)
|
||||||
|
|
||||||
|
for _, depend := range pkgInfo.Depends {
|
||||||
|
allDepends[depend] = InstallationReasonDependency
|
||||||
|
}
|
||||||
|
if includeOptionalDepends {
|
||||||
|
for _, depend := range pkgInfo.OptionalDepends {
|
||||||
|
if _, ok := allDepends[depend]; !ok {
|
||||||
|
allDepends[depend] = InstallationReasonDependency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if includeMakeDepends {
|
||||||
|
for _, depend := range pkgInfo.MakeDepends {
|
||||||
|
if _, ok := allDepends[depend]; !ok {
|
||||||
|
allDepends[depend] = InstallationReasonMakeDependency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allDepends
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkgInfo *PackageInfo) GetAllDependencies(includeMakeDepends, includeOptionalDepends bool, rootDir string) (resolved []string) {
|
||||||
|
// Initialize slices
|
||||||
|
resolved = make([]string, 0)
|
||||||
|
unresolved := make([]string, 0)
|
||||||
|
|
||||||
|
// Call unexported function
|
||||||
|
pkgInfo.getAllDependencies(&resolved, &unresolved, includeMakeDepends, includeOptionalDepends, rootDir)
|
||||||
|
|
||||||
|
return resolved
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkgInfo *PackageInfo) getAllDependencies(resolved *[]string, unresolved *[]string, includeMakeDepends, includeOptionalDepends bool, rootDir string) {
|
||||||
|
// Add current package name to unresolved slice
|
||||||
|
*unresolved = append(*unresolved, pkgInfo.Name)
|
||||||
|
|
||||||
|
// Loop through all dependencies
|
||||||
|
for depend := range pkgInfo.GetDependencies(includeMakeDepends, includeOptionalDepends) {
|
||||||
|
if isVirtual, p := IsVirtualPackage(depend, rootDir); isVirtual {
|
||||||
|
depend = p
|
||||||
|
}
|
||||||
|
if !slices.Contains(*resolved, depend) {
|
||||||
|
// Add current dependency to resolved slice when circular dependency is detected
|
||||||
|
if slices.Contains(*unresolved, depend) {
|
||||||
|
if !slices.Contains(*resolved, depend) {
|
||||||
|
*resolved = append(*resolved, depend)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dependInfo := GetPackageInfo(depend, rootDir)
|
||||||
|
|
||||||
|
if dependInfo != nil {
|
||||||
|
dependInfo.getAllDependencies(resolved, unresolved, includeMakeDepends, includeOptionalDepends, rootDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !slices.Contains(*resolved, pkgInfo.Name) {
|
||||||
|
*resolved = append(*resolved, pkgInfo.Name)
|
||||||
|
}
|
||||||
|
*unresolved = stringSliceRemove(*unresolved, pkgInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveAllPackageDependenciesFromDatabases(pkgInfo *PackageInfo, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) (resolved map[string]InstallationReason, unresolved []string) {
|
||||||
|
// Initialize slices
|
||||||
|
resolved = make(map[string]InstallationReason)
|
||||||
|
unresolved = make([]string, 0)
|
||||||
|
|
||||||
|
// Call unexported function
|
||||||
|
resolvePackageDependenciesFromDatabase(resolved, &unresolved, pkgInfo, InstallationReasonDependency, checkMake, checkOptional, ignoreInstalled, verbose, rootDir)
|
||||||
|
|
||||||
|
return resolved, unresolved
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolvePackageDependenciesFromDatabase(resolved map[string]InstallationReason, unresolved *[]string, pkgInfo *PackageInfo, installationReason InstallationReason, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) {
|
||||||
|
// Add current package name to unresolved slice
|
||||||
|
*unresolved = append(*unresolved, pkgInfo.Name)
|
||||||
|
|
||||||
|
// Loop through all dependencies
|
||||||
|
for depend, ir := range pkgInfo.GetDependencies(pkgInfo.Type == "source", checkOptional) {
|
||||||
|
if _, ok := resolved[depend]; !ok {
|
||||||
|
// Add current dependency to resolved slice when circular dependency is detected
|
||||||
|
if slices.Contains(*unresolved, depend) {
|
||||||
|
if verbose {
|
||||||
|
fmt.Printf("Circular dependency was detected (%s -> %s). Installing %s first\n", pkgInfo.Name, depend, depend)
|
||||||
|
}
|
||||||
|
if _, ok := resolved[depend]; !ok {
|
||||||
|
resolved[depend] = ir
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else if ignoreInstalled && IsPackageProvided(depend, rootDir) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
var entry *BPMDatabaseEntry
|
||||||
|
entry, _, err = GetDatabaseEntry(depend)
|
||||||
|
if err != nil {
|
||||||
|
if entry = ResolveVirtualPackage(depend); entry == nil {
|
||||||
|
if !slices.Contains(*unresolved, depend) {
|
||||||
|
*unresolved = append(*unresolved, depend)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolvePackageDependenciesFromDatabase(resolved, unresolved, entry.Info, ir, checkMake, checkOptional, ignoreInstalled, verbose, rootDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := resolved[pkgInfo.Name]; !ok {
|
||||||
|
resolved[pkgInfo.Name] = installationReason
|
||||||
|
}
|
||||||
|
*unresolved = stringSliceRemove(*unresolved, pkgInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPackageDependants(pkgName string, rootDir string) ([]string, error) {
|
||||||
|
ret := make([]string, 0)
|
||||||
|
|
||||||
|
// Get BPM package
|
||||||
|
pkg := GetPackage(pkgName, rootDir)
|
||||||
|
if pkg == nil {
|
||||||
|
return nil, errors.New("package not found: " + pkgName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get installed package names
|
||||||
|
pkgs, err := GetInstalledPackages(rootDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("could not get installed packages")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through all installed packages
|
||||||
|
for _, installedPkgName := range pkgs {
|
||||||
|
// Get installed BPM package
|
||||||
|
installedPkg := GetPackage(installedPkgName, rootDir)
|
||||||
|
if installedPkg == nil {
|
||||||
|
return nil, errors.New("package not found: " + installedPkgName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip iteration if comparing the same packages
|
||||||
|
if installedPkg.PkgInfo.Name == pkgName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get installed package dependencies
|
||||||
|
dependencies := installedPkg.PkgInfo.GetDependencies(false, true)
|
||||||
|
|
||||||
|
// Add installed package to list if its dependencies include pkgName
|
||||||
|
if _, ok := dependencies[pkgName]; ok {
|
||||||
|
ret = append(ret, installedPkgName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through each virtual package
|
||||||
|
for _, vpkg := range pkg.PkgInfo.Provides {
|
||||||
|
// Add installed package to list if its dependencies contain a provided virtual package
|
||||||
|
if _, ok := dependencies[vpkg]; ok {
|
||||||
|
ret = append(ret, installedPkgName)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
@ -10,7 +10,7 @@ type PackageNotFoundErr struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e PackageNotFoundErr) Error() string {
|
func (e PackageNotFoundErr) Error() string {
|
||||||
return "The following packages were not found in any repositories: " + strings.Join(e.packages, ", ")
|
return "The following packages were not found in any databases: " + strings.Join(e.packages, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
type DependencyNotFoundErr struct {
|
type DependencyNotFoundErr struct {
|
||||||
@ -18,7 +18,7 @@ type DependencyNotFoundErr struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e DependencyNotFoundErr) Error() string {
|
func (e DependencyNotFoundErr) Error() string {
|
||||||
return "The following dependencies were not found in any repositories: " + strings.Join(e.dependencies, ", ")
|
return "The following dependencies were not found in any databases: " + strings.Join(e.dependencies, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
type PackageConflictErr struct {
|
type PackageConflictErr struct {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"slices"
|
"slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,15 +17,15 @@ const (
|
|||||||
ReinstallMethodAll ReinstallMethod = iota
|
ReinstallMethodAll ReinstallMethod = iota
|
||||||
)
|
)
|
||||||
|
|
||||||
// InstallPackages installs the specified packages into the given root directory by fetching them from repositories or directly from local bpm archives
|
// 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, installationReason InstallationReason, reinstallMethod ReinstallMethod, installOptionalDependencies, forceInstallation, verbose bool, packages ...string) (operation *BPMOperation, err error) {
|
func InstallPackages(rootDir string, installationReason InstallationReason, reinstallMethod ReinstallMethod, installOptionalDependencies, forceInstallation, verbose bool, packages ...string) (operation *BPMOperation, err error) {
|
||||||
// Setup operation struct
|
// Setup operation struct
|
||||||
operation = &BPMOperation{
|
operation = &BPMOperation{
|
||||||
Actions: make([]OperationAction, 0),
|
Actions: make([]OperationAction, 0),
|
||||||
UnresolvedDepends: make([]string, 0),
|
UnresolvedDepends: make([]string, 0),
|
||||||
Changes: make(map[string]string),
|
Changes: make(map[string]string),
|
||||||
RootDir: rootDir,
|
RootDir: rootDir,
|
||||||
ForceInstallationReason: installationReason,
|
compiledPackages: make(map[string]string),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve packages
|
// Resolve packages
|
||||||
@ -35,21 +36,39 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read package: %s", err)
|
return nil, fmt.Errorf("could not read package: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bpmpkg.PkgInfo.Type == "source" && bpmpkg.PkgInfo.IsSplitPackage() {
|
||||||
|
for _, splitPkg := range bpmpkg.PkgInfo.SplitPackages {
|
||||||
|
if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(splitPkg.Name, rootDir) && GetPackageInfo(splitPkg.Name, rootDir).GetFullVersion() == splitPkg.GetFullVersion() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
operation.AppendAction(&InstallPackageAction{
|
||||||
|
File: pkg,
|
||||||
|
InstallationReason: installationReason,
|
||||||
|
BpmPackage: bpmpkg,
|
||||||
|
SplitPackageToInstall: splitPkg.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() {
|
if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
operation.AppendAction(&InstallPackageAction{
|
operation.AppendAction(&InstallPackageAction{
|
||||||
File: pkg,
|
File: pkg,
|
||||||
IsDependency: false,
|
InstallationReason: installationReason,
|
||||||
BpmPackage: bpmpkg,
|
BpmPackage: bpmpkg,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
var entry *RepositoryEntry
|
var entry *BPMDatabaseEntry
|
||||||
|
|
||||||
if e, _, err := GetRepositoryEntry(pkg); err == nil {
|
if e, _, err := GetDatabaseEntry(pkg); err == nil {
|
||||||
entry = e
|
entry = e
|
||||||
} else if isVirtual, p := IsVirtualPackage(pkg, rootDir); isVirtual {
|
} else if isVirtual, p := IsVirtualPackage(pkg, rootDir); isVirtual {
|
||||||
entry, _, err = GetRepositoryEntry(p)
|
entry, _, err = GetDatabaseEntry(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pkgsNotFound = append(pkgsNotFound, pkg)
|
pkgsNotFound = append(pkgsNotFound, pkg)
|
||||||
continue
|
continue
|
||||||
@ -63,9 +82,10 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein
|
|||||||
if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(entry.Info.Name, rootDir) && GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() {
|
if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(entry.Info.Name, rootDir) && GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
operation.AppendAction(&FetchPackageAction{
|
operation.AppendAction(&FetchPackageAction{
|
||||||
IsDependency: false,
|
InstallationReason: installationReason,
|
||||||
RepositoryEntry: entry,
|
DatabaseEntry: entry,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,12 +132,13 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemovePackages removes the specified packages from the given root directory
|
// RemovePackages removes the specified packages from the given root directory
|
||||||
func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencies, verbose bool, packages ...string) (operation *BPMOperation, err error) {
|
func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencies bool, packages ...string) (operation *BPMOperation, err error) {
|
||||||
operation = &BPMOperation{
|
operation = &BPMOperation{
|
||||||
Actions: make([]OperationAction, 0),
|
Actions: make([]OperationAction, 0),
|
||||||
UnresolvedDepends: make([]string, 0),
|
UnresolvedDepends: make([]string, 0),
|
||||||
Changes: make(map[string]string),
|
Changes: make(map[string]string),
|
||||||
RootDir: rootDir,
|
RootDir: rootDir,
|
||||||
|
compiledPackages: make(map[string]string),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for packages
|
// Search for packages
|
||||||
@ -139,7 +160,7 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie
|
|||||||
|
|
||||||
// Do package cleanup
|
// Do package cleanup
|
||||||
if cleanupDependencies {
|
if cleanupDependencies {
|
||||||
err := operation.Cleanup(verbose)
|
err := operation.Cleanup(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not perform cleanup for operation: %s", err)
|
return nil, fmt.Errorf("could not perform cleanup for operation: %s", err)
|
||||||
}
|
}
|
||||||
@ -148,16 +169,17 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CleanupPackages finds packages installed as dependencies which are no longer required by the rest of the system in the given root directory
|
// CleanupPackages finds packages installed as dependencies which are no longer required by the rest of the system in the given root directory
|
||||||
func CleanupPackages(rootDir string, verbose bool) (operation *BPMOperation, err error) {
|
func CleanupPackages(cleanupMakeDepends bool, rootDir string) (operation *BPMOperation, err error) {
|
||||||
operation = &BPMOperation{
|
operation = &BPMOperation{
|
||||||
Actions: make([]OperationAction, 0),
|
Actions: make([]OperationAction, 0),
|
||||||
UnresolvedDepends: make([]string, 0),
|
UnresolvedDepends: make([]string, 0),
|
||||||
Changes: make(map[string]string),
|
Changes: make(map[string]string),
|
||||||
RootDir: rootDir,
|
RootDir: rootDir,
|
||||||
|
compiledPackages: make(map[string]string),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do package cleanup
|
// Do package cleanup
|
||||||
err = operation.Cleanup(verbose)
|
err = operation.Cleanup(cleanupMakeDepends)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not perform cleanup for operation: %s", err)
|
return nil, fmt.Errorf("could not perform cleanup for operation: %s", err)
|
||||||
}
|
}
|
||||||
@ -165,9 +187,91 @@ func CleanupPackages(rootDir string, verbose bool) (operation *BPMOperation, err
|
|||||||
return operation, nil
|
return operation, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CleanupCache(rootDir string, cleanupCompilationFiles, cleanupCompiledPackages, cleanupFetchedPackages, verbose bool) error {
|
||||||
|
if cleanupCompilationFiles {
|
||||||
|
globalCompilationCacheDir := path.Join(rootDir, "var/cache/bpm/compilation")
|
||||||
|
|
||||||
|
// Ensure path exists and is a directory
|
||||||
|
if stat, err := os.Stat(globalCompilationCacheDir); err == nil && stat.IsDir() {
|
||||||
|
// Delete directory
|
||||||
|
if verbose {
|
||||||
|
log.Printf("Removing directory (%s)\n", globalCompilationCacheDir)
|
||||||
|
}
|
||||||
|
err = os.RemoveAll(globalCompilationCacheDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get home directories of users in root directory
|
||||||
|
homeDirs, err := os.ReadDir(path.Join(rootDir, "home"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through all home directories
|
||||||
|
for _, homeDir := range homeDirs {
|
||||||
|
// Skip if not a directory
|
||||||
|
if !homeDir.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
localCompilationDir := path.Join(rootDir, "home", homeDir.Name(), ".cache/bpm/compilation")
|
||||||
|
|
||||||
|
// Ensure path exists and is a directory
|
||||||
|
if stat, err := os.Stat(localCompilationDir); err != nil || !stat.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete directory
|
||||||
|
if verbose {
|
||||||
|
log.Printf("Removing directory (%s)\n", localCompilationDir)
|
||||||
|
}
|
||||||
|
err = os.RemoveAll(localCompilationDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cleanupCompiledPackages {
|
||||||
|
dirToRemove := path.Join(rootDir, "var/cache/bpm/compiled")
|
||||||
|
|
||||||
|
// Ensure path exists and is a directory
|
||||||
|
if stat, err := os.Stat(dirToRemove); err == nil && stat.IsDir() {
|
||||||
|
// Delete directory
|
||||||
|
if verbose {
|
||||||
|
log.Printf("Removing directory (%s)\n", dirToRemove)
|
||||||
|
}
|
||||||
|
err = os.RemoveAll(dirToRemove)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cleanupFetchedPackages {
|
||||||
|
dirToRemove := path.Join(rootDir, "var/cache/bpm/fetched")
|
||||||
|
|
||||||
|
// Ensure path exists and is a directory
|
||||||
|
if stat, err := os.Stat(dirToRemove); err == nil && stat.IsDir() {
|
||||||
|
// Delete directory
|
||||||
|
if verbose {
|
||||||
|
log.Printf("Removing directory (%s)\n", dirToRemove)
|
||||||
|
}
|
||||||
|
err = os.RemoveAll(dirToRemove)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// UpdatePackages fetches the newest versions of all installed packages from
|
// UpdatePackages fetches the newest versions of all installed packages from
|
||||||
func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependencies, forceInstallation, verbose bool) (operation *BPMOperation, err error) {
|
func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependencies, forceInstallation, verbose bool) (operation *BPMOperation, err error) {
|
||||||
// Sync repositories
|
// Sync databases
|
||||||
if syncDatabase {
|
if syncDatabase {
|
||||||
err := SyncDatabase(verbose)
|
err := SyncDatabase(verbose)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -176,12 +280,16 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci
|
|||||||
if verbose {
|
if verbose {
|
||||||
fmt.Println("All package databases synced successfully!")
|
fmt.Println("All package databases synced successfully!")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Reload config and local databases
|
// Reload config and local databases
|
||||||
err = ReadConfig()
|
err = ReadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read BPM config: %s", err)
|
return nil, fmt.Errorf("could not read BPM config: %s", err)
|
||||||
|
}
|
||||||
|
err = ReadLocalDatabaseFiles()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not read local databases: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get installed packages and check for updates
|
// Get installed packages and check for updates
|
||||||
@ -191,11 +299,11 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci
|
|||||||
}
|
}
|
||||||
|
|
||||||
operation = &BPMOperation{
|
operation = &BPMOperation{
|
||||||
Actions: make([]OperationAction, 0),
|
Actions: make([]OperationAction, 0),
|
||||||
UnresolvedDepends: make([]string, 0),
|
UnresolvedDepends: make([]string, 0),
|
||||||
Changes: make(map[string]string),
|
Changes: make(map[string]string),
|
||||||
RootDir: rootDir,
|
RootDir: rootDir,
|
||||||
ForceInstallationReason: InstallationReasonUnknown,
|
compiledPackages: make(map[string]string),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for packages
|
// Search for packages
|
||||||
@ -203,11 +311,11 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci
|
|||||||
if slices.Contains(BPMConfig.IgnorePackages, pkg) {
|
if slices.Contains(BPMConfig.IgnorePackages, pkg) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var entry *RepositoryEntry
|
var entry *BPMDatabaseEntry
|
||||||
// Check if installed package can be replaced and install that instead
|
// Check if installed package can be replaced and install that instead
|
||||||
if e := FindReplacement(pkg); e != nil {
|
if e := FindReplacement(pkg); e != nil {
|
||||||
entry = e
|
entry = e
|
||||||
} else if entry, _, err = GetRepositoryEntry(pkg); err != nil {
|
} else if entry, _, err = GetDatabaseEntry(pkg); err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,8 +326,8 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci
|
|||||||
comparison := ComparePackageVersions(*entry.Info, *installedInfo)
|
comparison := ComparePackageVersions(*entry.Info, *installedInfo)
|
||||||
if comparison > 0 {
|
if comparison > 0 {
|
||||||
operation.AppendAction(&FetchPackageAction{
|
operation.AppendAction(&FetchPackageAction{
|
||||||
IsDependency: false,
|
InstallationReason: GetInstallationReason(pkg, rootDir),
|
||||||
RepositoryEntry: entry,
|
DatabaseEntry: entry,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,12 +353,12 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci
|
|||||||
|
|
||||||
// SyncDatabase syncs all databases declared in /etc/bpm.conf
|
// SyncDatabase syncs all databases declared in /etc/bpm.conf
|
||||||
func SyncDatabase(verbose bool) (err error) {
|
func SyncDatabase(verbose bool) (err error) {
|
||||||
for _, repo := range BPMConfig.Repositories {
|
for _, db := range BPMConfig.Databases {
|
||||||
if verbose {
|
if verbose {
|
||||||
fmt.Printf("Fetching package database for repository (%s)...\n", repo.Name)
|
fmt.Printf("Fetching package database file for database (%s)...\n", db.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := repo.SyncLocalDatabase()
|
err := db.SyncLocalDatabaseFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,10 @@ func (hook *BPMHook) Execute(packageChanges map[string]string, verbose bool, roo
|
|||||||
// Get modified files slice
|
// Get modified files slice
|
||||||
modifiedFiles := make([]*PackageFileEntry, 0)
|
modifiedFiles := make([]*PackageFileEntry, 0)
|
||||||
for pkg := range packageChanges {
|
for pkg := range packageChanges {
|
||||||
modifiedFiles = append(modifiedFiles, GetPackageFiles(pkg, rootDir)...)
|
if GetPackage(pkg, rootDir) != nil {
|
||||||
|
modifiedFiles = append(modifiedFiles, GetPackage(pkg, rootDir).PkgFiles...)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any targets are met
|
// Check if any targets are met
|
||||||
|
243
src/bpmlib/installed_packages.go
Normal file
243
src/bpmlib/installed_packages.go
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
package bpmlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var localPackageInformation map[string]map[string]*BPMPackage = make(map[string]map[string]*BPMPackage)
|
||||||
|
|
||||||
|
func initializeLocalPackageInformation(rootDir string) (err error) {
|
||||||
|
// Return if information is already initialized
|
||||||
|
if _, ok := localPackageInformation[rootDir]; ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tempPackageInformation := make(map[string]*BPMPackage)
|
||||||
|
|
||||||
|
// Get path to installed package information directory
|
||||||
|
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
|
||||||
|
|
||||||
|
// Get directory content
|
||||||
|
items, err := os.ReadDir(installedDir)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
localPackageInformation[rootDir] = make(map[string]*BPMPackage)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through each subdirectory
|
||||||
|
for _, item := range items {
|
||||||
|
// Skip if not a directory
|
||||||
|
if !item.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read package info
|
||||||
|
infoData, err := os.ReadFile(path.Join(installedDir, item.Name(), "info"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
info, err := ReadPackageInfo(string(infoData))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read package files
|
||||||
|
files := getPackageFiles(info.Name, rootDir)
|
||||||
|
|
||||||
|
// Add package to slice
|
||||||
|
tempPackageInformation[info.Name] = &BPMPackage{
|
||||||
|
PkgInfo: info,
|
||||||
|
PkgFiles: files,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
localPackageInformation[rootDir] = tempPackageInformation
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetInstalledPackages(rootDir string) (ret []string, err error) {
|
||||||
|
// Initialize local package information
|
||||||
|
err = initializeLocalPackageInformation(rootDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through each package and add it to slice
|
||||||
|
for _, bpmpkg := range localPackageInformation[rootDir] {
|
||||||
|
ret = append(ret, bpmpkg.PkgInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsPackageInstalled(pkg, rootDir string) bool {
|
||||||
|
// Initialize local package information
|
||||||
|
err := initializeLocalPackageInformation(rootDir)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := localPackageInformation[rootDir][pkg]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPackageInfo(pkg string, rootDir string) *PackageInfo {
|
||||||
|
// Get BPM package
|
||||||
|
bpmpkg := GetPackage(pkg, rootDir)
|
||||||
|
|
||||||
|
// Return nil if not found
|
||||||
|
if bpmpkg == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return bpmpkg.PkgInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsVirtualPackage(pkg, rootDir string) (bool, string) {
|
||||||
|
pkgs, err := GetInstalledPackages(rootDir)
|
||||||
|
if err != nil {
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
for _, p := range pkgs {
|
||||||
|
if p == pkg {
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
i := GetPackageInfo(p, rootDir)
|
||||||
|
if i == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if slices.Contains(i.Provides, pkg) {
|
||||||
|
return true, p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsPackageProvided(pkg, rootDir string) bool {
|
||||||
|
pkgs, err := GetInstalledPackages(rootDir)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, p := range pkgs {
|
||||||
|
if p == pkg {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
i := GetPackageInfo(p, rootDir)
|
||||||
|
if i == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if slices.Contains(i.Provides, pkg) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPackage(pkg, rootDir string) *BPMPackage {
|
||||||
|
err := initializeLocalPackageInformation(rootDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bpmpkg := localPackageInformation[rootDir][pkg]
|
||||||
|
|
||||||
|
return bpmpkg
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllPackageFiles(rootDir string, excludePackages ...string) (map[string][]*BPMPackage, error) {
|
||||||
|
ret := make(map[string][]*BPMPackage)
|
||||||
|
|
||||||
|
pkgNames, err := GetInstalledPackages(rootDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pkgName := range pkgNames {
|
||||||
|
if slices.Contains(excludePackages, pkgName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bpmpkg := GetPackage(pkgName, rootDir)
|
||||||
|
if bpmpkg == nil {
|
||||||
|
return nil, errors.New(fmt.Sprintf("could not get BPM package (%s)", pkgName))
|
||||||
|
}
|
||||||
|
for _, entry := range bpmpkg.PkgFiles {
|
||||||
|
if _, ok := ret[entry.Path]; ok {
|
||||||
|
ret[entry.Path] = append(ret[entry.Path], bpmpkg)
|
||||||
|
} else {
|
||||||
|
ret[entry.Path] = []*BPMPackage{bpmpkg}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPackageFiles(pkg, rootDir string) []*PackageFileEntry {
|
||||||
|
var pkgFiles []*PackageFileEntry
|
||||||
|
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
|
||||||
|
pkgDir := path.Join(installedDir, pkg)
|
||||||
|
files := path.Join(pkgDir, "files")
|
||||||
|
if _, err := os.Stat(installedDir); os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(pkgDir); os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
bs, err := os.ReadFile(files)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range strings.Split(string(bs), "\n") {
|
||||||
|
if strings.TrimSpace(line) == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
stringEntry := strings.Split(strings.TrimSpace(line), " ")
|
||||||
|
if len(stringEntry) < 5 {
|
||||||
|
pkgFiles = append(pkgFiles, &PackageFileEntry{
|
||||||
|
Path: strings.TrimSuffix(line, "/"),
|
||||||
|
OctalPerms: 0,
|
||||||
|
UserID: 0,
|
||||||
|
GroupID: 0,
|
||||||
|
SizeInBytes: 0,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
uid, err := strconv.ParseInt(stringEntry[len(stringEntry)-4], 0, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
octalPerms, err := strconv.ParseInt(stringEntry[len(stringEntry)-3], 0, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
gid, err := strconv.ParseInt(stringEntry[len(stringEntry)-2], 0, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
size, err := strconv.ParseUint(stringEntry[len(stringEntry)-1], 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pkgFiles = append(pkgFiles, &PackageFileEntry{
|
||||||
|
Path: strings.TrimSuffix(strings.Join(stringEntry[:len(stringEntry)-4], " "), "/"),
|
||||||
|
OctalPerms: uint32(octalPerms),
|
||||||
|
UserID: int(uid),
|
||||||
|
GroupID: int(gid),
|
||||||
|
SizeInBytes: size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkgFiles
|
||||||
|
}
|
@ -11,11 +11,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type BPMOperation struct {
|
type BPMOperation struct {
|
||||||
Actions []OperationAction
|
Actions []OperationAction
|
||||||
UnresolvedDepends []string
|
UnresolvedDepends []string
|
||||||
Changes map[string]string
|
Changes map[string]string
|
||||||
RootDir string
|
RootDir string
|
||||||
ForceInstallationReason InstallationReason
|
compiledPackages map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (operation *BPMOperation) ActionsContainPackage(pkg string) bool {
|
func (operation *BPMOperation) ActionsContainPackage(pkg string) bool {
|
||||||
@ -25,7 +25,7 @@ func (operation *BPMOperation) ActionsContainPackage(pkg string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} else if action.GetActionType() == "fetch" {
|
} else if action.GetActionType() == "fetch" {
|
||||||
if action.(*FetchPackageAction).RepositoryEntry.Info.Name == pkg {
|
if action.(*FetchPackageAction).DatabaseEntry.Info.Name == pkg {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} else if action.GetActionType() == "remove" {
|
} else if action.GetActionType() == "remove" {
|
||||||
@ -57,7 +57,7 @@ func (operation *BPMOperation) InsertActionAt(index int, action OperationAction)
|
|||||||
operation.Changes[pkgInfo.Name] = "upgrade"
|
operation.Changes[pkgInfo.Name] = "upgrade"
|
||||||
}
|
}
|
||||||
} else if action.GetActionType() == "fetch" {
|
} else if action.GetActionType() == "fetch" {
|
||||||
pkgInfo := action.(*FetchPackageAction).RepositoryEntry.Info
|
pkgInfo := action.(*FetchPackageAction).DatabaseEntry.Info
|
||||||
if !IsPackageInstalled(pkgInfo.Name, operation.RootDir) {
|
if !IsPackageInstalled(pkgInfo.Name, operation.RootDir) {
|
||||||
operation.Changes[pkgInfo.Name] = "install"
|
operation.Changes[pkgInfo.Name] = "install"
|
||||||
} else {
|
} else {
|
||||||
@ -76,7 +76,7 @@ func (operation *BPMOperation) RemoveAction(pkg, actionType string) {
|
|||||||
if a.GetActionType() == "install" {
|
if a.GetActionType() == "install" {
|
||||||
return a.(*InstallPackageAction).BpmPackage.PkgInfo.Name == pkg
|
return a.(*InstallPackageAction).BpmPackage.PkgInfo.Name == pkg
|
||||||
} else if a.GetActionType() == "fetch" {
|
} else if a.GetActionType() == "fetch" {
|
||||||
return a.(*FetchPackageAction).RepositoryEntry.Info.Name == pkg
|
return a.(*FetchPackageAction).DatabaseEntry.Info.Name == pkg
|
||||||
} else if a.GetActionType() == "remove" {
|
} else if a.GetActionType() == "remove" {
|
||||||
return a.(*RemovePackageAction).BpmPackage.PkgInfo.Name == pkg
|
return a.(*RemovePackageAction).BpmPackage.PkgInfo.Name == pkg
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ func (operation *BPMOperation) GetTotalDownloadSize() uint64 {
|
|||||||
var ret uint64 = 0
|
var ret uint64 = 0
|
||||||
for _, action := range operation.Actions {
|
for _, action := range operation.Actions {
|
||||||
if action.GetActionType() == "fetch" {
|
if action.GetActionType() == "fetch" {
|
||||||
ret += action.(*FetchPackageAction).RepositoryEntry.DownloadSize
|
ret += action.(*FetchPackageAction).DatabaseEntry.DownloadSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
@ -100,7 +100,7 @@ func (operation *BPMOperation) GetTotalInstalledSize() uint64 {
|
|||||||
if action.GetActionType() == "install" {
|
if action.GetActionType() == "install" {
|
||||||
ret += action.(*InstallPackageAction).BpmPackage.GetInstalledSize()
|
ret += action.(*InstallPackageAction).BpmPackage.GetInstalledSize()
|
||||||
} else if action.GetActionType() == "fetch" {
|
} else if action.GetActionType() == "fetch" {
|
||||||
ret += action.(*FetchPackageAction).RepositoryEntry.InstalledSize
|
ret += action.(*FetchPackageAction).DatabaseEntry.InstalledSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
@ -115,7 +115,7 @@ func (operation *BPMOperation) GetFinalActionSize(rootDir string) int64 {
|
|||||||
ret -= int64(GetPackage(action.(*InstallPackageAction).BpmPackage.PkgInfo.Name, rootDir).GetInstalledSize())
|
ret -= int64(GetPackage(action.(*InstallPackageAction).BpmPackage.PkgInfo.Name, rootDir).GetInstalledSize())
|
||||||
}
|
}
|
||||||
} else if action.GetActionType() == "fetch" {
|
} else if action.GetActionType() == "fetch" {
|
||||||
ret += int64(action.(*FetchPackageAction).RepositoryEntry.InstalledSize)
|
ret += int64(action.(*FetchPackageAction).DatabaseEntry.InstalledSize)
|
||||||
} else if action.GetActionType() == "remove" {
|
} else if action.GetActionType() == "remove" {
|
||||||
ret -= int64(action.(*RemovePackageAction).BpmPackage.GetInstalledSize())
|
ret -= int64(action.(*RemovePackageAction).BpmPackage.GetInstalledSize())
|
||||||
}
|
}
|
||||||
@ -132,28 +132,28 @@ func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, instal
|
|||||||
pkgInfo = action.BpmPackage.PkgInfo
|
pkgInfo = action.BpmPackage.PkgInfo
|
||||||
} else if value.GetActionType() == "fetch" {
|
} else if value.GetActionType() == "fetch" {
|
||||||
action := value.(*FetchPackageAction)
|
action := value.(*FetchPackageAction)
|
||||||
pkgInfo = action.RepositoryEntry.Info
|
pkgInfo = action.DatabaseEntry.Info
|
||||||
} else {
|
} else {
|
||||||
pos++
|
pos++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
resolved, unresolved := pkgInfo.ResolveDependencies(&[]string{}, &[]string{}, pkgInfo.Type == "source", installOptionalDependencies, !reinstallDependencies, verbose, operation.RootDir)
|
resolved, unresolved := ResolveAllPackageDependenciesFromDatabases(pkgInfo, pkgInfo.Type == "source", installOptionalDependencies, !reinstallDependencies, verbose, operation.RootDir)
|
||||||
|
|
||||||
operation.UnresolvedDepends = append(operation.UnresolvedDepends, unresolved...)
|
operation.UnresolvedDepends = append(operation.UnresolvedDepends, unresolved...)
|
||||||
|
|
||||||
for _, depend := range resolved {
|
for depend, installationReason := range resolved {
|
||||||
if !operation.ActionsContainPackage(depend) && depend != pkgInfo.Name {
|
if !operation.ActionsContainPackage(depend) && depend != pkgInfo.Name {
|
||||||
if !reinstallDependencies && IsPackageInstalled(depend, operation.RootDir) {
|
if !reinstallDependencies && IsPackageInstalled(depend, operation.RootDir) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
entry, _, err := GetRepositoryEntry(depend)
|
entry, _, err := GetDatabaseEntry(depend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("could not get repository entry for package (" + depend + ")")
|
return errors.New("could not get database entry for package (" + depend + ")")
|
||||||
}
|
}
|
||||||
operation.InsertActionAt(pos, &FetchPackageAction{
|
operation.InsertActionAt(pos, &FetchPackageAction{
|
||||||
IsDependency: true,
|
InstallationReason: installationReason,
|
||||||
RepositoryEntry: entry,
|
DatabaseEntry: entry,
|
||||||
})
|
})
|
||||||
pos++
|
pos++
|
||||||
}
|
}
|
||||||
@ -173,7 +173,7 @@ func (operation *BPMOperation) RemoveNeededPackages() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for pkg, action := range removeActions {
|
for pkg, action := range removeActions {
|
||||||
dependants, err := action.BpmPackage.PkgInfo.GetDependants(operation.RootDir)
|
dependants, err := GetPackageDependants(action.BpmPackage.PkgInfo.Name, operation.RootDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("could not get dependant packages for package (" + pkg + ")")
|
return errors.New("could not get dependant packages for package (" + pkg + ")")
|
||||||
}
|
}
|
||||||
@ -191,7 +191,7 @@ func (operation *BPMOperation) RemoveNeededPackages() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (operation *BPMOperation) Cleanup(verbose bool) error {
|
func (operation *BPMOperation) Cleanup(cleanupMakeDepends bool) error {
|
||||||
// Get all installed packages
|
// Get all installed packages
|
||||||
installedPackageNames, err := GetInstalledPackages(operation.RootDir)
|
installedPackageNames, err := GetInstalledPackages(operation.RootDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -227,9 +227,9 @@ func (operation *BPMOperation) Cleanup(verbose bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
keepPackages = append(keepPackages, pkg.Name)
|
keepPackages = append(keepPackages, pkg.Name)
|
||||||
resolved, _ := pkg.ResolveDependencies(&[]string{}, &[]string{}, false, true, false, verbose, operation.RootDir)
|
resolved := pkg.GetAllDependencies(!cleanupMakeDepends, true, operation.RootDir)
|
||||||
for _, value := range resolved {
|
for _, value := range resolved {
|
||||||
if !slices.Contains(keepPackages, value) && slices.Contains(installedPackageNames, value) {
|
if !slices.Contains(keepPackages, value) {
|
||||||
keepPackages = append(keepPackages, value)
|
keepPackages = append(keepPackages, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,7 +262,7 @@ func (operation *BPMOperation) ReplaceObsoletePackages() {
|
|||||||
|
|
||||||
} else if value.GetActionType() == "fetch" {
|
} else if value.GetActionType() == "fetch" {
|
||||||
action := value.(*FetchPackageAction)
|
action := value.(*FetchPackageAction)
|
||||||
pkgInfo = action.RepositoryEntry.Info
|
pkgInfo = action.DatabaseEntry.Info
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -300,7 +300,7 @@ func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error)
|
|||||||
allPackages = append(allPackages, pkgInfo)
|
allPackages = append(allPackages, pkgInfo)
|
||||||
} else if value.GetActionType() == "fetch" {
|
} else if value.GetActionType() == "fetch" {
|
||||||
action := value.(*FetchPackageAction)
|
action := value.(*FetchPackageAction)
|
||||||
pkgInfo := action.RepositoryEntry.Info
|
pkgInfo := action.DatabaseEntry.Info
|
||||||
allPackages = append(allPackages, pkgInfo)
|
allPackages = append(allPackages, pkgInfo)
|
||||||
} else if value.GetActionType() == "remove" {
|
} else if value.GetActionType() == "remove" {
|
||||||
action := value.(*RemovePackageAction)
|
action := value.(*RemovePackageAction)
|
||||||
@ -335,10 +335,16 @@ func (operation *BPMOperation) ShowOperationSummary() {
|
|||||||
|
|
||||||
for _, value := range operation.Actions {
|
for _, value := range operation.Actions {
|
||||||
var pkgInfo *PackageInfo
|
var pkgInfo *PackageInfo
|
||||||
|
var installationReason = InstallationReasonUnknown
|
||||||
if value.GetActionType() == "install" {
|
if value.GetActionType() == "install" {
|
||||||
|
installationReason = value.(*InstallPackageAction).InstallationReason
|
||||||
pkgInfo = value.(*InstallPackageAction).BpmPackage.PkgInfo
|
pkgInfo = value.(*InstallPackageAction).BpmPackage.PkgInfo
|
||||||
|
if value.(*InstallPackageAction).SplitPackageToInstall != "" {
|
||||||
|
pkgInfo = pkgInfo.GetSplitPackageInfo(value.(*InstallPackageAction).SplitPackageToInstall)
|
||||||
|
}
|
||||||
} else if value.GetActionType() == "fetch" {
|
} else if value.GetActionType() == "fetch" {
|
||||||
pkgInfo = value.(*FetchPackageAction).RepositoryEntry.Info
|
installationReason = value.(*FetchPackageAction).InstallationReason
|
||||||
|
pkgInfo = value.(*FetchPackageAction).DatabaseEntry.Info
|
||||||
} else {
|
} else {
|
||||||
pkgInfo = value.(*RemovePackageAction).BpmPackage.PkgInfo
|
pkgInfo = value.(*RemovePackageAction).BpmPackage.PkgInfo
|
||||||
fmt.Printf("%s: %s (Remove)\n", pkgInfo.Name, pkgInfo.GetFullVersion())
|
fmt.Printf("%s: %s (Remove)\n", pkgInfo.Name, pkgInfo.GetFullVersion())
|
||||||
@ -346,21 +352,32 @@ func (operation *BPMOperation) ShowOperationSummary() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
installedInfo := GetPackageInfo(pkgInfo.Name, operation.RootDir)
|
installedInfo := GetPackageInfo(pkgInfo.Name, operation.RootDir)
|
||||||
sourceInfo := ""
|
additionalInfo := ""
|
||||||
|
switch installationReason {
|
||||||
|
case InstallationReasonManual:
|
||||||
|
additionalInfo = "(Manual)"
|
||||||
|
case InstallationReasonDependency:
|
||||||
|
additionalInfo = "(Dependency)"
|
||||||
|
case InstallationReasonMakeDependency:
|
||||||
|
additionalInfo = "(Make dependency)"
|
||||||
|
default:
|
||||||
|
additionalInfo = "(Unknown)"
|
||||||
|
}
|
||||||
|
|
||||||
if pkgInfo.Type == "source" {
|
if pkgInfo.Type == "source" {
|
||||||
sourceInfo = "(From Source)"
|
additionalInfo += " (From Source)"
|
||||||
}
|
}
|
||||||
|
|
||||||
if installedInfo == nil {
|
if installedInfo == nil {
|
||||||
fmt.Printf("%s: %s (Install) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), sourceInfo)
|
fmt.Printf("%s: %s (Install) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), additionalInfo)
|
||||||
} else {
|
} else {
|
||||||
comparison := ComparePackageVersions(*pkgInfo, *installedInfo)
|
comparison := ComparePackageVersions(*pkgInfo, *installedInfo)
|
||||||
if comparison < 0 {
|
if comparison < 0 {
|
||||||
fmt.Printf("%s: %s -> %s (Downgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), sourceInfo)
|
fmt.Printf("%s: %s -> %s (Downgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), additionalInfo)
|
||||||
} else if comparison > 0 {
|
} else if comparison > 0 {
|
||||||
fmt.Printf("%s: %s -> %s (Upgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), sourceInfo)
|
fmt.Printf("%s: %s -> %s (Upgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), additionalInfo)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s: %s (Reinstall) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), sourceInfo)
|
fmt.Printf("%s: %s (Reinstall) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), additionalInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -409,30 +426,68 @@ func (operation *BPMOperation) RunHooks(verbose bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (operation *BPMOperation) Execute(verbose, force bool) error {
|
func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
|
||||||
// Fetch packages from repositories
|
// Fetch packages from databases
|
||||||
if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool {
|
if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool {
|
||||||
return action.GetActionType() == "fetch"
|
return action.GetActionType() == "fetch"
|
||||||
}) {
|
}) {
|
||||||
fmt.Println("Fetching packages from available repositories...")
|
fmt.Println("Fetching packages from available databases...")
|
||||||
|
|
||||||
|
// Create map for fetched packages
|
||||||
|
fetchedPackages := make(map[string]string)
|
||||||
|
|
||||||
for i, action := range operation.Actions {
|
for i, action := range operation.Actions {
|
||||||
if action.GetActionType() != "fetch" {
|
if action.GetActionType() != "fetch" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
entry := action.(*FetchPackageAction).RepositoryEntry
|
|
||||||
fetchedPackage, err := entry.Repository.FetchPackage(entry.Info.Name)
|
// Get database entry
|
||||||
if err != nil {
|
entry := action.(*FetchPackageAction).DatabaseEntry
|
||||||
return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err))
|
|
||||||
|
// Create bpmpkg variable
|
||||||
|
var bpmpkg *BPMPackage
|
||||||
|
|
||||||
|
// Check if package has already been fetched from download link
|
||||||
|
if _, ok := fetchedPackages[entry.Download]; !ok {
|
||||||
|
// Fetch package from database
|
||||||
|
fetchedPackage, err := entry.Database.FetchPackage(entry.Info.Name)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read fetched package
|
||||||
|
bpmpkg, err = ReadPackage(fetchedPackage)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add fetched package to map
|
||||||
|
fetchedPackages[entry.Download] = fetchedPackage
|
||||||
|
|
||||||
|
fmt.Printf("Package (%s) was successfully fetched!\n", entry.Info.Name)
|
||||||
|
} else {
|
||||||
|
// Read fetched package
|
||||||
|
bpmpkg, err = ReadPackage(fetchedPackages[entry.Download])
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("could not read package (%s): %s\n", entry.Info.Name, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Package (%s) was successfully fetched!\n", entry.Info.Name)
|
||||||
}
|
}
|
||||||
bpmpkg, err := ReadPackage(fetchedPackage)
|
|
||||||
if err != nil {
|
if bpmpkg.PkgInfo.IsSplitPackage() {
|
||||||
return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err))
|
operation.Actions[i] = &InstallPackageAction{
|
||||||
}
|
File: fetchedPackages[entry.Download],
|
||||||
fmt.Printf("Package (%s) was successfully fetched!\n", bpmpkg.PkgInfo.Name)
|
InstallationReason: action.(*FetchPackageAction).InstallationReason,
|
||||||
operation.Actions[i] = &InstallPackageAction{
|
BpmPackage: bpmpkg,
|
||||||
File: fetchedPackage,
|
SplitPackageToInstall: entry.Info.Name,
|
||||||
IsDependency: action.(*FetchPackageAction).IsDependency,
|
}
|
||||||
BpmPackage: bpmpkg,
|
} else {
|
||||||
|
operation.Actions[i] = &InstallPackageAction{
|
||||||
|
File: fetchedPackages[entry.Download],
|
||||||
|
InstallationReason: action.(*FetchPackageAction).InstallationReason,
|
||||||
|
BpmPackage: bpmpkg,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -466,24 +521,61 @@ func (operation *BPMOperation) Execute(verbose, force bool) error {
|
|||||||
}
|
}
|
||||||
} else if action.GetActionType() == "install" {
|
} else if action.GetActionType() == "install" {
|
||||||
value := action.(*InstallPackageAction)
|
value := action.(*InstallPackageAction)
|
||||||
|
fileToInstall := value.File
|
||||||
bpmpkg := value.BpmPackage
|
bpmpkg := value.BpmPackage
|
||||||
isReinstall := IsPackageInstalled(bpmpkg.PkgInfo.Name, operation.RootDir)
|
isReinstall := IsPackageInstalled(bpmpkg.PkgInfo.Name, operation.RootDir)
|
||||||
var err error
|
var err error
|
||||||
if value.IsDependency {
|
|
||||||
err = installPackage(value.File, operation.RootDir, verbose, true)
|
// Compile package if type is 'source'
|
||||||
|
if bpmpkg.PkgInfo.Type == "source" {
|
||||||
|
// Get path to compiled package directory
|
||||||
|
compiledDir := path.Join(operation.RootDir, "/var/cache/bpm/compiled/")
|
||||||
|
|
||||||
|
// Create compiled package directory if not exists
|
||||||
|
if _, err := os.Stat(compiledDir); err != nil {
|
||||||
|
err := os.MkdirAll(compiledDir, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get package name to install
|
||||||
|
pkgNameToInstall := bpmpkg.PkgInfo.Name
|
||||||
|
if bpmpkg.PkgInfo.IsSplitPackage() {
|
||||||
|
pkgNameToInstall = value.SplitPackageToInstall
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile source package if not compiled already
|
||||||
|
if _, ok := operation.compiledPackages[pkgNameToInstall]; !ok {
|
||||||
|
outputBpmPackages, err := CompileSourcePackage(value.File, compiledDir, false)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not compile source package (%s): %s\n", value.File, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add compiled packages to slice
|
||||||
|
for pkgName, pkgFile := range outputBpmPackages {
|
||||||
|
operation.compiledPackages[pkgName] = pkgFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set values
|
||||||
|
fileToInstall = operation.compiledPackages[pkgNameToInstall]
|
||||||
|
bpmpkg, err = ReadPackage(fileToInstall)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not read package (%s): %s\n", fileToInstall, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.InstallationReason != InstallationReasonManual {
|
||||||
|
err = installPackage(fileToInstall, operation.RootDir, verbose, true)
|
||||||
} else {
|
} else {
|
||||||
err = installPackage(value.File, operation.RootDir, verbose, force)
|
err = installPackage(fileToInstall, operation.RootDir, verbose, force)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(fmt.Sprintf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err))
|
return errors.New(fmt.Sprintf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err))
|
||||||
}
|
}
|
||||||
if operation.ForceInstallationReason != InstallationReasonUnknown && !value.IsDependency {
|
if !isReinstall {
|
||||||
err := SetInstallationReason(bpmpkg.PkgInfo.Name, operation.ForceInstallationReason, operation.RootDir)
|
err := SetInstallationReason(bpmpkg.PkgInfo.Name, value.InstallationReason, operation.RootDir)
|
||||||
if err != nil {
|
|
||||||
return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err))
|
|
||||||
}
|
|
||||||
} else if value.IsDependency && !isReinstall {
|
|
||||||
err := SetInstallationReason(bpmpkg.PkgInfo.Name, InstallationReasonDependency, operation.RootDir)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err))
|
return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err))
|
||||||
}
|
}
|
||||||
@ -501,9 +593,10 @@ type OperationAction interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type InstallPackageAction struct {
|
type InstallPackageAction struct {
|
||||||
File string
|
File string
|
||||||
IsDependency bool
|
InstallationReason InstallationReason
|
||||||
BpmPackage *BPMPackage
|
SplitPackageToInstall string
|
||||||
|
BpmPackage *BPMPackage
|
||||||
}
|
}
|
||||||
|
|
||||||
func (action *InstallPackageAction) GetActionType() string {
|
func (action *InstallPackageAction) GetActionType() string {
|
||||||
@ -511,8 +604,8 @@ func (action *InstallPackageAction) GetActionType() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FetchPackageAction struct {
|
type FetchPackageAction struct {
|
||||||
IsDependency bool
|
InstallationReason InstallationReason
|
||||||
RepositoryEntry *RepositoryEntry
|
DatabaseEntry *BPMDatabaseEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
func (action *FetchPackageAction) GetActionType() string {
|
func (action *FetchPackageAction) GetActionType() string {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
version "github.com/knqyf263/go-rpm-version"
|
version "github.com/knqyf263/go-rpm-version"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
@ -23,21 +24,22 @@ type BPMPackage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PackageInfo struct {
|
type PackageInfo struct {
|
||||||
Name string `yaml:"name,omitempty"`
|
Name string `yaml:"name,omitempty"`
|
||||||
Description string `yaml:"description,omitempty"`
|
Description string `yaml:"description,omitempty"`
|
||||||
Version string `yaml:"version,omitempty"`
|
Version string `yaml:"version,omitempty"`
|
||||||
Revision int `yaml:"revision,omitempty"`
|
Revision int `yaml:"revision,omitempty"`
|
||||||
Url string `yaml:"url,omitempty"`
|
Url string `yaml:"url,omitempty"`
|
||||||
License string `yaml:"license,omitempty"`
|
License string `yaml:"license,omitempty"`
|
||||||
Arch string `yaml:"architecture,omitempty"`
|
Arch string `yaml:"architecture,omitempty"`
|
||||||
Type string `yaml:"type,omitempty"`
|
Type string `yaml:"type,omitempty"`
|
||||||
Keep []string `yaml:"keep,omitempty"`
|
Keep []string `yaml:"keep,omitempty"`
|
||||||
Depends []string `yaml:"depends,omitempty"`
|
Depends []string `yaml:"depends,omitempty"`
|
||||||
MakeDepends []string `yaml:"make_depends,omitempty"`
|
MakeDepends []string `yaml:"make_depends,omitempty"`
|
||||||
OptionalDepends []string `yaml:"optional_depends,omitempty"`
|
OptionalDepends []string `yaml:"optional_depends,omitempty"`
|
||||||
Conflicts []string `yaml:"conflicts,omitempty"`
|
Conflicts []string `yaml:"conflicts,omitempty"`
|
||||||
Replaces []string `yaml:"replaces,omitempty"`
|
Replaces []string `yaml:"replaces,omitempty"`
|
||||||
Provides []string `yaml:"provides,omitempty"`
|
Provides []string `yaml:"provides,omitempty"`
|
||||||
|
SplitPackages []*PackageInfo `yaml:"split_packages,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PackageFileEntry struct {
|
type PackageFileEntry struct {
|
||||||
@ -68,12 +70,32 @@ func (pkgInfo *PackageInfo) GetFullVersion() string {
|
|||||||
return pkgInfo.Version + "-" + strconv.Itoa(pkgInfo.Revision)
|
return pkgInfo.Version + "-" + strconv.Itoa(pkgInfo.Revision)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pkgInfo *PackageInfo) IsSplitPackage() bool {
|
||||||
|
// Return false if not a source package
|
||||||
|
if pkgInfo.Type != "source" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(pkgInfo.SplitPackages) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkgInfo *PackageInfo) GetSplitPackageInfo(splitPkg string) *PackageInfo {
|
||||||
|
for _, splitPkgInfo := range pkgInfo.SplitPackages {
|
||||||
|
if splitPkgInfo.Name == splitPkg {
|
||||||
|
return splitPkgInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type InstallationReason string
|
type InstallationReason string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
InstallationReasonManual InstallationReason = "manual"
|
InstallationReasonManual InstallationReason = "manual"
|
||||||
InstallationReasonDependency InstallationReason = "dependency"
|
InstallationReasonDependency InstallationReason = "dependency"
|
||||||
InstallationReasonUnknown InstallationReason = "unknown"
|
InstallationReasonMakeDependency InstallationReason = "make_dependency"
|
||||||
|
InstallationReasonUnknown InstallationReason = "unknown"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ComparePackageVersions(info1, info2 PackageInfo) int {
|
func ComparePackageVersions(info1, info2 PackageInfo) int {
|
||||||
@ -98,6 +120,8 @@ func GetInstallationReason(pkg, rootDir string) InstallationReason {
|
|||||||
return InstallationReasonManual
|
return InstallationReasonManual
|
||||||
} else if reason == "dependency" {
|
} else if reason == "dependency" {
|
||||||
return InstallationReasonDependency
|
return InstallationReasonDependency
|
||||||
|
} else if reason == "make_dependency" {
|
||||||
|
return InstallationReasonMakeDependency
|
||||||
}
|
}
|
||||||
return InstallationReasonUnknown
|
return InstallationReasonUnknown
|
||||||
}
|
}
|
||||||
@ -147,7 +171,6 @@ func ReadPackage(filename string) (*BPMPackage, error) {
|
|||||||
var pkgFiles []*PackageFileEntry
|
var pkgFiles []*PackageFileEntry
|
||||||
|
|
||||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||||
fmt.Println("a")
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,6 +241,31 @@ func ReadPackage(filename string) (*BPMPackage, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPackageScripts(filename string) (packageScripts []string) {
|
||||||
|
content, err := listTarballContent(filename)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range content {
|
||||||
|
if file == "pre_install.sh" {
|
||||||
|
packageScripts = append(packageScripts, "pre_install.sh")
|
||||||
|
} else if file == "post_install.sh" {
|
||||||
|
packageScripts = append(packageScripts, "post_install.sh")
|
||||||
|
} else if file == "pre_update.sh" {
|
||||||
|
packageScripts = append(packageScripts, "pre_update.sh")
|
||||||
|
} else if file == "post_update.sh" {
|
||||||
|
packageScripts = append(packageScripts, "post_update.sh")
|
||||||
|
} else if file == "pre_remove.sh" {
|
||||||
|
packageScripts = append(packageScripts, "pre_remove.sh")
|
||||||
|
} else if file == "post_remove.sh" {
|
||||||
|
packageScripts = append(packageScripts, "post_remove.sh")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return packageScripts
|
||||||
|
}
|
||||||
|
|
||||||
func ReadPackageScripts(filename string) (map[string]string, error) {
|
func ReadPackageScripts(filename string) (map[string]string, error) {
|
||||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -237,31 +285,7 @@ func ReadPackageScripts(filename string) (map[string]string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if header.Name == "pre_install.sh" {
|
if header.Name == "pre_install.sh" || header.Name == "post_install.sh" || header.Name == "pre_update.sh" || header.Name == "post_update.sh" || header.Name == "pre_remove.sh" || header.Name == "post_remove.sh" {
|
||||||
bs, err := io.ReadAll(tr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret[header.Name] = string(bs)
|
|
||||||
} else if header.Name == "post_install.sh" {
|
|
||||||
bs, err := io.ReadAll(tr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret[header.Name] = string(bs)
|
|
||||||
} else if header.Name == "pre_update.sh" {
|
|
||||||
bs, err := io.ReadAll(tr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret[header.Name] = string(bs)
|
|
||||||
} else if header.Name == "post_update.sh" {
|
|
||||||
bs, err := io.ReadAll(tr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret[header.Name] = string(bs)
|
|
||||||
} else if header.Name == "post_remove.sh" {
|
|
||||||
bs, err := io.ReadAll(tr)
|
bs, err := io.ReadAll(tr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -385,7 +409,7 @@ func executePackageScripts(filename, rootDir string, operation packageOperation,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ReadPackageInfo(contents string) (*PackageInfo, error) {
|
func ReadPackageInfo(contents string) (*PackageInfo, error) {
|
||||||
pkgInfo := PackageInfo{
|
pkgInfo := &PackageInfo{
|
||||||
Name: "",
|
Name: "",
|
||||||
Description: "",
|
Description: "",
|
||||||
Version: "",
|
Version: "",
|
||||||
@ -401,11 +425,14 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
|
|||||||
Conflicts: make([]string, 0),
|
Conflicts: make([]string, 0),
|
||||||
Replaces: make([]string, 0),
|
Replaces: make([]string, 0),
|
||||||
Provides: make([]string, 0),
|
Provides: make([]string, 0),
|
||||||
|
SplitPackages: make([]*PackageInfo, 0),
|
||||||
}
|
}
|
||||||
err := yaml.Unmarshal([]byte(contents), &pkgInfo)
|
err := yaml.Unmarshal([]byte(contents), &pkgInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure required fields are set properly
|
||||||
if pkgInfo.Name == "" {
|
if pkgInfo.Name == "" {
|
||||||
return nil, errors.New("this package contains no name")
|
return nil, errors.New("this package contains no name")
|
||||||
} else if pkgInfo.Description == "" {
|
} else if pkgInfo.Description == "" {
|
||||||
@ -419,13 +446,47 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
|
|||||||
} else if pkgInfo.Type == "" {
|
} else if pkgInfo.Type == "" {
|
||||||
return nil, errors.New("this package contains no type")
|
return nil, errors.New("this package contains no type")
|
||||||
}
|
}
|
||||||
for i := 0; i < len(pkgInfo.Keep); i++ {
|
for _, val := range pkgInfo.Keep {
|
||||||
pkgInfo.Keep[i] = strings.TrimPrefix(pkgInfo.Keep[i], "/")
|
if strings.HasPrefix(val, "/") {
|
||||||
|
return nil, fmt.Errorf("cannot keep file (%s) after update because it starts with a slash", val)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return &pkgInfo, nil
|
|
||||||
|
// Setup split package information
|
||||||
|
for i, splitPkg := range pkgInfo.SplitPackages {
|
||||||
|
// Ensure split package contains a name
|
||||||
|
if splitPkg.Name == "" {
|
||||||
|
return nil, fmt.Errorf("invalid split package name: %s", splitPkg.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn split package into json data
|
||||||
|
splitPkgJson, err := yaml.Marshal(splitPkg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone all main package fields onto split package
|
||||||
|
pkgInfoClone := *pkgInfo
|
||||||
|
pkgInfo.SplitPackages[i] = &pkgInfoClone
|
||||||
|
|
||||||
|
// Set split package field of split package to nil
|
||||||
|
pkgInfo.SplitPackages[i].SplitPackages = nil
|
||||||
|
|
||||||
|
// Unmarshal json data back to struct
|
||||||
|
err = yaml.Unmarshal(splitPkgJson, &pkgInfo.SplitPackages[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force set split package version, revision
|
||||||
|
pkgInfo.SplitPackages[i].Version = pkgInfo.Version
|
||||||
|
pkgInfo.SplitPackages[i].Revision = pkgInfo.Revision
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkgInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, pkgInfo *PackageInfo, rootDir string) string {
|
func CreateReadableInfo(showArchitecture, showType, showPackageRelations, showInstallationReason bool, pkgInfo *PackageInfo, rootDir string) string {
|
||||||
ret := make([]string, 0)
|
ret := make([]string, 0)
|
||||||
appendArray := func(label string, array []string) {
|
appendArray := func(label string, array []string) {
|
||||||
if len(array) == 0 {
|
if len(array) == 0 {
|
||||||
@ -450,7 +511,7 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p
|
|||||||
appendArray("Make Dependencies", pkgInfo.MakeDepends)
|
appendArray("Make Dependencies", pkgInfo.MakeDepends)
|
||||||
}
|
}
|
||||||
appendArray("Optional dependencies", pkgInfo.OptionalDepends)
|
appendArray("Optional dependencies", pkgInfo.OptionalDepends)
|
||||||
dependants, err := pkgInfo.GetDependants(rootDir)
|
dependants, err := GetPackageDependants(pkgInfo.Name, rootDir)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
appendArray("Dependant packages", dependants)
|
appendArray("Dependant packages", dependants)
|
||||||
}
|
}
|
||||||
@ -458,7 +519,28 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p
|
|||||||
appendArray("Provided packages", pkgInfo.Provides)
|
appendArray("Provided packages", pkgInfo.Provides)
|
||||||
appendArray("Replaces packages", pkgInfo.Replaces)
|
appendArray("Replaces packages", pkgInfo.Replaces)
|
||||||
}
|
}
|
||||||
ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir)))
|
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)
|
||||||
|
}
|
||||||
|
if IsPackageInstalled(pkgInfo.Name, rootDir) && showInstallationReason {
|
||||||
|
installationReason := GetInstallationReason(pkgInfo.Name, rootDir)
|
||||||
|
var installationReasonString string
|
||||||
|
switch installationReason {
|
||||||
|
case InstallationReasonManual:
|
||||||
|
installationReasonString = "Manual"
|
||||||
|
case InstallationReasonDependency:
|
||||||
|
installationReasonString = "Dependency"
|
||||||
|
case InstallationReasonMakeDependency:
|
||||||
|
installationReasonString = "Make dependency"
|
||||||
|
default:
|
||||||
|
installationReasonString = "Unknown"
|
||||||
|
}
|
||||||
|
ret = append(ret, "Installation Reason: "+installationReasonString)
|
||||||
|
}
|
||||||
return strings.Join(ret, "\n")
|
return strings.Join(ret, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,7 +562,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tarballFile, err := readTarballContent(filename, "files.tar.gz")
|
tarballFile, err := readTarballFile(filename, "files.tar.gz")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -605,11 +687,17 @@ func installPackage(filename, rootDir string, verbose, force bool) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure package type is 'binary'
|
||||||
|
if bpmpkg.PkgInfo.Type != "binary" {
|
||||||
|
return fmt.Errorf("can only extract binary packages")
|
||||||
|
}
|
||||||
|
|
||||||
packageInstalled := IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir)
|
packageInstalled := IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir)
|
||||||
// Check if package is installed and remove current files
|
// Check if package is installed and remove current files
|
||||||
if packageInstalled {
|
if packageInstalled {
|
||||||
// Fetching and reversing package file entry list
|
// Fetching and reversing package file entry list
|
||||||
fileEntries := GetPackageFiles(bpmpkg.PkgInfo.Name, rootDir)
|
fileEntries := GetPackage(bpmpkg.PkgInfo.Name, rootDir).PkgFiles
|
||||||
sort.Slice(fileEntries, func(i, j int) bool {
|
sort.Slice(fileEntries, func(i, j int) bool {
|
||||||
return fileEntries[i].Path < fileEntries[j].Path
|
return fileEntries[i].Path < fileEntries[j].Path
|
||||||
})
|
})
|
||||||
@ -710,15 +798,10 @@ func installPackage(filename, rootDir string, verbose, force bool) error {
|
|||||||
fmt.Printf("Extracting files for package (%s)...\n", bpmpkg.PkgInfo.Name)
|
fmt.Printf("Extracting files for package (%s)...\n", bpmpkg.PkgInfo.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if bpmpkg.PkgInfo.Type == "binary" {
|
// Extract package files into rootDir
|
||||||
err := extractPackage(bpmpkg, verbose, filename, rootDir)
|
err = extractPackage(bpmpkg, verbose, filename, rootDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
} else if bpmpkg.PkgInfo.Type == "source" {
|
|
||||||
return errors.New("direct source package compilation in BPM has been temporarily removed and is being reworked on")
|
|
||||||
} else {
|
|
||||||
return errors.New("unknown package type: " + bpmpkg.PkgInfo.Type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
|
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
|
||||||
@ -738,7 +821,7 @@ func installPackage(filename, rootDir string, verbose, force bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tarballFile, err := readTarballContent(filename, "pkg.files")
|
tarballFile, err := readTarballFile(filename, "pkg.files")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -766,23 +849,30 @@ func installPackage(filename, rootDir string, verbose, force bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
scripts, err := ReadPackageScripts(filename)
|
// Save remove package scripts
|
||||||
|
packageScripts, err := ReadPackageScripts(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if val, ok := scripts["post_remove.sh"]; ok {
|
for script, content := range packageScripts {
|
||||||
f, err = os.Create(path.Join(pkgDir, "post_remove.sh"))
|
if !strings.HasSuffix(script, "_remove.sh") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create file
|
||||||
|
f, err = os.Create(path.Join(pkgDir, script))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = f.WriteString(val)
|
|
||||||
if err != nil {
|
// Write script contents to file
|
||||||
return err
|
_, err = f.WriteString(content)
|
||||||
}
|
|
||||||
err = f.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close file
|
||||||
|
f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !packageInstalled {
|
if !packageInstalled {
|
||||||
@ -796,315 +886,19 @@ func installPackage(filename, rootDir string, verbose, force bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure local package information has been initialized for rootDir
|
||||||
|
err = initializeLocalPackageInformation(rootDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add or update package information for rootDir
|
||||||
|
localPackageInformation[rootDir][bpmpkg.PkgInfo.Name] = bpmpkg
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pkgInfo *PackageInfo) GetAllDependencies(checkMake, checkOptional bool) []string {
|
|
||||||
allDepends := make([]string, 0)
|
|
||||||
allDepends = append(allDepends, pkgInfo.Depends...)
|
|
||||||
if checkMake {
|
|
||||||
allDepends = append(allDepends, pkgInfo.MakeDepends...)
|
|
||||||
}
|
|
||||||
if checkOptional {
|
|
||||||
allDepends = append(allDepends, pkgInfo.OptionalDepends...)
|
|
||||||
}
|
|
||||||
return allDepends
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pkgInfo *PackageInfo) CheckDependencies(checkMake, checkOptional bool, rootDir string) []string {
|
|
||||||
var ret []string
|
|
||||||
for _, dependency := range pkgInfo.Depends {
|
|
||||||
if !IsPackageProvided(dependency, rootDir) {
|
|
||||||
ret = append(ret, dependency)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if checkMake {
|
|
||||||
for _, dependency := range pkgInfo.MakeDepends {
|
|
||||||
if !IsPackageProvided(dependency, rootDir) {
|
|
||||||
ret = append(ret, dependency)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if checkOptional {
|
|
||||||
for _, dependency := range pkgInfo.OptionalDepends {
|
|
||||||
if !IsPackageProvided(dependency, rootDir) {
|
|
||||||
ret = append(ret, dependency)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pkgInfo *PackageInfo) GetDependants(rootDir string) ([]string, error) {
|
|
||||||
ret := make([]string, 0)
|
|
||||||
|
|
||||||
pkgs, err := GetInstalledPackages(rootDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("could not get installed packages")
|
|
||||||
}
|
|
||||||
for _, pkg := range pkgs {
|
|
||||||
bpmpkg := GetPackage(pkg, rootDir)
|
|
||||||
if bpmpkg == nil {
|
|
||||||
return nil, errors.New("package not found: " + pkg)
|
|
||||||
}
|
|
||||||
if bpmpkg.PkgInfo.Name == pkgInfo.Name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies := bpmpkg.PkgInfo.GetAllDependencies(false, true)
|
|
||||||
|
|
||||||
if slices.Contains(dependencies, pkgInfo.Name) {
|
|
||||||
ret = append(ret, pkg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, vpkg := range pkgInfo.Provides {
|
|
||||||
if slices.Contains(dependencies, vpkg) {
|
|
||||||
ret = append(ret, pkg)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pkgInfo *PackageInfo) CheckConflicts(rootDir string) []string {
|
|
||||||
var ret []string
|
|
||||||
for _, conflict := range pkgInfo.Conflicts {
|
|
||||||
if IsPackageInstalled(conflict, rootDir) {
|
|
||||||
ret = append(ret, conflict)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pkgInfo *PackageInfo) ResolveDependencies(resolved, unresolved *[]string, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) ([]string, []string) {
|
|
||||||
*unresolved = append(*unresolved, pkgInfo.Name)
|
|
||||||
for _, depend := range pkgInfo.GetAllDependencies(checkMake, checkOptional) {
|
|
||||||
depend = strings.TrimSpace(depend)
|
|
||||||
depend = strings.ToLower(depend)
|
|
||||||
if !slices.Contains(*resolved, depend) {
|
|
||||||
if slices.Contains(*unresolved, depend) {
|
|
||||||
if verbose {
|
|
||||||
fmt.Printf("Circular dependency was detected (%s -> %s). Installing %s first\n", pkgInfo.Name, depend, depend)
|
|
||||||
}
|
|
||||||
if !slices.Contains(*resolved, depend) {
|
|
||||||
*resolved = append(*resolved, depend)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
} else if ignoreInstalled && IsPackageProvided(depend, rootDir) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
var entry *RepositoryEntry
|
|
||||||
entry, _, err = GetRepositoryEntry(depend)
|
|
||||||
if err != nil {
|
|
||||||
if entry = ResolveVirtualPackage(depend); entry == nil {
|
|
||||||
if !slices.Contains(*unresolved, depend) {
|
|
||||||
*unresolved = append(*unresolved, depend)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entry.Info.ResolveDependencies(resolved, unresolved, checkMake, checkOptional, ignoreInstalled, verbose, rootDir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !slices.Contains(*resolved, pkgInfo.Name) {
|
|
||||||
*resolved = append(*resolved, pkgInfo.Name)
|
|
||||||
}
|
|
||||||
*unresolved = stringSliceRemove(*unresolved, pkgInfo.Name)
|
|
||||||
return *resolved, *unresolved
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsPackageInstalled(pkg, rootDir string) bool {
|
|
||||||
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
|
|
||||||
pkgDir := path.Join(installedDir, pkg)
|
|
||||||
if _, err := os.Stat(pkgDir); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsVirtualPackage(pkg, rootDir string) (bool, string) {
|
|
||||||
pkgs, err := GetInstalledPackages(rootDir)
|
|
||||||
if err != nil {
|
|
||||||
return false, ""
|
|
||||||
}
|
|
||||||
for _, p := range pkgs {
|
|
||||||
if p == pkg {
|
|
||||||
return false, ""
|
|
||||||
}
|
|
||||||
i := GetPackageInfo(p, rootDir)
|
|
||||||
if i == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if slices.Contains(i.Provides, pkg) {
|
|
||||||
return true, p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsPackageProvided(pkg, rootDir string) bool {
|
|
||||||
pkgs, err := GetInstalledPackages(rootDir)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, p := range pkgs {
|
|
||||||
if p == pkg {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
i := GetPackageInfo(p, rootDir)
|
|
||||||
if i == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if slices.Contains(i.Provides, pkg) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetInstalledPackages(rootDir string) ([]string, error) {
|
|
||||||
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
|
|
||||||
items, err := os.ReadDir(installedDir)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var ret []string
|
|
||||||
for _, item := range items {
|
|
||||||
ret = append(ret, item.Name())
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPackageFiles(pkg, rootDir string) []*PackageFileEntry {
|
|
||||||
var pkgFiles []*PackageFileEntry
|
|
||||||
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
|
|
||||||
pkgDir := path.Join(installedDir, pkg)
|
|
||||||
files := path.Join(pkgDir, "files")
|
|
||||||
if _, err := os.Stat(installedDir); os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(pkgDir); os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
bs, err := os.ReadFile(files)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, line := range strings.Split(string(bs), "\n") {
|
|
||||||
if strings.TrimSpace(line) == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
stringEntry := strings.Split(strings.TrimSpace(line), " ")
|
|
||||||
if len(stringEntry) < 5 {
|
|
||||||
pkgFiles = append(pkgFiles, &PackageFileEntry{
|
|
||||||
Path: strings.TrimSuffix(line, "/"),
|
|
||||||
OctalPerms: 0,
|
|
||||||
UserID: 0,
|
|
||||||
GroupID: 0,
|
|
||||||
SizeInBytes: 0,
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
uid, err := strconv.ParseInt(stringEntry[len(stringEntry)-4], 0, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
octalPerms, err := strconv.ParseInt(stringEntry[len(stringEntry)-3], 0, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
gid, err := strconv.ParseInt(stringEntry[len(stringEntry)-2], 0, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
size, err := strconv.ParseUint(stringEntry[len(stringEntry)-1], 0, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
pkgFiles = append(pkgFiles, &PackageFileEntry{
|
|
||||||
Path: strings.TrimSuffix(strings.Join(stringEntry[:len(stringEntry)-4], " "), "/"),
|
|
||||||
OctalPerms: uint32(octalPerms),
|
|
||||||
UserID: int(uid),
|
|
||||||
GroupID: int(gid),
|
|
||||||
SizeInBytes: size,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return pkgFiles
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPackageInfo(pkg, rootDir string) *PackageInfo {
|
|
||||||
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
|
|
||||||
pkgDir := path.Join(installedDir, pkg)
|
|
||||||
files := path.Join(pkgDir, "info")
|
|
||||||
if _, err := os.Stat(installedDir); os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(pkgDir); os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
file, err := os.Open(files)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
bs, err := io.ReadAll(file)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
info, err := ReadPackageInfo(string(bs))
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPackage(pkg, rootDir string) *BPMPackage {
|
|
||||||
if !IsPackageInstalled(pkg, rootDir) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &BPMPackage{
|
|
||||||
PkgInfo: GetPackageInfo(pkg, rootDir),
|
|
||||||
PkgFiles: GetPackageFiles(pkg, rootDir),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAllPackageFiles(rootDir string, excludePackages ...string) (map[string][]*BPMPackage, error) {
|
|
||||||
ret := make(map[string][]*BPMPackage)
|
|
||||||
|
|
||||||
pkgNames, err := GetInstalledPackages(rootDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, pkgName := range pkgNames {
|
|
||||||
if slices.Contains(excludePackages, pkgName) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
bpmpkg := GetPackage(pkgName, rootDir)
|
|
||||||
if bpmpkg == nil {
|
|
||||||
return nil, errors.New(fmt.Sprintf("could not get BPM package (%s)", pkgName))
|
|
||||||
}
|
|
||||||
for _, entry := range bpmpkg.PkgFiles {
|
|
||||||
if _, ok := ret[entry.Path]; ok {
|
|
||||||
ret[entry.Path] = append(ret[entry.Path], bpmpkg)
|
|
||||||
} else {
|
|
||||||
ret[entry.Path] = []*BPMPackage{bpmpkg}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func removePackage(pkg string, verbose bool, rootDir string) error {
|
func removePackage(pkg string, verbose bool, rootDir string) error {
|
||||||
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
|
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
|
||||||
pkgDir := path.Join(installedDir, pkg)
|
pkgDir := path.Join(installedDir, pkg)
|
||||||
@ -1113,8 +907,26 @@ func removePackage(pkg string, verbose bool, rootDir string) error {
|
|||||||
return errors.New("could not get package info")
|
return errors.New("could not get package info")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Executing pre_remove script
|
||||||
|
if _, err := os.Stat(path.Join(pkgDir, "pre_remove.sh")); err == nil {
|
||||||
|
cmd := exec.Command("/bin/bash", path.Join(pkgDir, "pre_remove.sh"))
|
||||||
|
cmd.Dir = rootDir
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.Name))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.Version))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", pkgInfo.Revision))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", pkgInfo.Url))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", pkgInfo.Arch))
|
||||||
|
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Warning: could not run pre_remove.sh package script: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fetching and reversing package file entry list
|
// Fetching and reversing package file entry list
|
||||||
fileEntries := GetPackageFiles(pkg, rootDir)
|
fileEntries := GetPackage(pkg, rootDir).PkgFiles
|
||||||
sort.Slice(fileEntries, func(i, j int) bool {
|
sort.Slice(fileEntries, func(i, j int) bool {
|
||||||
return fileEntries[i].Path < fileEntries[j].Path
|
return fileEntries[i].Path < fileEntries[j].Path
|
||||||
})
|
})
|
||||||
@ -1193,27 +1005,14 @@ func removePackage(pkg string, verbose bool, rootDir string) error {
|
|||||||
cmd.Env = os.Environ()
|
cmd.Env = os.Environ()
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir))
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.Name))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.Name))
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DESC=%s", pkgInfo.Description))
|
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.Version))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.Version))
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", pkgInfo.Revision))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", pkgInfo.Revision))
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", pkgInfo.Url))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", pkgInfo.Url))
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", pkgInfo.Arch))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", pkgInfo.Arch))
|
||||||
depends := make([]string, len(pkgInfo.Depends))
|
|
||||||
copy(depends, pkgInfo.Depends)
|
|
||||||
for i := 0; i < len(depends); i++ {
|
|
||||||
depends[i] = fmt.Sprintf("\"%s\"", depends[i])
|
|
||||||
}
|
|
||||||
makeDepends := make([]string, len(pkgInfo.MakeDepends))
|
|
||||||
copy(makeDepends, pkgInfo.MakeDepends)
|
|
||||||
for i := 0; i < len(makeDepends); i++ {
|
|
||||||
makeDepends[i] = fmt.Sprintf("\"%s\"", makeDepends[i])
|
|
||||||
}
|
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DEPENDS=(%s)", strings.Join(depends, " ")))
|
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_MAKE_DEPENDS=(%s)", strings.Join(makeDepends, " ")))
|
|
||||||
cmd.Env = append(cmd.Env, "BPM_PKG_TYPE=source")
|
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Printf("Warning: could not run pre_remove.sh package script: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1226,5 +1025,14 @@ func removePackage(pkg string, verbose bool, rootDir string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure local package information has been initialized for rootDir
|
||||||
|
err = initializeLocalPackageInformation(rootDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add or update package information for rootDir
|
||||||
|
delete(localPackageInformation[rootDir], pkgInfo.Name)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,235 +0,0 @@
|
|||||||
package bpmlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Repository struct {
|
|
||||||
Name string `yaml:"name"`
|
|
||||||
Source string `yaml:"source"`
|
|
||||||
Disabled *bool `yaml:"disabled"`
|
|
||||||
Entries map[string]*RepositoryEntry
|
|
||||||
VirtualPackages map[string][]string
|
|
||||||
}
|
|
||||||
|
|
||||||
type RepositoryEntry struct {
|
|
||||||
Info *PackageInfo `yaml:"info"`
|
|
||||||
Download string `yaml:"download"`
|
|
||||||
DownloadSize uint64 `yaml:"download_size"`
|
|
||||||
InstalledSize uint64 `yaml:"installed_size"`
|
|
||||||
Repository *Repository
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *Repository) ContainsPackage(pkg string) bool {
|
|
||||||
_, ok := repo.Entries[pkg]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *Repository) ReadLocalDatabase() error {
|
|
||||||
repoFile := "/var/lib/bpm/repositories/" + repo.Name + ".bpmdb"
|
|
||||||
if _, err := os.Stat(repoFile); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes, err := os.ReadFile(repoFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
data := string(bytes)
|
|
||||||
for _, b := range strings.Split(data, "---") {
|
|
||||||
entry := RepositoryEntry{
|
|
||||||
Info: &PackageInfo{
|
|
||||||
Name: "",
|
|
||||||
Description: "",
|
|
||||||
Version: "",
|
|
||||||
Revision: 1,
|
|
||||||
Url: "",
|
|
||||||
License: "",
|
|
||||||
Arch: "",
|
|
||||||
Type: "",
|
|
||||||
Keep: make([]string, 0),
|
|
||||||
Depends: make([]string, 0),
|
|
||||||
MakeDepends: make([]string, 0),
|
|
||||||
OptionalDepends: make([]string, 0),
|
|
||||||
Conflicts: make([]string, 0),
|
|
||||||
Provides: make([]string, 0),
|
|
||||||
},
|
|
||||||
Download: "",
|
|
||||||
DownloadSize: 0,
|
|
||||||
InstalledSize: 0,
|
|
||||||
Repository: repo,
|
|
||||||
}
|
|
||||||
err := yaml.Unmarshal([]byte(b), &entry)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range entry.Info.Provides {
|
|
||||||
repo.VirtualPackages[p] = append(repo.VirtualPackages[p], entry.Info.Name)
|
|
||||||
}
|
|
||||||
repo.Entries[entry.Info.Name] = &entry
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *Repository) SyncLocalDatabase() error {
|
|
||||||
repoFile := "/var/lib/bpm/repositories/" + repo.Name + ".bpmdb"
|
|
||||||
|
|
||||||
// Get URL to database
|
|
||||||
u, err := url.JoinPath(repo.Source, "database.bpmdb")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve data from URL
|
|
||||||
resp, err := http.Get(u)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
// Load data into byte buffer
|
|
||||||
buffer, err := io.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
// Unmarshal data to ensure it is a valid BPM repository
|
|
||||||
err = yaml.Unmarshal(buffer, &Repository{})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not decode repository: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create parent directories to repository file
|
|
||||||
err = os.MkdirAll(path.Dir(repoFile), 0755)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create file and save repository data
|
|
||||||
out, err := os.Create(repoFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
|
|
||||||
_, err = out.Write(buffer)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadLocalDatabases() (err error) {
|
|
||||||
for _, repo := range BPMConfig.Repositories {
|
|
||||||
// Initialize struct values
|
|
||||||
repo.Entries = make(map[string]*RepositoryEntry)
|
|
||||||
repo.VirtualPackages = make(map[string][]string)
|
|
||||||
|
|
||||||
// Read database
|
|
||||||
err = repo.ReadLocalDatabase()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetRepository(name string) *Repository {
|
|
||||||
for _, repo := range BPMConfig.Repositories {
|
|
||||||
if repo.Name == name {
|
|
||||||
return repo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetRepositoryEntry(str string) (*RepositoryEntry, *Repository, error) {
|
|
||||||
split := strings.Split(str, "/")
|
|
||||||
if len(split) == 1 {
|
|
||||||
pkgName := strings.TrimSpace(split[0])
|
|
||||||
if pkgName == "" {
|
|
||||||
return nil, nil, errors.New("could not find repository entry for this package")
|
|
||||||
}
|
|
||||||
for _, repo := range BPMConfig.Repositories {
|
|
||||||
if repo.ContainsPackage(pkgName) {
|
|
||||||
return repo.Entries[pkgName], repo, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, nil, errors.New("could not find repository entry for this package")
|
|
||||||
} else if len(split) == 2 {
|
|
||||||
repoName := strings.TrimSpace(split[0])
|
|
||||||
pkgName := strings.TrimSpace(split[1])
|
|
||||||
if repoName == "" || pkgName == "" {
|
|
||||||
return nil, nil, errors.New("could not find repository entry for this package")
|
|
||||||
}
|
|
||||||
repo := GetRepository(repoName)
|
|
||||||
if repo == nil || !repo.ContainsPackage(pkgName) {
|
|
||||||
return nil, nil, errors.New("could not find repository entry for this package")
|
|
||||||
}
|
|
||||||
return repo.Entries[pkgName], repo, nil
|
|
||||||
} else {
|
|
||||||
return nil, nil, errors.New("could not find repository entry for this package")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func FindReplacement(pkg string) *RepositoryEntry {
|
|
||||||
for _, repo := range BPMConfig.Repositories {
|
|
||||||
for _, entry := range repo.Entries {
|
|
||||||
for _, replaced := range entry.Info.Replaces {
|
|
||||||
if replaced == pkg {
|
|
||||||
return entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ResolveVirtualPackage(vpkg string) *RepositoryEntry {
|
|
||||||
for _, repo := range BPMConfig.Repositories {
|
|
||||||
if v, ok := repo.VirtualPackages[vpkg]; ok {
|
|
||||||
for _, pkg := range v {
|
|
||||||
return repo.Entries[pkg]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *Repository) FetchPackage(pkg string) (string, error) {
|
|
||||||
if !repo.ContainsPackage(pkg) {
|
|
||||||
return "", errors.New("could not fetch package '" + pkg + "'")
|
|
||||||
}
|
|
||||||
entry := repo.Entries[pkg]
|
|
||||||
URL, err := url.JoinPath(repo.Source, entry.Download)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
resp, err := http.Get(URL)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
err = os.MkdirAll("/var/cache/bpm/packages/", 0755)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
out, err := os.Create("/var/cache/bpm/packages/" + path.Base(entry.Download))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
|
|
||||||
_, err = io.Copy(out, resp.Body)
|
|
||||||
return "/var/cache/bpm/packages/" + path.Base(entry.Download), nil
|
|
||||||
}
|
|
@ -5,6 +5,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tarballFileReader struct {
|
type tarballFileReader struct {
|
||||||
@ -12,7 +14,35 @@ type tarballFileReader struct {
|
|||||||
file *os.File
|
file *os.File
|
||||||
}
|
}
|
||||||
|
|
||||||
func readTarballContent(tarballPath, fileToExtract string) (*tarballFileReader, error) {
|
func listTarballContent(tarballPath string) (content []string, err error) {
|
||||||
|
file, err := os.Open(tarballPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
tr := tar.NewReader(file)
|
||||||
|
for {
|
||||||
|
header, err := tr.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch header.Typeflag {
|
||||||
|
case tar.TypeDir:
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
content = append(content, header.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTarballFile(tarballPath, fileToExtract string) (*tarballFileReader, error) {
|
||||||
file, err := os.Open(tarballPath)
|
file, err := os.Open(tarballPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -41,3 +71,139 @@ func readTarballContent(tarballPath, fileToExtract string) (*tarballFileReader,
|
|||||||
|
|
||||||
return nil, errors.New("could not file in tarball")
|
return nil, errors.New("could not file in tarball")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractTarballFile(tarballPath, fileToExtract string, workingDirectory string, uid, gid int) (err error) {
|
||||||
|
file, err := os.Open(tarballPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
tr := tar.NewReader(file)
|
||||||
|
for {
|
||||||
|
header, err := tr.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if filename does not match
|
||||||
|
if header.Name != fileToExtract {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim directory name from header name
|
||||||
|
header.Name = strings.Split(header.Name, "/")[len(strings.Split(header.Name, "/"))-1]
|
||||||
|
outputPath := path.Join(workingDirectory, header.Name)
|
||||||
|
|
||||||
|
switch header.Typeflag {
|
||||||
|
case tar.TypeReg:
|
||||||
|
// Create file and set permissions
|
||||||
|
file, err = os.Create(outputPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err := file.Chmod(header.FileInfo().Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if uid >= 0 && gid >= 0 {
|
||||||
|
err = file.Chown(uid, gid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy data to file
|
||||||
|
_, err = io.Copy(file, tr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close file
|
||||||
|
file.Close()
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractTarballDirectory(tarballPath, directoryToExtract, workingDirectory string, uid, gid int) (err error) {
|
||||||
|
file, err := os.Open(tarballPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
tr := tar.NewReader(file)
|
||||||
|
for {
|
||||||
|
header, err := tr.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(header.Name, directoryToExtract+"/") {
|
||||||
|
// Skip directory to extract
|
||||||
|
if strings.TrimRight(header.Name, "/") == workingDirectory {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim directory name from header name
|
||||||
|
header.Name = strings.TrimPrefix(header.Name, directoryToExtract+"/")
|
||||||
|
outputPath := path.Join(workingDirectory, header.Name)
|
||||||
|
|
||||||
|
switch header.Typeflag {
|
||||||
|
case tar.TypeDir:
|
||||||
|
// Create directory
|
||||||
|
err := os.MkdirAll(outputPath, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set directory owner
|
||||||
|
if uid >= 0 && gid >= 0 {
|
||||||
|
err = os.Chown(outputPath, uid, gid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case tar.TypeReg:
|
||||||
|
// Create file and set permissions
|
||||||
|
file, err = os.Create(outputPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err := file.Chmod(header.FileInfo().Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if uid >= 0 && gid >= 0 {
|
||||||
|
err = file.Chown(uid, gid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy data to file
|
||||||
|
_, err = io.Copy(file, tr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close file
|
||||||
|
file.Close()
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -2,9 +2,7 @@ package bpmlib
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"math"
|
"math"
|
||||||
"os"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,7 +10,7 @@ func GetArch() string {
|
|||||||
uname := syscall.Utsname{}
|
uname := syscall.Utsname{}
|
||||||
err := syscall.Uname(&uname)
|
err := syscall.Uname(&uname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
var byteString [65]byte
|
var byteString [65]byte
|
||||||
@ -23,29 +21,6 @@ func GetArch() string {
|
|||||||
return string(byteString[:indexLength])
|
return string(byteString[:indexLength])
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyFileContents(src, dst string) (err error) {
|
|
||||||
in, err := os.Open(src)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer in.Close()
|
|
||||||
out, err := os.Create(dst)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
cerr := out.Close()
|
|
||||||
if err == nil {
|
|
||||||
err = cerr
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if _, err = io.Copy(out, in); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = out.Sync()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringSliceRemove(s []string, r string) []string {
|
func stringSliceRemove(s []string, r string) []string {
|
||||||
for i, v := range s {
|
for i, v := range s {
|
||||||
if v == r {
|
if v == r {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user