Code cleanup

This commit is contained in:
Sergio Gasquez 2022-08-12 07:41:41 +00:00
parent 84c6d64729
commit 582df61e2c
7 changed files with 624 additions and 1875 deletions

View File

@ -1,281 +0,0 @@
extern crate json;
use clap::Arg;
use clap_nested::{Command, Commander, MultiCommand};
use std::fs;
use std::path::Path;
use md5;
use std::env;
use dirs::home_dir;
use json::JsonValue;
use crate::shell::run_command;
pub fn get_espressif_base_path() -> String {
env::var("IDF_TOOLS_PATH").unwrap_or_else(|_e|
home_dir().unwrap().display().to_string() + "/.espressif"
)
}
// TODO: Use &str like get_dist_path
pub fn get_tool_path(tool_name:String) -> String {
let tools_path = get_espressif_base_path();
format!("{}/tools/{}", tools_path, tool_name)
}
pub fn get_dist_path(tool_name:&str) -> String {
let tools_path = get_espressif_base_path();
format!("{}/dist/{}", tools_path, tool_name)
}
pub fn get_python_env_path(idf_version: String, python_version: String) -> String {
let tools_path = get_espressif_base_path();
format!("{}/python_env/idf{}_py{}_env", tools_path, idf_version, python_version)
}
pub fn get_selected_idf_path() -> String {
let selected_idf_id = get_property("idfSelectedId".to_string());
get_property_with_idf_id("path".to_string(), selected_idf_id)
}
fn get_json_path() -> String {
let idf_json_path = format!("{}/esp_idf.json", get_espressif_base_path());
return idf_json_path;
}
pub fn get_idf_id(idf_path: &str) -> String {
let idf_path_with_slash = format!("{}", idf_path.replace("\\", "/"));
let digest = md5::compute(idf_path_with_slash);
return format!("esp-idf-{:x}", digest);
}
fn bootstrap_json(_json_path: String, tools_path: String) {
let template = json::object!{
"$schema": "http://json-schema.org/schema#",
"$id": "http://dl.espressif.com/dl/schemas/esp_idf",
"_comment": "Configuration file for ESP-IDF Eclipse plugin.",
"_warning": "Use / or \\ when specifying path. Single backslash is not allowed by JSON format.",
"gitPath": "",
"idfToolsPath": tools_path,
"idfSelectedId": "",
"idfInstalled": json::JsonValue::new_object()
};
fs::write(get_json_path(), template.to_string()).unwrap();
}
fn load_json() -> json::JsonValue {
let json_path = get_json_path();
if !Path::new(&json_path).exists() {
println!("Configuration file not found, creating new one: {}", json_path);
bootstrap_json(json_path.clone(), get_espressif_base_path());
}
let content = fs::read_to_string(json_path)
.expect("Failure");
return json::parse(&content.to_string()).unwrap();
}
pub fn get_property(property_name: String) -> String {
let parsed_json = load_json();
return parsed_json[property_name].to_string();
}
fn print_property(property_name: String) {
print!("{}", &get_property(property_name));
}
pub fn get_git_path() -> String {
get_property("gitPath".to_string())
}
pub fn get_property_with_idf_id(property_name: String, idf_id: String) -> String {
let parsed_json = load_json();
return parsed_json["idfInstalled"][idf_id][property_name].to_string();
}
pub fn get_property_with_path(property_name: String, idf_path: String) -> String {
let parsed_json = load_json();
let idf_id = get_idf_id(&idf_path);
return parsed_json["idfInstalled"][idf_id][property_name].to_string();
}
fn print_property_with_path(property_name: String, idf_path: String) {
print!("{}", get_property_with_path(property_name, idf_path));
}
fn print_property_with_id(property_name: String, idf_id: String) {
print!("{}", get_property_with_idf_id(property_name, idf_id));
}
pub fn update_property(property_name: String, property_value: String) {
let mut parsed_json = load_json();
parsed_json[property_name] = JsonValue::String(property_value);
fs::write(get_json_path(), format!("{:#}", parsed_json)).unwrap();
}
// pub fn add_idf_config(idf_path: String, version: String, python_path: String) {
// let idf_id = get_idf_id(&idf_path);
// let _data = json::object! {
// version: version,
// python: python_path,
// path: idf_path
// };
// let mut parsed_json = load_json();
// parsed_json["idfInstalled"].insert(&idf_id, _data).unwrap();
// parsed_json["idfSelectedId"] = JsonValue::String(idf_id);
// fs::write(get_json_path(), format!("{:#}", parsed_json)).unwrap();
// }
// pub fn get_cmd<'a>() -> Command<'a, str> {
// Command::new("get")
// .description("Retrieve configuration")
// .options(|app| {
// app.arg(
// Arg::with_name("property")
// .short("p")
// .long("property")
// .help("Filter result for property name")
// .takes_value(true)
// )
// .arg(
// Arg::with_name("idf-path")
// .short("i")
// .long("idf-path")
// .help("Path to ESP-IDF")
// .takes_value(true),
// )
// .arg(
// Arg::with_name("idf-id")
// .short("j")
// .long("idf-id")
// .help("ESP-IDF installation ID")
// .takes_value(true),
// )
// })
// .runner(|_args, matches| {
// if matches.is_present("property") {
// let property_name = matches.value_of("property").unwrap().to_string();
// if matches.is_present("idf-id") {
// let idf_id = matches.value_of("idf-id").unwrap().to_string();
// print_property_with_id(property_name, idf_id);
// } else if matches.is_present("idf-path") {
// let idf_path = matches.value_of("idf-path").unwrap().to_string();
// print_property_with_path(property_name, idf_path);
// } else {
// print_property(property_name);
// }
// } else {
// let content = load_json();
// println!("{:#}", &content);
// }
// Ok(())
// })
// }
fn open_idf_config() {
let mut arguments: Vec<String> = [].to_vec();
arguments.push(get_json_path());
match run_command("notepad".to_string(), arguments, "".to_string()) {
Ok(_) => { println!("Ok"); },
Err(_e) => { println!("Failed");}
}
}
pub fn get_edit_cmd<'a>() -> Command<'a, str> {
Command::new("edit")
.description("Open configuration file in editor")
.runner(|_args, _matches| {
open_idf_config();
Ok(())
})
}
// pub fn get_add_cmd<'a>() -> Command<'a, str> {
// Command::new("add")
// .description("Add configuration")
// .options(|app| {
// app.arg(
// Arg::with_name("python")
// .short("p")
// .long("python")
// .help("Full path to Python binary")
// .takes_value(true)
// )
// .arg(
// Arg::with_name("idf-path")
// .short("i")
// .long("idf-path")
// .help("Path to ESP-IDF")
// .takes_value(true),
// )
// .arg(
// Arg::with_name("idf-version")
// .short("x")
// .long("idf-version")
// .help("ESP-IDF version")
// .takes_value(true)
// )
// .arg(
// Arg::with_name("git")
// .short("g")
// .long("git")
// .help("Full path to Git binary")
// .takes_value(true)
// )
// .arg(
// Arg::with_name("name")
// .short("n")
// .long("name")
// .help("Custom name of ESP-IDF installation")
// .takes_value(true)
// )
// })
// .runner(|_args, matches| {
// let python_path = matches.value_of("python").unwrap().to_string();
// let version = matches.value_of("idf-version").unwrap().to_string();
// let idf_path = matches.value_of("idf-path").unwrap().to_string();
// add_idf_config(idf_path, version, python_path);
// Ok(())
// })
// }
fn get_set_runner(_args: &str, matches: &clap::ArgMatches) -> std::result::Result<(), clap::Error> {
let git_path = matches.value_of("git").unwrap().to_string();
update_property("gitPath".to_string(), git_path);
Ok(())
}
// pub fn get_set_cmd<'a>() -> Command<'a, str> {
// Command::new("set")
// .description("set configuration")
// .options(|app| {
// app.arg(
// Arg::with_name("git")
// .short("g")
// .long("git")
// .help("Full path to Git binary")
// .takes_value(true)
// )
// })
// .runner(|_args, matches|
// get_set_runner(_args, matches)
// )
// }
// pub fn get_multi_cmd<'a>() -> MultiCommand<'a, str, str> {
// let multi_cmd: MultiCommand<str, str> = Commander::new()
// .add_cmd(get_cmd())
// .add_cmd(get_edit_cmd())
// .add_cmd(get_add_cmd())
// .add_cmd(get_set_cmd())
// .into_cmd("config")
// // Optionally specify a description
// .description("Maintain configuration of ESP-IDF installations.");
// return multi_cmd;
// }

View File

