Bug 1375207 - Display hash from whence geckodriver came in --version; r?jgraham draft
authorAndreas Tolfsen <ato@sny.no>
Thu, 22 Jun 2017 16:28:18 +0100
changeset 599066 bb1474236d50fed12653559c784e58876206ce7d
parent 599065 fd7290c6d7995c48d4aa4c9d802fbf3706dd60a3
child 634675 bfd647cfc857e3c631e91e03aa6cd0146526c873
push id65419
push userbmo:ato@sny.no
push dateThu, 22 Jun 2017 17:23:43 +0000
reviewersjgraham
bugs1375207, 1374977
milestone56.0a1
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(())
     }