1 | //! This tiny crate checks that the running or installed `rustc` meets some |
2 | //! version requirements. The version is queried by calling the Rust compiler |
3 | //! with `--version`. The path to the compiler is determined first via the |
4 | //! `RUSTC` environment variable. If it is not set, then `rustc` is used. If |
5 | //! that fails, no determination is made, and calls return `None`. |
6 | //! |
7 | //! # Examples |
8 | //! |
9 | //! **Note:** Please see [feature detection] for a note on enabling unstable |
10 | //! features based on detection via this crate. |
11 | //! |
12 | //! [feature detection]: crate#feature-detection |
13 | //! |
14 | //! * Set a `cfg` flag in `build.rs` if the running compiler was determined to |
15 | //! be at least version `1.13.0`: |
16 | //! |
17 | //! ```rust |
18 | //! extern crate version_check as rustc; |
19 | //! |
20 | //! if rustc::is_min_version("1.13.0" ).unwrap_or(false) { |
21 | //! println!("cargo:rustc-cfg=question_mark_operator" ); |
22 | //! } |
23 | //! ``` |
24 | //! |
25 | //! See [`is_max_version`] or [`is_exact_version`] to check if the compiler |
26 | //! is _at most_ or _exactly_ a certain version. |
27 | //! <br /><br /> |
28 | //! |
29 | //! * Check that the running compiler was released on or after `2018-12-18`: |
30 | //! |
31 | //! ```rust |
32 | //! extern crate version_check as rustc; |
33 | //! |
34 | //! match rustc::is_min_date("2018-12-18" ) { |
35 | //! Some(true) => "Yep! It's recent!" , |
36 | //! Some(false) => "No, it's older." , |
37 | //! None => "Couldn't determine the rustc version." |
38 | //! }; |
39 | //! ``` |
40 | //! |
41 | //! See [`is_max_date`] or [`is_exact_date`] to check if the compiler was |
42 | //! released _prior to_ or _exactly on_ a certain date. |
43 | //! <br /><br /> |
44 | //! |
45 | //! * Check that the running compiler supports feature flags: |
46 | //! |
47 | //! ```rust |
48 | //! extern crate version_check as rustc; |
49 | //! |
50 | //! match rustc::is_feature_flaggable() { |
51 | //! Some(true) => "Yes! It's a dev or nightly release!" , |
52 | //! Some(false) => "No, it's stable or beta." , |
53 | //! None => "Couldn't determine the rustc version." |
54 | //! }; |
55 | //! ``` |
56 | //! |
57 | //! Please see the note on [feature detection]. |
58 | //! <br /><br /> |
59 | //! |
60 | //! * Check that the running compiler supports a specific feature: |
61 | //! |
62 | //! ```rust |
63 | //! extern crate version_check as rustc; |
64 | //! |
65 | //! if let Some(true) = rustc::supports_feature("doc_cfg" ) { |
66 | //! println!("cargo:rustc-cfg=has_doc_cfg" ); |
67 | //! } |
68 | //! ``` |
69 | //! |
70 | //! Please see the note on [feature detection]. |
71 | //! <br /><br /> |
72 | //! |
73 | //! * Check that the running compiler is on the stable channel: |
74 | //! |
75 | //! ```rust |
76 | //! extern crate version_check as rustc; |
77 | //! |
78 | //! match rustc::Channel::read() { |
79 | //! Some(c) if c.is_stable() => format!("Yes! It's stable." ), |
80 | //! Some(c) => format!("No, the channel {} is not stable." , c), |
81 | //! None => format!("Couldn't determine the rustc version." ) |
82 | //! }; |
83 | //! ``` |
84 | //! |
85 | //! To interact with the version, release date, and release channel as structs, |
86 | //! use [`Version`], [`Date`], and [`Channel`], respectively. The [`triple()`] |
87 | //! function returns all three values efficiently. |
88 | //! |
89 | //! # Feature Detection |
90 | //! |
91 | //! While this crate can be used to determine if the current compiler supports |
92 | //! an unstable feature, no crate can determine whether that feature will work |
93 | //! in a way that you expect ad infinitum. If the feature changes in an |
94 | //! incompatible way, then your crate, as well as all of its transitive |
95 | //! dependents, will fail to build. As a result, great care should be taken when |
96 | //! enabling nightly features even when they're supported by the compiler. |
97 | //! |
98 | //! One common mitigation used in practice is to make using unstable features |
99 | //! transitively opt-in via a crate feature or `cfg` so that broken builds only |
100 | //! affect those that explicitly asked for the feature. Another complementary |
101 | //! approach is to probe `rustc` at build-time by asking it to compile a small |
102 | //! but exemplary program that determines whether the feature works as expected, |
103 | //! enabling the feature only if the probe succeeds. Finally, eschewing these |
104 | //! recommendations, you should track the `nightly` channel closely to minimize |
105 | //! the total impact of a nightly breakages. |
106 | //! |
107 | //! # Alternatives |
108 | //! |
109 | //! This crate is dead simple with no dependencies. If you need something more |
110 | //! and don't care about panicking if the version cannot be obtained, or if you |
111 | //! don't mind adding dependencies, see |
112 | //! [rustc_version](https://crates.io/crates/rustc_version). |
113 | |
114 | #![allow (deprecated)] |
115 | |
116 | mod version; |
117 | mod channel; |
118 | mod date; |
119 | |
120 | use std::env; |
121 | use std::process::Command; |
122 | |
123 | #[doc (inline)] pub use version::*; |
124 | #[doc (inline)] pub use channel::*; |
125 | #[doc (inline)] pub use date::*; |
126 | |
127 | /// Parses (version, date) as available from rustc version string. |
128 | fn version_and_date_from_rustc_version(s: &str) -> (Option<String>, Option<String>) { |
129 | let last_line: &str = s.lines().last().unwrap_or(default:s); |
130 | let mut components: Split<'_, &'static str> = last_line.trim().split(" " ); |
131 | let version: Option<&str> = components.nth(1); |
132 | let date: Option<&str> = componentsOption<&str>.filter(|c: &&str| c.ends_with(')' )).next() |
133 | .map(|s: &str| s.trim_right().trim_right_matches(")" ).trim_left().trim_left_matches('(' )); |
134 | (version.map(|s: &str| s.to_string()), date.map(|s: &str| s.to_string())) |
135 | } |
136 | |
137 | /// Parses (version, date) as available from rustc verbose version output. |
138 | fn version_and_date_from_rustc_verbose_version(s: &str) -> (Option<String>, Option<String>) { |
139 | let (mut version: Option, mut date: Option) = (None, None); |
140 | for line: &str in s.lines() { |
141 | let split: impl Fn(&str) -> Option = |s: &str| s.splitn(n:2, pat:":" ).nth(1).map(|s: &str| s.trim().to_string()); |
142 | match line.trim().split(" " ).nth(0) { |
143 | Some("rustc" ) => { |
144 | let (v: Option, d: Option) = version_and_date_from_rustc_version(line); |
145 | version = version.or(optb:v); |
146 | date = date.or(optb:d); |
147 | }, |
148 | Some("release:" ) => version = split(line), |
149 | Some("commit-date:" ) if line.ends_with("unknown" ) => date = None, |
150 | Some("commit-date:" ) => date = split(line), |
151 | _ => continue |
152 | } |
153 | } |
154 | |
155 | (version, date) |
156 | } |
157 | |
158 | /// Returns (version, date) as available from `rustc --version`. |
159 | fn get_version_and_date() -> Option<(Option<String>, Option<String>)> { |
160 | let rustc: String = env::var("RUSTC" ).unwrap_or_else(|_| "rustc" .to_string()); |
161 | CommandOption::new(program:rustc).arg("--verbose" ).arg("--version" ).output().ok() |
162 | .and_then(|output: Output| String::from_utf8(vec:output.stdout).ok()) |
163 | .map(|s: String| version_and_date_from_rustc_verbose_version(&s)) |
164 | } |
165 | |
166 | /// Reads the triple of [`Version`], [`Channel`], and [`Date`] of the installed |
167 | /// or running `rustc`. |
168 | /// |
169 | /// If any attribute cannot be determined (see the [top-level |
170 | /// documentation](crate)), returns `None`. |
171 | /// |
172 | /// To obtain only one of three attributes, use [`Version::read()`], |
173 | /// [`Channel::read()`], or [`Date::read()`]. |
174 | pub fn triple() -> Option<(Version, Channel, Date)> { |
175 | let (version_str: String, date_str: String) = match get_version_and_date() { |
176 | Some((Some(version: String), Some(date: String))) => (version, date), |
177 | _ => return None |
178 | }; |
179 | |
180 | // Can't use `?` or `try!` for `Option` in 1.0.0. |
181 | match Version::parse(&version_str) { |
182 | Some(version: Version) => match Channel::parse(&version_str) { |
183 | Some(channel: Channel) => match Date::parse(&date_str) { |
184 | Some(date: Date) => Some((version, channel, date)), |
185 | _ => None, |
186 | }, |
187 | _ => None, |
188 | }, |
189 | _ => None |
190 | } |
191 | } |
192 | |
193 | /// Checks that the running or installed `rustc` was released **on or after** |
194 | /// some date. |
195 | /// |
196 | /// The format of `min_date` must be YYYY-MM-DD. For instance: `2016-12-20` or |
197 | /// `2017-01-09`. |
198 | /// |
199 | /// If the date cannot be retrieved or parsed, or if `min_date` could not be |
200 | /// parsed, returns `None`. Otherwise returns `true` if the installed `rustc` |
201 | /// was release on or after `min_date` and `false` otherwise. |
202 | pub fn is_min_date(min_date: &str) -> Option<bool> { |
203 | match (Date::read(), Date::parse(min_date)) { |
204 | (Some(rustc_date: Date), Some(min_date: Date)) => Some(rustc_date >= min_date), |
205 | _ => None |
206 | } |
207 | } |
208 | |
209 | /// Checks that the running or installed `rustc` was released **on or before** |
210 | /// some date. |
211 | /// |
212 | /// The format of `max_date` must be YYYY-MM-DD. For instance: `2016-12-20` or |
213 | /// `2017-01-09`. |
214 | /// |
215 | /// If the date cannot be retrieved or parsed, or if `max_date` could not be |
216 | /// parsed, returns `None`. Otherwise returns `true` if the installed `rustc` |
217 | /// was release on or before `max_date` and `false` otherwise. |
218 | pub fn is_max_date(max_date: &str) -> Option<bool> { |
219 | match (Date::read(), Date::parse(max_date)) { |
220 | (Some(rustc_date: Date), Some(max_date: Date)) => Some(rustc_date <= max_date), |
221 | _ => None |
222 | } |
223 | } |
224 | |
225 | /// Checks that the running or installed `rustc` was released **exactly** on |
226 | /// some date. |
227 | /// |
228 | /// The format of `date` must be YYYY-MM-DD. For instance: `2016-12-20` or |
229 | /// `2017-01-09`. |
230 | /// |
231 | /// If the date cannot be retrieved or parsed, or if `date` could not be parsed, |
232 | /// returns `None`. Otherwise returns `true` if the installed `rustc` was |
233 | /// release on `date` and `false` otherwise. |
234 | pub fn is_exact_date(date: &str) -> Option<bool> { |
235 | match (Date::read(), Date::parse(date)) { |
236 | (Some(rustc_date: Date), Some(date: Date)) => Some(rustc_date == date), |
237 | _ => None |
238 | } |
239 | } |
240 | |
241 | /// Checks that the running or installed `rustc` is **at least** some minimum |
242 | /// version. |
243 | /// |
244 | /// The format of `min_version` is a semantic version: `1.3.0`, `1.15.0-beta`, |
245 | /// `1.14.0`, `1.16.0-nightly`, etc. |
246 | /// |
247 | /// If the version cannot be retrieved or parsed, or if `min_version` could not |
248 | /// be parsed, returns `None`. Otherwise returns `true` if the installed `rustc` |
249 | /// is at least `min_version` and `false` otherwise. |
250 | pub fn is_min_version(min_version: &str) -> Option<bool> { |
251 | match (Version::read(), Version::parse(min_version)) { |
252 | (Some(rustc_ver: Version), Some(min_ver: Version)) => Some(rustc_ver >= min_ver), |
253 | _ => None |
254 | } |
255 | } |
256 | |
257 | /// Checks that the running or installed `rustc` is **at most** some maximum |
258 | /// version. |
259 | /// |
260 | /// The format of `max_version` is a semantic version: `1.3.0`, `1.15.0-beta`, |
261 | /// `1.14.0`, `1.16.0-nightly`, etc. |
262 | /// |
263 | /// If the version cannot be retrieved or parsed, or if `max_version` could not |
264 | /// be parsed, returns `None`. Otherwise returns `true` if the installed `rustc` |
265 | /// is at most `max_version` and `false` otherwise. |
266 | pub fn is_max_version(max_version: &str) -> Option<bool> { |
267 | match (Version::read(), Version::parse(max_version)) { |
268 | (Some(rustc_ver: Version), Some(max_ver: Version)) => Some(rustc_ver <= max_ver), |
269 | _ => None |
270 | } |
271 | } |
272 | |
273 | /// Checks that the running or installed `rustc` is **exactly** some version. |
274 | /// |
275 | /// The format of `version` is a semantic version: `1.3.0`, `1.15.0-beta`, |
276 | /// `1.14.0`, `1.16.0-nightly`, etc. |
277 | /// |
278 | /// If the version cannot be retrieved or parsed, or if `version` could not be |
279 | /// parsed, returns `None`. Otherwise returns `true` if the installed `rustc` is |
280 | /// exactly `version` and `false` otherwise. |
281 | pub fn is_exact_version(version: &str) -> Option<bool> { |
282 | match (Version::read(), Version::parse(version)) { |
283 | (Some(rustc_ver: Version), Some(version: Version)) => Some(rustc_ver == version), |
284 | _ => None |
285 | } |
286 | } |
287 | |
288 | /// Checks whether the running or installed `rustc` supports feature flags. |
289 | /// |
290 | /// Returns true if the channel is either "nightly" or "dev". |
291 | /// |
292 | /// **Please see the note on [feature detection](crate#feature-detection).** |
293 | /// |
294 | /// Note that support for specific `rustc` features can be enabled or disabled |
295 | /// via the `allow-features` compiler flag, which this function _does not_ |
296 | /// check. That is, this function _does not_ check whether a _specific_ feature |
297 | /// is supported, but instead whether features are supported at all. To check |
298 | /// for support for a specific feature, use [`supports_feature()`]. |
299 | /// |
300 | /// If the version could not be determined, returns `None`. Otherwise returns |
301 | /// `true` if the running version supports feature flags and `false` otherwise. |
302 | pub fn is_feature_flaggable() -> Option<bool> { |
303 | Channel::read().map(|c: Channel| c.supports_features()) |
304 | } |
305 | |
306 | /// Checks whether the running or installed `rustc` supports `feature`. |
307 | /// |
308 | /// **Please see the note on [feature detection](crate#feature-detection).** |
309 | /// |
310 | /// Returns _true_ _iff_ [`is_feature_flaggable()`] returns `true` _and_ the |
311 | /// feature is not disabled via exclusion in `allow-features` via `RUSTFLAGS` or |
312 | /// `CARGO_ENCODED_RUSTFLAGS`. If the version could not be determined, returns |
313 | /// `None`. |
314 | /// |
315 | /// # Example |
316 | /// |
317 | /// ```rust |
318 | /// use version_check as rustc; |
319 | /// |
320 | /// if let Some(true) = rustc::supports_feature("doc_cfg" ) { |
321 | /// println!("cargo:rustc-cfg=has_doc_cfg" ); |
322 | /// } |
323 | /// ``` |
324 | pub fn supports_feature(feature: &str) -> Option<bool> { |
325 | match is_feature_flaggable() { |
326 | Some(true) => { /* continue */ } |
327 | Some(false) => return Some(false), |
328 | None => return None, |
329 | } |
330 | |
331 | let env_flags = env::var_os("CARGO_ENCODED_RUSTFLAGS" ) |
332 | .map(|flags| (flags, ' \x1f' )) |
333 | .or_else(|| env::var_os("RUSTFLAGS" ).map(|flags| (flags, ' ' ))); |
334 | |
335 | if let Some((flags, delim)) = env_flags { |
336 | const ALLOW_FEATURES: &'static str = "allow-features=" ; |
337 | |
338 | let rustflags = flags.to_string_lossy(); |
339 | let allow_features = rustflags.split(delim) |
340 | .map(|flag| flag.trim_left_matches("-Z" ).trim()) |
341 | .filter(|flag| flag.starts_with(ALLOW_FEATURES)) |
342 | .map(|flag| &flag[ALLOW_FEATURES.len()..]); |
343 | |
344 | if let Some(allow_features) = allow_features.last() { |
345 | return Some(allow_features.split(',' ).any(|f| f.trim() == feature)); |
346 | } |
347 | } |
348 | |
349 | // If there are no `RUSTFLAGS` or `CARGO_ENCODED_RUSTFLAGS` or they don't |
350 | // contain an `allow-features` flag, assume compiler allows all features. |
351 | Some(true) |
352 | } |
353 | |
354 | #[cfg (test)] |
355 | mod tests { |
356 | use std::{env, fs}; |
357 | |
358 | use super::version_and_date_from_rustc_version; |
359 | use super::version_and_date_from_rustc_verbose_version; |
360 | |
361 | macro_rules! check_parse { |
362 | (@ $f:expr, $s:expr => $v:expr, $d:expr) => ({ |
363 | if let (Some(v), d) = $f(&$s) { |
364 | let e_d: Option<&str> = $d.into(); |
365 | assert_eq!((v, d), ($v.to_string(), e_d.map(|s| s.into()))); |
366 | } else { |
367 | panic!("{:?} didn't parse for version testing." , $s); |
368 | } |
369 | }); |
370 | ($f:expr, $s:expr => $v:expr, $d:expr) => ({ |
371 | let warn = "warning: invalid logging spec 'warning', ignoring it" ; |
372 | let warn2 = "warning: sorry, something went wrong :(sad)" ; |
373 | check_parse!(@ $f, $s => $v, $d); |
374 | check_parse!(@ $f, &format!("{} \n{}" , warn, $s) => $v, $d); |
375 | check_parse!(@ $f, &format!("{} \n{}" , warn2, $s) => $v, $d); |
376 | check_parse!(@ $f, &format!("{} \n{} \n{}" , warn, warn2, $s) => $v, $d); |
377 | check_parse!(@ $f, &format!("{} \n{} \n{}" , warn2, warn, $s) => $v, $d); |
378 | }) |
379 | } |
380 | |
381 | macro_rules! check_terse_parse { |
382 | ($($s:expr => $v:expr, $d:expr,)+) => {$( |
383 | check_parse!(version_and_date_from_rustc_version, $s => $v, $d); |
384 | )+} |
385 | } |
386 | |
387 | macro_rules! check_verbose_parse { |
388 | ($($s:expr => $v:expr, $d:expr,)+) => {$( |
389 | check_parse!(version_and_date_from_rustc_verbose_version, $s => $v, $d); |
390 | )+} |
391 | } |
392 | |
393 | #[test ] |
394 | fn test_version_parse() { |
395 | check_terse_parse! { |
396 | "rustc 1.18.0" => "1.18.0" , None, |
397 | "rustc 1.8.0" => "1.8.0" , None, |
398 | "rustc 1.20.0-nightly" => "1.20.0-nightly" , None, |
399 | "rustc 1.20" => "1.20" , None, |
400 | "rustc 1.3" => "1.3" , None, |
401 | "rustc 1" => "1" , None, |
402 | "rustc 1.5.1-beta" => "1.5.1-beta" , None, |
403 | "rustc 1.20.0 (2017-07-09)" => "1.20.0" , Some("2017-07-09" ), |
404 | "rustc 1.20.0-dev (2017-07-09)" => "1.20.0-dev" , Some("2017-07-09" ), |
405 | "rustc 1.20.0-nightly (d84693b93 2017-07-09)" => "1.20.0-nightly" , Some("2017-07-09" ), |
406 | "rustc 1.20.0 (d84693b93 2017-07-09)" => "1.20.0" , Some("2017-07-09" ), |
407 | "rustc 1.30.0-nightly (3bc2ca7e4 2018-09-20)" => "1.30.0-nightly" , Some("2018-09-20" ), |
408 | }; |
409 | } |
410 | |
411 | #[test ] |
412 | fn test_verbose_version_parse() { |
413 | check_verbose_parse! { |
414 | "rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14) \n\ |
415 | binary: rustc \n\ |
416 | commit-hash: a59de37e99060162a2674e3ff45409ac73595c0e \n\ |
417 | commit-date: 2015-05-13 \n\ |
418 | build-date: 2015-05-14 \n\ |
419 | host: x86_64-unknown-linux-gnu \n\ |
420 | release: 1.0.0" => "1.0.0" , Some("2015-05-13" ), |
421 | |
422 | "rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14) \n\ |
423 | commit-hash: a59de37e99060162a2674e3ff45409ac73595c0e \n\ |
424 | commit-date: 2015-05-13 \n\ |
425 | build-date: 2015-05-14 \n\ |
426 | host: x86_64-unknown-linux-gnu \n\ |
427 | release: 1.0.0" => "1.0.0" , Some("2015-05-13" ), |
428 | |
429 | "rustc 1.50.0 (cb75ad5db 2021-02-10) \n\ |
430 | binary: rustc \n\ |
431 | commit-hash: cb75ad5db02783e8b0222fee363c5f63f7e2cf5b \n\ |
432 | commit-date: 2021-02-10 \n\ |
433 | host: x86_64-unknown-linux-gnu \n\ |
434 | release: 1.50.0" => "1.50.0" , Some("2021-02-10" ), |
435 | |
436 | "rustc 1.52.0-nightly (234781afe 2021-03-07) \n\ |
437 | binary: rustc \n\ |
438 | commit-hash: 234781afe33d3f339b002f85f948046d8476cfc9 \n\ |
439 | commit-date: 2021-03-07 \n\ |
440 | host: x86_64-unknown-linux-gnu \n\ |
441 | release: 1.52.0-nightly \n\ |
442 | LLVM version: 12.0.0" => "1.52.0-nightly" , Some("2021-03-07" ), |
443 | |
444 | "rustc 1.41.1 \n\ |
445 | binary: rustc \n\ |
446 | commit-hash: unknown \n\ |
447 | commit-date: unknown \n\ |
448 | host: x86_64-unknown-linux-gnu \n\ |
449 | release: 1.41.1 \n\ |
450 | LLVM version: 7.0" => "1.41.1" , None, |
451 | |
452 | "rustc 1.49.0 \n\ |
453 | binary: rustc \n\ |
454 | commit-hash: unknown \n\ |
455 | commit-date: unknown \n\ |
456 | host: x86_64-unknown-linux-gnu \n\ |
457 | release: 1.49.0" => "1.49.0" , None, |
458 | |
459 | "rustc 1.50.0 (Fedora 1.50.0-1.fc33) \n\ |
460 | binary: rustc \n\ |
461 | commit-hash: unknown \n\ |
462 | commit-date: unknown \n\ |
463 | host: x86_64-unknown-linux-gnu \n\ |
464 | release: 1.50.0" => "1.50.0" , None, |
465 | }; |
466 | } |
467 | |
468 | fn read_static(verbose: bool, channel: &str, minor: usize) -> String { |
469 | use std::fs::File; |
470 | use std::path::Path; |
471 | use std::io::{BufReader, Read}; |
472 | |
473 | let subdir = if verbose { "verbose" } else { "terse" }; |
474 | let path = Path::new(STATIC_PATH) |
475 | .join(channel) |
476 | .join(subdir) |
477 | .join(format!("rustc-1.{}.0" , minor)); |
478 | |
479 | let file = File::open(path).unwrap(); |
480 | let mut buf_reader = BufReader::new(file); |
481 | let mut contents = String::new(); |
482 | buf_reader.read_to_string(&mut contents).unwrap(); |
483 | contents |
484 | } |
485 | |
486 | static STATIC_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR" ), "/static" ); |
487 | |
488 | static DATES: [&'static str; 51] = [ |
489 | "2015-05-13" , "2015-06-19" , "2015-08-03" , "2015-09-15" , "2015-10-27" , |
490 | "2015-12-04" , "2016-01-19" , "2016-02-29" , "2016-04-11" , "2016-05-18" , |
491 | "2016-07-03" , "2016-08-15" , "2016-09-23" , "2016-11-07" , "2016-12-16" , |
492 | "2017-01-19" , "2017-03-10" , "2017-04-24" , "2017-06-06" , "2017-07-17" , |
493 | "2017-08-27" , "2017-10-09" , "2017-11-20" , "2018-01-01" , "2018-02-12" , |
494 | "2018-03-25" , "2018-05-07" , "2018-06-19" , "2018-07-30" , "2018-09-11" , |
495 | "2018-10-24" , "2018-12-04" , "2019-01-16" , "2019-02-28" , "2019-04-10" , |
496 | "2019-05-20" , "2019-07-03" , "2019-08-13" , "2019-09-23" , "2019-11-04" , |
497 | "2019-12-16" , "2020-01-27" , "2020-03-09" , "2020-04-20" , "2020-06-01" , |
498 | "2020-07-13" , "2020-08-24" , "2020-10-07" , "2020-11-16" , "2020-12-29" , |
499 | "2021-02-10" , |
500 | ]; |
501 | |
502 | #[test ] |
503 | fn test_stable_compatibility() { |
504 | if env::var_os("FORCE_STATIC" ).is_none() && fs::metadata(STATIC_PATH).is_err() { |
505 | // We exclude `/static` when we package `version_check`, so don't |
506 | // run if static files aren't present unless we know they should be. |
507 | return; |
508 | } |
509 | |
510 | // Ensure we can parse all output from all Linux stable releases. |
511 | for v in 0..DATES.len() { |
512 | let (version, date) = (&format!("1.{}.0" , v), Some(DATES[v])); |
513 | check_terse_parse!(read_static(false, "stable" , v) => version, date,); |
514 | check_verbose_parse!(read_static(true, "stable" , v) => version, date,); |
515 | } |
516 | } |
517 | |
518 | #[test ] |
519 | fn test_parse_current() { |
520 | let (version, channel) = (::Version::read(), ::Channel::read()); |
521 | assert!(version.is_some()); |
522 | assert!(channel.is_some()); |
523 | |
524 | if let Ok(known_channel) = env::var("KNOWN_CHANNEL" ) { |
525 | assert_eq!(channel, ::Channel::parse(&known_channel)); |
526 | } |
527 | } |
528 | } |
529 | |