Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 54 additions & 82 deletions src/binding_generator/cffi_binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<GeneratorOutput> {
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`.
Expand Down
8 changes: 6 additions & 2 deletions src/binding_generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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())?;
}
}
Expand Down
18 changes: 7 additions & 11 deletions src/build_context.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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)?;
Expand Down
Loading