|
3 | 3 | use std::env::consts::EXE_SUFFIX;
|
4 | 4 | use std::io;
|
5 | 5 | use std::io::{BufWriter, Write};
|
6 |
| -use std::path::Path; |
| 6 | +use std::path::{Path, PathBuf}; |
7 | 7 |
|
8 | 8 | use fs_err as fs;
|
9 | 9 | use fs_err::File;
|
10 | 10 | use itertools::Itertools;
|
11 | 11 | use tracing::debug;
|
12 |
| - |
13 |
| -use uv_fs::{cachedir, Simplified, CWD}; |
| 12 | +use uv_cache::Cache; |
| 13 | +use uv_fs::{cachedir, normalize_absolute_path, Simplified, CWD}; |
14 | 14 | use uv_pypi_types::Scheme;
|
15 | 15 | use uv_python::{Interpreter, VirtualEnvironment};
|
16 | 16 | use uv_version::version;
|
@@ -52,15 +52,40 @@ pub(crate) fn create(
|
52 | 52 | allow_existing: bool,
|
53 | 53 | relocatable: bool,
|
54 | 54 | seed: bool,
|
| 55 | + cache: &Cache, |
55 | 56 | ) -> Result<VirtualEnvironment, Error> {
|
56 | 57 | // Determine the base Python executable; that is, the Python executable that should be
|
57 | 58 | // considered the "base" for the virtual environment. This is typically the Python executable
|
58 | 59 | // from the [`Interpreter`]; however, if the interpreter is a virtual environment itself, then
|
59 | 60 | // the base Python executable is the Python executable of the interpreter's base interpreter.
|
60 | 61 | let base_python = if cfg!(unix) {
|
61 |
| - // On Unix, follow symlinks to resolve the base interpreter, since the Python executable in |
62 |
| - // a virtual environment is a symlink to the base interpreter. |
63 |
| - uv_fs::canonicalize_executable(interpreter.sys_executable())? |
| 62 | + // If we're in virtual environment, resolve symlinks until we find a non-virtual interpreter. |
| 63 | + if interpreter.is_virtualenv() { |
| 64 | + if let Some(base_executable) = |
| 65 | + uv_fs::read_executable_link(interpreter.sys_executable())? |
| 66 | + { |
| 67 | + let mut base_interpreter = Interpreter::query(base_executable, cache)?; |
| 68 | + while base_interpreter.is_virtualenv() { |
| 69 | + let Some(base_executable) = |
| 70 | + uv_fs::read_executable_link(base_interpreter.sys_executable())? |
| 71 | + else { |
| 72 | + break; |
| 73 | + }; |
| 74 | + base_interpreter = Interpreter::query(base_executable, cache)?; |
| 75 | + } |
| 76 | + base_interpreter.sys_executable().to_path_buf() |
| 77 | + } else { |
| 78 | + // If the interpreter isn't a symlink, use `sys._base_executable` or, as a last |
| 79 | + // resort, `sys.executable`. |
| 80 | + if let Some(base_executable) = interpreter.sys_base_executable() { |
| 81 | + base_executable.to_path_buf() |
| 82 | + } else { |
| 83 | + interpreter.sys_executable().to_path_buf() |
| 84 | + } |
| 85 | + } |
| 86 | + } else { |
| 87 | + interpreter.sys_executable().to_path_buf() |
| 88 | + } |
64 | 89 | } else if cfg!(windows) {
|
65 | 90 | // On Windows, follow `virtualenv`. If we're in a virtual environment, use
|
66 | 91 | // `sys._base_executable` if it exists; if not, use `sys.base_prefix`. For example, with
|
|
0 commit comments