feat: Weevil v1.0.0-beta1 - FTC Project Generator
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.
This commit is contained in:
142
src/sdk/android.rs
Normal file
142
src/sdk/android.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use std::path::Path;
|
||||
use anyhow::{Result, Context};
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use reqwest::blocking::Client;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use colored::*;
|
||||
|
||||
const ANDROID_SDK_URL_LINUX: &str = "https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip";
|
||||
const ANDROID_SDK_URL_MAC: &str = "https://dl.google.com/android/repository/commandlinetools-mac-11076708_latest.zip";
|
||||
|
||||
pub fn install(sdk_path: &Path) -> Result<()> {
|
||||
if sdk_path.exists() {
|
||||
println!("{} Android SDK already installed at: {}",
|
||||
"✓".green(),
|
||||
sdk_path.display()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("{}", "Installing Android SDK...".bright_yellow());
|
||||
|
||||
let url = if cfg!(target_os = "macos") {
|
||||
ANDROID_SDK_URL_MAC
|
||||
} else {
|
||||
ANDROID_SDK_URL_LINUX
|
||||
};
|
||||
|
||||
// Download
|
||||
println!("Downloading from: {}", url);
|
||||
let client = Client::new();
|
||||
let response = client.get(url)
|
||||
.send()
|
||||
.context("Failed to download Android SDK")?;
|
||||
|
||||
let total_size = response.content_length().unwrap_or(0);
|
||||
|
||||
let pb = ProgressBar::new(total_size);
|
||||
pb.set_style(
|
||||
ProgressStyle::default_bar()
|
||||
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})")
|
||||
.unwrap()
|
||||
.progress_chars("#>-"),
|
||||
);
|
||||
|
||||
let temp_zip = sdk_path.parent().unwrap().join("android-sdk-temp.zip");
|
||||
let mut file = File::create(&temp_zip)?;
|
||||
|
||||
let content = response.bytes()?;
|
||||
pb.inc(content.len() as u64);
|
||||
file.write_all(&content)?;
|
||||
|
||||
pb.finish_with_message("Download complete");
|
||||
|
||||
// Extract
|
||||
println!("Extracting...");
|
||||
let file = File::open(&temp_zip)?;
|
||||
let mut archive = zip::ZipArchive::new(file)?;
|
||||
|
||||
std::fs::create_dir_all(sdk_path)?;
|
||||
archive.extract(sdk_path)?;
|
||||
|
||||
// Cleanup
|
||||
std::fs::remove_file(&temp_zip)?;
|
||||
|
||||
// Install required packages
|
||||
install_packages(sdk_path)?;
|
||||
|
||||
println!("{} Android SDK installed successfully", "✓".green());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn install_packages(sdk_path: &Path) -> Result<()> {
|
||||
println!("Installing Android SDK packages...");
|
||||
|
||||
let sdkmanager = sdk_path
|
||||
.join("cmdline-tools/bin/sdkmanager");
|
||||
|
||||
if !sdkmanager.exists() {
|
||||
// Try alternate location
|
||||
let alt = sdk_path.join("cmdline-tools/latest/bin/sdkmanager");
|
||||
if alt.exists() {
|
||||
return run_sdkmanager(&alt, sdk_path);
|
||||
}
|
||||
|
||||
// Need to move cmdline-tools to correct location
|
||||
let from = sdk_path.join("cmdline-tools");
|
||||
let to = sdk_path.join("cmdline-tools/latest");
|
||||
if from.exists() {
|
||||
std::fs::create_dir_all(sdk_path.join("cmdline-tools"))?;
|
||||
std::fs::rename(&from, &to)?;
|
||||
return run_sdkmanager(&to.join("bin/sdkmanager"), sdk_path);
|
||||
}
|
||||
}
|
||||
|
||||
run_sdkmanager(&sdkmanager, sdk_path)
|
||||
}
|
||||
|
||||
fn run_sdkmanager(sdkmanager: &Path, sdk_root: &Path) -> Result<()> {
|
||||
use std::process::Command;
|
||||
use std::io::Write;
|
||||
|
||||
// Accept licenses
|
||||
let mut yes_cmd = Command::new("yes");
|
||||
let yes_output = yes_cmd.output()?;
|
||||
|
||||
let mut cmd = Command::new(sdkmanager);
|
||||
cmd.arg("--sdk_root")
|
||||
.arg(sdk_root)
|
||||
.arg("--licenses")
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.spawn()?
|
||||
.stdin
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.write_all(&yes_output.stdout)?;
|
||||
|
||||
// Install packages
|
||||
Command::new(sdkmanager)
|
||||
.arg("--sdk_root")
|
||||
.arg(sdk_root)
|
||||
.arg("platform-tools")
|
||||
.arg("platforms;android-34")
|
||||
.arg("build-tools;34.0.0")
|
||||
.status()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn verify(sdk_path: &Path) -> Result<()> {
|
||||
if !sdk_path.exists() {
|
||||
anyhow::bail!("Android SDK not found at: {}", sdk_path.display());
|
||||
}
|
||||
|
||||
let platform_tools = sdk_path.join("platform-tools");
|
||||
if !platform_tools.exists() {
|
||||
anyhow::bail!("Android SDK incomplete: platform-tools not found");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user