Skip to content

Commit ea53b4f

Browse files
authored
Merge branch 'master' into 1.16
2 parents 362fc3c + 198ee85 commit ea53b4f

File tree

4 files changed

+154
-60
lines changed

4 files changed

+154
-60
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ repository = "https://github.com/Kimundi/rustc-version-rs"
1212
keywords = ["version", "rustc"]
1313

1414
[dependencies]
15-
semver = "0.1"
15+
semver = "0.2.3"

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,20 @@ git = "https://github.com/Kimundi/rustc-version-rs"
2626
```
2727
# Example
2828

29+
(This uses the version at `master`, and may differ from the most recently released version.)
30+
2931
```rust
3032
// This could be a cargo build script
3133

3234
extern crate rustc_version;
33-
use rustc_version::{version, version_matches, version_meta, Channel};
35+
use rustc_version::{version, version_meta, Channel, Version};
3436

3537
fn main() {
3638
// Assert we haven't travelled back in time
37-
assert!(version().major >= 1);
39+
assert!(version().unwrap().major >= 1);
3840

3941
// Set cfg flags depending on release channel
40-
match version_meta().channel {
42+
match version_meta().unwrap().channel {
4143
Channel::Stable => {
4244
println!("cargo:rustc-cfg=RUSTC_IS_STABLE");
4345
}
@@ -52,8 +54,8 @@ fn main() {
5254
}
5355
}
5456

55-
// Directly check a semver version requirment
56-
if version_matches(">= 1.4.0") {
57+
// Check for a minimum version
58+
if version().unwrap() >= Version::parse("1.4.0").unwrap() {
5759
println!("cargo:rustc-cfg=compiler_has_important_bugfix");
5860
}
5961
}

src/errors.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use std::{self, error, fmt, io, str};
2+
use semver::{self, Identifier};
3+
4+
/// The error type for this crate.
5+
#[derive(Debug)]
6+
pub enum Error {
7+
/// An error ocurrend when executing the `rustc` command.
8+
CouldNotExecuteCommand(io::Error),
9+
/// The output of `rustc -vV` was not valid utf-8.
10+
Utf8Error(str::Utf8Error),
11+
/// The output of `rustc -vV` was not in the expected format.
12+
UnexpectedVersionFormat,
13+
/// An error ocurred in parsing a `VersionReq`.
14+
ReqParseError(semver::ReqParseError),
15+
/// An error ocurred in parsing the semver.
16+
SemVerError(semver::SemVerError),
17+
/// The pre-release tag is unknown.
18+
UnknownPreReleaseTag(Identifier),
19+
}
20+
use Error::*;
21+
22+
impl fmt::Display for Error {
23+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24+
use std::error::Error;
25+
match *self {
26+
CouldNotExecuteCommand(ref e) => write!(f, "{}: {}", self.description(), e),
27+
Utf8Error(_) => write!(f, "{}", self.description()),
28+
UnexpectedVersionFormat => write!(f, "{}", self.description()),
29+
ReqParseError(ref e) => write!(f, "{}: {}", self.description(), e),
30+
SemVerError(ref e) => write!(f, "{}: {}", self.description(), e),
31+
UnknownPreReleaseTag(ref i) => write!(f, "{}: {}", self.description(), i),
32+
}
33+
}
34+
}
35+
36+
impl error::Error for Error {
37+
fn cause(&self) -> Option<&error::Error> {
38+
match *self {
39+
CouldNotExecuteCommand(ref e) => Some(e),
40+
Utf8Error(ref e) => Some(e),
41+
UnexpectedVersionFormat => None,
42+
ReqParseError(ref e) => Some(e),
43+
SemVerError(ref e) => Some(e),
44+
UnknownPreReleaseTag(_) => None,
45+
}
46+
}
47+
48+
fn description(&self) -> &str {
49+
match *self {
50+
CouldNotExecuteCommand(_) => "could not execute command",
51+
Utf8Error(_) => "invalid UTF-8 output from `rustc -vV`",
52+
UnexpectedVersionFormat => "unexpected `rustc -vV` format",
53+
ReqParseError(_) => "error parsing version requirement",
54+
SemVerError(_) => "error parsing version",
55+
UnknownPreReleaseTag(_) => "unknown pre-release tag",
56+
}
57+
}
58+
}
59+
60+
macro_rules! impl_from {
61+
($($err_ty:ty => $variant:ident),* $(,)*) => {
62+
$(
63+
impl From<$err_ty> for Error {
64+
fn from(e: $err_ty) -> Error {
65+
Error::$variant(e)
66+
}
67+
}
68+
)*
69+
}
70+
}
71+
72+
impl_from! {
73+
str::Utf8Error => Utf8Error,
74+
semver::SemVerError => SemVerError,
75+
semver::ReqParseError => ReqParseError,
76+
}
77+
78+
/// The result type for this crate.
79+
pub type Result<T> = std::result::Result<T, Error>;

src/lib.rs

Lines changed: 67 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@
2020
//! // This could be a cargo build script
2121
//!
2222
//! extern crate rustc_version;
23-
//! use rustc_version::{version, version_matches, version_meta, Channel};
23+
//! use rustc_version::{version, version_meta, Channel, Version};
2424
//!
2525
//! fn main() {
2626
//! // Assert we haven't travelled back in time
27-
//! assert!(version().major >= 1);
27+
//! assert!(version().unwrap().major >= 1);
2828
//!
2929
//! // Set cfg flags depending on release channel
30-
//! match version_meta().channel {
30+
//! match version_meta().unwrap().channel {
3131
//! Channel::Stable => {
3232
//! println!("cargo:rustc-cfg=RUSTC_IS_STABLE");
3333
//! }
@@ -42,19 +42,26 @@
4242
//! }
4343
//! }
4444
//!
45-
//! // Directly check a semver version requirment
46-
//! if version_matches(">= 1.4.0") {
45+
//! // Check for a minimum version
46+
//! if version().unwrap() >= Version::parse("1.4.0").unwrap() {
4747
//! println!("cargo:rustc-cfg=compiler_has_important_bugfix");
4848
//! }
4949
//! }
5050
//! ```
5151
5252
extern crate semver;
53-
use semver::{Version, VersionReq, Identifier};
53+
use semver::Identifier;
5454
use std::process::Command;
55-
use std::env;
55+
use std::{env, str};
5656
use std::ffi::OsString;
5757

58+
// Convenience re-export to allow version comparison without needing to add
59+
// semver crate.
60+
pub use semver::Version;
61+
62+
mod errors;
63+
pub use errors::{Error, Result};
64+
5865
/// Release channel of the compiler.
5966
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
6067
pub enum Channel {
@@ -69,7 +76,7 @@ pub enum Channel {
6976
}
7077

7178
/// Rustc version plus metada like git short hash and build date.
72-
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
79+
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
7380
pub struct VersionMeta {
7481
/// Version of the compiler
7582
pub semver: Version,
@@ -93,51 +100,60 @@ pub struct VersionMeta {
93100
pub short_version_string: String,
94101
}
95102

103+
impl VersionMeta {
104+
/// Returns the version metadata for `cmd`, which should be a `rustc` command.
105+
pub fn for_command(cmd: Command) -> Result<VersionMeta> {
106+
let mut cmd = cmd;
107+
108+
let out = match cmd.arg("-vV").output() {
109+
Err(e) => return Err(Error::CouldNotExecuteCommand(e)),
110+
Ok(out) => out,
111+
};
112+
let out = try!(str::from_utf8(&out.stdout));
113+
114+
version_meta_for(out)
115+
}
116+
}
117+
96118
/// Returns the `rustc` SemVer version.
97-
pub fn version() -> Version {
98-
version_meta().semver
119+
pub fn version() -> Result<Version> {
120+
Ok(try!(version_meta()).semver)
99121
}
100122

101123
/// Returns the `rustc` SemVer version and additional metadata
102124
/// like the git short hash and build date.
103-
pub fn version_meta() -> VersionMeta {
125+
pub fn version_meta() -> Result<VersionMeta> {
104126
let cmd = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc"));
105127

106-
let out = Command::new(&cmd)
107-
.arg("-vV")
108-
.output()
109-
.unwrap_or_else(|e| { panic!("failed to execute `RUSTC -vV`: {}", e) });
128+
VersionMeta::for_command(Command::new(cmd))
110129

111-
let out = String::from_utf8(out.stdout)
112-
.ok()
113-
.expect("non utf8 output from RUSTC -vV");
114-
115-
version_meta_for(&out)
116130
}
117131

118132
/// Parses a "rustc -vV" output string and returns
119133
/// the SemVer version and additional metadata
120134
/// like the git short hash and build date.
121-
pub fn version_meta_for(verbose_version_string: &str) -> VersionMeta {
135+
pub fn version_meta_for(verbose_version_string: &str) -> Result<VersionMeta> {
122136
let out: Vec<_> = verbose_version_string.lines().collect();
123137

124-
const ERR_MSG: &'static str = "unexpected -vV format";
125-
126-
assert!(out.len() >= 6 && out.len() <= 8, ERR_MSG);
138+
if (out.len() >= 6 && out.len() <= 8) {
139+
return Err(Error::UnexpectedVersionFormat);
140+
}
127141

128142
let short_version_string = out[0];
129143

130-
fn expect_prefix<'a>(line: &'a str, prefix: &str) -> &'a str {
131-
assert!(line.starts_with(prefix), ERR_MSG);
132-
&line[prefix.len()..]
144+
fn expect_prefix<'a>(line: &'a str, prefix: &str) -> Result<&'a str> {
145+
match line.starts_with(prefix) {
146+
true => Ok(&line[prefix.len()..]),
147+
false => Err(Error::UnexpectedVersionFormat),
148+
}
133149
}
134150

135-
let commit_hash = match expect_prefix(out[2], "commit-hash: ") {
151+
let commit_hash = match try!(expect_prefix(out[2], "commit-hash: ")) {
136152
"unknown" => None,
137153
hash => Some(hash.to_owned()),
138154
};
139155

140-
let commit_date = match expect_prefix(out[3], "commit-date: ") {
156+
let commit_date = match try!(expect_prefix(out[3], "commit-date: ")) {
141157
"unknown" => None,
142158
hash => Some(hash.to_owned()),
143159
};
@@ -146,19 +162,18 @@ pub fn version_meta_for(verbose_version_string: &str) -> VersionMeta {
146162
let mut idx = 4;
147163
let mut build_date = None;
148164
if out[idx].starts_with("build-date") {
149-
build_date = match expect_prefix(out[idx], "build-date: ") {
165+
build_date = match try!(expect_prefix(out[idx], "build-date: ")) {
150166
"unknown" => None,
151167
s => Some(s.to_owned()),
152168
};
153169
idx = idx + 1;
154170
}
155171

172+
let host = try!(expect_prefix(out[idx], "host: "));
173+
idx = idx + 1;
174+
let release = try!(expect_prefix(out[idx], "release: "));
156175

157-
let host = expect_prefix(out[idx], "host: ");
158-
idx = idx +1;
159-
let release = expect_prefix(out[idx], "release: ");
160-
161-
let semver = Version::parse(release).unwrap();
176+
let semver: Version = try!(release.parse());
162177

163178
let channel = if semver.pre.is_empty() {
164179
Channel::Stable
@@ -170,50 +185,48 @@ pub fn version_meta_for(verbose_version_string: &str) -> VersionMeta {
170185
if s == "beta" => Channel::Beta,
171186
Identifier::AlphaNumeric(ref s)
172187
if s == "nightly" => Channel::Nightly,
173-
ref x => panic!("Unknown pre-release tag {}", x),
188+
ref x => return Err(Error::UnknownPreReleaseTag(x.clone())),
174189
}
175190
};
176191

177-
VersionMeta {
192+
Ok(VersionMeta {
178193
semver: semver,
179194
commit_hash: commit_hash,
180195
commit_date: commit_date,
181196
build_date: build_date,
182197
channel: channel,
183198
host: host.into(),
184199
short_version_string: short_version_string.into(),
185-
}
186-
}
187-
188-
/// Check wether the `rustc` version matches the given SemVer
189-
/// version requirement.
190-
pub fn version_matches(req: &str) -> bool {
191-
VersionReq::parse(req).unwrap().matches(&version())
200+
})
192201
}
193202

194203
#[test]
195204
fn smoketest() {
196-
let v = version();
205+
let v = version().unwrap();
197206
assert!(v.major >= 1);
198207

199-
let v = version_meta();
208+
let v = version_meta().unwrap();
200209
assert!(v.semver.major >= 1);
201210

202-
assert!(version_matches(">= 1.0.0"));
211+
assert!(version().unwrap() >= Version::parse("1.0.0").unwrap());
203212
}
204213

205214
#[test]
206-
#[should_panic(expected = "unexpected")]
207-
// Characterization test for behavior on an unexpected key.
208215
fn parse_unexpected() {
209-
version_meta_for(
216+
let res = version_meta_for(
210217
"rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14)
211218
binary: rustc
212219
commit-hash: a59de37e99060162a2674e3ff45409ac73595c0e
213220
commit-date: 2015-05-13
214221
rust-birthday: 2015-05-14
215222
host: x86_64-unknown-linux-gnu
216223
release: 1.0.0");
224+
225+
assert!(match res {
226+
Err(Error::UnexpectedVersionFormat) => true,
227+
_ => false,
228+
});
229+
217230
}
218231

219232
#[test]
@@ -225,7 +238,7 @@ commit-hash: a59de37e99060162a2674e3ff45409ac73595c0e
225238
commit-date: 2015-05-13
226239
build-date: 2015-05-14
227240
host: x86_64-unknown-linux-gnu
228-
release: 1.0.0");
241+
release: 1.0.0").unwrap();
229242

230243
assert_eq!(version.semver, Version::parse("1.0.0").unwrap());
231244
assert_eq!(version.commit_hash, Some("a59de37e99060162a2674e3ff45409ac73595c0e".into()));
@@ -245,7 +258,7 @@ binary: rustc
245258
commit-hash: unknown
246259
commit-date: unknown
247260
host: x86_64-unknown-linux-gnu
248-
release: 1.3.0");
261+
release: 1.3.0").unwrap();
249262

250263
assert_eq!(version.semver, Version::parse("1.3.0").unwrap());
251264
assert_eq!(version.commit_hash, None);
@@ -263,7 +276,7 @@ binary: rustc
263276
commit-hash: 65d5c083377645a115c4ac23a620d3581b9562b6
264277
commit-date: 2015-09-29
265278
host: x86_64-unknown-linux-gnu
266-
release: 1.5.0-nightly");
279+
release: 1.5.0-nightly").unwrap();
267280

268281
assert_eq!(version.semver, Version::parse("1.5.0-nightly").unwrap());
269282
assert_eq!(version.commit_hash, Some("65d5c083377645a115c4ac23a620d3581b9562b6".into()));
@@ -281,7 +294,7 @@ binary: rustc
281294
commit-hash: 9a92aaf19a64603b02b4130fe52958cc12488900
282295
commit-date: 2015-09-15
283296
host: x86_64-unknown-linux-gnu
284-
release: 1.3.0");
297+
release: 1.3.0").unwrap();
285298

286299
assert_eq!(version.semver, Version::parse("1.3.0").unwrap());
287300
assert_eq!(version.commit_hash, Some("9a92aaf19a64603b02b4130fe52958cc12488900".into()));

0 commit comments

Comments
 (0)