mirror of
https://github.com/esp-rs/espup.git
synced 2025-09-26 20:30:28 +00:00
Refactor espup (#182)
* chore: 📌 Update crate version * refactor: ♻️ Initial refactor of subcommands * feat: ✨ Add config file for install and update * refactor: ♻️ Avoid using config file * feat: ✨ Use temp_dir * feat: ⚡️ Rename windows toolchain * feat: ✨ Add todo, remove unnecesary modules, fix Windows install * test: 🐛 Fix tests * fix: 🐛 Fix Windows build * fix: 🐛 Fix LLVM installation * fix: 🐛 Fix GCC paths * docs: 🎨 Update todos * style: 🎨 Remove debug prints * feat: ✨ Update nightly installation * feat: ⚡️ Check the result of cmds * fix: 🐛 Fix errors * feat: ✨ Update errors * feat: ⚡️ Remove unnecesary into_diagnostics * build: ➖ Cleanup dependencies * feat: ✨ Only install nightly toolchain when using riscv targets * refactor: ♻️ Update how cmd! results are checked * refactor: ♻️ Use std::process::Command * style: 🎨 Remove unused comments * feat: ⚡️ Run Xtensa install commands in parallel * style: 🔊 Remove rustup logs * feat: ✨ Check if the toolchain is already installed an reuse it * feat: ⚡️ Improve toolchain detection * docs: 🔊 Update log warning about extended semver corner case * docs: 💡 Remove addressed todo coment * refactor: ♻️ Update options * test: 🐛 Fix tests * docs: 🔊 Add todo * docs: ⚡️ Update llvm error * refactor: ♻️ Use an error for LLVM * fix: 🐛 Fix llvm object * feat: ✨ Allow only `esp` as -a/--name * chore: 🎨 Format variables and standarize logs * feat: ⚡️ Remove unused import * feat: ✨ Inject Windows variables * chore: ⚡️ Remove llvm-version option * feat: ⚡️ Add std option * style: 🔇 Remove debug log * fix: 🎨 Fix clippy warnings * fix: 🐛 Fix clippy warnings on unix * chore: 🔊 Update logs * feat: ✨ Stip zip files * feat: ✨ Allow -a/--name in Windows * feat: ✨ Allow -a/--name in Windows * style: 🐛 Fix clippy warning * ci: ⚡️ Improve CD and add publish job * ci: 🐛 Fix cross installation * build: ⬆️ bump dependencies * docs: 📝 Remove limitations sections * docs: 📝 Update usage section * docs: 📝 Update docs * docs: 📝 Update readme * docs: 📝 Update requirements * docs: 📝 Update format * docs: 📝 Update docs * docs: 📝 Update requirements * build: 🏗️ Dependencies update * docs: 📝 Fix typo * refactor: ♻️ Change xtensa-version option to be backward compatible
This commit is contained in:
parent
d8e302f637
commit
5040e11a30
@ -9,13 +9,13 @@ ARG CONTAINER_GROUP=esp
|
||||
|
||||
# Ubuntu/Debian
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git python3 python3-pip gcc build-essential curl pkg-config libudev-dev\
|
||||
&& apt-get install -y git gcc build-essential curl pkg-config \
|
||||
&& apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts
|
||||
RUN adduser --disabled-password --gecos "" ${CONTAINER_USER}
|
||||
|
||||
# Fedora
|
||||
# RUN dnf -y update \
|
||||
# && dnf -y install git python3 python3-pip perl gcc systemd-devel \
|
||||
# && dnf -y install git perl gcc \
|
||||
# && dnf clean all
|
||||
# RUN adduser ${CONTAINER_USER}
|
||||
|
||||
@ -23,7 +23,7 @@ USER ${CONTAINER_USER}
|
||||
WORKDIR /home/${CONTAINER_USER}
|
||||
|
||||
# openSUSE Tumbleweed/Leap
|
||||
# RUN zypper install -y git gcc libudev-devel ninja python3 python3-pip make \
|
||||
# RUN zypper install -y git gcc ninja make \
|
||||
# && zypper clean
|
||||
|
||||
# Install Rust
|
||||
|
52
.github/workflows/cd.yaml
vendored
52
.github/workflows/cd.yaml
vendored
@ -32,34 +32,30 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
target: ${{ matrix.job.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Enable caching
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: Install cross and build
|
||||
if: matrix.job.target == 'aarch64-unknown-linux-gnu'
|
||||
run: cargo install cross && cross build --release --target ${{ matrix.job.target }}
|
||||
- name: Cargo build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
use-cross: ${{ matrix.job.cross }}
|
||||
args: --release --target ${{ matrix.job.target }}
|
||||
if: matrix.job.target != 'aarch64-unknown-linux-gnu'
|
||||
run: cargo build --release --target ${{ matrix.job.target }}
|
||||
- name: Compress (Unix)
|
||||
if: ${{ matrix.job.os != 'windows-latest' }}
|
||||
run: zip -j espup-${{ matrix.job.target }}.zip target/${{ matrix.job.target }}/release/espup${{ matrix.job.binary-postfix }}
|
||||
|
||||
- name: Compress (Windows)
|
||||
if: ${{ matrix.job.os == 'windows-latest' }}
|
||||
run: Compress-Archive target/${{ matrix.job.target }}/release/espup${{ matrix.job.binary-postfix }} espup-${{ matrix.job.target }}.zip
|
||||
|
||||
- name: Upload compressed artifact
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: espup-${{ matrix.job.target }}.zip
|
||||
tag: ${{ github.ref }}
|
||||
|
||||
- name: Upload binary artifact
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
@ -67,20 +63,18 @@ jobs:
|
||||
file: target/${{ matrix.job.target }}/release/espup${{ matrix.job.binary-postfix }}
|
||||
asset_name: espup-${{ matrix.job.target }}${{ matrix.job.binary-postfix }}
|
||||
tag: ${{ github.ref }}
|
||||
|
||||
# publish-cratesio:
|
||||
# name: Publishing to Crates.io
|
||||
# runs-on: ubuntu-20.04
|
||||
# steps:
|
||||
# - name: Checkout repository
|
||||
# uses: actions/checkout@v3
|
||||
# - uses: actions-rs/toolchain@v1
|
||||
# with:
|
||||
# toolchain: stable
|
||||
# profile: minimal
|
||||
# override: true
|
||||
# - uses: Swatinem/rust-cache@v2
|
||||
# - uses: actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: publish
|
||||
# args: --token ${{ secrets.CARGO_API_KEY }}
|
||||
publish-cratesio:
|
||||
name: Publishing to Crates.io
|
||||
needs: publish-release
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Enable caching
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: Cargo publish
|
||||
run: cargo publish --token ${{ secrets.CARGO_API_KEY }}
|
||||
|
137
Cargo.lock
generated
137
Cargo.lock
generated
@ -49,12 +49,6 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
|
||||
|
||||
[[package]]
|
||||
name = "assert_cmd"
|
||||
version = "2.0.8"
|
||||
@ -324,40 +318,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.14"
|
||||
@ -403,15 +363,6 @@ dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
@ -435,27 +386,6 @@ version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "embuild"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65388407a519276cd9aa94291380649d4cedbe8cf566d40f436f7a1ea9b5f8fe"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"dirs",
|
||||
"filetime",
|
||||
"log",
|
||||
"remove_dir_all 0.7.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shlex",
|
||||
"strum",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
@ -507,16 +437,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "espup"
|
||||
version = "0.2.9"
|
||||
version = "0.3.0-dev"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
"assert_fs",
|
||||
"async-trait",
|
||||
"clap",
|
||||
"console",
|
||||
"directories",
|
||||
"embuild",
|
||||
"env_logger",
|
||||
"flate2",
|
||||
"guess_host_triple",
|
||||
@ -1023,15 +951,6 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette"
|
||||
version = "5.5.0"
|
||||
@ -1410,28 +1329,6 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
@ -1484,19 +1381,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "882f368737489ea543bc5c340e6f3d34a28c39980bd9a979e47322b26f60ac40"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"num_cpus",
|
||||
"rayon",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.14"
|
||||
@ -1739,12 +1623,6 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
@ -1885,7 +1763,7 @@ dependencies = [
|
||||
"fastrand",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"remove_dir_all 0.5.3",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@ -2352,17 +2230,6 @@ dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
|
||||
dependencies = [
|
||||
"either",
|
||||
"libc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "espup"
|
||||
version = "0.2.9"
|
||||
version = "0.3.0-dev"
|
||||
authors = ["Sergio Gasquez Arcos <sergio.gasquez@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
@ -14,8 +14,7 @@ categories = ["command-line-utilities", "development-tools", "embedded"]
|
||||
rust-version = "1.64"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.69"
|
||||
clap = { version = "4.1.6", features = ["derive"] }
|
||||
clap = { version = "4.1.4", features = ["derive"] }
|
||||
flate2 = "1.0.25"
|
||||
guess_host_triple = "0.1.3"
|
||||
reqwest = { version = "0.11.14", features = ["blocking"] }
|
||||
@ -26,7 +25,6 @@ console = "0.15.5"
|
||||
tempfile = "3.3.0"
|
||||
log = "0.4.17"
|
||||
env_logger = "0.10.0"
|
||||
embuild = { version = "0.31.0", features = ["espidf", "git"] }
|
||||
strum = { version = "0.24", features = ["derive"] }
|
||||
toml = "0.7.2"
|
||||
directories = "4.0.1"
|
||||
|
147
README.md
147
README.md
@ -9,39 +9,26 @@
|
||||
|
||||
> `rustup` for [esp-rs](https://github.com/esp-rs/)
|
||||
|
||||
`espup` is a tool for installing and maintaining the required toolchains for
|
||||
developing applications in Rust for Espressif SoC's.
|
||||
`espup` is a tool for installing and maintaining the required toolchains for developing applications in Rust for Espressif SoC's.
|
||||
|
||||
To better understand what `espup` installs, see [`Rust on ESP targets` chapter of `The Rust on ESP Book`](https://esp-rs.github.io/book/installation/index.html)
|
||||
|
||||
## Requirements
|
||||
Before running or installing `espup`, make sure that the following dependencies are installed.
|
||||
### Windows
|
||||
|
||||
- [git](https://git-scm.com/download/win).
|
||||
- [Python](https://www.python.org/downloads/): Only required when installing ESP-IDF.
|
||||
|
||||
### Linux
|
||||
Before running or installing `espup`, make sure that [`rustup`](https://rustup.rs/) is installed. Linux systems also require the following packages:
|
||||
- Ubuntu/Debian
|
||||
```sh
|
||||
sudo apt-get install -y git python3 python3-pip gcc build-essential curl pkg-config libudev-dev
|
||||
sudo apt-get install -y gcc build-essential curl pkg-config
|
||||
```
|
||||
- `libudev-dev` is only required when installing `cargo-espflash`.
|
||||
- `python3` and `python3-pip` are only required when installing ESP-IDF.
|
||||
- Fedora
|
||||
```sh
|
||||
sudo dnf -y install git python3 python3-pip perl gcc systemd-devel
|
||||
sudo dnf -y install perl gcc
|
||||
```
|
||||
- `systemd-devel` is only required when installing `cargo-espflash`.
|
||||
- `python3` and `python3-pip` are only required when installing ESP-IDF.
|
||||
- `perl` is required to build openssl-sys
|
||||
- openSUSE Thumbleweed/Leap
|
||||
```
|
||||
sudo zypper install -y git gcc libudev-devel ninja python3 python3-pip make
|
||||
sudo zypper install -y gcc ninja make
|
||||
```
|
||||
- `libudev-devel` is only required when installing `cargo-espflash`.
|
||||
- `python3` and `python3-pip` are only required when installing ESP-IDF.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
@ -76,32 +63,31 @@ It's also possible to use [cargo-binstall](https://github.com/cargo-bins/cargo-b
|
||||
```
|
||||
|
||||
## Quickstart
|
||||
|
||||
See [Usage](#usage) section for more details.
|
||||
|
||||
### Install
|
||||
|
||||
```sh
|
||||
espup install
|
||||
# Unix
|
||||
. $HOME/export-esp.sh
|
||||
# Windows
|
||||
%USERPROFILE%\export-esp.ps1
|
||||
# Windows does not require sourcing any file
|
||||
```
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> The generated export file, by default `export-esp`, needs to be sourced in every terminal
|
||||
> before building an application.
|
||||
> The generated export file, by default `export-esp`, needs to be sourced in every terminal in Unix systems before building an application. In Windows, environment variables are automatically injected into your system and don't need to be sourced.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> The export file can be regenerated by running `espup install` again, in this case, it won't
|
||||
> install the components again as it would detect the current installation and reuse it. The
|
||||
> `-p/--config-path` option can be used to determine where to regenerate the export file.
|
||||
|
||||
### Uninstall
|
||||
|
||||
```sh
|
||||
espup uninstall
|
||||
```
|
||||
|
||||
### Update
|
||||
|
||||
```sh
|
||||
espup update
|
||||
```
|
||||
@ -127,32 +113,13 @@ Options:
|
||||
> **Note**
|
||||
>
|
||||
> #### Xtensa Rust destination path
|
||||
> Installation paths can be modified by setting the environment
|
||||
variables [`CARGO_HOME`](https://doc.rust-lang.org/cargo/reference/environment-variables.html)
|
||||
and [`RUSTUP_HOME`](https://rust-lang.github.io/rustup/environment-variables.html)
|
||||
before running the `install` command. By default, Xtensa Rust toolchain will be installed under `<rustup_home>/toolchains/esp`.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> #### ESP-IDF instalation
|
||||
> When building `std` applications, [ESP-IDF](https://github.com/espressif/esp-idf) is required, there are two ways of installing it:
|
||||
> - Using [`esp-idf-sys`](https://github.com/esp-rs/esp-idf-sys), a crate that all `std` applications
|
||||
> require (and its already included in our [`esp-idf-template`](https://github.com/esp-rs/esp-idf-template)).
|
||||
> - **We reccomend using this approach as its easier (you dont have to deal with ESP-IDF instalaltion and sourcing), and its more flexible.**
|
||||
> - Using `espup`. `espup install` has a `-e/--esp-idf-version` option that allows installing the desired ESP-IDF version. When using this argument:
|
||||
> - [`ldproxy`](https://github.com/esp-rs/embuild/tree/master/ldproxy), a tool required for all `std` projects, will be installed if not present already.
|
||||
> - The generated export file will include the necessary environment variables for ESP-IDF
|
||||
> - When building `std` applications that use the installed version of ESP-IDF, `esp-idf-sys` will
|
||||
> recognize there is an activated ESP-IDF environment and will use it instead of downloading and
|
||||
> installing it.
|
||||
> - This is very helpful when we want to build several projects with the same ESP-IDF version as it saves time and space.
|
||||
> - **Be aware that if you source the environment with an ESP-IDF version and you try to build and `std` application that uses a different version it will fail to build.**
|
||||
> Installation paths can be modified by setting the environment variables [`CARGO_HOME`](https://doc.rust-lang.org/cargo/reference/environment-variables.html) and [`RUSTUP_HOME`](https://rust-lang.github.io/rustup/environment-variables.html) before running the `install` command. By default, toolchains will be installed under `<rustup_home>/toolchains/esp`, although this can be changed using the `-a/--name` option.
|
||||
|
||||
```
|
||||
Usage: espup install [OPTIONS]
|
||||
|
||||
Options:
|
||||
-p, --config-path <CONFIG_PATH>
|
||||
-c, --config-path <CONFIG_PATH>
|
||||
Path to where the espup configuration file will be written to
|
||||
|
||||
-d, --default-host <DEFAULT_HOST>
|
||||
@ -160,34 +127,13 @@ Options:
|
||||
|
||||
[possible values: x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, x86_64-pc-windows-msvc, x86_64-pc-windows-gnu, x86_64-apple-darwin, aarch64-apple-darwin]
|
||||
|
||||
-e, --esp-idf-version <ESP_IDF_VERSION>
|
||||
ESP-IDF version to install. If empty, no ESP-IDF is installed. ESP-IDF installation can also be managed by esp-idf-sys(https://github.com/esp-rs/esp-idf-sys).
|
||||
|
||||
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.
|
||||
|
||||
When using this option, `ldproxy` crate will also be installed.
|
||||
|
||||
-f, --export-file <EXPORT_FILE>
|
||||
Relative or full path for the export file that will be generated. If no path is provided, the file will be generated under home directory (https://docs.rs/dirs/latest/dirs/fn.home_dir.html)
|
||||
|
||||
-c, --extra-crates <EXTRA_CRATES>
|
||||
Comma or space list of extra crates to install
|
||||
-e, --extended-llvm
|
||||
Extends the LLVM installation.
|
||||
|
||||
-x, --llvm-version <LLVM_VERSION>
|
||||
LLVM version
|
||||
|
||||
[default: 15]
|
||||
[possible values: 15]
|
||||
This will install the whole LLVM instead of only installing the libs.
|
||||
|
||||
-l, --log-level <LOG_LEVEL>
|
||||
Verbosity level of the logs
|
||||
@ -195,15 +141,20 @@ Options:
|
||||
[default: info]
|
||||
[possible values: debug, info, warn, error]
|
||||
|
||||
-a, --name <NAME>
|
||||
Xtensa Rust toolchain name
|
||||
|
||||
[default: esp]
|
||||
|
||||
-n, --nightly-version <NIGHTLY_VERSION>
|
||||
Nightly Rust toolchain version
|
||||
|
||||
[default: nightly]
|
||||
|
||||
-m, --profile-minimal
|
||||
Minifies the installation.
|
||||
-s, --std
|
||||
Only install toolchains required for STD applications.
|
||||
|
||||
This will install a reduced version of LLVM, delete the folder where all the assets are downloaded, and, if installing ESP-IDF, delete some unnecessary folders like docs and examples.
|
||||
With this option, espup will skip GCC installation (it will be handled by esp-idf-sys), hence you won't be able to build no_std applications.
|
||||
|
||||
-t, --targets <TARGETS>
|
||||
Comma or space separated list of targets [esp32,esp32s2,esp32s3,esp32c2,esp32c3,all]
|
||||
@ -214,10 +165,10 @@ Options:
|
||||
Xtensa Rust toolchain version
|
||||
|
||||
-h, --help
|
||||
Print help information (use `-h` for a summary)
|
||||
Print help (see a summary with '-h')
|
||||
|
||||
-V, --version
|
||||
Print version information
|
||||
Print version
|
||||
```
|
||||
|
||||
### Uninstall Subcommand
|
||||
@ -226,10 +177,11 @@ Options:
|
||||
Usage: espup uninstall [OPTIONS]
|
||||
|
||||
Options:
|
||||
-p, --config-path <CONFIG_PATH> Path to where the espup configuration file will be written to
|
||||
-l, --log-level <LOG_LEVEL> Verbosity level of the logs [default: info] [possible values: debug, info, warn, error]
|
||||
-h, --help Print help information
|
||||
-V, --version Print version information
|
||||
-c, --config-path <CONFIG_PATH> Path to where the espup configuration file will be written to
|
||||
-l, --log-level <LOG_LEVEL> Verbosity level of the logs [default: info] [possible values: debug, info, warn, error]
|
||||
-a, --name <NAME> Xtensa Rust toolchain name [default: esp]
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
```
|
||||
|
||||
### Update Subcommand
|
||||
@ -238,41 +190,22 @@ Options:
|
||||
Usage: espup update [OPTIONS]
|
||||
|
||||
Options:
|
||||
-p, --config-path <CONFIG_PATH>
|
||||
-c, --config-path <CONFIG_PATH>
|
||||
Path to where the espup configuration file will be written to
|
||||
-d, --default-host <DEFAULT_HOST>
|
||||
Target triple of the host [possible values: x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, x86_64-pc-windows-msvc, x86_64-pc-windows-gnu, x86_64-apple-darwin, aarch64-apple-darwin]
|
||||
-l, --log-level <LOG_LEVEL>
|
||||
Verbosity level of the logs [default: info] [possible values: debug, info, warn, error]
|
||||
-a, --name <NAME>
|
||||
Xtensa Rust toolchain name [default: esp]
|
||||
-v, --toolchain-version <TOOLCHAIN_VERSION>
|
||||
Xtensa Rust toolchain version
|
||||
-h, --help
|
||||
Print help information
|
||||
Print help
|
||||
-V, --version
|
||||
Print version information
|
||||
Print version
|
||||
```
|
||||
|
||||
## Known Issues or Limitations
|
||||
|
||||
- If installing esp-idf in Windows, only `all` targets is allowed.
|
||||
- In Windows, when installing esp-idf fails with:
|
||||
```
|
||||
ERROR: Could not find a version that satisfies the requirement windows-curses; sys_platform == "win32" (from esp-windows-curses) (from versions: none)
|
||||
ERROR: No matching distribution found for windows-curses; sys_platform == "win32"
|
||||
Traceback (most recent call last):
|
||||
File "<home_dir>/.espressif\esp-idf-ae062fbba3ded0aa\release-v4.4\tools\idf_tools.py", line 1973, in <module>
|
||||
main(sys.argv[1:])
|
||||
File "<home_dir>/.espressif\esp-idf-ae062fbba3ded0aa\release-v4.4\tools\idf_tools.py", line 1969, in main
|
||||
action_func(args)
|
||||
File "<home_dir>/.espressif\esp-idf-ae062fbba3ded0aa\release-v4.4\tools\idf_tools.py", line 1619, in action_install_python_env
|
||||
subprocess.check_call(run_args, stdout=sys.stdout, stderr=sys.stderr, env=env_copy)
|
||||
File "C:\Python311\Lib\subprocess.py", line 413, in check_call
|
||||
raise CalledProcessError(retcode, cmd)
|
||||
subprocess.CalledProcessError: Command '['<home_dir>/.espressif\\python_env\\idf4.4_py3.11_env\\Scripts\\python.exe', '-m', 'pip', 'install', '--no-warn-script-location', '-r', <home_dir>/.espressif\\esp-idf-ae062fbba3ded0aa\\release-v4.4\\requirements.txt', '--extra-index-url', 'https://dl.espressif.com/pypi']' returned non-zero exit status 1.
|
||||
Error: Could not install esp-idf
|
||||
```
|
||||
*_Solution_*: Use a python version between `3.6` and `3.10` as `3.11` Python wheels are not yet released for Windows.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of:
|
||||
@ -284,6 +217,4 @@ at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
|
||||
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
|
||||
any additional terms or conditions.
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
|
||||
|
@ -1,88 +0,0 @@
|
||||
use crate::{
|
||||
emoji, error::Error, host_triple::HostTriple, targets::Target, toolchain::rust::XtensaRust,
|
||||
};
|
||||
use directories::ProjectDirs;
|
||||
use log::info;
|
||||
use miette::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fs::{create_dir_all, read, remove_file, write},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
/// Deserialized contents of a configuration file
|
||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||
pub struct Config {
|
||||
/// ESP-IDF version
|
||||
pub esp_idf_version: Option<String>,
|
||||
/// Destination of the generated export file.
|
||||
pub export_file: Option<PathBuf>,
|
||||
/// Extra crates to installed.
|
||||
pub extra_crates: HashSet<String>,
|
||||
/// Host triple
|
||||
pub host_triple: HostTriple,
|
||||
/// LLVM toolchain path.
|
||||
pub llvm_path: Option<PathBuf>,
|
||||
/// Nightly Rust toolchain version.
|
||||
pub nightly_version: String,
|
||||
/// List of targets instaled.
|
||||
pub targets: HashSet<Target>,
|
||||
/// Xtensa Rust toolchain.
|
||||
pub xtensa_rust: Option<XtensaRust>,
|
||||
}
|
||||
|
||||
pub struct ConfigFile {
|
||||
pub path: PathBuf,
|
||||
pub config: Config,
|
||||
}
|
||||
|
||||
impl ConfigFile {
|
||||
/// Construcs a new config file with the given path and config
|
||||
pub fn new(config_path: &Option<PathBuf>, config: Config) -> Result<Self, Error> {
|
||||
let config_path = config_path.clone().unwrap_or(Self::get_config_path()?);
|
||||
Ok(ConfigFile {
|
||||
path: config_path,
|
||||
config,
|
||||
})
|
||||
}
|
||||
|
||||
/// Load the config from config file
|
||||
pub fn load(config_path: &Option<PathBuf>) -> Result<Self, Error> {
|
||||
let config_path = config_path.clone().unwrap_or(Self::get_config_path()?);
|
||||
let config: Config = if let Ok(data) = read(&config_path) {
|
||||
toml::from_str(std::str::from_utf8(&data).unwrap())
|
||||
.map_err(|_| Error::FailedToDeserialize)?
|
||||
} else {
|
||||
return Err(Error::FileNotFound(
|
||||
config_path.to_string_lossy().into_owned(),
|
||||
));
|
||||
};
|
||||
Self::new(&Some(config_path), config)
|
||||
}
|
||||
|
||||
/// Save the config to file
|
||||
pub fn save(&self) -> Result<(), Error> {
|
||||
let serialized =
|
||||
toml::to_string(&self.config.clone()).map_err(|_| Error::FailedToSerialize)?;
|
||||
create_dir_all(self.path.parent().unwrap()).map_err(|_| Error::FailedToCreateConfigFile)?;
|
||||
write(&self.path, serialized)
|
||||
.map_err(|_| Error::FailedToWrite(self.path.display().to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete the config file
|
||||
pub fn delete(&self) -> Result<(), Error> {
|
||||
info!("{} Deleting config file", emoji::WRENCH);
|
||||
remove_file(&self.path)
|
||||
.map_err(|_| Error::FailedToRemoveFile(self.path.display().to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets the default path to the configuration file.
|
||||
pub fn get_config_path() -> Result<PathBuf, Error> {
|
||||
let dirs = ProjectDirs::from("rs", "esp", "espup").unwrap();
|
||||
let file = dirs.config_dir().join("espup.toml");
|
||||
Ok(file)
|
||||
}
|
||||
}
|
141
src/error.rs
141
src/error.rs
@ -2,92 +2,77 @@ use crate::emoji;
|
||||
|
||||
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
|
||||
pub enum Error {
|
||||
// Host Triple
|
||||
#[diagnostic(code(espup::host_triple::unsupported_host_triple))]
|
||||
#[error("{} Host triple '{0}' is not supported", emoji::ERROR)]
|
||||
UnsupportedHostTriple(String),
|
||||
// Target
|
||||
#[diagnostic(code(espup::targets::unsupported_target))]
|
||||
#[error("{} Target '{0}' is not supported", emoji::ERROR)]
|
||||
UnsupportedTarget(String),
|
||||
// Config
|
||||
#[diagnostic(code(espup::config::file_not_found))]
|
||||
#[error("{} No config file found in '{0}'", emoji::ERROR)]
|
||||
FileNotFound(String),
|
||||
#[diagnostic(code(espup::config::failed_to_deserialize))]
|
||||
#[error("{} Failed to deserialize config", emoji::ERROR)]
|
||||
FailedToDeserialize,
|
||||
#[diagnostic(code(espup::config::failed_to_serialize))]
|
||||
#[error("{} Failed to serialize config", emoji::ERROR)]
|
||||
FailedToSerialize,
|
||||
#[diagnostic(code(espup::config::failed_to_create_config_file))]
|
||||
#[error("{} Failed to create config directory", emoji::ERROR)]
|
||||
FailedToCreateConfigFile,
|
||||
#[diagnostic(code(espup::config::failed_to_write))]
|
||||
#[error("{} Failed to write config to '{0}'", emoji::ERROR)]
|
||||
FailedToWrite(String),
|
||||
// Toolchain
|
||||
#[error(transparent)]
|
||||
IoError(#[from] std::io::Error),
|
||||
#[error(transparent)]
|
||||
RewquestError(#[from] reqwest::Error),
|
||||
#[diagnostic(code(espup::toolchain::failed_to_create_directory))]
|
||||
#[diagnostic(code(espup::toolchain::create_directory))]
|
||||
#[error("{} Creating directory '{0}' failed", emoji::ERROR)]
|
||||
FailedToCreateDirectory(String),
|
||||
#[diagnostic(code(espup::toolchain::unsupported_file_extension))]
|
||||
#[error("{} Unsuported file extension: '{0}'", emoji::ERROR)]
|
||||
UnsuportedFileExtension(String),
|
||||
// Toolchain - Rust
|
||||
#[diagnostic(code(espup::toolchain::rust::failed_to_query_github))]
|
||||
#[error("{} Failed To Query GitHub API.", emoji::ERROR)]
|
||||
FailedGithubQuery,
|
||||
#[diagnostic(code(espup::toolchain::rust::failed_to_get_latest_version))]
|
||||
#[error("{} Failed To serialize Json from string.", emoji::ERROR)]
|
||||
FailedToSerializeJson,
|
||||
CreateDirectory(String),
|
||||
|
||||
#[diagnostic(code(espup::toolchain::rust::query_github))]
|
||||
#[error("{} Failed to query GitHub API.", emoji::ERROR)]
|
||||
GithubQuery,
|
||||
|
||||
#[diagnostic(code(espup::toolchain::rust::install_xtensa_rust))]
|
||||
#[error("{} Failed to Install Xtensa Rust toolchain.", emoji::ERROR)]
|
||||
InstallXtensaRust,
|
||||
|
||||
#[diagnostic(code(espup::toolchain::rust::install_riscv_target))]
|
||||
#[error(
|
||||
"{} Failed to Install RISC-V targets for '{0}' toolchain.",
|
||||
emoji::ERROR
|
||||
)]
|
||||
InstallRiscvTarget(String),
|
||||
|
||||
#[diagnostic(code(espup::ivalid_destination))]
|
||||
#[error(
|
||||
"{} Invalid export file destination: '{0}'. Please, use an absolute or releative path (including the file and its extension).",
|
||||
emoji::ERROR
|
||||
)]
|
||||
InvalidDestination(String),
|
||||
|
||||
#[diagnostic(code(espup::toolchain::rust::invalid_version))]
|
||||
#[error(
|
||||
"{} Invalid toolchain version '{0}'. Verify that the format is correct: '<major>.<minor>.<patch>.<subpatch>' or '<major>.<minor>.<patch>', and that the release exists in https://github.com/esp-rs/rust-build/releases",
|
||||
emoji::ERROR
|
||||
)]
|
||||
InvalidXtensaToolchanVersion(String),
|
||||
#[diagnostic(code(espup::toolchain::rust::detection_error))]
|
||||
#[error("{} Error detecting rustup: {0}", emoji::ERROR)]
|
||||
RustupDetectionError(String),
|
||||
InvalidVersion(String),
|
||||
|
||||
#[error(transparent)]
|
||||
CmdError(#[from] embuild::cmd::CmdError),
|
||||
// Toolchain - ESP-IDF
|
||||
#[diagnostic(code(espup::toolchain::espidf::failed_to_instatiate_cmake))]
|
||||
#[error("{} Failed to add CMake to ESP-IDF tools", emoji::ERROR)]
|
||||
FailedToInstantiateCmake,
|
||||
#[diagnostic(code(espup::toolchain::espidf::failed_to_create_esp_idf_install_closure))]
|
||||
#[error("{} Failed to create ESP-IDF install closure", emoji::ERROR)]
|
||||
FailedToCreateEspIdfInstallClosure,
|
||||
#[diagnostic(code(espup::toolchain::espidf::failed_to_install_esp_idf))]
|
||||
#[error("{} Failed to install ESP-IDF", emoji::ERROR)]
|
||||
FailedToInstallEspIdf,
|
||||
// Main
|
||||
#[diagnostic(code(espup::wrong_windows_arguments))]
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
#[diagnostic(code(espup::toolchain::rust::missing_rust))]
|
||||
#[error(
|
||||
"{} When installing esp-idf in Windows, only --targets \"all\" is supported.",
|
||||
"{} Rust is not installed. Please, install Rust via rustup: https://rustup.rs/",
|
||||
emoji::ERROR
|
||||
)]
|
||||
WrongWindowsArguments,
|
||||
#[diagnostic(code(espup::failed_to_remove_directory))]
|
||||
#[error(
|
||||
"{} Failed to remove '{0}' directory. Please, manually verify that the directory is properly removed and run 'espup uninstall' again.",
|
||||
emoji::ERROR
|
||||
)]
|
||||
FailedToRemoveDirectory(String),
|
||||
#[diagnostic(code(espup::failed_to_remove_file))]
|
||||
#[error(
|
||||
"{} Failed to remove '{0}' file. Please, manually verify that the file is properly removed.",
|
||||
emoji::ERROR
|
||||
)]
|
||||
FailedToRemoveFile(String),
|
||||
#[diagnostic(code(espup::wrong_export_file))]
|
||||
#[error(
|
||||
"{} Wrong export file destination: '{0}'. Please, use an absolte or releative path (including the file and its extension).",
|
||||
emoji::ERROR
|
||||
)]
|
||||
WrongExportFile(String),
|
||||
MissingRust,
|
||||
|
||||
#[diagnostic(code(espup::remove_directory))]
|
||||
#[error("{} Failed to remove '{0}' directory.", emoji::ERROR)]
|
||||
RemoveDirectory(String),
|
||||
|
||||
#[error(transparent)]
|
||||
RewquestError(#[from] reqwest::Error),
|
||||
|
||||
#[diagnostic(code(espup::toolchain::rust::rustup_detection_error))]
|
||||
#[error("{} Error detecting rustup: {0}", emoji::ERROR)]
|
||||
RustupDetection(String),
|
||||
|
||||
#[diagnostic(code(espup::toolchain::rust::serialize_json))]
|
||||
#[error("{} Failed to serialize json from string.", emoji::ERROR)]
|
||||
SerializeJson,
|
||||
|
||||
#[diagnostic(code(espup::toolchain::rust::uninstall_riscv_target))]
|
||||
#[error("{} Failed to uninstall RISC-V target.", emoji::ERROR)]
|
||||
UninstallRiscvTarget,
|
||||
|
||||
#[diagnostic(code(espup::toolchain::unsupported_file_extension))]
|
||||
#[error("{} Unsuported file extension: '{0}'", emoji::ERROR)]
|
||||
UnsuportedFileExtension(String),
|
||||
|
||||
#[diagnostic(code(espup::host_triple::unsupported_host_triple))]
|
||||
#[error("{} Host triple '{0}' is not supported", emoji::ERROR)]
|
||||
UnsupportedHostTriple(String),
|
||||
|
||||
#[diagnostic(code(espup::targets::unsupported_target))]
|
||||
#[error("{} Target '{0}' is not supported", emoji::ERROR)]
|
||||
UnsupportedTarget(String),
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
pub mod config;
|
||||
pub mod emoji;
|
||||
pub mod error;
|
||||
pub mod host_triple;
|
||||
|
347
src/main.rs
347
src/main.rs
@ -1,26 +1,27 @@
|
||||
use clap::Parser;
|
||||
use directories::BaseDirs;
|
||||
use espup::{
|
||||
config::{Config, ConfigFile},
|
||||
emoji,
|
||||
error::Error,
|
||||
host_triple::get_host_triple,
|
||||
logging::initialize_logger,
|
||||
targets::{parse_targets, Target},
|
||||
toolchain::{
|
||||
espidf::{get_dist_path, EspIdfRepo},
|
||||
gcc::Gcc,
|
||||
gcc::{uninstall_gcc_toolchains, Gcc},
|
||||
llvm::Llvm,
|
||||
rust::{check_rust_installation, Crate, RiscVTarget, XtensaRust},
|
||||
rust::{check_rust_installation, get_rustup_home, RiscVTarget, XtensaRust},
|
||||
Installable,
|
||||
},
|
||||
update::check_for_update,
|
||||
};
|
||||
use log::{debug, info, warn};
|
||||
use miette::Result;
|
||||
#[cfg(windows)]
|
||||
use std::process::{Command, Stdio};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fs::{remove_dir_all, remove_file, File},
|
||||
env,
|
||||
fs::{remove_dir_all, File},
|
||||
io::Write,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
@ -48,61 +49,45 @@ struct Cli {
|
||||
|
||||
#[derive(Parser)]
|
||||
pub enum SubCommand {
|
||||
/// Installs esp-rs environment
|
||||
/// Installs Rust on ESPs ecosystem.
|
||||
// We use a Box here to make clippy happy (see https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant)
|
||||
Install(Box<InstallOpts>),
|
||||
/// Uninstalls esp-rs environment
|
||||
/// Uninstalls Rust on ESPs ecosystem.
|
||||
Uninstall(UninstallOpts),
|
||||
/// Updates Xtensa Rust toolchain
|
||||
/// Updates Xtensa Rust toolchain.
|
||||
Update(UpdateOpts),
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct InstallOpts {
|
||||
/// Path to where the espup configuration file will be written to.
|
||||
#[arg(short = 'p', long)]
|
||||
#[arg(short = 'c', long)]
|
||||
pub config_path: Option<PathBuf>,
|
||||
/// Target triple of the host.
|
||||
#[arg(short = 'd', long, required = false, value_parser = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "x86_64-pc-windows-msvc", "x86_64-pc-windows-gnu" , "x86_64-apple-darwin" , "aarch64-apple-darwin"])]
|
||||
#[arg(short = 'd', long, value_parser = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "x86_64-pc-windows-msvc", "x86_64-pc-windows-gnu" , "x86_64-apple-darwin" , "aarch64-apple-darwin"])]
|
||||
pub default_host: Option<String>,
|
||||
/// ESP-IDF version to install. If empty, no ESP-IDF is installed. ESP-IDF installation can also be managed by esp-idf-sys(https://github.com/esp-rs/esp-idf-sys).
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// When using this option, `ldproxy` crate will also be installed.
|
||||
#[arg(short = 'e', long, required = false)]
|
||||
pub esp_idf_version: Option<String>,
|
||||
/// Relative or full path for the export file that will be generated. If no path is provided, the file will be generated under home directory (https://docs.rs/dirs/latest/dirs/fn.home_dir.html).
|
||||
#[arg(short = 'f', long)]
|
||||
pub export_file: Option<PathBuf>,
|
||||
/// Comma or space list of extra crates to install.
|
||||
#[arg(short = 'c', long, default_value = "", value_parser = Crate::parse_crates)]
|
||||
pub extra_crates: HashSet<Crate>,
|
||||
/// LLVM version.
|
||||
#[arg(short = 'x', long, default_value = "15", value_parser = ["15"])]
|
||||
pub llvm_version: String,
|
||||
/// Extends the LLVM installation.
|
||||
///
|
||||
/// This will install the whole LLVM instead of only installing the libs.
|
||||
#[arg(short = 'e', long)]
|
||||
pub extended_llvm: bool,
|
||||
/// Verbosity level of the logs.
|
||||
#[arg(short = 'l', long, default_value = "info", value_parser = ["debug", "info", "warn", "error"])]
|
||||
pub log_level: String,
|
||||
/// Xtensa Rust toolchain name.
|
||||
#[arg(short = 'a', long, default_value = "esp")]
|
||||
pub name: String,
|
||||
/// Nightly Rust toolchain version.
|
||||
#[arg(short = 'n', long, default_value = "nightly")]
|
||||
pub nightly_version: String,
|
||||
/// Minifies the installation.
|
||||
/// Only install toolchains required for STD applications.
|
||||
///
|
||||
/// This will install a reduced version of LLVM, delete the folder where all the assets are downloaded,
|
||||
/// and, if installing ESP-IDF, delete some unnecessary folders like docs and examples.
|
||||
#[arg(short = 'm', long)]
|
||||
pub profile_minimal: bool,
|
||||
/// With this option, espup will skip GCC installation (it will be handled by esp-idf-sys), hence you won't be able to build no_std applications.
|
||||
#[arg(short = 's', long)]
|
||||
pub std: bool,
|
||||
/// Comma or space separated list of targets [esp32,esp32s2,esp32s3,esp32c2,esp32c3,all].
|
||||
#[arg(short = 't', long, default_value = "all", value_parser = parse_targets)]
|
||||
pub targets: HashSet<Target>,
|
||||
@ -114,14 +99,17 @@ pub struct InstallOpts {
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct UpdateOpts {
|
||||
/// Path to where the espup configuration file will be written to.
|
||||
#[arg(short = 'p', long)]
|
||||
#[arg(short = 'c', long)]
|
||||
pub config_path: Option<PathBuf>,
|
||||
/// Target triple of the host.
|
||||
#[arg(short = 'd', long, required = false, value_parser = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "x86_64-pc-windows-msvc", "x86_64-pc-windows-gnu" , "x86_64-apple-darwin" , "aarch64-apple-darwin"])]
|
||||
#[arg(short = 'd', long, value_parser = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "x86_64-pc-windows-msvc", "x86_64-pc-windows-gnu" , "x86_64-apple-darwin" , "aarch64-apple-darwin"])]
|
||||
pub default_host: Option<String>,
|
||||
/// Verbosity level of the logs.
|
||||
#[arg(short = 'l', long, default_value = "info", value_parser = ["debug", "info", "warn", "error"])]
|
||||
pub log_level: String,
|
||||
/// Xtensa Rust toolchain name.
|
||||
#[arg(short = 'a', long, default_value = "esp")]
|
||||
pub name: String,
|
||||
/// Xtensa Rust toolchain version.
|
||||
#[arg(short = 'v', long, value_parser = XtensaRust::parse_version)]
|
||||
pub toolchain_version: Option<String>,
|
||||
@ -130,69 +118,66 @@ pub struct UpdateOpts {
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct UninstallOpts {
|
||||
/// Path to where the espup configuration file will be written to.
|
||||
#[arg(short = 'p', long)]
|
||||
#[arg(short = 'c', long)]
|
||||
pub config_path: Option<PathBuf>,
|
||||
/// Verbosity level of the logs.
|
||||
#[arg(short = 'l', long, default_value = "info", value_parser = ["debug", "info", "warn", "error"])]
|
||||
pub log_level: String,
|
||||
/// Xtensa Rust toolchain name.
|
||||
#[arg(short = 'a', long, default_value = "esp")]
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// Installs the Rust for ESP chips environment
|
||||
async fn install(args: InstallOpts) -> Result<()> {
|
||||
initialize_logger(&args.log_level);
|
||||
check_for_update(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
|
||||
info!("{} Installing esp-rs", emoji::DISC);
|
||||
let targets = args.targets;
|
||||
let host_triple = get_host_triple(args.default_host)?;
|
||||
let mut extra_crates = args.extra_crates;
|
||||
|
||||
info!("{} Installing the Espressif Rust ecosystem", emoji::DISC);
|
||||
|
||||
let export_file = get_export_file(args.export_file)?;
|
||||
let mut exports: Vec<String> = Vec::new();
|
||||
let host_triple = get_host_triple(args.default_host)?;
|
||||
let install_path = get_rustup_home().join("toolchains").join(args.name);
|
||||
let llvm = Llvm::new(&install_path, &host_triple, args.extended_llvm)?;
|
||||
let targets = args.targets;
|
||||
let xtensa_rust = if targets.contains(&Target::ESP32)
|
||||
|| targets.contains(&Target::ESP32S2)
|
||||
|| targets.contains(&Target::ESP32S3)
|
||||
{
|
||||
let xtensa_rust: XtensaRust = if let Some(toolchain_version) = &args.toolchain_version {
|
||||
XtensaRust::new(toolchain_version, &host_triple)
|
||||
XtensaRust::new(toolchain_version, &host_triple, &install_path)
|
||||
} else {
|
||||
let latest_version = XtensaRust::get_latest_version().await?;
|
||||
XtensaRust::new(&latest_version, &host_triple)
|
||||
XtensaRust::new(&latest_version, &host_triple, &install_path)
|
||||
};
|
||||
Some(xtensa_rust)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let export_file = get_export_file(args.export_file)?;
|
||||
let llvm = Llvm::new(args.llvm_version, args.profile_minimal, &host_triple);
|
||||
let llvm_path = Some(llvm.path.clone());
|
||||
|
||||
debug!(
|
||||
"{} Arguments:
|
||||
- Host triple: {}
|
||||
- Targets: {:?}
|
||||
- ESP-IDF version: {:?}
|
||||
- Export file: {:?}
|
||||
- Extra crates: {:?}
|
||||
- Host triple: {}
|
||||
- LLVM Toolchain: {:?}
|
||||
- Nightly version: {:?}
|
||||
- Rust Toolchain: {:?}
|
||||
- Profile Minimal: {:?}
|
||||
- Targets: {:?}
|
||||
- Toolchain path: {:?}
|
||||
- Toolchain version: {:?}",
|
||||
emoji::INFO,
|
||||
host_triple,
|
||||
targets,
|
||||
&args.esp_idf_version,
|
||||
&export_file,
|
||||
&extra_crates,
|
||||
host_triple,
|
||||
&llvm,
|
||||
&args.nightly_version,
|
||||
xtensa_rust,
|
||||
args.profile_minimal,
|
||||
targets,
|
||||
&install_path,
|
||||
args.toolchain_version,
|
||||
);
|
||||
|
||||
#[cfg(windows)]
|
||||
check_arguments(&targets, &args.esp_idf_version)?;
|
||||
|
||||
check_rust_installation(&args.nightly_version, &host_triple).await?;
|
||||
check_rust_installation().await?;
|
||||
|
||||
// Build up a vector of installable applications, all of which implement the
|
||||
// `Installable` async trait.
|
||||
@ -209,30 +194,21 @@ async fn install(args: InstallOpts) -> Result<()> {
|
||||
to_install.push(Box::new(riscv_target));
|
||||
}
|
||||
|
||||
if let Some(esp_idf_version) = &args.esp_idf_version {
|
||||
let repo = EspIdfRepo::new(esp_idf_version, args.profile_minimal, &targets);
|
||||
to_install.push(Box::new(repo));
|
||||
|
||||
extra_crates.insert(Crate::new("ldproxy"));
|
||||
} else {
|
||||
if !args.std {
|
||||
targets.iter().for_each(|target| {
|
||||
if target.xtensa() {
|
||||
let gcc = Gcc::new(target, &host_triple);
|
||||
let gcc = Gcc::new(target, &host_triple, &install_path);
|
||||
to_install.push(Box::new(gcc));
|
||||
}
|
||||
});
|
||||
// All RISC-V targets use the same GCC toolchain
|
||||
// ESP32S2 and ESP32S3 also install the RISC-V toolchain for their ULP coprocessor
|
||||
if targets.iter().any(|t| t != &Target::ESP32) {
|
||||
let riscv_gcc = Gcc::new_riscv(&host_triple);
|
||||
let riscv_gcc = Gcc::new_riscv(&host_triple, &install_path);
|
||||
to_install.push(Box::new(riscv_gcc));
|
||||
}
|
||||
}
|
||||
|
||||
for extra_crate in &extra_crates {
|
||||
to_install.push(Box::new(extra_crate.to_owned()));
|
||||
}
|
||||
|
||||
// With a list of applications to install, install them all in parallel.
|
||||
let installable_items = to_install.len();
|
||||
let (tx, mut rx) = mpsc::channel::<Result<Vec<String>, Error>>(installable_items);
|
||||
@ -262,33 +238,8 @@ async fn install(args: InstallOpts) -> Result<()> {
|
||||
exports.extend(names);
|
||||
}
|
||||
|
||||
if args.profile_minimal {
|
||||
clear_dist_folder()?;
|
||||
}
|
||||
|
||||
create_export_file(&export_file, &exports)?;
|
||||
|
||||
let config = Config {
|
||||
esp_idf_version: args.esp_idf_version,
|
||||
export_file: Some(export_file.clone()),
|
||||
extra_crates: extra_crates
|
||||
.iter()
|
||||
.map(|x| x.name.clone())
|
||||
.collect::<HashSet<_>>(),
|
||||
host_triple,
|
||||
llvm_path,
|
||||
nightly_version: args.nightly_version,
|
||||
targets,
|
||||
xtensa_rust,
|
||||
};
|
||||
let config_file = ConfigFile::new(&args.config_path, config)?;
|
||||
info!(
|
||||
"{} Storing configuration file at '{:?}'",
|
||||
emoji::WRENCH,
|
||||
config_file.path
|
||||
);
|
||||
config_file.save()?;
|
||||
|
||||
info!("{} Installation successfully completed!", emoji::CHECK);
|
||||
export_environment(&export_file)?;
|
||||
Ok(())
|
||||
@ -299,83 +250,30 @@ async fn uninstall(args: UninstallOpts) -> Result<()> {
|
||||
initialize_logger(&args.log_level);
|
||||
check_for_update(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
|
||||
|
||||
info!("{} Uninstalling esp-rs", emoji::DISC);
|
||||
let mut config_file = ConfigFile::load(&args.config_path)?;
|
||||
info!("{} Uninstalling the Espressif Rust ecosystem", emoji::DISC);
|
||||
|
||||
debug!(
|
||||
"{} Arguments:
|
||||
- Config: {:#?}",
|
||||
emoji::INFO,
|
||||
config_file.config
|
||||
let install_path = get_rustup_home().join("toolchains").join(args.name);
|
||||
|
||||
Llvm::uninstall(&install_path)?;
|
||||
|
||||
uninstall_gcc_toolchains(&install_path)?;
|
||||
|
||||
info!(
|
||||
"{} Deleting the Xtensa Rust toolchain located in '{}'",
|
||||
emoji::DISC,
|
||||
&install_path.display()
|
||||
);
|
||||
remove_dir_all(&install_path)
|
||||
.map_err(|_| Error::RemoveDirectory(install_path.display().to_string()))?;
|
||||
|
||||
if let Some(xtensa_rust) = config_file.config.xtensa_rust {
|
||||
config_file.config.xtensa_rust = None;
|
||||
config_file.save()?;
|
||||
xtensa_rust.uninstall()?;
|
||||
#[cfg(windows)]
|
||||
if cfg!(windows) {
|
||||
Command::new("setx")
|
||||
.args(["PATH", &std::env::var("PATH").unwrap(), "/m"])
|
||||
.output()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if let Some(llvm_path) = config_file.config.llvm_path {
|
||||
let llvm_path = llvm_path.parent().unwrap();
|
||||
config_file.config.llvm_path = None;
|
||||
config_file.save()?;
|
||||
Llvm::uninstall(llvm_path)?;
|
||||
}
|
||||
|
||||
if config_file.config.targets.iter().any(|t| t.riscv()) {
|
||||
RiscVTarget::uninstall(&config_file.config.nightly_version)?;
|
||||
}
|
||||
|
||||
if let Some(esp_idf_version) = config_file.config.esp_idf_version {
|
||||
config_file.config.esp_idf_version = None;
|
||||
config_file.save()?;
|
||||
EspIdfRepo::uninstall(&esp_idf_version)?;
|
||||
} else {
|
||||
info!("{} Deleting GCC targets", emoji::WRENCH);
|
||||
if config_file
|
||||
.config
|
||||
.targets
|
||||
.iter()
|
||||
.any(|t| t != &Target::ESP32)
|
||||
{
|
||||
// All RISC-V targets use the same GCC toolchain
|
||||
// ESP32S2 and ESP32S3 also install the RISC-V toolchain for their ULP coprocessor
|
||||
config_file.config.targets.remove(&Target::ESP32C3);
|
||||
config_file.config.targets.remove(&Target::ESP32C2);
|
||||
config_file.save()?;
|
||||
Gcc::uninstall_riscv()?;
|
||||
}
|
||||
for target in &config_file.config.targets.clone() {
|
||||
if target.xtensa() {
|
||||
config_file.config.targets.remove(target);
|
||||
config_file.save()?;
|
||||
Gcc::uninstall(target)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !config_file.config.extra_crates.is_empty() {
|
||||
info!("{} Uninstalling extra crates", emoji::WRENCH);
|
||||
let mut updated_extra_crates = config_file.config.extra_crates.clone();
|
||||
for extra_crate in &config_file.config.extra_crates.clone() {
|
||||
updated_extra_crates.remove(extra_crate);
|
||||
config_file.config.extra_crates = updated_extra_crates.clone();
|
||||
config_file.save()?;
|
||||
Crate::uninstall(extra_crate)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(export_file) = config_file.config.export_file {
|
||||
info!("{} Deleting export file", emoji::WRENCH);
|
||||
config_file.config.export_file = None;
|
||||
config_file.save()?;
|
||||
remove_file(&export_file)
|
||||
.map_err(|_| Error::FailedToRemoveFile(export_file.display().to_string()))?;
|
||||
}
|
||||
|
||||
clear_dist_folder()?;
|
||||
config_file.delete()?;
|
||||
|
||||
info!("{} Uninstallation successfully completed!", emoji::CHECK);
|
||||
Ok(())
|
||||
}
|
||||
@ -385,42 +283,31 @@ async fn update(args: UpdateOpts) -> Result<()> {
|
||||
initialize_logger(&args.log_level);
|
||||
check_for_update(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
|
||||
|
||||
info!("{} Updating ESP Rust environment", emoji::DISC);
|
||||
info!("{} Updating Espressif Rust ecosystem", emoji::DISC);
|
||||
|
||||
let host_triple = get_host_triple(args.default_host)?;
|
||||
let mut config_file = ConfigFile::load(&args.config_path)?;
|
||||
let install_path = get_rustup_home().join("toolchains").join(args.name);
|
||||
let xtensa_rust: XtensaRust = if let Some(toolchain_version) = args.toolchain_version {
|
||||
XtensaRust::new(&toolchain_version, &host_triple)
|
||||
XtensaRust::new(&toolchain_version, &host_triple, &install_path)
|
||||
} else {
|
||||
let latest_version = XtensaRust::get_latest_version().await?;
|
||||
XtensaRust::new(&latest_version, &host_triple)
|
||||
XtensaRust::new(&latest_version, &host_triple, &install_path)
|
||||
};
|
||||
|
||||
debug!(
|
||||
"{} Arguments:
|
||||
- Host triple: {}
|
||||
- Toolchain version: {:#?}
|
||||
- Config: {:#?}",
|
||||
- Install path: {:#?}
|
||||
- Toolchain version: {:#?}",
|
||||
emoji::INFO,
|
||||
host_triple,
|
||||
install_path,
|
||||
xtensa_rust,
|
||||
config_file.config
|
||||
);
|
||||
|
||||
if let Some(config_xtensa_rust) = config_file.config.xtensa_rust {
|
||||
if config_xtensa_rust.version == xtensa_rust.version {
|
||||
info!(
|
||||
"{} Toolchain '{}' is already up to date",
|
||||
emoji::CHECK,
|
||||
xtensa_rust.version
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
config_xtensa_rust.uninstall()?;
|
||||
xtensa_rust.install().await?;
|
||||
config_file.config.xtensa_rust = Some(xtensa_rust);
|
||||
}
|
||||
XtensaRust::uninstall(&install_path)?;
|
||||
|
||||
config_file.save()?;
|
||||
xtensa_rust.install().await?;
|
||||
|
||||
info!("{} Update successfully completed!", emoji::CHECK);
|
||||
Ok(())
|
||||
@ -435,22 +322,11 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Deletes dist folder.
|
||||
fn clear_dist_folder() -> Result<(), Error> {
|
||||
let dist_path = PathBuf::from(get_dist_path(""));
|
||||
if dist_path.exists() {
|
||||
info!("{} Clearing dist folder", emoji::WRENCH);
|
||||
remove_dir_all(&dist_path)
|
||||
.map_err(|_| Error::FailedToRemoveDirectory(dist_path.display().to_string()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the absolute path to the export file, uses the DEFAULT_EXPORT_FILE if no arg is provided.
|
||||
fn get_export_file(export_file: Option<PathBuf>) -> Result<PathBuf, Error> {
|
||||
if let Some(export_file) = export_file {
|
||||
if export_file.is_dir() {
|
||||
return Err(Error::WrongExportFile(export_file.display().to_string()));
|
||||
return Err(Error::InvalidDestination(export_file.display().to_string()));
|
||||
}
|
||||
if export_file.is_absolute() {
|
||||
Ok(export_file)
|
||||
@ -483,40 +359,33 @@ fn create_export_file(export_file: &PathBuf, exports: &[String]) -> Result<(), E
|
||||
/// Instructions to export the environment variables.
|
||||
fn export_environment(export_file: &Path) -> Result<(), Error> {
|
||||
#[cfg(windows)]
|
||||
warn!(
|
||||
"{} PLEASE set up the environment variables running: '{}'",
|
||||
emoji::INFO,
|
||||
export_file.display()
|
||||
);
|
||||
#[cfg(unix)]
|
||||
warn!(
|
||||
"{} PLEASE set up the environment variables running: '. {}'",
|
||||
emoji::INFO,
|
||||
export_file.display()
|
||||
);
|
||||
warn!(
|
||||
"{} This step must be done every time you open a new terminal.",
|
||||
emoji::WARN
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
/// For Windows, we need to check that we are installing all the targets if we are installing esp-idf.
|
||||
pub fn check_arguments(
|
||||
targets: &HashSet<Target>,
|
||||
espidf_version: &Option<String>,
|
||||
) -> Result<(), Error> {
|
||||
if espidf_version.is_some()
|
||||
&& (!targets.contains(&Target::ESP32)
|
||||
|| !targets.contains(&Target::ESP32C3)
|
||||
|| !targets.contains(&Target::ESP32S2)
|
||||
|| !targets.contains(&Target::ESP32S3))
|
||||
{
|
||||
return Err(Error::WrongWindowsArguments);
|
||||
if cfg!(windows) {
|
||||
Command::new("setx")
|
||||
.args(["PATH", &env::var("PATH").unwrap(), "/m"])
|
||||
.stdout(Stdio::null())
|
||||
.output()?;
|
||||
warn!(
|
||||
"{} Your environments variables have been updated! Shell may need to be restarted for changes to be effective.",
|
||||
emoji::INFO
|
||||
);
|
||||
warn!(
|
||||
"{} A file was created at '{}' showing the injected environment variables.",
|
||||
emoji::INFO,
|
||||
export_file.display()
|
||||
);
|
||||
}
|
||||
#[cfg(unix)]
|
||||
if cfg!(unix) {
|
||||
warn!(
|
||||
"{} Please, set up the environment variables by running: '. {}'",
|
||||
emoji::INFO,
|
||||
export_file.display()
|
||||
);
|
||||
warn!(
|
||||
"{} This step must be done every time you open a new terminal.",
|
||||
emoji::WARN
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,260 +0,0 @@
|
||||
//! GCC Toolchain source and installation tools
|
||||
use super::Installable;
|
||||
use crate::{
|
||||
emoji,
|
||||
error::Error,
|
||||
targets::Target,
|
||||
toolchain::gcc::{get_toolchain_name, get_ulp_toolchain_name},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use directories::BaseDirs;
|
||||
use embuild::{espidf, espidf::EspIdfRemote, git};
|
||||
use log::{debug, info};
|
||||
use miette::Result;
|
||||
use std::{
|
||||
collections::hash_map::DefaultHasher,
|
||||
collections::HashSet,
|
||||
env,
|
||||
fs::remove_dir_all,
|
||||
hash::{Hash, Hasher},
|
||||
path::PathBuf,
|
||||
};
|
||||
use strum::{Display, EnumIter, EnumString, IntoStaticStr};
|
||||
|
||||
pub const DEFAULT_GIT_REPOSITORY: &str = "https://github.com/espressif/esp-idf";
|
||||
|
||||
const DEFAULT_CMAKE_GENERATOR: Generator = {
|
||||
// No Ninja builds for linux=aarch64 from Espressif yet
|
||||
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
|
||||
{
|
||||
Generator::UnixMakefiles
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_os = "linux", target_arch = "aarch64")))]
|
||||
{
|
||||
Generator::Ninja
|
||||
}
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, EnumString, Display, EnumIter, IntoStaticStr)]
|
||||
pub enum Generator {
|
||||
Ninja,
|
||||
NinjaMultiConfig,
|
||||
UnixMakefiles,
|
||||
BorlandMakefiles,
|
||||
MSYSMakefiles,
|
||||
MinGWMakefiles,
|
||||
NMakeMakefiles,
|
||||
NMakeMakefilesJOM,
|
||||
WatcomWMake,
|
||||
}
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct EspIdfRepo {
|
||||
/// The repository containing GCC sources.
|
||||
pub repository_url: String,
|
||||
/// ESP-IDF Version.
|
||||
pub version: String,
|
||||
/// Minify ESP-IDF?.
|
||||
pub minified: bool,
|
||||
/// Installation directory.
|
||||
pub install_path: PathBuf,
|
||||
/// ESP targets.
|
||||
pub targets: HashSet<Target>,
|
||||
}
|
||||
|
||||
impl EspIdfRepo {
|
||||
/// Create a new instance with the proper arguments.
|
||||
pub fn new(version: &str, minified: bool, targets: &HashSet<Target>) -> EspIdfRepo {
|
||||
let install_path = PathBuf::from(get_tools_path());
|
||||
debug!(
|
||||
"{} ESP-IDF install path: '{}'",
|
||||
emoji::DEBUG,
|
||||
install_path.display()
|
||||
);
|
||||
|
||||
Self {
|
||||
repository_url: DEFAULT_GIT_REPOSITORY.to_string(),
|
||||
version: version.to_string(),
|
||||
minified,
|
||||
install_path,
|
||||
targets: targets.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Uninstall ESP-IDF.
|
||||
pub fn uninstall(version: &str) -> Result<(), Error> {
|
||||
info!("{} Deleting ESP-IDF {}", emoji::WRENCH, version);
|
||||
let repo = EspIdfRemote {
|
||||
git_ref: espidf::parse_esp_idf_git_ref(version),
|
||||
repo_url: Some(DEFAULT_GIT_REPOSITORY.to_string()),
|
||||
};
|
||||
remove_dir_all(get_install_path(repo.clone()).parent().unwrap()).map_err(|_| {
|
||||
Error::FailedToRemoveDirectory(
|
||||
get_install_path(repo)
|
||||
.parent()
|
||||
.unwrap()
|
||||
.display()
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Installable for EspIdfRepo {
|
||||
async fn install(&self) -> Result<Vec<String>, Error> {
|
||||
let cmake_generator = DEFAULT_CMAKE_GENERATOR;
|
||||
let mut exports: Vec<String> = Vec::new();
|
||||
let targets = self.targets.clone();
|
||||
// A closure to specify which tools `idf-tools.py` should install.
|
||||
let make_tools = move |repo: &git::Repository,
|
||||
version: &anyhow::Result<espidf::EspIdfVersion>|
|
||||
-> anyhow::Result<Vec<espidf::Tools>> {
|
||||
let version_str = match version {
|
||||
Ok(v) => format!("v{v}"),
|
||||
Err(_) => "(unknown version)".to_string(),
|
||||
};
|
||||
info!(
|
||||
"{} Using esp-idf {} at '{}'",
|
||||
emoji::INFO,
|
||||
version_str,
|
||||
repo.worktree().display()
|
||||
);
|
||||
|
||||
let mut tools = vec![];
|
||||
let mut subtools = Vec::new();
|
||||
for target in targets {
|
||||
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());
|
||||
if !cfg!(target_os = "linux") || !cfg!(target_arch = "aarch64") {
|
||||
if let Some(ulp_toolchain_name) = ulp_toolchain_name {
|
||||
subtools.push(ulp_toolchain_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use custom cmake for esp-idf<4.4, because we need at least cmake-3.20
|
||||
match version.as_ref().map(|v| (v.major, v.minor, v.patch)) {
|
||||
Ok((major, minor, _)) if major >= 4 && minor >= 4 => {
|
||||
subtools.push("cmake".to_string())
|
||||
}
|
||||
_ => {
|
||||
tools
|
||||
.push(espidf::Tools::cmake().map_err(|_| Error::FailedToInstantiateCmake)?);
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
subtools.push("openocd-esp32".to_string());
|
||||
#[cfg(windows)]
|
||||
subtools.push("idf-exe".to_string());
|
||||
#[cfg(windows)]
|
||||
subtools.push("ccache".to_string());
|
||||
#[cfg(windows)]
|
||||
subtools.push("dfu-util".to_string());
|
||||
|
||||
if cmake_generator == Generator::Ninja {
|
||||
subtools.push("ninja".to_string())
|
||||
}
|
||||
|
||||
tools.push(espidf::Tools::new(subtools));
|
||||
|
||||
Ok(tools)
|
||||
};
|
||||
|
||||
let install = |esp_idf_origin: espidf::EspIdfOrigin| -> Result<espidf::EspIdf, Error> {
|
||||
espidf::Installer::new(esp_idf_origin)
|
||||
.install_dir(Some(self.install_path.clone()))
|
||||
.with_tools(make_tools)
|
||||
.install()
|
||||
.map_err(|_| Error::FailedToCreateEspIdfInstallClosure)
|
||||
};
|
||||
|
||||
let repo = espidf::EspIdfRemote {
|
||||
git_ref: espidf::parse_esp_idf_git_ref(&self.version),
|
||||
repo_url: Some("https://github.com/espressif/esp-idf".to_string()),
|
||||
};
|
||||
|
||||
let espidf_origin = espidf::EspIdfOrigin::Managed(repo.clone());
|
||||
#[cfg(unix)]
|
||||
let espidf = install(espidf_origin).map_err(|_| Error::FailedToInstallEspIdf)?;
|
||||
#[cfg(windows)]
|
||||
install(espidf_origin).map_err(|_| Error::FailedToInstallEspIdf)?;
|
||||
let espidf_dir = get_install_path(repo);
|
||||
#[cfg(windows)]
|
||||
exports.push(format!("$Env:IDF_PATH = \"{}\"", espidf_dir.display()));
|
||||
#[cfg(unix)]
|
||||
exports.push(format!("export IDF_PATH={}", espidf_dir.display()));
|
||||
#[cfg(windows)]
|
||||
exports.push(espidf_dir.join("export.ps1").display().to_string());
|
||||
#[cfg(unix)]
|
||||
exports.push(format!("export PATH={:?}", espidf.exported_path));
|
||||
if self.minified {
|
||||
info!("{} Minifying ESP-IDF", emoji::INFO);
|
||||
remove_dir_all(espidf_dir.join("docs"))?;
|
||||
remove_dir_all(espidf_dir.join("examples"))?;
|
||||
remove_dir_all(espidf_dir.join("tools").join("esp_app_trace"))?;
|
||||
remove_dir_all(espidf_dir.join("tools").join("test_idf_size"))?;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
exports.push(format!("$Env:IDF_TOOLS_PATH = \"{}\"", get_tools_path()));
|
||||
#[cfg(unix)]
|
||||
exports.push(format!("export IDF_TOOLS_PATH=\"{}\"", get_tools_path()));
|
||||
|
||||
Ok(exports)
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
"ESP-IDF".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the esp-idf installation path.
|
||||
pub fn get_install_path(repo: EspIdfRemote) -> PathBuf {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
repo.repo_url.as_ref().unwrap().hash(&mut hasher);
|
||||
let repo_url_hash = format!("{:x}", hasher.finish());
|
||||
let repo_dir = match repo.git_ref {
|
||||
git::Ref::Branch(n) | git::Ref::Tag(n) | git::Ref::Commit(n) => n,
|
||||
};
|
||||
// Replace all directory separators with a dash `-`, so that we don't create
|
||||
// subfolders for tag or branch names that contain such characters.
|
||||
let repo_dir = repo_dir.replace(['/', '\\'], "-");
|
||||
|
||||
let mut install_path = PathBuf::from(get_tools_path());
|
||||
install_path = install_path.join(PathBuf::from(format!("esp-idf-{repo_url_hash}")));
|
||||
install_path = install_path.join(PathBuf::from(repo_dir));
|
||||
install_path
|
||||
}
|
||||
|
||||
/// Gets path where esp-idf tools where be downloaded and installed. If environment
|
||||
/// variable IDF_TOOLS_PATH is not set. Uses HOME/.espressif on Linux and macOS,
|
||||
/// and %USER_PROFILE%\.espressif on Windows.
|
||||
pub fn get_tools_path() -> String {
|
||||
env::var("IDF_TOOLS_PATH").unwrap_or_else(|_e| {
|
||||
format!(
|
||||
"{}",
|
||||
BaseDirs::new()
|
||||
.unwrap()
|
||||
.home_dir()
|
||||
.join(".espressif")
|
||||
.display()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets the espressif tools directory path. Tools directory is where the tools
|
||||
/// are extracted.
|
||||
pub fn get_tool_path(tool_name: &str) -> String {
|
||||
format!("{}/tools/{}", get_tools_path(), tool_name)
|
||||
}
|
||||
|
||||
/// Gets the Espressif dist directory path. Dist directory is where the archives
|
||||
/// of the tools are downloaded.
|
||||
pub fn get_dist_path(tool_name: &str) -> String {
|
||||
let tools_path = get_tools_path();
|
||||
format!("{tools_path}/dist/{tool_name}")
|
||||
}
|
@ -2,15 +2,10 @@
|
||||
|
||||
use super::Installable;
|
||||
use crate::{
|
||||
emoji,
|
||||
error::Error,
|
||||
host_triple::HostTriple,
|
||||
targets::Target,
|
||||
toolchain::{download_file, espidf::get_tool_path},
|
||||
emoji, error::Error, host_triple::HostTriple, targets::Target, toolchain::download_file,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use embuild::espidf::EspIdfVersion;
|
||||
use log::{debug, warn};
|
||||
use log::{debug, info, warn};
|
||||
use miette::Result;
|
||||
use std::{
|
||||
fs::remove_dir_all,
|
||||
@ -20,21 +15,23 @@ use std::{
|
||||
const DEFAULT_GCC_REPOSITORY: &str = "https://github.com/espressif/crosstool-NG/releases/download";
|
||||
const DEFAULT_GCC_RELEASE: &str = "esp-2021r2-patch5";
|
||||
const DEFAULT_GCC_VERSION: &str = "8_4_0";
|
||||
const ESP32_GCC: &str = "xtensa-esp32-elf";
|
||||
const ESP32S2_GCC: &str = "xtensa-esp32s2-elf";
|
||||
const ESP32S3_GCC: &str = "xtensa-esp32s3-elf";
|
||||
pub const ESP32_GCC: &str = "xtensa-esp32-elf";
|
||||
pub const ESP32S2_GCC: &str = "xtensa-esp32s2-elf";
|
||||
pub const ESP32S3_GCC: &str = "xtensa-esp32s3-elf";
|
||||
pub const RISCV_GCC: &str = "riscv32-esp-elf";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Gcc {
|
||||
/// Host triple.
|
||||
pub host_triple: HostTriple,
|
||||
/// GCC Toolchain name.
|
||||
pub name: String,
|
||||
/// Repository release version to use.
|
||||
pub release: String,
|
||||
/// The repository containing GCC sources.
|
||||
pub repository_url: String,
|
||||
/// GCC Toolchain target.
|
||||
pub toolchain_name: String,
|
||||
/// GCC Toolchain path.
|
||||
pub path: PathBuf,
|
||||
/// GCC Version.
|
||||
pub version: String,
|
||||
}
|
||||
@ -42,68 +39,63 @@ pub struct Gcc {
|
||||
impl Gcc {
|
||||
/// Gets the binary path.
|
||||
pub fn get_bin_path(&self) -> String {
|
||||
let toolchain_path = format!(
|
||||
"{}/{}-{}/{}/bin",
|
||||
&self.toolchain_name, self.release, self.version, &self.toolchain_name
|
||||
);
|
||||
get_tool_path(&toolchain_path)
|
||||
format!("{}/{}/bin", &self.path.to_str().unwrap(), &self.name)
|
||||
}
|
||||
|
||||
/// 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, toolchain_path: &Path) -> Self {
|
||||
let name = get_gcc_name(target);
|
||||
let version = DEFAULT_GCC_VERSION.to_string();
|
||||
let release = DEFAULT_GCC_RELEASE.to_string();
|
||||
let path = toolchain_path
|
||||
.join(&name)
|
||||
.join(format!("{release}-{version}"));
|
||||
|
||||
Self {
|
||||
host_triple: host_triple.clone(),
|
||||
release: DEFAULT_GCC_RELEASE.to_string(),
|
||||
name,
|
||||
release,
|
||||
repository_url: DEFAULT_GCC_REPOSITORY.to_string(),
|
||||
toolchain_name: get_toolchain_name(target),
|
||||
version: DEFAULT_GCC_VERSION.to_string(),
|
||||
path,
|
||||
version,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new instance of RISC-V GCC with default values and proper toolchain name.
|
||||
pub fn new_riscv(host_triple: &HostTriple) -> Self {
|
||||
pub fn new_riscv(host_triple: &HostTriple, toolchain_path: &Path) -> Self {
|
||||
let version = DEFAULT_GCC_VERSION.to_string();
|
||||
let release = DEFAULT_GCC_RELEASE.to_string();
|
||||
let name = RISCV_GCC.to_string();
|
||||
let path = toolchain_path
|
||||
.join(&name)
|
||||
.join(format!("{release}-{version}"));
|
||||
|
||||
Self {
|
||||
host_triple: host_triple.clone(),
|
||||
release: DEFAULT_GCC_RELEASE.to_string(),
|
||||
name,
|
||||
release,
|
||||
repository_url: DEFAULT_GCC_REPOSITORY.to_string(),
|
||||
toolchain_name: String::from("riscv32-esp-elf"),
|
||||
version: DEFAULT_GCC_VERSION.to_string(),
|
||||
path,
|
||||
version,
|
||||
}
|
||||
}
|
||||
|
||||
/// Uninstall the GCC toolchain for the desired target.
|
||||
pub fn uninstall(target: &Target) -> Result<(), Error> {
|
||||
let gcc_path = get_tool_path(&get_toolchain_name(target));
|
||||
remove_dir_all(&gcc_path).map_err(|_| Error::FailedToRemoveDirectory(gcc_path))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Uninstall the RISC-V GCC toolchain.
|
||||
pub fn uninstall_riscv() -> Result<(), Error> {
|
||||
let riscv_gcc_path = get_tool_path(RISCV_GCC);
|
||||
remove_dir_all(&riscv_gcc_path)
|
||||
.map_err(|_| Error::FailedToRemoveDirectory(riscv_gcc_path))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Installable for Gcc {
|
||||
async fn install(&self) -> Result<Vec<String>, Error> {
|
||||
let target_dir = format!("{}/{}-{}", self.toolchain_name, self.release, self.version);
|
||||
let gcc_path = get_tool_path(&target_dir);
|
||||
let extension = get_artifact_extension(&self.host_triple);
|
||||
debug!("{} GCC path: {}", emoji::DEBUG, gcc_path);
|
||||
if Path::new(&PathBuf::from(&gcc_path)).exists() {
|
||||
debug!("{} GCC path: {}", emoji::DEBUG, self.path.display());
|
||||
if self.path.exists() {
|
||||
warn!(
|
||||
"{} Previous installation of GCC exists in: '{}'. Reusing this installation.",
|
||||
emoji::WARN,
|
||||
&gcc_path
|
||||
&self.path.display()
|
||||
);
|
||||
} else {
|
||||
let gcc_file = format!(
|
||||
"{}-gcc{}-{}-{}.{}",
|
||||
self.toolchain_name,
|
||||
self.name,
|
||||
self.version,
|
||||
self.release,
|
||||
get_arch(&self.host_triple).unwrap(),
|
||||
@ -112,16 +104,23 @@ impl Installable for Gcc {
|
||||
let gcc_dist_url = format!("{}/{}/{}", self.repository_url, self.release, gcc_file);
|
||||
download_file(
|
||||
gcc_dist_url,
|
||||
&format!("{}.{}", &self.toolchain_name, extension),
|
||||
&gcc_path,
|
||||
&format!("{}.{}", &self.name, extension),
|
||||
&self.path.display().to_string(),
|
||||
true,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
let mut exports: Vec<String> = Vec::new();
|
||||
|
||||
#[cfg(windows)]
|
||||
exports.push(format!("$Env:PATH += \";{}\"", &self.get_bin_path()));
|
||||
if cfg!(windows) {
|
||||
exports.push(format!("$Env:PATH += \";{}\"", &self.get_bin_path()));
|
||||
std::env::set_var(
|
||||
"PATH",
|
||||
std::env::var("PATH").unwrap() + ";" + &self.get_bin_path().replace('/', "\\"),
|
||||
);
|
||||
}
|
||||
#[cfg(unix)]
|
||||
exports.push(format!("export PATH=\"{}:$PATH\"", &self.get_bin_path()));
|
||||
|
||||
@ -129,7 +128,7 @@ impl Installable for Gcc {
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
format!("GCC ({})", self.toolchain_name)
|
||||
format!("GCC ({})", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,7 +151,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_gcc_name(target: &Target) -> String {
|
||||
let toolchain = match target {
|
||||
Target::ESP32 => ESP32_GCC,
|
||||
Target::ESP32S2 => ESP32S2_GCC,
|
||||
@ -162,24 +161,34 @@ pub fn get_toolchain_name(target: &Target) -> String {
|
||||
toolchain.to_string()
|
||||
}
|
||||
|
||||
/// Gets the toolchain name based on the Target
|
||||
pub fn get_ulp_toolchain_name(target: Target, version: Option<&EspIdfVersion>) -> Option<String> {
|
||||
match target {
|
||||
Target::ESP32 => Some("esp32ulp-elf".to_string()),
|
||||
Target::ESP32S2 | Target::ESP32S3 => Some(
|
||||
if version
|
||||
.map(|version| {
|
||||
version.major > 4
|
||||
|| version.major == 4 && version.minor > 4
|
||||
|| version.major == 4 && version.minor == 4 && version.patch >= 2
|
||||
})
|
||||
.unwrap_or(true)
|
||||
{
|
||||
"esp32ulp-elf".to_string()
|
||||
} else {
|
||||
"esp32s2ulp-elf".to_string()
|
||||
},
|
||||
),
|
||||
_ => None,
|
||||
/// Checks if the toolchain is pressent, if present uninstalls it.
|
||||
pub fn uninstall_gcc_toolchains(toolchain_path: &Path) -> Result<(), Error> {
|
||||
info!("{} Uninstalling GCC toolchain", emoji::WRENCH);
|
||||
|
||||
let gcc_toolchains = vec![ESP32_GCC, ESP32S2_GCC, ESP32S3_GCC, RISCV_GCC];
|
||||
|
||||
for toolchain in gcc_toolchains {
|
||||
let gcc_path = toolchain_path.join(toolchain);
|
||||
if gcc_path.exists() {
|
||||
#[cfg(windows)]
|
||||
if cfg!(windows) {
|
||||
let gcc_path = format!(
|
||||
"{}\\{}-{}\\{}\\bin",
|
||||
gcc_path.display(),
|
||||
DEFAULT_GCC_RELEASE,
|
||||
DEFAULT_GCC_VERSION,
|
||||
toolchain
|
||||
);
|
||||
std::env::set_var(
|
||||
"PATH",
|
||||
std::env::var("PATH")
|
||||
.unwrap()
|
||||
.replace(&format!("{gcc_path};"), ""),
|
||||
);
|
||||
}
|
||||
remove_dir_all(gcc_path)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
//! LLVM Toolchain source and installation tools
|
||||
|
||||
use super::Installable;
|
||||
use crate::{
|
||||
emoji,
|
||||
error::Error,
|
||||
host_triple::HostTriple,
|
||||
toolchain::{download_file, espidf::get_tool_path},
|
||||
};
|
||||
use crate::{emoji, error::Error, host_triple::HostTriple, toolchain::download_file};
|
||||
use async_trait::async_trait;
|
||||
use log::{info, warn};
|
||||
use miette::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(windows)]
|
||||
use std::process::{Command, Stdio};
|
||||
use std::{
|
||||
fs::remove_dir_all,
|
||||
path::{Path, PathBuf},
|
||||
@ -17,9 +15,12 @@ use std::{
|
||||
|
||||
const DEFAULT_LLVM_REPOSITORY: &str = "https://github.com/espressif/llvm-project/releases/download";
|
||||
const DEFAULT_LLVM_15_VERSION: &str = "esp-15.0.0-20221201";
|
||||
pub const CLANG_NAME: &str = "xtensa-esp32-elf-clang";
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct Llvm {
|
||||
// /// If `true`, full LLVM, instead of only libraries, are installed.
|
||||
extended: bool,
|
||||
/// LLVM Toolchain file name.
|
||||
pub file_name: String,
|
||||
/// Host triple.
|
||||
@ -30,8 +31,6 @@ pub struct Llvm {
|
||||
pub repository_url: String,
|
||||
/// LLVM Version ["15"].
|
||||
pub version: String,
|
||||
/// If `true`, only libraries are installed.
|
||||
minified: bool,
|
||||
}
|
||||
|
||||
impl Llvm {
|
||||
@ -54,6 +53,7 @@ impl Llvm {
|
||||
let llvm_path = format!("{}/esp-clang/lib", self.path.to_str().unwrap());
|
||||
llvm_path
|
||||
}
|
||||
|
||||
/// Gets the binary path of clang
|
||||
fn get_bin_path(&self) -> String {
|
||||
#[cfg(windows)]
|
||||
@ -64,38 +64,62 @@ impl Llvm {
|
||||
}
|
||||
|
||||
/// Create a new instance with default values and proper toolchain version.
|
||||
pub fn new(version: String, minified: bool, host_triple: &HostTriple) -> Self {
|
||||
pub fn new(
|
||||
toolchain_path: &Path,
|
||||
host_triple: &HostTriple,
|
||||
extended: bool,
|
||||
) -> Result<Self, Error> {
|
||||
let version = DEFAULT_LLVM_15_VERSION.to_string();
|
||||
let mut file_name = format!(
|
||||
"llvm-{}-{}.tar.xz",
|
||||
DEFAULT_LLVM_15_VERSION,
|
||||
version,
|
||||
Self::get_arch(host_triple).unwrap()
|
||||
);
|
||||
if minified {
|
||||
if !extended {
|
||||
file_name = format!("libs_{file_name}");
|
||||
}
|
||||
let repository_url =
|
||||
format!("{DEFAULT_LLVM_REPOSITORY}/{DEFAULT_LLVM_15_VERSION}/{file_name}");
|
||||
let path = PathBuf::from(format!(
|
||||
"{}/{}-{}",
|
||||
get_tool_path("xtensa-esp32-elf-clang"),
|
||||
DEFAULT_LLVM_15_VERSION,
|
||||
host_triple
|
||||
));
|
||||
Self {
|
||||
let repository_url = format!("{DEFAULT_LLVM_REPOSITORY}/{version}/{file_name}");
|
||||
let path = toolchain_path.join(CLANG_NAME).join(&version);
|
||||
|
||||
Ok(Self {
|
||||
extended,
|
||||
file_name,
|
||||
host_triple: host_triple.clone(),
|
||||
path,
|
||||
repository_url,
|
||||
version,
|
||||
minified,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Uninstall LLVM toolchain.
|
||||
pub fn uninstall(llvm_path: &Path) -> Result<(), Error> {
|
||||
info!("{} Deleting Xtensa LLVM", emoji::WRENCH);
|
||||
remove_dir_all(llvm_path)
|
||||
.map_err(|_| Error::FailedToRemoveDirectory(llvm_path.display().to_string()))?;
|
||||
pub fn uninstall(toolchain_path: &Path) -> Result<(), Error> {
|
||||
info!("{} Uninstalling Xtensa LLVM", emoji::WRENCH);
|
||||
let llvm_path = toolchain_path.join(CLANG_NAME);
|
||||
if llvm_path.exists() {
|
||||
#[cfg(windows)]
|
||||
if cfg!(windows) {
|
||||
Command::new("setx")
|
||||
.args(["LIBCLANG_PATH", "", "/m"])
|
||||
.stdout(Stdio::null())
|
||||
.output()?;
|
||||
Command::new("setx")
|
||||
.args(["CLANG_PATH", "", "/m"])
|
||||
.stdout(Stdio::null())
|
||||
.output()?;
|
||||
std::env::set_var(
|
||||
"PATH",
|
||||
std::env::var("PATH").unwrap().replace(
|
||||
&format!(
|
||||
"{}\\{}\\esp-clang\\bin;",
|
||||
llvm_path.display().to_string().replace('/', "\\"),
|
||||
DEFAULT_LLVM_15_VERSION,
|
||||
),
|
||||
"",
|
||||
),
|
||||
);
|
||||
}
|
||||
remove_dir_all(toolchain_path.join(CLANG_NAME))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -118,23 +142,43 @@ impl Installable for Llvm {
|
||||
"idf_tool_xtensa_elf_clang.tar.xz",
|
||||
self.path.to_str().unwrap(),
|
||||
true,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
// Set environment variables.
|
||||
#[cfg(windows)]
|
||||
exports.push(format!(
|
||||
"$Env:LIBCLANG_PATH = \"{}/libclang.dll\"",
|
||||
self.get_lib_path()
|
||||
));
|
||||
#[cfg(windows)]
|
||||
exports.push(format!("$Env:PATH += \";{}\"", self.get_lib_path()));
|
||||
if cfg!(windows) {
|
||||
exports.push(format!(
|
||||
"$Env:LIBCLANG_PATH = \"{}/libclang.dll\"",
|
||||
self.get_lib_path()
|
||||
));
|
||||
exports.push(format!("$Env:PATH += \";{}\"", self.get_lib_path()));
|
||||
Command::new("setx")
|
||||
.args([
|
||||
"LIBCLANG_PATH",
|
||||
&format!("{}\\libclang.dll", self.get_lib_path().replace('/', "\\")),
|
||||
"/m",
|
||||
])
|
||||
.stdout(Stdio::null())
|
||||
.output()?;
|
||||
std::env::set_var(
|
||||
"PATH",
|
||||
std::env::var("PATH").unwrap() + ";" + &self.get_lib_path().replace('/', "\\"),
|
||||
);
|
||||
}
|
||||
#[cfg(unix)]
|
||||
exports.push(format!("export LIBCLANG_PATH=\"{}\"", self.get_lib_path()));
|
||||
|
||||
if !self.minified {
|
||||
if self.extended {
|
||||
#[cfg(windows)]
|
||||
exports.push(format!("$Env:CLANG_PATH = \"{}\"", self.get_bin_path()));
|
||||
if cfg!(windows) {
|
||||
exports.push(format!("$Env:CLANG_PATH = \"{}\"", self.get_bin_path()));
|
||||
Command::new("setx")
|
||||
.args(["CLANG_PATH", &self.get_bin_path().replace('/', "\\"), "/m"])
|
||||
.stdout(Stdio::null())
|
||||
.output()?;
|
||||
}
|
||||
#[cfg(unix)]
|
||||
exports.push(format!("export CLANG_PATH=\"{}\"", self.get_bin_path()));
|
||||
}
|
||||
|
@ -10,12 +10,12 @@ use std::{
|
||||
env,
|
||||
fs::{create_dir_all, remove_file, File},
|
||||
io::Write,
|
||||
path::Path,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use tar::Archive;
|
||||
use xz2::read::XzDecoder;
|
||||
use zip::ZipArchive;
|
||||
|
||||
pub mod espidf;
|
||||
pub mod gcc;
|
||||
pub mod llvm;
|
||||
pub mod rust;
|
||||
@ -34,6 +34,7 @@ pub async fn download_file(
|
||||
file_name: &str,
|
||||
output_directory: &str,
|
||||
uncompress: bool,
|
||||
strip: bool,
|
||||
) -> Result<String, Error> {
|
||||
let file_path = format!("{output_directory}/{file_name}");
|
||||
if Path::new(&file_path).exists() {
|
||||
@ -50,7 +51,7 @@ pub async fn download_file(
|
||||
output_directory
|
||||
);
|
||||
if let Err(_e) = create_dir_all(output_directory) {
|
||||
return Err(Error::FailedToCreateDirectory(output_directory.to_string()));
|
||||
return Err(Error::CreateDirectory(output_directory.to_string()));
|
||||
}
|
||||
}
|
||||
info!(
|
||||
@ -67,8 +68,29 @@ pub async fn download_file(
|
||||
"zip" => {
|
||||
let mut tmpfile = tempfile::tempfile()?;
|
||||
tmpfile.write_all(&bytes)?;
|
||||
let mut zipfile = zip::ZipArchive::new(tmpfile).unwrap();
|
||||
zipfile.extract(output_directory).unwrap();
|
||||
let mut zipfile = ZipArchive::new(tmpfile).unwrap();
|
||||
if strip {
|
||||
for i in 0..zipfile.len() {
|
||||
let mut file = zipfile.by_index(i).unwrap();
|
||||
if !file.name().starts_with("esp/") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_path = PathBuf::from(file.name().to_string());
|
||||
let stripped_name = file_path.strip_prefix("esp/").unwrap();
|
||||
let outpath = Path::new(output_directory).join(stripped_name);
|
||||
|
||||
if file.name().ends_with('/') {
|
||||
create_dir_all(&outpath)?;
|
||||
} else {
|
||||
create_dir_all(outpath.parent().unwrap())?;
|
||||
let mut outfile = File::create(&outpath)?;
|
||||
std::io::copy(&mut file, &mut outfile)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
zipfile.extract(output_directory).unwrap();
|
||||
}
|
||||
}
|
||||
"gz" => {
|
||||
info!(
|
||||
@ -133,10 +155,10 @@ pub fn github_query(url: &str) -> Result<serde_json::Value, Error> {
|
||||
"https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting",
|
||||
) {
|
||||
warn!("{} GitHub rate limit exceeded", emoji::WARN);
|
||||
return Err(Error::FailedGithubQuery);
|
||||
return Err(Error::GithubQuery);
|
||||
}
|
||||
let json: serde_json::Value =
|
||||
serde_json::from_str(&res).map_err(|_| Error::FailedToSerializeJson)?;
|
||||
serde_json::from_str(&res).map_err(|_| Error::SerializeJson)?;
|
||||
Ok(json)
|
||||
},
|
||||
)
|
||||
|
@ -5,17 +5,26 @@ use crate::{
|
||||
emoji,
|
||||
error::Error,
|
||||
host_triple::HostTriple,
|
||||
toolchain::{download_file, espidf::get_dist_path, github_query},
|
||||
toolchain::{
|
||||
download_file,
|
||||
// espidf::get_dist_path,
|
||||
gcc::{ESP32S2_GCC, ESP32S3_GCC, ESP32_GCC, RISCV_GCC},
|
||||
github_query,
|
||||
llvm::CLANG_NAME,
|
||||
},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use directories::BaseDirs;
|
||||
use embuild::cmd;
|
||||
use log::{debug, info, warn};
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
use miette::Result;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::HashSet, env, fmt::Debug, fs::remove_dir_all, path::PathBuf, process::Stdio,
|
||||
env,
|
||||
fmt::Debug,
|
||||
fs::{read_dir, remove_dir_all},
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
/// Xtensa Rust Toolchain repository
|
||||
@ -41,6 +50,8 @@ pub struct XtensaRust {
|
||||
pub dist_url: String,
|
||||
/// Host triple.
|
||||
pub host_triple: String,
|
||||
/// LLVM Toolchain path.
|
||||
pub path: PathBuf,
|
||||
/// Path to the rustup home directory.
|
||||
pub rustup_home: PathBuf,
|
||||
#[cfg(unix)]
|
||||
@ -68,7 +79,7 @@ impl XtensaRust {
|
||||
}
|
||||
|
||||
/// Create a new instance.
|
||||
pub fn new(toolchain_version: &str, host_triple: &HostTriple) -> Self {
|
||||
pub fn new(toolchain_version: &str, host_triple: &HostTriple, toolchain_path: &Path) -> Self {
|
||||
let artifact_extension = get_artifact_extension(host_triple);
|
||||
let version = toolchain_version.to_string();
|
||||
let dist = format!("rust-{version}-{host_triple}");
|
||||
@ -82,15 +93,14 @@ impl XtensaRust {
|
||||
let src_dist_url = format!("{DEFAULT_XTENSA_RUST_REPOSITORY}/v{version}/{src_dist_file}");
|
||||
let cargo_home = get_cargo_home();
|
||||
let rustup_home = get_rustup_home();
|
||||
#[cfg(unix)]
|
||||
let toolchain_destination = rustup_home.join("toolchains").join("esp");
|
||||
#[cfg(windows)]
|
||||
let toolchain_destination = rustup_home.join("toolchains");
|
||||
let toolchain_destination = toolchain_path.to_path_buf();
|
||||
|
||||
Self {
|
||||
cargo_home,
|
||||
dist_file,
|
||||
dist_url,
|
||||
host_triple: host_triple.to_string(),
|
||||
path: toolchain_path.to_path_buf(),
|
||||
rustup_home,
|
||||
#[cfg(unix)]
|
||||
src_dist_file,
|
||||
@ -116,7 +126,7 @@ impl XtensaRust {
|
||||
}
|
||||
}
|
||||
if extended_versions.is_empty() {
|
||||
return Err(Error::InvalidXtensaToolchanVersion(arg.to_string()));
|
||||
return Err(Error::InvalidVersion(arg.to_string()));
|
||||
}
|
||||
let mut max_version = extended_versions.pop().unwrap();
|
||||
let mut max_subpatch = 0;
|
||||
@ -142,18 +152,24 @@ impl XtensaRust {
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::InvalidXtensaToolchanVersion(arg.to_string()))
|
||||
Err(Error::InvalidVersion(arg.to_string()))
|
||||
}
|
||||
|
||||
/// Removes the Xtensa Rust toolchain.
|
||||
pub fn uninstall(&self) -> Result<()> {
|
||||
pub fn uninstall(toolchain_path: &Path) -> Result<(), Error> {
|
||||
info!("{} Uninstalling Xtensa Rust toolchain", emoji::WRENCH);
|
||||
let toolchain_path = self.toolchain_destination.clone();
|
||||
#[cfg(windows)]
|
||||
let toolchain_path = toolchain_path.join("esp");
|
||||
remove_dir_all(&toolchain_path)
|
||||
.into_diagnostic()
|
||||
.map_err(|_| Error::FailedToRemoveDirectory(toolchain_path.display().to_string()))?;
|
||||
let dir = read_dir(toolchain_path)?;
|
||||
for entry in dir {
|
||||
let subdir_name = entry.unwrap().path().display().to_string();
|
||||
if !subdir_name.contains(RISCV_GCC)
|
||||
&& !subdir_name.contains(ESP32_GCC)
|
||||
&& !subdir_name.contains(ESP32S2_GCC)
|
||||
&& !subdir_name.contains(ESP32S3_GCC)
|
||||
&& !subdir_name.contains(CLANG_NAME)
|
||||
{
|
||||
remove_dir_all(Path::new(&subdir_name)).unwrap();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -161,18 +177,35 @@ impl XtensaRust {
|
||||
#[async_trait]
|
||||
impl Installable for XtensaRust {
|
||||
async fn install(&self) -> Result<Vec<String>, Error> {
|
||||
#[cfg(unix)]
|
||||
let toolchain_path = self.toolchain_destination.clone();
|
||||
#[cfg(windows)]
|
||||
let toolchain_path = self.toolchain_destination.clone().join("esp");
|
||||
if toolchain_path.exists() {
|
||||
let toolchain_name = format!(
|
||||
"+{}",
|
||||
self.toolchain_destination
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
);
|
||||
let cargo_cmd = Command::new("cargo")
|
||||
.args([&toolchain_name, "--version"])
|
||||
.stdout(Stdio::piped())
|
||||
.output()?;
|
||||
let output = String::from_utf8_lossy(&cargo_cmd.stdout);
|
||||
let toolchain_semver = self.version.rsplit_once('.').unwrap().0;
|
||||
if self.toolchain_destination.exists()
|
||||
&& cargo_cmd.status.success()
|
||||
&& output.contains(toolchain_semver)
|
||||
{
|
||||
warn!(
|
||||
"{} Previous installation of Xtensa Rust exists in: '{}'. Reusing this installation.",
|
||||
"{} Previous installation of Xtensa Rust {} exists in: '{}'. Reusing this installation. Since Xtensa Rust uses an extended semantic versioning, the toolchain might be different from the one you are expecting. If you want to reinstall the toolchain, please run `espup update -v <VERSION>`.",
|
||||
emoji::WARN,
|
||||
&toolchain_path.display()
|
||||
toolchain_semver,
|
||||
&self.toolchain_destination.display()
|
||||
);
|
||||
return Ok(vec![]);
|
||||
} else {
|
||||
Self::uninstall(&self.toolchain_destination)?;
|
||||
}
|
||||
|
||||
info!(
|
||||
"{} Installing Xtensa Rust {} toolchain",
|
||||
emoji::WRENCH,
|
||||
@ -181,53 +214,80 @@ impl Installable for XtensaRust {
|
||||
|
||||
#[cfg(unix)]
|
||||
if cfg!(unix) {
|
||||
let temp_rust_dir = tempfile::TempDir::new()
|
||||
.unwrap()
|
||||
.into_path()
|
||||
.display()
|
||||
.to_string();
|
||||
download_file(
|
||||
self.dist_url.clone(),
|
||||
"rust.tar.xz",
|
||||
&get_dist_path("rust"),
|
||||
&temp_rust_dir,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("{} Installing rust esp toolchain", emoji::WRENCH);
|
||||
info!(
|
||||
"{} Installing 'rust' component for Xtensa Rust toolchain",
|
||||
emoji::WRENCH
|
||||
);
|
||||
let arguments = format!(
|
||||
"{}/rust-nightly-{}/install.sh --destdir={} --prefix='' --without=rust-docs-json-preview,rust-docs --disable-ldconfig",
|
||||
get_dist_path("rust"),
|
||||
temp_rust_dir,
|
||||
&self.host_triple,
|
||||
self.toolchain_destination.display()
|
||||
);
|
||||
cmd!("/usr/bin/env", "bash", "-c", arguments)
|
||||
.into_inner()
|
||||
.stdout(Stdio::null())
|
||||
.output()?;
|
||||
|
||||
let mut child1 = Command::new("/usr/bin/env")
|
||||
.args(["bash", "-c", &arguments])
|
||||
.stdout(Stdio::null())
|
||||
.spawn()?;
|
||||
|
||||
let temp_rust_src_dir = tempfile::TempDir::new()
|
||||
.unwrap()
|
||||
.into_path()
|
||||
.display()
|
||||
.to_string();
|
||||
download_file(
|
||||
self.src_dist_url.clone(),
|
||||
"rust-src.tar.xz",
|
||||
&get_dist_path("rust-src"),
|
||||
&temp_rust_src_dir,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
info!("{} Installing rust-src for esp toolchain", emoji::WRENCH);
|
||||
info!(
|
||||
"{} Installing 'rust-src' component for Xtensa Rust toolchain",
|
||||
emoji::WRENCH
|
||||
);
|
||||
let arguments = format!(
|
||||
"{}/rust-src-nightly/install.sh --destdir={} --prefix='' --disable-ldconfig",
|
||||
get_dist_path("rust-src"),
|
||||
temp_rust_src_dir,
|
||||
self.toolchain_destination.display()
|
||||
);
|
||||
cmd!("/usr/bin/env", "bash", "-c", arguments)
|
||||
.into_inner()
|
||||
|
||||
let mut child2 = Command::new("/usr/bin/env")
|
||||
.args(["bash", "-c", &arguments])
|
||||
.stdout(Stdio::null())
|
||||
.output()?;
|
||||
.spawn()?;
|
||||
|
||||
// Wait for both child processes to finish and check their exit status
|
||||
if !child1.wait()?.success() || !child2.wait()?.success() {
|
||||
return Err(Error::InstallXtensaRust);
|
||||
}
|
||||
}
|
||||
// Some platfroms like Windows are available in single bundle rust + src, because install
|
||||
// script in dist is not available for the plaform. It's sufficient to extract the toolchain
|
||||
#[cfg(windows)]
|
||||
if cfg!(windows) {
|
||||
// TODO: Windows only supports `esp` as toolchain name atm since its hardcoded in the rust-build asset
|
||||
download_file(
|
||||
self.dist_url.clone(),
|
||||
"rust.zip",
|
||||
&self.toolchain_destination.display().to_string(),
|
||||
true,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@ -240,61 +300,7 @@ impl Installable for XtensaRust {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Hash, Eq, PartialEq, Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct Crate {
|
||||
/// Crate name.
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl Crate {
|
||||
/// Create a crate instance.
|
||||
pub fn new(name: &str) -> Self {
|
||||
Crate {
|
||||
name: name.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the extra crates to be installed.
|
||||
pub fn parse_crates(arg: &str) -> Result<HashSet<Crate>> {
|
||||
Ok(arg.split(',').map(Crate::new).collect())
|
||||
}
|
||||
|
||||
pub fn uninstall(extra_crate: &str) -> Result<(), Error> {
|
||||
cmd!("cargo", "uninstall", extra_crate, "--quiet")
|
||||
.into_inner()
|
||||
.stdout(Stdio::null())
|
||||
.output()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Installable for Crate {
|
||||
async fn install(&self) -> Result<Vec<String>, Error> {
|
||||
debug!("{} Installing crate: {}", emoji::DEBUG, self.name);
|
||||
|
||||
#[cfg(unix)]
|
||||
let crate_path = format!("{}/bin/{}", get_cargo_home().display(), self.name);
|
||||
#[cfg(windows)]
|
||||
let crate_path = format!("{}/bin/{}.exe", get_cargo_home().display(), self.name);
|
||||
|
||||
if PathBuf::from(crate_path).exists() {
|
||||
warn!("{} {} is already installed", emoji::WARN, self.name);
|
||||
} else {
|
||||
cmd!("cargo", "install", &self.name, "--quiet")
|
||||
.into_inner()
|
||||
.stdout(Stdio::null())
|
||||
.output()?;
|
||||
}
|
||||
|
||||
Ok(vec![]) // No exports
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
format!("crate {}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RiscVTarget {
|
||||
/// Nightly version.
|
||||
pub nightly_version: String,
|
||||
@ -311,18 +317,22 @@ impl RiscVTarget {
|
||||
/// Uninstalls the RISC-V target.
|
||||
pub fn uninstall(nightly_version: &str) -> Result<(), Error> {
|
||||
info!("{} Uninstalling RISC-V target", emoji::WRENCH);
|
||||
cmd!(
|
||||
"rustup",
|
||||
"target",
|
||||
"remove",
|
||||
"--toolchain",
|
||||
nightly_version,
|
||||
"riscv32imc-unknown-none-elf",
|
||||
"riscv32imac-unknown-none-elf"
|
||||
)
|
||||
.into_inner()
|
||||
.stdout(Stdio::null())
|
||||
.output()?;
|
||||
|
||||
if !Command::new("rustup")
|
||||
.args([
|
||||
"target",
|
||||
"remove",
|
||||
"--toolchain",
|
||||
nightly_version,
|
||||
"riscv32imc-unknown-none-elf",
|
||||
"riscv32imac-unknown-none-elf",
|
||||
])
|
||||
.stdout(Stdio::null())
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
return Err(Error::UninstallRiscvTarget);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -330,36 +340,38 @@ impl RiscVTarget {
|
||||
#[async_trait]
|
||||
impl Installable for RiscVTarget {
|
||||
async fn install(&self) -> Result<Vec<String>, Error> {
|
||||
info!("{} Installing RISC-V target", emoji::WRENCH);
|
||||
cmd!(
|
||||
"rustup",
|
||||
"component",
|
||||
"add",
|
||||
"rust-src",
|
||||
"--toolchain",
|
||||
info!(
|
||||
"{} Installing RISC-V targets ('riscv32imc-unknown-none-elf' and 'riscv32imac-unknown-none-elf') for '{}' toolchain",
|
||||
emoji::WRENCH,
|
||||
&self.nightly_version
|
||||
)
|
||||
.into_inner()
|
||||
.stderr(Stdio::null())
|
||||
.output()?;
|
||||
cmd!(
|
||||
"rustup",
|
||||
"target",
|
||||
"add",
|
||||
"--toolchain",
|
||||
&self.nightly_version,
|
||||
"riscv32imc-unknown-none-elf",
|
||||
"riscv32imac-unknown-none-elf"
|
||||
)
|
||||
.into_inner()
|
||||
.stderr(Stdio::null())
|
||||
.output()?;
|
||||
);
|
||||
|
||||
if !Command::new("rustup")
|
||||
.args([
|
||||
"toolchain",
|
||||
"install",
|
||||
&self.nightly_version,
|
||||
"--profile",
|
||||
"minimal",
|
||||
"--component",
|
||||
"rust-src",
|
||||
"--target",
|
||||
"riscv32imc-unknown-none-elf",
|
||||
"riscv32imac-unknown-none-elf",
|
||||
])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status()?
|
||||
.success()
|
||||
{
|
||||
return Err(Error::InstallRiscvTarget(self.nightly_version.clone()));
|
||||
}
|
||||
|
||||
Ok(vec![]) // No exports
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
"RISC-V rust target".to_string()
|
||||
"RISC-V Rust target".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@ -395,141 +407,32 @@ pub fn get_rustup_home() -> PathBuf {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Checks if rustup and the proper nightly version are installed. If rustup is not installed,
|
||||
/// it returns an error. If nigthly version is not installed, proceed to install it.
|
||||
pub async fn check_rust_installation(
|
||||
nightly_version: &str,
|
||||
host_triple: &HostTriple,
|
||||
) -> Result<()> {
|
||||
info!("{} Checking existing Rust installation", emoji::WRENCH);
|
||||
/// Checks if rustup is installed.
|
||||
pub async fn check_rust_installation() -> Result<(), Error> {
|
||||
info!("{} Checking Rust installation", emoji::WRENCH);
|
||||
|
||||
match cmd!("rustup", "toolchain", "list")
|
||||
.into_inner()
|
||||
if let Err(e) = Command::new("rustup")
|
||||
.arg("--version")
|
||||
.stdout(Stdio::piped())
|
||||
.output()
|
||||
{
|
||||
Ok(child_output) => {
|
||||
let result = String::from_utf8_lossy(&child_output.stdout);
|
||||
if !result.contains("nightly") {
|
||||
warn!("{} Rust nightly toolchain not found", emoji::WARN);
|
||||
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, host_triple).await?;
|
||||
} else {
|
||||
return Err(Error::RustupDetectionError(e.to_string())).into_diagnostic();
|
||||
}
|
||||
if let std::io::ErrorKind::NotFound = e.kind() {
|
||||
return Err(Error::MissingRust);
|
||||
} else {
|
||||
return Err(Error::RustupDetection(e.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Installs rustup
|
||||
async fn install_rustup(nightly_version: &str, host_triple: &HostTriple) -> Result<(), Error> {
|
||||
#[cfg(windows)]
|
||||
let rustup_init_path = download_file(
|
||||
"https://win.rustup.rs/x86_64".to_string(),
|
||||
"rustup-init.exe",
|
||||
&get_dist_path("rustup"),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
#[cfg(unix)]
|
||||
let rustup_init_path = download_file(
|
||||
"https://sh.rustup.rs".to_string(),
|
||||
"rustup-init.sh",
|
||||
&get_dist_path("rustup"),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
info!(
|
||||
"{} Installing rustup with {} toolchain",
|
||||
emoji::WRENCH,
|
||||
nightly_version
|
||||
);
|
||||
|
||||
#[cfg(windows)]
|
||||
cmd!(
|
||||
rustup_init_path,
|
||||
"--default-toolchain",
|
||||
nightly_version,
|
||||
"--default-host",
|
||||
host_triple.to_string(),
|
||||
"--profile",
|
||||
"minimal",
|
||||
"-y"
|
||||
)
|
||||
.into_inner()
|
||||
.stdout(Stdio::null())
|
||||
.output()?;
|
||||
#[cfg(unix)]
|
||||
cmd!(
|
||||
"/usr/bin/env",
|
||||
"bash",
|
||||
rustup_init_path,
|
||||
"--default-toolchain",
|
||||
nightly_version,
|
||||
"--default-host",
|
||||
host_triple.to_string(),
|
||||
"--profile",
|
||||
"minimal",
|
||||
"-y"
|
||||
)
|
||||
.into_inner()
|
||||
.stdout(Stdio::null())
|
||||
.output()?;
|
||||
|
||||
#[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(())
|
||||
}
|
||||
|
||||
/// Installs the desired version of the nightly toolchain.
|
||||
fn install_rust_nightly(version: &str) -> Result<(), Error> {
|
||||
info!("{} Installing {} toolchain", emoji::WRENCH, version);
|
||||
cmd!(
|
||||
"rustup",
|
||||
"toolchain",
|
||||
"install",
|
||||
version,
|
||||
"--profile",
|
||||
"minimal"
|
||||
)
|
||||
.into_inner()
|
||||
.stdout(Stdio::null())
|
||||
.output()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
logging::initialize_logger,
|
||||
toolchain::rust::{get_cargo_home, get_rustup_home, Crate, XtensaRust},
|
||||
toolchain::rust::{get_cargo_home, get_rustup_home, XtensaRust},
|
||||
};
|
||||
use directories::BaseDirs;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[test]
|
||||
fn test_xtensa_rust_parse_version() {
|
||||
@ -548,35 +451,6 @@ mod tests {
|
||||
assert!(XtensaRust::parse_version("1._.*.1").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(unused_variables)]
|
||||
fn test_parse_crates() {
|
||||
let mut crates: HashSet<Crate> = HashSet::new();
|
||||
crates.insert(Crate::new("ldproxy"));
|
||||
assert!(matches!(Crate::parse_crates("ldproxy"), Ok(crates)));
|
||||
let mut crates: HashSet<Crate> = HashSet::new();
|
||||
crates.insert(Crate::new("ldproxy"));
|
||||
crates.insert(Crate::new("espflash"));
|
||||
assert!(matches!(
|
||||
Crate::parse_crates("ldproxy, espflash"),
|
||||
Ok(crates)
|
||||
));
|
||||
let mut crates: HashSet<Crate> = HashSet::new();
|
||||
crates.insert(Crate::new("cargo-generate"));
|
||||
crates.insert(Crate::new("sccache"));
|
||||
assert!(matches!(
|
||||
Crate::parse_crates("cargo-generate sccache"),
|
||||
Ok(crates)
|
||||
));
|
||||
let mut crates: HashSet<Crate> = HashSet::new();
|
||||
crates.insert(Crate::new("cargo-binstall"));
|
||||
crates.insert(Crate::new("espmonitor"));
|
||||
assert!(matches!(
|
||||
Crate::parse_crates("cargo-binstall,espmonitor"),
|
||||
Ok(crates)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cargo_home() {
|
||||
// No CARGO_HOME set
|
||||
|
Loading…
x
Reference in New Issue
Block a user