diff --git a/.gitignore b/.gitignore index 712d01c..1b36d8f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ /packages_temp.json /APKINDEX /APPINDEX -/browser-amd64-nightly-linux.tar.gz \ No newline at end of file +/browser-amd64-nightly-linux.tar.gz +/browser-amd64-nightly-windows.tar.gz \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a92ad4d --- /dev/null +++ b/README.md @@ -0,0 +1,145 @@ +

+ Logo +

+ +

+ Spitfire Builder +

+ +

+This is a "simple" script for building the Spitfire Browser based on Mozilla Firefox source code. +

+ +## Dependencies + +- Mercurial (hg) +- Git +- Golang (tested with v1.21) +- Python 3.11 and pip3 + +# Example usage: + +## Build: + +```sh +go run . -a +``` + +## Upload: + +```sh +go run . --upload -c --upload-path=./mozilla-central/obj-x86_64-pc-linux-gnu/dist/bin +``` + +## Build and upload: + +```sh +go run . --upload -c --upload-path=./mozilla-central/obj-x86_64-pc-linux-gnu/dist/bin -a +``` + +## Display all flags: + +```sh +go run . -h +``` + +### Config file for uploading example: + +*sourceforge_config.json* + +```json +{ + "SFKeyPath": "~/.ssh/id_rsa.pub", + "SFUser": "internet-addict", + "SFHost": "web.sourceforge.net", + "SFProject": "spitfire-browser" +} +``` + +## APPINDEX example: + +``` +C:905cd0cc2dea9e400e1ecd099462b6b19188a9f1 +P:Spitfire +R:nightly +V:2024.09.08 +A:amd64 +S:788506622 +I:3324483350 +T:Spitfire build +U:https://spitfirebrowser.com/ +L:AGPL-3.0 +o:browser +m:Internet Addict +t:1725830641 +c:905cd0cc2dea9e400e1ecd099462b6b19188a9f1 +D: +p:linux +q: +Z:905cd0cc2dea9e400e1ecd099462b6b19188a9f1 +``` + +## Repositary structure + +``` +spitfire-browser/ +├── browser/ +│ ├── amd64/ +│ │ ├── stable/ +│ │ │ ├── latest/deb.tar.gz +│ │ │ ├── x.x.x/deb.tar.gz +│ │ ├── nightly/ +│ │ ├── latest/deb.tar.gz +│ │ ├── yyyy-mm-dd/deb.tar.gz +│ ├── arm/ +│ │ ├── stable/ +│ │ │ ├── latest/ +│ │ │ ├── x.x.x/ +│ │ ├── nightly/ +│ │ ├── latest/ +│ │ ├── yyyy-mm-dd/ +├── cli-package-manager/ +│ ├── stable/ +│ │ ├── latest/ +│ │ ├── x.x.x/ +│ ├── nightly/ +│ ├── latest/ +│ ├── yyyy-mm-dd/ +├── gui-installer/ +│ ├── stable/ +│ │ ├── latest/ +│ │ ├── x.x.x/ +│ ├── nightly/ +│ ├── latest/ +│ ├── yyyy-mm-dd/ +├── gui-package-manager/ +│ ├── stable/ +│ │ ├── latest/ +│ │ ├── x.x.x/ +│ ├── nightly/ +│ ├── latest/ +│ ├── yyyy-mm-dd/ +├── addons/ +│ ├── themes/ +│ │ ├── stable/ +│ │ │ ├── latest/ +│ │ │ ├── x.x.x/ +│ │ ├── nightly/ +│ │ ├── latest/ +│ │ ├── yyyy-mm-dd/ +│ ├── custom-configs/ +│ │ ├── stable/ +│ │ │ ├── latest/ +│ │ │ ├── x.x.x/ +│ │ ├── nightly/ +│ │ ├── latest/ +│ │ ├── yyyy-mm-dd/ +│ ├── search-engines/ +│ ├── stable/ +│ │ ├── latest/ +│ │ ├── x.x.x/ +│ ├── nightly/ +│ ├── latest/ +│ ├── yyyy-mm-dd/ +├── APPINDEX +``` \ No newline at end of file diff --git a/main.go b/main.go index b829dbd..5eefa46 100644 --- a/main.go +++ b/main.go @@ -1,231 +1,254 @@ package main import ( - "flag" - "fmt" - "log" - "os" - "path/filepath" - "runtime" // for detecting system architecture and platform - "time" - "spitfire/spitfire" - //"errors" + "flag" + "fmt" + "log" + "os" + "path/filepath" + "runtime" // for detecting system architecture and platform + "spitfire/spitfire" + "time" + //"errors" ) var ( - // Define all flags as package-level variables - all bool - buildFlag bool - clean bool - update bool - patches 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://weforgecode.xyz/Spitfire/Browser.git" - url = "https://spitfirebrowser.com/" - licence = "AGPL-3.0" - name = "Spitfire" - maintainer = "Internet Addict" - initialDir string + // Define all flags as package-level variables + all bool + buildFlag bool + clean bool + update bool + patches 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://weforgecode.xyz/Spitfire/Browser.git" + url = "https://spitfirebrowser.com/" + licence = "AGPL-3.0" + name = "Spitfire" + maintainer = "Internet Addict" + initialDir string ) 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, "Clean build") - flag.BoolVar(&update, "u", false, "Update Mozilla repository") - flag.BoolVar(&patches, "patches", false, "Update patches") - 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.Bool("h", false, "Display help message") + 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, "Clean build") + flag.BoolVar(&update, "u", false, "Update Mozilla repository") + flag.BoolVar(&patches, "patches", false, "Update patches") + 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.Bool("h", false, "Display help message") } func printHelp() { - fmt.Println("Usage: ./main -p= -t= [-c|--compress] [-v|--version=] [-component=] [-arch=] [-release=] [-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) + fmt.Println("Usage: go run . -p= -t= [-c|--compress] [-v|--version=] [-component=] [-arch=] [-release=] [-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) } func main() { - flag.Parse() + // // Check system dependencies + // err := spitfire.CheckSystemDependencies() + // if err != nil { + // log.Fatalf("System check failed: %v", err) + // } - if flag.Lookup("h").Value.(flag.Getter).Get().(bool) { - printHelp() - } + flag.Parse() - // 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 - } + if flag.Lookup("h").Value.(flag.Getter).Get().(bool) { + printHelp() + } - // Save the initial directory - var err error - initialDir, err = os.Getwd() - if err != nil { - log.Fatalf("Failed to get current working directory: %v", err) - } - fmt.Printf("Initial working directory: %s\n", initialDir) + // 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 + } - if all || buildFlag { - BuildProcess() - } + // 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) - if compress || upload { - PackageAndUploadProcess() - } + // Convert buildPath and uploadPath to absolute paths + if buildPath != "" { + 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) + } - spitfire.PrintErrors() + 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 { + BuildProcess() + } + + if compress || upload { + PackageAndUploadProcess() + } + + spitfire.PrintErrors() } // BuildProcess handles the build process: downloading, cleaning, configuring, and building the project. func BuildProcess() { - sourcePath, err := spitfire.ResolvePath("./mozilla-central") - if err != nil { - log.Fatalf("Error resolving source path: %v", err) - } + sourcePath, err := spitfire.ResolvePath("./mozilla-central") + if err != nil { + log.Fatalf("Error resolving source path: %v", err) + } - patchesDir, err := spitfire.ResolvePath(filepath.Join(sourcePath, "Spitfire")) - if err != nil { - log.Fatalf("Error resolving patches directory: %v", err) - } + patchesDir, err := spitfire.ResolvePath(filepath.Join(sourcePath, "Spitfire")) + if err != nil { + log.Fatalf("Error resolving patches directory: %v", err) + } - if all { - spitfire.DownloadSource(sourcePath, sourceRepo) - spitfire.DiscardChanges(sourcePath) - spitfire.CleanBuild(sourcePath) - spitfire.UpdateRepo(sourcePath) - spitfire.UpdatePatches(patchesDir, patchesRepo, sourcePath) - spitfire.Configure(sourcePath) - spitfire.Build(sourcePath) - if run { - spitfire.RunProject(sourcePath) - } - fmt.Println("Spitfire build completed successfully.") - } else if clean { - spitfire.CleanBuild(sourcePath) - fmt.Println("Cleaned Firefox build.") - } else if update { - spitfire.DownloadSource(sourcePath, sourceRepo) - spitfire.UpdateRepo(sourcePath) - fmt.Println("Mozilla repository updated.") - } else if patches { - spitfire.DownloadSource(sourcePath, sourceRepo) - spitfire.UpdatePatches(patchesDir, patchesRepo, sourcePath) - fmt.Println("Patches updated.") - } else if buildFlag { - spitfire.Configure(sourcePath) - spitfire.Build(sourcePath) - if run { - spitfire.RunProject(sourcePath) - } - fmt.Println("Spitfire build completed successfully.") - } + if all { + spitfire.DownloadSource(sourcePath, sourceRepo) + spitfire.DiscardChanges(sourcePath) + spitfire.CleanBuild(sourcePath) + spitfire.UpdateRepo(sourcePath) + spitfire.UpdatePatches(patchesDir, patchesRepo, sourcePath) + spitfire.Configure(sourcePath) + spitfire.Build(sourcePath) + if run { + spitfire.RunProject(sourcePath) + } + fmt.Println("Spitfire build completed successfully.") + } else if clean { + spitfire.CleanBuild(sourcePath) + fmt.Println("Cleaned Firefox build.") + } else if update { + spitfire.DownloadSource(sourcePath, sourceRepo) + spitfire.UpdateRepo(sourcePath) + fmt.Println("Mozilla repository updated.") + } else if patches { + spitfire.DownloadSource(sourcePath, sourceRepo) + spitfire.UpdatePatches(patchesDir, patchesRepo, sourcePath) + fmt.Println("Patches updated.") + } else if buildFlag { + spitfire.Configure(sourcePath) + spitfire.Build(sourcePath) + if run { + spitfire.RunProject(sourcePath) + } + fmt.Println("Spitfire build completed successfully.") + } } // 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 - } + // Restore working directory before performing SourceForge operations + restoreWorkingDirectory() - if pathToUse == "" { - log.Fatalf("Error: no valid build or upload path provided.") - } + pathToUse := buildPath + if upload && uploadPath != "" { + pathToUse = uploadPath + } - // // This is stupid, it wait for the path to exist (up to a maximum wait time) - // err := waitForPath(pathToUse, 60, 5) // Max 60 seconds, checking every 5 seconds - // if err != nil { - // log.Fatalf("Error: Build path or upload path not found: %v", err) - // } + if pathToUse == "" { + log.Fatalf("Error: no valid build or upload path provided.") + } - uncompressedSize, err := spitfire.GetDirectorySize(pathToUse) - if err != nil { - log.Fatalf("Failed to calculate uncompressed size: %v", err) - } - fmt.Printf("Uncompressed directory size: %d bytes\n", uncompressedSize) + // // This is stupid, it wait for the path to exist (up to a maximum wait time) + // err := waitForPath(pathToUse, 60, 5) // Max 60 seconds, checking every 5 seconds + // if err != nil { + // log.Fatalf("Error: Build path or upload path not found: %v", err) + // } - outputCompressedFile := filepath.Join(".", fmt.Sprintf("%s-%s-%s-%s.tar.gz", component, arch, release, platform)) - 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) - } + uncompressedSize, err := spitfire.GetDirectorySize(pathToUse) + if err != nil { + log.Fatalf("Failed to calculate uncompressed size: %v", err) + } + fmt.Printf("Uncompressed directory size: %d bytes\n", uncompressedSize) - compressedSize, err := spitfire.GetFileSize(outputCompressedFile) - if err != nil { - log.Fatalf("Failed to get compressed file size: %v", err) - } - fmt.Printf("Compressed file size: %d bytes\n", compressedSize) + outputCompressedFile := filepath.Join(".", fmt.Sprintf("%s-%s-%s-%s.tar.gz", component, arch, release, platform)) + 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) + } - if upload { - config, err := spitfire.LoadConfig() - if err != nil { - log.Fatalf("Failed to load SourceForge config: %v", err) - } + compressedSize, err := spitfire.GetFileSize(outputCompressedFile) + if err != nil { + log.Fatalf("Failed to get compressed file size: %v", err) + } + fmt.Printf("Compressed file size: %d bytes\n", compressedSize) - if _, err := os.Stat(outputCompressedFile); err == nil { - err = spitfire.Upload(config, outputCompressedFile, "/home/frs/project/spitfire-browser/"+component+"/"+arch+"/"+release+"/"+version+"/") - if err != nil { - log.Fatalf("Failed to upload compressed file: %v", err) - } - fmt.Println("Compressed file uploaded successfully.") - } else { - log.Fatalf("No compressed file found to upload.") - } + if upload { + config, err := spitfire.LoadConfig() + if err != nil { + log.Fatalf("Failed to load SourceForge config: %v", err) + } - err = spitfire.DownloadAPPINDEX(config, "/home/frs/project/spitfire-browser/") - if err != nil { - fmt.Println("Failed to download APPINDEX. A new APPINDEX will be created and uploaded.") - } + if _, err := os.Stat(outputCompressedFile); err == nil { + err = spitfire.Upload(config, outputCompressedFile, "/home/frs/project/spitfire-browser/"+component+"/"+arch+"/"+release+"/"+version+"/") + if err != nil { + log.Fatalf("Failed to upload compressed file: %v", err) + } + fmt.Println("Compressed file uploaded successfully.") + } else { + log.Fatalf("No compressed file found to upload.") + } - err = spitfire.PackageAPPINDEX( - name, release, version, arch, - fmt.Sprintf("%d", compressedSize), - fmt.Sprintf("%d", uncompressedSize), - "Spitfire build", url, licence, component, maintainer, "", platform, - ) - if err != nil { - log.Fatalf("Failed to update APPINDEX: %v", err) - } - fmt.Println("APPINDEX updated successfully.") + err = spitfire.DownloadAPPINDEX(config, "/home/frs/project/spitfire-browser/") + if err != nil { + fmt.Println("Failed to download APPINDEX. A new APPINDEX will be created and uploaded.") + } - if err := spitfire.CleanAppIndex(); err != nil { - log.Fatalf("Failed to clean APPINDEX: %v", err) - } + err = spitfire.PackageAPPINDEX( + name, release, version, arch, + fmt.Sprintf("%d", compressedSize), + fmt.Sprintf("%d", uncompressedSize), + "Spitfire build", url, licence, component, maintainer, "", platform, + ) + if err != nil { + log.Fatalf("Failed to update APPINDEX: %v", err) + } + fmt.Println("APPINDEX updated successfully.") - 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.") - } + if err := spitfire.CleanAppIndex(); err != nil { + log.Fatalf("Failed to clean APPINDEX: %v", err) + } + + 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.") + } } // // waitForPath checks if a path exists, waiting for up to maxWait seconds and retrying every interval seconds. @@ -252,9 +275,9 @@ func PackageAndUploadProcess() { // 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) -} \ No newline at end of file + 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) +} diff --git a/sourceforge_config.json b/sourceforge_config.json index c98a8cf..c1250c3 100644 --- a/sourceforge_config.json +++ b/sourceforge_config.json @@ -1,7 +1,6 @@ { - "SFKeyPath": "~/.ssh/id_rsa.pub", - "SFUser": "internet-addict", - "SFHost": "web.sourceforge.net", - "SFProject": "spitfire-browser" - } - \ No newline at end of file + "SFKeyPath": "~/.ssh/id_rsa.pub", + "SFUser": "internet-addict", + "SFHost": "web.sourceforge.net", + "SFProject": "spitfire-browser" +} \ No newline at end of file diff --git a/spitfire/build.go b/spitfire/build.go index a35433a..3f5be5d 100644 --- a/spitfire/build.go +++ b/spitfire/build.go @@ -5,13 +5,39 @@ import ( "os" "os/exec" "path/filepath" + "runtime" ) // 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 @@ -20,9 +46,13 @@ func runCommand(command string, args ...string) error { // 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", path) + return "", fmt.Errorf("failed to resolve path: %s, error: %v", path, err) } return absPath, nil } @@ -54,10 +84,21 @@ func CleanBuild(sourcePath string) { 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" + } + + // 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("./mach", "clobber"); err != nil { + // Clean the build + if err := runCommand(machCmd, "clobber"); err != nil { errors = append(errors, "Failed to clean build.") } } @@ -113,9 +154,19 @@ func UpdatePatches(patchesDir, patchesRepo, sourcePath string) { 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 err := runCommand("rsync", "-av", "--exclude=.git", patchesDir+"/", sourcePath+"/"); err != nil { - errors = append(errors, "Failed to copy files.") + if runtime.GOOS == "windows" { + // 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).") + } } } @@ -126,7 +177,16 @@ func Configure(sourcePath string) { errors = append(errors, "Failed to navigate to source directory.") return } - if err := runCommand("./mach", "configure"); err != nil { + + // 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.") } } @@ -138,7 +198,16 @@ func Build(sourcePath string) { errors = append(errors, "Failed to navigate to source directory.") return } - if err := runCommand("./mach", "build"); err != nil { + + // 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.") } } @@ -146,11 +215,16 @@ func Build(sourcePath string) { // Function to run the project after build func RunProject(sourcePath string) { fmt.Println("Running the project...") - 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("./mach", "run"); err != nil { + + if err := runCommand(machCmd, "run"); err != nil { errors = append(errors, "Failed to run the project.") } } diff --git a/spitfire/checks.go b/spitfire/checks.go new file mode 100644 index 0000000..c0bb232 --- /dev/null +++ b/spitfire/checks.go @@ -0,0 +1,78 @@ +package spitfire + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" +) + +// CheckSystemDependencies ensures that required tools for building are installed. +func CheckSystemDependencies() error { + requiredTools := map[string]string{ + "git": "https://git-scm.com/download/win", // Git + "python": "https://www.python.org/downloads/", // Python + "pip3": "https://pip.pypa.io/en/stable/installing/", // Pip3 + } + + if runtime.GOOS == "windows" { + // Check for MozillaBuild installation + mozBuildPath := os.Getenv("MOZILLABUILD") + if mozBuildPath == "" { + mozBuildPath = "C:\\mozilla-build" // Default to standard MozillaBuild path + } + + // Check if MozillaBuild exists at the specified location + if !dirExists(mozBuildPath) { + requiredTools["mozbuild"] = "https://ftp.mozilla.org/pub/mozilla/libraries/win32/MozillaBuildSetup-Latest.exe" + } + } + + missingTools := []string{} + + // Check for each required tool + for tool, downloadLink := range requiredTools { + if !isCommandAvailable(tool) { + missingTools = append(missingTools, fmt.Sprintf("%s (Download: %s)", tool, downloadLink)) + } + } + + // Special check for mach in the local source directory (mozilla-central) + machPath := filepath.Join("mozilla-central", "mach") + if !fileExists(machPath) { + missingTools = append(missingTools, fmt.Sprintf("mach (run from mozilla-central directory)")) + } + + if len(missingTools) > 0 { + fmt.Println("The following tools are missing and are required for the build:") + for _, tool := range missingTools { + fmt.Println(" - " + tool) + } + return fmt.Errorf("missing required tools") + } + + fmt.Println("All required system dependencies are installed.") + return nil +} + +// isCommandAvailable checks if a command/tool is available on the system. +func isCommandAvailable(command string) bool { + _, err := exec.LookPath(command) + return err == nil +} + +// fileExists checks if a file exists at the given path. +func fileExists(path string) bool { + _, err := os.Stat(path) + return err == nil +} + +// dirExists checks if a directory exists at the given path. +func dirExists(path string) bool { + info, err := os.Stat(path) + if os.IsNotExist(err) { + return false + } + return info.IsDir() +}