Builder/main.go
2024-12-29 08:54:45 +01:00

387 lines
13 KiB
Go

package main
import (
"flag"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"runtime" // for detecting system architecture and platform
"spitfire-builder/spitfire"
"time"
//"errors"
)
var (
// Define all flags as package-level variables
all bool
buildFlag bool
clean bool
fullClean bool
update bool
prePatch bool
postPatch bool
skipPatchUpdate bool
run bool
compress bool
buildPath string
target string
version string
component string
arch string
release string
platform string
upload bool
uploadPath string
sourceRepo = "https://hg.mozilla.org/mozilla-central"
patchesRepo = "https://weforge.xyz/Spitfire/Browser.git"
url = "https://spitfirebrowser.xyz/"
licence = "AGPL-3.0"
name = "Spitfire"
packageName = "spitfire-browser"
maintainer = "Internet Addict"
initialDir string
skipDeps bool
)
func init() {
flag.StringVar(&buildPath, "p", "", "Path to the build directory")
flag.StringVar(&target, "t", "", "Target location format: component-arch-release-platform")
flag.BoolVar(&compress, "c", false, "Compress the build directory into a tar.gz file before uploading")
flag.StringVar(&version, "v", "", "Specify version for the package. For nightly, use current date if not specified.")
flag.StringVar(&component, "component", "browser", "Component name (default: browser)")
flag.StringVar(&arch, "arch", runtime.GOARCH, "Architecture (default: system architecture)")
flag.StringVar(&release, "release", "nightly", "Release type (default: nightly)")
flag.StringVar(&platform, "platform", runtime.GOOS, "Platform (default: system platform)")
flag.BoolVar(&all, "a", false, "Perform all steps (build, clean, update)")
flag.BoolVar(&buildFlag, "b", false, "Build Spitfire")
flag.BoolVar(&clean, "clean", false, "Revert uncommitted changes without removing build artifacts")
flag.BoolVar(&fullClean, "full-clean", false, "Perform a full clean by removing all build artifacts (clobber)")
flag.BoolVar(&update, "u", false, "Update Mozilla repository")
flag.BoolVar(&prePatch, "pre-patch", false, "Apply pre-build patches")
flag.BoolVar(&postPatch, "post-patch", false, "Apply post-build patches")
flag.BoolVar(&skipPatchUpdate, "skip-patch-update", false, "Skip updating the patches repository")
flag.BoolVar(&run, "r", false, "Run the project after build")
flag.BoolVar(&upload, "upload", false, "Upload the compressed build file to SourceForge")
flag.StringVar(&uploadPath, "upload-path", "", "Path to the file to upload if no build present")
flag.BoolVar(&skipDeps, "skip-deps", false, "Skip checking for required system dependencies")
flag.Bool("h", false, "Display help message")
}
func printHelp() {
fmt.Println("Usage: go run . -p=<path-to-build> -t=<target> [-c|--compress] [-v|--version=<version>] [-component=<component>] [-arch=<architecture>] [-release=<release>] [-platform=<platform>]")
flag.PrintDefaults()
fmt.Println("Example: go run . --upload -c --upload-path=./mozilla-central/obj-x86_64-pc-linux-gnu/dist/bin -a")
os.Exit(0)
}
// checkDependencies verifies if required dependencies are installed
func checkDependencies() error {
dependencies := map[string]string{
"mercurial": "hg",
"python3": "python3",
"rsync": "rsync",
"git": "git",
}
// Log the PATH for debugging
fmt.Printf("Current PATH: %s\n", os.Getenv("PATH"))
for dep, cmd := range dependencies {
// Use LookPath to check availability
if _, err := exec.LookPath(cmd); err != nil {
return fmt.Errorf("dependency %s is not installed or not found in PATH. Please run: sudo apt install %s", dep, dep)
}
// For python3, run a specific version check to confirm
if dep == "python3" {
out, err := exec.Command(cmd, "--version").Output()
if err != nil {
return fmt.Errorf("dependency %s is installed but not functional. Ensure 'python3 --version' works correctly: %v", dep, err)
}
fmt.Printf("Detected %s: %s", dep, string(out))
}
}
return nil
}
func main() {
flag.Parse()
if flag.Lookup("h").Value.(flag.Getter).Get().(bool) {
printHelp()
}
// Only check dependencies if NOT skipping them
if !skipDeps {
if err := checkDependencies(); err != nil {
log.Fatalf("System check failed: %v", err)
}
}
// Set version to current date if it's empty and release is nightly
if version == "" && release == "nightly" {
version = time.Now().Format("2006.01.02") // Set version to current date if nightly
}
// Save the initial directory
var err2 error
initialDir, err2 = os.Getwd()
if err2 != nil {
log.Fatalf("Failed to get current working directory: %v", err2)
}
fmt.Printf("Initial working directory: %s\n", initialDir)
// Determine buildPath dynamically if not provided
if buildPath == "" {
sourcePath, err := spitfire.ResolvePath("./mozilla-central")
if err != nil {
log.Fatalf("Error resolving source path: %v", err)
}
resolvedBuildPath, err := spitfire.ResolveBuildDir(sourcePath)
if err != nil {
log.Fatalf("Failed to detect build directory dynamically: %v", err)
}
buildPath = resolvedBuildPath
fmt.Printf("Automatically detected buildPath: %s\n", buildPath)
} else {
// Convert buildPath to absolute path
buildPath, err2 = spitfire.ResolvePath(buildPath)
if err2 != nil {
log.Fatalf("Failed to convert buildPath to absolute path: %v", err2)
}
fmt.Printf("Resolved buildPath: %s\n", buildPath)
}
if uploadPath != "" {
uploadPath, err2 = spitfire.ResolvePath(uploadPath)
if err2 != nil {
log.Fatalf("Failed to convert uploadPath to absolute path: %v", err2)
}
fmt.Printf("Resolved uploadPath: %s\n", uploadPath)
}
if all || buildFlag || prePatch || postPatch || clean || update || run {
BuildProcess()
}
if compress || upload {
PackageAndUploadProcess()
}
spitfire.PrintErrors()
}
// Update the BuildProcess function to pass patchesRepo
func BuildProcess() {
sourcePath, err := spitfire.ResolvePath("./mozilla-central")
if err != nil {
log.Fatalf("Error resolving source path: %v", err)
}
if all {
spitfire.DownloadSource(sourcePath, sourceRepo)
spitfire.DiscardChanges(sourcePath)
spitfire.CleanBuild(sourcePath, fullClean)
spitfire.UpdateRepo(sourcePath)
if err := spitfire.ApplyPatches(sourcePath, patchesRepo, "pre-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil {
log.Fatalf("Error during patch application: %v", err)
}
spitfire.Configure(sourcePath)
spitfire.Build(sourcePath)
// Detect the build directory dynamically
buildDir, err := spitfire.ResolveBuildDir(sourcePath)
if err != nil {
log.Fatalf("Error resolving build directory: %v", err)
}
if err := spitfire.ApplyPatches(buildDir, patchesRepo, "post-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil {
log.Fatalf("Error during patch application: %v", err)
}
if run {
spitfire.RunProject(sourcePath)
}
fmt.Println("Spitfire build completed successfully.")
} else if buildFlag {
if prePatch {
if err := spitfire.ApplyPatches(sourcePath, patchesRepo, "pre-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil {
log.Fatalf("Error during patch application: %v", err)
}
}
spitfire.Configure(sourcePath)
spitfire.Build(sourcePath)
// Detect the build directory dynamically
buildDir, err := spitfire.ResolveBuildDir(sourcePath)
if err != nil {
log.Fatalf("Error resolving build directory: %v", err)
}
if postPatch {
if err := spitfire.ApplyPatches(buildDir, patchesRepo, "post-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil {
log.Fatalf("Error during patch application: %v", err)
}
}
if run {
spitfire.RunProject(sourcePath)
}
fmt.Println("Spitfire build completed successfully.")
} else if clean {
spitfire.CleanBuild(sourcePath, fullClean)
fmt.Println("Cleaned Firefox build.")
} else if update {
spitfire.DownloadSource(sourcePath, sourceRepo)
spitfire.UpdateRepo(sourcePath)
fmt.Println("Mozilla repository updated.")
} else if prePatch {
spitfire.DownloadSource(sourcePath, sourceRepo)
if err := spitfire.ApplyPatches(sourcePath, patchesRepo, "pre-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil {
log.Fatalf("Error during patch application: %v", err)
}
fmt.Println("Patches updated.")
} else if postPatch {
// Detect the build directory dynamically
buildDir, err := spitfire.ResolveBuildDir(sourcePath)
if err != nil {
log.Fatalf("Error resolving build directory: %v", err)
}
if _, err := os.Stat(buildDir); err == nil {
if err := spitfire.ApplyPatches(buildDir, patchesRepo, "post-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil {
log.Fatalf("Error during patch application: %v", err)
}
}
if run {
spitfire.RunProject(sourcePath)
}
} else if run {
spitfire.RunProject(sourcePath)
}
}
// PackageAndUploadProcess handles compressing, packaging, and uploading the build to SourceForge.
func PackageAndUploadProcess() {
// Restore working directory before performing SourceForge operations
restoreWorkingDirectory()
pathToUse := buildPath
if upload && uploadPath != "" {
pathToUse = uploadPath
}
if pathToUse == "" {
log.Fatalf("Error: no valid build or upload path provided.")
}
// Calculate & display uncompressed size
uncompressedSize, err := spitfire.GetDirectorySize(pathToUse)
if err != nil {
log.Fatalf("Failed to calculate uncompressed size: %v", err)
}
fmt.Printf("Uncompressed directory size: %s\n", spitfire.BytesToHumanReadable(uncompressedSize))
// Create the tar.gz name, e.g. "browser-amd64-nightly-linux.tar.gz"
outputCompressedFile := filepath.Join(".", fmt.Sprintf(
"%s-%s-%s-%s.tar.gz",
component, arch, release, platform,
))
// Compress if requested
if compress {
err := spitfire.CompressDirectory(pathToUse, outputCompressedFile)
if err != nil {
log.Fatalf("Failed to compress build directory: %v", err)
}
fmt.Printf("Build directory compressed to: %s\n", outputCompressedFile)
}
// Compressed size
compressedSize, err := spitfire.GetFileSize(outputCompressedFile)
if err != nil {
log.Fatalf("Failed to get compressed file size: %v", err)
}
fmt.Printf("Compressed file size: %s\n", spitfire.BytesToHumanReadable(compressedSize))
// Show compression ratio & efficiency
compressionRatio, efficiency := spitfire.CalculateCompressionEfficiency(uncompressedSize, compressedSize)
fmt.Printf("Compression ratio: %.2f:1\n", compressionRatio)
fmt.Printf("Compression efficiency: %.2f%%\n", efficiency)
// If not uploading, we're done
if !upload {
return
}
// Load SourceForge config
config, err := spitfire.LoadConfig()
if err != nil {
log.Fatalf("Failed to load SourceForge config: %v", err)
}
// Check tarball existence
if _, err := os.Stat(outputCompressedFile); err != nil {
log.Fatalf("No compressed file found to upload: %v", err)
}
// The subdirectory path in the SF project
// e.g. "browser/amd64/nightly/2024.12.23"
uploadDir := fmt.Sprintf("%s/%s/%s/%s", component, arch, release, version)
// 1) Upload the file to SourceForge once
err = spitfire.Upload(config, outputCompressedFile,
"/home/frs/project/spitfire-browser/"+uploadDir+"/")
if err != nil {
log.Fatalf("Failed to upload compressed file: %v", err)
}
fmt.Println("Compressed file uploaded successfully.")
// 2) Download existing APPINDEX or create new
err = spitfire.DownloadAPPINDEX(config, "/home/frs/project/spitfire-browser/")
if err != nil {
fmt.Println("Failed to download APPINDEX. A new one will be created and uploaded.")
}
// 3) Update the APPINDEX
err = spitfire.PackageAPPINDEX(
packageName, // e.g. "spitfire-browser"
release, // e.g. "nightly"
version, // e.g. "2024.12.23"
arch,
fmt.Sprintf("%d", compressedSize),
fmt.Sprintf("%d", uncompressedSize),
name, // e.g. "Spitfire"
url,
licence,
component,
maintainer,
"", // dependencies
platform,
uploadDir,
)
if err != nil {
log.Fatalf("Failed to update APPINDEX: %v", err)
}
fmt.Println("APPINDEX updated successfully.")
// 4) Clean
if err := spitfire.CleanAppIndex(); err != nil {
log.Fatalf("Failed to clean APPINDEX: %v", err)
}
// 5) Upload the updated APPINDEX
err = spitfire.UploadAPPINDEX(config, "/home/frs/project/spitfire-browser/")
if err != nil {
log.Fatalf("Failed to upload updated APPINDEX: %v", err)
}
fmt.Println("APPINDEX uploaded successfully.")
}
// restoreWorkingDirectory restores the initial working directory after any operation that might change it.
func restoreWorkingDirectory() {
err := os.Chdir(initialDir)
if err != nil {
log.Fatalf("Failed to restore the working directory: %v", err)
}
fmt.Printf("Restored working directory to: %s\n", initialDir)
}