Use cli parser with auto-generated help

This commit is contained in:
Aleksey Kladov 2021-03-01 21:12:44 +03:00
parent 9860a39603
commit 4ce20b80c5
8 changed files with 195 additions and 134 deletions

20
Cargo.lock generated
View file

@ -1921,6 +1921,24 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06069a848f95fceae3e5e03c0ddc8cb78452b56654ee0c8e68f938cf790fb9e3" checksum = "06069a848f95fceae3e5e03c0ddc8cb78452b56654ee0c8e68f938cf790fb9e3"
[[package]]
name = "xflags"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a6292b9528efc06cb25a41b8a0814dd3a9590c0fe2cd95341fe41bbe034fafb"
dependencies = [
"xflags-macros",
]
[[package]]
name = "xflags-macros"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba2108d40e49a0653f2ee4eda59f51447e0cab5cc2cc197a5abd96525c6bd89e"
dependencies = [
"proc-macro2",
]
[[package]] [[package]]
name = "xshell" name = "xshell"
version = "0.1.8" version = "0.1.8"
@ -1942,11 +1960,11 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"flate2", "flate2",
"pico-args",
"proc-macro2", "proc-macro2",
"quote", "quote",
"ungrammar", "ungrammar",
"walkdir", "walkdir",
"write-json", "write-json",
"xflags",
"xshell", "xshell",
] ]

View file

@ -9,11 +9,11 @@ license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
anyhow = "1.0.26" anyhow = "1.0.26"
flate2 = "1.0" flate2 = "1.0"
pico-args = "0.4.0"
proc-macro2 = "1.0.8" proc-macro2 = "1.0.8"
quote = "1.0.2" quote = "1.0.2"
ungrammar = "=1.11" ungrammar = "=1.11"
walkdir = "2.3.1" walkdir = "2.3.1"
write-json = "0.1.0" write-json = "0.1.0"
xshell = "0.1" xshell = "0.1"
xflags = "0.1.2"
# Avoid adding more dependencies to this crate # Avoid adding more dependencies to this crate

View file

