diff --git a/.gitignore b/.gitignore index 2bf4925647ddcd..228455787e5fdf 100644 --- a/.gitignore +++ b/.gitignore @@ -178,3 +178,6 @@ CLAUDE.local.md #### main branch only stuff below this line, things to backport go above. #### # main branch only: ABI files are not checked/maintained. Doc/data/python*.abi + +# Rust build artifacts +/target/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000000000..afc3744abd1cb6 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,235 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "_base64" +version = "0.1.0" +dependencies = [ + "cpython-sys", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cpython-sys" +version = "0.1.0" +dependencies = [ + "bindgen", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000000000..8b75182b7a596a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +resolver = "3" +members = [ + "Modules/_base64", + "Modules/cpython-sys" +] diff --git a/Makefile.pre.in b/Makefile.pre.in index dd28ff5d2a3ed1..10f299819cd40f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -58,6 +58,10 @@ DTRACE_HEADERS= @DTRACE_HEADERS@ DTRACE_OBJS= @DTRACE_OBJS@ DSYMUTIL= @DSYMUTIL@ DSYMUTIL_PATH= @DSYMUTIL_PATH@ +CARGO_HOME=@CARGO_HOME@ +CARGO_TARGET_DIR=@CARGO_TARGET_DIR@ +CARGO_PROFILE=@CARGO_PROFILE@ +CARGO_TARGET=@CARGO_TARGET@ GNULD= @GNULD@ @@ -3254,8 +3258,15 @@ profile-removal: rm -f profile-run-stamp rm -f profile-bolt-stamp +.PHONY: clean-rust +clean-rust: + @if test @CARGO_HOME@ != ''; then \ + echo Running cargo clean...; \ + $(CARGO_HOME)/bin/cargo clean; \ + fi + .PHONY: clean -clean: clean-retain-profile clean-bolt +clean: clean-retain-profile clean-bolt clean-rust @if test @DEF_MAKE_ALL_RULE@ = profile-opt -o @DEF_MAKE_ALL_RULE@ = bolt-opt; then \ rm -f profile-gen-stamp profile-clean-stamp; \ $(MAKE) profile-removal; \ @@ -3362,6 +3373,9 @@ Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h ########################################################################## # Module dependencies and platform-specific files +cpython-sys: Modules/cpython-sys/Cargo.toml Modules/cpython-sys/build.rs Modules/cpython-sys/wrapper.h Modules/cpython-sys/parser.h + CARGO_TARGET_DIR=$(abs_builddir)/target PYTHON_BUILD_DIR=$(abs_builddir) \$(CARGO_HOME)/bin/cargo build --lib --locked --package cpython-sys --profile $(CARGO_PROFILE) $(if $(CARGO_TARGET),--target=$(CARGO_TARGET)) --manifest-path $(srcdir)/Cargo.toml + # force rebuild when header file or module build flavor (static/shared) is changed MODULE_DEPS_STATIC=Modules/config.c MODULE_DEPS_SHARED=@MODULE_DEPS_SHARED@ diff --git a/Modules/Setup b/Modules/Setup index 8a54c0aaec60a0..86b43541f37ecc 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -182,6 +182,7 @@ PYTHONPATH=$(COREPYTHONPATH) #_codecs_tw cjkcodecs/_codecs_tw.c #_multibytecodec cjkcodecs/multibytecodec.c #unicodedata unicodedata.c +#_base64 _base64/Cargo.toml _base64/src/lib.rs lib_base64.a # Modules with some UNIX dependencies diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 2c3013e3d0c144..635be3cbe8dbc0 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -117,6 +117,11 @@ @MODULE__MULTIBYTECODEC_TRUE@_multibytecodec cjkcodecs/multibytecodec.c @MODULE_UNICODEDATA_TRUE@unicodedata unicodedata.c +############################################################################ +# Rust modules +# +@MODULE__BASE64_TRUE@_base64 _base64/Cargo.toml _base64/src/lib.rs lib_base64.a + ############################################################################ # Modules with some UNIX dependencies # diff --git a/Modules/_base64/Cargo.toml b/Modules/_base64/Cargo.toml new file mode 100644 index 00000000000000..0810b787ab2773 --- /dev/null +++ b/Modules/_base64/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "_base64" +version = "0.1.0" +edition = "2024" + +[dependencies] +cpython-sys ={ path = "../cpython-sys" } + +[lib] +name = "_base64" +crate-type = ["staticlib"] diff --git a/Modules/_base64/src/lib.rs b/Modules/_base64/src/lib.rs new file mode 100644 index 00000000000000..7e13438ca21c6f --- /dev/null +++ b/Modules/_base64/src/lib.rs @@ -0,0 +1,252 @@ +use std::cell::UnsafeCell; +use std::ffi::{c_char, c_int, c_void}; +use std::mem::MaybeUninit; +use std::ptr; +use std::slice; + +use cpython_sys::METH_FASTCALL; +use cpython_sys::Py_DecRef; +use cpython_sys::Py_buffer; +use cpython_sys::Py_ssize_t; +use cpython_sys::PyBuffer_Release; +use cpython_sys::PyBytes_AsString; +use cpython_sys::PyBytes_FromStringAndSize; +use cpython_sys::PyErr_NoMemory; +use cpython_sys::PyErr_SetString; +use cpython_sys::PyExc_TypeError; +use cpython_sys::PyMethodDef; +use cpython_sys::PyMethodDefFuncPointer; +use cpython_sys::PyModuleDef; +use cpython_sys::PyModuleDef_HEAD_INIT; +use cpython_sys::PyModuleDef_Init; +use cpython_sys::PyObject; +use cpython_sys::PyObject_GetBuffer; + +const PYBUF_SIMPLE: c_int = 0; +const PAD_BYTE: u8 = b'='; +const ENCODE_TABLE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +#[inline] +fn encoded_output_len(input_len: usize) -> Option { + input_len + .checked_add(2) + .map(|n| n / 3) + .and_then(|blocks| blocks.checked_mul(4)) +} + +#[inline] +fn encode_into(input: &[u8], output: &mut [u8]) -> usize { + let mut src_index = 0; + let mut dst_index = 0; + let len = input.len(); + + while src_index + 3 <= len { + let chunk = (u32::from(input[src_index]) << 16) + | (u32::from(input[src_index + 1]) << 8) + | u32::from(input[src_index + 2]); + output[dst_index] = ENCODE_TABLE[((chunk >> 18) & 0x3f) as usize]; + output[dst_index + 1] = ENCODE_TABLE[((chunk >> 12) & 0x3f) as usize]; + output[dst_index + 2] = ENCODE_TABLE[((chunk >> 6) & 0x3f) as usize]; + output[dst_index + 3] = ENCODE_TABLE[(chunk & 0x3f) as usize]; + src_index += 3; + dst_index += 4; + } + + match len - src_index { + 0 => {} + 1 => { + let chunk = u32::from(input[src_index]) << 16; + output[dst_index] = ENCODE_TABLE[((chunk >> 18) & 0x3f) as usize]; + output[dst_index + 1] = ENCODE_TABLE[((chunk >> 12) & 0x3f) as usize]; + output[dst_index + 2] = PAD_BYTE; + output[dst_index + 3] = PAD_BYTE; + dst_index += 4; + } + 2 => { + let chunk = + (u32::from(input[src_index]) << 16) | (u32::from(input[src_index + 1]) << 8); + output[dst_index] = ENCODE_TABLE[((chunk >> 18) & 0x3f) as usize]; + output[dst_index + 1] = ENCODE_TABLE[((chunk >> 12) & 0x3f) as usize]; + output[dst_index + 2] = ENCODE_TABLE[((chunk >> 6) & 0x3f) as usize]; + output[dst_index + 3] = PAD_BYTE; + dst_index += 4; + } + _ => unreachable!("len - src_index cannot exceed 2"), + } + + dst_index +} + +struct BorrowedBuffer { + view: Py_buffer, +} + +impl BorrowedBuffer { + fn from_object(obj: &PyObject) -> Result { + let mut view = MaybeUninit::::uninit(); + let buffer = unsafe { + if PyObject_GetBuffer(obj.as_raw(), view.as_mut_ptr(), PYBUF_SIMPLE) != 0 { + return Err(()); + } + Self { + view: view.assume_init(), + } + }; + Ok(buffer) + } + + fn len(&self) -> Py_ssize_t { + self.view.len + } + + fn as_ptr(&self) -> *const u8 { + self.view.buf.cast::() as *const u8 + } +} + +impl Drop for BorrowedBuffer { + fn drop(&mut self) { + unsafe { + PyBuffer_Release(&mut self.view); + } + } +} + +/// # Safety +/// `module` must be a valid pointer of PyObject representing the module. +/// `args` must be a valid pointer to an array of valid PyObject pointers with length `nargs`. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn standard_b64encode( + _module: *mut PyObject, + args: *mut *mut PyObject, + nargs: Py_ssize_t, +) -> *mut PyObject { + if nargs != 1 { + unsafe { + PyErr_SetString( + PyExc_TypeError, + c"standard_b64encode() takes exactly one argument".as_ptr(), + ); + } + return ptr::null_mut(); + } + + let source = unsafe { &**args }; + + // Safe cast by Safety + match standard_b64encode_impl(source) { + Ok(result) => result, + Err(_) => ptr::null_mut(), + } +} + +fn standard_b64encode_impl(source: &PyObject) -> Result<*mut PyObject, ()> { + let buffer = match BorrowedBuffer::from_object(source) { + Ok(buf) => buf, + Err(_) => return Err(()), + }; + + let view_len = buffer.len(); + if view_len < 0 { + unsafe { + PyErr_SetString( + PyExc_TypeError, + c"standard_b64encode() argument has negative length".as_ptr(), + ); + } + return Err(()); + } + + let input_len = view_len as usize; + let input = unsafe { slice::from_raw_parts(buffer.as_ptr(), input_len) }; + + let Some(output_len) = encoded_output_len(input_len) else { + unsafe { + PyErr_NoMemory(); + } + return Err(()); + }; + + if output_len > isize::MAX as usize { + unsafe { + PyErr_NoMemory(); + } + return Err(()); + } + + let result = unsafe { PyBytes_FromStringAndSize(ptr::null(), output_len as Py_ssize_t) }; + if result.is_null() { + return Err(()); + } + + let dest_ptr = unsafe { PyBytes_AsString(result) }; + if dest_ptr.is_null() { + unsafe { + Py_DecRef(result); + } + return Err(()); + } + let dest = unsafe { slice::from_raw_parts_mut(dest_ptr.cast::(), output_len) }; + + let written = encode_into(input, dest); + debug_assert_eq!(written, output_len); + Ok(result) +} + +#[unsafe(no_mangle)] +pub extern "C" fn _base64_clear(_obj: *mut PyObject) -> c_int { + //TODO + 0 +} + +#[unsafe(no_mangle)] +pub extern "C" fn _base64_free(_o: *mut c_void) { + //TODO +} + +pub struct ModuleDef { + ffi: UnsafeCell, +} + +impl ModuleDef { + fn init_multi_phase(&'static self) -> *mut PyObject { + unsafe { PyModuleDef_Init(self.ffi.get()) } + } +} + +unsafe impl Sync for ModuleDef {} + +pub static _BASE64_MODULE_METHODS: [PyMethodDef; 2] = { + [ + PyMethodDef { + ml_name: c"standard_b64encode".as_ptr() as *mut c_char, + ml_meth: PyMethodDefFuncPointer { + PyCFunctionFast: standard_b64encode, + }, + ml_flags: METH_FASTCALL, + ml_doc: c"Demo for the _base64 module".as_ptr() as *mut c_char, + }, + PyMethodDef::zeroed(), + ] +}; + +pub static _BASE64_MODULE: ModuleDef = { + ModuleDef { + ffi: UnsafeCell::new(PyModuleDef { + m_base: PyModuleDef_HEAD_INIT, + m_name: c"_base64".as_ptr() as *mut _, + m_doc: c"A test Rust module".as_ptr() as *mut _, + m_size: 0, + m_methods: &_BASE64_MODULE_METHODS as *const PyMethodDef as *mut _, + m_slots: std::ptr::null_mut(), + m_traverse: None, + m_clear: Some(_base64_clear), + m_free: Some(_base64_free), + }), + } +}; + +#[unsafe(no_mangle)] +pub extern "C" fn PyInit__base64() -> *mut PyObject { + _BASE64_MODULE.init_multi_phase() +} diff --git a/Modules/cpython-sys/Cargo.toml b/Modules/cpython-sys/Cargo.toml new file mode 100644 index 00000000000000..de0b88d5d5b723 --- /dev/null +++ b/Modules/cpython-sys/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "cpython-sys" +version = "0.1.0" +edition = "2024" + +[dependencies] + +[build-dependencies] +bindgen = "0.72.1" \ No newline at end of file diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs new file mode 100644 index 00000000000000..8256e2fc93cd03 --- /dev/null +++ b/Modules/cpython-sys/build.rs @@ -0,0 +1,67 @@ +use std::env; +use std::path::{Path, PathBuf}; + +fn main() { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let srcdir = manifest_dir + .parent() + .and_then(Path::parent) + .expect("expected Modules/cpython-sys to live under the source tree"); + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + let builddir = env::var("PYTHON_BUILD_DIR").ok(); + if gil_disabled(&srcdir, builddir.as_deref()) { + println!("cargo:rustc-cfg=py_gil_disabled"); + } + generate_c_api_bindings(srcdir, builddir.as_deref(), &out_path.as_path()); + // TODO(emmatyping): generate bindings to the internal parser API + // The parser includes things slightly differently, so we should generate + // it's bindings independently + //generate_parser_bindings(srcdir, &out_path.as_path()); +} + +fn gil_disabled(srcdir: &Path, builddir: Option<&str>) -> bool { + let mut candidates = Vec::new(); + if let Some(build) = builddir { + candidates.push(PathBuf::from(build)); + } + candidates.push(srcdir.to_path_buf()); + for base in candidates { + let path = base.join("pyconfig.h"); + if let Ok(contents) = std::fs::read_to_string(&path) { + if contents.contains("Py_GIL_DISABLED 1") { + return true; + } + } + } + false +} + +fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Path) { + let mut builder = bindgen::Builder::default().header("wrapper.h"); + + // Always search the source dir and the public headers. + let mut include_dirs = vec![srcdir.to_path_buf(), srcdir.join("Include")]; + // Include the build directory if provided; out-of-tree builds place + // the generated pyconfig.h there. + if let Some(build) = builddir { + include_dirs.push(PathBuf::from(build)); + } + for dir in include_dirs { + builder = builder.clang_arg(format!("-I{}", dir.display())); + } + + let bindings = builder + .allowlist_function("_?Py.*") + .allowlist_type("_?Py.*") + .allowlist_var("_?Py.*") + .blocklist_type("^PyMethodDef$") + .blocklist_type("PyObject") + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .generate() + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/c_api.rs file. + bindings + .write_to_file(out_path.join("c_api.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/Modules/cpython-sys/parser.h b/Modules/cpython-sys/parser.h new file mode 100644 index 00000000000000..e58539b7236611 --- /dev/null +++ b/Modules/cpython-sys/parser.h @@ -0,0 +1,11 @@ +/* Private APIs */ +#define Py_BUILD_CORE + +// Parser +#include "Parser/pegen.h" +#include "Parser/string_parser.h" +#include "Parser/lexer/buffer.h" +#include "Parser/lexer/lexer.h" +#include "Parser/lexer/state.h" +#include "Parser/tokenizer/tokenizer.h" +#include "Parser/tokenizer/helpers.h" diff --git a/Modules/cpython-sys/src/lib.rs b/Modules/cpython-sys/src/lib.rs new file mode 100644 index 00000000000000..27765abd4b7fe2 --- /dev/null +++ b/Modules/cpython-sys/src/lib.rs @@ -0,0 +1,144 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(unsafe_op_in_unsafe_fn)] +#![allow(unnecessary_transmutes)] + +use std::ffi::{c_char, c_int, c_void}; + +include!(concat!(env!("OUT_DIR"), "/c_api.rs")); + +// TODO(emmatyping): include parser bindings (see build.rs) +//include!(concat!(env!("OUT_DIR"), "/parser.rs")); +/* Flag passed to newmethodobject */ +/* #define METH_OLDARGS 0x0000 -- unsupported now */ +pub const METH_VARARGS: c_int = 0x0001; +pub const METH_KEYWORDS: c_int = 0x0002; +/* METH_NOARGS and METH_O must not be combined with the flags above. */ +pub const METH_NOARGS: c_int = 0x0004; +pub const METH_O: c_int = 0x0008; + +/* METH_CLASS and METH_STATIC are a little different; these control +the construction of methods for a class. These cannot be used for +functions in modules. */ +pub const METH_CLASS: c_int = 0x0010; +pub const METH_STATIC: c_int = 0x0020; + +/* METH_COEXIST allows a method to be entered even though a slot has +already filled the entry. When defined, the flag allows a separate +method, "__contains__" for example, to coexist with a defined +slot like sq_contains. */ + +pub const METH_COEXIST: c_int = 0x0040; + +pub const METH_FASTCALL: c_int = 0x0080; + +/* This bit is preserved for Stackless Python +pub const METH_STACKLESS: c_int = 0x0100; +pub const METH_STACKLESS: c_int = 0x0000; +*/ + +/* METH_METHOD means the function stores an + * additional reference to the class that defines it; + * both self and class are passed to it. + * It uses PyCMethodObject instead of PyCFunctionObject. + * May not be combined with METH_NOARGS, METH_O, METH_CLASS or METH_STATIC. + */ + +pub const METH_METHOD: c_int = 0x0200; + +#[cfg(target_pointer_width = "64")] +pub const _Py_STATIC_FLAG_BITS: Py_ssize_t = + (_Py_STATICALLY_ALLOCATED_FLAG | _Py_IMMORTAL_FLAGS) as Py_ssize_t; +#[cfg(target_pointer_width = "64")] +pub const _Py_STATIC_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = + (_Py_IMMORTAL_INITIAL_REFCNT as Py_ssize_t) | (_Py_STATIC_FLAG_BITS << 48); +#[cfg(not(target_pointer_width = "64"))] +pub const _Py_STATIC_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = 7u32 << 28; + +#[repr(transparent)] +pub struct PyObject(std::cell::UnsafeCell<_object>); + +impl PyObject { + #[inline] + pub fn as_raw(&self) -> *mut Self { + self.0.get() as *mut Self + } +} + +#[repr(C)] +pub union PyMethodDefFuncPointer { + pub PyCFunction: unsafe extern "C" fn(slf: *mut PyObject, args: *mut PyObject) -> *mut PyObject, + pub PyCFunctionFast: unsafe extern "C" fn( + slf: *mut PyObject, + args: *mut *mut PyObject, + nargs: Py_ssize_t, + ) -> *mut PyObject, + pub PyCFunctionWithKeywords: unsafe extern "C" fn( + slf: *mut PyObject, + args: *mut PyObject, + kwargs: *mut PyObject, + ) -> *mut PyObject, + pub PyCFunctionFastWithKeywords: unsafe extern "C" fn( + slf: *mut PyObject, + args: *mut *mut PyObject, + nargs: Py_ssize_t, + kwargs: *mut PyObject, + ) -> *mut PyObject, + pub PyCMethod: unsafe extern "C" fn( + slf: *mut PyObject, + typ: *mut PyTypeObject, + args: *mut *mut PyObject, + nargs: Py_ssize_t, + kwargs: *mut PyObject, + ) -> *mut PyObject, + pub Void: *mut c_void, +} + +#[repr(C)] +pub struct PyMethodDef { + pub ml_name: *mut c_char, + pub ml_meth: PyMethodDefFuncPointer, + pub ml_flags: c_int, + pub ml_doc: *mut c_char, +} + +impl PyMethodDef { + pub const fn zeroed() -> Self { + Self { + ml_name: std::ptr::null_mut(), + ml_meth: PyMethodDefFuncPointer { + Void: std::ptr::null_mut(), + }, + ml_flags: 0, + ml_doc: std::ptr::null_mut(), + } + } +} + +// TODO: this is pretty unsafe, we should probably wrap this in a nicer +// abstraction +unsafe impl Sync for PyMethodDef {} +unsafe impl Send for PyMethodDef {} + +#[cfg(py_gil_disabled)] +pub const PyObject_HEAD_INIT: PyObject = { + let mut obj: _object = unsafe { std::mem::MaybeUninit::zeroed().assume_init() }; + obj.ob_flags = _Py_STATICALLY_ALLOCATED_FLAG as _; + PyObject(std::cell::UnsafeCell::new(obj)) +}; + +#[cfg(not(py_gil_disabled))] +pub const PyObject_HEAD_INIT: PyObject = PyObject(std::cell::UnsafeCell::new(_object { + __bindgen_anon_1: _object__bindgen_ty_1 { + ob_refcnt_full: _Py_STATIC_IMMORTAL_INITIAL_REFCNT as i64, + }, + ob_type: std::ptr::null_mut(), +})); + +pub const PyModuleDef_HEAD_INIT: PyModuleDef_Base = PyModuleDef_Base { + ob_base: PyObject_HEAD_INIT, + m_init: None, + m_index: 0, + m_copy: std::ptr::null_mut(), +}; diff --git a/Modules/cpython-sys/wrapper.h b/Modules/cpython-sys/wrapper.h new file mode 100644 index 00000000000000..f79a44f0580633 --- /dev/null +++ b/Modules/cpython-sys/wrapper.h @@ -0,0 +1,177 @@ +/* Public APIs */ +#include "Python.h" + +// Misc + +// OS macros used in C, not necessary in Rust +//#include "osdefs.h" + +// Valgrind / analysis tools macros and functions +#include "dynamic_annotations.h" +// Macros for error codes +// #include "errcode.h" +// Macros to define symbol visibility +// #include "exports.h" +// Includes pyframe.h and cpython/frameobject.h +#include "frameobject.h" +// Includes cpython/marshal.h +#include "marshal.h" +// Macros defining opcodes +#include "opcode.h" +// More macros defining opcodes +#include "opcode_ids.h" +// Dtrace probes +#include "pydtrace.h" +//New code should use descrobject.h +//#include "structmember.h" + +// List of all stdlib names, autogenerated +#include "Python/stdlib_module_names.h" + +/* Private APIs */ +#define Py_BUILD_CORE + +// Internal +#include "internal/pycore_parser.h" +#include "internal/pycore_mimalloc.h" +#include "internal/mimalloc/mimalloc.h" +#include "internal/mimalloc/mimalloc/atomic.h" +#include "internal/mimalloc/mimalloc/internal.h" +#include "internal/mimalloc/mimalloc/prim.h" +#include "internal/mimalloc/mimalloc/track.h" +#include "internal/mimalloc/mimalloc/types.h" +#include "internal/pycore_abstract.h" +#include "internal/pycore_asdl.h" +#include "internal/pycore_ast.h" +#include "internal/pycore_ast_state.h" +#include "internal/pycore_atexit.h" +#include "internal/pycore_audit.h" +#include "internal/pycore_backoff.h" +#include "internal/pycore_bitutils.h" +#include "internal/pycore_blocks_output_buffer.h" +#include "internal/pycore_brc.h" +#include "internal/pycore_bytes_methods.h" +#include "internal/pycore_bytesobject.h" +#include "internal/pycore_call.h" +#include "internal/pycore_capsule.h" +#include "internal/pycore_cell.h" +#include "internal/pycore_ceval.h" +#include "internal/pycore_ceval_state.h" +#include "internal/pycore_code.h" +#include "internal/pycore_codecs.h" +#include "internal/pycore_compile.h" +#include "internal/pycore_complexobject.h" +#include "internal/pycore_condvar.h" +#include "internal/pycore_context.h" +#include "internal/pycore_critical_section.h" +#include "internal/pycore_crossinterp.h" +#include "internal/pycore_debug_offsets.h" +#include "internal/pycore_descrobject.h" +#include "internal/pycore_dict.h" +#include "internal/pycore_dict_state.h" +#include "internal/pycore_dtoa.h" +#include "internal/pycore_exceptions.h" +#include "internal/pycore_faulthandler.h" +#include "internal/pycore_fileutils.h" +#include "internal/pycore_floatobject.h" +#include "internal/pycore_flowgraph.h" +#include "internal/pycore_format.h" +#include "internal/pycore_frame.h" +#include "internal/pycore_freelist.h" +#include "internal/pycore_freelist_state.h" +#include "internal/pycore_function.h" +#include "internal/pycore_gc.h" +#include "internal/pycore_genobject.h" +#include "internal/pycore_getopt.h" +#include "internal/pycore_gil.h" +#include "internal/pycore_global_objects.h" +#include "internal/pycore_global_objects_fini_generated.h" +#include "internal/pycore_global_strings.h" +#include "internal/pycore_hamt.h" +#include "internal/pycore_hashtable.h" +#include "internal/pycore_import.h" +#include "internal/pycore_importdl.h" +#include "internal/pycore_index_pool.h" +#include "internal/pycore_initconfig.h" +#include "internal/pycore_instruments.h" +#include "internal/pycore_instruction_sequence.h" +#include "internal/pycore_interp.h" +#include "internal/pycore_interp_structs.h" +#include "internal/pycore_interpframe.h" +#include "internal/pycore_interpframe_structs.h" +#include "internal/pycore_interpolation.h" +#include "internal/pycore_intrinsics.h" +#include "internal/pycore_jit.h" +#include "internal/pycore_list.h" +#include "internal/pycore_llist.h" +#include "internal/pycore_lock.h" +#include "internal/pycore_long.h" +#include "internal/pycore_memoryobject.h" +#include "internal/pycore_mimalloc.h" +#include "internal/pycore_modsupport.h" +#include "internal/pycore_moduleobject.h" +#include "internal/pycore_namespace.h" +#include "internal/pycore_object.h" +#include "internal/pycore_object_alloc.h" +#include "internal/pycore_object_deferred.h" +#include "internal/pycore_object_stack.h" +#include "internal/pycore_object_state.h" +#include "internal/pycore_obmalloc.h" +#include "internal/pycore_obmalloc_init.h" +#include "internal/pycore_opcode_metadata.h" +#include "internal/pycore_opcode_utils.h" +#include "internal/pycore_optimizer.h" +#include "internal/pycore_parking_lot.h" +#include "internal/pycore_parser.h" +#include "internal/pycore_pathconfig.h" +#include "internal/pycore_pyarena.h" +#include "internal/pycore_pyatomic_ft_wrappers.h" +#include "internal/pycore_pybuffer.h" +#include "internal/pycore_pyerrors.h" +#include "internal/pycore_pyhash.h" +#include "internal/pycore_pylifecycle.h" +#include "internal/pycore_pymath.h" +#include "internal/pycore_pymem.h" +#include "internal/pycore_pymem_init.h" +#include "internal/pycore_pystate.h" +#include "internal/pycore_pystats.h" +#include "internal/pycore_pythonrun.h" +#include "internal/pycore_pythread.h" +#include "internal/pycore_qsbr.h" +#include "internal/pycore_range.h" +#include "internal/pycore_runtime.h" +#include "internal/pycore_runtime_init.h" +#include "internal/pycore_runtime_init_generated.h" +#include "internal/pycore_runtime_structs.h" +#include "internal/pycore_semaphore.h" +#include "internal/pycore_setobject.h" +#include "internal/pycore_signal.h" +#include "internal/pycore_sliceobject.h" +#include "internal/pycore_stats.h" +#include "internal/pycore_strhex.h" +#include "internal/pycore_stackref.h" +#include "internal/pycore_structs.h" +#include "internal/pycore_structseq.h" +#include "internal/pycore_symtable.h" +#include "internal/pycore_sysmodule.h" +#include "internal/pycore_template.h" +#include "internal/pycore_time.h" +#include "internal/pycore_token.h" +#include "internal/pycore_traceback.h" +#include "internal/pycore_tracemalloc.h" +#include "internal/pycore_tstate.h" +#include "internal/pycore_tuple.h" +#include "internal/pycore_typedefs.h" +#include "internal/pycore_typeobject.h" +#include "internal/pycore_typevarobject.h" +#include "internal/pycore_ucnhash.h" +#include "internal/pycore_unicodectype.h" +#include "internal/pycore_unicodeobject.h" +#include "internal/pycore_unicodeobject_generated.h" +#include "internal/pycore_unionobject.h" +#include "internal/pycore_uniqueid.h" +#include "internal/pycore_uop.h" +#include "internal/pycore_uop_ids.h" +#include "internal/pycore_uop_metadata.h" +#include "internal/pycore_warnings.h" +#include "internal/pycore_weakref.h" diff --git a/Modules/makesetup b/Modules/makesetup index 104c824b846540..586e26dd5891b0 100755 --- a/Modules/makesetup +++ b/Modules/makesetup @@ -83,6 +83,8 @@ case $makepre in '') makepre=Makefile.pre;; esac +UNAME_SYSTEM=`uname -s 2>/dev/null || echo unknown` + # Newline for sed i and a commands NL='\ ' @@ -144,6 +146,8 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | srcs= cpps= libs= + rust= + manifest= mods= mods_upper= skip= @@ -176,6 +180,8 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | *.c++) srcs="$srcs $arg";; *.cxx) srcs="$srcs $arg";; *.cpp) srcs="$srcs $arg";; + *.rs) srcs="$srcs $arg"; rust="true";; + *.toml) manifest="$arg";; \$\(*_CFLAGS\)) cpps="$cpps $arg";; \$\(*_INCLUDES\)) cpps="$cpps $arg";; \$\(*_LIBS\)) libs="$libs $arg";; @@ -226,44 +232,75 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | yes) continue;; esac objs='' - for src in $srcs - do - case $src in - *.c) obj=`basename $src .c`.o; cc='$(CC)';; - *.cc) obj=`basename $src .cc`.o; cc='$(CXX)';; - *.c++) obj=`basename $src .c++`.o; cc='$(CXX)';; - *.C) obj=`basename $src .C`.o; cc='$(CXX)';; - *.cxx) obj=`basename $src .cxx`.o; cc='$(CXX)';; - *.cpp) obj=`basename $src .cpp`.o; cc='$(CXX)';; - *.m) obj=`basename $src .m`.o; cc='$(CC)';; # Obj-C - *) continue;; - esac - case $src in - */*) obj="$srcdir/`dirname $src`/$obj";; - *) obj="$srcdir/$obj";; - esac - objs="$objs $obj" - case $src in - glmodule.c) ;; - /*) ;; - \$*) ;; - *) src='$(srcdir)/'"$srcdir/$src";; - esac - # custom flags first, PY_STDMODULE_CFLAGS may contain -I with system libmpdec - case $doconfig in - no) - cc="$cc $cpps \$(PY_STDMODULE_CFLAGS) \$(CCSHARED)" - rule="$obj: $src \$(MODULE_${mods_upper}_DEPS) \$(MODULE_DEPS_SHARED) \$(PYTHON_HEADERS)" - rule="$rule; $cc -c $src -o $obj" - ;; - *) - cc="$cc $cpps \$(PY_BUILTIN_MODULE_CFLAGS)" - rule="$obj: $src \$(MODULE_${mods_upper}_DEPS) \$(MODULE_DEPS_STATIC) \$(PYTHON_HEADERS)" - rule="$rule; $cc -c $src -o $obj" - ;; - esac + custom_ldflags='' + if test "x$rust" = "x"; then + for src in $srcs + do + case $src in + *.c) obj=`basename $src .c`.o; cc='$(CC)';; + *.cc) obj=`basename $src .cc`.o; cc='$(CXX)';; + *.c++) obj=`basename $src .c++`.o; cc='$(CXX)';; + *.C) obj=`basename $src .C`.o; cc='$(CXX)';; + *.cxx) obj=`basename $src .cxx`.o; cc='$(CXX)';; + *.cpp) obj=`basename $src .cpp`.o; cc='$(CXX)';; + *.m) obj=`basename $src .m`.o; cc='$(CC)';; # Obj-C + *) continue;; + esac + case $src in + */*) obj="$srcdir/`dirname $src`/$obj";; + *) obj="$srcdir/$obj";; + esac + objs="$objs $obj" + case $src in + glmodule.c) ;; + /*) ;; + \$*) ;; + *) src='$(srcdir)/'"$srcdir/$src";; + esac + # custom flags first, PY_STDMODULE_CFLAGS may contain -I with system libmpdec + case $doconfig in + no) + cc="$cc $cpps \$(PY_STDMODULE_CFLAGS) \$(CCSHARED)" + rule="$obj: $src \$(MODULE_${mods_upper}_DEPS) \$(MODULE_DEPS_SHARED) \$(PYTHON_HEADERS)" + rule="$rule; $cc -c $src -o $obj" + ;; + *) + cc="$cc $cpps \$(PY_BUILTIN_MODULE_CFLAGS)" + rule="$obj: $src \$(MODULE_${mods_upper}_DEPS) \$(MODULE_DEPS_STATIC) \$(PYTHON_HEADERS)" + rule="$rule; $cc -c $src -o $obj" + ;; + esac + echo "$rule" >>$rulesf + done + else + prefixed_srcs= + for src in $srcs + do + prefixed_srcs="$prefixed_srcs $srcdir/$src" + done + objs= + # there's actually only one obj, so just set it to the lib + for lib in $libs + do + objs="target/\$(CARGO_TARGET_DIR)/$lib" + done + libs= + # depends on the headers through cpython-sys + rule="$objs: cpython-sys \$(srcdir)/Cargo.toml \$(srcdir)/Cargo.lock \$(srcdir)/$srcdir/$manifest $prefixed_srcs \$(PYTHON_HEADERS)" + rule="$rule; CARGO_TARGET_DIR=\$(abs_builddir)/target PYTHON_BUILD_DIR=\$(abs_builddir) \$(CARGO_HOME)/bin/cargo build --lib --locked --package ${mods} --profile \$(CARGO_PROFILE) \$(if \$(CARGO_TARGET),--target=\$(CARGO_TARGET)) --manifest-path \$(srcdir)/Cargo.toml" echo "$rule" >>$rulesf - done + for mod in $mods + do + case $UNAME_SYSTEM in + Darwin*) + custom_ldflags="$custom_ldflags -Wl,-u,_PyInit_$mod" + ;; + *) + custom_ldflags="$custom_ldflags -Wl,--defsym=PyInit_$mod=PyInit_$mod" + ;; + esac + done + fi case $doconfig in yes) OBJS="$OBJS $objs";; esac @@ -277,7 +314,7 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | ;; esac rule="$file: $objs \$(MODULE_${mods_upper}_LDEPS)" - rule="$rule; \$(BLDSHARED) $objs $libs \$(LIBPYTHON) -o $file" + rule="$rule; \$(BLDSHARED) $custom_ldflags $objs $libs \$(LIBPYTHON) -o $file" echo "$rule" >>$rulesf done done diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h index 8937e666bbbdd5..54cbc7106069bc 100644 --- a/Python/stdlib_module_names.h +++ b/Python/stdlib_module_names.h @@ -10,6 +10,7 @@ static const char* _Py_stdlib_module_names[] = { "_ast", "_ast_unparse", "_asyncio", +"_base64", "_bisect", "_blake2", "_bz2", diff --git a/Tools/build/generate_stdlib_module_names.py b/Tools/build/generate_stdlib_module_names.py index bda72539640611..646f31b49761c1 100644 --- a/Tools/build/generate_stdlib_module_names.py +++ b/Tools/build/generate_stdlib_module_names.py @@ -3,6 +3,7 @@ from __future__ import annotations import _imp +import os import os.path import sys import sysconfig @@ -14,6 +15,7 @@ SRC_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) STDLIB_PATH = os.path.join(SRC_DIR, 'Lib') +MODULES_PATH = os.path.join(SRC_DIR, 'Modules') IGNORE = { '__init__', @@ -84,6 +86,19 @@ def list_modules_setup_extensions(names: set[str]) -> None: names.update(checker.list_module_names(all=True)) +def list_rust_modules(names: set[str]) -> None: + if not os.path.isdir(MODULES_PATH): + return + for entry in os.scandir(MODULES_PATH): + if not entry.is_dir(): + continue + if entry.name == "cpython-sys": + continue + cargo_toml = os.path.join(entry.path, "Cargo.toml") + if os.path.isfile(cargo_toml): + names.add(entry.name) + + # List frozen modules of the PyImport_FrozenModules list (Python/frozen.c). # Use the "./Programs/_testembed list_frozen" command. def list_frozen(names: set[str]) -> None: @@ -109,6 +124,7 @@ def list_modules() -> set[str]: list_builtin_modules(names) list_modules_setup_extensions(names) + list_rust_modules(names) list_packages(names) list_python_modules(names) list_frozen(names) diff --git a/configure b/configure index eeb24c1d844e86..8af7ee0e042e81 100755 --- a/configure +++ b/configure @@ -687,6 +687,8 @@ MODULE_BINASCII_FALSE MODULE_BINASCII_TRUE MODULE_ZLIB_FALSE MODULE_ZLIB_TRUE +MODULE__BASE64_FALSE +MODULE__BASE64_TRUE MODULE__UUID_FALSE MODULE__UUID_TRUE MODULE__TKINTER_FALSE @@ -885,6 +887,11 @@ TCLTK_LIBS TCLTK_CFLAGS LIBSQLITE3_LIBS LIBSQLITE3_CFLAGS +CARGO_TARGET +CARGO_PROFILE +CARGO_TARGET_DIR +CARGO_HOME +HAVE_CARGO LIBMPDEC_INTERNAL LIBMPDEC_LIBS LIBMPDEC_CFLAGS @@ -16034,6 +16041,85 @@ fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Rust" >&5 +printf %s "checking for Rust... " >&6; } +CARGO_TARGET_DIR= +CARGO_PROFILE= +if test "x$CARGO_HOME" = "x"; then + CARGO_HOME="$HOME/.cargo" +fi +# Extract the first word of "cargo", so it can be a program name with args. +set dummy cargo; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_HAVE_CARGO+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) if test -n "$HAVE_CARGO"; then + ac_cv_prog_HAVE_CARGO="$HAVE_CARGO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in no +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_HAVE_CARGO=""$CARGO_HOME"" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_HAVE_CARGO" && ac_cv_prog_HAVE_CARGO="yes" +fi ;; +esac +fi +HAVE_CARGO=$ac_cv_prog_HAVE_CARGO +if test -n "$HAVE_CARGO"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $HAVE_CARGO" >&5 +printf "%s\n" "$HAVE_CARGO" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +if test $HAVE_CARGO = "no"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: n/a" >&5 +printf "%s\n" "n/a" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Could not find the cargo executable. It can be installed via rustup" >&5 +printf "%s\n" "$as_me: WARNING: Could not find the cargo executable. It can be installed via rustup" >&2;} +else + if test "$Py_OPT" = 'true'; then + CARGO_TARGET_DIR='release' + CARGO_PROFILE='release' + else + CARGO_TARGET_DIR='debug' + CARGO_PROFILE='dev' + fi + # Set CARGO_TARGET for cross-compilation + case "$host" in + aarch64-apple-ios-simulator) + CARGO_TARGET="aarch64-apple-ios-sim" + ;; + *) + CARGO_TARGET="$host" + ;; + esac +fi + + + + + @@ -33479,6 +33565,47 @@ printf "%s\n" "$py_cv_module__uuid" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _base64" >&5 +printf %s "checking for stdlib extension module _base64... " >&6; } + if test "$py_cv_module__base64" != "n/a" +then : + + if test "$HAVE_CARGO" = yes +then : + if test "$HAVE_CARGO" = yes +then : + py_cv_module__base64=yes +else case e in #( + e) py_cv_module__base64=missing ;; +esac +fi +else case e in #( + e) py_cv_module__base64=disabled ;; +esac +fi + +fi + as_fn_append MODULE_BLOCK "MODULE__BASE64_STATE=$py_cv_module__base64$as_nl" + if test "x$py_cv_module__base64" = xyes +then : + + + + +fi + if test "$py_cv_module__base64" = yes; then + MODULE__BASE64_TRUE= + MODULE__BASE64_FALSE='#' +else + MODULE__BASE64_TRUE='#' + MODULE__BASE64_FALSE= +fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $py_cv_module__base64" >&5 +printf "%s\n" "$py_cv_module__base64" >&6; } + + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module zlib" >&5 printf %s "checking for stdlib extension module zlib... " >&6; } if test "$py_cv_module_zlib" != "n/a" @@ -34715,6 +34842,10 @@ if test -z "${MODULE__UUID_TRUE}" && test -z "${MODULE__UUID_FALSE}"; then as_fn_error $? "conditional \"MODULE__UUID\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${MODULE__BASE64_TRUE}" && test -z "${MODULE__BASE64_FALSE}"; then + as_fn_error $? "conditional \"MODULE__BASE64\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${MODULE_ZLIB_TRUE}" && test -z "${MODULE_ZLIB_FALSE}"; then as_fn_error $? "conditional \"MODULE_ZLIB\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/configure.ac b/configure.ac index 92adc44da0d6fe..a3cc99c8add8c3 100644 --- a/configure.ac +++ b/configure.ac @@ -4307,6 +4307,42 @@ fi AC_SUBST([LIBMPDEC_CFLAGS]) AC_SUBST([LIBMPDEC_INTERNAL]) +dnl Try to detect cargo in the environment. Cargo and rustup +dnl install into CARGO_HOME and RUSTUP_HOME, so check for those initially +AC_MSG_CHECKING([for Rust]) +CARGO_TARGET_DIR= +CARGO_PROFILE= +if test "x$CARGO_HOME" = "x"; then + dnl try to guess the default UNIX value of ~/.cargo + CARGO_HOME="$HOME/.cargo" +fi +AC_CHECK_PROG(HAVE_CARGO, [cargo], ["$CARGO_HOME"], [yes], [no]) +if test $HAVE_CARGO = "no"; then + AC_MSG_RESULT([n/a]) + AC_MSG_WARN([Could not find the cargo executable. It can be installed via rustup]) +else + if test "$Py_OPT" = 'true'; then + CARGO_TARGET_DIR='release' + CARGO_PROFILE='release' + else + CARGO_TARGET_DIR='debug' + CARGO_PROFILE='dev' + fi + # Set CARGO_TARGET for cross-compilation + case "$host" in + aarch64-apple-ios-simulator) + CARGO_TARGET="aarch64-apple-ios-sim" + ;; + *) + CARGO_TARGET="$host" + ;; + esac +fi +AC_SUBST([CARGO_HOME]) +AC_SUBST([CARGO_TARGET_DIR]) +AC_SUBST([CARGO_PROFILE]) +AC_SUBST([CARGO_TARGET]) + dnl detect sqlite3 from Emscripten emport PY_CHECK_EMSCRIPTEN_PORT([LIBSQLITE3], [-sUSE_SQLITE3]) @@ -8156,6 +8192,10 @@ PY_STDLIB_MOD([_uuid], [], [test "$have_uuid" = "yes"], [$LIBUUID_CFLAGS], [$LIBUUID_LIBS]) +PY_STDLIB_MOD([_base64], + [test "$HAVE_CARGO" = yes], [test "$HAVE_CARGO" = yes], + [], []) + dnl compression libs PY_STDLIB_MOD([zlib], [], [test "$have_zlib" = yes], [$ZLIB_CFLAGS], [$ZLIB_LIBS]) diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000000000..da064b583d29e7 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.91.1" \ No newline at end of file