Skip to content

Commit c9deee6

Browse files
authored
Make Rust build work on more platforms (#16)
1 parent 6f5bc87 commit c9deee6

File tree

6 files changed

+69
-10
lines changed

6 files changed

+69
-10
lines changed

Makefile.pre.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3373,7 +3373,7 @@ Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h
33733373
# Module dependencies and platform-specific files
33743374

33753375
cpython-sys: Modules/cpython-sys/Cargo.toml Modules/cpython-sys/build.rs Modules/cpython-sys/wrapper.h Modules/cpython-sys/parser.h
3376-
cargo build --lib --locked --package cpython-sys --profile $(CARGO_PROFILE)
3376+
CARGO_TARGET_DIR=$(abs_builddir)/target PYTHON_BUILD_DIR=$(abs_builddir) cargo build --lib --locked --package cpython-sys --profile $(CARGO_PROFILE) --manifest-path $(srcdir)/Cargo.toml
33773377

33783378
# force rebuild when header file or module build flavor (static/shared) is changed
33793379
MODULE_DEPS_STATIC=Modules/config.c

Modules/cpython-sys/build.rs

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,55 @@ use std::env;
22
use std::path::{Path, PathBuf};
33

44
fn main() {
5-
let curdir = std::env::current_dir().unwrap();
6-
let srcdir = curdir.parent().and_then(Path::parent).unwrap();
5+
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
6+
let srcdir = manifest_dir
7+
.parent()
8+
.and_then(Path::parent)
9+
.expect("expected Modules/cpython-sys to live under the source tree");
710
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
8-
generate_c_api_bindings(srcdir, &out_path.as_path());
11+
let builddir = env::var("PYTHON_BUILD_DIR").ok();
12+
if gil_disabled(&srcdir, builddir.as_deref()) {
13+
println!("cargo:rustc-cfg=py_gil_disabled");
14+
}
15+
generate_c_api_bindings(srcdir, builddir.as_deref(), &out_path.as_path());
916
// TODO(emmatyping): generate bindings to the internal parser API
1017
// The parser includes things slightly differently, so we should generate
1118
// it's bindings independently
1219
//generate_parser_bindings(srcdir, &out_path.as_path());
1320
}
1421

15-
fn generate_c_api_bindings(srcdir: &Path, out_path: &Path) {
16-
let bindings = bindgen::Builder::default()
17-
.header("wrapper.h")
18-
.clang_arg(format!("-I{}", srcdir.as_os_str().to_str().unwrap()))
19-
.clang_arg(format!("-I{}/Include", srcdir.as_os_str().to_str().unwrap()))
22+
fn gil_disabled(srcdir: &Path, builddir: Option<&str>) -> bool {
23+
let mut candidates = Vec::new();
24+
if let Some(build) = builddir {
25+
candidates.push(PathBuf::from(build));
26+
}
27+
candidates.push(srcdir.to_path_buf());
28+
for base in candidates {
29+
let path = base.join("pyconfig.h");
30+
if let Ok(contents) = std::fs::read_to_string(&path) {
31+
if contents.contains("Py_GIL_DISABLED 1") {
32+
return true;
33+
}
34+
}
35+
}
36+
false
37+
}
38+
39+
fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Path) {
40+
let mut builder = bindgen::Builder::default().header("wrapper.h");
41+
42+
// Always search the source dir and the public headers.
43+
let mut include_dirs = vec![srcdir.to_path_buf(), srcdir.join("Include")];
44+
// Include the build directory if provided; out-of-tree builds place
45+
// the generated pyconfig.h there.
46+
if let Some(build) = builddir {
47+
include_dirs.push(PathBuf::from(build));
48+
}
49+
for dir in include_dirs {
50+
builder = builder.clang_arg(format!("-I{}", dir.display()));
51+
}
52+
53+
let bindings = builder
2054
.allowlist_function("_?Py.*")
2155
.allowlist_type("_?Py.*")
2256
.allowlist_var("_?Py.*")

Modules/cpython-sys/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ impl PyMethodDef {
111111
unsafe impl Sync for PyMethodDef {}
112112
unsafe impl Send for PyMethodDef {}
113113

114+
#[cfg(py_gil_disabled)]
115+
pub const PyObject_HEAD_INIT: PyObject = {
116+
let mut obj: PyObject = unsafe { std::mem::MaybeUninit::zeroed().assume_init() };
117+
obj.ob_flags = _Py_STATICALLY_ALLOCATED_FLAG as _;
118+
obj
119+
};
120+
121+
#[cfg(not(py_gil_disabled))]
114122
pub const PyObject_HEAD_INIT: PyObject = PyObject {
115123
__bindgen_anon_1: _object__bindgen_ty_1 {
116124
ob_refcnt_full: _Py_STATIC_IMMORTAL_INITIAL_REFCNT as i64,

Modules/makesetup

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' |
287287
libs=
288288
# depends on the headers through cpython-sys
289289
rule="$objs: cpython-sys \$(srcdir)/Cargo.toml \$(srcdir)/Cargo.lock \$(srcdir)/$srcdir/$manifest $prefixed_srcs \$(PYTHON_HEADERS)"
290-
rule="$rule; cargo build --lib --locked --package ${mods} --profile \$(CARGO_PROFILE)"
290+
rule="$rule; CARGO_TARGET_DIR=\$(abs_builddir)/target PYTHON_BUILD_DIR=\$(abs_builddir) cargo build --lib --locked --package ${mods} --profile \$(CARGO_PROFILE) --manifest-path \$(srcdir)/Cargo.toml"
291291
echo "$rule" >>$rulesf
292292
for mod in $mods
293293
do

Python/stdlib_module_names.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Tools/build/generate_stdlib_module_names.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import _imp
6+
import os
67
import os.path
78
import sys
89
import sysconfig
@@ -14,6 +15,7 @@
1415

1516
SRC_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
1617
STDLIB_PATH = os.path.join(SRC_DIR, 'Lib')
18+
MODULES_PATH = os.path.join(SRC_DIR, 'Modules')
1719

1820
IGNORE = {
1921
'__init__',
@@ -84,6 +86,19 @@ def list_modules_setup_extensions(names: set[str]) -> None:
8486
names.update(checker.list_module_names(all=True))
8587

8688

89+
def list_rust_modules(names: set[str]) -> None:
90+
if not os.path.isdir(MODULES_PATH):
91+
return
92+
for entry in os.scandir(MODULES_PATH):
93+
if not entry.is_dir():
94+
continue
95+
if entry.name == "cpython-sys":
96+
continue
97+
cargo_toml = os.path.join(entry.path, "Cargo.toml")
98+
if os.path.isfile(cargo_toml):
99+
names.add(entry.name)
100+
101+
87102
# List frozen modules of the PyImport_FrozenModules list (Python/frozen.c).
88103
# Use the "./Programs/_testembed list_frozen" command.
89104
def list_frozen(names: set[str]) -> None:
@@ -109,6 +124,7 @@ def list_modules() -> set[str]:
109124

110125
list_builtin_modules(names)
111126
list_modules_setup_extensions(names)
127+
list_rust_modules(names)
112128
list_packages(names)
113129
list_python_modules(names)
114130
list_frozen(names)

0 commit comments

Comments
 (0)