aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock23
-rw-r--r--xtask/Cargo.toml2
-rw-r--r--xtask/src/codegen.rs19
-rw-r--r--xtask/src/codegen/gen_features.rs10
-rw-r--r--xtask/src/dist.rs33
-rw-r--r--xtask/src/install.rs46
-rw-r--r--xtask/src/lib.rs52
-rw-r--r--xtask/src/main.rs4
-rw-r--r--xtask/src/metrics.rs39
-rw-r--r--xtask/src/not_bash.rs169
-rw-r--r--xtask/src/pre_cache.rs5
-rw-r--r--xtask/src/pre_commit.rs8
-rw-r--r--xtask/src/release.rs60
-rw-r--r--xtask/tests/tidy.rs36
14 files changed, 181 insertions, 325 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d470d84f2..7a77ed722 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -422,12 +422,6 @@ dependencies = [
422] 422]
423 423
424[[package]] 424[[package]]
425name = "fs-err"
426version = "2.5.0"
427source = "registry+https://github.com/rust-lang/crates.io-index"
428checksum = "bcd1163ae48bda72a20ae26d66a04d3094135cadab911cff418ae5e33f253431"
429
430[[package]]
431name = "fsevent" 425name = "fsevent"
432version = "2.0.2" 426version = "2.0.2"
433source = "registry+https://github.com/rust-lang/crates.io-index" 427source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1921,16 +1915,31 @@ dependencies = [
1921] 1915]
1922 1916
1923[[package]] 1917[[package]]
1918name = "xshell"
1919version = "0.1.0"
1920source = "registry+https://github.com/rust-lang/crates.io-index"
1921checksum = "1f7f756f2faab73adb00db44db716598ab2c9e4bce4a875c053022291bd3cab4"
1922dependencies = [
1923 "xshell-macros",
1924]
1925
1926[[package]]
1927name = "xshell-macros"
1928version = "0.1.0"
1929source = "registry+https://github.com/rust-lang/crates.io-index"
1930checksum = "51b020c2f3132b34067e2f6ebc58f0f210624898713a8186b8cdb75d3b8c3001"
1931
1932[[package]]
1924name = "xtask" 1933name = "xtask"
1925version = "0.1.0" 1934version = "0.1.0"
1926dependencies = [ 1935dependencies = [
1927 "anyhow", 1936 "anyhow",
1928 "flate2", 1937 "flate2",
1929 "fs-err",
1930 "pico-args", 1938 "pico-args",
1931 "proc-macro2", 1939 "proc-macro2",
1932 "quote", 1940 "quote",
1933 "ungrammar", 1941 "ungrammar",
1934 "walkdir", 1942 "walkdir",
1935 "write-json", 1943 "write-json",
1944 "xshell",
1936] 1945]
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index 01a838825..2ef956485 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -18,5 +18,5 @@ quote = "1.0.2"
18ungrammar = "1.1.3" 18ungrammar = "1.1.3"
19walkdir = "2.3.1" 19walkdir = "2.3.1"
20write-json = "0.1.0" 20write-json = "0.1.0"
21fs-err = "2.3" 21xshell = "0.1"
22# Avoid adding more dependencies to this crate 22# Avoid adding more dependencies to this crate
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index 1e7894617..3ee4c1adf 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -15,12 +15,9 @@ use std::{
15 fmt, mem, 15 fmt, mem,
16 path::{Path, PathBuf}, 16 path::{Path, PathBuf},
17}; 17};
18use xshell::{cmd, pushenv, read_file, write_file};
18 19
19use crate::{ 20use crate::{ensure_rustfmt, project_root, Result};
20 ensure_rustfmt,
21 not_bash::{fs2, pushenv, run},
22 project_root, Result,
23};
24 21
25pub use self::{ 22pub use self::{
26 gen_assists_docs::{generate_assists_docs, generate_assists_tests}, 23 gen_assists_docs::{generate_assists_docs, generate_assists_tests},
@@ -57,7 +54,7 @@ impl CodegenCmd {
57/// A helper to update file on disk if it has changed. 54/// A helper to update file on disk if it has changed.
58/// With verify = false, 55/// With verify = false,
59fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { 56fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
60 match fs2::read_to_string(path) { 57 match read_file(path) {
61 Ok(old_contents) if normalize(&old_contents) == normalize(contents) => { 58 Ok(old_contents) if normalize(&old_contents) == normalize(contents) => {
62 return Ok(()); 59 return Ok(());
63 } 60 }
@@ -67,7 +64,7 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
67 anyhow::bail!("`{}` is not up-to-date", path.display()); 64 anyhow::bail!("`{}` is not up-to-date", path.display());
68 } 65 }
69 eprintln!("updating {}", path.display()); 66 eprintln!("updating {}", path.display());
70 fs2::write(path, contents)?; 67 write_file(path, contents)?;
71 return Ok(()); 68 return Ok(());
72 69
73 fn normalize(s: &str) -> String { 70 fn normalize(s: &str) -> String {
@@ -80,10 +77,10 @@ const PREAMBLE: &str = "Generated file, do not edit by hand, see `xtask/src/code
80fn reformat(text: &str) -> Result<String> { 77fn reformat(text: &str) -> Result<String> {
81 let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); 78 let _e = pushenv("RUSTUP_TOOLCHAIN", "stable");
82 ensure_rustfmt()?; 79 ensure_rustfmt()?;
83 let stdout = run!( 80 let rustfmt_toml = project_root().join("rustfmt.toml");
84 "rustfmt --config-path {} --config fn_single_line=true", project_root().join("rustfmt.toml").display(); 81 let stdout = cmd!("rustfmt --config-path {rustfmt_toml} --config fn_single_line=true")
85 <text.as_bytes() 82 .stdin(text)
86 )?; 83 .read()?;
87 Ok(format!("//! {}\n\n{}\n", PREAMBLE, stdout)) 84 Ok(format!("//! {}\n\n{}\n", PREAMBLE, stdout))
88} 85}
89 86
diff --git a/xtask/src/codegen/gen_features.rs b/xtask/src/codegen/gen_features.rs
index b58c4a0c9..3cf15ce02 100644
--- a/xtask/src/codegen/gen_features.rs
+++ b/xtask/src/codegen/gen_features.rs
@@ -3,15 +3,13 @@ use std::path::{Path, PathBuf};
3 3
4use quote::quote; 4use quote::quote;
5use walkdir::WalkDir; 5use walkdir::WalkDir;
6use xshell::{cmd, read_file};
6 7
7use crate::{ 8use crate::codegen::{project_root, reformat, update, Mode, Result};
8 codegen::{project_root, reformat, update, Mode, Result},
9 not_bash::{fs2, run},
10};
11 9
12pub fn generate_features(mode: Mode) -> Result<()> { 10pub fn generate_features(mode: Mode) -> Result<()> {
13 if !Path::new("./target/rust").exists() { 11 if !Path::new("./target/rust").exists() {
14 run!("git clone https://github.com/rust-lang/rust ./target/rust")?; 12 cmd!("git clone https://github.com/rust-lang/rust ./target/rust").run()?;
15 } 13 }
16 14
17 let contents = generate_descriptor("./target/rust/src/doc/unstable-book/src".into())?; 15 let contents = generate_descriptor("./target/rust/src/doc/unstable-book/src".into())?;
@@ -34,7 +32,7 @@ fn generate_descriptor(src_dir: PathBuf) -> Result<String> {
34 .map(|entry| { 32 .map(|entry| {
35 let path = entry.path(); 33 let path = entry.path();
36 let feature_ident = path.file_stem().unwrap().to_str().unwrap().replace("-", "_"); 34 let feature_ident = path.file_stem().unwrap().to_str().unwrap().replace("-", "_");
37 let doc = fs2::read_to_string(path).unwrap(); 35 let doc = read_file(path).unwrap();
38 36
39 quote! { LintCompletion { label: #feature_ident, description: #doc } } 37 quote! { LintCompletion { label: #feature_ident, description: #doc } }
40 }); 38 });
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs
index aa7d94967..9e15a5a4c 100644
--- a/xtask/src/dist.rs
+++ b/xtask/src/dist.rs
@@ -1,4 +1,3 @@
1use flate2::{write::GzEncoder, Compression};
2use std::{ 1use std::{
3 env, 2 env,
4 fs::File, 3 fs::File,
@@ -7,11 +6,10 @@ use std::{
7}; 6};
8 7
9use anyhow::Result; 8use anyhow::Result;
9use flate2::{write::GzEncoder, Compression};
10use xshell::{cmd, cp, mkdir_p, pushd, read_file, rm_rf, write_file};
10 11
11use crate::{ 12use crate::{date_iso, project_root};
12 not_bash::{date_iso, fs2, pushd, rm_rf, run},
13 project_root,
14};
15 13
16pub struct DistCmd { 14pub struct DistCmd {
17 pub nightly: bool, 15 pub nightly: bool,
@@ -22,7 +20,7 @@ impl DistCmd {
22 pub fn run(self) -> Result<()> { 20 pub fn run(self) -> Result<()> {
23 let dist = project_root().join("dist"); 21 let dist = project_root().join("dist");
24 rm_rf(&dist)?; 22 rm_rf(&dist)?;
25 fs2::create_dir_all(&dist)?; 23 mkdir_p(&dist)?;
26 24
27 if let Some(version) = self.client_version { 25 if let Some(version) = self.client_version {
28 let release_tag = if self.nightly { "nightly".to_string() } else { date_iso()? }; 26 let release_tag = if self.nightly { "nightly".to_string() } else { date_iso()? };
@@ -34,7 +32,7 @@ impl DistCmd {
34} 32}
35 33
36fn dist_client(version: &str, release_tag: &str) -> Result<()> { 34fn dist_client(version: &str, release_tag: &str) -> Result<()> {
37 let _d = pushd("./editors/code"); 35 let _d = pushd("./editors/code")?;
38 let nightly = release_tag == "nightly"; 36 let nightly = release_tag == "nightly";
39 37
40 let mut patch = Patch::new("./package.json")?; 38 let mut patch = Patch::new("./package.json")?;
@@ -54,20 +52,16 @@ fn dist_client(version: &str, release_tag: &str) -> Result<()> {
54 } 52 }
55 patch.commit()?; 53 patch.commit()?;
56 54
57 run!("npm ci")?; 55 cmd!("npm ci").run()?;
58 run!("npx vsce package -o ../../dist/rust-analyzer.vsix")?; 56 cmd!("npx vsce package -o ../../dist/rust-analyzer.vsix").run()?;
59 Ok(()) 57 Ok(())
60} 58}
61 59
62fn dist_server() -> Result<()> { 60fn dist_server() -> Result<()> {
63 if cfg!(target_os = "linux") { 61 if cfg!(target_os = "linux") {
64 env::set_var("CC", "clang"); 62 env::set_var("CC", "clang");
65 run!(
66 "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release"
67 )?;
68 } else {
69 run!("cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release")?;
70 } 63 }
64 cmd!("cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release").run()?;
71 65
72 let (src, dst) = if cfg!(target_os = "linux") { 66 let (src, dst) = if cfg!(target_os = "linux") {
73 ("./target/release/rust-analyzer", "./dist/rust-analyzer-linux") 67 ("./target/release/rust-analyzer", "./dist/rust-analyzer-linux")
@@ -82,7 +76,7 @@ fn dist_server() -> Result<()> {
82 let src = Path::new(src); 76 let src = Path::new(src);
83 let dst = Path::new(dst); 77 let dst = Path::new(dst);
84 78
85 fs2::copy(&src, &dst)?; 79 cp(&src, &dst)?;
86 gzip(&src, &dst.with_extension("gz"))?; 80 gzip(&src, &dst.with_extension("gz"))?;
87 81
88 Ok(()) 82 Ok(())
@@ -105,7 +99,7 @@ struct Patch {
105impl Patch { 99impl Patch {
106 fn new(path: impl Into<PathBuf>) -> Result<Patch> { 100 fn new(path: impl Into<PathBuf>) -> Result<Patch> {
107 let path = path.into(); 101 let path = path.into();
108 let contents = fs2::read_to_string(&path)?; 102 let contents = read_file(&path)?;
109 Ok(Patch { path, original_contents: contents.clone(), contents }) 103 Ok(Patch { path, original_contents: contents.clone(), contents })
110 } 104 }
111 105
@@ -115,13 +109,14 @@ impl Patch {
115 self 109 self
116 } 110 }
117 111
118 fn commit(&self) -> io::Result<()> { 112 fn commit(&self) -> Result<()> {
119 fs2::write(&self.path, &self.contents) 113 write_file(&self.path, &self.contents)?;
114 Ok(())
120 } 115 }
121} 116}
122 117
123impl Drop for Patch { 118impl Drop for Patch {
124 fn drop(&mut self) { 119 fn drop(&mut self) {
125 fs2::write(&self.path, &self.original_contents).unwrap(); 120 write_file(&self.path, &self.original_contents).unwrap();
126 } 121 }
127} 122}
diff --git a/xtask/src/install.rs b/xtask/src/install.rs
index fcc4f05e4..789e9f27b 100644
--- a/xtask/src/install.rs
+++ b/xtask/src/install.rs
@@ -3,8 +3,7 @@
3use std::{env, path::PathBuf, str}; 3use std::{env, path::PathBuf, str};
4 4
5use anyhow::{bail, format_err, Context, Result}; 5use anyhow::{bail, format_err, Context, Result};
6 6use xshell::{cmd, pushd};
7use crate::not_bash::{pushd, run};
8 7
9// Latest stable, feel free to send a PR if this lags behind. 8// Latest stable, feel free to send a PR if this lags behind.
10const REQUIRED_RUST_VERSION: u32 = 47; 9const REQUIRED_RUST_VERSION: u32 = 47;
@@ -76,7 +75,7 @@ fn fix_path_for_mac() -> Result<()> {
76} 75}
77 76
78fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { 77fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
79 let _dir = pushd("./editors/code"); 78 let _dir = pushd("./editors/code")?;
80 79
81 let find_code = |f: fn(&str) -> bool| -> Result<&'static str> { 80 let find_code = |f: fn(&str) -> bool| -> Result<&'static str> {
82 ["code", "code-insiders", "codium", "code-oss"] 81 ["code", "code-insiders", "codium", "code-oss"]
@@ -89,24 +88,25 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
89 }; 88 };
90 89
91 let installed_extensions = if cfg!(unix) { 90 let installed_extensions = if cfg!(unix) {
92 run!("npm --version").context("`npm` is required to build the VS Code plugin")?; 91 cmd!("npm --version").run().context("`npm` is required to build the VS Code plugin")?;
93 run!("npm install")?; 92 cmd!("npm install").run()?;
94 93
95 run!("npm run package --scripts-prepend-node-path")?; 94 cmd!("npm run package --scripts-prepend-node-path").run()?;
96 95
97 let code = find_code(|bin| run!("{} --version", bin).is_ok())?; 96 let code = find_code(|bin| cmd!("{bin} --version").read().is_ok())?;
98 run!("{} --install-extension rust-analyzer.vsix --force", code)?; 97 cmd!("{code} --install-extension rust-analyzer.vsix --force").run()?;
99 run!("{} --list-extensions", code; echo = false)? 98 cmd!("{code} --list-extensions").read()?
100 } else { 99 } else {
101 run!("cmd.exe /c npm --version") 100 cmd!("cmd.exe /c npm --version")
101 .run()
102 .context("`npm` is required to build the VS Code plugin")?; 102 .context("`npm` is required to build the VS Code plugin")?;
103 run!("cmd.exe /c npm install")?; 103 cmd!("cmd.exe /c npm install").run()?;
104 104
105 run!("cmd.exe /c npm run package")?; 105 cmd!("cmd.exe /c npm run package").run()?;
106 106
107 let code = find_code(|bin| run!("cmd.exe /c {}.cmd --version", bin).is_ok())?; 107 let code = find_code(|bin| cmd!("cmd.exe /c {bin}.cmd --version").read().is_ok())?;
108 run!(r"cmd.exe /c {}.cmd --install-extension rust-analyzer.vsix --force", code)?; 108 cmd!("cmd.exe /c {code}.cmd --install-extension rust-analyzer.vsix --force").run()?;
109 run!("cmd.exe /c {}.cmd --list-extensions", code; echo = false)? 109 cmd!("cmd.exe /c {code}.cmd --list-extensions").read()?
110 }; 110 };
111 111
112 if !installed_extensions.contains("rust-analyzer") { 112 if !installed_extensions.contains("rust-analyzer") {
@@ -122,7 +122,7 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
122 122
123fn install_server(opts: ServerOpt) -> Result<()> { 123fn install_server(opts: ServerOpt) -> Result<()> {
124 let mut old_rust = false; 124 let mut old_rust = false;
125 if let Ok(stdout) = run!("cargo --version") { 125 if let Ok(stdout) = cmd!("cargo --version").read() {
126 if !check_version(&stdout, REQUIRED_RUST_VERSION) { 126 if !check_version(&stdout, REQUIRED_RUST_VERSION) {
127 old_rust = true; 127 old_rust = true;
128 } 128 }
@@ -134,12 +134,13 @@ fn install_server(opts: ServerOpt) -> Result<()> {
134 REQUIRED_RUST_VERSION, 134 REQUIRED_RUST_VERSION,
135 ) 135 )
136 } 136 }
137 137 let features = match opts.malloc {
138 let malloc_feature = match opts.malloc { 138 Malloc::System => &[][..],
139 Malloc::System => "", 139 Malloc::Mimalloc => &["--features", "mimalloc"],
140 Malloc::Mimalloc => "--features mimalloc",
141 }; 140 };
142 let res = run!("cargo install --path crates/rust-analyzer --locked --force {}", malloc_feature); 141
142 let cmd = cmd!("cargo install --path crates/rust-analyzer --locked --force {features...}");
143 let res = cmd.run();
143 144
144 if res.is_err() && old_rust { 145 if res.is_err() && old_rust {
145 eprintln!( 146 eprintln!(
@@ -148,7 +149,8 @@ fn install_server(opts: ServerOpt) -> Result<()> {
148 ); 149 );
149 } 150 }
150 151
151 res.map(drop) 152 res?;
153 Ok(())
152} 154}
153 155
154fn check_version(version_output: &str, min_minor_version: u32) -> bool { 156fn check_version(version_output: &str, min_minor_version: u32) -> bool {
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
index e790d995f..babec2dbd 100644
--- a/xtask/src/lib.rs
+++ b/xtask/src/lib.rs
@@ -2,7 +2,6 @@
2//! 2//!
3//! See https://github.com/matklad/cargo-xtask/ 3//! See https://github.com/matklad/cargo-xtask/
4 4
5pub mod not_bash;
6pub mod codegen; 5pub mod codegen;
7mod ast_src; 6mod ast_src;
8 7
@@ -19,11 +18,9 @@ use std::{
19}; 18};
20 19
21use walkdir::{DirEntry, WalkDir}; 20use walkdir::{DirEntry, WalkDir};
21use xshell::{cmd, pushd, pushenv};
22 22
23use crate::{ 23use crate::codegen::Mode;
24 codegen::Mode,
25 not_bash::{pushd, pushenv},
26};
27 24
28pub use anyhow::{bail, Context as _, Result}; 25pub use anyhow::{bail, Context as _, Result};
29 26
@@ -53,18 +50,19 @@ pub fn rust_files(path: &Path) -> impl Iterator<Item = PathBuf> {
53} 50}
54 51
55pub fn run_rustfmt(mode: Mode) -> Result<()> { 52pub fn run_rustfmt(mode: Mode) -> Result<()> {
56 let _dir = pushd(project_root()); 53 let _dir = pushd(project_root())?;
57 let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); 54 let _e = pushenv("RUSTUP_TOOLCHAIN", "stable");
58 ensure_rustfmt()?; 55 ensure_rustfmt()?;
59 match mode { 56 let check = match mode {
60 Mode::Overwrite => run!("cargo fmt"), 57 Mode::Overwrite => &[][..],
61 Mode::Verify => run!("cargo fmt -- --check"), 58 Mode::Verify => &["--", "--check"],
62 }?; 59 };
60 cmd!("cargo fmt {check...}").run()?;
63 Ok(()) 61 Ok(())
64} 62}
65 63
66fn ensure_rustfmt() -> Result<()> { 64fn ensure_rustfmt() -> Result<()> {
67 let out = run!("rustfmt --version")?; 65 let out = cmd!("rustfmt --version").read()?;
68 if !out.contains("stable") { 66 if !out.contains("stable") {
69 bail!( 67 bail!(
70 "Failed to run rustfmt from toolchain 'stable'. \ 68 "Failed to run rustfmt from toolchain 'stable'. \
@@ -75,40 +73,46 @@ fn ensure_rustfmt() -> Result<()> {
75} 73}
76 74
77pub fn run_clippy() -> Result<()> { 75pub fn run_clippy() -> Result<()> {
78 if run!("cargo clippy --version").is_err() { 76 if cmd!("cargo clippy --version").read().is_err() {
79 bail!( 77 bail!(
80 "Failed run cargo clippy. \ 78 "Failed run cargo clippy. \
81 Please run `rustup component add clippy` to install it.", 79 Please run `rustup component add clippy` to install it.",
82 ) 80 )
83 } 81 }
84 82
85 let allowed_lints = [ 83 let allowed_lints = "
86 "clippy::collapsible_if", 84 -A clippy::collapsible_if
87 "clippy::needless_pass_by_value", 85 -A clippy::needless_pass_by_value
88 "clippy::nonminimal_bool", 86 -A clippy::nonminimal_bool
89 "clippy::redundant_pattern_matching", 87 -A clippy::redundant_pattern_matching
90 ]; 88 "
91 run!("cargo clippy --all-features --all-targets -- -A {}", allowed_lints.join(" -A "))?; 89 .split_ascii_whitespace();
90 cmd!("cargo clippy --all-features --all-targets -- {allowed_lints...}").run()?;
92 Ok(()) 91 Ok(())
93} 92}
94 93
95pub fn run_fuzzer() -> Result<()> { 94pub fn run_fuzzer() -> Result<()> {
96 let _d = pushd("./crates/syntax"); 95 let _d = pushd("./crates/syntax")?;
97 let _e = pushenv("RUSTUP_TOOLCHAIN", "nightly"); 96 let _e = pushenv("RUSTUP_TOOLCHAIN", "nightly");
98 if run!("cargo fuzz --help").is_err() { 97 if cmd!("cargo fuzz --help").read().is_err() {
99 run!("cargo install cargo-fuzz")?; 98 cmd!("cargo install cargo-fuzz").run()?;
100 }; 99 };
101 100
102 // Expecting nightly rustc 101 // Expecting nightly rustc
103 let out = run!("rustc --version")?; 102 let out = cmd!("rustc --version").read()?;
104 if !out.contains("nightly") { 103 if !out.contains("nightly") {
105 bail!("fuzz tests require nightly rustc") 104 bail!("fuzz tests require nightly rustc")
106 } 105 }
107 106
108 run!("cargo fuzz run parser")?; 107 cmd!("cargo fuzz run parser").run()?;
109 Ok(()) 108 Ok(())
110} 109}
111 110
111fn date_iso() -> Result<String> {
112 let res = cmd!("date --iso --utc").read()?;
113 Ok(res)
114}
115
112fn is_release_tag(tag: &str) -> bool { 116fn is_release_tag(tag: &str) -> bool {
113 tag.len() == "2020-02-24".len() && tag.starts_with(|c: char| c.is_ascii_digit()) 117 tag.len() == "2020-02-24".len() && tag.starts_with(|c: char| c.is_ascii_digit())
114} 118}
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 3f4aa5497..97e5dcd4e 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -12,12 +12,12 @@ use std::env;
12 12
13use codegen::CodegenCmd; 13use codegen::CodegenCmd;
14use pico_args::Arguments; 14use pico_args::Arguments;
15use xshell::pushd;
15use xtask::{ 16use xtask::{
16 codegen::{self, Mode}, 17 codegen::{self, Mode},
17 dist::DistCmd, 18 dist::DistCmd,
18 install::{ClientOpt, InstallCmd, Malloc, ServerOpt}, 19 install::{ClientOpt, InstallCmd, Malloc, ServerOpt},
19 metrics::MetricsCmd, 20 metrics::MetricsCmd,
20 not_bash::pushd,
21 pre_cache::PreCacheCmd, 21 pre_cache::PreCacheCmd,
22 pre_commit, project_root, 22 pre_commit, project_root,
23 release::{PromoteCmd, ReleaseCmd}, 23 release::{PromoteCmd, ReleaseCmd},
@@ -29,7 +29,7 @@ fn main() -> Result<()> {
29 return pre_commit::run_hook(); 29 return pre_commit::run_hook();
30 } 30 }
31 31
32 let _d = pushd(project_root()); 32 let _d = pushd(project_root())?;
33 33
34 let mut args = Arguments::from_env(); 34 let mut args = Arguments::from_env();
35 let subcommand = args.subcommand()?.unwrap_or_default(); 35 let subcommand = args.subcommand()?.unwrap_or_default();
diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs
index 4bade2c7e..e0d1aaf97 100644
--- a/xtask/src/metrics.rs
+++ b/xtask/src/metrics.rs
@@ -7,8 +7,7 @@ use std::{
7}; 7};
8 8
9use anyhow::{bail, format_err, Result}; 9use anyhow::{bail, format_err, Result};
10 10use xshell::{cmd, mkdir_p, pushd, pushenv, read_file, rm_rf};
11use crate::not_bash::{fs2, pushd, pushenv, rm_rf, run};
12 11
13type Unit = String; 12type Unit = String;
14 13
@@ -23,12 +22,13 @@ impl MetricsCmd {
23 rm_rf("./target/release")?; 22 rm_rf("./target/release")?;
24 } 23 }
25 if !Path::new("./target/rustc-perf").exists() { 24 if !Path::new("./target/rustc-perf").exists() {
26 fs2::create_dir_all("./target/rustc-perf")?; 25 mkdir_p("./target/rustc-perf")?;
27 run!("git clone https://github.com/rust-lang/rustc-perf.git ./target/rustc-perf")?; 26 cmd!("git clone https://github.com/rust-lang/rustc-perf.git ./target/rustc-perf")
27 .run()?;
28 } 28 }
29 { 29 {
30 let _d = pushd("./target/rustc-perf"); 30 let _d = pushd("./target/rustc-perf")?;
31 run!("git reset --hard 1d9288b0da7febf2599917da1b57dc241a1af033")?; 31 cmd!("git reset --hard 1d9288b0da7febf2599917da1b57dc241a1af033").run()?;
32 } 32 }
33 33
34 let _env = pushenv("RA_METRICS", "1"); 34 let _env = pushenv("RA_METRICS", "1");
@@ -39,17 +39,20 @@ impl MetricsCmd {
39 metrics.measure_analysis_stats("webrender")?; 39 metrics.measure_analysis_stats("webrender")?;
40 40
41 if !self.dry_run { 41 if !self.dry_run {
42 let _d = pushd("target"); 42 let _d = pushd("target")?;
43 let metrics_token = env::var("METRICS_TOKEN").unwrap(); 43 let metrics_token = env::var("METRICS_TOKEN").unwrap();
44 let repo = format!("https://{}@github.com/rust-analyzer/metrics.git", metrics_token); 44 cmd!(
45 run!("git clone --depth 1 {}", repo)?; 45 "git clone --depth 1 https://{metrics_token}@github.com/rust-analyzer/metrics.git"
46 let _d = pushd("metrics"); 46 )
47 .run()?;
48 let _d = pushd("metrics")?;
47 49
48 let mut file = std::fs::OpenOptions::new().append(true).open("metrics.json")?; 50 let mut file = std::fs::OpenOptions::new().append(true).open("metrics.json")?;
49 writeln!(file, "{}", metrics.json())?; 51 writeln!(file, "{}", metrics.json())?;
50 run!("git add .")?; 52 cmd!("git add .").run()?;
51 run!("git -c user.name=Bot -c [email protected] commit --message 📈")?; 53 cmd!("git -c user.name=Bot -c [email protected] commit --message 📈")
52 run!("git push origin master")?; 54 .run()?;
55 cmd!("git push origin master").run()?;
53 } 56 }
54 eprintln!("{:#?}", metrics); 57 eprintln!("{:#?}", metrics);
55 Ok(()) 58 Ok(())
@@ -59,10 +62,10 @@ impl MetricsCmd {
59impl Metrics { 62impl Metrics {
60 fn measure_build(&mut self) -> Result<()> { 63 fn measure_build(&mut self) -> Result<()> {
61 eprintln!("\nMeasuring build"); 64 eprintln!("\nMeasuring build");
62 run!("cargo fetch")?; 65 cmd!("cargo fetch").run()?;
63 66
64 let time = Instant::now(); 67 let time = Instant::now();
65 run!("cargo build --release --package rust-analyzer --bin rust-analyzer")?; 68 cmd!("cargo build --release --package rust-analyzer --bin rust-analyzer").run()?;
66 let time = time.elapsed(); 69 let time = time.elapsed();
67 self.report("build", time.as_millis() as u64, "ms".into()); 70 self.report("build", time.as_millis() as u64, "ms".into());
68 Ok(()) 71 Ok(())
@@ -78,7 +81,7 @@ impl Metrics {
78 } 81 }
79 fn measure_analysis_stats_path(&mut self, name: &str, path: &str) -> Result<()> { 82 fn measure_analysis_stats_path(&mut self, name: &str, path: &str) -> Result<()> {
80 eprintln!("\nMeasuring analysis-stats/{}", name); 83 eprintln!("\nMeasuring analysis-stats/{}", name);
81 let output = run!("./target/release/rust-analyzer analysis-stats --quiet {}", path)?; 84 let output = cmd!("./target/release/rust-analyzer analysis-stats --quiet {path}").read()?;
82 for (metric, value, unit) in parse_metrics(&output) { 85 for (metric, value, unit) in parse_metrics(&output) {
83 self.report(&format!("analysis-stats/{}/{}", name, metric), value, unit.into()); 86 self.report(&format!("analysis-stats/{}/{}", name, metric), value, unit.into());
84 } 87 }
@@ -118,7 +121,7 @@ impl Metrics {
118 fn new() -> Result<Metrics> { 121 fn new() -> Result<Metrics> {
119 let host = Host::new()?; 122 let host = Host::new()?;
120 let timestamp = SystemTime::now(); 123 let timestamp = SystemTime::now();
121 let revision = run!("git rev-parse HEAD")?; 124 let revision = cmd!("git rev-parse HEAD").read()?;
122 Ok(Metrics { host, timestamp, revision, metrics: BTreeMap::new() }) 125 Ok(Metrics { host, timestamp, revision, metrics: BTreeMap::new() })
123 } 126 }
124 127
@@ -160,7 +163,7 @@ impl Host {
160 return Ok(Host { os, cpu, mem }); 163 return Ok(Host { os, cpu, mem });
161 164
162 fn read_field<'a>(path: &str, field: &str) -> Result<String> { 165 fn read_field<'a>(path: &str, field: &str) -> Result<String> {
163 let text = fs2::read_to_string(path)?; 166 let text = read_file(path)?;
164 167
165 let line = text 168 let line = text
166 .lines() 169 .lines()
diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs
deleted file mode 100644
index 038898993..000000000
--- a/xtask/src/not_bash.rs
+++ /dev/null
@@ -1,169 +0,0 @@
1//! A bad shell -- small cross platform module for writing glue code
2
3use std::{
4 cell::RefCell,
5 env,
6 ffi::OsString,
7 io::{self, Write},
8 path::{Path, PathBuf},
9 process::{Command, Stdio},
10};
11
12use anyhow::{bail, Context, Result};
13
14pub use fs_err as fs2;
15
16#[macro_export]
17macro_rules! run {
18 ($($expr:expr),*) => {
19 run!($($expr),*; echo = true)
20 };
21 ($($expr:expr),* ; echo = $echo:expr) => {
22 $crate::not_bash::run_process(format!($($expr),*), $echo, None)
23 };
24 ($($expr:expr),* ; <$stdin:expr) => {
25 $crate::not_bash::run_process(format!($($expr),*), false, Some($stdin))
26 };
27}
28pub use crate::run;
29
30pub struct Pushd {
31 _p: (),
32}
33
34pub fn pushd(path: impl Into<PathBuf>) -> Pushd {
35 Env::with(|env| env.pushd(path.into()));
36 Pushd { _p: () }
37}
38
39impl Drop for Pushd {
40 fn drop(&mut self) {
41 Env::with(|env| env.popd())
42 }
43}
44
45pub struct Pushenv {
46 _p: (),
47}
48
49pub fn pushenv(var: &str, value: &str) -> Pushenv {
50 Env::with(|env| env.pushenv(var.into(), value.into()));
51 Pushenv { _p: () }
52}
53
54impl Drop for Pushenv {
55 fn drop(&mut self) {
56 Env::with(|env| env.popenv())
57 }
58}
59
60pub fn rm_rf(path: impl AsRef<Path>) -> io::Result<()> {
61 let path = path.as_ref();
62 if !path.exists() {
63 return Ok(());
64 }
65 if path.is_file() {
66 fs2::remove_file(path)
67 } else {
68 fs2::remove_dir_all(path)
69 }
70}
71
72#[doc(hidden)]
73pub fn run_process(cmd: String, echo: bool, stdin: Option<&[u8]>) -> Result<String> {
74 run_process_inner(&cmd, echo, stdin).with_context(|| format!("process `{}` failed", cmd))
75}
76
77pub fn date_iso() -> Result<String> {
78 run!("date --iso --utc")
79}
80
81fn run_process_inner(cmd: &str, echo: bool, stdin: Option<&[u8]>) -> Result<String> {
82 let mut args = shelx(cmd);
83 let binary = args.remove(0);
84 let current_dir = Env::with(|it| it.cwd().to_path_buf());
85
86 if echo {
87 println!("> {}", cmd)
88 }
89
90 let mut command = Command::new(binary);
91 command.args(args).current_dir(current_dir).stderr(Stdio::inherit());
92 let output = match stdin {
93 None => command.stdin(Stdio::null()).output(),
94 Some(stdin) => {
95 command.stdin(Stdio::piped()).stdout(Stdio::piped());
96 let mut process = command.spawn()?;
97 process.stdin.take().unwrap().write_all(stdin)?;
98 process.wait_with_output()
99 }
100 }?;
101 let stdout = String::from_utf8(output.stdout)?;
102
103 if echo {
104 print!("{}", stdout)
105 }
106
107 if !output.status.success() {
108 bail!("{}", output.status)
109 }
110
111 Ok(stdout.trim().to_string())
112}
113
114// FIXME: some real shell lexing here
115fn shelx(cmd: &str) -> Vec<String> {
116 let mut res = Vec::new();
117 for (string_piece, in_quotes) in cmd.split('\'').zip([false, true].iter().copied().cycle()) {
118 if in_quotes {
119 res.push(string_piece.to_string())
120 } else {
121 if !string_piece.is_empty() {
122 res.extend(string_piece.split_ascii_whitespace().map(|it| it.to_string()))
123 }
124 }
125 }
126 res
127}
128
129struct Env {
130 pushd_stack: Vec<PathBuf>,
131 pushenv_stack: Vec<(OsString, Option<OsString>)>,
132}
133
134impl Env {
135 fn with<F: FnOnce(&mut Env) -> T, T>(f: F) -> T {
136 thread_local! {
137 static ENV: RefCell<Env> = RefCell::new(Env {
138 pushd_stack: vec![env::current_dir().unwrap()],
139 pushenv_stack: vec![],
140 });
141 }
142 ENV.with(|it| f(&mut *it.borrow_mut()))
143 }
144
145 fn pushd(&mut self, dir: PathBuf) {
146 let dir = self.cwd().join(dir);
147 self.pushd_stack.push(dir);
148 env::set_current_dir(self.cwd())
149 .unwrap_or_else(|err| panic!("Failed to set cwd to {}: {}", self.cwd().display(), err));
150 }
151 fn popd(&mut self) {
152 self.pushd_stack.pop().unwrap();
153 env::set_current_dir(self.cwd()).unwrap();
154 }
155 fn pushenv(&mut self, var: OsString, value: OsString) {
156 self.pushenv_stack.push((var.clone(), env::var_os(&var)));
157 env::set_var(var, value)
158 }
159 fn popenv(&mut self) {
160 let (var, value) = self.pushenv_stack.pop().unwrap();
161 match value {
162 None => env::remove_var(var),
163 Some(value) => env::set_var(var, value),
164 }
165 }
166 fn cwd(&self) -> &Path {
167 self.pushd_stack.last().unwrap()
168 }
169}
diff --git a/xtask/src/pre_cache.rs b/xtask/src/pre_cache.rs
index 47ba6ba24..569f88f68 100644
--- a/xtask/src/pre_cache.rs
+++ b/xtask/src/pre_cache.rs
@@ -4,8 +4,7 @@ use std::{
4}; 4};
5 5
6use anyhow::Result; 6use anyhow::Result;
7 7use xshell::rm_rf;
8use crate::not_bash::{fs2, rm_rf};
9 8
10pub struct PreCacheCmd; 9pub struct PreCacheCmd;
11 10
@@ -26,7 +25,7 @@ impl PreCacheCmd {
26 } 25 }
27 } 26 }
28 27
29 fs2::remove_file("./target/.rustc_info.json")?; 28 rm_rf("./target/.rustc_info.json")?;
30 29
31 let to_delete = read_dir("./crates", FileType::is_dir)? 30 let to_delete = read_dir("./crates", FileType::is_dir)?
32 .into_iter() 31 .into_iter()
diff --git a/xtask/src/pre_commit.rs b/xtask/src/pre_commit.rs
index 056f34acf..8f2dbea19 100644
--- a/xtask/src/pre_commit.rs
+++ b/xtask/src/pre_commit.rs
@@ -3,19 +3,21 @@
3use std::{fs, path::PathBuf}; 3use std::{fs, path::PathBuf};
4 4
5use anyhow::{bail, Result}; 5use anyhow::{bail, Result};
6use xshell::cmd;
6 7
7use crate::{not_bash::run, project_root, run_rustfmt, Mode}; 8use crate::{project_root, run_rustfmt, Mode};
8 9
9// FIXME: if there are changed `.ts` files, also reformat TypeScript (by 10// FIXME: if there are changed `.ts` files, also reformat TypeScript (by
10// shelling out to `npm fmt`). 11// shelling out to `npm fmt`).
11pub fn run_hook() -> Result<()> { 12pub fn run_hook() -> Result<()> {
12 run_rustfmt(Mode::Overwrite)?; 13 run_rustfmt(Mode::Overwrite)?;
13 14
14 let diff = run!("git diff --diff-filter=MAR --name-only --cached")?; 15 let diff = cmd!("git diff --diff-filter=MAR --name-only --cached").read()?;
15 16
16 let root = project_root(); 17 let root = project_root();
17 for line in diff.lines() { 18 for line in diff.lines() {
18 run!("git update-index --add {}", root.join(line).display())?; 19 let file = root.join(line);
20 cmd!("git update-index --add {file}").run()?;
19 } 21 }
20 22
21 Ok(()) 23 Ok(())
diff --git a/xtask/src/release.rs b/xtask/src/release.rs
index 3aab29801..14fc1f0dd 100644
--- a/xtask/src/release.rs
+++ b/xtask/src/release.rs
@@ -1,8 +1,6 @@
1use crate::{ 1use xshell::{cmd, cp, pushd, read_dir, write_file};
2 codegen, is_release_tag, 2
3 not_bash::{date_iso, fs2, pushd, run}, 3use crate::{codegen, date_iso, is_release_tag, project_root, Mode, Result};
4 project_root, Mode, Result,
5};
6 4
7pub struct ReleaseCmd { 5pub struct ReleaseCmd {
8 pub dry_run: bool, 6 pub dry_run: bool,
@@ -11,10 +9,10 @@ pub struct ReleaseCmd {
11impl ReleaseCmd { 9impl ReleaseCmd {
12 pub fn run(self) -> Result<()> { 10 pub fn run(self) -> Result<()> {
13 if !self.dry_run { 11 if !self.dry_run {
14 run!("git switch release")?; 12 cmd!("git switch release").run()?;
15 run!("git fetch upstream --tags --force")?; 13 cmd!("git fetch upstream --tags --force").run()?;
16 run!("git reset --hard tags/nightly")?; 14 cmd!("git reset --hard tags/nightly").run()?;
17 run!("git push")?; 15 cmd!("git push").run()?;
18 } 16 }
19 codegen::generate_assists_docs(Mode::Overwrite)?; 17 codegen::generate_assists_docs(Mode::Overwrite)?;
20 codegen::generate_feature_docs(Mode::Overwrite)?; 18 codegen::generate_feature_docs(Mode::Overwrite)?;
@@ -23,8 +21,8 @@ impl ReleaseCmd {
23 let changelog_dir = website_root.join("./thisweek/_posts"); 21 let changelog_dir = website_root.join("./thisweek/_posts");
24 22
25 let today = date_iso()?; 23 let today = date_iso()?;
26 let commit = run!("git rev-parse HEAD")?; 24 let commit = cmd!("git rev-parse HEAD").read()?;
27 let changelog_n = fs2::read_dir(changelog_dir.as_path())?.count(); 25 let changelog_n = read_dir(changelog_dir.as_path())?.len();
28 26
29 let contents = format!( 27 let contents = format!(
30 "\ 28 "\
@@ -52,20 +50,20 @@ https://github.com/sponsors/rust-analyzer[GitHub Sponsors].
52 ); 50 );
53 51
54 let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); 52 let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
55 fs2::write(&path, &contents)?; 53 write_file(&path, &contents)?;
56 54
57 for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() { 55 for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() {
58 let src = project_root().join("./docs/user/").join(adoc); 56 let src = project_root().join("./docs/user/").join(adoc);
59 let dst = website_root.join(adoc); 57 let dst = website_root.join(adoc);
60 fs2::copy(src, dst)?; 58 cp(src, dst)?;
61 } 59 }
62 60
63 let tags = run!("git tag --list"; echo = false)?; 61 let tags = cmd!("git tag --list").read()?;
64 let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); 62 let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap();
65 63
66 let git_log = run!("git log {}..HEAD --merges --reverse", prev_tag; echo = false)?; 64 let git_log = cmd!("git log {prev_tag}..HEAD --merges --reverse").read()?;
67 let git_log_dst = website_root.join("git.log"); 65 let git_log_dst = website_root.join("git.log");
68 fs2::write(git_log_dst, &git_log)?; 66 write_file(git_log_dst, &git_log)?;
69 67
70 Ok(()) 68 Ok(())
71 } 69 }
@@ -77,27 +75,25 @@ pub struct PromoteCmd {
77 75
78impl PromoteCmd { 76impl PromoteCmd {
79 pub fn run(self) -> Result<()> { 77 pub fn run(self) -> Result<()> {
80 let _dir = pushd("../rust-rust-analyzer"); 78 let _dir = pushd("../rust-rust-analyzer")?;
81 run!("git switch master")?; 79 cmd!("git switch master").run()?;
82 run!("git fetch upstream")?; 80 cmd!("git fetch upstream").run()?;
83 run!("git reset --hard upstream/master")?; 81 cmd!("git reset --hard upstream/master").run()?;
84 run!("git submodule update --recursive")?; 82 cmd!("git submodule update --recursive").run()?;
85 83
86 let branch = format!("rust-analyzer-{}", date_iso()?); 84 let branch = format!("rust-analyzer-{}", date_iso()?);
87 run!("git switch -c {}", branch)?; 85 cmd!("git switch -c {branch}").run()?;
88 { 86 {
89 let _dir = pushd("src/tools/rust-analyzer"); 87 let _dir = pushd("src/tools/rust-analyzer")?;
90 run!("git fetch origin")?; 88 cmd!("git fetch origin").run()?;
91 run!("git reset --hard origin/release")?; 89 cmd!("git reset --hard origin/release").run()?;
92 } 90 }
93 run!("git add src/tools/rust-analyzer")?; 91 cmd!("git add src/tools/rust-analyzer").run()?;
94 run!("git commit -m':arrow_up: rust-analyzer'")?; 92 cmd!("git commit -m':arrow_up: rust-analyzer'").run()?;
95 if !self.dry_run { 93 if !self.dry_run {
96 run!("git push")?; 94 cmd!("git push").run()?;
97 run!( 95 cmd!("xdg-open https://github.com/matklad/rust/pull/new/{branch}?body=r%3F%20%40ghost")
98 "xdg-open https://github.com/matklad/rust/pull/new/{}?body=r%3F%20%40ghost", 96 .run()?;
99 branch
100 )?;
101 } 97 }
102 Ok(()) 98 Ok(())
103 } 99 }
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index b3bb9d543..d335adb72 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -3,9 +3,9 @@ use std::{
3 path::{Path, PathBuf}, 3 path::{Path, PathBuf},
4}; 4};
5 5
6use xshell::{cmd, read_file};
6use xtask::{ 7use xtask::{
7 codegen::{self, Mode}, 8 codegen::{self, Mode},
8 not_bash::{fs2, run},
9 project_root, run_rustfmt, rust_files, 9 project_root, run_rustfmt, rust_files,
10}; 10};
11 11
@@ -48,14 +48,13 @@ fn smoke_test_docs_generation() {
48fn check_lsp_extensions_docs() { 48fn check_lsp_extensions_docs() {
49 let expected_hash = { 49 let expected_hash = {
50 let lsp_ext_rs = 50 let lsp_ext_rs =
51 fs2::read_to_string(project_root().join("crates/rust-analyzer/src/lsp_ext.rs")) 51 read_file(project_root().join("crates/rust-analyzer/src/lsp_ext.rs")).unwrap();
52 .unwrap();
53 stable_hash(lsp_ext_rs.as_str()) 52 stable_hash(lsp_ext_rs.as_str())
54 }; 53 };
55 54
56 let actual_hash = { 55 let actual_hash = {
57 let lsp_extensions_md = 56 let lsp_extensions_md =
58 fs2::read_to_string(project_root().join("docs/dev/lsp-extensions.md")).unwrap(); 57 read_file(project_root().join("docs/dev/lsp-extensions.md")).unwrap();
59 let text = lsp_extensions_md 58 let text = lsp_extensions_md
60 .lines() 59 .lines()
61 .find_map(|line| line.strip_prefix("lsp_ext.rs hash:")) 60 .find_map(|line| line.strip_prefix("lsp_ext.rs hash:"))
@@ -83,7 +82,7 @@ Please adjust docs/dev/lsp-extensions.md.
83fn rust_files_are_tidy() { 82fn rust_files_are_tidy() {
84 let mut tidy_docs = TidyDocs::default(); 83 let mut tidy_docs = TidyDocs::default();
85 for path in rust_files(&project_root().join("crates")) { 84 for path in rust_files(&project_root().join("crates")) {
86 let text = fs2::read_to_string(&path).unwrap(); 85 let text = read_file(&path).unwrap();
87 check_todo(&path, &text); 86 check_todo(&path, &text);
88 check_trailing_ws(&path, &text); 87 check_trailing_ws(&path, &text);
89 deny_clippy(&path, &text); 88 deny_clippy(&path, &text);
@@ -94,8 +93,10 @@ fn rust_files_are_tidy() {
94 93
95#[test] 94#[test]
96fn check_merge_commits() { 95fn check_merge_commits() {
97 let stdout = run!("git rev-list --merges --invert-grep --author 'bors\\[bot\\]' HEAD~19.."; echo = false) 96 let stdout =
98 .unwrap(); 97 dbg!(cmd!("git rev-list --merges --invert-grep --author 'bors\\[bot\\]' HEAD~19.."))
98 .read()
99 .unwrap();
99 if !stdout.is_empty() { 100 if !stdout.is_empty() {
100 panic!( 101 panic!(
101 " 102 "
@@ -168,7 +169,7 @@ Zlib OR Apache-2.0 OR MIT
168 .filter(|it| !it.is_empty()) 169 .filter(|it| !it.is_empty())
169 .collect::<Vec<_>>(); 170 .collect::<Vec<_>>();
170 171
171 let meta = run!("cargo metadata --format-version 1"; echo = false).unwrap(); 172 let meta = cmd!("cargo metadata --format-version 1").read().unwrap();
172 let mut licenses = meta 173 let mut licenses = meta
173 .split(|c| c == ',' || c == '{' || c == '}') 174 .split(|c| c == ',' || c == '{' || c == '}')
174 .filter(|it| it.contains(r#""license""#)) 175 .filter(|it| it.contains(r#""license""#))
@@ -177,6 +178,25 @@ Zlib OR Apache-2.0 OR MIT
177 .collect::<Vec<_>>(); 178 .collect::<Vec<_>>();
178 licenses.sort(); 179 licenses.sort();
179 licenses.dedup(); 180 licenses.dedup();
181 if licenses != expected {
182 let mut diff = String::new();
183
184 diff += &format!("New Licenses:\n");
185 for &l in licenses.iter() {
186 if !expected.contains(&l) {
187 diff += &format!(" {}\n", l)
188 }
189 }
190
191 diff += &format!("\nMissing Licenses:\n");
192 for &l in expected.iter() {
193 if !licenses.contains(&l) {
194 diff += &format!(" {}\n", l)
195 }
196 }
197
198 panic!("different set of licenses!\n{}", diff);
199 }
180 assert_eq!(licenses, expected); 200 assert_eq!(licenses, expected);
181} 201}
182 202