use anyhow::{Context, Result};
use cargo_options::heading;
#[cfg(feature = "zig")]
use cargo_zigbuild::Zig;
#[cfg(feature = "cli-completion")]
use clap::CommandFactory;
use clap::Parser;
#[cfg(feature = "schemars")]
use maturin::GenerateJsonSchemaOptions;
#[cfg(feature = "upload")]
use maturin::PublishOpt;
use maturin::{BuildOptions, CargoOptions, DevelopOptions, PythonOptions, TargetTriple};
#[cfg(feature = "scaffolding")]
use maturin::{GenerateProjectOptions, ci::GenerateCI};
use std::env;
use std::path::PathBuf;
use std::str::FromStr;
use tracing::instrument;
use tracing_subscriber::filter::Directive;
use tracing_subscriber::{EnvFilter, Layer, layer::SubscriberExt, util::SubscriberInitExt};
mod commands;
use crate::commands::StripOption;
use crate::commands::pep517::Pep517Command;
#[derive(Debug, Parser)]
#[command(
version,
name = env!("CARGO_PKG_NAME"),
display_order = 1,
after_help = "Visit https://maturin.rs to learn more about maturin.",
styles = cargo_options::styles(),
)]
struct Opt {
#[arg(global = true, action = clap::ArgAction::Count, long, short)]
verbose: u8,
#[command(subcommand)]
command: Command,
}
#[derive(Debug, Parser)]
enum Command {
#[command(name = "build", alias = "b")]
Build {
#[arg(short = 'r', long, help_heading = heading::COMPILATION_OPTIONS, conflicts_with = "profile")]
release: bool,
#[command(flatten)]
strip_opt: StripOption,
#[arg(long)]
sdist: bool,
#[arg(long)]
pgo: bool,
#[command(flatten)]
build: BuildOptions,
},
#[cfg(feature = "upload")]
#[command(name = "publish")]
Publish {
#[arg(long, conflicts_with = "profile")]
debug: bool,
#[arg(long = "no-strip")]
no_strip: bool,
#[arg(long = "no-sdist")]
no_sdist: bool,
#[arg(long)]
pgo: bool,
#[command(flatten)]
publish: PublishOpt,
#[command(flatten)]
build: BuildOptions,
},
#[command(name = "list-python")]
ListPython {
#[arg(long)]
target: Option<TargetTriple>,
},
#[command(name = "develop", alias = "dev")]
Develop(DevelopOptions),
#[command(name = "sdist")]
SDist {
#[arg(short = 'm', long = "manifest-path")]
manifest_path: Option<PathBuf>,
#[arg(short, long)]
out: Option<PathBuf>,
},
#[cfg(feature = "scaffolding")]
#[command(name = "init")]
InitProject {
path: Option<String>,
#[command(flatten)]
options: GenerateProjectOptions,
},
#[cfg(feature = "scaffolding")]
#[command(name = "new")]
NewProject {
path: String,
#[command(flatten)]
options: GenerateProjectOptions,
},
#[cfg(feature = "scaffolding")]
#[command(name = "generate-ci")]
GenerateCI(GenerateCI),
#[cfg(feature = "upload")]
#[command(name = "upload")]
Upload {
#[command(flatten)]
publish: PublishOpt,
#[arg(value_name = "FILE")]
files: Vec<PathBuf>,
},
#[command(name = "generate-stubs")]
GenerateStub {
#[arg(short, long)]
out: PathBuf,
#[command(flatten)]
python: PythonOptions,
#[command(flatten)]
cargo: CargoOptions,
},
#[command(subcommand)]
Pep517(Pep517Command),
#[cfg(feature = "cli-completion")]
#[command(name = "completions", hide = true)]
Completions {
#[arg(value_name = "SHELL")]
shell: clap_complete_command::Shell,
},
#[cfg(feature = "zig")]
#[command(subcommand, hide = true)]
Zig(Zig),
#[cfg(feature = "schemars")]
#[command(name = "generate-json-schema", hide = true)]
GenerateJsonSchema(GenerateJsonSchemaOptions),
}
#[instrument]
fn run() -> Result<()> {
#[cfg(feature = "zig")]
{
let mut args = env::args();
let program_path = PathBuf::from(args.next().expect("no program path"));
let program_name = program_path.file_stem().expect("no program name");
if program_name.eq_ignore_ascii_case("ar") {
let zig = Zig::Ar {
args: args.collect(),
};
zig.execute()?;
return Ok(());
} else if program_name.eq_ignore_ascii_case("lib") {
let zig = Zig::Lib {
args: args.collect(),
};
zig.execute()?;
return Ok(());
} else if program_name.to_string_lossy().ends_with("dlltool") {
let zig = Zig::Dlltool {
args: args.collect(),
};
zig.execute()?;
return Ok(());
} else if program_name.eq_ignore_ascii_case("install_name_tool") {
cargo_zigbuild::macos::install_name_tool::execute(args)?;
return Ok(());
}
}
#[cfg(not(feature = "wild"))]
let opt = Opt::parse();
#[cfg(feature = "wild")]
let opt = Opt::parse_from(wild::args_os());
setup_logging(opt.verbose)?;
match opt.command {
Command::Build {
build,
release,
strip_opt,
sdist,
pgo,
} => commands::build::build(build, release, strip_opt, sdist, pgo)?,
#[cfg(feature = "upload")]
Command::Publish {
build,
publish,
debug,
no_strip,
no_sdist,
pgo,
} => commands::publish::publish(build, publish, debug, no_strip, no_sdist, pgo)?,
Command::ListPython { target } => commands::list_python(target)?,
Command::Develop(develop_options) => commands::develop::develop_cmd(develop_options)?,
Command::SDist { manifest_path, out } => commands::sdist::sdist(manifest_path, out)?,
Command::Pep517(subcommand) => commands::pep517::pep517(subcommand)?,
#[cfg(feature = "scaffolding")]
Command::InitProject { path, options } => commands::init_project(path, options)?,
#[cfg(feature = "scaffolding")]
Command::NewProject { path, options } => commands::new_project(path, options)?,
#[cfg(feature = "scaffolding")]
Command::GenerateCI(generate_ci) => commands::generate_ci(generate_ci)?,
#[cfg(feature = "upload")]
Command::Upload { publish, files } => commands::upload(publish, files)?,
Command::GenerateStub { out, python, cargo } => {
commands::generate_stubs::generate_stubs(out, python, cargo)?
}
#[cfg(feature = "cli-completion")]
Command::Completions { shell } => {
commands::completions(shell, &mut Opt::command());
}
#[cfg(feature = "zig")]
Command::Zig(subcommand) => commands::zig(subcommand)?,
#[cfg(feature = "schemars")]
Command::GenerateJsonSchema(args) => commands::generate_json_schema(args)?,
}
Ok(())
}
#[cfg(not(debug_assertions))]
fn setup_panic_hook() {
let default_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
eprintln!("\n===================================================================");
eprintln!("maturin has panicked. This is a bug in maturin. Please report this");
eprintln!("at https://github.com/PyO3/maturin/issues/new/choose.");
eprintln!("If you can reliably reproduce this panic, include the");
eprintln!("reproduction steps and re-run with the RUST_BACKTRACE=1 environment");
eprintln!("variable set and include the backtrace in your report.");
eprintln!();
eprintln!("Platform: {} {}", env::consts::OS, env::consts::ARCH);
eprintln!("Version: {}", env!("CARGO_PKG_VERSION"));
eprintln!("Args: {}", env::args().collect::<Vec<_>>().join(" "));
eprintln!();
default_hook(panic_info);
std::process::exit(101);
}));
}
fn setup_logging(verbose: u8) -> Result<()> {
let default_directive = match verbose {
0..=1 => tracing::level_filters::LevelFilter::OFF.into(),
2 => Directive::from_str("debug").unwrap(),
3.. => Directive::from_str("trace").unwrap(),
};
let filter = EnvFilter::builder()
.with_default_directive(default_directive)
.from_env()
.context("Invalid RUST_LOG directives")?;
let logger = tracing_subscriber::fmt::layer()
.compact()
.with_span_events(tracing_subscriber::fmt::format::FmtSpan::CLOSE);
tracing_subscriber::registry()
.with(logger.with_filter(filter))
.init();
Ok(())
}
fn main() {
#[cfg(not(debug_assertions))]
setup_panic_hook();
if let Err(e) = run() {
eprintln!("💥 maturin failed");
for cause in e.chain() {
eprintln!(" Caused by: {cause}");
}
std::process::exit(1);
}
}