WIP: Readd source package compilation functionality #11

Draft
EnumDev wants to merge 33 commits from readd_source_packages into develop
5 changed files with 238 additions and 151 deletions
Showing only changes of commit 5085981f52 - Show all commits

View File

@ -671,12 +671,14 @@ func resolveCommand() {
outputFilename = path.Join(outputFilename, fmt.Sprintf("%s-%s-%d.bpm", bpmpkg.PkgInfo.Name, bpmpkg.PkgInfo.Version, bpmpkg.PkgInfo.Revision))
}
err = bpmlib.CompileSourcePackage(sourcePackage, outputFilename, skipChecks)
outputBpmPackages, err := bpmlib.CompileSourcePackage(sourcePackage, outputFilename, skipChecks)
if err != nil {
log.Fatalf("Error: could not compile source package (%s): %s", sourcePackage, err)
}
fmt.Printf("Package (%s) was successfully compiled! Binary package generated at: %s\n", sourcePackage, outputFilename)
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 {

View File

@ -15,28 +15,31 @@ import (
var rootCompilationUID = "65534"
var rootCompilationGID = "65534"
func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks bool) (err error) {
func CompileSourcePackage(archiveFilename, outputFilename 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 err
return nil, err
}
// Ensure package type is 'source'
if bpmpkg.PkgInfo.Type != "source" {
return errors.New("cannot compile a non-source package")
return nil, errors.New("cannot compile a non-source package")
}
// Read compilation options file in current directory
compilationOptions, err := readCompilationOptionsFile()
if err != nil {
return err
return nil, err
}
// Get HOME directory
homeDir, err := os.UserHomeDir()
if err != nil {
return err
return nil, err
}
// Get UID and GID to use for compilation
@ -44,11 +47,11 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo
if os.Getuid() == 0 {
_uid, err := strconv.ParseInt(rootCompilationUID, 10, 32)
if err != nil {
return fmt.Errorf("could not convert UID '%s' to int", rootCompilationUID)
return nil, fmt.Errorf("could not convert UID '%s' to int", rootCompilationUID)
}
_gid, err := strconv.ParseInt(rootCompilationGID, 10, 32)
if err != nil {
return fmt.Errorf("could not convert GID '%s' to int", rootCompilationGID)
return nil, fmt.Errorf("could not convert GID '%s' to int", rootCompilationGID)
}
uid = int(_uid)
gid = int(_gid)
@ -63,26 +66,26 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo
if _, err := os.Stat(tempDirectory); err == nil {
err := os.RemoveAll(tempDirectory)
if err != nil {
return err
return nil, err
}
}
// Create temporary directory
err = os.MkdirAll(tempDirectory, 0755)
if err != nil {
return err
return nil, err
}
// Change temporary directory owner
err = os.Chown(tempDirectory, uid, gid)
if err != nil {
return err
return nil, err
}
// Extract source.sh file
err = extractTarballFile(archiveFilename, "source.sh", tempDirectory, uid, gid)
if err != nil {
return err
return nil, err
}
// Get package scripts and extract them
@ -90,26 +93,26 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo
for _, script := range packageScripts {
err = extractTarballFile(archiveFilename, script, tempDirectory, uid, gid)
if err != nil {
return err
return nil, err
}
}
// Extract source files
err = extractTarballDirectory(archiveFilename, "source-files", tempDirectory, uid, gid)
if err != nil {
return err
return nil, err
}
// Create source directory
err = os.Mkdir(path.Join(tempDirectory, "source"), 0755)
if err != nil {
return err
return nil, err
}
// Change source directory owner
err = os.Chown(path.Join(tempDirectory, "source"), uid, gid)
if err != nil {
return err
return nil, err
}
// Setup environment for commands
@ -145,7 +148,7 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
err = cmd.Run()
if err != nil {
return err
return nil, err
}
// Execute check function in source.sh script if not skipping checks
@ -164,129 +167,192 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
err = cmd.Run()
if err != nil {
return err
return nil, err
}
}
// Remove output directory if it already exists
if _, err := os.Stat(path.Join(tempDirectory, "output")); err == nil {
err := os.RemoveAll(path.Join(tempDirectory, "output"))
// Variable that will be used later
isSplitPkg := true
// Get all packages to compile
packagesToCompile := bpmpkg.PkgInfo.SplitPackages
if len(packagesToCompile) == 0 {
packagesToCompile = append(packagesToCompile, bpmpkg.PkgInfo)
isSplitPkg = false
}
// Compile each package
for _, pkg := range packagesToCompile {
// Get package function name
packageFunctionName := "package"
if isSplitPkg {
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 err
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
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
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
var pkgInfo PackageInfo
if !isSplitPkg {
pkgInfo = *bpmpkg.PkgInfo
} else {
pkgInfo = *pkg
// Ensure required fields are set
if strings.TrimSpace(pkgInfo.Name) == "" {
return nil, fmt.Errorf("split package name is empty")
}
// Copy data from main source package
if pkgInfo.Description == "" {
pkgInfo.Description = bpmpkg.PkgInfo.Description
}
pkgInfo.Version = bpmpkg.PkgInfo.Version
pkgInfo.Revision = bpmpkg.PkgInfo.Revision
pkgInfo.Url = bpmpkg.PkgInfo.Url
if pkgInfo.License == "" {
pkgInfo.License = bpmpkg.PkgInfo.License
}
}
// 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 source package specific fields
pkgInfo.MakeDepends = nil
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)
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 if split package
if len(bpmpkg.PkgInfo.SplitPackages) != 1 {
// Get current working directory
workdir, err := os.Getwd()
if err != nil {
return nil, fmt.Errorf("could not get working directory: %s", err)
}
outputFilename = path.Join(workdir, fmt.Sprintf("%s-%s-%d.bpm", pkgInfo.Name, pkgInfo.Version, pkgInfo.Revision))
}
// 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
}
// Create new output directory
err = os.Mkdir(path.Join(tempDirectory, "output"), 0755)
if err != nil {
return err
}
// Change output directory owner
err = os.Chown(path.Join(tempDirectory, "output"), uid, gid)
if err != nil {
return 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 package() function\"\n"+
"( cd \"$BPM_SOURCE\" && fakeroot -s \"$BPM_WORKDIR\"/fakeroot_file bash -e -c 'package' ) || 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
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
err = cmd.Run()
if err != nil {
return 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
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
err = cmd.Run()
if err != nil {
return fmt.Errorf("files.tar.gz archive could not be created: %s", err)
}
// Copy pkgInfo struct
pkgInfo := bpmpkg.PkgInfo
// Set package type to binary
pkgInfo.Type = "binary"
// Set package architecture
if val, ok := compilationOptions["ARCH"]; ok {
pkgInfo.Arch = val
} else {
pkgInfo.Arch = GetArch()
}
// Marshal package info
pkgInfoBytes, err := yaml.Marshal(pkgInfo)
if err != nil {
return err
}
pkgInfoBytes = append(pkgInfoBytes, '\n')
// Create pkg.info file
err = os.WriteFile(path.Join(tempDirectory, "pkg.info"), pkgInfoBytes, 0644)
if err != nil {
return err
}
// Change pkg.info file owner
err = os.Chown(path.Join(tempDirectory, "pkg.info"), uid, gid)
if err != nil {
return 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 err
}
cmd.Env = append(env, "CURRENT_DIR="+currentDir)
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
err = cmd.Run()
if err != nil {
return fmt.Errorf("BPM archive could not be created: %s", err)
}
// Move final BPM archive
err = os.Rename(path.Join(tempDirectory, "final-archive.bpm"), outputFilename)
if err != nil {
return err
}
// Set final BPM archive owner
err = os.Chown(outputFilename, os.Getuid(), os.Getgid())
if err != nil {
return err
}
return nil
return outputBpmPackages, nil
}
func readCompilationOptionsFile() (options map[string]string, err error) {

View File

@ -38,6 +38,11 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein
if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() {
continue
}
if bpmpkg.PkgInfo.Type == "source" && len(bpmpkg.PkgInfo.SplitPackages) != 0 {
return nil, fmt.Errorf("direct source package installation has not been implemented")
}
operation.AppendAction(&InstallPackageAction{
File: pkg,
IsDependency: false,
@ -63,6 +68,11 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein
if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(entry.Info.Name, rootDir) && GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() {
continue
}
if entry.Info.Type == "source" && len(entry.Info.SplitPackages) != 0 {
return nil, fmt.Errorf("direct source package installation has not been implemented")
}
operation.AppendAction(&FetchPackageAction{
IsDependency: false,
RepositoryEntry: entry,

View File

@ -486,13 +486,13 @@ func (operation *BPMOperation) Execute(verbose, force bool) error {
}
// Compile source package
err = CompileSourcePackage(value.File, outputFilename, false)
outputBpmPackages, err := CompileSourcePackage(value.File, outputFilename, false)
if err != nil {
return fmt.Errorf("could not compile source package (%s): %s\n", value.File, err)
}
// Set values
fileToInstall = outputFilename
fileToInstall = outputBpmPackages[bpmpkg.PkgInfo.Name]
bpmpkg, err = ReadPackage(outputFilename)
if err != nil {
return fmt.Errorf("could not read package (%s): %s\n", fileToInstall, err)

View File

@ -24,21 +24,22 @@ type BPMPackage struct {
}
type PackageInfo struct {
Name string `yaml:"name,omitempty"`
Description string `yaml:"description,omitempty"`
Version string `yaml:"version,omitempty"`
Revision int `yaml:"revision,omitempty"`
Url string `yaml:"url,omitempty"`
License string `yaml:"license,omitempty"`
Arch string `yaml:"architecture,omitempty"`
Type string `yaml:"type,omitempty"`
Keep []string `yaml:"keep,omitempty"`
Depends []string `yaml:"depends,omitempty"`
MakeDepends []string `yaml:"make_depends,omitempty"`
OptionalDepends []string `yaml:"optional_depends,omitempty"`
Conflicts []string `yaml:"conflicts,omitempty"`
Replaces []string `yaml:"replaces,omitempty"`
Provides []string `yaml:"provides,omitempty"`
Name string `yaml:"name,omitempty"`
Description string `yaml:"description,omitempty"`
Version string `yaml:"version,omitempty"`
Revision int `yaml:"revision,omitempty"`
Url string `yaml:"url,omitempty"`
License string `yaml:"license,omitempty"`
Arch string `yaml:"architecture,omitempty"`
Type string `yaml:"type,omitempty"`
Keep []string `yaml:"keep,omitempty"`
Depends []string `yaml:"depends,omitempty"`
MakeDepends []string `yaml:"make_depends,omitempty"`
OptionalDepends []string `yaml:"optional_depends,omitempty"`
Conflicts []string `yaml:"conflicts,omitempty"`
Replaces []string `yaml:"replaces,omitempty"`
Provides []string `yaml:"provides,omitempty"`
SplitPackages []*PackageInfo `yaml:"split_packages,omitempty"`
}
type PackageFileEntry struct {
@ -403,6 +404,7 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
Conflicts: make([]string, 0),
Replaces: make([]string, 0),
Provides: make([]string, 0),
SplitPackages: make([]*PackageInfo, 0),
}
err := yaml.Unmarshal([]byte(contents), &pkgInfo)
if err != nil {
@ -460,6 +462,13 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p
appendArray("Provided packages", pkgInfo.Provides)
appendArray("Replaces packages", pkgInfo.Replaces)
}
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)
}
ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir)))
return strings.Join(ret, "\n")
}