use anyhow::{Context, Result, bail};
use clap::Subcommand;
use ignore::overrides::Override;
use maturin::{
BuildContext, BuildOptions, BuildOrchestrator, CargoOptions, OutputOptions, PathWriter,
VirtualWriter, write_dist_info,
};
use std::path::PathBuf;
use tracing::instrument;
use crate::commands::StripOption;
#[derive(Debug, Subcommand)]
#[command(name = "pep517", hide = true)]
pub enum Pep517Command {
#[command(name = "write-dist-info")]
WriteDistInfo {
#[command(flatten)]
build_options: BuildOptions,
#[arg(long = "metadata-directory")]
metadata_directory: PathBuf,
#[command(flatten)]
strip_opt: StripOption,
#[arg(long, env = "MATURIN_PGO", value_parser = clap::builder::FalseyValueParser::new())]
pgo: bool,
},
#[command(name = "build-wheel")]
BuildWheel {
#[command(flatten)]
build_options: BuildOptions,
#[command(flatten)]
strip_opt: StripOption,
#[arg(long, env = "MATURIN_PGO", value_parser = clap::builder::FalseyValueParser::new())]
pgo: bool,
#[arg(long)]
editable: bool,
},
#[command(name = "write-sdist")]
WriteSDist {
#[arg(long = "sdist-directory")]
sdist_directory: PathBuf,
#[arg(short = 'm', long = "manifest-path", value_name = "PATH")]
manifest_path: Option<PathBuf>,
},
}
fn require_single_interpreter(opts: &mut BuildOptions, cmd: &str) -> Result<()> {
let mut seen = std::collections::HashSet::new();
opts.python.interpreter.retain(|p| seen.insert(p.clone()));
if opts.python.interpreter.len() > 1 {
bail!(
"`maturin pep517 {cmd}` expects exactly one --interpreter, got {}: {:?}",
opts.python.interpreter.len(),
opts.python.interpreter,
);
}
Ok(())
}
#[instrument(skip_all)]
pub fn pep517(subcommand: Pep517Command) -> Result<()> {
fn ensure_release_profile(context: &mut BuildContext) {
if context.project.cargo_options.profile.is_none() {
context.project.cargo_options.profile = Some("release".to_string());
}
}
match subcommand {
Pep517Command::WriteDistInfo {
mut build_options,
metadata_directory,
strip_opt,
pgo,
} => {
require_single_interpreter(&mut build_options, "write-dist-info")?;
let mut context = build_options
.into_build_context()
.strip(strip_opt.strip)
.editable(false)
.pgo(pgo)
.build()?;
ensure_release_profile(&mut context);
let mut writer =
VirtualWriter::new(PathWriter::from_path(metadata_directory), Override::empty());
let orchestrator = BuildOrchestrator::new(&context);
let dist_info_dir = write_dist_info(
&mut writer,
&context.project.project_layout.project_root,
&context.project.metadata24,
&orchestrator.tags_from_bridge()?,
)?;
writer.finish()?;
println!("{}", dist_info_dir.display());
}
Pep517Command::BuildWheel {
mut build_options,
strip_opt,
pgo,
editable,
} => {
require_single_interpreter(&mut build_options, "build-wheel")?;
let mut build_context = build_options
.into_build_context()
.strip(strip_opt.strip)
.editable(editable)
.pgo(pgo)
.build()?;
ensure_release_profile(&mut build_context);
let orchestrator = BuildOrchestrator::new(&build_context);
let wheels = orchestrator.build_wheels()?;
if wheels.len() != 1 {
bail!(
"expected exactly one wheel to be built, got {}: {:?}",
wheels.len(),
wheels
.iter()
.map(|wheel| wheel.path.display().to_string())
.collect::<Vec<_>>(),
);
}
let wheel_path = wheels[0].path.to_str().with_context(|| {
format!(
"wheel path is not valid UTF-8: {}",
wheels[0].path.display()
)
})?;
println!("{wheel_path}");
}
Pep517Command::WriteSDist {
sdist_directory,
manifest_path,
} => {
let build_options = BuildOptions {
output: OutputOptions {
out: Some(sdist_directory),
..Default::default()
},
cargo: CargoOptions {
manifest_path,
all_features: true,
..Default::default()
},
..Default::default()
};
let build_context = build_options
.into_build_context()
.strip(Some(false))
.editable(false)
.sdist_only(true)
.build()?;
let orchestrator = BuildOrchestrator::new(&build_context);
let sdist = orchestrator
.build_source_distribution()?
.context("Failed to build source distribution, pyproject.toml not found")?;
println!("{}", sdist.path.file_name().unwrap().to_str().unwrap());
}
};
Ok(())
}