Port bench and build to clap

This commit is contained in:
Aleksey Kladov 2018-03-07 00:01:03 +03:00
parent 3f7a42663f
commit 38f81e05b3
5 changed files with 457 additions and 12 deletions

View File

@ -54,6 +54,7 @@ tempdir = "0.3"
termcolor = "0.3"
toml = "0.4"
url = "1.1"
clap = "2.27.0"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = { version = "0.5.1", features = ["mac_os_10_7_support"] }

View File

@ -9,6 +9,7 @@ extern crate log;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate clap;
use std::collections::BTreeSet;
use std::collections::HashMap;
@ -20,6 +21,8 @@ use cargo::core::shell::{Shell, Verbosity};
use cargo::util::{self, CliResult, lev_distance, Config, CargoResult};
use cargo::util::{CliError, ProcessError};
mod cli;
#[derive(Deserialize)]
pub struct Flags {
flag_list: bool,
@ -85,17 +88,26 @@ fn main() {
}
};
let result = (|| {
let args: Vec<_> = try!(env::args_os()
.map(|s| {
s.into_string().map_err(|s| {
format_err!("invalid unicode in argument: {:?}", s)
let is_clapified = ::std::env::args().any(|arg| match arg.as_ref() {
"build" | "bench" => true,
_ => false
});
let result = if is_clapified {
cli::do_main(&mut config)
} else {
(|| {
let args: Vec<_> = try!(env::args_os()
.map(|s| {
s.into_string().map_err(|s| {
format_err!("invalid unicode in argument: {:?}", s)
})
})
})
.collect());
let rest = &args;
cargo::call_main_without_stdin(execute, &mut config, USAGE, rest, true)
})();
.collect());
let rest = &args;
cargo::call_main_without_stdin(execute, &mut config, USAGE, rest, true)
})()
};
match result {
Err(e) => cargo::exit_with_error(e, &mut *config.shell()),
@ -105,8 +117,8 @@ fn main() {
macro_rules! each_subcommand{
($mac:ident) => {
$mac!(bench);
$mac!(build);
// $mac!(bench);
// $mac!(build);
$mac!(check);
$mac!(clean);
$mac!(doc);

66
src/bin/cli/bench.rs Normal file
View File

@ -0,0 +1,66 @@
use super::utils::*;
pub fn cli() -> App {
subcommand("bench")
.about("Execute all benchmarks of a local package")
.arg(
Arg::with_name("BENCHNAME").help(
"If specified, only run benches containing this string in their names"
)
)
.arg(
Arg::with_name("args").help(
"Arguments for the bench binary"
).multiple(true).last(true)
)
.arg_target(
"Benchmark only this package's library",
"Benchmark only the specified binary",
"Benchmark all binaries",
"Benchmark only the specified example",
"Benchmark all examples",
"Benchmark only the specified test target",
"Benchmark all tests",
"Benchmark only the specified bench target",
"Benchmark all benches",
"Benchmark all targets (default)",
)
.arg(
opt("no-run", "Compile, but don't run benchmarks")
)
.arg_package(
"Package to run benchmarks for",
"Benchmark all packages in the workspace",
"Exclude packages from the benchmark",
)
.arg_jobs()
.arg_features()
.arg_target_triple()
.arg_manifest_path()
.arg_message_format()
.arg(
opt("no-fail-fast", "Run all benchmarks regardless of failure")
)
.arg_locked()
.after_help("\
All of the trailing arguments are passed to the benchmark binaries generated
for filtering benchmarks and generally providing options configuring how they
run.
If the --package argument is given, then SPEC is a package id specification
which indicates which package should be benchmarked. If it is not given, then
the current package is benchmarked. For more information on SPEC and its format,
see the `cargo help pkgid` command.
All packages in the workspace are benchmarked if the `--all` flag is supplied. The
`--all` flag is automatically assumed for a virtual manifest.
Note that `--exclude` has to be specified in conjunction with the `--all` flag.
The --jobs argument affects the building of the benchmark executable but does
not affect how many jobs are used when running the benchmarks.
Compilation can be customized with the `bench` profile in the manifest.
")
}

47
src/bin/cli/build.rs Normal file
View File

@ -0,0 +1,47 @@
use super::utils::*;
pub fn cli() -> App {
subcommand("build")
.about("Compile a local package and all of its dependencies")
.arg_package(
"Package to build",
"Build all packages in the workspace",
"Exclude packages from the build",
)
.arg_jobs()
.arg_target(
"Build only this package's library",
"Build only the specified binary",
"Build all binaries",
"Build only the specified example",
"Build all examples",
"Build only the specified test target",
"Build all tests",
"Build only the specified bench target",
"Build all benches",
"Build all targets (lib and bin targets by default)",
)
.arg(
opt("release", "Build artifacts in release mode, with optimizations")
)
.arg_features()
.arg_target_triple()
.arg_manifest_path()
.arg_message_format()
.arg_locked()
.after_help("\
If the --package argument is given, then SPEC is a package id specification
which indicates which package should be built. If it is not given, then the
current package is built. For more information on SPEC and its format, see the
`cargo help pkgid` command.
All packages in the workspace are built if the `--all` flag is supplied. The
`--all` flag is automatically assumed for a virtual manifest.
Note that `--exclude` has to be specified in conjunction with the `--all` flag.
Compilation can be configured via the use of profiles which are configured in
the manifest. The default profile for this command is `dev`, but passing
the --release flag will use the `release` profile instead.
")
}

319
src/bin/cli/mod.rs Normal file
View File

@ -0,0 +1,319 @@
extern crate clap;
#[cfg(never)]
extern crate cargo;
use cargo;
use clap::AppSettings;
use clap::Arg;
use clap::SubCommand;
use clap::ArgMatches;
use cargo::Config;
use cargo::CargoResult;
use cargo::core::Workspace;
use cargo::util::important_paths::find_root_manifest_for_wd;
use cargo::ops::Packages;
use cargo::ops::CompileOptions;
use cargo::ops;
use std::slice;
use cargo::ops::MessageFormat;
use cargo::CliError;
use cargo::ops::CompileMode;
pub fn do_main(config: &mut Config) -> Result<(), CliError> {
let args = cli().get_matches();
if args.is_present("version") {
let version = cargo::version();
println!("{}", version);
if args.occurrences_of("verbose") > 0 {
println!("release: {}.{}.{}",
version.major,
version.minor,
version.patch);
if let Some(ref cfg) = version.cfg_info {
if let Some(ref ci) = cfg.commit_info {
println!("commit-hash: {}", ci.commit_hash);
println!("commit-date: {}", ci.commit_date);
}
}
}
return Ok(());
}
fn values<'a>(args: &ArgMatches, name: &str) -> &'a [String] {
let owned: Vec<String> = args.values_of(name).unwrap_or_default()
.map(|s| s.to_string())
.collect();
let owned = owned.into_boxed_slice();
let ptr = owned.as_ptr();
let len = owned.len();
::std::mem::forget(owned);
unsafe {
slice::from_raw_parts(ptr, len)
}
}
fn config_from_args(config: &mut Config, args: &ArgMatches) -> CargoResult<()> {
let color = args.value_of("color").map(|s| s.to_string());
config.configure(
args.occurrences_of("verbose") as u32,
if args.is_present("quite") { Some(true) } else { None },
&color,
args.is_present("frozen"),
args.is_present("locked"),
&args.values_of_lossy("unstable-features").unwrap_or_default(),
)
}
fn workspace_from_args<'a>(config: &'a Config, args: &ArgMatches) -> CargoResult<Workspace<'a>> {
let manifest_path = args.value_of("manifest-path").map(|s| s.to_string());
let root = find_root_manifest_for_wd(manifest_path, config.cwd())?;
Workspace::new(&root, config)
}
fn compile_options_from_args<'a>(
config: &'a Config,
args: &'a ArgMatches<'a>,
mode: CompileMode,
) -> CargoResult<CompileOptions<'a>> {
let spec = Packages::from_flags(
args.is_present("all"),
&values(args, "exclude"),
&values(args, "package")
)?;
let release = mode == CompileMode::Bench || args.is_present("release");
let message_format = match args.value_of("message-format") {
Some("json") => MessageFormat::Json,
Some("human") => MessageFormat::Human,
f => panic!("Impossible message format: {:?}", f),
};
let opts = CompileOptions {
config,
jobs: args.value_of("jobs").and_then(|v| v.parse().ok()),
target: args.value_of("target"),
features: &values(args, "features"),
all_features: args.is_present("all-features"),
no_default_features: args.is_present("no-default-features"),
spec,
mode,
release,
filter: ops::CompileFilter::new(args.is_present("lib"),
values(args, "bin"), args.is_present("bins"),
values(args, "test"), args.is_present("tests"),
values(args, "example"), args.is_present("examples"),
values(args, "bench"), args.is_present("benches"),
args.is_present("all-targets")),
message_format,
target_rustdoc_args: None,
target_rustc_args: None,
};
Ok(opts)
}
match args.subcommand() {
("bench", Some(args)) => {
config_from_args(config, args)?;
let ws = workspace_from_args(config, args)?;
let compile_opts = compile_options_from_args(config, args, CompileMode::Bench)?;
let ops = ops::TestOptions {
no_run: args.is_present("no-run"),
no_fail_fast: args.is_present("no-fail-fast"),
only_doc: false,
compile_opts,
};
let mut bench_args = vec![];
bench_args.extend(args.value_of("BENCHNAME").into_iter().map(|s| s.to_string()));
bench_args.extend(args.values_of("args").unwrap_or_default().map(|s| s.to_string()));
let err = ops::run_benches(&ws, &ops, &bench_args)?;
return match err {
None => Ok(()),
Some(err) => {
Err(match err.exit.as_ref().and_then(|e| e.code()) {
Some(i) => CliError::new(format_err!("bench failed"), i),
None => CliError::new(err.into(), 101)
})
}
};
}
("build", Some(args)) => {
config_from_args(config, args)?;
let ws = workspace_from_args(config, args)?;
let compile_opts = compile_options_from_args(config, args, CompileMode::Build)?;
ops::compile(&ws, &compile_opts)?;
return Ok(());
}
_ => return Ok(())
}
}
use self::utils::*;
fn cli() -> App {
let app = App::new("cargo")
.settings(&[
AppSettings::DisableVersion,
AppSettings::UnifiedHelpMessage,
AppSettings::DeriveDisplayOrder,
AppSettings::VersionlessSubcommands,
])
.about("Rust's package manager")
.arg(
opt("version", "Print version info and exit")
.short("V")
)
.arg(
opt("list", "List installed commands")
)
.arg(
opt("explain", "Run `rustc --explain CODE`")
.value_name("CODE")
)
.arg(
opt("verbose", "Use verbose output (-vv very verbose/build.rs output)")
.short("v").multiple(true).global(true)
)
.arg(
opt("quite", "No output printed to stdout")
.short("q").global(true)
)
.arg(
opt("color", "Coloring: auto, always, never")
.value_name("WHEN").global(true)
)
.arg(
Arg::with_name("unstable-features").help("Unstable (nightly-only) flags to Cargo")
.short("Z").value_name("FLAG").multiple(true).global(true)
)
.after_help("\
Some common cargo commands are (see all commands with --list):
build Compile the current project
check Analyze the current project and report errors, but don't build object files
clean Remove the target directory
doc Build this project's and its dependencies' documentation
new Create a new cargo project
init Create a new cargo project in an existing directory
run Build and execute src/main.rs
test Run the tests
bench Run the benchmarks
update Update dependencies listed in Cargo.lock
search Search registry for crates
publish Package and upload this project to the registry
install Install a Rust binary
uninstall Uninstall a Rust binary
See 'cargo help <command>' for more information on a specific command.
")
.subcommands(vec![
bench::cli(),
build::cli(),
])
;
app
}
mod utils {
use clap::{self, SubCommand, AppSettings};
pub use clap::Arg;
pub type App = clap::App<'static, 'static>;
pub trait CommonArgs: Sized {
fn _arg(self, arg: Arg<'static, 'static>) -> Self;
fn arg_package(self, package: &'static str, all: &'static str, exclude: &'static str) -> Self {
self._arg(opt("package", package).short("p").value_name("SPEC").multiple(true))
._arg(opt("all", all))
._arg(opt("exclude", exclude).value_name("SPEC").multiple(true))
}
fn arg_jobs(self) -> Self {
self._arg(
opt("jobs", "Number of parallel jobs, defaults to # of CPUs")
.short("j").value_name("N")
)
}
fn arg_target(
self,
lib: &'static str,
bin: &'static str,
bins: &'static str,
examle: &'static str,
examles: &'static str,
test: &'static str,
tests: &'static str,
bench: &'static str,
benchs: &'static str,
all: &'static str,
) -> Self {
self._arg(opt("lib", lib))
._arg(opt("bin", bin).value_name("NAME").multiple(true))
._arg(opt("bins", bins))
._arg(opt("example", examle).value_name("NAME").multiple(true))
._arg(opt("examples", examles))
._arg(opt("test", test).value_name("NAME").multiple(true))
._arg(opt("tests", tests))
._arg(opt("bench", bench).value_name("NAME").multiple(true))
._arg(opt("benches", benchs))
._arg(opt("all-targets", all))
}
fn arg_features(self) -> Self {
self._arg(
opt("features", "Space-separated list of features to also build")
.value_name("FEATURES")
)
._arg(opt("all-features", "Build all available features"))
._arg(opt("no-default-features", "Do not build the `default` feature"))
}
fn arg_target_triple(self) -> Self {
self._arg(opt("target", "Build for the target triple").value_name("TRIPLE"))
}
fn arg_manifest_path(self) -> Self {
self._arg(opt("manifest-path", "Path to Cargo.toml").value_name("PATH"))
}
fn arg_message_format(self) -> Self {
self._arg(
opt("message-format", "Error format")
.value_name("FMT").possible_values(&["human", "json"]).default_value("human")
)
}
fn arg_locked(self) -> Self {
self._arg(opt("frozen", "Require Cargo.lock and cache are up to date"))
._arg(opt("locked", "Require Cargo.lock is up to date"))
}
}
impl CommonArgs for App {
fn _arg(self, arg: Arg<'static, 'static>) -> Self {
self.arg(arg)
}
}
pub fn opt(name: &'static str, help: &'static str) -> Arg<'static, 'static> {
Arg::with_name(name).long(name).help(help)
}
pub fn subcommand(name: &'static str) -> App {
SubCommand::with_name(name)
.settings(&[
AppSettings::UnifiedHelpMessage,
AppSettings::DeriveDisplayOrder,
AppSettings::TrailingVarArg,
AppSettings::DontCollapseArgsInUsage,
])
}
}
mod bench;
mod build;