#[cfg(feature = "auditwheel")]
use crate::auditwheel::auditwheel_rs;
use crate::compile;
use crate::module_writer::WheelWriter;
use crate::module_writer::{write_bin, write_bindings_module, write_cffi_module};
use crate::Manylinux;
use crate::Metadata21;
use crate::PythonInterpreter;
use crate::Target;
use cargo_metadata::Metadata;
use failure::{bail, Context, Error, ResultExt};
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BridgeModel {
Cffi,
Bin,
Bindings(String),
}
impl BridgeModel {
pub fn unwrap_bindings(&self) -> &str {
match self {
BridgeModel::Bindings(value) => &value,
_ => panic!("Expected Bindings"),
}
}
}
pub struct BuildContext {
pub target: Target,
pub bridge: BridgeModel,
pub metadata21: Metadata21,
pub scripts: HashMap<String, String>,
pub module_name: String,
pub manifest_path: PathBuf,
pub out: PathBuf,
pub release: bool,
pub strip: bool,
pub manylinux: Manylinux,
pub cargo_extra_args: Vec<String>,
pub rustc_extra_args: Vec<String>,
pub interpreter: Vec<PythonInterpreter>,
pub cargo_metadata: Metadata,
}
impl BuildContext {
pub fn build_wheels(&self) -> Result<Vec<(PathBuf, String, Option<PythonInterpreter>)>, Error> {
fs::create_dir_all(&self.out)
.context("Failed to create the target directory for the wheels")?;
let wheels = match &self.bridge {
BridgeModel::Cffi => vec![(self.build_cffi_wheel()?, "py2.py3".to_string(), None)],
BridgeModel::Bin => vec![(self.build_bin_wheel()?, "py2.py3".to_string(), None)],
BridgeModel::Bindings(_) => self.build_binding_wheels()?,
};
Ok(wheels)
}
pub fn build_binding_wheels(
&self,
) -> Result<Vec<(PathBuf, String, Option<PythonInterpreter>)>, Error> {
let mut wheels = Vec::new();
for python_version in &self.interpreter {
let artifact = self.compile_cdylib(Some(&python_version))?;
let tag = python_version.get_tag(&self.manylinux);
let mut builder = WheelWriter::new(
&tag,
&self.out,
&self.metadata21,
&self.scripts,
&[tag.clone()],
)?;
write_bindings_module(&mut builder, &self.module_name, &artifact, &python_version)?;
let wheel_path = builder.finish()?;
println!(
"📦 Built wheel for python {}.{}{} to {}",
python_version.major,
python_version.minor,
python_version.abiflags,
wheel_path.display()
);
wheels.push((
wheel_path,
format!("cp{}{}", python_version.major, python_version.minor),
Some(python_version.clone()),
));
}
Ok(wheels)
}
pub fn compile_cdylib(
&self,
python_interpreter: Option<&PythonInterpreter>,
) -> Result<PathBuf, Error> {
let artifacts = compile(&self, python_interpreter, &self.bridge)
.context("Failed to build a native library through cargo")?;
let artifact = artifacts.get("cdylib").cloned().ok_or_else(|| {
Context::new(
"Cargo didn't build a cdylib. Did you miss crate-type = [\"cdylib\"] \
in the lib section of your Cargo.toml?",
)
})?;
let target = python_interpreter
.map(|x| &x.target)
.unwrap_or(&self.target);
#[cfg(feature = "auditwheel")]
auditwheel_rs(&artifact, target, &self.manylinux)
.context("Failed to ensure manylinux compliance")?;
Ok(artifact)
}
fn get_unversal_tags(&self) -> (String, Vec<String>) {
let tag = format!(
"py2.py3-none-{platform}",
platform = self.target.get_platform_tag(&self.manylinux)
);
let tags = self.target.get_py2_and_py3_tags(&self.manylinux);
(tag, tags)
}
pub fn build_cffi_wheel(&self) -> Result<PathBuf, Error> {
let artifact = self.compile_cdylib(None)?;
let (tag, tags) = self.get_unversal_tags();
let mut builder =
WheelWriter::new(&tag, &self.out, &self.metadata21, &self.scripts, &tags)?;
write_cffi_module(
&mut builder,
self.manifest_path.parent().unwrap(),
&self.module_name,
&artifact,
&self.interpreter[0].executable,
)?;
let wheel_path = builder.finish()?;
println!("📦 Built wheel to {}", wheel_path.display());
Ok(wheel_path)
}
pub fn build_bin_wheel(&self) -> Result<PathBuf, Error> {
let artifacts = compile(&self, None, &self.bridge)
.context("Failed to build a native library through cargo")?;
let artifact = artifacts
.get("bin")
.cloned()
.ok_or_else(|| Context::new("Cargo didn't build a binary."))?;
#[cfg(feature = "auditwheel")]
auditwheel_rs(&artifact, &self.target, &self.manylinux)
.context("Failed to ensure manylinux compliance")?;
let (tag, tags) = self.get_unversal_tags();
if !self.scripts.is_empty() {
bail!("Defining entrypoints and working with a binary doesn't mix well");
}
let mut builder =
WheelWriter::new(&tag, &self.out, &self.metadata21, &self.scripts, &tags)?;
let bin_name = artifact
.file_name()
.expect("Couldn't get the filename from the binary produced by cargo");
write_bin(&mut builder, &artifact, &self.metadata21, bin_name)?;
let wheel_path = builder.finish()?;
println!("📦 Built wheel to {}", wheel_path.display());
Ok(wheel_path)
}
}