mod audit;
mod build;
mod cli;
mod config;
mod libclang;
mod macos;
mod meta;
mod native_toolchain;
mod out_dir_archive;
mod platform;
mod report;
mod sandbox;
mod self_update;
mod store;
mod zig;
use anyhow::Result;
use clap::{CommandFactory, Parser};
pub const METAL_XCODE_BUILD: &str = "16A242d";
fn curl() -> std::process::Command {
std::process::Command::new(std::env::var_os("CORGI_CURL").unwrap_or_else(|| "curl".into()))
}
impl From<cli::TargetSelectionArgs> for build::TargetSelection {
fn from(args: cli::TargetSelectionArgs) -> Self {
Self {
named_bins: args.named_bins,
named_tests: args.named_tests,
named_benches: args.named_benches,
named_examples: args.named_examples,
lib: args.lib,
bins: args.bins,
tests: args.tests,
benches: args.all_benches,
examples: args.examples,
all_targets: args.all_targets,
}
}
}
fn main() {
if let Err(e) = real_main() {
if let Some(exit) = e.downcast_ref::<build::RunExit>() {
std::process::exit(exit.code);
}
eprintln!("corgi error: {e:#}");
std::process::exit(1);
}
}
fn real_main() -> Result<()> {
let argv: Vec<_> = std::env::args_os().collect();
if macos::is_driver_invocation(&argv) {
return macos::run_driver_invocation(&argv);
}
if zig::is_linker_invocation(&argv) {
return zig::run_linker_invocation(&argv);
}
let had_argument_delimiter = argv.iter().any(|arg| arg == "--");
let invocation_dir = match cli::invocation_directory(&argv) {
Some(dir) if dir.is_absolute() => dir,
Some(dir) => std::env::current_dir()?.join(dir),
None => std::env::current_dir()?,
};
self_update::update_if_required(&invocation_dir, &argv)?;
let cli::Cli {
dir,
manifest_path,
verbose,
command,
} = cli::Cli::try_parse_from(argv)
.map_err(cli::explain_parse_error)
.unwrap_or_else(|error| error.exit());
let dir = if let Some(manifest) = manifest_path {
anyhow::ensure!(
manifest
.file_name()
.is_some_and(|name| name == "Cargo.toml")
&& manifest.is_file(),
"--manifest-path must name an existing Cargo.toml: {}",
manifest.display()
);
Some(
manifest
.parent()
.filter(|parent| !parent.as_os_str().is_empty())
.unwrap_or(std::path::Path::new("."))
.to_path_buf(),
)
} else {
dir
};
let dir = match dir {
Some(d) => d,
None => std::env::current_dir()?,
};
let Some(command) = command else {
cli::Cli::command().print_help()?;
return Ok(());
};
let store_root = store::default_root()?;
match command {
cli::Command::Audit(args) => audit::audit(
&dir,
args.release,
verbose,
args.target.as_deref(),
args.root.as_deref(),
),
cli::Command::Pin => {
let path = build::pin_corgi_version(&dir, env!("CARGO_PKG_VERSION"))?;
eprintln!(
"{:>12} corgi {} in {}",
"Pinned",
env!("CARGO_PKG_VERSION"),
path.display()
);
Ok(())
}
command => {
let store = store::Store::new(store_root)?;
let run_build = |store: store::Store,
args: cli::BuildArgs,
workspace: bool,
mode: build::Mode,
targets: cli::TargetSelectionArgs,
clippy_args: Vec<String>,
no_run: bool,
force_tests: bool,
test_timeout: Option<std::time::Duration>,
test_filters: Vec<String>,
exec_args: Vec<String>| {
if args.all_features {
anyhow::bail!(
"--all-features is not supported. Use corgi roots and explicitly test the feature combinations you care about."
);
}
build::build(
store,
&dir,
build::BuildOpts {
verbose,
release: args.release,
profile: args.profile,
workspace,
packages: args.packages,
features: args.features,
no_default_features: args.no_default_features,
target: args.target,
target_dir: args.target_dir,
root: args.root,
mode,
targets: targets.into(),
clippy_args,
timings: args.timings,
no_incremental: args.no_incremental,
no_run,
force_tests,
test_timeout,
test_filters,
exec_args,
},
)
};
match command {
cli::Command::Build(args) => run_build(
store,
args.build,
args.workspace,
build::Mode::Build,
args.targets,
Vec::new(),
false,
false,
None,
Vec::new(),
Vec::new(),
),
cli::Command::Bench(mut args) => {
if args.build.build.release {
anyhow::bail!("`corgi bench` does not accept `--release`");
}
if let Some(filter) = args.filter {
args.exec_args.insert(0, filter);
}
run_build(
store,
args.build.build,
args.build.workspace,
build::Mode::Bench,
args.build.targets,
Vec::new(),
args.no_run,
false,
None,
Vec::new(),
args.exec_args,
)
}
cli::Command::Check(args) => run_build(
store,
args.build,
args.workspace,
build::Mode::Check,
args.targets,
Vec::new(),
false,
false,
None,
Vec::new(),
Vec::new(),
),
cli::Command::Clippy(args) => run_build(
store,
args.build.build,
args.build.workspace,
build::Mode::Clippy,
args.build.targets,
args.clippy_args,
false,
false,
None,
Vec::new(),
Vec::new(),
),
cli::Command::Run(args) => run_build(
store,
args.build,
false,
build::Mode::Run,
cli::TargetSelectionArgs {
named_bins: args.bin.into_iter().collect(),
named_examples: args.example.into_iter().collect(),
..Default::default()
},
Vec::new(),
false,
false,
None,
Vec::new(),
args.exec_args,
),
cli::Command::Test(args) => run_build(
store,
args.build,
args.workspace,
build::Mode::Test,
args.targets,
Vec::new(),
args.no_run,
args.force,
args.timeout.map(std::time::Duration::from_secs),
args.filters,
args.exec_args,
),
cli::Command::Fmt(mut args) => {
if had_argument_delimiter {
args.args.insert(0, "--".to_string());
}
build::fmt(
store,
&dir,
args.workspace,
&args.packages,
verbose,
&args.args,
)
}
cli::Command::Fetch => build::fetch(store, &dir),
cli::Command::Clean(args) => build::clean(&store, args.cache, args.older_than),
cli::Command::Audit(_) | cli::Command::Pin => unreachable!(),
}
}
}
}