diff options
Diffstat (limited to 'xtask/src')
-rw-r--r-- | xtask/src/dist.rs | 27 | ||||
-rw-r--r-- | xtask/src/lib.rs | 1 | ||||
-rw-r--r-- | xtask/src/main.rs | 9 | ||||
-rw-r--r-- | xtask/src/metrics.rs | 225 |
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 | ||
16 | pub fn run_dist(nightly: bool, client_version: Option<String>) -> Result<()> { | 16 | pub 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 { | 21 | impl 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 | ||
29 | fn dist_client(version: &str, release_tag: &str) -> Result<()> { | 36 | fn 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; | |||
7 | pub mod release; | 7 | pub mod release; |
8 | pub mod dist; | 8 | pub mod dist; |
9 | pub mod pre_commit; | 9 | pub mod pre_commit; |
10 | pub mod metrics; | ||
10 | 11 | ||
11 | pub mod codegen; | 12 | pub mod codegen; |
12 | mod ast_src; | 13 | mod 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; | |||
13 | use pico_args::Arguments; | 13 | use pico_args::Arguments; |
14 | use xtask::{ | 14 | use 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 @@ | |||
1 | use std::{ | ||
2 | collections::BTreeMap, | ||
3 | env, | ||
4 | fmt::{self, Write as _}, | ||
5 | io::Write as _, | ||
6 | time::{Instant, SystemTime, UNIX_EPOCH}, | ||
7 | }; | ||
8 | |||
9 | use anyhow::{bail, format_err, Result}; | ||
10 | |||
11 | use crate::not_bash::{fs2, pushd, rm_rf, run}; | ||
12 | |||
13 | type Unit = &'static str; | ||
14 | |||
15 | pub 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 | |||
36 | impl 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)] | ||
50 | struct Metrics { | ||
51 | host: Host, | ||
52 | timestamp: SystemTime, | ||
53 | revision: String, | ||
54 | metrics: BTreeMap<String, (u64, Unit)>, | ||
55 | } | ||
56 | |||
57 | #[derive(Debug)] | ||
58 | struct Host { | ||
59 | os: String, | ||
60 | cpu: String, | ||
61 | mem: String, | ||
62 | } | ||
63 | |||
64 | impl 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 | |||
113 | impl 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 | |||
154 | struct State { | ||
155 | obj: bool, | ||
156 | first: bool, | ||
157 | } | ||
158 | |||
159 | #[derive(Default)] | ||
160 | struct Json { | ||
161 | stack: Vec<State>, | ||
162 | buf: String, | ||
163 | } | ||
164 | |||
165 | impl 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 | |||
221 | impl fmt::Display for Json { | ||
222 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
223 | write!(f, "{}", self.buf) | ||
224 | } | ||
225 | } | ||