Add "extension-module" feature to suppress linking pythonX.Y.so.
The symbols are instead kept unresolved, so that they can be used with any compatible python interpreter, even if the target system uses a statically linked python and lacks pythonX.Y.so altogether.
This commit is contained in:
parent
62a083e38f
commit
e2d7781433
13
Cargo.toml
13
Cargo.toml
|
@ -46,6 +46,16 @@ default = ["python3-sys"]
|
|||
# Enable additional features that require nightly rust
|
||||
nightly = []
|
||||
|
||||
# Use this feature when building an extension module.
|
||||
# It tells the linker to keep the python symbols unresolved,
|
||||
# so that the module can also be used with statically linked python interpreters.
|
||||
extension-module = [ "python3-sys/extension-module" ]
|
||||
|
||||
# Unfortunately we can't use the forward the same feature to either python27-sys
|
||||
# or python3-sys. (honestly, we should probably merge both crates into 'python-sys')
|
||||
extension-module-2-7 = [ "python27-sys/extension-module" ]
|
||||
|
||||
|
||||
# Optional features to support explicitly specifying python minor version.
|
||||
# If you don't care which minor version, just specify python3-sys as a
|
||||
# feature.
|
||||
|
@ -55,3 +65,6 @@ python-3-4 = ["python3-sys/python-3-4"]
|
|||
|
||||
#pep-384 = ["python3-sys/pep-384"]
|
||||
|
||||
[workspace]
|
||||
members = ["python27-sys", "python3-sys", "extensions/hello"]
|
||||
|
||||
|
|
2
Makefile
2
Makefile
|
@ -49,7 +49,7 @@ doc: build
|
|||
cargo doc --no-deps $(CARGO_FLAGS)
|
||||
|
||||
extensions: build
|
||||
make -C extensions/ PY=$(PY)
|
||||
make -C extensions/tests PY=$(PY)
|
||||
|
||||
clean:
|
||||
rm -r target
|
||||
|
|
21
extensions/hello/Cargo.toml
Normal file
21
extensions/hello/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "hello"
|
||||
version = "0.1.0"
|
||||
authors = ["Daniel Grunwald <daniel@danielgrunwald.de>"]
|
||||
|
||||
[lib]
|
||||
## Python extension modules should be compiled as 'cdylib'
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies.cpython]
|
||||
path = "../.."
|
||||
features = ["extension-module"]
|
||||
# The 'extension-module' feature allows using the resulting binary module
|
||||
# with statically linked python interpreters.
|
||||
|
||||
## By default, cpython will use whichever Python 3.x interpreter is found in PATH.
|
||||
## To target Python 2.7, use:
|
||||
#default-features=false
|
||||
#features = ["python27-sys", "extension-module-2-7"]
|
||||
|
||||
|
38
extensions/hello/src/hello.rs
Normal file
38
extensions/hello/src/hello.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
#[macro_use] extern crate cpython;
|
||||
|
||||
use cpython::{PyObject, PyResult, Python, PyTuple, PyDict};
|
||||
|
||||
// Our module is named 'hello', and can be imported using `import hello`.
|
||||
// This requires that the output binary file is named `hello.so` (or Windows: `hello.pyd`).
|
||||
// As the output name cannot be configured in cargo (https://github.com/rust-lang/cargo/issues/1970),
|
||||
// you'll have to rename the output file.
|
||||
py_module_initializer!(hello, inithello, PyInit_hello, |py, m| {
|
||||
m.add(py, "__doc__", "Module documentation string")?;
|
||||
m.add(py, "func", py_fn!(py, func(a: &str, b: i32)))?;
|
||||
m.add(py, "run", py_fn!(py, run(*args, **kwargs)))?;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// The py_fn!()-macro can translate between Python and Rust values,
|
||||
// so you can use `&str`, `i32` or `String` in the signature of a function
|
||||
// callable from Python.
|
||||
// The first argument of type `Python<'p>` is used to indicate that your
|
||||
// function may assume that the current thread holds the global interpreter lock.
|
||||
// Most functions in the `cpython` crate require that you pass this argument.
|
||||
fn func(_: Python, a: &str, b: i32) -> PyResult<String> {
|
||||
Ok(format!("func({}, {})", a, b))
|
||||
}
|
||||
|
||||
fn run(py: Python, args: &PyTuple, kwargs: Option<&PyDict>) -> PyResult<PyObject> {
|
||||
println!("Rust says: Hello Python!");
|
||||
for arg in args.iter(py) {
|
||||
println!("Rust got {}", arg);
|
||||
}
|
||||
if let Some(kwargs) = kwargs {
|
||||
for (key, val) in kwargs.items(py) {
|
||||
println!("{} = {}", key, val);
|
||||
}
|
||||
}
|
||||
Ok(py.None())
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
TARGETDIR=../target/debug
|
||||
TARGETDIR=../../target/debug
|
||||
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
|
||||
|
||||
.PHONY: all clean
|
||||
|
@ -27,10 +27,10 @@ clean:
|
|||
stamps:
|
||||
mkdir stamps
|
||||
|
||||
stamps/rust-cpython-$(PY): $(call rwildcard,../src,*.rs) Makefile | stamps
|
||||
stamps/rust-cpython-$(PY): $(call rwildcard,../../src,*.rs) Makefile | stamps
|
||||
-rm stamps/rust-cpython-*
|
||||
cd .. && make build PY=$(PY)
|
||||
touch $@
|
||||
cd ../.. && make build PY=$(PY)
|
||||
touch "$@"
|
||||
|
||||
%.so: %.rs stamps/rust-cpython-$(PY)
|
||||
rustc $< -g -L $(TARGETDIR) -L $(TARGETDIR)/deps -o $@
|
|
@ -18,6 +18,7 @@ exclude = [
|
|||
".gitignore",
|
||||
".travis.yml",
|
||||
]
|
||||
workspace = ".."
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
|
@ -36,3 +37,9 @@ regex = "0.1"
|
|||
# where supporting multiple 3.x's is more important.
|
||||
default = ["python-2-7"]
|
||||
python-2-7 = []
|
||||
|
||||
# Use this feature when building an extension module.
|
||||
# It tells the linker to keep the python symbols unresolved,
|
||||
# so that the module can also be used with statically linked python interpreters.
|
||||
extension-module = [ ]
|
||||
|
||||
|
|
|
@ -297,13 +297,14 @@ fn configure_from_path(expected_version: &PythonVersion) -> Result<String, Strin
|
|||
let enable_shared: &str = &lines[2];
|
||||
let exec_prefix: &str = &lines[3];
|
||||
|
||||
println!("{}", get_rustc_link_lib(&interpreter_version,
|
||||
enable_shared == "1").unwrap());
|
||||
|
||||
if libpath != "None" {
|
||||
println!("cargo:rustc-link-search=native={}", libpath);
|
||||
} else if cfg!(target_os="windows") {
|
||||
println!("cargo:rustc-link-search=native={}\\libs", exec_prefix);
|
||||
let is_extension_module = env::var_os("CARGO_FEATURE_EXTENSION_MODULE").is_some();
|
||||
if !is_extension_module || cfg!(target_os="windows") {
|
||||
println!("{}", get_rustc_link_lib(&interpreter_version, enable_shared == "1").unwrap());
|
||||
if libpath != "None" {
|
||||
println!("cargo:rustc-link-search=native={}", libpath);
|
||||
} else if cfg!(target_os="windows") {
|
||||
println!("cargo:rustc-link-search=native={}\\libs", exec_prefix);
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(interpreter_path);
|
||||
|
|
|
@ -18,6 +18,7 @@ exclude = [
|
|||
".gitignore",
|
||||
".travis.yml",
|
||||
]
|
||||
workspace = ".."
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
|
@ -30,6 +31,11 @@ regex = "0.1"
|
|||
# to try to bind to.
|
||||
default = ["python-3"]
|
||||
|
||||
# Use this feature when building an extension module.
|
||||
# It tells the linker to keep the python symbols unresolved,
|
||||
# so that the module can also be used with statically linked python interpreters.
|
||||
extension-module = [ ]
|
||||
|
||||
# Bind to any python 3.x.
|
||||
python-3 = []
|
||||
|
||||
|
|
|
@ -298,8 +298,16 @@ fn configure_from_path(expected_version: &PythonVersion) -> Result<String, Strin
|
|||
let ld_version: &str = &lines[3];
|
||||
let exec_prefix: &str = &lines[4];
|
||||
|
||||
println!("{}", get_rustc_link_lib(&interpreter_version,
|
||||
ld_version, enable_shared == "1").unwrap());
|
||||
let is_extension_module = env::var_os("CARGO_FEATURE_EXTENSION_MODULE").is_some();
|
||||
if !is_extension_module || cfg!(target_os="windows") {
|
||||
println!("{}", get_rustc_link_lib(&interpreter_version,
|
||||
ld_version, enable_shared == "1").unwrap());
|
||||
if libpath != "None" {
|
||||
println!("cargo:rustc-link-search=native={}", libpath);
|
||||
} else if cfg!(target_os="windows") {
|
||||
println!("cargo:rustc-link-search=native={}\\libs", exec_prefix);
|
||||
}
|
||||
}
|
||||
|
||||
let is_pep_384 = env::var_os("CARGO_FEATURE_PEP_384").is_some();
|
||||
if is_pep_384 {
|
||||
|
@ -312,12 +320,6 @@ fn configure_from_path(expected_version: &PythonVersion) -> Result<String, Strin
|
|||
}
|
||||
}
|
||||
|
||||
if libpath != "None" {
|
||||
println!("cargo:rustc-link-search=native={}", libpath);
|
||||
} else if cfg!(target_os="windows") {
|
||||
println!("cargo:rustc-link-search=native={}\\libs", exec_prefix);
|
||||
}
|
||||
|
||||
return Ok(interpreter_path);
|
||||
}
|
||||
|
||||
|
|
30
src/lib.rs
30
src/lib.rs
|
@ -212,13 +212,12 @@ pub mod _detail {
|
|||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// #![crate_type = "dylib"]
|
||||
/// #[macro_use] extern crate cpython;
|
||||
/// use cpython::{Python, PyResult, PyObject};
|
||||
///
|
||||
/// py_module_initializer!(example, initexample, PyInit_example, |py, m| {
|
||||
/// try!(m.add(py, "__doc__", "Module documentation string"));
|
||||
/// try!(m.add(py, "run", py_fn!(py, run())));
|
||||
/// py_module_initializer!(hello, inithello, PyInit_hello, |py, m| {
|
||||
/// m.add(py, "__doc__", "Module documentation string")?;
|
||||
/// m.add(py, "run", py_fn!(py, run()))?;
|
||||
/// Ok(())
|
||||
/// });
|
||||
///
|
||||
|
@ -228,16 +227,29 @@ pub mod _detail {
|
|||
/// }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
/// The code must be compiled into a file `example.so`.
|
||||
///
|
||||
/// In your `Cargo.toml`, use the `extension-module` feature for the `cpython` dependency:
|
||||
/// ```cargo
|
||||
/// [dependencies.cpython]
|
||||
/// version = "*"
|
||||
/// features = ["extension-module"]
|
||||
/// ```
|
||||
/// The full example project can be found at:
|
||||
/// https://github.com/dgrunwald/rust-cpython/tree/master/extensions/hello
|
||||
///
|
||||
/// Rust will compile the code into a file named `libhello.so`, but we have to
|
||||
/// rename the file in order to use it with Python:
|
||||
///
|
||||
/// ```bash
|
||||
/// rustc example.rs -o example.so
|
||||
/// cp ./target/debug/libhello.so ./hello.so
|
||||
/// ```
|
||||
/// It can then be imported into Python:
|
||||
/// (Note: on Mac OS you will have to rename `libhello.dynlib` to `libhello.so`)
|
||||
///
|
||||
/// The extension module can then be imported into Python:
|
||||
///
|
||||
/// ```python
|
||||
/// >>> import example
|
||||
/// >>> example.run()
|
||||
/// >>> import hello
|
||||
/// >>> hello.run()
|
||||
/// Rust says: Hello Python!
|
||||
/// ```
|
||||
///
|
||||
|
|
Loading…
Reference in a new issue