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
|
# Enable additional features that require nightly rust
|
||||||
nightly = []
|
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.
|
# Optional features to support explicitly specifying python minor version.
|
||||||
# If you don't care which minor version, just specify python3-sys as a
|
# If you don't care which minor version, just specify python3-sys as a
|
||||||
# feature.
|
# feature.
|
||||||
|
@ -55,3 +65,6 @@ python-3-4 = ["python3-sys/python-3-4"]
|
||||||
|
|
||||||
#pep-384 = ["python3-sys/pep-384"]
|
#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)
|
cargo doc --no-deps $(CARGO_FLAGS)
|
||||||
|
|
||||||
extensions: build
|
extensions: build
|
||||||
make -C extensions/ PY=$(PY)
|
make -C extensions/tests PY=$(PY)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -r target
|
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))
|
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
|
||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
@ -27,10 +27,10 @@ clean:
|
||||||
stamps:
|
stamps:
|
||||||
mkdir 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-*
|
-rm stamps/rust-cpython-*
|
||||||
cd .. && make build PY=$(PY)
|
cd ../.. && make build PY=$(PY)
|
||||||
touch $@
|
touch "$@"
|
||||||
|
|
||||||
%.so: %.rs stamps/rust-cpython-$(PY)
|
%.so: %.rs stamps/rust-cpython-$(PY)
|
||||||
rustc $< -g -L $(TARGETDIR) -L $(TARGETDIR)/deps -o $@
|
rustc $< -g -L $(TARGETDIR) -L $(TARGETDIR)/deps -o $@
|
|
@ -18,6 +18,7 @@ exclude = [
|
||||||
".gitignore",
|
".gitignore",
|
||||||
".travis.yml",
|
".travis.yml",
|
||||||
]
|
]
|
||||||
|
workspace = ".."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
@ -36,3 +37,9 @@ regex = "0.1"
|
||||||
# where supporting multiple 3.x's is more important.
|
# where supporting multiple 3.x's is more important.
|
||||||
default = ["python-2-7"]
|
default = ["python-2-7"]
|
||||||
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 enable_shared: &str = &lines[2];
|
||||||
let exec_prefix: &str = &lines[3];
|
let exec_prefix: &str = &lines[3];
|
||||||
|
|
||||||
println!("{}", get_rustc_link_lib(&interpreter_version,
|
let is_extension_module = env::var_os("CARGO_FEATURE_EXTENSION_MODULE").is_some();
|
||||||
enable_shared == "1").unwrap());
|
if !is_extension_module || cfg!(target_os="windows") {
|
||||||
|
println!("{}", get_rustc_link_lib(&interpreter_version, enable_shared == "1").unwrap());
|
||||||
if libpath != "None" {
|
if libpath != "None" {
|
||||||
println!("cargo:rustc-link-search=native={}", libpath);
|
println!("cargo:rustc-link-search=native={}", libpath);
|
||||||
} else if cfg!(target_os="windows") {
|
} else if cfg!(target_os="windows") {
|
||||||
println!("cargo:rustc-link-search=native={}\\libs", exec_prefix);
|
println!("cargo:rustc-link-search=native={}\\libs", exec_prefix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(interpreter_path);
|
return Ok(interpreter_path);
|
||||||
|
|
|
@ -18,6 +18,7 @@ exclude = [
|
||||||
".gitignore",
|
".gitignore",
|
||||||
".travis.yml",
|
".travis.yml",
|
||||||
]
|
]
|
||||||
|
workspace = ".."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
@ -30,6 +31,11 @@ regex = "0.1"
|
||||||
# to try to bind to.
|
# to try to bind to.
|
||||||
default = ["python-3"]
|
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.
|
# Bind to any python 3.x.
|
||||||
python-3 = []
|
python-3 = []
|
||||||
|
|
||||||
|
|
|
@ -298,8 +298,16 @@ fn configure_from_path(expected_version: &PythonVersion) -> Result<String, Strin
|
||||||
let ld_version: &str = &lines[3];
|
let ld_version: &str = &lines[3];
|
||||||
let exec_prefix: &str = &lines[4];
|
let exec_prefix: &str = &lines[4];
|
||||||
|
|
||||||
println!("{}", get_rustc_link_lib(&interpreter_version,
|
let is_extension_module = env::var_os("CARGO_FEATURE_EXTENSION_MODULE").is_some();
|
||||||
ld_version, enable_shared == "1").unwrap());
|
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();
|
let is_pep_384 = env::var_os("CARGO_FEATURE_PEP_384").is_some();
|
||||||
if is_pep_384 {
|
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);
|
return Ok(interpreter_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
30
src/lib.rs
30
src/lib.rs
|
@ -212,13 +212,12 @@ pub mod _detail {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// #![crate_type = "dylib"]
|
|
||||||
/// #[macro_use] extern crate cpython;
|
/// #[macro_use] extern crate cpython;
|
||||||
/// use cpython::{Python, PyResult, PyObject};
|
/// use cpython::{Python, PyResult, PyObject};
|
||||||
///
|
///
|
||||||
/// py_module_initializer!(example, initexample, PyInit_example, |py, m| {
|
/// py_module_initializer!(hello, inithello, PyInit_hello, |py, m| {
|
||||||
/// try!(m.add(py, "__doc__", "Module documentation string"));
|
/// m.add(py, "__doc__", "Module documentation string")?;
|
||||||
/// try!(m.add(py, "run", py_fn!(py, run())));
|
/// m.add(py, "run", py_fn!(py, run()))?;
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
/// });
|
/// });
|
||||||
///
|
///
|
||||||
|
@ -228,16 +227,29 @@ pub mod _detail {
|
||||||
/// }
|
/// }
|
||||||
/// # fn main() {}
|
/// # 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
|
/// ```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
|
/// ```python
|
||||||
/// >>> import example
|
/// >>> import hello
|
||||||
/// >>> example.run()
|
/// >>> hello.run()
|
||||||
/// Rust says: Hello Python!
|
/// Rust says: Hello Python!
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
|
Loading…
Reference in a new issue