From 9c1fe215747db1977938538256a6499fa907b03b Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Fri, 20 Oct 2023 17:17:19 +0200 Subject: [PATCH 01/33] feat: Initial env mod implementation --- src/env/env.fish | 12 ++ src/env/env.sh | 20 +++ src/{env.rs => env/mod.rs} | 23 ++- src/env/shell.rs | 293 +++++++++++++++++++++++++++++++++++++ src/error.rs | 2 + src/toolchain/gcc.rs | 6 +- 6 files changed, 352 insertions(+), 4 deletions(-) create mode 100644 src/env/env.fish create mode 100644 src/env/env.sh rename src/{env.rs => env/mod.rs} (87%) create mode 100644 src/env/shell.rs diff --git a/src/env/env.fish b/src/env/env.fish new file mode 100644 index 00000000..60881bb4 --- /dev/null +++ b/src/env/env.fish @@ -0,0 +1,12 @@ +# espup shell setup +if not contains "{xtensa_gcc}" $PATH + # Prepending path in case a system-installed rustc needs to be overridden + set -x PATH "{xtensa_gcc}" $PATH +end + +if not contains "{riscv_gcc}" $PATH + # Prepending path in case a system-installed rustc needs to be overridden + set -x PATH "{riscv_gcc}" $PATH +end + +set -x LIBCLANG_PATH "{libclang}" diff --git a/src/env/env.sh b/src/env/env.sh new file mode 100644 index 00000000..dfbc6dd7 --- /dev/null +++ b/src/env/env.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# espup shell setup +# affix colons on either side of $PATH to simplify matching +case ":${PATH}:" in +*:"{xtensa_gcc}":*) ;; +*) + # Prepending path in case a system-installed rustc needs to be overridden + export PATH="{xtensa_gcc}:$PATH" + ;; +esac + +case ":${PATH}:" in +*:"{riscv_gcc}":*) ;; +*) + # Prepending path in case a system-installed rustc needs to be overridden + export PATH="{riscv_gcc}:$PATH" + ;; +esac + +export LIBCLANG_PATH="{libclang_path}" diff --git a/src/env.rs b/src/env/mod.rs similarity index 87% rename from src/env.rs rename to src/env/mod.rs index 9b17d915..ad80e7c6 100644 --- a/src/env.rs +++ b/src/env/mod.rs @@ -7,6 +7,8 @@ use log::info; use log::warn; #[cfg(windows)] use std::env; +#[cfg(unix)] +use std::fs::OpenOptions; use std::{ fs::File, io::Write, @@ -18,11 +20,14 @@ use winreg::{ RegKey, }; +pub mod shell; + #[cfg(windows)] const DEFAULT_EXPORT_FILE: &str = "export-esp.ps1"; #[cfg(not(windows))] const DEFAULT_EXPORT_FILE: &str = "export-esp.sh"; +// TODO: Move this to a windows.rs file #[cfg(windows)] /// Sets an environment variable for the current user. pub fn set_environment_variable(key: &str, value: &str) -> Result<(), Error> { @@ -98,9 +103,23 @@ pub fn export_environment(export_file: &Path) -> Result<(), Error> { } #[cfg(unix)] if cfg!(unix) { + let source_command = format!(". {}", export_file.display()); + // let profile = BaseDirs::new().unwrap().home_dir().join(".profile"); + // let bashrc = BaseDirs::new().unwrap().home_dir().join(".bashrc"); + // let rcs = vec![profile, bashrc]; + // for rc in rcs { + // if rc.exists() { + // let mut file = OpenOptions::new() + // .write(true) + // .append(true) + // .open(rc) + // .unwrap(); + // writeln!(file, "{}", source_command)?; + // } + // } println!( - "\n\tTo get started, you need to set up some environment variables by running: '. {}'", - export_file.display() + "\n\tTo get started, you need to set up some environment variables by running: '{}'", + source_command ); println!( "\tThis step must be done every time you open a new terminal.\n\t See other methods for setting the environment in https://esp-rs.github.io/book/installation/riscv-and-xtensa.html#3-set-up-the-environment-variables", diff --git a/src/env/shell.rs b/src/env/shell.rs new file mode 100644 index 00000000..79d474e4 --- /dev/null +++ b/src/env/shell.rs @@ -0,0 +1,293 @@ +// THIS FILE IS BASED ON RUSTUP SOURCE CODE: https://github.com/rust-lang/rustup/blob/b900a6cd87e1f463a55ce02e956c24b2cccdd0f0/src/cli/self_update/shell.rs + +//! Paths and Unix shells +//! +//! MacOS, Linux, FreeBSD, and many other OS model their design on Unix, +//! so handling them is relatively consistent. But only relatively. +//! POSIX postdates Unix by 20 years, and each "Unix-like" shell develops +//! unique quirks over time. +//! +//! +//! Windowing Managers, Desktop Environments, GUI Terminals, and PATHs +//! +//! Duplicating paths in PATH can cause performance issues when the OS searches +//! the same place multiple times. Traditionally, Unix configurations have +//! resolved this by setting up PATHs in the shell's login profile. +//! +//! This has its own issues. Login profiles are only intended to run once, but +//! changing the PATH is common enough that people may run it twice. Desktop +//! environments often choose to NOT start login shells in GUI terminals. Thus, +//! a trend has emerged to place PATH updates in other run-commands (rc) files, +//! leaving Rustup with few assumptions to build on for fulfilling its promise +//! to set up PATH appropriately. +//! +//! Rustup addresses this by: +//! 1) using a shell script that updates PATH if the path is not in PATH +//! 2) sourcing this script (`. /path/to/script`) in any appropriate rc file + +use crate::error::Error; +use directories::BaseDirs; +use log::{error, warn}; +use std::borrow::Cow; +use std::env; +use std::fs::OpenOptions; +use std::io::{Result, Write}; + +use std::cell::RefCell; +use std::path::{Path, PathBuf}; + +// use super::get_export_file; + +// thread_local! { +// pub(crate) static PROCESS:RefCell> = RefCell::new(None); +// } + +// use super::utils; +// use crate::{currentprocess::varsource::VarSource, process}; + +pub(crate) type Shell = Box; + +#[derive(Debug, PartialEq)] +pub(crate) struct ShellScript { + content: &'static str, + name: &'static str, +} + +impl ShellScript { + pub(crate) fn write(&self) -> Result<()> { + // // Export file name + // let env_name = toolch.join(self.name); + // // Export file content + // let env_file = self.content; + // write_file(self.name, &env_name, &env_file)?; + Ok(()) + } +} + +// TODO: Tcsh (BSD) +// TODO?: Make a decision on Ion Shell, Power Shell, Nushell +// Cross-platform non-POSIX shells have not been assessed for integration yet +fn enumerate_shells() -> Vec { + vec![ + Box::new(Posix), + Box::new(Bash), + Box::new(Zsh), + Box::new(Fish), + ] +} + +pub(crate) fn get_available_shells() -> impl Iterator { + enumerate_shells().into_iter().filter(|sh| sh.does_exist()) +} + +pub(crate) trait UnixShell { + // Detects if a shell "exists". Users have multiple shells, so an "eager" + // heuristic should be used, assuming shells exist if any traces do. + fn does_exist(&self) -> bool; + + // Gives all rcfiles of a given shell that Rustup is concerned with. + // Used primarily in checking rcfiles for cleanup. + fn rcfiles(&self) -> Vec; + + // Gives rcs that should be written to. + fn update_rcs(&self) -> Vec; + + // Writes the relevant env file. + fn env_script(&self) -> ShellScript { + ShellScript { + name: "env", + content: include_str!("env.sh"), + } + } + + fn source_string(&self) -> Result { + Ok(format!(r#". "{}/env""#, "get_export_file()?")) + } +} + +struct Posix; +impl UnixShell for Posix { + fn does_exist(&self) -> bool { + true + } + + fn rcfiles(&self) -> Vec { + vec![BaseDirs::new().unwrap().home_dir().join(".profile")] + } + + fn update_rcs(&self) -> Vec { + // Write to .profile even if it doesn't exist. It's the only rc in the + // POSIX spec so it should always be set up. + self.rcfiles() + } +} + +struct Bash; + +impl UnixShell for Bash { + fn does_exist(&self) -> bool { + !self.update_rcs().is_empty() + } + + fn rcfiles(&self) -> Vec { + // Bash also may read .profile, however Rustup already includes handling + // .profile as part of POSIX and always does setup for POSIX shells. + [".bash_profile", ".bash_login", ".bashrc"] + .iter() + .map(|rc| BaseDirs::new().unwrap().home_dir().join(rc)) + .collect() + // TODO: Verify the output of this + } + + fn update_rcs(&self) -> Vec { + self.rcfiles() + .into_iter() + .filter(|rc| rc.is_file()) + .collect() + } +} + +struct Zsh; + +impl Zsh { + fn zdotdir() -> Result { + use std::ffi::OsStr; + use std::os::unix::ffi::OsStrExt; + + if matches!(std::env::var("SHELL"), Ok(sh) if sh.contains("zsh")) { + match std::env::var("ZDOTDIR") { + Ok(dir) if !dir.is_empty() => Ok(PathBuf::from(dir)), + _ => panic!("ZDOTDIR not set"), + // TODO:IMPROVE + } + } else { + match std::process::Command::new("zsh") + .args(["-c", "'echo $ZDOTDIR'"]) + .output() + { + Ok(io) if !io.stdout.is_empty() => Ok(PathBuf::from(OsStr::from_bytes(&io.stdout))), + _ => panic!("ZDOTDIR not set"), + // TODO:IMPROVE + } + } + } +} + +impl UnixShell for Zsh { + fn does_exist(&self) -> bool { + // zsh has to either be the shell or be callable for zsh setup. + matches!(std::env::var("SHELL"), Ok(sh) if sh.contains("zsh")) + || find_cmd(&["zsh"]).is_some() + } + + fn rcfiles(&self) -> Vec { + let home_dir: Option = Some(BaseDirs::new().unwrap().home_dir().into()); + [Zsh::zdotdir().ok(), home_dir] + .iter() + .filter_map(|dir| dir.as_ref().map(|p| p.join(".zshenv"))) + .collect() + } + + fn update_rcs(&self) -> Vec { + // zsh can change $ZDOTDIR both _before_ AND _during_ reading .zshenv, + // so we: write to $ZDOTDIR/.zshenv if-exists ($ZDOTDIR changes before) + // OR write to $HOME/.zshenv if it exists (change-during) + // if neither exist, we create it ourselves, but using the same logic, + // because we must still respond to whether $ZDOTDIR is set or unset. + // In any case we only write once. + self.rcfiles() + .into_iter() + .filter(|env| env.is_file()) + .chain(self.rcfiles()) + .take(1) + .collect() + } +} + +struct Fish; + +impl UnixShell for Fish { + fn does_exist(&self) -> bool { + // fish has to either be the shell or be callable for fish setup. + matches!(std::env::var("SHELL"), Ok(sh) if sh.contains("fish")) + || find_cmd(&["fish"]).is_some() + } + + // > "$XDG_CONFIG_HOME/fish/conf.d" (or "~/.config/fish/conf.d" if that variable is unset) for the user + // from + fn rcfiles(&self) -> Vec { + let p0 = std::env::var("XDG_CONFIG_HOME").ok().map(|p| { + let mut path = PathBuf::from(p); + path.push("fish/conf.d/espup.fish"); + path + }); + + let p1 = BaseDirs::new() + .unwrap() + .home_dir() + .join(".config/fish/conf.d/espup.fish"); + + p0.into_iter().chain(Some(p1)).collect() + } + + fn update_rcs(&self) -> Vec { + self.rcfiles() + } + + fn env_script(&self) -> ShellScript { + ShellScript { + name: "env.fish", + content: include_str!("env.fish"), + } + } + + fn source_string(&self) -> Result { + Ok(format!(r#". "{}/env.fish""#, "get_export_file()?")) + } +} + +pub(crate) fn legacy_paths() -> impl Iterator { + // TODO: Avoid using BaseDirs::new... so many times, also in other places. + let zprofiles = Zsh::zdotdir() + .into_iter() + .chain(Some(BaseDirs::new().unwrap().home_dir().into())) + .map(|d| d.join(".zprofile")); + let profiles = [".bash_profile", ".profile"] + .iter() + .map(|rc| BaseDirs::new().unwrap().home_dir().join(rc)); + profiles.chain(zprofiles) +} + +pub(crate) fn find_cmd<'a>(cmds: &[&'a str]) -> Option<&'a str> { + cmds.iter().cloned().find(|&s| has_cmd(s)) +} + +fn has_cmd(cmd: &str) -> bool { + let cmd = format!("{}{}", cmd, env::consts::EXE_SUFFIX); + let path = std::env::var_os("PATH").unwrap_or_default(); + env::split_paths(&path) + .map(|p| p.join(&cmd)) + .any(|p| p.exists()) +} + +pub fn write_file(path: &Path, contents: &str) -> Result<()> { + let mut file = OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(path)?; + + Write::write_all(&mut file, contents.as_bytes())?; + + file.sync_data()?; + + Ok(()) +} + +// /// Obtain the current instance of HomeProcess +// pub(crate) fn home_process() -> Process { +// match PROCESS.with(|p| p.borrow().clone()) { +// None => panic!("No process instance"), +// Some(p) => p, +// } +// } diff --git a/src/error.rs b/src/error.rs index d3452070..48553ada 100644 --- a/src/error.rs +++ b/src/error.rs @@ -69,4 +69,6 @@ pub enum Error { #[diagnostic(code(espup::toolchain::rust::rust_src))] #[error("Failed to install 'rust-src' component of Xtensa Rust")] XtensaRustSrc, + // #[error(transparent)] + // Zsh(#[from] std::io::Error), } diff --git a/src/toolchain/gcc.rs b/src/toolchain/gcc.rs index 53130c44..fb32db46 100644 --- a/src/toolchain/gcc.rs +++ b/src/toolchain/gcc.rs @@ -93,8 +93,10 @@ impl Installable for Gcc { ); } #[cfg(unix)] - exports.push(format!("export PATH=\"{}:$PATH\"", &self.get_bin_path())); - + if cfg!(windows) { + exports.push(format!("export PATH=\"{}:$PATH\"", &self.get_bin_path())); + // TODO: Write env files? + } Ok(exports) } From c1f6ec58064f5a520c1ccabf133e944e7e4b59ca Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Fri, 20 Oct 2023 20:34:03 +0200 Subject: [PATCH 02/33] feat: Add windows and unix mods --- src/env/mod.rs | 47 +++++------------------------- src/env/shell.rs | 18 ++---------- src/env/unix.rs | 68 +++++++++++++++++++++++++++++++++++++++++++ src/env/windows.rs | 32 ++++++++++++++++++++ src/toolchain/gcc.rs | 8 +++-- src/toolchain/llvm.rs | 6 ++-- 6 files changed, 121 insertions(+), 58 deletions(-) create mode 100644 src/env/unix.rs create mode 100644 src/env/windows.rs diff --git a/src/env/mod.rs b/src/env/mod.rs index ad80e7c6..330d2c6f 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -3,57 +3,23 @@ use crate::error::Error; use directories::BaseDirs; use log::info; -#[cfg(windows)] -use log::warn; -#[cfg(windows)] -use std::env; -#[cfg(unix)] -use std::fs::OpenOptions; use std::{ fs::File, io::Write, path::{Path, PathBuf}, }; -#[cfg(windows)] -use winreg::{ - enums::{HKEY_CURRENT_USER, KEY_READ, KEY_WRITE}, - RegKey, -}; pub mod shell; +#[cfg(unix)] +pub mod unix; +#[cfg(windows)] +pub mod windows; #[cfg(windows)] const DEFAULT_EXPORT_FILE: &str = "export-esp.ps1"; -#[cfg(not(windows))] +#[cfg(unix)] const DEFAULT_EXPORT_FILE: &str = "export-esp.sh"; -// TODO: Move this to a windows.rs file -#[cfg(windows)] -/// Sets an environment variable for the current user. -pub fn set_environment_variable(key: &str, value: &str) -> Result<(), Error> { - env::set_var(key, value); - - let hkcu = RegKey::predef(HKEY_CURRENT_USER); - let environment_key = hkcu.open_subkey_with_flags("Environment", KEY_WRITE)?; - environment_key.set_value(key, &value)?; - Ok(()) -} - -#[cfg(windows)] -/// Deletes an environment variable for the current user. -pub fn delete_environment_variable(key: &str) -> Result<(), Error> { - if env::var_os(key).is_none() { - return Ok(()); - } - - env::remove_var(key); - - let hkcu = RegKey::predef(HKEY_CURRENT_USER); - let environment_key = hkcu.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)?; - environment_key.delete_value(key)?; - Ok(()) -} - /// Returns the absolute path to the export file, uses the DEFAULT_EXPORT_FILE if no arg is provided. pub fn get_export_file(export_file: Option) -> Result { if let Some(export_file) = export_file { @@ -104,6 +70,9 @@ pub fn export_environment(export_file: &Path) -> Result<(), Error> { #[cfg(unix)] if cfg!(unix) { let source_command = format!(". {}", export_file.display()); + + // Check if the GCC_RISCV environment variable is set + // let profile = BaseDirs::new().unwrap().home_dir().join(".profile"); // let bashrc = BaseDirs::new().unwrap().home_dir().join(".bashrc"); // let rcs = vec![profile, bashrc]; diff --git a/src/env/shell.rs b/src/env/shell.rs index 79d474e4..4352fcaa 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -25,26 +25,12 @@ //! 1) using a shell script that updates PATH if the path is not in PATH //! 2) sourcing this script (`. /path/to/script`) in any appropriate rc file -use crate::error::Error; use directories::BaseDirs; -use log::{error, warn}; -use std::borrow::Cow; use std::env; use std::fs::OpenOptions; use std::io::{Result, Write}; - -use std::cell::RefCell; use std::path::{Path, PathBuf}; -// use super::get_export_file; - -// thread_local! { -// pub(crate) static PROCESS:RefCell> = RefCell::new(None); -// } - -// use super::utils; -// use crate::{currentprocess::varsource::VarSource, process}; - pub(crate) type Shell = Box; #[derive(Debug, PartialEq)] @@ -58,7 +44,9 @@ impl ShellScript { // // Export file name // let env_name = toolch.join(self.name); // // Export file content - // let env_file = self.content; + // REPLACE PLACEHOLDERS WITH ENVIRONMENT CVAIRABLES VALUES + // let env_file = self.content.replace(std::); + // write_file(self.name, &env_name, &env_file)?; Ok(()) } diff --git a/src/env/unix.rs b/src/env/unix.rs new file mode 100644 index 00000000..50f48abd --- /dev/null +++ b/src/env/unix.rs @@ -0,0 +1,68 @@ +use crate::error::Error; +use std::fs::OpenOptions; + +// // TODO: USED FOR UNINSTALING +// pub(crate) fn do_remove_from_path() -> Result<()> { +// for sh in shell::get_available_shells() { +// let source_bytes = format!("{}\n", sh.source_string()?).into_bytes(); + +// // Check more files for cleanup than normally are updated. +// for rc in sh.rcfiles().iter().filter(|rc| rc.is_file()) { +// let file = utils::read_file("rcfile", rc)?; +// let file_bytes = file.into_bytes(); +// // FIXME: This is whitespace sensitive where it should not be. +// if let Some(idx) = file_bytes +// .windows(source_bytes.len()) +// .position(|w| w == source_bytes.as_slice()) +// { +// // Here we rewrite the file without the offending line. +// let mut new_bytes = file_bytes[..idx].to_vec(); +// new_bytes.extend(&file_bytes[idx + source_bytes.len()..]); +// let new_file = String::from_utf8(new_bytes).unwrap(); +// utils::write_file("rcfile", rc, &new_file)?; +// } +// } +// } + +// remove_legacy_paths()?; + +// Ok(()) +// } + +// pub(crate) fn do_add_to_path() -> Result<()> { +// for sh in shell::get_available_shells() { +// let source_cmd = sh.source_string()?; +// let source_cmd_with_newline = format!("\n{}", &source_cmd); + +// for rc in sh.update_rcs() { +// let cmd_to_write = match utils::read_file("rcfile", &rc) { +// Ok(contents) if contents.contains(&source_cmd) => continue, +// Ok(contents) if !contents.ends_with('\n') => &source_cmd_with_newline, +// _ => &source_cmd, +// }; + +// utils::append_file("rcfile", &rc, cmd_to_write) +// .with_context(|| format!("could not amend shell profile: '{}'", rc.display()))?; +// } +// } + +// remove_legacy_paths()?; + +// Ok(()) +// } + +// // TODO: THIS IS CALLED BEFORE do_add_to_path() +// pub(crate) fn do_write_env_files() -> Result<()> { +// let mut written = vec![]; + +// for sh in shell::get_available_shells() { +// let script = sh.env_script(); +// // Only write each possible script once. +// if !written.contains(&script) { +// script.write()?; +// written.push(script); +// } +// } + +// Ok(()) +// } diff --git a/src/env/windows.rs b/src/env/windows.rs new file mode 100644 index 00000000..188ce7fd --- /dev/null +++ b/src/env/windows.rs @@ -0,0 +1,32 @@ +use log::warn; +use std::env; +use winreg::{ + enums::{HKEY_CURRENT_USER, KEY_READ, KEY_WRITE}, + RegKey, +}; + +#[cfg(windows)] +/// Sets an environment variable for the current user. +pub fn set_environment_variable(key: &str, value: &str) -> Result<(), Error> { + env::set_var(key, value); + + let hkcu = RegKey::predef(HKEY_CURRENT_USER); + let environment_key = hkcu.open_subkey_with_flags("Environment", KEY_WRITE)?; + environment_key.set_value(key, &value)?; + Ok(()) +} + +#[cfg(windows)] +/// Deletes an environment variable for the current user. +pub fn delete_environment_variable(key: &str) -> Result<(), Error> { + if env::var_os(key).is_none() { + return Ok(()); + } + + env::remove_var(key); + + let hkcu = RegKey::predef(HKEY_CURRENT_USER); + let environment_key = hkcu.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)?; + environment_key.delete_value(key)?; + Ok(()) +} diff --git a/src/toolchain/gcc.rs b/src/toolchain/gcc.rs index fb32db46..ddca6bd2 100644 --- a/src/toolchain/gcc.rs +++ b/src/toolchain/gcc.rs @@ -93,9 +93,13 @@ impl Installable for Gcc { ); } #[cfg(unix)] - if cfg!(windows) { + if cfg!(unix) { exports.push(format!("export PATH=\"{}:$PATH\"", &self.get_bin_path())); - // TODO: Write env files? + if self.arch == RISCV_GCC { + std::env::set_var("RISCV_GCC", self.get_bin_path()); + } else { + std::env::set_var("XTENSA_GCC", self.get_bin_path()); + } } Ok(exports) } diff --git a/src/toolchain/llvm.rs b/src/toolchain/llvm.rs index 7998c6e5..dbf513ee 100644 --- a/src/toolchain/llvm.rs +++ b/src/toolchain/llvm.rs @@ -186,8 +186,10 @@ impl Installable for Llvm { ); } #[cfg(unix)] - exports.push(format!("export LIBCLANG_PATH=\"{}\"", self.get_lib_path())); - + if cfg!(unix) { + exports.push(format!("export LIBCLANG_PATH=\"{}\"", self.get_lib_path())); + std::env::set_var("LIBCLANG_PATH", self.get_lib_path()); + } if self.extended { #[cfg(windows)] if cfg!(windows) { From 1ce88556371f9429fc3a5ae09ad5ca0c81c0ed24 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Fri, 20 Oct 2023 21:47:17 +0200 Subject: [PATCH 03/33] feat: Write env files --- src/env/env.fish | 3 +++ src/env/mod.rs | 14 +++++++------- src/env/shell.rs | 35 ++++++++++++++++++++++++----------- src/env/unix.rs | 26 ++++++++++++++------------ src/toolchain/mod.rs | 16 ++++++++-------- 5 files changed, 56 insertions(+), 38 deletions(-) diff --git a/src/env/env.fish b/src/env/env.fish index 60881bb4..c5ae5f04 100644 --- a/src/env/env.fish +++ b/src/env/env.fish @@ -1,4 +1,7 @@ # espup shell setup +# TODO: WE NEED TO VERIFY THAT PLACEHOLDERS ARE REPLACED. +# EG: USING THE --NO-STD FLAG WONT INSTALL GCC, HENCE WE WILL WRITE THE PATH WITH THE PLACEHOLDER + if not contains "{xtensa_gcc}" $PATH # Prepending path in case a system-installed rustc needs to be overridden set -x PATH "{xtensa_gcc}" $PATH diff --git a/src/env/mod.rs b/src/env/mod.rs index 330d2c6f..66d41436 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -21,7 +21,10 @@ const DEFAULT_EXPORT_FILE: &str = "export-esp.ps1"; const DEFAULT_EXPORT_FILE: &str = "export-esp.sh"; /// Returns the absolute path to the export file, uses the DEFAULT_EXPORT_FILE if no arg is provided. -pub fn get_export_file(export_file: Option) -> Result { +pub fn get_export_file( + export_file: Option, + toolchain_dir: &PathBuf, +) -> Result { if let Some(export_file) = export_file { if export_file.is_dir() { return Err(Error::InvalidDestination(export_file.display().to_string())); @@ -33,10 +36,7 @@ pub fn get_export_file(export_file: Option) -> Result { Ok(current_dir.join(export_file)) } } else { - Ok(BaseDirs::new() - .unwrap() - .home_dir() - .join(DEFAULT_EXPORT_FILE)) + Ok(toolchain_dir.join(DEFAULT_EXPORT_FILE)) } } @@ -55,7 +55,7 @@ pub fn create_export_file(export_file: &PathBuf, exports: &[String]) -> Result<( } /// Instructions to export the environment variables. -pub fn export_environment(export_file: &Path) -> Result<(), Error> { +pub fn export_environment(export_file: &Path, toolchain_dir: &PathBuf) -> Result<(), Error> { #[cfg(windows)] if cfg!(windows) { set_environment_variable("PATH", &env::var("PATH").unwrap())?; @@ -72,7 +72,7 @@ pub fn export_environment(export_file: &Path) -> Result<(), Error> { let source_command = format!(". {}", export_file.display()); // Check if the GCC_RISCV environment variable is set - + unix::do_write_env_files(toolchain_dir)?; // let profile = BaseDirs::new().unwrap().home_dir().join(".profile"); // let bashrc = BaseDirs::new().unwrap().home_dir().join(".bashrc"); // let rcs = vec![profile, bashrc]; diff --git a/src/env/shell.rs b/src/env/shell.rs index 4352fcaa..8e5f4ba3 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -37,23 +37,34 @@ pub(crate) type Shell = Box; pub(crate) struct ShellScript { content: &'static str, name: &'static str, + toolchain_dir: PathBuf, } impl ShellScript { pub(crate) fn write(&self) -> Result<()> { - // // Export file name - // let env_name = toolch.join(self.name); - // // Export file content - // REPLACE PLACEHOLDERS WITH ENVIRONMENT CVAIRABLES VALUES - // let env_file = self.content.replace(std::); - - // write_file(self.name, &env_name, &env_file)?; + let env_file_path = self.toolchain_dir.join(self.name); + let mut env_file: String = self.content.to_string(); + if std::env::var("XTENSA_GCC").is_ok() { + env_file = env_file.replace( + "{xtensa_gcc}", + std::env::var("XTENSA_GCC").unwrap().as_str(), + ); + } + if std::env::var("RISCV_GCC").is_ok() { + env_file = + env_file.replace("{riscv_gcc}", std::env::var("RISCV_GCC").unwrap().as_str()); + } + if std::env::var("LIBCLANG_PATH").is_ok() { + env_file = env_file.replace( + "{libclang_path}", + std::env::var("LIBCLANG_PATH").unwrap().as_str(), + ); + } + write_file(&env_file_path, &env_file)?; Ok(()) } } -// TODO: Tcsh (BSD) -// TODO?: Make a decision on Ion Shell, Power Shell, Nushell // Cross-platform non-POSIX shells have not been assessed for integration yet fn enumerate_shells() -> Vec { vec![ @@ -81,10 +92,11 @@ pub(crate) trait UnixShell { fn update_rcs(&self) -> Vec; // Writes the relevant env file. - fn env_script(&self) -> ShellScript { + fn env_script(&self, toolchain_dir: &PathBuf) -> ShellScript { ShellScript { name: "env", content: include_str!("env.sh"), + toolchain_dir: toolchain_dir.clone(), } } @@ -222,10 +234,11 @@ impl UnixShell for Fish { self.rcfiles() } - fn env_script(&self) -> ShellScript { + fn env_script(&self, toolchain_dir: &PathBuf) -> ShellScript { ShellScript { name: "env.fish", content: include_str!("env.fish"), + toolchain_dir: toolchain_dir.clone(), } } diff --git a/src/env/unix.rs b/src/env/unix.rs index 50f48abd..101f2940 100644 --- a/src/env/unix.rs +++ b/src/env/unix.rs @@ -1,5 +1,7 @@ +use crate::env::shell; use crate::error::Error; use std::fs::OpenOptions; +use std::path::PathBuf; // // TODO: USED FOR UNINSTALING // pub(crate) fn do_remove_from_path() -> Result<()> { @@ -52,17 +54,17 @@ use std::fs::OpenOptions; // } // // TODO: THIS IS CALLED BEFORE do_add_to_path() -// pub(crate) fn do_write_env_files() -> Result<()> { -// let mut written = vec![]; +pub(crate) fn do_write_env_files(toolchain_dir: &PathBuf) -> Result<(), Error> { + let mut written = vec![]; -// for sh in shell::get_available_shells() { -// let script = sh.env_script(); -// // Only write each possible script once. -// if !written.contains(&script) { -// script.write()?; -// written.push(script); -// } -// } + for sh in shell::get_available_shells() { + let script = sh.env_script(toolchain_dir); + // Only write each possible script once. + if !written.contains(&script) { + script.write()?; + written.push(script); + } + } -// Ok(()) -// } + Ok(()) +} diff --git a/src/toolchain/mod.rs b/src/toolchain/mod.rs index efc1f2b6..79e41e74 100644 --- a/src/toolchain/mod.rs +++ b/src/toolchain/mod.rs @@ -133,7 +133,6 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> InstallMode::Install => info!("Installing the Espressif Rust ecosystem"), InstallMode::Update => info!("Updating the Espressif Rust ecosystem"), } - let export_file = get_export_file(args.export_file)?; let mut exports: Vec = Vec::new(); let host_triple = get_host_triple(args.default_host)?; let xtensa_rust_version = if let Some(toolchain_version) = &args.toolchain_version { @@ -145,9 +144,10 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> } else { XtensaRust::get_latest_version().await? }; - let install_path = get_rustup_home().join("toolchains").join(args.name); + let toolchain_dir = get_rustup_home().join("toolchains").join(args.name); + let export_file = get_export_file(args.export_file, &toolchain_dir)?; let llvm: Llvm = Llvm::new( - &install_path, + &toolchain_dir, &host_triple, args.extended_llvm, &xtensa_rust_version, @@ -160,7 +160,7 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> Some(XtensaRust::new( &xtensa_rust_version, &host_triple, - &install_path, + &toolchain_dir, )) } else { None @@ -184,7 +184,7 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> xtensa_rust, &args.skip_version_parse, targets, - &install_path, + &toolchain_dir, args.toolchain_version, ); @@ -210,13 +210,13 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> .iter() .any(|t| t == &Target::ESP32 || t == &Target::ESP32S2 || t == &Target::ESP32S3) { - let xtensa_gcc = Gcc::new(XTENSA_GCC, &host_triple, &install_path); + let xtensa_gcc = Gcc::new(XTENSA_GCC, &host_triple, &toolchain_dir); to_install.push(Box::new(xtensa_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_GCC, &host_triple, &install_path); + let riscv_gcc = Gcc::new(RISCV_GCC, &host_triple, &toolchain_dir); to_install.push(Box::new(riscv_gcc)); } } @@ -251,7 +251,7 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> InstallMode::Install => info!("Installation successfully completed!"), InstallMode::Update => info!("Update successfully completed!"), } - export_environment(&export_file)?; + export_environment(&export_file, &toolchain_dir)?; Ok(()) } From 0841d8c61dde8d21b8c088b6e78ded92f8e6ba59 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Fri, 20 Oct 2023 20:59:24 +0000 Subject: [PATCH 04/33] feat: Add writing env files and cleanup --- src/env/mod.rs | 117 +++++++++++++++++++++-------------------------- src/env/shell.rs | 28 ++---------- src/env/unix.rs | 103 ++++++++++++++++++++++++----------------- src/error.rs | 8 +++- src/main.rs | 16 ++++--- 5 files changed, 134 insertions(+), 138 deletions(-) diff --git a/src/env/mod.rs b/src/env/mod.rs index 66d41436..f4680055 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -1,7 +1,6 @@ //! Environment variables set up and export file support. use crate::error::Error; -use directories::BaseDirs; use log::info; use std::{ fs::File, @@ -73,19 +72,7 @@ pub fn export_environment(export_file: &Path, toolchain_dir: &PathBuf) -> Result // Check if the GCC_RISCV environment variable is set unix::do_write_env_files(toolchain_dir)?; - // let profile = BaseDirs::new().unwrap().home_dir().join(".profile"); - // let bashrc = BaseDirs::new().unwrap().home_dir().join(".bashrc"); - // let rcs = vec![profile, bashrc]; - // for rc in rcs { - // if rc.exists() { - // let mut file = OpenOptions::new() - // .write(true) - // .append(true) - // .open(rc) - // .unwrap(); - // writeln!(file, "{}", source_command)?; - // } - // } + unix::do_add_to_path(toolchain_dir)?; println!( "\n\tTo get started, you need to set up some environment variables by running: '{}'", source_command @@ -97,57 +84,57 @@ pub fn export_environment(export_file: &Path, toolchain_dir: &PathBuf) -> Result Ok(()) } -#[cfg(test)] -mod tests { - use crate::env::{create_export_file, get_export_file, DEFAULT_EXPORT_FILE}; - use directories::BaseDirs; - use std::{env::current_dir, path::PathBuf}; +// #[cfg(test)] +// mod tests { +// use crate::env::{create_export_file, get_export_file, DEFAULT_EXPORT_FILE}; +// use directories::BaseDirs; +// use std::{env::current_dir, path::PathBuf}; - #[test] - #[allow(unused_variables)] - fn test_get_export_file() { - // No arg provided - let home_dir = BaseDirs::new().unwrap().home_dir().to_path_buf(); - let export_file = home_dir.join(DEFAULT_EXPORT_FILE); - assert!(matches!(get_export_file(None), Ok(export_file))); - // Relative path - let current_dir = current_dir().unwrap(); - let export_file = current_dir.join("export.sh"); - assert!(matches!( - get_export_file(Some(PathBuf::from("export.sh"))), - Ok(export_file) - )); - // Absolute path - let export_file = PathBuf::from("/home/user/export.sh"); - assert!(matches!( - get_export_file(Some(PathBuf::from("/home/user/export.sh"))), - Ok(export_file) - )); - // Path is a directory instead of a file - assert!(get_export_file(Some(home_dir)).is_err()); - } +// #[test] +// #[allow(unused_variables)] +// fn test_get_export_file() { +// // No arg provided +// let home_dir = BaseDirs::new().unwrap().home_dir().to_path_buf(); +// let export_file = home_dir.join(DEFAULT_EXPORT_FILE); +// assert!(matches!(get_export_file(None), Ok(export_file))); +// // Relative path +// let current_dir = current_dir().unwrap(); +// let export_file = current_dir.join("export.sh"); +// assert!(matches!( +// get_export_file(Some(PathBuf::from("export.sh"))), +// Ok(export_file) +// )); +// // Absolute path +// let export_file = PathBuf::from("/home/user/export.sh"); +// assert!(matches!( +// get_export_file(Some(PathBuf::from("/home/user/export.sh"))), +// Ok(export_file) +// )); +// // Path is a directory instead of a file +// assert!(get_export_file(Some(home_dir)).is_err()); +// } - #[test] - fn test_create_export_file() { - // Creates the export file and writes the correct content to it - let temp_dir = tempfile::TempDir::new().unwrap(); - let export_file = temp_dir.path().join("export.sh"); - let exports = vec![ - "export VAR1=value1".to_string(), - "export VAR2=value2".to_string(), - ]; - create_export_file(&export_file, &exports).unwrap(); - let contents = std::fs::read_to_string(export_file).unwrap(); - assert_eq!(contents, "export VAR1=value1\nexport VAR2=value2\n"); +// #[test] +// fn test_create_export_file() { +// // Creates the export file and writes the correct content to it +// let temp_dir = tempfile::TempDir::new().unwrap(); +// let export_file = temp_dir.path().join("export.sh"); +// let exports = vec![ +// "export VAR1=value1".to_string(), +// "export VAR2=value2".to_string(), +// ]; +// create_export_file(&export_file, &exports).unwrap(); +// let contents = std::fs::read_to_string(export_file).unwrap(); +// assert_eq!(contents, "export VAR1=value1\nexport VAR2=value2\n"); - // Returns the correct error when it fails to create the export file (it already exists) - let temp_dir = tempfile::TempDir::new().unwrap(); - let export_file = temp_dir.path().join("export.sh"); - std::fs::create_dir_all(&export_file).unwrap(); - let exports = vec![ - "export VAR1=value1".to_string(), - "export VAR2=value2".to_string(), - ]; - assert!(create_export_file(&export_file, &exports).is_err()); - } -} +// // Returns the correct error when it fails to create the export file (it already exists) +// let temp_dir = tempfile::TempDir::new().unwrap(); +// let export_file = temp_dir.path().join("export.sh"); +// std::fs::create_dir_all(&export_file).unwrap(); +// let exports = vec![ +// "export VAR1=value1".to_string(), +// "export VAR2=value2".to_string(), +// ]; +// assert!(create_export_file(&export_file, &exports).is_err()); +// } +// } diff --git a/src/env/shell.rs b/src/env/shell.rs index 8e5f4ba3..0842c4a7 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -100,8 +100,8 @@ pub(crate) trait UnixShell { } } - fn source_string(&self) -> Result { - Ok(format!(r#". "{}/env""#, "get_export_file()?")) + fn source_string(&self, toolchain_dir: &str) -> Result { + Ok(format!(r#". "{}/env""#, toolchain_dir)) } } @@ -242,23 +242,11 @@ impl UnixShell for Fish { } } - fn source_string(&self) -> Result { - Ok(format!(r#". "{}/env.fish""#, "get_export_file()?")) + fn source_string(&self, toolchain_dir: &str) -> Result { + Ok(format!(r#". "{}/env.fish""#, toolchain_dir)) } } -pub(crate) fn legacy_paths() -> impl Iterator { - // TODO: Avoid using BaseDirs::new... so many times, also in other places. - let zprofiles = Zsh::zdotdir() - .into_iter() - .chain(Some(BaseDirs::new().unwrap().home_dir().into())) - .map(|d| d.join(".zprofile")); - let profiles = [".bash_profile", ".profile"] - .iter() - .map(|rc| BaseDirs::new().unwrap().home_dir().join(rc)); - profiles.chain(zprofiles) -} - pub(crate) fn find_cmd<'a>(cmds: &[&'a str]) -> Option<&'a str> { cmds.iter().cloned().find(|&s| has_cmd(s)) } @@ -284,11 +272,3 @@ pub fn write_file(path: &Path, contents: &str) -> Result<()> { Ok(()) } - -// /// Obtain the current instance of HomeProcess -// pub(crate) fn home_process() -> Process { -// match PROCESS.with(|p| p.borrow().clone()) { -// None => panic!("No process instance"), -// Some(p) => p, -// } -// } diff --git a/src/env/unix.rs b/src/env/unix.rs index 101f2940..91678d79 100644 --- a/src/env/unix.rs +++ b/src/env/unix.rs @@ -1,59 +1,80 @@ use crate::env::shell; use crate::error::Error; use std::fs::OpenOptions; +use std::io::Write; use std::path::PathBuf; // // TODO: USED FOR UNINSTALING -// pub(crate) fn do_remove_from_path() -> Result<()> { -// for sh in shell::get_available_shells() { -// let source_bytes = format!("{}\n", sh.source_string()?).into_bytes(); +pub fn do_remove_from_path(toolchain_dir: &PathBuf) -> Result<(), Error> { + for sh in shell::get_available_shells() { + let source_bytes = format!( + "{}\n", + sh.source_string(&toolchain_dir.display().to_string())? + ) + .into_bytes(); -// // Check more files for cleanup than normally are updated. -// for rc in sh.rcfiles().iter().filter(|rc| rc.is_file()) { -// let file = utils::read_file("rcfile", rc)?; -// let file_bytes = file.into_bytes(); -// // FIXME: This is whitespace sensitive where it should not be. -// if let Some(idx) = file_bytes -// .windows(source_bytes.len()) -// .position(|w| w == source_bytes.as_slice()) -// { -// // Here we rewrite the file without the offending line. -// let mut new_bytes = file_bytes[..idx].to_vec(); -// new_bytes.extend(&file_bytes[idx + source_bytes.len()..]); -// let new_file = String::from_utf8(new_bytes).unwrap(); -// utils::write_file("rcfile", rc, &new_file)?; -// } -// } -// } + // Check more files for cleanup than normally are updated. + for rc in sh.rcfiles().iter().filter(|rc| rc.is_file()) { + let file = std::fs::read_to_string(&rc).map_err(|_| Error::ReadingFile { + name: "rcfile", + path: PathBuf::from(&rc), + })?; + let file_bytes = file.into_bytes(); + // FIXME: This is whitespace sensitive where it should not be. + if let Some(idx) = file_bytes + .windows(source_bytes.len()) + .position(|w| w == source_bytes.as_slice()) + { + // Here we rewrite the file without the offending line. + let mut new_bytes = file_bytes[..idx].to_vec(); + new_bytes.extend(&file_bytes[idx + source_bytes.len()..]); + let new_file = String::from_utf8(new_bytes).unwrap(); + let mut file = OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(rc)?; + Write::write_all(&mut file, new_file.as_bytes())?; -// remove_legacy_paths()?; + file.sync_data()?; + } + } + } -// Ok(()) -// } + Ok(()) +} -// pub(crate) fn do_add_to_path() -> Result<()> { -// for sh in shell::get_available_shells() { -// let source_cmd = sh.source_string()?; -// let source_cmd_with_newline = format!("\n{}", &source_cmd); +pub(crate) fn do_add_to_path(toolchain_dir: &PathBuf) -> Result<(), Error> { + for sh in shell::get_available_shells() { + let source_cmd = sh.source_string(&toolchain_dir.display().to_string())?; + let source_cmd_with_newline = format!("\n{}", &source_cmd); -// for rc in sh.update_rcs() { -// let cmd_to_write = match utils::read_file("rcfile", &rc) { -// Ok(contents) if contents.contains(&source_cmd) => continue, -// Ok(contents) if !contents.ends_with('\n') => &source_cmd_with_newline, -// _ => &source_cmd, -// }; + for rc in sh.update_rcs() { + let file = std::fs::read_to_string(&rc).map_err(|_| Error::ReadingFile { + name: "rcfile", + path: PathBuf::from(&rc), + }); + let cmd_to_write: &str = match file { + Ok(contents) if contents.contains(&source_cmd) => continue, + Ok(contents) if !contents.ends_with('\n') => &source_cmd_with_newline, + _ => &source_cmd, + }; -// utils::append_file("rcfile", &rc, cmd_to_write) -// .with_context(|| format!("could not amend shell profile: '{}'", rc.display()))?; -// } -// } + let mut dest_file = OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(&rc)?; -// remove_legacy_paths()?; + writeln!(dest_file, "{cmd_to_write}")?; -// Ok(()) -// } + dest_file.sync_data()?; + } + } + + Ok(()) +} -// // TODO: THIS IS CALLED BEFORE do_add_to_path() pub(crate) fn do_write_env_files(toolchain_dir: &PathBuf) -> Result<(), Error> { let mut written = vec![]; diff --git a/src/error.rs b/src/error.rs index 48553ada..e40cdb63 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,7 @@ //! Custom error implementations. +use std::path::PathBuf; + #[derive(Debug, miette::Diagnostic, thiserror::Error)] pub enum Error { #[diagnostic(code(espup::toolchain::create_directory))] @@ -69,6 +71,8 @@ pub enum Error { #[diagnostic(code(espup::toolchain::rust::rust_src))] #[error("Failed to install 'rust-src' component of Xtensa Rust")] XtensaRustSrc, - // #[error(transparent)] - // Zsh(#[from] std::io::Error), + + #[diagnostic(code(espup::env::unix))] + #[error("Failed to read {name} file: '{}'", .path.display())] + ReadingFile { name: &'static str, path: PathBuf }, } diff --git a/src/main.rs b/src/main.rs index ba5b2618..b1916d7d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ use clap::{CommandFactory, Parser}; #[cfg(windows)] use espup::env::set_environment_variable; +#[cfg(unix)] +use espup::env::unix; use espup::{ cli::{CompletionsOpts, InstallOpts, UninstallOpts}, error::Error, @@ -69,21 +71,23 @@ async fn uninstall(args: UninstallOpts) -> Result<()> { check_for_update(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); info!("Uninstalling the Espressif Rust ecosystem"); - let install_path = get_rustup_home().join("toolchains").join(args.name); + let install_dir = get_rustup_home().join("toolchains").join(args.name); - Llvm::uninstall(&install_path)?; + Llvm::uninstall(&install_dir)?; - uninstall_gcc_toolchains(&install_path)?; + uninstall_gcc_toolchains(&install_dir)?; info!( "Deleting the Xtensa Rust toolchain located in '{}'", - &install_path.display() + &install_dir.display() ); - remove_dir_all(&install_path) - .map_err(|_| Error::RemoveDirectory(install_path.display().to_string()))?; + remove_dir_all(&install_dir) + .map_err(|_| Error::RemoveDirectory(install_dir.display().to_string()))?; #[cfg(windows)] set_environment_variable("PATH", &env::var("PATH").unwrap())?; + #[cfg(unix)] + unix::do_remove_from_path(&install_dir)?; info!("Uninstallation successfully completed!"); Ok(()) From f524d90cb7053d0100128b03f509f876dfbda7c4 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Fri, 20 Oct 2023 21:54:02 +0000 Subject: [PATCH 05/33] chore: Clippy lints --- src/env/mod.rs | 4 ++-- src/env/shell.rs | 8 ++++---- src/env/unix.rs | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/env/mod.rs b/src/env/mod.rs index f4680055..660b42e7 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -22,7 +22,7 @@ const DEFAULT_EXPORT_FILE: &str = "export-esp.sh"; /// Returns the absolute path to the export file, uses the DEFAULT_EXPORT_FILE if no arg is provided. pub fn get_export_file( export_file: Option, - toolchain_dir: &PathBuf, + toolchain_dir: &Path, ) -> Result { if let Some(export_file) = export_file { if export_file.is_dir() { @@ -54,7 +54,7 @@ pub fn create_export_file(export_file: &PathBuf, exports: &[String]) -> Result<( } /// Instructions to export the environment variables. -pub fn export_environment(export_file: &Path, toolchain_dir: &PathBuf) -> Result<(), Error> { +pub fn export_environment(export_file: &Path, toolchain_dir: &Path) -> Result<(), Error> { #[cfg(windows)] if cfg!(windows) { set_environment_variable("PATH", &env::var("PATH").unwrap())?; diff --git a/src/env/shell.rs b/src/env/shell.rs index 0842c4a7..35415f7d 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -92,11 +92,11 @@ pub(crate) trait UnixShell { fn update_rcs(&self) -> Vec; // Writes the relevant env file. - fn env_script(&self, toolchain_dir: &PathBuf) -> ShellScript { + fn env_script(&self, toolchain_dir: &Path) -> ShellScript { ShellScript { name: "env", content: include_str!("env.sh"), - toolchain_dir: toolchain_dir.clone(), + toolchain_dir: toolchain_dir.to_path_buf(), } } @@ -234,11 +234,11 @@ impl UnixShell for Fish { self.rcfiles() } - fn env_script(&self, toolchain_dir: &PathBuf) -> ShellScript { + fn env_script(&self, toolchain_dir: &Path) -> ShellScript { ShellScript { name: "env.fish", content: include_str!("env.fish"), - toolchain_dir: toolchain_dir.clone(), + toolchain_dir: toolchain_dir.to_path_buf(), } } diff --git a/src/env/unix.rs b/src/env/unix.rs index 91678d79..0b6797e3 100644 --- a/src/env/unix.rs +++ b/src/env/unix.rs @@ -2,10 +2,10 @@ use crate::env::shell; use crate::error::Error; use std::fs::OpenOptions; use std::io::Write; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; // // TODO: USED FOR UNINSTALING -pub fn do_remove_from_path(toolchain_dir: &PathBuf) -> Result<(), Error> { +pub fn do_remove_from_path(toolchain_dir: &Path) -> Result<(), Error> { for sh in shell::get_available_shells() { let source_bytes = format!( "{}\n", @@ -15,7 +15,7 @@ pub fn do_remove_from_path(toolchain_dir: &PathBuf) -> Result<(), Error> { // Check more files for cleanup than normally are updated. for rc in sh.rcfiles().iter().filter(|rc| rc.is_file()) { - let file = std::fs::read_to_string(&rc).map_err(|_| Error::ReadingFile { + let file = std::fs::read_to_string(rc).map_err(|_| Error::ReadingFile { name: "rcfile", path: PathBuf::from(&rc), })?; @@ -44,7 +44,7 @@ pub fn do_remove_from_path(toolchain_dir: &PathBuf) -> Result<(), Error> { Ok(()) } -pub(crate) fn do_add_to_path(toolchain_dir: &PathBuf) -> Result<(), Error> { +pub(crate) fn do_add_to_path(toolchain_dir: &Path) -> Result<(), Error> { for sh in shell::get_available_shells() { let source_cmd = sh.source_string(&toolchain_dir.display().to_string())?; let source_cmd_with_newline = format!("\n{}", &source_cmd); @@ -75,7 +75,7 @@ pub(crate) fn do_add_to_path(toolchain_dir: &PathBuf) -> Result<(), Error> { Ok(()) } -pub(crate) fn do_write_env_files(toolchain_dir: &PathBuf) -> Result<(), Error> { +pub(crate) fn do_write_env_files(toolchain_dir: &Path) -> Result<(), Error> { let mut written = vec![]; for sh in shell::get_available_shells() { From 9c3baee1aaf31dfed3ccc870cc9af4bd7e0d0075 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Fri, 20 Oct 2023 22:37:58 +0000 Subject: [PATCH 06/33] feat: Update env scripts --- src/env/env.fish | 21 ++++++++++++--------- src/env/env.sh | 36 ++++++++++++++++++++---------------- src/env/shell.rs | 26 ++++++++++---------------- src/env/unix.rs | 1 - 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/env/env.fish b/src/env/env.fish index c5ae5f04..cac52887 100644 --- a/src/env/env.fish +++ b/src/env/env.fish @@ -1,15 +1,18 @@ # espup shell setup -# TODO: WE NEED TO VERIFY THAT PLACEHOLDERS ARE REPLACED. -# EG: USING THE --NO-STD FLAG WONT INSTALL GCC, HENCE WE WILL WRITE THE PATH WITH THE PLACEHOLDER - -if not contains "{xtensa_gcc}" $PATH - # Prepending path in case a system-installed rustc needs to be overridden - set -x PATH "{xtensa_gcc}" $PATH +set XTENSA_GCC "{xtensa_gcc}" +if test -n "$XTENSA_GCC" + if not contains "{xtensa_gcc}" $PATH + # Prepending path in case a system-installed rustc needs to be overridden + set -x PATH "{xtensa_gcc}" $PATH + end end -if not contains "{riscv_gcc}" $PATH - # Prepending path in case a system-installed rustc needs to be overridden - set -x PATH "{riscv_gcc}" $PATH +set RISCV_GCC "{riscv_gcc}" +if test -n "$RISCV_GCC" + if not contains "{riscv_gcc}" $PATH + # Prepending path in case a system-installed rustc needs to be overridden + set -x PATH "{riscv_gcc}" $PATH + end end set -x LIBCLANG_PATH "{libclang}" diff --git a/src/env/env.sh b/src/env/env.sh index dfbc6dd7..1f7248ff 100644 --- a/src/env/env.sh +++ b/src/env/env.sh @@ -1,20 +1,24 @@ #!/bin/sh # espup shell setup # affix colons on either side of $PATH to simplify matching -case ":${PATH}:" in -*:"{xtensa_gcc}":*) ;; -*) - # Prepending path in case a system-installed rustc needs to be overridden - export PATH="{xtensa_gcc}:$PATH" - ;; -esac - -case ":${PATH}:" in -*:"{riscv_gcc}":*) ;; -*) - # Prepending path in case a system-installed rustc needs to be overridden - export PATH="{riscv_gcc}:$PATH" - ;; -esac - +XTENSA_GCC="{xtensa_gcc}" +if [[ -n "${XTENSA_GCC}" ]]; then + case ":${PATH}:" in + *:"{xtensa_gcc}":*) ;; + *) + # Prepending path in case a system-installed rustc needs to be overridden + export PATH="{xtensa_gcc}:$PATH" + ;; + esac +fi +RISCV_GCC="{riscv_gcc}" +if [[ -n "${RISCV_GCC}" ]]; then + case ":${PATH}:" in + *:"{riscv_gcc}":*) ;; + *) + # Prepending path in case a system-installed rustc needs to be overridden + export PATH="{riscv_gcc}:$PATH" + ;; + esac +fi export LIBCLANG_PATH="{libclang_path}" diff --git a/src/env/shell.rs b/src/env/shell.rs index 35415f7d..9db5a27f 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -44,22 +44,16 @@ impl ShellScript { pub(crate) fn write(&self) -> Result<()> { let env_file_path = self.toolchain_dir.join(self.name); let mut env_file: String = self.content.to_string(); - if std::env::var("XTENSA_GCC").is_ok() { - env_file = env_file.replace( - "{xtensa_gcc}", - std::env::var("XTENSA_GCC").unwrap().as_str(), - ); - } - if std::env::var("RISCV_GCC").is_ok() { - env_file = - env_file.replace("{riscv_gcc}", std::env::var("RISCV_GCC").unwrap().as_str()); - } - if std::env::var("LIBCLANG_PATH").is_ok() { - env_file = env_file.replace( - "{libclang_path}", - std::env::var("LIBCLANG_PATH").unwrap().as_str(), - ); - } + + let xtensa_gcc = std::env::var("XTENSA_GCC").unwrap_or_default(); + env_file = env_file.replace("{xtensa_gcc}", &xtensa_gcc); + + let riscv_gcc = std::env::var("RISCV_GCC").unwrap_or_default(); + env_file = env_file.replace("{riscv_gcc}", &riscv_gcc); + + let libclang_path = std::env::var("LIBCLANG_PATH").unwrap_or_default(); + env_file = env_file.replace("{libclang_path}", &libclang_path); + write_file(&env_file_path, &env_file)?; Ok(()) } diff --git a/src/env/unix.rs b/src/env/unix.rs index 0b6797e3..bccf3515 100644 --- a/src/env/unix.rs +++ b/src/env/unix.rs @@ -4,7 +4,6 @@ use std::fs::OpenOptions; use std::io::Write; use std::path::{Path, PathBuf}; -// // TODO: USED FOR UNINSTALING pub fn do_remove_from_path(toolchain_dir: &Path) -> Result<(), Error> { for sh in shell::get_available_shells() { let source_bytes = format!( From 94453175189ca8b2e5b42f5139cdf058ccd0b5e0 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Fri, 20 Oct 2023 23:03:33 +0000 Subject: [PATCH 07/33] feat: Use custom error --- src/env/shell.rs | 21 ++++++++++----------- src/env/unix.rs | 1 + src/error.rs | 4 ++++ 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/env/shell.rs b/src/env/shell.rs index 9db5a27f..81367580 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -25,10 +25,12 @@ //! 1) using a shell script that updates PATH if the path is not in PATH //! 2) sourcing this script (`. /path/to/script`) in any appropriate rc file +use crate::error::Error; use directories::BaseDirs; +use miette::Result; use std::env; use std::fs::OpenOptions; -use std::io::{Result, Write}; +use std::io::Write; use std::path::{Path, PathBuf}; pub(crate) type Shell = Box; @@ -41,7 +43,7 @@ pub(crate) struct ShellScript { } impl ShellScript { - pub(crate) fn write(&self) -> Result<()> { + pub(crate) fn write(&self) -> Result<(), Error> { let env_file_path = self.toolchain_dir.join(self.name); let mut env_file: String = self.content.to_string(); @@ -94,7 +96,7 @@ pub(crate) trait UnixShell { } } - fn source_string(&self, toolchain_dir: &str) -> Result { + fn source_string(&self, toolchain_dir: &str) -> Result { Ok(format!(r#". "{}/env""#, toolchain_dir)) } } @@ -130,7 +132,6 @@ impl UnixShell for Bash { .iter() .map(|rc| BaseDirs::new().unwrap().home_dir().join(rc)) .collect() - // TODO: Verify the output of this } fn update_rcs(&self) -> Vec { @@ -144,15 +145,14 @@ impl UnixShell for Bash { struct Zsh; impl Zsh { - fn zdotdir() -> Result { + fn zdotdir() -> Result { use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; if matches!(std::env::var("SHELL"), Ok(sh) if sh.contains("zsh")) { match std::env::var("ZDOTDIR") { Ok(dir) if !dir.is_empty() => Ok(PathBuf::from(dir)), - _ => panic!("ZDOTDIR not set"), - // TODO:IMPROVE + _ => Err(Error::Zdotdir), } } else { match std::process::Command::new("zsh") @@ -160,8 +160,7 @@ impl Zsh { .output() { Ok(io) if !io.stdout.is_empty() => Ok(PathBuf::from(OsStr::from_bytes(&io.stdout))), - _ => panic!("ZDOTDIR not set"), - // TODO:IMPROVE + _ => Err(Error::Zdotdir), } } } @@ -236,7 +235,7 @@ impl UnixShell for Fish { } } - fn source_string(&self, toolchain_dir: &str) -> Result { + fn source_string(&self, toolchain_dir: &str) -> Result { Ok(format!(r#". "{}/env.fish""#, toolchain_dir)) } } @@ -253,7 +252,7 @@ fn has_cmd(cmd: &str) -> bool { .any(|p| p.exists()) } -pub fn write_file(path: &Path, contents: &str) -> Result<()> { +pub fn write_file(path: &Path, contents: &str) -> Result<(), Error> { let mut file = OpenOptions::new() .write(true) .truncate(true) diff --git a/src/env/unix.rs b/src/env/unix.rs index bccf3515..356b8db1 100644 --- a/src/env/unix.rs +++ b/src/env/unix.rs @@ -1,5 +1,6 @@ use crate::env::shell; use crate::error::Error; +use miette::Result; use std::fs::OpenOptions; use std::io::Write; use std::path::{Path, PathBuf}; diff --git a/src/error.rs b/src/error.rs index e40cdb63..2a014fd4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -75,4 +75,8 @@ pub enum Error { #[diagnostic(code(espup::env::unix))] #[error("Failed to read {name} file: '{}'", .path.display())] ReadingFile { name: &'static str, path: PathBuf }, + + #[diagnostic(code(espup::env::shell))] + #[error("ZDOTDIR not set")] + Zdotdir, } From 5b0316de11011cf1725d1dd01cf1cef11654de7e Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Fri, 20 Oct 2023 23:06:31 +0000 Subject: [PATCH 08/33] style: Format includes --- src/env/shell.rs | 10 ++++++---- src/env/unix.rs | 11 ++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/env/shell.rs b/src/env/shell.rs index 81367580..993f1f23 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -28,10 +28,12 @@ use crate::error::Error; use directories::BaseDirs; use miette::Result; -use std::env; -use std::fs::OpenOptions; -use std::io::Write; -use std::path::{Path, PathBuf}; +use std::{ + env, + fs::OpenOptions, + io::Write, + path::{Path, PathBuf}, +}; pub(crate) type Shell = Box; diff --git a/src/env/unix.rs b/src/env/unix.rs index 356b8db1..c4f1b4ad 100644 --- a/src/env/unix.rs +++ b/src/env/unix.rs @@ -1,9 +1,10 @@ -use crate::env::shell; -use crate::error::Error; +use crate::{env::shell, error::Error}; use miette::Result; -use std::fs::OpenOptions; -use std::io::Write; -use std::path::{Path, PathBuf}; +use std::{ + fs::OpenOptions, + io::Write, + path::{Path, PathBuf}, +}; pub fn do_remove_from_path(toolchain_dir: &Path) -> Result<(), Error> { for sh in shell::get_available_shells() { From 804b154d6aca30550ab6122e9da17465ff4951cd Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sat, 21 Oct 2023 08:04:38 +0000 Subject: [PATCH 09/33] docs: Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a0ba731..0b742263 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Add automatically sourcing the environment files + ### Fixed ### Changed From e378fcfeba328c25090882106592ab2efe5acbe1 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sat, 21 Oct 2023 10:12:50 +0200 Subject: [PATCH 10/33] feat: Avoid code duplication --- src/env/mod.rs | 5 +++++ src/env/shell.rs | 14 +++++--------- src/toolchain/rust.rs | 38 ++++++++++++-------------------------- 3 files changed, 22 insertions(+), 35 deletions(-) diff --git a/src/env/mod.rs b/src/env/mod.rs index 660b42e7..94ee0a78 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -1,6 +1,7 @@ //! Environment variables set up and export file support. use crate::error::Error; +use directories::BaseDirs; use log::info; use std::{ fs::File, @@ -84,6 +85,10 @@ pub fn export_environment(export_file: &Path, toolchain_dir: &Path) -> Result<() Ok(()) } +pub fn get_home_dir() -> PathBuf { + BaseDirs::new().unwrap().home_dir().into() +} + // #[cfg(test)] // mod tests { // use crate::env::{create_export_file, get_export_file, DEFAULT_EXPORT_FILE}; diff --git a/src/env/shell.rs b/src/env/shell.rs index 993f1f23..01874117 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -25,8 +25,7 @@ //! 1) using a shell script that updates PATH if the path is not in PATH //! 2) sourcing this script (`. /path/to/script`) in any appropriate rc file -use crate::error::Error; -use directories::BaseDirs; +use crate::{env::get_home_dir, error::Error}; use miette::Result; use std::{ env, @@ -110,7 +109,7 @@ impl UnixShell for Posix { } fn rcfiles(&self) -> Vec { - vec![BaseDirs::new().unwrap().home_dir().join(".profile")] + vec![get_home_dir().join(".profile")] } fn update_rcs(&self) -> Vec { @@ -132,7 +131,7 @@ impl UnixShell for Bash { // .profile as part of POSIX and always does setup for POSIX shells. [".bash_profile", ".bash_login", ".bashrc"] .iter() - .map(|rc| BaseDirs::new().unwrap().home_dir().join(rc)) + .map(|rc| get_home_dir().join(rc)) .collect() } @@ -176,7 +175,7 @@ impl UnixShell for Zsh { } fn rcfiles(&self) -> Vec { - let home_dir: Option = Some(BaseDirs::new().unwrap().home_dir().into()); + let home_dir: Option = Some(get_home_dir()); [Zsh::zdotdir().ok(), home_dir] .iter() .filter_map(|dir| dir.as_ref().map(|p| p.join(".zshenv"))) @@ -217,10 +216,7 @@ impl UnixShell for Fish { path }); - let p1 = BaseDirs::new() - .unwrap() - .home_dir() - .join(".config/fish/conf.d/espup.fish"); + let p1 = get_home_dir().join(".config/fish/conf.d/espup.fish"); p0.into_iter().chain(Some(p1)).collect() } diff --git a/src/toolchain/rust.rs b/src/toolchain/rust.rs index c556cd61..68769dbd 100644 --- a/src/toolchain/rust.rs +++ b/src/toolchain/rust.rs @@ -1,6 +1,7 @@ //! Xtensa Rust Toolchain source and installation tools. use crate::{ + env::get_home_dir, error::Error, host_triple::HostTriple, toolchain::{ @@ -12,7 +13,6 @@ use crate::{ }, }; use async_trait::async_trait; -use directories::BaseDirs; use log::{debug, info, warn}; use miette::Result; use regex::Regex; @@ -390,26 +390,18 @@ fn get_artifact_extension(host_triple: &HostTriple) -> &str { /// Gets the default cargo home path. fn get_cargo_home() -> PathBuf { - PathBuf::from(env::var("CARGO_HOME").unwrap_or_else(|_e| { - format!( - "{}", - BaseDirs::new().unwrap().home_dir().join(".cargo").display() - ) - })) + PathBuf::from( + env::var("CARGO_HOME") + .unwrap_or_else(|_e| format!("{}", get_home_dir().join(".cargo").display())), + ) } /// Gets the default rustup home path. pub fn get_rustup_home() -> PathBuf { - PathBuf::from(env::var("RUSTUP_HOME").unwrap_or_else(|_e| { - format!( - "{}", - BaseDirs::new() - .unwrap() - .home_dir() - .join(".rustup") - .display() - ) - })) + PathBuf::from( + env::var("RUSTUP_HOME") + .unwrap_or_else(|_e| format!("{}", get_home_dir().join(".rustup").display())), + ) } /// Checks if rustup is installed. @@ -434,10 +426,10 @@ pub async fn check_rust_installation() -> Result<(), Error> { #[cfg(test)] mod tests { use crate::{ + env::get_home_dir, logging::initialize_logger, toolchain::rust::{get_cargo_home, get_rustup_home, XtensaRust}, }; - use directories::BaseDirs; #[test] fn test_xtensa_rust_parse_version() { @@ -460,10 +452,7 @@ mod tests { fn test_get_cargo_home() { // No CARGO_HOME set std::env::remove_var("CARGO_HOME"); - assert_eq!( - get_cargo_home(), - BaseDirs::new().unwrap().home_dir().join(".cargo") - ); + assert_eq!(get_cargo_home(), get_home_dir().join(".cargo")); // CARGO_HOME set let temp_dir = tempfile::TempDir::new().unwrap(); let cargo_home = temp_dir.path().to_path_buf(); @@ -475,10 +464,7 @@ mod tests { fn test_get_rustup_home() { // No RUSTUP_HOME set std::env::remove_var("RUSTUP_HOME"); - assert_eq!( - get_rustup_home(), - BaseDirs::new().unwrap().home_dir().join(".rustup") - ); + assert_eq!(get_rustup_home(), get_home_dir().join(".rustup")); // RUSTUP_HOME set let temp_dir = tempfile::TempDir::new().unwrap(); let rustup_home = temp_dir.path().to_path_buf(); From 257ef2b672ef4682523eb34597b937d767a5e73f Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sat, 21 Oct 2023 10:41:28 +0200 Subject: [PATCH 11/33] feat: Remove legacy export file --- src/env/mod.rs | 15 ++++++++++----- src/env/unix.rs | 19 +++++++++++++++++-- src/error.rs | 4 ++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/env/mod.rs b/src/env/mod.rs index 94ee0a78..aa782e4c 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -3,6 +3,7 @@ use crate::error::Error; use directories::BaseDirs; use log::info; +use miette::Result; use std::{ fs::File, io::Write, @@ -86,7 +87,7 @@ pub fn export_environment(export_file: &Path, toolchain_dir: &Path) -> Result<() } pub fn get_home_dir() -> PathBuf { - BaseDirs::new().unwrap().home_dir().into() + BaseDirs::new().unwrap().home_dir().to_path_buf() } // #[cfg(test)] @@ -101,22 +102,26 @@ pub fn get_home_dir() -> PathBuf { // // No arg provided // let home_dir = BaseDirs::new().unwrap().home_dir().to_path_buf(); // let export_file = home_dir.join(DEFAULT_EXPORT_FILE); -// assert!(matches!(get_export_file(None), Ok(export_file))); +// let toolchain_dir = home_dir.join(".rustup").join("toolchains").join("esp"); +// assert!(matches!( +// get_export_file(None, &toolchain_dir), +// Ok(export_file) +// )); // // Relative path // let current_dir = current_dir().unwrap(); // let export_file = current_dir.join("export.sh"); // assert!(matches!( -// get_export_file(Some(PathBuf::from("export.sh"))), +// get_export_file(Some(PathBuf::from("export.sh")), &toolchain_dir), // Ok(export_file) // )); // // Absolute path // let export_file = PathBuf::from("/home/user/export.sh"); // assert!(matches!( -// get_export_file(Some(PathBuf::from("/home/user/export.sh"))), +// get_export_file(Some(PathBuf::from("/home/user/export.sh")), &toolchain_dir), // Ok(export_file) // )); // // Path is a directory instead of a file -// assert!(get_export_file(Some(home_dir)).is_err()); +// assert!(get_export_file(Some(home_dir), &toolchain_dir).is_err()); // } // #[test] diff --git a/src/env/unix.rs b/src/env/unix.rs index c4f1b4ad..ff7d613e 100644 --- a/src/env/unix.rs +++ b/src/env/unix.rs @@ -1,11 +1,13 @@ -use crate::{env::shell, error::Error}; +use crate::{env::get_home_dir, env::shell, error::Error}; use miette::Result; use std::{ - fs::OpenOptions, + fs::{remove_file, OpenOptions}, io::Write, path::{Path, PathBuf}, }; +const LEGACY_EXPORT_FILE: &str = "export-esp.sh"; + pub fn do_remove_from_path(toolchain_dir: &Path) -> Result<(), Error> { for sh in shell::get_available_shells() { let source_bytes = format!( @@ -42,6 +44,8 @@ pub fn do_remove_from_path(toolchain_dir: &Path) -> Result<(), Error> { } } + remove_legacy_export_file()?; + Ok(()) } @@ -73,6 +77,8 @@ pub(crate) fn do_add_to_path(toolchain_dir: &Path) -> Result<(), Error> { } } + remove_legacy_export_file()?; + Ok(()) } @@ -90,3 +96,12 @@ pub(crate) fn do_write_env_files(toolchain_dir: &Path) -> Result<(), Error> { Ok(()) } + +fn remove_legacy_export_file() -> Result<(), Error> { + let legacy_file = get_home_dir().join(LEGACY_EXPORT_FILE); + if legacy_file.exists() { + remove_file(&legacy_file)?; + } + + Ok(()) +} diff --git a/src/error.rs b/src/error.rs index 2a014fd4..c9afb007 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,6 +21,10 @@ pub enum Error { "Invalid export file destination: '{0}'. Please, use an absolute or releative path (including the file and its extension)")] InvalidDestination(String), + #[diagnostic(code(espup::env::home_dir))] + #[error("Failed to query GitHub API")] + InvalidHome, + #[diagnostic(code(espup::toolchain::rust::invalid_version))] #[error( "Invalid toolchain version '{0}'. Verify that the format is correct: '...' or '..', and that the release exists in https://github.com/esp-rs/rust-build/releases")] From 50de174c21676e5105ab535e61f8b304bffbb3d3 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sat, 21 Oct 2023 12:02:32 +0200 Subject: [PATCH 12/33] feat: Add unix post install message --- src/env/mod.rs | 31 +++++++++++-------------------- src/env/unix.rs | 11 +++++++++++ src/toolchain/mod.rs | 4 ++-- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/env/mod.rs b/src/env/mod.rs index aa782e4c..0a40e020 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -42,18 +42,18 @@ pub fn get_export_file( } /// Creates the export file with the necessary environment variables. -pub fn create_export_file(export_file: &PathBuf, exports: &[String]) -> Result<(), Error> { - info!("Creating export file"); - let mut file = File::create(export_file)?; - for e in exports.iter() { - #[cfg(windows)] - let e = e.replace('/', r"\"); - file.write_all(e.as_bytes())?; - file.write_all(b"\n")?; - } +// pub fn create_export_file(export_file: &PathBuf, exports: &[String]) -> Result<(), Error> { +// info!("Creating export file"); +// let mut file = File::create(export_file)?; +// for e in exports.iter() { +// #[cfg(windows)] +// let e = e.replace('/', r"\"); +// file.write_all(e.as_bytes())?; +// file.write_all(b"\n")?; +// } - Ok(()) -} +// Ok(()) +// } /// Instructions to export the environment variables. pub fn export_environment(export_file: &Path, toolchain_dir: &Path) -> Result<(), Error> { @@ -70,18 +70,9 @@ pub fn export_environment(export_file: &Path, toolchain_dir: &Path) -> Result<() } #[cfg(unix)] if cfg!(unix) { - let source_command = format!(". {}", export_file.display()); - // Check if the GCC_RISCV environment variable is set unix::do_write_env_files(toolchain_dir)?; unix::do_add_to_path(toolchain_dir)?; - println!( - "\n\tTo get started, you need to set up some environment variables by running: '{}'", - source_command - ); - println!( - "\tThis step must be done every time you open a new terminal.\n\t See other methods for setting the environment in https://esp-rs.github.io/book/installation/riscv-and-xtensa.html#3-set-up-the-environment-variables", - ); } Ok(()) } diff --git a/src/env/unix.rs b/src/env/unix.rs index ff7d613e..77bb9b0a 100644 --- a/src/env/unix.rs +++ b/src/env/unix.rs @@ -79,6 +79,8 @@ pub(crate) fn do_add_to_path(toolchain_dir: &Path) -> Result<(), Error> { remove_legacy_export_file()?; + print_post_install_msg(&toolchain_dir.display().to_string()); + Ok(()) } @@ -105,3 +107,12 @@ fn remove_legacy_export_file() -> Result<(), Error> { Ok(()) } + +fn print_post_install_msg(toolchain_dir: &str) { + println!("\tTo get started you may need to restart your current shell."); + println!("\tTo configure your current shell, run:"); + println!( + "\t'. {}/env' or '. {}/env.fish' depending on your shell", + toolchain_dir, toolchain_dir + ); +} diff --git a/src/toolchain/mod.rs b/src/toolchain/mod.rs index 79e41e74..a320fc16 100644 --- a/src/toolchain/mod.rs +++ b/src/toolchain/mod.rs @@ -2,7 +2,7 @@ use crate::{ cli::InstallOpts, - env::{create_export_file, export_environment, get_export_file}, + env::{export_environment, get_export_file}, error::Error, host_triple::get_host_triple, targets::Target, @@ -246,7 +246,7 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> exports.extend(names); } - create_export_file(&export_file, &exports)?; + // create_export_file(&export_file, &exports)?; match install_mode { InstallMode::Install => info!("Installation successfully completed!"), InstallMode::Update => info!("Update successfully completed!"), From e1501d733526e0e93c368c994b93cee92f296624 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sat, 21 Oct 2023 12:57:21 +0200 Subject: [PATCH 13/33] feat: Add no-modify-env argumentr --- src/cli.rs | 3 +++ src/env/mod.rs | 34 ++++++++++++++++++++-------------- src/env/unix.rs | 11 ----------- src/toolchain/mod.rs | 8 ++++++-- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index a24730fe..6dc7b2ec 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -34,6 +34,9 @@ pub struct InstallOpts { /// Nightly Rust toolchain version. #[arg(short = 'n', long, default_value = "nightly")] pub nightly_version: String, + /// Don't configure environment variables + #[arg(short = 'o', long)] + pub no_modify_env: bool, /// Skips parsing Xtensa Rust version. #[arg(short = 'k', long)] pub skip_version_parse: bool, diff --git a/src/env/mod.rs b/src/env/mod.rs index 0a40e020..89d4c11c 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -2,13 +2,8 @@ use crate::error::Error; use directories::BaseDirs; -use log::info; use miette::Result; -use std::{ - fs::File, - io::Write, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; pub mod shell; #[cfg(unix)] @@ -56,17 +51,10 @@ pub fn get_export_file( // } /// Instructions to export the environment variables. -pub fn export_environment(export_file: &Path, toolchain_dir: &Path) -> Result<(), Error> { +pub fn set_environment(toolchain_dir: &Path) -> Result<(), Error> { #[cfg(windows)] if cfg!(windows) { set_environment_variable("PATH", &env::var("PATH").unwrap())?; - warn!( - "Your environments variables have been updated! Shell may need to be restarted for changes to be effective" - ); - warn!( - "A file was created at '{}' showing the injected environment variables", - export_file.display() - ); } #[cfg(unix)] if cfg!(unix) { @@ -81,6 +69,24 @@ pub fn get_home_dir() -> PathBuf { BaseDirs::new().unwrap().home_dir().to_path_buf() } +pub fn print_post_install_msg(toolchain_dir: &str, no_modify_env: bool) { + if no_modify_env { + println!( + "\tTo get started you need to configure some environment variable. This has not been done automatically." + ); + } else { + println!("\tTo get started you may need to restart your current shell."); + } + println!("\tTo configure your current shell, run:"); + #[cfg(unix)] + println!( + "\t'. {}/env' or '. {}/env.fish' depending on your shell", + toolchain_dir, toolchain_dir + ); + #[cfg(windows)] + println!("\t'. {}\\export-esp.ps1'", toolchain_dir); +} + // #[cfg(test)] // mod tests { // use crate::env::{create_export_file, get_export_file, DEFAULT_EXPORT_FILE}; diff --git a/src/env/unix.rs b/src/env/unix.rs index 77bb9b0a..ff7d613e 100644 --- a/src/env/unix.rs +++ b/src/env/unix.rs @@ -79,8 +79,6 @@ pub(crate) fn do_add_to_path(toolchain_dir: &Path) -> Result<(), Error> { remove_legacy_export_file()?; - print_post_install_msg(&toolchain_dir.display().to_string()); - Ok(()) } @@ -107,12 +105,3 @@ fn remove_legacy_export_file() -> Result<(), Error> { Ok(()) } - -fn print_post_install_msg(toolchain_dir: &str) { - println!("\tTo get started you may need to restart your current shell."); - println!("\tTo configure your current shell, run:"); - println!( - "\t'. {}/env' or '. {}/env.fish' depending on your shell", - toolchain_dir, toolchain_dir - ); -} diff --git a/src/toolchain/mod.rs b/src/toolchain/mod.rs index a320fc16..60cf3abc 100644 --- a/src/toolchain/mod.rs +++ b/src/toolchain/mod.rs @@ -2,7 +2,7 @@ use crate::{ cli::InstallOpts, - env::{export_environment, get_export_file}, + env::{get_export_file, print_post_install_msg, set_environment}, error::Error, host_triple::get_host_triple, targets::Target, @@ -251,7 +251,11 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> InstallMode::Install => info!("Installation successfully completed!"), InstallMode::Update => info!("Update successfully completed!"), } - export_environment(&export_file, &toolchain_dir)?; + if !args.no_modify_env { + set_environment(&toolchain_dir)?; + } + print_post_install_msg(&toolchain_dir.display().to_string(), args.no_modify_env); + Ok(()) } From 7a03981e418991a50f9a2161c101cd2c7d0001b3 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sat, 21 Oct 2023 15:27:06 +0200 Subject: [PATCH 14/33] feat: Improve WindowsShells --- src/env/env.bat | 32 +++++++++++++++++++++++++++ src/env/env.fish | 7 +++--- src/env/env.ps1 | 28 ++++++++++++++++++++++++ src/env/env.sh | 5 +++-- src/env/mod.rs | 3 +++ src/env/shell.rs | 50 +++++++++++++++++++++++++++++++++++++++++++ src/env/windows.rs | 48 +++++++++++++++++++++++++++++++++++++++++ src/toolchain/gcc.rs | 23 +++++++++++--------- src/toolchain/llvm.rs | 44 ++++++++++--------------------------- 9 files changed, 192 insertions(+), 48 deletions(-) create mode 100644 src/env/env.bat create mode 100644 src/env/env.ps1 diff --git a/src/env/env.bat b/src/env/env.bat new file mode 100644 index 00000000..e8e76762 --- /dev/null +++ b/src/env/env.bat @@ -0,0 +1,32 @@ +@echo off +rem espup CMD setup + +set XTENSA_GCC={xtensa_gcc} +if not "%XTENSA_GCC%" == "" ( + echo %PATH% | findstr /C:"%XTENSA_GCC%" 1>nul + if errorlevel 1 ( + rem Prepending path + set PATH=%XTENSA_GCC%;%PATH% + ) +) + +set RISCV_GCC={riscv_gcc} +if not "%RISCV_GCC%" == "" ( + echo %PATH% | findstr /C:"%RISCV_GCC%" 1>nul + if errorlevel 1 ( + rem Prepending path + set PATH=%RISCV_GCC%;%PATH% + ) +) + +set LIBCLANG_PATH={libclang_path} +set LIBCLANG_BIN_PATH={libclang_bin_path} +if not "%LIBCLANG_BIN_PATH%" == "" ( + echo %PATH% | findstr /C:"%LIBCLANG_BIN_PATH%" 1>nul + if errorlevel 1 ( + rem Prepending path + set PATH=%LIBCLANG_BIN_PATH%;%PATH% + ) +) + +set CLANG_PATH={clang_path} diff --git a/src/env/env.fish b/src/env/env.fish index cac52887..236c7d3a 100644 --- a/src/env/env.fish +++ b/src/env/env.fish @@ -2,7 +2,7 @@ set XTENSA_GCC "{xtensa_gcc}" if test -n "$XTENSA_GCC" if not contains "{xtensa_gcc}" $PATH - # Prepending path in case a system-installed rustc needs to be overridden + # Prepending path set -x PATH "{xtensa_gcc}" $PATH end end @@ -10,9 +10,10 @@ end set RISCV_GCC "{riscv_gcc}" if test -n "$RISCV_GCC" if not contains "{riscv_gcc}" $PATH - # Prepending path in case a system-installed rustc needs to be overridden + # Prepending path set -x PATH "{riscv_gcc}" $PATH end end -set -x LIBCLANG_PATH "{libclang}" +set -x LIBCLANG_PATH "{libclang_path}" +set -x CLANG_PATH "{clang_path}" diff --git a/src/env/env.ps1 b/src/env/env.ps1 new file mode 100644 index 00000000..0d54e16c --- /dev/null +++ b/src/env/env.ps1 @@ -0,0 +1,28 @@ +# espup PowerShell setup +# affix semicolons on either side of $Env:PATH to simplify matching +$XTENSA_GCC = "{xtensa_gcc}" +if ($XTENSA_GCC -ne "") { + if (-not ($Env:PATH -like "*;$XTENSA_GCC;*")) { + # Prepending path + $Env:PATH = "$XTENSA_GCC;$Env:PATH" + } +} + +$RISCV_GCC = "{riscv_gcc}" +if ($RISCV_GCC -ne "") { + if (-not ($Env:PATH -like "*;$RISCV_GCC;*")) { + # Prepending path + $Env:PATH = "$RISCV_GCC;$Env:PATH" + } +} + +$Env:LIBCLANG_PATH = "{libclang_path}" +$LIBCLANG_BIN_PATH = "{libclang_bin_path}" +if ($LIBCLANG_BIN_PATH -ne "") { + if (-not ($Env:PATH -like "*;$LIBCLANG_BIN_PATH;*")) { + # Prepending path + $Env:PATH = "$LIBCLANG_BIN_PATH;$Env:PATH" + } +} + +$Env:CLANG_PATH = "{clang_path}" diff --git a/src/env/env.sh b/src/env/env.sh index 1f7248ff..a13b984d 100644 --- a/src/env/env.sh +++ b/src/env/env.sh @@ -6,7 +6,7 @@ if [[ -n "${XTENSA_GCC}" ]]; then case ":${PATH}:" in *:"{xtensa_gcc}":*) ;; *) - # Prepending path in case a system-installed rustc needs to be overridden + # Prepending path export PATH="{xtensa_gcc}:$PATH" ;; esac @@ -16,9 +16,10 @@ if [[ -n "${RISCV_GCC}" ]]; then case ":${PATH}:" in *:"{riscv_gcc}":*) ;; *) - # Prepending path in case a system-installed rustc needs to be overridden + # Prepending path export PATH="{riscv_gcc}:$PATH" ;; esac fi export LIBCLANG_PATH="{libclang_path}" +export CLANG_PATH="{clang_path}" diff --git a/src/env/mod.rs b/src/env/mod.rs index 89d4c11c..f5f6cc69 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -52,9 +52,12 @@ pub fn get_export_file( /// Instructions to export the environment variables. pub fn set_environment(toolchain_dir: &Path) -> Result<(), Error> { + // TODO: UNIFY do_write_env_files and do_add_to_path and then have a common function that does the checking of OS + // TODO: RENAME do_write_env_files and do_add_to_path methods #[cfg(windows)] if cfg!(windows) { set_environment_variable("PATH", &env::var("PATH").unwrap())?; + windows::do_write_env_files(toolchain_dir)?; } #[cfg(unix)] if cfg!(unix) { diff --git a/src/env/shell.rs b/src/env/shell.rs index 01874117..ef25af35 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -45,6 +45,8 @@ pub(crate) struct ShellScript { impl ShellScript { pub(crate) fn write(&self) -> Result<(), Error> { + // TODO: SOMETHING SIMILAR FOR WINDOWS + // TODO: IN WINDOWS, REPLACE SLASHS FOR BACKSLASH let env_file_path = self.toolchain_dir.join(self.name); let mut env_file: String = self.content.to_string(); @@ -56,6 +58,14 @@ impl ShellScript { let libclang_path = std::env::var("LIBCLANG_PATH").unwrap_or_default(); env_file = env_file.replace("{libclang_path}", &libclang_path); + #[cfg(windows)] + if cfg!(windows) { + let libclang_bin_path = std::env::var("LIBCLANG_BIN_PATH").unwrap_or_default(); + env_file = env_file.replace("{libclang_bin_path}", &libclang_bin_path); + } + + let clang_path = std::env::var("CLANG_PATH").unwrap_or_default(); + env_file = env_file.replace("{clang_path}", &clang_path); write_file(&env_file_path, &env_file)?; Ok(()) @@ -76,6 +86,45 @@ pub(crate) fn get_available_shells() -> impl Iterator { enumerate_shells().into_iter().filter(|sh| sh.does_exist()) } +pub(crate) trait WindowsShell { + // Writes the relevant env file. + fn env_script(&self, toolchain_dir: &Path) -> ShellScript; + + // Gives the source string for a given shell. + fn source_string(&self, toolchain_dir: &str) -> Result; +} + +struct Batch; +impl WindowsShell for Batch { + fn env_script(&self, toolchain_dir: &Path) -> ShellScript { + ShellScript { + name: "env.bat", + content: include_str!("env.bat"), + toolchain_dir: toolchain_dir.to_path_buf(), + } + } + + fn source_string(&self, toolchain_dir: &str) -> Result { + Ok(format!(r#"{}/env.bat""#, toolchain_dir)) + // TODO: VERIFY THE SOURCE COMMAND + } +} + +struct Powershell; +impl WindowsShell for Powershell { + fn env_script(&self, toolchain_dir: &Path) -> ShellScript { + ShellScript { + name: "env.ps1", + content: include_str!("env.ps1"), + toolchain_dir: toolchain_dir.to_path_buf(), + } + } + + fn source_string(&self, toolchain_dir: &str) -> Result { + Ok(format!(r#". "{}/env.ps1""#, toolchain_dir)) + } +} + pub(crate) trait UnixShell { // Detects if a shell "exists". Users have multiple shells, so an "eager" // heuristic should be used, assuming shells exist if any traces do. @@ -97,6 +146,7 @@ pub(crate) trait UnixShell { } } + // Gives the source string for a given shell. fn source_string(&self, toolchain_dir: &str) -> Result { Ok(format!(r#". "{}/env""#, toolchain_dir)) } diff --git a/src/env/windows.rs b/src/env/windows.rs index 188ce7fd..c12f3af6 100644 --- a/src/env/windows.rs +++ b/src/env/windows.rs @@ -5,6 +5,9 @@ use winreg::{ RegKey, }; +const LEGACY_EXPORT_FILE: &str = "export-esp.ps1"; + +// TODO: REVIEW CFGS #[cfg(windows)] /// Sets an environment variable for the current user. pub fn set_environment_variable(key: &str, value: &str) -> Result<(), Error> { @@ -30,3 +33,48 @@ pub fn delete_environment_variable(key: &str) -> Result<(), Error> { environment_key.delete_value(key)?; Ok(()) } + +pub(crate) fn do_write_env_files(toolchain_dir: &Path) -> Result<(), Error> { + let windows_shells: Vec = vec![Box::new(Batch), Box::new(Powershell)]; + for sh in windows_shells.into_iter() { + let script = sh.env_script(toolchain_dir); + script.write()?; + } + + Ok(()) +} + +pub(crate) fn do_add_to_path(toolchain_dir: &Path) -> Result<(), Error> { + let path = std::env::var_os("PATH").unwrap_or_default(); + set_environment_variable("PATH", path)?; + + let xtensa_gcc = std::env::var_os("XTENSA_GCC").unwrap_or_default(); + set_environment_variable("XTENSA_GCC", xtensa_gcc)?; + + let riscv_gcc = std::env::var_os("RISCV_GCC").unwrap_or_default(); + set_environment_variable("RISCV_GCC", riscv_gcc)?; + + let libclang_path = std::env::var_os("LIBCLANG_PATH"); + if let Some(libclang_path) = libclang_path { + set_environment_variable("LIBCLANG_PATH", libclang_path)?; + } + + let clang_path = std::env::var_os("CLANG_PATH"); + if let Some(libclang_path) = clang_path { + set_environment_variable("CLANG_PATH", clang_path)?; + } + + remove_legacy_export_file()?; + + Ok(()) +} + +fn remove_legacy_export_file() -> Result<(), Error> { + let legacy_file = get_home_dir().join(LEGACY_EXPORT_FILE); + if legacy_file.exists() { + remove_file(&legacy_file)?; + } + + Ok(()) +} +// TODO: REMOVE LEGACY FILE diff --git a/src/toolchain/gcc.rs b/src/toolchain/gcc.rs index ddca6bd2..e1d0f58f 100644 --- a/src/toolchain/gcc.rs +++ b/src/toolchain/gcc.rs @@ -79,22 +79,24 @@ impl Installable for Gcc { ) .await?; } - let mut exports: Vec = Vec::new(); + let exports: Vec = Vec::new(); #[cfg(windows)] if cfg!(windows) { - exports.push(format!( - "$Env:PATH = \"{};\" + $Env:PATH", - &self.get_bin_path() - )); - std::env::set_var( - "PATH", - self.get_bin_path().replace('/', "\\") + ";" + &std::env::var("PATH").unwrap(), - ); + // exports.push(format!( + // "$Env:PATH = \"{};\" + $Env:PATH", + // &self.get_bin_path() + // )); + let windows_path = self.get_bin_path().replace('/', "\\"); + if self.arch == RISCV_GCC { + std::env::set_var("RISCV_GCC", self.get_bin_path()); + } else { + std::env::set_var("XTENSA_GCC", self.get_bin_path()); + } } #[cfg(unix)] if cfg!(unix) { - exports.push(format!("export PATH=\"{}:$PATH\"", &self.get_bin_path())); + // exports.push(format!("export PATH=\"{}:$PATH\"", &self.get_bin_path())); if self.arch == RISCV_GCC { std::env::set_var("RISCV_GCC", self.get_bin_path()); } else { @@ -131,6 +133,7 @@ fn get_artifact_extension(host_triple: &HostTriple) -> &str { } /// Checks if the toolchain is pressent, if present uninstalls it. +// TODO: REVIEW UNINSTALL METHODS pub fn uninstall_gcc_toolchains(toolchain_path: &Path) -> Result<(), Error> { info!("Uninstalling GCC"); diff --git a/src/toolchain/llvm.rs b/src/toolchain/llvm.rs index dbf513ee..9567067a 100644 --- a/src/toolchain/llvm.rs +++ b/src/toolchain/llvm.rs @@ -53,7 +53,10 @@ impl Llvm { /// Gets the binary path. fn get_lib_path(&self) -> String { #[cfg(windows)] - let llvm_path = format!("{}/esp-clang/bin", self.path.to_str().unwrap()); + let llvm_path = format!( + "{}/esp-clang/bin", + self.path.to_str().unwrap().replace('/', "\\") + ); #[cfg(unix)] let llvm_path = format!("{}/esp-clang/lib", self.path.to_str().unwrap()); llvm_path @@ -62,7 +65,8 @@ impl Llvm { /// Gets the binary path of clang fn get_bin_path(&self) -> String { #[cfg(windows)] - let llvm_path = format!("{}/esp-clang/bin/clang.exe", self.path.to_str().unwrap()); + let llvm_path = + format!("{}/esp-clang/bin/clang.exe", self.path.to_str().unwrap()).replace('/', "\\"); #[cfg(unix)] let llvm_path = format!("{}/esp-clang/bin/clang", self.path.to_str().unwrap()); llvm_path @@ -165,39 +169,13 @@ impl Installable for Llvm { .await?; } // Set environment variables. + // TODO: REMOVE EXPORTS #[cfg(windows)] - if cfg!(windows) { - exports.push(format!( - "$Env:LIBCLANG_PATH = \"{}/libclang.dll\"", - self.get_lib_path() - )); - exports.push(format!( - "$Env:PATH = \"{};\" + $Env:PATH", - self.get_lib_path() - )); - set_environment_variable( - "LIBCLANG_PATH", - &format!("{}\\libclang.dll", self.get_lib_path().replace('/', "\\")), - )?; - - std::env::set_var( - "PATH", - self.get_lib_path().replace('/', "\\") + ";" + &std::env::var("PATH").unwrap(), - ); - } - #[cfg(unix)] - if cfg!(unix) { - exports.push(format!("export LIBCLANG_PATH=\"{}\"", self.get_lib_path())); - std::env::set_var("LIBCLANG_PATH", self.get_lib_path()); - } + std::env::set_var("LIBCLANG_BIN_PATH", self.get_lib_path()); + std::env::set_var("LIBCLANG_PATH", self.get_lib_path()); + // } if self.extended { - #[cfg(windows)] - if cfg!(windows) { - exports.push(format!("$Env:CLANG_PATH = \"{}\"", self.get_bin_path())); - set_environment_variable("CLANG_PATH", &self.get_bin_path().replace('/', "\\"))?; - } - #[cfg(unix)] - exports.push(format!("export CLANG_PATH=\"{}\"", self.get_bin_path())); + std::env::set_var("CLANG_PATH", self.get_bin_path()); } Ok(exports) From ea68ad457ebdbcb2fa17b40d197c2d77b07e2a9f Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sat, 21 Oct 2023 15:35:42 +0200 Subject: [PATCH 15/33] feat: Remove export files --- src/cli.rs | 5 +- src/env/mod.rs | 105 ++---------------------------------------- src/toolchain/gcc.rs | 10 +--- src/toolchain/llvm.rs | 7 +-- src/toolchain/mod.rs | 16 ++----- src/toolchain/rust.rs | 10 ++-- 6 files changed, 20 insertions(+), 133 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 6dc7b2ec..9d9f6fc4 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,7 +1,7 @@ use crate::targets::{parse_targets, Target}; use clap::Parser; use clap_complete::Shell; -use std::{collections::HashSet, path::PathBuf}; +use std::collections::HashSet; #[derive(Debug, Parser)] pub struct CompletionsOpts { @@ -17,9 +17,6 @@ pub struct InstallOpts { /// Target triple of the host. #[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, - /// 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, /// Extends the LLVM installation. /// /// This will install the whole LLVM instead of only installing the libs. diff --git a/src/env/mod.rs b/src/env/mod.rs index f5f6cc69..6e86f5ab 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -1,4 +1,4 @@ -//! Environment variables set up and export file support. +//! Environment variables set up and environment file support. use crate::error::Error; use directories::BaseDirs; @@ -11,45 +11,6 @@ pub mod unix; #[cfg(windows)] pub mod windows; -#[cfg(windows)] -const DEFAULT_EXPORT_FILE: &str = "export-esp.ps1"; -#[cfg(unix)] -const DEFAULT_EXPORT_FILE: &str = "export-esp.sh"; - -/// Returns the absolute path to the export file, uses the DEFAULT_EXPORT_FILE if no arg is provided. -pub fn get_export_file( - export_file: Option, - toolchain_dir: &Path, -) -> Result { - if let Some(export_file) = export_file { - if export_file.is_dir() { - return Err(Error::InvalidDestination(export_file.display().to_string())); - } - if export_file.is_absolute() { - Ok(export_file) - } else { - let current_dir = std::env::current_dir()?; - Ok(current_dir.join(export_file)) - } - } else { - Ok(toolchain_dir.join(DEFAULT_EXPORT_FILE)) - } -} - -/// Creates the export file with the necessary environment variables. -// pub fn create_export_file(export_file: &PathBuf, exports: &[String]) -> Result<(), Error> { -// info!("Creating export file"); -// let mut file = File::create(export_file)?; -// for e in exports.iter() { -// #[cfg(windows)] -// let e = e.replace('/', r"\"); -// file.write_all(e.as_bytes())?; -// file.write_all(b"\n")?; -// } - -// Ok(()) -// } - /// Instructions to export the environment variables. pub fn set_environment(toolchain_dir: &Path) -> Result<(), Error> { // TODO: UNIFY do_write_env_files and do_add_to_path and then have a common function that does the checking of OS @@ -87,64 +48,8 @@ pub fn print_post_install_msg(toolchain_dir: &str, no_modify_env: bool) { toolchain_dir, toolchain_dir ); #[cfg(windows)] - println!("\t'. {}\\export-esp.ps1'", toolchain_dir); + println!( + "\t'. {}\\env.ps1' or '{}\\env.bat dependeing on your shell'", + toolchain_dir + ); } - -// #[cfg(test)] -// mod tests { -// use crate::env::{create_export_file, get_export_file, DEFAULT_EXPORT_FILE}; -// use directories::BaseDirs; -// use std::{env::current_dir, path::PathBuf}; - -// #[test] -// #[allow(unused_variables)] -// fn test_get_export_file() { -// // No arg provided -// let home_dir = BaseDirs::new().unwrap().home_dir().to_path_buf(); -// let export_file = home_dir.join(DEFAULT_EXPORT_FILE); -// let toolchain_dir = home_dir.join(".rustup").join("toolchains").join("esp"); -// assert!(matches!( -// get_export_file(None, &toolchain_dir), -// Ok(export_file) -// )); -// // Relative path -// let current_dir = current_dir().unwrap(); -// let export_file = current_dir.join("export.sh"); -// assert!(matches!( -// get_export_file(Some(PathBuf::from("export.sh")), &toolchain_dir), -// Ok(export_file) -// )); -// // Absolute path -// let export_file = PathBuf::from("/home/user/export.sh"); -// assert!(matches!( -// get_export_file(Some(PathBuf::from("/home/user/export.sh")), &toolchain_dir), -// Ok(export_file) -// )); -// // Path is a directory instead of a file -// assert!(get_export_file(Some(home_dir), &toolchain_dir).is_err()); -// } - -// #[test] -// fn test_create_export_file() { -// // Creates the export file and writes the correct content to it -// let temp_dir = tempfile::TempDir::new().unwrap(); -// let export_file = temp_dir.path().join("export.sh"); -// let exports = vec![ -// "export VAR1=value1".to_string(), -// "export VAR2=value2".to_string(), -// ]; -// create_export_file(&export_file, &exports).unwrap(); -// let contents = std::fs::read_to_string(export_file).unwrap(); -// assert_eq!(contents, "export VAR1=value1\nexport VAR2=value2\n"); - -// // Returns the correct error when it fails to create the export file (it already exists) -// let temp_dir = tempfile::TempDir::new().unwrap(); -// let export_file = temp_dir.path().join("export.sh"); -// std::fs::create_dir_all(&export_file).unwrap(); -// let exports = vec![ -// "export VAR1=value1".to_string(), -// "export VAR2=value2".to_string(), -// ]; -// assert!(create_export_file(&export_file, &exports).is_err()); -// } -// } diff --git a/src/toolchain/gcc.rs b/src/toolchain/gcc.rs index e1d0f58f..600a156b 100644 --- a/src/toolchain/gcc.rs +++ b/src/toolchain/gcc.rs @@ -50,7 +50,7 @@ impl Gcc { #[async_trait] impl Installable for Gcc { - async fn install(&self) -> Result, Error> { + async fn install(&self) -> Result<(), Error> { let extension = get_artifact_extension(&self.host_triple); debug!("GCC path: {}", self.path.display()); if self.path.exists() { @@ -79,14 +79,9 @@ impl Installable for Gcc { ) .await?; } - let exports: Vec = Vec::new(); #[cfg(windows)] if cfg!(windows) { - // exports.push(format!( - // "$Env:PATH = \"{};\" + $Env:PATH", - // &self.get_bin_path() - // )); let windows_path = self.get_bin_path().replace('/', "\\"); if self.arch == RISCV_GCC { std::env::set_var("RISCV_GCC", self.get_bin_path()); @@ -96,14 +91,13 @@ impl Installable for Gcc { } #[cfg(unix)] if cfg!(unix) { - // exports.push(format!("export PATH=\"{}:$PATH\"", &self.get_bin_path())); if self.arch == RISCV_GCC { std::env::set_var("RISCV_GCC", self.get_bin_path()); } else { std::env::set_var("XTENSA_GCC", self.get_bin_path()); } } - Ok(exports) + Ok(()) } fn name(&self) -> String { diff --git a/src/toolchain/llvm.rs b/src/toolchain/llvm.rs index 9567067a..ad749002 100644 --- a/src/toolchain/llvm.rs +++ b/src/toolchain/llvm.rs @@ -149,9 +149,7 @@ impl Llvm { #[async_trait] impl Installable for Llvm { - async fn install(&self) -> Result, Error> { - let mut exports: Vec = Vec::new(); - + async fn install(&self) -> Result<(), Error> { if Path::new(&self.path).exists() { warn!( "Previous installation of LLVM exists in: '{}'. Reusing this installation", @@ -169,7 +167,6 @@ impl Installable for Llvm { .await?; } // Set environment variables. - // TODO: REMOVE EXPORTS #[cfg(windows)] std::env::set_var("LIBCLANG_BIN_PATH", self.get_lib_path()); std::env::set_var("LIBCLANG_PATH", self.get_lib_path()); @@ -178,7 +175,7 @@ impl Installable for Llvm { std::env::set_var("CLANG_PATH", self.get_bin_path()); } - Ok(exports) + Ok(()) } fn name(&self) -> String { diff --git a/src/toolchain/mod.rs b/src/toolchain/mod.rs index 60cf3abc..72c21f11 100644 --- a/src/toolchain/mod.rs +++ b/src/toolchain/mod.rs @@ -2,7 +2,7 @@ use crate::{ cli::InstallOpts, - env::{get_export_file, print_post_install_msg, set_environment}, + env::{print_post_install_msg, set_environment}, error::Error, host_triple::get_host_triple, targets::Target, @@ -41,8 +41,8 @@ pub enum InstallMode { #[async_trait] pub trait Installable { - /// Install some application, returning a vector of any required exports - async fn install(&self) -> Result, Error>; + /// Install some application + async fn install(&self) -> Result<(), Error>; /// Returns the name of the toolchain being installeds fn name(&self) -> String; } @@ -133,7 +133,6 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> InstallMode::Install => info!("Installing the Espressif Rust ecosystem"), InstallMode::Update => info!("Updating the Espressif Rust ecosystem"), } - let mut exports: Vec = Vec::new(); let host_triple = get_host_triple(args.default_host)?; let xtensa_rust_version = if let Some(toolchain_version) = &args.toolchain_version { if !args.skip_version_parse { @@ -145,7 +144,6 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> XtensaRust::get_latest_version().await? }; let toolchain_dir = get_rustup_home().join("toolchains").join(args.name); - let export_file = get_export_file(args.export_file, &toolchain_dir)?; let llvm: Llvm = Llvm::new( &toolchain_dir, &host_triple, @@ -168,7 +166,6 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> debug!( "Arguments: - - Export file: {:?} - Host triple: {} - LLVM Toolchain: {:?} - Nightly version: {:?} @@ -177,7 +174,6 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> - Targets: {:?} - Toolchain path: {:?} - Toolchain version: {:?}", - &export_file, host_triple, &llvm, &args.nightly_version, @@ -223,7 +219,7 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> // With a list of applications to install, install them all in parallel. let installable_items = to_install.len(); - let (tx, mut rx) = mpsc::channel::, Error>>(installable_items); + let (tx, mut rx) = mpsc::channel::>(installable_items); for app in to_install { let tx = tx.clone(); let retry_strategy = FixedInterval::from_millis(50).take(3); @@ -242,11 +238,9 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> // Read the results of the install tasks as they complete. for _ in 0..installable_items { - let names = rx.recv().await.unwrap()?; - exports.extend(names); + rx.recv().await.unwrap()?; } - // create_export_file(&export_file, &exports)?; match install_mode { InstallMode::Install => info!("Installation successfully completed!"), InstallMode::Update => info!("Update successfully completed!"), diff --git a/src/toolchain/rust.rs b/src/toolchain/rust.rs index 68769dbd..7f1ce922 100644 --- a/src/toolchain/rust.rs +++ b/src/toolchain/rust.rs @@ -182,7 +182,7 @@ impl XtensaRust { #[async_trait] impl Installable for XtensaRust { - async fn install(&self) -> Result, Error> { + async fn install(&self) -> Result<(), Error> { if self.toolchain_destination.exists() { let toolchain_name = format!( "+{}", @@ -203,7 +203,7 @@ impl Installable for XtensaRust { &self.version, &self.toolchain_destination.display() ); - return Ok(vec![]); + return Ok(()); } else { if !rustc_version.status.success() { warn!("Failed to detect version of Xtensa Rust, reinstalling it"); @@ -299,7 +299,7 @@ impl Installable for XtensaRust { .await?; } - Ok(vec![]) // No exports + Ok(()) } fn name(&self) -> String { @@ -346,7 +346,7 @@ impl RiscVTarget { #[async_trait] impl Installable for RiscVTarget { - async fn install(&self) -> Result, Error> { + async fn install(&self) -> Result<(), Error> { info!( "Installing RISC-V Rust targets ('riscv32imc-unknown-none-elf' and 'riscv32imac-unknown-none-elf') for '{}' toolchain", &self.nightly_version ); @@ -372,7 +372,7 @@ impl Installable for RiscVTarget { return Err(Error::InstallRiscvTarget(self.nightly_version.clone())); } - Ok(vec![]) // No exports + Ok(()) } fn name(&self) -> String { From 8dfdb7495567e5f603bcb2f06e51bad6b6dfc0cc Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sat, 21 Oct 2023 15:42:38 +0200 Subject: [PATCH 16/33] refactor: Improve methods name --- src/env/mod.rs | 11 ++++------- src/env/shell.rs | 2 -- src/env/unix.rs | 4 ++-- src/env/windows.rs | 5 ++--- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/env/mod.rs b/src/env/mod.rs index 6e86f5ab..30017dad 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -13,18 +13,15 @@ pub mod windows; /// Instructions to export the environment variables. pub fn set_environment(toolchain_dir: &Path) -> Result<(), Error> { - // TODO: UNIFY do_write_env_files and do_add_to_path and then have a common function that does the checking of OS - // TODO: RENAME do_write_env_files and do_add_to_path methods #[cfg(windows)] if cfg!(windows) { - set_environment_variable("PATH", &env::var("PATH").unwrap())?; - windows::do_write_env_files(toolchain_dir)?; + windows::write_env_files(toolchain_dir)?; + windows::update_env(toolchain_dir)?; } #[cfg(unix)] if cfg!(unix) { - // Check if the GCC_RISCV environment variable is set - unix::do_write_env_files(toolchain_dir)?; - unix::do_add_to_path(toolchain_dir)?; + unix::write_env_files(toolchain_dir)?; + unix::update_env(toolchain_dir)?; } Ok(()) } diff --git a/src/env/shell.rs b/src/env/shell.rs index ef25af35..2e83678d 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -45,8 +45,6 @@ pub(crate) struct ShellScript { impl ShellScript { pub(crate) fn write(&self) -> Result<(), Error> { - // TODO: SOMETHING SIMILAR FOR WINDOWS - // TODO: IN WINDOWS, REPLACE SLASHS FOR BACKSLASH let env_file_path = self.toolchain_dir.join(self.name); let mut env_file: String = self.content.to_string(); diff --git a/src/env/unix.rs b/src/env/unix.rs index ff7d613e..9ef937b5 100644 --- a/src/env/unix.rs +++ b/src/env/unix.rs @@ -49,7 +49,7 @@ pub fn do_remove_from_path(toolchain_dir: &Path) -> Result<(), Error> { Ok(()) } -pub(crate) fn do_add_to_path(toolchain_dir: &Path) -> Result<(), Error> { +pub(crate) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { for sh in shell::get_available_shells() { let source_cmd = sh.source_string(&toolchain_dir.display().to_string())?; let source_cmd_with_newline = format!("\n{}", &source_cmd); @@ -82,7 +82,7 @@ pub(crate) fn do_add_to_path(toolchain_dir: &Path) -> Result<(), Error> { Ok(()) } -pub(crate) fn do_write_env_files(toolchain_dir: &Path) -> Result<(), Error> { +pub(crate) fn write_env_files(toolchain_dir: &Path) -> Result<(), Error> { let mut written = vec![]; for sh in shell::get_available_shells() { diff --git a/src/env/windows.rs b/src/env/windows.rs index c12f3af6..fc027844 100644 --- a/src/env/windows.rs +++ b/src/env/windows.rs @@ -34,7 +34,7 @@ pub fn delete_environment_variable(key: &str) -> Result<(), Error> { Ok(()) } -pub(crate) fn do_write_env_files(toolchain_dir: &Path) -> Result<(), Error> { +pub(crate) fn write_env_files(toolchain_dir: &Path) -> Result<(), Error> { let windows_shells: Vec = vec![Box::new(Batch), Box::new(Powershell)]; for sh in windows_shells.into_iter() { let script = sh.env_script(toolchain_dir); @@ -44,7 +44,7 @@ pub(crate) fn do_write_env_files(toolchain_dir: &Path) -> Result<(), Error> { Ok(()) } -pub(crate) fn do_add_to_path(toolchain_dir: &Path) -> Result<(), Error> { +pub(crate) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { let path = std::env::var_os("PATH").unwrap_or_default(); set_environment_variable("PATH", path)?; @@ -77,4 +77,3 @@ fn remove_legacy_export_file() -> Result<(), Error> { Ok(()) } -// TODO: REMOVE LEGACY FILE From 1b81a6e93e291894e1a7b54d23438e99ea7fe5e8 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sat, 21 Oct 2023 16:10:43 +0200 Subject: [PATCH 17/33] feat: Add environment cleanup --- src/env/mod.rs | 8 ++++++++ src/env/unix.rs | 2 +- src/env/windows.rs | 15 ++++++++++++++- src/main.rs | 10 ++-------- src/toolchain/llvm.rs | 11 +++++------ 5 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/env/mod.rs b/src/env/mod.rs index 30017dad..7ab75502 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -30,6 +30,14 @@ pub fn get_home_dir() -> PathBuf { BaseDirs::new().unwrap().home_dir().to_path_buf() } +pub fn clean_env(install_dir: &Path) -> Result<(), Error> { + #[cfg(windows)] + windows::clean_env(install_dir)?; + #[cfg(unix)] + unix::clean_env(install_dir)?; + + Ok(()) +} pub fn print_post_install_msg(toolchain_dir: &str, no_modify_env: bool) { if no_modify_env { println!( diff --git a/src/env/unix.rs b/src/env/unix.rs index 9ef937b5..06b78649 100644 --- a/src/env/unix.rs +++ b/src/env/unix.rs @@ -8,7 +8,7 @@ use std::{ const LEGACY_EXPORT_FILE: &str = "export-esp.sh"; -pub fn do_remove_from_path(toolchain_dir: &Path) -> Result<(), Error> { +pub fn clean_env(toolchain_dir: &Path) -> Result<(), Error> { for sh in shell::get_available_shells() { let source_bytes = format!( "{}\n", diff --git a/src/env/windows.rs b/src/env/windows.rs index fc027844..6bac5209 100644 --- a/src/env/windows.rs +++ b/src/env/windows.rs @@ -47,7 +47,8 @@ pub(crate) fn write_env_files(toolchain_dir: &Path) -> Result<(), Error> { pub(crate) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { let path = std::env::var_os("PATH").unwrap_or_default(); set_environment_variable("PATH", path)?; - + // TODO: REVIEW THIS - DO WE NEED TO SET THE X_GCC ENV? + // TODO: ARE WE ADDING X_GCC TO THE PATH? let xtensa_gcc = std::env::var_os("XTENSA_GCC").unwrap_or_default(); set_environment_variable("XTENSA_GCC", xtensa_gcc)?; @@ -69,6 +70,18 @@ pub(crate) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { Ok(()) } +pub(crate) fn clean_env(toolchain_dir: &Path) -> Result<(), Error> { + delete_environment_variable("LIBCLANG_PATH")?; + delete_environment_variable("CLANG_PATH")?; + if let Some(path) = env::var("PATH") { + set_environment_variable("PATH", &path)?; + }; + + remove_legacy_export_file()?; + + Ok(()) +} + fn remove_legacy_export_file() -> Result<(), Error> { let legacy_file = get_home_dir().join(LEGACY_EXPORT_FILE); if legacy_file.exists() { diff --git a/src/main.rs b/src/main.rs index b1916d7d..349db384 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,7 @@ use clap::{CommandFactory, Parser}; -#[cfg(windows)] -use espup::env::set_environment_variable; -#[cfg(unix)] -use espup::env::unix; use espup::{ cli::{CompletionsOpts, InstallOpts, UninstallOpts}, + env::clean_env, error::Error, logging::initialize_logger, toolchain::{ @@ -84,10 +81,7 @@ async fn uninstall(args: UninstallOpts) -> Result<()> { remove_dir_all(&install_dir) .map_err(|_| Error::RemoveDirectory(install_dir.display().to_string()))?; - #[cfg(windows)] - set_environment_variable("PATH", &env::var("PATH").unwrap())?; - #[cfg(unix)] - unix::do_remove_from_path(&install_dir)?; + clean_env(&install_dir)?; info!("Uninstallation successfully completed!"); Ok(()) diff --git a/src/toolchain/llvm.rs b/src/toolchain/llvm.rs index ad749002..ceadeb11 100644 --- a/src/toolchain/llvm.rs +++ b/src/toolchain/llvm.rs @@ -127,8 +127,8 @@ impl Llvm { if llvm_path.exists() { #[cfg(windows)] if cfg!(windows) { - delete_environment_variable("LIBCLANG_PATH")?; - delete_environment_variable("CLANG_PATH")?; + std::env::remove_var("LIBCLANG_PATH"); + std::env::remove_var("CLANG_PATH"); let updated_path = std::env::var("PATH").unwrap().replace( &format!( "{}\\{}\\esp-clang\\bin;", @@ -137,11 +137,10 @@ impl Llvm { ), "", ); - set_environment_variable("PATH", &updated_path)?; + std::env::set_var("PATH", updated_path); } - let path = toolchain_path.join(CLANG_NAME); - remove_dir_all(&path) - .map_err(|_| Error::RemoveDirectory(path.display().to_string()))?; + remove_dir_all(&llvm_path) + .map_err(|_| Error::RemoveDirectory(llvm_path.display().to_string()))?; } Ok(()) } From 671114100ae5fd97c73de87bec616b24c8fe986b Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sat, 21 Oct 2023 16:31:42 +0200 Subject: [PATCH 18/33] refactor: Review methods visibility --- src/env/mod.rs | 2 +- src/env/shell.rs | 12 +++---- src/env/unix.rs | 26 ++++++++------- src/env/windows.rs | 75 ++++++++++++++++++++++--------------------- src/toolchain/gcc.rs | 2 +- src/toolchain/llvm.rs | 2 -- src/toolchain/mod.rs | 8 ++--- src/toolchain/rust.rs | 2 +- 8 files changed, 65 insertions(+), 64 deletions(-) diff --git a/src/env/mod.rs b/src/env/mod.rs index 7ab75502..b3b2cf94 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -12,7 +12,7 @@ pub mod unix; pub mod windows; /// Instructions to export the environment variables. -pub fn set_environment(toolchain_dir: &Path) -> Result<(), Error> { +pub fn set_env(toolchain_dir: &Path) -> Result<(), Error> { #[cfg(windows)] if cfg!(windows) { windows::write_env_files(toolchain_dir)?; diff --git a/src/env/shell.rs b/src/env/shell.rs index 2e83678d..d8c0201a 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -34,10 +34,10 @@ use std::{ path::{Path, PathBuf}, }; -pub(crate) type Shell = Box; +pub(super) type Shell = Box; #[derive(Debug, PartialEq)] -pub(crate) struct ShellScript { +pub struct ShellScript { content: &'static str, name: &'static str, toolchain_dir: PathBuf, @@ -80,11 +80,11 @@ fn enumerate_shells() -> Vec { ] } -pub(crate) fn get_available_shells() -> impl Iterator { +pub(super) fn get_available_shells() -> impl Iterator { enumerate_shells().into_iter().filter(|sh| sh.does_exist()) } -pub(crate) trait WindowsShell { +pub trait WindowsShell { // Writes the relevant env file. fn env_script(&self, toolchain_dir: &Path) -> ShellScript; @@ -123,7 +123,7 @@ impl WindowsShell for Powershell { } } -pub(crate) trait UnixShell { +pub trait UnixShell { // Detects if a shell "exists". Users have multiple shells, so an "eager" // heuristic should be used, assuming shells exist if any traces do. fn does_exist(&self) -> bool; @@ -192,7 +192,6 @@ impl UnixShell for Bash { } struct Zsh; - impl Zsh { fn zdotdir() -> Result { use std::ffi::OsStr; @@ -247,7 +246,6 @@ impl UnixShell for Zsh { } struct Fish; - impl UnixShell for Fish { fn does_exist(&self) -> bool { // fish has to either be the shell or be callable for fish setup. diff --git a/src/env/unix.rs b/src/env/unix.rs index 06b78649..5ee5d5f2 100644 --- a/src/env/unix.rs +++ b/src/env/unix.rs @@ -8,7 +8,8 @@ use std::{ const LEGACY_EXPORT_FILE: &str = "export-esp.sh"; -pub fn clean_env(toolchain_dir: &Path) -> Result<(), Error> { +// Clean the environment for Windows. +pub(super) fn clean_env(toolchain_dir: &Path) -> Result<(), Error> { for sh in shell::get_available_shells() { let source_bytes = format!( "{}\n", @@ -49,6 +50,17 @@ pub fn clean_env(toolchain_dir: &Path) -> Result<(), Error> { Ok(()) } +// Delete the legacy export file. +fn remove_legacy_export_file() -> Result<(), Error> { + let legacy_file = get_home_dir().join(LEGACY_EXPORT_FILE); + if legacy_file.exists() { + remove_file(&legacy_file)?; + } + + Ok(()) +} + +// Update the environment for Unix. pub(crate) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { for sh in shell::get_available_shells() { let source_cmd = sh.source_string(&toolchain_dir.display().to_string())?; @@ -82,7 +94,8 @@ pub(crate) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { Ok(()) } -pub(crate) fn write_env_files(toolchain_dir: &Path) -> Result<(), Error> { +// Write the environment files for Unix. +pub(super) fn write_env_files(toolchain_dir: &Path) -> Result<(), Error> { let mut written = vec![]; for sh in shell::get_available_shells() { @@ -96,12 +109,3 @@ pub(crate) fn write_env_files(toolchain_dir: &Path) -> Result<(), Error> { Ok(()) } - -fn remove_legacy_export_file() -> Result<(), Error> { - let legacy_file = get_home_dir().join(LEGACY_EXPORT_FILE); - if legacy_file.exists() { - remove_file(&legacy_file)?; - } - - Ok(()) -} diff --git a/src/env/windows.rs b/src/env/windows.rs index 6bac5209..410b00a0 100644 --- a/src/env/windows.rs +++ b/src/env/windows.rs @@ -7,21 +7,21 @@ use winreg::{ const LEGACY_EXPORT_FILE: &str = "export-esp.ps1"; -// TODO: REVIEW CFGS -#[cfg(windows)] -/// Sets an environment variable for the current user. -pub fn set_environment_variable(key: &str, value: &str) -> Result<(), Error> { - env::set_var(key, value); +// Clean the environment for Windows. +pub(super) fn clean_env(toolchain_dir: &Path) -> Result<(), Error> { + delete_env_variable("LIBCLANG_PATH")?; + delete_env_variable("CLANG_PATH")?; + if let Some(path) = env::var("PATH") { + set_env_variable("PATH", &path)?; + }; + + remove_legacy_export_file()?; - let hkcu = RegKey::predef(HKEY_CURRENT_USER); - let environment_key = hkcu.open_subkey_with_flags("Environment", KEY_WRITE)?; - environment_key.set_value(key, &value)?; Ok(()) } -#[cfg(windows)] /// Deletes an environment variable for the current user. -pub fn delete_environment_variable(key: &str) -> Result<(), Error> { +fn delete_env_variable(key: &str) -> Result<(), Error> { if env::var_os(key).is_none() { return Ok(()); } @@ -34,35 +34,46 @@ pub fn delete_environment_variable(key: &str) -> Result<(), Error> { Ok(()) } -pub(crate) fn write_env_files(toolchain_dir: &Path) -> Result<(), Error> { - let windows_shells: Vec = vec![Box::new(Batch), Box::new(Powershell)]; - for sh in windows_shells.into_iter() { - let script = sh.env_script(toolchain_dir); - script.write()?; +/// Sets an environment variable for the current user. +fn set_env_variable(key: &str, value: &str) -> Result<(), Error> { + env::set_var(key, value); + + let hkcu = RegKey::predef(HKEY_CURRENT_USER); + let environment_key = hkcu.open_subkey_with_flags("Environment", KEY_WRITE)?; + environment_key.set_value(key, &value)?; + Ok(()) +} + +// Delete the legacy export file. +fn remove_legacy_export_file() -> Result<(), Error> { + let legacy_file = get_home_dir().join(LEGACY_EXPORT_FILE); + if legacy_file.exists() { + remove_file(&legacy_file)?; } Ok(()) } -pub(crate) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { +// Update the environment for Windows. +pub(super) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { let path = std::env::var_os("PATH").unwrap_or_default(); - set_environment_variable("PATH", path)?; + set_env_variable("PATH", path)?; // TODO: REVIEW THIS - DO WE NEED TO SET THE X_GCC ENV? // TODO: ARE WE ADDING X_GCC TO THE PATH? let xtensa_gcc = std::env::var_os("XTENSA_GCC").unwrap_or_default(); - set_environment_variable("XTENSA_GCC", xtensa_gcc)?; + set_env_variable("XTENSA_GCC", xtensa_gcc)?; let riscv_gcc = std::env::var_os("RISCV_GCC").unwrap_or_default(); - set_environment_variable("RISCV_GCC", riscv_gcc)?; + set_env_variable("RISCV_GCC", riscv_gcc)?; let libclang_path = std::env::var_os("LIBCLANG_PATH"); if let Some(libclang_path) = libclang_path { - set_environment_variable("LIBCLANG_PATH", libclang_path)?; + set_env_variable("LIBCLANG_PATH", libclang_path)?; } let clang_path = std::env::var_os("CLANG_PATH"); if let Some(libclang_path) = clang_path { - set_environment_variable("CLANG_PATH", clang_path)?; + set_env_variable("CLANG_PATH", clang_path)?; } remove_legacy_export_file()?; @@ -70,22 +81,12 @@ pub(crate) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { Ok(()) } -pub(crate) fn clean_env(toolchain_dir: &Path) -> Result<(), Error> { - delete_environment_variable("LIBCLANG_PATH")?; - delete_environment_variable("CLANG_PATH")?; - if let Some(path) = env::var("PATH") { - set_environment_variable("PATH", &path)?; - }; - - remove_legacy_export_file()?; - - Ok(()) -} - -fn remove_legacy_export_file() -> Result<(), Error> { - let legacy_file = get_home_dir().join(LEGACY_EXPORT_FILE); - if legacy_file.exists() { - remove_file(&legacy_file)?; +// Write the environment files for Windows. +pub(super) fn write_env_files(toolchain_dir: &Path) -> Result<(), Error> { + let windows_shells: Vec = vec![Box::new(Batch), Box::new(Powershell)]; + for sh in windows_shells.into_iter() { + let script = sh.env_script(toolchain_dir); + script.write()?; } Ok(()) diff --git a/src/toolchain/gcc.rs b/src/toolchain/gcc.rs index 600a156b..788e8810 100644 --- a/src/toolchain/gcc.rs +++ b/src/toolchain/gcc.rs @@ -30,7 +30,7 @@ pub struct Gcc { impl Gcc { /// Gets the binary path. - pub fn get_bin_path(&self) -> String { + fn get_bin_path(&self) -> String { format!("{}/{}/bin", &self.path.to_str().unwrap(), &self.arch) } diff --git a/src/toolchain/llvm.rs b/src/toolchain/llvm.rs index ceadeb11..122a59e1 100644 --- a/src/toolchain/llvm.rs +++ b/src/toolchain/llvm.rs @@ -1,7 +1,5 @@ //! LLVM Toolchain source and installation tools. -#[cfg(windows)] -use crate::env::{delete_environment_variable, set_environment_variable}; use crate::{ error::Error, host_triple::HostTriple, diff --git a/src/toolchain/mod.rs b/src/toolchain/mod.rs index 72c21f11..70cd26ba 100644 --- a/src/toolchain/mod.rs +++ b/src/toolchain/mod.rs @@ -2,7 +2,7 @@ use crate::{ cli::InstallOpts, - env::{print_post_install_msg, set_environment}, + env::{print_post_install_msg, set_env}, error::Error, host_triple::get_host_triple, targets::Target, @@ -48,7 +48,7 @@ pub trait Installable { } /// Downloads a file from a URL and uncompresses it, if necesary, to the output directory. -pub async fn download_file( +pub(super) async fn download_file( url: String, file_name: &str, output_directory: &str, @@ -246,7 +246,7 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> InstallMode::Update => info!("Update successfully completed!"), } if !args.no_modify_env { - set_environment(&toolchain_dir)?; + set_env(&toolchain_dir)?; } print_post_install_msg(&toolchain_dir.display().to_string(), args.no_modify_env); @@ -254,7 +254,7 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> } /// Queries the GitHub API and returns the JSON response. -pub fn github_query(url: &str) -> Result { +pub(super) fn github_query(url: &str) -> Result { info!("Querying GitHub API: '{}'", url); let mut headers = header::HeaderMap::new(); headers.insert(header::USER_AGENT, "espup".parse().unwrap()); diff --git a/src/toolchain/rust.rs b/src/toolchain/rust.rs index 7f1ce922..9841e1d5 100644 --- a/src/toolchain/rust.rs +++ b/src/toolchain/rust.rs @@ -405,7 +405,7 @@ pub fn get_rustup_home() -> PathBuf { } /// Checks if rustup is installed. -pub async fn check_rust_installation() -> Result<(), Error> { +pub(super) async fn check_rust_installation() -> Result<(), Error> { info!("Checking Rust installation"); if let Err(e) = Command::new("rustup") From ec8ad0d2cc60b41a3b87720eae43920a5a2c567d Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sat, 21 Oct 2023 16:45:39 +0200 Subject: [PATCH 19/33] feat: Rework Windows env setup --- src/env/shell.rs | 2 +- src/env/windows.rs | 33 +++++++++++++++++++-------------- src/toolchain/gcc.rs | 1 - 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/env/shell.rs b/src/env/shell.rs index d8c0201a..8514a37f 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -290,7 +290,7 @@ pub(crate) fn find_cmd<'a>(cmds: &[&'a str]) -> Option<&'a str> { fn has_cmd(cmd: &str) -> bool { let cmd = format!("{}{}", cmd, env::consts::EXE_SUFFIX); - let path = std::env::var_os("PATH").unwrap_or_default(); + let path = std::env::var("PATH").unwrap_or_default(); env::split_paths(&path) .map(|p| p.join(&cmd)) .any(|p| p.exists()) diff --git a/src/env/windows.rs b/src/env/windows.rs index 410b00a0..6601ea9d 100644 --- a/src/env/windows.rs +++ b/src/env/windows.rs @@ -22,7 +22,7 @@ pub(super) fn clean_env(toolchain_dir: &Path) -> Result<(), Error> { /// Deletes an environment variable for the current user. fn delete_env_variable(key: &str) -> Result<(), Error> { - if env::var_os(key).is_none() { + if env::var(key).is_none() { return Ok(()); } @@ -56,25 +56,30 @@ fn remove_legacy_export_file() -> Result<(), Error> { // Update the environment for Windows. pub(super) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { - let path = std::env::var_os("PATH").unwrap_or_default(); - set_env_variable("PATH", path)?; - // TODO: REVIEW THIS - DO WE NEED TO SET THE X_GCC ENV? - // TODO: ARE WE ADDING X_GCC TO THE PATH? - let xtensa_gcc = std::env::var_os("XTENSA_GCC").unwrap_or_default(); - set_env_variable("XTENSA_GCC", xtensa_gcc)?; + let mut path = std::env::var("PATH").unwrap_or_default(); - let riscv_gcc = std::env::var_os("RISCV_GCC").unwrap_or_default(); - set_env_variable("RISCV_GCC", riscv_gcc)?; + if let Some(xtensa_gcc) = std::env::var("XTENSA_GCC") { + if !path.contains(xtensa_gcc) { + path = format!("{};{}", xtensa_gcc, path); + } + } - let libclang_path = std::env::var_os("LIBCLANG_PATH"); - if let Some(libclang_path) = libclang_path { + if let Some(riscv_gcc) = std::env::var("RISCV_GCC") { + if !path.contains(riscv_gcc) { + path = format!("{};{}", riscv_gcc, path); + } + } + + if let Some(libclang_path) = std::env::var("LIBCLANG_PATH") { set_env_variable("LIBCLANG_PATH", libclang_path)?; } - let clang_path = std::env::var_os("CLANG_PATH"); - if let Some(libclang_path) = clang_path { - set_env_variable("CLANG_PATH", clang_path)?; + if let Some(clang_path) = std::env::var("CLANG_PATH") { + if !path.contains(clang_path) { + path = format!("{};{}", clang_path, path); + } } + set_env_variable("PATH", path)?; remove_legacy_export_file()?; diff --git a/src/toolchain/gcc.rs b/src/toolchain/gcc.rs index 788e8810..882bb4ec 100644 --- a/src/toolchain/gcc.rs +++ b/src/toolchain/gcc.rs @@ -127,7 +127,6 @@ fn get_artifact_extension(host_triple: &HostTriple) -> &str { } /// Checks if the toolchain is pressent, if present uninstalls it. -// TODO: REVIEW UNINSTALL METHODS pub fn uninstall_gcc_toolchains(toolchain_path: &Path) -> Result<(), Error> { info!("Uninstalling GCC"); From c9a1188870ddacfdb57c60cb8b4cff89e072389b Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sat, 21 Oct 2023 15:00:54 +0000 Subject: [PATCH 20/33] fix: Fix clippy lints --- src/env/mod.rs | 2 +- src/env/shell.rs | 3 +++ src/env/windows.rs | 11 ++++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/env/mod.rs b/src/env/mod.rs index b3b2cf94..7eaee86e 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -55,6 +55,6 @@ pub fn print_post_install_msg(toolchain_dir: &str, no_modify_env: bool) { #[cfg(windows)] println!( "\t'. {}\\env.ps1' or '{}\\env.bat dependeing on your shell'", - toolchain_dir + toolchain_dir, toolchain_dir ); } diff --git a/src/env/shell.rs b/src/env/shell.rs index 8514a37f..4b5d1411 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -34,7 +34,10 @@ use std::{ path::{Path, PathBuf}, }; +#[cfg(unix)] pub(super) type Shell = Box; +#[cfg(windows)] +pub(super) type Shell = Box; #[derive(Debug, PartialEq)] pub struct ShellScript { diff --git a/src/env/windows.rs b/src/env/windows.rs index 6601ea9d..5ccb84ac 100644 --- a/src/env/windows.rs +++ b/src/env/windows.rs @@ -1,5 +1,9 @@ -use log::warn; -use std::env; +use crate::env::get_home_dir; +use miette::Result; +use std::{ + env, + fs::{remove_file, Path}, +}; use winreg::{ enums::{HKEY_CURRENT_USER, KEY_READ, KEY_WRITE}, RegKey, @@ -58,6 +62,7 @@ fn remove_legacy_export_file() -> Result<(), Error> { pub(super) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { let mut path = std::env::var("PATH").unwrap_or_default(); + // TODO: FIX THIS if let Some(xtensa_gcc) = std::env::var("XTENSA_GCC") { if !path.contains(xtensa_gcc) { path = format!("{};{}", xtensa_gcc, path); @@ -79,7 +84,7 @@ pub(super) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { path = format!("{};{}", clang_path, path); } } - set_env_variable("PATH", path)?; + set_env_variable("PATH", &path)?; remove_legacy_export_file()?; From 6f3c26420cf92bbccac27a99d870c1a8df2ea70d Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sat, 21 Oct 2023 17:13:24 +0200 Subject: [PATCH 21/33] style: Improve fmt --- src/env/shell.rs | 24 +++++++++++------------- src/env/windows.rs | 12 ++++++------ src/toolchain/gcc.rs | 13 +++++++------ src/toolchain/llvm.rs | 15 ++++++++------- src/toolchain/rust.rs | 8 ++++---- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/env/shell.rs b/src/env/shell.rs index 4b5d1411..5c12764b 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -51,21 +51,21 @@ impl ShellScript { let env_file_path = self.toolchain_dir.join(self.name); let mut env_file: String = self.content.to_string(); - let xtensa_gcc = std::env::var("XTENSA_GCC").unwrap_or_default(); + let xtensa_gcc = env::var("XTENSA_GCC").unwrap_or_default(); env_file = env_file.replace("{xtensa_gcc}", &xtensa_gcc); - let riscv_gcc = std::env::var("RISCV_GCC").unwrap_or_default(); + let riscv_gcc = env::var("RISCV_GCC").unwrap_or_default(); env_file = env_file.replace("{riscv_gcc}", &riscv_gcc); - let libclang_path = std::env::var("LIBCLANG_PATH").unwrap_or_default(); + let libclang_path = env::var("LIBCLANG_PATH").unwrap_or_default(); env_file = env_file.replace("{libclang_path}", &libclang_path); #[cfg(windows)] if cfg!(windows) { - let libclang_bin_path = std::env::var("LIBCLANG_BIN_PATH").unwrap_or_default(); + let libclang_bin_path = env::var("LIBCLANG_BIN_PATH").unwrap_or_default(); env_file = env_file.replace("{libclang_bin_path}", &libclang_bin_path); } - let clang_path = std::env::var("CLANG_PATH").unwrap_or_default(); + let clang_path = env::var("CLANG_PATH").unwrap_or_default(); env_file = env_file.replace("{clang_path}", &clang_path); write_file(&env_file_path, &env_file)?; @@ -200,8 +200,8 @@ impl Zsh { use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; - if matches!(std::env::var("SHELL"), Ok(sh) if sh.contains("zsh")) { - match std::env::var("ZDOTDIR") { + if matches!(env::var("SHELL"), Ok(sh) if sh.contains("zsh")) { + match env::var("ZDOTDIR") { Ok(dir) if !dir.is_empty() => Ok(PathBuf::from(dir)), _ => Err(Error::Zdotdir), } @@ -220,8 +220,7 @@ impl Zsh { impl UnixShell for Zsh { fn does_exist(&self) -> bool { // zsh has to either be the shell or be callable for zsh setup. - matches!(std::env::var("SHELL"), Ok(sh) if sh.contains("zsh")) - || find_cmd(&["zsh"]).is_some() + matches!(env::var("SHELL"), Ok(sh) if sh.contains("zsh")) || find_cmd(&["zsh"]).is_some() } fn rcfiles(&self) -> Vec { @@ -252,14 +251,13 @@ struct Fish; impl UnixShell for Fish { fn does_exist(&self) -> bool { // fish has to either be the shell or be callable for fish setup. - matches!(std::env::var("SHELL"), Ok(sh) if sh.contains("fish")) - || find_cmd(&["fish"]).is_some() + matches!(env::var("SHELL"), Ok(sh) if sh.contains("fish")) || find_cmd(&["fish"]).is_some() } // > "$XDG_CONFIG_HOME/fish/conf.d" (or "~/.config/fish/conf.d" if that variable is unset) for the user // from fn rcfiles(&self) -> Vec { - let p0 = std::env::var("XDG_CONFIG_HOME").ok().map(|p| { + let p0 = env::var("XDG_CONFIG_HOME").ok().map(|p| { let mut path = PathBuf::from(p); path.push("fish/conf.d/espup.fish"); path @@ -293,7 +291,7 @@ pub(crate) fn find_cmd<'a>(cmds: &[&'a str]) -> Option<&'a str> { fn has_cmd(cmd: &str) -> bool { let cmd = format!("{}{}", cmd, env::consts::EXE_SUFFIX); - let path = std::env::var("PATH").unwrap_or_default(); + let path = env::var("PATH").unwrap_or_default(); env::split_paths(&path) .map(|p| p.join(&cmd)) .any(|p| p.exists()) diff --git a/src/env/windows.rs b/src/env/windows.rs index 5ccb84ac..ff96638e 100644 --- a/src/env/windows.rs +++ b/src/env/windows.rs @@ -1,4 +1,4 @@ -use crate::env::get_home_dir; +use crate::{env::get_home_dir, error::Error}; use miette::Result; use std::{ env, @@ -60,26 +60,26 @@ fn remove_legacy_export_file() -> Result<(), Error> { // Update the environment for Windows. pub(super) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { - let mut path = std::env::var("PATH").unwrap_or_default(); + let mut path = env::var("PATH").unwrap_or_default(); // TODO: FIX THIS - if let Some(xtensa_gcc) = std::env::var("XTENSA_GCC") { + if let Some(xtensa_gcc) = env::var("XTENSA_GCC") { if !path.contains(xtensa_gcc) { path = format!("{};{}", xtensa_gcc, path); } } - if let Some(riscv_gcc) = std::env::var("RISCV_GCC") { + if let Some(riscv_gcc) = env::var("RISCV_GCC") { if !path.contains(riscv_gcc) { path = format!("{};{}", riscv_gcc, path); } } - if let Some(libclang_path) = std::env::var("LIBCLANG_PATH") { + if let Some(libclang_path) = env::var("LIBCLANG_PATH") { set_env_variable("LIBCLANG_PATH", libclang_path)?; } - if let Some(clang_path) = std::env::var("CLANG_PATH") { + if let Some(clang_path) = env::var("CLANG_PATH") { if !path.contains(clang_path) { path = format!("{};{}", clang_path, path); } diff --git a/src/toolchain/gcc.rs b/src/toolchain/gcc.rs index 882bb4ec..3cd65c9c 100644 --- a/src/toolchain/gcc.rs +++ b/src/toolchain/gcc.rs @@ -9,6 +9,7 @@ use async_trait::async_trait; use log::{debug, info, warn}; use miette::Result; use std::{ + env, fs::remove_dir_all, path::{Path, PathBuf}, }; @@ -84,17 +85,17 @@ impl Installable for Gcc { if cfg!(windows) { let windows_path = self.get_bin_path().replace('/', "\\"); if self.arch == RISCV_GCC { - std::env::set_var("RISCV_GCC", self.get_bin_path()); + env::set_var("RISCV_GCC", self.get_bin_path()); } else { - std::env::set_var("XTENSA_GCC", self.get_bin_path()); + env::set_var("XTENSA_GCC", self.get_bin_path()); } } #[cfg(unix)] if cfg!(unix) { if self.arch == RISCV_GCC { - std::env::set_var("RISCV_GCC", self.get_bin_path()); + env::set_var("RISCV_GCC", self.get_bin_path()); } else { - std::env::set_var("XTENSA_GCC", self.get_bin_path()); + env::set_var("XTENSA_GCC", self.get_bin_path()); } } Ok(()) @@ -143,9 +144,9 @@ pub fn uninstall_gcc_toolchains(toolchain_path: &Path) -> Result<(), Error> { DEFAULT_GCC_RELEASE, toolchain ); - std::env::set_var( + env::set_var( "PATH", - std::env::var("PATH") + env::var("PATH") .unwrap() .replace(&format!("{gcc_path};"), ""), ); diff --git a/src/toolchain/llvm.rs b/src/toolchain/llvm.rs index 122a59e1..081e8af0 100644 --- a/src/toolchain/llvm.rs +++ b/src/toolchain/llvm.rs @@ -10,6 +10,7 @@ use log::{info, warn}; use miette::Result; use regex::Regex; use std::{ + env, fs::remove_dir_all, path::{Path, PathBuf}, u8, @@ -125,9 +126,9 @@ impl Llvm { if llvm_path.exists() { #[cfg(windows)] if cfg!(windows) { - std::env::remove_var("LIBCLANG_PATH"); - std::env::remove_var("CLANG_PATH"); - let updated_path = std::env::var("PATH").unwrap().replace( + env::remove_var("LIBCLANG_PATH"); + env::remove_var("CLANG_PATH"); + let updated_path = env::var("PATH").unwrap().replace( &format!( "{}\\{}\\esp-clang\\bin;", llvm_path.display().to_string().replace('/', "\\"), @@ -135,7 +136,7 @@ impl Llvm { ), "", ); - std::env::set_var("PATH", updated_path); + env::set_var("PATH", updated_path); } remove_dir_all(&llvm_path) .map_err(|_| Error::RemoveDirectory(llvm_path.display().to_string()))?; @@ -165,11 +166,11 @@ impl Installable for Llvm { } // Set environment variables. #[cfg(windows)] - std::env::set_var("LIBCLANG_BIN_PATH", self.get_lib_path()); - std::env::set_var("LIBCLANG_PATH", self.get_lib_path()); + env::set_var("LIBCLANG_BIN_PATH", self.get_lib_path()); + env::set_var("LIBCLANG_PATH", self.get_lib_path()); // } if self.extended { - std::env::set_var("CLANG_PATH", self.get_bin_path()); + env::set_var("CLANG_PATH", self.get_bin_path()); } Ok(()) diff --git a/src/toolchain/rust.rs b/src/toolchain/rust.rs index 9841e1d5..cccce795 100644 --- a/src/toolchain/rust.rs +++ b/src/toolchain/rust.rs @@ -451,24 +451,24 @@ mod tests { #[test] fn test_get_cargo_home() { // No CARGO_HOME set - std::env::remove_var("CARGO_HOME"); + env::remove_var("CARGO_HOME"); assert_eq!(get_cargo_home(), get_home_dir().join(".cargo")); // CARGO_HOME set let temp_dir = tempfile::TempDir::new().unwrap(); let cargo_home = temp_dir.path().to_path_buf(); - std::env::set_var("CARGO_HOME", cargo_home.to_str().unwrap()); + env::set_var("CARGO_HOME", cargo_home.to_str().unwrap()); assert_eq!(get_cargo_home(), cargo_home); } #[test] fn test_get_rustup_home() { // No RUSTUP_HOME set - std::env::remove_var("RUSTUP_HOME"); + env::remove_var("RUSTUP_HOME"); assert_eq!(get_rustup_home(), get_home_dir().join(".rustup")); // RUSTUP_HOME set let temp_dir = tempfile::TempDir::new().unwrap(); let rustup_home = temp_dir.path().to_path_buf(); - std::env::set_var("RUSTUP_HOME", rustup_home.to_str().unwrap()); + env::set_var("RUSTUP_HOME", rustup_home.to_str().unwrap()); assert_eq!(get_rustup_home(), rustup_home); } } From 52086db6631332a41d18f8d4d1cc044f83233efb Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sun, 22 Oct 2023 14:08:33 +0200 Subject: [PATCH 22/33] fix: Fix Windows build --- src/env/shell.rs | 26 +++++++++++++++++++++----- src/env/windows.rs | 39 ++++++++++++++++++--------------------- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/env/shell.rs b/src/env/shell.rs index 5c12764b..d94dc216 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -25,7 +25,7 @@ //! 1) using a shell script that updates PATH if the path is not in PATH //! 2) sourcing this script (`. /path/to/script`) in any appropriate rc file -use crate::{env::get_home_dir, error::Error}; +use crate::error::Error; use miette::Result; use std::{ env, @@ -37,7 +37,7 @@ use std::{ #[cfg(unix)] pub(super) type Shell = Box; #[cfg(windows)] -pub(super) type Shell = Box; +pub(super) type Shell = Box; #[derive(Debug, PartialEq)] pub struct ShellScript { @@ -74,6 +74,7 @@ impl ShellScript { } // Cross-platform non-POSIX shells have not been assessed for integration yet +#[cfg(unix)] fn enumerate_shells() -> Vec { vec![ Box::new(Posix), @@ -83,10 +84,12 @@ fn enumerate_shells() -> Vec { ] } +#[cfg(unix)] pub(super) fn get_available_shells() -> impl Iterator { enumerate_shells().into_iter().filter(|sh| sh.does_exist()) } +#[cfg(windows)] pub trait WindowsShell { // Writes the relevant env file. fn env_script(&self, toolchain_dir: &Path) -> ShellScript; @@ -95,7 +98,9 @@ pub trait WindowsShell { fn source_string(&self, toolchain_dir: &str) -> Result; } -struct Batch; +#[cfg(windows)] +pub struct Batch; +#[cfg(windows)] impl WindowsShell for Batch { fn env_script(&self, toolchain_dir: &Path) -> ShellScript { ShellScript { @@ -111,7 +116,9 @@ impl WindowsShell for Batch { } } -struct Powershell; +#[cfg(windows)] +pub struct Powershell; +#[cfg(windows)] impl WindowsShell for Powershell { fn env_script(&self, toolchain_dir: &Path) -> ShellScript { ShellScript { @@ -126,6 +133,7 @@ impl WindowsShell for Powershell { } } +#[cfg(unix)] pub trait UnixShell { // Detects if a shell "exists". Users have multiple shells, so an "eager" // heuristic should be used, assuming shells exist if any traces do. @@ -153,7 +161,9 @@ pub trait UnixShell { } } +#[cfg(unix)] struct Posix; +#[cfg(unix)] impl UnixShell for Posix { fn does_exist(&self) -> bool { true @@ -170,8 +180,9 @@ impl UnixShell for Posix { } } +#[cfg(unix)] struct Bash; - +#[cfg(unix)] impl UnixShell for Bash { fn does_exist(&self) -> bool { !self.update_rcs().is_empty() @@ -194,7 +205,9 @@ impl UnixShell for Bash { } } +#[cfg(unix)] struct Zsh; +#[cfg(unix)] impl Zsh { fn zdotdir() -> Result { use std::ffi::OsStr; @@ -217,6 +230,7 @@ impl Zsh { } } +#[cfg(unix)] impl UnixShell for Zsh { fn does_exist(&self) -> bool { // zsh has to either be the shell or be callable for zsh setup. @@ -247,7 +261,9 @@ impl UnixShell for Zsh { } } +#[cfg(unix)] struct Fish; +#[cfg(unix)] impl UnixShell for Fish { fn does_exist(&self) -> bool { // fish has to either be the shell or be callable for fish setup. diff --git a/src/env/windows.rs b/src/env/windows.rs index ff96638e..8cb71c52 100644 --- a/src/env/windows.rs +++ b/src/env/windows.rs @@ -1,9 +1,6 @@ -use crate::{env::get_home_dir, error::Error}; +use crate::{env::get_home_dir, env::shell, error::Error}; use miette::Result; -use std::{ - env, - fs::{remove_file, Path}, -}; +use std::{env, fs::remove_file, path::Path}; use winreg::{ enums::{HKEY_CURRENT_USER, KEY_READ, KEY_WRITE}, RegKey, @@ -15,8 +12,8 @@ const LEGACY_EXPORT_FILE: &str = "export-esp.ps1"; pub(super) fn clean_env(toolchain_dir: &Path) -> Result<(), Error> { delete_env_variable("LIBCLANG_PATH")?; delete_env_variable("CLANG_PATH")?; - if let Some(path) = env::var("PATH") { - set_env_variable("PATH", &path)?; + if let Some(path) = env::var_os("PATH") { + set_env_variable("PATH", &path.to_string_lossy())?; }; remove_legacy_export_file()?; @@ -26,7 +23,7 @@ pub(super) fn clean_env(toolchain_dir: &Path) -> Result<(), Error> { /// Deletes an environment variable for the current user. fn delete_env_variable(key: &str) -> Result<(), Error> { - if env::var(key).is_none() { + if env::var(key).is_ok() { return Ok(()); } @@ -62,26 +59,25 @@ fn remove_legacy_export_file() -> Result<(), Error> { pub(super) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { let mut path = env::var("PATH").unwrap_or_default(); - // TODO: FIX THIS - if let Some(xtensa_gcc) = env::var("XTENSA_GCC") { - if !path.contains(xtensa_gcc) { - path = format!("{};{}", xtensa_gcc, path); + if let Some(xtensa_gcc) = env::var_os("XTENSA_GCC") { + if !path.contains(xtensa_gcc.into()) { + path = format!("{};{}", xtensa_gcc.into(), path); } } - if let Some(riscv_gcc) = env::var("RISCV_GCC") { - if !path.contains(riscv_gcc) { - path = format!("{};{}", riscv_gcc, path); + if let Some(riscv_gcc) = env::var_os("RISCV_GCC") { + if !path.contains(riscv_gcc.into()) { + path = format!("{};{}", riscv_gcc.into(), path); } } - if let Some(libclang_path) = env::var("LIBCLANG_PATH") { - set_env_variable("LIBCLANG_PATH", libclang_path)?; + if let Some(libclang_path) = env::var_os("LIBCLANG_PATH") { + set_env_variable("LIBCLANG_PATH", &libclang_path.to_string_lossy())?; } - if let Some(clang_path) = env::var("CLANG_PATH") { - if !path.contains(clang_path) { - path = format!("{};{}", clang_path, path); + if let Some(clang_path) = env::var_os("CLANG_PATH") { + if !path.contains(clang_path.into()) { + path = format!("{};{}", clang_path.into(), path); } } set_env_variable("PATH", &path)?; @@ -93,7 +89,8 @@ pub(super) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { // Write the environment files for Windows. pub(super) fn write_env_files(toolchain_dir: &Path) -> Result<(), Error> { - let windows_shells: Vec = vec![Box::new(Batch), Box::new(Powershell)]; + let windows_shells: Vec = + vec![Box::new(shell::Batch), Box::new(shell::Powershell)]; for sh in windows_shells.into_iter() { let script = sh.env_script(toolchain_dir); script.write()?; From 8663d289434de9a8e360bebbee30244ce74e1dbb Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sun, 22 Oct 2023 14:08:33 +0200 Subject: [PATCH 23/33] fix: Fix Windows build --- src/env/mod.rs | 2 +- src/env/shell.rs | 2 ++ src/env/windows.rs | 29 ++++++++++++++++------------- src/toolchain/gcc.rs | 28 +++++++++++----------------- src/toolchain/rust.rs | 1 + 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/env/mod.rs b/src/env/mod.rs index 7eaee86e..14644602 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -16,7 +16,7 @@ pub fn set_env(toolchain_dir: &Path) -> Result<(), Error> { #[cfg(windows)] if cfg!(windows) { windows::write_env_files(toolchain_dir)?; - windows::update_env(toolchain_dir)?; + windows::update_env()?; } #[cfg(unix)] if cfg!(unix) { diff --git a/src/env/shell.rs b/src/env/shell.rs index d94dc216..dff658b1 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -301,10 +301,12 @@ impl UnixShell for Fish { } } +#[cfg(unix)] pub(crate) fn find_cmd<'a>(cmds: &[&'a str]) -> Option<&'a str> { cmds.iter().cloned().find(|&s| has_cmd(s)) } +#[cfg(unix)] fn has_cmd(cmd: &str) -> bool { let cmd = format!("{}{}", cmd, env::consts::EXE_SUFFIX); let path = env::var("PATH").unwrap_or_default(); diff --git a/src/env/windows.rs b/src/env/windows.rs index 8cb71c52..64b196b2 100644 --- a/src/env/windows.rs +++ b/src/env/windows.rs @@ -9,7 +9,7 @@ use winreg::{ const LEGACY_EXPORT_FILE: &str = "export-esp.ps1"; // Clean the environment for Windows. -pub(super) fn clean_env(toolchain_dir: &Path) -> Result<(), Error> { +pub(super) fn clean_env(_install_dir: &Path) -> Result<(), Error> { delete_env_variable("LIBCLANG_PATH")?; delete_env_variable("CLANG_PATH")?; if let Some(path) = env::var_os("PATH") { @@ -56,28 +56,31 @@ fn remove_legacy_export_file() -> Result<(), Error> { } // Update the environment for Windows. -pub(super) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { +pub(super) fn update_env() -> Result<(), Error> { let mut path = env::var("PATH").unwrap_or_default(); - if let Some(xtensa_gcc) = env::var_os("XTENSA_GCC") { - if !path.contains(xtensa_gcc.into()) { - path = format!("{};{}", xtensa_gcc.into(), path); + if let Ok(xtensa_gcc) = env::var("XTENSA_GCC") { + let xtensa_gcc: &str = &xtensa_gcc; + if !path.contains(xtensa_gcc) { + path = format!("{};{}", xtensa_gcc, path); } } - if let Some(riscv_gcc) = env::var_os("RISCV_GCC") { - if !path.contains(riscv_gcc.into()) { - path = format!("{};{}", riscv_gcc.into(), path); + if let Ok(riscv_gcc) = env::var("RISCV_GCC") { + let riscv_gcc: &str = &riscv_gcc; + if !path.contains(riscv_gcc) { + path = format!("{};{}", riscv_gcc, path); } } - if let Some(libclang_path) = env::var_os("LIBCLANG_PATH") { - set_env_variable("LIBCLANG_PATH", &libclang_path.to_string_lossy())?; + if let Ok(libclang_path) = env::var("LIBCLANG_PATH") { + set_env_variable("LIBCLANG_PATH", &libclang_path)?; } - if let Some(clang_path) = env::var_os("CLANG_PATH") { - if !path.contains(clang_path.into()) { - path = format!("{};{}", clang_path.into(), path); + if let Ok(clang_path) = env::var("CLANG_PATH") { + let clang_path: &str = &clang_path; + if !path.contains(clang_path) { + path = format!("{};{}", clang_path, path); } } set_env_variable("PATH", &path)?; diff --git a/src/toolchain/gcc.rs b/src/toolchain/gcc.rs index 3cd65c9c..686da4d1 100644 --- a/src/toolchain/gcc.rs +++ b/src/toolchain/gcc.rs @@ -32,7 +32,12 @@ pub struct Gcc { impl Gcc { /// Gets the binary path. fn get_bin_path(&self) -> String { - format!("{}/{}/bin", &self.path.to_str().unwrap(), &self.arch) + #[cfg(windows)] + let bin_path = + format!("{}/{}/bin", &self.path.to_str().unwrap(), &self.arch).replace('/', "\\"); + #[cfg(unix)] + let bin_path = format!("{}/{}/bin", &self.path.to_str().unwrap(), &self.arch); + bin_path } /// Create a new instance with default values and proper toolchain name. @@ -81,23 +86,12 @@ impl Installable for Gcc { .await?; } - #[cfg(windows)] - if cfg!(windows) { - let windows_path = self.get_bin_path().replace('/', "\\"); - if self.arch == RISCV_GCC { - env::set_var("RISCV_GCC", self.get_bin_path()); - } else { - env::set_var("XTENSA_GCC", self.get_bin_path()); - } - } - #[cfg(unix)] - if cfg!(unix) { - if self.arch == RISCV_GCC { - env::set_var("RISCV_GCC", self.get_bin_path()); - } else { - env::set_var("XTENSA_GCC", self.get_bin_path()); - } + if self.arch == RISCV_GCC { + env::set_var("RISCV_GCC", self.get_bin_path()); + } else { + env::set_var("XTENSA_GCC", self.get_bin_path()); } + Ok(()) } diff --git a/src/toolchain/rust.rs b/src/toolchain/rust.rs index cccce795..4d83e205 100644 --- a/src/toolchain/rust.rs +++ b/src/toolchain/rust.rs @@ -430,6 +430,7 @@ mod tests { logging::initialize_logger, toolchain::rust::{get_cargo_home, get_rustup_home, XtensaRust}, }; + use std::env; #[test] fn test_xtensa_rust_parse_version() { From 332c45d6c0cdcd92c1f58b1b4aa5b6c1468ea143 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sun, 22 Oct 2023 14:08:33 +0200 Subject: [PATCH 24/33] fix: Fix Windows build --- src/env/shell.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/env/shell.rs b/src/env/shell.rs index dff658b1..2e14973a 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -25,6 +25,8 @@ //! 1) using a shell script that updates PATH if the path is not in PATH //! 2) sourcing this script (`. /path/to/script`) in any appropriate rc file +#[cfg(unix)] +use crate::env::get_home_dir; use crate::error::Error; use miette::Result; use std::{ From cf29809d95ad050d965c9ac3316fa06c152bfab1 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sun, 22 Oct 2023 19:10:16 +0200 Subject: [PATCH 25/33] fix: Fix Windows environment --- src/env/mod.rs | 18 ++++++++++-------- src/env/windows.rs | 8 ++++++++ src/toolchain/mod.rs | 6 +++--- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/env/mod.rs b/src/env/mod.rs index 14644602..29ab387c 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -12,17 +12,19 @@ pub mod unix; pub mod windows; /// Instructions to export the environment variables. -pub fn set_env(toolchain_dir: &Path) -> Result<(), Error> { +pub fn set_env(toolchain_dir: &Path, no_modify_env: bool) -> Result<(), Error> { #[cfg(windows)] - if cfg!(windows) { - windows::write_env_files(toolchain_dir)?; - windows::update_env()?; - } + windows::write_env_files(toolchain_dir)?; #[cfg(unix)] - if cfg!(unix) { - unix::write_env_files(toolchain_dir)?; + unix::write_env_files(toolchain_dir)?; + + if !no_modify_env { + #[cfg(windows)] + windows::update_env()?; + #[cfg(unix)] unix::update_env(toolchain_dir)?; } + Ok(()) } @@ -54,7 +56,7 @@ pub fn print_post_install_msg(toolchain_dir: &str, no_modify_env: bool) { ); #[cfg(windows)] println!( - "\t'. {}\\env.ps1' or '{}\\env.bat dependeing on your shell'", + "\t'. {}\\env.ps1' or '{}\\env.bat' depending on your shell'", toolchain_dir, toolchain_dir ); } diff --git a/src/env/windows.rs b/src/env/windows.rs index 64b196b2..949a1842 100644 --- a/src/env/windows.rs +++ b/src/env/windows.rs @@ -77,12 +77,20 @@ pub(super) fn update_env() -> Result<(), Error> { set_env_variable("LIBCLANG_PATH", &libclang_path)?; } + if let Ok(libclang_bin_path) = env::var("LIBCLANG_BIN_PATH") { + let libclang_bin_path: &str = &libclang_bin_path; + if !path.contains(libclang_bin_path) { + path = format!("{};{}", libclang_bin_path, path); + } + } + if let Ok(clang_path) = env::var("CLANG_PATH") { let clang_path: &str = &clang_path; if !path.contains(clang_path) { path = format!("{};{}", clang_path, path); } } + set_env_variable("PATH", &path)?; remove_legacy_export_file()?; diff --git a/src/toolchain/mod.rs b/src/toolchain/mod.rs index 70cd26ba..50c91823 100644 --- a/src/toolchain/mod.rs +++ b/src/toolchain/mod.rs @@ -245,9 +245,9 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> InstallMode::Install => info!("Installation successfully completed!"), InstallMode::Update => info!("Update successfully completed!"), } - if !args.no_modify_env { - set_env(&toolchain_dir)?; - } + + set_env(&toolchain_dir, args.no_modify_env)?; + print_post_install_msg(&toolchain_dir.display().to_string(), args.no_modify_env); Ok(()) From 24ed143bae14e0ef50261c875fed2b983754f8e1 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Sun, 22 Oct 2023 19:54:05 +0200 Subject: [PATCH 26/33] feat: Remove TODO --- src/env/shell.rs | 1 - src/toolchain/llvm.rs | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/env/shell.rs b/src/env/shell.rs index 2e14973a..da700272 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -114,7 +114,6 @@ impl WindowsShell for Batch { fn source_string(&self, toolchain_dir: &str) -> Result { Ok(format!(r#"{}/env.bat""#, toolchain_dir)) - // TODO: VERIFY THE SOURCE COMMAND } } diff --git a/src/toolchain/llvm.rs b/src/toolchain/llvm.rs index 081e8af0..bdaa2d34 100644 --- a/src/toolchain/llvm.rs +++ b/src/toolchain/llvm.rs @@ -55,7 +55,8 @@ impl Llvm { let llvm_path = format!( "{}/esp-clang/bin", self.path.to_str().unwrap().replace('/', "\\") - ); + ) + .replace('/', "\\"); #[cfg(unix)] let llvm_path = format!("{}/esp-clang/lib", self.path.to_str().unwrap()); llvm_path @@ -64,8 +65,8 @@ impl Llvm { /// Gets the binary path of clang fn get_bin_path(&self) -> String { #[cfg(windows)] - let llvm_path = - format!("{}/esp-clang/bin/clang.exe", self.path.to_str().unwrap()).replace('/', "\\"); + let llvm_path = format!("{}\\esp-clang\\bin\\clang.exe", self.path.to_str().unwrap()) + .replace('/', "\\"); #[cfg(unix)] let llvm_path = format!("{}/esp-clang/bin/clang", self.path.to_str().unwrap()); llvm_path From dd10bcfa68cc1752bcf85ffcf08bce4901d40083 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Mon, 23 Oct 2023 12:34:40 +0200 Subject: [PATCH 27/33] feat: Improve Windwos environment setup --- Cargo.lock | 1 + Cargo.toml | 1 + src/env/mod.rs | 2 +- src/env/windows.rs | 27 ++++++++++++++++++++++++++- src/toolchain/llvm.rs | 10 +++++++++- 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe1ffd01..83409736 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -522,6 +522,7 @@ dependencies = [ "tokio", "tokio-retry", "update-informer", + "winapi", "winreg 0.51.0", "xz2", "zip", diff --git a/Cargo.toml b/Cargo.toml index 1b2a7b6a..1aab7586 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ openssl = { version = "0.10.57", features = ["vendored"] } [target.'cfg(windows)'.dependencies] winreg = "0.51.0" +winapi = { version = "0.3.9", features = ["winuser"] } [dev-dependencies] assert_cmd = "2.0.12" diff --git a/src/env/mod.rs b/src/env/mod.rs index 29ab387c..c0ea0392 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -56,7 +56,7 @@ pub fn print_post_install_msg(toolchain_dir: &str, no_modify_env: bool) { ); #[cfg(windows)] println!( - "\t'. {}\\env.ps1' or '{}\\env.bat' depending on your shell'", + "\t'. {}\\env.ps1' or '{}\\env.bat' depending on your shell", toolchain_dir, toolchain_dir ); } diff --git a/src/env/windows.rs b/src/env/windows.rs index 949a1842..a8cddafc 100644 --- a/src/env/windows.rs +++ b/src/env/windows.rs @@ -23,7 +23,11 @@ pub(super) fn clean_env(_install_dir: &Path) -> Result<(), Error> { /// Deletes an environment variable for the current user. fn delete_env_variable(key: &str) -> Result<(), Error> { - if env::var(key).is_ok() { + let root = RegKey::predef(HKEY_CURRENT_USER); + let environment = root.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)?; + + let reg_value = environment.get_raw_value(key); + if reg_value.is_err() { return Ok(()); } @@ -37,11 +41,32 @@ fn delete_env_variable(key: &str) -> Result<(), Error> { /// Sets an environment variable for the current user. fn set_env_variable(key: &str, value: &str) -> Result<(), Error> { + use std::ptr; + use winapi::shared::minwindef::*; + use winapi::um::winuser::{ + SendMessageTimeoutA, HWND_BROADCAST, SMTO_ABORTIFHUNG, WM_SETTINGCHANGE, + }; + env::set_var(key, value); let hkcu = RegKey::predef(HKEY_CURRENT_USER); let environment_key = hkcu.open_subkey_with_flags("Environment", KEY_WRITE)?; environment_key.set_value(key, &value)?; + + // Tell other processes to update their environment + #[allow(clippy::unnecessary_cast)] + unsafe { + SendMessageTimeoutA( + HWND_BROADCAST, + WM_SETTINGCHANGE, + 0 as WPARAM, + "Environment\0".as_ptr() as LPARAM, + SMTO_ABORTIFHUNG, + 5000, + ptr::null_mut(), + ); + } + Ok(()) } diff --git a/src/toolchain/llvm.rs b/src/toolchain/llvm.rs index bdaa2d34..0774262d 100644 --- a/src/toolchain/llvm.rs +++ b/src/toolchain/llvm.rs @@ -129,7 +129,7 @@ impl Llvm { if cfg!(windows) { env::remove_var("LIBCLANG_PATH"); env::remove_var("CLANG_PATH"); - let updated_path = env::var("PATH").unwrap().replace( + let mut updated_path = env::var("PATH").unwrap().replace( &format!( "{}\\{}\\esp-clang\\bin;", llvm_path.display().to_string().replace('/', "\\"), @@ -137,6 +137,14 @@ impl Llvm { ), "", ); + updated_path = updated_path.replace( + &format!( + "{}\\{}\\esp-clang\\bin;", + llvm_path.display().to_string().replace('/', "\\"), + DEFAULT_LLVM_16_VERSION, + ), + "", + ); env::set_var("PATH", updated_path); } remove_dir_all(&llvm_path) From 12af08bb5eaa547937ec2b20cfa83225880d5138 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Mon, 23 Oct 2023 14:38:36 +0200 Subject: [PATCH 28/33] docs: Update docstrings --- src/cli.rs | 2 ++ src/env/mod.rs | 4 ++++ src/env/shell.rs | 24 ++++++++++++++---------- src/env/unix.rs | 10 ++++++---- src/env/windows.rs | 8 +++++--- 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 9d9f6fc4..fa1170b0 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,3 +1,5 @@ +//! Command line interface. + use crate::targets::{parse_targets, Target}; use clap::Parser; use clap_complete::Shell; diff --git a/src/env/mod.rs b/src/env/mod.rs index c0ea0392..4262f9fc 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -28,10 +28,12 @@ pub fn set_env(toolchain_dir: &Path, no_modify_env: bool) -> Result<(), Error> { Ok(()) } +/// Get the home directory. pub fn get_home_dir() -> PathBuf { BaseDirs::new().unwrap().home_dir().to_path_buf() } +/// Clean the environment variables. pub fn clean_env(install_dir: &Path) -> Result<(), Error> { #[cfg(windows)] windows::clean_env(install_dir)?; @@ -40,6 +42,8 @@ pub fn clean_env(install_dir: &Path) -> Result<(), Error> { Ok(()) } + +/// Print to post installation message pub fn print_post_install_msg(toolchain_dir: &str, no_modify_env: bool) { if no_modify_env { println!( diff --git a/src/env/shell.rs b/src/env/shell.rs index da700272..672f4111 100644 --- a/src/env/shell.rs +++ b/src/env/shell.rs @@ -75,8 +75,8 @@ impl ShellScript { } } -// Cross-platform non-POSIX shells have not been assessed for integration yet #[cfg(unix)] +/// Cross-platform non-POSIX shells have not been assessed for integration yet fn enumerate_shells() -> Vec { vec![ Box::new(Posix), @@ -87,16 +87,17 @@ fn enumerate_shells() -> Vec { } #[cfg(unix)] +/// Returns all shells that exist on the system. pub(super) fn get_available_shells() -> impl Iterator { enumerate_shells().into_iter().filter(|sh| sh.does_exist()) } #[cfg(windows)] pub trait WindowsShell { - // Writes the relevant env file. + /// Writes the relevant env file. fn env_script(&self, toolchain_dir: &Path) -> ShellScript; - // Gives the source string for a given shell. + /// Gives the source string for a given shell. fn source_string(&self, toolchain_dir: &str) -> Result; } @@ -136,18 +137,18 @@ impl WindowsShell for Powershell { #[cfg(unix)] pub trait UnixShell { - // Detects if a shell "exists". Users have multiple shells, so an "eager" - // heuristic should be used, assuming shells exist if any traces do. + /// Detects if a shell "exists". Users have multiple shells, so an "eager" + /// heuristic should be used, assuming shells exist if any traces do. fn does_exist(&self) -> bool; - // Gives all rcfiles of a given shell that Rustup is concerned with. - // Used primarily in checking rcfiles for cleanup. + /// Gives all rcfiles of a given shell that Rustup is concerned with. + /// Used primarily in checking rcfiles for cleanup. fn rcfiles(&self) -> Vec; - // Gives rcs that should be written to. + /// Gives rcs that should be written to. fn update_rcs(&self) -> Vec; - // Writes the relevant env file. + /// Writes the relevant env file. fn env_script(&self, toolchain_dir: &Path) -> ShellScript { ShellScript { name: "env", @@ -156,7 +157,7 @@ pub trait UnixShell { } } - // Gives the source string for a given shell. + /// Gives the source string for a given shell. fn source_string(&self, toolchain_dir: &str) -> Result { Ok(format!(r#". "{}/env""#, toolchain_dir)) } @@ -303,11 +304,13 @@ impl UnixShell for Fish { } #[cfg(unix)] +/// Finds the command for a given string. pub(crate) fn find_cmd<'a>(cmds: &[&'a str]) -> Option<&'a str> { cmds.iter().cloned().find(|&s| has_cmd(s)) } #[cfg(unix)] +/// Checks if a command exists in the PATH. fn has_cmd(cmd: &str) -> bool { let cmd = format!("{}{}", cmd, env::consts::EXE_SUFFIX); let path = env::var("PATH").unwrap_or_default(); @@ -316,6 +319,7 @@ fn has_cmd(cmd: &str) -> bool { .any(|p| p.exists()) } +/// Writes a file to a given path. pub fn write_file(path: &Path, contents: &str) -> Result<(), Error> { let mut file = OpenOptions::new() .write(true) diff --git a/src/env/unix.rs b/src/env/unix.rs index 5ee5d5f2..a0531008 100644 --- a/src/env/unix.rs +++ b/src/env/unix.rs @@ -1,3 +1,5 @@ +//! Unix specific environment functions. + use crate::{env::get_home_dir, env::shell, error::Error}; use miette::Result; use std::{ @@ -8,7 +10,7 @@ use std::{ const LEGACY_EXPORT_FILE: &str = "export-esp.sh"; -// Clean the environment for Windows. +/// Clean the environment for Windows. pub(super) fn clean_env(toolchain_dir: &Path) -> Result<(), Error> { for sh in shell::get_available_shells() { let source_bytes = format!( @@ -50,7 +52,7 @@ pub(super) fn clean_env(toolchain_dir: &Path) -> Result<(), Error> { Ok(()) } -// Delete the legacy export file. +/// Delete the legacy export file. fn remove_legacy_export_file() -> Result<(), Error> { let legacy_file = get_home_dir().join(LEGACY_EXPORT_FILE); if legacy_file.exists() { @@ -60,7 +62,7 @@ fn remove_legacy_export_file() -> Result<(), Error> { Ok(()) } -// Update the environment for Unix. +/// Update the environment for Unix. pub(crate) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { for sh in shell::get_available_shells() { let source_cmd = sh.source_string(&toolchain_dir.display().to_string())?; @@ -94,7 +96,7 @@ pub(crate) fn update_env(toolchain_dir: &Path) -> Result<(), Error> { Ok(()) } -// Write the environment files for Unix. +/// Write the environment files for Unix. pub(super) fn write_env_files(toolchain_dir: &Path) -> Result<(), Error> { let mut written = vec![]; diff --git a/src/env/windows.rs b/src/env/windows.rs index a8cddafc..6d64181a 100644 --- a/src/env/windows.rs +++ b/src/env/windows.rs @@ -1,3 +1,5 @@ +//! Windows specific environment functions. + use crate::{env::get_home_dir, env::shell, error::Error}; use miette::Result; use std::{env, fs::remove_file, path::Path}; @@ -8,7 +10,7 @@ use winreg::{ const LEGACY_EXPORT_FILE: &str = "export-esp.ps1"; -// Clean the environment for Windows. +/// Clean the environment for Windows. pub(super) fn clean_env(_install_dir: &Path) -> Result<(), Error> { delete_env_variable("LIBCLANG_PATH")?; delete_env_variable("CLANG_PATH")?; @@ -70,7 +72,7 @@ fn set_env_variable(key: &str, value: &str) -> Result<(), Error> { Ok(()) } -// Delete the legacy export file. +/// Delete the legacy export file. fn remove_legacy_export_file() -> Result<(), Error> { let legacy_file = get_home_dir().join(LEGACY_EXPORT_FILE); if legacy_file.exists() { @@ -123,7 +125,7 @@ pub(super) fn update_env() -> Result<(), Error> { Ok(()) } -// Write the environment files for Windows. +/// Write the environment files for Windows. pub(super) fn write_env_files(toolchain_dir: &Path) -> Result<(), Error> { let windows_shells: Vec = vec![Box::new(shell::Batch), Box::new(shell::Powershell)]; From 742905ee0cced66eb7896af2f8c1ae3637a1592d Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Mon, 23 Oct 2023 13:54:25 +0000 Subject: [PATCH 29/33] fix: Avoid error if the install_dir does not exists --- src/main.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 349db384..9004d84c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,12 +74,14 @@ async fn uninstall(args: UninstallOpts) -> Result<()> { uninstall_gcc_toolchains(&install_dir)?; - info!( - "Deleting the Xtensa Rust toolchain located in '{}'", - &install_dir.display() - ); - remove_dir_all(&install_dir) - .map_err(|_| Error::RemoveDirectory(install_dir.display().to_string()))?; + if install_dir.exists() { + info!( + "Deleting the Xtensa Rust toolchain located in '{}'", + &install_dir.display() + ); + remove_dir_all(&install_dir) + .map_err(|_| Error::RemoveDirectory(install_dir.display().to_string()))?; + } clean_env(&install_dir)?; From 91f6f1842e35e725effa416118bf3d8b3f55315f Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Mon, 23 Oct 2023 14:08:17 +0000 Subject: [PATCH 30/33] refactor: Update logs to make them less verbose --- src/toolchain/gcc.rs | 1 + src/toolchain/mod.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/toolchain/gcc.rs b/src/toolchain/gcc.rs index 686da4d1..82046cf1 100644 --- a/src/toolchain/gcc.rs +++ b/src/toolchain/gcc.rs @@ -58,6 +58,7 @@ impl Gcc { impl Installable for Gcc { async fn install(&self) -> Result<(), Error> { let extension = get_artifact_extension(&self.host_triple); + info!("Installing GCC ({})", self.arch); debug!("GCC path: {}", self.path.display()); if self.path.exists() { warn!( diff --git a/src/toolchain/mod.rs b/src/toolchain/mod.rs index 50c91823..3c84c4bd 100644 --- a/src/toolchain/mod.rs +++ b/src/toolchain/mod.rs @@ -63,11 +63,11 @@ pub(super) async fn download_file( ); remove_file(&file_path)?; } else if !Path::new(&output_directory).exists() { - info!("Creating directory: '{}'", output_directory); + debug!("Creating directory: '{}'", output_directory); create_dir_all(output_directory) .map_err(|_| Error::CreateDirectory(output_directory.to_string()))?; } - info!("Downloading file '{}' from '{}'", &file_path, url); + info!("Downloading '{}'", &file_name); let resp = reqwest::get(&url).await?; let bytes = resp.bytes().await?; if uncompress { @@ -101,7 +101,7 @@ pub(super) async fn download_file( } } "gz" => { - info!("Extracting tar.gz file to '{}'", output_directory); + debug!("Extracting tar.gz file to '{}'", output_directory); let bytes = bytes.to_vec(); let tarfile = GzDecoder::new(bytes.as_slice()); @@ -109,7 +109,7 @@ pub(super) async fn download_file( archive.unpack(output_directory)?; } "xz" => { - info!("Extracting tar.xz file to '{}'", output_directory); + debug!("Extracting tar.xz file to '{}'", output_directory); let bytes = bytes.to_vec(); let tarfile = XzDecoder::new(bytes.as_slice()); let mut archive = Archive::new(tarfile); @@ -120,7 +120,7 @@ pub(super) async fn download_file( } } } else { - info!("Creating file: '{}'", file_path); + debug!("Creating file: '{}'", file_path); let mut out = File::create(&file_path)?; out.write_all(&bytes)?; } @@ -255,7 +255,7 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()> /// Queries the GitHub API and returns the JSON response. pub(super) fn github_query(url: &str) -> Result { - info!("Querying GitHub API: '{}'", url); + debug!("Querying GitHub API: '{}'", url); let mut headers = header::HeaderMap::new(); headers.insert(header::USER_AGENT, "espup".parse().unwrap()); headers.insert( From c3107035d42e1dd4af0dd56b2f99ed7328b708bb Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Mon, 23 Oct 2023 14:30:33 +0000 Subject: [PATCH 31/33] docs: Update readme --- README.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 638f7d17..acc170e4 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,11 @@ Options: ### Install Subcommand +> **Warning** +> +> #### Environment modification +> Installation will, by default, modify your environment. If you dont want `espup` to modify your environment, please use the `--no-modify-env` argument. + > **Note** > > #### Xtensa Rust destination path @@ -125,7 +130,7 @@ Options: > **Note** > > #### GitHub API -> During the installation process, several GitHub queries are made, [which are subject to certain limits](https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#rate-limiting). Our number of queries should not hit the limits unless you are running `espup install` command numerous times in a short span of time. We recommend setting the [`GITHUB_TOKEN` environment variable](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret) when using `espup` in CI, if you want to use `espup` on CI, recommend using it via the [`xtensa-toolchain` action](https://github.com/esp-rs/xtensa-toolchain/), and making sure `GITHUB_TOKEN` is not set when using it on a host machine. See https://github.com/esp-rs/xtensa-toolchain/issues/15 for more details on this. +> During the installation process, several GitHub queries are made, [which are subject to certain limits](https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#rate-limiting). Our number of queries should not hit the limit unless you are running `espup install` command numerous times in a short span of time. We recommend setting the [`GITHUB_TOKEN` environment variable](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret) when using `espup` in CI, if you want to use `espup` on CI, recommend using it via the [`xtensa-toolchain` action](https://github.com/esp-rs/xtensa-toolchain/), and making sure `GITHUB_TOKEN` is not set when using it on a host machine. See https://github.com/esp-rs/xtensa-toolchain/issues/15 for more details on this. ``` Usage: espup install [OPTIONS] @@ -136,9 +141,6 @@ 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] - -f, --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) - -e, --extended-llvm Extends the LLVM installation. @@ -160,6 +162,9 @@ Options: [default: nightly] + -o, --no-modify-env + Don't configure environment variables + -k, --skip-version-parse Skips parsing Xtensa Rust version @@ -202,9 +207,6 @@ 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] - -f, --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) - -e, --extended-llvm Extends the LLVM installation. @@ -226,6 +228,9 @@ Options: [default: nightly] + -o, --no-modify-env + Don't configure environment variables + -k, --skip-version-parse Skips parsing Xtensa Rust version From af750a1e525cc903da38e867e549d0d4c4c658d5 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Mon, 23 Oct 2023 14:31:21 +0000 Subject: [PATCH 32/33] docs: Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b742263..b050393c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add automatically sourcing the environment files +- Setup user environment (#381) ### Fixed From 19f75130aa7928fedf0c2e5a8f110c278d005bd4 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Date: Thu, 2 Nov 2023 16:14:19 +0100 Subject: [PATCH 33/33] fix: Remove leftover comment --- src/toolchain/llvm.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/toolchain/llvm.rs b/src/toolchain/llvm.rs index 0774262d..c25bcfb6 100644 --- a/src/toolchain/llvm.rs +++ b/src/toolchain/llvm.rs @@ -177,7 +177,6 @@ impl Installable for Llvm { #[cfg(windows)] env::set_var("LIBCLANG_BIN_PATH", self.get_lib_path()); env::set_var("LIBCLANG_PATH", self.get_lib_path()); - // } if self.extended { env::set_var("CLANG_PATH", self.get_bin_path()); }