From f4bb6f697691ac38fc75deaddeb5718cadfa957a Mon Sep 17 00:00:00 2001 From: ijl Date: Tue, 8 Feb 2022 23:40:29 +0000 Subject: [PATCH 1/7] Support pyo3-ffi --- src/build_options.rs | 122 +++++++++++++++++++++++-------------------- 1 file changed, 66 insertions(+), 56 deletions(-) diff --git a/src/build_options.rs b/src/build_options.rs index 5ca9c8e35..608f9655c 100644 --- a/src/build_options.rs +++ b/src/build_options.rs @@ -17,6 +17,12 @@ use std::env; use std::io; use std::path::PathBuf; +// This is used for BridgeModel::Bindings("pyo3-ffi") and BridgeModel::Bindings("pyo3"). +// These should be treated almost identically but must be correctly identified +// as one or the other in logs. pyo3-ffi is ordered first because it is newer +// and more restrictive. +const PYO3_BINDING_CRATES: [&str; 2] = ["pyo3-ffi", "pyo3"]; + /// High level API for building wheels from a crate which is also used for the CLI #[derive(Debug, Serialize, Deserialize, clap::Parser, Clone, Eq, PartialEq)] #[serde(default)] @@ -385,44 +391,44 @@ fn has_abi3(cargo_metadata: &Metadata) -> Result> { .resolve .as_ref() .context("Expected cargo to return metadata with resolve")?; - let pyo3_packages = resolve - .nodes - .iter() - .filter(|package| cargo_metadata[&package.id].name == "pyo3") - .collect::>(); - match pyo3_packages.as_slice() { - [pyo3_crate] => { - // Find the minimal abi3 python version. If there is none, abi3 hasn't been selected - // This parser abi3-py{major}{minor} and returns the minimal (major, minor) tuple - let abi3_selected = pyo3_crate.features.iter().any(|x| x == "abi3"); - - let min_abi3_version = pyo3_crate - .features - .iter() - .filter(|x| x.starts_with("abi3-py") && x.len() >= "abi3-pyxx".len()) - .map(|x| { - Ok(( - (x.as_bytes()[7] as char).to_string().parse::()?, - x[8..].parse::()?, - )) - }) - .collect::>>() - .context("Bogus pyo3 cargo features")? - .into_iter() - .min(); - if abi3_selected && min_abi3_version.is_none() { - bail!( - "You have selected the `abi3` feature but not a minimum version (e.g. the `abi3-py36` feature). \ - maturin needs a minimum version feature to build abi3 wheels." - ) + 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] => { + // Find the minimal abi3 python version. If there is none, abi3 hasn't been selected + // This parser abi3-py{major}{minor} and returns the minimal (major, minor) tuple + let abi3_selected = pyo3_crate.features.iter().any(|x| x == "abi3"); + + let min_abi3_version = pyo3_crate + .features + .iter() + .filter(|x| x.starts_with("abi3-py") && x.len() >= "abi3-pyxx".len()) + .map(|x| { + Ok(( + (x.as_bytes()[7] as char).to_string().parse::()?, + x[8..].parse::()?, + )) + }) + .collect::>>() + .context(format!("Bogus {} cargo features", lib))? + .into_iter() + .min(); + if abi3_selected && min_abi3_version.is_none() { + bail!( + "You have selected the `abi3` feature but not a minimum version (e.g. the `abi3-py36` feature). \ + maturin needs a minimum version feature to build abi3 wheels." + ) + } + return Ok(min_abi3_version); } - Ok(min_abi3_version) + _ => continue, } - _ => bail!(format!( - "Expected exactly one pyo3 dependency, found {}", - pyo3_packages.len() - )), } + Ok(None) } /// Tries to determine the [BridgeModel] for the target crate @@ -454,6 +460,8 @@ pub fn find_bridge(cargo_metadata: &Metadata, bridge: Option<&str>) -> Result
) -> Result
Date: Wed, 9 Feb 2022 13:57:00 +0800 Subject: [PATCH 2/7] Account for pyo3-ffi in compiling and finding Python interperters --- src/compile.rs | 2 +- src/python_interpreter.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/compile.rs b/src/compile.rs index b6fe1a09b..0ffab08b0 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -249,7 +249,7 @@ fn compile_target( if let Some(python_interpreter) = python_interpreter { // Target python interpreter isn't runnable when cross compiling if python_interpreter.runnable { - if bindings_crate.is_bindings("pyo3") { + if bindings_crate.is_bindings("pyo3") || bindings_crate.is_bindings("pyo3-ffi") { build_command.env("PYO3_PYTHON", &python_interpreter.executable); } diff --git a/src/python_interpreter.rs b/src/python_interpreter.rs index 78b3d3588..9829ef25f 100644 --- a/src/python_interpreter.rs +++ b/src/python_interpreter.rs @@ -550,7 +550,10 @@ impl PythonInterpreter { .map(|minor| format!("python3.{}", minor)) .collect(); // Also try to find PyPy for cffi and pyo3 bindings - if matches!(bridge, BridgeModel::Cffi) || bridge.is_bindings("pyo3") { + if matches!(bridge, BridgeModel::Cffi) + || bridge.is_bindings("pyo3") + || bridge.is_bindings("pyo3-ffi") + { executables.extend( (min_python_minor..MAXIMUM_PYPY_MINOR).map(|minor| format!("pypy3.{}", minor)), ); From 5cb8e6b72b30736128453d25f8b3611bc205966f Mon Sep 17 00:00:00 2001 From: ijl Date: Tue, 8 Feb 2022 23:45:12 +0000 Subject: [PATCH 3/7] Add pyo3-ffi-pure test-crate Co-authored-by: messense --- test-crates/pyo3-ffi-pure/Cargo.lock | 39 ++++++++++++++ test-crates/pyo3-ffi-pure/Cargo.toml | 11 ++++ test-crates/pyo3-ffi-pure/LICENSE | 25 +++++++++ test-crates/pyo3-ffi-pure/Readme.md | 18 +++++++ .../check_installed/check_installed.py | 6 +++ test-crates/pyo3-ffi-pure/pyproject.toml | 15 ++++++ test-crates/pyo3-ffi-pure/src/lib.rs | 53 +++++++++++++++++++ .../pyo3-ffi-pure/test_pyo3_ffi_pure.py | 7 +++ tests/run.rs | 28 ++++++++++ 9 files changed, 202 insertions(+) create mode 100644 test-crates/pyo3-ffi-pure/Cargo.lock create mode 100644 test-crates/pyo3-ffi-pure/Cargo.toml create mode 100644 test-crates/pyo3-ffi-pure/LICENSE create mode 100644 test-crates/pyo3-ffi-pure/Readme.md create mode 100755 test-crates/pyo3-ffi-pure/check_installed/check_installed.py create mode 100644 test-crates/pyo3-ffi-pure/pyproject.toml create mode 100644 test-crates/pyo3-ffi-pure/src/lib.rs create mode 100644 test-crates/pyo3-ffi-pure/test_pyo3_ffi_pure.py diff --git a/test-crates/pyo3-ffi-pure/Cargo.lock b/test-crates/pyo3-ffi-pure/Cargo.lock new file mode 100644 index 000000000..e4bc35e67 --- /dev/null +++ b/test-crates/pyo3-ffi-pure/Cargo.lock @@ -0,0 +1,39 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "libc" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "pyo3-build-config" +version = "0.15.1" +source = "git+https://github.com/PyO3/pyo3.git?branch=main#ada301773e13713fc173ca84f0547b2463d5e342" +dependencies = [ + "once_cell", +] + +[[package]] +name = "pyo3-ffi" +version = "0.15.1" +source = "git+https://github.com/PyO3/pyo3.git?branch=main#ada301773e13713fc173ca84f0547b2463d5e342" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-ffi-pure" +version = "1.0.0" +dependencies = [ + "pyo3-ffi", +] diff --git a/test-crates/pyo3-ffi-pure/Cargo.toml b/test-crates/pyo3-ffi-pure/Cargo.toml new file mode 100644 index 000000000..6cc933b1c --- /dev/null +++ b/test-crates/pyo3-ffi-pure/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "pyo3-ffi-pure" +version = "1.0.0" +edition = "2018" + +[dependencies] +pyo3-ffi = { git = "https://github.com/PyO3/pyo3.git", branch = "main", features = ["extension-module"] } + +[lib] +name = "pyo3_ffi_pure" +crate-type = ["cdylib"] diff --git a/test-crates/pyo3-ffi-pure/LICENSE b/test-crates/pyo3-ffi-pure/LICENSE new file mode 100644 index 000000000..3e349a199 --- /dev/null +++ b/test-crates/pyo3-ffi-pure/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2022-present maturin contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/test-crates/pyo3-ffi-pure/Readme.md b/test-crates/pyo3-ffi-pure/Readme.md new file mode 100644 index 000000000..2224361b2 --- /dev/null +++ b/test-crates/pyo3-ffi-pure/Readme.md @@ -0,0 +1,18 @@ +# pyo3-ffi-pure + +A package with pyo3-ffi bindings for testing maturin. + +## Usage + +```python +import pyo3_ffi_pure +assert pyo3_ffi_pure.sum(2, 40) == 42 +``` + +## Testing + +Install `pytest` and run: + +```bash +pytest -v test_pyo3_ffi_pure.py +``` diff --git a/test-crates/pyo3-ffi-pure/check_installed/check_installed.py b/test-crates/pyo3-ffi-pure/check_installed/check_installed.py new file mode 100755 index 000000000..3dbb231d8 --- /dev/null +++ b/test-crates/pyo3-ffi-pure/check_installed/check_installed.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 +import pyo3_ffi_pure + +assert pyo3_ffi_pure.sum(2, 40) == 42 + +print("SUCCESS") diff --git a/test-crates/pyo3-ffi-pure/pyproject.toml b/test-crates/pyo3-ffi-pure/pyproject.toml new file mode 100644 index 000000000..83403ba31 --- /dev/null +++ b/test-crates/pyo3-ffi-pure/pyproject.toml @@ -0,0 +1,15 @@ +[build-system] +requires = ["maturin>=0.12,<0.13"] +build-backend = "maturin" + +[project] +name = "pyo3-ffi-pure" +classifiers = [ + "Programming Language :: Rust" +] +description = "Tests compilation of packages using pyo3-ffi bindings" +readme = "Readme.md" +maintainers = [ + {name = "messense", email = "messense@icloud.com"} +] +license = { file = "LICENSE" } diff --git a/test-crates/pyo3-ffi-pure/src/lib.rs b/test-crates/pyo3-ffi-pure/src/lib.rs new file mode 100644 index 000000000..6348cbe47 --- /dev/null +++ b/test-crates/pyo3-ffi-pure/src/lib.rs @@ -0,0 +1,53 @@ +use pyo3_ffi::*; +use std::os::raw::c_char; + +#[allow(non_snake_case)] +#[no_mangle] +pub unsafe extern "C" fn PyInit_pyo3_ffi_pure() -> *mut PyObject { + let module_name = "pyo3_ffi_pure\0".as_ptr() as *const c_char; + let init = PyModuleDef { + m_base: PyModuleDef_HEAD_INIT, + m_name: module_name, + m_doc: std::ptr::null(), + m_size: 0, + m_methods: std::ptr::null_mut(), + m_slots: std::ptr::null_mut(), + m_traverse: None, + m_clear: None, + m_free: None, + }; + let mptr = PyModule_Create(Box::into_raw(Box::new(init))); + + let wrapped_sum = PyMethodDef { + ml_name: "sum\0".as_ptr() as *const c_char, + ml_meth: Some(std::mem::transmute::( + sum, + )), + ml_flags: METH_VARARGS, + ml_doc: std::ptr::null_mut(), + }; + PyModule_AddObject( + mptr, + "sum\0".as_ptr() as *const c_char, + PyCFunction_NewEx( + Box::into_raw(Box::new(wrapped_sum)), + std::ptr::null_mut(), + PyUnicode_InternFromString(module_name), + ), + ); + + mptr +} + +#[no_mangle] +pub unsafe extern "C" fn sum( + _self: *mut PyObject, + args: *mut PyObject, + _kwds: *mut PyObject, +) -> *mut PyObject { + // this is a minimal test of compilation, not good example code + let val_a = PyTuple_GET_ITEM(args, 0); + let val_b = PyTuple_GET_ITEM(args, 1); + let res: i64 = PyLong_AsLongLong(val_a) + PyLong_AsLongLong(val_b); + PyLong_FromLongLong(res) +} diff --git a/test-crates/pyo3-ffi-pure/test_pyo3_ffi_pure.py b/test-crates/pyo3-ffi-pure/test_pyo3_ffi_pure.py new file mode 100644 index 000000000..c2a1b90ef --- /dev/null +++ b/test-crates/pyo3-ffi-pure/test_pyo3_ffi_pure.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 + +import pyo3_ffi_pure + + +def test_static(): + assert pyo3_ffi_pure.sum(2, 40) == 42 diff --git a/tests/run.rs b/tests/run.rs index 4b8ac3c19..03f9c6041 100644 --- a/tests/run.rs +++ b/tests/run.rs @@ -67,6 +67,15 @@ fn develop_hello_world() { )); } +#[test] +fn develop_pyo3_ffi_pure() { + handle_result(develop::test_develop( + "test-crates/pyo3-ffi-pure", + None, + "develop_pyo3_ffi_pure", + )); +} + #[test] fn editable_pyo3_pure() { handle_result(editable::test_editable( @@ -94,6 +103,15 @@ fn editable_pyo3_mixed_py_subdir() { )); } +#[test] +fn editable_pyo3_ffi_pure() { + handle_result(editable::test_editable( + "test-crates/pyo3-ffi-pure", + None, + "editable_pyo3_ffi_pure", + )); +} + #[test] fn integration_pyo3_pure() { handle_result(integration::test_integration( @@ -174,6 +192,16 @@ fn integration_hello_world() { )); } +#[test] +fn integration_pyo3_ffi_pure() { + handle_result(integration::test_integration( + "test-crates/pyo3-ffi-pure", + None, + "integration_pyo3_ffi_pure", + false, + )); +} + #[test] fn abi3_without_version() { handle_result(errors::abi3_without_version()) From b8f88d31d877b6e7d2b5f3fd6d8c476183e82c51 Mon Sep 17 00:00:00 2001 From: messense Date: Wed, 9 Feb 2022 14:07:50 +0800 Subject: [PATCH 4/7] Add changelog entry for pyo3-ffi support --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 830f9eacb..6024ace0e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +* Add support for `pyo3-ffi` by ijl in [#804](https://github.com/PyO3/maturin/pull/804) + ## [0.12.9] - 2022-02-09 * Don't require `pyproject.toml` when cargo manifest is not specified in [#806](https://github.com/PyO3/maturin/pull/806) From 9c6e1092c82c719eaf03cf2617bbccb9f6928229 Mon Sep 17 00:00:00 2001 From: messense Date: Wed, 9 Feb 2022 14:26:10 +0800 Subject: [PATCH 5/7] Convert pyo3-ffi-pure to abi3 mode --- test-crates/pyo3-ffi-pure/Cargo.toml | 2 +- test-crates/pyo3-ffi-pure/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test-crates/pyo3-ffi-pure/Cargo.toml b/test-crates/pyo3-ffi-pure/Cargo.toml index 6cc933b1c..471908414 100644 --- a/test-crates/pyo3-ffi-pure/Cargo.toml +++ b/test-crates/pyo3-ffi-pure/Cargo.toml @@ -4,7 +4,7 @@ version = "1.0.0" edition = "2018" [dependencies] -pyo3-ffi = { git = "https://github.com/PyO3/pyo3.git", branch = "main", features = ["extension-module"] } +pyo3-ffi = { git = "https://github.com/PyO3/pyo3.git", branch = "main", features = ["abi3-py37", "extension-module"] } [lib] name = "pyo3_ffi_pure" diff --git a/test-crates/pyo3-ffi-pure/src/lib.rs b/test-crates/pyo3-ffi-pure/src/lib.rs index 6348cbe47..d8847a5a6 100644 --- a/test-crates/pyo3-ffi-pure/src/lib.rs +++ b/test-crates/pyo3-ffi-pure/src/lib.rs @@ -46,8 +46,8 @@ pub unsafe extern "C" fn sum( _kwds: *mut PyObject, ) -> *mut PyObject { // this is a minimal test of compilation, not good example code - let val_a = PyTuple_GET_ITEM(args, 0); - let val_b = PyTuple_GET_ITEM(args, 1); + let val_a = PyTuple_GetItem(args, 0); + let val_b = PyTuple_GetItem(args, 1); let res: i64 = PyLong_AsLongLong(val_a) + PyLong_AsLongLong(val_b); PyLong_FromLongLong(res) } From 225f5bceb8d05b25e6c9b319460654bfeb1a8eec Mon Sep 17 00:00:00 2001 From: messense Date: Wed, 9 Feb 2022 14:36:01 +0800 Subject: [PATCH 6/7] pyo3-ffi requires Python 3.7 and later --- src/build_options.rs | 5 +++-- src/python_interpreter.rs | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/build_options.rs b/src/build_options.rs index 608f9655c..4006809fe 100644 --- a/src/build_options.rs +++ b/src/build_options.rs @@ -568,7 +568,7 @@ pub fn find_interpreter( } } - if binding_name == "pyo3" && target.is_unix() && target.cross_compiling() { + if binding_name.starts_with("pyo3") && target.is_unix() && target.cross_compiling() { if let Some(cross_lib_dir) = std::env::var_os("PYO3_CROSS_LIB_DIR") { println!("⚠️ Cross-compiling is poorly supported"); let host_python = &interpreter[0]; @@ -658,7 +658,8 @@ pub fn find_interpreter( PythonInterpreter::check_executables(interpreter, target, bridge) .unwrap_or_default() } else { - PythonInterpreter::find_all(target, bridge, min_python_minor).unwrap_or_default() + PythonInterpreter::find_all(target, bridge, Some(*minor as usize)) + .unwrap_or_default() }; // Ideally, we wouldn't want to use any python interpreter without abi3 at all. // Unfortunately, on windows we need one to figure out base_prefix for a linker diff --git a/src/python_interpreter.rs b/src/python_interpreter.rs index 9829ef25f..7b1246161 100644 --- a/src/python_interpreter.rs +++ b/src/python_interpreter.rs @@ -542,7 +542,14 @@ impl PythonInterpreter { bridge: &BridgeModel, min_python_minor: Option, ) -> Result> { - let min_python_minor = min_python_minor.unwrap_or(MINIMUM_PYTHON_MINOR); + let min_python_minor = min_python_minor.unwrap_or_else(|| { + if bridge.is_bindings("pyo3-ffi") { + // pyo3-ffi requires at least Python 3.7 + 7 + } else { + MINIMUM_PYTHON_MINOR + } + }); let executables = if target.is_windows() { find_all_windows(target, min_python_minor)? } else { From 14e5227ba145c568d3c9ba87060d682b62a8227e Mon Sep 17 00:00:00 2001 From: messense Date: Wed, 9 Feb 2022 15:18:02 +0800 Subject: [PATCH 7/7] Refactor venv creation in test_integration --- tests/common/develop.rs | 2 +- tests/common/editable.rs | 2 +- tests/common/integration.rs | 59 ++++++++++--------------------------- tests/common/mod.rs | 7 ++++- 4 files changed, 24 insertions(+), 46 deletions(-) diff --git a/tests/common/develop.rs b/tests/common/develop.rs index 95b7d9545..941cd302c 100644 --- a/tests/common/develop.rs +++ b/tests/common/develop.rs @@ -14,7 +14,7 @@ pub fn test_develop( ) -> Result<()> { maybe_mock_cargo(); - let (venv_dir, python) = create_virtualenv(&package, "develop")?; + let (venv_dir, python) = create_virtualenv(&package, "develop", None)?; // Ensure the test doesn't wrongly pass check_installed(package.as_ref(), &python).unwrap_err(); diff --git a/tests/common/editable.rs b/tests/common/editable.rs index 65b195682..c905f98cf 100644 --- a/tests/common/editable.rs +++ b/tests/common/editable.rs @@ -18,7 +18,7 @@ pub fn test_editable( let package_string = package.as_ref().join("Cargo.toml").display().to_string(); - let (venv_dir, python) = create_virtualenv(&package, "editable")?; + let (venv_dir, python) = create_virtualenv(&package, "editable", None)?; let interpreter = python.to_str().expect("invalid interpreter path"); let cargo_extra_args = format!( "--cargo-extra-args=--quiet --target-dir test-crates/targets/{}", diff --git a/tests/common/integration.rs b/tests/common/integration.rs index 2d1d70f5e..1b402d805 100644 --- a/tests/common/integration.rs +++ b/tests/common/integration.rs @@ -1,9 +1,11 @@ -use crate::common::{adjust_canonicalization, check_installed, maybe_mock_cargo}; +use crate::common::{ + adjust_canonicalization, check_installed, create_virtualenv, maybe_mock_cargo, +}; use anyhow::{bail, Context, Result}; use clap::Parser; -use maturin::{BuildOptions, PythonInterpreter, Target, Zig}; +use maturin::{BuildOptions, PythonInterpreter, Zig}; use std::env; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::process::Command; use std::str; @@ -20,8 +22,6 @@ pub fn test_integration( // Pass CARGO_BIN_EXE_maturin for testing purpose std::env::set_var("CARGO_BIN_EXE_maturin", env!("CARGO_BIN_EXE_maturin")); - let target = Target::from_target_triple(None)?; - let package_string = package.as_ref().join("Cargo.toml").display().to_string(); // The first argument is ignored by clap @@ -55,13 +55,6 @@ pub fn test_integration( let build_context = options.into_build_context(false, cfg!(feature = "faster-tests"), false)?; let wheels = build_context.build_wheels()?; - let test_name = package - .as_ref() - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_string(); // For abi3 on unix, we didn't use a python interpreter, but we need one here let interpreter = if build_context.interpreter.is_empty() { let error_message = "python3 should be a python interpreter"; @@ -84,38 +77,16 @@ pub fn test_integration( .to_string_lossy() .ends_with("manylinux_2_12_x86_64.manylinux2010_x86_64.whl")) } - let venv_name = if supported_version == "py3" { - format!("{}-cffi", test_name) + let venv_suffix = if supported_version == "py3" { + "py3".to_string() } else { - format!( - "{}-{}.{}", - test_name, python_interpreter.major, python_interpreter.minor, - ) + format!("{}.{}", python_interpreter.major, python_interpreter.minor,) }; - let venv_dir = PathBuf::from("test-crates") - .canonicalize()? - .join("venvs") - .join(venv_name); - - if !venv_dir.is_dir() { - let output = Command::new("virtualenv") - .arg("-p") - .arg(python_interpreter.executable.clone()) - .arg(&adjust_canonicalization(&venv_dir)) - .output()?; - - if !output.status.success() { - bail!( - "Failed to create a virtualenv at {}: {}\n--- Stdout:\n{}\n--- Stderr:\n{}", - venv_dir.display(), - output.status, - str::from_utf8(&output.stdout)?, - str::from_utf8(&output.stderr)?, - ); - } - } - - let python = target.get_venv_python(&venv_dir); + let (venv_dir, python) = create_virtualenv( + &package, + &venv_suffix, + Some(python_interpreter.executable.clone()), + )?; let command = [ "-m", @@ -131,10 +102,11 @@ pub fn test_integration( .output() .context(format!("pip install failed with {:?}", python))?; if !output.status.success() { + let full_command = format!("{} {}", python.display(), command.join(" ")); bail!( "pip install in {} failed running {:?}: {}\n--- Stdout:\n{}\n--- Stderr:\n{}\n---\n", venv_dir.display(), - &command, + full_command, output.status, str::from_utf8(&output.stdout)?.trim(), str::from_utf8(&output.stderr)?.trim(), @@ -172,6 +144,7 @@ fn create_conda_env(name: &str, major: usize, minor: usize) { #[cfg(target_os = "windows")] pub fn test_integration_conda(package: impl AsRef, bindings: Option) -> Result<()> { + use std::path::PathBuf; use std::process::Stdio; let package_string = package.as_ref().join("Cargo.toml").display().to_string(); diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 2a8e24479..606de6eb7 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -100,6 +100,7 @@ pub fn handle_result(result: Result) -> T { pub fn create_virtualenv( package: impl AsRef, venv_suffix: &str, + python_interp: Option, ) -> Result<(PathBuf, PathBuf)> { let test_name = package .as_ref() @@ -119,7 +120,11 @@ pub fn create_virtualenv( fs::remove_dir_all(&venv_dir)?; } - let output = Command::new("virtualenv") + let mut cmd = Command::new("virtualenv"); + if let Some(interp) = python_interp { + cmd.arg("-p").arg(interp); + } + let output = cmd .arg(adjust_canonicalization(&venv_dir)) .stderr(Stdio::inherit()) .output()