Compare commits

...

6 Commits

6 changed files with 144 additions and 535 deletions

View File

@ -2,6 +2,7 @@ package main
import (
"bufio"
"errors"
"flag"
"fmt"
"git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib"
@ -26,9 +27,6 @@ var subcommandArgs []string
var rootDir = "/"
var verbose = false
var yesAll = false
var buildSource = false
var skipCheck = false
var keepTempDir = false
var force = false
var pkgListNumbers = false
var pkgListNames = false
@ -42,7 +40,10 @@ var doCleanup = false
var showRepoInfo = false
func main() {
bpmlib.ReadConfig()
err := bpmlib.ReadConfig()
if err != nil {
log.Fatalf("Error: could not read BPM config: %s", err)
}
resolveFlags()
resolveCommand()
}
@ -235,8 +236,19 @@ func resolveCommand() {
reinstallMethod = bpmlib.ReinstallMethodNone
}
// Create Installation Operation
// Create installation operation
operation, err := bpmlib.InstallPackages(rootDir, ir, reinstallMethod, !noOptional, force, verbose, subcommandArgs...)
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
log.Fatalf("Error: %s", err)
} else if err != nil {
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()
@ -275,10 +287,18 @@ func resolveCommand() {
log.Fatalf("Error: this subcommand needs to be run with superuser permissions")
}
// Create Update Operation
// Create update operation
operation, err := bpmlib.UpdatePackages(rootDir, !nosync, !noOptional, force, verbose)
if err != nil {
log.Fatalf("Error: could not update packages: %s\n", err)
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
log.Fatalf("Error: %s", err)
} else if err != nil {
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
@ -324,6 +344,7 @@ func resolveCommand() {
}
}
// Sync databases
err := bpmlib.SyncDatabase(verbose)
if err != nil {
log.Fatalf("Error: could not sync local database: %s\n", err)
@ -341,10 +362,18 @@ func resolveCommand() {
return
}
// Remove packages
// Create remove operation
operation, err := bpmlib.RemovePackages(rootDir, removeUnused, doCleanup, verbose, subcommandArgs...)
if err != nil {
log.Fatalf("Error: could not remove packages: %s\n", err)
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
log.Fatalf("Error: %s", err)
} else if err != nil {
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
@ -379,10 +408,18 @@ func resolveCommand() {
log.Fatalf("Error: this subcommand needs to be run with superuser permissions")
}
// Do cleanup
// Create cleanup operation
operation, err := bpmlib.CleanupPackages(rootDir, verbose)
if err != nil {
log.Fatalf("Error: could not cleanup packages: %s\n", err)
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
log.Fatalf("Error: %s", err)
} else if err != nil {
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
@ -484,9 +521,6 @@ func printHelp() {
fmt.Println(" -v Show additional information about what BPM is doing")
fmt.Println(" -y skips the confirmation prompt")
fmt.Println(" -f skips dependency, conflict and architecture checking")
fmt.Println(" -o=<path> set the binary package output directory (defaults to /var/lib/bpm/compiled)")
fmt.Println(" -c=<path> set the compilation directory (defaults to /var/tmp)")
fmt.Println(" -b creates a binary package from a source package after compilation and saves it in the binary package output directory")
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-all Same as --reinstall but also reinstalls dependencies")
@ -507,8 +541,8 @@ func printHelp() {
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(" -y skips the confirmation prompt")
fmt.Println(" -unused removes only packages that aren't required as dependencies by other packages")
fmt.Println(" -cleanup performs a dependency cleanup")
fmt.Println(" --unused removes only packages that aren't required as dependencies by other packages")
fmt.Println(" --cleanup performs a dependency cleanup")
fmt.Println("-> bpm cleanup [-R, -v, -y] | remove all unused dependency packages")
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")
@ -535,11 +569,6 @@ func resolveFlags() {
installFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
installFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing")
installFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
installFlagSet.StringVar(&bpmlib.BPMConfig.BinaryOutputDir, "o", bpmlib.BPMConfig.BinaryOutputDir, "Set the binary output directory")
installFlagSet.StringVar(&bpmlib.BPMConfig.CompilationDir, "c", bpmlib.BPMConfig.CompilationDir, "Set the compilation directory")
installFlagSet.BoolVar(&buildSource, "b", false, "Build binary package from source package")
installFlagSet.BoolVar(&skipCheck, "s", false, "Skip check function during source compilation")
installFlagSet.BoolVar(&keepTempDir, "k", false, "Keep temporary directory after source compilation")
installFlagSet.BoolVar(&force, "f", false, "Force installation by skipping architecture and dependency resolution")
installFlagSet.BoolVar(&reinstall, "reinstall", false, "Reinstalls packages even if they do not have a newer version available")
installFlagSet.BoolVar(&reinstallAll, "reinstall-all", false, "Same as --reinstall but also reinstalls dependencies")

View File

@ -2,50 +2,46 @@ package bpmlib
import (
"gopkg.in/yaml.v3"
"log"
"os"
)
type BPMConfigStruct struct {
CompilationEnv []string `yaml:"compilation_env"`
SilentCompilation bool `yaml:"silent_compilation"`
BinaryOutputDir string `yaml:"binary_output_dir"`
CompilationDir string `yaml:"compilation_dir"`
IgnorePackages []string `yaml:"ignore_packages"`
Repositories []*Repository `yaml:"repositories"`
}
var BPMConfig BPMConfigStruct
func ReadConfig() {
if _, err := os.Stat("/etc/bpm.conf"); os.IsNotExist(err) {
log.Fatal(err)
func ReadConfig() (err error) {
if _, err = os.Stat("/etc/bpm.conf"); os.IsNotExist(err) {
return err
}
bytes, err := os.ReadFile("/etc/bpm.conf")
if err != nil {
log.Fatal(err)
}
BPMConfig = BPMConfigStruct{
CompilationEnv: make([]string, 0),
SilentCompilation: false,
BinaryOutputDir: "/var/lib/bpm/compiled/",
CompilationDir: "/var/tmp/",
return err
}
BPMConfig = BPMConfigStruct{}
err = yaml.Unmarshal(bytes, &BPMConfig)
if err != nil {
log.Fatal(err)
return err
}
for i := len(BPMConfig.Repositories) - 1; i >= 0; i-- {
if BPMConfig.Repositories[i].Disabled != nil && *BPMConfig.Repositories[i].Disabled {
BPMConfig.Repositories = append(BPMConfig.Repositories[:i], BPMConfig.Repositories[i+1:]...)
}
}
for _, repo := range BPMConfig.Repositories {
repo.Entries = make(map[string]*RepositoryEntry)
repo.VirtualPackages = make(map[string][]string)
err := repo.ReadLocalDatabase()
if err != nil {
log.Fatal(err)
return err
}
}
return nil
}

31
src/bpmlib/errors.go Normal file
View File

@ -0,0 +1,31 @@
package bpmlib
import (
"fmt"
"strings"
)
type PackageNotFoundErr struct {
packages []string
}
func (e PackageNotFoundErr) Error() string {
return "The following packages were not found in any repositories: " + strings.Join(e.packages, ", ")
}
type DependencyNotFoundErr struct {
dependencies []string
}
func (e DependencyNotFoundErr) Error() string {
return "The following dependencies were not found in any repositories: " + strings.Join(e.dependencies, ", ")
}
type PackageConflictErr struct {
pkg string
conflicts []string
}
func (e PackageConflictErr) Error() string {
return fmt.Sprintf("Package (%s) is in conflict with the following packages: %s", e.pkg, strings.Join(e.conflicts, ", "))
}

View File

@ -1,11 +1,11 @@
package bpmlib
import (
"errors"
"fmt"
"log"
"os"
"slices"
"strings"
)
type ReinstallMethod uint8
@ -28,6 +28,7 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein
}
// Resolve packages
pkgsNotFound := make([]string, 0)
for _, pkg := range packages {
if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() {
bpmpkg, err := ReadPackage(pkg)
@ -50,12 +51,14 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein
} else if isVirtual, p := IsVirtualPackage(pkg, rootDir); isVirtual {
entry, _, err = GetRepositoryEntry(p)
if err != nil {
return nil, fmt.Errorf("could not find package (%s) in any repositor", p)
pkgsNotFound = append(pkgsNotFound, pkg)
continue
}
} else if e := ResolveVirtualPackage(pkg); e != nil {
entry = e
} else {
return nil, fmt.Errorf("could not find package (%s) in any repository", pkg)
pkgsNotFound = append(pkgsNotFound, pkg)
continue
}
if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(entry.Info.Name, rootDir) && GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() {
continue
@ -67,6 +70,11 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein
}
}
// Return error if not all packages are found
if len(pkgsNotFound) != 0 {
return nil, PackageNotFoundErr{pkgsNotFound}
}
// Resolve dependencies
err = operation.ResolveDependencies(reinstallMethod == ReinstallMethodAll, installOptionalDependencies, verbose)
if err != nil {
@ -74,9 +82,9 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein
}
if len(operation.UnresolvedDepends) != 0 {
if !forceInstallation {
return nil, fmt.Errorf("dependencies (%s) could not be found in any repositories", strings.Join(operation.UnresolvedDepends, ", "))
return nil, DependencyNotFoundErr{operation.UnresolvedDepends}
} else if verbose {
log.Println("Warning: The following dependencies could not be found in any repositories: " + strings.Join(operation.UnresolvedDepends, ", "))
log.Printf("Warning: %s", DependencyNotFoundErr{operation.UnresolvedDepends})
}
}
@ -89,15 +97,14 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein
return nil, fmt.Errorf("could not complete package conflict check: %s", err)
}
if len(conflicts) > 0 {
if verbose {
err = nil
for pkg, conflict := range conflicts {
fmt.Printf("%s is in conflict with packages (%s)\n", pkg, strings.Join(conflict, ", "))
}
err = errors.Join(err, PackageConflictErr{pkg, conflict})
}
if !forceInstallation {
return nil, fmt.Errorf("conflicting packages found")
return nil, err
} else {
log.Println("Warning: conflicting packages found")
log.Printf("Warning: %s", err)
}
}
@ -172,7 +179,10 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci
}
// Reload config and local databases
ReadConfig()
err = ReadConfig()
if err != nil {
return nil, fmt.Errorf("could not read BPM config: %s", err)
}
// Get installed packages and check for updates
pkgs, err := GetInstalledPackages(rootDir)
@ -222,9 +232,9 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci
}
if len(operation.UnresolvedDepends) != 0 {
if !forceInstallation {
return nil, fmt.Errorf("dependencies (%s) could not be found in any repositories", strings.Join(operation.UnresolvedDepends, ", "))
return nil, DependencyNotFoundErr{operation.UnresolvedDepends}
} else if verbose {
log.Printf("Warning: dependencies (%s) could not be found in any repositories\n", strings.Join(operation.UnresolvedDepends, ", "))
log.Printf("Warning: %s", DependencyNotFoundErr{operation.UnresolvedDepends})
}
}

View File

@ -195,7 +195,7 @@ func (operation *BPMOperation) Cleanup(verbose bool) error {
// Get all installed packages
installedPackageNames, err := GetInstalledPackages(operation.RootDir)
if err != nil {
log.Fatalf("Error: could not get installed packages: %s\n", err)
return fmt.Errorf("could not get installed packages: %s", err)
}
installedPackages := make([]*PackageInfo, len(installedPackageNames))
for i, value := range installedPackageNames {
@ -329,8 +329,8 @@ func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error)
func (operation *BPMOperation) ShowOperationSummary() {
if len(operation.Actions) == 0 {
fmt.Println("All packages are up to date!")
os.Exit(0)
fmt.Println("No action needs to be taken")
return
}
for _, value := range operation.Actions {
@ -348,9 +348,6 @@ func (operation *BPMOperation) ShowOperationSummary() {
installedInfo := GetPackageInfo(pkgInfo.Name, operation.RootDir)
sourceInfo := ""
if pkgInfo.Type == "source" {
if operation.RootDir != "/" {
log.Fatalf("cannot compile and install source packages to a different root directory")
}
sourceInfo = "(From Source)"
}
@ -468,9 +465,9 @@ func (operation *BPMOperation) Execute(verbose, force bool) error {
isReinstall := IsPackageInstalled(bpmpkg.PkgInfo.Name, operation.RootDir)
var err error
if value.IsDependency {
err = installPackage(value.File, operation.RootDir, verbose, true, false, false, false)
err = installPackage(value.File, operation.RootDir, verbose, true)
} else {
err = installPackage(value.File, operation.RootDir, verbose, force, false, false, false)
err = installPackage(value.File, operation.RootDir, verbose, force)
}
if err != nil {
return errors.New(fmt.Sprintf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err))

View File

@ -8,16 +8,13 @@ import (
version "github.com/knqyf263/go-rpm-version"
"gopkg.in/yaml.v3"
"io"
"io/fs"
"os"
"os/exec"
"path"
"path/filepath"
"slices"
"sort"
"strconv"
"strings"
"syscall"
)
type BPMPackage struct {
@ -309,11 +306,6 @@ func executePackageScripts(filename, rootDir string, operation packageOperation,
}
cmd := exec.Command("/bin/bash", temp.Name())
if !BPMConfig.SilentCompilation {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
cmd.Dir = rootDir
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir))
@ -433,14 +425,6 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
return &pkgInfo, nil
}
func CreateInfoFile(pkgInfo *PackageInfo) string {
b, err := yaml.Marshal(&pkgInfo)
if err != nil {
return ""
}
return string(b)
}
func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, pkgInfo *PackageInfo, rootDir string) string {
ret := make([]string, 0)
appendArray := func(label string, array []string) {
@ -611,447 +595,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
return nil
}
func isSplitPackage(filename string) bool {
pkgInfo, err := ReadPackage(filename)
if err != nil {
return false
}
if pkgInfo.PkgInfo.Type != "source" {
return false
}
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("test $(tar -tf %s | grep '^pkg.info' | wc -l) -eq 1", filename))
if err := cmd.Run(); err == nil {
return false
}
return true
}
func compilePackage(bpmpkg *BPMPackage, filename, rootDir string, verbose, binaryPkgFromSrc, skipCheck, keepTempDir bool) (error, []string) {
var files []string
if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) {
err := executePackageScripts(filename, rootDir, packageOperationInstall, false)
if err != nil {
return err, nil
}
} else {
err := executePackageScripts(filename, rootDir, packageOperationUpdate, false)
if err != nil {
return err, nil
}
}
//seenHardlinks := make(map[string]string)
file, err := os.Open(filename)
if err != nil {
return err, nil
}
tr := tar.NewReader(file)
temp := path.Join(BPMConfig.CompilationDir, "bpm_source-"+bpmpkg.PkgInfo.Name)
err = os.RemoveAll(temp)
if err != nil {
return err, nil
}
if verbose {
fmt.Println("Creating temp directory at: " + temp)
}
err = os.Mkdir(temp, 0755)
if err != nil {
return err, nil
}
err = os.Chown(temp, 65534, 65534)
if err != nil {
return err, nil
}
for {
header, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return err, nil
}
if strings.HasPrefix(header.Name, "source-files/") && header.Name != "source-files/" {
extractFilename := path.Join(temp, strings.TrimPrefix(header.Name, "source-files/"))
switch header.Typeflag {
case tar.TypeDir:
if err := os.Mkdir(extractFilename, 0755); err != nil {
if !os.IsExist(err) {
return err, nil
}
} else {
if verbose {
fmt.Println("Creating Directory: " + extractFilename)
}
err = os.Chown(extractFilename, 65534, 65534)
if err != nil {
return err, nil
}
}
case tar.TypeReg:
err := os.Remove(extractFilename)
if err != nil && !os.IsNotExist(err) {
return err, nil
}
outFile, err := os.Create(extractFilename)
if verbose {
fmt.Println("Creating File: " + extractFilename)
}
if err != nil {
return err, nil
}
err = os.Chown(extractFilename, 65534, 65534)
if err != nil {
return err, nil
}
if _, err := io.Copy(outFile, tr); err != nil {
return err, nil
}
if err := os.Chmod(extractFilename, header.FileInfo().Mode()); err != nil {
return err, nil
}
err = outFile.Close()
if err != nil {
return err, nil
}
case tar.TypeSymlink:
if verbose {
fmt.Println("Skipping symlink (Bundling symlinks in source packages is not supported)")
}
case tar.TypeLink:
if verbose {
fmt.Println("Skipping hard link (Bundling hard links in source packages is not supported)")
}
default:
return errors.New("unknown type (" + strconv.Itoa(int(header.Typeflag)) + ") in " + extractFilename), nil
}
}
if header.Name == "source.sh" {
bs, err := io.ReadAll(tr)
if err != nil {
return err, nil
}
err = os.WriteFile(path.Join(temp, "source.sh"), bs, 0644)
if err != nil {
return err, nil
}
err = os.Chown(path.Join(temp, "source.sh"), 65534, 65534)
if err != nil {
return err, nil
}
}
}
if _, err := os.Stat(path.Join(temp, "source.sh")); os.IsNotExist(err) {
return errors.New("source.sh file could not be found in the temporary build directory"), nil
}
fmt.Println("Running source.sh file...")
if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) {
err = executePackageScripts(filename, rootDir, packageOperationInstall, false)
if err != nil {
return err, nil
}
} else {
err = executePackageScripts(filename, rootDir, packageOperationUpdate, false)
if err != nil {
return err, nil
}
}
bs, err := os.ReadFile(path.Join(temp, "source.sh"))
if err != nil {
return err, nil
}
if !strings.Contains(string(bs), "package()") {
fmt.Print("This package does not seem to have the required 'package' function\nThe source.sh file may have been created for an older BPM version\nPlease update the source.sh file")
return errors.New("invalid source.sh format"), nil
}
runScript := `
cd "$BPM_WORKDIR"
set -a
source "source.sh"
set +a
if [[ $(type -t prepare) == function ]]; then
echo "Running prepare() function..."
bash -e -c prepare
if [ $? -ne 0 ]; then
echo "Failed to run prepare() function in source.sh"
exit 1
fi
fi
cd "$BPM_SOURCE"
if [[ $(type -t build) == function ]]; then
echo "Running build() function..."
bash -e -c build
if [ $? -ne 0 ]; then
echo "Failed to run build() function in source.sh"
exit 1
fi
fi
cd "$BPM_SOURCE"
if [[ $(type -t check) == function ]] && [ -z "$SKIPCHECK" ]; then
echo "Running check() function..."
bash -e -c check
if [ $? -ne 0 ]; then
echo "Failed to run check() function in source.sh"
exit 1
fi
fi
cd "$BPM_SOURCE"
if ! [[ $(type -t package) == function ]]; then
echo "Failed to locate package() function in source.sh"
exit 1
fi
echo "Running package() function..."
touch "$BPM_WORKDIR"/fakeroot_file
fakeroot -s "$BPM_WORKDIR"/fakeroot_file bash -e -c package
bash -e -c package
if [ $? -ne 0 ]; then
echo "Failed to run package() function in source.sh"
fi
`
err = os.WriteFile(path.Join(temp, "run.sh"), []byte(runScript), 0644)
if err != nil {
return err, nil
}
err = os.Chown(path.Join(temp, "run.sh"), 65534, 65534)
if err != nil {
return err, nil
}
cmd := exec.Command("/bin/bash", "-e", "run.sh")
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: 65534, Gid: 65534}
cmd.Dir = temp
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "USER=nobody")
cmd.Env = append(cmd.Env, "HOME="+temp)
err = os.Mkdir(path.Join(temp, "source"), 0755)
if err != nil {
return err, nil
}
err = os.Chown(path.Join(temp, "source"), 65534, 65534)
if err != nil {
return err, nil
}
err = os.Mkdir(path.Join(temp, "output"), 0755)
if err != nil {
return err, nil
}
err = os.Chown(path.Join(temp, "output"), 65534, 65534)
if err != nil {
return err, nil
}
cmd.Env = append(cmd.Env, "BPM_WORKDIR="+temp)
cmd.Env = append(cmd.Env, "BPM_SOURCE="+path.Join(temp, "source"))
cmd.Env = append(cmd.Env, "BPM_OUTPUT="+path.Join(temp, "output"))
cmd.Env = append(cmd.Env, "SKIPCHECK="+strconv.FormatBool(skipCheck))
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir))
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", bpmpkg.PkgInfo.Name))
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DESC=%s", bpmpkg.PkgInfo.Description))
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", bpmpkg.PkgInfo.Version))
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", bpmpkg.PkgInfo.Revision))
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", bpmpkg.PkgInfo.Url))
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", bpmpkg.PkgInfo.Arch))
depends := make([]string, len(bpmpkg.PkgInfo.Depends))
copy(depends, bpmpkg.PkgInfo.Depends)
for i := 0; i < len(depends); i++ {
depends[i] = fmt.Sprintf("\"%s\"", depends[i])
}
makeDepends := make([]string, len(bpmpkg.PkgInfo.MakeDepends))
copy(makeDepends, bpmpkg.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, " ")))
for _, value := range BPMConfig.CompilationEnv {
cmd.Env = append(cmd.Env, value)
}
cmd.Env = append(cmd.Env, "BPM_PKG_TYPE=source")
if !BPMConfig.SilentCompilation {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
err = cmd.Run()
if err != nil {
return err, nil
}
if _, err := os.Stat(path.Join(temp, "output/")); err != nil {
if os.IsNotExist(err) {
return errors.New("output directory not be found at " + path.Join(temp, "output/")), nil
}
return err, nil
}
if dir, _ := os.ReadDir(path.Join(temp, "output/")); len(dir) == 0 {
return errors.New("output directory is empty"), nil
}
fmt.Println("Copying all files...")
err = filepath.WalkDir(path.Join(temp, "/output/"), func(fullpath string, d fs.DirEntry, err error) error {
relFilename, err := filepath.Rel(path.Join(temp, "/output/"), fullpath)
if relFilename == "." {
return nil
}
extractFilename := path.Join(rootDir, relFilename)
if err != nil {
return err
}
if d.Type() == os.ModeDir {
files = append(files, relFilename+"/")
if err := os.Mkdir(extractFilename, 0755); err != nil {
if !os.IsExist(err) {
return err
}
} else {
if verbose {
fmt.Println("Creating Directory: " + extractFilename)
}
}
} else if d.Type().IsRegular() {
if _, err := os.Stat(extractFilename); err == nil {
if slices.Contains(bpmpkg.PkgInfo.Keep, relFilename) {
if verbose {
fmt.Println("Skipping File: " + extractFilename + "(File is configured to be kept during installs/updates)")
}
files = append(files, relFilename)
return nil
}
}
err := os.Remove(extractFilename)
if err != nil && !os.IsNotExist(err) {
return err
}
outFile, err := os.Create(extractFilename)
if verbose {
fmt.Println("Creating File: " + extractFilename)
}
files = append(files, relFilename)
if err != nil {
return err
}
f, err := os.Open(fullpath)
if err != nil {
return err
}
if _, err := io.Copy(outFile, f); err != nil {
return err
}
info, err := os.Stat(fullpath)
if err != nil {
return err
}
if err := os.Chmod(extractFilename, info.Mode()); err != nil {
return err
}
err = outFile.Close()
if err != nil {
return err
}
err = f.Close()
if err != nil {
return err
}
} else if d.Type() == os.ModeSymlink {
link, err := os.Readlink(fullpath)
if err != nil {
return err
}
err = os.Remove(extractFilename)
if err != nil && !os.IsNotExist(err) {
return err
}
if verbose {
fmt.Println("Creating Symlink: "+extractFilename, " -> "+link)
}
files = append(files, relFilename)
err = os.Symlink(link, extractFilename)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return err, nil
}
if binaryPkgFromSrc {
compiledDir := path.Join(BPMConfig.BinaryOutputDir)
err = os.MkdirAll(compiledDir, 0755)
compiledInfo := PackageInfo{}
compiledInfo = *bpmpkg.PkgInfo
compiledInfo.Type = "binary"
compiledInfo.Arch = GetArch()
err = os.WriteFile(path.Join(temp, "pkg.info"), []byte(CreateInfoFile(&compiledInfo)), 0644)
if err != nil {
return err, nil
}
err = os.Chown(path.Join(temp, "pkg.info"), 65534, 65534)
if err != nil {
return err, nil
}
scripts, err := ReadPackageScripts(filename)
for key, val := range scripts {
err = os.WriteFile(path.Join(temp, key), []byte(val), 0644)
if err != nil {
return err, nil
}
err = os.Chown(path.Join(temp, key), 65534, 65534)
if err != nil {
return err, nil
}
}
sed := fmt.Sprintf("s/output/files/")
fileName := compiledInfo.Name + "-" + compiledInfo.GetFullVersion() + "-" + compiledInfo.Arch + ".bpm"
cmd := exec.Command("/usr/bin/fakeroot", "-i fakeroot_file", "tar", "-czvpf", fileName, "pkg.info", "output/", "--transform", sed)
if !BPMConfig.SilentCompilation {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: 65534, Gid: 65534}
cmd.Dir = temp
cmd.Env = os.Environ()
fmt.Printf("running command: %s\n", strings.Join(cmd.Args, " "))
err = cmd.Run()
if err != nil {
return err, nil
}
err = copyFileContents(path.Join(temp, fileName), path.Join(compiledDir, fileName))
if err != nil {
return err, nil
}
err = os.Chown(path.Join(compiledDir, fileName), 0, 0)
if err != nil {
return err, nil
}
}
if !keepTempDir {
err := os.RemoveAll(temp)
if err != nil {
return err, nil
}
}
if len(files) == 0 {
return errors.New("no output files for source package. Cancelling package installation"), nil
}
defer file.Close()
return nil, files
}
func installPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc, skipCheck, keepTempDir bool) error {
func installPackage(filename, rootDir string, verbose, force bool) error {
if _, err := os.Stat(filename); os.IsNotExist(err) {
return err
}
@ -1170,13 +714,7 @@ func installPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc,
return err
}
} else if bpmpkg.PkgInfo.Type == "source" {
if isSplitPackage(filename) {
return errors.New("BPM is unable to install split source packages")
}
err, _ := compilePackage(bpmpkg, filename, rootDir, verbose, binaryPkgFromSrc, skipCheck, keepTempDir)
if err != nil {
return err
}
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)
}
@ -1308,8 +846,21 @@ func (pkgInfo *PackageInfo) GetDependants(rootDir string) ([]string, error) {
if bpmpkg == nil {
return nil, errors.New("package not found: " + pkg)
}
if bpmpkg.PkgInfo.Name != pkgInfo.Name && slices.Contains(bpmpkg.PkgInfo.GetAllDependencies(false, true), pkgInfo.Name) {
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
}
}
}
@ -1636,11 +1187,6 @@ func removePackage(pkg string, verbose bool, rootDir string) error {
// Executing post_remove script
if _, err := os.Stat(path.Join(pkgDir, "post_remove.sh")); err == nil {
cmd := exec.Command("/bin/bash", path.Join(pkgDir, "post_remove.sh"))
if !BPMConfig.SilentCompilation {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
cmd.Dir = rootDir
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir))