Generate .idea/ run configurations for one-click build and deployment directly from Android Studio. Students can now open projects in the IDE they already know and hit the green play button to deploy to their robot. Run configurations generated: - Build: compiles APK without deploying (build.sh / build.bat) - Deploy (auto): auto-detects USB or WiFi connection - Deploy (USB): forces USB deployment (deploy.sh --usb) - Deploy (WiFi): forces WiFi deployment (deploy.sh --wifi) - Test: runs unit tests (./gradlew test) Both Unix (.sh) and Windows (.bat) variants are generated. Android Studio automatically hides the configurations whose script files don't exist, so only platform-appropriate configs appear in the Run dropdown. workspace.xml configures the project tree to hide internal directories (build/, .gradle/, gradle/) and expand src/ by default, giving students a clean view of just their code and the deployment scripts. Technical notes: - Uses ShConfigurationType (not the old ShellScript type) for Android Studio 2025.2+ compatibility - All paths use $PROJECT_DIR$ for portability - INTERPRETER_PATH is /bin/bash on Unix, cmd.exe on Windows - upgrade.rs regenerates all .idea/ files so run configs stay in sync with any future deploy.sh flag changes Requires Shell Script plugin (by JetBrains) to be installed in Android Studio. README.md updated with installation instructions. Files modified: - src/project/mod.rs: generate_idea_files() writes 5 XML files per platform - src/commands/upgrade.rs: add .idea/ files to safe_to_overwrite
153 lines
5.9 KiB
Rust
153 lines
5.9 KiB
Rust
use anyhow::{Result, bail};
|
|
use colored::*;
|
|
use std::path::PathBuf;
|
|
use std::fs;
|
|
|
|
pub fn upgrade_project(path: &str) -> Result<()> {
|
|
let project_path = PathBuf::from(path);
|
|
|
|
// Verify it's a weevil project (check for old .weevil-version or new .weevil.toml)
|
|
let has_old_version = project_path.join(".weevil-version").exists();
|
|
let has_config = project_path.join(".weevil.toml").exists();
|
|
|
|
if !has_old_version && !has_config {
|
|
bail!("Not a weevil project: {} (missing .weevil-version or .weevil.toml)", path);
|
|
}
|
|
|
|
println!("{}", format!("Upgrading project: {}", path).bright_yellow());
|
|
println!();
|
|
|
|
// Get SDK config
|
|
let sdk_config = crate::sdk::SdkConfig::new()?;
|
|
|
|
// Ensure FTC SDK has local.properties (in case it was installed before this feature)
|
|
ensure_local_properties(&sdk_config)?;
|
|
|
|
// Load or create project config
|
|
let project_config = if has_config {
|
|
println!("Found existing .weevil.toml");
|
|
crate::project::ProjectConfig::load(&project_path)?
|
|
} else {
|
|
println!("Creating .weevil.toml (migrating from old format)");
|
|
let project_name = project_path.file_name()
|
|
.and_then(|n| n.to_str())
|
|
.unwrap_or("unknown");
|
|
crate::project::ProjectConfig::new(project_name, sdk_config.ftc_sdk_path.clone(), sdk_config.android_sdk_path.clone())?
|
|
};
|
|
|
|
println!("Current SDK: {}", project_config.ftc_sdk_path.display());
|
|
println!("SDK Version: {}", project_config.ftc_sdk_version);
|
|
println!();
|
|
|
|
// Files that are safe to overwrite (infrastructure, not user code)
|
|
let safe_to_overwrite = vec![
|
|
"build.gradle.kts",
|
|
"settings.gradle.kts",
|
|
"build.sh",
|
|
"build.bat",
|
|
"deploy.sh",
|
|
"deploy.bat",
|
|
"gradlew",
|
|
"gradlew.bat",
|
|
"gradle/wrapper/gradle-wrapper.properties",
|
|
"gradle/wrapper/gradle-wrapper.jar",
|
|
".gitignore",
|
|
// Android Studio integration — regenerated so run configs stay in
|
|
// sync if deploy.sh flags or script names ever change.
|
|
".idea/workspace.xml",
|
|
".idea/runConfigurations/Build.xml",
|
|
".idea/runConfigurations/Build (Windows).xml",
|
|
".idea/runConfigurations/Deploy (auto).xml",
|
|
".idea/runConfigurations/Deploy (auto) (Windows).xml",
|
|
".idea/runConfigurations/Deploy (USB).xml",
|
|
".idea/runConfigurations/Deploy (USB) (Windows).xml",
|
|
".idea/runConfigurations/Deploy (WiFi).xml",
|
|
".idea/runConfigurations/Deploy (WiFi) (Windows).xml",
|
|
".idea/runConfigurations/Test.xml",
|
|
".idea/runConfigurations/Test (Windows).xml",
|
|
];
|
|
|
|
println!("{}", "Updating infrastructure files...".bright_yellow());
|
|
|
|
// Create a modified SDK config that uses the project's configured FTC SDK
|
|
let project_sdk_config = crate::sdk::SdkConfig {
|
|
ftc_sdk_path: project_config.ftc_sdk_path.clone(),
|
|
android_sdk_path: sdk_config.android_sdk_path.clone(),
|
|
cache_dir: sdk_config.cache_dir.clone(),
|
|
};
|
|
|
|
// Regenerate safe files using the project's configured SDK
|
|
let builder = crate::project::ProjectBuilder::new(
|
|
&project_config.project_name,
|
|
&project_sdk_config
|
|
)?;
|
|
|
|
// Temporarily create files in a temp location
|
|
let temp_dir = tempfile::tempdir()?;
|
|
builder.create(temp_dir.path(), &project_sdk_config)?;
|
|
|
|
// Copy only the safe files
|
|
for file in safe_to_overwrite {
|
|
let src = temp_dir.path().join(file);
|
|
let dst = project_path.join(file);
|
|
|
|
if src.exists() {
|
|
if let Some(parent) = dst.parent() {
|
|
fs::create_dir_all(parent)?;
|
|
}
|
|
fs::copy(&src, &dst)?;
|
|
println!(" {} {}", "✓".green(), file);
|
|
|
|
// Make executable if needed
|
|
#[cfg(unix)]
|
|
if file.ends_with(".sh") || file == "gradlew" {
|
|
use std::os::unix::fs::PermissionsExt;
|
|
let mut perms = fs::metadata(&dst)?.permissions();
|
|
perms.set_mode(0o755);
|
|
fs::set_permissions(&dst, perms)?;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save/update the config
|
|
project_config.save(&project_path)?;
|
|
println!(" {} {}", "✓".green(), ".weevil.toml");
|
|
|
|
// Remove old version marker if it exists
|
|
let old_version_file = project_path.join(".weevil-version");
|
|
if old_version_file.exists() {
|
|
fs::remove_file(old_version_file)?;
|
|
println!(" {} {}", "✓".green(), "Removed old .weevil-version");
|
|
}
|
|
|
|
println!();
|
|
println!("{}", "═══════════════════════════════════════════════════════════".bright_green());
|
|
println!("{}", " ✓ Project Upgraded!".bright_green().bold());
|
|
println!("{}", "═══════════════════════════════════════════════════════════".bright_green());
|
|
println!();
|
|
println!("{}", "Your code in src/ was preserved.".green());
|
|
println!("{}", "Build scripts and gradle configuration updated.".green());
|
|
println!();
|
|
println!("Test it: ./gradlew test");
|
|
println!();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn ensure_local_properties(sdk_config: &crate::sdk::SdkConfig) -> Result<()> {
|
|
let local_properties_path = sdk_config.ftc_sdk_path.join("local.properties");
|
|
|
|
if !local_properties_path.exists() {
|
|
println!("Creating local.properties in FTC SDK...");
|
|
let android_sdk_str = sdk_config.android_sdk_path
|
|
.display()
|
|
.to_string()
|
|
.replace("\\", "/");
|
|
|
|
let local_properties = format!("sdk.dir={}\n", android_sdk_str);
|
|
fs::write(&local_properties_path, local_properties)?;
|
|
println!("{} Created local.properties", "✓".green());
|
|
}
|
|
|
|
Ok(())
|
|
} |