@ -1,805 +0,0 @@
use clap::Arg;
use clap_nested::{Command, Commander, MultiCommand};
use git2::Repository;
use std::path::Path;
use tokio::runtime::Handle;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
use std::env;
use std::io::Read;
use std::process::Stdio;
use std::time::Instant;
use crate::config::get_selected_idf_path;
use crate::config::get_espressif_base_path;
use crate::config::{
get_dist_path, get_git_path, get_python_env_path, get_tool_path, update_property,
};
use crate::package::prepare_package;
use crate::shell::run_command;
use dirs::home_dir;
async fn excecute_async(command: String, arguments: Vec<String>) {
let _child_process = tokio::process::Command::new(command)
.args(arguments)
.status()
.await;
}
fn execute_command(command: String, arguments: Vec<String>) -> Result<()> {
let argument_string = arguments
.clone()
.into_iter()
.map(|i| format!("{} ", i.to_string()))
.collect::<String>();
println!("Executing: {} {}", command, argument_string);
let handle = Handle::current().clone();
let th = std::thread::spawn(move || handle.block_on(excecute_async(command, arguments)));
th.join().unwrap();
Ok(())
}
fn reset_repository(repository_path: String) -> Result<()> {
let idf_path = Path::new(&repository_path);
assert!(env::set_current_dir(&idf_path).is_ok());
println!("Working directory: {}", idf_path.display());
let git_path = get_git_path();
let mut arguments: Vec<String> = [].to_vec();
arguments.push("reset".to_string());
arguments.push("--hard".to_string());
assert!(execute_command(git_path, arguments).is_ok());
let mut arguments_submodule: Vec<String> = [].to_vec();
arguments_submodule.push("submodule".to_string());
arguments_submodule.push("foreach".to_string());
arguments_submodule.push("git".to_string());
arguments_submodule.push("reset".to_string());
arguments_submodule.push("--hard".to_string());
assert!(execute_command(get_git_path(), arguments_submodule).is_ok());
let mut arguments_clean: Vec<String> = [].to_vec();
arguments_clean.push("clean".to_string());
arguments_clean.push("force".to_string());
arguments_clean.push("-d".to_string());
assert!(execute_command(get_git_path(), arguments_clean).is_ok());
let mut arguments_status: Vec<String> = [].to_vec();
arguments_status.push("status".to_string());
assert!(execute_command(get_git_path(), arguments_status).is_ok());
Ok(())
}
fn update_submodule(
idf_path: String,
submodule: String,
depth: String,
progress: bool,
) -> Result<()> {
let mut arguments_submodule: Vec<String> = [].to_vec();
arguments_submodule.push("-C".to_string());
arguments_submodule.push(idf_path);
arguments_submodule.push("submodule".to_string());
arguments_submodule.push("update".to_string());
arguments_submodule.push("--depth".to_string());
arguments_submodule.push(depth);
if progress {
arguments_submodule.push("--progress".to_string());
}
arguments_submodule.push("--recommend-shallow".to_string());
arguments_submodule.push("--recursive".to_string());
arguments_submodule.push(submodule);
assert!(execute_command(get_git_path(), arguments_submodule).is_ok());
Ok(())
}
// fn get_reset_cmd<'a>() -> Command<'a, str> {
// Command::new("reset")
// .description("Reset ESP-IDF git repository to initial state and wipe out modified data")
// .options(|app| {
// app.arg(
// Arg::with_name("idf-path")
// .short("d")
// .long("idf-path")
// .help("Path to existing ESP-IDF")
// .takes_value(true)
// )
// })
// .runner(|_args, matches| {
// if matches.value_of("idf-path").is_some() {
// let dir = matches.value_of("idf-path").unwrap();
// assert!(reset_repository(dir.to_string()).is_ok());
// }
// Ok(())
// })
// }
#[cfg(unix)]
fn get_idf_base_directory() -> String {
home_dir().unwrap().display().to_string() + "/.espressif"
}
#[cfg(windows)]
fn get_esp_idf_directory(idf_name: String) -> String {
format!("{}/{}", get_idf_base_directory(), idf_name).replace("/", "\\")
}
#[cfg(unix)]
fn get_esp_idf_directory(idf_name: String) -> String {
format!("{}/{}", get_idf_base_directory(), idf_name)
}
fn get_install_runner(
_args: &str,
_matches: &clap::ArgMatches,
) -> std::result::Result<(), clap::Error> {
let esp_idf = get_esp_idf_directory("esp-idf-master/".to_string());
println!("ESP-IDF Path: {}", esp_idf);
#[cfg(windows)]
match prepare_package(
"https://dl.espressif.com/dl/idf-git/idf-git-2.30.1-win64.zip".to_string(),
get_dist_path("idf-git-2.30.1-win64.zip").as_str(),
get_tool_path("idf-git/2.30.1".to_string()),
) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
#[cfg(windows)]
match prepare_package(
"https://dl.espressif.com/dl/idf-python/idf-python-3.8.7-embed-win64.zip".to_string(),
get_dist_path("idf-python-3.8.7-embed-win64.zip").as_str(),
get_tool_path("idf-python/3.8.7".to_string()),
) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
#[cfg(windows)]
let git_path = get_tool_path("idf-git/2.30.1/cmd/git.exe".to_string());
#[cfg(unix)]
let git_path = "/usr/bin/git".to_string();
update_property("gitPath".to_string(), git_path.clone());
#[cfg(windows)]
let python_path = get_tool_path("idf-python/3.8.7/python.exe".to_string());
#[cfg(unix)]
let python_path = "/usr/bin/python".to_string();
let virtual_env_path = get_python_env_path("4.4".to_string(), "3.8".to_string());
if !Path::new(&esp_idf).exists() {
// let clone_command = format!("git clone --shallow-since=2020-01-01 --jobs 8 --recursive git@github.com:espressif/esp-idf.git ");
let mut arguments: Vec<String> = [].to_vec();
arguments.push("clone".to_string());
arguments.push("--shallow-since=2020-01-01".to_string());
arguments.push("--jobs".to_string());
arguments.push("8".to_string());
arguments.push("--recursive".to_string());
arguments.push("https://github.com/espressif/esp-idf.git".to_string());
// arguments.push("git@github.com:espressif/esp-idf.git".to_string());
arguments.push(esp_idf.clone());
println!("Cloning: {} {:?}", git_path, arguments);
match run_command(git_path, arguments, "".to_string()) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
}
if !Path::new(&virtual_env_path).exists() {
println!("Creating virtual environment: {}", virtual_env_path);
let mut arguments: Vec<String> = [].to_vec();
arguments.push("-m".to_string());
arguments.push("virtualenv".to_string());
arguments.push(virtual_env_path.clone());
match run_command(python_path, arguments, "".to_string()) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
}
#[cfg(windows)]
let python_path = format!("{}/Scripts/python.exe", virtual_env_path);
#[cfg(unix)]
let python_path = format!("{}/bin/python", virtual_env_path);
let idf_tools = format!("{}/tools/idf_tools.py", esp_idf);
let mut arguments: Vec<String> = [].to_vec();
arguments.push(idf_tools.clone());
arguments.push("install".to_string());
match run_command(python_path.clone(), arguments, "".to_string()) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
let mut arguments: Vec<String> = [].to_vec();
arguments.push(idf_tools);
arguments.push("install-python-env".to_string());
match run_command(python_path.clone(), arguments, "".to_string()) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
// add_idf_config(esp_idf, "4.4".to_string(), python_path);
Ok(())
}
pub fn install_espidf(targets: &str, version: String) -> Result<()> {
let espidf_path = get_esp_idf_directory("frameworks/esp-idf".to_string());
println!("ESP-IDF Path: {}", espidf_path);
#[cfg(windows)]
match prepare_package(
"https://dl.espressif.com/dl/idf-git/idf-git-2.30.1-win64.zip".to_string(),
get_dist_path("idf-git-2.30.1-win64.zip").as_str(),
get_tool_path("idf-git/2.30.1".to_string()),
) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
#[cfg(windows)]
match prepare_package(
"https://dl.espressif.com/dl/idf-python/idf-python-3.8.7-embed-win64.zip".to_string(),
get_dist_path("idf-python-3.8.7-embed-win64.zip").as_str(),
get_tool_path("idf-python/3.8.7".to_string()),
) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
#[cfg(windows)]
let git_path = get_tool_path("idf-git/2.30.1/cmd/git.exe".to_string());
#[cfg(unix)]
let git_path = "/usr/bin/git".to_string();
update_property("gitPath".to_string(), git_path.clone());
#[cfg(windows)]
let python_path = get_tool_path("idf-python/3.8.7/python.exe".to_string());
#[cfg(unix)]
let python_path = "/usr/bin/python3".to_string();
let virtual_env_path = get_python_env_path("4.4".to_string(), "3.8".to_string());
// TODO: Use any git crate?
if !Path::new(&espidf_path).exists() {
// let clone_command = format!("git clone --shallow-since=2020-01-01 --jobs 8 --recursive git@github.com:espressif/esp-idf.git ");
let mut arguments: Vec<String> = [].to_vec();
arguments.push("clone".to_string());
arguments.push("--jobs".to_string());
arguments.push("8".to_string());
arguments.push("--branch".to_string());
arguments.push(version);
arguments.push("--depth".to_string());
arguments.push("1".to_string());
arguments.push("--shallow-submodules".to_string());
arguments.push("--recursive".to_string());
arguments.push("https://github.com/espressif/esp-idf.git".to_string());
// arguments.push("git@github.com:espressif/esp-idf.git".to_string());
arguments.push(espidf_path.clone());
println!("Cloning: {} {:?}", git_path, arguments);
match run_command(git_path, arguments, "".to_string()) {
Ok(_) => {
println!("Cloned esp-idf suscessfuly");
}
Err(_e) => {
println!("Cloned esp-idf failed");
}
}
}
println!("Installing esp-idf for {} with {}/install.sh", targets, espidf_path);
let install_script_path = format!("{}/install.sh", espidf_path);
let mut arguments: Vec<String> = [].to_vec();
arguments.push(targets.to_string());
match run_command(install_script_path, arguments, "".to_string()) {
Ok(_) => {
println!("ESP-IDF installation succeeded");
}
Err(_e) => {
println!("ESP-IDF installation failed");
}
}
// match std::process::Command::new(install_script_path)
// .arg("esp32 esp32s2")
// .stdout(Stdio::piped())
// .output()
// {
// Ok(child_output) => {
// let result = String::from_utf8_lossy(&child_output.stdout);
// println!("ESP-IDF installation succeeded: {}", result);
// }
// Err(e) => {
// println!("ESP-IDF installation failed: {}", e);
// }
// }
println!("Installing CMake");
let mut arguments: Vec<String> = [].to_vec();
let mut idf_tools_scritp_path = format!("{}/tools/idf_tools.py", espidf_path);
arguments.push(idf_tools_scritp_path);
arguments.push("install".to_string());
arguments.push("cmake".to_string());
match run_command(python_path, arguments, "".to_string()) {
Ok(_) => {
println!("CMake installation succeeded");
}
Err(_e) => {
println!("CMake installation failed");
}
}
Ok(())
}
// pub fn get_install_cmd<'a>() -> Command<'a, str> {
// Command::new("install")
// .description("Install new instance of IDF")
// .options(|app| {
// app.arg(
// Arg::with_name("installer")
// .short("e")
// .long("installer")
// .help("Path to installer binary"),
// )
// .arg(
// Arg::with_name("interactive")
// .short("i")
// .long("interactive")
// .help("Run installation in interactive mode"),
// )
// .arg(
// Arg::with_name("upgrade")
// .short("u")
// .long("upgrade")
// .takes_value(false)
// .help("Upgrade existing installation"))
// .arg(
// Arg::with_name("idf-version")
// .short("x")
// .long("idf-version")
// .takes_value(true)
// .help("ESP-IDF version"))
// .arg(
// Arg::with_name("idf-path")
// .short("d")
// .long("idf-path")
// .takes_value(true)
// .help("ESP-IDF installation directory"))
// .arg(
// Arg::with_name("verbose")
// .short("w")
// .long("verbose")
// .takes_value(false)
// .help("display diagnostic log after installation"))
// })
// .runner(|_args, matches|
// get_install_runner(_args, matches)
// )
// }
#[cfg(unix)]
fn get_shell() -> String {
"/bin/bash".to_string()
}
#[cfg(unix)]
fn get_initializer() -> String {
format!("{}/export.sh", get_selected_idf_path())
}
#[cfg(unix)]
fn get_initializer_arguments() -> Vec<String> {
let mut arguments: Vec<String> = [].to_vec();
arguments.push("-c".to_string());
arguments.push(
". ./export.sh;cd examples/get-started/blink;idf.py fullclean; idf.py build".to_string(),
);
arguments
}
#[cfg(windows)]
fn get_shell() -> String {
"powershell".to_string()
}
#[cfg(windows)]
fn get_initializer() -> String {
format!("{}/Initialize-Idf.ps1", get_espressif_base_path())
}
#[cfg(windows)]
fn get_initializer_arguments() -> Vec<String> {
let mut arguments: Vec<String> = [].to_vec();
arguments.push("-ExecutionPolicy".to_string());
arguments.push("Bypass".to_string());
arguments.push("-NoExit".to_string());
arguments.push("-File".to_string());
arguments.push(get_initializer());
arguments
}
fn get_shell_runner(
_args: &str,
_matches: &clap::ArgMatches,
) -> std::result::Result<(), clap::Error> {
println!("Starting process");
// let root = Path::new("C:\\esp");
// assert!(env::set_current_dir(&root).is_ok());
// println!("Successfully changed working directory to {}!", root.display());
let process = std::process::Command::new(get_shell())
.args(get_initializer_arguments())
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.spawn()
.unwrap();
let mut s = String::new();
match process.stdout.unwrap().read_to_string(&mut s) {
Err(why) => panic!("couldn't read stdout: {}", why),
Ok(_) => print!("{}", s),
}
Ok(())
}
// pub fn get_shell_cmd<'a>() -> Command<'a, str> {
// Command::new("shell")
// .description("Start the companion")
// .options(|app| {
// app.arg(
// Arg::with_name("port")
// .short("p")
// .long("port")
// .help("Name of communication port")
// .takes_value(true)
// )
// })
// .runner(|_args, matches| get_shell_runner(_args, matches) )
// }
#[cfg(unix)]
fn run_build(
idf_path: &String,
shell_initializer: &String,
) -> std::result::Result<(), clap::Error> {
// println!("Starting process");
let root = Path::new(&idf_path);
assert!(env::set_current_dir(&root).is_ok());
run_idf_command("cd examples/get-started/blink; idf.py fullclean; idf.py build".to_string());
//println!("output = {:?}", output);
Ok(())
}
fn run_idf_command(command: String) {
match run_command(get_shell(), get_initializer_arguments(), command) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
}
#[cfg(windows)]
fn run_build(
idf_path: &String,
_shell_initializer: &String,
) -> std::result::Result<(), clap::Error> {
// println!("Starting process");
let root = Path::new(&idf_path);
assert!(env::set_current_dir(&root).is_ok());
run_idf_command("cd examples/get-started/blink; idf.py fullclean; idf.py build\n".to_string());
Ok(())
}
fn get_build_runner(
_args: &str,
matches: &clap::ArgMatches,
) -> std::result::Result<(), clap::Error> {
let build_repetitions: i32 = matches
.value_of("repeat")
.unwrap()
.to_string()
.parse()
.unwrap();
let idf_path = matches
.value_of("idf-path")
.unwrap_or(&*get_selected_idf_path())
.to_string();
let initializer = get_initializer();
println!("Number of CPU cores: {}", num_cpus::get());
println!("ESP-IDF Shell Initializer: {}", initializer);
println!("ESP-IDF Path: {}", idf_path);
for _build_number in 0..build_repetitions {
let start = Instant::now();
match run_build(&idf_path, &initializer) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
let duration = start.elapsed();
println!("Time elapsed in build: {:?}", duration);
}
Ok(())
}
fn change_submodules_mirror(mut repo: Repository, submodule_url: String) {
let mut change_set: Vec<(String, String)> = Vec::new();
for submodule in repo.submodules().unwrap() {
let repo_name = submodule.name().unwrap().to_string();
let original_url = submodule.url().unwrap();
if !(original_url.starts_with("../../") || original_url.starts_with("https://github.com")) {
println!("Submodule: {}, URL: {} - skip", repo_name, original_url);
continue;
}
let mut old_repo = original_url.split('/').last().unwrap();
// Correction of some names
if old_repo.starts_with("unity") {
old_repo = "Unity"
} else if old_repo.starts_with("cexception") {
old_repo = "CException"
}
let new_url = format!("{}{}", submodule_url, old_repo);
change_set.push((repo_name, new_url));
}
for submodule in change_set {
println!("Submodule: {}, new URL: {}", submodule.0, submodule.1);
match repo.submodule_set_url(&*submodule.0, &*submodule.1) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
}
}
fn get_mirror_switch_runner(
_args: &str,
matches: &clap::ArgMatches,
) -> std::result::Result<(), clap::Error> {
let idf_path = matches
.value_of("idf-path")
.unwrap_or(&*get_selected_idf_path())
.to_string();
let url = matches.value_of("url").unwrap().to_string();
let submodule_url = matches.value_of("submodule-url").unwrap().to_string();
println!("Processing main repository...");
match Repository::open(idf_path.clone()) {
Ok(repo) => {
//repo.find_remote("origin")?.url()
if matches.is_present("url") {
match repo.remote_set_url("origin", url.as_str()) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
}
change_submodules_mirror(repo, submodule_url.clone());
}
Err(e) => {
println!("failed to open: {}", e.to_string());
std::process::exit(1);
}
};
println!("Processing submodules...");
match Repository::open(idf_path.clone()) {
Ok(repo) => {
//repo.find_remote("origin")?.url()
if matches.is_present("url") {
match repo.remote_set_url("origin", url.as_str()) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
}
for mut submodule_repo_reference in repo.submodules().unwrap() {
match submodule_repo_reference.init(false) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
let progress = matches.is_present("progress");
if matches.is_present("depth") {
// git2 crate does not support depth for submodules, we need to call git instead
let depth = matches.value_of("depth").unwrap().to_string();
match update_submodule(
idf_path.clone(),
submodule_repo_reference.name().unwrap().to_string(),
depth,
progress,
) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
} else {
match submodule_repo_reference.update(true, None) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
}
match submodule_repo_reference.open() {
Ok(sub_repo) => {
println!("Processing submodule: {:?}", sub_repo.workdir().unwrap());
change_submodules_mirror(sub_repo, submodule_url.clone());
}
Err(_e) => {
println!("Unable to update submodule");
}
}
}
}
Err(e) => {
println!("failed to open: {}", e.to_string());
std::process::exit(1);
}
};
Ok(())
}
// pub fn get_build_cmd<'a>() -> Command<'a, str> {
// Command::new("build")
// .description("Start build process")
// .options(|app| {
// app.arg(
// Arg::with_name("repeat")
// .short("r")
// .long("repeat")
// .help("Number of repetitions of the same command")
// .takes_value(true)
// .default_value("1")
// )
// .arg(
// Arg::with_name("idf-path")
// .short("p")
// .long("idf-path")
// .help("Path to ESP IDF source code repository")
// .takes_value(true)
// )
// .arg(
// Arg::with_name("tools-path")
// .short("t")
// .long("tools-path")
// .help("Path to Tools directory")
// .takes_value(true)
// )
// })
// .runner(|_args, matches|
// get_build_runner(_args, matches)
// )
// }
// pub fn get_mirror_cmd<'a>() -> Command<'a, str> {
// Command::new("mirror")
// .description("Switch the URL of repository mirror")
// .options(|app| {
// app.arg(
// Arg::with_name("url")
// .short("u")
// .long("url")
// .help("Base URL of the main repo")
// .takes_value(true)
// )
// .arg(
// Arg::with_name("idf-path")
// .short("p")
// .long("idf-path")
// .help("Path to ESP IDF source code repository")
// .takes_value(true)
// )
// .arg(
// Arg::with_name("submodule-url")
// .short("s")
// .long("submodule-url")
// .help("Base URL for submodule mirror")
// .required(true)
// .takes_value(true)
// )
// .arg(
// Arg::with_name("depth")
// .short("d")
// .long("depth")
// .help("Create shallow clone of the repo and submodules")
// .takes_value(true)
// )
// .arg(
// Arg::with_name("progress")
// .short("r")
// .long("progress")
// .help("Display progress status of git operation")
// )
// })
// .runner(|_args, matches|
// get_mirror_switch_runner(_args, matches)
// )
// }
// pub fn get_multi_cmd<'a>() -> MultiCommand<'a, str, str> {
// let multi_cmd: MultiCommand<str, str> = Commander::new()
// .add_cmd(get_build_cmd())
// .add_cmd(get_install_cmd())
// .add_cmd(get_mirror_cmd())
// .add_cmd(get_reset_cmd())
// .add_cmd(get_shell_cmd())
// .into_cmd("idf")
// // Optionally specify a description
// .description("Maintain configuration of ESP-IDF installations.");
// return multi_cmd;
// }

