diff --git a/README.md b/README.md index 03a9925..fd1556b 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,8 @@ Below is a detailed description of all the flags supported by the Spitfire Build Runs the built project after the build process completes successfully. - **`--skip-deps`**: Skip checking for required system dependencies. +- **`ignoreErrors`**: + Processes all steps even if errors occur. ### **Compression and Upload Flags** diff --git a/main.go b/main.go index 2a96ed2..f2601cc 100644 --- a/main.go +++ b/main.go @@ -6,14 +6,13 @@ import ( "log" "os" "path/filepath" - "runtime" // for detecting system architecture and platform + "runtime" "spitfire-builder/spitfire" "time" //"errors" ) var ( - // Define all flags as package-level variables all bool buildFlag bool clean bool @@ -41,8 +40,11 @@ var ( maintainer = "Internet Addict" initialDir string skipDeps bool + ignoreErrors bool ) +var errors []error // This is also in build.go. Great! This code is a mess. + func init() { 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") @@ -63,6 +65,7 @@ func init() { 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.BoolVar(&ignoreErrors, "ignore-errors", false, "Processes all steps even if errors occur") flag.Bool("h", false, "Display help message") } @@ -73,6 +76,15 @@ func printHelp() { os.Exit(0) } +func handleError(err error) { + if err != nil { + errors = append(errors, err) + if !ignoreErrors { + log.Fatal(err) + } + } +} + func main() { flag.Parse() @@ -82,7 +94,7 @@ func main() { if !skipDeps { if err := spitfire.CheckDependencies(); err != nil { - log.Fatalf("System check failed: %v", err) + handleError(fmt.Errorf("system check failed: %w", err)) } } @@ -94,138 +106,155 @@ func main() { var err error initialDir, err = os.Getwd() if err != nil { - log.Fatalf("Failed to get current working directory: %v", err) + handleError(fmt.Errorf("failed to get current directory: %w", err)) } - var buildDir string // Store the resolved build directory here + var buildDir string - // Perform build if necessary if all || buildFlag || prePatch || postPatch || clean || update || run { - buildDir = BuildProcess() - if buildDir == "" { - log.Fatalf("Build process completed, but no build directory was found.") + buildDir, err = BuildProcess() + if err != nil { + handleError(fmt.Errorf("build process failed: %w", err)) + } else if buildDir == "" && (buildFlag || all || run) { + handleError(fmt.Errorf("build directory not found after build process")) } - fmt.Printf("Build directory from process: %s\n", buildDir) } - // Resolve build directory for compression/upload if compress || upload { - if buildDir == "" { // Resolve dynamically if no build was performed - fmt.Println("No build directory detected during build process. Resolving dynamically...") - sourcePath, err := spitfire.ResolvePath("./mozilla-release") - if err != nil { - log.Fatalf("Error resolving source path: %v", err) - } - fmt.Printf("Resolved source path: %s\n", sourcePath) - - buildDir, err = spitfire.ResolveBuildDir(sourcePath) - if err != nil { - log.Fatalf("Error resolving build directory dynamically: %v", err) - } + if err := PackageAndUploadProcess(buildDir); err != nil { + handleError(fmt.Errorf("package/upload failed: %w", err)) } - - if buildDir == "" { - log.Fatalf("No build directory found for compression or upload.") - } - fmt.Printf("Resolved build directory for compress/upload: %s\n", buildDir) - - PackageAndUploadProcess(buildDir) } spitfire.PrintErrors() + + if len(errors) > 0 { + fmt.Fprintf(os.Stderr, "Exiting with %d error(s):\n", len(errors)) + for _, e := range errors { + fmt.Fprintf(os.Stderr, "- %v\n", e) + } + os.Exit(1) + } else { + os.Exit(0) + } } -// Update the BuildProcess function to pass patchesRepo -func BuildProcess() string { +func BuildProcess() (string, error) { sourcePath, err := spitfire.ResolvePath("./mozilla-release") if err != nil { - log.Fatalf("Error resolving source path: %v", err) + return "", fmt.Errorf("resolve source path: %w", err) } var buildDir string 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) + if err := spitfire.DownloadSource(sourcePath, sourceRepo); err != nil { + return "", fmt.Errorf("download source: %w", err) + } + if err := spitfire.DiscardChanges(sourcePath); err != nil { + return "", fmt.Errorf("discard changes: %w", err) + } + if err := spitfire.CleanBuild(sourcePath, fullClean); err != nil { + return "", fmt.Errorf("clean build: %w", err) + } + if err := spitfire.UpdateRepo(sourcePath); err != nil { + return "", fmt.Errorf("update repo: %w", err) + } + if err := spitfire.ApplyPatches(sourcePath, patchesRepo, "pre-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { + return "", fmt.Errorf("pre-patch: %w", err) + } + if err := spitfire.Configure(sourcePath); err != nil { + return "", fmt.Errorf("configure: %w", err) + } + if err := spitfire.Build(sourcePath); err != nil { + return "", fmt.Errorf("build: %w", 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) + return "", fmt.Errorf("resolve build dir: %w", 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) + return "", fmt.Errorf("post-patch: %w", err) } if run { - spitfire.RunProject(sourcePath) + if err := spitfire.RunProject(sourcePath); err != nil { + return "", fmt.Errorf("run project: %w", err) + } } 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) + return "", fmt.Errorf("pre-patch: %w", err) } } - spitfire.Configure(sourcePath) - spitfire.Build(sourcePath) - // Detect the build directory dynamically + if err := spitfire.Configure(sourcePath); err != nil { + return "", fmt.Errorf("configure: %w", err) + } + if err := spitfire.Build(sourcePath); err != nil { + return "", fmt.Errorf("build: %w", err) + } buildDir, err = spitfire.ResolveBuildDir(sourcePath) if err != nil { - log.Fatalf("Error resolving build directory: %v", err) + return "", fmt.Errorf("resolve build dir: %w", 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) + return "", fmt.Errorf("post-patch: %w", err) } } if run { - spitfire.RunProject(sourcePath) + if err := spitfire.RunProject(sourcePath); err != nil { + return "", fmt.Errorf("run project: %w", err) + } } fmt.Println("Spitfire build completed successfully.") } else if clean { - spitfire.CleanBuild(sourcePath, fullClean) + if err := spitfire.CleanBuild(sourcePath, fullClean); err != nil { + return "", fmt.Errorf("clean build: %w", err) + } fmt.Println("Cleaned Firefox build.") } else if update { - spitfire.DownloadSource(sourcePath, sourceRepo) - spitfire.UpdateRepo(sourcePath) + if err := spitfire.DownloadSource(sourcePath, sourceRepo); err != nil { + return "", fmt.Errorf("download source: %w", err) + } + if err := spitfire.UpdateRepo(sourcePath); err != nil { + return "", fmt.Errorf("update repo: %w", err) + } fmt.Println("Mozilla repository updated.") } else if prePatch { - spitfire.DownloadSource(sourcePath, sourceRepo) + if err := spitfire.DownloadSource(sourcePath, sourceRepo); err != nil { + return "", fmt.Errorf("download source: %w", err) + } if err := spitfire.ApplyPatches(sourcePath, patchesRepo, "pre-compile-patches", filepath.Join(sourcePath, "patcher"), skipPatchUpdate); err != nil { - log.Fatalf("Error during patch application: %v", err) + return "", fmt.Errorf("pre-patch: %w", 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) + return "", fmt.Errorf("resolve build dir: %w", 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) + return "", fmt.Errorf("post-patch: %w", err) } } if run { - spitfire.RunProject(sourcePath) + if err := spitfire.RunProject(sourcePath); err != nil { + return "", fmt.Errorf("run project: %w", err) + } } } else if run { - spitfire.RunProject(sourcePath) + if err := spitfire.RunProject(sourcePath); err != nil { + return "", fmt.Errorf("run project: %w", err) + } } - return buildDir + return buildDir, nil } -// PackageAndUploadProcess handles compressing, packaging, and uploading the build to SourceForge. -func PackageAndUploadProcess(buildDir string) { - - // Restore working directory before performing SourceForge operations +func PackageAndUploadProcess(buildDir string) error { restoreWorkingDirectory() pathToUse := buildDir @@ -234,120 +263,95 @@ func PackageAndUploadProcess(buildDir string) { } if pathToUse == "" { - log.Fatalf("Error: no valid build or upload path provided.") + return fmt.Errorf("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) + return fmt.Errorf("get directory size: %w", 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) + if err := spitfire.CompressDirectory(pathToUse, outputCompressedFile); err != nil { + return fmt.Errorf("compress directory: %w", 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) + return fmt.Errorf("get compressed file size: %w", 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) - - // Display compressed directory path fmt.Printf("Compressed dir: %s\n", pathToUse) - // If not uploading, we're done if !upload { - return + return nil } - // Load SourceForge config config, err := spitfire.LoadConfig() if err != nil { - log.Fatalf("Failed to load SourceForge config: %v", err) + return fmt.Errorf("load config: %w", err) } - // Check tarball existence if _, err := os.Stat(outputCompressedFile); err != nil { - log.Fatalf("No compressed file found to upload: %v", err) + return fmt.Errorf("compressed file not found: %w", 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) + if err := spitfire.Upload(config, outputCompressedFile, "/home/frs/project/spitfire-browser/"+uploadDir+"/"); err != nil { + return fmt.Errorf("upload compressed file: %w", 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 { + if err := spitfire.DownloadAPPINDEX(config, "/home/frs/project/spitfire-browser/"); 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" + if err := spitfire.PackageAPPINDEX( + packageName, + release, + version, arch, fmt.Sprintf("%d", compressedSize), fmt.Sprintf("%d", uncompressedSize), - name, // e.g. "Spitfire" + name, url, licence, component, maintainer, - "", // dependencies + "", platform, uploadDir, - ) - if err != nil { - log.Fatalf("Failed to update APPINDEX: %v", err) + ); err != nil { + return fmt.Errorf("package APPINDEX: %w", err) } fmt.Println("APPINDEX updated successfully.") - // 4) Clean if err := spitfire.CleanAppIndex(); err != nil { - log.Fatalf("Failed to clean APPINDEX: %v", err) + return fmt.Errorf("clean APPINDEX: %w", 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) + if err := spitfire.UploadAPPINDEX(config, "/home/frs/project/spitfire-browser/"); err != nil { + return fmt.Errorf("upload APPINDEX: %w", err) } fmt.Println("APPINDEX uploaded successfully.") + + return nil } -// 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) + if err := os.Chdir(initialDir); err != nil { + log.Printf("Failed to restore working directory: %v", err) } - fmt.Printf("Restored working directory to: %s\n", initialDir) } diff --git a/spitfire/build.go b/spitfire/build.go index aef2512..069dfa4 100644 --- a/spitfire/build.go +++ b/spitfire/build.go @@ -59,63 +59,59 @@ func ResolvePath(path string) (string, error) { } // Function to download Mozilla source if not present -func DownloadSource(sourcePath string, sourceRepo string) { +func DownloadSource(sourcePath, sourceRepo string) error { if _, err := os.Stat(sourcePath); os.IsNotExist(err) { fmt.Println("Mozilla source not found. Cloning from repository...") if err := runCommand("hg", "clone", sourceRepo, sourcePath); err != nil { - errors = append(errors, "Failed to clone Mozilla repository.") + return fmt.Errorf("failed to clone Mozilla repository: %w", err) } } else { fmt.Println("Mozilla source already exists.") } + return nil } // Function to discard uncommitted changes -func DiscardChanges(sourcePath string) { +func DiscardChanges(sourcePath string) error { fmt.Println("Discarding uncommitted changes...") if err := runCommand("hg", "revert", "--all", "--no-backup", "-R", sourcePath); err != nil { - errors = append(errors, "Failed to revert changes in Mozilla repository.") + return fmt.Errorf("failed to revert changes: %w", err) } + return nil } // Function to clean build -func CleanBuild(sourcePath string, fullClean bool) { +func CleanBuild(sourcePath string, fullClean bool) error { fmt.Println("Cleaning build...") - - // Revert uncommitted changes - if err := runCommand("hg", "revert", "--all", "--no-backup"); err != nil { - errors = append(errors, "Failed to revert changes in Mozilla repository.") + if err := runCommand("hg", "revert", "--all", "--no-backup", "-R", sourcePath); err != nil { + return fmt.Errorf("failed to revert changes: %w", err) } if fullClean { - // Perform full clean or clobber if necessary - var machCmd string + machCmd := filepath.Join(sourcePath, "mach") if runtime.GOOS == "windows" { - machCmd = ".\\mach" - } else { - machCmd = "./mach" + machCmd = filepath.Join(sourcePath, "mach.bat") } - - if err := runCommand(machCmd, "clobber"); err != nil { - errors = append(errors, "Failed to clean build.") - } else { - fmt.Println("Build artifacts cleaned successfully.") + cmd := exec.Command(machCmd, "clobber") + cmd.Dir = sourcePath // Run in the source directory + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to clean build: %w", err) } - } else { - fmt.Println("Skipping full clean. Build artifacts remain intact.") + fmt.Println("Build artifacts cleaned successfully.") } + return nil } // Function to update Mozilla repository -func UpdateRepo(sourcePath string) { +func UpdateRepo(sourcePath string) error { fmt.Println("Updating Mozilla repository...") if err := os.Chdir(sourcePath); err != nil { - errors = append(errors, "Failed to navigate to source directory.") - return + return fmt.Errorf("failed to navigate to source directory: %w", err) } if err := runCommand("hg", "pull", "-u"); err != nil { - errors = append(errors, "Failed to update Mozilla repository.") + return fmt.Errorf("failed to update repository: %w", err) } + return nil } // Function to update patches @@ -174,56 +170,41 @@ func UpdatePatches(patchesDir, patchesRepo, sourcePath string) { } // Function to configure Spitfire -func Configure(sourcePath string) { +func Configure(sourcePath string) error { fmt.Println("Configuring Spitfire...") if err := os.Chdir(sourcePath); err != nil { - errors = append(errors, "Failed to navigate to source directory.") - return + return fmt.Errorf("failed to navigate to source directory: %w", err) } - // Use the appropriate mach command for Windows or Unix-like systems - var machCmd string + machCmd := "./mach" if runtime.GOOS == "windows" { machCmd = ".\\mach" - } else { - machCmd = "./mach" - } - - if err := runCommand(machCmd, "configure"); err != nil { - errors = append(errors, "Configuration failed.") } + return runCommand(machCmd, "configure") } // Function to build Spitfire -func Build(sourcePath string) { +func Build(sourcePath string) error { fmt.Println("Building Spitfire...") if err := os.Chdir(sourcePath); err != nil { - errors = append(errors, "Failed to navigate to source directory.") - return + return fmt.Errorf("failed to navigate to source directory: %w", err) } - // Use the appropriate mach command for Windows or Unix-like systems - var machCmd string + machCmd := "./mach" if runtime.GOOS == "windows" { machCmd = ".\\mach" - } else { - machCmd = "./mach" - } - - if err := runCommand(machCmd, "build"); err != nil { - errors = append(errors, "Build failed.") } + return runCommand(machCmd, "build") } // Function to run the project after build -func RunProject(sourcePath string) { +func RunProject(sourcePath string) error { fmt.Println("Running the project...") // Resolve the build directory buildDir, err := ResolveBuildDir(sourcePath) if err != nil { - fmt.Printf("Error resolving build directory: %v\n", err) - return + return fmt.Errorf("error resolving build directory: %w", err) } // List of possible binaries @@ -242,15 +223,13 @@ func RunProject(sourcePath string) { } if binaryPath == "" { - fmt.Println("No suitable binary found to run the project.") - return + return fmt.Errorf("no suitable binary found to run the project") } // Create a unique profile directory for each run profilePath := filepath.Join(buildDir, fmt.Sprintf("profile_%d", time.Now().UnixNano())) if err := os.Mkdir(profilePath, 0755); err != nil { - fmt.Printf("Failed to create profile directory: %v\n", err) - return + return fmt.Errorf("failed to create profile directory: %w", err) } // Run the binary with the new profile @@ -260,19 +239,22 @@ func RunProject(sourcePath string) { cmd.Stdin = os.Stdin fmt.Printf("Running binary: %s with new profile at %s\n", binaryPath, profilePath) - if err := cmd.Run(); err != nil { - fmt.Printf("Failed to run the project: %v\n", err) - } else { - fmt.Println("Binary execution finished successfully.") - } + runErr := cmd.Run() // Delete the profile directory after execution fmt.Printf("Deleting profile directory: %s\n", profilePath) - if err := os.RemoveAll(profilePath); err != nil { - fmt.Printf("Failed to delete profile directory: %v\n", err) + if removeErr := os.RemoveAll(profilePath); removeErr != nil { + fmt.Printf("Warning: failed to delete profile directory: %v\n", removeErr) } else { fmt.Println("Profile directory deleted successfully.") } + + if runErr != nil { + return fmt.Errorf("failed to run the project: %w", runErr) + } + + fmt.Println("Binary execution finished successfully.") + return nil } // ResolveBuildDir detects the build directory dynamically