mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-03 18:38:21 +00:00
Patch embedded install path for Python dylib on macOS during python install
(#10629)
## Summary Fixes #10598 ## Test Plan Looking for input here @zanieb. How/where would you include tests for this? More broadly: do we want a failure to perform the rename to be a hard error? Or should it start out as a warning? --------- Co-authored-by: Zanie Blue <contact@zanie.dev>
This commit is contained in:
parent
04fc36f066
commit
1af02ce8f2
6 changed files with 141 additions and 2 deletions
|
@ -165,6 +165,9 @@ impl PythonInstallation {
|
|||
installed.ensure_externally_managed()?;
|
||||
installed.ensure_sysconfig_patched()?;
|
||||
installed.ensure_canonical_executables()?;
|
||||
if let Err(e) = installed.ensure_dylib_patched() {
|
||||
e.warn_user(&installed);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
source: PythonSource::Managed,
|
||||
|
|
|
@ -30,6 +30,7 @@ mod implementation;
|
|||
mod installation;
|
||||
mod interpreter;
|
||||
mod libc;
|
||||
pub mod macos_dylib;
|
||||
pub mod managed;
|
||||
#[cfg(windows)]
|
||||
mod microsoft_store;
|
||||
|
|
63
crates/uv-python/src/macos_dylib.rs
Normal file
63
crates/uv-python/src/macos_dylib.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use std::{io::ErrorKind, path::PathBuf};
|
||||
|
||||
use uv_fs::Simplified as _;
|
||||
use uv_warnings::warn_user;
|
||||
|
||||
use crate::managed::ManagedPythonInstallation;
|
||||
|
||||
pub fn patch_dylib_install_name(dylib: PathBuf) -> Result<(), Error> {
|
||||
let output = match std::process::Command::new("install_name_tool")
|
||||
.arg("-id")
|
||||
.arg(&dylib)
|
||||
.arg(&dylib)
|
||||
.output()
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(e) => {
|
||||
let e = if e.kind() == ErrorKind::NotFound {
|
||||
Error::MissingInstallNameTool
|
||||
} else {
|
||||
e.into()
|
||||
};
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
|
||||
return Err(Error::RenameError { dylib, stderr });
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("`install_name_tool` is not available on this system.
|
||||
This utility is part of macOS Developer Tools. Please ensure that the Xcode Command Line Tools are installed by running:
|
||||
|
||||
xcode-select --install
|
||||
|
||||
For more information, see: https://developer.apple.com/xcode/")]
|
||||
MissingInstallNameTool,
|
||||
#[error("Failed to update the install name of the Python dynamic library located at `{}`", dylib.user_display())]
|
||||
RenameError { dylib: PathBuf, stderr: String },
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Emit a user-friendly warning about the patching failure.
|
||||
pub fn warn_user(&self, installation: &ManagedPythonInstallation) {
|
||||
let error = if tracing::enabled!(tracing::Level::DEBUG) {
|
||||
format!("\nUnderlying error: {self}")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
warn_user!(
|
||||
"Failed to patch the install name of the dynamic library for {}. This may cause issues when building Python native extensions.{}",
|
||||
installation.executable().simplified_display(),
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
|
@ -25,7 +25,8 @@ use crate::libc::LibcDetectionError;
|
|||
use crate::platform::Error as PlatformError;
|
||||
use crate::platform::{Arch, Libc, Os};
|
||||
use crate::python_version::PythonVersion;
|
||||
use crate::{sysconfig, PythonRequest, PythonVariant};
|
||||
use crate::{macos_dylib, sysconfig, PythonRequest, PythonVariant};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
|
@ -88,6 +89,8 @@ pub enum Error {
|
|||
NameParseError(#[from] installation::PythonInstallationKeyError),
|
||||
#[error(transparent)]
|
||||
LibcDetection(#[from] LibcDetectionError),
|
||||
#[error(transparent)]
|
||||
MacOsDylib(#[from] macos_dylib::Error),
|
||||
}
|
||||
/// A collection of uv-managed Python installations installed on the current system.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
@ -508,6 +511,28 @@ impl ManagedPythonInstallation {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// On macOS, ensure that the `install_name` for the Python dylib is set
|
||||
/// correctly, rather than pointing at `/install/lib/libpython{version}.dylib`.
|
||||
/// This is necessary to ensure that native extensions written in Rust
|
||||
/// link to the correct location for the Python library.
|
||||
///
|
||||
/// See <https://github.com/astral-sh/uv/issues/10598> for more information.
|
||||
pub fn ensure_dylib_patched(&self) -> Result<(), macos_dylib::Error> {
|
||||
if cfg!(target_os = "macos") {
|
||||
if *self.implementation() == ImplementationName::CPython {
|
||||
let dylib_path = self.python_dir().join("lib").join(format!(
|
||||
"{}python{}{}{}",
|
||||
std::env::consts::DLL_PREFIX,
|
||||
self.key.version().python_version(),
|
||||
self.key.variant().suffix(),
|
||||
std::env::consts::DLL_SUFFIX
|
||||
));
|
||||
macos_dylib::patch_dylib_install_name(dylib_path)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a link to the managed Python executable.
|
||||
///
|
||||
/// If the file already exists at the target path, an error will be returned.
|
||||
|
@ -603,7 +628,7 @@ impl ManagedPythonInstallation {
|
|||
}
|
||||
|
||||
/// Generate a platform portion of a key from the environment.
|
||||
fn platform_key_from_env() -> Result<String, Error> {
|
||||
pub fn platform_key_from_env() -> Result<String, Error> {
|
||||
let os = Os::from_env();
|
||||
let arch = Arch::from_env();
|
||||
let libc = Libc::from_env()?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue