build.rs (3378B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 // Writes build information to ${OUT_DIR}/build-info.rs which is included in 6 // the program during compilation: 7 // 8 // ```no_run 9 // const COMMIT_HASH: Option<&'static str> = Some("c31a366"); 10 // const COMMIT_DATE: Option<&'static str> = Some("1988-05-10"); 11 // ``` 12 // 13 // The values are `None` if running hg failed, e.g. if it is not installed or 14 // if we are not in an hg repo. 15 16 use std::env; 17 use std::ffi::OsStr; 18 use std::fs::File; 19 use std::io; 20 use std::io::Write; 21 use std::path::{Path, PathBuf}; 22 use std::process::Command; 23 24 fn main() -> io::Result<()> { 25 let cur_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); 26 let build_info = get_build_info(&cur_dir); 27 28 let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 29 let mut fh = File::create(out_dir.join("build-info.rs"))?; 30 writeln!( 31 fh, 32 "const COMMIT_HASH: Option<&'static str> = {:?};", 33 build_info.hash() 34 )?; 35 writeln!( 36 fh, 37 "const COMMIT_DATE: Option<&'static str> = {:?};", 38 build_info.date() 39 )?; 40 41 Ok(()) 42 } 43 44 fn get_build_info(dir: &Path) -> Box<dyn BuildInfo> { 45 if Path::exists(&dir.join(".hg")) { 46 Box::new(Hg {}) 47 } else if Path::exists(&dir.join(".git")) { 48 Box::new(Git {}) 49 } else if let Some(parent) = dir.parent() { 50 get_build_info(parent) 51 } else { 52 eprintln!("unable to detect vcs"); 53 Box::new(Noop {}) 54 } 55 } 56 57 trait BuildInfo { 58 fn hash(&self) -> Option<String>; 59 fn date(&self) -> Option<String>; 60 } 61 62 struct Hg; 63 64 impl Hg { 65 fn exec<I, S>(&self, args: I) -> Option<String> 66 where 67 I: IntoIterator<Item = S>, 68 S: AsRef<OsStr>, 69 { 70 Command::new("hg") 71 .env("HGPLAIN", "1") 72 .args(args) 73 .output() 74 .ok() 75 .and_then(|r| String::from_utf8(r.stdout).ok()) 76 .map(|s| s.trim_end().into()) 77 } 78 } 79 80 impl BuildInfo for Hg { 81 fn hash(&self) -> Option<String> { 82 self.exec(["log", "-r.", "-T{node|short}"]) 83 } 84 85 fn date(&self) -> Option<String> { 86 self.exec(["log", "-r.", "-T{date|isodate}"]) 87 } 88 } 89 90 struct Git; 91 92 impl Git { 93 fn exec<I, S>(&self, args: I) -> Option<String> 94 where 95 I: IntoIterator<Item = S>, 96 S: AsRef<OsStr>, 97 { 98 Command::new("git") 99 .env("GIT_CONFIG_NOSYSTEM", "1") 100 .args(args) 101 .output() 102 .ok() 103 .and_then(|r| String::from_utf8(r.stdout).ok()) 104 .map(|s| s.trim_end().into()) 105 } 106 107 fn to_hg_sha(&self, git_sha: String) -> Option<String> { 108 self.exec(["cinnabar", "git2hg", &git_sha]) 109 } 110 } 111 112 impl BuildInfo for Git { 113 fn hash(&self) -> Option<String> { 114 self.exec(["rev-parse", "HEAD"]) 115 .and_then(|sha| self.to_hg_sha(sha)) 116 .map(|mut s| { 117 s.truncate(12); 118 s 119 }) 120 } 121 122 fn date(&self) -> Option<String> { 123 self.exec(["log", "-1", "--date=short", "--pretty=format:%cd"]) 124 } 125 } 126 127 struct Noop; 128 129 impl BuildInfo for Noop { 130 fn hash(&self) -> Option<String> { 131 None 132 } 133 fn date(&self) -> Option<String> { 134 None 135 } 136 }