2024-09-13 12:47:30 +02:00
|
|
|
package spitfire
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
2025-01-11 23:11:11 +01:00
|
|
|
"strings"
|
2025-04-13 19:21:04 +02:00
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
reportedCommands = make(map[string]bool)
|
|
|
|
reportMutex sync.Mutex
|
2024-09-13 12:47:30 +02:00
|
|
|
)
|
|
|
|
|
2025-01-11 23:11:11 +01:00
|
|
|
// CheckDependencies ensures that all required tools and dependencies for the build are installed.
|
|
|
|
func CheckDependencies() error {
|
2025-04-13 19:21:04 +02:00
|
|
|
// Just to make sure, reset reported commands at start
|
|
|
|
reportMutex.Lock()
|
|
|
|
reportedCommands = make(map[string]bool)
|
|
|
|
reportMutex.Unlock()
|
|
|
|
|
2025-01-11 23:11:11 +01:00
|
|
|
// Common dependencies
|
|
|
|
dependencies := map[string]string{
|
2025-01-18 20:41:00 +01:00
|
|
|
"git": "https://git-scm.com/downloads", // Git
|
|
|
|
"python": "https://www.python.org/downloads/", // Python
|
|
|
|
"pip3": "https://pip.pypa.io/en/stable/installing/", // Pip3
|
|
|
|
"hg": "https://www.mercurial-scm.org/", // Mercurial (hg)
|
2025-04-13 19:21:04 +02:00
|
|
|
"rustc": "https://www.rust-lang.org/tools/install", // Rust compiler
|
|
|
|
"cargo": "https://www.rust-lang.org/tools/install", // Cargo package manager
|
2024-09-13 12:47:30 +02:00
|
|
|
}
|
|
|
|
|
2025-01-11 23:11:11 +01:00
|
|
|
// Add platform-specific dependencies
|
2025-01-18 20:41:00 +01:00
|
|
|
if runtime.GOOS == "windows" {
|
2024-09-13 12:47:30 +02:00
|
|
|
mozBuildPath := os.Getenv("MOZILLABUILD")
|
|
|
|
if mozBuildPath == "" {
|
2025-01-18 20:41:00 +01:00
|
|
|
mozBuildPath = "C:\\mozilla-build" // Default path for MozillaBuild on Windows
|
2024-09-13 12:47:30 +02:00
|
|
|
}
|
|
|
|
if !dirExists(mozBuildPath) {
|
2025-01-11 23:11:11 +01:00
|
|
|
dependencies["mozbuild"] = "https://ftp.mozilla.org/pub/mozilla/libraries/win32/MozillaBuildSetup-Latest.exe"
|
2024-09-13 12:47:30 +02:00
|
|
|
}
|
2025-01-18 20:41:00 +01:00
|
|
|
} else {
|
2025-01-11 23:11:11 +01:00
|
|
|
dependencies["rsync"] = "https://rsync.samba.org/download.html" // rsync for non-Windows platforms
|
2024-09-13 12:47:30 +02:00
|
|
|
}
|
|
|
|
|
2025-01-11 23:11:11 +01:00
|
|
|
// Check for missing tools
|
2024-09-13 12:47:30 +02:00
|
|
|
missingTools := []string{}
|
2025-01-11 23:11:11 +01:00
|
|
|
for tool, downloadLink := range dependencies {
|
2024-09-13 12:47:30 +02:00
|
|
|
if !isCommandAvailable(tool) {
|
|
|
|
missingTools = append(missingTools, fmt.Sprintf("%s (Download: %s)", tool, downloadLink))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-04-13 19:21:04 +02:00
|
|
|
// Check Rust version if installed
|
|
|
|
if isCommandAvailable("rustc") {
|
|
|
|
_, err := getCommandOutput("rustc", "--version")
|
|
|
|
if err != nil {
|
|
|
|
missingTools = append(missingTools, "rustc (version check failed)")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-18 20:41:00 +01:00
|
|
|
// Check for `mach` script in the source directory
|
2025-01-18 11:48:30 +01:00
|
|
|
machPath := filepath.Join("mozilla-release", "mach")
|
2025-04-13 19:21:04 +02:00
|
|
|
repoExists, err := CheckRepoExists("mozilla-release")
|
|
|
|
if err != nil {
|
|
|
|
// Repository exists but is corrupted
|
|
|
|
fmt.Println("⚠️ Existing repository appears corrupted")
|
|
|
|
}
|
|
|
|
|
2024-09-13 12:47:30 +02:00
|
|
|
if !fileExists(machPath) {
|
2025-04-13 19:21:04 +02:00
|
|
|
if len(missingTools) == 0 {
|
|
|
|
// Only mach is missing and other dependencies are satisfied - try to clone
|
|
|
|
fmt.Println("⚠️ mach script missing - attempting to clone repository...")
|
|
|
|
if err := CloneRepo("mozilla-release", "https://hg.mozilla.org/releases/mozilla-release/"); err != nil {
|
|
|
|
missingTools = append(missingTools, "mach (failed to automatically clone repository)")
|
|
|
|
} else {
|
|
|
|
// Verify mach exists after clone
|
|
|
|
if !fileExists(machPath) {
|
|
|
|
missingTools = append(missingTools, "mach (repository cloned but mach still missing)")
|
|
|
|
} else {
|
|
|
|
fmt.Println("✅ Successfully cloned repository and found mach script")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Other tools are also missing - just report mach as missing
|
|
|
|
if !repoExists {
|
|
|
|
missingTools = append(missingTools, "mach (repository not found)")
|
|
|
|
} else {
|
|
|
|
missingTools = append(missingTools, "mach (found repository but mach is missing)")
|
|
|
|
}
|
|
|
|
}
|
2024-09-13 12:47:30 +02:00
|
|
|
}
|
|
|
|
|
2025-01-11 23:11:11 +01:00
|
|
|
// Report missing tools if any
|
2024-09-13 12:47:30 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2025-04-13 19:21:04 +02:00
|
|
|
// CheckRepoExists verifies if the Mozilla repository exists and is valid
|
|
|
|
func CheckRepoExists(sourcePath string) (bool, error) {
|
|
|
|
hgDir := filepath.Join(sourcePath, ".hg")
|
|
|
|
if _, err := os.Stat(hgDir); os.IsNotExist(err) {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2024-09-13 12:47:30 +02:00
|
|
|
// isCommandAvailable checks if a command/tool is available on the system.
|
|
|
|
func isCommandAvailable(command string) bool {
|
2025-01-18 20:41:00 +01:00
|
|
|
path, err := exec.LookPath(command)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
2025-04-13 19:21:04 +02:00
|
|
|
|
|
|
|
reportMutex.Lock()
|
|
|
|
defer reportMutex.Unlock()
|
|
|
|
|
|
|
|
if !reportedCommands[command] {
|
|
|
|
fmt.Printf("Command '%s' found at path '%s'\n", command, path)
|
|
|
|
reportedCommands[command] = true
|
|
|
|
}
|
2025-01-18 20:41:00 +01:00
|
|
|
return true
|
2024-09-13 12:47:30 +02:00
|
|
|
}
|
|
|
|
|
2025-04-13 19:21:04 +02:00
|
|
|
// Helper function to get command output
|
|
|
|
func getCommandOutput(name string, arg ...string) (string, error) {
|
|
|
|
cmd := exec.Command(name, arg...)
|
|
|
|
output, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(output), nil
|
|
|
|
}
|
|
|
|
|
2024-09-13 12:47:30 +02:00
|
|
|
// 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()
|
|
|
|
}
|
2025-01-11 23:11:11 +01:00
|
|
|
|
|
|
|
// isMsys2 detects if the environment is MSYS2 by checking the MSYSTEM environment variable.
|
|
|
|
func isMsys2() bool {
|
|
|
|
return strings.Contains(strings.ToLower(os.Getenv("MSYSTEM")), "msys")
|
|
|
|
}
|