use anyhow::{Result, Context, bail}; use std::path::{Path, PathBuf}; use std::process::Command; use colored::*; use crate::sdk::SdkConfig; use crate::sdk::proxy::ProxyConfig; use crate::project::ProjectConfig; /// Setup development environment - either system-wide or for a specific project pub fn setup_environment(project_path: Option<&str>, proxy: &ProxyConfig) -> Result<()> { match project_path { Some(path) => setup_project(path, proxy), None => setup_system(proxy), } } /// Setup system-wide development environment with default SDKs fn setup_system(proxy: &ProxyConfig) -> Result<()> { println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan()); println!("{}", " System Setup - Preparing FTC Development Environment".bright_cyan().bold()); println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan()); println!(); let mut issues = Vec::new(); let mut installed = Vec::new(); // Check and install SDKs let sdk_config = SdkConfig::new()?; // 1. Check Java println!("{}", "Checking Java JDK...".bright_yellow()); match check_java() { Ok(version) => { println!("{} Java JDK {} found", "✓".green(), version); installed.push(format!("Java JDK {}", version)); } Err(e) => { println!("{} {}", "✗".red(), e); issues.push(("Java JDK", get_java_install_instructions())); } } println!(); // 2. Check/Install FTC SDK println!("{}", "Checking FTC SDK...".bright_yellow()); if sdk_config.ftc_sdk_path.exists() { match crate::sdk::ftc::verify(&sdk_config.ftc_sdk_path) { Ok(_) => { let version = crate::sdk::ftc::get_version(&sdk_config.ftc_sdk_path) .unwrap_or_else(|_| "unknown".to_string()); println!("{} FTC SDK {} found at: {}", "✓".green(), version, sdk_config.ftc_sdk_path.display() ); installed.push(format!("FTC SDK {}", version)); } Err(_) => { println!("{} FTC SDK found but incomplete, reinstalling...", "⚠".yellow()); crate::sdk::ftc::install(&sdk_config.ftc_sdk_path, &sdk_config.android_sdk_path, proxy)?; let version = crate::sdk::ftc::get_version(&sdk_config.ftc_sdk_path) .unwrap_or_else(|_| "unknown".to_string()); installed.push(format!("FTC SDK {} (installed)", version)); } } } else { println!("FTC SDK not found. Installing..."); crate::sdk::ftc::install(&sdk_config.ftc_sdk_path, &sdk_config.android_sdk_path, proxy)?; let version = crate::sdk::ftc::get_version(&sdk_config.ftc_sdk_path) .unwrap_or_else(|_| "unknown".to_string()); installed.push(format!("FTC SDK {} (installed)", version)); } println!(); // 3. Check/Install Android SDK println!("{}", "Checking Android SDK...".bright_yellow()); if sdk_config.android_sdk_path.exists() { match crate::sdk::android::verify(&sdk_config.android_sdk_path) { Ok(_) => { println!("{} Android SDK found at: {}", "✓".green(), sdk_config.android_sdk_path.display() ); installed.push("Android SDK".to_string()); } Err(_) => { println!("{} Android SDK found but incomplete, reinstalling...", "⚠".yellow()); crate::sdk::android::install(&sdk_config.android_sdk_path, proxy)?; installed.push("Android SDK (installed)".to_string()); } } } else { println!("Android SDK not found. Installing..."); crate::sdk::android::install(&sdk_config.android_sdk_path, proxy)?; installed.push("Android SDK (installed)".to_string()); } println!(); // 4. Check ADB println!("{}", "Checking ADB (Android Debug Bridge)...".bright_yellow()); match check_adb(&sdk_config.android_sdk_path) { Ok(version) => { println!("{} ADB {} found", "✓".green(), version); installed.push(format!("ADB {}", version)); } Err(e) => { println!("{} {}", "⚠".yellow(), e); println!(" ADB is included in Android SDK platform-tools"); println!(" Add to PATH: {}", sdk_config.android_sdk_path.join("platform-tools").display()); } } println!(); // 5. Check Gradle println!("{}", "Checking Gradle...".bright_yellow()); match check_gradle() { Ok(version) => { println!("{} Gradle {} found", "✓".green(), version); installed.push(format!("Gradle {}", version)); } Err(e) => { println!("{} {}", "⚠".yellow(), e); println!(" Note: Weevil projects include Gradle wrapper, so this is optional"); } } println!(); // Print summary print_system_summary(&installed, &issues, &sdk_config); Ok(()) } /// Setup dependencies for a specific project by reading its .weevil.toml fn setup_project(project_path: &str, proxy: &ProxyConfig) -> Result<()> { let project_path = PathBuf::from(project_path); if !project_path.exists() { bail!("Project directory not found: {}", project_path.display()); } println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan()); println!("{}", " Project Setup - Installing Dependencies".bright_cyan().bold()); println!("{}", "═══════════════════════════════════════════════════════════".bright_cyan()); println!(); // Load project configuration println!("{}", "Reading project configuration...".bright_yellow()); let config = ProjectConfig::load(&project_path) .context("Failed to load .weevil.toml")?; println!(); println!("{}", "Project Configuration:".bright_yellow().bold()); println!(" Project: {}", config.project_name.bright_white()); println!(" FTC SDK: {} ({})", config.ftc_sdk_version.bright_white(), config.ftc_sdk_path.display() ); println!(" Android SDK: {}", config.android_sdk_path.display()); println!(); let mut installed = Vec::new(); let mut issues = Vec::new(); // 1. Check Java println!("{}", "Checking Java JDK...".bright_yellow()); match check_java() { Ok(version) => { println!("{} Java JDK {} found", "✓".green(), version); installed.push(format!("Java JDK {}", version)); } Err(e) => { println!("{} {}", "✗".red(), e); issues.push(("Java JDK", get_java_install_instructions())); } } println!(); // 2. Check/Install project-specific FTC SDK println!("{}", format!("Checking FTC SDK {}...", config.ftc_sdk_version).bright_yellow()); if config.ftc_sdk_path.exists() { match crate::sdk::ftc::verify(&config.ftc_sdk_path) { Ok(_) => { println!("{} FTC SDK {} found at: {}", "✓".green(), config.ftc_sdk_version, config.ftc_sdk_path.display() ); installed.push(format!("FTC SDK {}", config.ftc_sdk_version)); } Err(_) => { println!("{} FTC SDK path exists but is invalid", "✗".red()); println!(" Expected at: {}", config.ftc_sdk_path.display()); println!(); println!("{}", "Solution:".bright_yellow().bold()); println!(" The .weevil.toml specifies an FTC SDK location that doesn't exist or is incomplete."); println!(" You have two options:"); println!(); println!(" 1. Update the project to use a different SDK:"); println!(" weevil config {} --set-sdk ", project_path.display()); println!(); println!(" 2. Install the SDK at the expected location:"); println!(" # Clone FTC SDK to the expected path"); println!(" git clone https://github.com/FIRST-Tech-Challenge/FtcRobotController.git \\"); println!(" {}", config.ftc_sdk_path.display()); println!(" cd {}", config.ftc_sdk_path.display()); println!(" git checkout {}", config.ftc_sdk_version); bail!("FTC SDK verification failed"); } } } else { println!("{} FTC SDK not found at: {}", "✗".red(), config.ftc_sdk_path.display()); println!(); // Try to install it automatically println!("{}", "Attempting automatic installation...".bright_yellow()); match crate::sdk::ftc::install(&config.ftc_sdk_path, &config.android_sdk_path, proxy) { Ok(_) => { println!("{} FTC SDK {} installed successfully", "✓".green(), config.ftc_sdk_version ); installed.push(format!("FTC SDK {} (installed)", config.ftc_sdk_version)); } Err(e) => { println!("{} Automatic installation failed: {}", "✗".red(), e); println!(); println!("{}", "Manual Installation Required:".bright_yellow().bold()); println!(" git clone https://github.com/FIRST-Tech-Challenge/FtcRobotController.git \\"); println!(" {}", config.ftc_sdk_path.display()); println!(" cd {}", config.ftc_sdk_path.display()); println!(" git checkout {}", config.ftc_sdk_version); bail!("FTC SDK installation failed"); } } } println!(); // 3. Check/Install Android SDK println!("{}", "Checking Android SDK...".bright_yellow()); if config.android_sdk_path.exists() { match crate::sdk::android::verify(&config.android_sdk_path) { Ok(_) => { println!("{} Android SDK found at: {}", "✓".green(), config.android_sdk_path.display() ); installed.push("Android SDK".to_string()); } Err(_) => { println!("{} Android SDK found but incomplete, reinstalling...", "⚠".yellow()); crate::sdk::android::install(&config.android_sdk_path, proxy)?; installed.push("Android SDK (installed)".to_string()); } } } else { println!("Android SDK not found. Installing..."); crate::sdk::android::install(&config.android_sdk_path, proxy)?; installed.push("Android SDK (installed)".to_string()); } println!(); // 4. Check ADB println!("{}", "Checking ADB...".bright_yellow()); match check_adb(&config.android_sdk_path) { Ok(version) => { println!("{} ADB {} found", "✓".green(), version); installed.push(format!("ADB {}", version)); } Err(e) => { println!("{} {}", "⚠".yellow(), e); println!(" Add to PATH: {}", config.android_sdk_path.join("platform-tools").display()); } } println!(); // 5. Check Gradle wrapper in project println!("{}", "Checking Gradle wrapper...".bright_yellow()); let gradlew = if cfg!(target_os = "windows") { project_path.join("gradlew.bat") } else { project_path.join("gradlew") }; if gradlew.exists() { println!("{} Gradle wrapper found in project", "✓".green()); installed.push("Gradle wrapper".to_string()); } else { println!("{} Gradle wrapper not found in project", "⚠".yellow()); println!(" Run 'weevil upgrade {}' to regenerate project files", project_path.display()); } println!(); // Print summary print_project_summary(&installed, &issues, &config, &project_path); Ok(()) } fn check_java() -> Result { let output = Command::new("java") .arg("-version") .output(); match output { Ok(out) => { let stderr = String::from_utf8_lossy(&out.stderr); // Java version is typically in stderr, format: java version "11.0.x" or openjdk version "11.0.x" for line in stderr.lines() { if line.contains("version") { if let Some(version_str) = line.split('"').nth(1) { return Ok(version_str.to_string()); } } } Ok("installed (version unknown)".to_string()) } Err(_) => bail!("Java JDK not found in PATH"), } } fn check_adb(android_sdk_path: &Path) -> Result { // First try system PATH let output = Command::new("adb") .arg("version") .output(); if let Ok(out) = output { if out.status.success() { let stdout = String::from_utf8_lossy(&out.stdout); for line in stdout.lines() { if line.starts_with("Android Debug Bridge version") { return Ok(line.replace("Android Debug Bridge version ", "")); } } return Ok("installed (version unknown)".to_string()); } } // Try Android SDK location let adb_path = if cfg!(target_os = "windows") { android_sdk_path.join("platform-tools").join("adb.exe") } else { android_sdk_path.join("platform-tools").join("adb") }; if adb_path.exists() { bail!("ADB found in Android SDK but not in PATH") } else { bail!("ADB not found") } } fn check_gradle() -> Result { let output = Command::new("gradle") .arg("--version") .output(); match output { Ok(out) => { let stdout = String::from_utf8_lossy(&out.stdout); for line in stdout.lines() { if line.starts_with("Gradle") { return Ok(line.replace("Gradle ", "")); } } Ok("installed (version unknown)".to_string()) } Err(_) => bail!("Gradle not found in PATH (optional)"), } } fn get_java_install_instructions() -> String { if cfg!(target_os = "windows") { format!( "Java JDK is required but not found.\n\ \n\ To install Java 11 on Windows:\n\ \n\ 1. Download from: {}\n\ 2. Run the installer\n\ 3. Add Java to your PATH (installer usually does this)\n\ 4. Run 'weevil setup' again to verify\n\ \n\ Verify installation: java -version", "https://adoptium.net/temurin/releases/?version=11".bright_white() ) } else if cfg!(target_os = "macos") { format!( "Java JDK is required but not found.\n\ \n\ To install Java 11 on macOS:\n\ \n\ Using Homebrew (recommended):\n\ {}\n\ \n\ Or download from: {}\n\ \n\ Verify installation: java -version", " brew install openjdk@11".bright_white(), "https://adoptium.net/temurin/releases/?version=11".bright_white() ) } else { format!( "Java JDK is required but not found.\n\ \n\ To install Java 11 on Ubuntu/Debian:\n\ {}\n\ {}\n\ \n\ To install on Fedora/RHEL:\n\ {}\n\ \n\ Verify installation: java -version", " sudo apt update".bright_white(), " sudo apt install openjdk-11-jdk".bright_white(), " sudo dnf install java-11-openjdk-devel".bright_white() ) } } fn print_system_summary(installed: &[String], issues: &[(&str, String)], sdk_config: &SdkConfig) { println!("{}", "═══════════════════════════════════════════════════════════".bright_green()); println!("{}", " System Setup Summary".bright_green().bold()); println!("{}", "═══════════════════════════════════════════════════════════".bright_green()); println!(); if !installed.is_empty() { println!("{}", "Installed Components:".bright_green().bold()); for component in installed { println!(" {} {}", "✓".green(), component); } println!(); } if !issues.is_empty() { println!("{}", "Manual Installation Required:".bright_yellow().bold()); println!(); for (name, instructions) in issues { println!("{} {}", "✗".red(), name.red().bold()); println!(); for line in instructions.lines() { println!(" {}", line); } println!(); } } println!("{}", "SDK Locations:".bright_cyan().bold()); println!(" FTC SDK: {}", sdk_config.ftc_sdk_path.display()); println!(" Android SDK: {}", sdk_config.android_sdk_path.display()); println!(" Cache: {}", sdk_config.cache_dir.display()); println!(); if issues.is_empty() { println!("{}", "✓ System is ready for FTC development!".bright_green().bold()); println!(); println!("{}", "Next steps:".bright_yellow().bold()); println!(" Create a new project: {}", "weevil new my-robot".bright_white()); println!(" Clone existing project: {}", "git clone && cd && weevil setup .".bright_white()); } else { println!("{}", "⚠ Please install the required components listed above".bright_yellow().bold()); println!(" Then run {} to verify", "weevil setup".bright_white()); } println!(); } fn print_project_summary(installed: &[String], issues: &[(&str, String)], config: &ProjectConfig, project_path: &Path) { println!("{}", "═══════════════════════════════════════════════════════════".bright_green()); println!("{}", " Project Setup Summary".bright_green().bold()); println!("{}", "═══════════════════════════════════════════════════════════".bright_green()); println!(); println!("{}", "Project Details:".bright_cyan().bold()); println!(" Name: {}", config.project_name); println!(" Location: {}", project_path.display()); println!(" FTC SDK: {} at {}", config.ftc_sdk_version, config.ftc_sdk_path.display()); println!(); if !installed.is_empty() { println!("{}", "Installed Components:".bright_green().bold()); for component in installed { println!(" {} {}", "✓".green(), component); } println!(); } if !issues.is_empty() { println!("{}", "Manual Installation Required:".bright_yellow().bold()); println!(); for (name, instructions) in issues { println!("{} {}", "✗".red(), name.red().bold()); println!(); for line in instructions.lines() { println!(" {}", line); } println!(); } } if issues.is_empty() { println!("{}", "✓ Project is ready for development!".bright_green().bold()); println!(); println!("{}", "Next steps:".bright_yellow().bold()); println!(" 1. Review the code: {}", format!("cd {}", project_path.display()).bright_white()); println!(" 2. Run tests: {}", "./gradlew test".bright_white()); println!(" 3. Build: {}", "./build.sh (or build.bat on Windows)".bright_white()); println!(" 4. Deploy to robot: {}", format!("weevil deploy {}", project_path.display()).bright_white()); } else { println!("{}", "⚠ Please install the required components listed above".bright_yellow().bold()); println!(" Then run {} to verify", format!("weevil setup {}", project_path.display()).bright_white()); } println!(); }