Skip to content

Commit e9df01c

Browse files
committed
WIP: Generalize the rpath API for relocatable Python installations
Relocatable Python installations like python-build-standalone have a similar need for an rpath as the macOS framework builds in Xcode. Furthermore, the platform on which you build your code is not really a function of the code itself. This change generalizes the recent `add_python_framework_link_args()` from PyO3#4833 to `add_python_link_args()`, which also does the right thing on python-build-standalone, and encourages everyone to set up a build.rs file that invokes it, whether or not they need it on their current machine.
1 parent f89b5f7 commit e9df01c

File tree

4 files changed

+174
-19
lines changed

4 files changed

+174
-19
lines changed

build.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::env;
22

33
use pyo3_build_config::pyo3_build_script_impl::{cargo_env_var, errors::Result};
44
use pyo3_build_config::{
5-
add_python_framework_link_args, bail, print_feature_cfgs, InterpreterConfig,
5+
add_python_link_args, bail, print_feature_cfgs, InterpreterConfig,
66
};
77

88
fn ensure_auto_initialize_ok(interpreter_config: &InterpreterConfig) -> Result<()> {
@@ -44,8 +44,8 @@ fn configure_pyo3() -> Result<()> {
4444
// Emit cfgs like `invalid_from_utf8_lint`
4545
print_feature_cfgs();
4646

47-
// Make `cargo test` etc work on macOS with Xcode bundled Python
48-
add_python_framework_link_args();
47+
// Make `cargo test` etc work on non-global Python installations
48+
add_python_link_args();
4949

5050
Ok(())
5151
}

guide/src/getting-started.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ crate-type = ["cdylib"]
120120

121121
[dependencies]
122122
pyo3 = { {{#PYO3_CRATE_VERSION}}, features = ["extension-module"] }
123+
124+
[build-dependencies]
125+
pyo3-build-config = { {{#PYO3_CRATE_VERSION} }
123126
```
124127

125128
## pyproject.toml
@@ -141,6 +144,17 @@ classifiers = [
141144
]
142145
```
143146

147+
## build.rs
148+
149+
Finally, in order to make `cargo test` work correctly, you should create
150+
a `build.rs` file with the following contents:
151+
152+
```
153+
fn main() {
154+
pyo3_build_config::add_python_link_args();
155+
}
156+
```
157+
144158
## Running code
145159

146160
After this you can setup Rust code to be available in Python as below; for example, you can place this code in `src/lib.rs`:

pyo3-build-config/src/impl_.rs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,12 @@ pub struct InterpreterConfig {
167167
///
168168
/// Serialized to multiple `extra_build_script_line` values.
169169
pub extra_build_script_lines: Vec<String>,
170+
170171
/// macOS Python3.framework requires special rpath handling
171172
pub python_framework_prefix: Option<String>,
173+
174+
/// Relocatable Python installations require special rpath handling
175+
pub relocatable: bool,
172176
}
173177

174178
impl InterpreterConfig {
@@ -265,6 +269,7 @@ print("calcsize_pointer", struct.calcsize("P"))
265269
print("mingw", get_platform().startswith("mingw"))
266270
print("ext_suffix", get_config_var("EXT_SUFFIX"))
267271
print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
272+
print_if_set("python_build_standalone", get_config_var("PYTHON_BUILD_STANDALONE"))
268273
"#;
269274
let output = run_python_script(interpreter.as_ref(), SCRIPT)?;
270275
let map: HashMap<String, String> = parse_script_output(&output);
@@ -315,6 +320,13 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
315320
_ => panic!("Unknown Py_GIL_DISABLED value"),
316321
};
317322

323+
let relocatable = match map.get("python_build_standalone").map(String::as_str) {
324+
None => false,
325+
Some("0") => false,
326+
Some("1") => true,
327+
_ => panic!("Unknown PYTHON_BUILD_STANDALONE value"),
328+
};
329+
318330
let lib_name = if cfg!(windows) {
319331
default_lib_name_windows(
320332
version,
@@ -365,6 +377,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
365377
suppress_build_script_link_lines: false,
366378
extra_build_script_lines: vec![],
367379
python_framework_prefix,
380+
relocatable,
368381
})
369382
}
370383

@@ -410,6 +423,10 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
410423
Some(value) => value == "1",
411424
None => false,
412425
};
426+
let relocatable = match sysconfigdata.get_value("python_build_standalone") {
427+
Some(value) => value == "1",
428+
None => false,
429+
};
413430
let lib_name = Some(default_lib_name_unix(
414431
version,
415432
implementation,
@@ -434,6 +451,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
434451
suppress_build_script_link_lines: false,
435452
extra_build_script_lines: vec![],
436453
python_framework_prefix,
454+
relocatable,
437455
})
438456
}
439457

@@ -511,6 +529,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
511529
let mut suppress_build_script_link_lines = None;
512530
let mut extra_build_script_lines = vec![];
513531
let mut python_framework_prefix = None;
532+
let mut relocatable = None;
514533

515534
for (i, line) in lines.enumerate() {
516535
let line = line.context("failed to read line from config")?;
@@ -540,6 +559,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
540559
extra_build_script_lines.push(value.to_string());
541560
}
542561
"python_framework_prefix" => parse_value!(python_framework_prefix, value),
562+
"relocatable" => parse_value!(relocatable, value),
543563
unknown => warn!("unknown config key `{}`", unknown),
544564
}
545565
}
@@ -571,6 +591,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
571591
suppress_build_script_link_lines: suppress_build_script_link_lines.unwrap_or(false),
572592
extra_build_script_lines,
573593
python_framework_prefix,
594+
relocatable: relocatable.unwrap_or(false),
574595
})
575596
}
576597

