Skip to main content

pimalaya_cli/
build.rs

1//! Helpers for binary build scripts.
2//!
3//! Small functions a build.rs calls to expose enabled features, target
4//! platform and git metadata as environment variables baked into the
5//! binary.
6
7use std::{
8    collections::HashMap,
9    env::{self, VarError},
10};
11
12use git2::{DescribeOptions, Repository};
13use serde::Deserialize;
14
15/// Builds the `CARGO_FEATURES` environment variable.
16///
17/// This function turns enabled cargo features into a simple string
18/// `+feature1 +feature2 +featureN`, which then exposes it via the
19/// `CARGO_FEATURES` environment variable.
20///
21/// It first reads and parses the Cargo.toml in order to extract all
22/// available features (omitting "default"). It then checks for
23/// enabled features via `CARGO_FEATURE_<name>` to finally collect
24/// them into a string.
25pub fn features_env(cargo: &str) {
26    #[derive(Deserialize)]
27    struct Config {
28        features: HashMap<String, Vec<String>>,
29    }
30
31    impl Config {
32        fn enabled_features(self) -> impl Iterator<Item = String> {
33            self.features
34                .into_keys()
35                .filter(|feature| feature != "default")
36                .filter(|feature| {
37                    let feature = feature.replace('-', "_").to_uppercase();
38                    env::var(format!("CARGO_FEATURE_{feature}")).is_ok()
39                })
40        }
41    }
42
43    let config: Config = toml::from_str(cargo).expect("should parse Cargo.toml");
44
45    let mut features = String::new();
46
47    for feature in config.enabled_features() {
48        if !features.is_empty() {
49            features.push(' ');
50        }
51        features.push_str(&format!("+{feature}"));
52    }
53
54    println!("cargo::rustc-env=CARGO_FEATURES={features}");
55}
56
57/// Builds environment variables related to the target platform.
58///
59/// This function basically forwards existing cargo environments
60/// related to the target platform.
61pub fn target_envs() {
62    forward_env("CARGO_CFG_TARGET_OS");
63    forward_env("CARGO_CFG_TARGET_ENV");
64    forward_env("CARGO_CFG_TARGET_ARCH");
65}
66
67/// Builds environment variables related to git.
68///
69/// This function basically tries to forward existing git environment
70/// variables. In case of failure, it tries to build them using
71/// [`git2`].
72pub fn git_envs() {
73    let git = Repository::open(".").ok();
74
75    if try_forward_env("GIT_DESCRIBE").is_err() {
76        let description = match &git {
77            None => String::from("unknown"),
78            Some(git) => {
79                let mut opts = DescribeOptions::new();
80                opts.describe_all();
81                opts.show_commit_oid_as_fallback(true);
82
83                git.describe(&opts)
84                    .expect("should describe git object")
85                    .format(None)
86                    .expect("should format git object description")
87            }
88        };
89
90        println!("cargo::rustc-env=GIT_DESCRIBE={description}");
91    };
92
93    if try_forward_env("GIT_REV").is_err() {
94        let rev = match &git {
95            None => String::from("unknown"),
96            Some(git) => {
97                let head = git.head().expect("should get git HEAD");
98                let commit = head.peel_to_commit().expect("should get git HEAD commit");
99                commit.id().to_string()
100            }
101        };
102
103        println!("cargo::rustc-env=GIT_REV={rev}");
104    };
105}
106
107/// Tries to forward the given environment variable.
108///
109/// For a more strict version, see [`forward_env`].
110fn try_forward_env(key: &str) -> Result<String, VarError> {
111    let env = env::var(key);
112
113    if let Ok(val) = &env {
114        println!("cargo::rustc-env={key}={val}");
115    }
116
117    env
118}
119
120/// Forwards the given environment variable.
121///
122/// This function panics in case the forward fails (when the
123/// environment variable does not exist for example).
124///
125/// For a less strict version, see [`try_forward_env`].
126fn forward_env(key: &str) {
127    try_forward_env(key).unwrap_or_else(|_| panic!("should get env {key}"));
128}