From d62486f4c555086c77ade58ad16c40350c6c9988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Fri, 22 Nov 2024 10:28:24 +0100 Subject: [PATCH] document build.rs --- build.rs | 140 +++++++++++++++++++++++++++++++++++------------------ src/cli.rs | 1 + 2 files changed, 95 insertions(+), 46 deletions(-) diff --git a/build.rs b/build.rs index fbbd59a3..48f4c2cb 100644 --- a/build.rs +++ b/build.rs @@ -1,76 +1,124 @@ -use std::{collections::HashMap, env}; +use std::{ + collections::HashMap, + env::{self, VarError}, +}; use git2::{DescribeOptions, Repository}; use serde::Deserialize; fn main() { - if let Ok(git) = Repository::open(".") { - if let None = maybe_forward_env("GIT_DESCRIBE") { - let mut opts = DescribeOptions::new(); - opts.describe_all(); - opts.show_commit_oid_as_fallback(true); + features_env(); + target_envs(); + git_envs(); +} - let description = git - .describe(&opts) - .expect("should describe git object") - .format(None) - .expect("should format git object description"); - - println!("cargo::rustc-env=GIT_DESCRIBE={description}"); - }; - - if let None = maybe_forward_env("GIT_REV") { - let head = git.head().expect("should get git HEAD"); - let commit = head.peel_to_commit().expect("should get git HEAD commit"); - let rev = commit.id().to_string(); - - println!("cargo::rustc-env=GIT_REV={rev}"); - }; +/// Builds the `CARGO_FEATURES` environment variable. +/// +/// This function turns enabled cargo features into a simple string +/// `+feature1 +feature2 +featureN`, which then exposes it via the +/// `CARGO_FEATURES` environment variable. +/// +/// It first reads and parses the Cargo.toml in order to extract all +/// available features (omitting "default"). It then checks for +/// enabled features via `CARGO_FEATURE_` to finally collect +/// them into a string. +fn features_env() { + #[derive(Deserialize)] + struct Config { + features: HashMap>, } - let toml: CargoToml = - toml::from_str(include_str!("./Cargo.toml")).expect("should read Cargo.toml"); + impl Config { + fn enabled_features(self) -> impl Iterator { + self.features + .into_keys() + .filter(|feature| feature != "default") + .filter(|feature| { + let feature = feature.replace('-', "_").to_uppercase(); + env::var(format!("CARGO_FEATURE_{feature}")).is_ok() + }) + } + } + + let config: Config = + toml::from_str(include_str!("./Cargo.toml")).expect("should parse Cargo.toml"); let mut features = String::new(); - for (feature, _) in toml.features { - if feature == "default" { - continue; - } - - if feature_enabled(&feature) { + for feature in config.enabled_features() { + if !features.is_empty() { features.push(' '); - features.push_str(&format!("+{feature}")); } + features.push_str(&format!("+{feature}")); } println!("cargo::rustc-env=CARGO_FEATURES={features}"); +} +/// Builds environment variables related to the target platform. +/// +/// This function basically forwards existing cargo environments +/// related to the target platform. +fn target_envs() { forward_env("CARGO_CFG_TARGET_OS"); forward_env("CARGO_CFG_TARGET_ENV"); forward_env("CARGO_CFG_TARGET_ARCH"); } -#[derive(Deserialize)] -struct CargoToml { - features: HashMap>, +/// Builds environment variables related to git. +/// +/// This function basically tries to forward existing git environment +/// variables. In case of failure, it tries to build them using +/// [`git2`]. +fn git_envs() { + // skip the process if the current directory is not a git + // repository (for example, from a nix build root jail) + let Ok(git) = Repository::open(".") else { + return; + }; + + if try_forward_env("GIT_DESCRIBE").is_err() { + let mut opts = DescribeOptions::new(); + opts.describe_all(); + opts.show_commit_oid_as_fallback(true); + + let description = git + .describe(&opts) + .expect("should describe git object") + .format(None) + .expect("should format git object description"); + + println!("cargo::rustc-env=GIT_DESCRIBE={description}"); + }; + + if try_forward_env("GIT_REV").is_err() { + let head = git.head().expect("should get git HEAD"); + let commit = head.peel_to_commit().expect("should get git HEAD commit"); + let rev = commit.id().to_string(); + + println!("cargo::rustc-env=GIT_REV={rev}"); + }; } -fn feature_enabled(feature: &str) -> bool { - let feature = feature.replace('-', "_").to_uppercase(); - env::var(format!("CARGO_FEATURE_{feature}")).is_ok() -} +/// Tries to forward the given environment variable. +/// +/// For a more strict version, see [`forward_env`]. +fn try_forward_env(key: &str) -> Result { + let env = env::var(key); -fn maybe_forward_env(key: &str) -> Option { - match env::var(key) { - Err(_) => None, - Ok(val) => { - println!("cargo::rustc-env={key}={val}"); - Some(val) - } + if let Ok(val) = &env { + println!("cargo::rustc-env={key}={val}"); } + + env } +/// Forwards the given environment variable. +/// +/// This function panics in case the forward fails (when the +/// environment variable does not exist for example). +/// +/// For a less strict version, see [`try_forward_env`]. fn forward_env(key: &str) { - maybe_forward_env(key).expect(&format!("should get env {key}")); + try_forward_env(key).expect(&format!("should get env {key}")); } diff --git a/src/cli.rs b/src/cli.rs index 5447eccf..ed973941 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -78,6 +78,7 @@ impl Cli { pub const LONG_VERSION: &'static str = concat!( "v", env!("CARGO_PKG_VERSION"), + " ", env!("CARGO_FEATURES"), "\nbuild: ", env!("CARGO_CFG_TARGET_OS"),