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
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(())
}