diff --git a/src/bin/juliainstaller.rs b/src/bin/juliainstaller.rs index 696dd5e6..6b5638d6 100644 --- a/src/bin/juliainstaller.rs +++ b/src/bin/juliainstaller.rs @@ -432,9 +432,9 @@ pub fn main() -> Result<()> { } } - let juliaupselfbin = install_choices.install_location.join("bin"); + let juliaupselfexecfolder = install_choices.install_location.join("bin"); - trace!("Set juliaupselfbin to `{:?}`", juliaupselfbin); + trace!("Set juliaupselfexecfolder to `{:?}`", juliaupselfexecfolder); println!("Now installing Juliaup"); @@ -447,10 +447,10 @@ pub fn main() -> Result<()> { })?; } - std::fs::create_dir_all(&juliaupselfbin).with_context(|| { + std::fs::create_dir_all(&juliaupselfexecfolder).with_context(|| { format!( "Failed to create install folder for Juliaup at `{}`.", - juliaupselfbin.display() + juliaupselfexecfolder.display() ) })?; @@ -473,7 +473,7 @@ pub fn main() -> Result<()> { ) })?; - download_extract_sans_parent(new_juliaup_url.as_ref(), &juliaupselfbin, 0)?; + download_extract_sans_parent(new_juliaup_url.as_ref(), &juliaupselfexecfolder, 0)?; { let new_selfconfig_data = JuliaupSelfConfig { @@ -526,7 +526,7 @@ pub fn main() -> Result<()> { ) })?; - paths.juliaupselfbin = juliaupselfbin.clone(); + paths.juliaupselfexecfolder = juliaupselfexecfolder.clone(); paths.juliaupselfconfig = self_config_path.clone(); } @@ -555,16 +555,15 @@ pub fn main() -> Result<()> { run_command_default(&args.default_channel, &paths) .with_context(|| "Failed to run `run_command_default`.")?; - let symlink_path = juliaupselfbin.join("julia"); + let symlink_path = juliaupselfexecfolder.join("julia"); - std::os::unix::fs::symlink(juliaupselfbin.join("julialauncher"), &symlink_path).with_context( - || { + std::os::unix::fs::symlink(juliaupselfexecfolder.join("julialauncher"), &symlink_path) + .with_context(|| { format!( "failed to create symlink `{}`.", symlink_path.to_string_lossy() ) - }, - )?; + })?; println!("Julia was successfully installed on your system."); diff --git a/src/bin/julialauncher.rs b/src/bin/julialauncher.rs index 20ebc462..61bd2419 100644 --- a/src/bin/julialauncher.rs +++ b/src/bin/julialauncher.rs @@ -7,7 +7,7 @@ use juliaup::config_file::{ load_config_db_lockfree, load_mut_config_db, save_config_db, JuliaupConfig, JuliaupConfigChannel, JuliaupConfigVersion, }; -use juliaup::global_paths::get_paths; +use juliaup::global_paths::{get_paths, GlobalPaths}; use juliaup::jsonstructs_versionsdb::JuliaupVersionDB; use juliaup::operations::{is_pr_channel, is_valid_channel}; use juliaup::utils::{print_juliaup_style, resolve_julia_binary_path, JuliaupMessageType}; @@ -37,25 +37,9 @@ pub struct UserError { msg: String, } -fn get_juliaup_path() -> Result { - let my_own_path = std::env::current_exe() - .with_context(|| "std::env::current_exe() did not find its own path.")? - .canonicalize() - .with_context(|| "Failed to canonicalize the path to the Julia launcher.")?; - - let juliaup_path = my_own_path - .parent() - .unwrap() // unwrap OK here because this can't happen - .join(format!("juliaup{}", std::env::consts::EXE_SUFFIX)); - - Ok(juliaup_path) -} - -fn do_initial_setup(juliaupconfig_path: &Path) -> Result<()> { +fn do_initial_setup(juliaupconfig_path: &Path, path: &GlobalPaths) -> Result<()> { if !juliaupconfig_path.exists() { - let juliaup_path = get_juliaup_path().with_context(|| "Failed to obtain juliaup path.")?; - - std::process::Command::new(juliaup_path) + std::process::Command::new(&path.juliaupselfexec) .arg("46029ef5-0b73-4a71-bff3-d0d05de42aac") // This is our internal command to do the initial setup .status() .with_context(|| "Failed to start juliaup for the initial setup.")?; @@ -65,6 +49,7 @@ fn do_initial_setup(juliaupconfig_path: &Path) -> Result<()> { fn run_versiondb_update( config_file: &juliaup::config_file::JuliaupReadonlyConfigFile, + paths: &GlobalPaths, ) -> Result<()> { use chrono::Utc; use std::process::Stdio; @@ -82,10 +67,7 @@ fn run_versiondb_update( }; if should_run { - let juliaup_path = - get_juliaup_path().with_context(|| "Failed to obtain juliaup path.")?; - - std::process::Command::new(juliaup_path) + std::process::Command::new(&paths.juliaupselfexec) .args(["0cf1528f-0b15-46b1-9ac9-e5bf5ccccbcf"]) .stdout(Stdio::null()) .stderr(Stdio::null()) @@ -99,7 +81,10 @@ fn run_versiondb_update( } #[cfg(feature = "selfupdate")] -fn run_selfupdate(config_file: &juliaup::config_file::JuliaupReadonlyConfigFile) -> Result<()> { +fn run_selfupdate( + config_file: &juliaup::config_file::JuliaupReadonlyConfigFile, + paths: &GlobalPaths, +) -> Result<()> { use chrono::Utc; use std::process::Stdio; @@ -113,10 +98,7 @@ fn run_selfupdate(config_file: &juliaup::config_file::JuliaupReadonlyConfigFile) }; if should_run { - let juliaup_path = - get_juliaup_path().with_context(|| "Failed to obtain juliaup path.")?; - - std::process::Command::new(juliaup_path) + std::process::Command::new(&paths.juliaupselfexec) .args(["self", "update"]) .stdout(Stdio::null()) .stderr(Stdio::null()) @@ -130,7 +112,10 @@ fn run_selfupdate(config_file: &juliaup::config_file::JuliaupReadonlyConfigFile) } #[cfg(not(feature = "selfupdate"))] -fn run_selfupdate(_config_file: &juliaup::config_file::JuliaupReadonlyConfigFile) -> Result<()> { +fn run_selfupdate( + _config_file: &juliaup::config_file::JuliaupReadonlyConfigFile, + _paths: &GlobalPaths, +) -> Result<()> { Ok(()) } @@ -251,7 +236,7 @@ fn set_auto_install_preference( fn spawn_juliaup_add( channel: &str, - _paths: &juliaup::global_paths::GlobalPaths, + paths: &juliaup::global_paths::GlobalPaths, is_automatic: bool, ) -> Result<()> { if is_automatic { @@ -268,9 +253,7 @@ fn spawn_juliaup_add( ); } - let juliaup_path = get_juliaup_path().with_context(|| "Failed to obtain juliaup path.")?; - - let status = std::process::Command::new(juliaup_path) + let status = std::process::Command::new(&paths.juliaupselfexec) .args(["add", channel]) .status() .with_context(|| format!("Failed to spawn juliaup to install channel '{}'", channel))?; @@ -621,7 +604,7 @@ fn run_app() -> Result { let paths = get_paths().with_context(|| "Trying to load all global paths.")?; - do_initial_setup(&paths.juliaupconfig) + do_initial_setup(&paths.juliaupconfig, &paths) .with_context(|| "The Julia launcher failed to run the initial setup steps.")?; // Read the configuration without taking the configuration lock, so that @@ -745,10 +728,11 @@ fn run_app() -> Result { ctrlc::set_handler(|| ()) .with_context(|| "Failed to set the Ctrl-C handler.")?; - run_versiondb_update(&config_file) + run_versiondb_update(&config_file, &paths) .with_context(|| "Failed to run version db update")?; - run_selfupdate(&config_file).with_context(|| "Failed to run selfupdate.")?; + run_selfupdate(&config_file, &paths) + .with_context(|| "Failed to run selfupdate.")?; } Err(_) => panic!("Could not double-fork"), } @@ -809,9 +793,10 @@ fn run_app() -> Result { ) }; - run_versiondb_update(&config_file).with_context(|| "Failed to run version db update")?; + run_versiondb_update(&config_file, &paths) + .with_context(|| "Failed to run version db update")?; - run_selfupdate(&config_file).with_context(|| "Failed to run selfupdate.")?; + run_selfupdate(&config_file, &paths).with_context(|| "Failed to run selfupdate.")?; let status = child_process .wait() diff --git a/src/command_config_backgroundselfupdate.rs b/src/command_config_backgroundselfupdate.rs index a23926e8..bf2a90f1 100644 --- a/src/command_config_backgroundselfupdate.rs +++ b/src/command_config_backgroundselfupdate.rs @@ -33,7 +33,7 @@ pub fn run_command_config_backgroundselfupdate( match value { Some(value) => { - install_background_selfupdate(value).unwrap(); + install_background_selfupdate(value, paths).unwrap(); } None => { uninstall_background_selfupdate().unwrap(); diff --git a/src/command_config_modifypath.rs b/src/command_config_modifypath.rs index 5088ad30..4ed39a2e 100644 --- a/src/command_config_modifypath.rs +++ b/src/command_config_modifypath.rs @@ -25,7 +25,10 @@ pub fn run_command_config_modifypath( } if value { - add_binfolder_to_path_in_shell_scripts(&paths.juliaupselfbin, &paths.juliauphome)?; + add_binfolder_to_path_in_shell_scripts( + &paths.juliaupselfexecfolder, + &paths.juliauphome, + )?; } else { remove_binfolder_from_path_in_shell_scripts()?; } diff --git a/src/command_config_symlinks.rs b/src/command_config_symlinks.rs index 0b7de5f7..a8dce462 100644 --- a/src/command_config_symlinks.rs +++ b/src/command_config_symlinks.rs @@ -27,7 +27,7 @@ pub fn run_command_config_symlinks( if value { create_symlink(channel, &format!("julia-{}", channel_name), paths)?; } else { - remove_symlink(&format!("julia-{}", channel_name))?; + remove_symlink(&format!("julia-{}", channel_name), paths)?; } } } diff --git a/src/command_remove.rs b/src/command_remove.rs index 5150dd45..aca0fc56 100644 --- a/src/command_remove.rs +++ b/src/command_remove.rs @@ -75,7 +75,7 @@ pub fn run_command_remove(channel: &str, paths: &GlobalPaths) -> Result<()> { config_file.data.installed_channels.remove(channel); #[cfg(not(windows))] - remove_symlink(&format!("julia-{channel}"))?; + remove_symlink(&format!("julia-{channel}"), paths)?; garbage_collect_versions(false, &mut config_file.data, paths)?; diff --git a/src/command_selfuninstall.rs b/src/command_selfuninstall.rs index d7fddd03..464b701a 100644 --- a/src/command_selfuninstall.rs +++ b/src/command_selfuninstall.rs @@ -55,12 +55,17 @@ pub fn run_command_selfuninstall(paths: &crate::global_paths::GlobalPaths) -> Re Err(e) => eprintln!(" Failed: {e}."), }; - if paths.juliauphome != paths.juliaupselfhome { - let juliaup_binfolder_path = paths.juliaupselfhome.join("bin"); + let juliaupselfhome = paths + .juliaupselfexecfolder + .parent() + .ok_or_else(|| anyhow::anyhow!("Could not determine parent of own executable folder."))?; + + if paths.juliauphome != juliaupselfhome { + let juliaup_binfolder_path = paths.juliaupselfexecfolder.clone(); let julia_symlink_path = juliaup_binfolder_path.join("julia"); let julialauncher_path = juliaup_binfolder_path.join("julialauncher"); let juliaup_path = juliaup_binfolder_path.join("juliaup"); - let juliaup_config_path = paths.juliaupselfhome.join("juliaupself.json"); + let juliaup_config_path = paths.juliaupselfconfig.clone(); eprint!("Deleting julia symlink {}.", julia_symlink_path.display()); match std::fs::remove_file(&julia_symlink_path) { @@ -111,23 +116,19 @@ pub fn run_command_selfuninstall(paths: &crate::global_paths::GlobalPaths) -> Re Err(e) => eprintln!(" Failed: {e}."), }; - if paths - .juliaupselfhome + if juliaupselfhome .read_dir() .with_context(|| { format!( "Failed to read Juliaup folder `{}`.", - paths.juliaupselfhome.display() + juliaupselfhome.display() ) })? .next() .is_none() { - eprint!( - "Deleting the Juliaup folder {}.", - paths.juliaupselfhome.display() - ); - match std::fs::remove_dir(&paths.juliaupselfhome) { + eprint!("Deleting the Juliaup folder {}.", juliaupselfhome.display()); + match std::fs::remove_dir(juliaupselfhome) { Ok(_) => eprintln!(" Success."), Err(e) => { eprintln!( @@ -138,7 +139,7 @@ pub fn run_command_selfuninstall(paths: &crate::global_paths::GlobalPaths) -> Re } else { eprintln!( "The Juliaup folder {} is not empty, skipping removal of the entire Juliaup folder.", - paths.juliaupselfhome.display() + juliaupselfhome.display() ); } } diff --git a/src/command_selfupdate.rs b/src/command_selfupdate.rs index a069e97c..7f981328 100644 --- a/src/command_selfupdate.rs +++ b/src/command_selfupdate.rs @@ -8,7 +8,7 @@ pub fn run_command_selfupdate(paths: &GlobalPaths) -> Result<()> { use crate::operations::{download_extract_sans_parent, download_juliaup_version}; use crate::utils::get_juliaserver_base_url; use crate::{get_juliaup_target, get_own_version}; - use anyhow::{anyhow, bail}; + use anyhow::bail; update_version_db(&None, paths).with_context(|| "Failed to update versions db.")?; @@ -100,27 +100,17 @@ pub fn run_command_selfupdate(paths: &GlobalPaths) -> Result<()> { ) })?; - let my_own_path = std::env::current_exe() - .with_context(|| "Could not determine the path of the running exe.")?; - - let my_own_folder = my_own_path - .parent() - .ok_or_else(|| anyhow!("Could not determine parent."))?; - eprintln!( "Found new version {} on channel {}.", version, juliaup_channel ); - download_extract_sans_parent(new_juliaup_url.as_ref(), my_own_folder, 0)?; + download_extract_sans_parent(new_juliaup_url.as_ref(), &paths.juliaupselfexecfolder, 0)?; - let new_juliaup = my_own_folder.join(format!("juliaup{}", std::env::consts::EXE_SUFFIX)); - if let Err(e) = std::process::Command::new(&new_juliaup) + std::process::Command::new(&paths.juliaupselfexec) .arg("_post-update") .status() - { - eprintln!("Warning: post-update hook failed: {e}"); - } + .with_context(|| "Failed to run post-update hook.")?; eprintln!("Updated Juliaup to version {}.", version); } diff --git a/src/config_file.rs b/src/config_file.rs index 2c884cb5..0de7117a 100644 --- a/src/config_file.rs +++ b/src/config_file.rs @@ -750,6 +750,10 @@ mod tests { GlobalPaths { juliauphome: dir.to_path_buf(), juliaupconfig: dir.join("juliaup.json"), + juliaupselfexecfolder: dir.join("bin"), + juliaupselfexec: dir + .join("bin") + .join(format!("juliaup{}", std::env::consts::EXE_SUFFIX)), lockfile: dir.join(".juliaup-lock"), versiondb: dir.join("versiondb-test.json"), } diff --git a/src/global_paths.rs b/src/global_paths.rs index 9fe8c036..140da2d6 100644 --- a/src/global_paths.rs +++ b/src/global_paths.rs @@ -1,7 +1,5 @@ use crate::get_juliaup_target; -#[cfg(feature = "selfupdate")] -use anyhow::Context; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; use std::path::PathBuf; pub struct GlobalPaths { pub juliauphome: PathBuf, @@ -9,11 +7,9 @@ pub struct GlobalPaths { pub lockfile: PathBuf, pub versiondb: PathBuf, #[cfg(feature = "selfupdate")] - pub juliaupselfhome: PathBuf, - #[cfg(feature = "selfupdate")] pub juliaupselfconfig: PathBuf, - #[cfg(feature = "selfupdate")] - pub juliaupselfbin: PathBuf, + pub juliaupselfexecfolder: PathBuf, + pub juliaupselfexec: PathBuf, } fn get_juliaup_home_path() -> Result { @@ -56,16 +52,27 @@ fn get_default_juliaup_home_path() -> Result { pub fn get_paths() -> Result { let juliauphome = get_juliaup_home_path()?; - #[cfg(feature = "selfupdate")] let my_own_path = std::env::current_exe() .with_context(|| "Could not determine the path of the running exe.")?; - #[cfg(feature = "selfupdate")] - let juliaupselfbin = my_own_path + let canonicalized = if let Ok(canonical) = dunce::canonicalize(&my_own_path) { + canonical + } else { + my_own_path + }; + + let juliaupselfexecfolder = canonicalized .parent() .ok_or_else(|| anyhow!("Could not determine parent."))? .to_path_buf(); + let juliaupselfexec = + juliaupselfexecfolder.join(format!("juliaup{}", std::env::consts::EXE_SUFFIX)); + // `get_paths()` is called from both `julialauncher` and `juliaup`. When the + // launcher calls it, `current_exe()` returns the launcher's path, not + // juliaup's. they are siblings in the bin dir, so we go `parent()` to get + // the bin dir, then rejoin `juliaup` to find the juliaup binary. + let juliaupconfig = juliauphome.join("juliaup.json"); let versiondb = juliauphome.join(format!("versiondb-{}.json", get_juliaup_target())); @@ -73,15 +80,10 @@ pub fn get_paths() -> Result { let lockfile = juliauphome.join(".juliaup-lock"); #[cfg(feature = "selfupdate")] - let juliaupselfhome = my_own_path + let juliaupselfconfig = juliaupselfexecfolder .parent() - .ok_or_else(|| anyhow!("Failed to get path of folder of own executable."))? - .parent() - .ok_or_else(|| anyhow!("Failed to get parent path of folder of own executable."))? - .to_path_buf(); - - #[cfg(feature = "selfupdate")] - let juliaupselfconfig = juliaupselfhome.join("juliaupself.json"); + .ok_or_else(|| anyhow!("Failed to get parent path of own executable folder."))? + .join("juliaupself.json"); Ok(GlobalPaths { juliauphome, @@ -89,10 +91,8 @@ pub fn get_paths() -> Result { lockfile, versiondb, #[cfg(feature = "selfupdate")] - juliaupselfhome, - #[cfg(feature = "selfupdate")] juliaupselfconfig, - #[cfg(feature = "selfupdate")] - juliaupselfbin, + juliaupselfexecfolder, + juliaupselfexec, }) } diff --git a/src/operations.rs b/src/operations.rs index 1b7ad5cd..88d2deb4 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -13,7 +13,7 @@ use crate::get_juliaup_target; use crate::global_paths::GlobalPaths; use crate::jsonstructs_versionsdb::JuliaupVersionDB; use crate::utils::check_server_supports_nightlies; -use crate::utils::get_bin_dir; +use crate::utils::get_channel_link_dir; use crate::utils::get_julianightlies_base_url; use crate::utils::get_juliaprs_base_url; use crate::utils::get_juliaserver_base_url; @@ -759,11 +759,7 @@ pub fn download_version_to_temp( // TODO At some point we could put this behind a conditional compile, we know // that we don't ship a bundled version for some platforms. let full_version_string_of_bundled_version = get_bundled_julia_version(); - let my_own_path = std::env::current_exe()?; - let path_of_bundled_version = my_own_path - .parent() - .unwrap() // unwrap OK because we can't get a path that does not have a parent - .join("BundledJulia"); + let path_of_bundled_version = paths.juliaupselfexecfolder.join("BundledJulia"); if fullversion == full_version_string_of_bundled_version && path_of_bundled_version.exists() { let mut options = fs_extra::dir::CopyOptions::new(); @@ -1645,7 +1641,7 @@ pub fn garbage_collect_versions( } } for channel in channels_to_uninstall { - remove_symlink(&format!("julia-{}", channel))?; + remove_symlink(&format!("julia-{}", channel), paths)?; config_data.installed_channels.remove(&channel); } } @@ -1688,8 +1684,8 @@ fn _remove_symlink(symlink_path: &Path) -> Result> { Ok(None) } -pub fn remove_symlink(symlink_name: &String) -> Result<()> { - let symlink_path = get_bin_dir() +pub fn remove_symlink(symlink_name: &String, paths: &GlobalPaths) -> Result<()> { + let symlink_path = get_channel_link_dir(paths) .with_context(|| "Failed to retrieve binary directory while trying to remove a symlink.")? .join(symlink_name); @@ -1846,7 +1842,7 @@ pub fn create_symlink( symlink_name: &String, paths: &GlobalPaths, ) -> Result<()> { - let symlink_folder = get_bin_dir() + let symlink_folder = get_channel_link_dir(paths) .with_context(|| "Failed to retrieve binary directory while trying to create a symlink.")?; let symlink_path = symlink_folder.join(symlink_name); @@ -1892,14 +1888,11 @@ pub fn create_symlink(_: &JuliaupConfigChannel, _: &String, _paths: &GlobalPaths } #[cfg(feature = "selfupdate")] -pub fn install_background_selfupdate(interval: i64) -> Result<()> { +pub fn install_background_selfupdate(interval: i64, paths: &GlobalPaths) -> Result<()> { use itertools::Itertools; use std::process::Stdio; - let own_exe_path = std::env::current_exe() - .with_context(|| "Could not determine the path of the running exe.")?; - - let my_own_path = own_exe_path.to_str().unwrap(); + let my_own_path = paths.juliaupselfexec.to_str().unwrap(); match std::env::var("WSL_DISTRO_NAME") { // This is the WSL case, where we schedule a Windows task to do the update diff --git a/src/utils.rs b/src/utils.rs index a69ef9ff..5715a868 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,5 @@ -use anyhow::{anyhow, bail, Context, Result}; +use crate::global_paths::GlobalPaths; +use anyhow::{bail, Context, Result}; use console::style; use retry::{ delay::{jitter, Fibonacci}, @@ -374,7 +375,7 @@ pub fn get_juliaprs_base_url() -> Result { Ok(parsed_url) } -pub fn get_bin_dir() -> Result { +pub fn get_channel_link_dir(paths: &GlobalPaths) -> Result { let entry_sep = if std::env::consts::OS == "windows" { ';' } else { @@ -392,11 +393,7 @@ pub fn get_bin_dir() -> Result { path } Err(_) => { - let mut path = std::env::current_exe() - .with_context(|| "Could not determine the path of the running exe.")? - .parent() - .ok_or_else(|| anyhow!("Could not determine parent."))? - .to_path_buf(); + let mut path = paths.juliaupselfexecfolder.clone(); if let Some(home_dir) = dirs::home_dir() { if !path.starts_with(&home_dir) {