@@ -663,12 +684,13 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED"))
663684
write_option_line!(executable)?;
664685
write_option_line!(pointer_width)?;
665686
write_line!(build_flags)?;
666-
write_option_line!(python_framework_prefix)?;
667687
write_line!(suppress_build_script_link_lines)?;
668688
for line in &self.extra_build_script_lines {
669689
writeln!(writer, "extra_build_script_line={}", line)
670690
.context("failed to write extra_build_script_line")?;
671691
}
692+
write_option_line!(python_framework_prefix)?;
693+
write_line!(relocatable)?;
672694
Ok(())
673695
}
674696

@@ -1601,7 +1623,10 @@ fn default_cross_compile(cross_compile_config: &CrossCompileConfig) -> Result<In
16011623
build_flags: BuildFlags::default(),
16021624
suppress_build_script_link_lines: false,
16031625
extra_build_script_lines: vec![],
1626+
// Because this is only used for extensions, we know we do not
1627+
// need any rpath handling.
16041628
python_framework_prefix: None,
1629+
relocatable: false,
16051630
})
16061631
}
16071632

@@ -1644,7 +1669,10 @@ fn default_abi3_config(host: &Triple, version: PythonVersion) -> Result<Interpre
16441669
build_flags: BuildFlags::default(),
16451670
suppress_build_script_link_lines: false,
16461671
extra_build_script_lines: vec![],
1672+
// Because this is only used for extensions, we know we do not
1673+
// need any rpath handling.
16471674
python_framework_prefix: None,
1675+
relocatable: false,
16481676
})
16491677
}
16501678

@@ -2028,6 +2056,7 @@ mod tests {
20282056
suppress_build_script_link_lines: true,
20292057
extra_build_script_lines: vec!["cargo:test1".to_string(), "cargo:test2".to_string()],
20302058
python_framework_prefix: None,
2059+
relocatable: false,
20312060
};
20322061
let mut buf: Vec<u8> = Vec::new();
20332062
config.to_writer(&mut buf).unwrap();
@@ -2057,6 +2086,7 @@ mod tests {
20572086
suppress_build_script_link_lines: false,
20582087
extra_build_script_lines: vec![],
20592088
python_framework_prefix: None,
2089+
relocatable: true,
20602090
};
20612091
let mut buf: Vec<u8> = Vec::new();
20622092
config.to_writer(&mut buf).unwrap();
@@ -2079,6 +2109,7 @@ mod tests {
20792109
suppress_build_script_link_lines: true,
20802110
extra_build_script_lines: vec!["cargo:test1".to_string(), "cargo:test2".to_string()],
20812111
python_framework_prefix: None,
2112+
relocatable: false,
20822113
};
20832114
let mut buf: Vec<u8> = Vec::new();
20842115
config.to_writer(&mut buf).unwrap();
@@ -2106,6 +2137,7 @@ mod tests {
21062137
suppress_build_script_link_lines: false,
21072138
extra_build_script_lines: vec![],
21082139
python_framework_prefix: None,
2140+
relocatable: false,
21092141
}
21102142
)
21112143
}
@@ -2129,6 +2161,7 @@ mod tests {
21292161
suppress_build_script_link_lines: false,
21302162
extra_build_script_lines: vec![],
21312163
python_framework_prefix: None,
2164+
relocatable: false,
21322165
}
21332166
)
21342167
}
@@ -2232,6 +2265,7 @@ mod tests {
22322265
suppress_build_script_link_lines: false,
22332266
extra_build_script_lines: vec![],
22342267
python_framework_prefix: None,
2268+
relocatable: false,
22352269
}
22362270
);
22372271
}
@@ -2262,6 +2296,7 @@ mod tests {
22622296
suppress_build_script_link_lines: false,
22632297
extra_build_script_lines: vec![],
22642298
python_framework_prefix: None,
2299+
relocatable: false,
22652300
}
22662301
);
22672302

