1use std::{io::ErrorKind, path::PathBuf};
2
3use uv_fs::Simplified as _;
4use uv_warnings::warn_user;
5
6use crate::managed::ManagedPythonInstallation;
7
8pub fn patch_dylib_install_name(dylib: PathBuf) -> Result<(), Error> {
9 let output = match std::process::Command::new("install_name_tool")
10 .arg("-id")
11 .arg(&dylib)
12 .arg(&dylib)
13 .output()
14 {
15 Ok(output) => output,
16 Err(e) => {
17 let e = if e.kind() == ErrorKind::NotFound {
18 Error::MissingInstallNameTool
19 } else {
20 e.into()
21 };
22 return Err(e);
23 }
24 };
25
26 if !output.status.success() {
27 let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
28 return Err(Error::RenameError { dylib, stderr });
29 }
30
31 Ok(())
32}
33
34#[derive(thiserror::Error, Debug)]
35pub enum Error {
36 #[error(transparent)]
37 Io(#[from] std::io::Error),
38 #[error("`install_name_tool` is not available on this system.
39This utility is part of macOS Developer Tools. Please ensure that the Xcode Command Line Tools are installed by running:
40
41 xcode-select --install
42
43For more information, see: https://developer.apple.com/xcode/")]
44 MissingInstallNameTool,
45 #[error("Failed to update the install name of the Python dynamic library located at `{}`", dylib.user_display())]
46 RenameError { dylib: PathBuf, stderr: String },
47}
48
49impl Error {
50 pub fn warn_user(&self, installation: &ManagedPythonInstallation) {
52 let error = if tracing::enabled!(tracing::Level::DEBUG) {
53 format!("\nUnderlying error: {self}")
54 } else {
55 String::new()
56 };
57 warn_user!(
58 "Failed to patch the install name of the dynamic library for {}. This may cause issues when building Python native extensions.{}",
59 installation.executable(false).simplified_display(),
60 error
61 );
62 }
63}