305 lines
9.3 KiB
Go
305 lines
9.3 KiB
Go
package spitfire
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"time"
|
|
)
|
|
|
|
// Array to store errors
|
|
var errors []string
|
|
|
|
// SetGlobalEnv sets the MOZILLABUILD environment variable globally for the user (or system)
|
|
func SetGlobalEnv(variable, value string, scope string) error {
|
|
if runtime.GOOS == "windows" {
|
|
var cmd *exec.Cmd
|
|
if scope == "user" {
|
|
cmd = exec.Command("setx", variable, value) // Set for current user
|
|
} else if scope == "system" {
|
|
cmd = exec.Command("setx", variable, value, "/M") // Set for system (requires admin privileges)
|
|
} else {
|
|
return fmt.Errorf("unknown scope: %s", scope)
|
|
}
|
|
|
|
// Run the command
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set environment variable %s=%s: %v\nOutput: %s", variable, value, err, string(out))
|
|
}
|
|
fmt.Printf("Successfully set %s=%s\n", variable, value)
|
|
return nil
|
|
} else {
|
|
return fmt.Errorf("global environment variable setting is not supported on non-Windows systems")
|
|
}
|
|
}
|
|
|
|
// Run an external command like scp or rsync
|
|
func runCommand(command string, args ...string) error {
|
|
fmt.Printf("Running command: %s %v\n", command, args)
|
|
cmd := exec.Command(command, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
return cmd.Run()
|
|
}
|
|
|
|
// Function to resolve paths using absolute path
|
|
func ResolvePath(path string) (string, error) {
|
|
// Convert Unix-style slashes to the platform's native slashes
|
|
path = filepath.FromSlash(path)
|
|
|
|
// Get the absolute path
|
|
absPath, err := filepath.Abs(path)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to resolve path: %s, error: %v", path, err)
|
|
}
|
|
return absPath, nil
|
|
}
|
|
|
|
// Function to download Mozilla source if not present
|
|
func DownloadSource(sourcePath string, sourceRepo string) {
|
|
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.")
|
|
}
|
|
} else {
|
|
fmt.Println("Mozilla source already exists.")
|
|
}
|
|
}
|
|
|
|
// Function to discard uncommitted changes
|
|
func DiscardChanges(sourcePath string) {
|
|
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.")
|
|
}
|
|
}
|
|
|
|
// Function to clean build
|
|
func CleanBuild(sourcePath string, fullClean bool) {
|
|
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 fullClean {
|
|
// Perform full clean or clobber if necessary
|
|
var machCmd string
|
|
if runtime.GOOS == "windows" {
|
|
machCmd = ".\\mach"
|
|
} else {
|
|
machCmd = "./mach"
|
|
}
|
|
|
|
if err := runCommand(machCmd, "clobber"); err != nil {
|
|
errors = append(errors, "Failed to clean build.")
|
|
} else {
|
|
fmt.Println("Build artifacts cleaned successfully.")
|
|
}
|
|
} else {
|
|
fmt.Println("Skipping full clean. Build artifacts remain intact.")
|
|
}
|
|
}
|
|
|
|
// Function to update Mozilla repository
|
|
func UpdateRepo(sourcePath string) {
|
|
fmt.Println("Updating Mozilla repository...")
|
|
if err := os.Chdir(sourcePath); err != nil {
|
|
errors = append(errors, "Failed to navigate to source directory.")
|
|
return
|
|
}
|
|
if err := runCommand("hg", "pull", "-u"); err != nil {
|
|
errors = append(errors, "Failed to update Mozilla repository.")
|
|
}
|
|
}
|
|
|
|
// Function to update patches
|
|
func UpdatePatches(patchesDir, patchesRepo, sourcePath string) {
|
|
fmt.Println("Updating patches...")
|
|
if _, err := os.Stat(patchesDir); err == nil {
|
|
fmt.Println("Patches directory already exists. Cleaning and pulling updates...")
|
|
if err := os.Chdir(patchesDir); err != nil {
|
|
errors = append(errors, "Failed to navigate to patches directory.")
|
|
return
|
|
}
|
|
if err := runCommand("git", "clean", "-xdf"); err != nil {
|
|
errors = append(errors, "Failed to clean patches directory.")
|
|
}
|
|
_ = runCommand("git", "stash", "push", "--include-untracked")
|
|
if err := runCommand("git", "fetch"); err != nil {
|
|
errors = append(errors, "Failed to fetch updates from patches repository.")
|
|
}
|
|
if runCommand("git", "show-ref", "--verify", "--quiet", "refs/heads/main") == nil {
|
|
if err := runCommand("git", "rebase", "origin/main"); err != nil {
|
|
errors = append(errors, "Failed to rebase updates from main branch.")
|
|
}
|
|
} else if runCommand("git", "show-ref", "--verify", "--quiet", "refs/heads/master") == nil {
|
|
if err := runCommand("git", "rebase", "origin/master"); err != nil {
|
|
errors = append(errors, "Failed to rebase updates from master branch.")
|
|
}
|
|
} else {
|
|
errors = append(errors, "No valid branch (main or master) found in patches repository.")
|
|
return
|
|
}
|
|
if runCommand("git", "stash", "list") == nil {
|
|
_ = runCommand("git", "stash", "pop")
|
|
} else {
|
|
fmt.Println("No stash entries found, skipping pop.")
|
|
}
|
|
} else {
|
|
fmt.Println("Patches directory does not exist. Cloning repository...")
|
|
if err := runCommand("git", "clone", patchesRepo, patchesDir); err != nil {
|
|
errors = append(errors, "Failed to clone patches repository.")
|
|
}
|
|
}
|
|
|
|
// Handle platform-specific rsync command
|
|
fmt.Println("Copying files from patches directory to Firefox source directory...")
|
|
if runtime.GOOS == "windows" || !isMsys2() {
|
|
// Use robocopy for Windows
|
|
if err := runCommand("robocopy", patchesDir, sourcePath, "*", "/S", "/XF", ".git", "/XD", ".git"); err != nil {
|
|
errors = append(errors, "Failed to copy files (Windows robocopy).")
|
|
}
|
|
} else {
|
|
// Use rsync for Unix-like systems
|
|
if err := runCommand("rsync", "-av", "--exclude=.git", patchesDir+"/", sourcePath+"/"); err != nil {
|
|
errors = append(errors, "Failed to copy files (rsync).")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Function to configure Spitfire
|
|
func Configure(sourcePath string) {
|
|
fmt.Println("Configuring Spitfire...")
|
|
if err := os.Chdir(sourcePath); err != nil {
|
|
errors = append(errors, "Failed to navigate to source directory.")
|
|
return
|
|
}
|
|
|
|
// Use the appropriate mach command for Windows or Unix-like systems
|
|
var machCmd string
|
|
if runtime.GOOS == "windows" {
|
|
machCmd = ".\\mach"
|
|
} else {
|
|
machCmd = "./mach"
|
|
}
|
|
|
|
if err := runCommand(machCmd, "configure"); err != nil {
|
|
errors = append(errors, "Configuration failed.")
|
|
}
|
|
}
|
|
|
|
// Function to build Spitfire
|
|
func Build(sourcePath string) {
|
|
fmt.Println("Building Spitfire...")
|
|
if err := os.Chdir(sourcePath); err != nil {
|
|
errors = append(errors, "Failed to navigate to source directory.")
|
|
return
|
|
}
|
|
|
|
// Use the appropriate mach command for Windows or Unix-like systems
|
|
var machCmd string
|
|
if runtime.GOOS == "windows" {
|
|
machCmd = ".\\mach"
|
|
} else {
|
|
machCmd = "./mach"
|
|
}
|
|
|
|
if err := runCommand(machCmd, "build"); err != nil {
|
|
errors = append(errors, "Build failed.")
|
|
}
|
|
}
|
|
|
|
// Function to run the project after build
|
|
func RunProject(sourcePath string) {
|
|
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
|
|
}
|
|
|
|
// List of possible binaries
|
|
binaries := []string{"firefox", "Firefox", "spitfire", "Spitfire"}
|
|
|
|
var binaryPath string
|
|
|
|
// Check for the existence of binaries
|
|
for _, binary := range binaries {
|
|
binaryPath = filepath.Join(buildDir, binary)
|
|
if _, err := os.Stat(binaryPath); err == nil {
|
|
fmt.Printf("Found binary: %s\n", binaryPath)
|
|
break
|
|
}
|
|
binaryPath = ""
|
|
}
|
|
|
|
if binaryPath == "" {
|
|
fmt.Println("No suitable binary found to run the project.")
|
|
return
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// Run the binary with the new profile
|
|
cmd := exec.Command(binaryPath, "-no-remote", "-profile", profilePath)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
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.")
|
|
}
|
|
|
|
// 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)
|
|
} else {
|
|
fmt.Println("Profile directory deleted successfully.")
|
|
}
|
|
}
|
|
|
|
// ResolveBuildDir detects the build directory dynamically
|
|
func ResolveBuildDir(sourcePath string) (string, error) {
|
|
// The expected build directory pattern
|
|
globPattern := filepath.Join(sourcePath, "obj-*")
|
|
|
|
// Find matching directories
|
|
matches, err := filepath.Glob(globPattern)
|
|
if err != nil {
|
|
return "", fmt.Errorf("error resolving build directory: %v", err)
|
|
}
|
|
if len(matches) == 0 {
|
|
return "", fmt.Errorf("build directory not found under %s", sourcePath)
|
|
}
|
|
full := filepath.Join(matches[0], "dist", "bin")
|
|
|
|
// Return the first match (assumes one build directory exists)
|
|
return full, nil
|
|
}
|
|
|
|
// Function to print collected errors
|
|
func PrintErrors() {
|
|
if len(errors) > 0 {
|
|
fmt.Println("The following errors occurred during execution:")
|
|
for _, err := range errors {
|
|
fmt.Printf("- %s\n", err)
|
|
}
|
|
}
|
|
}
|