Cross-platform tool for generating clean, testable FTC robot projects without editing the SDK installation. Features: - Standalone project generation with proper separation from SDK - Per-project SDK configuration via .weevil.toml - Local unit testing support (no robot required) - Cross-platform build/deploy scripts (Linux/macOS/Windows) - Project upgrade system preserving user code - Configuration management commands - Comprehensive test suite (11 passing tests) - Zero-warning builds Architecture: - Pure Rust implementation with embedded Gradle wrapper - Projects use deployToSDK task to copy code to FTC SDK TeamCode - Git-ready projects with automatic initialization - USB and WiFi deployment with auto-detection Commands: - weevil new <name> - Create new project - weevil upgrade <path> - Update project infrastructure - weevil config <path> - View/modify project configuration - weevil sdk status/install/update - Manage SDKs Addresses the core problem: FTC's SDK structure forces students to edit framework internals instead of separating concerns like industry standard practices. Weevil enables proper software engineering workflows for robotics education.
103 lines
2.8 KiB
Rust
103 lines
2.8 KiB
Rust
use std::path::{Path, PathBuf};
|
|
use anyhow::{Result, Context, bail};
|
|
use std::fs;
|
|
use colored::*;
|
|
|
|
pub mod android;
|
|
pub mod ftc;
|
|
pub mod gradle;
|
|
|
|
pub struct SdkConfig {
|
|
pub ftc_sdk_path: PathBuf,
|
|
pub android_sdk_path: PathBuf,
|
|
pub cache_dir: PathBuf,
|
|
}
|
|
|
|
impl SdkConfig {
|
|
pub fn new() -> Result<Self> {
|
|
let home = dirs::home_dir()
|
|
.context("Could not determine home directory")?;
|
|
|
|
let cache_dir = home.join(".weevil");
|
|
fs::create_dir_all(&cache_dir)?;
|
|
|
|
Ok(Self {
|
|
ftc_sdk_path: cache_dir.join("ftc-sdk"),
|
|
android_sdk_path: Self::find_android_sdk().unwrap_or_else(|| cache_dir.join("android-sdk")),
|
|
cache_dir,
|
|
})
|
|
}
|
|
|
|
pub fn with_paths(ftc_sdk: Option<&str>, android_sdk: Option<&str>) -> Result<Self> {
|
|
let mut config = Self::new()?;
|
|
|
|
if let Some(path) = ftc_sdk {
|
|
config.ftc_sdk_path = PathBuf::from(path);
|
|
}
|
|
|
|
if let Some(path) = android_sdk {
|
|
config.android_sdk_path = PathBuf::from(path);
|
|
}
|
|
|
|
Ok(config)
|
|
}
|
|
|
|
fn find_android_sdk() -> Option<PathBuf> {
|
|
// Check common locations
|
|
let home = dirs::home_dir()?;
|
|
|
|
let candidates = vec![
|
|
home.join("Android/Sdk"),
|
|
home.join(".android-sdk"),
|
|
PathBuf::from("/usr/lib/android-sdk"),
|
|
];
|
|
|
|
for candidate in candidates {
|
|
if candidate.join("platform-tools").exists() {
|
|
return Some(candidate);
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn validate(&self) -> Result<()> {
|
|
if !self.ftc_sdk_path.exists() {
|
|
bail!(
|
|
"FTC SDK not found at: {}\nRun: weevil sdk install",
|
|
self.ftc_sdk_path.display()
|
|
);
|
|
}
|
|
|
|
if !self.android_sdk_path.exists() {
|
|
bail!(
|
|
"Android SDK not found at: {}\nRun: weevil sdk install",
|
|
self.android_sdk_path.display()
|
|
);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn print_status(&self) {
|
|
println!("{}", "SDK Configuration:".bright_yellow().bold());
|
|
println!();
|
|
|
|
self.print_sdk_status("FTC SDK", &self.ftc_sdk_path);
|
|
self.print_sdk_status("Android SDK", &self.android_sdk_path);
|
|
|
|
println!();
|
|
println!("{}: {}", "Cache Directory".bright_yellow(), self.cache_dir.display());
|
|
}
|
|
|
|
fn print_sdk_status(&self, name: &str, path: &Path) {
|
|
let status = if path.exists() {
|
|
format!("{} {}", "✓".green(), path.display())
|
|
} else {
|
|
format!("{} {} {}", "✗".red(), path.display(), "(not found)".red())
|
|
};
|
|
|
|
println!("{}: {}", name.bright_yellow(), status);
|
|
}
|
|
} |