From 9f6ad964d955c50991f57870b007b0623fd439f0 Mon Sep 17 00:00:00 2001 From: Sergey Kvachonok Date: Wed, 6 Apr 2022 17:41:57 +0300 Subject: [PATCH] pyo3-build-config: Add `python3-dll-a` crate support Automatically generate `python3.dll` import libraries for Windows compile targets in the build script. Adds a new PyO3 crate feature `auto-abi3-import-lib` enabling automatic import library generation. Closes #2231 --- Cargo.toml | 3 ++ pyo3-build-config/Cargo.toml | 2 ++ pyo3-build-config/src/impl_.rs | 19 +++++++++-- pyo3-build-config/src/util.rs | 59 ++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 pyo3-build-config/src/util.rs diff --git a/Cargo.toml b/Cargo.toml index 7cc0f413efc..d6727aa4620 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,9 @@ abi3-py38 = ["abi3-py39", "pyo3-build-config/abi3-py38", "pyo3-ffi/abi3-py38"] abi3-py39 = ["abi3-py310", "pyo3-build-config/abi3-py39", "pyo3-ffi/abi3-py39"] abi3-py310 = ["abi3", "pyo3-build-config/abi3-py310", "pyo3-ffi/abi3-py310"] +# Generates `python3.dll` import libraries automatically. +auto-abi3-import-lib = ["pyo3-build-config/python3-dll-a"] + # Changes `Python::with_gil` and `Python::acquire_gil` to automatically initialize the # Python interpreter if needed. auto-initialize = [] diff --git a/pyo3-build-config/Cargo.toml b/pyo3-build-config/Cargo.toml index 16d189622cb..86ffa36e101 100644 --- a/pyo3-build-config/Cargo.toml +++ b/pyo3-build-config/Cargo.toml @@ -12,9 +12,11 @@ edition = "2018" [dependencies] once_cell = "1" +python3-dll-a = { version = "0.2", optional = true } target-lexicon = "0.12" [build-dependencies] +python3-dll-a = { version = "0.2", optional = true } target-lexicon = "0.12" [features] diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index afcb6494c13..6d31bcc365c 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -1,3 +1,6 @@ +#[path = "util.rs"] +mod util; + use std::{ collections::{HashMap, HashSet}, convert::AsRef, @@ -22,6 +25,8 @@ use crate::{ warn, }; +use self::util::generate_abi3_import_lib; + /// Minimum Python version PyO3 supports. const MINIMUM_SUPPORTED_VERSION: PythonVersion = PythonVersion { major: 3, minor: 7 }; @@ -1381,7 +1386,12 @@ fn default_cross_compile(cross_compile_config: &CrossCompileConfig) -> Result Result { Ok(interpreter_config) } else if let Some(version) = abi3_version { let host = Triple::host(); - Ok(default_abi3_config(&host, version)) + let mut interpreter_config = default_abi3_config(&host, version); + + // Auto generate python3.dll import libraries for Windows targets. + interpreter_config.lib_dir = generate_abi3_import_lib(&host)?; + + Ok(interpreter_config) } else { bail!("An abi3-py3* feature must be specified when compiling without a Python interpreter.") } diff --git a/pyo3-build-config/src/util.rs b/pyo3-build-config/src/util.rs new file mode 100644 index 00000000000..c375e0cc5d1 --- /dev/null +++ b/pyo3-build-config/src/util.rs @@ -0,0 +1,59 @@ +//! Additional functionality enabled by crate features + +#![allow(clippy::unnecessary_wraps)] + +use super::Triple; +use crate::errors::Result; + +/// Generates `python3.dll` import library for Windows targets. +/// +/// Places the import library into the build script output directory +/// and returns the full library directory path. +/// +/// Does nothing if the target OS is not Windows. +#[cfg(feature = "python3-dll-a")] +pub(super) fn generate_abi3_import_lib(target: &Triple) -> Result> { + use super::{Architecture, OperatingSystem}; + use crate::errors::Context; + + if target.operating_system != OperatingSystem::Windows { + return Ok(None); + } + + let out_dir = std::env::var_os("OUT_DIR") + .expect("generate_abi3_import_lib() must be called from a build script"); + + // Put the import library into the build script output directory. + let mut out_lib_dir = std::path::PathBuf::from(out_dir); + out_lib_dir.push("lib"); + + // Convert `Architecture` to rustc `target_arch` format. + let arch = match target.architecture { + // i686, i586, etc. + Architecture::X86_32(_) => "x86".to_string(), + other => other.to_string(), + }; + + let env = target.environment.to_string(); + + python3_dll_a::generate_implib_for_target(&out_lib_dir, &arch, &env) + .context("failed to generate python3.dll import library")?; + + let out_lib_dir_string = out_lib_dir + .to_str() + .ok_or("build directory is not a valid UTF-8 string")? + .to_owned(); + + Ok(Some(out_lib_dir_string)) +} + +/// Generates `python3.dll` import library for Windows targets. +/// +/// Places the import library into the build script output directory +/// and returns the full library directory path. +/// +/// Does nothing if the target OS is not Windows. +#[cfg(not(feature = "python3-dll-a"))] +pub(super) fn generate_abi3_import_lib(_target: &Triple) -> Result> { + Ok(None) +}