1 Commits

Author SHA1 Message Date
Eric Ratliff
f83936cbe8 Included new setup command. It has not been tested yet 2026-01-29 22:01:35 -06:00
3 changed files with 525 additions and 1 deletions

View File

@@ -2,4 +2,5 @@ pub mod new;
pub mod upgrade; pub mod upgrade;
pub mod deploy; pub mod deploy;
pub mod sdk; pub mod sdk;
pub mod config; pub mod config;
pub mod setup;

514
src/commands/setup.rs Normal file
View File

@@ -0,0 +1,514 @@
use anyhow::{Result, Context, bail};
use std::path::{Path, PathBuf};
use std::process::Command;
use colored::*;
use crate::sdk::SdkConfig;
use crate::project::ProjectConfig;
/// Setup development environment - either system-wide or for a specific project
pub fn setup_environment(project_path: Option<&str>) -> Result<()> {
match project_path {
Some(path) => setup_project(path),
None => setup_system(),
}
}
/// Setup system-wide development environment with default SDKs
fn setup_system() -> 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)?;
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)?;
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)?;
installed.push("Android SDK (installed)".to_string());
}
}
} else {
println!("Android SDK not found. Installing...");
crate::sdk::android::install(&sdk_config.android_sdk_path)?;
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) -> 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 <path-to-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) {
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)?;
installed.push("Android SDK (installed)".to_string());
}
}
} else {
println!("Android SDK not found. Installing...");
crate::sdk::android::install(&config.android_sdk_path)?;
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<String> {
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<String> {
// 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<String> {
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 <repo> && cd <repo> && 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!();
}

View File

@@ -33,6 +33,12 @@ enum Commands {
android_sdk: Option<String>, android_sdk: Option<String>,
}, },
/// Setup development environment (system or project)
Setup {
/// Path to project directory (optional - without it, sets up system)
path: Option<String>,
},
/// Upgrade an existing project to the latest generator version /// Upgrade an existing project to the latest generator version
Upgrade { Upgrade {
/// Path to the project directory /// Path to the project directory
@@ -99,6 +105,9 @@ fn main() -> Result<()> {
Commands::New { name, ftc_sdk, android_sdk } => { Commands::New { name, ftc_sdk, android_sdk } => {
commands::new::create_project(&name, ftc_sdk.as_deref(), android_sdk.as_deref()) commands::new::create_project(&name, ftc_sdk.as_deref(), android_sdk.as_deref())
} }
Commands::Setup { path } => {
commands::setup::setup_environment(path.as_deref())
}
Commands::Upgrade { path } => { Commands::Upgrade { path } => {
commands::upgrade::upgrade_project(&path) commands::upgrade::upgrade_project(&path)
} }