Add basic compilation functionality
This commit is contained in:
parent
7b0a8bf1d6
commit
e8d5f0a565
@ -8,6 +8,7 @@ import (
|
||||
"git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
@ -62,6 +63,7 @@ const (
|
||||
remove
|
||||
cleanup
|
||||
file
|
||||
compile
|
||||
)
|
||||
|
||||
func getCommandType() commandType {
|
||||
@ -86,6 +88,8 @@ func getCommandType() commandType {
|
||||
return cleanup
|
||||
case "file":
|
||||
return file
|
||||
case "compile":
|
||||
return compile
|
||||
default:
|
||||
return help
|
||||
}
|
||||
@ -543,6 +547,51 @@ func resolveCommand() {
|
||||
}
|
||||
}
|
||||
}
|
||||
case compile:
|
||||
if len(subcommandArgs) == 0 {
|
||||
fmt.Println("No source packages were given")
|
||||
return
|
||||
}
|
||||
|
||||
// Read local databases
|
||||
err := bpmlib.ReadLocalDatabases()
|
||||
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 current working directory
|
||||
workdir, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Error: could not get working directory: %s", err)
|
||||
}
|
||||
|
||||
outputFilename := fmt.Sprintf(path.Join(workdir, "%s-%s-%d.bpm"), bpmpkg.PkgInfo.Name, bpmpkg.PkgInfo.Version, bpmpkg.PkgInfo.Revision)
|
||||
|
||||
err = bpmlib.CompileSourcePackage(sourcePackage, outputFilename)
|
||||
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)
|
||||
}
|
||||
|
||||
default:
|
||||
printHelp()
|
||||
}
|
||||
|
185
src/bpmlib/compilation.go
Normal file
185
src/bpmlib/compilation.go
Normal file
@ -0,0 +1,185 @@
|
||||
package bpmlib
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v3"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func CompileSourcePackage(archiveFilename, outputFilename string) (err error) {
|
||||
// Read BPM archive
|
||||
bpmpkg, err := ReadPackage(archiveFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure package type is 'source'
|
||||
if bpmpkg.PkgInfo.Type != "source" {
|
||||
return errors.New("cannot compile a non-source package")
|
||||
}
|
||||
|
||||
// Get HOME directory
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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 err
|
||||
}
|
||||
}
|
||||
|
||||
// Create temporary directory
|
||||
err = os.MkdirAll(tempDirectory, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Extract source.sh file
|
||||
content, err := readTarballContent(archiveFilename, "source.sh")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sourceFile, err := os.Create(path.Join(tempDirectory, "source.sh"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(sourceFile, content.tarReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = sourceFile.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = content.file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Extract source files
|
||||
err = extractTarballDirectory(archiveFilename, "source-files", tempDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create source directory
|
||||
err = os.Mkdir(path.Join(tempDirectory, "source"), 0755)
|
||||
if err != nil {
|
||||
return 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))
|
||||
env = append(env, "BPM_PKG_ARCH="+GetArch())
|
||||
|
||||
// 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\" && cd \"$BPM_SOURCE\" && set -e && prepare)\n"+ // Run prepare() function if it exists
|
||||
"[[ $(type -t build) == \"function\" ]] && (echo \"Running build() function\" && cd \"$BPM_SOURCE\" && set -e && build)\n"+ // Run build() function if it exists
|
||||
"[[ $(type -t check) == \"function\" ]] && (echo \"Running check() function\" && cd \"$BPM_SOURCE\" && set -e && check)\n"+ // Run check() function if it exists
|
||||
"exit 0")
|
||||
cmd.Dir = tempDirectory
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Env = env
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return 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"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Create new 'output' directory
|
||||
err = os.Mkdir(path.Join(tempDirectory, "output"), 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Run bash command
|
||||
cmd = exec.Command("bash", "-e", "-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\" && cd \"$BPM_SOURCE\" && fakeroot -s \"$BPM_WORKDIR\"/fakeroot_file package)\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
|
||||
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
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("files.tar.gz archive could not be created: %s", err)
|
||||
}
|
||||
|
||||
// Copy pkgInfo struct and set package type to binary
|
||||
pkgInfo := bpmpkg.PkgInfo
|
||||
pkgInfo.Type = "binary"
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Create final BPM archive
|
||||
cmd = exec.Command("bash", "-c", "tar -cf "+outputFilename+" --owner=0 --group=0 -C \"$BPM_WORKDIR\" pkg.info pkg.files ${PACKAGE_SCRIPTS[@]} files.tar.gz")
|
||||
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)
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("BPM archive could not be created: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -5,6 +5,8 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type tarballFileReader struct {
|
||||
@ -41,3 +43,65 @@ func readTarballContent(tarballPath, fileToExtract string) (*tarballFileReader,
|
||||
|
||||
return nil, errors.New("could not file in tarball")
|
||||
}
|
||||
|
||||
func extractTarballDirectory(tarballPath, directoryToExtract, workingDirectory string) (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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// Copy data to file
|
||||
_, err = io.Copy(file, tr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Close file
|
||||
file.Close()
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user