From 2b47d13453cdd9ca5258a44faabde2ee7e385d5e Mon Sep 17 00:00:00 2001
From: messense <messense@icloud.com>
Date: Wed, 8 Jun 2022 21:00:38 +0800
Subject: [PATCH] Add `--find-interpreter` option to `build` and `publish`
 commands

Some behaviors changed:

1. `maturin build` now implicitly translate to `maturin build -i python3`, meaning that we only build for the current python3 interpreter in `PATH`
2. `maturin build --find-interpreter` makes maturin search for interpreters on host, also search for bundled sysconfig when cross compiling
3. Mix `-i` and `--find-interpreter` is not allowed
4. `maturin publish` does the same as `maturin build`
---
 Changelog.md                  |   2 +
 src/build_context.rs          |   5 +-
 src/build_options.rs          | 527 ++++++++++++++++++----------------
 src/compile.rs                |   7 +-
 src/develop.rs                |   1 +
 src/main.rs                   |   2 +-
 src/python_interpreter/mod.rs |  28 +-
 7 files changed, 311 insertions(+), 261 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index d1e09636d..8f730afa7 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 * **Breaking Change**: Drop support for python 3.6, which is end of life in [#945](https://github.com/PyO3/maturin/pull/945)
 * **Breaking Change**: Don't build source distribution by default in `maturin build` command in [#955](https://github.com/PyO3/maturin/pull/955), `--no-sdist` option is replaced by `--sdist`
+* **Breaking Change**: maturin no longer search for python interpreters by default and only build for current interpreter in `PATH` in [#964](https://github.com/PyO3/maturin/pull/964)
 * Add support for building with multiple binary targets in [#948](https://github.com/PyO3/maturin/pull/948)
 * Fix incompatibility with cibuildwheel for 32-bit Windows in [#951](https://github.com/PyO3/maturin/pull/951)
 * Don't require `pip` error messages to be utf-8 encoding in [#953](https://github.com/PyO3/maturin/pull/953)
@@ -18,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 * Set `PYO3_PYTHON` env var for PyPy when abi3 is enabled in [#960](https://github.com/PyO3/maturin/pull/960)
 * Add sysconfigs for x64 Windows PyPy in [#962](https://github.com/PyO3/maturin/pull/962)
 * Add support for cross compiling PyPy wheels when abi3 feature is enabled in [#963](https://github.com/PyO3/maturin/pull/963)
+* Add `--find-interpreter` option to `build` and `publish` commands to search for python interpreters in [#964](https://github.com/PyO3/maturin/pull/964)
 
 ## [0.12.19] - 2022-06-05
 
diff --git a/src/build_context.rs b/src/build_context.rs
index d3f4b0657..e5b41a536 100644
--- a/src/build_context.rs
+++ b/src/build_context.rs
@@ -4,7 +4,6 @@ use crate::compile::warn_missing_py_init;
 use crate::module_writer::{
     add_data, write_bin, write_bindings_module, write_cffi_module, write_python_part, WheelWriter,
 };
-use crate::python_interpreter::InterpreterKind;
 use crate::source_distribution::source_distribution;
 use crate::{compile, Metadata21, ModuleWriter, PyProjectToml, PythonInterpreter, Target};
 use anyhow::{anyhow, bail, Context, Result};
@@ -229,13 +228,13 @@ impl BuildContext {
                 let cpythons: Vec<_> = self
                     .interpreter
                     .iter()
-                    .filter(|interp| interp.interpreter_kind == InterpreterKind::CPython)
+                    .filter(|interp| interp.interpreter_kind.is_cpython())
                     .cloned()
                     .collect();
                 let pypys: Vec<_> = self
                     .interpreter
                     .iter()
-                    .filter(|interp| interp.interpreter_kind == InterpreterKind::PyPy)
+                    .filter(|interp| interp.interpreter_kind.is_pypy())
                     .cloned()
                     .collect();
                 let mut built_wheels = Vec::new();
diff --git a/src/build_options.rs b/src/build_options.rs
index e03864f7b..f70206349 100644
--- a/src/build_options.rs
+++ b/src/build_options.rs
@@ -70,6 +70,10 @@ pub struct BuildOptions {
     #[clap(short, long, multiple_values = true, multiple_occurrences = true)]
     pub interpreter: Vec<PathBuf>,
 
+    /// Find interpreters from the host machine
+    #[clap(short = 'f', long, conflicts_with = "interpreter")]
+    pub find_interpreter: bool,
+
     /// Which kind of bindings to use. Possible values are pyo3, rust-cpython, cffi and bin
     #[clap(short, long)]
     pub bindings: Option<String>,
@@ -160,6 +164,266 @@ impl BuildOptions {
             ))
         }
     }
+
+    /// Finds the appropriate amount for python versions for each [BridgeModel].
+    fn find_interpreters(
+        &self,
+        bridge: &BridgeModel,
+        interpreter: &[PathBuf],
+        min_python_minor: Option<usize>,
+        generate_import_lib: bool,
+    ) -> Result<Vec<PythonInterpreter>> {
+        let target = &Target::from_target_triple(self.target.clone())?;
+        match bridge {
+            BridgeModel::Bindings(binding_name, _) | BridgeModel::Bin(Some((binding_name, _))) => {
+                let mut native_interpreters = false;
+                let mut interpreters = Vec::new();
+                if let Some(config_file) = env::var_os("PYO3_CONFIG_FILE") {
+                    if !binding_name.starts_with("pyo3") {
+                        bail!("Only pyo3 bindings can be configured with PYO3_CONFIG_FILE");
+                    }
+                    let interpreter_config =
+                        InterpreterConfig::from_pyo3_config(config_file.as_ref(), target)
+                            .context("Invalid PYO3_CONFIG_FILE")?;
+                    interpreters.push(PythonInterpreter::from_config(interpreter_config));
+                } else if binding_name.starts_with("pyo3") && target.cross_compiling() {
+                    if let Some(cross_lib_dir) = env::var_os("PYO3_CROSS_LIB_DIR") {
+                        let host_interpreters = find_interpreter_in_host(
+                            bridge,
+                            interpreter,
+                            target,
+                            min_python_minor,
+                        )?;
+                        let host_python = &host_interpreters[0];
+                        println!(
+                            "🐍 Using host {} for cross-compiling preparation",
+                            host_python
+                        );
+                        // pyo3
+                        env::set_var("PYO3_PYTHON", &host_python.executable);
+                        // rust-cpython, and legacy pyo3 versions
+                        env::set_var("PYTHON_SYS_EXECUTABLE", &host_python.executable);
+
+                        let sysconfig_path = find_sysconfigdata(cross_lib_dir.as_ref(), target)?;
+                        env::set_var(
+                            "MATURIN_PYTHON_SYSCONFIGDATA_DIR",
+                            sysconfig_path.parent().unwrap(),
+                        );
+
+                        let sysconfig_data = parse_sysconfigdata(host_python, sysconfig_path)?;
+                        let major = sysconfig_data
+                            .get("version_major")
+                            .context("version_major is not defined")?
+                            .parse::<usize>()
+                            .context("Could not parse value of version_major")?;
+                        let minor = sysconfig_data
+                            .get("version_minor")
+                            .context("version_minor is not defined")?
+                            .parse::<usize>()
+                            .context("Could not parse value of version_minor")?;
+                        let abiflags = sysconfig_data
+                            .get("ABIFLAGS")
+                            .map(ToString::to_string)
+                            .unwrap_or_default();
+                        let ext_suffix = sysconfig_data
+                            .get("EXT_SUFFIX")
+                            .context("syconfig didn't define an `EXT_SUFFIX` ಠ_ಠ")?;
+                        let abi_tag = sysconfig_data
+                            .get("SOABI")
+                            .and_then(|abi| abi.split('-').nth(1).map(ToString::to_string));
+                        let interpreter_kind = sysconfig_data
+                            .get("SOABI")
+                            .and_then(|tag| {
+                                if tag.starts_with("pypy") {
+                                    Some(InterpreterKind::PyPy)
+                                } else if tag.starts_with("cpython") {
+                                    Some(InterpreterKind::CPython)
+                                } else {
+                                    None
+                                }
+                            })
+                            .context("unsupported Python interpreter")?;
+                        interpreters.push(PythonInterpreter {
+                            config: InterpreterConfig {
+                                major,
+                                minor,
+                                interpreter_kind,
+                                abiflags,
+                                ext_suffix: ext_suffix.to_string(),
+                                abi_tag,
+                                pointer_width: None,
+                            },
+                            executable: PathBuf::new(),
+                            platform: None,
+                            runnable: false,
+                        });
+                    } else {
+                        if interpreter.is_empty() && !self.find_interpreter {
+                            bail!("Couldn't find any python interpreters. Please specify at least one with -i");
+                        }
+                        interpreters =
+                            find_interpreter_in_sysconfig(interpreter, target, min_python_minor)?;
+                    }
+                } else {
+                    match find_interpreter_in_host(bridge, interpreter, target, min_python_minor) {
+                        Ok(host_interps) => {
+                            interpreters = host_interps;
+                            native_interpreters = true;
+                        }
+                        Err(err) => {
+                            if binding_name.starts_with("pyo3") && target.is_unix() {
+                                interpreters = find_interpreter_in_sysconfig(
+                                    interpreter,
+                                    target,
+                                    min_python_minor,
+                                )
+                                .map_err(|_| err)?;
+                            } else {
+                                return Err(err);
+                            }
+                        }
+                    }
+                }
+
+                let interpreters_str = interpreters
+                    .iter()
+                    .map(ToString::to_string)
+                    .collect::<Vec<String>>()
+                    .join(", ");
+                if native_interpreters {
+                    println!("🐍 Found {}", interpreters_str);
+                } else {
+                    println!("🐍 Found cross compiling target {}", interpreters_str);
+                }
+
+                Ok(interpreters)
+            }
+            BridgeModel::Cffi => {
+                let interpreter =
+                    find_single_python_interpreter(bridge, interpreter, target, "cffi")?;
+                println!("🐍 Using {} to generate the cffi bindings", interpreter);
+                Ok(vec![interpreter])
+            }
+            BridgeModel::Bin(None) => Ok(vec![]),
+            BridgeModel::BindingsAbi3(major, minor) => {
+                if target.is_windows() {
+                    // 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
+                    // argument.
+                    let interpreters = find_interpreter_in_host(
+                        bridge,
+                        interpreter,
+                        target,
+                        Some(*minor as usize),
+                    )
+                    .unwrap_or_default();
+                    if env::var_os("PYO3_CROSS_LIB_DIR").is_some() {
+                        // PYO3_CROSS_LIB_DIR should point to the `libs` directory inside base_prefix
+                        // when cross compiling, so we fake a python interpreter matching it
+                        println!("⚠️  Cross-compiling is poorly supported");
+                        Ok(vec![PythonInterpreter {
+                            config: InterpreterConfig {
+                                major: *major as usize,
+                                minor: *minor as usize,
+                                interpreter_kind: InterpreterKind::CPython,
+                                abiflags: "".to_string(),
+                                ext_suffix: ".pyd".to_string(),
+                                abi_tag: None,
+                                pointer_width: None,
+                            },
+                            executable: PathBuf::new(),
+                            platform: None,
+                            runnable: false,
+                        }])
+                    } else if let Some(interp) = interpreters.get(0) {
+                        println!("🐍 Using {} to generate to link bindings (With abi3, an interpreter is only required on windows)", interp);
+                        Ok(interpreters)
+                    } else if generate_import_lib {
+                        println!("🐍 Not using a specific python interpreter (Automatically generating windows import library)");
+                        // fake a python interpreter
+                        Ok(vec![PythonInterpreter {
+                            config: InterpreterConfig {
+                                major: *major as usize,
+                                minor: *minor as usize,
+                                interpreter_kind: InterpreterKind::CPython,
+                                abiflags: "".to_string(),
+                                ext_suffix: ".pyd".to_string(),
+                                abi_tag: None,
+                                pointer_width: None,
+                            },
+                            executable: PathBuf::new(),
+                            platform: None,
+                            runnable: false,
+                        }])
+                    } else {
+                        bail!("Failed to find a python interpreter");
+                    }
+                } else {
+                    let interpreters = find_interpreter_in_host(
+                        bridge,
+                        interpreter,
+                        target,
+                        Some(*minor as usize),
+                    )
+                    .unwrap_or_else(|_| {
+                        find_interpreter_in_sysconfig(interpreter, target, Some(*minor as usize))
+                            .unwrap_or_default()
+                    });
+                    println!("🐍 Not using a specific python interpreter");
+                    if interpreter.is_empty() {
+                        // Fake one to make `BuildContext::build_wheels` happy for abi3 when no cpython/pypy found on host
+                        // The python interpreter config doesn't matter, as it's not used for anything
+                        Ok(vec![PythonInterpreter {
+                            config: InterpreterConfig {
+                                major: *major as usize,
+                                minor: *minor as usize,
+                                interpreter_kind: InterpreterKind::CPython,
+                                abiflags: "".to_string(),
+                                ext_suffix: "".to_string(),
+                                abi_tag: None,
+                                pointer_width: None,
+                            },
+                            executable: PathBuf::new(),
+                            platform: None,
+                            runnable: false,
+                        }])
+                    } else if target.cross_compiling() {
+                        let mut interps = Vec::with_capacity(interpreters.len());
+                        let mut pypys = Vec::new();
+                        for interp in interpreters {
+                            if interp.interpreter_kind.is_pypy() {
+                                pypys.push(PathBuf::from(format!(
+                                    "pypy{}.{}",
+                                    interp.major, interp.minor
+                                )));
+                            } else {
+                                interps.push(interp);
+                            }
+                        }
+                        // cross compiling to PyPy with abi3 feature enabled,
+                        // we cannot use host pypy so switch to bundled sysconfig instead
+                        if !pypys.is_empty() {
+                            interps.extend(find_interpreter_in_sysconfig(
+                                &pypys,
+                                target,
+                                min_python_minor,
+                            )?)
+                        }
+                        if interps.is_empty() {
+                            bail!("Failed to find any python interpreter");
+                        }
+                        Ok(interps)
+                    } else {
+                        if interpreters.is_empty() {
+                            bail!("Failed to find any python interpreter");
+                        }
+                        Ok(interpreters)
+                    }
+                }
+            }
+        }
+    }
+
     /// Tries to fill the missing metadata for a BuildContext by querying cargo and python
     pub fn into_build_context(
         self,
@@ -277,24 +541,22 @@ impl BuildOptions {
         };
 
         let generate_import_lib = is_generating_import_lib(&cargo_metadata)?;
-        let interpreter = if self.interpreter.is_empty() {
+        let interpreter = if self.find_interpreter {
             // Auto-detect interpreters
-            find_interpreter(
+            self.find_interpreters(
                 &bridge,
                 &[],
-                &target,
                 get_min_python_minor(&metadata21),
                 generate_import_lib,
             )?
         } else {
             // User given list of interpreters
-            find_interpreter(
-                &bridge,
-                &self.interpreter,
-                &target,
-                None,
-                generate_import_lib,
-            )?
+            let interpreter = if self.interpreter.is_empty() && !target.cross_compiling() {
+                vec![PathBuf::from("python3")]
+            } else {
+                self.interpreter.clone()
+            };
+            self.find_interpreters(&bridge, &interpreter, None, generate_import_lib)?
         };
 
         let mut rustc_extra_args = self.rustc_extra_args.clone();
@@ -721,7 +983,11 @@ fn find_interpreter_in_host(
 fn find_interpreter_in_sysconfig(
     interpreter: &[PathBuf],
     target: &Target,
+    min_python_minor: Option<usize>,
 ) -> Result<Vec<PythonInterpreter>> {
+    if interpreter.is_empty() {
+        return Ok(PythonInterpreter::find_by_target(target, min_python_minor));
+    }
     let mut interpreters = Vec::new();
     for interp in interpreter {
         let python = interp
@@ -773,247 +1039,6 @@ fn find_interpreter_in_sysconfig(
     Ok(interpreters)
 }
 
-/// Finds the appropriate amount for python versions for each [BridgeModel].
-///
-/// This means all for bindings, one for cffi and zero for bin.
-pub fn find_interpreter(
-    bridge: &BridgeModel,
-    interpreter: &[PathBuf],
-    target: &Target,
-    min_python_minor: Option<usize>,
-    generate_import_lib: bool,
-) -> Result<Vec<PythonInterpreter>> {
-    match bridge {
-        BridgeModel::Bindings(binding_name, _) | BridgeModel::Bin(Some((binding_name, _))) => {
-            let mut native_interpreters = false;
-            let mut interpreters = Vec::new();
-            if let Some(config_file) = env::var_os("PYO3_CONFIG_FILE") {
-                if !binding_name.starts_with("pyo3") {
-                    bail!("Only pyo3 bindings can be configured with PYO3_CONFIG_FILE");
-                }
-                let interpreter_config =
-                    InterpreterConfig::from_pyo3_config(config_file.as_ref(), target)
-                        .context("Invalid PYO3_CONFIG_FILE")?;
-                interpreters.push(PythonInterpreter::from_config(interpreter_config));
-            } else if binding_name.starts_with("pyo3") && target.cross_compiling() {
-                if let Some(cross_lib_dir) = std::env::var_os("PYO3_CROSS_LIB_DIR") {
-                    let host_interpreters =
-                        find_interpreter_in_host(bridge, interpreter, target, min_python_minor)?;
-                    let host_python = &host_interpreters[0];
-                    println!(
-                        "🐍 Using host {} for cross-compiling preparation",
-                        host_python
-                    );
-                    // pyo3
-                    env::set_var("PYO3_PYTHON", &host_python.executable);
-                    // rust-cpython, and legacy pyo3 versions
-                    env::set_var("PYTHON_SYS_EXECUTABLE", &host_python.executable);
-
-                    let sysconfig_path = find_sysconfigdata(cross_lib_dir.as_ref(), target)?;
-                    env::set_var(
-                        "MATURIN_PYTHON_SYSCONFIGDATA_DIR",
-                        sysconfig_path.parent().unwrap(),
-                    );
-
-                    let sysconfig_data = parse_sysconfigdata(host_python, sysconfig_path)?;
-                    let major = sysconfig_data
-                        .get("version_major")
-                        .context("version_major is not defined")?
-                        .parse::<usize>()
-                        .context("Could not parse value of version_major")?;
-                    let minor = sysconfig_data
-                        .get("version_minor")
-                        .context("version_minor is not defined")?
-                        .parse::<usize>()
-                        .context("Could not parse value of version_minor")?;
-                    let abiflags = sysconfig_data
-                        .get("ABIFLAGS")
-                        .map(ToString::to_string)
-                        .unwrap_or_default();
-                    let ext_suffix = sysconfig_data
-                        .get("EXT_SUFFIX")
-                        .context("syconfig didn't define an `EXT_SUFFIX` ಠ_ಠ")?;
-                    let abi_tag = sysconfig_data
-                        .get("SOABI")
-                        .and_then(|abi| abi.split('-').nth(1).map(ToString::to_string));
-                    let interpreter_kind = sysconfig_data
-                        .get("SOABI")
-                        .and_then(|tag| {
-                            if tag.starts_with("pypy") {
-                                Some(InterpreterKind::PyPy)
-                            } else if tag.starts_with("cpython") {
-                                Some(InterpreterKind::CPython)
-                            } else {
-                                None
-                            }
-                        })
-                        .context("unsupported Python interpreter")?;
-                    interpreters.push(PythonInterpreter {
-                        config: InterpreterConfig {
-                            major,
-                            minor,
-                            interpreter_kind,
-                            abiflags,
-                            ext_suffix: ext_suffix.to_string(),
-                            abi_tag,
-                            pointer_width: None,
-                        },
-                        executable: PathBuf::new(),
-                        platform: None,
-                        runnable: false,
-                    });
-                } else {
-                    if interpreter.is_empty() {
-                        bail!("Couldn't find any python interpreters. Please specify at least one with -i");
-                    }
-                    interpreters = find_interpreter_in_sysconfig(interpreter, target)?;
-                }
-            } else {
-                match find_interpreter_in_host(bridge, interpreter, target, min_python_minor) {
-                    Ok(host_interps) => {
-                        interpreters = host_interps;
-                        native_interpreters = true;
-                    }
-                    Err(err) => {
-                        if !interpreter.is_empty()
-                            && binding_name.starts_with("pyo3")
-                            && target.is_unix()
-                        {
-                            interpreters = find_interpreter_in_sysconfig(interpreter, target)
-                                .map_err(|_| err)?;
-                        } else {
-                            return Err(err);
-                        }
-                    }
-                }
-            }
-
-            let interpreters_str = interpreters
-                .iter()
-                .map(ToString::to_string)
-                .collect::<Vec<String>>()
-                .join(", ");
-            if native_interpreters {
-                println!("🐍 Found {}", interpreters_str);
-            } else {
-                println!("🐍 Found cross compiling target {}", interpreters_str);
-            }
-
-            Ok(interpreters)
-        }
-        BridgeModel::Cffi => {
-            let interpreter = find_single_python_interpreter(bridge, interpreter, target, "cffi")?;
-            println!("🐍 Using {} to generate the cffi bindings", interpreter);
-            Ok(vec![interpreter])
-        }
-        BridgeModel::Bin(None) => Ok(vec![]),
-        BridgeModel::BindingsAbi3(major, minor) => {
-            let interpreters = if !interpreter.is_empty() {
-                match find_interpreter_in_host(bridge, interpreter, target, Some(*minor as usize)) {
-                    Ok(host_interps) => host_interps,
-                    Err(err) => {
-                        if !interpreter.is_empty() {
-                            find_interpreter_in_sysconfig(interpreter, target).map_err(|_| err)?
-                        } else {
-                            Vec::new()
-                        }
-                    }
-                }
-            } else {
-                find_interpreter_in_host(bridge, interpreter, target, 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
-            // argument.
-            if target.is_windows() {
-                if env::var_os("PYO3_CROSS_LIB_DIR").is_some() {
-                    // PYO3_CROSS_LIB_DIR should point to the `libs` directory inside base_prefix
-                    // when cross compiling, so we fake a python interpreter matching it
-                    println!("⚠️  Cross-compiling is poorly supported");
-                    Ok(vec![PythonInterpreter {
-                        config: InterpreterConfig {
-                            major: *major as usize,
-                            minor: *minor as usize,
-                            interpreter_kind: InterpreterKind::CPython,
-                            abiflags: "".to_string(),
-                            ext_suffix: ".pyd".to_string(),
-                            abi_tag: None,
-                            pointer_width: None,
-                        },
-                        executable: PathBuf::new(),
-                        platform: None,
-                        runnable: false,
-                    }])
-                } else if let Some(interp) = interpreters.get(0) {
-                    println!("🐍 Using {} to generate to link bindings (With abi3, an interpreter is only required on windows)", interp);
-                    Ok(interpreters)
-                } else if generate_import_lib {
-                    println!("🐍 Not using a specific python interpreter (Automatically generating windows import library)");
-                    // fake a python interpreter
-                    Ok(vec![PythonInterpreter {
-                        config: InterpreterConfig {
-                            major: *major as usize,
-                            minor: *minor as usize,
-                            interpreter_kind: InterpreterKind::CPython,
-                            abiflags: "".to_string(),
-                            ext_suffix: ".pyd".to_string(),
-                            abi_tag: None,
-                            pointer_width: None,
-                        },
-                        executable: PathBuf::new(),
-                        platform: None,
-                        runnable: false,
-                    }])
-                } else {
-                    bail!("Failed to find a python interpreter");
-                }
-            } else {
-                println!("🐍 Not using a specific python interpreter");
-                if interpreter.is_empty() {
-                    // Fake one to make `BuildContext::build_wheels` happy for abi3 when no cpython/pypy found on host
-                    // The python interpreter config doesn't matter, as it's not used for anything
-                    Ok(vec![PythonInterpreter {
-                        config: InterpreterConfig {
-                            major: *major as usize,
-                            minor: *minor as usize,
-                            interpreter_kind: InterpreterKind::CPython,
-                            abiflags: "".to_string(),
-                            ext_suffix: "".to_string(),
-                            abi_tag: None,
-                            pointer_width: None,
-                        },
-                        executable: PathBuf::new(),
-                        platform: None,
-                        runnable: false,
-                    }])
-                } else if target.cross_compiling() {
-                    let mut interps = Vec::with_capacity(interpreters.len());
-                    let mut pypys = Vec::new();
-                    for interp in interpreters {
-                        if matches!(interp.interpreter_kind, InterpreterKind::PyPy) {
-                            pypys.push(PathBuf::from(format!(
-                                "pypy{}.{}",
-                                interp.major, interp.minor
-                            )));
-                        } else {
-                            interps.push(interp);
-                        }
-                    }
-                    // cross compiling to PyPy with abi3 feature enabled,
-                    // we cannot use host pypy so switch to bundled sysconfig instead
-                    if !pypys.is_empty() {
-                        interps.extend(find_interpreter_in_sysconfig(&pypys, target)?)
-                    }
-                    Ok(interps)
-                } else {
-                    Ok(interpreters)
-                }
-            }
-        }
-    }
-}
-
 /// Helper function that calls shlex on all extra args given
 fn split_extra_args(given_args: &[String]) -> Result<Vec<String>> {
     let mut splitted_args = vec![];
diff --git a/src/compile.rs b/src/compile.rs
index cdd655e4b..e15c0f055 100644
--- a/src/compile.rs
+++ b/src/compile.rs
@@ -1,5 +1,4 @@
 use crate::build_context::BridgeModel;
-use crate::python_interpreter::InterpreterKind;
 use crate::{BuildContext, PlatformTag, PythonInterpreter};
 use anyhow::{anyhow, bail, Context, Result};
 use fat_macho::FatWriter;
@@ -285,7 +284,7 @@ fn compile_target(
 
     if let BridgeModel::BindingsAbi3(_, _) = bindings_crate {
         let is_pypy = python_interpreter
-            .map(|p| p.interpreter_kind == InterpreterKind::PyPy)
+            .map(|p| p.interpreter_kind.is_pypy())
             .unwrap_or(false);
         if !is_pypy && !target.is_windows() {
             let pyo3_ver = pyo3_version(&context.cargo_metadata)
@@ -305,7 +304,7 @@ fn compile_target(
             if bindings_crate.is_bindings("pyo3")
                 || bindings_crate.is_bindings("pyo3-ffi")
                 || (matches!(bindings_crate, BridgeModel::BindingsAbi3(_, _))
-                    && matches!(interpreter.interpreter_kind, InterpreterKind::PyPy))
+                    && interpreter.interpreter_kind.is_pypy())
             {
                 build_command.env("PYO3_PYTHON", &interpreter.executable);
             }
@@ -315,7 +314,7 @@ fn compile_target(
         } else if (bindings_crate.is_bindings("pyo3")
             || bindings_crate.is_bindings("pyo3-ffi")
             || (matches!(bindings_crate, BridgeModel::BindingsAbi3(_, _))
-                && matches!(interpreter.interpreter_kind, InterpreterKind::PyPy)))
+                && interpreter.interpreter_kind.is_pypy()))
             && env::var_os("PYO3_CONFIG_FILE").is_none()
         {
             let pyo3_config = interpreter.pyo3_config_file();
diff --git a/src/develop.rs b/src/develop.rs
index d3549c952..9f00154af 100644
--- a/src/develop.rs
+++ b/src/develop.rs
@@ -30,6 +30,7 @@ pub fn develop(
     let build_options = BuildOptions {
         platform_tag: vec![PlatformTag::Linux],
         interpreter: vec![python.clone()],
+        find_interpreter: false,
         bindings,
         manifest_path: Some(manifest_file.to_path_buf()),
         out: Some(wheel_dir.path().to_path_buf()),
diff --git a/src/main.rs b/src/main.rs
index 7097ff0e9..8a0a2e984 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -367,7 +367,7 @@ fn run() -> Result<()> {
         Opt::ListPython { target } => {
             let found = if target.is_some() {
                 let target = Target::from_target_triple(target)?;
-                PythonInterpreter::find_by_target(&target)
+                PythonInterpreter::find_by_target(&target, None)
             } else {
                 let target = Target::from_target_triple(None)?;
                 // We don't know the targeted bindings yet, so we use the most lenient
diff --git a/src/python_interpreter/mod.rs b/src/python_interpreter/mod.rs
index 91f3de7a8..017199872 100644
--- a/src/python_interpreter/mod.rs
+++ b/src/python_interpreter/mod.rs
@@ -278,6 +278,18 @@ pub enum InterpreterKind {
     PyPy,
 }
 
+impl InterpreterKind {
+    /// Is this a CPython interpreter?
+    pub fn is_cpython(&self) -> bool {
+        matches!(self, InterpreterKind::CPython)
+    }
+
+    /// Is this a PyPy interpreter?
+    pub fn is_pypy(&self) -> bool {
+        matches!(self, InterpreterKind::PyPy)
+    }
+}
+
 impl fmt::Display for InterpreterKind {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
@@ -620,10 +632,22 @@ impl PythonInterpreter {
     }
 
     /// Find all available python interpreters for a given target
-    pub fn find_by_target(target: &Target) -> Vec<PythonInterpreter> {
+    pub fn find_by_target(
+        target: &Target,
+        min_python_minor: Option<usize>,
+    ) -> Vec<PythonInterpreter> {
         InterpreterConfig::lookup_target(target)
             .into_iter()
-            .map(Self::from_config)
+            .filter_map(|config| match min_python_minor {
+                Some(min_python_minor) => {
+                    if config.minor < min_python_minor {
+                        None
+                    } else {
+                        Some(Self::from_config(config))
+                    }
+                }
+                None => Some(Self::from_config(config)),
+            })
             .collect()
     }