Files
weevil/src/sdk/ftc.rs
Eric Ratliff 6626ca83d1 feat: Add Windows support and stabilize SDK installation (v1.0.0-rc1)
Complete Windows compatibility overhaul with robust cross-platform SDK management.
This release candidate establishes feature freeze for the 1.0.0 release.

Key improvements:
- Fixed Android SDK installation on Windows
  * Use cmd.exe wrapper for sdkmanager.bat with piped stdin
  * Properly reorganize cmdline-tools directory structure
  * Write license acceptances synchronously to avoid hangs

- Fixed FTC SDK configuration
  * Auto-generate local.properties with Android SDK path
  * Escape backslashes in Kotlin build.gradle.kts strings
  * Support both new installs and upgrades via ensure_local_properties()

- Enhanced Windows console output
  * Enable ANSI color support via enable_ansi_support crate
  * Maintain color compatibility across Windows versions

- Improved error handling and debugging
  * Added comprehensive logging throughout SDK installation
  * Better context messages for troubleshooting failures

Cross-platform testing verified on:
- Windows 11 with Eclipse Adoptium JDK 21
- Linux (existing support maintained)

Breaking changes: None
This RC introduces feature freeze - subsequent 1.0.x releases will be
bug fixes only. New features deferred to 1.1.0.

Closes Windows compatibility milestone.
2026-01-25 18:10:18 -06:00

136 lines
4.1 KiB
Rust

use std::path::Path;
use anyhow::{Result, Context};
use git2::Repository;
use colored::*;
use std::fs;
const FTC_SDK_URL: &str = "https://github.com/FIRST-Tech-Challenge/FtcRobotController.git";
const FTC_SDK_VERSION: &str = "v10.1.1";
pub fn install(sdk_path: &Path, android_sdk_path: &Path) -> Result<()> {
if sdk_path.exists() {
println!("{} FTC SDK already installed at: {}",
"".green(),
sdk_path.display()
);
// Make sure local.properties exists even if SDK was already installed
create_local_properties(sdk_path, android_sdk_path)?;
return check_version(sdk_path);
}
println!("{}", "Installing FTC SDK...".bright_yellow());
println!("Cloning from: {}", FTC_SDK_URL);
println!("Version: {}", FTC_SDK_VERSION);
// Clone the repository
let repo = Repository::clone(FTC_SDK_URL, sdk_path)
.context("Failed to clone FTC SDK")?;
// Checkout specific version
let obj = repo.revparse_single(FTC_SDK_VERSION)?;
repo.checkout_tree(&obj, None)?;
repo.set_head_detached(obj.id())?;
// Create local.properties with Android SDK path
create_local_properties(sdk_path, android_sdk_path)?;
println!("{} FTC SDK installed successfully", "".green());
Ok(())
}
fn create_local_properties(sdk_path: &Path, android_sdk_path: &Path) -> Result<()> {
// Convert path to use forward slashes (works on both Windows and Unix)
let android_sdk_str = android_sdk_path
.display()
.to_string()
.replace("\\", "/");
let local_properties = format!("sdk.dir={}\n", android_sdk_str);
let properties_path = sdk_path.join("local.properties");
fs::write(&properties_path, local_properties)
.context("Failed to create local.properties")?;
println!("{} Created local.properties with Android SDK path", "".green());
Ok(())
}
fn check_version(sdk_path: &Path) -> Result<()> {
let repo = Repository::open(sdk_path)?;
// Get current HEAD
let head = repo.head()?;
let commit = head.peel_to_commit()?;
// Try to find a tag for this commit
let tag_names = repo.tag_names(None)?;
let tags: Vec<_> = tag_names
.iter()
.filter_map(|t| t)
.collect();
println!("Current version: {}",
tags.first()
.map(|t| t.to_string())
.unwrap_or_else(|| format!("{}", commit.id()))
);
Ok(())
}
pub fn update(sdk_path: &Path) -> Result<()> {
println!("{}", "Updating FTC SDK...".bright_yellow());
let repo = Repository::open(sdk_path)
.context("FTC SDK not found or not a git repository")?;
// Fetch latest
let mut remote = repo.find_remote("origin")?;
remote.fetch(&["refs/tags/*:refs/tags/*"], None, None)?;
// Checkout latest version
let obj = repo.revparse_single(FTC_SDK_VERSION)?;
repo.checkout_tree(&obj, None)?;
repo.set_head_detached(obj.id())?;
println!("{} FTC SDK updated to {}", "".green(), FTC_SDK_VERSION);
Ok(())
}
pub fn verify(sdk_path: &Path) -> Result<()> {
if !sdk_path.exists() {
anyhow::bail!("FTC SDK not found at: {}", sdk_path.display());
}
// Check for essential directories
let team_code = sdk_path.join("TeamCode");
let ftc_robot_controller = sdk_path.join("FtcRobotController");
if !team_code.exists() || !ftc_robot_controller.exists() {
anyhow::bail!("FTC SDK incomplete: missing essential directories");
}
Ok(())
}
pub fn get_version(sdk_path: &Path) -> Result<String> {
let repo = Repository::open(sdk_path)?;
let head = repo.head()?;
let commit = head.peel_to_commit()?;
// Try to find a tag
let tags = repo.tag_names(None)?;
for tag in tags.iter().flatten() {
let tag_ref = repo.find_reference(&format!("refs/tags/{}", tag))?;
if let Ok(tag_commit) = tag_ref.peel_to_commit() {
if tag_commit.id() == commit.id() {
return Ok(tag.to_string());
}
}
}
Ok(format!("commit-{}", &commit.id().to_string()[..8]))
}