Skip to content

Commit 8aa6825

Browse files
messensedavidhewitt
authored andcommitted
Add an API to set rpath when using macOS system Python (#4833)
1 parent e19d048 commit 8aa6825

File tree

5 files changed

+138
-12
lines changed

5 files changed

+138
-12
lines changed

build.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use std::env;
22

33
use pyo3_build_config::pyo3_build_script_impl::{cargo_env_var, errors::Result};
4-
use pyo3_build_config::{bail, print_feature_cfgs, InterpreterConfig};
4+
use pyo3_build_config::{
5+
add_python_framework_link_args, bail, print_feature_cfgs, InterpreterConfig,
6+
};
57

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

47+
// Make `cargo test` etc work on macOS with Xcode bundled Python
48+
add_python_framework_link_args();
49+
4550
Ok(())
4651
}
4752

guide/src/building-and-distribution.md

+11-11
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,17 @@ rustflags = [
144144
]
145145
```
146146

147-
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`:
147+
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`.
148+
149+
The easiest way to set the correct linker arguments is to add a `build.rs` with the following content:
150+
151+
```rust,ignore
152+
fn main() {
153+
pyo3_build_config::add_python_framework_link_args();
154+
}
155+
```
156+
157+
Alternatively it can be resolved with another addition to `.cargo/config.toml`:
148158

149159
```toml
150160
[build]
@@ -153,16 +163,6 @@ rustflags = [
153163
]
154164
```
155165

156-
Alternatively, one can include in `build.rs`:
157-
158-
```rust
159-
fn main() {
160-
println!(
161-
"cargo:rustc-link-arg=-Wl,-rpath,/Library/Developer/CommandLineTools/Library/Frameworks"
162-
);
163-
}
164-
```
165-
166166
For more discussion on and workarounds for MacOS linking problems [see this issue](https://github.com/PyO3/pyo3/issues/1800#issuecomment-906786649).
167167

168168
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)).

newsfragments/4833.added.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add `pyo3_build_config::add_python_framework_link_args` build script API to set rpath when using macOS system Python.

pyo3-build-config/src/impl_.rs

+37
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ pub struct InterpreterConfig {
167167
///
168168
/// Serialized to multiple `extra_build_script_line` values.
169169
pub extra_build_script_lines: Vec<String>,
170+
/// macOS Python3.framework requires special rpath handling
171+
pub python_framework_prefix: Option<String>,
170172
}
171173

172174
impl InterpreterConfig {
@@ -245,6 +247,7 @@ WINDOWS = platform.system() == "Windows"
245247
246248
# macOS framework packages use shared linking
247249
FRAMEWORK = bool(get_config_var("PYTHONFRAMEWORK"))
250+
FRAMEWORK_PREFIX = get_config_var("PYTHONFRAMEWORKPREFIX")
248251
249252
# unix-style shared library enabled
250253
SHARED = bool(get_config_var("Py_ENABLE_SHARED"))
@@ -253,6 +256,7 @@ print("implementation", platform.python_implementation())
253256
print("version_major", sys.version_info[0])
254257
print("version_minor", sys.version_info[1])
255258
print("shared", PYPY or GRAALPY or ANACONDA or WINDOWS or FRAMEWORK or SHARED)
259+
print("python_framework_prefix", FRAMEWORK_PREFIX)
256260
print_if_set("ld_version", get_config_var("LDVERSION"))
257261
print_if_set("libdir", get_config_var("LIBDIR"))
258262
print_if_set("base_prefix", base_prefix)
@@ -289,6 +293,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
289293
};
290294

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

293298
let version = PythonVersion {
294299
major: map["version_major"]
@@ -359,6 +364,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
359364
build_flags: BuildFlags::from_interpreter(interpreter)?,
360365
suppress_build_script_link_lines: false,
361366
extra_build_script_lines: vec![],
367+
python_framework_prefix,
362368
})
363369
}
364370

@@ -396,6 +402,9 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
396402
Some(s) => !s.is_empty(),
397403
_ => false,
398404
};
405+
let python_framework_prefix = sysconfigdata
406+
.get_value("PYTHONFRAMEWORKPREFIX")
407+
.map(str::to_string);
399408
let lib_dir = get_key!(sysconfigdata, "LIBDIR").ok().map(str::to_string);
400409
let gil_disabled = match sysconfigdata.get_value("Py_GIL_DISABLED") {
401410
Some(value) => value == "1",
@@ -424,6 +433,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
424433
build_flags,
425434
suppress_build_script_link_lines: false,
426435
extra_build_script_lines: vec![],
436+
python_framework_prefix,
427437
})
428438
}
429439

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

504515
for (i, line) in lines.enumerate() {
505516
let line = line.context("failed to read line from config")?;
@@ -528,6 +539,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
528539
"extra_build_script_line" => {
529540
extra_build_script_lines.push(value.to_string());
530541
}
542+
"python_framework_prefix" => parse_value!(python_framework_prefix, value),
531543
unknown => warn!("unknown config key `{}`", unknown),
532544
}
533545
}
@@ -558,6 +570,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
558570
build_flags,
559571
suppress_build_script_link_lines: suppress_build_script_link_lines.unwrap_or(false),
560572
extra_build_script_lines,
573+
python_framework_prefix,
561574
})
562575
}
563576

@@ -650,6 +663,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
650663
write_option_line!(executable)?;
651664
write_option_line!(pointer_width)?;
652665
write_line!(build_flags)?;
666+
write_option_line!(python_framework_prefix)?;
653667
write_line!(suppress_build_script_link_lines)?;
654668
for line in &self.extra_build_script_lines {
655669
writeln!(writer, "extra_build_script_line={}", line)
@@ -1587,6 +1601,7 @@ fn default_cross_compile(cross_compile_config: &CrossCompileConfig) -> Result<In
15871601
build_flags: BuildFlags::default(),
15881602
suppress_build_script_link_lines: false,
15891603
extra_build_script_lines: vec![],
1604+
python_framework_prefix: None,
15901605
})
15911606
}
15921607

@@ -1629,6 +1644,7 @@ fn default_abi3_config(host: &Triple, version: PythonVersion) -> Result<Interpre
16291644
build_flags: BuildFlags::default(),
16301645
suppress_build_script_link_lines: false,
16311646
extra_build_script_lines: vec![],
1647+
python_framework_prefix: None,
16321648
})
16331649
}
16341650

@@ -2011,6 +2027,7 @@ mod tests {
20112027
version: MINIMUM_SUPPORTED_VERSION,
20122028
suppress_build_script_link_lines: true,
20132029
extra_build_script_lines: vec!["cargo:test1".to_string(), "cargo:test2".to_string()],
2030+
python_framework_prefix: None,
20142031
};
20152032
let mut buf: Vec<u8> = Vec::new();
20162033
config.to_writer(&mut buf).unwrap();
@@ -2039,6 +2056,7 @@ mod tests {
20392056
},
20402057
suppress_build_script_link_lines: false,
20412058
extra_build_script_lines: vec![],
2059+
python_framework_prefix: None,
20422060
};
20432061
let mut buf: Vec<u8> = Vec::new();
20442062
config.to_writer(&mut buf).unwrap();
@@ -2060,6 +2078,7 @@ mod tests {
20602078
version: MINIMUM_SUPPORTED_VERSION,
20612079
suppress_build_script_link_lines: true,
20622080
extra_build_script_lines: vec!["cargo:test1".to_string(), "cargo:test2".to_string()],
2081+
python_framework_prefix: None,
20632082
};
20642083
let mut buf: Vec<u8> = Vec::new();
20652084
config.to_writer(&mut buf).unwrap();
@@ -2086,6 +2105,7 @@ mod tests {
20862105
build_flags: BuildFlags::default(),
20872106
suppress_build_script_link_lines: false,
20882107
extra_build_script_lines: vec![],
2108+
python_framework_prefix: None,
20892109
}
20902110
)
20912111
}
@@ -2108,6 +2128,7 @@ mod tests {
21082128
build_flags: BuildFlags::default(),
21092129
suppress_build_script_link_lines: false,
21102130
extra_build_script_lines: vec![],
2131+
python_framework_prefix: None,
21112132
}
21122133
)
21132134
}
@@ -2210,6 +2231,7 @@ mod tests {
22102231
version: PythonVersion::PY37,
22112232
suppress_build_script_link_lines: false,
22122233
extra_build_script_lines: vec![],
2234+
python_framework_prefix: None,
22132235
}
22142236
);
22152237
}
@@ -2239,6 +2261,7 @@ mod tests {
22392261
version: PythonVersion::PY37,
22402262
suppress_build_script_link_lines: false,
22412263
extra_build_script_lines: vec![],
2264+
python_framework_prefix: None,
22422265
}
22432266
);
22442267

@@ -2265,6 +2288,7 @@ mod tests {
22652288
version: PythonVersion::PY37,
22662289
suppress_build_script_link_lines: false,
22672290
extra_build_script_lines: vec![],
2291+
python_framework_prefix: None,
22682292
}
22692293
);
22702294
}
@@ -2288,6 +2312,7 @@ mod tests {
22882312
build_flags: BuildFlags::default(),
22892313
suppress_build_script_link_lines: false,
22902314
extra_build_script_lines: vec![],
2315+
python_framework_prefix: None,
22912316
}
22922317
);
22932318
}
@@ -2311,6 +2336,7 @@ mod tests {
23112336
build_flags: BuildFlags::default(),
23122337
suppress_build_script_link_lines: false,
23132338
extra_build_script_lines: vec![],
2339+
python_framework_prefix: None,
23142340
}
23152341
);
23162342
}
@@ -2345,6 +2371,7 @@ mod tests {
23452371
build_flags: BuildFlags::default(),
23462372
suppress_build_script_link_lines: false,
23472373
extra_build_script_lines: vec![],
2374+
python_framework_prefix: None,
23482375
}
23492376
);
23502377
}
@@ -2379,6 +2406,7 @@ mod tests {
23792406
build_flags: BuildFlags::default(),
23802407
suppress_build_script_link_lines: false,
23812408
extra_build_script_lines: vec![],
2409+
python_framework_prefix: None,
23822410
}
23832411
);
23842412
}
@@ -2413,6 +2441,7 @@ mod tests {
24132441
build_flags: BuildFlags::default(),
24142442
suppress_build_script_link_lines: false,
24152443
extra_build_script_lines: vec![],
2444+
python_framework_prefix: None,
24162445
}
24172446
);
24182447
}
@@ -2449,6 +2478,7 @@ mod tests {
24492478
build_flags: BuildFlags::default(),
24502479
suppress_build_script_link_lines: false,
24512480
extra_build_script_lines: vec![],
2481+
python_framework_prefix: None,
24522482
}
24532483
);
24542484
}
@@ -2796,6 +2826,7 @@ mod tests {
27962826
version: PythonVersion { major: 3, minor: 7 },
27972827
suppress_build_script_link_lines: false,
27982828
extra_build_script_lines: vec![],
2829+
python_framework_prefix: None,
27992830
};
28002831

28012832
config
@@ -2818,6 +2849,7 @@ mod tests {
28182849
version: PythonVersion { major: 3, minor: 7 },
28192850
suppress_build_script_link_lines: false,
28202851
extra_build_script_lines: vec![],
2852+
python_framework_prefix: None,
28212853
};
28222854

28232855
assert!(config
@@ -2882,6 +2914,7 @@ mod tests {
28822914
version: interpreter_config.version,
28832915
suppress_build_script_link_lines: false,
28842916
extra_build_script_lines: vec![],
2917+
python_framework_prefix: None,
28852918
}
28862919
)
28872920
}
@@ -3006,6 +3039,7 @@ mod tests {
30063039
build_flags: BuildFlags::default(),
30073040
suppress_build_script_link_lines: false,
30083041
extra_build_script_lines: vec![],
3042+
python_framework_prefix: None,
30093043
};
30103044
assert_eq!(
30113045
interpreter_config.build_script_outputs(),
@@ -3045,6 +3079,7 @@ mod tests {
30453079
build_flags: BuildFlags::default(),
30463080
suppress_build_script_link_lines: false,
30473081
extra_build_script_lines: vec![],
3082+
python_framework_prefix: None,
30483083
};
30493084

30503085
assert_eq!(
@@ -3092,6 +3127,7 @@ mod tests {
30923127
build_flags,
30933128
suppress_build_script_link_lines: false,
30943129
extra_build_script_lines: vec![],
3130+
python_framework_prefix: None,
30953131
};
30963132

30973133
assert_eq!(
@@ -3125,6 +3161,7 @@ mod tests {
31253161
build_flags,
31263162
suppress_build_script_link_lines: false,
31273163
extra_build_script_lines: vec![],
3164+
python_framework_prefix: None,
31283165
};
31293166

31303167
assert_eq!(

0 commit comments

Comments
 (0)