Skip to content

Commit

Permalink
Fix: path install name for Python's dylibs on macOS when the toolchai…
Browse files Browse the repository at this point in the history
…n is installed
  • Loading branch information
LukeMathWalker committed Jan 15, 2025
1 parent d8b5e7e commit 79092d9
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 1 deletion.
1 change: 1 addition & 0 deletions crates/uv-python/src/installation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ impl PythonInstallation {
installed.ensure_externally_managed()?;
installed.ensure_sysconfig_patched()?;
installed.ensure_canonical_executables()?;
installed.ensure_dylib_patched()?;

Ok(Self {
source: PythonSource::Managed,
Expand Down
1 change: 1 addition & 0 deletions crates/uv-python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ mod implementation;
mod installation;
mod interpreter;
mod libc;
pub mod macos_dylib;
pub mod managed;
#[cfg(windows)]
mod microsoft_store;
Expand Down
47 changes: 47 additions & 0 deletions crates/uv-python/src/macos_dylib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use std::path::PathBuf;

use uv_fs::Simplified as _;

pub fn patch_dylib_install_name(dylib: PathBuf) -> Result<(), Error> {
// Check that `install_name_tool` is available before
// trying to run it.
if std::process::Command::new("install_name_tool")
.arg("--version")
.output()
.is_err()
{
return Ok(());
}

let output = std::process::Command::new("install_name_tool")
.arg("-id")
.arg(&dylib)
.arg(&dylib)
.output()?;

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
If `install_name_tool` is missing even after installing the tools, ensure the command line tools are properly configured:
sudo xcode-select --reset
For more information, see: https://developer.apple.com/xcode/")]
MissingInstallNameTool,
#[error("`install_name_tool` failed to update the install name of the Python dynamic library located at `{}`", dylib.user_display())]
RenameError { dylib: PathBuf, stderr: String },
}
27 changes: 26 additions & 1 deletion crates/uv-python/src/managed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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<(), 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.
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/commands/python/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ pub(crate) async fn install(
installation.ensure_externally_managed()?;
installation.ensure_sysconfig_patched()?;
installation.ensure_canonical_executables()?;
installation.ensure_dylib_patched()?;

if preview.is_disabled() {
debug!("Skipping installation of Python executables, use `--preview` to enable.");
Expand Down

0 comments on commit 79092d9

Please sign in to comment.