miden_node_utils/version/
mod.rs

1#[cfg(feature = "vergen")]
2pub use vergen::vergen;
3
4/// Contains build metadata which can be formatted into a pretty --version
5/// output using its Display implementation.
6///
7/// The build metadata can be embedded at compile time using the `vergen` function
8/// available from the `vergen` feature. See that functions description for a list
9/// of the environment variables emitted which map nicely to [`LongVersion`].
10///
11/// Unfortunately these values must be transferred manually by the end user since the
12/// env variables are only available once the caller's build script has run - which is
13/// after this crate is compiled.
14pub struct LongVersion {
15    pub version: &'static str,
16    pub sha: &'static str,
17    pub branch: &'static str,
18    pub dirty: &'static str,
19    pub features: &'static str,
20    pub rust_version: &'static str,
21    pub host: &'static str,
22    pub target: &'static str,
23    pub opt_level: &'static str,
24    pub debug: &'static str,
25}
26
27impl std::fmt::Display for LongVersion {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        let &Self {
30            version,
31            mut sha,
32            mut branch,
33            dirty,
34            features,
35            rust_version,
36            host,
37            target,
38            opt_level,
39            debug,
40        } = self;
41
42        let dirty = match dirty {
43            "true" => "-dirty",
44            _ => "",
45        };
46
47        // This is the default value set by `vergen` when these values are missing.
48        // The git values can be missing for a published crate, and while we do attempt
49        // to set default values in the build.rs, its still possible for these to be skipped
50        // e.g. when cargo publish --allow-dirty is used.
51        if branch == "VERGEN_IDEMPOTENT_OUTPUT" {
52            branch = "";
53        }
54        if sha == "VERGEN_IDEMPOTENT_OUTPUT" {
55            sha = "";
56        }
57
58        f.write_fmt(format_args!(
59            "{version}
60
61SHA:          {sha}{dirty}
62branch:       {branch}
63features:     {features}
64rust version: {rust_version}
65target arch:  {target}
66host arch:    {host}
67opt-level:    {opt_level}
68debug:        {debug}
69"
70        ))
71    }
72}
73
74#[cfg(feature = "vergen")]
75mod vergen {
76    use std::path::PathBuf;
77
78    use anyhow::{Context, Result};
79
80    /// Emits environment variables for build metadata intended for extended version information.
81    ///
82    /// The following environment variables are emitted:
83    ///
84    ///   - `VERGEN_GIT_BRANCH`
85    ///   - `VERGEN_GIT_SHA`
86    ///   - `VERGEN_GIT_DIRTY`
87    ///   - `VERGEN_RUSTC_SEMVER`
88    ///   - `VERGEN_RUSTC_HOST_TRIPLE`
89    ///   - `VERGEN_CARGO_TARGET_TRIPLE`
90    ///   - `VERGEN_CARGO_FEATURES`
91    ///   - `VERGEN_CARGO_OPT_LEVEL`
92    ///   - `VERGEN_CARGO_DEBUG`
93    pub fn vergen() -> Result<()> {
94        if let Some(sha) = published_git_sha().context("Checking for published vcs info")? {
95            // git data is not available if in a published state, so we set them manually.
96            println!("cargo::rustc-env=VERGEN_GIT_SHA={sha}");
97            println!("cargo::rustc-env=VERGEN_GIT_BRANCH=NA (published)");
98            println!("cargo::rustc-env=VERGEN_GIT_DIRTY=");
99
100            vergen_gitcl::Emitter::new()
101        } else {
102            // In a non-published state so we can expect git instructions to work.
103            let mut emitter = vergen_gitcl::Emitter::new();
104            emitter
105                .add_instructions(&git_instructions()?)
106                .context("Adding git instructions")?;
107
108            emitter
109        }
110        .add_instructions(&cargo_instructions()?)
111        .context("Adding cargo instructions")?
112        .add_instructions(&rustc_instructions()?)
113        .context("Adding rustc instructions")?
114        .emit()
115    }
116
117    /// Normal git info is lost on `cargo publish`, which instead adds a file containing the SHA1
118    /// hash.
119    ///
120    /// This function returns the short SHA value. If present, this indicates this we're in a
121    /// published state.
122    fn published_git_sha() -> Result<Option<String>> {
123        let cargo_vcs_info = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(".cargo_vcs_info.json");
124        if cargo_vcs_info.exists() {
125            // The file is small so reading to string is acceptable.
126            let contents = std::fs::read_to_string(cargo_vcs_info).context("Reading vcs info")?;
127
128            // File format:
129            // {
130            //   "git": {
131            //     "sha1": "9d48046e9654d93a86212e77d6c92f14c95de44b"
132            //   },
133            //   "path_in_vcs": "bin/node"
134            // }
135            let offset = contents.find(r#""sha1""#).context("Searching for sha1 property")?
136                + r#""sha1""#.len();
137
138            let sha1 = contents[offset + 1..]
139            .chars()
140            // Find and skip opening quote.
141            .skip_while(|&c| c != '"')
142            .skip(1)
143            // Take until closing quote.
144            .take_while(|&c| c != '"')
145            // Short SHA format is 7 digits.
146            .take(7)
147            .collect();
148
149            Ok(Some(sha1))
150        } else {
151            Ok(None)
152        }
153    }
154
155    fn git_instructions() -> Result<vergen_gitcl::Gitcl> {
156        const INCLUDE_UNTRACKED: bool = true;
157        const SHORT_SHA: bool = true;
158
159        vergen_gitcl::GitclBuilder::default()
160            .branch(true)
161            .dirty(INCLUDE_UNTRACKED)
162            .sha(SHORT_SHA)
163            .build()
164            .context("Building git instructions")
165    }
166
167    fn cargo_instructions() -> Result<vergen::Cargo> {
168        vergen_gitcl::CargoBuilder::default()
169            .debug(true)
170            .features(true)
171            .target_triple(true)
172            .opt_level(true)
173            .build()
174            .context("Building git instructions")
175    }
176
177    fn rustc_instructions() -> Result<vergen::Rustc> {
178        vergen_gitcl::RustcBuilder::default()
179            .semver(true)
180            .host_triple(true)
181            .build()
182            .context("Building rustc instructions")
183    }
184}