Bug 1375207 - Display hash from whence geckodriver came in --version; r=jgraham
authorAndreas Tolfsen <ato@sny.no>
Thu, 22 Jun 2017 16:28:18 +0100
changeset 365675 32fe813d0a0e48c57bb455d5cce8a358cf05ee72
parent 365674 d9e437ac052d8b7927659756537b380b951c2326
child 365676 7c6658c33dc68841634e060e4417ec9263c6c144
push id91809
push usercbook@mozilla.com
push dateFri, 23 Jun 2017 09:44:41 +0000
treeherdermozilla-inbound@ab1d1b0135fe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgraham
bugs1375207, 1374977
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1375207 - Display hash from whence geckodriver came in --version; r=jgraham Because we no longer can tag geckodriver releases in mozilla-central, we need to include build information in the program itself. In the version information message displayed when passing the --version flag, we now include the current tip's SHA1 and build date following the version number. This patch could be made simpler by dumping this information correctly formatted into a text file in the output directory, but it was requested in https://bugzilla.mozilla.org/show_bug.cgi?id=1374977 to also include the version information in the log output, which means we need to access it differently and in different places. MozReview-Commit-ID: CbFQn7IV8ew
testing/geckodriver/build.rs
testing/geckodriver/src/main.rs
new file mode 100644
--- /dev/null
+++ b/testing/geckodriver/build.rs
@@ -0,0 +1,73 @@
+/// Writes build information to ${OUT_DIR}/build-info.rs which is included in
+/// the program during compilation:
+///
+/// ```no_run
+/// const COMMIT_HASH: Option<&'static str> = Some("c31a366");
+/// const COMMIT_DATE: Option<&'static str> = Some("1988-05-10");
+/// ```
+///
+/// The values are `None` if running hg failed, e.g. if it is not installed or
+/// if we are not in an hg repo.
+
+use std::env;
+use std::ffi::OsStr;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+use std::process::Command;
+
+fn main() {
+    let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
+    let mut fh = File::create(out_dir.join("build-info.rs")).unwrap();
+    writeln!(
+        fh,
+        "const COMMIT_HASH: Option<&'static str> = {:?};",
+        commit_hash()
+    ).unwrap();
+    writeln!(
+        fh,
+        "const COMMIT_DATE: Option<&'static str> = {:?};",
+        commit_date()
+    ).unwrap();
+}
+
+fn commit_hash() -> Option<String> {
+    exec(&"hg", &["log", "-r.", "-T '{node|short}'"]).or_else(
+        || {
+            exec(&"git", &["rev-parse", "HEAD"]).and_then(hg2git_sha)
+        },
+    )
+}
+
+fn commit_date() -> Option<String> {
+    exec(&"hg", &["log", "-r.", "-T '{date|isodate}'"]).or_else(|| {
+        exec(
+            &"git",
+            &["log", "-1", "--date=short", "--pretty=format:%cd"],
+        )
+    })
+}
+
+fn exec<S, I>(program: S, args: I) -> Option<String>
+where
+    S: AsRef<OsStr>,
+    I: IntoIterator<Item = S>,
+{
+    let mut cmd = Command::new(program);
+    for arg in args {
+        cmd.arg(arg.as_ref());
+    }
+    cmd.output()
+        .ok()
+        .and_then(|r| if r.status.success() {
+            Some(r.stdout)
+        } else {
+            None
+        })
+        .and_then(|o| String::from_utf8(o).ok())
+        .map(|s| s.trim_right().into())
+}
+
+fn hg2git_sha(hg_sha: String) -> Option<String> {
+    exec(&"git", &["cinnabar", "git2hg", &hg_sha])
+}
--- a/testing/geckodriver/src/main.rs
+++ b/testing/geckodriver/src/main.rs
@@ -16,18 +16,20 @@ extern crate slog_stdlog;
 extern crate slog_stream;
 extern crate zip;
 extern crate webdriver;
 
 #[macro_use]
 extern crate log;
 
 use std::borrow::ToOwned;
+use std::fmt;
+use std::fmt::Display;
 use std::io::Write;
-use std::net::{SocketAddr, IpAddr};
+use std::net::{IpAddr, SocketAddr};
 use std::path::PathBuf;
 use std::str::FromStr;
 
 use clap::{App, Arg};
 
 macro_rules! try_opt {
     ($expr:expr, $err_type:expr, $err_msg:expr) => ({
         match $expr {
@@ -40,16 +42,45 @@ macro_rules! try_opt {
 mod logging;
 mod prefs;
 mod marionette;
 mod capabilities;
 
 use logging::LogLevel;
 use marionette::{MarionetteHandler, MarionetteSettings, extension_routes};
 
+include!(concat!(env!("OUT_DIR"), "/build-info.rs"));
+
+struct BuildInfo;
+impl BuildInfo {
+    pub fn version() -> &'static str {
+        crate_version!()
+    }
+
+    pub fn hash() -> Option<&'static str> {
+        COMMIT_HASH
+    }
+
+    pub fn date() -> Option<&'static str> {
+        COMMIT_DATE
+    }
+}
+
+impl Display for BuildInfo {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BuildInfo::version())?;
+        match (BuildInfo::hash(), BuildInfo::date()) {
+            (Some(hash), Some(date)) => write!(f, " ({} {})", hash, date)?,
+            (Some(hash), None) => write!(f, " ({})", hash)?,
+            _ => {},
+        }
+        Ok(())
+    }
+}
+
 type ProgramResult = std::result::Result<(), (ExitCode, String)>;
 
 enum ExitCode {
     Ok = 0,
     Usage = 64,
     Unavailable = 69,
 }
 
@@ -99,17 +130,17 @@ fn app<'a, 'b>() -> App<'a, 'b> {
             .long("version")
             .help("Prints version and copying information"))
 }
 
 fn run() -> ProgramResult {
     let matches = app().get_matches();
 
     if matches.is_present("version") {
-        println!("geckodriver {}\n\n{}", crate_version!(),
+        println!("geckodriver {}\n\n{}", BuildInfo,
 "The source code of this program is available at
 https://github.com/mozilla/geckodriver.
 
 This program is subject to the terms of the Mozilla Public License 2.0.
 You can obtain a copy of the license at https://mozilla.org/MPL/2.0/.");
         return Ok(())
     }