From c0a0ddd638dacac7123a0c3f5c3e3488dc0fb8ca Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 7 May 2018 21:01:23 +0200 Subject: [PATCH] Add rustc version check to build script --- .gitignore | 3 ++ Cargo.toml | 4 +++ build.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ min_version.txt | 7 ++++ 4 files changed, 105 insertions(+) create mode 100644 min_version.txt diff --git a/.gitignore b/.gitignore index 3c8f96c0f..5c665bebd 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ helper.txt *.iml .vscode .idea + +# Used by the Clippy build script +min_version.txt diff --git a/Cargo.toml b/Cargo.toml index 72a47d725..1a924bc7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,5 +51,9 @@ clippy-mini-macro-test = { version = "0.2", path = "mini-macro" } serde = "1.0" derive-new = "0.5" +[build-dependencies] +rustc_version = "0.2.2" +ansi_term = "0.11" + [features] debugging = [] diff --git a/build.rs b/build.rs index 1c930c1b2..c13eed1f5 100644 --- a/build.rs +++ b/build.rs @@ -1,8 +1,99 @@ +//! This build script ensures that clippy is not compiled with an +//! incompatible version of rust. It will panic with a descriptive +//! error message instead. +//! +//! We specifially want to ensure that clippy is only built with a +//! rustc version that is newer or equal to the one specified in the +//! `min_version.txt` file. +//! +//! `min_version.txt` is in the repo but also in the `.gitignore` to +//! make sure that it is not updated manually by accident. Only CI +//! should update that file. +//! +//! This build script was originally taken from the Rocket web framework: +//! https://github.com/SergioBenitez/Rocket + +extern crate rustc_version; +extern crate ansi_term; + use std::env; +use rustc_version::{version_meta, version_meta_for, Channel, Version, VersionMeta}; +use ansi_term::Colour::Red; fn main() { + let string = include_str!("min_version.txt"); + let min_version_meta = version_meta_for(string) + .expect("Could not parse version string in min_version.txt"); + let current_version_meta = version_meta() + .expect("Could not retrieve current rustc version information from ENV"); + + let min_version = min_version_meta.clone().semver; + let min_date_str = min_version_meta.clone().commit_date + .expect("min_version.txt does not contain a rustc commit date"); + + let current_version = current_version_meta.clone().semver; + let current_date_str = current_version_meta.clone().commit_date + .expect("current rustc version information does not contain a rustc commit date"); + + let print_version_err = |version: &Version, date: &str| { + eprintln!("> {} {}. {} {}.\n", + "Installed rustc version is:", + format!("{} ({})", version, date), + "Minimum required rustc version:", + format!("{} ({})", min_version, min_date_str)); + }; + + if !correct_channel(¤t_version_meta) { + eprintln!("\n{} {}", + Red.bold().paint("error:"), + "clippy requires a nightly version of Rust."); + print_version_err(¤t_version, &*current_date_str); + eprintln!("{}{}{}", + "See the README (", + "https://github.com/rust-lang-nursery/rust-clippy#usage", + ") for more information."); + panic!("Aborting compilation due to incompatible compiler.") + } + + let current_date = str_to_ymd(¤t_date_str).unwrap(); + let min_date = str_to_ymd(&min_date_str).unwrap(); + + if current_date < min_date { + eprintln!("\n{} {}", + Red.bold().paint("error:"), + "clippy does not support this version of rustc nightly."); + eprintln!("> {}{}{}", + "Use `", + "rustup update", + "` or your preferred method to update Rust."); + print_version_err(¤t_version, &*current_date_str); + panic!("Aborting compilation due to incompatible compiler.") + } + // Forward the profile to the main compilation println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap()); // Don't rebuild even if nothing changed println!("cargo:rerun-if-changed=build.rs"); } + +fn correct_channel(version_meta: &VersionMeta) -> bool { + match version_meta.channel { + Channel::Stable | Channel::Beta => { + false + }, + Channel::Nightly | Channel::Dev => { + true + } + } +} + +/// Convert a string of %Y-%m-%d to a single u32 maintaining ordering. +fn str_to_ymd(ymd: &str) -> Option { + let ymd: Vec = ymd.split("-").filter_map(|s| s.parse::().ok()).collect(); + if ymd.len() != 3 { + return None + } + + let (y, m, d) = (ymd[0], ymd[1], ymd[2]); + Some((y << 9) | (m << 5) | d) +} diff --git a/min_version.txt b/min_version.txt new file mode 100644 index 000000000..b901c8ec5 --- /dev/null +++ b/min_version.txt @@ -0,0 +1,7 @@ +rustc 1.27.0-nightly (e82261dfb 2018-05-03) +binary: rustc +commit-hash: e82261dfbb5feaa2d28d2b138f4aabb2aa52c94b +commit-date: 2018-05-03 +host: x86_64-unknown-linux-gnu +release: 1.27.0-nightly +LLVM version: 6.0