1 //! See https://github.com/matklad/cargo-xtask/.
3 //! This binary defines various auxiliary build commands, which are not
4 //! expressible with just `cargo`. Notably, it provides tests via `cargo test -p xtask`
5 //! for code generation and `cargo xtask install` for installation of
6 //! rust-analyzer server and client.
8 //! This binary is integrated into the `cargo` command line by using an alias in
23 use anyhow::{bail, Result};
26 path::{Path, PathBuf},
28 use walkdir::{DirEntry, WalkDir};
29 use xshell::{cmd, cp, pushd, pushenv};
31 fn main() -> Result<()> {
32 let _d = pushd(project_root())?;
34 let flags = flags::Xtask::from_env()?;
35 match flags.subcommand {
36 flags::XtaskCmd::Help(_) => {
37 println!("{}", flags::Xtask::HELP);
40 flags::XtaskCmd::Install(cmd) => cmd.run(),
41 flags::XtaskCmd::FuzzTests(_) => run_fuzzer(),
42 flags::XtaskCmd::PreCache(cmd) => cmd.run(),
43 flags::XtaskCmd::Release(cmd) => cmd.run(),
44 flags::XtaskCmd::Promote(cmd) => cmd.run(),
45 flags::XtaskCmd::Dist(cmd) => cmd.run(),
46 flags::XtaskCmd::Metrics(cmd) => cmd.run(),
47 flags::XtaskCmd::Bb(cmd) => {
49 let _d = pushd("./crates/rust-analyzer")?;
50 cmd!("cargo build --release --features jemalloc").run()?;
52 cp("./target/release/rust-analyzer", format!("./target/rust-analyzer-{}", cmd.suffix))?;
58 fn project_root() -> PathBuf {
60 &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
68 fn rust_files() -> impl Iterator<Item = PathBuf> {
69 rust_files_in(&project_root().join("crates"))
73 fn cargo_files() -> impl Iterator<Item = PathBuf> {
74 files_in(&project_root(), "toml")
75 .filter(|path| path.file_name().map(|it| it == "Cargo.toml").unwrap_or(false))
78 fn rust_files_in(path: &Path) -> impl Iterator<Item = PathBuf> {
82 fn ensure_rustfmt() -> Result<()> {
83 let out = cmd!("rustfmt --version").read()?;
84 if !out.contains("stable") {
86 "Failed to run rustfmt from toolchain 'stable'. \
87 Please run `rustup component add rustfmt --toolchain stable` to install it.",
93 fn run_fuzzer() -> Result<()> {
94 let _d = pushd("./crates/syntax")?;
95 let _e = pushenv("RUSTUP_TOOLCHAIN", "nightly");
96 if cmd!("cargo fuzz --help").read().is_err() {
97 cmd!("cargo install cargo-fuzz").run()?;
100 // Expecting nightly rustc
101 let out = cmd!("rustc --version").read()?;
102 if !out.contains("nightly") {
103 bail!("fuzz tests require nightly rustc")
106 cmd!("cargo fuzz run parser").run()?;
110 fn date_iso() -> Result<String> {
111 let res = cmd!("date --iso --utc").read()?;
115 fn is_release_tag(tag: &str) -> bool {
116 tag.len() == "2020-02-24".len() && tag.starts_with(|c: char| c.is_ascii_digit())
119 fn files_in(path: &Path, ext: &'static str) -> impl Iterator<Item = PathBuf> {
120 let iter = WalkDir::new(path);
123 .filter_entry(|e| !is_hidden(e))
125 .filter(|e| !e.file_type().is_dir())
126 .map(|e| e.into_path())
127 .filter(move |path| path.extension().map(|it| it == ext).unwrap_or(false));
129 fn is_hidden(entry: &DirEntry) -> bool {
130 entry.file_name().to_str().map(|s| s.starts_with('.')).unwrap_or(false)