|
1 | | -//! Simple build script to pass through the target we are building. |
| 1 | +use std::env; |
| 2 | +use std::io; |
| 3 | +use std::path::PathBuf; |
| 4 | +use std::process; |
2 | 5 |
|
3 | | -fn main() { |
| 6 | +use anyhow::{bail, Context, Result}; |
| 7 | +use once_cell::sync::Lazy; |
| 8 | + |
| 9 | +/// The directory containing the `Cargo.toml` file. |
| 10 | +static CARGO_MANIFEST_DIR: Lazy<PathBuf> = |
| 11 | + Lazy::new(|| PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap())); |
| 12 | + |
| 13 | +/// Nicely format an error message for when the subprocess didn't exit |
| 14 | +/// successfully. |
| 15 | +pub fn format_error_msg(cmd: &process::Command, output: process::Output) -> String { |
| 16 | + let stdout = String::from_utf8_lossy(&output.stdout); |
| 17 | + let stderr = String::from_utf8_lossy(&output.stderr); |
| 18 | + let mut msg = format!( |
| 19 | + "subprocess didn't exit successfully `{:?}` ({})", |
| 20 | + cmd, output.status |
| 21 | + ); |
| 22 | + if !stdout.trim().is_empty() { |
| 23 | + msg.push_str(&format!("\n--- stdout\n{}", stdout)); |
| 24 | + } |
| 25 | + if !stderr.trim().is_empty() { |
| 26 | + msg.push_str(&format!("\n--- stderr\n{}", stderr)); |
| 27 | + } |
| 28 | + msg |
| 29 | +} |
| 30 | + |
| 31 | +/// Whether underlying error kind for the given error is |
| 32 | +/// `io::ErrorKind::NotFound`. |
| 33 | +pub fn is_io_not_found(error: &anyhow::Error) -> bool { |
| 34 | + for cause in error.chain() { |
| 35 | + if let Some(io_error) = cause.downcast_ref::<io::Error>() { |
| 36 | + return io_error.kind() == io::ErrorKind::NotFound; |
| 37 | + } |
| 38 | + } |
| 39 | + false |
| 40 | +} |
| 41 | + |
| 42 | +trait CommandExt { |
| 43 | + /// Run the command return the standard output as a UTF-8 string. |
| 44 | + fn output_text(&mut self) -> Result<String>; |
| 45 | +} |
| 46 | + |
| 47 | +impl CommandExt for process::Command { |
| 48 | + /// Run the command return the standard output as a UTF-8 string. |
| 49 | + fn output_text(&mut self) -> Result<String> { |
| 50 | + let output = self |
| 51 | + .output() |
| 52 | + .with_context(|| format!("could not execute subprocess: `{:?}`", self))?; |
| 53 | + if !output.status.success() { |
| 54 | + bail!(format_error_msg(self, output)); |
| 55 | + } |
| 56 | + Ok(String::from_utf8(output.stdout).context("failed to parse stdout")?) |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +/// Simple macro to run a Git subcommand and set the result as a rustc |
| 61 | +/// environment variable. |
| 62 | +/// |
| 63 | +/// Note: |
| 64 | +/// - The Cargo manifest directory is passed as the Git directory. |
| 65 | +/// - If the Git subcommand is not available then this macro will `return |
| 66 | +/// Ok(())`. |
| 67 | +macro_rules! print_git_env { |
| 68 | + ($key:expr, $cmd:expr) => {{ |
| 69 | + let mut split = $cmd.split_whitespace(); |
| 70 | + let value = match process::Command::new(split.next().unwrap()) |
| 71 | + .arg("-C") |
| 72 | + .arg(&*CARGO_MANIFEST_DIR) |
| 73 | + .args(split) |
| 74 | + .output_text() |
| 75 | + { |
| 76 | + Ok(text) => text.trim().to_string(), |
| 77 | + Err(err) if is_io_not_found(&err) => return Ok(()), |
| 78 | + Err(err) => return Err(err.into()), |
| 79 | + }; |
| 80 | + println!("cargo:rustc-env={}={}", $key, value); |
| 81 | + }}; |
| 82 | +} |
| 83 | + |
| 84 | +/// Fetch Git info and set as rustc environment variables. |
| 85 | +/// |
| 86 | +/// If the Git subcommand is missing or the `.git` directory does not exist then |
| 87 | +/// no errors will be produced. |
| 88 | +fn print_git_envs() -> Result<()> { |
| 89 | + let git_dir = CARGO_MANIFEST_DIR.join(".git"); |
| 90 | + println!("cargo:rerun-if-changed={}", git_dir.display()); |
| 91 | + if !git_dir.exists() { |
| 92 | + return Ok(()); |
| 93 | + } |
| 94 | + print_git_env!( |
| 95 | + "GIT_COMMIT_DATE", |
| 96 | + "git log -1 --no-show-signature --date=short --format=%cd" |
| 97 | + ); |
| 98 | + print_git_env!("GIT_COMMIT_HASH", "git rev-parse HEAD"); |
| 99 | + print_git_env!("GIT_COMMIT_SHORT_HASH", "git rev-parse --short=9 HEAD"); |
| 100 | + Ok(()) |
| 101 | +} |
| 102 | + |
| 103 | +/// Fetch rustc info and set as rustc environment variables. |
| 104 | +fn print_rustc_envs() -> Result<()> { |
| 105 | + let text = process::Command::new(env::var("RUSTC")?) |
| 106 | + .arg("--verbose") |
| 107 | + .arg("--version") |
| 108 | + .output_text()?; |
| 109 | + let mut lines = text.lines(); |
4 | 110 | println!( |
5 | | - "cargo:rustc-env=TARGET={}", |
6 | | - std::env::var("TARGET").unwrap() |
| 111 | + "cargo:rustc-env=RUSTC_VERSION_SUMMARY={}", |
| 112 | + lines.next().unwrap() |
7 | 113 | ); |
| 114 | + for line in lines { |
| 115 | + let mut split = line.splitn(2, ": "); |
| 116 | + let key = split.next().unwrap(); |
| 117 | + let value = split.next().unwrap(); |
| 118 | + println!( |
| 119 | + "cargo:rustc-env=RUSTC_VERSION_{}={}", |
| 120 | + key.replace('-', "_").replace(' ', "_").to_uppercase(), |
| 121 | + value, |
| 122 | + ) |
| 123 | + } |
| 124 | + Ok(()) |
| 125 | +} |
| 126 | + |
| 127 | +fn main() -> Result<()> { |
| 128 | + print_git_envs().context("failed to fetch Git information")?; |
| 129 | + print_rustc_envs().context("failed to fetch rustc information")?; |
| 130 | + println!("cargo:rustc-env=TARGET={}", env::var("TARGET")?); |
| 131 | + Ok(()) |
8 | 132 | } |
0 commit comments