From 531aa5426665bdd7b198ec7e1db3cd65ca51c674 Mon Sep 17 00:00:00 2001 From: Eashwar Ranganathan Date: Mon, 24 Nov 2025 16:32:59 -0500 Subject: [PATCH] Add Cffi impl --- src/binding_generator/cffi_binding.rs | 136 ++++++++++---------------- src/binding_generator/mod.rs | 8 +- src/build_context.rs | 18 ++-- 3 files changed, 67 insertions(+), 95 deletions(-) diff --git a/src/binding_generator/cffi_binding.rs b/src/binding_generator/cffi_binding.rs index 628ab9a17..7bfe507b2 100644 --- a/src/binding_generator/cffi_binding.rs +++ b/src/binding_generator/cffi_binding.rs @@ -10,101 +10,73 @@ use std::str; use anyhow::Context as _; use anyhow::Result; +use anyhow::anyhow; use anyhow::bail; use fs_err as fs; -use fs_err::File; use tempfile::TempDir; use tempfile::tempdir; use tracing::debug; -use crate::ModuleWriter; -use crate::PyProjectToml; -use crate::Target; -use crate::module_writer::ModuleWriterExt; -use crate::module_writer::write_python_part; -use crate::project_layout::ProjectLayout; +use crate::BuildArtifact; +use crate::BuildContext; +use crate::PythonInterpreter; use crate::target::Os; -/// Creates the cffi module with the shared library, the cffi declarations and the cffi loader -#[allow(clippy::too_many_arguments)] -pub fn write_cffi_module( - writer: &mut impl ModuleWriter, - target: &Target, - project_layout: &ProjectLayout, - crate_dir: &Path, - target_dir: &Path, - module_name: &str, - artifact: &Path, - python: &Path, - editable: bool, - pyproject_toml: Option<&PyProjectToml>, -) -> Result<()> { - let cffi_declarations = generate_cffi_declarations(crate_dir, target_dir, python)?; +use super::BindingGenerator; +use super::GeneratorOutput; - if !editable { - write_python_part(writer, project_layout, pyproject_toml) - .context("Failed to add the python module to the package")?; - } +/// A generator for producing Cffi bindings. +#[derive(Default)] +pub struct CffiBindingGenerator {} - let cffi_module_file_name = { - let extension_name = &project_layout.extension_name; - // https://cffi.readthedocs.io/en/stable/embedding.html#issues-about-using-the-so - match target.target_os() { - Os::Macos => format!("lib{extension_name}.dylib"), - Os::Windows => format!("{extension_name}.dll"), - _ => format!("lib{extension_name}.so"), - } - }; - let module; - if let Some(python_module) = &project_layout.python_module { - if editable { - let base_path = python_module.join(&project_layout.extension_name); - fs::create_dir_all(&base_path)?; - let target = base_path.join(&cffi_module_file_name); - fs::copy(artifact, &target).context(format!( - "Failed to copy {} to {}", - artifact.display(), - target.display() - ))?; - File::create(base_path.join("__init__.py"))? - .write_all(cffi_init_file(&cffi_module_file_name).as_bytes())?; - File::create(base_path.join("ffi.py"))?.write_all(cffi_declarations.as_bytes())?; - } +impl BindingGenerator for CffiBindingGenerator { + fn generate_bindings( + &self, + context: &BuildContext, + interpreter: Option<&PythonInterpreter>, + _artifact: &BuildArtifact, + module: &Path, + _temp_dir: &TempDir, + ) -> Result { + let cffi_module_file_name = { + let extension_name = &context.project_layout.extension_name; + // https://cffi.readthedocs.io/en/stable/embedding.html#issues-about-using-the-so + match context.target.target_os() { + Os::Macos => format!("lib{extension_name}.dylib"), + Os::Windows => format!("{extension_name}.dll"), + _ => format!("lib{extension_name}.so"), + } + }; + let base_path = if context.project_layout.python_module.is_some() { + module.join(&context.project_layout.extension_name) + } else { + module.to_path_buf() + }; + let artifact_target = base_path.join(&cffi_module_file_name); - let relative = project_layout - .rust_module - .strip_prefix(python_module.parent().unwrap()) - .unwrap(); - module = relative.join(&project_layout.extension_name); - } else { - module = PathBuf::from(module_name); - let type_stub = project_layout - .rust_module - .join(format!("{module_name}.pyi")); - if type_stub.exists() { - eprintln!("📖 Found type stub file at {module_name}.pyi"); - writer.add_file(module.join("__init__.pyi"), type_stub, false)?; - writer.add_empty_file(module.join("py.typed"))?; - } - }; + let mut additional_files = HashMap::new(); + additional_files.insert( + base_path.join("__init__.py"), + cffi_init_file(&cffi_module_file_name).into(), + ); + additional_files.insert( + base_path.join("ffi.py"), + generate_cffi_declarations( + context.manifest_path.parent().unwrap(), + &context.target_dir, + &interpreter + .ok_or_else(|| anyhow!("A python interpreter is required for cffi builds but one was not provided"))? + .executable, + )? + .into(), + ); - if !editable || project_layout.python_module.is_none() { - writer.add_bytes( - module.join("__init__.py"), - None, - cffi_init_file(&cffi_module_file_name).as_bytes(), - false, - )?; - writer.add_bytes( - module.join("ffi.py"), - None, - cffi_declarations.as_bytes(), - false, - )?; - writer.add_file(module.join(&cffi_module_file_name), artifact, true)?; + Ok(GeneratorOutput { + artifact_target, + artifact_source_override: None, + additional_files: Some(additional_files), + }) } - - Ok(()) } /// Glue code that exposes `lib`. diff --git a/src/binding_generator/mod.rs b/src/binding_generator/mod.rs index 6be87c709..ad3a9e554 100644 --- a/src/binding_generator/mod.rs +++ b/src/binding_generator/mod.rs @@ -24,7 +24,7 @@ mod pyo3_binding; mod uniffi_binding; mod wasm_binding; -pub use cffi_binding::write_cffi_module; +pub use cffi_binding::CffiBindingGenerator; pub use pyo3_binding::Pyo3BindingGenerator; pub use uniffi_binding::write_uniffi_module; pub use wasm_binding::write_wasm_launcher; @@ -143,7 +143,11 @@ pub fn generate_binding( let target = base_path.join(target); fs::create_dir_all(target.parent().unwrap())?; debug!("Generating file {}", target.display()); - let mut file = File::options().create(true).truncate(true).open(&target)?; + let mut file = File::options() + .write(true) + .create(true) + .truncate(true) + .open(&target)?; file.write_all(data.as_slice())?; } } diff --git a/src/build_context.rs b/src/build_context.rs index 9851d3684..d8ddb4ac8 100644 --- a/src/build_context.rs +++ b/src/build_context.rs @@ -1,7 +1,7 @@ use crate::auditwheel::{AuditWheelMode, get_policy_and_libs, patchelf, relpath}; use crate::auditwheel::{PlatformTag, Policy}; use crate::binding_generator::{ - Pyo3BindingGenerator, generate_binding, write_bin, write_cffi_module, write_uniffi_module, + CffiBindingGenerator, Pyo3BindingGenerator, generate_binding, write_bin, write_uniffi_module, write_wasm_launcher, }; use crate::bridge::Abi3Version; @@ -976,17 +976,13 @@ impl BuildContext { )?; self.add_external_libs(&mut writer, &[&artifact], &[ext_libs])?; - write_cffi_module( + let generator = CffiBindingGenerator::default(); + generate_binding( &mut writer, - &self.target, - &self.project_layout, - self.manifest_path.parent().unwrap(), - &self.target_dir, - &self.module_name, - &artifact.path, - &self.interpreter[0].executable, - self.editable, - self.pyproject_toml.as_ref(), + &generator, + self, + self.interpreter.first(), + &artifact, )?; self.add_pth(&mut writer)?;