Merge pull request #31 from esp-rs/feature/config-file

Add configuration file
This commit is contained in:
Sergio Gasquez Arcos 2022-10-26 16:52:59 +02:00 committed by GitHub
commit b2ca668652
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 264 additions and 77 deletions

66
Cargo.lock generated
View File

@ -297,6 +297,16 @@ dependencies = [
"subtle",
]
[[package]]
name = "directories-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc"
dependencies = [
"cfg-if",
"dirs-sys-next",
]
[[package]]
name = "dirs"
version = "4.0.0"
@ -317,6 +327,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "either"
version = "1.8.0"
@ -400,18 +421,23 @@ dependencies = [
"anyhow",
"clap",
"console",
"directories-next",
"dirs",
"embuild",
"env_logger",
"flate2",
"guess_host_triple",
"log",
"miette",
"openssl",
"regex",
"reqwest",
"serde",
"strum",
"strum_macros",
"tar",
"tempfile",
"toml",
"xz2",
"zip",
]
@ -790,6 +816,29 @@ dependencies = [
"autocfg",
]
[[package]]
name = "miette"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28d6092d7e94a90bb9ea8e6c26c99d5d112d49dda2afdb4f7ea8cf09e1a5a6d"
dependencies = [
"miette-derive",
"once_cell",
"thiserror",
"unicode-width",
]
[[package]]
name = "miette-derive"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f2485ed7d1fe80704928e3eb86387439609bd0c6bb96db8208daa364cfd1e09"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "mime"
version = "0.3.16"
@ -1195,18 +1244,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.145"
version = "1.0.146"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
checksum = "6df50b7a60a0ad48e1b42eb38373eac8ff785d619fb14db917b4e63d5439361f"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.145"
version = "1.0.146"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
checksum = "a714fd32ba1d66047ce7d53dabd809e9922d538f9047de13cc4cffca47b36205"
dependencies = [
"proc-macro2",
"quote",
@ -1466,6 +1515,15 @@ dependencies = [
"tracing",
]
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]]
name = "tower-service"
version = "0.3.2"

View File

