319 lines
10 KiB
Go
319 lines
10 KiB
Go
// install_deps.go - iOS development dependency checker
|
|
// This script checks for required iOS development tools.
|
|
// It's designed to be portable across different shells by using Go instead of shell scripts.
|
|
//
|
|
// Usage:
|
|
// go run install_deps.go # Interactive mode
|
|
// TASK_FORCE_YES=true go run install_deps.go # Auto-accept prompts
|
|
// CI=true go run install_deps.go # CI mode (auto-accept)
|
|
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
)
|
|
|
|
type Dependency struct {
|
|
Name string
|
|
CheckFunc func() (bool, string) // Returns (success, details)
|
|
Required bool
|
|
InstallCmd []string
|
|
InstallMsg string
|
|
SuccessMsg string
|
|
FailureMsg string
|
|
}
|
|
|
|
func main() {
|
|
fmt.Println("Checking iOS development dependencies...")
|
|
fmt.Println("=" + strings.Repeat("=", 50))
|
|
fmt.Println()
|
|
|
|
hasErrors := false
|
|
dependencies := []Dependency{
|
|
{
|
|
Name: "Xcode",
|
|
CheckFunc: func() (bool, string) {
|
|
// Check if xcodebuild exists
|
|
if !checkCommand([]string{"xcodebuild", "-version"}) {
|
|
return false, ""
|
|
}
|
|
// Get version info
|
|
out, err := exec.Command("xcodebuild", "-version").Output()
|
|
if err != nil {
|
|
return false, ""
|
|
}
|
|
lines := strings.Split(string(out), "\n")
|
|
if len(lines) > 0 {
|
|
return true, strings.TrimSpace(lines[0])
|
|
}
|
|
return true, ""
|
|
},
|
|
Required: true,
|
|
InstallMsg: "Please install Xcode from the Mac App Store:\n https://apps.apple.com/app/xcode/id497799835\n Xcode is REQUIRED for iOS development (includes iOS SDKs, simulators, and frameworks)",
|
|
SuccessMsg: "✅ Xcode found",
|
|
FailureMsg: "❌ Xcode not found (REQUIRED)",
|
|
},
|
|
{
|
|
Name: "Xcode Developer Path",
|
|
CheckFunc: func() (bool, string) {
|
|
// Check if xcode-select points to a valid Xcode path
|
|
out, err := exec.Command("xcode-select", "-p").Output()
|
|
if err != nil {
|
|
return false, "xcode-select not configured"
|
|
}
|
|
path := strings.TrimSpace(string(out))
|
|
|
|
// Check if path exists and is in Xcode.app
|
|
if _, err := os.Stat(path); err != nil {
|
|
return false, "Invalid Xcode path"
|
|
}
|
|
|
|
// Verify it's pointing to Xcode.app (not just Command Line Tools)
|
|
if !strings.Contains(path, "Xcode.app") {
|
|
return false, fmt.Sprintf("Points to %s (should be Xcode.app)", path)
|
|
}
|
|
|
|
return true, path
|
|
},
|
|
Required: true,
|
|
InstallCmd: []string{"sudo", "xcode-select", "-s", "/Applications/Xcode.app/Contents/Developer"},
|
|
InstallMsg: "Xcode developer path needs to be configured",
|
|
SuccessMsg: "✅ Xcode developer path configured",
|
|
FailureMsg: "❌ Xcode developer path not configured correctly",
|
|
},
|
|
{
|
|
Name: "iOS SDK",
|
|
CheckFunc: func() (bool, string) {
|
|
// Get the iOS Simulator SDK path
|
|
cmd := exec.Command("xcrun", "--sdk", "iphonesimulator", "--show-sdk-path")
|
|
output, err := cmd.Output()
|
|
if err != nil {
|
|
return false, "Cannot find iOS SDK"
|
|
}
|
|
sdkPath := strings.TrimSpace(string(output))
|
|
|
|
// Check if the SDK path exists
|
|
if _, err := os.Stat(sdkPath); err != nil {
|
|
return false, "iOS SDK path not found"
|
|
}
|
|
|
|
// Check for UIKit framework (essential for iOS development)
|
|
uikitPath := fmt.Sprintf("%s/System/Library/Frameworks/UIKit.framework", sdkPath)
|
|
if _, err := os.Stat(uikitPath); err != nil {
|
|
return false, "UIKit.framework not found"
|
|
}
|
|
|
|
// Get SDK version
|
|
versionCmd := exec.Command("xcrun", "--sdk", "iphonesimulator", "--show-sdk-version")
|
|
versionOut, _ := versionCmd.Output()
|
|
version := strings.TrimSpace(string(versionOut))
|
|
|
|
return true, fmt.Sprintf("iOS %s SDK", version)
|
|
},
|
|
Required: true,
|
|
InstallMsg: "iOS SDK comes with Xcode. Please ensure Xcode is properly installed.",
|
|
SuccessMsg: "✅ iOS SDK found with UIKit framework",
|
|
FailureMsg: "❌ iOS SDK not found or incomplete",
|
|
},
|
|
{
|
|
Name: "iOS Simulator Runtime",
|
|
CheckFunc: func() (bool, string) {
|
|
if !checkCommand([]string{"xcrun", "simctl", "help"}) {
|
|
return false, ""
|
|
}
|
|
// Check if we can list runtimes
|
|
out, err := exec.Command("xcrun", "simctl", "list", "runtimes").Output()
|
|
if err != nil {
|
|
return false, "Cannot access simulator"
|
|
}
|
|
// Count iOS runtimes
|
|
lines := strings.Split(string(out), "\n")
|
|
count := 0
|
|
var versions []string
|
|
for _, line := range lines {
|
|
if strings.Contains(line, "iOS") && !strings.Contains(line, "unavailable") {
|
|
count++
|
|
// Extract version number
|
|
if parts := strings.Fields(line); len(parts) > 2 {
|
|
for _, part := range parts {
|
|
if strings.HasPrefix(part, "(") && strings.HasSuffix(part, ")") {
|
|
versions = append(versions, strings.Trim(part, "()"))
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if count > 0 {
|
|
return true, fmt.Sprintf("%d runtime(s): %s", count, strings.Join(versions, ", "))
|
|
}
|
|
return false, "No iOS runtimes installed"
|
|
},
|
|
Required: true,
|
|
InstallMsg: "iOS Simulator runtimes come with Xcode. You may need to download them:\n Xcode → Settings → Platforms → iOS",
|
|
SuccessMsg: "✅ iOS Simulator runtime available",
|
|
FailureMsg: "❌ iOS Simulator runtime not available",
|
|
},
|
|
}
|
|
|
|
// Check each dependency
|
|
for _, dep := range dependencies {
|
|
success, details := dep.CheckFunc()
|
|
if success {
|
|
msg := dep.SuccessMsg
|
|
if details != "" {
|
|
msg = fmt.Sprintf("%s (%s)", dep.SuccessMsg, details)
|
|
}
|
|
fmt.Println(msg)
|
|
} else {
|
|
fmt.Println(dep.FailureMsg)
|
|
if details != "" {
|
|
fmt.Printf(" Details: %s\n", details)
|
|
}
|
|
if dep.Required {
|
|
hasErrors = true
|
|
if len(dep.InstallCmd) > 0 {
|
|
fmt.Println()
|
|
fmt.Println(" " + dep.InstallMsg)
|
|
fmt.Printf(" Fix command: %s\n", strings.Join(dep.InstallCmd, " "))
|
|
if promptUser("Do you want to run this command?") {
|
|
fmt.Println("Running command...")
|
|
cmd := exec.Command(dep.InstallCmd[0], dep.InstallCmd[1:]...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Stdin = os.Stdin
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Printf("Command failed: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println("✅ Command completed. Please run this check again.")
|
|
} else {
|
|
fmt.Printf(" Please run manually: %s\n", strings.Join(dep.InstallCmd, " "))
|
|
}
|
|
} else {
|
|
fmt.Println(" " + dep.InstallMsg)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for iPhone simulators
|
|
fmt.Println()
|
|
fmt.Println("Checking for iPhone simulator devices...")
|
|
if !checkCommand([]string{"xcrun", "simctl", "list", "devices"}) {
|
|
fmt.Println("❌ Cannot check for iPhone simulators")
|
|
hasErrors = true
|
|
} else {
|
|
out, err := exec.Command("xcrun", "simctl", "list", "devices").Output()
|
|
if err != nil {
|
|
fmt.Println("❌ Failed to list simulator devices")
|
|
hasErrors = true
|
|
} else if !strings.Contains(string(out), "iPhone") {
|
|
fmt.Println("⚠️ No iPhone simulator devices found")
|
|
fmt.Println()
|
|
|
|
// Get the latest iOS runtime
|
|
runtimeOut, err := exec.Command("xcrun", "simctl", "list", "runtimes").Output()
|
|
if err != nil {
|
|
fmt.Println(" Failed to get iOS runtimes:", err)
|
|
} else {
|
|
lines := strings.Split(string(runtimeOut), "\n")
|
|
var latestRuntime string
|
|
for _, line := range lines {
|
|
if strings.Contains(line, "iOS") && !strings.Contains(line, "unavailable") {
|
|
// Extract runtime identifier
|
|
parts := strings.Fields(line)
|
|
if len(parts) > 0 {
|
|
latestRuntime = parts[len(parts)-1]
|
|
}
|
|
}
|
|
}
|
|
|
|
if latestRuntime == "" {
|
|
fmt.Println(" No iOS runtime found. Please install iOS simulators in Xcode:")
|
|
fmt.Println(" Xcode → Settings → Platforms → iOS")
|
|
} else {
|
|
fmt.Println(" Would you like to create an iPhone 15 Pro simulator?")
|
|
createCmd := []string{"xcrun", "simctl", "create", "iPhone 15 Pro", "iPhone 15 Pro", latestRuntime}
|
|
fmt.Printf(" Command: %s\n", strings.Join(createCmd, " "))
|
|
if promptUser("Create simulator?") {
|
|
cmd := exec.Command(createCmd[0], createCmd[1:]...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Printf(" Failed to create simulator: %v\n", err)
|
|
} else {
|
|
fmt.Println(" ✅ iPhone 15 Pro simulator created")
|
|
}
|
|
} else {
|
|
fmt.Println(" Skipping simulator creation")
|
|
fmt.Printf(" Create manually: %s\n", strings.Join(createCmd, " "))
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Count iPhone devices
|
|
count := 0
|
|
lines := strings.Split(string(out), "\n")
|
|
for _, line := range lines {
|
|
if strings.Contains(line, "iPhone") && !strings.Contains(line, "unavailable") {
|
|
count++
|
|
}
|
|
}
|
|
fmt.Printf("✅ %d iPhone simulator device(s) available\n", count)
|
|
}
|
|
}
|
|
|
|
// Final summary
|
|
fmt.Println()
|
|
fmt.Println("=" + strings.Repeat("=", 50))
|
|
if hasErrors {
|
|
fmt.Println("❌ Some required dependencies are missing or misconfigured.")
|
|
fmt.Println()
|
|
fmt.Println("Quick setup guide:")
|
|
fmt.Println("1. Install Xcode from Mac App Store (if not installed)")
|
|
fmt.Println("2. Open Xcode once and agree to the license")
|
|
fmt.Println("3. Install additional components when prompted")
|
|
fmt.Println("4. Run: sudo xcode-select -s /Applications/Xcode.app/Contents/Developer")
|
|
fmt.Println("5. Download iOS simulators: Xcode → Settings → Platforms → iOS")
|
|
fmt.Println("6. Run this check again")
|
|
os.Exit(1)
|
|
} else {
|
|
fmt.Println("✅ All required dependencies are installed!")
|
|
fmt.Println(" You're ready for iOS development with Wails!")
|
|
}
|
|
}
|
|
|
|
func checkCommand(args []string) bool {
|
|
if len(args) == 0 {
|
|
return false
|
|
}
|
|
cmd := exec.Command(args[0], args[1:]...)
|
|
cmd.Stdout = nil
|
|
cmd.Stderr = nil
|
|
err := cmd.Run()
|
|
return err == nil
|
|
}
|
|
|
|
func promptUser(question string) bool {
|
|
// Check if we're in a non-interactive environment
|
|
if os.Getenv("CI") != "" || os.Getenv("TASK_FORCE_YES") == "true" {
|
|
fmt.Printf("%s [y/N]: y (auto-accepted)\n", question)
|
|
return true
|
|
}
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
fmt.Printf("%s [y/N]: ", question)
|
|
|
|
response, err := reader.ReadString('\n')
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
response = strings.ToLower(strings.TrimSpace(response))
|
|
return response == "y" || response == "yes"
|
|
} |