View File

@ -1,25 +1,29 @@
extern crate clap;
extern crate json;
use clap::Parser;
use clap_nested::Commander;
use dirs::home_dir;
// use clap_nested::Commander;
// use dirs::home_dir;
// use std::error::Error;
use std::path::{Path, PathBuf};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
use crate::config::get_tool_path;
use crate::idf::install_espidf;
use crate::package::{prepare_package_strip_prefix, prepare_single_binary};
use crate::shell::{run_command, update_env_path};
// use crate::config::get_tool_path;
// use crate::idf::install_espidf;
// use crate::package::{prepare_package_strip_prefix, prepare_single_binary};
// use crate::shell::{run_command, update_env_path};
use crate::utils::*;
use crate::toolchain::*;
use espflash::Chip;
use std::env;
use std::io::{Error, ErrorKind};
use std::process::Stdio;
use std::str::FromStr;
mod config;
mod idf;
mod package;
mod shell;
use std::fs;
// use std::env;
// use std::io::{Error, ErrorKind};
// use std::process::Stdio;
// use std::str::FromStr;
// mod config;
// mod idf;
// mod package;
// mod shell;
mod utils;
mod toolchain;
// use std::fs;
// General TODOs:
// - Prettify prints (add emojis)
@ -31,6 +35,7 @@ use std::fs;
// - Add tests
// - Clean unused code
// - Add progress bar
// - Shall we delete <espressif>/tools/rust and <espressif>/tools/rust-src?
#[derive(Parser)]
struct Opts {
@ -146,7 +151,7 @@ fn install(args: InstallOpts) -> Result<()> {
);
let idf_tool_xtensa_elf_clang = format!(
"{}/{}-{}",
get_tool_path("xtensa-esp32-elf-clang".to_string()),
get_tool_path("xtensa-esp32-elf-clang"),
&llvm_version,
arch
);
@ -178,7 +183,7 @@ fn install(args: InstallOpts) -> Result<()> {
} else {
match prepare_package_strip_prefix(
&rust_dist_url,
get_tool_path("rust".to_string()),
get_tool_path("rust"),
&format!("rust-nightly-{}", arch),
) {
Ok(_) => {
@ -192,13 +197,13 @@ fn install(args: InstallOpts) -> Result<()> {
let mut arguments: Vec<String> = [].to_vec();
println!(
"{}/install.sh --destdir={} --prefix='' --without=rust-docs",
get_tool_path("rust".to_string()),
get_tool_path("rust"),
args.toolchain_destination.display()
);
arguments.push("-c".to_string());
arguments.push(format!(
"{}/install.sh --destdir={} --prefix='' --without=rust-docs",
get_tool_path("rust".to_string()),
get_tool_path("rust"),
args.toolchain_destination.display()
));
@ -213,7 +218,7 @@ fn install(args: InstallOpts) -> Result<()> {
match prepare_package_strip_prefix(
&rust_src_dist_url,
get_tool_path("rust-src".to_string()),
get_tool_path("rust-src"),
"rust-src-nightly",
) {
Ok(_) => {
@ -227,13 +232,13 @@ fn install(args: InstallOpts) -> Result<()> {
let mut arguments: Vec<String> = [].to_vec();
println!(
"{}/install.sh --destdir={} --prefix='' --without=rust-docs",
get_tool_path("rust-src".to_string()),
get_tool_path("rust-src"),
args.toolchain_destination.display()
);
arguments.push("-c".to_string());
arguments.push(format!(
"{}/install.sh --destdir={} --prefix='' --without=rust-docs",
get_tool_path("rust-src".to_string()),
get_tool_path("rust-src"),
args.toolchain_destination.display()
));
match run_command("/bin/bash".to_string(), arguments, "".to_string()) {
@ -259,7 +264,7 @@ fn install(args: InstallOpts) -> Result<()> {
match prepare_package_strip_prefix(
&llvm_url,
get_tool_path(
format!("xtensa-esp32-elf-clang-{}-{}", &llvm_version, llvm_arch).to_string(),
&format!("xtensa-esp32-elf-clang-{}-{}", llvm_version, llvm_arch),
),
"",
) {
@ -273,7 +278,7 @@ fn install(args: InstallOpts) -> Result<()> {
}
let libclang_path = format!(
"{}/lib",
get_tool_path("xtensa-esp32-elf-clang".to_string())
get_tool_path("xtensa-esp32-elf-clang")
);
println!("export LIBCLANG_PATH=\"{}\"", &libclang_path);
exports.push(format!("export LIBCLANG_PATH=\"{}\"", &libclang_path));
@ -287,10 +292,10 @@ fn install(args: InstallOpts) -> Result<()> {
if args.espidf_version.is_some() {
idf::install_espidf(&args.build_target, args.espidf_version.unwrap())?;
install_espidf(&args.build_target, args.espidf_version.unwrap())?;
exports.push(format!(
"export IDF_TOOLS_PATH=\"{}\"",
config::get_espressif_base_path()
get_espressif_base_path()
));
exports.push(format!(". ./{}/export.sh\"", "TODO:UPDATE"));
} else {
@ -361,274 +366,7 @@ async fn main() -> Result<()> {
}
}
fn get_rust_installer(arch: &str) -> &str {
match arch {
"x86_64-pc-windows-msvc" => "",
"x86_64-pc-windows-gnu" => "",
_ => "./install.sh",
}
}
fn install_rust_nightly(version: &str) {
println!("installing nightly toolchain");
match std::process::Command::new("rustup")
.arg("toolchain")
.arg("install")
.arg(version)
.arg("--profile")
.arg("minimal")
.stdout(Stdio::piped())
.output()
{
Ok(child_output) => {
let result = String::from_utf8_lossy(&child_output.stdout);
println!("Result: {}", result);
}
Err(e) => {
println!("Error: {}", e);
}
}
}
fn install_riscv_target(version: &str){
match std::process::Command::new("rustup")
.arg("component")
.arg("add")
.arg("rust-src")
.arg("--toolchain")
.arg(version)
.stdout(Stdio::piped())
.output()
{
Ok(child_output) => {
let result = String::from_utf8_lossy(&child_output.stdout);
println!("Rust-src for RiscV target installed suscesfully: {}", result);
}
Err(e) => {
println!("Rust-src for RiscV target installation failed: {}", e);
}
}
match std::process::Command::new("rustup")
.arg("target")
.arg("add")
.arg("--toolchain")
.arg(version)
.arg("riscv32imc-unknown-none-elf")
.stdout(Stdio::piped())
.output()
{
Ok(child_output) => {
let result = String::from_utf8_lossy(&child_output.stdout);
println!("RiscV target installed suscesfully: {}", result);
}
Err(e) => {
println!("RiscV target installation failed: {}", e);
}
}
}
fn install_rustup() {
#[cfg(windows)]
let rustup_init_path =
prepare_single_binary("https://win.rustup.rs/x86_64", "rustup-init.exe", "rustup");
#[cfg(unix)]
let rustup_init_path = prepare_single_binary("https://sh.rustup.rs/", "rustup-init", "rustup");
println!("rustup stable");
match std::process::Command::new(rustup_init_path)
.arg("--default-toolchain")
.arg("none")
.arg("--profile")
.arg("minimal")
.arg("-y")
.stdout(Stdio::piped())
.output()
{
Ok(child_output) => {
let result = String::from_utf8_lossy(&child_output.stdout);
println!("{}", result);
}
Err(e) => {
println!("Error: {}", e);
}
}
}
fn get_llvm_version_with_underscores(llvm_version: &str) -> String {
let version: Vec<&str> = llvm_version.split("-").collect();
let llvm_dot_version = version[1];
llvm_dot_version.replace(".", "_")
}
fn get_artifact_file_extension(arch: &str) -> &str {
match arch {
"x86_64-pc-windows-msvc" => "zip",
"x86_64-pc-windows-gnu" => "zip",
_ => "tar.xz",
}
}
fn get_llvm_arch(arch: &str) -> &str {
match arch {
"aarch64-apple-darwin" => "macos",
"x86_64-apple-darwin" => "macos",
"x86_64-unknown-linux-gnu" => "linux-amd64",
"x86_64-pc-windows-msvc" => "win64",
"x86_64-pc-windows-gnu" => "win64",
_ => arch,
}
}
fn get_gcc_arch(arch: &str) -> &str {
match arch {
"aarch64-apple-darwin" => "macos",
"aarch64-unknown-linux-gnu" => "linux-arm64",
"x86_64-apple-darwin" => "macos",
"x86_64-unknown-linux-gnu" => "linux-amd64",
"x86_64-pc-windows-msvc" => "win64",
"x86_64-pc-windows-gnu" => "win64",
_ => arch,
}
}
fn install_gcc_targets(targets: Vec<Chip>) -> Result<Vec<String>> {
let mut exports: Vec<String> = Vec::new();
for target in targets {
match target {
Chip::Esp32 => {
install_gcc("xtensa-esp32-elf");
exports.push(format!(
"export PATH={}:$PATH",
get_tool_path("xtensa-esp32-elf/bin".to_string())
));
}
Chip::Esp32s2 => {
install_gcc("xtensa-esp32s2-elf");
exports.push(format!(
"export PATH={}:$PATH",
get_tool_path("xtensa-esp32s2-elf/bin".to_string())
));
}
Chip::Esp32s3 => {
install_gcc("xtensa-esp32s3-elf");
exports.push(format!(
"export PATH={}:$PATH",
get_tool_path("xtensa-esp32s3-elf/bin".to_string())
));
}
Chip::Esp32c3 => {
install_gcc("riscv32-esp-elf");
exports.push(format!(
"export PATH={}:$PATH",
get_tool_path("riscv32-esp-elf/bin".to_string())
));
}
_ => {
println!("Unknown target")
}
}
}
Ok(exports)
}
fn install_gcc(gcc_target: &str) {
let gcc_path = get_tool_path(gcc_target.to_string());
println!("gcc path: {}", gcc_path);
// if Path::new(&gcc_path).exists() {
// println!("Previous installation of GCC for target: {}", gcc_path);
// // return Ok(());
// } else {
// fs::create_dir_all(&gcc_path).unwrap();
let gcc_file = format!(
"{}-gcc8_4_0-esp-2021r2-patch3-{}.tar.gz",
gcc_target,
get_gcc_arch(guess_host_triple::guess_host_triple().unwrap())
);
let gcc_dist_url = format!(
"https://github.com/espressif/crosstool-NG/releases/download/esp-2021r2-patch3/{}",
gcc_file
);
match prepare_package_strip_prefix(&gcc_dist_url, gcc_path, "") {
Ok(_) => {
println!("Package {} ready", gcc_file);
}
Err(_e) => {
println!("Unable to prepare {}", gcc_file);
}
}
// }
}
// TODO: Create test for this function
fn parse_targets(build_target: &str) -> Result<Vec<Chip>> {
println!("Parsing targets: {}", build_target);
let mut chips: Vec<Chip> = Vec::new();
if build_target.contains("all") {
chips.push(Chip::Esp32);
chips.push(Chip::Esp32s2);
chips.push(Chip::Esp32s3);
chips.push(Chip::Esp32c3);
return Ok(chips);
}
let mut targets: Vec<&str>;
if build_target.contains(' ') || build_target.contains(',') {
targets = build_target.split([',', ' ']).collect();
} else {
targets = vec![build_target];
}
for target in targets {
match target {
"esp32" => chips.push(Chip::Esp32),
"esp32s2" => chips.push(Chip::Esp32s2),
"esp32s3" => chips.push(Chip::Esp32s3),
"esp32c3" => chips.push(Chip::Esp32c3),
_ => {
return Err(Box::new(Error::new(
ErrorKind::Other,
format!("Unknown target: {}", target),
)));
}
};
}
Ok(chips)
}
fn parse_llvm_version(llvm_version: &str) -> Result<String> {
let parsed_version = match llvm_version {
"13" => "esp-13.0.0-20211203",
"14" => "esp-14.0.0-20220415",
"15" => "", // TODO: Fill when released
_ => {
return Err(Box::new(Error::new(
ErrorKind::Other,
format!("Unknown LLVM Version: {}", llvm_version),
)));
}
};
Ok(parsed_version.to_string())
}
fn check_rust_installation(nightly_version: &str) {
match std::process::Command::new("rustup")
.args(["toolchain", "list"])
.stdout(Stdio::piped())
.output()
{
Ok(child_output) => {
println!("rustup found.");
let result = String::from_utf8_lossy(&child_output.stdout);
if !result.contains(nightly_version) {
println!("nightly toolchain not found");
install_rust_nightly(nightly_version);
} else {
println!("nightly toolchain found.");
}
}
Err(e) => {
println!("Error: {}", e);
install_rustup();
}
}
}

View File

@ -1,317 +0,0 @@
use anyhow::Context;
use std::fs::File;
use std::io::Cursor;
use std::{path::{Component::Normal, Path, PathBuf}};
use std::{fs, io};
use tar::Archive;
use xz2::read::XzDecoder;
use crate::config::{get_dist_path, get_tool_path};
use flate2::bufread::GzDecoder;
use std::io::BufReader;
use tokio::runtime::Handle;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
pub fn unzip(file_path: String, output_directory: String) -> Result<()> {
let file_name = std::path::Path::new(&file_path);
let file = fs::File::open(&file_name).unwrap();
let mut archive = zip::ZipArchive::new(file).unwrap();
for i in 0..archive.len() {
let mut file = archive.by_index(i).unwrap();
let file_outpath = match file.enclosed_name() {
Some(path) => path.to_owned(),
None => continue,
};
// Add path prefix to extract the file
let mut outpath = std::path::PathBuf::new();
outpath.push(&output_directory);
outpath.push(file_outpath);
{
let comment = file.comment();
if !comment.is_empty() {
println!("File {} comment: {}", i, comment);
}
}
if (&*file.name()).ends_with('/') {
println!("* extracted: \"{}\"", outpath.display());
fs::create_dir_all(&outpath).unwrap();
} else {
println!(
"* extracted: \"{}\" ({} bytes)",
outpath.display(),
file.size()
);
if let Some(p) = outpath.parent() {
if !p.exists() {
fs::create_dir_all(&p).unwrap();
}
}
let mut outfile = fs::File::create(&outpath).unwrap();
io::copy(&mut file, &mut outfile).unwrap();
}
}
Ok(())
}
pub fn unzip_strip_prefix(
file_path: String,
output_directory: String,
strip_prefix: &str,
) -> Result<()> {
let file_name = std::path::Path::new(&file_path);
let file = fs::File::open(&file_name).unwrap();
let mut archive = zip::ZipArchive::new(file).unwrap();
for i in 0..archive.len() {
let mut file = archive.by_index(i).unwrap();
let file_outpath = match file.enclosed_name() {
Some(path) => path.to_owned(),
None => continue,
};
// Add path prefix to extract the file
let mut outpath = std::path::PathBuf::new();
outpath.push(&output_directory);
// Skip files in top level directories which are not under directory with prefix
if !file_outpath.starts_with(strip_prefix) {
println!("* skipped: \"{}\"", file_outpath.display());
continue;
}
let stripped_file_outpath = file_outpath.strip_prefix(strip_prefix).unwrap();
outpath.push(stripped_file_outpath);
{
let comment = file.comment();
if !comment.is_empty() {
println!("File {} comment: {}", i, comment);
}
}
if (&*file.name()).ends_with('/') {
if !Path::new(file.name()).exists() {
println!("* created: \"{}\"", outpath.display());
fs::create_dir_all(&outpath).unwrap();
}
} else {
println!(
"* extracted: \"{}\" ({} bytes)",
outpath.display(),
file.size()
);
if let Some(p) = outpath.parent() {
if !p.exists() {
fs::create_dir_all(&p).unwrap();
}
}
let mut outfile = fs::File::create(&outpath).unwrap();
io::copy(&mut file, &mut outfile).unwrap();
}
}
Ok(())
}
pub fn untar_strip_prefix(
file_path: String,
output_directory: String,
strip_prefix: &str,
) -> Result<()> {
println!(
"Untar: file {} output_dir {} strip_prefix {}",
file_path, output_directory, strip_prefix
);
let tar_xz = File::open(file_path)?;
let tar = XzDecoder::new(tar_xz);
let mut archive = Archive::new(tar);
archive
.entries()?
.filter_map(|e| e.ok())
.map(|mut entry| -> Result<PathBuf> {
let path = entry.path()?.strip_prefix(strip_prefix)?.to_owned();
let full_path = format!("{}/{}", output_directory, path.display().to_string());
entry.unpack(&full_path)?;
Ok(full_path.parse().unwrap())
})
.filter_map(|e| e.ok())
.for_each(|x| println!("> {}", x.display()));
Ok(())
}
async fn fetch_url(url: String, output: String) -> Result<()> {
let response = reqwest::get(&url).await;
match response {
Ok(r) => {
let mut file = std::fs::File::create(output)?;
let mut content = Cursor::new(r.bytes().await?);
std::io::copy(&mut content, &mut file)?;
return Ok(());
}
_ => {
println!("Download of {} failed", url);
// Exit code is 0, there is temporal issue with Windows Installer which does not recover from error exit code
#[cfg(windows)]
std::process::exit(0);
#[cfg(unix)]
std::process::exit(1);
}
};
}
async fn download_file(url: String, output: String) -> Result<()> {
if Path::new(&output).exists() {
println!("Using cached archive: {}", output);
return Ok(());
}
println!("Downloading {} to {}", url, output);
fetch_url(url, output).await
}
pub fn download_package(package_url: String, package_archive: String) -> Result<()> {
let handle = Handle::current().clone();
let th = std::thread::spawn(move || {
handle
.block_on(download_file(package_url, package_archive))
.unwrap();
});
Ok(th.join().unwrap())
}
pub fn prepare_package(
package_url: String,
package_archive: &str,
output_directory: String,
) -> Result<()> {
if Path::new(&output_directory).exists() {
println!("Using cached directory: {}", output_directory);
return Ok(());
}
let dist_path = get_dist_path("");
if !Path::new(&dist_path).exists() {
println!("Creating dist directory: {}", dist_path);
match fs::create_dir_all(&dist_path) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
}
let package_archive = get_dist_path(package_archive);
match download_package(package_url, package_archive.clone()) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
unzip(package_archive, output_directory).unwrap();
Ok(())
}
pub fn prepare_single_binary(
package_url: &str,
binary_name: &str,
output_directory: &str,
) -> String {
let tool_path = get_tool_path(output_directory.to_string());
let binary_path = format!("{}/{}", tool_path, binary_name);
if Path::new(&binary_path).exists() {
println!("Using cached tool: {}", binary_path);
return binary_path;
}
if !Path::new(&tool_path).exists() {
println!("Creating tool directory: {}", tool_path);
match fs::create_dir_all(&tool_path) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
}
match download_package(package_url.to_string(), binary_path.to_string()) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
return binary_path;
}
pub fn prepare_package_strip_prefix(
package_url: &str,
output_directory: String,
strip_prefix: &str,
) -> Result<()> {
println!("prepare_package_strip_prefix:
-pacakge_url: {}
-output dir: {}
-strip_prefix: {}", &package_url, &output_directory, &strip_prefix);
if Path::new(&output_directory).exists() {
println!("Using cached directory: {}", output_directory);
return Ok(());
}
let tools_path = get_tool_path("".to_string());
if !Path::new(&tools_path).exists() {
println!("Creating tools directory: {}", tools_path);
match fs::create_dir_all(&tools_path) {
Ok(_) => {
println!("tools_path created");
}
Err(_e) => {
println!("tools_path creating failed");
}
}
}
let resp = reqwest::blocking::get(package_url).unwrap();
let content_br = BufReader::new(resp);
if package_url.contains(".xz") {
let tarfile = XzDecoder::new(content_br);
let mut archive = Archive::new(tarfile);
archive.unpack(&tools_path)?;
} else {
let tarfile = GzDecoder::new(content_br);
let mut archive = Archive::new(tarfile);
archive.unpack(&tools_path)?;
}
if !strip_prefix.is_empty(){
let extracted_folder = format!("{}{}", &tools_path, strip_prefix);
println!("Renaming: {} to {}", &extracted_folder, &output_directory);
fs::rename(extracted_folder, output_directory)?;
}
Ok(())
}
pub fn remove_package(package_archive: &str, output_directory: &str) -> Result<()> {
if Path::new(package_archive).exists() {
fs::remove_file(package_archive)
.with_context(|| format!("Unable to delete `{}`", package_archive))?;
}
if Path::new(output_directory).exists() {
fs::remove_dir_all(output_directory)
.with_context(|| format!("Unable to delete `{}`", output_directory))?;
}
Ok(())
}

View File

@ -1,179 +0,0 @@
use std::process::Stdio;
use std::io::{Write};
use std::env;
use clap::Arg;
use clap_nested::{Command, Commander, MultiCommand};
#[cfg(windows)]
pub fn run_command(shell: String, arguments: Vec<String>, command: String) -> std::result::Result<(), clap::Error> {
// println!("arguments = {:?}", arguments);
let mut child_process = std::process::Command::new(shell)
.args(arguments)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
{
let child_stdin = child_process.stdin.as_mut().unwrap();
child_stdin.write_all(&*command.into_bytes())?;
// Close stdin to finish and avoid indefinite blocking
drop(child_stdin);
}
let _output = child_process.wait_with_output()?;
// println!("output = {:?}", output);
Ok(())
}
#[cfg(unix)]
pub fn run_command(shell: String, arguments: Vec<String>, command: String) -> std::result::Result<(), clap::Error> {
// Unix - pass command as parameter for initializer
let mut arguments = arguments.clone();
if !command.is_empty() {
arguments.push(command);
}
//println!("arguments = {:?}", arguments);
let mut child_process = std::process::Command::new(shell)
.args(arguments)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
{
}
let output = child_process.wait_with_output()?;
//println!("output = {:?}", output);
Ok(())
}
pub fn wide_null(s: &str) -> Vec<u16> {
s.encode_utf16().chain(Some(0)).collect()
}
#[cfg(windows)]
pub fn set_env_variable(key:&str, value:String) {
use winreg::{enums::HKEY_CURRENT_USER, RegKey};
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
let (env, _) = hkcu.create_subkey("Environment").unwrap(); // create_subkey opens with write permissions
env.set_value(key, &value).unwrap();
// It's necessary to notify applications about update of the environment
// https://stackoverflow.com/questions/19705401/how-to-set-system-environment-variable-in-c/19705691#19705691
let param = wide_null("Environment").as_ptr() as winapi::shared::minwindef::LPARAM;
unsafe {
winapi::um::winuser::SendNotifyMessageW(
winapi::um::winuser::HWND_BROADCAST,
winapi::um::winuser::WM_SETTINGCHANGE,
0,
param
);
}
}
#[cfg(unix)]
pub fn set_env_variable(key:&str, value:&str) {
}
fn append_path(original_path: &str, new_path: &str) -> String {
if original_path.len() == 0 {
return new_path.to_string();
}
if original_path.contains(new_path) {
return original_path.to_string();
}
if original_path.chars().last().unwrap() != ';' {
return format!("{};{};", original_path, new_path);
}
format!("{}{};", original_path, new_path)
}
#[cfg(test)]
mod tests {
use crate::shell::append_path;
#[test]
fn test_append_path() {
assert_eq!(append_path("",""), "");
assert_eq!(append_path("a",""), "a");
assert_eq!(append_path("a","b"), "a;b;");
assert_eq!(append_path("","b"), "b");
assert_eq!(append_path("a;b;","b"), "a;b;");
assert_eq!(append_path("a;c;","b"), "a;c;b;");
}
}
#[cfg(windows)]
pub fn update_env_variable(variable_name: &str, value: &str) {
use winreg::{enums::HKEY_CURRENT_USER, RegKey};
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
let env = hkcu.open_subkey("Environment").unwrap();
let env_path:String = env.get_value(variable_name).unwrap();
let updated_env_path = append_path(env_path.as_str(), value);
set_env_variable(variable_name, updated_env_path);
}
#[cfg(windows)]
pub fn update_env_path(value: &str) {
update_env_variable("PATH", value);
}
#[cfg(unix)]
pub fn update_env_variable(variable_name: &str, value: &str) {
env::set_var(variable_name, value);
}
#[cfg(unix)]
pub fn update_env_path(value: &str) {
let env_path:String = env::var("PATH").unwrap();
let updated_env_path = append_path(env_path.as_str(), value);
env::set_var("PATH", updated_env_path);
}
// pub fn get_cmd<'a>() -> Command<'a, str> {
// Command::new("append")
// .description("Append path to environment variable")
// .options(|app| {
// app.arg(
// Arg::with_name("variable")
// .short("v")
// .long("variable")
// .help("Name of environment variable")
// .takes_value(true),
// )
// .arg(
// Arg::with_name("path")
// .short("p")
// .long("path")
// .help("Path which should be added to the variable")
// .takes_value(true),
// )
// })
// .runner(|_args, matches| {
// let variable_name = matches.value_of("variable").unwrap().to_string();
// let path_value = matches.value_of("path").unwrap().to_string();
// update_env_variable(variable_name.as_str(), path_value.as_str());
// Ok(())
// })
// }
// pub fn get_multi_cmd<'a>() -> MultiCommand<'a, str, str> {
// let multi_cmd: MultiCommand<str, str> = Commander::new()
// .add_cmd(get_cmd())
// .into_cmd("shell")
// // Optionally specify a description
// .description("Detection of Antivirus and handling exception registration.");
// return multi_cmd;
// }

297
src/toolchain.rs Normal file
View File

@ -0,0 +1,297 @@
use std::process::Stdio;
use espflash::Chip;
use crate::utils::*;
use std::path::Path;
pub fn check_rust_installation(nightly_version: &str) {
match std::process::Command::new("rustup")
.args(["toolchain", "list"])
.stdout(Stdio::piped())
.output()
{
Ok(child_output) => {
println!("rustup found.");
let result = String::from_utf8_lossy(&child_output.stdout);
if !result.contains(nightly_version) {
println!("nightly toolchain not found");
install_rust_nightly(nightly_version);
} else {
println!("nightly toolchain found.");
}
}
Err(e) => {
println!("Error: {}", e);
install_rustup();
}
}
}
pub fn install_riscv_target(version: &str){
match std::process::Command::new("rustup")
.arg("component")
.arg("add")
.arg("rust-src")
.arg("--toolchain")
.arg(version)
.stdout(Stdio::piped())
.output()
{
Ok(child_output) => {
let result = String::from_utf8_lossy(&child_output.stdout);
println!("Rust-src for RiscV target installed suscesfully: {}", result);
}
Err(e) => {
println!("Rust-src for RiscV target installation failed: {}", e);
}
}
match std::process::Command::new("rustup")
.arg("target")
.arg("add")
.arg("--toolchain")
.arg(version)
.arg("riscv32imc-unknown-none-elf")
.stdout(Stdio::piped())
.output()
{
Ok(child_output) => {
let result = String::from_utf8_lossy(&child_output.stdout);
println!("RiscV target installed suscesfully: {}", result);
}
Err(e) => {
println!("RiscV target installation failed: {}", e);
}
}
}
pub fn install_rustup() {
#[cfg(windows)]
let rustup_init_path =
prepare_single_binary("https://win.rustup.rs/x86_64", "rustup-init.exe", "rustup");
#[cfg(unix)]
let rustup_init_path = prepare_single_binary("https://sh.rustup.rs/", "rustup-init", "rustup");
println!("rustup stable");
match std::process::Command::new(rustup_init_path)
.arg("--default-toolchain")
.arg("none")
.arg("--profile")
.arg("minimal")
.arg("-y")
.stdout(Stdio::piped())
.output()
{
Ok(child_output) => {
let result = String::from_utf8_lossy(&child_output.stdout);
println!("{}", result);
}
Err(e) => {
println!("Error: {}", e);
}
}
}
pub fn install_rust_nightly(version: &str) {
println!("installing nightly toolchain");
match std::process::Command::new("rustup")
.arg("toolchain")
.arg("install")
.arg(version)
.arg("--profile")
.arg("minimal")
.stdout(Stdio::piped())
.output()
{
Ok(child_output) => {
let result = String::from_utf8_lossy(&child_output.stdout);
println!("Result: {}", result);
}
Err(e) => {
println!("Error: {}", e);
}
}
}
pub fn install_gcc_targets(targets: Vec<Chip>) -> Result<Vec<String>, String> {
let mut exports: Vec<String> = Vec::new();
for target in targets {
match target {
Chip::Esp32 => {
install_gcc("xtensa-esp32-elf");
exports.push(format!(
"export PATH={}:$PATH",
get_tool_path("xtensa-esp32-elf/bin")
));
}
Chip::Esp32s2 => {
install_gcc("xtensa-esp32s2-elf");
exports.push(format!(
"export PATH={}:$PATH",
get_tool_path("xtensa-esp32s2-elf/bin")
));
}
Chip::Esp32s3 => {
install_gcc("xtensa-esp32s3-elf");
exports.push(format!(
"export PATH={}:$PATH",
get_tool_path("xtensa-esp32s3-elf/bin")
));
}
Chip::Esp32c3 => {
install_gcc("riscv32-esp-elf");
exports.push(format!(
"export PATH={}:$PATH",
get_tool_path("riscv32-esp-elf/bin")
));
}
_ => {
println!("Unknown target")
}
}
}
Ok(exports)
}
pub fn install_gcc(gcc_target: &str) {
let gcc_path = get_tool_path(gcc_target);
println!("gcc path: {}", gcc_path);
// if Path::new(&gcc_path).exists() {
// println!("Previous installation of GCC for target: {}", gcc_path);
// // return Ok(());
// } else {
// fs::create_dir_all(&gcc_path).unwrap();
let gcc_file = format!(
"{}-gcc8_4_0-esp-2021r2-patch3-{}.tar.gz",
gcc_target,
get_gcc_arch(guess_host_triple::guess_host_triple().unwrap())
);
let gcc_dist_url = format!(
"https://github.com/espressif/crosstool-NG/releases/download/esp-2021r2-patch3/{}",
gcc_file
);
match prepare_package_strip_prefix(&gcc_dist_url, gcc_path, "") {
Ok(_) => {
println!("Package {} ready", gcc_file);
}
Err(_e) => {
println!("Unable to prepare {}", gcc_file);
}
}
// }
}
pub fn install_espidf(targets: &str, version: String) -> Result<(), String> {
let espidf_path = format!("{}/frameworks/esp-idf", get_espressif_base_path());
println!("ESP-IDF Path: {}", espidf_path);
#[cfg(windows)]
match prepare_package(
"https://dl.espressif.com/dl/idf-git/idf-git-2.30.1-win64.zip".to_string(),
get_dist_path("idf-git-2.30.1-win64.zip").as_str(),
get_tool_path("idf-git/2.30.1".to_string()),
) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
#[cfg(windows)]
match prepare_package(
"https://dl.espressif.com/dl/idf-python/idf-python-3.8.7-embed-win64.zip".to_string(),
get_dist_path("idf-python-3.8.7-embed-win64.zip").as_str(),
get_tool_path("idf-python/3.8.7".to_string()),
) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
#[cfg(windows)]
let git_path = get_tool_path("idf-git/2.30.1/cmd/git.exe".to_string());
#[cfg(unix)]
let git_path = "/usr/bin/git".to_string();
// TODO: See if needed
// update_property("gitPath".to_string(), git_path.clone());
#[cfg(windows)]
let python_path = get_tool_path("idf-python/3.8.7/python.exe".to_string());
#[cfg(unix)]
let python_path = "/usr/bin/python3".to_string();
let virtual_env_path = get_python_env_path("4.4", "3.8");
// TODO: Use any git crate?
if !Path::new(&espidf_path).exists() {
// let clone_command = format!("git clone --shallow-since=2020-01-01 --jobs 8 --recursive git@github.com:espressif/esp-idf.git ");
let mut arguments: Vec<String> = [].to_vec();
arguments.push("clone".to_string());
arguments.push("--jobs".to_string());
arguments.push("8".to_string());
arguments.push("--branch".to_string());
arguments.push(version);
arguments.push("--depth".to_string());
arguments.push("1".to_string());
arguments.push("--shallow-submodules".to_string());
arguments.push("--recursive".to_string());
arguments.push("https://github.com/espressif/esp-idf.git".to_string());
// arguments.push("git@github.com:espressif/esp-idf.git".to_string());
arguments.push(espidf_path.clone());
println!("Cloning: {} {:?}", git_path, arguments);
match run_command(git_path, arguments, "".to_string()) {
Ok(_) => {
println!("Cloned esp-idf suscessfuly");
}
Err(_e) => {
println!("Cloned esp-idf failed");
}
}
}
println!("Installing esp-idf for {} with {}/install.sh", targets, espidf_path);
let install_script_path = format!("{}/install.sh", espidf_path);
let mut arguments: Vec<String> = [].to_vec();
arguments.push(targets.to_string());
match run_command(install_script_path, arguments, "".to_string()) {
Ok(_) => {
println!("ESP-IDF installation succeeded");
}
Err(_e) => {
println!("ESP-IDF installation failed");
}
}
// match std::process::Command::new(install_script_path)
// .arg("esp32 esp32s2")
// .stdout(Stdio::piped())
// .output()
// {
// Ok(child_output) => {
// let result = String::from_utf8_lossy(&child_output.stdout);
// println!("ESP-IDF installation succeeded: {}", result);
// }
// Err(e) => {
// println!("ESP-IDF installation failed: {}", e);
// }
// }
println!("Installing CMake");
let mut arguments: Vec<String> = [].to_vec();
let mut idf_tools_scritp_path = format!("{}/tools/idf_tools.py", espidf_path);
arguments.push(idf_tools_scritp_path);
arguments.push("install".to_string());
arguments.push("cmake".to_string());
match run_command(python_path, arguments, "".to_string()) {
Ok(_) => {
println!("CMake installation succeeded");
}
Err(_e) => {
println!("CMake installation failed");
}
}
Ok(())
}

296
src/utils.rs Normal file
View File

@ -0,0 +1,296 @@
use espflash::Chip;
use std::path::Path;
use dirs::home_dir;
use std::env;
use std::fs;
use tar::Archive;
use flate2::bufread::GzDecoder;
use xz2::read::XzDecoder;
use std::io::BufReader;
use std::process::Stdio;
use tokio::runtime::Handle;
use std::io::Cursor;
// TODO: Create test for this function
pub fn parse_targets(build_target: &str) -> Result<Vec<Chip>, String> {
println!("Parsing targets: {}", build_target);
let mut chips: Vec<Chip> = Vec::new();
if build_target.contains("all") {
chips.push(Chip::Esp32);
chips.push(Chip::Esp32s2);
chips.push(Chip::Esp32s3);
chips.push(Chip::Esp32c3);
return Ok(chips);
}
let mut targets: Vec<&str>;
if build_target.contains(' ') || build_target.contains(',') {
targets = build_target.split([',', ' ']).collect();
} else {
targets = vec![build_target];
}
for target in targets {
match target {
"esp32" => chips.push(Chip::Esp32),
"esp32s2" => chips.push(Chip::Esp32s2),
"esp32s3" => chips.push(Chip::Esp32s3),
"esp32c3" => chips.push(Chip::Esp32c3),
_ => {
return Err(
format!("Unknown target: {}", target),
);
}
};
}
Ok(chips)
}
pub fn parse_llvm_version(llvm_version: &str) -> Result<String, String> {
let parsed_version = match llvm_version {
"13" => "esp-13.0.0-20211203",
"14" => "esp-14.0.0-20220415",
"15" => "", // TODO: Fill when released
_ => {
return Err(
format!("Unknown LLVM Version: {}", llvm_version),
);
}
};
Ok(parsed_version.to_string())
}
pub fn get_llvm_version_with_underscores(llvm_version: &str) -> String {
let version: Vec<&str> = llvm_version.split("-").collect();
let llvm_dot_version = version[1];
llvm_dot_version.replace(".", "_")
}
pub fn get_artifact_file_extension(arch: &str) -> &str {
match arch {
"x86_64-pc-windows-msvc" => "zip",
"x86_64-pc-windows-gnu" => "zip",
_ => "tar.xz",
}
}
pub fn get_llvm_arch(arch: &str) -> &str {
match arch {
"aarch64-apple-darwin" => "macos",
"x86_64-apple-darwin" => "macos",
"x86_64-unknown-linux-gnu" => "linux-amd64",
"x86_64-pc-windows-msvc" => "win64",
"x86_64-pc-windows-gnu" => "win64",
_ => arch,
}
}
pub fn get_gcc_arch(arch: &str) -> &str {
match arch {
"aarch64-apple-darwin" => "macos",
"aarch64-unknown-linux-gnu" => "linux-arm64",
"x86_64-apple-darwin" => "macos",
"x86_64-unknown-linux-gnu" => "linux-amd64",
"x86_64-pc-windows-msvc" => "win64",
"x86_64-pc-windows-gnu" => "win64",
_ => arch,
}
}
pub fn get_rust_installer(arch: &str) -> &str {
match arch {
"x86_64-pc-windows-msvc" => "",
"x86_64-pc-windows-gnu" => "",
_ => "./install.sh",
}
}
pub fn get_espressif_base_path() -> String {
env::var("IDF_TOOLS_PATH").unwrap_or_else(|_e|
home_dir().unwrap().display().to_string() + "/.espressif/"
)
}
pub fn get_tool_path(tool_name: &str) -> String {
format!("{}/tools/{}", get_espressif_base_path(), tool_name)
}
pub fn prepare_package_strip_prefix(
package_url: &str,
output_directory: String,
strip_prefix: &str,
) -> Result<(), String> {
println!("prepare_package_strip_prefix:
-pacakge_url: {}
-output dir: {}
-strip_prefix: {}", &package_url, &output_directory, &strip_prefix);
if Path::new(&output_directory).exists() {
println!("Using cached directory: {}", output_directory);
return Ok(());
}
let tools_path = get_tool_path("");
if !Path::new(&tools_path).exists() {
println!("Creating tools directory: {}", tools_path);
match fs::create_dir_all(&tools_path) {
Ok(_) => {
println!("tools_path created");
}
Err(_e) => {
println!("tools_path creating failed");
}
}
}
let resp = reqwest::blocking::get(package_url).unwrap();
let content_br = BufReader::new(resp);
if package_url.contains(".xz") {
let tarfile = XzDecoder::new(content_br);
let mut archive = Archive::new(tarfile);
archive.unpack(&tools_path).unwrap();
} else {
let tarfile = GzDecoder::new(content_br);
let mut archive = Archive::new(tarfile);
archive.unpack(&tools_path).unwrap();
}
if !strip_prefix.is_empty(){
let extracted_folder = format!("{}{}", &tools_path, strip_prefix);
println!("Renaming: {} to {}", &extracted_folder, &output_directory);
fs::rename(extracted_folder, output_directory).unwrap();
}
Ok(())
}
#[cfg(windows)]
pub fn run_command(shell: String, arguments: Vec<String>, command: String) -> std::result::Result<(), clap::Error> {
// println!("arguments = {:?}", arguments);
let mut child_process = std::process::Command::new(shell)
.args(arguments)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
{
let child_stdin = child_process.stdin.as_mut().unwrap();
child_stdin.write_all(&*command.into_bytes())?;
// Close stdin to finish and avoid indefinite blocking
drop(child_stdin);
}
let _output = child_process.wait_with_output()?;
// println!("output = {:?}", output);
Ok(())
}
#[cfg(unix)]
pub fn run_command(shell: String, arguments: Vec<String>, command: String) -> std::result::Result<(), clap::Error> {
// Unix - pass command as parameter for initializer
let mut arguments = arguments.clone();
if !command.is_empty() {
arguments.push(command);
}
//println!("arguments = {:?}", arguments);
let mut child_process = std::process::Command::new(shell)
.args(arguments)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
{
}
let output = child_process.wait_with_output()?;
//println!("output = {:?}", output);
Ok(())
}
pub fn prepare_single_binary(
package_url: &str,
binary_name: &str,
output_directory: &str,
) -> String {
let tool_path = get_tool_path(output_directory);
let binary_path = format!("{}/{}", tool_path, binary_name);
if Path::new(&binary_path).exists() {
println!("Using cached tool: {}", binary_path);
return binary_path;
}
if !Path::new(&tool_path).exists() {
println!("Creating tool directory: {}", tool_path);
match fs::create_dir_all(&tool_path) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
}
match download_package(package_url.to_string(), binary_path.to_string()) {
Ok(_) => {
println!("Ok");
}
Err(_e) => {
println!("Failed");
}
}
return binary_path;
}
pub fn get_python_env_path(idf_version: &str, python_version: &str) -> String {
let tools_path = get_espressif_base_path();
format!("{}/python_env/idf{}_py{}_env", tools_path, idf_version, python_version)
}
pub fn download_package(package_url: String, package_archive: String) -> Result<(), String> {
let handle = Handle::current().clone();
let th = std::thread::spawn(move || {
handle
.block_on(download_file(package_url.to_string(), package_archive.to_string()))
.unwrap();
});
Ok(th.join().unwrap())
}
async fn download_file(url: String, output: String) -> Result<(), String> {
if Path::new(&output).exists() {
println!("Using cached archive: {}", output);
return Ok(());
}
println!("Downloading {} to {}", url, output);
fetch_url(url, output).await
}
async fn fetch_url(url: String, output: String) -> Result<(), String> {
let response = reqwest::get(&url).await;
match response {
Ok(r) => {
let mut file = std::fs::File::create(output).unwrap();
let mut content = Cursor::new(r.bytes().await.unwrap());
std::io::copy(&mut content, &mut file).unwrap();
return Ok(());
}
_ => {
println!("Download of {} failed", url);
// Exit code is 0, there is temporal issue with Windows Installer which does not recover from error exit code
#[cfg(windows)]
std::process::exit(0);
#[cfg(unix)]
std::process::exit(1);
}
};
}