@ -18,7 +18,7 @@ use std::{
}; };
use xshell::{cmd, pushenv, read_file, write_file}; use xshell::{cmd, pushenv, read_file, write_file};
use crate::{ensure_rustfmt, project_root, Result}; use crate::{ensure_rustfmt, flags, project_root, Result};
pub(crate) use self::{ pub(crate) use self::{
gen_assists_docs::{generate_assists_docs, generate_assists_tests}, gen_assists_docs::{generate_assists_docs, generate_assists_tests},
@ -35,11 +35,7 @@ pub(crate) enum Mode {
Verify, Verify,
} }
pub(crate) struct CodegenCmd { impl flags::Codegen {
pub(crate) features: bool,
}
impl CodegenCmd {
pub(crate) fn run(self) -> Result<()> { pub(crate) fn run(self) -> Result<()> {
if self.features { if self.features {
generate_lint_completions(Mode::Overwrite)?; generate_lint_completions(Mode::Overwrite)?;

139
xtask/src/flags.rs Normal file
View file

@ -0,0 +1,139 @@
#![allow(unreachable_pub)]
xflags::args_parser! {
/// Run custom build command.
cmd xtask {
default cmd help {
/// Print help information.
optional -h, --help
}
/// Install rust-analyzer server or editor plugin.
cmd install {
/// Install only VS Code plugin.
optional --client
/// One of 'code', 'code-exploration', 'code-insiders', 'codium', or 'code-oss'.
optional --code-bin name: String
/// Install only the language server.
optional --server
/// Use mimalloc allocator for server
optional --mimalloc
/// Use jemalloc allocator for server
optional --jemalloc
}
cmd codegen {
optional --features
}
cmd lint {}
cmd fuzz-tests {}
cmd pre-cache {}
cmd release {
optional --dry-run
}
cmd promote {
optional --dry-run
}
cmd dist {
optional --nightly
optional --client version: String
}
cmd metrics {
optional --dry-run
}
/// Builds a benchmark version of rust-analyzer and puts it into `./target`.
cmd bb
required suffix : String
{}
}
}
// generated start
// The following code is generated by `xflags` macro.
// Run `env XFLAGS_DUMP= cargo build` to regenerate.
#[derive(Debug)]
pub struct Xtask {
pub subcommand: XtaskCmd,
}
#[derive(Debug)]
pub enum XtaskCmd {
Help(Help),
Install(Install),
Codegen(Codegen),
Lint(Lint),
FuzzTests(FuzzTests),
PreCache(PreCache),
Release(Release),
Promote(Promote),
Dist(Dist),
Metrics(Metrics),
Bb(Bb),
}
#[derive(Debug)]
pub struct Help {
pub help: bool,
}
#[derive(Debug)]
pub struct Install {
pub client: bool,
pub code_bin: Option<String>,
pub server: bool,
pub mimalloc: bool,
pub jemalloc: bool,
}
#[derive(Debug)]
pub struct Codegen {
pub features: bool,
}
#[derive(Debug)]
pub struct Lint {}
#[derive(Debug)]
pub struct FuzzTests {}
#[derive(Debug)]
pub struct PreCache {}
#[derive(Debug)]
pub struct Release {
pub dry_run: bool,
}
#[derive(Debug)]
pub struct Promote {
pub dry_run: bool,
}
#[derive(Debug)]
pub struct Dist {
pub nightly: bool,
pub client: Option<String>,
}
#[derive(Debug)]
pub struct Metrics {
pub dry_run: bool,
}
#[derive(Debug)]
pub struct Bb {
pub suffix: String,
}
impl Xtask {
pub const HELP: &'static str = Self::_HELP;
pub fn from_env() -> xflags::Result<Self> {
let mut p = xflags::rt::Parser::new_from_env();
Self::_parse(&mut p)
}
}
// generated end

View file

@ -7,6 +7,8 @@
//! //!
//! This binary is integrated into the `cargo` command line by using an alias in //! This binary is integrated into the `cargo` command line by using an alias in
//! `.cargo/config`. //! `.cargo/config`.
mod flags;
mod codegen; mod codegen;
mod ast_src; mod ast_src;
#[cfg(test)] #[cfg(test)]
@ -19,8 +21,6 @@ mod metrics;
mod pre_cache; mod pre_cache;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use codegen::CodegenCmd;
use pico_args::Arguments;
use std::{ use std::{
env, env,
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -32,42 +32,19 @@ use crate::{
codegen::Mode, codegen::Mode,
dist::DistCmd, dist::DistCmd,
install::{InstallCmd, Malloc, ServerOpt}, install::{InstallCmd, Malloc, ServerOpt},
metrics::MetricsCmd,
pre_cache::PreCacheCmd,
release::{PromoteCmd, ReleaseCmd},
}; };
fn main() -> Result<()> { fn main() -> Result<()> {
let _d = pushd(project_root())?; let _d = pushd(project_root())?;
let mut args = Arguments::from_env(); let flags = flags::Xtask::from_env()?;
let subcommand = args.subcommand()?.unwrap_or_default(); match flags.subcommand {
flags::XtaskCmd::Help(_) => {
match subcommand.as_str() { println!("{}", flags::Xtask::HELP);
"install" => { return Ok(());
if args.contains(["-h", "--help"]) { }
eprintln!( flags::XtaskCmd::Install(flags) => {
"\ if flags.server && flags.client {
cargo xtask install
Install rust-analyzer server or editor plugin.
USAGE:
cargo xtask install [FLAGS]
FLAGS:
--client[=CLIENT] Install only VS Code plugin.
CLIENT is one of 'code', 'code-exploration', 'code-insiders', 'codium', or 'code-oss'
--server Install only the language server
--mimalloc Use mimalloc allocator for server
--jemalloc Use jemalloc allocator for server
-h, --help Prints help information
"
);
return Ok(());
}
let server = args.contains("--server");
let client_code = args.contains("--client");
if server && client_code {
eprintln!( eprintln!(
"error: The argument `--server` cannot be used with `--client`\n\n\ "error: The argument `--server` cannot be used with `--client`\n\n\
For more information try --help" For more information try --help"
@ -75,102 +52,43 @@ FLAGS:
return Ok(()); return Ok(());
} }
let malloc = if args.contains("--mimalloc") { let malloc = if flags.mimalloc {
Malloc::Mimalloc Malloc::Mimalloc
} else if args.contains("--jemalloc") { } else if flags.jemalloc {
Malloc::Jemalloc Malloc::Jemalloc
} else { } else {
Malloc::System Malloc::System
}; };
let client_opt = args.opt_value_from_str("--client")?; let client_bin = flags.code_bin.map(|it| it.parse()).transpose()?;
finish_args(args)?;
InstallCmd { InstallCmd {
client: if server { None } else { Some(client_opt.unwrap_or_default()) }, client: if flags.server { None } else { client_bin },
server: if client_code { None } else { Some(ServerOpt { malloc }) }, server: if flags.client { None } else { Some(ServerOpt { malloc }) },
} }
.run() .run()
} }
"codegen" => { flags::XtaskCmd::Codegen(cmd) => cmd.run(),
let features = args.contains("--features"); flags::XtaskCmd::Lint(_) => run_clippy(),
finish_args(args)?; flags::XtaskCmd::FuzzTests(_) => run_fuzzer(),
CodegenCmd { features }.run() flags::XtaskCmd::PreCache(cmd) => cmd.run(),
flags::XtaskCmd::Release(cmd) => cmd.run(),
flags::XtaskCmd::Promote(cmd) => cmd.run(),
flags::XtaskCmd::Dist(flags) => {
DistCmd { nightly: flags.nightly, client_version: flags.client }.run()
} }
"lint" => { flags::XtaskCmd::Metrics(cmd) => cmd.run(),
finish_args(args)?; flags::XtaskCmd::Bb(cmd) => {
run_clippy()
}
"fuzz-tests" => {
finish_args(args)?;
run_fuzzer()
}
"pre-cache" => {
finish_args(args)?;
PreCacheCmd.run()
}
"release" => {
let dry_run = args.contains("--dry-run");
finish_args(args)?;
ReleaseCmd { dry_run }.run()
}
"promote" => {
let dry_run = args.contains("--dry-run");
finish_args(args)?;
PromoteCmd { dry_run }.run()
}
"dist" => {
let nightly = args.contains("--nightly");
let client_version: Option<String> = args.opt_value_from_str("--client")?;
finish_args(args)?;
DistCmd { nightly, client_version }.run()
}
"metrics" => {
let dry_run = args.contains("--dry-run");
finish_args(args)?;
MetricsCmd { dry_run }.run()
}
"bb" => {
let suffix: String = args.free_from_str()?;
finish_args(args)?;
{ {
let _d = pushd("./crates/rust-analyzer")?; let _d = pushd("./crates/rust-analyzer")?;
cmd!("cargo build --release --features jemalloc").run()?; cmd!("cargo build --release --features jemalloc").run()?;
} }
cp("./target/release/rust-analyzer", format!("./target/rust-analyzer-{}", suffix))?; cp("./target/release/rust-analyzer", format!("./target/rust-analyzer-{}", cmd.suffix))?;
Ok(())
}
_ => {
eprintln!(
"\
cargo xtask
Run custom build command.
USAGE:
cargo xtask <SUBCOMMAND>
SUBCOMMANDS:
fuzz-tests
codegen
install
lint
dist
promote
bb"
);
Ok(()) Ok(())
} }
} }
} }
fn finish_args(args: Arguments) -> Result<()> {
if !args.finish().is_empty() {
bail!("Unused arguments.");
}
Ok(())
}
fn project_root() -> PathBuf { fn project_root() -> PathBuf {
Path::new( Path::new(
&env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()), &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),

View file

@ -9,13 +9,11 @@ use std::{
use anyhow::{bail, format_err, Result}; use anyhow::{bail, format_err, Result};
use xshell::{cmd, mkdir_p, pushd, pushenv, read_file, rm_rf}; use xshell::{cmd, mkdir_p, pushd, pushenv, read_file, rm_rf};
use crate::flags;
type Unit = String; type Unit = String;
pub(crate) struct MetricsCmd { impl flags::Metrics {
pub(crate) dry_run: bool,
}
impl MetricsCmd {
pub(crate) fn run(self) -> Result<()> { pub(crate) fn run(self) -> Result<()> {
let mut metrics = Metrics::new()?; let mut metrics = Metrics::new()?;
if !self.dry_run { if !self.dry_run {

View file

@ -6,9 +6,9 @@ use std::{
use anyhow::Result; use anyhow::Result;
use xshell::rm_rf; use xshell::rm_rf;
pub(crate) struct PreCacheCmd; use crate::flags;
impl PreCacheCmd { impl flags::PreCache {
/// Cleans the `./target` dir after the build such that only /// Cleans the `./target` dir after the build such that only
/// dependencies are cached on CI. /// dependencies are cached on CI.
pub(crate) fn run(self) -> Result<()> { pub(crate) fn run(self) -> Result<()> {

View file

@ -2,13 +2,9 @@ use std::fmt::Write;
use xshell::{cmd, cp, pushd, read_dir, write_file}; use xshell::{cmd, cp, pushd, read_dir, write_file};
use crate::{codegen, date_iso, is_release_tag, project_root, Mode, Result}; use crate::{codegen, date_iso, flags, is_release_tag, project_root, Mode, Result};
pub(crate) struct ReleaseCmd { impl flags::Release {
pub(crate) dry_run: bool,
}
impl ReleaseCmd {
pub(crate) fn run(self) -> Result<()> { pub(crate) fn run(self) -> Result<()> {
if !self.dry_run { if !self.dry_run {
cmd!("git switch release").run()?; cmd!("git switch release").run()?;
@ -86,11 +82,7 @@ https://github.com/sponsors/rust-analyzer[GitHub Sponsors].
} }
} }
pub(crate) struct PromoteCmd { impl flags::Promote {
pub(crate) dry_run: bool,
}
impl PromoteCmd {
pub(crate) fn run(self) -> Result<()> { pub(crate) fn run(self) -> Result<()> {
let _dir = pushd("../rust-rust-analyzer")?; let _dir = pushd("../rust-rust-analyzer")?;
cmd!("git switch master").run()?; cmd!("git switch master").run()?;