Skip to content

Add an API to set rpath when using macOS system Python #4833

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::env;

use pyo3_build_config::pyo3_build_script_impl::{cargo_env_var, errors::Result};
use pyo3_build_config::{bail, print_feature_cfgs, InterpreterConfig};
use pyo3_build_config::{
add_python_framework_link_args, bail, print_feature_cfgs, InterpreterConfig,
};

fn ensure_auto_initialize_ok(interpreter_config: &InterpreterConfig) -> Result<()> {
if cargo_env_var("CARGO_FEATURE_AUTO_INITIALIZE").is_some() && !interpreter_config.shared {
Expand Down Expand Up @@ -42,6 +44,9 @@ fn configure_pyo3() -> Result<()> {
// Emit cfgs like `invalid_from_utf8_lint`
print_feature_cfgs();

// Make `cargo test` etc work on macOS with Xcode bundled Python
add_python_framework_link_args();

Ok(())
}

Expand Down
22 changes: 11 additions & 11 deletions guide/src/building-and-distribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,17 @@ rustflags = [
]
```

Using the MacOS system python3 (`/usr/bin/python3`, as opposed to python installed via homebrew, pyenv, nix, etc.) may result in runtime errors such as `Library not loaded: @rpath/Python3.framework/Versions/3.8/Python3`. These can be resolved with another addition to `.cargo/config.toml`:
Using the MacOS system python3 (`/usr/bin/python3`, as opposed to python installed via homebrew, pyenv, nix, etc.) may result in runtime errors such as `Library not loaded: @rpath/Python3.framework/Versions/3.8/Python3`.

The easiest way to set the correct linker arguments is to add a `build.rs` with the following content:

```rust,ignore
fn main() {
pyo3_build_config::add_python_framework_link_args();
}
```

Alternatively it can be resolved with another addition to `.cargo/config.toml`:

```toml
[build]
Expand All @@ -153,16 +163,6 @@ rustflags = [
]
```

Alternatively, one can include in `build.rs`:

```rust
fn main() {
println!(
"cargo:rustc-link-arg=-Wl,-rpath,/Library/Developer/CommandLineTools/Library/Frameworks"
);
}
```

For more discussion on and workarounds for MacOS linking problems [see this issue](https://github.com/PyO3/pyo3/issues/1800#issuecomment-906786649).

Finally, don't forget that on MacOS the `extension-module` feature will cause `cargo test` to fail without the `--no-default-features` flag (see [the FAQ](https://pyo3.rs/main/faq.html#i-cant-run-cargo-test-or-i-cant-build-in-a-cargo-workspace-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror)).
Expand Down
1 change: 1 addition & 0 deletions newsfragments/4833.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `pyo3_build_config::add_python_framework_link_args` build script API to set rpath when using macOS system Python.
37 changes: 37 additions & 0 deletions pyo3-build-config/src/impl_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ pub struct InterpreterConfig {
///
/// Serialized to multiple `extra_build_script_line` values.
pub extra_build_script_lines: Vec<String>,
/// macOS Python3.framework requires special rpath handling
pub python_framework_prefix: Option<String>,
}

impl InterpreterConfig {
Expand Down Expand Up @@ -245,6 +247,7 @@ WINDOWS = platform.system() == "Windows"

# macOS framework packages use shared linking
FRAMEWORK = bool(get_config_var("PYTHONFRAMEWORK"))
FRAMEWORK_PREFIX = get_config_var("PYTHONFRAMEWORKPREFIX")

# unix-style shared library enabled
SHARED = bool(get_config_var("Py_ENABLE_SHARED"))
Expand All @@ -253,6 +256,7 @@ print("implementation", platform.python_implementation())
print("version_major", sys.version_info[0])
print("version_minor", sys.version_info[1])
print("shared", PYPY or GRAALPY or ANACONDA or WINDOWS or FRAMEWORK or SHARED)
print("python_framework_prefix", FRAMEWORK_PREFIX)
print_if_set("ld_version", get_config_var("LDVERSION"))
print_if_set("libdir", get_config_var("LIBDIR"))
print_if_set("base_prefix", base_prefix)
Expand Down Expand Up @@ -289,6 +293,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
};

let shared = map["shared"].as_str() == "True";
let python_framework_prefix = map.get("python_framework_prefix").cloned();

let version = PythonVersion {
major: map["version_major"]
Expand Down Expand Up @@ -359,6 +364,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
build_flags: BuildFlags::from_interpreter(interpreter)?,
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix,
})
}

Expand Down Expand Up @@ -396,6 +402,9 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
Some(s) => !s.is_empty(),
_ => false,
};
let python_framework_prefix = sysconfigdata
.get_value("PYTHONFRAMEWORKPREFIX")
.map(str::to_string);
let lib_dir = get_key!(sysconfigdata, "LIBDIR").ok().map(str::to_string);
let gil_disabled = match sysconfigdata.get_value("Py_GIL_DISABLED") {
Some(value) => value == "1",
Expand Down Expand Up @@ -424,6 +433,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
build_flags,
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix,
})
}

Expand Down Expand Up @@ -500,6 +510,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
let mut build_flags: Option<BuildFlags> = None;
let mut suppress_build_script_link_lines = None;
let mut extra_build_script_lines = vec![];
let mut python_framework_prefix = None;

for (i, line) in lines.enumerate() {
let line = line.context("failed to read line from config")?;
Expand Down Expand Up @@ -528,6 +539,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
"extra_build_script_line" => {
extra_build_script_lines.push(value.to_string());
}
"python_framework_prefix" => parse_value!(python_framework_prefix, value),
unknown => warn!("unknown config key `{}`", unknown),
}
}
Expand Down Expand Up @@ -558,6 +570,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
build_flags,
suppress_build_script_link_lines: suppress_build_script_link_lines.unwrap_or(false),
extra_build_script_lines,
python_framework_prefix,
})
}

Expand Down Expand Up @@ -650,6 +663,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
write_option_line!(executable)?;
write_option_line!(pointer_width)?;
write_line!(build_flags)?;
write_option_line!(python_framework_prefix)?;
write_line!(suppress_build_script_link_lines)?;
for line in &self.extra_build_script_lines {
writeln!(writer, "extra_build_script_line={}", line)
Expand Down Expand Up @@ -1587,6 +1601,7 @@ fn default_cross_compile(cross_compile_config: &CrossCompileConfig) -> Result<In
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
})
}

Expand Down Expand Up @@ -1629,6 +1644,7 @@ fn default_abi3_config(host: &Triple, version: PythonVersion) -> Result<Interpre
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
})
}

Expand Down Expand Up @@ -2011,6 +2027,7 @@ mod tests {
version: MINIMUM_SUPPORTED_VERSION,
suppress_build_script_link_lines: true,
extra_build_script_lines: vec!["cargo:test1".to_string(), "cargo:test2".to_string()],
python_framework_prefix: None,
};
let mut buf: Vec<u8> = Vec::new();
config.to_writer(&mut buf).unwrap();
Expand Down Expand Up @@ -2039,6 +2056,7 @@ mod tests {
},
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
};
let mut buf: Vec<u8> = Vec::new();
config.to_writer(&mut buf).unwrap();
Expand All @@ -2060,6 +2078,7 @@ mod tests {
version: MINIMUM_SUPPORTED_VERSION,
suppress_build_script_link_lines: true,
extra_build_script_lines: vec!["cargo:test1".to_string(), "cargo:test2".to_string()],
python_framework_prefix: None,
};
let mut buf: Vec<u8> = Vec::new();
config.to_writer(&mut buf).unwrap();
Expand All @@ -2086,6 +2105,7 @@ mod tests {
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
}
)
}
Expand All @@ -2108,6 +2128,7 @@ mod tests {
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
}
)
}
Expand Down Expand Up @@ -2210,6 +2231,7 @@ mod tests {
version: PythonVersion::PY37,
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
}
);
}
Expand Down Expand Up @@ -2239,6 +2261,7 @@ mod tests {
version: PythonVersion::PY37,
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
}
);

Expand All @@ -2265,6 +2288,7 @@ mod tests {
version: PythonVersion::PY37,
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
}
);
}
Expand All @@ -2288,6 +2312,7 @@ mod tests {
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
}
);
}
Expand All @@ -2311,6 +2336,7 @@ mod tests {
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
}
);
}
Expand Down Expand Up @@ -2345,6 +2371,7 @@ mod tests {
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
}
);
}
Expand Down Expand Up @@ -2379,6 +2406,7 @@ mod tests {
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
}
);
}
Expand Down Expand Up @@ -2413,6 +2441,7 @@ mod tests {
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
}
);
}
Expand Down Expand Up @@ -2449,6 +2478,7 @@ mod tests {
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
}
);
}
Expand Down Expand Up @@ -2796,6 +2826,7 @@ mod tests {
version: PythonVersion { major: 3, minor: 7 },
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
};

config
Expand All @@ -2818,6 +2849,7 @@ mod tests {
version: PythonVersion { major: 3, minor: 7 },
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
};

assert!(config
Expand Down Expand Up @@ -2882,6 +2914,7 @@ mod tests {
version: interpreter_config.version,
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
}
)
}
Expand Down Expand Up @@ -3006,6 +3039,7 @@ mod tests {
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
};
assert_eq!(
interpreter_config.build_script_outputs(),
Expand Down Expand Up @@ -3045,6 +3079,7 @@ mod tests {
build_flags: BuildFlags::default(),
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
};

assert_eq!(
Expand Down Expand Up @@ -3092,6 +3127,7 @@ mod tests {
build_flags,
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
};

assert_eq!(
Expand Down Expand Up @@ -3125,6 +3161,7 @@ mod tests {
build_flags,
suppress_build_script_link_lines: false,
extra_build_script_lines: vec![],
python_framework_prefix: None,
};

assert_eq!(
Expand Down
Loading
Loading