diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 718a11559..ca55f8cc6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -107,6 +107,12 @@ jobs: cargo run -- build --no-sdist -i python -m test-crates/pyo3-pure/Cargo.toml --target aarch64-apple-darwin --zig # Check wheels with twine twine check --strict test-crates/pyo3-pure/target/wheels/*.whl + - name: test cross compiling windows abi3 wheel + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get install -y mingw-w64 + rustup target add x86_64-pc-windows-gnu + cargo run -- build --no-sdist -m test-crates/pyo3-pure/Cargo.toml --target x86_64-pc-windows-gnu test-alpine: name: Test Alpine Linux diff --git a/Changelog.md b/Changelog.md index 6af36f9ec..856f685d8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] * Stop setting `PYO3_NO_PYTHON` environment variable for pyo3 0.16.4 and later in [#875](https://github.com/PyO3/maturin/pull/875) +* Build Windows abi3 wheels with `pyo3` 0.16.4 and later versions no longer require a Python interpreter in [#879](https://github.com/PyO3/maturin/pull/879) ## [0.12.12] - 2022-04-07 diff --git a/src/build_context.rs b/src/build_context.rs index 44f9194ea..af782185e 100644 --- a/src/build_context.rs +++ b/src/build_context.rs @@ -211,9 +211,7 @@ impl BuildContext { .cloned() .collect(); let mut built_wheels = Vec::new(); - if !cpythons.is_empty() { - built_wheels.extend(self.build_binding_wheel_abi3(&cpythons, *major, *minor)?); - } + built_wheels.extend(self.build_binding_wheel_abi3(&cpythons, *major, *minor)?); if !pypys.is_empty() { println!( "⚠️ Warning: PyPy does not yet support abi3 so the build artifacts will be version-specific. \ diff --git a/src/build_options.rs b/src/build_options.rs index 9d035bca8..898295f3b 100644 --- a/src/build_options.rs +++ b/src/build_options.rs @@ -272,12 +272,25 @@ impl BuildOptions { None => PathBuf::from(&cargo_metadata.target_directory).join("wheels"), }; + let generate_abi3_import_lib = is_generating_abi3_import_lib(&cargo_metadata)?; let interpreter = if self.interpreter.is_empty() { // Auto-detect interpreters - find_interpreter(&bridge, &[], &target, get_min_python_minor(&metadata21))? + find_interpreter( + &bridge, + &[], + &target, + get_min_python_minor(&metadata21), + generate_abi3_import_lib, + )? } else { // User given list of interpreters - find_interpreter(&bridge, &self.interpreter, &target, None)? + find_interpreter( + &bridge, + &self.interpreter, + &target, + None, + generate_abi3_import_lib, + )? }; let mut rustc_extra_args = self.rustc_extra_args.clone(); @@ -450,6 +463,33 @@ fn has_abi3(cargo_metadata: &Metadata) -> Result> { Ok(None) } +/// pyo3 0.16.4+ supports building abi3 wheels without a working Python interpreter for Windows +/// when `generate-abi3-import-lib` feature is enabled +fn is_generating_abi3_import_lib(cargo_metadata: &Metadata) -> Result { + let resolve = cargo_metadata + .resolve + .as_ref() + .context("Expected cargo to return metadata with resolve")?; + for &lib in PYO3_BINDING_CRATES.iter() { + let pyo3_packages = resolve + .nodes + .iter() + .filter(|package| cargo_metadata[&package.id].name.as_str() == lib) + .collect::>(); + match pyo3_packages.as_slice() { + [pyo3_crate] => { + let generate_import_lib = pyo3_crate + .features + .iter() + .any(|x| x == "generate-abi3-import-lib"); + return Ok(generate_import_lib); + } + _ => continue, + } + } + Ok(false) +} + /// Tries to determine the [BridgeModel] for the target crate pub fn find_bridge(cargo_metadata: &Metadata, bridge: Option<&str>) -> Result { let resolve = cargo_metadata @@ -505,7 +545,9 @@ pub fn find_bridge(cargo_metadata: &Metadata, bridge: Option<&str>) -> Result
= package .targets .iter() @@ -585,6 +627,7 @@ pub fn find_interpreter( interpreter: &[PathBuf], target: &Target, min_python_minor: Option, + generate_abi3_import_lib: bool, ) -> Result> { match bridge { BridgeModel::Bindings(binding_name, _) => { @@ -718,15 +761,17 @@ pub fn find_interpreter( platform: None, runnable: false, }]) - } else { - let interp = interpreter - .get(0) - .context("Failed to find a python interpreter")?; + } else if let Some(interp) = interpreter.get(0) { println!("🐍 Using {} to generate to link bindings (With abi3, an interpreter is only required on windows)", interp); Ok(interpreter) + } else if generate_abi3_import_lib { + println!("🐍 Not using a specific python interpreter (Automatically generating windows import library)"); + Ok(Vec::new()) + } else { + bail!("Failed to find a python interpreter"); } } else { - println!("🐍 Not using a specific python interpreter (With abi3, an interpreter is only required on windows)"); + println!("🐍 Not using a specific python interpreter"); Ok(interpreter) } } diff --git a/test-crates/pyo3-pure/Cargo.toml b/test-crates/pyo3-pure/Cargo.toml index 4de0aa750..5eedffd77 100644 --- a/test-crates/pyo3-pure/Cargo.toml +++ b/test-crates/pyo3-pure/Cargo.toml @@ -7,7 +7,7 @@ description = "Implements a dummy function (get_fortytwo.DummyClass.get_42()) in license = "MIT" [dependencies] -pyo3 = { version = "0.16.4", features = ["abi3-py37", "extension-module"] } +pyo3 = { version = "0.16.4", features = ["abi3-py37", "extension-module", "generate-abi3-import-lib"] } [lib] name = "pyo3_pure"