Merge branch 'main' into feature/doc-update

This commit is contained in:
Sergio Gasquez Arcos 2022-10-27 10:34:50 +02:00 committed by GitHub
commit f3bdc714b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 477 additions and 273 deletions

103
Cargo.lock generated
View File

@ -31,9 +31,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.65" version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
[[package]] [[package]]
name = "atty" name = "atty"
@ -144,9 +144,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.0.13" version = "4.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69d64e88428747154bd8bc378d178377ef4dace7a5735ca1f3855be72f2c2cb5" checksum = "335867764ed2de42325fafe6d18b8af74ba97ee0c590fa016f157535b42ab04b"
dependencies = [ dependencies = [
"atty", "atty",
"bitflags", "bitflags",
@ -159,9 +159,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.0.13" version = "4.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f169caba89a7d512b5418b09864543eeb4d497416c917d7137863bd2076ad" checksum = "16a1b0f6422af32d5da0c58e2703320f379216ee70198241c84173a8c5ac28f3"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error", "proc-macro-error",
@ -297,6 +297,16 @@ dependencies = [
"subtle", "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]] [[package]]
name = "dirs" name = "dirs"
version = "4.0.0" version = "4.0.0"
@ -317,6 +327,17 @@ dependencies = [
"winapi", "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]] [[package]]
name = "either" name = "either"
version = "1.8.0" version = "1.8.0"
@ -400,17 +421,23 @@ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
"console", "console",
"directories-next",
"dirs", "dirs",
"embuild", "embuild",
"env_logger", "env_logger",
"flate2", "flate2",
"guess_host_triple", "guess_host_triple",
"log", "log",
"miette",
"openssl", "openssl",
"regex",
"reqwest", "reqwest",
"serde",
"strum", "strum",
"strum_macros",
"tar", "tar",
"tempfile", "tempfile",
"toml",
"xz2", "xz2",
"zip", "zip",
] ]
@ -789,6 +816,29 @@ dependencies = [
"autocfg", "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]] [[package]]
name = "mime" name = "mime"
version = "0.3.16" version = "0.3.16"
@ -928,9 +978,9 @@ checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
[[package]] [[package]]
name = "password-hash" name = "password-hash"
version = "0.3.2" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
dependencies = [ dependencies = [
"base64ct", "base64ct",
"rand_core", "rand_core",
@ -939,9 +989,9 @@ dependencies = [
[[package]] [[package]]
name = "pbkdf2" name = "pbkdf2"
version = "0.10.1" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
dependencies = [ dependencies = [
"digest", "digest",
"hmac", "hmac",
@ -1194,18 +1244,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.145" version = "1.0.146"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" checksum = "6df50b7a60a0ad48e1b42eb38373eac8ff785d619fb14db917b4e63d5439361f"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.145" version = "1.0.146"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" checksum = "a714fd32ba1d66047ce7d53dabd809e9922d538f9047de13cc4cffca47b36205"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1465,6 +1515,15 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.2" version = "0.3.2"
@ -1759,9 +1818,9 @@ dependencies = [
[[package]] [[package]]
name = "zip" name = "zip"
version = "0.6.2" version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf225bcf73bb52cbb496e70475c7bd7a3f769df699c0020f6c7bd9a96dcf0b8d" checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080"
dependencies = [ dependencies = [
"aes", "aes",
"byteorder", "byteorder",
@ -1779,18 +1838,18 @@ dependencies = [
[[package]] [[package]]
name = "zstd" name = "zstd"
version = "0.10.2+zstd.1.5.2" version = "0.11.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f4a6bd64f22b5e3e94b4e238669ff9f10815c27a5180108b849d24174a83847" checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
dependencies = [ dependencies = [
"zstd-safe", "zstd-safe",
] ]
[[package]] [[package]]
name = "zstd-safe" name = "zstd-safe"
version = "4.1.6+zstd.1.5.2" version = "5.0.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94b61c51bb270702d6167b8ce67340d2754b088d0c091b06e593aa772c3ee9bb" checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db"
dependencies = [ dependencies = [
"libc", "libc",
"zstd-sys", "zstd-sys",
@ -1798,9 +1857,9 @@ dependencies = [
[[package]] [[package]]
name = "zstd-sys" name = "zstd-sys"
version = "1.6.3+zstd.1.5.2" version = "2.0.1+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc49afa5c8d634e75761feda8c592051e7eeb4683ba827211eb0d731d3402ea8" checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",

View File

@ -11,10 +11,11 @@ Tool for installing and maintaining ESP Rust environment.
""" """
keywords = ["esp", "esp-rs", "embedded", "cli", "xtensa", "espidf"] keywords = ["esp", "esp-rs", "embedded", "cli", "xtensa", "espidf"]
categories = ["command-line-utilities", "development-tools", "embedded"] categories = ["command-line-utilities", "development-tools", "embedded"]
rust-version = "1.62"
[dependencies] [dependencies]
anyhow = "*" anyhow = "*"
clap = { version = "4.0.10", features = ["derive"] } clap = { version = "4.0.18", features = ["derive"] }
dirs = "*" dirs = "*"
flate2 = "1.0.22" flate2 = "1.0.22"
guess_host_triple = "0.1.3" guess_host_triple = "0.1.3"
@ -28,6 +29,12 @@ log = "0.4.17"
env_logger = "0.9.0" env_logger = "0.9.0"
embuild = { version = "0.30.4", features = ["espidf", "git"] } embuild = { version = "0.30.4", features = ["espidf", "git"] }
strum = { version = "0.24", features = ["derive"] } 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] [target.aarch64-unknown-linux-gnu.dependencies]
openssl = { version = "0.10", features = ["vendored"] } openssl = { version = "0.10", features = ["vendored"] }
@ -41,5 +48,5 @@ bin-dir = "{ bin }{ binary-ext }"
pkg-fmt = "zip" pkg-fmt = "zip"
[profile.release] [profile.release]
lto = "thin" lto = "thin"
strip = true strip = true

View File

@ -3,6 +3,7 @@
[![Continuous Integration](https://github.com/esp-rs/espup/actions/workflows/ci.yaml/badge.svg)](https://github.com/esp-rs/espup/actions/workflows/ci.yaml) [![Continuous Integration](https://github.com/esp-rs/espup/actions/workflows/ci.yaml/badge.svg)](https://github.com/esp-rs/espup/actions/workflows/ci.yaml)
[![Security audit](https://github.com/esp-rs/espup/actions/workflows/audit.yaml/badge.svg)](https://github.com/esp-rs/espup/actions/workflows/audit.yaml) [![Security audit](https://github.com/esp-rs/espup/actions/workflows/audit.yaml/badge.svg)](https://github.com/esp-rs/espup/actions/workflows/audit.yaml)
[![Open in Remote - Containers](https://img.shields.io/static/v1?label=Remote%20-%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/esp-rs/espup) [![Open in Remote - Containers](https://img.shields.io/static/v1?label=Remote%20-%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/esp-rs/espup)
[![Matrix](https://img.shields.io/matrix/esp-rs:matrix.org?label=join%20matrix&color=BEC5C9&labelColor=1C2C2E&logo=matrix&style=flat-square)](https://matrix.to/#/#esp-rs:matrix.org)
> `rustup` for [esp-rs](https://github.com/esp-rs/) > `rustup` for [esp-rs](https://github.com/esp-rs/)
@ -14,10 +15,12 @@ developing applications in Rust for Espressif SoC's.
> This application is still under development and should be considered experimental > This application is still under development and should be considered experimental
## Requirements ## Requirements
Before running or installing `espup`, make sure that [`rustup`](https://www.rust-lang.org/tools/install)
and the following dependencies are installed.
### Windows ### Windows
- Python must be installed and the version should be between `3.6` and `3.10`. - [Python](https://www.python.org/downloads/). Version should be between `3.6` and `3.10`.
- [git](https://git-scm.com/download/win)
- Toolchain. Select one of the following: - Toolchain. Select one of the following:
- [Windows x86_64 GNU](https://github.com/esp-rs/rust-build#windows-x86_64-gnu) - [Windows x86_64 GNU](https://github.com/esp-rs/rust-build#windows-x86_64-gnu)
- [Windows x86_64 MSVC](https://github.com/esp-rs/rust-build#windows-x86_64-msvc) - [Windows x86_64 MSVC](https://github.com/esp-rs/rust-build#windows-x86_64-msvc)
@ -26,14 +29,18 @@ developing applications in Rust for Espressif SoC's.
### Linux ### Linux
- Ubuntu/Debian - Ubuntu/Debian
```sh ```sh
sudo apt-get install -y git curl gcc clang ninja-build cmake libudev-dev \ apt-get install -y git python3 python3-pip gcc build-essential curl pkg-config libudev-dev libssl-dev
python3 python3-pip python3-venv libusb-1.0-0 libssl-dev pkg-config ```
- Fedora
```sh
dnf -y install git python3 python3-pip gcc openssl1.1 systemd-devel
``` ```
- openSUSE Thumbleweed - openSUSE Thumbleweed
``` ```
sudo zypper install -y git gcc libudev-devel ninja python3 python38-pip sudo zypper install -y git gcc libudev-devel ninja python3 python38-pip
``` ```
## Installation ## Installation
Download the pre-compiled [release binaries](https://github.com/esp-rs/espup/releases) or using [cargo-binstall](https://github.com/cargo-bins/cargo-binstall). Download the pre-compiled [release binaries](https://github.com/esp-rs/espup/releases) or using [cargo-binstall](https://github.com/cargo-bins/cargo-binstall).

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()))
}
}

44
src/host_triple.rs Normal file
View File

@ -0,0 +1,44 @@
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, Deserialize, Serialize, Default)]
pub enum HostTriple {
/// 64-bit Linux
#[strum(serialize = "x86_64-unknown-linux-gnu")]
#[default]
X86_64UnknownLinuxGnu,
/// ARM64 Linux
#[strum(serialize = "aarch64-unknown-linux-gnu")]
Aarch64UnknownLinuxGnu,
/// 64-bit MSVC
#[strum(serialize = "x86_64-pc-windows-msvc")]
X86_64PcWindowsMsvc,
/// 64-bit MinGW
#[strum(serialize = "x86_64-pc-windows-gnu")]
X86_64PcWindowsGnu,
/// 64-bit macOS
#[strum(serialize = "x86_64-apple-darwin")]
X86_64AppleDarwin,
/// ARM64 macOS
#[strum(serialize = "aarch64-apple-darwin")]
Aarch64AppleDarwin,
}
/// Parse the host triple if specified, otherwise guess it.
pub fn get_host_triple(host_triple_arg: Option<String>) -> Result<HostTriple> {
if let Some(host_triple_arg) = host_triple_arg {
HostTriple::from_str(&host_triple_arg).context(format!(
"{} Host triple '{}' is not supported.",
emoji::ERROR,
host_triple_arg,
))
} else {
HostTriple::from_str(guess_host_triple().unwrap())
.context(format!("{} Unable to guess host triple.", emoji::ERROR,))
}
}

View File

@ -1,4 +1,6 @@
pub mod config;
pub mod emoji; pub mod emoji;
pub mod host_triple;
pub mod targets; pub mod targets;
pub mod toolchain; pub mod toolchain;
pub mod logging { pub mod logging {

View File

@ -1,36 +1,43 @@
#[cfg(windows)] use anyhow::{bail, Result};
use anyhow::bail;
use anyhow::Result;
use clap::Parser; 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::{ use espup::{
config::Config,
emoji, emoji,
host_triple::get_host_triple,
logging::initialize_logger, logging::initialize_logger,
targets::{parse_targets, Target}, targets::{parse_targets, Target},
toolchain::{ toolchain::{
espidf::{ espidf::{
get_dist_path, get_install_path, get_tool_path, EspIdfRepo, DEFAULT_GIT_REPOSITORY, 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, llvm_toolchain::LlvmToolchain,
rust_toolchain::{ rust_toolchain::{check_rust_installation, install_riscv_target, RustCrate, RustToolchain},
check_rust_installation, get_rustup_home, install_riscv_target, RustCrate,
RustToolchain,
},
}, },
}; };
use log::{debug, info, warn}; use log::{debug, info, warn};
use regex::Regex;
use std::{ use std::{
collections::HashSet, collections::HashSet,
fs::{remove_dir_all, File}, fs::{remove_dir_all, remove_file, File},
io::Write, io::Write,
path::PathBuf, path::{Path, PathBuf},
}; };
#[cfg(windows)] #[cfg(windows)]
const DEFAULT_EXPORT_FILE: &str = "export-esp.ps1"; const DEFAULT_EXPORT_FILE: &str = "export-esp.ps1";
#[cfg(not(windows))] #[cfg(not(windows))]
const DEFAULT_EXPORT_FILE: &str = "export-esp.sh"; 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)] #[derive(Parser)]
#[command( #[command(
name = "espup", name = "espup",
@ -56,6 +63,9 @@ pub enum SubCommand {
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
pub struct InstallOpts { pub struct InstallOpts {
/// Target triple of the host.
#[arg(short = 'd', long, required = false)]
pub default_host: Option<String>,
/// ESP-IDF version to install. If empty, no esp-idf is installed. Version format: /// ESP-IDF version to install. If empty, no esp-idf is installed. Version format:
/// ///
/// - `commit:<hash>`: Uses the commit `<hash>` of the `esp-idf` repository. /// - `commit:<hash>`: Uses the commit `<hash>` of the `esp-idf` repository.
@ -89,63 +99,63 @@ pub struct InstallOpts {
#[arg(short = 't', long, default_value = "all")] #[arg(short = 't', long, default_value = "all")]
pub targets: String, pub targets: String,
/// Xtensa Rust toolchain version. /// 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, pub toolchain_version: String,
} }
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
pub struct UpdateOpts { pub struct UpdateOpts {
/// Target triple of the host.
#[arg(short = 'd', long, required = false)]
pub default_host: Option<String>,
/// Verbosity level of the logs. /// Verbosity level of the logs.
#[arg(short = 'l', long, default_value = "info", value_parser = ["debug", "info", "warn", "error"])] #[arg(short = 'l', long, default_value = "info", value_parser = ["debug", "info", "warn", "error"])]
pub log_level: String, pub log_level: String,
/// Xtensa Rust toolchain version. /// 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, pub toolchain_version: Option<String>,
} }
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
pub struct UninstallOpts { 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. /// Verbosity level of the logs.
#[arg(short = 'l', long, default_value = "info", value_parser = ["debug", "info", "warn", "error"])] #[arg(short = 'l', long, default_value = "info", value_parser = ["debug", "info", "warn", "error"])]
pub log_level: String, 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<()> { fn install(args: InstallOpts) -> Result<()> {
initialize_logger(&args.log_level); initialize_logger(&args.log_level);
info!("{} Installing esp-rs", emoji::DISC); info!("{} Installing esp-rs", emoji::DISC);
let targets: HashSet<Target> = parse_targets(&args.targets).unwrap(); let targets: HashSet<Target> = parse_targets(&args.targets).unwrap();
let host_triple = get_host_triple(args.default_host)?;
let mut extra_crates: HashSet<RustCrate> = let mut extra_crates: HashSet<RustCrate> =
args.extra_crates.split(',').map(RustCrate::new).collect(); args.extra_crates.split(',').map(RustCrate::new).collect();
let mut exports: Vec<String> = Vec::new(); let mut exports: Vec<String> = Vec::new();
let export_file = args.export_file.clone(); let export_file = args.export_file.clone();
let rust_toolchain = RustToolchain::new(args.toolchain_version.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. // 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"))] #[cfg(all(target_arch = "x86_64", target_os = "linux"))]
let llvm = LlvmToolchain::new(args.profile_minimal); let llvm = LlvmToolchain::new(args.profile_minimal, &host_triple);
#[cfg(any(not(target_arch = "x86_64"), not(target_os = "linux")))] #[cfg(any(not(target_arch = "x86_64"), not(target_os = "linux")))]
let llvm = LlvmToolchain::new(true); let llvm = LlvmToolchain::new(true, &host_triple);
debug!( debug!(
"{} Arguments: "{} Arguments:
- Host triple: {}
- Targets: {:?} - Targets: {:?}
- ESP-IDF version: {:?} - ESP-IDF version: {:?}
- Export file: {:?} - Export file: {:?}
@ -156,10 +166,11 @@ fn install(args: InstallOpts) -> Result<()> {
- Profile Minimal: {:?} - Profile Minimal: {:?}
- Toolchain version: {:?}", - Toolchain version: {:?}",
emoji::INFO, emoji::INFO,
host_triple,
targets, targets,
&args.espidf_version, &args.espidf_version,
export_file, export_file,
extra_crates, &extra_crates,
llvm, llvm,
&args.nightly_version, &args.nightly_version,
rust_toolchain, rust_toolchain,
@ -181,11 +192,11 @@ fn install(args: InstallOpts) -> Result<()> {
} }
if let Some(espidf_version) = &args.espidf_version { 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()?); exports.extend(repo.install()?);
extra_crates.insert(RustCrate::new("ldproxy")); extra_crates.insert(RustCrate::new("ldproxy"));
} else { } else {
exports.extend(install_gcc_targets(targets)?); exports.extend(install_gcc_targets(&targets, &host_triple)?);
} }
debug!( debug!(
@ -193,7 +204,7 @@ fn install(args: InstallOpts) -> Result<()> {
emoji::DEBUG, emoji::DEBUG,
extra_crates extra_crates
); );
for extra_crate in extra_crates { for extra_crate in &extra_crates {
extra_crate.install()?; extra_crate.install()?;
} }
@ -203,6 +214,24 @@ fn install(args: InstallOpts) -> Result<()> {
export_environment(&export_file, &exports)?; 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); info!("{} Installation suscesfully completed!", emoji::CHECK);
warn!( warn!(
"{} Please, source the export file, as state above, to properly setup the environment!", "{} Please, source the export file, as state above, to properly setup the environment!",
@ -211,61 +240,102 @@ fn install(args: InstallOpts) -> Result<()> {
Ok(()) Ok(())
} }
/// Uninstalls esp-rs environment /// Uninstalls the Rust for ESP chips environment
fn uninstall(args: UninstallOpts) -> Result<()> { fn uninstall(args: UninstallOpts) -> Result<()> {
initialize_logger(&args.log_level); initialize_logger(&args.log_level);
info!("{} Uninstalling esp-rs", emoji::DISC); info!("{} Uninstalling esp-rs", emoji::DISC);
let config = Config::load().unwrap();
debug!( debug!(
"{} Arguments: "{} Arguments:
- Remove Clang: {} - Config: {:#?}",
- ESP-IDF version: {:#?}",
emoji::INFO, emoji::INFO,
&args.remove_clang, config
&args.espidf_version,
); );
info!("{} Deleting Xtensa Rust toolchain", emoji::WRENCH); 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 LLVM", emoji::WRENCH);
info!("{} Deleting Xtensa Clang", emoji::WRENCH); remove_dir_all(config.llvm_path)?;
remove_dir_all(PathBuf::from(get_tool_path("")).join("xtensa-esp32-elf-clang"))?;
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()?; clear_dist_folder()?;
if let Some(espidf_version) = &args.espidf_version { info!("{} Deleting export file", emoji::WRENCH);
info!("{} Deleting ESP-IDF", emoji::WRENCH); remove_file(Path::new(&config.export_file))?;
let repo = EspIdfRemote {
git_ref: parse_esp_idf_git_ref(espidf_version), info!("{} Deleting config file", emoji::WRENCH);
repo_url: Some(DEFAULT_GIT_REPOSITORY.to_string()), let conf_dirs = ProjectDirs::from("rs", "esp", "espup").unwrap();
}; let conf_file = conf_dirs.config_dir().join("espup.toml");
remove_dir_all(get_install_path(repo).parent().unwrap())?; remove_file(conf_file)?;
}
info!("{} Uninstallation suscesfully completed!", emoji::CHECK); info!("{} Uninstallation suscesfully completed!", emoji::CHECK);
Ok(()) Ok(())
} }
/// Updates Xtensa Rust toolchain /// Updates Xtensa Rust toolchain.
fn update(args: UpdateOpts) -> Result<()> { fn update(args: UpdateOpts) -> Result<()> {
initialize_logger(&args.log_level); initialize_logger(&args.log_level);
info!("{} Updating Xtensa Rust toolchain", emoji::DISC); 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!( debug!(
"{} Arguments: "{} Arguments:
- Toolchain version: {}", - Host triple: {}
- Toolchain version: {:#?}
- Config: {:#?}",
emoji::INFO, emoji::INFO,
&args.toolchain_version, host_triple,
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); 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);
rust_toolchain.install_xtensa_rust()?; 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); info!("{} Update suscesfully completed!", emoji::CHECK);
Ok(()) Ok(())
} }
@ -280,8 +350,11 @@ fn main() -> Result<()> {
/// Deletes dist folder. /// Deletes dist folder.
fn clear_dist_folder() -> Result<()> { fn clear_dist_folder() -> Result<()> {
info!("{} Clearing dist folder", emoji::WRENCH); let dist_path = PathBuf::from(get_dist_path(""));
remove_dir_all(&get_dist_path(""))?; if dist_path.exists() {
info!("{} Clearing dist folder", emoji::WRENCH);
remove_dir_all(&dist_path)?;
}
Ok(()) Ok(())
} }

View File

@ -1,12 +1,14 @@
//! ESP32 chip variants support. //! ESP32 chip variants support.
use crate::emoji; use crate::emoji;
use anyhow::Context;
use log::debug; use log::debug;
use serde::{Deserialize, Serialize};
use std::{collections::HashSet, str::FromStr}; use std::{collections::HashSet, str::FromStr};
use strum::Display; use strum::Display;
use strum_macros::EnumString;
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug, Display)] #[derive(Clone, Copy, EnumString, PartialEq, Hash, Eq, Debug, Display, Deserialize, Serialize)]
pub enum Target { pub enum Target {
/// Xtensa LX7 based dual core /// Xtensa LX7 based dual core
#[strum(serialize = "esp32")] #[strum(serialize = "esp32")]
@ -22,20 +24,6 @@ pub enum Target {
ESP32C3, ESP32C3,
} }
impl FromStr for Target {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"esp32" => Ok(Target::ESP32),
"esp32s2" => Ok(Target::ESP32S2),
"esp32s3" => Ok(Target::ESP32S3),
"esp32c3" => Ok(Target::ESP32C3),
_ => Err(()),
}
}
}
/// Returns a vector of Chips from a comma or space separated string. /// Returns a vector of Chips from a comma or space separated string.
pub fn parse_targets(targets_str: &str) -> Result<HashSet<Target>, String> { pub fn parse_targets(targets_str: &str) -> Result<HashSet<Target>, String> {
debug!("{} Parsing targets: {}", emoji::DEBUG, targets_str); debug!("{} Parsing targets: {}", emoji::DEBUG, targets_str);
@ -54,7 +42,15 @@ pub fn parse_targets(targets_str: &str) -> Result<HashSet<Target>, String> {
}; };
for target in targets_str { for target in targets_str {
targets.insert(FromStr::from_str(target).unwrap()); targets.insert(
Target::from_str(target)
.context(format!(
"{} Target '{}' is not supported",
emoji::ERROR,
target
))
.unwrap(),
);
} }
debug!("{} Parsed targets: {:?}", emoji::DEBUG, targets); debug!("{} Parsed targets: {:?}", emoji::DEBUG, targets);
Ok(targets) Ok(targets)

View File

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

View File

@ -2,28 +2,34 @@
use crate::{ use crate::{
emoji, emoji,
host_triple::HostTriple,
targets::Target, targets::Target,
toolchain::{download_file, espidf::get_tool_path}, toolchain::{download_file, espidf::get_tool_path},
}; };
use anyhow::Result; use anyhow::Result;
use embuild::espidf::EspIdfVersion; use embuild::espidf::EspIdfVersion;
use log::{debug, info}; use log::{debug, info, warn};
use std::collections::HashSet; use std::{
collections::HashSet,
path::{Path, PathBuf},
};
const DEFAULT_GCC_REPOSITORY: &str = "https://github.com/espressif/crosstool-NG/releases/download"; const DEFAULT_GCC_REPOSITORY: &str = "https://github.com/espressif/crosstool-NG/releases/download";
const DEFAULT_GCC_RELEASE: &str = "esp-2021r2-patch5"; const DEFAULT_GCC_RELEASE: &str = "esp-2021r2-patch5";
const DEFAULT_GCC_VERSION: &str = "8_4_0"; const DEFAULT_GCC_VERSION: &str = "8_4_0";
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct GccToolchain { pub struct GccToolchain {
/// The repository containing GCC sources. /// Host triple.
pub repository_url: String, pub host_triple: HostTriple,
/// Repository release version to use. /// Repository release version to use.
pub release: String, pub release: String,
/// GCC Version. /// The repository containing GCC sources.
pub version: String, pub repository_url: String,
/// GCC Toolchain target. /// GCC Toolchain target.
pub toolchain_name: String, pub toolchain_name: String,
/// GCC Version.
pub version: String,
} }
impl GccToolchain { impl GccToolchain {
@ -39,17 +45,23 @@ impl GccToolchain {
/// Installs the gcc toolchain. /// Installs the gcc toolchain.
pub fn install(&self) -> Result<()> { pub fn install(&self) -> Result<()> {
let target_dir = format!("{}/{}-{}", self.toolchain_name, self.release, self.version); let target_dir = format!("{}/{}-{}", self.toolchain_name, self.release, self.version);
let gcc_path = get_tool_path(&target_dir); let gcc_path = get_tool_path(&target_dir);
let host_triple = guess_host_triple::guess_host_triple().unwrap(); let extension = get_artifact_extension(&self.host_triple);
let extension = get_artifact_extension(host_triple);
debug!("{} GCC path: {}", emoji::DEBUG, gcc_path); debug!("{} GCC path: {}", emoji::DEBUG, gcc_path);
if Path::new(&PathBuf::from(&gcc_path)).exists() {
warn!(
"{} Previous installation of GCC exist in: '{}'. Reusing this installation.",
emoji::WARN,
&gcc_path
);
return Ok(());
}
let gcc_file = format!( let gcc_file = format!(
"{}-gcc{}-{}-{}.{}", "{}-gcc{}-{}-{}.{}",
self.toolchain_name, self.toolchain_name,
self.version, self.version,
self.release, self.release,
get_arch(host_triple).unwrap(), get_arch(&self.host_triple).unwrap(),
extension extension
); );
let gcc_dist_url = format!("{}/{}/{}", self.repository_url, self.release, gcc_file); let gcc_dist_url = format!("{}/{}/{}", self.repository_url, self.release, gcc_file);
@ -63,40 +75,37 @@ impl GccToolchain {
} }
/// Create a new instance with default values and proper toolchain name. /// Create a new instance with default values and proper toolchain name.
pub fn new(target: Target) -> Self { pub fn new(target: &Target, host_triple: &HostTriple) -> Self {
Self { Self {
repository_url: DEFAULT_GCC_REPOSITORY.to_string(), host_triple: host_triple.clone(),
release: DEFAULT_GCC_RELEASE.to_string(), release: DEFAULT_GCC_RELEASE.to_string(),
version: DEFAULT_GCC_VERSION.to_string(), repository_url: DEFAULT_GCC_REPOSITORY.to_string(),
toolchain_name: get_toolchain_name(target), toolchain_name: get_toolchain_name(target),
version: DEFAULT_GCC_VERSION.to_string(),
} }
} }
} }
/// Gets the name of the GCC arch based on the host triple. /// Gets the name of the GCC arch based on the host triple.
fn get_arch(host_triple: &str) -> Result<&str, String> { fn get_arch(host_triple: &HostTriple) -> Result<&str> {
match host_triple { match host_triple {
"aarch64-apple-darwin" | "x86_64-apple-darwin" => Ok("macos"), HostTriple::Aarch64AppleDarwin | HostTriple::X86_64AppleDarwin => Ok("macos"),
"aarch64-unknown-linux-gnu" => Ok("linux-arm64"), HostTriple::X86_64UnknownLinuxGnu => Ok("linux-amd64"),
"x86_64-unknown-linux-gnu" => Ok("linux-amd64"), HostTriple::Aarch64UnknownLinuxGnu => Ok("linux-arm64"),
"x86_64-pc-windows-msvc" | "x86_64-pc-windows-gnu" => Ok("win64"), HostTriple::X86_64PcWindowsMsvc | HostTriple::X86_64PcWindowsGnu => Ok("win64"),
_ => Err(format!(
"No GCC arch found for the host triple: {}",
host_triple
)),
} }
} }
/// Gets the artifact extension based on the host architecture. /// Gets the artifact extension based on the host triple.
fn get_artifact_extension(host_triple: &str) -> &str { fn get_artifact_extension(host_triple: &HostTriple) -> &str {
match host_triple { match host_triple {
"x86_64-pc-windows-msvc" | "x86_64-pc-windows-gnu" => "zip", HostTriple::X86_64PcWindowsMsvc | HostTriple::X86_64PcWindowsGnu => "zip",
_ => "tar.gz", _ => "tar.gz",
} }
} }
/// Gets the toolchain name based on the Target /// 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 { match target {
Target::ESP32 => "xtensa-esp32-elf".to_string(), Target::ESP32 => "xtensa-esp32-elf".to_string(),
Target::ESP32S2 => "xtensa-esp32s2-elf".to_string(), Target::ESP32S2 => "xtensa-esp32s2-elf".to_string(),
@ -128,11 +137,14 @@ pub fn get_ulp_toolchain_name(target: Target, version: Option<&EspIdfVersion>) -
} }
/// Installs GCC toolchain the selected targets. /// Installs GCC toolchain the selected targets.
pub fn install_gcc_targets(targets: HashSet<Target>) -> Result<Vec<String>> { pub fn install_gcc_targets(
targets: &HashSet<Target>,
host_triple: &HostTriple,
) -> Result<Vec<String>> {
info!("{} Installing gcc for build targets", emoji::WRENCH); info!("{} Installing gcc for build targets", emoji::WRENCH);
let mut exports: Vec<String> = Vec::new(); let mut exports: Vec<String> = Vec::new();
for target in targets { for target in targets {
let gcc = GccToolchain::new(target); let gcc = GccToolchain::new(target, host_triple);
gcc.install()?; gcc.install()?;
#[cfg(windows)] #[cfg(windows)]

View File

@ -2,10 +2,11 @@
use crate::{ use crate::{
emoji, emoji,
host_triple::HostTriple,
toolchain::{download_file, espidf::get_tool_path}, toolchain::{download_file, espidf::get_tool_path},
}; };
use anyhow::{bail, Ok, Result}; use anyhow::{bail, Ok, Result};
use log::info; use log::{info, warn};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
const DEFAULT_LLVM_COMPLETE_REPOSITORY: &str = const DEFAULT_LLVM_COMPLETE_REPOSITORY: &str =
@ -14,37 +15,39 @@ const DEFAULT_LLVM_MINIFIED_REPOSITORY: &str =
"https://github.com/esp-rs/rust-build/releases/download/llvm-project-14.0-minified"; "https://github.com/esp-rs/rust-build/releases/download/llvm-project-14.0-minified";
const DEFAULT_LLVM_VERSION: &str = "esp-14.0.0-20220415"; const DEFAULT_LLVM_VERSION: &str = "esp-14.0.0-20220415";
#[derive(Debug)] #[derive(Debug, Clone, Default)]
pub struct LlvmToolchain { pub struct LlvmToolchain {
/// LLVM Toolchain file name.
pub file_name: String,
/// Host triple.
pub host_triple: HostTriple,
/// LLVM Toolchain path.
pub path: PathBuf,
/// The repository containing LVVM sources. /// The repository containing LVVM sources.
pub repository_url: String, pub repository_url: String,
/// Repository release version to use. /// Repository release version to use.
pub version: String, pub version: String,
/// LLVM Toolchain file name.
pub file_name: String,
/// LLVM Toolchain path.
pub path: PathBuf,
} }
impl LlvmToolchain { impl LlvmToolchain {
/// Gets the name of the LLVM arch based on the host triple. /// Gets the name of the LLVM arch based on the host triple.
fn get_arch(host_triple: &str) -> Result<String> { fn get_arch(host_triple: &HostTriple) -> Result<&str> {
match host_triple { match host_triple {
"aarch64-apple-darwin" | "x86_64-apple-darwin" => Ok("macos".to_string()), HostTriple::Aarch64AppleDarwin | HostTriple::X86_64AppleDarwin => Ok("macos"),
"x86_64-unknown-linux-gnu" => Ok("linux-amd64".to_string()), HostTriple::X86_64UnknownLinuxGnu => Ok("linux-amd64"),
"x86_64-pc-windows-msvc" | "x86_64-pc-windows-gnu" => Ok("win64".to_string()), HostTriple::X86_64PcWindowsMsvc | HostTriple::X86_64PcWindowsGnu => Ok("win64"),
_ => bail!( _ => bail!(
"{} No LLVM arch found for the host triple: {}", "{} No LLVM arch found for the host triple: '{}'",
emoji::ERROR, emoji::ERROR,
host_triple host_triple
), ),
} }
} }
/// Gets the artifact extension based on the host architecture. /// Gets the artifact extension based on the host triple.
fn get_artifact_extension(host_triple: &str) -> &str { fn get_artifact_extension(host_triple: &HostTriple) -> &str {
match host_triple { match host_triple {
"x86_64-pc-windows-msvc" | "x86_64-pc-windows-gnu" => "zip", HostTriple::X86_64PcWindowsMsvc | HostTriple::X86_64PcWindowsGnu => "zip",
_ => "tar.xz", _ => "tar.xz",
} }
} }
@ -63,18 +66,18 @@ impl LlvmToolchain {
let mut exports: Vec<String> = Vec::new(); let mut exports: Vec<String> = Vec::new();
if Path::new(&self.path).exists() { if Path::new(&self.path).exists() {
bail!( warn!(
"{} Previous installation of LLVM exist in: {}.\n Please, remove the directory before new installation.", "{} Previous installation of LLVM exist in: '{}'. Reusing this installation.",
emoji::WARN, emoji::WARN,
self.path.to_str().unwrap() self.path.to_str().unwrap()
); );
} else { } else {
info!("{} Installing Xtensa elf Clang", emoji::WRENCH); info!("{} Installing Xtensa elf Clang", emoji::WRENCH);
download_file( download_file(
self.repository_url.clone(), self.repository_url.clone(),
&format!( &format!(
"idf_tool_xtensa_elf_clang.{}", "idf_tool_xtensa_elf_clang.{}",
Self::get_artifact_extension(guess_host_triple::guess_host_triple().unwrap()) Self::get_artifact_extension(&self.host_triple)
), ),
self.path.to_str().unwrap(), self.path.to_str().unwrap(),
true, true,
@ -95,10 +98,9 @@ impl LlvmToolchain {
} }
/// Create a new instance with default values and proper toolchain version. /// Create a new instance with default values and proper toolchain version.
pub fn new(minified: bool) -> Self { pub fn new(minified: bool, host_triple: &HostTriple) -> Self {
let host_triple = guess_host_triple::guess_host_triple().unwrap();
let version = DEFAULT_LLVM_VERSION.to_string();
let file_name: String; let file_name: String;
let version = DEFAULT_LLVM_VERSION.to_string();
let repository_url: String; let repository_url: String;
if minified { if minified {
file_name = format!( file_name = format!(
@ -130,10 +132,11 @@ impl LlvmToolchain {
) )
.into(); .into();
Self { Self {
file_name,
host_triple: host_triple.clone(),
path,
repository_url, repository_url,
version, version,
file_name,
path,
} }
} }
} }

View File

@ -30,13 +30,17 @@ pub fn download_file(
) -> Result<String> { ) -> Result<String> {
let file_path = format!("{}/{}", output_directory, file_name); let file_path = format!("{}/{}", output_directory, file_name);
if Path::new(&file_path).exists() { if Path::new(&file_path).exists() {
info!("{} Using cached file: {}", emoji::INFO, file_path); info!("{} Using cached file: '{}'", emoji::INFO, file_path);
return Ok(file_path); return Ok(file_path);
} else if !Path::new(&output_directory).exists() { } else if !Path::new(&output_directory).exists() {
info!("{} Creating directory: {}", emoji::WRENCH, output_directory); info!(
"{} Creating directory: '{}'",
emoji::WRENCH,
output_directory
);
if let Err(_e) = create_dir_all(output_directory) { if let Err(_e) = create_dir_all(output_directory) {
bail!( bail!(
"{} Creating directory {} failed", "{} Creating directory '{}' failed",
emoji::ERROR, emoji::ERROR,
output_directory output_directory
); );
@ -61,7 +65,7 @@ pub fn download_file(
} }
"gz" => { "gz" => {
info!( info!(
"{} Uncompressing tar.gz file to {}", "{} Uncompressing tar.gz file to '{}'",
emoji::WRENCH, emoji::WRENCH,
output_directory output_directory
); );
@ -72,7 +76,7 @@ pub fn download_file(
} }
"xz" => { "xz" => {
info!( info!(
"{} Uncompressing tar.xz file to {}", "{} Uncompressing tar.xz file to '{}'",
emoji::WRENCH, emoji::WRENCH,
output_directory output_directory
); );
@ -82,11 +86,15 @@ pub fn download_file(
archive.unpack(output_directory).unwrap(); archive.unpack(output_directory).unwrap();
} }
_ => { _ => {
bail!("{} Unsuported file extension: {}", emoji::ERROR, extension); bail!(
"{} Unsuported file extension: '{}'",
emoji::ERROR,
extension
);
} }
} }
} else { } else {
info!("{} Creating file: {}", emoji::WRENCH, file_path); info!("{} Creating file: '{}'", emoji::WRENCH, file_path);
let mut out = File::create(file_path)?; let mut out = File::create(file_path)?;
copy(&mut resp, &mut out)?; copy(&mut resp, &mut out)?;
} }

View File

@ -1,34 +1,40 @@
//! Xtensa Rust Toolchain source and installation tools //! Xtensa Rust Toolchain source and installation tools
#[cfg(unix)]
use super::espidf::get_dist_path;
use crate::{ use crate::{
emoji, emoji,
toolchain::{download_file, espidf::get_dist_path, get_home_dir}, host_triple::HostTriple,
toolchain::{download_file, get_home_dir},
}; };
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use embuild::cmd; use embuild::cmd;
use log::{info, warn}; use log::{info, warn};
use serde::{Deserialize, Serialize};
use std::fmt::Debug; use std::fmt::Debug;
use std::{env, path::PathBuf, process::Stdio}; use std::{env, path::PathBuf, process::Stdio};
const DEFAULT_XTENSA_RUST_REPOSITORY: &str = const DEFAULT_XTENSA_RUST_REPOSITORY: &str =
"https://github.com/esp-rs/rust-build/releases/download"; "https://github.com/esp-rs/rust-build/releases/download";
#[derive(Debug)] #[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct RustToolchain { pub struct RustToolchain {
/// Path to the cargo home directory.
pub cargo_home: PathBuf,
/// Xtensa Rust toolchain file. /// Xtensa Rust toolchain file.
pub dist_file: String, pub dist_file: String,
/// Xtensa Rust toolchain URL. /// Xtensa Rust toolchain URL.
pub dist_url: String, pub dist_url: String,
/// Host triple.
pub host_triple: String,
/// Path to the rustup home directory.
pub rustup_home: PathBuf,
#[cfg(unix)] #[cfg(unix)]
/// Xtensa Src Rust toolchain file. /// Xtensa Src Rust toolchain file.
pub src_dist_file: String, pub src_dist_file: String,
#[cfg(unix)] #[cfg(unix)]
/// Xtensa Src Rust toolchain URL. /// Xtensa Src Rust toolchain URL.
pub src_dist_url: String, pub src_dist_url: String,
/// Path to the cargo home directory.
pub cargo_home: PathBuf,
/// Path to the rustup home directory.
pub rustup_home: PathBuf,
/// Xtensa Rust toolchain destination path. /// Xtensa Rust toolchain destination path.
pub toolchain_destination: PathBuf, pub toolchain_destination: PathBuf,
/// Xtensa Rust Toolchain version. /// Xtensa Rust Toolchain version.
@ -44,7 +50,7 @@ impl RustToolchain {
let toolchain_path = self.toolchain_destination.clone().join("esp"); let toolchain_path = self.toolchain_destination.clone().join("esp");
if toolchain_path.exists() { if toolchain_path.exists() {
bail!( bail!(
"{} Previous installation of Rust Toolchain exist in: {}.\n Please, remove the directory before new installation.", "{} Previous installation of Rust Toolchain exist in: '{}'. Please, remove the directory before new installation.",
emoji::WARN, emoji::WARN,
self.toolchain_destination.display() self.toolchain_destination.display()
); );
@ -57,7 +63,6 @@ impl RustToolchain {
#[cfg(unix)] #[cfg(unix)]
if cfg!(unix) { if cfg!(unix) {
let host_triple = guess_host_triple::guess_host_triple().unwrap();
download_file( download_file(
self.dist_url.clone(), self.dist_url.clone(),
"rust.tar.xz", "rust.tar.xz",
@ -69,7 +74,7 @@ impl RustToolchain {
let arguments = format!( let arguments = format!(
"{}/rust-nightly-{}/install.sh --destdir={} --prefix='' --without=rust-docs", "{}/rust-nightly-{}/install.sh --destdir={} --prefix='' --without=rust-docs",
get_dist_path("rust"), get_dist_path("rust"),
host_triple, &self.host_triple,
self.toolchain_destination.display() self.toolchain_destination.display()
); );
cmd!("/bin/bash", "-c", arguments).run()?; cmd!("/bin/bash", "-c", arguments).run()?;
@ -104,10 +109,9 @@ impl RustToolchain {
} }
/// Create a new instance. /// Create a new instance.
pub fn new(toolchain_version: String) -> Self { pub fn new(toolchain_version: &str, host_triple: &HostTriple) -> Self {
let host_triple = guess_host_triple::guess_host_triple().unwrap();
let artifact_extension = get_artifact_extension(host_triple); let artifact_extension = get_artifact_extension(host_triple);
let version = toolchain_version; let version = toolchain_version.to_string();
let dist = format!("rust-{}-{}", version, host_triple); let dist = format!("rust-{}-{}", version, host_triple);
let dist_file = format!("{}.{}", dist, artifact_extension); let dist_file = format!("{}.{}", dist, artifact_extension);
let dist_url = format!( let dist_url = format!(
@ -130,21 +134,22 @@ impl RustToolchain {
#[cfg(windows)] #[cfg(windows)]
let toolchain_destination = rustup_home.join("toolchains"); let toolchain_destination = rustup_home.join("toolchains");
Self { Self {
cargo_home,
dist_file, dist_file,
dist_url, dist_url,
host_triple: host_triple.to_string(),
rustup_home,
#[cfg(unix)] #[cfg(unix)]
src_dist_file, src_dist_file,
#[cfg(unix)] #[cfg(unix)]
src_dist_url, src_dist_url,
cargo_home,
rustup_home,
toolchain_destination, toolchain_destination,
version, version,
} }
} }
} }
#[derive(Hash, Eq, PartialEq, Debug)] #[derive(Hash, Eq, PartialEq, Debug, Clone, Serialize, Deserialize, Default)]
pub struct RustCrate { pub struct RustCrate {
/// Crate name. /// Crate name.
pub name: String, pub name: String,
@ -175,9 +180,9 @@ impl RustCrate {
} }
/// Gets the artifact extension based on the host architecture. /// Gets the artifact extension based on the host architecture.
fn get_artifact_extension(host_triple: &str) -> &str { fn get_artifact_extension(host_triple: &HostTriple) -> &str {
match host_triple { match host_triple {
"x86_64-pc-windows-msvc" | "x86_64-pc-windows-gnu" => "zip", HostTriple::X86_64PcWindowsMsvc | HostTriple::X86_64PcWindowsGnu => "zip",
_ => "tar.xz", _ => "tar.xz",
} }
} }
@ -192,97 +197,24 @@ pub fn get_rustup_home() -> PathBuf {
PathBuf::from(env::var("RUSTUP_HOME").unwrap_or_else(|_e| get_home_dir() + "/.rustup")) PathBuf::from(env::var("RUSTUP_HOME").unwrap_or_else(|_e| get_home_dir() + "/.rustup"))
} }
/// Checks if rustup and the proper nightly version are installed. If they are /// Checks if rustup and the proper nightly version are installed. If rustup is not installed,
/// not, proceed to install them. /// it bails. If nigthly version is not installed, proceed to install it.
pub fn check_rust_installation(nightly_version: &str) -> Result<()> { pub fn check_rust_installation(nightly_version: &str) -> Result<()> {
info!("{} Checking existing Rust installation", emoji::WRENCH); info!("{} Checking existing Rust installation", emoji::WRENCH);
match cmd!("rustup", "toolchain", "list") if let Ok(child_output) = cmd!("rustup", "toolchain", "list")
.into_inner() .into_inner()
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.output() .output()
{ {
Ok(child_output) => { let result = String::from_utf8_lossy(&child_output.stdout);
let result = String::from_utf8_lossy(&child_output.stdout); if !result.contains("nightly") {
if !result.contains("nightly") { warn!("{} Rust nightly toolchain not found", emoji::WARN);
warn!("{} Rust nightly toolchain not found", emoji::WARN); install_rust_nightly(nightly_version)?;
install_rust_nightly(nightly_version)?;
}
}
Err(e) => {
if let std::io::ErrorKind::NotFound = e.kind() {
warn!("{} rustup was not found.", emoji::WARN);
install_rustup(nightly_version)?;
} else {
bail!("{} Error: {}", emoji::ERROR, e);
}
} }
} else {
bail!("{} rustup was not found. Please, install rustup: https://www.rust-lang.org/tools/install", emoji::ERROR);
} }
Ok(())
}
/// Installs rustup
fn install_rustup(nightly_version: &str) -> Result<()> {
#[cfg(windows)]
let rustup_init_path = download_file(
"https://win.rustup.rs/x86_64".to_string(),
"rustup-init.exe",
&get_dist_path("rustup"),
false,
)?;
#[cfg(unix)]
let rustup_init_path = download_file(
"https://sh.rustup.rs".to_string(),
"rustup-init.sh",
&get_dist_path("rustup"),
false,
)?;
info!(
"{} Installing rustup with {} toolchain",
emoji::WRENCH,
nightly_version
);
#[cfg(windows)]
cmd!(
rustup_init_path,
"--default-toolchain",
nightly_version,
"--profile",
"minimal",
"-y"
)
.run()?;
#[cfg(not(windows))]
cmd!(
"/bin/bash",
rustup_init_path,
"--default-toolchain",
nightly_version,
"--profile",
"minimal",
"-y"
)
.run()?;
#[cfg(windows)]
let path = format!(
"{};{}",
std::env::var("PATH").unwrap(),
get_cargo_home().join("bin").display()
);
#[cfg(unix)]
let path = format!(
"{}:{}",
std::env::var("PATH").unwrap(),
get_cargo_home().join("bin").display()
);
std::env::set_var("PATH", path);
warn!(
"{} Please restart your terminal after the installation for the changes to take effect.",
emoji::WARN
);
Ok(()) Ok(())
} }