@@ -2289,6 +2324,37 @@ mod tests {
22892324
suppress_build_script_link_lines: false,
22902325
extra_build_script_lines: vec![],
22912326
python_framework_prefix: None,
2327+
relocatable: false,
2328+
}
2329+
);
2330+
}
2331+
2332+
#[test]
2333+
fn config_from_sysconfigdata_relocatable() {
2334+
let mut sysconfigdata = Sysconfigdata::new();
2335+
sysconfigdata.insert("SOABI", "cpython-37m-x86_64-linux-gnu");
2336+
sysconfigdata.insert("VERSION", "3.7");
2337+
sysconfigdata.insert("Py_ENABLE_SHARED", "1");
2338+
sysconfigdata.insert("LIBDIR", "/home/xyz/Downloads/python/lib");
2339+
sysconfigdata.insert("LDVERSION", "3.7m");
2340+
sysconfigdata.insert("SIZEOF_VOID_P", "8");
2341+
sysconfigdata.insert("PYTHON_BUILD_STANDALONE", "1");
2342+
assert_eq!(
2343+
InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(),
2344+
InterpreterConfig {
2345+
abi3: false,
2346+
build_flags: BuildFlags::from_sysconfigdata(&sysconfigdata),
2347+
pointer_width: Some(64),
2348+
executable: None,
2349+
implementation: PythonImplementation::CPython,
2350+
lib_dir: Some("/home/xyz/Downloads/python/lib".into()),
2351+
lib_name: Some("python3.7m".into()),
2352+
shared: true,
2353+
version: PythonVersion::PY37,
2354+
suppress_build_script_link_lines: false,
2355+
extra_build_script_lines: vec![],
2356+
python_framework_prefix: None,
2357+
relocatable: true,
22922358
}
22932359
);
22942360
}
@@ -2313,6 +2379,7 @@ mod tests {
23132379
suppress_build_script_link_lines: false,
23142380
extra_build_script_lines: vec![],
23152381
python_framework_prefix: None,
2382+
relocatable: false,
23162383
}
23172384
);
23182385
}
@@ -2337,6 +2404,7 @@ mod tests {
23372404
suppress_build_script_link_lines: false,
23382405
extra_build_script_lines: vec![],
23392406
python_framework_prefix: None,
2407+
relocatable: false,
23402408
}
23412409
);
23422410
}
@@ -2372,6 +2440,7 @@ mod tests {
23722440
suppress_build_script_link_lines: false,
23732441
extra_build_script_lines: vec![],
23742442
python_framework_prefix: None,
2443+
relcoatable: false,
23752444
}
23762445
);
23772446
}
@@ -2407,6 +2476,7 @@ mod tests {
24072476
suppress_build_script_link_lines: false,
24082477
extra_build_script_lines: vec![],
24092478
python_framework_prefix: None,
2479+
relocatable: false,
24102480
}
24112481
);
24122482
}
@@ -2442,6 +2512,7 @@ mod tests {
24422512
suppress_build_script_link_lines: false,
24432513
extra_build_script_lines: vec![],
24442514
python_framework_prefix: None,
2515+
relocatable; false,
24452516
}
24462517
);
24472518
}
@@ -2479,6 +2550,7 @@ mod tests {
24792550
suppress_build_script_link_lines: false,
24802551
extra_build_script_lines: vec![],
24812552
python_framework_prefix: None,
2553+
relocatable: false,
24822554
}
24832555
);
24842556
}
@@ -2827,6 +2899,7 @@ mod tests {
28272899
suppress_build_script_link_lines: false,
28282900
extra_build_script_lines: vec![],
28292901
python_framework_prefix: None,
2902+
relocatable: false,
28302903
};
28312904

28322905
config
@@ -2850,6 +2923,7 @@ mod tests {
28502923
suppress_build_script_link_lines: false,
28512924
extra_build_script_lines: vec![],
28522925
python_framework_prefix: None,
2926+
relocatable: false,
28532927
};
28542928

28552929
assert!(config
@@ -2915,6 +2989,7 @@ mod tests {
29152989
suppress_build_script_link_lines: false,
29162990
extra_build_script_lines: vec![],
29172991
python_framework_prefix: None,
2992+
relocatable: false,
29182993
}
29192994
)
29202995
}
@@ -3040,6 +3115,7 @@ mod tests {
30403115
suppress_build_script_link_lines: false,
30413116
extra_build_script_lines: vec![],
30423117
python_framework_prefix: None,
3118+
relocatable: false,
30433119
};
30443120
assert_eq!(
30453121
interpreter_config.build_script_outputs(),
@@ -3080,6 +3156,7 @@ mod tests {
30803156
suppress_build_script_link_lines: false,
30813157
extra_build_script_lines: vec![],
30823158
python_framework_prefix: None,
3159+
relocatable: false,
30833160
};
30843161

30853162
assert_eq!(
@@ -3128,6 +3205,7 @@ mod tests {
31283205
suppress_build_script_link_lines: false,
31293206
extra_build_script_lines: vec![],
31303207
python_framework_prefix: None,
3208+
relocatable: false,
31313209
};
31323210

31333211
assert_eq!(
@@ -3162,6 +3240,7 @@ mod tests {
31623240
suppress_build_script_link_lines: false,
31633241
extra_build_script_lines: vec![],
31643242
python_framework_prefix: None,
3243+
relocatable: false,
31653244
};
31663245

31673246
assert_eq!(

0 commit comments

Comments
 (0)