Add persistent storage of installed toolchains (#3797)

Extends #3726 

Moves toolchain storage out of `UV_BOOTSTRAP_DIR` (`./bin`) into the
proper user data directory as defined by #3726.

Replaces `UV_BOOTSTRAP_DIR` with `UV_TOOLCHAIN_DIR` for customization.
Installed toolchains will be discovered without opt-in, but the idea is
still that these are not yet user-facing.
This commit is contained in:
Zanie Blue 2024-05-26 23:54:49 -04:00 committed by GitHub
parent 4191c331a7
commit 30e780e1dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 448 additions and 210 deletions

View file

@ -14,7 +14,7 @@ use tracing::{info, info_span, Instrument};
use uv_fs::Simplified;
use uv_interpreter::managed::{
DownloadResult, Error, PythonDownload, PythonDownloadRequest, TOOLCHAIN_DIRECTORY,
DownloadResult, Error, InstalledToolchains, PythonDownload, PythonDownloadRequest,
};
#[derive(Parser, Debug)]
@ -25,13 +25,8 @@ pub(crate) struct FetchPythonArgs {
pub(crate) async fn fetch_python(args: FetchPythonArgs) -> Result<()> {
let start = Instant::now();
let bootstrap_dir = TOOLCHAIN_DIRECTORY.clone().unwrap_or_else(|| {
std::env::current_dir()
.expect("Use `UV_BOOTSTRAP_DIR` if the current directory is not usable.")
.join("bin")
});
fs_err::create_dir_all(&bootstrap_dir)?;
let toolchains = InstalledToolchains::from_settings()?.init()?;
let toolchain_dir = toolchains.root();
let versions = if args.versions.is_empty() {
info!("Reading versions from file...");
@ -61,7 +56,7 @@ pub(crate) async fn fetch_python(args: FetchPythonArgs) -> Result<()> {
let mut tasks = futures::stream::iter(downloads.iter())
.map(|download| {
async {
let result = download.fetch(&client, &bootstrap_dir).await;
let result = download.fetch(&client, toolchain_dir).await;
(download.python_version(), result)
}
.instrument(info_span!("download", key = %download))
@ -98,7 +93,7 @@ pub(crate) async fn fetch_python(args: FetchPythonArgs) -> Result<()> {
};
// Order matters here, as we overwrite previous links
info!("Installing to `{}`...", bootstrap_dir.user_display());
info!("Installing to `{}`...", toolchain_dir.user_display());
// On Windows, linking the executable generally results in broken installations
// and each toolchain path will need to be added to the PATH separately in the
@ -110,10 +105,10 @@ pub(crate) async fn fetch_python(args: FetchPythonArgs) -> Result<()> {
// TODO(zanieb): This path should be a part of the download metadata
let executable = path.join("install").join("bin").join("python3");
for target in [
bootstrap_dir.join(format!("python{}", version.python_full_version())),
bootstrap_dir.join(format!("python{}.{}", version.major(), version.minor())),
bootstrap_dir.join(format!("python{}", version.major())),
bootstrap_dir.join("python"),
toolchain_dir.join(format!("python{}", version.python_full_version())),
toolchain_dir.join(format!("python{}.{}", version.major(), version.minor())),
toolchain_dir.join(format!("python{}", version.major())),
toolchain_dir.join("python"),
] {
// Attempt to remove it, we'll fail on link if we couldn't remove it for some reason
// but if it's missing we don't want to error
@ -132,10 +127,6 @@ pub(crate) async fn fetch_python(args: FetchPythonArgs) -> Result<()> {
};
info!("Installed {} versions", requests.len());
info!(
r#"To enable discovery: export UV_BOOTSTRAP_DIR="{}""#,
bootstrap_dir.display()
);
Ok(())
}