From 4707c3325ef208004af1e46a7463c896e2842b66 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 `generate-abi3-import-lib` enabling automatic import library generation. Closes #2231 --- Cargo.toml | 3 ++ pyo3-build-config/Cargo.toml | 2 + pyo3-build-config/src/abi3_import_lib.rs | 48 ++++++++++++++++++++++++ pyo3-build-config/src/impl_.rs | 29 ++++++++++++-- 4 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 pyo3-build-config/src/abi3_import_lib.rs diff --git a/Cargo.toml b/Cargo.toml index 7cc0f413efc..99655923824 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"] +# Automatically generates `python3.dll` import libraries for Windows targets. +generate-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/abi3_import_lib.rs b/pyo3-build-config/src/abi3_import_lib.rs new file mode 100644 index 00000000000..2a089fbadfe --- /dev/null +++ b/pyo3-build-config/src/abi3_import_lib.rs @@ -0,0 +1,48 @@ +//! Optional `python3.dll` import library generator for Windows + +use std::env; +use std::path::PathBuf; + +use python3_dll_a::generate_implib_for_target; + +use crate::errors::{Context, Result}; + +use super::{Architecture, OperatingSystem, Triple}; + +/// Generates the `python3.dll` import library for Windows targets. +/// +/// Places the generated import library into the build script output directory +/// and returns the full library directory path. +/// +/// Does nothing if the target OS is not Windows. +pub(super) fn generate_abi3_import_lib(target: &Triple) -> Result> { + if target.operating_system != OperatingSystem::Windows { + return Ok(None); + } + + let out_dir = env::var_os("OUT_DIR") + .expect("generate_abi3_import_lib() must be called from a build script"); + + // Put the newly created import library into the build script output directory. + let mut out_lib_dir = PathBuf::from(out_dir); + out_lib_dir.push("lib"); + + // Convert `Architecture` enum to rustc `target_arch` option 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(); + + 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)) +} diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index afcb6494c13..f9b10849411 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -1,3 +1,11 @@ +//! Main implementation module included in both the `pyo3-build-config` library crate +//! and its build script. + +// Optional python3.dll import library generator for Windows +#[cfg(feature = "python3-dll-a")] +#[path = "abi3_import_lib.rs"] +mod abi3_import_lib; + use std::{ collections::{HashMap, HashSet}, convert::AsRef, @@ -1352,6 +1360,7 @@ fn cross_compile_from_sysconfigdata( /// Windows, macOS and Linux. /// /// Must be called from a PyO3 crate build script. +#[allow(unused_mut)] fn default_cross_compile(cross_compile_config: &CrossCompileConfig) -> Result { let version = cross_compile_config .version @@ -1381,7 +1390,13 @@ fn default_cross_compile(cross_compile_config: &CrossCompileConfig) -> Result Result> { /// Generates an interpreter config which will be hard-coded into the pyo3-build-config crate. /// Only used by `pyo3-build-config` build script. -#[allow(dead_code)] +#[allow(dead_code, unused_mut)] pub fn make_interpreter_config() -> Result { let abi3_version = get_abi3_version(); @@ -1651,7 +1666,15 @@ pub fn make_interpreter_config() -> 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. + #[cfg(feature = "python3-dll-a")] + { + interpreter_config.lib_dir = self::abi3_import_lib::generate_abi3_import_lib(&host)?; + } + + Ok(interpreter_config) } else { bail!("An abi3-py3* feature must be specified when compiling without a Python interpreter.") }