aboutsummaryrefslogtreecommitdiff
path: root/xtask
diff options
context:
space:
mode:
Diffstat (limited to 'xtask')
-rw-r--r--xtask/src/dist.rs27
-rw-r--r--xtask/src/lib.rs1
-rw-r--r--xtask/src/main.rs9
-rw-r--r--xtask/src/metrics.rs225
4 files changed, 249 insertions, 13 deletions
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs
index c198c0907..01d903cde 100644
--- a/xtask/src/dist.rs
+++ b/xtask/src/dist.rs
@@ -13,17 +13,24 @@ use crate::{
13 project_root, 13 project_root,
14}; 14};
15 15
16pub fn run_dist(nightly: bool, client_version: Option<String>) -> Result<()> { 16pub struct DistCmd {
17 let dist = project_root().join("dist"); 17 pub nightly: bool,
18 rm_rf(&dist)?; 18 pub client_version: Option<String>,
19 fs2::create_dir_all(&dist)?; 19}
20 20
21 if let Some(version) = client_version { 21impl DistCmd {
22 let release_tag = if nightly { "nightly".to_string() } else { date_iso()? }; 22 pub fn run(self) -> Result<()> {
23 dist_client(&version, &release_tag)?; 23 let dist = project_root().join("dist");
24 rm_rf(&dist)?;
25 fs2::create_dir_all(&dist)?;
26
27 if let Some(version) = self.client_version {
28 let release_tag = if self.nightly { "nightly".to_string() } else { date_iso()? };
29 dist_client(&version, &release_tag)?;
30 }
31 dist_server()?;
32 Ok(())
24 } 33 }
25 dist_server()?;
26 Ok(())
27} 34}
28 35
29fn dist_client(version: &str, release_tag: &str) -> Result<()> { 36fn dist_client(version: &str, release_tag: &str) -> Result<()> {
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
index 94d451e23..2fdb08f2e 100644
--- a/xtask/src/lib.rs
+++ b/xtask/src/lib.rs
@@ -7,6 +7,7 @@ pub mod install;
7pub mod release; 7pub mod release;
8pub mod dist; 8pub mod dist;
9pub mod pre_commit; 9pub mod pre_commit;
10pub mod metrics;
10 11
11pub mod codegen; 12pub mod codegen;
12mod ast_src; 13mod ast_src;
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 53d3ce3e7..604954269 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -13,8 +13,9 @@ use std::env;
13use pico_args::Arguments; 13use pico_args::Arguments;
14use xtask::{ 14use xtask::{
15 codegen::{self, Mode}, 15 codegen::{self, Mode},
16 dist::run_dist, 16 dist::DistCmd,
17 install::{ClientOpt, InstallCmd, Malloc, ServerOpt}, 17 install::{ClientOpt, InstallCmd, Malloc, ServerOpt},
18 metrics::run_metrics,
18 not_bash::pushd, 19 not_bash::pushd,
19 pre_commit, project_root, 20 pre_commit, project_root,
20 release::{PromoteCmd, ReleaseCmd}, 21 release::{PromoteCmd, ReleaseCmd},
@@ -115,8 +116,9 @@ FLAGS:
115 let nightly = args.contains("--nightly"); 116 let nightly = args.contains("--nightly");
116 let client_version: Option<String> = args.opt_value_from_str("--client")?; 117 let client_version: Option<String> = args.opt_value_from_str("--client")?;
117 args.finish()?; 118 args.finish()?;
118 run_dist(nightly, client_version) 119 DistCmd { nightly, client_version }.run()
119 } 120 }
121 "metrics" => run_metrics(),
120 _ => { 122 _ => {
121 eprintln!( 123 eprintln!(
122 "\ 124 "\
@@ -133,7 +135,8 @@ SUBCOMMANDS:
133 codegen 135 codegen
134 install 136 install
135 lint 137 lint
136 dist" 138 dist
139 promote"
137 ); 140 );
138 Ok(()) 141 Ok(())
139 } 142 }
diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs
new file mode 100644
index 000000000..9567f18f0
--- /dev/null
+++ b/xtask/src/metrics.rs
@@ -0,0 +1,225 @@
1use std::{
2 collections::BTreeMap,
3 env,
4 fmt::{self, Write as _},
5 io::Write as _,
6 time::{Instant, SystemTime, UNIX_EPOCH},
7};
8
9use anyhow::{bail, format_err, Result};
10
11use crate::not_bash::{fs2, pushd, rm_rf, run};
12
13type Unit = &'static str;
14
15pub fn run_metrics() -> Result<()> {
16 let mut metrics = Metrics::new()?;
17 metrics.measure_build()?;
18
19 {
20 let _d = pushd("target");
21 let metrics_token = env::var("METRICS_TOKEN").unwrap();
22 let repo = format!("https://{}@github.com/rust-analyzer/metrics.git", metrics_token);
23 run!("git clone --depth 1 {}", repo)?;
24 let _d = pushd("metrics");
25
26 let mut file = std::fs::OpenOptions::new().append(true).open("metrics.json")?;
27 writeln!(file, "{}", metrics.json())?;
28 run!("git add .")?;
29 run!("git -c user.name=Bot -c [email protected] commit --message 📈")?;
30 run!("git push origin master")?;
31 }
32 eprintln!("{:#?}", metrics);
33 Ok(())
34}
35
36impl Metrics {
37 fn measure_build(&mut self) -> Result<()> {
38 run!("cargo fetch")?;
39 rm_rf("./target/release")?;
40
41 let build = Instant::now();
42 run!("cargo build --release --package rust-analyzer --bin rust-analyzer")?;
43 let build = build.elapsed();
44 self.report("build", build.as_millis() as u64, "ms");
45 Ok(())
46 }
47}
48
49#[derive(Debug)]
50struct Metrics {
51 host: Host,
52 timestamp: SystemTime,
53 revision: String,
54 metrics: BTreeMap<String, (u64, Unit)>,
55}
56
57#[derive(Debug)]
58struct Host {
59 os: String,
60 cpu: String,
61 mem: String,
62}
63
64impl Metrics {
65 fn new() -> Result<Metrics> {
66 let host = Host::new()?;
67 let timestamp = SystemTime::now();
68 let revision = run!("git rev-parse HEAD")?;
69 Ok(Metrics { host, timestamp, revision, metrics: BTreeMap::new() })
70 }
71
72 fn report(&mut self, name: &str, value: u64, unit: Unit) {
73 self.metrics.insert(name.into(), (value, unit));
74 }
75
76 fn json(&self) -> Json {
77 let mut json = Json::default();
78 self.to_json(&mut json);
79 json
80 }
81 fn to_json(&self, json: &mut Json) {
82 json.begin_object();
83 {
84 json.field("host");
85 self.host.to_json(json);
86
87 json.field("timestamp");
88 let timestamp = self.timestamp.duration_since(UNIX_EPOCH).unwrap();
89 json.number(timestamp.as_secs() as f64);
90
91 json.field("revision");
92 json.string(&self.revision);
93
94 json.field("metrics");
95 json.begin_object();
96 {
97 for (k, &(value, unit)) in &self.metrics {
98 json.field(k);
99 json.begin_array();
100 {
101 json.number(value as f64);
102 json.string(unit);
103 }
104 json.end_array();
105 }
106 }
107 json.end_object()
108 }
109 json.end_object();
110 }
111}
112
113impl Host {
114 fn new() -> Result<Host> {
115 if cfg!(not(target_os = "linux")) {
116 bail!("can only collect metrics on Linux ");
117 }
118
119 let os = read_field("/etc/os-release", "PRETTY_NAME=")?.trim_matches('"').to_string();
120
121 let cpu =
122 read_field("/proc/cpuinfo", "model name")?.trim_start_matches(':').trim().to_string();
123
124 let mem = read_field("/proc/meminfo", "MemTotal:")?;
125
126 return Ok(Host { os, cpu, mem });
127
128 fn read_field<'a>(path: &str, field: &str) -> Result<String> {
129 let text = fs2::read_to_string(path)?;
130
131 let line = text
132 .lines()
133 .find(|it| it.starts_with(field))
134 .ok_or_else(|| format_err!("can't parse {}", path))?;
135 Ok(line[field.len()..].trim().to_string())
136 }
137 }
138 fn to_json(&self, json: &mut Json) {
139 json.begin_object();
140 {
141 json.field("os");
142 json.string(&self.os);
143
144 json.field("cpu");
145 json.string(&self.cpu);
146
147 json.field("mem");
148 json.string(&self.mem);
149 }
150 json.end_object();
151 }
152}
153
154struct State {
155 obj: bool,
156 first: bool,
157}
158
159#[derive(Default)]
160struct Json {
161 stack: Vec<State>,
162 buf: String,
163}
164
165impl Json {
166 fn begin_object(&mut self) {
167 self.stack.push(State { obj: true, first: true });
168 self.buf.push('{');
169 }
170 fn end_object(&mut self) {
171 self.stack.pop();
172 self.buf.push('}')
173 }
174 fn begin_array(&mut self) {
175 self.stack.push(State { obj: false, first: true });
176 self.buf.push('[');
177 }
178 fn end_array(&mut self) {
179 self.stack.pop();
180 self.buf.push(']')
181 }
182 fn field(&mut self, name: &str) {
183 self.object_comma();
184 self.string_token(name);
185 self.buf.push(':');
186 }
187 fn string(&mut self, value: &str) {
188 self.array_comma();
189 self.string_token(value);
190 }
191 fn string_token(&mut self, value: &str) {
192 self.buf.push('"');
193 self.buf.extend(value.escape_default());
194 self.buf.push('"');
195 }
196 fn number(&mut self, value: f64) {
197 self.array_comma();
198 write!(self.buf, "{}", value).unwrap();
199 }
200
201 fn array_comma(&mut self) {
202 let state = self.stack.last_mut().unwrap();
203 if state.obj {
204 return;
205 }
206 if !state.first {
207 self.buf.push(',');
208 }
209 state.first = false;
210 }
211
212 fn object_comma(&mut self) {
213 let state = self.stack.last_mut().unwrap();
214 if !state.first {
215 self.buf.push(',');
216 }
217 state.first = false;
218 }
219}
220
221impl fmt::Display for Json {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 write!(f, "{}", self.buf)
224 }
225}