pub mod cfg_eval;
pub mod cli;
pub mod config;
pub mod package;
pub mod runner;
pub mod target;
pub mod tee;
pub mod workspace;
pub use cli::{ArgumentParser, Command, Options, parse_arguments};
pub use package::{FeatureCombinationError, Package};
pub use runner::{
ExitCode, color_spec, error_counts, print_feature_matrix, print_feature_matrix_for_target,
print_summary, run_cargo_command, run_cargo_command_for_target, warning_counts,
};
pub use workspace::Workspace;
use cfg_eval::RustcCfgEvaluator;
use cli::cargo_subcommand;
use runner::print_feature_combination_error;
use target::{RustcTargetDetector, TargetDetector};
use color_eyre::eyre;
use std::process;
macro_rules! metadata_key {
() => {
"cargo-feature-combinations"
};
}
pub(crate) const METADATA_KEY: &str = metadata_key!();
pub(crate) const PKG_METADATA_SECTION: &str = concat!("[package.metadata.", metadata_key!(), "]");
pub(crate) const WS_METADATA_SECTION: &str = concat!("[workspace.metadata.", metadata_key!(), "]");
pub fn run(bin_name: &str) -> eyre::Result<()> {
color_eyre::install()?;
let (options, cargo_args) = parse_arguments(bin_name)?;
if let Some(Command::Help) = options.command {
cli::print_help();
return Ok(());
}
if let Some(Command::Version) = options.command {
println!("cargo-{bin_name} v{}", env!("CARGO_PKG_VERSION"));
return Ok(());
}
let mut cmd = cargo_metadata::MetadataCommand::new();
if let Some(ref manifest_path) = options.manifest_path {
cmd.manifest_path(manifest_path);
}
let metadata = cmd.exec()?;
let mut packages = metadata.packages_for_fc()?;
if options.manifest_path.is_some()
&& options.packages.is_empty()
&& let Some(root) = metadata.root_package()
{
packages.retain(|p| p.id == root.id);
}
packages.retain(|p| !options.exclude_packages.contains(p.name.as_str()));
if options.only_packages_with_lib_target {
packages.retain(|p| {
p.targets
.iter()
.any(|t| t.kind.contains(&cargo_metadata::TargetKind::Lib))
});
}
if !options.packages.is_empty() {
packages.retain(|p| options.packages.contains(p.name.as_str()));
}
let cargo_args_owned = cargo_args;
let cargo_args: Vec<&str> = cargo_args_owned.iter().map(String::as_str).collect();
let detector = RustcTargetDetector::default();
let target = detector.detect_target(&cargo_args_owned)?;
let mut evaluator = RustcCfgEvaluator::default();
let result = match options.command {
Some(Command::Help | Command::Version) => Ok(None),
Some(Command::FeatureMatrix { pretty }) => print_feature_matrix_for_target(
&packages,
pretty,
options.packages_only,
&target,
&mut evaluator,
),
None => {
if cargo_subcommand(cargo_args.as_slice()) == cli::CargoSubcommand::Other {
eprintln!(
"warning: `cargo {bin_name}` only supports cargo's `build`, `test`, `run`, `check`, `doc`, and `clippy` subcommands",
);
}
run_cargo_command_for_target(&packages, cargo_args, &options, &target, &mut evaluator)
}
};
match result {
Ok(Some(exit_code)) => process::exit(exit_code),
Ok(None) => Ok(()),
Err(err) => {
if let Some(e) = err.downcast_ref::<FeatureCombinationError>() {
print_feature_combination_error(e);
process::exit(2);
}
Err(err)
}
}
}