@ -11,6 +11,7 @@ Tool for installing and maintaining ESP Rust environment.
"""
keywords = ["esp", "esp-rs", "embedded", "cli", "xtensa", "espidf"]
categories = ["command-line-utilities", "development-tools", "embedded"]
rust-version = "1.62"
[dependencies]
anyhow = "*"
@ -29,6 +30,11 @@ env_logger = "0.9.0"
embuild = { version = "0.30.4", features = ["espidf", "git"] }
strum = { version = "0.24", features = ["derive"] }
strum_macros = "0.24.3"
toml = "0.5.9"
directories-next = "2.0.0"
serde = { version = "1.0.146", features = ["derive"] }
miette = "5.3.0"
regex = "1.6.0"
[target.aarch64-unknown-linux-gnu.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
@ -39,5 +45,5 @@ bin-dir = "{ bin }{ binary-ext }"
pkg-fmt = "zip"
[profile.release]
lto = "thin"
lto = "thin"
strip = true

61
src/config.rs Normal file
View File

@ -0,0 +1,61 @@
use crate::{host_triple::HostTriple, targets::Target, toolchain::rust_toolchain::RustToolchain};
use directories_next::ProjectDirs;
use miette::{ErrReport, IntoDiagnostic, Result, WrapErr};
use serde::{Deserialize, Serialize};
use std::{
collections::HashSet,
fs::{create_dir_all, read, write},
path::PathBuf,
};
/// Deserialized contents of a configuration file
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
pub struct Config {
// /// ESP-IDF version
pub espidf_version: Option<String>,
/// Destination of the generated export file.
pub export_file: PathBuf,
/// Extra crates to installed.
pub extra_crates: HashSet<String>,
/// Host triple
pub host_triple: HostTriple,
/// LLVM toolchain path.
pub llvm_path: PathBuf,
/// Nightly Rust toolchain version.
pub nightly_version: String,
/// List of targets instaled.
pub targets: HashSet<Target>,
/// Xtensa Rust toolchain.
pub xtensa_toolchain: RustToolchain,
}
impl Config {
/// Load the config from config file
pub fn load() -> Result<Self> {
let dirs = ProjectDirs::from("rs", "esp", "espup").unwrap();
let file = dirs.config_dir().join("espup.toml");
let config = if let Ok(data) = read(&file) {
toml::from_slice(&data).into_diagnostic()?
} else {
return Err(ErrReport::msg("No config file found"));
};
Ok(config)
}
/// Save the config to file
pub fn save(&self) -> Result<()> {
let dirs = ProjectDirs::from("rs", "esp", "espup").unwrap();
let file = dirs.config_dir().join("espup.toml");
let serialized = toml::to_string(&self.clone())
.into_diagnostic()
.wrap_err("Failed to serialize config")?;
create_dir_all(file.parent().unwrap())
.into_diagnostic()
.wrap_err("Failed to create config directory")?;
write(&file, serialized)
.into_diagnostic()
.wrap_err_with(|| format!("Failed to write config to {}", file.display()))
}
}

View File

@ -1,15 +1,17 @@
use crate::emoji;
use anyhow::{Context, Result};
use guess_host_triple::guess_host_triple;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use strum::Display;
use strum_macros::EnumString;
#[derive(Display, Debug, Clone, EnumString)]
#[derive(Display, Debug, Clone, EnumString, Deserialize, Serialize, Default)]
pub enum HostTriple {
/// 64-bit Linux
#[strum(serialize = "x86_64-unknown-linux-gnu")]
X86_64UnknownLinuxGnu = 0,
#[default]
X86_64UnknownLinuxGnu,
/// ARM64 Linux
#[strum(serialize = "aarch64-unknown-linux-gnu")]
Aarch64UnknownLinuxGnu,

View File

@ -1,3 +1,4 @@
pub mod config;
pub mod emoji;
pub mod host_triple;
pub mod targets;

View File

@ -1,9 +1,12 @@
#[cfg(windows)]
use anyhow::bail;
use anyhow::Result;
use anyhow::{bail, Result};
use clap::Parser;
use embuild::espidf::{parse_esp_idf_git_ref, EspIdfRemote};
use directories_next::ProjectDirs;
use embuild::{
cmd,
espidf::{parse_esp_idf_git_ref, EspIdfRemote},
};
use espup::{
config::Config,
emoji,
host_triple::get_host_triple,
logging::initialize_logger,
@ -12,26 +15,29 @@ use espup::{
espidf::{
get_dist_path, get_install_path, get_tool_path, EspIdfRepo, DEFAULT_GIT_REPOSITORY,
},
gcc_toolchain::install_gcc_targets,
gcc_toolchain::{get_toolchain_name, install_gcc_targets},
llvm_toolchain::LlvmToolchain,
rust_toolchain::{
check_rust_installation, get_rustup_home, install_riscv_target, RustCrate,
RustToolchain,
},
rust_toolchain::{check_rust_installation, install_riscv_target, RustCrate, RustToolchain},
},
};
use log::{debug, info, warn};
use regex::Regex;
use std::{
collections::HashSet,
fs::{remove_dir_all, File},
fs::{remove_dir_all, remove_file, File},
io::Write,
path::PathBuf,
path::{Path, PathBuf},
};
#[cfg(windows)]
const DEFAULT_EXPORT_FILE: &str = "export-esp.ps1";
#[cfg(not(windows))]
const DEFAULT_EXPORT_FILE: &str = "export-esp.sh";
/// Xtensa Toolchain version regex.
const RE_TOOLCHAIN_VERSION: &str = r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)\.(?P<subpatch>0|[1-9]\d*)?$";
/// Latest Xtensa Toolchain version.
const LATEST_TOOLCHAIN_VERSION: &str = "1.64.0.0";
#[derive(Parser)]
#[command(
name = "espup",
@ -93,7 +99,7 @@ pub struct InstallOpts {
#[arg(short = 't', long, default_value = "all")]
pub targets: String,
/// Xtensa Rust toolchain version.
#[arg(short = 'v', long, default_value = "1.64.0.0")]
#[arg(short = 'v', long, default_value = LATEST_TOOLCHAIN_VERSION, value_parser = parse_version)]
pub toolchain_version: String,
}
@ -106,34 +112,30 @@ pub struct UpdateOpts {
#[arg(short = 'l', long, default_value = "info", value_parser = ["debug", "info", "warn", "error"])]
pub log_level: String,
/// Xtensa Rust toolchain version.
#[arg(short = 'v', long, default_value = "1.64.0.0")]
pub toolchain_version: String,
#[arg(short = 'v', long, default_value = LATEST_TOOLCHAIN_VERSION, value_parser = parse_version)]
pub toolchain_version: Option<String>,
}
#[derive(Debug, Parser)]
pub struct UninstallOpts {
/// ESP-IDF version to uninstall. If empty, no esp-idf is uninstalled. Version format:
///
/// - `commit:<hash>`: Uses the commit `<hash>` of the `esp-idf` repository.
///
/// - `tag:<tag>`: Uses the tag `<tag>` of the `esp-idf` repository.
///
/// - `branch:<branch>`: Uses the branch `<branch>` of the `esp-idf` repository.
///
/// - `v<major>.<minor>` or `<major>.<minor>`: Uses the tag `v<major>.<minor>` of the `esp-idf` repository.
///
/// - `<branch>`: Uses the branch `<branch>` of the `esp-idf` repository.
#[arg(short = 'e', long, required = false)]
pub espidf_version: Option<String>,
/// Verbosity level of the logs.
#[arg(short = 'l', long, default_value = "info", value_parser = ["debug", "info", "warn", "error"])]
pub log_level: String,
/// Removes clang.
#[arg(short = 'c', long)]
pub remove_clang: bool,
}
/// Installs esp-rs environment
/// Parses the version of the Xtensa toolchain.
fn parse_version(arg: &str) -> Result<String> {
let re = Regex::new(RE_TOOLCHAIN_VERSION).unwrap();
if !re.is_match(arg) {
bail!(
"{} Invalid toolchain version, must be in the form of '<major>.<minor>.<patch>.<subpatch>'",
emoji::ERROR
);
}
Ok(arg.to_string())
}
/// Installs the Rust for ESP chips environment
fn install(args: InstallOpts) -> Result<()> {
initialize_logger(&args.log_level);
@ -145,7 +147,6 @@ fn install(args: InstallOpts) -> Result<()> {
let mut exports: Vec<String> = Vec::new();
let export_file = args.export_file.clone();
let rust_toolchain = RustToolchain::new(&args.toolchain_version, &host_triple);
// Complete LLVM is failing for Windows, aarch64 MacOs, and aarch64 Linux, so we are using always minified.
#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
let llvm = LlvmToolchain::new(args.profile_minimal, &host_triple);
@ -169,7 +170,7 @@ fn install(args: InstallOpts) -> Result<()> {
targets,
&args.espidf_version,
export_file,
extra_crates,
&extra_crates,
llvm,
&args.nightly_version,
rust_toolchain,
@ -191,11 +192,11 @@ fn install(args: InstallOpts) -> Result<()> {
}
if let Some(espidf_version) = &args.espidf_version {
let repo = EspIdfRepo::new(espidf_version, args.profile_minimal, targets);
let repo = EspIdfRepo::new(espidf_version, args.profile_minimal, &targets);
exports.extend(repo.install()?);
extra_crates.insert(RustCrate::new("ldproxy"));
} else {
exports.extend(install_gcc_targets(targets, &host_triple)?);
exports.extend(install_gcc_targets(&targets, &host_triple)?);
}
debug!(
@ -203,7 +204,7 @@ fn install(args: InstallOpts) -> Result<()> {
emoji::DEBUG,
extra_crates
);
for extra_crate in extra_crates {
for extra_crate in &extra_crates {
extra_crate.install()?;
}
@ -213,6 +214,24 @@ fn install(args: InstallOpts) -> Result<()> {
export_environment(&export_file, &exports)?;
let config = Config {
espidf_version: args.espidf_version,
export_file,
extra_crates: extra_crates
.iter()
.map(|x| x.name.clone())
.collect::<HashSet<String>>(),
host_triple,
llvm_path: llvm.path,
nightly_version: args.nightly_version,
targets,
xtensa_toolchain: rust_toolchain,
};
if let Err(e) = config.save() {
bail!("{} Failed to save config {:#}", emoji::ERROR, e);
}
info!("{} Installation suscesfully completed!", emoji::CHECK);
warn!(
"{} Please, source the export file, as state above, to properly setup the environment!",
@ -221,64 +240,102 @@ fn install(args: InstallOpts) -> Result<()> {
Ok(())
}
/// Uninstalls esp-rs environment
/// Uninstalls the Rust for ESP chips environment
fn uninstall(args: UninstallOpts) -> Result<()> {
initialize_logger(&args.log_level);
info!("{} Uninstalling esp-rs", emoji::DISC);
let config = Config::load().unwrap();
debug!(
"{} Arguments:
- Remove Clang: {}
- ESP-IDF version: {:#?}",
- Config: {:#?}",
emoji::INFO,
&args.remove_clang,
&args.espidf_version,
config
);
info!("{} Deleting Xtensa Rust toolchain", emoji::WRENCH);
remove_dir_all(get_rustup_home().join("toolchains").join("esp"))?;
remove_dir_all(config.xtensa_toolchain.toolchain_destination)?;
if args.remove_clang {
info!("{} Deleting Xtensa Clang", emoji::WRENCH);
remove_dir_all(PathBuf::from(get_tool_path("")).join("xtensa-esp32-elf-clang"))?;
info!("{} Deleting Xtensa LLVM", emoji::WRENCH);
remove_dir_all(config.llvm_path)?;
if let Some(espidf_version) = config.espidf_version {
info!("{} Deleting ESP-IDF {}", emoji::WRENCH, espidf_version);
let repo = EspIdfRemote {
git_ref: parse_esp_idf_git_ref(&espidf_version),
repo_url: Some(DEFAULT_GIT_REPOSITORY.to_string()),
};
remove_dir_all(get_install_path(repo).parent().unwrap())?;
} else {
info!("{} Deleting GCC targets", emoji::WRENCH);
for target in &config.targets {
let gcc_path = get_tool_path(&get_toolchain_name(target));
remove_dir_all(gcc_path)?;
}
}
info!("{} Uninstalling extra crates", emoji::WRENCH);
for extra_crate in &config.extra_crates {
cmd!("cargo", "uninstall", extra_crate).run()?;
}
clear_dist_folder()?;
if let Some(espidf_version) = &args.espidf_version {
info!("{} Deleting ESP-IDF", emoji::WRENCH);
let repo = EspIdfRemote {
git_ref: parse_esp_idf_git_ref(espidf_version),
repo_url: Some(DEFAULT_GIT_REPOSITORY.to_string()),
};
remove_dir_all(get_install_path(repo).parent().unwrap())?;
}
info!("{} Deleting export file", emoji::WRENCH);
remove_file(Path::new(&config.export_file))?;
info!("{} Deleting config file", emoji::WRENCH);
let conf_dirs = ProjectDirs::from("rs", "esp", "espup").unwrap();
let conf_file = conf_dirs.config_dir().join("espup.toml");
remove_file(conf_file)?;
info!("{} Uninstallation suscesfully completed!", emoji::CHECK);
Ok(())
}
/// Updates Xtensa Rust toolchain
/// Updates Xtensa Rust toolchain.
fn update(args: UpdateOpts) -> Result<()> {
initialize_logger(&args.log_level);
info!("{} Updating Xtensa Rust toolchain", emoji::DISC);
let host_triple = get_host_triple(args.default_host)?;
let mut config = Config::load().unwrap();
let rust_toolchain: RustToolchain;
if let Some(toolchain_version) = args.toolchain_version {
rust_toolchain = RustToolchain::new(&toolchain_version, &host_triple);
} else {
rust_toolchain = RustToolchain::new(LATEST_TOOLCHAIN_VERSION, &host_triple);
}
debug!(
"{} Arguments:
- Host triple: {}
- Toolchain version: {}",
- Toolchain version: {:#?}
- Config: {:#?}",
emoji::INFO,
host_triple,
&args.toolchain_version,
rust_toolchain,
config
);
if rust_toolchain.version == config.xtensa_toolchain.version {
info!(
"{} Toolchain '{}' is already up to date",
emoji::CHECK,
rust_toolchain.version
);
return Ok(());
}
info!("{} Deleting previous Xtensa Rust toolchain", emoji::WRENCH);
remove_dir_all(get_rustup_home().join("toolchains").join("esp"))?;
remove_dir_all(&config.xtensa_toolchain.toolchain_destination)?;
let rust_toolchain = RustToolchain::new(&args.toolchain_version, &host_triple);
rust_toolchain.install_xtensa_rust()?;
config.xtensa_toolchain = rust_toolchain;
if let Err(e) = config.save() {
bail!("{} Failed to save config {:#}", emoji::ERROR, e);
}
info!("{} Update suscesfully completed!", emoji::CHECK);
Ok(())
}

View File

@ -3,11 +3,12 @@
use crate::emoji;
use anyhow::Context;
use log::debug;
use serde::{Deserialize, Serialize};
use std::{collections::HashSet, str::FromStr};
use strum::Display;
use strum_macros::EnumString;
#[derive(Clone, Copy, EnumString, PartialEq, Hash, Eq, Debug, Display)]
#[derive(Clone, Copy, EnumString, PartialEq, Hash, Eq, Debug, Display, Deserialize, Serialize)]
pub enum Target {
/// Xtensa LX7 based dual core
#[strum(serialize = "esp32")]

View File

@ -48,7 +48,7 @@ pub enum Generator {
NMakeMakefilesJOM,
WatcomWMake,
}
#[derive(Debug)]
#[derive(Debug, Clone, Default)]
pub struct EspIdfRepo {
/// The repository containing GCC sources.
pub repository_url: String,
@ -82,7 +82,7 @@ impl EspIdfRepo {
let mut tools = vec![];
let mut subtools = Vec::new();
for target in self.targets {
let gcc_toolchain_name = get_toolchain_name(target);
let gcc_toolchain_name = get_toolchain_name(&target);
subtools.push(gcc_toolchain_name);
let ulp_toolchain_name = get_ulp_toolchain_name(target, version.as_ref().ok());
@ -163,7 +163,7 @@ impl EspIdfRepo {
}
/// Create a new instance with the proper arguments.
pub fn new(version: &str, minified: bool, targets: HashSet<Target>) -> EspIdfRepo {
pub fn new(version: &str, minified: bool, targets: &HashSet<Target>) -> EspIdfRepo {
let install_path = PathBuf::from(get_tools_path());
debug!(
"{} ESP-IDF install path: '{}'",
@ -175,7 +175,7 @@ impl EspIdfRepo {
version: version.to_string(),
minified,
install_path,
targets,
targets: targets.clone(),
}
}
}

View File

@ -18,7 +18,7 @@ const DEFAULT_GCC_REPOSITORY: &str = "https://github.com/espressif/crosstool-NG/
const DEFAULT_GCC_RELEASE: &str = "esp-2021r2-patch5";
const DEFAULT_GCC_VERSION: &str = "8_4_0";
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct GccToolchain {
/// Host triple.
pub host_triple: HostTriple,
@ -75,7 +75,7 @@ impl GccToolchain {
}
/// Create a new instance with default values and proper toolchain name.
pub fn new(target: Target, host_triple: &HostTriple) -> Self {
pub fn new(target: &Target, host_triple: &HostTriple) -> Self {
Self {
host_triple: host_triple.clone(),
release: DEFAULT_GCC_RELEASE.to_string(),
@ -105,7 +105,7 @@ fn get_artifact_extension(host_triple: &HostTriple) -> &str {
}
/// Gets the toolchain name based on the Target
pub fn get_toolchain_name(target: Target) -> String {
pub fn get_toolchain_name(target: &Target) -> String {
match target {
Target::ESP32 => "xtensa-esp32-elf".to_string(),
Target::ESP32S2 => "xtensa-esp32s2-elf".to_string(),
@ -138,7 +138,7 @@ pub fn get_ulp_toolchain_name(target: Target, version: Option<&EspIdfVersion>) -
/// Installs GCC toolchain the selected targets.
pub fn install_gcc_targets(
targets: HashSet<Target>,
targets: &HashSet<Target>,
host_triple: &HostTriple,
) -> Result<Vec<String>> {
info!("{} Installing gcc for build targets", emoji::WRENCH);

View File

@ -15,7 +15,7 @@ const DEFAULT_LLVM_MINIFIED_REPOSITORY: &str =
"https://github.com/esp-rs/rust-build/releases/download/llvm-project-14.0-minified";
const DEFAULT_LLVM_VERSION: &str = "esp-14.0.0-20220415";
#[derive(Debug)]
#[derive(Debug, Clone, Default)]
pub struct LlvmToolchain {
/// LLVM Toolchain file name.
pub file_name: String,

View File

@ -8,13 +8,14 @@ use crate::{
use anyhow::{bail, Result};
use embuild::cmd;
use log::{info, warn};
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::{env, path::PathBuf, process::Stdio};
const DEFAULT_XTENSA_RUST_REPOSITORY: &str =
"https://github.com/esp-rs/rust-build/releases/download";
#[derive(Debug)]
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct RustToolchain {
/// Path to the cargo home directory.
pub cargo_home: PathBuf,
@ -146,7 +147,7 @@ impl RustToolchain {
}
}
#[derive(Hash, Eq, PartialEq, Debug)]
#[derive(Hash, Eq, PartialEq, Debug, Clone, Serialize, Deserialize, Default)]
pub struct RustCrate {
/// Crate name.
pub name: String,