vergen-8.3.2/.cargo_vcs_info.json0000644000000001440000000000100123330ustar { "git": { "sha1": "ef4d0cdf4ccbc61c3972a728ce842b48cd67a9e8" }, "path_in_vcs": "vergen" }vergen-8.3.2/Cargo.toml0000644000000061350000000000100103370ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "vergen" version = "8.3.2" authors = ["Jason Ozias "] build = "build.rs" autobins = false autoexamples = false autotests = false autobenches = false description = "Generate 'cargo:rustc-env' instructions via 'build.rs' for use in your code via the 'env!' macro" homepage = "https://github.com/rustyhorde/vergen" documentation = "https://docs.rs/vergen" readme = "README.md" keywords = [ "cargo", "instructions", "build", "tool", ] categories = [ "development-tools", "development-tools::build-utils", ] license = "MIT OR Apache-2.0" repository = "https://github.com/rustyhorde/vergen" [package.metadata.cargo-all-features] denylist = [ "cargo_metadata", "cfg-if", "git", "git2", "git2-rs", "gitcl", "gix", "gitoxide", "regex", "rustc_version", "sysinfo", "time", "unstable", ] [package.metadata.docs.rs] features = [ "build", "cargo", "git", "gitcl", "rustc", "si", ] rustdoc-args = [ "--cfg", "docsrs", ] [lib] name = "vergen" path = "src/lib.rs" [[test]] name = "build_output" path = "tests/build_output.rs" [[test]] name = "cargo_output" path = "tests/cargo_output.rs" [[test]] name = "git_output" path = "tests/git_output.rs" [[test]] name = "rustc_output" path = "tests/rustc_output.rs" [[test]] name = "sysinfo_output" path = "tests/sysinfo_output.rs" [dependencies.anyhow] version = "1.0.72" [dependencies.cargo_metadata] version = "0.18.1" optional = true [dependencies.cfg-if] version = "1.0.0" optional = true [dependencies.git2-rs] version = "0.19.0" optional = true default-features = false package = "git2" [dependencies.gix] version = "0.63.0" features = [ "revision", "interrupt", ] optional = true default-features = false [dependencies.regex] version = "1.9.1" optional = true [dependencies.rustc_version] version = "0.4.0" optional = true [dependencies.sysinfo] version = "0.30.4" optional = true default-features = false [dependencies.time] version = "0.3.23" features = [ "formatting", "local-offset", "parsing", ] optional = true [dev-dependencies.gix] version = "0.63.0" [dev-dependencies.lazy_static] version = "1.4.0" [dev-dependencies.regex] version = "1.9.1" [dev-dependencies.serial_test] version = "3.0.0" [dev-dependencies.temp-env] version = "0.3.6" [build-dependencies.rustversion] version = "1.0.14" [features] build = ["time"] cargo = [ "cargo_metadata", "regex", ] default = [] git = ["cfg-if"] git2 = [ "time", "git2-rs", ] gitcl = ["time"] gitoxide = [ "time", "gix", ] rustc = ["rustc_version"] si = ["sysinfo"] unstable = [] vergen-8.3.2/Cargo.toml.orig000064400000000000000000000036741046102023000140250ustar 00000000000000[package] authors = ["Jason Ozias "] categories = ["development-tools", "development-tools::build-utils"] description = "Generate 'cargo:rustc-env' instructions via 'build.rs' for use in your code via the 'env!' macro" documentation = "https://docs.rs/vergen" edition = "2021" homepage = "https://github.com/rustyhorde/vergen" keywords = ["cargo", "instructions", "build", "tool"] license = "MIT OR Apache-2.0" name = "vergen" readme = "README.md" repository = "https://github.com/rustyhorde/vergen" version = "8.3.2" [package.metadata.cargo-all-features] denylist = [ "cargo_metadata", "cfg-if", "git", "git2", "git2-rs", "gitcl", "gix", "gitoxide", "regex", "rustc_version", "sysinfo", "time", "unstable", ] [features] default = [] build = ["time"] cargo = ["cargo_metadata", "regex"] git = ["cfg-if"] gitcl = ["time"] git2 = ["time", "git2-rs"] gitoxide = ["time", "gix"] rustc = ["rustc_version"] unstable = [] si = ["sysinfo"] [dependencies] anyhow = "1.0.72" cargo_metadata = { version = "0.18.1", optional = true } cfg-if = { version = "1.0.0", optional = true } git2-rs = { version = "0.19.0", package = "git2", optional = true, default-features = false } gix = { version = "0.63.0", optional = true, default-features = false, features = [ "revision", "interrupt", ] } regex = { version = "1.9.1", optional = true } rustc_version = { version = "0.4.0", optional = true } sysinfo = { version = "0.30.4", optional = true, default-features = false } time = { version = "0.3.23", features = [ "formatting", "local-offset", "parsing", ], optional = true } [build-dependencies] rustversion = "1.0.14" [dev-dependencies] gix = "0.63.0" lazy_static = "1.4.0" regex = "1.9.1" repo_util = { path = "../repo_util" } serial_test = "3.0.0" temp-env = "0.3.6" [package.metadata.docs.rs] features = ["build", "cargo", "git", "gitcl", "rustc", "si"] rustdoc-args = ["--cfg", "docsrs"] vergen-8.3.2/LICENSE-APACHE000064400000000000000000000251411046102023000130530ustar 00000000000000 Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. vergen-8.3.2/LICENSE-MIT000064400000000000000000000020571046102023000125640ustar 00000000000000Copyright (c) 2016 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. vergen-8.3.2/README.md000064400000000000000000000153271046102023000124130ustar 00000000000000# vergen - Emit cargo instructions from a build script `vergen`, when used in conjunction with cargo [build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script) can emit the following: - Will emit [`cargo:rustc-env=VAR=VALUE`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-envvarvalue) for each feature you have enabled. These can be referenced with the [`env!`](https://doc.rust-lang.org/std/macro.env.html) macro in your code. - Will emit [`cargo:rerun-if-changed=.git/HEAD`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed) if the git feature is enabled. This is done to ensure any git instructions are regenerated when commits are made. - Will emit [`cargo:rerun-if-changed=.git/`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed) if the git feature is enabled. This is done to ensure any git instructions are regenerated when commits are made. - Can emit [`cargo:warning`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargo-warning) outputs if the [`fail_on_error`](EmitBuilder::fail_on_error) feature is not enabled and the requested variable is defaulted through error or the [`idempotent`](EmitBuilder::idempotent) flag. - Will emit [`cargo:rerun-if-changed=build.rs`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed) to rerun instruction emission if the `build.rs` file changed. - Will emit [`cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed) to rerun instruction emission if the `VERGEN_IDEMPOTENT` environment variable has changed. - Will emit [`cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed) to rerun instruction emission if the `SOURCE_DATE_EPOCH` environment variable has changed. ## Current Release [![docs.rs](https://docs.rs/vergen/badge.svg)](https://docs.rs/vergen) [![Crates.io](https://img.shields.io/crates/v/vergen.svg)](https://crates.io/crates/vergen) [![Crates.io](https://img.shields.io/crates/l/vergen.svg)](https://crates.io/crates/vergen) [![Crates.io](https://img.shields.io/crates/d/vergen.svg)](https://crates.io/crates/vergen) [![codecov](https://codecov.io/gh/rustyhorde/vergen/branch/master/graph/badge.svg?token=cBXro7o2UN)](https://codecov.io/gh/rustyhorde/vergen) [![CI](https://github.com/rustyhorde/vergen/actions/workflows/main.yml/badge.svg)](https://github.com/rustyhorde/vergen/actions) [![sponsor](https://img.shields.io/github/sponsors/crazysacx?logo=github-sponsors)](https://github.com/sponsors/CraZySacX) ## MSRV The current minimum supported rust version is 1.70.0 ## Example Usage See the documentation at [docs.rs](https://docs.rs/vergen/latest/vergen/) for example usage ## `Cargo` feature unification for `vergen` versions prior to 8.3.0 When a dependency is used by multiple packages, Cargo will [use the union](https://doc.rust-lang.org/cargo/reference/features.html#feature-unification) of all features enabled on that dependency when building it. Prior to version **8.3.0**, `vergen` had a set of mutually exclusive features `gitcl`, `git2`, and `gitoxide` to enable to specific git backend you wished to use. If your crate has a dependency on another crate that uses `vergen`, your crate may fail to compile if you select a different `git` backend then the crate you depend on. For example, your crate depends on `fancy-lib`. #### fancy-lib `Cargo.toml` ```toml [build-dependencies] vergen = { version = "8.2.10", features = ["git","gitcl"] } ``` #### your crate `Cargo.toml` ```toml [dependencies] fancy-lib = "0.1.0" [build-dependencies] vergen = { version = "8.2.10", features = ["git","gitoxide"] } ``` Your crate will fail to compile because `cargo` unifies this to ```toml vergen = { version = "8.2.10", features = ["git","gitcl","gitoxide"] } ``` and prior to **8.3.0** `vergen` will not compile with both `gitcl` and `gitoxide` as features. As a workaround, you can use `cargo tree -f "{p} {f}" | grep vergen` to determine the feature list cargo has set for `vergen`. If a `git` backend has already been determined you will be able to use that without declaring those features in your dependency list. This is not perfect as this leaves you at the mercy of your dependency and the git feature they selected, but it's a workaround until version 9 comes out. #### fancy-lib `Cargo.toml` ```toml [build-dependencies] vergen = { version = "8.2.10", features = ["git","gitcl"] } ``` #### your crate `Cargo.toml` ```toml [dependencies] fancy-lib = "0.1.0" [build-dependencies] vergen = "8.2.10" ``` #### Unified ```toml vergen = { version = "8.2.10", features = ["git","gitcl"] } ``` ## `Cargo` feature unification for `vergen` versions 8.3.0 and beyond `vergen` will accept `gitcl`,`git2`, and `gitoxide` as features. If more than one of them is included, `vergen` will select `gitcl` before `git2` and `git2` before `gitoxide`. ## Notes about the optional `git2` dependency `git2` picked up some [security related features](https://github.blog/2022-04-12-git-security-vulnerability-announced/). In docker environments especially, this requires a `safe.directory` configuration. There are a couple methods for achieving this. 1. If you control the build, you can add `git config --global --add safe.directory /workspace` to the build file. 2. If you do not control the docker build, you can add `git config --global --add safe.directory /workspace &&` before the actual command you are running when using docker run. 3. If you do not control the docker build, you can mount a `.gitconfig` file at `/root` that includes the `safe.directory` configuration. I use this method myself when building static binaries with clux/muslrust. ````docker run -v cargo-cache:/root/.cargo/registry -v (pwd):/volume -v ~/.gitconfig:/root/.gitconfig:ro --rm -t clux/muslrust:stable cargo build --release```` See https://github.com/rustyhorde/vergen/pull/126 for more discussion on the topic. If the solutions above do not work for your usecase, you can pin your `vergen` version to 7.4.3, with the caveat you will be exposed to the issues described at the link above. ## Contributing See the documentation at [CONTRIBUTING.md](CONTRIBUTING.md) ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. vergen-8.3.2/build.rs000064400000000000000000000015131046102023000125710ustar 00000000000000pub fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rustc-check-cfg=cfg(coverage_nightly)"); nightly(); beta(); stable(); } #[rustversion::nightly] fn nightly() { println!("cargo:rustc-check-cfg=cfg(nightly)"); println!("cargo:rustc-cfg=nightly"); } #[rustversion::not(nightly)] fn nightly() { println!("cargo:rustc-check-cfg=cfg(nightly)"); } #[rustversion::beta] fn beta() { println!("cargo:rustc-check-cfg=cfg(beta)"); println!("cargo:rustc-cfg=beta"); } #[rustversion::not(beta)] fn beta() { println!("cargo:rustc-check-cfg=cfg(beta)"); } #[rustversion::stable] fn stable() { println!("cargo:rustc-check-cfg=cfg(stable)"); println!("cargo:rustc-cfg=stable"); } #[rustversion::not(stable)] fn stable() { println!("cargo:rustc-check-cfg=cfg(stable)"); } vergen-8.3.2/src/constants.rs000064400000000000000000000202061046102023000142750ustar 00000000000000// Copyright (c) 2022 vergen developers // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. //! Internal Constants // Idempotent Constant #[cfg(any( feature = "build", feature = "cargo", all( feature = "git", any(feature = "git2", feature = "gitcl", feature = "gix") ), feature = "rustc", feature = "si", ))] pub(crate) const VERGEN_IDEMPOTENT_DEFAULT: &str = "VERGEN_IDEMPOTENT_OUTPUT"; #[cfg(any( feature = "build", feature = "cargo", all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ), feature = "rustc", feature = "si" ))] pub(crate) use self::features::*; #[cfg(any( feature = "build", feature = "cargo", all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ), feature = "rustc", feature = "si" ))] mod features { // Build Constants #[cfg(feature = "build")] pub(crate) const BUILD_TIMESTAMP_NAME: &str = "VERGEN_BUILD_TIMESTAMP"; #[cfg(feature = "build")] pub(crate) const BUILD_DATE_NAME: &str = "VERGEN_BUILD_DATE"; // git Constants #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] pub(crate) const GIT_BRANCH_NAME: &str = "VERGEN_GIT_BRANCH"; #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] pub(crate) const GIT_COMMIT_AUTHOR_EMAIL: &str = "VERGEN_GIT_COMMIT_AUTHOR_EMAIL"; #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] pub(crate) const GIT_COMMIT_AUTHOR_NAME: &str = "VERGEN_GIT_COMMIT_AUTHOR_NAME"; #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] pub(crate) const GIT_COMMIT_COUNT: &str = "VERGEN_GIT_COMMIT_COUNT"; #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] pub(crate) const GIT_COMMIT_MESSAGE: &str = "VERGEN_GIT_COMMIT_MESSAGE"; #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] pub(crate) const GIT_COMMIT_DATE_NAME: &str = "VERGEN_GIT_COMMIT_DATE"; #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] pub(crate) const GIT_COMMIT_TIMESTAMP_NAME: &str = "VERGEN_GIT_COMMIT_TIMESTAMP"; #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] pub(crate) const GIT_DESCRIBE_NAME: &str = "VERGEN_GIT_DESCRIBE"; #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] pub(crate) const GIT_SHA_NAME: &str = "VERGEN_GIT_SHA"; #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] pub(crate) const GIT_DIRTY_NAME: &str = "VERGEN_GIT_DIRTY"; // rustc Constants #[cfg(feature = "rustc")] pub(crate) const RUSTC_CHANNEL_NAME: &str = "VERGEN_RUSTC_CHANNEL"; #[cfg(feature = "rustc")] pub(crate) const RUSTC_HOST_TRIPLE_NAME: &str = "VERGEN_RUSTC_HOST_TRIPLE"; #[cfg(feature = "rustc")] pub(crate) const RUSTC_SEMVER_NAME: &str = "VERGEN_RUSTC_SEMVER"; #[cfg(feature = "rustc")] pub(crate) const RUSTC_COMMIT_HASH: &str = "VERGEN_RUSTC_COMMIT_HASH"; #[cfg(feature = "rustc")] pub(crate) const RUSTC_COMMIT_DATE: &str = "VERGEN_RUSTC_COMMIT_DATE"; #[cfg(feature = "rustc")] pub(crate) const RUSTC_LLVM_VERSION: &str = "VERGEN_RUSTC_LLVM_VERSION"; // cargo Constants #[cfg(feature = "cargo")] pub(crate) const CARGO_DEBUG: &str = "VERGEN_CARGO_DEBUG"; #[cfg(feature = "cargo")] pub(crate) const CARGO_DEPENDENCIES: &str = "VERGEN_CARGO_DEPENDENCIES"; #[cfg(feature = "cargo")] pub(crate) const CARGO_FEATURES: &str = "VERGEN_CARGO_FEATURES"; #[cfg(feature = "cargo")] pub(crate) const CARGO_OPT_LEVEL: &str = "VERGEN_CARGO_OPT_LEVEL"; #[cfg(feature = "cargo")] pub(crate) const CARGO_TARGET_TRIPLE: &str = "VERGEN_CARGO_TARGET_TRIPLE"; // sysinfo Constants #[cfg(feature = "si")] pub(crate) const SYSINFO_NAME: &str = "VERGEN_SYSINFO_NAME"; #[cfg(feature = "si")] pub(crate) const SYSINFO_OS_VERSION: &str = "VERGEN_SYSINFO_OS_VERSION"; #[cfg(feature = "si")] pub(crate) const SYSINFO_USER: &str = "VERGEN_SYSINFO_USER"; #[cfg(feature = "si")] pub(crate) const SYSINFO_MEMORY: &str = "VERGEN_SYSINFO_TOTAL_MEMORY"; #[cfg(feature = "si")] pub(crate) const SYSINFO_CPU_VENDOR: &str = "VERGEN_SYSINFO_CPU_VENDOR"; #[cfg(feature = "si")] pub(crate) const SYSINFO_CPU_CORE_COUNT: &str = "VERGEN_SYSINFO_CPU_CORE_COUNT"; #[cfg(feature = "si")] pub(crate) const SYSINFO_CPU_NAME: &str = "VERGEN_SYSINFO_CPU_NAME"; #[cfg(feature = "si")] pub(crate) const SYSINFO_CPU_BRAND: &str = "VERGEN_SYSINFO_CPU_BRAND"; #[cfg(feature = "si")] pub(crate) const SYSINFO_CPU_FREQUENCY: &str = "VERGEN_SYSINFO_CPU_FREQUENCY"; } #[cfg(all( test, any( feature = "build", feature = "cargo", all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ) ), feature = "rustc", feature = "si" ))] mod test { use super::*; #[cfg(feature = "build")] #[test] fn build_constants_dont_change() { // Build Constants assert_eq!(BUILD_TIMESTAMP_NAME, "VERGEN_BUILD_TIMESTAMP"); assert_eq!(BUILD_DATE_NAME, "VERGEN_BUILD_DATE"); } #[cfg(feature = "cargo")] #[test] fn cargo_constants_dont_change() { // cargo Constants assert_eq!(CARGO_TARGET_TRIPLE, "VERGEN_CARGO_TARGET_TRIPLE"); assert_eq!(CARGO_FEATURES, "VERGEN_CARGO_FEATURES"); } #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] #[test] fn git_constants_dont_change() { // git Constants assert_eq!(GIT_BRANCH_NAME, "VERGEN_GIT_BRANCH"); assert_eq!(GIT_COMMIT_AUTHOR_EMAIL, "VERGEN_GIT_COMMIT_AUTHOR_EMAIL"); assert_eq!(GIT_COMMIT_AUTHOR_NAME, "VERGEN_GIT_COMMIT_AUTHOR_NAME"); assert_eq!(GIT_COMMIT_COUNT, "VERGEN_GIT_COMMIT_COUNT"); assert_eq!(GIT_COMMIT_MESSAGE, "VERGEN_GIT_COMMIT_MESSAGE"); assert_eq!(GIT_COMMIT_DATE_NAME, "VERGEN_GIT_COMMIT_DATE"); assert_eq!(GIT_COMMIT_TIMESTAMP_NAME, "VERGEN_GIT_COMMIT_TIMESTAMP"); assert_eq!(GIT_DESCRIBE_NAME, "VERGEN_GIT_DESCRIBE"); assert_eq!(GIT_SHA_NAME, "VERGEN_GIT_SHA"); assert_eq!(GIT_DIRTY_NAME, "VERGEN_GIT_DIRTY"); } #[cfg(feature = "rustc")] #[test] fn rustc_constants_dont_change() { // rustc Constants assert_eq!(RUSTC_SEMVER_NAME, "VERGEN_RUSTC_SEMVER"); assert_eq!(RUSTC_CHANNEL_NAME, "VERGEN_RUSTC_CHANNEL"); assert_eq!(RUSTC_HOST_TRIPLE_NAME, "VERGEN_RUSTC_HOST_TRIPLE"); assert_eq!(RUSTC_COMMIT_HASH, "VERGEN_RUSTC_COMMIT_HASH"); assert_eq!(RUSTC_COMMIT_DATE, "VERGEN_RUSTC_COMMIT_DATE"); assert_eq!(RUSTC_LLVM_VERSION, "VERGEN_RUSTC_LLVM_VERSION"); } #[cfg(feature = "si")] #[test] fn sysinfo_constants_dont_change() { // sysinfo Constants assert_eq!(SYSINFO_NAME, "VERGEN_SYSINFO_NAME"); assert_eq!(SYSINFO_OS_VERSION, "VERGEN_SYSINFO_OS_VERSION"); assert_eq!(SYSINFO_USER, "VERGEN_SYSINFO_USER"); assert_eq!(SYSINFO_MEMORY, "VERGEN_SYSINFO_TOTAL_MEMORY"); assert_eq!(SYSINFO_CPU_VENDOR, "VERGEN_SYSINFO_CPU_VENDOR"); assert_eq!(SYSINFO_CPU_CORE_COUNT, "VERGEN_SYSINFO_CPU_CORE_COUNT"); assert_eq!(SYSINFO_CPU_NAME, "VERGEN_SYSINFO_CPU_NAME"); assert_eq!(SYSINFO_CPU_BRAND, "VERGEN_SYSINFO_CPU_BRAND"); assert_eq!(SYSINFO_CPU_FREQUENCY, "VERGEN_SYSINFO_CPU_FREQUENCY"); } } vergen-8.3.2/src/emitter.rs000064400000000000000000000774231046102023000137470ustar 00000000000000// Copyright (c) 2022 vergen developers // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. use anyhow::Result; use std::{ env, io::{self, Write}, }; #[cfg(any( feature = "build", feature = "cargo", feature = "git", feature = "rustc", feature = "si" ))] use {crate::key::VergenKey, std::collections::BTreeMap}; #[cfg(feature = "build")] use crate::feature::build::Config as BuildConfig; #[cfg(feature = "cargo")] use crate::feature::cargo::Config as CargoConfig; #[cfg(all( feature = "git", any(feature = "git2", feature = "gitcl", feature = "gix") ))] use crate::feature::git::Config as GitConfig; #[cfg(feature = "rustc")] use crate::feature::rustc::Config as RustcConfig; #[cfg(feature = "si")] use crate::feature::si::Config as SysinfoConfig; use std::path::PathBuf; #[cfg(any( feature = "build", feature = "cargo", feature = "git", feature = "rustc", feature = "si" ))] pub(crate) type RustcEnvMap = BTreeMap; // Everything that can be emitted as cargo build instructions #[derive(Clone, Debug, Default)] pub(crate) struct Emitter { pub(crate) failed: bool, #[cfg(any( feature = "build", feature = "cargo", feature = "git", feature = "rustc", feature = "si" ))] pub(crate) cargo_rustc_env_map: RustcEnvMap, #[cfg(any( feature = "build", feature = "cargo", feature = "git", feature = "rustc", feature = "si" ))] pub(crate) rerun_if_changed: Vec, #[cfg(any( feature = "build", feature = "cargo", feature = "git", feature = "rustc", feature = "si" ))] pub(crate) warnings: Vec, } impl Emitter { #[cfg(feature = "build")] fn add_build_entries(&mut self, builder: &EmitBuilder) -> Result<()> { let idem = builder.idempotent; let fail_on_error = builder.fail_on_error; let mut empty = BTreeMap::new(); let cargo_rustc_env_map = if builder.disable_build { &mut empty } else { &mut self.cargo_rustc_env_map }; builder .add_build_map_entries(idem, cargo_rustc_env_map, &mut self.warnings) .or_else(|e| { builder.add_build_default(e, fail_on_error, cargo_rustc_env_map, &mut self.warnings) }) } #[cfg(not(feature = "build"))] #[allow( clippy::unnecessary_wraps, clippy::trivially_copy_pass_by_ref, clippy::unused_self )] fn add_build_entries(&mut self, _builder: &EmitBuilder) -> Result<()> { Ok(()) } #[cfg(feature = "cargo")] fn add_cargo_entries(&mut self, builder: &EmitBuilder) -> Result<()> { let fail_on_error = builder.fail_on_error; let mut empty = BTreeMap::new(); let cargo_rustc_env_map = if builder.disable_cargo { &mut empty } else { &mut self.cargo_rustc_env_map }; builder .add_cargo_map_entries(cargo_rustc_env_map) .or_else(|e| { builder.add_cargo_default(e, fail_on_error, cargo_rustc_env_map, &mut self.warnings) }) } #[cfg(not(feature = "cargo"))] #[allow( clippy::unnecessary_wraps, clippy::trivially_copy_pass_by_ref, clippy::unused_self )] fn add_cargo_entries(&mut self, _builder: &EmitBuilder) -> Result<()> { Ok(()) } #[cfg(all( feature = "git", any(feature = "git2", feature = "gitcl", feature = "gix") ))] fn add_git_entries(&mut self, builder: &EmitBuilder, repo_path: Option) -> Result<()> { let idem = builder.idempotent; let fail_on_error = builder.fail_on_error; let mut empty_cargo_rustc_env_map = BTreeMap::new(); let mut empty_rerun_if_changed = vec![]; let mut empty_warnings = vec![]; let (cargo_rustc_env_map, rerun_if_changed, warnings) = if builder.disable_git { ( &mut empty_cargo_rustc_env_map, &mut empty_rerun_if_changed, &mut empty_warnings, ) } else { ( &mut self.cargo_rustc_env_map, &mut self.rerun_if_changed, &mut self.warnings, ) }; builder .add_git_map_entries( repo_path, idem, cargo_rustc_env_map, warnings, rerun_if_changed, ) .or_else(|e| { self.failed = true; builder.add_git_default( e, fail_on_error, cargo_rustc_env_map, warnings, rerun_if_changed, ) }) } #[cfg(not(all( feature = "git", any(feature = "git2", feature = "gitcl", feature = "gix") )))] #[allow( clippy::unnecessary_wraps, clippy::trivially_copy_pass_by_ref, clippy::unused_self, clippy::needless_pass_by_value )] fn add_git_entries(&mut self, _builder: &EmitBuilder, _path: Option) -> Result<()> { Ok(()) } #[cfg(feature = "rustc")] fn add_rustc_entries(&mut self, builder: &EmitBuilder) -> Result<()> { let fail_on_error = builder.fail_on_error; let mut empty = BTreeMap::new(); let cargo_rustc_env_map = if builder.disable_rustc { &mut empty } else { &mut self.cargo_rustc_env_map }; builder .add_rustc_map_entries(cargo_rustc_env_map, &mut self.warnings) .or_else(|e| { builder.add_rustc_default(e, fail_on_error, cargo_rustc_env_map, &mut self.warnings) }) } #[cfg(not(feature = "rustc"))] #[allow( clippy::unnecessary_wraps, clippy::trivially_copy_pass_by_ref, clippy::unused_self )] fn add_rustc_entries(&mut self, _builder: &EmitBuilder) -> Result<()> { Ok(()) } #[cfg(feature = "si")] fn add_si_entries(&mut self, builder: &EmitBuilder) { let idem = builder.idempotent; let mut empty = BTreeMap::new(); let cargo_rustc_env_map = if builder.disable_sysinfo { &mut empty } else { &mut self.cargo_rustc_env_map }; builder.add_sysinfo_map_entries(idem, cargo_rustc_env_map, &mut self.warnings); } #[cfg(not(feature = "si"))] #[allow( clippy::unnecessary_wraps, clippy::trivially_copy_pass_by_ref, clippy::unused_self )] fn add_si_entries(&mut self, _builder: &EmitBuilder) {} fn emit_output( &self, quiet: bool, custom_buildrs: Option<&'static str>, stdout: &mut T, ) -> Result<()> where T: Write, { self.emit_instructions(quiet, custom_buildrs, stdout) } #[cfg(not(any( feature = "build", feature = "cargo", feature = "git", feature = "rustc", feature = "si" )))] #[allow(clippy::unused_self, clippy::unnecessary_wraps)] fn emit_instructions( &self, _quiet: bool, _custom_buildrs: Option<&'static str>, _stdout: &mut T, ) -> Result<()> where T: Write, { Ok(()) } #[cfg(any( feature = "build", feature = "cargo", feature = "git", feature = "rustc", feature = "si" ))] fn emit_instructions( &self, quiet: bool, custom_buildrs: Option<&'static str>, stdout: &mut T, ) -> Result<()> where T: Write, { // Emit the 'cargo:rustc-env' instructions for (k, v) in &self.cargo_rustc_env_map { let output = Self::filter_newlines(v); writeln!(stdout, "cargo:rustc-env={}={output}", k.name())?; } // Emit the `cargo:warning` instructions if !quiet { for warning in &self.warnings { let output = Self::filter_newlines(warning); writeln!(stdout, "cargo:warning={output}")?; } } // Emit the 'cargo:rerun-if-changed' instructions for the git paths (if added) for path in &self.rerun_if_changed { let output = Self::filter_newlines(path); writeln!(stdout, "cargo:rerun-if-changed={output}")?; } // Emit the 'cargo:rerun-if-changed' instructions if !self.cargo_rustc_env_map.is_empty() || !self.warnings.is_empty() { let buildrs = custom_buildrs.unwrap_or("build.rs"); let output = Self::filter_newlines(buildrs); writeln!(stdout, "cargo:rerun-if-changed={output}")?; writeln!(stdout, "cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT")?; writeln!(stdout, "cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH")?; } Ok(()) } #[cfg(any( feature = "build", feature = "cargo", feature = "git", feature = "rustc", feature = "si" ))] fn filter_newlines(s: &str) -> String { s.chars().filter(|c| *c != '\n').collect() } } /// Build the `vergen` configuration to enable specific cargo instruction /// output #[derive(Clone, Copy, Debug)] #[allow(clippy::struct_excessive_bools)] pub struct EmitBuilder { idempotent: bool, fail_on_error: bool, quiet: bool, custom_buildrs: Option<&'static str>, #[cfg(feature = "build")] disable_build: bool, #[cfg(feature = "build")] pub(crate) build_config: BuildConfig, #[cfg(feature = "cargo")] disable_cargo: bool, #[cfg(feature = "cargo")] pub(crate) cargo_config: CargoConfig, #[cfg(all( feature = "git", any(feature = "git2", feature = "gitcl", feature = "gix") ))] disable_git: bool, #[cfg(all( feature = "git", any(feature = "git2", feature = "gitcl", feature = "gix") ))] pub(crate) git_config: GitConfig, #[cfg(feature = "rustc")] disable_rustc: bool, #[cfg(feature = "rustc")] pub(crate) rustc_config: RustcConfig, #[cfg(feature = "si")] disable_sysinfo: bool, #[cfg(feature = "si")] pub(crate) sysinfo_config: SysinfoConfig, } impl EmitBuilder { /// Instantiate the builder to configure the cargo instruction emits #[must_use] pub fn builder() -> Self { let idempotent = matches!(env::var("VERGEN_IDEMPOTENT"), Ok(_val)); Self { idempotent, fail_on_error: false, quiet: false, custom_buildrs: None, #[cfg(feature = "build")] disable_build: false, #[cfg(feature = "build")] build_config: BuildConfig::default(), #[cfg(feature = "cargo")] disable_cargo: false, #[cfg(feature = "cargo")] cargo_config: CargoConfig::default(), #[cfg(all( feature = "git", any(feature = "git2", feature = "gitcl", feature = "gix") ))] disable_git: false, #[cfg(all( feature = "git", any(feature = "git2", feature = "gitcl", feature = "gix") ))] git_config: GitConfig::default(), #[cfg(feature = "rustc")] disable_rustc: false, #[cfg(feature = "rustc")] rustc_config: RustcConfig::default(), #[cfg(feature = "si")] disable_sysinfo: false, #[cfg(feature = "si")] sysinfo_config: SysinfoConfig::default(), } } /// Enable the `idempotent` feature /// /// **NOTE** - This feature can also be enabled via the `VERGEN_IDEMPOTENT` /// environment variable. /// /// When this feature is enabled, certain vergen output (i.e. timestamps, sysinfo) /// will be set to an idempotent default. This will allow systems that /// depend on deterministic builds to override user requested `vergen` /// impurities. This will mainly allow for package maintainers to build /// packages that depend on `vergen` in a deterministic manner. /// /// See [this issue](https://github.com/rustyhorde/vergen/issues/141) for /// more details /// /// | Variable | Sample | /// | ------- | ------ | /// | `VERGEN_BUILD_DATE` | `VERGEN_IDEMPOTENT_OUTPUT` | /// | `VERGEN_BUILD_TIMESTAMP` | `VERGEN_IDEMPOTENT_OUTPUT` | /// /// # Example /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( feature = "build", doc = r##" EmitBuilder::builder().idempotent().all_build().emit()?; "## )] /// // or /// env::set_var("VERGEN_IDEMPOTENT", "true"); #[cfg_attr( feature = "build", doc = r##" EmitBuilder::builder().all_build().emit()?; "## )] /// # env::remove_var("VERGEN_IDEMPOTENT"); /// # Ok(()) /// # } /// ``` /// pub fn idempotent(&mut self) -> &mut Self { self.idempotent = true; self } /// Enable the `fail_on_error` feature /// /// By default `vergen` will emit the instructions you requested. If for some /// reason those instructions cannot be generated correctly, placeholder values /// will be used instead. `vergen` will also emit [`cargo:warning`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargo-warning) /// instructions notifying you this has happened. /// /// For example, if you configure `vergen` to emit `VERGEN_GIT_*` instructions and /// you run a build from a source tarball with no `.git` directory, the instructions /// will be populated with placeholder values, rather than information gleaned through git. /// /// You can turn off this behavior by enabling `fail_on_error`. /// /// # Example /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( feature = "build", doc = r##" EmitBuilder::builder().fail_on_error().all_build().emit()?; "## )] /// # Ok(()) /// # } /// ``` pub fn fail_on_error(&mut self) -> &mut Self { self.fail_on_error = true; self } /// Set a custom build.rs path if you are using a non-standard path /// /// By default `vergen` will use `build.rs` as the build path for the /// `cargo:rerun-if-changed` emit. You can specify a custom `build.rs` /// path here if you have changed this default /// /// # Example /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( feature = "build", doc = r##" EmitBuilder::builder().custom_build_rs("my/custom/build.rs").all_build().emit()?; "## )] /// # Ok(()) /// # } /// ``` pub fn custom_build_rs(&mut self, path: &'static str) -> &mut Self { self.custom_buildrs = Some(path); self } /// Enable the quiet feature /// /// Suppress the emission of the `cargo:warning` instructions. /// /// # Example /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( feature = "build", doc = r##" EmitBuilder::builder().quiet().all_build().emit()?; "## )] /// # Ok(()) /// # } /// ``` pub fn quiet(&mut self) -> &mut Self { self.quiet = true; self } /// Disable the build output, even when the build feature is enabled. /// /// # Example /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( feature = "build", doc = r##" EmitBuilder::builder().all_build().disable_build().emit()?; "## )] /// # Ok(()) /// # } /// ``` #[cfg(feature = "build")] pub fn disable_build(&mut self) -> &mut Self { self.disable_build = true; self } /// Disable the cargo output, even when the cargo feature is enabled. /// /// # Example /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( feature = "cargo", doc = r##" EmitBuilder::builder().all_cargo().disable_cargo().emit()?; "## )] /// # Ok(()) /// # } /// ``` #[cfg(feature = "cargo")] pub fn disable_cargo(&mut self) -> &mut Self { self.disable_cargo = true; self } /// Disable the git output, even when the git feature is enabled. /// /// # Example /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( feature = "git", doc = r##" EmitBuilder::builder().all_git().disable_git().emit()?; "## )] /// # Ok(()) /// # } /// ``` #[cfg(all( feature = "git", any(feature = "git2", feature = "gitcl", feature = "gix") ))] pub fn disable_git(&mut self) -> &mut Self { self.disable_git = true; self } /// Disable the rustc output, even when the rustc feature is enabled. /// /// # Example /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( feature = "rustc", doc = r##" EmitBuilder::builder().all_rustc().disable_rustc().emit()?; "## )] /// # Ok(()) /// # } /// ``` #[cfg(feature = "rustc")] pub fn disable_rustc(&mut self) -> &mut Self { self.disable_rustc = true; self } /// Disable the sysinfo output, even when the sysinfo feature is enabled. /// /// # Example /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( feature = "si", doc = r##" EmitBuilder::builder().all_sysinfo().disable_sysinfo().emit()?; "## )] /// # Ok(()) /// # } /// ``` #[cfg(feature = "si")] pub fn disable_sysinfo(&mut self) -> &mut Self { self.disable_sysinfo = true; self } /// Emit cargo instructions from your build script /// /// - Will emit [`cargo:rustc-env=VAR=VALUE`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-envvarvalue) for each feature you have enabled. #[cfg_attr( feature = "git", doc = r##" - Will emit [`cargo:rerun-if-changed=PATH`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed) if the git feature is enabled. This is done to ensure any git variables are regenerated when commits are made. "## )] /// - Can emit [`cargo:warning`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargo-warning) outputs if the /// [`fail_on_error`](Self::fail_on_error) feature is not enabled and the requested variable is defaulted through error or /// the [`idempotent`](Self::idempotent) flag. /// /// # Errors /// * The [`writeln!`](std::writeln!) macro can throw a [`std::io::Error`] /// /// # Example /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( all( feature = "build", feature = "cargo", all(feature = "git", feature = "gitcl"), feature = "rustc", feature = "si" ), doc = r##" # env::set_var("CARGO_FEATURE_BUILD", "build"); # env::set_var("CARGO_FEATURE_GIT", "git"); # env::set_var("DEBUG", "true"); # env::set_var("OPT_LEVEL", "1"); # env::set_var("TARGET", "x86_64-unknown-linux-gnu"); EmitBuilder::builder() .all_build() .all_cargo() .all_git() .all_rustc() .all_sysinfo() .emit()?; # env::remove_var("CARGO_FEATURE_BUILD"); # env::remove_var("CARGO_FEATURE_GIT"); # env::remove_var("DEBUG"); # env::remove_var("OPT_LEVEL"); # env::remove_var("TARGET"); "## )] /// # Ok(()) /// # } /// ``` /// /// # Sample Output /// /// **NOTE** - You won't see this output unless you invoke cargo with the `-vv` flag. /// The instruction output is not displayed by default. /// /// ```text /// cargo:rustc-env=VERGEN_BUILD_DATE=2023-01-04 /// cargo:rustc-env=VERGEN_BUILD_TIMESTAMP=2023-01-04T15:38:11.097507114Z /// cargo:rustc-env=VERGEN_CARGO_DEBUG=true /// cargo:rustc-env=VERGEN_CARGO_FEATURES=build,git /// cargo:rustc-env=VERGEN_CARGO_OPT_LEVEL=1 /// cargo:rustc-env=VERGEN_CARGO_TARGET_TRIPLE=x86_64-unknown-linux-gnu /// cargo:rustc-env=VERGEN_GIT_BRANCH=feature/version8 /// cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_EMAIL=your@email.com /// cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_NAME=Yoda /// cargo:rustc-env=VERGEN_GIT_COMMIT_COUNT=476 /// cargo:rustc-env=VERGEN_GIT_COMMIT_DATE=2023-01-03 /// cargo:rustc-env=VERGEN_GIT_COMMIT_MESSAGE=The best message /// cargo:rustc-env=VERGEN_GIT_COMMIT_TIMESTAMP=2023-01-03T14:08:12.000000000-05:00 /// cargo:rustc-env=VERGEN_GIT_DESCRIBE=7.4.4-103-g53ae8a6 /// cargo:rustc-env=VERGEN_GIT_SHA=53ae8a69ab7917a2909af40f2e5d015f5b29ae28 /// cargo:rustc-env=VERGEN_RUSTC_CHANNEL=nightly /// cargo:rustc-env=VERGEN_RUSTC_COMMIT_DATE=2023-01-03 /// cargo:rustc-env=VERGEN_RUSTC_COMMIT_HASH=c7572670a1302f5c7e245d069200e22da9df0316 /// cargo:rustc-env=VERGEN_RUSTC_HOST_TRIPLE=x86_64-unknown-linux-gnu /// cargo:rustc-env=VERGEN_RUSTC_LLVM_VERSION=15.0 /// cargo:rustc-env=VERGEN_RUSTC_SEMVER=1.68.0-nightly /// cargo:rustc-env=VERGEN_SYSINFO_NAME=Arch Linux /// cargo:rustc-env=VERGEN_SYSINFO_OS_VERSION=Linux Arch Linux /// cargo:rustc-env=VERGEN_SYSINFO_USER=jozias /// cargo:rustc-env=VERGEN_SYSINFO_TOTAL_MEMORY=31 GiB /// cargo:rustc-env=VERGEN_SYSINFO_CPU_VENDOR=AuthenticAMD /// cargo:rustc-env=VERGEN_SYSINFO_CPU_CORE_COUNT=8 /// cargo:rustc-env=VERGEN_SYSINFO_CPU_NAME=cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7 /// cargo:rustc-env=VERGEN_SYSINFO_CPU_BRAND=AMD Ryzen Threadripper 1900X 8-Core Processor /// cargo:rustc-env=VERGEN_SYSINFO_CPU_FREQUENCY=3792 /// cargo:rerun-if-changed=.git/HEAD /// cargo:rerun-if-changed=.git/refs/heads/feature/version8 /// cargo:rerun-if-changed=build.rs /// cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT /// cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH /// ``` /// pub fn emit(self) -> Result<()> { self.inner_emit(None) .and_then(|x| x.emit_output(self.quiet, self.custom_buildrs, &mut io::stdout())) } /// Emit cargo instructions from your build script and set environment variables for use in `build.rs` /// /// - Will emit [`cargo:rustc-env=VAR=VALUE`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-envvarvalue) for each feature you have enabled. #[cfg_attr( feature = "git", doc = r##" - Will emit [`cargo:rerun-if-changed=PATH`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed) if the git feature is enabled. This is done to ensure any git variables are regenerated when commits are made. "## )] /// - Can emit [`cargo:warning`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargo-warning) outputs if the /// [`fail_on_error`](Self::fail_on_error) feature is not enabled and the requested variable is defaulted through error or /// the [`idempotent`](Self::idempotent) flag. /// /// # Errors /// * The [`writeln!`](std::writeln!) macro can throw a [`std::io::Error`] /// /// # Example /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( all( feature = "build", feature = "cargo", all(feature = "git", feature = "gitcl"), feature = "rustc", feature = "si" ), doc = r##" # env::set_var("CARGO_FEATURE_BUILD", "build"); # env::set_var("CARGO_FEATURE_GIT", "git"); # env::set_var("DEBUG", "true"); # env::set_var("OPT_LEVEL", "1"); # env::set_var("TARGET", "x86_64-unknown-linux-gnu"); EmitBuilder::builder() .all_build() .all_cargo() .all_git() .all_rustc() .all_sysinfo() .emit_and_set()?; # env::remove_var("CARGO_FEATURE_BUILD"); # env::remove_var("CARGO_FEATURE_GIT"); # env::remove_var("DEBUG"); # env::remove_var("OPT_LEVEL"); # env::remove_var("TARGET"); "## )] /// # Ok(()) /// # } /// ``` #[cfg(any( feature = "build", feature = "cargo", feature = "git", feature = "rustc", feature = "si" ))] pub fn emit_and_set(self) -> Result<()> { self.inner_emit(None) .and_then(|x| { x.emit_output(self.quiet, self.custom_buildrs, &mut io::stdout()) .map(|()| x) }) .map(|x| { for (k, v) in &x.cargo_rustc_env_map { if env::var(k.name()).is_err() { env::set_var(k.name(), v); } } }) } /// Emit instructions from the given repository path. /// /// # Errors /// #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] pub fn emit_at(self, repo_path: PathBuf) -> Result<()> { self.inner_emit(Some(repo_path)) .and_then(|x| x.emit_output(self.quiet, self.custom_buildrs, &mut io::stdout())) } #[cfg(all( test, any( feature = "build", feature = "cargo", all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ), feature = "rustc", feature = "si" ) ))] pub(crate) fn test_emit(self) -> Result { self.inner_emit(None) } #[cfg(all( test, all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ), ))] pub(crate) fn test_emit_at(self, repo_path: Option) -> Result { self.inner_emit(repo_path) } #[doc(hidden)] /// Emit the cargo build script instructions to the given [`Write`](std::io::Write). /// /// **NOTE** - This is generally only used for testing and probably shouldn't be used /// within a `build.rs` file. /// /// # Errors /// * The [`writeln!`](std::writeln!) macro can throw a [`std::io::Error`] /// pub fn emit_to(self, stdout: &mut T) -> Result where T: Write, { self.inner_emit(None).and_then(|x| { x.emit_output(self.quiet, self.custom_buildrs, stdout) .map(|()| x.failed) }) } #[doc(hidden)] /// Emit the cargo build script instructions to the given [`Write`](std::io::Write) at /// the given repository path for git instructions /// /// **NOTE** - This is generally only used for testing and probably shouldn't be used /// within a `build.rs` file. /// /// # Errors /// * The [`writeln!`](std::writeln!) macro can throw a [`std::io::Error`] /// pub fn emit_to_at(self, stdout: &mut T, path: Option) -> Result where T: Write, { self.inner_emit(path).and_then(|x| { x.emit_output(self.quiet, self.custom_buildrs, stdout) .map(|()| x.failed) }) } fn inner_emit(self, path: Option) -> Result { let mut config = Emitter::default(); config.add_build_entries(&self)?; config.add_cargo_entries(&self)?; config.add_git_entries(&self, path)?; config.add_rustc_entries(&self)?; config.add_si_entries(&self); Ok(config) } } #[cfg(test)] pub(crate) mod test { use super::EmitBuilder; use anyhow::Result; #[cfg(any( feature = "build", feature = "cargo", all( feature = "git", any(feature = "git2", feature = "gitcl", feature = "gix") ), feature = "rustc", feature = "si", ))] use {super::RustcEnvMap, crate::constants::VERGEN_IDEMPOTENT_DEFAULT}; #[cfg(any( feature = "build", feature = "cargo", all( feature = "git", any(feature = "git2", feature = "gitcl", feature = "gix") ), feature = "rustc", feature = "si", ))] pub(crate) fn count_idempotent(map: &RustcEnvMap) -> usize { map.values() .filter(|x| *x == VERGEN_IDEMPOTENT_DEFAULT) .count() } #[test] #[serial_test::serial] fn default_is_no_emit() -> Result<()> { let mut stdout_buf = vec![]; _ = EmitBuilder::builder().emit_to(&mut stdout_buf)?; assert!(stdout_buf.is_empty()); Ok(()) } #[test] #[serial_test::serial] fn gen_is_ok() -> Result<()> { assert!(EmitBuilder::builder().emit().is_ok()); Ok(()) } #[cfg(all( feature = "build", feature = "rustc", feature = "cargo", feature = "si" ))] #[test] #[serial_test::serial] fn everything_enabled() -> Result<()> { use crate::utils::testutils::{setup, teardown}; setup(); let mut stdout_buf = vec![]; _ = EmitBuilder::builder() .idempotent() .fail_on_error() .all_build() .all_cargo() .all_rustc() .all_sysinfo() .emit_to(&mut stdout_buf)?; teardown(); Ok(()) } #[cfg(all( feature = "build", feature = "rustc", feature = "cargo", feature = "si" ))] #[test] #[serial_test::serial] fn all_output_non_git() -> Result<()> { use crate::utils::testutils::{setup, teardown}; setup(); let mut stdout_buf = vec![]; _ = EmitBuilder::builder() .all_build() .all_cargo() .all_rustc() .all_sysinfo() .emit_to(&mut stdout_buf)?; assert!(!stdout_buf.is_empty()); teardown(); Ok(()) } #[cfg(all( feature = "build", feature = "rustc", all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ), feature = "cargo", feature = "si" ))] #[test] #[serial_test::serial] fn all_output() -> Result<()> { use crate::utils::testutils::{setup, teardown}; setup(); let mut stdout_buf = vec![]; _ = EmitBuilder::builder() .all_build() .all_cargo() .all_git() .all_rustc() .all_sysinfo() .emit_to(&mut stdout_buf)?; assert!(!stdout_buf.is_empty()); teardown(); Ok(()) } #[cfg(all( feature = "build", feature = "rustc", all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ), feature = "cargo", feature = "si" ))] #[test] #[serial_test::serial] fn all_features_no_output() -> Result<()> { use crate::utils::testutils::{setup, teardown}; setup(); let mut stdout_buf = vec![]; _ = EmitBuilder::builder().emit_to(&mut stdout_buf)?; assert!(stdout_buf.is_empty()); teardown(); Ok(()) } } vergen-8.3.2/src/feature/build.rs000064400000000000000000000312731046102023000150210ustar 00000000000000use crate::{ constants::{BUILD_DATE_NAME, BUILD_TIMESTAMP_NAME}, emitter::{EmitBuilder, RustcEnvMap}, key::VergenKey, utils::fns::{add_default_map_entry, add_map_entry}, }; use anyhow::{Context, Error, Result}; use std::{env, str::FromStr}; use time::{ format_description::{self, well_known::Iso8601}, OffsetDateTime, }; #[derive(Clone, Copy, Debug, Default)] pub(crate) struct Config { pub(crate) build_date: bool, pub(crate) build_timestamp: bool, use_local: bool, } impl Config { pub(crate) fn any(self) -> bool { self.build_date || self.build_timestamp } } /// The `VERGEN_BUILD_*` configuration features /// /// | Variable | Sample | /// | ------- | ------ | /// | `VERGEN_BUILD_DATE` | 2021-02-25 | /// | `VERGEN_BUILD_TIMESTAMP` | 2021-02-25T23:28:39.493201+00:00 | /// /// # Example /// Emit all of the build instructions /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// EmitBuilder::builder().all_build().emit()?; /// # Ok(()) /// # } /// ``` /// /// Emit some of the build instructions /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// EmitBuilder::builder().build_timestamp().emit()?; /// # Ok(()) /// # } /// ``` /// /// Override output with your own value /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// env::set_var("VERGEN_BUILD_DATE", "this is the date I want output"); /// EmitBuilder::builder().build_date().emit()?; /// # env::remove_var("VERGEN_BUILD_DATE"); /// # Ok(()) /// # } /// ``` /// /// # Example /// This feature can also be used in conjuction with the [`SOURCE_DATE_EPOCH`](https://reproducible-builds.org/docs/source-date-epoch/) /// environment variable to generate deterministic timestamps based off the /// last modification time of the source/package /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// env::set_var("SOURCE_DATE_EPOCH", "1671809360"); #[cfg_attr( feature = "build", doc = r##" EmitBuilder::builder().all_build().emit()?; "## )] /// # env::remove_var("SOURCE_DATE_EPOCH"); /// # Ok(()) /// # } /// ``` /// /// The above will always generate the following output for the timestamp /// related instructions /// /// ```text /// cargo:rustc-env=VERGEN_BUILD_DATE=2022-12-23 /// cargo:rustc-env=VERGEN_BUILD_TIMESTAMP=2022-12-23T15:29:20.000000000Z /// ``` /// /// # Example /// This feature also recognizes the idempotent flag. /// /// **NOTE** - `SOURCE_DATE_EPOCH` takes precedence over the idempotent flag. If you /// use both, the output will be based off `SOURCE_DATE_EPOCH`. This would still be /// deterministic. /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( feature = "build", doc = r##" EmitBuilder::builder().idempotent().all_build().emit()?; "## )] /// # Ok(()) /// # } /// ``` /// /// The above will always generate the following output for the timestamp /// related instructions /// /// ```text /// cargo:rustc-env=VERGEN_BUILD_DATE=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_BUILD_TIMESTAMP=VERGEN_IDEMPOTENT_OUTPUT /// cargo:warning=VERGEN_BUILD_DATE set to default /// cargo:warning=VERGEN_BUILD_TIMESTAMP set to default /// cargo:rerun-if-changed=build.rs /// cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT /// cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH /// ``` /// #[cfg_attr(docsrs, doc(cfg(feature = "build")))] impl EmitBuilder { /// Enable all of the `VERGEN_BUILD_*` options pub fn all_build(&mut self) -> &mut Self { self.build_date().build_timestamp() } /// Enable the `VERGEN_BUILD_DATE` date output pub fn build_date(&mut self) -> &mut Self { self.build_config.build_date = true; self } /// Enable the `VERGEN_BUILD_TIMESTAMP` date output pub fn build_timestamp(&mut self) -> &mut Self { self.build_config.build_timestamp = true; self } /// Enable local offset date/timestamp output pub fn use_local_build(&mut self) -> &mut Self { self.build_config.use_local = true; self } pub(crate) fn add_build_default( &self, e: Error, fail_on_error: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { if fail_on_error { Err(e) } else { if self.build_config.build_date { add_default_map_entry(VergenKey::BuildDate, map, warnings); } if self.build_config.build_timestamp { add_default_map_entry(VergenKey::BuildTimestamp, map, warnings); } Ok(()) } } pub(crate) fn add_build_map_entries( &self, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { if self.build_config.any() { self.add_timestamp_entries(idempotent, map, warnings) .with_context(|| "Error adding build timestamp entries")?; } Ok(()) } fn add_timestamp_entries( &self, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { let (sde, ts) = match env::var("SOURCE_DATE_EPOCH") { Ok(v) => ( true, OffsetDateTime::from_unix_timestamp(i64::from_str(&v)?)?, ), Err(env::VarError::NotPresent) => { if self.build_config.use_local { (false, OffsetDateTime::now_local()?) } else { (false, OffsetDateTime::now_utc()) } } Err(e) => return Err(e.into()), }; self.add_date_entry(idempotent, sde, &ts, map, warnings)?; self.add_timestamp_entry(idempotent, sde, &ts, map, warnings)?; Ok(()) } fn add_date_entry( &self, idempotent: bool, source_date_epoch: bool, ts: &OffsetDateTime, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { if self.build_config.build_date { if let Ok(value) = env::var(BUILD_DATE_NAME) { add_map_entry(VergenKey::BuildDate, value, map); } else if idempotent && !source_date_epoch { add_default_map_entry(VergenKey::BuildDate, map, warnings); } else { let format = format_description::parse("[year]-[month]-[day]")?; add_map_entry(VergenKey::BuildDate, ts.format(&format)?, map); } } Ok(()) } fn add_timestamp_entry( &self, idempotent: bool, source_date_epoch: bool, ts: &OffsetDateTime, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { if self.build_config.build_timestamp { if let Ok(value) = env::var(BUILD_TIMESTAMP_NAME) { add_map_entry(VergenKey::BuildTimestamp, value, map); } else if idempotent && !source_date_epoch { add_default_map_entry(VergenKey::BuildTimestamp, map, warnings); } else { add_map_entry( VergenKey::BuildTimestamp, ts.format(&Iso8601::DEFAULT)?, map, ); } } Ok(()) } } #[cfg(test)] mod test { use crate::{emitter::test::count_idempotent, EmitBuilder}; use anyhow::Result; use std::env; #[test] #[serial_test::serial] fn build_all_idempotent() -> Result<()> { let config = EmitBuilder::builder() .idempotent() .all_build() .test_emit()?; assert_eq!(2, config.cargo_rustc_env_map.len()); assert_eq!(2, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(2, config.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn build_all() -> Result<()> { let config = EmitBuilder::builder().all_build().test_emit()?; assert_eq!(2, config.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(0, config.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn source_date_epoch_works() -> Result<()> { env::set_var("SOURCE_DATE_EPOCH", "1671809360"); let mut stdout_buf = vec![]; _ = EmitBuilder::builder() .idempotent() .all_build() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); for (idx, line) in output.lines().enumerate() { if idx == 0 { assert_eq!("cargo:rustc-env=VERGEN_BUILD_DATE=2022-12-23", line); } else if idx == 1 { assert_eq!( "cargo:rustc-env=VERGEN_BUILD_TIMESTAMP=2022-12-23T15:29:20.000000000Z", line ); } } env::remove_var("SOURCE_DATE_EPOCH"); Ok(()) } #[test] #[serial_test::serial] #[cfg(unix)] fn bad_source_date_epoch_fails() -> Result<()> { use std::ffi::OsStr; use std::os::unix::prelude::OsStrExt; let source = [0x66, 0x6f, 0x80, 0x6f]; let os_str = OsStr::from_bytes(&source[..]); env::set_var("SOURCE_DATE_EPOCH", os_str); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .idempotent() .fail_on_error() .all_build() .emit_to(&mut stdout_buf) .is_err()); env::remove_var("SOURCE_DATE_EPOCH"); Ok(()) } #[test] #[serial_test::serial] #[cfg(unix)] fn bad_source_date_epoch_defaults() -> Result<()> { use std::ffi::OsStr; use std::os::unix::prelude::OsStrExt; let source = [0x66, 0x6f, 0x80, 0x6f]; let os_str = OsStr::from_bytes(&source[..]); env::set_var("SOURCE_DATE_EPOCH", os_str); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .idempotent() .all_build() .emit_to(&mut stdout_buf) .is_ok()); env::remove_var("SOURCE_DATE_EPOCH"); Ok(()) } #[test] #[serial_test::serial] #[cfg(windows)] fn bad_source_date_epoch_fails() -> Result<()> { use std::ffi::OsString; use std::os::windows::prelude::OsStringExt; let source = [0x0066, 0x006f, 0xD800, 0x006f]; let os_string = OsString::from_wide(&source[..]); let os_str = os_string.as_os_str(); env::set_var("SOURCE_DATE_EPOCH", os_str); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .idempotent() .fail_on_error() .all_build() .emit_to(&mut stdout_buf) .is_err()); env::remove_var("SOURCE_DATE_EPOCH"); Ok(()) } #[test] #[serial_test::serial] #[cfg(windows)] fn bad_source_date_epoch_defaults() -> Result<()> { use std::ffi::OsString; use std::os::windows::prelude::OsStringExt; let source = [0x0066, 0x006f, 0xD800, 0x006f]; let os_string = OsString::from_wide(&source[..]); let os_str = os_string.as_os_str(); env::set_var("SOURCE_DATE_EPOCH", os_str); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .idempotent() .all_build() .emit_to(&mut stdout_buf) .is_ok()); env::remove_var("SOURCE_DATE_EPOCH"); Ok(()) } #[test] #[serial_test::serial] fn build_date_override_works() -> Result<()> { env::set_var("VERGEN_BUILD_DATE", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_build() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_BUILD_DATE=this is a bad date")); env::remove_var("VERGEN_BUILD_DATE"); Ok(()) } #[test] #[serial_test::serial] fn build_timestamp_override_works() -> Result<()> { env::set_var("VERGEN_BUILD_TIMESTAMP", "this is a bad timestamp"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_build() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_BUILD_TIMESTAMP=this is a bad timestamp")); env::remove_var("VERGEN_BUILD_TIMESTAMP"); Ok(()) } } vergen-8.3.2/src/feature/cargo.rs000064400000000000000000000403601046102023000150120ustar 00000000000000// Copyright (c) 2022 vergen developers // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. use crate::{ constants::{ CARGO_DEBUG, CARGO_DEPENDENCIES, CARGO_FEATURES, CARGO_OPT_LEVEL, CARGO_TARGET_TRIPLE, }, emitter::{EmitBuilder, RustcEnvMap}, key::VergenKey, utils::fns::{add_default_map_entry, add_map_entry}, }; use anyhow::{anyhow, Error, Result}; use cargo_metadata::{DepKindInfo, DependencyKind, MetadataCommand, Package, PackageId}; use regex::Regex; use std::env; #[derive(Clone, Copy, Debug, Default)] #[allow(clippy::struct_excessive_bools, clippy::struct_field_names)] pub(crate) struct Config { pub(crate) cargo_debug: bool, pub(crate) cargo_features: bool, pub(crate) cargo_opt_level: bool, pub(crate) cargo_target_triple: bool, pub(crate) cargo_dependencies: bool, cargo_name_filter: Option<&'static str>, cargo_dep_kind_filter: Option, } impl Config { pub(crate) fn any(self) -> bool { self.cargo_debug || self.cargo_features || self.cargo_opt_level || self.cargo_target_triple || self.cargo_dependencies } } /// Copnfigure the emission of `VERGEN_CARGO_*` instructions /// /// **NOTE** - All cargo instructions are considered deterministic. If you change /// the version of cargo you are compiling with, these values should change if /// being used in the generated binary. /// /// | Variable | Sample | /// | ------- | ------ | /// | `VERGEN_CARGO_DEBUG` | true | /// | `VERGEN_CARGO_FEATURES` | git,build | /// | `VERGEN_CARGO_OPT_LEVEL` | 1 | /// | `VERGEN_CARGO_TARGET_TRIPLE` | x86_64-unknown-linux-gnu | /// /// # Example /// Emit all of the cargo instructions /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// # env::set_var("CARGO_FEATURE_BUILD", ""); /// # env::set_var("DEBUG", "true"); /// # env::set_var("OPT_LEVEL", "1"); /// # env::set_var("TARGET", "x86_64-unknown-linux-gnu"); /// EmitBuilder::builder().all_cargo().emit()?; /// # env::remove_var("CARGO_FEATURE_BUILD"); /// # env::remove_var("DEBUG"); /// # env::remove_var("OPT_LEVEL"); /// # env::remove_var("TARGET"); /// # Ok(()) /// # } /// ``` /// Emit some of the cargo instructions /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// # env::set_var("DEBUG", "true"); /// # env::set_var("OPT_LEVEL", "1"); /// EmitBuilder::builder().cargo_debug().cargo_opt_level().emit()?; /// # env::remove_var("DEBUG"); /// # env::remove_var("OPT_LEVEL"); /// # Ok(()) /// # } /// ``` /// /// Override output with your own value /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// # env::set_var("DEBUG", "true"); /// # env::set_var("OPT_LEVEL", "1"); /// env::set_var("VERGEN_CARGO_DEBUG", "this is the debug I want output"); /// EmitBuilder::builder().all_cargo().emit()?; /// # env::remove_var("VERGEN_BUILD_DATE"); /// # env::remove_var("DEBUG"); /// # env::remove_var("OPT_LEVEL"); /// # Ok(()) /// # } /// ``` /// #[cfg_attr(docsrs, doc(cfg(feature = "cargo")))] impl EmitBuilder { /// Emit all of the `VERGEN_CARGO_*` instructions pub fn all_cargo(&mut self) -> &mut Self { self.cargo_debug() .cargo_features() .cargo_opt_level() .cargo_target_triple() .cargo_dependencies() } /// Emit the DEBUG value set by cargo /// /// ```text /// cargo:rustc-env=VERGEN_CARGO_DEBUG=true|false /// ``` /// pub fn cargo_debug(&mut self) -> &mut Self { self.cargo_config.cargo_debug = true; self } /// Emit the `CARGO_FEATURE_*` values set by cargo /// /// ```text /// cargo:rustc-env=VERGEN_CARGO_FEATURES= /// ``` /// pub fn cargo_features(&mut self) -> &mut Self { self.cargo_config.cargo_features = true; self } /// Emit the `OPT_LEVEL` value set by cargo /// /// ```text /// cargo:rustc-env=VERGEN_CARGO_OPT_LEVEL= /// ``` /// pub fn cargo_opt_level(&mut self) -> &mut Self { self.cargo_config.cargo_opt_level = true; self } /// Emit the TARGET value set by cargo /// /// ```text /// cargo:rustc-env=VERGEN_CARGO_TARGET_TRIPLE= /// ``` /// pub fn cargo_target_triple(&mut self) -> &mut Self { self.cargo_config.cargo_target_triple = true; self } /// Emit the dependencies value derived from `Cargo.toml` /// /// ```text /// cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES= /// ``` pub fn cargo_dependencies(&mut self) -> &mut Self { self.cargo_config.cargo_dependencies = true; self } /// Add a name [`Regex`](regex::Regex) filter for cargo dependencies /// /// ```text /// cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES= /// ``` pub fn cargo_dependencies_name_filter( &mut self, name_filter: Option<&'static str>, ) -> &mut Self { self.cargo_config.cargo_name_filter = name_filter; self } /// Add a [`DependencyKind`](cargo_metadata::DependencyKind) filter for cargo dependencies /// /// ```text /// cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES= /// ``` pub fn cargo_dependencies_dep_kind_filter( &mut self, dep_kind_filter: Option, ) -> &mut Self { self.cargo_config.cargo_dep_kind_filter = dep_kind_filter; self } pub(crate) fn add_cargo_default( &self, e: Error, fail_on_error: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { if fail_on_error { Err(e) } else { if self.cargo_config.cargo_debug { add_default_map_entry(VergenKey::CargoDebug, map, warnings); } if self.cargo_config.cargo_features { add_default_map_entry(VergenKey::CargoFeatures, map, warnings); } if self.cargo_config.cargo_opt_level { add_default_map_entry(VergenKey::CargoOptLevel, map, warnings); } if self.cargo_config.cargo_target_triple { add_default_map_entry(VergenKey::CargoTargetTriple, map, warnings); } if self.cargo_config.cargo_dependencies { add_default_map_entry(VergenKey::CargoDependencies, map, warnings); } Ok(()) } } pub(crate) fn add_cargo_map_entries(&self, map: &mut RustcEnvMap) -> Result<()> { if self.cargo_config.any() { if self.cargo_config.cargo_debug { if let Ok(value) = env::var(CARGO_DEBUG) { add_map_entry(VergenKey::CargoDebug, value, map); } else { add_map_entry(VergenKey::CargoDebug, env::var("DEBUG")?, map); } } if self.cargo_config.cargo_features { if let Ok(value) = env::var(CARGO_FEATURES) { add_map_entry(VergenKey::CargoFeatures, value, map); } else { let features: Vec = env::vars().filter_map(is_cargo_feature).collect(); let feature_str = features.as_slice().join(","); add_map_entry(VergenKey::CargoFeatures, feature_str, map); } } if self.cargo_config.cargo_opt_level { if let Ok(value) = env::var(CARGO_OPT_LEVEL) { add_map_entry(VergenKey::CargoOptLevel, value, map); } else { add_map_entry(VergenKey::CargoOptLevel, env::var("OPT_LEVEL")?, map); } } if self.cargo_config.cargo_target_triple { if let Ok(value) = env::var(CARGO_TARGET_TRIPLE) { add_map_entry(VergenKey::CargoTargetTriple, value, map); } else { add_map_entry(VergenKey::CargoTargetTriple, env::var("TARGET")?, map); } } if self.cargo_config.cargo_dependencies { if let Ok(value) = env::var(CARGO_DEPENDENCIES) { add_map_entry(VergenKey::CargoDependencies, value, map); } else { let value = Self::get_dependencies( self.cargo_config.cargo_name_filter, self.cargo_config.cargo_dep_kind_filter, )?; if !value.is_empty() { add_map_entry(VergenKey::CargoDependencies, value, map); } } } } Ok(()) } fn get_dependencies( name_filter: Option<&'static str>, dep_kind_filter: Option, ) -> Result { let metadata = MetadataCommand::new().exec()?; let resolved_crates = metadata.resolve.ok_or_else(|| anyhow!("No resolve"))?; let root_id = resolved_crates.root.ok_or_else(|| anyhow!("No root id"))?; let root = resolved_crates .nodes .into_iter() .find(|node| node.id == root_id) .ok_or_else(|| anyhow!("No root node"))?; let package_ids: Vec<(PackageId, Vec)> = root .deps .into_iter() .map(|node_dep| (node_dep.pkg, node_dep.dep_kinds)) .collect(); let packages: Vec<(&Package, &Vec)> = package_ids .iter() .filter_map(|(package_id, dep_kinds)| { metadata .packages .iter() .find(|&package| package.id == *package_id) .map(|package| (package, dep_kinds)) }) .collect(); let results: Vec = packages .iter() .filter_map(|(package, dep_kind_info)| { if let Some(name_regex) = name_filter { if let Ok(regex) = Regex::new(name_regex) { if regex.is_match(&package.name) { Some((package, dep_kind_info)) } else { None } } else { Some((package, dep_kind_info)) } } else { Some((package, dep_kind_info)) } }) .filter_map(|(package, dep_kind_info)| { if let Some(dep_kind_filter) = dep_kind_filter { let kinds: Vec = dep_kind_info .iter() .map(|dep_kind_info| dep_kind_info.kind) .collect(); if kinds.contains(&dep_kind_filter) { Some(package) } else { None } } else { Some(package) } }) .map(|package| format!("{} {}", package.name, package.version)) .collect(); Ok(results.join(",")) } } fn is_cargo_feature(var: (String, String)) -> Option { let (k, _) = var; if k.starts_with("CARGO_FEATURE_") { Some(k.replace("CARGO_FEATURE_", "").to_lowercase()) } else { None } } #[cfg(test)] mod test { use crate::{ emitter::test::count_idempotent, utils::testutils::{setup, teardown}, EmitBuilder, }; use anyhow::Result; use std::env; #[test] #[serial_test::serial] fn build_all_idempotent() -> Result<()> { setup(); let config = EmitBuilder::builder() .idempotent() .all_cargo() .test_emit()?; assert_eq!(5, config.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(0, config.warnings.len()); teardown(); Ok(()) } #[test] #[serial_test::serial] fn build_all() -> Result<()> { setup(); let config = EmitBuilder::builder().all_cargo().test_emit()?; assert_eq!(5, config.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(0, config.warnings.len()); teardown(); Ok(()) } #[test] #[serial_test::serial] fn bad_env_fails() { assert!(EmitBuilder::builder() .fail_on_error() .all_cargo() .test_emit() .is_err()); } #[test] #[serial_test::serial] fn bad_env_emits_default() -> Result<()> { let emit_res = EmitBuilder::builder().all_cargo().test_emit(); assert!(emit_res.is_ok()); let emit = emit_res?; assert_eq!(5, emit.cargo_rustc_env_map.len()); assert_eq!(5, count_idempotent(&emit.cargo_rustc_env_map)); assert_eq!(5, emit.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn cargo_debug_override_works() -> Result<()> { setup(); env::set_var("VERGEN_CARGO_DEBUG", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_cargo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_CARGO_DEBUG=this is a bad date")); env::remove_var("VERGEN_CARGO_DEBUG"); teardown(); Ok(()) } #[test] #[serial_test::serial] fn cargo_features_override_works() -> Result<()> { setup(); env::set_var("VERGEN_CARGO_FEATURES", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_cargo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_CARGO_FEATURES=this is a bad date")); env::remove_var("VERGEN_CARGO_FEATURES"); teardown(); Ok(()) } #[test] #[serial_test::serial] fn cargo_opt_level_override_works() -> Result<()> { setup(); env::set_var("VERGEN_CARGO_OPT_LEVEL", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_cargo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_CARGO_OPT_LEVEL=this is a bad date")); env::remove_var("VERGEN_CARGO_OPT_LEVEL"); teardown(); Ok(()) } #[test] #[serial_test::serial] fn cargo_target_triple_override_works() -> Result<()> { setup(); env::set_var("VERGEN_CARGO_TARGET_TRIPLE", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_cargo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_CARGO_TARGET_TRIPLE=this is a bad date")); env::remove_var("VERGEN_CARGO_TARGET_TRIPLE"); teardown(); Ok(()) } #[test] #[serial_test::serial] fn cargo_dependencies_override_works() -> Result<()> { setup(); env::set_var("VERGEN_CARGO_DEPENDENCIES", "this is a bad some dep"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_cargo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES=this is a bad some dep")); env::remove_var("VERGEN_CARGO_DEPENDENCIES"); teardown(); Ok(()) } } vergen-8.3.2/src/feature/git/cmd.rs000064400000000000000000001004611046102023000152440ustar 00000000000000// Copyright (c) 2022 vergen developers // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. use crate::{ constants::{ GIT_BRANCH_NAME, GIT_COMMIT_AUTHOR_EMAIL, GIT_COMMIT_AUTHOR_NAME, GIT_COMMIT_COUNT, GIT_COMMIT_DATE_NAME, GIT_COMMIT_MESSAGE, GIT_COMMIT_TIMESTAMP_NAME, GIT_DESCRIBE_NAME, GIT_DIRTY_NAME, GIT_SHA_NAME, }, emitter::{EmitBuilder, RustcEnvMap}, key::VergenKey, utils::fns::{add_default_map_entry, add_map_entry}, }; use anyhow::{anyhow, Error, Result}; use std::{ env, path::PathBuf, process::{Command, Output, Stdio}, str::FromStr, }; use time::{ format_description::{ self, well_known::{Iso8601, Rfc3339}, }, OffsetDateTime, UtcOffset, }; #[derive(Clone, Copy, Debug, Default)] #[allow(clippy::struct_excessive_bools)] pub(crate) struct Config { // git rev-parse --abbrev-ref --symbolic-full-name HEAD pub(crate) git_branch: bool, // git log -1 --pretty=format:'%ae' pub(crate) git_commit_author_email: bool, // git log -1 --pretty=format:'%an' pub(crate) git_commit_author_name: bool, // git rev-list --count HEAD pub(crate) git_commit_count: bool, // git log -1 --pretty=format:'%cs' pub(crate) git_commit_date: bool, // git log -1 --format=%s pub(crate) git_commit_message: bool, // git log -1 --pretty=format:'%cI' pub(crate) git_commit_timestamp: bool, // git describe --always (optionally --tags, --dirty, --match) pub(crate) git_describe: bool, git_describe_dirty: bool, git_describe_tags: bool, git_describe_match_pattern: Option<&'static str>, // git rev-parse HEAD (optionally with --short) pub(crate) git_sha: bool, git_sha_short: bool, // if output from: // git status --porcelain (optionally with "-u no") pub(crate) git_dirty: bool, git_dirty_include_untracked: bool, git_cmd: Option<&'static str>, use_local: bool, } // This funkiness allows the command to be output in the docs macro_rules! branch_cmd { () => { "git rev-parse --abbrev-ref --symbolic-full-name HEAD" }; } const BRANCH_CMD: &str = branch_cmd!(); macro_rules! author_email { () => { "git log -1 --pretty=format:'%ae'" }; } const COMMIT_AUTHOR_EMAIL: &str = author_email!(); macro_rules! author_name { () => { "git log -1 --pretty=format:'%an'" }; } const COMMIT_AUTHOR_NAME: &str = author_name!(); macro_rules! commit_count { () => { "git rev-list --count HEAD" }; } const COMMIT_COUNT: &str = commit_count!(); macro_rules! commit_date { () => { "git log -1 --pretty=format:'%cs'" }; } macro_rules! commit_message { () => { "git log -1 --format=%s" }; } const COMMIT_MESSAGE: &str = commit_message!(); macro_rules! commit_timestamp { () => { "git log -1 --pretty=format:'%cI'" }; } const COMMIT_TIMESTAMP: &str = commit_timestamp!(); macro_rules! describe { () => { "git describe --always" }; } const DESCRIBE: &str = describe!(); macro_rules! sha { () => { "git rev-parse" }; } const SHA: &str = sha!(); macro_rules! dirty { () => { "git status --porcelain" }; } const DIRTY: &str = dirty!(); /// The `VERGEN_GIT_*` configuration features /// /// | Variable | Sample | /// | ------- | ------ | /// | `VERGEN_GIT_BRANCH` | feature/fun | /// | `VERGEN_GIT_COMMIT_AUTHOR_EMAIL` | janedoe@email.com | /// | `VERGEN_GIT_COMMIT_AUTHOR_NAME` | Jane Doe | /// | `VERGEN_GIT_COMMIT_COUNT` | 330 | /// | `VERGEN_GIT_COMMIT_DATE` | 2021-02-24 | /// | `VERGEN_GIT_COMMIT_MESSAGE` | feat: add commit messages | /// | `VERGEN_GIT_COMMIT_TIMESTAMP` | 2021-02-24T20:55:21+00:00 | /// | `VERGEN_GIT_DESCRIBE` | 5.0.0-2-gf49246c | /// | `VERGEN_GIT_SHA` | f49246ce334567bff9f950bfd0f3078184a2738a | /// | `VERGEN_GIT_DIRTY` | true | /// /// # Example /// Emit all of the git instructions /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// EmitBuilder::builder().all_git().emit()?; /// # Ok(()) /// # } /// ``` /// /// Emit some of the git instructions /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// EmitBuilder::builder().git_describe(true, false, None).emit()?; /// # Ok(()) /// # } /// ``` /// /// Override output with your own value /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// env::set_var("VERGEN_GIT_BRANCH", "this is the branch I want output"); /// EmitBuilder::builder().all_git().emit()?; /// # env::remove_var("VERGEN_GIT_BRANCH"); /// # Ok(()) /// # } /// ``` /// /// # Example /// This feature can also be used in conjuction with the [`SOURCE_DATE_EPOCH`](https://reproducible-builds.org/docs/source-date-epoch/) /// environment variable to generate deterministic timestamps based off the /// last modification time of the source/package /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// env::set_var("SOURCE_DATE_EPOCH", "1671809360"); #[cfg_attr( all(feature = "git", feature = "gitcl"), doc = r##" EmitBuilder::builder().all_git().emit()?; "## )] /// # env::remove_var("SOURCE_DATE_EPOCH"); /// # Ok(()) /// # } /// ``` /// /// The above will always generate the following output for the timestamp /// related instructions /// /// ```text /// ... /// cargo:rustc-env=VERGEN_GIT_COMMIT_DATE=2022-12-23 /// ... /// cargo:rustc-env=VERGEN_GIT_COMMIT_TIMESTAMP=2022-12-23T15:29:20.000000000Z /// ... /// ``` /// /// # Example /// This feature also recognizes the idempotent flag. /// /// **NOTE** - `SOURCE_DATE_EPOCH` takes precedence over the idempotent flag. If you /// use both, the output will be based off `SOURCE_DATE_EPOCH`. This would still be /// deterministic. /// /// # Example /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( all(feature = "git", feature = "gitcl"), doc = r##" EmitBuilder::builder().idempotent().all_git().emit()?; "## )] /// # Ok(()) /// # } /// ``` /// /// The above will always generate the following instructions /// /// ```text /// cargo:rustc-env=VERGEN_GIT_BRANCH=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_EMAIL=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_NAME=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_GIT_COMMIT_COUNT=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_GIT_COMMIT_DATE=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_GIT_COMMIT_MESSAGE=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_GIT_COMMIT_TIMESTAMP=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_GIT_DESCRIBE=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_GIT_SHA=VERGEN_IDEMPOTENT_OUTPUT /// cargo:warning=VERGEN_GIT_BRANCH set to default /// cargo:warning=VERGEN_GIT_COMMIT_AUTHOR_EMAIL set to default /// cargo:warning=VERGEN_GIT_COMMIT_AUTHOR_NAME set to default /// cargo:warning=VERGEN_GIT_COMMIT_COUNT set to default /// cargo:warning=VERGEN_GIT_COMMIT_DATE set to default /// cargo:warning=VERGEN_GIT_COMMIT_MESSAGE set to default /// cargo:warning=VERGEN_GIT_COMMIT_TIMESTAMP set to default /// cargo:warning=VERGEN_GIT_DESCRIBE set to default /// cargo:warning=VERGEN_GIT_SHA set to default /// cargo:rerun-if-changed=build.rs /// cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT /// cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH /// ``` /// #[cfg_attr(docsrs, doc(cfg(feature = "git")))] impl EmitBuilder { /// Emit all of the `VERGEN_GIT_*` instructions pub fn all_git(&mut self) -> &mut Self { self.git_branch() .git_commit_author_email() .git_commit_author_name() .git_commit_count() .git_commit_date() .git_commit_message() .git_commit_timestamp() .git_describe(false, false, None) .git_sha(false) .git_dirty(false) .git_cmd(None) } fn any(&self) -> bool { let cfg = self.git_config; cfg.git_branch || cfg.git_commit_author_email || cfg.git_commit_author_name || cfg.git_commit_count || cfg.git_commit_date || cfg.git_commit_message || cfg.git_commit_timestamp || cfg.git_describe || cfg.git_sha || cfg.git_dirty } /// Emit the current git branch /// /// ```text /// cargo:rustc-env=VERGEN_GIT_BRANCH= /// ``` /// /// The value is determined with the following command /// ```text #[doc = concat!(branch_cmd!())] /// ``` pub fn git_branch(&mut self) -> &mut Self { self.git_config.git_branch = true; self } /// Emit the author email of the most recent commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_EMAIL= /// ``` /// /// The value is determined with the following command /// ```text #[doc = concat!(author_email!())] /// ``` pub fn git_commit_author_email(&mut self) -> &mut Self { self.git_config.git_commit_author_email = true; self } /// Emit the author name of the most recent commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_NAME= /// ``` /// /// The value is determined with the following command /// ```text #[doc = concat!(author_name!())] /// ``` pub fn git_commit_author_name(&mut self) -> &mut Self { self.git_config.git_commit_author_name = true; self } /// Emit the total commit count to HEAD /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_COUNT= /// ``` /// /// The value is determined with the following command /// ```text #[doc = concat!(commit_count!())] /// ``` pub fn git_commit_count(&mut self) -> &mut Self { self.git_config.git_commit_count = true; self } /// Emit the commit date of the latest commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_DATE= /// ``` /// /// The value is determined with the following command /// ```text #[doc = concat!(commit_date!())] /// ``` pub fn git_commit_date(&mut self) -> &mut Self { self.git_config.git_commit_date = true; self } /// Emit the commit message of the latest commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_MESSAGE= /// ``` /// /// The value is determined with the following command /// ```text #[doc = concat!(commit_message!())] /// ``` pub fn git_commit_message(&mut self) -> &mut Self { self.git_config.git_commit_message = true; self } /// Emit the commit timestamp of the latest commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_TIMESTAMP= /// ``` /// /// The value is determined with the following command /// ```text #[doc = concat!(commit_timestamp!())] /// ``` pub fn git_commit_timestamp(&mut self) -> &mut Self { self.git_config.git_commit_timestamp = true; self } /// Emit the describe output /// /// ```text /// cargo:rustc-env=VERGEN_GIT_DESCRIBE= /// ``` /// /// The value is determined with the following command /// ```text #[doc = concat!(describe!())] /// ``` /// /// Optionally, add the `dirty`, `tags`, or `match` flag to describe. /// See [`git describe`](https://git-scm.com/docs/git-describe#_options) for more details /// pub fn git_describe( &mut self, dirty: bool, tags: bool, match_pattern: Option<&'static str>, ) -> &mut Self { self.git_config.git_describe = true; self.git_config.git_describe_dirty = dirty; self.git_config.git_describe_tags = tags; self.git_config.git_describe_match_pattern = match_pattern; self } /// Emit the SHA of the latest commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_SHA= /// ``` /// /// The value is determined with the following command /// ```text #[doc = concat!(sha!(), " HEAD")] /// ``` /// /// Optionally, add the `short` flag to rev-parse. /// See [`git rev-parse`](https://git-scm.com/docs/git-rev-parse#_options_for_output) for more details. /// pub fn git_sha(&mut self, short: bool) -> &mut Self { self.git_config.git_sha = true; self.git_config.git_sha_short = short; self } /// Emit the dirty state of the git repository /// ```text /// cargo:rustc-env=VERGEN_GIT_DIRTY=(true|false) /// ``` /// /// Optionally, include/ignore untracked files in deciding whether the repository /// is dirty. pub fn git_dirty(&mut self, include_untracked: bool) -> &mut Self { self.git_config.git_dirty = true; self.git_config.git_dirty_include_untracked = include_untracked; self } /// Set the command used to test if git exists on the path. /// Defaults to `git --version` if not set explicitly. pub fn git_cmd(&mut self, cmd: Option<&'static str>) -> &mut Self { self.git_config.git_cmd = cmd; self } /// Enable local offset date/timestamp output pub fn use_local_git(&mut self) -> &mut Self { self.git_config.use_local = true; self } pub(crate) fn add_git_default( &self, e: Error, fail_on_error: bool, map: &mut RustcEnvMap, warnings: &mut Vec, rerun_if_changed: &mut Vec, ) -> Result<()> { if fail_on_error { Err(e) } else { // Clear any previous data. We are re-populating // map isn't cleared because keys will overwrite. warnings.clear(); rerun_if_changed.clear(); warnings.push(format!("{e}")); if self.git_config.git_branch { add_default_map_entry(VergenKey::GitBranch, map, warnings); } if self.git_config.git_commit_author_email { add_default_map_entry(VergenKey::GitCommitAuthorEmail, map, warnings); } if self.git_config.git_commit_author_name { add_default_map_entry(VergenKey::GitCommitAuthorName, map, warnings); } if self.git_config.git_commit_count { add_default_map_entry(VergenKey::GitCommitCount, map, warnings); } if self.git_config.git_commit_date { add_default_map_entry(VergenKey::GitCommitDate, map, warnings); } if self.git_config.git_commit_message { add_default_map_entry(VergenKey::GitCommitMessage, map, warnings); } if self.git_config.git_commit_timestamp { add_default_map_entry(VergenKey::GitCommitTimestamp, map, warnings); } if self.git_config.git_describe { add_default_map_entry(VergenKey::GitDescribe, map, warnings); } if self.git_config.git_sha { add_default_map_entry(VergenKey::GitSha, map, warnings); } if self.git_config.git_dirty { add_default_map_entry(VergenKey::GitDirty, map, warnings); } Ok(()) } } pub(crate) fn add_git_map_entries( &self, path: Option, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, rerun_if_changed: &mut Vec, ) -> Result<()> { if self.any() { let git_cmd = self.git_config.git_cmd.unwrap_or("git --version"); check_git(git_cmd).and_then(|()| check_inside_git_worktree(&path))?; self.inner_add_git_map_entries(path, idempotent, map, warnings, rerun_if_changed)?; } Ok(()) } #[allow(clippy::needless_pass_by_value)] fn inner_add_git_map_entries( &self, path: Option, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, rerun_if_changed: &mut Vec, ) -> Result<()> { if !idempotent && self.any() { add_rerun_if_changed(rerun_if_changed, &path)?; } if self.git_config.git_branch { if let Ok(value) = env::var(GIT_BRANCH_NAME) { add_map_entry(VergenKey::GitBranch, value, map); } else { add_git_cmd_entry(BRANCH_CMD, &path, VergenKey::GitBranch, map)?; } } if self.git_config.git_commit_author_email { if let Ok(value) = env::var(GIT_COMMIT_AUTHOR_EMAIL) { add_map_entry(VergenKey::GitCommitAuthorEmail, value, map); } else { add_git_cmd_entry( COMMIT_AUTHOR_EMAIL, &path, VergenKey::GitCommitAuthorEmail, map, )?; } } if self.git_config.git_commit_author_name { if let Ok(value) = env::var(GIT_COMMIT_AUTHOR_NAME) { add_map_entry(VergenKey::GitCommitAuthorName, value, map); } else { add_git_cmd_entry( COMMIT_AUTHOR_NAME, &path, VergenKey::GitCommitAuthorName, map, )?; } } if self.git_config.git_commit_count { if let Ok(value) = env::var(GIT_COMMIT_COUNT) { add_map_entry(VergenKey::GitCommitCount, value, map); } else { add_git_cmd_entry(COMMIT_COUNT, &path, VergenKey::GitCommitCount, map)?; } } self.add_git_timestamp_entries(COMMIT_TIMESTAMP, &path, idempotent, map, warnings)?; if self.git_config.git_commit_message { if let Ok(value) = env::var(GIT_COMMIT_MESSAGE) { add_map_entry(VergenKey::GitCommitMessage, value, map); } else { add_git_cmd_entry(COMMIT_MESSAGE, &path, VergenKey::GitCommitMessage, map)?; } } if self.git_config.git_describe { if let Ok(value) = env::var(GIT_DESCRIBE_NAME) { add_map_entry(VergenKey::GitDescribe, value, map); } else { let mut describe_cmd = String::from(DESCRIBE); if self.git_config.git_describe_dirty { describe_cmd.push_str(" --dirty"); } if self.git_config.git_describe_tags { describe_cmd.push_str(" --tags"); } if let Some(pattern) = self.git_config.git_describe_match_pattern { describe_cmd.push_str(" --match \""); describe_cmd.push_str(pattern); describe_cmd.push('\"'); } add_git_cmd_entry(&describe_cmd, &path, VergenKey::GitDescribe, map)?; } } if self.git_config.git_sha { if let Ok(value) = env::var(GIT_SHA_NAME) { add_map_entry(VergenKey::GitSha, value, map); } else { let mut sha_cmd = String::from(SHA); if self.git_config.git_sha_short { sha_cmd.push_str(" --short"); } sha_cmd.push_str(" HEAD"); add_git_cmd_entry(&sha_cmd, &path, VergenKey::GitSha, map)?; } } if self.git_config.git_dirty { if let Ok(value) = env::var(GIT_DIRTY_NAME) { add_map_entry(VergenKey::GitDirty, value, map); } else { let mut dirty_cmd = String::from(DIRTY); if !self.git_config.git_dirty_include_untracked { dirty_cmd.push_str(" --untracked-files=no"); } let output = run_cmd(&dirty_cmd, &path)?; if output.stdout.is_empty() { add_map_entry(VergenKey::GitDirty, "false", map); } else { add_map_entry(VergenKey::GitDirty, "true", map); } } } Ok(()) } fn add_git_timestamp_entries( &self, cmd: &str, path: &Option, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { let mut date_override = false; if let Ok(value) = env::var(GIT_COMMIT_DATE_NAME) { add_map_entry(VergenKey::GitCommitDate, value, map); date_override = true; } let mut timestamp_override = false; if let Ok(value) = env::var(GIT_COMMIT_TIMESTAMP_NAME) { add_map_entry(VergenKey::GitCommitTimestamp, value, map); timestamp_override = true; } let output = run_cmd(cmd, path)?; if output.status.success() { let stdout = String::from_utf8_lossy(&output.stdout) .lines() .last() .ok_or_else(|| anyhow!("invalid 'git log' output"))? .trim() .trim_matches('\'') .to_string(); let (sde, ts) = match env::var("SOURCE_DATE_EPOCH") { Ok(v) => ( true, OffsetDateTime::from_unix_timestamp(i64::from_str(&v)?)?, ), Err(env::VarError::NotPresent) => { let no_offset = OffsetDateTime::parse(&stdout, &Rfc3339)?; if self.git_config.use_local { let local = UtcOffset::local_offset_at(no_offset)?; let local_offset = no_offset.checked_to_offset(local).unwrap_or(no_offset); (false, local_offset) } else { (false, no_offset) } } Err(e) => return Err(e.into()), }; if idempotent && !sde { if self.git_config.git_commit_date && !date_override { add_default_map_entry(VergenKey::GitCommitDate, map, warnings); } if self.git_config.git_commit_timestamp && !timestamp_override { add_default_map_entry(VergenKey::GitCommitTimestamp, map, warnings); } } else { if self.git_config.git_commit_date && !date_override { let format = format_description::parse("[year]-[month]-[day]")?; add_map_entry(VergenKey::GitCommitDate, ts.format(&format)?, map); } if self.git_config.git_commit_timestamp && !timestamp_override { add_map_entry( VergenKey::GitCommitTimestamp, ts.format(&Iso8601::DEFAULT)?, map, ); } } } else { if self.git_config.git_commit_date && !date_override { add_default_map_entry(VergenKey::GitCommitDate, map, warnings); } if self.git_config.git_commit_timestamp && !timestamp_override { add_default_map_entry(VergenKey::GitCommitTimestamp, map, warnings); } } Ok(()) } } fn check_git(cmd: &str) -> Result<()> { if git_cmd_exists(cmd) { Ok(()) } else { Err(anyhow!("no suitable 'git' command found!")) } } fn check_inside_git_worktree(path: &Option) -> Result<()> { if inside_git_worktree(path) { Ok(()) } else { Err(anyhow!("not within a suitable 'git' worktree!")) } } fn git_cmd_exists(cmd: &str) -> bool { run_cmd(cmd, &None) .map(|output| output.status.success()) .unwrap_or(false) } fn inside_git_worktree(path: &Option) -> bool { run_cmd("git rev-parse --is-inside-work-tree", path) .map(|output| { let stdout = String::from_utf8_lossy(&output.stdout); output.status.success() && stdout.trim() == "true" }) .unwrap_or(false) } #[cfg(not(target_env = "msvc"))] fn run_cmd(command: &str, path_opt: &Option) -> Result { let shell = if let Some(shell_path) = env::var_os("SHELL") { shell_path.to_string_lossy().into_owned() } else { // Fallback to sh if SHELL not defined "sh".to_string() }; let mut cmd = Command::new(shell); if let Some(path) = path_opt { _ = cmd.current_dir(path); } _ = cmd.arg("-c"); _ = cmd.arg(command); _ = cmd.stdout(Stdio::piped()); _ = cmd.stderr(Stdio::piped()); Ok(cmd.output()?) } #[cfg(target_env = "msvc")] fn run_cmd(command: &str, path_opt: &Option) -> Result { let mut cmd = Command::new("cmd"); if let Some(path) = path_opt { _ = cmd.current_dir(path); } _ = cmd.arg("/c"); _ = cmd.arg(command); _ = cmd.stdout(Stdio::piped()); _ = cmd.stderr(Stdio::piped()); Ok(cmd.output()?) } fn add_git_cmd_entry( cmd: &str, path: &Option, key: VergenKey, map: &mut RustcEnvMap, ) -> Result<()> { let output = run_cmd(cmd, path)?; if output.status.success() { let stdout = String::from_utf8_lossy(&output.stdout) .trim() .trim_matches('\'') .to_string(); add_map_entry(key, stdout, map); } else { let stderr = String::from_utf8_lossy(&output.stderr); return Err(anyhow!("Failed to run '{cmd}'! {stderr}")); } Ok(()) } fn add_rerun_if_changed(rerun_if_changed: &mut Vec, path: &Option) -> Result<()> { let git_path = run_cmd("git rev-parse --git-dir", path)?; if git_path.status.success() { let git_path_str = String::from_utf8_lossy(&git_path.stdout).trim().to_string(); let git_path = PathBuf::from(&git_path_str); // Setup the head path let mut head_path = git_path.clone(); head_path.push("HEAD"); if head_path.exists() { rerun_if_changed.push(format!("{}", head_path.display())); } // Setup the ref path let refp = setup_ref_path(path)?; if refp.status.success() { let ref_path_str = String::from_utf8_lossy(&refp.stdout).trim().to_string(); let mut ref_path = git_path; ref_path.push(ref_path_str); if ref_path.exists() { rerun_if_changed.push(format!("{}", ref_path.display())); } } } Ok(()) } #[cfg(not(test))] fn setup_ref_path(path: &Option) -> Result { run_cmd("git symbolic-ref HEAD", path) } #[cfg(all(test, not(target_os = "windows")))] fn setup_ref_path(path: &Option) -> Result { run_cmd("pwd", path) } #[cfg(all(test, target_os = "windows"))] fn setup_ref_path(path: &Option) -> Result { run_cmd("cd", path) } #[cfg(test)] mod test { use super::{add_git_cmd_entry, check_git, check_inside_git_worktree}; use crate::{emitter::test::count_idempotent, key::VergenKey, EmitBuilder}; use anyhow::Result; use repo_util::TestRepos; use std::{collections::BTreeMap, env}; #[test] #[serial_test::serial] fn bad_command_is_error() -> Result<()> { let mut map = BTreeMap::new(); assert!(add_git_cmd_entry( "such_a_terrible_cmd", &None, VergenKey::GitCommitMessage, &mut map ) .is_err()); Ok(()) } #[test] #[serial_test::serial] fn non_working_tree_is_error() -> Result<()> { assert!(check_inside_git_worktree(&Some(env::temp_dir())).is_err()); Ok(()) } #[test] #[serial_test::serial] fn invalid_git_is_error() -> Result<()> { assert!(check_git("such_a_terrible_cmd -v").is_err()); Ok(()) } #[cfg(not(target_family = "windows"))] #[test] #[serial_test::serial] fn shell_env_works() -> Result<()> { temp_env::with_var("SHELL", Some("bash"), || { let mut map = BTreeMap::new(); assert!( add_git_cmd_entry("git -v", &None, VergenKey::GitCommitMessage, &mut map).is_ok() ); }); Ok(()) } #[test] #[serial_test::serial] fn git_all_idempotent() -> Result<()> { let config = EmitBuilder::builder() .idempotent() .all_git() .test_emit_at(None)?; assert_eq!(10, config.cargo_rustc_env_map.len()); assert_eq!(2, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(2, config.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn git_all_idempotent_no_warn() -> Result<()> { let config = EmitBuilder::builder() .idempotent() .quiet() .all_git() .test_emit_at(None)?; assert_eq!(10, config.cargo_rustc_env_map.len()); assert_eq!(2, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(2, config.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn git_all_at_path() -> Result<()> { let repo = TestRepos::new(false, false, false)?; let config = EmitBuilder::builder() .all_git() .test_emit_at(Some(repo.path()))?; assert_eq!(10, config.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(0, config.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn git_all() -> Result<()> { let config = EmitBuilder::builder().all_git().test_emit_at(None)?; assert_eq!(10, config.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(0, config.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn git_all_shallow_clone() -> Result<()> { let repo = TestRepos::new(false, false, true)?; let emitter = EmitBuilder::builder() .all_git() .test_emit_at(Some(repo.path()))?; assert_eq!(10, emitter.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(0, emitter.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn git_all_dirty_tags_short() -> Result<()> { let config = EmitBuilder::builder() .all_git() .git_describe(true, true, None) .git_sha(true) .test_emit()?; assert_eq!(10, config.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(0, config.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn fails_on_bad_git_command() -> Result<()> { let mut config = EmitBuilder::builder(); _ = config.fail_on_error(); _ = config.all_git(); config.git_config.git_cmd = Some("this_is_not_a_git_cmd"); assert!(config.test_emit().is_err()); Ok(()) } #[test] #[serial_test::serial] fn defaults_on_bad_git_command() -> Result<()> { let mut config = EmitBuilder::builder(); _ = config.all_git(); config.git_config.git_cmd = Some("this_is_not_a_git_cmd"); let emitter = config.test_emit()?; assert_eq!(10, emitter.cargo_rustc_env_map.len()); assert_eq!(10, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(11, emitter.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn bad_timestamp_defaults() -> Result<()> { let mut map = BTreeMap::new(); let mut warnings = vec![]; let mut config = EmitBuilder::builder(); _ = config.all_git(); assert!(config .add_git_timestamp_entries( "this_is_not_a_git_cmd", &None, false, &mut map, &mut warnings ) .is_ok()); assert_eq!(2, map.len()); assert_eq!(2, warnings.len()); Ok(()) } } vergen-8.3.2/src/feature/git/git2.rs000064400000000000000000000634411046102023000153540ustar 00000000000000// Copyright (c) 2022 vergen developers // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. use crate::{ constants::{ GIT_BRANCH_NAME, GIT_COMMIT_AUTHOR_EMAIL, GIT_COMMIT_AUTHOR_NAME, GIT_COMMIT_COUNT, GIT_COMMIT_DATE_NAME, GIT_COMMIT_MESSAGE, GIT_COMMIT_TIMESTAMP_NAME, GIT_DESCRIBE_NAME, GIT_DIRTY_NAME, GIT_SHA_NAME, }, emitter::{EmitBuilder, RustcEnvMap}, key::VergenKey, utils::fns::{add_default_map_entry, add_map_entry}, }; #[cfg(test)] use anyhow::anyhow; use anyhow::{Error, Result}; use git2_rs::{ BranchType, Commit, DescribeFormatOptions, DescribeOptions, Reference, Repository, StatusOptions, }; use std::{ env, path::{Path, PathBuf}, str::FromStr, }; use time::{ format_description::{self, well_known::Iso8601}, OffsetDateTime, UtcOffset, }; #[derive(Clone, Copy, Debug, Default)] #[allow(clippy::struct_excessive_bools)] pub(crate) struct Config { // git rev-parse --abbrev-ref HEAD pub(crate) git_branch: bool, // git log -1 --pretty=format:'%an' pub(crate) git_commit_author_name: bool, // git log -1 --pretty=format:'%ae' pub(crate) git_commit_author_email: bool, // git rev-list --count HEAD pub(crate) git_commit_count: bool, // git log -1 --format=%s pub(crate) git_commit_message: bool, // git log -1 --pretty=format:'%cs' pub(crate) git_commit_date: bool, // git log -1 --pretty=format:'%cI' pub(crate) git_commit_timestamp: bool, // git describe --always (optionally --tags, --dirty, --match) pub(crate) git_describe: bool, git_describe_dirty: bool, git_describe_tags: bool, git_describe_match_pattern: Option<&'static str>, // git rev-parse HEAD (optionally with --short) pub(crate) git_sha: bool, git_sha_short: bool, // if output from: // git status --porcelain (optionally with "--untracked-files=no") pub(crate) git_dirty: bool, git_dirty_include_untracked: bool, use_local: bool, #[cfg(test)] fail: bool, } /// The `VERGEN_GIT_*` configuration features /// /// | Variable | Sample | /// | ------- | ------ | /// | `VERGEN_GIT_BRANCH` | feature/fun | /// | `VERGEN_GIT_COMMIT_AUTHOR_EMAIL` | janedoe@email.com | /// | `VERGEN_GIT_COMMIT_AUTHOR_NAME` | Jane Doe | /// | `VERGEN_GIT_COMMIT_COUNT` | 330 | /// | `VERGEN_GIT_COMMIT_DATE` | 2021-02-24 | /// | `VERGEN_GIT_COMMIT_MESSAGE` | feat: add commit messages | /// | `VERGEN_GIT_COMMIT_TIMESTAMP` | 2021-02-24T20:55:21+00:00 | /// | `VERGEN_GIT_DESCRIBE` | 5.0.0-2-gf49246c | /// | `VERGEN_GIT_SHA` | f49246ce334567bff9f950bfd0f3078184a2738a | /// | `VERGEN_GIT_DIRTY` | true | /// /// # Example /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// EmitBuilder::builder().all_git().emit()?; /// # Ok(()) /// # } /// ``` /// /// Override output with your own value /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// env::set_var("VERGEN_GIT_BRANCH", "this is the branch I want output"); /// EmitBuilder::builder().all_git().emit()?; /// # env::remove_var("VERGEN_GIT_BRANCH"); /// # Ok(()) /// # } /// ``` /// #[cfg_attr(docsrs, doc(cfg(feature = "git")))] impl EmitBuilder { /// Emit all of the `VERGEN_GIT_*` instructions pub fn all_git(&mut self) -> &mut Self { self.git_branch() .git_commit_author_email() .git_commit_author_name() .git_commit_count() .git_commit_date() .git_commit_message() .git_commit_timestamp() .git_describe(false, false, None) .git_sha(false) .git_dirty(false) } fn any(&self) -> bool { let cfg = self.git_config; cfg.git_branch || cfg.git_commit_author_email || cfg.git_commit_author_name || cfg.git_commit_count || cfg.git_commit_date || cfg.git_commit_message || cfg.git_commit_timestamp || cfg.git_describe || cfg.git_sha || cfg.git_dirty } /// Emit the current git branch /// /// ```text /// cargo:rustc-env=VERGEN_GIT_BRANCH= /// ``` /// pub fn git_branch(&mut self) -> &mut Self { self.git_config.git_branch = true; self } /// Emit the author email of the most recent commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_EMAIL= /// ``` /// pub fn git_commit_author_email(&mut self) -> &mut Self { self.git_config.git_commit_author_email = true; self } /// Emit the author name of the most recent commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_NAME= /// ``` /// pub fn git_commit_author_name(&mut self) -> &mut Self { self.git_config.git_commit_author_name = true; self } /// Emit the total commit count to HEAD /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_COUNT= /// ``` /// pub fn git_commit_count(&mut self) -> &mut Self { self.git_config.git_commit_count = true; self } /// Emit the commit date of the latest commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_DATE= /// ``` /// pub fn git_commit_date(&mut self) -> &mut Self { self.git_config.git_commit_date = true; self } /// Emit the commit message of the latest commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_MESSAGE= /// ``` /// pub fn git_commit_message(&mut self) -> &mut Self { self.git_config.git_commit_message = true; self } /// Emit the commit timestamp of the latest commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_TIMESTAMP= /// ``` /// pub fn git_commit_timestamp(&mut self) -> &mut Self { self.git_config.git_commit_timestamp = true; self } /// Emit the describe output /// /// ```text /// cargo:rustc-env=VERGEN_GIT_DESCRIBE= /// ``` /// /// Optionally, add the `dirty`, `tags`, or `match` flag to describe. /// See [`git describe`](https://git-scm.com/docs/git-describe#_options) for more details /// pub fn git_describe( &mut self, dirty: bool, tags: bool, match_pattern: Option<&'static str>, ) -> &mut Self { self.git_config.git_describe = true; self.git_config.git_describe_dirty = dirty; self.git_config.git_describe_tags = tags; self.git_config.git_describe_match_pattern = match_pattern; self } /// Emit the SHA of the latest commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_SHA= /// ``` /// /// Optionally, add the `short` flag to rev-parse. /// See [`git rev-parse`](https://git-scm.com/docs/git-rev-parse#_options_for_output) for more details. /// pub fn git_sha(&mut self, short: bool) -> &mut Self { self.git_config.git_sha = true; self.git_config.git_sha_short = short; self } /// Emit the dirty state of the git repository /// ```text /// cargo:rustc-env=VERGEN_GIT_DIRTY=(true|false) /// ``` /// /// Optionally, include/ignore untracked files in deciding whether the repository /// is dirty. pub fn git_dirty(&mut self, include_untracked_files: bool) -> &mut Self { self.git_config.git_dirty = true; self.git_config.git_dirty_include_untracked = include_untracked_files; self } /// Enable local offset date/timestamp output pub fn use_local_git(&mut self) -> &mut Self { self.git_config.use_local = true; self } pub(crate) fn add_git_default( &self, e: Error, fail_on_error: bool, map: &mut RustcEnvMap, warnings: &mut Vec, rerun_if_changed: &mut Vec, ) -> Result<()> { if fail_on_error { Err(e) } else { // Clear any previous warnings. This should be it. warnings.clear(); rerun_if_changed.clear(); warnings.push(format!("{e}")); if self.git_config.git_branch { add_default_map_entry(VergenKey::GitBranch, map, warnings); } if self.git_config.git_commit_author_email { add_default_map_entry(VergenKey::GitCommitAuthorEmail, map, warnings); } if self.git_config.git_commit_author_name { add_default_map_entry(VergenKey::GitCommitAuthorName, map, warnings); } if self.git_config.git_commit_count { add_default_map_entry(VergenKey::GitCommitCount, map, warnings); } if self.git_config.git_commit_date { add_default_map_entry(VergenKey::GitCommitDate, map, warnings); } if self.git_config.git_commit_message { add_default_map_entry(VergenKey::GitCommitMessage, map, warnings); } if self.git_config.git_commit_timestamp { add_default_map_entry(VergenKey::GitCommitTimestamp, map, warnings); } if self.git_config.git_describe { add_default_map_entry(VergenKey::GitDescribe, map, warnings); } if self.git_config.git_sha { add_default_map_entry(VergenKey::GitSha, map, warnings); } if self.git_config.git_dirty { add_default_map_entry(VergenKey::GitDirty, map, warnings); } Ok(()) } } #[cfg(not(test))] pub(crate) fn add_git_map_entries( &self, path: Option, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, rerun_if_changed: &mut Vec, ) -> Result<()> { if self.any() { self.inner_add_git_map_entries(path, idempotent, map, warnings, rerun_if_changed)?; } Ok(()) } #[cfg(test)] pub(crate) fn add_git_map_entries( &self, path: Option, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, rerun_if_changed: &mut Vec, ) -> Result<()> { if self.any() { if self.git_config.fail { return Err(anyhow!("failed to create entries")); } self.inner_add_git_map_entries(path, idempotent, map, warnings, rerun_if_changed)?; } Ok(()) } #[allow(clippy::too_many_lines)] fn inner_add_git_map_entries( &self, path: Option, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, rerun_if_changed: &mut Vec, ) -> Result<()> { let curr_dir = if let Some(path) = path { path } else { env::current_dir()? }; let repo = Repository::discover(curr_dir)?; let ref_head = repo.find_reference("HEAD")?; let git_path = repo.path().to_path_buf(); let commit = ref_head.peel_to_commit()?; if !idempotent && self.any() { self.add_rerun_if_changed(&ref_head, &git_path, rerun_if_changed); } if self.git_config.git_branch { if let Ok(value) = env::var(GIT_BRANCH_NAME) { add_map_entry(VergenKey::GitBranch, value, map); } else { add_branch_name(false, &repo, map, warnings)?; } } if self.git_config.git_commit_author_email { if let Ok(value) = env::var(GIT_COMMIT_AUTHOR_EMAIL) { add_map_entry(VergenKey::GitCommitAuthorEmail, value, map); } else { add_opt_value( commit.author().email(), VergenKey::GitCommitAuthorEmail, map, warnings, ); } } if self.git_config.git_commit_author_name { if let Ok(value) = env::var(GIT_COMMIT_AUTHOR_NAME) { add_map_entry(VergenKey::GitCommitAuthorName, value, map); } else { add_opt_value( commit.author().name(), VergenKey::GitCommitAuthorName, map, warnings, ); } } if self.git_config.git_commit_count { if let Ok(value) = env::var(GIT_COMMIT_COUNT) { add_map_entry(VergenKey::GitCommitCount, value, map); } else { add_commit_count(false, &repo, map, warnings); } } self.add_git_timestamp_entries(&commit, idempotent, map, warnings)?; if self.git_config.git_commit_message { if let Ok(value) = env::var(GIT_COMMIT_MESSAGE) { add_map_entry(VergenKey::GitCommitMessage, value, map); } else { add_opt_value(commit.message(), VergenKey::GitCommitMessage, map, warnings); } } if self.git_config.git_sha { if let Ok(value) = env::var(GIT_SHA_NAME) { add_map_entry(VergenKey::GitSha, value, map); } else if self.git_config.git_sha_short { let obj = repo.revparse_single("HEAD")?; add_opt_value(obj.short_id()?.as_str(), VergenKey::GitSha, map, warnings); } else { add_map_entry(VergenKey::GitSha, commit.id().to_string(), map); } } if self.git_config.git_dirty { if let Ok(value) = env::var(GIT_DIRTY_NAME) { add_map_entry(VergenKey::GitDirty, value, map); } else { let mut status_options = StatusOptions::new(); _ = status_options.include_untracked(self.git_config.git_dirty_include_untracked); let statuses = repo.statuses(Some(&mut status_options))?; let n_dirty = statuses .iter() .filter(|each_status| !each_status.status().is_ignored()) .count(); add_map_entry(VergenKey::GitDirty, format!("{}", n_dirty > 0), map); } } if self.git_config.git_describe { if let Ok(value) = env::var(GIT_DESCRIBE_NAME) { add_map_entry(VergenKey::GitDescribe, value, map); } else { let mut describe_opts = DescribeOptions::new(); let mut format_opts = DescribeFormatOptions::new(); _ = describe_opts.show_commit_oid_as_fallback(true); if self.git_config.git_describe_dirty { _ = format_opts.dirty_suffix("-dirty"); } if self.git_config.git_describe_tags { _ = describe_opts.describe_tags(); } if let Some(pattern) = self.git_config.git_describe_match_pattern { _ = describe_opts.pattern(pattern); } let describe = repo .describe(&describe_opts) .map(|x| x.format(Some(&format_opts)).map_err(Error::from))??; add_map_entry(VergenKey::GitDescribe, describe, map); } } Ok(()) } fn add_git_timestamp_entries( &self, commit: &Commit<'_>, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { let (sde, ts) = match env::var("SOURCE_DATE_EPOCH") { Ok(v) => ( true, OffsetDateTime::from_unix_timestamp(i64::from_str(&v)?)?, ), Err(env::VarError::NotPresent) => { let no_offset = OffsetDateTime::from_unix_timestamp(commit.time().seconds())?; if self.git_config.use_local { let local = UtcOffset::local_offset_at(no_offset)?; let local_offset = no_offset.checked_to_offset(local).unwrap_or(no_offset); (false, local_offset) } else { (false, no_offset) } } Err(e) => return Err(e.into()), }; if let Ok(value) = env::var(GIT_COMMIT_DATE_NAME) { add_map_entry(VergenKey::GitCommitDate, value, map); } else { self.add_git_date_entry(idempotent, sde, &ts, map, warnings)?; } if let Ok(value) = env::var(GIT_COMMIT_TIMESTAMP_NAME) { add_map_entry(VergenKey::GitCommitTimestamp, value, map); } else { self.add_git_timestamp_entry(idempotent, sde, &ts, map, warnings)?; } Ok(()) } fn add_git_date_entry( &self, idempotent: bool, source_date_epoch: bool, ts: &OffsetDateTime, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { if self.git_config.git_commit_date { if idempotent && !source_date_epoch { add_default_map_entry(VergenKey::GitCommitDate, map, warnings); } else { let format = format_description::parse("[year]-[month]-[day]")?; add_map_entry(VergenKey::GitCommitDate, ts.format(&format)?, map); } } Ok(()) } fn add_git_timestamp_entry( &self, idempotent: bool, source_date_epoch: bool, ts: &OffsetDateTime, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { if self.git_config.git_commit_timestamp { if idempotent && !source_date_epoch { add_default_map_entry(VergenKey::GitCommitTimestamp, map, warnings); } else { add_map_entry( VergenKey::GitCommitTimestamp, ts.format(&Iso8601::DEFAULT)?, map, ); } } Ok(()) } #[allow(clippy::unused_self)] fn add_rerun_if_changed( &self, ref_head: &Reference<'_>, git_path: &Path, rerun_if_changed: &mut Vec, ) { // Setup the head path let mut head_path = git_path.to_path_buf(); head_path.push("HEAD"); // Check whether the path exists in the filesystem before emitting it if head_path.exists() { rerun_if_changed.push(format!("{}", head_path.display())); } if let Ok(resolved) = ref_head.resolve() { if let Some(name) = resolved.name() { let ref_path = git_path.to_path_buf(); let path = ref_path.join(name); // Check whether the path exists in the filesystem before emitting it if path.exists() { rerun_if_changed.push(format!("{}", ref_path.display())); } } } } } #[allow(clippy::map_unwrap_or)] fn add_opt_value( value: Option<&str>, key: VergenKey, map: &mut RustcEnvMap, warnings: &mut Vec, ) { value .map(|val| add_map_entry(key, val, map)) .unwrap_or_else(|| add_default_map_entry(key, map, warnings)); } fn add_commit_count( add_default: bool, repo: &Repository, map: &mut RustcEnvMap, warnings: &mut Vec, ) { let key = VergenKey::GitCommitCount; if !add_default { if let Ok(mut revwalk) = repo.revwalk() { if revwalk.push_head().is_ok() { add_map_entry(key, revwalk.count().to_string(), map); return; } } } add_default_map_entry(key, map, warnings); } fn add_branch_name( add_default: bool, repo: &Repository, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { if repo.head_detached()? { if add_default { add_default_map_entry(VergenKey::GitBranch, map, warnings); } else { add_map_entry(VergenKey::GitBranch, "HEAD", map); } } else { let locals = repo.branches(Some(BranchType::Local))?; let mut found_head = false; for (local, _bt) in locals.filter_map(std::result::Result::ok) { if local.is_head() { if let Some(name) = local.name()? { add_map_entry(VergenKey::GitBranch, name, map); found_head = !add_default; break; } } } if !found_head { add_default_map_entry(VergenKey::GitBranch, map, warnings); } } Ok(()) } #[cfg(test)] mod test { use super::{add_branch_name, add_commit_count, add_opt_value}; use crate::{emitter::test::count_idempotent, key::VergenKey, EmitBuilder}; use anyhow::Result; use git2_rs::Repository; use repo_util::TestRepos; use std::{collections::BTreeMap, env, vec}; fn repo_exists() -> Result { let curr_dir = env::current_dir()?; let _repo = Repository::discover(curr_dir)?; Ok(true) } #[test] #[serial_test::serial] fn empty_email_is_default() -> Result<()> { let mut map = BTreeMap::new(); let mut warnings = vec![]; add_opt_value( None, VergenKey::GitCommitAuthorEmail, &mut map, &mut warnings, ); assert_eq!(1, map.len()); assert_eq!(1, warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn bad_revwalk_is_default() -> Result<()> { let mut map = BTreeMap::new(); let mut warnings = vec![]; if let Ok(repo) = Repository::discover(env::current_dir()?) { add_commit_count(true, &repo, &mut map, &mut warnings); assert_eq!(1, map.len()); assert_eq!(1, warnings.len()); } Ok(()) } #[test] #[serial_test::serial] fn head_not_found_is_default() -> Result<()> { let repo = TestRepos::new(false, false, false)?; let mut map = BTreeMap::new(); let mut warnings = vec![]; if let Ok(repo) = Repository::discover(env::current_dir()?) { add_branch_name(true, &repo, &mut map, &mut warnings)?; assert_eq!(1, map.len()); assert_eq!(1, warnings.len()); } let mut map = BTreeMap::new(); let mut warnings = vec![]; if let Ok(repo) = Repository::discover(repo.path()) { add_branch_name(true, &repo, &mut map, &mut warnings)?; assert_eq!(1, map.len()); assert_eq!(1, warnings.len()); } Ok(()) } #[test] #[serial_test::serial] fn git_all_idempotent() -> Result<()> { let config = EmitBuilder::builder() .idempotent() .all_git() .test_emit_at(None)?; assert_eq!(10, config.cargo_rustc_env_map.len()); if repo_exists().is_ok() && !config.failed { assert_eq!(2, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(2, config.warnings.len()); } else { assert_eq!(10, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(11, config.warnings.len()); } Ok(()) } #[test] #[serial_test::serial] fn git_all_shallow_clone() -> Result<()> { let repo = TestRepos::new(false, false, true)?; let emitter = EmitBuilder::builder() .all_git() .test_emit_at(Some(repo.path()))?; assert_eq!(10, emitter.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(0, emitter.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn git_all_idempotent_no_warn() -> Result<()> { let config = EmitBuilder::builder() .idempotent() .quiet() .all_git() .test_emit_at(None)?; assert_eq!(10, config.cargo_rustc_env_map.len()); if repo_exists().is_ok() && !config.failed { assert_eq!(2, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(2, config.warnings.len()); } else { assert_eq!(9, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(10, config.warnings.len()); } Ok(()) } #[test] #[serial_test::serial] fn git_all() -> Result<()> { let config = EmitBuilder::builder().all_git().test_emit_at(None)?; assert_eq!(10, config.cargo_rustc_env_map.len()); if repo_exists().is_ok() && !config.failed { assert_eq!(0, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(0, config.warnings.len()); } else { assert_eq!(9, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(10, config.warnings.len()); } Ok(()) } #[test] #[serial_test::serial] fn git_error_fails() -> Result<()> { let mut config = EmitBuilder::builder(); _ = config.fail_on_error(); _ = config.all_git(); config.git_config.fail = true; assert!(config.test_emit().is_err()); Ok(()) } #[test] #[serial_test::serial] fn git_error_defaults() -> Result<()> { let mut config = EmitBuilder::builder(); _ = config.all_git(); config.git_config.fail = true; let emitter = config.test_emit()?; assert_eq!(10, emitter.cargo_rustc_env_map.len()); assert_eq!(10, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(11, emitter.warnings.len()); Ok(()) } } vergen-8.3.2/src/feature/git/gix.rs000064400000000000000000000537071046102023000153020ustar 00000000000000// Copyright (c) 2022 vergen developers // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. use crate::{ constants::{ GIT_BRANCH_NAME, GIT_COMMIT_AUTHOR_EMAIL, GIT_COMMIT_AUTHOR_NAME, GIT_COMMIT_COUNT, GIT_COMMIT_DATE_NAME, GIT_COMMIT_MESSAGE, GIT_COMMIT_TIMESTAMP_NAME, GIT_DESCRIBE_NAME, GIT_DIRTY_NAME, GIT_SHA_NAME, }, emitter::{EmitBuilder, RustcEnvMap}, key::VergenKey, utils::fns::{add_default_map_entry, add_map_entry}, }; use anyhow::anyhow; use anyhow::{Error, Result}; use gix::{head::Kind, Commit, Head, Id, Repository}; use std::{ env, path::{Path, PathBuf}, str::FromStr, }; use time::{ format_description::{self, well_known::Iso8601}, OffsetDateTime, UtcOffset, }; #[derive(Clone, Copy, Debug, Default)] #[allow(clippy::struct_excessive_bools)] pub(crate) struct Config { // git rev-parse --abbrev-ref HEAD pub(crate) git_branch: bool, // git log -1 --pretty=format:'%an' pub(crate) git_commit_author_name: bool, // git log -1 --pretty=format:'%ae' pub(crate) git_commit_author_email: bool, // git rev-list --count HEAD pub(crate) git_commit_count: bool, // git log -1 --format=%s pub(crate) git_commit_message: bool, // git log -1 --pretty=format:'%cs' pub(crate) git_commit_date: bool, // git log -1 --pretty=format:'%cI' pub(crate) git_commit_timestamp: bool, // git describe --always (optionally --tags, --dirty) pub(crate) git_describe: bool, git_describe_dirty: bool, git_describe_tags: bool, // git rev-parse HEAD (optionally with --short) pub(crate) git_sha: bool, git_sha_short: bool, use_local: bool, // if output from: // git status --porcelain (optionally with "--untracked-files=no") pub(crate) git_dirty: bool, git_dirty_include_untracked: bool, #[cfg(test)] fail: bool, } /// The `VERGEN_GIT_*` configuration features /// /// | Variable | Sample | /// | ------- | ------ | /// | `VERGEN_GIT_BRANCH` | feature/fun | /// | `VERGEN_GIT_COMMIT_AUTHOR_EMAIL` | janedoe@email.com | /// | `VERGEN_GIT_COMMIT_AUTHOR_NAME` | Jane Doe | /// | `VERGEN_GIT_COMMIT_COUNT` | 330 | /// | `VERGEN_GIT_COMMIT_DATE` | 2021-02-24 | /// | `VERGEN_GIT_COMMIT_MESSAGE` | feat: add commit messages | /// | `VERGEN_GIT_COMMIT_TIMESTAMP` | 2021-02-24T20:55:21+00:00 | /// | `VERGEN_GIT_DESCRIBE` | 5.0.0-2-gf49246c | /// | `VERGEN_GIT_SHA` | f49246ce334567bff9f950bfd0f3078184a2738a | /// /// # Example /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// EmitBuilder::builder().all_git().emit()?; /// # Ok(()) /// # } /// ``` /// /// Override output with your own value /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// env::set_var("VERGEN_GIT_BRANCH", "this is the branch I want output"); /// EmitBuilder::builder().all_git().emit()?; /// # env::remove_var("VERGEN_GIT_BRANCH"); /// # Ok(()) /// # } /// ``` /// #[cfg_attr(docsrs, doc(cfg(feature = "git")))] impl EmitBuilder { /// Emit all of the `VERGEN_GIT_*` instructions pub fn all_git(&mut self) -> &mut Self { self.git_branch() .git_commit_author_email() .git_commit_author_name() .git_commit_count() .git_commit_date() .git_commit_message() .git_commit_timestamp() .git_describe(false, false, None) .git_sha(false) .git_dirty(false) } fn any(&self) -> bool { let cfg = &self.git_config; cfg.git_branch || cfg.git_commit_author_email || cfg.git_commit_author_name || cfg.git_commit_count || cfg.git_commit_date || cfg.git_commit_message || cfg.git_commit_timestamp || cfg.git_describe || cfg.git_sha || cfg.git_dirty } /// Emit the current git branch /// /// ```text /// cargo:rustc-env=VERGEN_GIT_BRANCH= /// ``` /// pub fn git_branch(&mut self) -> &mut Self { self.git_config.git_branch = true; self } /// Emit the author email of the most recent commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_EMAIL= /// ``` /// pub fn git_commit_author_email(&mut self) -> &mut Self { self.git_config.git_commit_author_email = true; self } /// Emit the author name of the most recent commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_NAME= /// ``` /// pub fn git_commit_author_name(&mut self) -> &mut Self { self.git_config.git_commit_author_name = true; self } /// Emit the total commit count to HEAD /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_COUNT= /// ``` /// pub fn git_commit_count(&mut self) -> &mut Self { self.git_config.git_commit_count = true; self } /// Emit the commit date of the latest commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_DATE= /// ``` /// pub fn git_commit_date(&mut self) -> &mut Self { self.git_config.git_commit_date = true; self } /// Emit the commit message of the latest commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_MESSAGE= /// ``` /// pub fn git_commit_message(&mut self) -> &mut Self { self.git_config.git_commit_message = true; self } /// Emit the commit timestamp of the latest commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_COMMIT_TIMESTAMP= /// ``` /// pub fn git_commit_timestamp(&mut self) -> &mut Self { self.git_config.git_commit_timestamp = true; self } /// Emit the describe output /// /// ```text /// cargo:rustc-env=VERGEN_GIT_DESCRIBE= /// ``` /// /// Optionally, add the `dirty` or `tags` flag to describe. /// See [`git describe`](https://git-scm.com/docs/git-describe#_options) for more details /// pub fn git_describe( &mut self, dirty: bool, tags: bool, _match_pattern: Option<&'static str>, ) -> &mut Self { self.git_config.git_describe = true; self.git_config.git_describe_dirty = dirty; self.git_config.git_describe_tags = tags; self } /// Emit the SHA of the latest commit /// /// ```text /// cargo:rustc-env=VERGEN_GIT_SHA= /// ``` /// /// Optionally, add the `short` flag to rev-parse. /// See [`git rev-parse`](https://git-scm.com/docs/git-rev-parse#_options_for_output) for more details. /// pub fn git_sha(&mut self, short: bool) -> &mut Self { self.git_config.git_sha = true; self.git_config.git_sha_short = short; self } /// Emit the dirty state of the git repository /// /// ** NOTE ** - Until `gix` has better support for status, this will always /// be false when using the `gix` feature. /// /// ```text /// cargo:rustc-env=VERGEN_GIT_DIRTY=(true|false) /// ``` /// /// Optionally, include/ignore untracked files in deciding whether the repository /// is dirty. pub fn git_dirty(&mut self, include_untracked: bool) -> &mut Self { self.git_config.git_dirty = true; self.git_config.git_dirty_include_untracked = include_untracked; self } pub(crate) fn add_git_default( &self, e: Error, fail_on_error: bool, map: &mut RustcEnvMap, warnings: &mut Vec, rerun_if_changed: &mut Vec, ) -> Result<()> { if fail_on_error { Err(e) } else { // Clear any previous warnings. This should be it. warnings.clear(); rerun_if_changed.clear(); warnings.push(format!("{e}")); if self.git_config.git_branch { add_default_map_entry(VergenKey::GitBranch, map, warnings); } if self.git_config.git_commit_author_email { add_default_map_entry(VergenKey::GitCommitAuthorEmail, map, warnings); } if self.git_config.git_commit_author_name { add_default_map_entry(VergenKey::GitCommitAuthorName, map, warnings); } if self.git_config.git_commit_count { add_default_map_entry(VergenKey::GitCommitCount, map, warnings); } if self.git_config.git_commit_date { add_default_map_entry(VergenKey::GitCommitDate, map, warnings); } if self.git_config.git_commit_message { add_default_map_entry(VergenKey::GitCommitMessage, map, warnings); } if self.git_config.git_commit_timestamp { add_default_map_entry(VergenKey::GitCommitTimestamp, map, warnings); } if self.git_config.git_describe { add_default_map_entry(VergenKey::GitDescribe, map, warnings); } if self.git_config.git_sha { add_default_map_entry(VergenKey::GitSha, map, warnings); } if self.git_config.git_dirty { add_default_map_entry(VergenKey::GitDirty, map, warnings); } Ok(()) } } /// Enable local offset date/timestamp output pub fn use_local_git(&mut self) -> &mut Self { self.git_config.use_local = true; self } #[cfg(not(test))] pub(crate) fn add_git_map_entries( &self, path: Option, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, rerun_if_changed: &mut Vec, ) -> Result<()> { if self.any() { self.inner_add_git_map_entries(path, idempotent, map, warnings, rerun_if_changed)?; } Ok(()) } #[cfg(test)] pub(crate) fn add_git_map_entries( &self, path: Option, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, rerun_if_changed: &mut Vec, ) -> Result<()> { if self.any() { if self.git_config.fail { return Err(anyhow!("failed to create entries")); } self.inner_add_git_map_entries(path, idempotent, map, warnings, rerun_if_changed)?; } Ok(()) } #[allow(clippy::unnecessary_wraps)] fn inner_add_git_map_entries( &self, path: Option, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, rerun_if_changed: &mut Vec, ) -> Result<()> { let curr_dir = if let Some(path) = path { path } else { env::current_dir()? }; let repo = gix::discover(curr_dir)?; let mut head = repo.head()?; let git_path = repo.git_dir().to_path_buf(); let commit = Self::get_commit(&repo, &mut head)?; if !idempotent && self.any() { self.add_rerun_if_changed(&head, &git_path, rerun_if_changed); } if self.git_config.git_branch { if let Ok(value) = env::var(GIT_BRANCH_NAME) { add_map_entry(VergenKey::GitBranch, value, map); } else { let branch_name = head .referent_name() .map_or_else(|| "HEAD".to_string(), |name| format!("{}", name.shorten())); add_map_entry(VergenKey::GitBranch, branch_name, map); } } if self.git_config.git_commit_author_email { if let Ok(value) = env::var(GIT_COMMIT_AUTHOR_EMAIL) { add_map_entry(VergenKey::GitCommitAuthorEmail, value, map); } else { let email = String::from_utf8_lossy(commit.author()?.email); add_map_entry(VergenKey::GitCommitAuthorEmail, email.into_owned(), map); } } if self.git_config.git_commit_author_name { if let Ok(value) = env::var(GIT_COMMIT_AUTHOR_NAME) { add_map_entry(VergenKey::GitCommitAuthorName, value, map); } else { let name = String::from_utf8_lossy(commit.author()?.name); add_map_entry(VergenKey::GitCommitAuthorName, name.into_owned(), map); } } if self.git_config.git_commit_count { if let Ok(value) = env::var(GIT_COMMIT_COUNT) { add_map_entry(VergenKey::GitCommitCount, value, map); } else { add_map_entry( VergenKey::GitCommitCount, commit.ancestors().all()?.count().to_string(), map, ); } } self.add_git_timestamp_entries(&commit, idempotent, map, warnings)?; if self.git_config.git_commit_message { if let Ok(value) = env::var(GIT_COMMIT_MESSAGE) { add_map_entry(VergenKey::GitCommitMessage, value, map); } else { let message = String::from_utf8_lossy(commit.message_raw()?); add_map_entry( VergenKey::GitCommitMessage, message.into_owned().trim(), map, ); } } if self.git_config.git_describe { if let Ok(value) = env::var(GIT_DESCRIBE_NAME) { add_map_entry(VergenKey::GitDescribe, value, map); } else { let describe = if let Some(mut fmt) = commit.describe().try_format()? { if fmt.depth > 0 && self.git_config.git_describe_dirty { fmt.dirty_suffix = Some("dirty".to_string()); } fmt.to_string() } else { String::new() }; add_map_entry(VergenKey::GitDescribe, describe, map); } } if self.git_config.git_sha { if let Ok(value) = env::var(GIT_SHA_NAME) { add_map_entry(VergenKey::GitSha, value, map); } else { let id = if self.git_config.git_sha_short { commit.short_id()?.to_string() } else { commit.id().to_string() }; add_map_entry(VergenKey::GitSha, id, map); } } if self.git_config.git_dirty { if let Ok(value) = env::var(GIT_DIRTY_NAME) { add_map_entry(VergenKey::GitDirty, value, map); } else { add_map_entry(VergenKey::GitDirty, "false", map); } } Ok(()) } fn get_commit<'a>(repo: &Repository, head: &mut Head<'a>) -> Result> { Ok(if repo.is_shallow() { let id = Self::get_id(head)?.ok_or_else(|| anyhow!("Not an Id"))?; let object = id.try_object()?.ok_or_else(|| anyhow!("Not an Object"))?; object.try_into_commit()? } else { head.peel_to_commit_in_place()? }) } fn get_id<'a>(head: &mut Head<'a>) -> Result>> { head.try_peel_to_id_in_place().map_err(Into::into) } fn add_git_timestamp_entries( &self, commit: &Commit<'_>, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { let (sde, ts) = match env::var("SOURCE_DATE_EPOCH") { Ok(v) => ( true, OffsetDateTime::from_unix_timestamp(i64::from_str(&v)?)?, ), Err(env::VarError::NotPresent) => { let no_offset = OffsetDateTime::from_unix_timestamp(commit.time()?.seconds)?; if self.git_config.use_local { let local = UtcOffset::local_offset_at(no_offset)?; let local_offset = no_offset.checked_to_offset(local).unwrap_or(no_offset); (false, local_offset) } else { (false, no_offset) } } Err(e) => return Err(e.into()), }; if let Ok(value) = env::var(GIT_COMMIT_DATE_NAME) { add_map_entry(VergenKey::GitCommitDate, value, map); } else { self.add_git_date_entry(idempotent, sde, &ts, map, warnings)?; } if let Ok(value) = env::var(GIT_COMMIT_TIMESTAMP_NAME) { add_map_entry(VergenKey::GitCommitTimestamp, value, map); } else { self.add_git_timestamp_entry(idempotent, sde, &ts, map, warnings)?; } Ok(()) } fn add_git_date_entry( &self, idempotent: bool, source_date_epoch: bool, ts: &OffsetDateTime, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { if self.git_config.git_commit_date { if idempotent && !source_date_epoch { add_default_map_entry(VergenKey::GitCommitDate, map, warnings); } else { let format = format_description::parse("[year]-[month]-[day]")?; add_map_entry(VergenKey::GitCommitDate, ts.format(&format)?, map); } } Ok(()) } fn add_git_timestamp_entry( &self, idempotent: bool, source_date_epoch: bool, ts: &OffsetDateTime, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { if self.git_config.git_commit_timestamp { if idempotent && !source_date_epoch { add_default_map_entry(VergenKey::GitCommitTimestamp, map, warnings); } else { add_map_entry( VergenKey::GitCommitTimestamp, ts.format(&Iso8601::DEFAULT)?, map, ); } } Ok(()) } #[allow(clippy::unused_self)] fn add_rerun_if_changed( &self, head: &Head<'_>, git_path: &Path, rerun_if_changed: &mut Vec, ) { // Setup the head path let mut head_path = git_path.to_path_buf(); head_path.push("HEAD"); // Check whether the path exists in the filesystem before emitting it if head_path.exists() { rerun_if_changed.push(format!("{}", head_path.display())); } if let Kind::Symbolic(reference) = &head.kind { let mut ref_path = git_path.to_path_buf(); ref_path.push(reference.name.to_path()); // Check whether the path exists in the filesystem before emitting it if ref_path.exists() { rerun_if_changed.push(format!("{}", ref_path.display())); } } } } #[cfg(test)] mod test { use crate::{emitter::test::count_idempotent, EmitBuilder}; use anyhow::Result; use repo_util::TestRepos; use serial_test::serial; #[test] #[serial_test::serial] fn git_all_idempotent() -> Result<()> { let emitter = EmitBuilder::builder() .idempotent() .all_git() .test_emit_at(None)?; assert_eq!(10, emitter.cargo_rustc_env_map.len()); assert_eq!(2, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(2, emitter.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn git_all_idempotent_no_warn() -> Result<()> { let emitter = EmitBuilder::builder() .idempotent() .quiet() .all_git() .test_emit_at(None)?; assert_eq!(10, emitter.cargo_rustc_env_map.len()); assert_eq!(2, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(2, emitter.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn git_all() -> Result<()> { let emitter = EmitBuilder::builder().all_git().test_emit_at(None)?; assert_eq!(10, emitter.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(0, emitter.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn git_all_at_path() -> Result<()> { let repo = TestRepos::new(false, false, false)?; let emitter = EmitBuilder::builder() .all_git() .test_emit_at(Some(repo.path()))?; assert_eq!(10, emitter.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(0, emitter.warnings.len()); Ok(()) } #[test] #[serial] fn git_all_shallow_clone() -> Result<()> { let repo = TestRepos::new(false, false, true)?; let emitter = EmitBuilder::builder() .all_git() .test_emit_at(Some(repo.path()))?; assert_eq!(10, emitter.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(0, emitter.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn git_error_fails() -> Result<()> { let mut config = EmitBuilder::builder(); _ = config.fail_on_error(); _ = config.all_git(); config.git_config.fail = true; assert!(config.test_emit().is_err()); Ok(()) } #[test] #[serial_test::serial] fn git_error_defaults() -> Result<()> { let mut config = EmitBuilder::builder(); _ = config.all_git(); config.git_config.fail = true; let emitter = config.test_emit()?; assert_eq!(10, emitter.cargo_rustc_env_map.len()); assert_eq!(10, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(11, emitter.warnings.len()); Ok(()) } } vergen-8.3.2/src/feature/git/mod.rs000064400000000000000000000013221046102023000152540ustar 00000000000000// Copyright (c) 2022 vergen developers // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. use cfg_if::cfg_if; cfg_if! { if #[cfg(feature = "gitcl")] { pub(crate) mod cmd; pub(crate) use self::cmd::Config; } else if #[cfg(feature = "git2")] { pub(crate) mod git2; pub(crate) use self::git2::Config; } else { pub(crate) mod gix; pub(crate) use self::gix::Config; } } vergen-8.3.2/src/feature/mod.rs000064400000000000000000000011441046102023000144730ustar 00000000000000// Copyright (c) 2022 vergen developers // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. #[cfg(feature = "build")] pub(crate) mod build; #[cfg(feature = "cargo")] pub(crate) mod cargo; #[cfg(feature = "git")] pub(crate) mod git; #[cfg(feature = "rustc")] pub(crate) mod rustc; #[cfg(feature = "si")] pub(crate) mod si; vergen-8.3.2/src/feature/rustc.rs000064400000000000000000000371241046102023000150630ustar 00000000000000use crate::{ constants::{ RUSTC_CHANNEL_NAME, RUSTC_COMMIT_DATE, RUSTC_COMMIT_HASH, RUSTC_HOST_TRIPLE_NAME, RUSTC_LLVM_VERSION, RUSTC_SEMVER_NAME, }, emitter::{EmitBuilder, RustcEnvMap}, key::VergenKey, utils::fns::{add_default_map_entry, add_map_entry}, }; use anyhow::{Error, Result}; use rustc_version::{version_meta, Channel, VersionMeta}; use std::env; #[derive(Clone, Copy, Debug, Default)] #[allow(clippy::struct_excessive_bools, clippy::struct_field_names)] pub(crate) struct Config { pub(crate) rustc_channel: bool, pub(crate) rustc_commit_date: bool, pub(crate) rustc_commit_hash: bool, pub(crate) rustc_host_triple: bool, pub(crate) rustc_llvm_version: bool, pub(crate) rustc_semver: bool, #[cfg(test)] rustc_str_to_test: Option<&'static str>, } impl Config { pub(crate) fn any(self) -> bool { self.rustc_channel || self.rustc_commit_date || self.rustc_commit_hash || self.rustc_host_triple || self.rustc_llvm_version || self.rustc_semver } } /// The `VERGEN_RUSTC_*` configuration features /// /// **NOTE** - All rustc instructions are considered deterministic. If you change /// the version of rustc you are compiling with, these values should change if /// being used in the generated binary. /// /// | Variable | Sample | /// | ------- | ------ | /// | `VERGEN_RUSTC_CHANNEL` | nightly | /// | `VERGEN_RUSTC_COMMIT_DATE` | 2021-02-24 | /// | `VERGEN_RUSTC_COMMIT_HASH` | a8486b64b0c87dabd045453b6c81500015d122d6 | /// | `VERGEN_RUSTC_HOST_TRIPLE` | x86_64-apple-darwin | /// | `VERGEN_RUSTC_LLVM_VERSION` | 11.0 | /// | `VERGEN_RUSTC_SEMVER` | 1.52.0-nightly | /// /// # Example /// Emit all of the rustc instructions /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// EmitBuilder::builder().all_rustc().emit()?; /// # Ok(()) /// # } /// ``` /// /// Emit some of the rustc instructions /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// EmitBuilder::builder() /// .rustc_channel() /// .rustc_semver() /// .emit()?; /// # Ok(()) /// # } /// ``` /// /// Override output with your own value /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// env::set_var("VERGEN_RUSTC_CHANNEL", "this is the channel I want output"); /// EmitBuilder::builder().all_rustc().emit()?; /// # env::remove_var("VERGEN_BUILD_CHANNEL"); /// # Ok(()) /// # } /// ``` /// #[cfg_attr(docsrs, doc(cfg(feature = "rustc")))] impl EmitBuilder { /// Enable all of the `VERGEN_RUSTC_*` options pub fn all_rustc(&mut self) -> &mut Self { self.rustc_channel() .rustc_commit_date() .rustc_commit_hash() .rustc_host_triple() .rustc_llvm_version() .rustc_semver() } /// Enable the rustc channel pub fn rustc_channel(&mut self) -> &mut Self { self.rustc_config.rustc_channel = true; self } /// Enable the rustc commit date pub fn rustc_commit_date(&mut self) -> &mut Self { self.rustc_config.rustc_commit_date = true; self } /// Enable the rustc SHA pub fn rustc_commit_hash(&mut self) -> &mut Self { self.rustc_config.rustc_commit_hash = true; self } /// Enable rustc host triple pub fn rustc_host_triple(&mut self) -> &mut Self { self.rustc_config.rustc_host_triple = true; self } /// Enable rustc LLVM version pub fn rustc_llvm_version(&mut self) -> &mut Self { self.rustc_config.rustc_llvm_version = true; self } /// Enable the rustc semver pub fn rustc_semver(&mut self) -> &mut Self { self.rustc_config.rustc_semver = true; self } pub(crate) fn add_rustc_default( &self, e: Error, fail_on_error: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { if fail_on_error { Err(e) } else { if self.rustc_config.rustc_channel { add_default_map_entry(VergenKey::RustcChannel, map, warnings); } if self.rustc_config.rustc_commit_date { add_default_map_entry(VergenKey::RustcCommitDate, map, warnings); } if self.rustc_config.rustc_commit_hash { add_default_map_entry(VergenKey::RustcCommitHash, map, warnings); } if self.rustc_config.rustc_host_triple { add_default_map_entry(VergenKey::RustcHostTriple, map, warnings); } if self.rustc_config.rustc_llvm_version { add_default_map_entry(VergenKey::RustcLlvmVersion, map, warnings); } if self.rustc_config.rustc_semver { add_default_map_entry(VergenKey::RustcSemver, map, warnings); } Ok(()) } } #[cfg(not(test))] pub(crate) fn add_rustc_map_entries( &self, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { if self.rustc_config.any() { self.add_rustc_to_map(version_meta(), map, warnings)?; } Ok(()) } #[cfg(test)] pub(crate) fn add_rustc_map_entries( &self, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { use rustc_version::version_meta_for; let vm = if let Some(rustc_str) = self.rustc_config.rustc_str_to_test { version_meta_for(rustc_str) } else { version_meta() }; if self.rustc_config.any() { self.add_rustc_to_map(vm, map, warnings)?; } Ok(()) } fn add_rustc_to_map( &self, rustc_res: std::result::Result, map: &mut RustcEnvMap, warnings: &mut Vec, ) -> Result<()> { let rustc = rustc_res?; if self.rustc_config.rustc_channel { if let Ok(value) = env::var(RUSTC_CHANNEL_NAME) { add_map_entry(VergenKey::RustcChannel, value, map); } else { let channel = match rustc.channel { Channel::Dev => "dev", Channel::Nightly => "nightly", Channel::Beta => "beta", Channel::Stable => "stable", }; add_map_entry(VergenKey::RustcChannel, channel, map); } } if self.rustc_config.rustc_commit_date { if let Ok(value) = env::var(RUSTC_COMMIT_DATE) { add_map_entry(VergenKey::RustcCommitDate, value, map); } else if let Some(commit_date) = rustc.commit_date { add_map_entry(VergenKey::RustcCommitDate, commit_date, map); } else { add_default_map_entry(VergenKey::RustcCommitDate, map, warnings); } } if self.rustc_config.rustc_commit_hash { if let Ok(value) = env::var(RUSTC_COMMIT_HASH) { add_map_entry(VergenKey::RustcCommitHash, value, map); } else if let Some(commit_hash) = rustc.commit_hash { add_map_entry(VergenKey::RustcCommitHash, commit_hash, map); } else { add_default_map_entry(VergenKey::RustcCommitHash, map, warnings); } } if self.rustc_config.rustc_host_triple { if let Ok(value) = env::var(RUSTC_HOST_TRIPLE_NAME) { add_map_entry(VergenKey::RustcHostTriple, value, map); } else { add_map_entry(VergenKey::RustcHostTriple, rustc.host, map); } } if self.rustc_config.rustc_llvm_version { if let Ok(value) = env::var(RUSTC_LLVM_VERSION) { add_map_entry(VergenKey::RustcLlvmVersion, value, map); } else if let Some(llvm_version) = rustc.llvm_version { add_map_entry(VergenKey::RustcLlvmVersion, format!("{llvm_version}"), map); } else { add_default_map_entry(VergenKey::RustcLlvmVersion, map, warnings); } } if self.rustc_config.rustc_semver { if let Ok(value) = env::var(RUSTC_SEMVER_NAME) { add_map_entry(VergenKey::RustcSemver, value, map); } else { add_map_entry(VergenKey::RustcSemver, format!("{}", rustc.semver), map); } } Ok(()) } } #[cfg(test)] mod test { use crate::{emitter::test::count_idempotent, EmitBuilder}; use anyhow::Result; use std::env; #[test] #[serial_test::serial] fn rustc_all_idempotent() -> Result<()> { let config = EmitBuilder::builder() .idempotent() .all_rustc() .test_emit()?; assert_eq!(6, config.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(0, config.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn rustc_all() -> Result<()> { let config = EmitBuilder::builder().all_rustc().test_emit()?; assert_eq!(6, config.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(0, config.warnings.len()); Ok(()) } const NO_LLVM: &str = r"rustc 1.68.0-nightly (270c94e48 2022-12-28) binary: rustc commit-hash: 270c94e484e19764a2832ef918c95224eb3f17c7 commit-date: 2022-12-28 host: x86_64-unknown-linux-gnu release: 1.68.0-nightly "; #[test] #[serial_test::serial] fn no_llvm_in_rustc() -> Result<()> { let mut config = EmitBuilder::builder(); _ = config.fail_on_error(); _ = config.all_rustc(); config.rustc_config.rustc_str_to_test = Some(NO_LLVM); let emitter = config.test_emit()?; assert_eq!(6, emitter.cargo_rustc_env_map.len()); assert_eq!(1, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(1, emitter.warnings.len()); Ok(()) } const DEV_BUILD: &str = r"rustc 1.68.0-nightly (270c94e48 2022-12-28) binary: rustc commit-hash: 270c94e484e19764a2832ef918c95224eb3f17c7 commit-date: 2022-12-28 host: x86_64-unknown-linux-gnu release: 1.68.0-dev LLVM version: 15.0.6 "; #[test] #[serial_test::serial] fn rustc_dev_build() -> Result<()> { let mut config = EmitBuilder::builder(); _ = config.fail_on_error(); _ = config.all_rustc(); config.rustc_config.rustc_str_to_test = Some(DEV_BUILD); let emitter = config.test_emit()?; assert_eq!(6, emitter.cargo_rustc_env_map.len()); assert_eq!(0, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(0, emitter.warnings.len()); Ok(()) } const UNKNOWN_BITS: &str = r"rustc 1.68.0-nightly (270c94e48 2022-12-28) binary: rustc commit-hash: unknown commit-date: unknown host: x86_64-unknown-linux-gnu release: 1.68.0-dev LLVM version: 15.0.6 "; #[test] #[serial_test::serial] fn rustc_unknown_bits() -> Result<()> { let mut config = EmitBuilder::builder(); _ = config.fail_on_error(); _ = config.all_rustc(); config.rustc_config.rustc_str_to_test = Some(UNKNOWN_BITS); let emitter = config.test_emit()?; assert_eq!(6, emitter.cargo_rustc_env_map.len()); assert_eq!(2, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(2, emitter.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn rustc_fails_on_bad_input() -> Result<()> { let mut config = EmitBuilder::builder(); _ = config.fail_on_error(); _ = config.all_rustc(); config.rustc_config.rustc_str_to_test = Some("a_bad_rustcvv_string"); assert!(config.test_emit().is_err()); Ok(()) } #[test] #[serial_test::serial] fn rustc_defaults_on_bad_input() -> Result<()> { let mut config = EmitBuilder::builder(); _ = config.all_rustc(); config.rustc_config.rustc_str_to_test = Some("a_bad_rustcvv_string"); let emitter = config.test_emit()?; assert_eq!(6, emitter.cargo_rustc_env_map.len()); assert_eq!(6, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(6, emitter.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn rustc_channel_override_works() -> Result<()> { env::set_var("VERGEN_RUSTC_CHANNEL", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_rustc() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_RUSTC_CHANNEL=this is a bad date")); env::remove_var("VERGEN_RUSTC_CHANNEL"); Ok(()) } #[test] #[serial_test::serial] fn rustc_commit_date_override_works() -> Result<()> { env::set_var("VERGEN_RUSTC_COMMIT_DATE", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_rustc() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_RUSTC_COMMIT_DATE=this is a bad date")); env::remove_var("VERGEN_RUSTC_COMMIT_DATE"); Ok(()) } #[test] #[serial_test::serial] fn rustc_commit_hash_override_works() -> Result<()> { env::set_var("VERGEN_RUSTC_COMMIT_HASH", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_rustc() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_RUSTC_COMMIT_HASH=this is a bad date")); env::remove_var("VERGEN_RUSTC_COMMIT_HASH"); Ok(()) } #[test] #[serial_test::serial] fn rustc_host_triple_override_works() -> Result<()> { env::set_var("VERGEN_RUSTC_HOST_TRIPLE", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_rustc() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_RUSTC_HOST_TRIPLE=this is a bad date")); env::remove_var("VERGEN_RUSTC_HOST_TRIPLE"); Ok(()) } #[test] #[serial_test::serial] fn rustc_llvm_version_override_works() -> Result<()> { env::set_var("VERGEN_RUSTC_LLVM_VERSION", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_rustc() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_RUSTC_LLVM_VERSION=this is a bad date")); env::remove_var("VERGEN_RUSTC_LLVM_VERSION"); Ok(()) } #[test] #[serial_test::serial] fn rustc_semver_override_works() -> Result<()> { env::set_var("VERGEN_RUSTC_SEMVER", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_rustc() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_RUSTC_SEMVER=this is a bad date")); env::remove_var("VERGEN_RUSTC_SEMVER"); Ok(()) } } vergen-8.3.2/src/feature/si.rs000064400000000000000000000623761046102023000143450ustar 00000000000000// Copyright (c) 2022 vergen developers // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. use crate::{ constants::{ SYSINFO_CPU_BRAND, SYSINFO_CPU_CORE_COUNT, SYSINFO_CPU_FREQUENCY, SYSINFO_CPU_NAME, SYSINFO_CPU_VENDOR, SYSINFO_MEMORY, SYSINFO_NAME, SYSINFO_OS_VERSION, SYSINFO_USER, }, emitter::{EmitBuilder, RustcEnvMap}, key::VergenKey, utils::fns::{add_default_map_entry, add_map_entry}, }; use anyhow::{anyhow, Result}; use std::env; use sysinfo::{get_current_pid, Cpu, Pid, Process, RefreshKind, System, User, Users}; #[derive(Clone, Copy, Debug, Default)] #[allow(clippy::struct_excessive_bools, clippy::struct_field_names)] pub(crate) struct Config { pub(crate) si_refresh_kind: Option, pub(crate) si_name: bool, pub(crate) si_os_version: bool, pub(crate) si_user: bool, pub(crate) si_memory: bool, pub(crate) si_cpu_vendor: bool, pub(crate) si_cpu_core_count: bool, pub(crate) si_cpu_name: bool, pub(crate) si_cpu_brand: bool, pub(crate) si_cpu_frequency: bool, #[cfg(test)] fail_pid: bool, } impl Config { pub(crate) fn any(&self) -> bool { self.si_name || self.si_os_version || self.si_user || self.si_memory || self.si_cpu_vendor || self.si_cpu_core_count || self.si_cpu_name || self.si_cpu_brand || self.si_cpu_frequency } } /// The `VERGEN_SYSINFO_*` configuration features /// /// | Variable | Sample | /// | ------- | ------ | /// | `VERGEN_SYSINFO_NAME` | Manjaro Linux | /// | `VERGEN_SYSINFO_OS_VERSION` | Linux Manjaro Linux | /// | `VERGEN_SYSINFO_USER` | Yoda | /// | `VERGEN_SYSINFO_TOTAL_MEMORY` | 33 GB | /// | `VERGEN_SYSINFO_CPU_VENDOR` | Authentic AMD | /// | `VERGEN_SYSINFO_CPU_CORE_COUNT` | 8 | /// | `VERGEN_SYSINFO_CPU_NAME` | cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,cpu6,cpu7 | /// | `VERGEN_SYSINFO_CPU_BRAND` | AMD Ryzen Threadripper 1900X 8-Core Processor | /// | `VERGEN_SYSINFO_CPU_FREQUENCY` | 3792 | /// /// # Example /// Emit all sysinfo instructions /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// EmitBuilder::builder().all_sysinfo().emit()?; /// # Ok(()) /// # } /// ``` /// /// Emit some of the sysinfo instructions /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// EmitBuilder::builder() /// .sysinfo_os_version() /// .sysinfo_cpu_core_count() /// .emit()?; /// # Ok(()) /// # } /// ``` /// /// Override output with your own value /// /// ``` /// # use anyhow::Result; /// # use std::env; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { /// env::set_var("VERGEN_SYSINFO_NAME", "this is the name I want output"); /// EmitBuilder::builder().all_sysinfo().emit()?; /// # env::remove_var("VERGEN_SYSINFO_NAME"); /// # Ok(()) /// # } /// ``` /// /// # Example /// This feature also recognizes the idempotent flag. /// /// ``` /// # use anyhow::Result; /// # use vergen::EmitBuilder; /// # /// # fn main() -> Result<()> { #[cfg_attr( feature = "sysinfo", doc = r##" EmitBuilder::builder().idempotent().all_sysinfo().emit()?; "## )] /// # Ok(()) /// # } /// ``` /// /// The above will always generate the following output /// /// ```text /// cargo:rustc-env=VERGEN_SYSINFO_NAME=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_SYSINFO_OS_VERSION=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_SYSINFO_USER=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_SYSINFO_TOTAL_MEMORY=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_SYSINFO_CPU_VENDOR=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_SYSINFO_CPU_CORE_COUNT=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_SYSINFO_CPU_NAME=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_SYSINFO_CPU_BRAND=VERGEN_IDEMPOTENT_OUTPUT /// cargo:rustc-env=VERGEN_SYSINFO_CPU_FREQUENCY=VERGEN_IDEMPOTENT_OUTPUT /// cargo:warning=VERGEN_SYSINFO_NAME set to default /// cargo:warning=VERGEN_SYSINFO_OS_VERSION set to default /// cargo:warning=VERGEN_SYSINFO_USER set to default /// cargo:warning=VERGEN_SYSINFO_TOTAL_MEMORY set to default /// cargo:warning=VERGEN_SYSINFO_CPU_VENDOR set to default /// cargo:warning=VERGEN_SYSINFO_CPU_CORE_COUNT set to default /// cargo:warning=VERGEN_SYSINFO_CPU_NAME set to default /// cargo:warning=VERGEN_SYSINFO_CPU_BRAND set to default /// cargo:warning=VERGEN_SYSINFO_CPU_FREQUENCY set to default /// cargo:rerun-if-changed=build.rs /// cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT /// cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH /// ``` /// #[cfg_attr(docsrs, doc(cfg(feature = "si")))] impl EmitBuilder { /// Enable all of the `VERGEN_SYSINFO_*` options pub fn all_sysinfo(&mut self) -> &mut Self { self.sysinfo_refresh_kind(None) .sysinfo_name() .sysinfo_os_version() .sysinfo_user() .sysinfo_memory() .sysinfo_cpu_vendor() .sysinfo_cpu_core_count() .sysinfo_cpu_name() .sysinfo_cpu_brand() .sysinfo_cpu_frequency() } /// Set the [`RefreshKind`](sysinfo::RefreshKind) to use during sysinfo initialization. /// /// This allows the user to control at a more fine level what `sysinfo` /// will refresh on initialization. /// /// # Example /// ``` /// # use anyhow::Result; /// # #[cfg(feature = "si")] /// # use sysinfo::{CpuRefreshKind, RefreshKind}; /// # use vergen::EmitBuilder; /// # /// # pub fn main() -> Result<()> { #[cfg_attr( feature = "si", doc = r##" let refresh_kind = RefreshKind::new(); let cpu_refresh_kind = CpuRefreshKind::everything() .without_cpu_usage() .without_frequency(); let config = EmitBuilder::builder() .sysinfo_refresh_kind(Some(refresh_kind.with_cpu(cpu_refresh_kind))) .sysinfo_cpu_brand() .emit()?; "## )] /// # Ok(()) /// # } /// ``` pub fn sysinfo_refresh_kind(&mut self, refresh_kind: Option) -> &mut Self { self.sysinfo_config.si_refresh_kind = refresh_kind; self } /// Enable the sysinfo name pub fn sysinfo_name(&mut self) -> &mut Self { self.sysinfo_config.si_name = true; self } /// Enable the sysinfo OS version pub fn sysinfo_os_version(&mut self) -> &mut Self { self.sysinfo_config.si_os_version = true; self } /// Enable sysinfo user pub fn sysinfo_user(&mut self) -> &mut Self { self.sysinfo_config.si_user = true; self } /// Enable sysinfo memory pub fn sysinfo_memory(&mut self) -> &mut Self { self.sysinfo_config.si_memory = true; self } /// Enable sysinfo cpu vendor pub fn sysinfo_cpu_vendor(&mut self) -> &mut Self { self.sysinfo_config.si_cpu_vendor = true; self } /// Enable sysinfo cpu core count pub fn sysinfo_cpu_core_count(&mut self) -> &mut Self { self.sysinfo_config.si_cpu_core_count = true; self } /// Enable sysinfo cpu name pub fn sysinfo_cpu_name(&mut self) -> &mut Self { self.sysinfo_config.si_cpu_name = true; self } /// Enable sysinfo cpu brand pub fn sysinfo_cpu_brand(&mut self) -> &mut Self { self.sysinfo_config.si_cpu_brand = true; self } /// Enable sysinfo cpu frequency pub fn sysinfo_cpu_frequency(&mut self) -> &mut Self { self.sysinfo_config.si_cpu_frequency = true; self } pub(crate) fn add_sysinfo_map_entries( &self, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) { if self.sysinfo_config.any() { let system = setup_system(self.sysinfo_config.si_refresh_kind); self.add_sysinfo_name(&system, idempotent, map, warnings); self.add_sysinfo_os_verison(&system, idempotent, map, warnings); self.add_sysinfo_user(&system, idempotent, map, warnings); self.add_sysinfo_total_memory(&system, idempotent, map, warnings); self.add_sysinfo_cpu_vendor(&system, idempotent, map, warnings); self.add_sysinfo_cpu_core_count(&system, idempotent, map, warnings); self.add_sysinfo_cpu_name(&system, idempotent, map, warnings); self.add_sysinfo_cpu_brand(&system, idempotent, map, warnings); self.add_sysinfo_cpu_frequency(&system, idempotent, map, warnings); } } fn add_sysinfo_name( &self, _system: &System, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) { if self.sysinfo_config.si_name { if let Ok(value) = env::var(SYSINFO_NAME) { add_map_entry(VergenKey::SysinfoName, value, map); } else { add_sysinfo_map_entry( VergenKey::SysinfoName, idempotent, System::name(), map, warnings, ); } } } fn add_sysinfo_os_verison( &self, _system: &System, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) { if self.sysinfo_config.si_os_version { if let Ok(value) = env::var(SYSINFO_OS_VERSION) { add_map_entry(VergenKey::SysinfoOsVersion, value, map); } else { add_sysinfo_map_entry( VergenKey::SysinfoOsVersion, idempotent, System::long_os_version(), map, warnings, ); } } } fn add_sysinfo_user( &self, system: &System, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) { if self.sysinfo_config.si_user { if let Ok(value) = env::var(SYSINFO_USER) { add_map_entry(VergenKey::SysinfoUser, value, map); } else { add_sysinfo_map_entry( VergenKey::SysinfoUser, idempotent, self.get_user(system), map, warnings, ); } } } fn add_sysinfo_total_memory( &self, system: &System, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) { if self.sysinfo_config.si_memory { if let Ok(value) = env::var(SYSINFO_MEMORY) { add_map_entry(VergenKey::SysinfoMemory, value, map); } else { add_sysinfo_map_entry( VergenKey::SysinfoMemory, idempotent, Some(suffix(system.total_memory())), map, warnings, ); } } } fn add_sysinfo_cpu_vendor( &self, system: &System, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) { if self.sysinfo_config.si_cpu_vendor { if let Ok(value) = env::var(SYSINFO_CPU_VENDOR) { add_map_entry(VergenKey::SysinfoCpuVendor, value, map); } else { add_sysinfo_map_entry( VergenKey::SysinfoCpuVendor, idempotent, system .cpus() .first() .map(|proc| proc.vendor_id().to_string()), map, warnings, ); } } } fn add_sysinfo_cpu_core_count( &self, system: &System, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) { if self.sysinfo_config.si_cpu_core_count { if let Ok(value) = env::var(SYSINFO_CPU_CORE_COUNT) { add_map_entry(VergenKey::SysinfoCpuCoreCount, value, map); } else { add_sysinfo_map_entry( VergenKey::SysinfoCpuCoreCount, idempotent, system.physical_core_count().as_ref().map(usize::to_string), map, warnings, ); } } } fn add_sysinfo_cpu_name( &self, system: &System, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) { if self.sysinfo_config.si_cpu_name { if let Ok(value) = env::var(SYSINFO_CPU_NAME) { add_map_entry(VergenKey::SysinfoCpuName, value, map); } else { add_sysinfo_map_entry( VergenKey::SysinfoCpuName, idempotent, Some( system .cpus() .iter() .map(Cpu::name) .collect::>() .join(","), ), map, warnings, ); } } } fn add_sysinfo_cpu_brand( &self, system: &System, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) { if self.sysinfo_config.si_cpu_brand { if let Ok(value) = env::var(SYSINFO_CPU_BRAND) { add_map_entry(VergenKey::SysinfoCpuBrand, value, map); } else { add_sysinfo_map_entry( VergenKey::SysinfoCpuBrand, idempotent, system .cpus() .first() .map(|processor| processor.brand().to_string()), map, warnings, ); } } } fn add_sysinfo_cpu_frequency( &self, system: &System, idempotent: bool, map: &mut RustcEnvMap, warnings: &mut Vec, ) { if self.sysinfo_config.si_cpu_frequency { if let Ok(value) = env::var(SYSINFO_CPU_FREQUENCY) { add_map_entry(VergenKey::SysinfoCpuFrequency, value, map); } else { add_sysinfo_map_entry( VergenKey::SysinfoCpuFrequency, idempotent, system .cpus() .first() .map(|proc| proc.frequency().to_string()), map, warnings, ); } } } fn get_user(&self, system: &System) -> Option { if let Ok(pid) = self.get_pid() { if let Some(process) = system.process(pid) { let users = Users::new_with_refreshed_list(); for user in &users { if check_user(process, user) { return Some(user.name().to_string()); } } } } None } #[cfg(not(test))] #[allow(clippy::unused_self)] fn get_pid(&self) -> Result { get_current_pid().map_err(|e| anyhow!(format!("{e}"))) } #[cfg(test)] fn get_pid(&self) -> Result { if self.sysinfo_config.fail_pid { Err(anyhow!("unable to determine pid")) } else { get_current_pid().map_err(|e| anyhow!(format!("{e}"))) } } } fn add_sysinfo_map_entry( key: VergenKey, idempotent: bool, value: Option, map: &mut RustcEnvMap, warnings: &mut Vec, ) { if idempotent { add_default_map_entry(key, map, warnings); } else if let Some(val) = value { add_map_entry(key, val, map); } else { add_default_map_entry(key, map, warnings); } } fn setup_system(refresh_kind: Option) -> System { if let Some(refresh_kind) = refresh_kind { let mut system = System::new(); system.refresh_specifics(refresh_kind); system } else { System::new_all() } } fn check_user(process: &Process, user: &User) -> bool { Some(user.id()) == process.user_id() } fn suffix(mut curr_memory: u64) -> String { let mut count = 0; while curr_memory >= 1024 { curr_memory /= 1024; count += 1; } format!( "{curr_memory} {}", match count { 0 => "B", 1 => "KiB", 2 => "MiB", 3 => "GiB", 4 => "TiB", 5 => "PiB", // This is the highest we can reach // at u64::MAX _ => "EiB", } ) } #[cfg(test)] mod test { use super::{add_sysinfo_map_entry, suffix}; use crate::{emitter::test::count_idempotent, key::VergenKey, EmitBuilder}; use anyhow::Result; use std::{collections::BTreeMap, env}; use sysinfo::{CpuRefreshKind, RefreshKind}; const IDEM_COUNT: usize = 0; const SYSINFO_COUNT: usize = 9; #[test] #[serial_test::serial] fn sysinfo_all_idempotent() -> Result<()> { let config = EmitBuilder::builder() .idempotent() .all_sysinfo() .test_emit()?; assert_eq!(SYSINFO_COUNT, config.cargo_rustc_env_map.len()); assert_eq!(SYSINFO_COUNT, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(SYSINFO_COUNT, config.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn sysinfo_all() -> Result<()> { let config = EmitBuilder::builder().all_sysinfo().test_emit()?; assert_eq!(SYSINFO_COUNT, config.cargo_rustc_env_map.len()); assert_eq!(IDEM_COUNT, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(IDEM_COUNT, config.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn sysinfo_refresh_kind() -> Result<()> { let refresh_kind = RefreshKind::new(); let cpu_refresh_kind = CpuRefreshKind::everything() .without_cpu_usage() .without_frequency(); let config = EmitBuilder::builder() .sysinfo_refresh_kind(Some(refresh_kind.with_cpu(cpu_refresh_kind))) .sysinfo_cpu_brand() .test_emit()?; assert_eq!(1, config.cargo_rustc_env_map.len()); assert_eq!(IDEM_COUNT, count_idempotent(&config.cargo_rustc_env_map)); assert_eq!(IDEM_COUNT, config.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn adding_none_defaults() -> Result<()> { let mut map = BTreeMap::new(); let mut warnings = vec![]; add_sysinfo_map_entry( VergenKey::SysinfoCpuBrand, false, None, &mut map, &mut warnings, ); Ok(()) } #[test] #[serial_test::serial] fn suffix_works() { assert_eq!(suffix(1023), "1023 B"); assert_eq!(suffix(1024), "1 KiB"); assert_eq!(suffix(1_048_575), "1023 KiB"); assert_eq!(suffix(1_048_576), "1 MiB"); assert_eq!(suffix(1_073_741_823), "1023 MiB"); assert_eq!(suffix(1_073_741_824), "1 GiB"); assert_eq!(suffix(1_099_511_627_775), "1023 GiB"); assert_eq!(suffix(1_099_511_627_776), "1 TiB"); assert_eq!(suffix(1_125_899_906_842_623), "1023 TiB"); assert_eq!(suffix(1_125_899_906_842_624), "1 PiB"); assert_eq!(suffix((1_125_899_906_842_624 * 1024) - 1), "1023 PiB"); assert_eq!(suffix(1_125_899_906_842_624 * 1024), "1 EiB"); assert_eq!(suffix(u64::MAX), "15 EiB"); } #[test] #[serial_test::serial] fn pid_lookup_fails() -> Result<()> { let mut config = EmitBuilder::builder(); _ = config.all_sysinfo(); config.sysinfo_config.fail_pid = true; let emitter = config.test_emit()?; assert_eq!(SYSINFO_COUNT, emitter.cargo_rustc_env_map.len()); assert_eq!(1, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(1, emitter.warnings.len()); Ok(()) } #[test] #[serial_test::serial] fn sysinfo_name_override_works() -> Result<()> { env::set_var("VERGEN_SYSINFO_NAME", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_sysinfo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_SYSINFO_NAME=this is a bad date")); env::remove_var("VERGEN_SYSINFO_NAME"); Ok(()) } #[test] #[serial_test::serial] fn sysinfo_os_version_override_works() -> Result<()> { env::set_var("VERGEN_SYSINFO_OS_VERSION", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_sysinfo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_SYSINFO_OS_VERSION=this is a bad date")); env::remove_var("VERGEN_SYSINFO_OS_VERSION"); Ok(()) } #[test] #[serial_test::serial] fn sysinfo_user_override_works() -> Result<()> { env::set_var("VERGEN_SYSINFO_USER", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_sysinfo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_SYSINFO_USER=this is a bad date")); env::remove_var("VERGEN_SYSINFO_USER"); Ok(()) } #[test] #[serial_test::serial] fn sysinfo_total_memory_override_works() -> Result<()> { env::set_var("VERGEN_SYSINFO_TOTAL_MEMORY", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_sysinfo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_SYSINFO_TOTAL_MEMORY=this is a bad date")); env::remove_var("VERGEN_SYSINFO_TOTAL_MEMORY"); Ok(()) } #[test] #[serial_test::serial] fn sysinfo_cpu_vendor_override_works() -> Result<()> { env::set_var("VERGEN_SYSINFO_CPU_VENDOR", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_sysinfo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_SYSINFO_CPU_VENDOR=this is a bad date")); env::remove_var("VERGEN_SYSINFO_CPU_VENDOR"); Ok(()) } #[test] #[serial_test::serial] fn sysinfo_cpu_core_count_override_works() -> Result<()> { env::set_var("VERGEN_SYSINFO_CPU_CORE_COUNT", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_sysinfo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_SYSINFO_CPU_CORE_COUNT=this is a bad date")); env::remove_var("VERGEN_SYSINFO_CPU_CORE_COUNT"); Ok(()) } #[test] #[serial_test::serial] fn sysinfo_cpu_name_override_works() -> Result<()> { env::set_var("VERGEN_SYSINFO_CPU_NAME", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_sysinfo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_SYSINFO_CPU_NAME=this is a bad date")); env::remove_var("VERGEN_SYSINFO_CPU_NAME"); Ok(()) } #[test] #[serial_test::serial] fn sysinfo_cpu_brand_override_works() -> Result<()> { env::set_var("VERGEN_SYSINFO_CPU_BRAND", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_sysinfo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_SYSINFO_CPU_BRAND=this is a bad date")); env::remove_var("VERGEN_SYSINFO_CPU_BRAND"); Ok(()) } #[test] #[serial_test::serial] fn sysinfo_cpu_frequency_override_works() -> Result<()> { env::set_var("VERGEN_SYSINFO_CPU_FREQUENCY", "this is a bad date"); let mut stdout_buf = vec![]; assert!(EmitBuilder::builder() .all_sysinfo() .emit_to(&mut stdout_buf) .is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_SYSINFO_CPU_FREQUENCY=this is a bad date")); env::remove_var("VERGEN_SYSINFO_CPU_FREQUENCY"); Ok(()) } } vergen-8.3.2/src/key.rs000064400000000000000000000270101046102023000130510ustar 00000000000000// Copyright (c) 2022 vergen developers // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. #[cfg(any( feature = "build", feature = "cargo", feature = "git", feature = "rustc", feature = "si" ))] pub(crate) use self::keys::VergenKey; #[cfg(any( feature = "build", feature = "cargo", feature = "git", feature = "rustc", feature = "si" ))] mod keys { #[cfg(feature = "build")] use crate::constants::{BUILD_DATE_NAME, BUILD_TIMESTAMP_NAME}; #[cfg(feature = "cargo")] use crate::constants::{ CARGO_DEBUG, CARGO_DEPENDENCIES, CARGO_FEATURES, CARGO_OPT_LEVEL, CARGO_TARGET_TRIPLE, }; #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] use crate::constants::{ GIT_BRANCH_NAME, GIT_COMMIT_AUTHOR_EMAIL, GIT_COMMIT_AUTHOR_NAME, GIT_COMMIT_COUNT, GIT_COMMIT_DATE_NAME, GIT_COMMIT_MESSAGE, GIT_COMMIT_TIMESTAMP_NAME, GIT_DESCRIBE_NAME, GIT_DIRTY_NAME, GIT_SHA_NAME, }; #[cfg(feature = "rustc")] use crate::constants::{ RUSTC_CHANNEL_NAME, RUSTC_COMMIT_DATE, RUSTC_COMMIT_HASH, RUSTC_HOST_TRIPLE_NAME, RUSTC_LLVM_VERSION, RUSTC_SEMVER_NAME, }; #[cfg(feature = "si")] use crate::constants::{ SYSINFO_CPU_BRAND, SYSINFO_CPU_CORE_COUNT, SYSINFO_CPU_FREQUENCY, SYSINFO_CPU_NAME, SYSINFO_CPU_VENDOR, SYSINFO_MEMORY, SYSINFO_NAME, SYSINFO_OS_VERSION, SYSINFO_USER, }; /// Build information keys. #[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] pub(crate) enum VergenKey { /// The build date. (`VERGEN_BUILD_DATE`) #[cfg(feature = "build")] BuildDate, /// The build timestamp. (`VERGEN_BUILD_TIMESTAMP`) #[cfg(feature = "build")] BuildTimestamp, /// The cargo debug flag (`VERGEN_CARGO_DEBUG`) #[cfg(feature = "cargo")] CargoDebug, /// The cargo features (`VERGEN_CARGO_FEATURES`) #[cfg(feature = "cargo")] CargoFeatures, /// The cargo opt level (`VERGEN_CARGO_OPT_LEVEL`) #[cfg(feature = "cargo")] CargoOptLevel, /// The cargo target triple (`VERGEN_CARGO_TARGET_TRIPLE`) #[cfg(feature = "cargo")] CargoTargetTriple, /// The cargo dependencies (`VERGEN_CARGO_DEPENDENCIES`) #[cfg(feature = "cargo")] CargoDependencies, /// The current working branch name (`VERGEN_GIT_BRANCH`) #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] GitBranch, /// The commit author's email. (`VERGEN_GIT_COMMIT_AUTHOR_EMAIL`) #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] GitCommitAuthorEmail, /// The commit author's name. (`VERGEN_GIT_COMMIT_AUTHOR_NAME`) #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] GitCommitAuthorName, /// Number of commits in current branch. (`VERGEN_GIT_COMMIT_COUNT`) #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] GitCommitCount, /// The commit date. (`VERGEN_GIT_COMMIT_DATE`) #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] GitCommitDate, /// Commit message (`VERGEN_GIT_COMMIT_MESSAGE`) #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] GitCommitMessage, /// The commit timestamp. (`VERGEN_GIT_COMMIT_TIMESTAMP`) #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] GitCommitTimestamp, /// The semver version from the last git tag. (`VERGEN_GIT_SEMVER`) #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] GitDescribe, /// The latest commit SHA. (`VERGEN_GIT_SHA`) #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] GitSha, /// Whether the repository is dirty. (`VERGEN_GIT_DIRTY`) #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] GitDirty, /// The release channel of the rust compiler. (`VERGEN_RUSTC_CHANNEL`) #[cfg(feature = "rustc")] RustcChannel, /// The rustc commit date. (`VERGEN_RUSTC_COMMIT_DATE`) #[cfg(feature = "rustc")] RustcCommitDate, /// The rustc commit hash. (`VERGEN_RUSTC_COMMIT_HASH`) #[cfg(feature = "rustc")] RustcCommitHash, /// The host triple. (`VERGEN_HOST_TRIPLE`) #[cfg(feature = "rustc")] RustcHostTriple, /// The rustc LLVM version. (`VERGEN_RUSTC_LLVM_VERSION`) #[cfg(feature = "rustc")] RustcLlvmVersion, /// The version information of the rust compiler. (`VERGEN_RUSTC_SEMVER`) #[cfg(feature = "rustc")] RustcSemver, /// The sysinfo system name (`VERGEN_SYSINFO_NAME`) #[cfg(feature = "si")] SysinfoName, /// The sysinfo os version (`VERGEN_SYSINFO_OS_VERSION`) #[cfg(feature = "si")] SysinfoOsVersion, /// The sysinfo user name (`VERGEN_SYSINFO_USER`) #[cfg(feature = "si")] SysinfoUser, /// The sysinfo total memory (`VERGEN_SYSINFO_TOTAL_MEMORY`) #[cfg(feature = "si")] SysinfoMemory, /// The sysinfo cpu vendor (`VERGEN_SYSINFO_CPU_VENDOR`) #[cfg(feature = "si")] SysinfoCpuVendor, /// The sysinfo cpu core count (`VERGEN_SYSINFO_CPU_CORE_COUNT`) #[cfg(feature = "si")] SysinfoCpuCoreCount, /// The sysinfo cpu core count (`VERGEN_SYSINFO_CPU_NAME`) #[cfg(feature = "si")] SysinfoCpuName, /// The sysinfo cpu core count (`VERGEN_SYSINFO_CPU_BRAND`) #[cfg(feature = "si")] SysinfoCpuBrand, /// The sysinfo cpu core count (`VERGEN_SYSINFO_CPU_FREQUENCY`) #[cfg(feature = "si")] SysinfoCpuFrequency, } impl VergenKey { /// Get the name for the given key. pub(crate) fn name(self) -> &'static str { match self { #[cfg(feature = "build")] VergenKey::BuildDate => BUILD_DATE_NAME, #[cfg(feature = "build")] VergenKey::BuildTimestamp => BUILD_TIMESTAMP_NAME, #[cfg(feature = "cargo")] VergenKey::CargoDebug => CARGO_DEBUG, #[cfg(feature = "cargo")] VergenKey::CargoFeatures => CARGO_FEATURES, #[cfg(feature = "cargo")] VergenKey::CargoOptLevel => CARGO_OPT_LEVEL, #[cfg(feature = "cargo")] VergenKey::CargoTargetTriple => CARGO_TARGET_TRIPLE, #[cfg(feature = "cargo")] VergenKey::CargoDependencies => CARGO_DEPENDENCIES, #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] VergenKey::GitBranch => GIT_BRANCH_NAME, #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] VergenKey::GitCommitAuthorEmail => GIT_COMMIT_AUTHOR_EMAIL, #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] VergenKey::GitCommitAuthorName => GIT_COMMIT_AUTHOR_NAME, #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] VergenKey::GitCommitCount => GIT_COMMIT_COUNT, #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] VergenKey::GitCommitDate => GIT_COMMIT_DATE_NAME, #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] VergenKey::GitCommitMessage => GIT_COMMIT_MESSAGE, #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] VergenKey::GitCommitTimestamp => GIT_COMMIT_TIMESTAMP_NAME, #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] VergenKey::GitDescribe => GIT_DESCRIBE_NAME, #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] VergenKey::GitSha => GIT_SHA_NAME, #[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] VergenKey::GitDirty => GIT_DIRTY_NAME, #[cfg(feature = "rustc")] VergenKey::RustcChannel => RUSTC_CHANNEL_NAME, #[cfg(feature = "rustc")] VergenKey::RustcCommitDate => RUSTC_COMMIT_DATE, #[cfg(feature = "rustc")] VergenKey::RustcCommitHash => RUSTC_COMMIT_HASH, #[cfg(feature = "rustc")] VergenKey::RustcHostTriple => RUSTC_HOST_TRIPLE_NAME, #[cfg(feature = "rustc")] VergenKey::RustcLlvmVersion => RUSTC_LLVM_VERSION, #[cfg(feature = "rustc")] VergenKey::RustcSemver => RUSTC_SEMVER_NAME, #[cfg(feature = "si")] VergenKey::SysinfoName => SYSINFO_NAME, #[cfg(feature = "si")] VergenKey::SysinfoOsVersion => SYSINFO_OS_VERSION, #[cfg(feature = "si")] VergenKey::SysinfoUser => SYSINFO_USER, #[cfg(feature = "si")] VergenKey::SysinfoMemory => SYSINFO_MEMORY, #[cfg(feature = "si")] VergenKey::SysinfoCpuVendor => SYSINFO_CPU_VENDOR, #[cfg(feature = "si")] VergenKey::SysinfoCpuCoreCount => SYSINFO_CPU_CORE_COUNT, #[cfg(feature = "si")] VergenKey::SysinfoCpuName => SYSINFO_CPU_NAME, #[cfg(feature = "si")] VergenKey::SysinfoCpuBrand => SYSINFO_CPU_BRAND, #[cfg(feature = "si")] VergenKey::SysinfoCpuFrequency => SYSINFO_CPU_FREQUENCY, } } } } #[allow(dead_code)] #[cfg(not(any( feature = "build", feature = "cargo", feature = "git", feature = "rustc", feature = "si" )))] mod keys { #[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] pub(crate) enum VergenKey {} } vergen-8.3.2/src/lib.rs000064400000000000000000000530051046102023000130320ustar 00000000000000// Copyright (c) 2022 vergen developers // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. //! # vergen - Emit cargo instructions from a build script //! `vergen`, when used in conjunction with cargo [build scripts] can emit the following: //! //! - Will emit [`cargo:rustc-env=VAR=VALUE`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-envvarvalue) //! for each feature you have enabled. These can be referenced with the [env!](std::env!) macro in your code. //! - Will emit [`cargo:rerun-if-changed=.git/HEAD`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed) //! if the git feature is enabled. This is done to ensure any git instructions are regenerated when commits are made. //! - Will emit [`cargo:rerun-if-changed=.git/`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed) //! if the git feature is enabled. This is done to ensure any git instructions are regenerated when commits are made. //! - Can emit [`cargo:warning`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargo-warning) outputs if the //! [`fail_on_error`](EmitBuilder::fail_on_error) feature is not enabled and the requested variable is defaulted through error or //! the [`idempotent`](EmitBuilder::idempotent) flag. //! - Will emit [`cargo:rerun-if-changed=build.rs`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed) //! to rerun instruction emission if the `build.rs` file changed. //! - Will emit [`cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed) //! to rerun instruction emission if the `VERGEN_IDEMPOTENT` environment variable has changed. //! - Will emit [`cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed) //! to rerun instruction emission if the `SOURCE_DATE_EPOCH` environment variable has changed. //! //! ## Usage //! //! 1. Ensure you have build scripts enabled via the `build` configuration in your `Cargo.toml` //! //! ```toml //! [package] //! #.. //! build = "build.rs" //! ``` //! //! 2. Add `vergen` as a build dependency in `Cargo.toml`, specifying the features you wish to enable. //! //! ```toml //! [dependencies] //! #.. //! //! [build-dependencies] //! # All features enabled //! vergen = { version = "8.0.0", features = ["build", "cargo", "git", "gitcl", "rustc", "si"] } //! # or //! vergen = { version = "8.0.0", features = ["build", "git", "gitcl"] } //! # if you wish to disable certain features //! ``` //! //! 3. Create a `build.rs` file that uses `vergen` to emit cargo instructions. Configuration //! starts with [`EmitBuilder`]. Eventually you will call [`emit`](EmitBuilder::emit) to output the //! cargo instructions. See the [`emit`](EmitBuilder::emit) documentation for more robust examples. //! //! #### Generate all output //! //! ``` //! use anyhow::Result; //! # use std::env; //! use vergen::EmitBuilder; //! //! pub fn main() -> Result<()> { #![cfg_attr( all( feature = "build", feature = "cargo", all(feature = "git", feature = "gitcl"), feature = "rustc", feature = "si" ), doc = r##" # env::set_var("CARGO_FEATURE_BUILD", "build"); # env::set_var("CARGO_FEATURE_GIT", "git"); # env::set_var("DEBUG", "true"); # env::set_var("OPT_LEVEL", "1"); # env::set_var("TARGET", "x86_64-unknown-linux-gnu"); // NOTE: This will output everything, and requires all features enabled. // NOTE: See the EmitBuilder documentation for configuration options. EmitBuilder::builder() .all_build() .all_cargo() .all_git() .all_rustc() .all_sysinfo() .emit()?; # env::remove_var("CARGO_FEATURE_BUILD"); # env::remove_var("CARGO_FEATURE_GIT"); # env::remove_var("DEBUG"); # env::remove_var("OPT_LEVEL"); # env::remove_var("TARGET"); "## )] //! Ok(()) //! } //! ``` //! //! #### Generate specific output //! //! ``` //! use anyhow::Result; //! # use std::env; //! use vergen::EmitBuilder; //! //! pub fn main() -> Result<()> { #![cfg_attr( all(feature = "build", all(feature = "git", feature = "gitcl"),), doc = r##" // NOTE: This will output only a build timestamp and long SHA from git. // NOTE: This set requires the build and git features. // NOTE: See the EmitBuilder documentation for configuration options. EmitBuilder::builder() .build_timestamp() .git_sha(false) .emit()?; "## )] //! Ok(()) //! } //! ``` //! //! 4. Use the [`env!`](std::env!) macro in your code to read the environment variables. //! //! ```ignore //! println!("Build Timestamp: {}", env!("VERGEN_BUILD_TIMESTAMP")); //! println!("git describe: {}", env!("VERGEN_GIT_DESCRIBE")); //! ``` //! //! ## Features //! `vergen` has five main feature toggles allowing you to customize your output. No features are enabled by default. //! You **must** specifically enable the features you wish to use. //! //! | Feature | Enables | //! | ------- | ------- | //! | build | `VERGEN_BUILD_*` instructions | //! | cargo | `VERGEN_CARGO_*` instructions | //! | git | `VERGEN_GIT_*` instructions and the `cargo:rerun-if-changed` instructions | //! | rustc | `VERGEN_RUSTC_*` instructions | //! | si | `VERGEN_SYSINFO_*` instructions | //! //! #### Configuring the `git` feature //! If you wish to use the git feature, you must also enable one of the git implementations. //! The `gitcl` features is lightweight, but depends on `git` being on the path. The other //! implementations allow for git instructions to be emitted without a reliance on //! the git binary. The [`git2`](https://github.com/rust-lang/git2-rs) library are bindings over //! the `libgit2` library, while [`gitoxide`](https://github.com/Byron/gitoxide) is entirely implemented in Rust. //! //! **NOTE** - These 3 features are mutually exclusive. Only one can be chosen. If you select //! multiple, `vergen` intentionally will not compile. //! //! | Features | Enables | //! | -------- | ------- | //! | gitcl | `VERGEN_GIT_` instructions emitted via the `git` binary at the command line | //! | git2 | `VERGEN_GIT_` instructions emitted via git `git2` library | //! | gitoxide | `VERGEN_GIT_` instructions emitted via the `gitoxide` library | //! //! A common configuration would be as follows: //! ```toml //! [build-dependencies] //! vergen = { version = "8.0.0", features = [ "build", "git", "gitcl" ]} //! # ... //! ``` //! ### `Cargo` feature unification for `vergen` versions prior to 8.3.0 //! When a dependency is used by multiple packages, Cargo will [use the union](https://doc.rust-lang.org/cargo/reference/features.html#feature-unification) of all features enabled on that dependency when building it. Prior to version **8.3.0**, `vergen` had a set of mutually exclusive features `gitcl`, `git2`, and `gitoxide` to enable to specific git backend you wished to use. If your crate has a dependency on another crate that uses `vergen`, your crate may fail to compile if you select a different `git` backend then the crate you depend on. For example, your crate depends on `fancy-lib`. //! //! #### fancy-lib `Cargo.toml` //! ```toml //! [build-dependencies] //! vergen = { version = "8.2.10", features = ["git","gitcl"] } //! ``` //! //! #### your crate `Cargo.toml` //! ```toml //! [dependencies] //! fancy-lib = "0.1.0" //! //! [build-dependencies] //! vergen = { version = "8.2.10", features = ["git","gitoxide"] } //! ``` //! //! Your crate will fail to compile because `cargo` unifies this to //! ```toml //! vergen = { version = "8.2.10", features = ["git","gitcl","gitoxide"] } //! ``` //! and prior to **8.3.0** `vergen` will not compile with both `gitcl` and `gitoxide` as features. //! //! As a workaround, you can use `cargo tree -f "{p} {f}" | grep vergen` to determine the feature list cargo has set for `vergen`. If //! a `git` backend has already been determined you will be able to use that without declaring those features in your dependency list. This is not perfect //! as this leaves you at the mercy of your dependency and the git feature they selected, but it's a workaround until version 9 comes out. //! //! #### fancy-lib `Cargo.toml` //! ```toml //! [build-dependencies] //! vergen = { version = "8.2.10", features = ["git","gitcl"] } //! ``` //! //! #### your crate `Cargo.toml` //! ```toml //! [dependencies] //! fancy-lib = "0.1.0" //! //! [build-dependencies] //! vergen = "8.2.10" //! ``` //! #### Unified //! ```toml //! vergen = { version = "8.2.10", features = ["git","gitcl"] } //! ``` //! ### `Cargo` feature unification for `vergen` versions 8.3.0 and beyond //! `vergen` will accept `gitcl`,`git2`, and `gitoxide` as features. If more than one of them is included, `vergen` will select `gitcl` before `git2` and `git2` before `gitoxide`. //! //! ## Environment Variables //! `vergen` currently recognizes the following environment variables //! //! | Variable | Functionality | //! | -------- | ------------- | //! | `VERGEN_IDEMPOTENT` | If this environment variable is set `vergen` will use the idempotent output feature regardless of the configuration set in `build.rs`. This exists mainly to allow package maintainers to force idempotent output to generate deterministic binary output. | //! | `SOURCE_DATE_EPOCH` | If this environment variable is set `vergen` will use the value (unix time since epoch) as the basis for a time based instructions. This can help emit deterministic instructions. | //! | `VERGEN_BUILD_*` | If this environment variable is set `vergen` will use the value you specify for the output rather than generating it. | //! | `VERGEN_CARGO_*` | If this environment variable is set `vergen` will use the value you specify for the output rather than generating it. | //! | `VERGEN_GIT_*` | If this environment variable is set `vergen` will use the value you specify for the output rather than generating it. | //! | `VERGEN_RUSTC_*` | If this environment variable is set `vergen` will use the value you specify for the output rather than generating it. | //! | `VERGEN_SYSINFO_*` | If this environment variable is set `vergen` will use the value you specify for the output rather than generating it. | //! //! ## Goals //! I initially wrote `vergen` (**ver**sion **gen**erator, so original) so I could embed a some git information in my //! personal projects. Now, usage has grown to the point that `vergen` needs to fit better in the rust ecosystem. //! //! The current goals are as follows: //! //! #### Minimize the tool footprint //! - Adopt an opt-in, rather than opt-out strategy for the features. The default feature set is empty //! and no instructions will be emitted. //! - The instructions you have configured **will** be emitted. If there are errors or idempotentcy //! has been configured, some of those instructions may be defaulted. //! - Allow overriding configurtion set in `build.rs` through environment variables. This will allow package //! maintainers to force sane defaults when packaging rust binaries for distribution. //! //! #### Minimize the compile time impact //! - `git2` and `gitoxide` are large features. These are opt-in now. I've also added back support for //! generating git instructions via the `git` binary. //! - I've removed some extraneous libraries. Any libraries added in the future will be checked against //! the current standard compile times to ensure the impact is not too great. //! - `vergen` should compile and test from a source tarball. //! //! #### Support deterministic output //! Compilations run from the same source oftentimes need to generate identical binaries. `vergen` now supports //! this determinism in a few ways. //! - An [`idempotent`](EmitBuilder::idempotent) configuration option has been added. When this is enabled in a //! build script, each build via cargo against the same source code should generate identical binaries. Instructions //! that output information that may change between builds (i.e. timestamps, sysinfo) will be defaulted. //! - Recognize common environment variables that support deterministic builds (i.e. [`SOURCE_DATE_EPOCH`](https://reproducible-builds.org/docs/source-date-epoch/)) //! - Allow `build.rs` configuration overrides though environment variables to allow users building a binary, but //! not controlling the source to generate deterministic binaries. //! //! # Use Cases //! I generally use vergen for the following two cases //! //! 1. Generating verbose output describing a command line application. //! //! ```text //! ~/p/r/app λ app -vv //! app 0.1.0 //! //! Build Timestamp: 2021-02-23T20:14:46.558472672+00:00 //! Describe: 0.1.0-9-g46f83e1 //! Commit SHA: 46f83e112520533338245862d366f6a02cef07d4 //! Commit Date: 2021-02-23T08:08:02-05:00 //! Commit Branch: master //! rustc Version: 1.52.0-nightly //! rustc Channel: nightly //! rustc Host Triple: x86_64-unknown-linux-gnu //! rustc Commit SHA: 3f5aee2d5241139d808f4fdece0026603489afd1 //! cargo Target Triple: x86_64-unknown-linux-musl //! cargo Profile: release //! ``` //! //! 2. Information endpoints in web apis //! //! ```json //! ~/p/r/app λ curl https://some.app.com/info | jq //! { //! "build_timestamp": "2021-02-19T21:32:22.932833758+00:00", //! "git_describe": "0.0.0-7-gc96c096", //! "git_sha": "c96c0961c3b7b749eab92f6f588b67915889c4cd", //! "git_commit_date": "2021-02-19T16:29:06-05:00", //! "git_branch": "master", //! "rustc_semver": "1.52.0-nightly", //! "rustc_channel": "nightly", //! "rustc_host_triple": "x86_64-unknown-linux-gnu", //! "rustc_commit_sha": "3f5aee2d5241139d808f4fdece0026603489afd1", //! "cargo_target_triple": "x86_64-unknown-linux-musl", //! "cargo_profile": "release" //! } //! ``` //! //! [build scripts]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script //! [cargo:rustc-env]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-env //! [cargo:rerun-if-changed]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed //! // rustc lints #![cfg_attr( all(feature = "unstable", nightly), feature( multiple_supertrait_upcastable, must_not_suspend, non_exhaustive_omitted_patterns_lint, rustdoc_missing_doc_code_examples, strict_provenance, ) )] #![cfg_attr(nightly, allow(single_use_lifetimes, unexpected_cfgs))] #![cfg_attr( nightly, deny( absolute_paths_not_starting_with_crate, ambiguous_glob_imports, ambiguous_glob_reexports, ambiguous_wide_pointer_comparisons, anonymous_parameters, array_into_iter, asm_sub_register, async_fn_in_trait, bad_asm_style, bare_trait_objects, break_with_label_and_loop, byte_slice_in_packed_struct_with_derive, clashing_extern_declarations, coherence_leak_check, confusable_idents, const_eval_mutable_ptr_in_final_value, const_evaluatable_unchecked, const_item_mutation, dead_code, deprecated, deprecated_in_future, deprecated_where_clause_location, deref_into_dyn_supertrait, deref_nullptr, drop_bounds, dropping_copy_types, dropping_references, duplicate_macro_attributes, dyn_drop, elided_lifetimes_in_associated_constant, elided_lifetimes_in_paths, ellipsis_inclusive_range_patterns, explicit_outlives_requirements, exported_private_dependencies, ffi_unwind_calls, forbidden_lint_groups, forgetting_copy_types, forgetting_references, for_loops_over_fallibles, function_item_references, hidden_glob_reexports, improper_ctypes, improper_ctypes_definitions, inline_no_sanitize, internal_features, invalid_from_utf8, invalid_macro_export_arguments, invalid_nan_comparisons, invalid_value, irrefutable_let_patterns, keyword_idents_2018, keyword_idents_2024, large_assignments, late_bound_lifetime_arguments, legacy_derive_helpers, let_underscore_drop, macro_use_extern_crate, map_unit_fn, meta_variable_misuse, missing_abi, missing_copy_implementations, missing_debug_implementations, missing_docs, mixed_script_confusables, named_arguments_used_positionally, never_type_fallback_flowing_into_unsafe, no_mangle_generic_items, non_ascii_idents, non_camel_case_types, non_contiguous_range_endpoints, non_fmt_panics, non_local_definitions, non_shorthand_field_patterns, non_snake_case, non_upper_case_globals, noop_method_call, opaque_hidden_inferred_bound, overlapping_range_endpoints, path_statements, private_bounds, private_interfaces, redundant_lifetimes, redundant_semicolons, refining_impl_trait_internal, refining_impl_trait_reachable, renamed_and_removed_lints, repr_transparent_external_private_fields, rust_2021_incompatible_closure_captures, rust_2021_incompatible_or_patterns, rust_2021_prefixes_incompatible_syntax, rust_2021_prelude_collisions, semicolon_in_expressions_from_macros, special_module_name, stable_features, static_mut_refs, suspicious_double_ref_op, temporary_cstring_as_ptr, trivial_bounds, trivial_casts, trivial_numeric_casts, type_alias_bounds, tyvar_behind_raw_pointer, uncommon_codepoints, unconditional_recursion, uncovered_param_in_projection, undefined_naked_function_abi, ungated_async_fn_track_caller, uninhabited_static, unit_bindings, unknown_lints, unknown_or_malformed_diagnostic_attributes, unnameable_test_items, unnameable_types, unreachable_code, unreachable_patterns, unreachable_pub, unsafe_code, unsafe_op_in_unsafe_fn, unstable_name_collisions, unstable_syntax_pre_expansion, unsupported_calling_conventions, unused_allocation, unused_assignments, unused_associated_type_bounds, unused_attributes, unused_braces, unused_comparisons, unused_crate_dependencies, unused_doc_comments, unused_extern_crates, unused_features, unused_import_braces, unused_imports, unused_labels, unused_lifetimes, unused_macro_rules, unused_macros, unused_must_use, unused_mut, unused_parens, unused_qualifications, unused_results, unused_unsafe, unused_variables, useless_ptr_null_checks, variant_size_differences, wasm_c_abi, while_true, writes_through_immutable_pointer, ) )] #![cfg_attr(all(nightly), allow(unstable_features))] // If nightly and unstable, allow `incomplete_features` and `unstable_features` #![cfg_attr(all(feature = "unstable", nightly), allow(incomplete_features))] // If nightly and not unstable, deny `incomplete_features` and `unstable_features` #![cfg_attr( all(not(feature = "unstable"), nightly), deny(incomplete_features, unstable_features) )] // The unstable lints #![cfg_attr( all(feature = "unstable", nightly), deny( fuzzy_provenance_casts, lossy_provenance_casts, multiple_supertrait_upcastable, must_not_suspend, non_exhaustive_omitted_patterns, unfulfilled_lint_expectations, ) )] // clippy lints #![cfg_attr(nightly, deny(clippy::all, clippy::pedantic))] // rustdoc lints #![cfg_attr( nightly, deny( rustdoc::bare_urls, rustdoc::broken_intra_doc_links, rustdoc::invalid_codeblock_attributes, rustdoc::invalid_html_tags, rustdoc::missing_crate_level_docs, rustdoc::private_doc_tests, rustdoc::private_intra_doc_links, ) )] #![cfg_attr( all(nightly, feature = "unstable"), deny(rustdoc::missing_doc_code_examples) )] #![cfg_attr(all(doc, nightly), feature(doc_auto_cfg))] #![cfg_attr(all(docsrs, nightly), feature(doc_cfg))] #![cfg_attr(coverage_nightly, feature(coverage_attribute))] mod constants; mod emitter; mod feature; mod key; mod utils; #[cfg(feature = "git")] cfg_if::cfg_if! { if #[cfg(all(feature = "gitcl", feature = "git2", feature = "gitoxide"))] { use gix as _; use git2_rs as _; } else if #[cfg(all(feature = "gitcl", feature = "gitoxide"))] { use gix as _; } else if #[cfg(all(feature = "gitcl", feature = "git2"))] { use git2_rs as _; } else if #[cfg(all(feature = "git2", feature = "gitoxide"))] { use gix as _; } } // This is here to appease the `unused_crate_dependencies` lint #[cfg(test)] use {gix as _, lazy_static as _, regex as _, repo_util as _, serial_test as _, temp_env as _}; pub use crate::emitter::EmitBuilder; #[cfg(feature = "cargo")] pub use cargo_metadata::DependencyKind; #[cfg(feature = "si")] pub use sysinfo::CpuRefreshKind; #[cfg(feature = "si")] pub use sysinfo::MemoryRefreshKind; #[cfg(feature = "si")] pub use sysinfo::ProcessRefreshKind; #[cfg(feature = "si")] pub use sysinfo::RefreshKind; vergen-8.3.2/src/utils.rs000064400000000000000000000036701046102023000134270ustar 00000000000000// Copyright (c) 2022 vergen developers // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. #[cfg(all(test, feature = "cargo"))] pub(crate) mod testutils { use std::env; pub(crate) fn setup() { env::set_var("CARGO_FEATURE_BUILD", "build"); env::set_var("CARGO_FEATURE_GIT", "git"); env::set_var("DEBUG", "true"); env::set_var("OPT_LEVEL", "1"); env::set_var("TARGET", "x86_64-unknown-linux-gnu"); } pub(crate) fn teardown() { env::remove_var("CARGO_FEATURE_BUILD"); env::remove_var("CARGO_FEATURE_GIT"); env::remove_var("DEBUG"); env::remove_var("OPT_LEVEL"); env::remove_var("TARGET"); } } #[cfg(any( feature = "build", feature = "cargo", all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ), feature = "rustc", feature = "si", ))] pub(crate) mod fns { use crate::{constants::VERGEN_IDEMPOTENT_DEFAULT, emitter::RustcEnvMap, key::VergenKey}; use std::env; pub(crate) fn add_default_map_entry( key: VergenKey, map: &mut RustcEnvMap, warnings: &mut Vec, ) { if let Ok(value) = env::var(key.name()) { add_map_entry(key, value, map); warnings.push(format!("{} overidden", key.name())); } else { add_map_entry(key, VERGEN_IDEMPOTENT_DEFAULT, map); warnings.push(format!("{} set to default", key.name())); } } pub(crate) fn add_map_entry(key: VergenKey, value: T, map: &mut RustcEnvMap) where T: Into, { let _old = map.insert(key, value.into()); } } vergen-8.3.2/tarpaulin.toml000064400000000000000000000002601046102023000140160ustar 00000000000000[gitcl] features = "build cargo git gitcl rustc si" [git2] features = "build cargo git git2 rustc si" [gix] features = "build cargo git gix rustc si" [default] features = ""vergen-8.3.2/tests/build_output.rs000064400000000000000000000127401046102023000153570ustar 00000000000000#[cfg(feature = "build")] mod test_build { use anyhow::Result; use lazy_static::lazy_static; use regex::Regex; use std::env; use vergen::EmitBuilder; lazy_static! { static ref DATE_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_BUILD_DATE=\d{4}-\d{2}-\d{2}"; static ref DATE_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_BUILD_DATE=VERGEN_IDEMPOTENT_OUTPUT"; static ref TIMESTAMP_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_BUILD_TIMESTAMP=([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([\+|\-]([01][0-9]|2[0-3]):[0-5][0-9]))"; static ref TIMESTAMP_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_BUILD_TIMESTAMP=VERGEN_IDEMPOTENT_OUTPUT"; static ref DATE_WARNING: &'static str = r"cargo:warning=VERGEN_BUILD_DATE set to default"; static ref TIMESTAMP_WARNING: &'static str = r"cargo:warning=VERGEN_BUILD_TIMESTAMP set to default"; static ref BUILD_REGEX_INST: Regex = { let re_str = [*DATE_RE_STR, *TIMESTAMP_RE_STR].join("\n"); Regex::new(&re_str).unwrap() }; } const IDEM_OUTPUT: &str = r"cargo:rustc-env=VERGEN_BUILD_DATE=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_BUILD_TIMESTAMP=VERGEN_IDEMPOTENT_OUTPUT cargo:warning=VERGEN_BUILD_DATE set to default cargo:warning=VERGEN_BUILD_TIMESTAMP set to default cargo:rerun-if-changed=build.rs cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH "; const IDEM_OUTPUT_CUSTOM_BUILDRS: &str = r"cargo:rustc-env=VERGEN_BUILD_DATE=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_BUILD_TIMESTAMP=VERGEN_IDEMPOTENT_OUTPUT cargo:warning=VERGEN_BUILD_DATE set to default cargo:warning=VERGEN_BUILD_TIMESTAMP set to default cargo:rerun-if-changed=a/custom_build.rs cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH "; const DISABLED_OUTPUT: &str = r""; const SOURCE_DATE_EPOCH_IDEM_OUTPUT: &str = r"cargo:rustc-env=VERGEN_BUILD_DATE=2022-12-23 cargo:rustc-env=VERGEN_BUILD_TIMESTAMP=2022-12-23T15:29:20.000000000Z cargo:rerun-if-changed=build.rs cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH "; const QUIET_IDEM_OUTPUT: &str = r"cargo:rustc-env=VERGEN_BUILD_DATE=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_BUILD_TIMESTAMP=VERGEN_IDEMPOTENT_OUTPUT cargo:rerun-if-changed=build.rs cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH "; #[test] #[serial_test::serial] fn build_all_output() -> Result<()> { let mut stdout_buf = vec![]; EmitBuilder::builder() .all_build() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(BUILD_REGEX_INST.is_match(&output)); Ok(()) } #[test] #[serial_test::serial] fn build_all_output_local() -> Result<()> { env::set_var("SOURCE_DATE_EPOCH", "1671809360"); let mut stdout_buf = vec![]; let result = EmitBuilder::builder() .all_build() .use_local_build() .fail_on_error() .emit_to(&mut stdout_buf); env::remove_var("SOURCE_DATE_EPOCH"); assert!(result.is_ok()); let output = String::from_utf8_lossy(&stdout_buf); assert!(BUILD_REGEX_INST.is_match(&output)); Ok(()) } #[test] #[serial_test::serial] fn build_disabled_output() -> Result<()> { let mut stdout_buf = vec![]; EmitBuilder::builder() .all_build() .disable_build() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert_eq!(DISABLED_OUTPUT, output); Ok(()) } #[test] #[serial_test::serial] fn build_all_idempotent_output() -> Result<()> { let mut stdout_buf = vec![]; EmitBuilder::builder() .idempotent() .all_build() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert_eq!(IDEM_OUTPUT, output); Ok(()) } #[test] #[serial_test::serial] fn build_all_idempotent_custom_buildrs_output() -> Result<()> { let mut stdout_buf = vec![]; EmitBuilder::builder() .idempotent() .custom_build_rs("a/custom_build.rs") .all_build() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert_eq!(IDEM_OUTPUT_CUSTOM_BUILDRS, output); Ok(()) } #[test] #[serial_test::serial] fn build_all_idempotent_output_quiet() -> Result<()> { let mut stdout_buf = vec![]; EmitBuilder::builder() .idempotent() .quiet() .all_build() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert_eq!(QUIET_IDEM_OUTPUT, output); Ok(()) } #[test] #[serial_test::serial] fn build_all_sde_output() -> Result<()> { env::set_var("SOURCE_DATE_EPOCH", "1671809360"); let mut stdout_buf = vec![]; EmitBuilder::builder() .all_build() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert_eq!(SOURCE_DATE_EPOCH_IDEM_OUTPUT, output); env::remove_var("SOURCE_DATE_EPOCH"); Ok(()) } } vergen-8.3.2/tests/cargo_output.rs000064400000000000000000000136151046102023000153550ustar 00000000000000#[cfg(feature = "cargo")] mod test_build { use anyhow::Result; use cargo_metadata::DependencyKind; use lazy_static::lazy_static; use regex::Regex; use std::env; use vergen::EmitBuilder; lazy_static! { static ref CARGO_DEBUG_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_CARGO_DEBUG=(true|false)"; static ref CARGO_FEA_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_CARGO_FEATURES=[a-zA-Z0-9-_]+,[a-zA-Z0-9-_]+"; static ref CARGO_OPT_LEVEL_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_CARGO_OPT_LEVEL=\d{1}"; static ref CARGO_TT_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_CARGO_TARGET_TRIPLE=[a-zA-Z0-9-_]+"; static ref CARGO_DEP_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES=.*"; static ref CARGO_DEP_NAME_RE_STR: &'static str = r"(?m)^cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES=anyhow 1\.0\.[0-9]{2,}$"; static ref CARGO_DEP_DK_RE_STR: &'static str = r"(?m)^cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES=gix 0\.63\.[0-9]{1,}$"; static ref CARGO_DEP_RV_RE_STR: &'static str = r"(?m)^cargo:rustc-env=VERGEN_CARGO_DEPENDENCIES=rustversion 1\.0\.[0-9]{2,}$"; static ref CARGO_REGEX: Regex = { let re_str = [ *CARGO_DEBUG_RE_STR, *CARGO_FEA_RE_STR, *CARGO_OPT_LEVEL_RE_STR, *CARGO_TT_RE_STR, *CARGO_DEP_RE_STR, ] .join("\n"); Regex::new(&re_str).unwrap() }; static ref CARGO_REGEX_NO_DEP: Regex = { let re_str = [ *CARGO_DEBUG_RE_STR, *CARGO_FEA_RE_STR, *CARGO_OPT_LEVEL_RE_STR, *CARGO_TT_RE_STR, ] .join("\n"); Regex::new(&re_str).unwrap() }; static ref CARGO_REGEX_NAME: Regex = Regex::new(&CARGO_DEP_NAME_RE_STR).unwrap(); static ref CARGO_REGEX_DK: Regex = Regex::new(&CARGO_DEP_DK_RE_STR).unwrap(); static ref CARGO_REGEX_RV: Regex = Regex::new(&CARGO_DEP_RV_RE_STR).unwrap(); } fn setup() { env::set_var("CARGO_FEATURE_BUILD", "build"); env::set_var("CARGO_FEATURE_GIT", "git"); env::set_var("DEBUG", "true"); env::set_var("OPT_LEVEL", "1"); env::set_var("TARGET", "x86_64-unknown-linux-gnu"); } fn teardown() { env::remove_var("CARGO_FEATURE_BUILD"); env::remove_var("CARGO_FEATURE_GIT"); env::remove_var("DEBUG"); env::remove_var("OPT_LEVEL"); env::remove_var("TARGET"); } const DISABLED_OUTPUT: &str = r""; #[test] #[serial_test::serial] fn cargo_all_output() -> Result<()> { setup(); let mut stdout_buf = vec![]; EmitBuilder::builder() .all_cargo() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(CARGO_REGEX.is_match(&output)); teardown(); Ok(()) } #[test] #[serial_test::serial] fn cargo_disabled_output() -> Result<()> { setup(); let mut stdout_buf = vec![]; EmitBuilder::builder() .all_cargo() .disable_cargo() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert_eq!(DISABLED_OUTPUT, output); teardown(); Ok(()) } #[test] #[serial_test::serial] fn cargo_all_idempotent_output() -> Result<()> { setup(); let mut stdout_buf = vec![]; EmitBuilder::builder() .idempotent() .all_cargo() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(CARGO_REGEX.is_match(&output)); teardown(); Ok(()) } #[test] #[serial_test::serial] fn cargo_all_name_filter_none_output() -> Result<()> { setup(); let mut stdout_buf = vec![]; EmitBuilder::builder() .all_cargo() .cargo_dependencies_name_filter(Some("blah")) .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(CARGO_REGEX_NO_DEP.is_match(&output)); teardown(); Ok(()) } #[test] #[serial_test::serial] fn cargo_all_name_filter_some_output() -> Result<()> { setup(); let mut stdout_buf = vec![]; EmitBuilder::builder() .all_cargo() .cargo_dependencies_name_filter(Some("anyhow")) .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(CARGO_REGEX.is_match(&output)); assert!(CARGO_REGEX_NAME.is_match(&output)); teardown(); Ok(()) } #[test] #[serial_test::serial] fn cargo_all_dep_kind_filter_none_output() -> Result<()> { setup(); let mut stdout_buf = vec![]; EmitBuilder::builder() .all_cargo() .cargo_dependencies_dep_kind_filter(Some(DependencyKind::Build)) .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(CARGO_REGEX_NO_DEP.is_match(&output)); assert!(CARGO_REGEX_RV.is_match(&output)); teardown(); Ok(()) } #[test] #[serial_test::serial] fn cargo_all_dep_kind_filter_some_output() -> Result<()> { setup(); let mut stdout_buf = vec![]; EmitBuilder::builder() .all_cargo() .cargo_dependencies_name_filter(Some("gix")) .cargo_dependencies_dep_kind_filter(Some(DependencyKind::Development)) .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(CARGO_REGEX.is_match(&output)); assert!(CARGO_REGEX_DK.is_match(&output)); teardown(); Ok(()) } } vergen-8.3.2/tests/git_output.rs000064400000000000000000000601411046102023000150410ustar 00000000000000#[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] mod test_git_git2 { use anyhow::Result; use lazy_static::lazy_static; use regex::Regex; use std::env; use vergen::EmitBuilder; use repo_util::TestRepos; lazy_static! { static ref GIT_BRANCH_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_BRANCH=.*"; static ref GIT_CAE_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_EMAIL=\S+@\S+"; static ref GIT_CAN_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_NAME=.*"; static ref GIT_CC_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_COUNT=([0-9]+)"; static ref GIT_CD_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_DATE=([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])"; static ref GIT_CD_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_DATE=VERGEN_IDEMPOTENT_OUTPUT"; static ref GIT_CM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_MESSAGE=[\s\S]+"; static ref GIT_CT_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_TIMESTAMP=([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([\+|\-]([01][0-9]|2[0-3]):[0-5][0-9]))"; static ref GIT_CT_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_TIMESTAMP=VERGEN_IDEMPOTENT_OUTPUT"; static ref GIT_DESCRIBE_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_DESCRIBE=.*"; static ref GIT_SHA_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_SHA=[0-9a-f]{40}"; static ref GIT_SHORT_SHA_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_SHA=[0-9a-f]{7}"; static ref GIT_DIRTY_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_DIRTY=(true|false)"; static ref GIT_BRANCH_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_BRANCH=VERGEN_IDEMPOTENT_OUTPUT"; static ref GIT_COMMIT_AUTHOR_EMAIL_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_EMAIL=VERGEN_IDEMPOTENT_OUTPUT"; static ref GIT_COMMIT_AUTHOR_NAME_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_NAME=VERGEN_IDEMPOTENT_OUTPUT"; static ref GIT_COMMIT_COUNT_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_COUNT=VERGEN_IDEMPOTENT_OUTPUT"; static ref GIT_COMMIT_DATE_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_DATE=VERGEN_IDEMPOTENT_OUTPUT"; static ref GIT_COMMIT_MESSAGE_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_MESSAGE=VERGEN_IDEMPOTENT_OUTPUT"; static ref GIT_COMMIT_TIMESTAMP_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_COMMIT_TIMESTAMP=VERGEN_IDEMPOTENT_OUTPUT"; static ref GIT_DESCRIBE_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_DESCRIBE=VERGEN_IDEMPOTENT_OUTPUT"; static ref GIT_SHA_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_SHA=VERGEN_IDEMPOTENT_OUTPUT"; static ref GIT_DIRTY_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_GIT_DIRTY=VERGEN_IDEMPOTENT_OUTPUT"; static ref WARNINGS_RERUN_RE_STR: &'static str = r"cargo:warning=(.*?) cargo:warning=VERGEN_GIT_BRANCH set to default cargo:warning=VERGEN_GIT_COMMIT_AUTHOR_EMAIL set to default cargo:warning=VERGEN_GIT_COMMIT_AUTHOR_NAME set to default cargo:warning=VERGEN_GIT_COMMIT_COUNT set to default cargo:warning=VERGEN_GIT_COMMIT_DATE set to default cargo:warning=VERGEN_GIT_COMMIT_MESSAGE set to default cargo:warning=VERGEN_GIT_COMMIT_TIMESTAMP set to default cargo:warning=VERGEN_GIT_DESCRIBE set to default cargo:warning=VERGEN_GIT_SHA set to default cargo:warning=VERGEN_GIT_DIRTY set to default cargo:rerun-if-changed=build.rs cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH"; static ref GIT_REGEX_INST: Regex = { let re_str = [ *GIT_BRANCH_RE_STR, *GIT_CAE_RE_STR, *GIT_CAN_RE_STR, *GIT_CC_RE_STR, *GIT_CD_RE_STR, *GIT_CM_RE_STR, *GIT_CT_RE_STR, *GIT_DESCRIBE_RE_STR, *GIT_SHA_RE_STR, *GIT_DIRTY_RE_STR, ] .join("\n"); Regex::new(&re_str).unwrap() }; static ref GIT_REGEX_SHORT_INST: Regex = { let re_str = [ *GIT_BRANCH_RE_STR, *GIT_CAE_RE_STR, *GIT_CAN_RE_STR, *GIT_CC_RE_STR, *GIT_CD_RE_STR, *GIT_CM_RE_STR, *GIT_CT_RE_STR, *GIT_DESCRIBE_RE_STR, *GIT_SHORT_SHA_RE_STR, *GIT_DIRTY_RE_STR, ] .join("\n"); Regex::new(&re_str).unwrap() }; static ref GIT_REGEX_IDEM_INST: Regex = { let re_str = [ *GIT_BRANCH_RE_STR, *GIT_CAE_RE_STR, *GIT_CAN_RE_STR, *GIT_CC_RE_STR, *GIT_CD_IDEM_RE_STR, *GIT_CM_RE_STR, *GIT_CT_IDEM_RE_STR, *GIT_DESCRIBE_RE_STR, *GIT_SHA_RE_STR, *GIT_DIRTY_RE_STR, ] .join("\n"); Regex::new(&re_str).unwrap() }; static ref ALL_IDEM_OUTPUT: Regex = { let re_str = [ *GIT_BRANCH_IDEM_RE_STR, *GIT_COMMIT_AUTHOR_EMAIL_IDEM_RE_STR, *GIT_COMMIT_AUTHOR_NAME_IDEM_RE_STR, *GIT_COMMIT_COUNT_IDEM_RE_STR, *GIT_COMMIT_DATE_IDEM_RE_STR, *GIT_COMMIT_MESSAGE_IDEM_RE_STR, *GIT_COMMIT_TIMESTAMP_IDEM_RE_STR, *GIT_DESCRIBE_IDEM_RE_STR, *GIT_SHA_IDEM_RE_STR, *GIT_DIRTY_IDEM_RE_STR, *WARNINGS_RERUN_RE_STR, ] .join("\n"); Regex::new(&re_str).unwrap() }; } const DISABLED_OUTPUT: &str = r""; cfg_if::cfg_if! { if #[cfg(feature = "gitcl")] { fn repo_exists() -> Result<()> { Ok(()) } } else if #[cfg(feature = "git2")] { use git2_rs::Repository; fn repo_exists() -> Result<()> { let curr_dir = env::current_dir()?; let _repo = Repository::discover(curr_dir)?; Ok(()) } } else if #[cfg(feature = "gix")] { fn repo_exists() -> Result<()> { let curr_dir = env::current_dir()?; let _repo = gix::discover(curr_dir)?; Ok(()) } } else { fn repo_exists() -> Result<()> { Ok(()) } } } #[test] #[serial_test::serial] fn git_all_output_idempotent() -> Result<()> { let mut stdout_buf = vec![]; let failed = EmitBuilder::builder() .all_git() .emit_to_at(&mut stdout_buf, Some(env::temp_dir()))?; let output = String::from_utf8_lossy(&stdout_buf); assert!(failed); assert!(ALL_IDEM_OUTPUT.is_match(&output)); Ok(()) } #[test] #[serial_test::serial] fn git_all_output_default_dir() -> Result<()> { let mut stdout_buf = vec![]; let failed = EmitBuilder::builder().all_git().emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(!failed); assert!(repo_exists().is_ok()); assert!(GIT_REGEX_INST.is_match(&output)); Ok(()) } #[test] #[serial_test::serial] fn git_all_flags_test_repo() -> Result<()> { let repo = TestRepos::new(true, false, false)?; let mut stdout_buf = vec![]; let failed = EmitBuilder::builder() .all_git() .git_describe(true, true, Some("0.1*")) .git_sha(true) .emit_to_at(&mut stdout_buf, Some(repo.path()))?; assert!(!failed); let output = String::from_utf8_lossy(&stdout_buf); assert!(GIT_REGEX_SHORT_INST.is_match(&output)); Ok(()) } #[test] #[serial_test::serial] fn git_all_flags_test_repo_local() -> Result<()> { let repo = TestRepos::new(true, false, false)?; let mut stdout_buf = vec![]; let result = EmitBuilder::builder() .all_git() .git_describe(true, true, Some("0.1*")) .git_sha(true) .use_local_git() .fail_on_error() .emit_to_at(&mut stdout_buf, Some(repo.path())); check_local_result(result, &stdout_buf); Ok(()) } #[cfg(target_os = "windows")] fn check_local_result(result: Result, stdout_buf: &[u8]) { assert!(result.is_ok()); let output = String::from_utf8_lossy(stdout_buf); assert!(GIT_REGEX_SHORT_INST.is_match(&output)); } #[cfg(any(target_os = "linux", target_os = "macos"))] fn check_local_result(result: Result, _stdout_buf: &[u8]) { assert!(result.is_err()); } #[test] #[serial_test::serial] fn git_all_output_test_repo() -> Result<()> { let repo = TestRepos::new(true, true, false)?; let mut stdout_buf = vec![]; let failed = EmitBuilder::builder() .all_git() .git_describe(true, true, Some("0.1*")) .emit_to_at(&mut stdout_buf, Some(repo.path()))?; assert!(!failed); let output = String::from_utf8_lossy(&stdout_buf); assert!(GIT_REGEX_INST.is_match(&output)); Ok(()) } #[test] #[serial_test::serial] fn git_disabled_output() -> Result<()> { let mut stdout_buf = vec![]; EmitBuilder::builder() .all_git() .disable_git() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert_eq!(DISABLED_OUTPUT, output); Ok(()) } #[test] #[serial_test::serial] fn git_emit_at_test_repo() -> Result<()> { let repo = TestRepos::new(true, false, false)?; assert!(EmitBuilder::builder() .all_git() .git_describe(true, true, None) .git_sha(true) .emit_at(repo.path()) .is_ok()); Ok(()) } #[cfg(all(feature = "git", any(feature = "gitcl", feature = "git2")))] #[cfg(test)] mod git_dirty { use anyhow::Result; use vergen::EmitBuilder; use repo_util::TestRepos; const GIT_DIRTY_TRUE_OUTPUT: &str = r"cargo:rustc-env=VERGEN_GIT_DIRTY=true"; const GIT_DIRTY_FALSE_OUTPUT: &str = r"cargo:rustc-env=VERGEN_GIT_DIRTY=false"; fn strip_reruns(output: &str) -> String { let lines: Vec<&str> = output .lines() .filter(|line| !line.starts_with("cargo:rerun-if")) .collect(); lines.join("\n") } #[test] #[serial_test::serial] fn git_dirty_ignore_untracked_no_modified_no_untracked() -> Result<()> { // On a repository with no modified files and no untracked files, // dirty should be false. let repo = TestRepos::new(false, false, false)?; let mut stdout_buf = vec![]; EmitBuilder::builder() .git_dirty(false) .emit_to_at(&mut stdout_buf, Some(repo.path()))?; let output = String::from_utf8_lossy(&stdout_buf); let stripped_output = strip_reruns(&output); assert_eq!(GIT_DIRTY_FALSE_OUTPUT, stripped_output); Ok(()) } #[test] #[serial_test::serial] fn git_dirty_include_untracked_no_modified_no_untracked() -> Result<()> { // On a repository with no modified files and no untracked files, // dirty should be false. let repo = TestRepos::new(false, false, false)?; let mut stdout_buf = vec![]; EmitBuilder::builder() .git_dirty(true) .emit_to_at(&mut stdout_buf, Some(repo.path()))?; let output = String::from_utf8_lossy(&stdout_buf); let stripped_output = strip_reruns(&output); assert_eq!(GIT_DIRTY_FALSE_OUTPUT, stripped_output); Ok(()) } #[test] #[serial_test::serial] fn git_dirty_ignore_untracked_modified_no_untracked() -> Result<()> { // On a repository with modified files and no untracked files, // dirty should be true. let repo = TestRepos::new(true, false, false)?; let mut stdout_buf = vec![]; EmitBuilder::builder() .git_dirty(false) .emit_to_at(&mut stdout_buf, Some(repo.path()))?; let output = String::from_utf8_lossy(&stdout_buf); let stripped_output = strip_reruns(&output); assert_eq!(GIT_DIRTY_TRUE_OUTPUT, stripped_output); Ok(()) } #[test] #[serial_test::serial] fn git_dirty_include_untracked_modified_no_untracked() -> Result<()> { // On a repository with modified files and no untracked files, // dirty should be true. let repo = TestRepos::new(true, false, false)?; let mut stdout_buf = vec![]; EmitBuilder::builder() .git_dirty(true) .emit_to_at(&mut stdout_buf, Some(repo.path()))?; let output = String::from_utf8_lossy(&stdout_buf); let stripped_output = strip_reruns(&output); assert_eq!(GIT_DIRTY_TRUE_OUTPUT, stripped_output); Ok(()) } #[test] #[serial_test::serial] fn git_dirty_ignore_untracked_no_modified_untracked() -> Result<()> { // On a repository with no modified files and untracked files, // dirty should be false when include_untracked is false. let repo = TestRepos::new(false, true, false)?; let mut stdout_buf = vec![]; EmitBuilder::builder() .git_dirty(false) .emit_to_at(&mut stdout_buf, Some(repo.path()))?; let output = String::from_utf8_lossy(&stdout_buf); let stripped_output = strip_reruns(&output); assert_eq!(GIT_DIRTY_FALSE_OUTPUT, stripped_output); Ok(()) } #[test] #[serial_test::serial] fn git_dirty_include_untracked_no_modified_untracked() -> Result<()> { // On a repository with no modified files and untracked files, // dirty should be true when include_untracked is true. let repo = TestRepos::new(false, true, false)?; let mut stdout_buf = vec![]; EmitBuilder::builder() .git_dirty(true) .emit_to_at(&mut stdout_buf, Some(repo.path()))?; let output = String::from_utf8_lossy(&stdout_buf); let stripped_output = strip_reruns(&output); assert_eq!(GIT_DIRTY_TRUE_OUTPUT, stripped_output); Ok(()) } #[test] #[serial_test::serial] fn git_dirty_ignore_untracked_modified_untracked() -> Result<()> { // On a repository with modified files and untracked files, // dirty should be true. let repo = TestRepos::new(true, true, false)?; let mut stdout_buf = vec![]; EmitBuilder::builder() .git_dirty(false) .emit_to_at(&mut stdout_buf, Some(repo.path()))?; let output = String::from_utf8_lossy(&stdout_buf); let stripped_output = strip_reruns(&output); assert_eq!(GIT_DIRTY_TRUE_OUTPUT, stripped_output); Ok(()) } #[test] #[serial_test::serial] fn git_dirty_include_untracked_modified_untracked() -> Result<()> { // On a repository with modified files and untracked files, // dirty should be true. let repo = TestRepos::new(true, true, false)?; let mut stdout_buf = vec![]; EmitBuilder::builder() .git_dirty(true) .emit_to_at(&mut stdout_buf, Some(repo.path()))?; let output = String::from_utf8_lossy(&stdout_buf); let stripped_output = strip_reruns(&output); assert_eq!(GIT_DIRTY_TRUE_OUTPUT, stripped_output); Ok(()) } } #[test] #[serial_test::serial] fn git_all_idempotent_output() -> Result<()> { let mut stdout_buf = vec![]; let failed = EmitBuilder::builder() .idempotent() .all_git() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(!failed); assert!(repo_exists().is_ok()); assert!(GIT_REGEX_IDEM_INST.is_match(&output)); Ok(()) } #[test] #[serial_test::serial] fn git_all_idempotent_output_quiet() -> Result<()> { let mut stdout_buf = vec![]; let failed = EmitBuilder::builder() .idempotent() .quiet() .all_git() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(!failed); assert!(repo_exists().is_ok()); assert!(GIT_REGEX_IDEM_INST.is_match(&output)); Ok(()) } #[test] #[serial_test::serial] fn git_branch_override_works() -> Result<()> { env::set_var("VERGEN_GIT_BRANCH", "this is a bad date"); let mut stdout_buf = vec![]; let failed = EmitBuilder::builder().all_git().emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_GIT_BRANCH=this is a bad date")); if failed { assert!(output.contains("cargo:warning=VERGEN_GIT_BRANCH overidden")); } env::remove_var("VERGEN_GIT_BRANCH"); Ok(()) } #[test] #[serial_test::serial] fn git_commit_author_email_override_works() -> Result<()> { env::set_var("VERGEN_GIT_COMMIT_AUTHOR_EMAIL", "this is a bad date"); let mut stdout_buf = vec![]; let failed = EmitBuilder::builder().all_git().emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!( output.contains("cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_EMAIL=this is a bad date") ); if failed { assert!(output.contains("cargo:warning=VERGEN_GIT_COMMIT_AUTHOR_EMAIL overidden")); } env::remove_var("VERGEN_GIT_COMMIT_AUTHOR_EMAIL"); Ok(()) } #[test] #[serial_test::serial] fn git_commit_author_name_override_works() -> Result<()> { env::set_var("VERGEN_GIT_COMMIT_AUTHOR_NAME", "this is a bad date"); let mut stdout_buf = vec![]; let failed = EmitBuilder::builder().all_git().emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR_NAME=this is a bad date")); if failed { assert!(output.contains("cargo:warning=VERGEN_GIT_COMMIT_AUTHOR_NAME overidden")); } env::remove_var("VERGEN_GIT_COMMIT_AUTHOR_NAME"); Ok(()) } #[test] #[serial_test::serial] fn git_commit_count_override_works() -> Result<()> { env::set_var("VERGEN_GIT_COMMIT_COUNT", "this is a bad date"); let mut stdout_buf = vec![]; let failed = EmitBuilder::builder().all_git().emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_GIT_COMMIT_COUNT=this is a bad date")); if failed { assert!(output.contains("cargo:warning=VERGEN_GIT_COMMIT_COUNT overidden")); } env::remove_var("VERGEN_GIT_COMMIT_COUNT"); Ok(()) } #[test] #[serial_test::serial] fn git_commit_date_override_works() -> Result<()> { env::set_var("VERGEN_GIT_COMMIT_DATE", "this is a bad date"); let mut stdout_buf = vec![]; let failed = EmitBuilder::builder().all_git().emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_GIT_COMMIT_DATE=this is a bad date")); if failed { assert!(output.contains("cargo:warning=VERGEN_GIT_COMMIT_DATE overidden")); } env::remove_var("VERGEN_GIT_COMMIT_DATE"); Ok(()) } #[test] #[serial_test::serial] fn git_commit_message_override_works() -> Result<()> { env::set_var("VERGEN_GIT_COMMIT_MESSAGE", "this is a bad date"); let mut stdout_buf = vec![]; let failed = EmitBuilder::builder().all_git().emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_GIT_COMMIT_MESSAGE=this is a bad date")); if failed { assert!(output.contains("cargo:warning=VERGEN_GIT_COMMIT_MESSAGE overidden")); } env::remove_var("VERGEN_GIT_COMMIT_MESSAGE"); Ok(()) } #[test] #[serial_test::serial] fn git_commit_timestamp_override_works() -> Result<()> { env::set_var("VERGEN_GIT_COMMIT_TIMESTAMP", "this is a bad date"); let mut stdout_buf = vec![]; let failed = EmitBuilder::builder().all_git().emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_GIT_COMMIT_TIMESTAMP=this is a bad date")); if failed { assert!(output.contains("cargo:warning=VERGEN_GIT_COMMIT_TIMESTAMP overidden")); } env::remove_var("VERGEN_GIT_COMMIT_TIMESTAMP"); Ok(()) } #[test] #[serial_test::serial] fn git_describe_override_works() -> Result<()> { env::set_var("VERGEN_GIT_DESCRIBE", "this is a bad date"); let mut stdout_buf = vec![]; let failed = EmitBuilder::builder().all_git().emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_GIT_DESCRIBE=this is a bad date")); if failed { assert!(output.contains("cargo:warning=VERGEN_GIT_DESCRIBE overidden")); } env::remove_var("VERGEN_GIT_DESCRIBE"); Ok(()) } #[test] #[serial_test::serial] fn git_sha_override_works() -> Result<()> { env::set_var("VERGEN_GIT_SHA", "this is a bad date"); let mut stdout_buf = vec![]; let failed = EmitBuilder::builder().all_git().emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_GIT_SHA=this is a bad date")); if failed { assert!(output.contains("cargo:warning=VERGEN_GIT_SHA overidden")); } env::remove_var("VERGEN_GIT_SHA"); Ok(()) } #[test] #[serial_test::serial] fn git_dirty_override_works() -> Result<()> { env::set_var("VERGEN_GIT_DIRTY", "this is a bad date"); let mut stdout_buf = vec![]; let failed = EmitBuilder::builder().all_git().emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(output.contains("cargo:rustc-env=VERGEN_GIT_DIRTY=this is a bad date")); if failed { assert!(output.contains("cargo:warning=VERGEN_GIT_DIRTY overidden")); } env::remove_var("VERGEN_GIT_DIRTY"); Ok(()) } #[cfg(feature = "gitcl")] #[test] #[serial_test::serial] fn git_cmd_override_works() -> Result<()> { let mut stdout_buf = vec![]; let failed = EmitBuilder::builder() .all_git() .git_cmd(Some("git -v")) .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(!failed); assert!(repo_exists().is_ok()); assert!(GIT_REGEX_INST.is_match(&output)); Ok(()) } } vergen-8.3.2/tests/rustc_output.rs000064400000000000000000000047531046102023000154250ustar 00000000000000#[cfg(feature = "rustc")] mod test_rustc { use anyhow::Result; use lazy_static::lazy_static; use regex::Regex; use vergen::EmitBuilder; lazy_static! { static ref RUSTC_CHANNEL_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_RUSTC_CHANNEL=.*"; static ref RUSTC_CD_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_RUSTC_COMMIT_DATE=\d{4}-\d{2}-\d{2}"; static ref RUSTC_CH_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_RUSTC_COMMIT_HASH=[0-9a-f]{40}"; static ref RUSTC_HT_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_RUSTC_HOST_TRIPLE=.*"; static ref RUSTC_LLVM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_RUSTC_LLVM_VERSION=\d{2}\.\d{1}"; static ref RUSTC_SEMVER_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_RUSTC_SEMVER=(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?"; static ref RUSTC_REGEX: Regex = { let re_str = [ *RUSTC_CHANNEL_RE_STR, *RUSTC_CD_RE_STR, *RUSTC_CH_RE_STR, *RUSTC_HT_RE_STR, *RUSTC_LLVM_RE_STR, *RUSTC_SEMVER_RE_STR, ] .join("\n"); Regex::new(&re_str).unwrap() }; } const DISABLED_OUTPUT: &str = r""; #[test] fn rustc_all_output() -> Result<()> { let mut stdout_buf = vec![]; EmitBuilder::builder() .all_rustc() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(RUSTC_REGEX.is_match(&output)); Ok(()) } #[test] #[serial_test::serial] fn rustc_disabled_output() -> Result<()> { let mut stdout_buf = vec![]; EmitBuilder::builder() .all_rustc() .disable_rustc() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert_eq!(DISABLED_OUTPUT, output); Ok(()) } #[test] fn rustc_all_idempotent_output() -> Result<()> { let mut stdout_buf = vec![]; EmitBuilder::builder() .idempotent() .all_rustc() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(RUSTC_REGEX.is_match(&output)); Ok(()) } } vergen-8.3.2/tests/sysinfo_output.rs000064400000000000000000000126011046102023000157460ustar 00000000000000#[cfg(feature = "si")] mod test_sysinfo { use anyhow::Result; use lazy_static::lazy_static; use regex::Regex; use vergen::EmitBuilder; lazy_static! { static ref NAME_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_SYSINFO_NAME=.*"; static ref NAME_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_SYSINFO_NAME=VERGEN_IDEMPOTENT_OUTPUT"; static ref OS_VERSION_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_SYSINFO_OS_VERSION=.*"; static ref OS_VERSION_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_SYSINFO_OS_VERSION=VERGEN_IDEMPOTENT_OUTPUT"; static ref USER_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_SYSINFO_USER=.*"; static ref USER_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_SYSINFO_USER=VERGEN_IDEMPOTENT_OUTPUT"; static ref TOTAL_MEMORY_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_SYSINFO_TOTAL_MEMORY=.*"; static ref TOTAL_MEMORY_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_SYSINFO_TOTAL_MEMORY=VERGEN_IDEMPOTENT_OUTPUT"; static ref CPU_VENDOR_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_SYSINFO_CPU_VENDOR=.*"; static ref CPU_VENDOR_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_SYSINFO_CPU_VENDOR=VERGEN_IDEMPOTENT_OUTPUT"; static ref CPU_CORE_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_SYSINFO_CPU_CORE_COUNT=.*"; static ref CPU_CORE_IDEM_RE_STR: &'static str = r"cargo:rustc-env=VERGEN_SYSINFO_CPU_CORE_COUNT=VERGEN_IDEMPOTENT_OUTPUT"; static ref SYSINFO_REGEX_INST: Regex = { let re_str = [ *NAME_RE_STR, *OS_VERSION_RE_STR, *USER_RE_STR, *TOTAL_MEMORY_RE_STR, *CPU_VENDOR_RE_STR, *CPU_CORE_RE_STR, ] .join("\n"); Regex::new(&re_str).unwrap() }; } const IDEM_OUTPUT: &str = r"cargo:rustc-env=VERGEN_SYSINFO_NAME=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_OS_VERSION=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_USER=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_TOTAL_MEMORY=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_CPU_VENDOR=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_CPU_CORE_COUNT=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_CPU_NAME=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_CPU_BRAND=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_CPU_FREQUENCY=VERGEN_IDEMPOTENT_OUTPUT cargo:warning=VERGEN_SYSINFO_NAME set to default cargo:warning=VERGEN_SYSINFO_OS_VERSION set to default cargo:warning=VERGEN_SYSINFO_USER set to default cargo:warning=VERGEN_SYSINFO_TOTAL_MEMORY set to default cargo:warning=VERGEN_SYSINFO_CPU_VENDOR set to default cargo:warning=VERGEN_SYSINFO_CPU_CORE_COUNT set to default cargo:warning=VERGEN_SYSINFO_CPU_NAME set to default cargo:warning=VERGEN_SYSINFO_CPU_BRAND set to default cargo:warning=VERGEN_SYSINFO_CPU_FREQUENCY set to default cargo:rerun-if-changed=build.rs cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH "; const IDEM_QUITE_OUTPUT: &str = r"cargo:rustc-env=VERGEN_SYSINFO_NAME=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_OS_VERSION=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_USER=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_TOTAL_MEMORY=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_CPU_VENDOR=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_CPU_CORE_COUNT=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_CPU_NAME=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_CPU_BRAND=VERGEN_IDEMPOTENT_OUTPUT cargo:rustc-env=VERGEN_SYSINFO_CPU_FREQUENCY=VERGEN_IDEMPOTENT_OUTPUT cargo:rerun-if-changed=build.rs cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH "; const DISABLED_OUTPUT: &str = r""; #[test] fn sysinfo_all_output() -> Result<()> { let mut stdout_buf = vec![]; EmitBuilder::builder() .all_sysinfo() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert!(SYSINFO_REGEX_INST.is_match(&output)); Ok(()) } #[test] #[serial_test::serial] fn sysinfo_disabled_output() -> Result<()> { let mut stdout_buf = vec![]; EmitBuilder::builder() .all_sysinfo() .disable_sysinfo() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert_eq!(DISABLED_OUTPUT, output); Ok(()) } #[test] fn sysinfo_all_idempotent_output() -> Result<()> { let mut stdout_buf = vec![]; EmitBuilder::builder() .idempotent() .all_sysinfo() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert_eq!(IDEM_OUTPUT, output); Ok(()) } #[test] fn sysinfo_all_idempotent_quiet_output() -> Result<()> { let mut stdout_buf = vec![]; EmitBuilder::builder() .idempotent() .quiet() .all_sysinfo() .emit_to(&mut stdout_buf)?; let output = String::from_utf8_lossy(&stdout_buf); assert_eq!(IDEM_QUITE_OUTPUT, output); Ok(()) } }