vergen-8.2.6/.cargo_vcs_info.json0000644000000001440000000000100123360ustar { "git": { "sha1": "825212d3b5663715053cfee6fd8ef2001a415f88" }, "path_in_vcs": "vergen" }vergen-8.2.6/Cargo.toml0000644000000047120000000000100103410ustar # 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.2.6" authors = ["Jason Ozias "] 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 = [ "git", "git2", "git2-rs", "gitcl", "gix", "gitoxide", "rustc_version", "sysinfo", "time", "unstable", ] [package.metadata.docs.rs] features = [ "build", "cargo", "git", "gitcl", "rustc", "si", ] rustdoc-args = [ "--cfg", "docsrs", ] [dependencies.anyhow] version = "1.0.72" [dependencies.git2-rs] version = "0.18.0" optional = true default-features = false package = "git2" [dependencies.gix] version = "0.55.2" features = [ "revision", "interrupt", ] optional = true default-features = false [dependencies.rustc_version] version = "0.4.0" optional = true [dependencies.sysinfo] version = "0.29.7" optional = true default-features = false [dependencies.time] version = "0.3.23" features = [ "formatting", "local-offset", "parsing", ] optional = true [dev-dependencies.gix] version = "0.55.2" features = [ "interrupt", "worktree-mutation", "blocking-network-client", ] default-features = false [dev-dependencies.lazy_static] version = "1.4.0" [dev-dependencies.regex] version = "1.9.1" [dev-dependencies.serial_test] version = "2.0.0" [build-dependencies.rustversion] version = "1.0.14" [features] build = ["time"] cargo = [] default = [] git = [] git2 = [ "time", "git2-rs", ] gitcl = ["time"] gitoxide = [ "time", "gix", ] rustc = ["rustc_version"] si = ["sysinfo"] unstable = [] vergen-8.2.6/Cargo.toml.orig000064400000000000000000000034171046102023000140230ustar 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.2.6" [package.metadata.cargo-all-features] denylist = [ "git", "git2", "git2-rs", "gitcl", "gix", "gitoxide", "rustc_version", "sysinfo", "time", "unstable", ] [features] default = [] build = ["time"] cargo = [] git = [] gitcl = ["time"] git2 = ["time", "git2-rs"] gitoxide = ["time", "gix"] rustc = ["rustc_version"] unstable = [] si = ["sysinfo"] [dependencies] anyhow = "1.0.72" git2-rs = { version = "0.18.0", package = "git2", optional = true, default-features = false } gix = { version = "0.55.2", optional = true, default-features = false, features = ["revision", "interrupt"] } rustc_version = { version = "0.4.0", optional = true } sysinfo = { version = "0.29.7", 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 = { version = "0.55.2", default-features = false, features = [ "interrupt", "worktree-mutation", "blocking-network-client", ] } lazy_static = "1.4.0" regex = "1.9.1" serial_test = "2.0.0" [package.metadata.docs.rs] features = ["build", "cargo", "git", "gitcl", "rustc", "si"] rustdoc-args = ["--cfg", "docsrs"] vergen-8.2.6/LICENSE-APACHE000064400000000000000000000251411046102023000130560ustar 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.2.6/LICENSE-MIT000064400000000000000000000020571046102023000125670ustar 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.2.6/README.md000064400000000000000000000115571046102023000124170ustar 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) ## Sponsors Special thanks to the sponsors of this project * [tryretool](https://github.com/tryretool) # ⚠️ This documention is for version 8 ⚠️ If you wish to refer to version 7 you can find that branch [`here`](https://github.com/rustyhorde/vergen/tree/legacy/v7) ## MSRV The current minimum supported rust version is 1.67.0 ## Example Usage See the documentation at [docs.rs](https://docs.rs/vergen/latest/vergen/) for example usage ## Notes about the optional `git2` dependency This update to 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.2.6/build.rs000064400000000000000000000011431046102023000125730ustar 00000000000000pub fn main() { println!("cargo:rerun-if-changed=build.rs"); nightly(); beta(); stable(); msrv(); } #[rustversion::nightly] fn nightly() { println!("cargo:rustc-cfg=nightly"); } #[rustversion::not(nightly)] fn nightly() {} #[rustversion::beta] fn beta() { println!("cargo:rustc-cfg=beta"); } #[rustversion::not(beta)] fn beta() {} #[rustversion::stable] fn stable() { println!("cargo:rustc-cfg=stable"); } #[rustversion::not(stable)] fn stable() {} #[rustversion::before(1.67)] fn msrv() {} #[rustversion::since(1.67)] fn msrv() { println!("cargo:rustc-cfg=msrv"); } vergen-8.2.6/src/constants.rs000064400000000000000000000174611046102023000143110ustar 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"; // 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_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"); } #[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.2.6/src/emitter.rs000064400000000000000000000765241046102023000137530ustar 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 )] 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 { writeln!(stdout, "cargo:rustc-env={}={v}", k.name())?; } // Emit the `cargo:warning` instructions if !quiet { for warning in &self.warnings { writeln!(stdout, "cargo:warning={warning}")?; } } // Emit the 'cargo:rerun-if-changed' instructions for the git paths (if added) for path in &self.rerun_if_changed { writeln!(stdout, "cargo:rerun-if-changed={path}")?; } // Emit the 'cargo:rerun-if-changed' instructions if !self.cargo_rustc_env_map.is_empty() || !self.warnings.is_empty() { let buildrs = if let Some(path) = custom_buildrs { path } else { "build.rs" }; writeln!(stdout, "cargo:rerun-if-changed={buildrs}")?; writeln!(stdout, "cargo:rerun-if-env-changed=VERGEN_IDEMPOTENT")?; writeln!(stdout, "cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH")?; } Ok(()) } } /// 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.2.6/src/feature/build.rs000064400000000000000000000313661046102023000150270ustar 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(std::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()?; println!("{:?}", config.cargo_rustc_env_map); 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.2.6/src/feature/cargo.rs000064400000000000000000000257101046102023000150170ustar 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_FEATURES, CARGO_OPT_LEVEL, CARGO_TARGET_TRIPLE}, emitter::{EmitBuilder, RustcEnvMap}, key::VergenKey, utils::fns::{add_default_map_entry, add_map_entry}, }; use anyhow::{Error, Result}; use std::env; #[derive(Clone, Copy, Debug, Default)] #[allow(clippy::struct_excessive_bools)] 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, } impl Config { pub(crate) fn any(self) -> bool { self.cargo_debug || self.cargo_features || self.cargo_opt_level || self.cargo_target_triple } } /// 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() } /// 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 } 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); } 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); } } } Ok(()) } } 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!(4, 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!(4, 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!(4, emit.cargo_rustc_env_map.len()); assert_eq!(4, count_idempotent(&emit.cargo_rustc_env_map)); assert_eq!(4, 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(()) } } vergen-8.2.6/src/feature/git/cmd.rs000064400000000000000000000734461046102023000152630ustar 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_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, 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!(); /// 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 /// 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_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 } /// 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 } /// 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); } 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 = if let Some(cmd) = self.git_config.git_cmd { cmd } else { "git --version" }; check_git(git_cmd).and_then(check_inside_git_worktree)?; 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 let Some(path) = path { env::set_current_dir(path)?; } if !idempotent && self.any() { add_rerun_if_changed(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_git_cmd_entry(BRANCH_CMD, 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, 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, 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, VergenKey::GitCommitCount, map)?; } } self.add_git_timestamp_entries(COMMIT_TIMESTAMP, 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, 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, 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, VergenKey::GitSha, map)?; } } Ok(()) } fn add_git_timestamp_entries( &self, cmd: &str, 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)?; 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(std::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((): ()) -> Result<()> { if inside_git_worktree() { Ok(()) } else { Err(anyhow!("not within a suitable 'git' worktree!")) } } fn git_cmd_exists(cmd: &str) -> bool { run_cmd(cmd) .map(|output| output.status.success()) .unwrap_or(false) } fn inside_git_worktree() -> bool { run_cmd("git rev-parse --is-inside-work-tree") .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) -> 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); _ = 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) -> Result { let mut cmd = Command::new("cmd"); _ = cmd.arg("/c"); _ = cmd.arg(command); _ = cmd.stdout(Stdio::piped()); _ = cmd.stderr(Stdio::piped()); Ok(cmd.output()?) } fn add_git_cmd_entry(cmd: &str, key: VergenKey, map: &mut RustcEnvMap) -> Result<()> { let output = run_cmd(cmd)?; 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) -> Result<()> { let git_path = run_cmd("git rev-parse --git-dir")?; 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()?; 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() -> Result { run_cmd("git symbolic-ref HEAD") } #[cfg(all(test, not(target_os = "windows")))] fn setup_ref_path() -> Result { run_cmd("pwd") } #[cfg(all(test, target_os = "windows"))] fn setup_ref_path() -> Result { run_cmd("cd") } #[cfg(test)] mod test { use super::{add_git_cmd_entry, check_git, check_inside_git_worktree}; use crate::{ emitter::test::count_idempotent, key::VergenKey, utils::repo::{clone_path, clone_test_repo, create_test_repo}, EmitBuilder, }; use anyhow::Result; 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", VergenKey::GitCommitMessage, &mut map) .is_err() ); Ok(()) } #[test] #[serial_test::serial] fn non_working_tree_is_error() -> Result<()> { let curr_dir = env::current_dir()?; env::set_current_dir("..")?; assert!(check_inside_git_worktree(()).is_err()); env::set_current_dir(curr_dir)?; 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<()> { let curr_shell = env::var("SHELL"); env::set_var("SHELL", "bash"); let mut map = BTreeMap::new(); assert!(add_git_cmd_entry("git -v", VergenKey::GitCommitMessage, &mut map).is_ok()); if let Ok(curr_shell) = curr_shell { env::set_var("SHELL", curr_shell); } Ok(()) } #[test] #[serial_test::serial] fn git_all_idempotent() -> Result<()> { let config = EmitBuilder::builder() .idempotent() .all_git() .test_emit_at(None)?; assert_eq!(9, 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!(9, 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<()> { create_test_repo(); clone_test_repo(); let config = EmitBuilder::builder() .all_git() .test_emit_at(Some(clone_path()))?; assert_eq!(9, 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!(9, 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_dirty_tags_short() -> Result<()> { let config = EmitBuilder::builder() .all_git() .git_describe(true, true, None) .git_sha(true) .test_emit()?; assert_eq!(9, 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!(9, emitter.cargo_rustc_env_map.len()); assert_eq!(9, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(10, 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", false, &mut map, &mut warnings) .is_ok()); assert_eq!(2, map.len()); assert_eq!(2, warnings.len()); Ok(()) } } vergen-8.2.6/src/feature/git/git2.rs000064400000000000000000000574651046102023000153700ustar 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_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}; 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, 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 | /// /// # 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) } 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 } /// 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 } /// 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); } 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(()) } 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_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(std::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, utils::repo::{clone_path, clone_test_repo, create_test_repo}, EmitBuilder, }; use anyhow::Result; use git2_rs::Repository; 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<()> { create_test_repo(); clone_test_repo(); 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(clone_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!(9, 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_idempotent_no_warn() -> Result<()> { let config = EmitBuilder::builder() .idempotent() .quiet() .all_git() .test_emit_at(None)?; assert_eq!(9, 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!(9, 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!(9, emitter.cargo_rustc_env_map.len()); assert_eq!(9, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(10, emitter.warnings.len()); Ok(()) } } vergen-8.2.6/src/feature/git/gix.rs000064400000000000000000000502151046102023000152740ustar 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_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 gix::{commit, head::Kind, Commit, Head}; 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, #[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) } 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 } /// 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 } 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); } 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 = head.peel_to_commit_in_place()?; 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 names = if self.git_config.git_describe_tags { commit::describe::SelectRef::AllTags } else { commit::describe::SelectRef::AnnotatedTags }; let describe = commit .describe() .names(names) // note: this turns on id_as_fallback .format() .map(|mut fmt| { if fmt.depth > 0 && self.git_config.git_describe_dirty { fmt.dirty_suffix = Some("dirty".to_string()); } fmt.to_string() })?; 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); } } 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(std::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, utils::repo::{clone_path, clone_test_repo, create_test_repo}, EmitBuilder, }; use anyhow::Result; #[test] #[serial_test::serial] fn git_all_idempotent() -> Result<()> { let emitter = EmitBuilder::builder() .idempotent() .all_git() .test_emit_at(None)?; assert_eq!(9, 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!(9, 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!(9, 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<()> { create_test_repo(); clone_test_repo(); let emitter = EmitBuilder::builder() .all_git() .test_emit_at(Some(clone_path()))?; assert_eq!(9, 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!(9, emitter.cargo_rustc_env_map.len()); assert_eq!(9, count_idempotent(&emitter.cargo_rustc_env_map)); assert_eq!(10, emitter.warnings.len()); Ok(()) } } vergen-8.2.6/src/feature/git/mod.rs000064400000000000000000000013731046102023000152650ustar 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 = "gitcl")] pub(crate) mod cmd; #[cfg(feature = "git2")] pub(crate) mod git2; #[cfg(feature = "gix")] pub(crate) mod gix; #[cfg(all(feature = "git", feature = "gitcl"))] pub(crate) use self::cmd::Config; #[cfg(all(feature = "git", feature = "git2"))] pub(crate) use self::git2::Config; #[cfg(all(feature = "git", feature = "gix"))] pub(crate) use self::gix::Config; vergen-8.2.6/src/feature/mod.rs000064400000000000000000000011441046102023000144760ustar 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.2.6/src/feature/rustc.rs000064400000000000000000000370701046102023000150660ustar 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)] 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.2.6/src/feature/si.rs000064400000000000000000000564011046102023000143400ustar 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, CpuExt, Pid, Process, ProcessExt, System, SystemExt, User, UserExt, }; #[derive(Clone, Copy, Debug, Default)] #[allow(clippy::struct_excessive_bools)] pub(crate) struct Config { 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_name() .sysinfo_os_version() .sysinfo_user() .sysinfo_memory() .sysinfo_cpu_vendor() .sysinfo_cpu_core_count() .sysinfo_cpu_name() .sysinfo_cpu_brand() .sysinfo_cpu_frequency() } /// 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.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(CpuExt::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) { for user in system.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() -> System { let mut system = System::new_all(); system.refresh_all(); system.refresh_users_list(); system } 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}; 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 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.2.6/src/key.rs000064400000000000000000000253521046102023000130630ustar 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_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_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 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, /// 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(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(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, } } } } #[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.2.6/src/lib.rs000064400000000000000000000422031046102023000130330ustar 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. //! //! ``` //! use std::error::Error; //! use vergen::EmitBuilder; //! //! fn main() -> Result<(), Box> { //! // Emit the instructions //! EmitBuilder::builder().emit()?; //! Ok(()) //! } //! ``` //! //! 4. Use the [`env!`](std::env!) macro in your code to read the environment variables. //! //! ```compile_fail //! 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" ]} //! # ... //! ``` //! //! ## 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(msrv, feature = "unstable", nightly), feature( lint_reasons, multiple_supertrait_upcastable, must_not_suspend, non_exhaustive_omitted_patterns_lint, strict_provenance, type_privacy_lints, rustdoc_missing_doc_code_examples, ) )] #![cfg_attr( msrv, deny( absolute_paths_not_starting_with_crate, anonymous_parameters, array_into_iter, asm_sub_register, bad_asm_style, bare_trait_objects, // box_pointers, break_with_label_and_loop, clashing_extern_declarations, coherence_leak_check, confusable_idents, const_evaluatable_unchecked, const_item_mutation, dead_code, deprecated, deprecated_in_future, deprecated_where_clause_location, deref_into_dyn_supertrait, deref_nullptr, drop_bounds, duplicate_macro_attributes, dyn_drop, elided_lifetimes_in_paths, ellipsis_inclusive_range_patterns, explicit_outlives_requirements, exported_private_dependencies, forbidden_lint_groups, for_loops_over_fallibles, function_item_references, illegal_floating_point_literal_pattern, improper_ctypes, improper_ctypes_definitions, incomplete_features, indirect_structural_match, inline_no_sanitize, invalid_doc_attributes, invalid_value, irrefutable_let_patterns, keyword_idents, large_assignments, late_bound_lifetime_arguments, legacy_derive_helpers, let_underscore_drop, macro_use_extern_crate, meta_variable_misuse, missing_abi, missing_copy_implementations, missing_debug_implementations, missing_docs, mixed_script_confusables, named_arguments_used_positionally, no_mangle_generic_items, non_ascii_idents, non_camel_case_types, non_fmt_panics, non_shorthand_field_patterns, non_snake_case, nontrivial_structural_match, non_upper_case_globals, noop_method_call, opaque_hidden_inferred_bound, overlapping_range_endpoints, path_statements, pointer_structural_match, redundant_semicolons, 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, single_use_lifetimes, special_module_name, stable_features, suspicious_auto_trait_impls, temporary_cstring_as_ptr, trivial_bounds, trivial_casts, trivial_numeric_casts, type_alias_bounds, tyvar_behind_raw_pointer, uncommon_codepoints, unconditional_recursion, unexpected_cfgs, ungated_async_fn_track_caller, uninhabited_static, unknown_lints, unnameable_test_items, unreachable_code, unreachable_patterns, unreachable_pub, unsafe_code, unsafe_op_in_unsafe_fn, unstable_features, unstable_name_collisions, unstable_syntax_pre_expansion, unsupported_calling_conventions, unused_allocation, unused_assignments, 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_tuple_struct_fields, unused_unsafe, unused_variables, variant_size_differences, where_clauses_object_safety, while_true, ) )] #![cfg_attr(msrv, allow(single_use_lifetimes))] // If nightly or beta and unstable, allow `unstable_features` #![cfg_attr( all(msrv, feature = "unstable", any(nightly, beta)), allow(unstable_features) )] // The unstable lints #![cfg_attr( all(msrv, feature = "unstable", nightly), deny( ffi_unwind_calls, fuzzy_provenance_casts, lossy_provenance_casts, multiple_supertrait_upcastable, must_not_suspend, non_exhaustive_omitted_patterns, private_bounds, private_interfaces, unfulfilled_lint_expectations, unnameable_types, ) )] // If nightly and not unstable, deny `unstable_features` #![cfg_attr(all(msrv, not(feature = "unstable"), nightly), deny(unstable_features))] // nightly only lints #![cfg_attr( all(msrv, nightly), deny(ambiguous_glob_imports, invalid_reference_casting) )] // nightly or beta only lints #![cfg_attr( all(msrv, any(beta, nightly)), deny( ambiguous_glob_reexports, byte_slice_in_packed_struct_with_derive, dropping_copy_types, dropping_references, forgetting_copy_types, forgetting_references, hidden_glob_reexports, invalid_from_utf8, invalid_macro_export_arguments, invalid_nan_comparisons, map_unit_fn, suspicious_double_ref_op, undefined_naked_function_abi, unused_associated_type_bounds, ) )] // beta only lints // #![cfg_attr( all(msrv, beta), deny())] // beta or stable only lints // #![cfg_attr(all(msrv, any(beta, stable)), deny())] // stable only lints #![cfg_attr( all(msrv, stable), deny(bindings_with_variant_name, implied_bounds_entailment) )] // clippy lints #![cfg_attr(msrv, deny(clippy::all, clippy::pedantic))] #![cfg_attr(all(msrv, nightly), allow(clippy::struct_field_names))] // rustdoc lints #![cfg_attr( msrv, 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(msrv, feature = "unstable", nightly), deny(rustdoc::missing_doc_code_examples) )] #![cfg_attr(all(doc, nightly), feature(doc_auto_cfg))] #![cfg_attr(all(docsrs, nightly), feature(doc_cfg))] mod constants; mod emitter; mod feature; mod key; mod utils; // This is here to appease the `unused_crate_dependencies` lint #[cfg(test)] use {gix as _, lazy_static as _, regex as _, serial_test as _}; pub use crate::emitter::EmitBuilder; vergen-8.2.6/src/utils.rs000064400000000000000000000234511046102023000134310ustar 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()); } } #[cfg(all( test, all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ) ))] pub(crate) mod repo { use anyhow::Result; use git::{create::Options, open, refs::transaction::PreviousValue}; use gix as git; use std::{ env, fs::{self, OpenOptions}, io::BufWriter, io::Write, path::PathBuf, sync::Once, }; const BARE_REPO_NAME: &str = "vergen_tmp1.git"; const CLONE_NAME: &str = "vergen_tmp1"; static CREATE_TEST_REPO: Once = Once::new(); static CLONE_TEST_REPO: Once = Once::new(); pub(crate) fn create_test_repo() { CREATE_TEST_REPO.call_once(|| { || -> Result<()> { let path = repo_path(); // Always make sure to re-create repo in CI if let Ok(_ci) = env::var("CI") { let _res = fs::remove_dir_all(&path); } if !path.exists() { // Initialize a bare repository let mut repo = git::init_bare(&path)?; // Create an empty tree for the initial commit let mut tree = git::objs::Tree::empty(); let empty_tree_id = repo.write_object(&tree)?.detach(); // Setup the base configuration let mut config = repo.config_snapshot_mut(); let _old = config.set_raw_value("user", None, "name", "Vergen Test")?; let _old = config.set_raw_value("user", None, "email", "vergen@blah.com")?; { // Create an empty commit with the initial empty tree let committer = config.commit_auto_rollback()?; let initial_commit_id = committer.commit( "HEAD", "initial commit", empty_tree_id, git::commit::NO_PARENT_IDS, )?; // Create a BLOB to commit, along with the corresponding tree entry let first_blob_id = committer.write_blob("hello, world")?.into(); let entry = git::objs::tree::Entry { mode: git::objs::tree::EntryMode::Blob, filename: "foo.txt".into(), oid: first_blob_id, }; // Add everything to the empty tree tree.entries.push(entry); let first_tree_id = committer.write_object(&tree)?; // Make the commit let first_commit_id = committer.commit( "HEAD", "foo commit", first_tree_id, [initial_commit_id], )?; // Tag the previous commit let _tag_id = committer.tag( "0.1.0", first_commit_id, git::objs::Kind::Commit, None, "v0.1.0", PreviousValue::MustNotExist, )?; // Create a new BLOB to commit let second_blob_id = committer.write_blob("Hello, World!")?.into(); let entry = git::objs::tree::Entry { mode: git::objs::tree::EntryMode::Blob, oid: second_blob_id, filename: "foo.txt".into(), }; // Setup a new tree for this commit let mut second_tree = git::objs::Tree::empty(); second_tree.entries.push(entry); let second_tree_id = committer.write_object(&second_tree)?; // Make the commit let _second_commit_id = committer.commit( "HEAD", "such bad casing", second_tree_id, [first_commit_id], )?; } } Ok(()) }() .expect("unable to create test repository"); }); } pub(crate) fn clone_test_repo() { CLONE_TEST_REPO.call_once(|| { || -> Result<()> { // The bare repository path let bare_repo_path = repo_path(); // The path we are cloning into let clone_path = clone_path(); // Always make sure to clone a fresh directory in CI if let Ok(_ci) = env::var("CI") { let _res = fs::remove_dir_all(&clone_path); } if !clone_path.exists() { fs::create_dir_all(&clone_path)?; let _res = git::interrupt::init_handler(0, || {})?; let url = git::url::parse(git::path::os_str_into_bstr(bare_repo_path.as_os_str())?)?; let opts = open::Options::isolated() .config_overrides(["user.name=Vergen Test", "user.email=vergen@blah.com"]); let mut prep = git::clone::PrepareFetch::new( url, &clone_path, git::create::Kind::WithWorktree, Options::default(), opts, )?; let (mut prepare_checkout, _) = prep.fetch_then_checkout( git::progress::Discard, &git::interrupt::IS_INTERRUPTED, )?; let (_repo, _) = prepare_checkout .main_worktree(git::progress::Discard, &git::interrupt::IS_INTERRUPTED)?; let file_path = clone_path.join("foo.txt"); let foo_txt = OpenOptions::new().append(true).open(file_path)?; let mut writer = BufWriter::new(foo_txt); writeln!(writer, "another test line")?; } Ok(()) }() .expect("unable to clone the test repository"); }); } pub(crate) fn repo_path() -> PathBuf { let clone_path = if let Ok(temp_path) = env::var("RUNNER_TEMP") { PathBuf::from(temp_path) } else { env::temp_dir() }; clone_path.join(BARE_REPO_NAME) } pub(crate) fn clone_path() -> PathBuf { let clone_path = if let Ok(temp_path) = env::var("RUNNER_TEMP") { PathBuf::from(temp_path) } else { env::temp_dir() }; clone_path.join(CLONE_NAME) } #[cfg(test)] mod test { use super::{clone_path, repo_path, BARE_REPO_NAME, CLONE_NAME}; use std::env; #[test] #[serial_test::serial] fn repo_path_temp_dir_works() { if let Ok(runner_temp) = env::var("RUNNER_TEMP") { env::remove_var("RUNNER_TEMP"); assert!(repo_path().ends_with(BARE_REPO_NAME)); env::set_var("RUNNER_TEMP", runner_temp); } else { assert!(repo_path().ends_with(BARE_REPO_NAME)); } } #[test] #[serial_test::serial] fn clone_path_temp_dir_works() { if let Ok(runner_temp) = env::var("RUNNER_TEMP") { env::remove_var("RUNNER_TEMP"); assert!(clone_path().ends_with(CLONE_NAME)); env::set_var("RUNNER_TEMP", runner_temp); } else { assert!(clone_path().ends_with(CLONE_NAME)); } } } } vergen-8.2.6/tarpaulin.toml000064400000000000000000000002601046102023000140210ustar 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.2.6/tests/build_output.rs000064400000000000000000000127401046102023000153620ustar 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.2.6/tests/cargo_output.rs000064400000000000000000000052721046102023000153600ustar 00000000000000#[cfg(feature = "cargo")] mod test_build { use anyhow::Result; 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_REGEX: 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() }; } 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(()) } } vergen-8.2.6/tests/git_output.rs000064400000000000000000000615671046102023000150610ustar 00000000000000#[cfg(all( feature = "git", any(feature = "gitcl", feature = "git2", feature = "gix") ))] mod test_git_git2 { use anyhow::Result; use git::{create::Options, open, refs::transaction::PreviousValue}; #[cfg(feature = "git2")] use git2_rs::Repository; use gix as git; use lazy_static::lazy_static; use regex::Regex; use std::{ env, fs::{self, OpenOptions}, io::BufWriter, io::Write, path::PathBuf, sync::Once, }; use vergen::EmitBuilder; 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_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 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: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, ] .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, ] .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, ] .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, *WARNINGS_RERUN_RE_STR, ] .join("\n"); Regex::new(&re_str).unwrap() }; } const IDEM_QUIET_OUTPUT: &str = r"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: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""; const BARE_REPO_NAME: &str = "vergen_tmp.git"; const CLONE_NAME: &str = "vergen_tmp"; static CREATE_TEST_REPO: Once = Once::new(); static CLONE_TEST_REPO: Once = Once::new(); fn create_test_repo() { CREATE_TEST_REPO.call_once(|| { || -> Result<()> { let path = repo_path(); // Always make sure to re-create repo in CI if let Ok(_ci) = env::var("CI") { let _res = fs::remove_dir_all(&path); } if !path.exists() { // Initialize a bare repository let mut repo = git::init_bare(&path)?; // Create an empty tree for the initial commit let mut tree = git::objs::Tree::empty(); let empty_tree_id = repo.write_object(&tree)?.detach(); // Setup the base configuration let mut config = repo.config_snapshot_mut(); let _old = config.set_raw_value("user", None, "name", "Vergen Test")?; let _old = config.set_raw_value("user", None, "email", "vergen@blah.com")?; { // Create an empty commit with the initial empty tree let committer = config.commit_auto_rollback()?; let initial_commit_id = committer.commit( "HEAD", "initial commit", empty_tree_id, git::commit::NO_PARENT_IDS, )?; // Create a BLOB to commit, along with the corresponding tree entry let first_blob_id = committer.write_blob("hello, world")?.into(); let entry = git::objs::tree::Entry { mode: git::objs::tree::EntryMode::Blob, filename: "foo.txt".into(), oid: first_blob_id, }; // Add everything to the empty tree tree.entries.push(entry); let first_tree_id = committer.write_object(&tree)?; // Make the commit let first_commit_id = committer.commit( "HEAD", "foo commit", first_tree_id, [initial_commit_id], )?; // Tag the previous commit let _tag_id = committer.tag( "0.1.0", first_commit_id, git::objs::Kind::Commit, None, "v0.1.0", PreviousValue::MustNotExist, )?; // Create a new BLOB to commit let second_blob_id = committer.write_blob("Hello, World!")?.into(); let entry = git::objs::tree::Entry { mode: git::objs::tree::EntryMode::Blob, oid: second_blob_id, filename: "foo.txt".into(), }; // Setup a new tree for this commit let mut second_tree = git::objs::Tree::empty(); second_tree.entries.push(entry); let second_tree_id = committer.write_object(&second_tree)?; // Make the commit let _second_commit_id = committer.commit( "HEAD", "such bad casing", second_tree_id, [first_commit_id], )?; } } Ok(()) }() .expect("unable to create test repository"); }); } fn clone_test_repo() { CLONE_TEST_REPO.call_once(|| { || -> Result<()> { // The bare repository path let bare_repo_path = repo_path(); // The path we are cloning into let clone_path = clone_path(); // Always make sure to clone a fresh directory in CI if let Ok(_ci) = env::var("CI") { let _res = fs::remove_dir_all(&clone_path); } if !clone_path.exists() { // Setup the directory fs::create_dir_all(&clone_path)?; // Clone into the directory let _res = git::interrupt::init_handler(0, || {})?; let url = git::url::parse(git::path::os_str_into_bstr(bare_repo_path.as_os_str())?)?; let opts = open::Options::isolated() .config_overrides(["user.name=Vergen Test", "user.email=vergen@blah.com"]); let mut prep = git::clone::PrepareFetch::new( url, &clone_path, git::create::Kind::WithWorktree, Options::default(), opts, )?; let (mut prepare_checkout, _) = prep.fetch_then_checkout( git::progress::Discard, &git::interrupt::IS_INTERRUPTED, )?; let (_repo, _) = prepare_checkout .main_worktree(git::progress::Discard, &git::interrupt::IS_INTERRUPTED)?; // "edit" a file to mark the repository describe as dirty let file_path = clone_path.join("foo.txt"); let foo = OpenOptions::new().append(true).open(file_path)?; let mut writer = BufWriter::new(foo); writeln!(writer, "another test line")?; } Ok(()) }() .expect("unable to clone the test repository") }); } fn repo_path() -> PathBuf { let clone_path = if let Ok(temp_path) = env::var("RUNNER_TEMP") { PathBuf::from(temp_path) } else { env::temp_dir() }; clone_path.join(BARE_REPO_NAME) } fn clone_path() -> PathBuf { let clone_path = if let Ok(temp_path) = env::var("RUNNER_TEMP") { PathBuf::from(temp_path) } else { env::temp_dir() }; clone_path.join(CLONE_NAME) } #[cfg(feature = "git2")] fn repo_exists() -> Result<()> { let curr_dir = env::current_dir()?; let _repo = Repository::discover(curr_dir)?; Ok(()) } #[cfg(feature = "gix")] fn repo_exists() -> Result<()> { let curr_dir = env::current_dir()?; let _repo = gix::discover(curr_dir)?; Ok(()) } #[cfg(feature = "gitcl")] fn repo_exists() -> Result<()> { 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); if repo_exists().is_ok() && !failed { assert!(GIT_REGEX_INST.is_match(&output)); } else { assert!(ALL_IDEM_OUTPUT.is_match(&output)); } Ok(()) } #[test] #[serial_test::serial] fn git_all_flags_test_repo() -> Result<()> { create_test_repo(); clone_test_repo(); 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(clone_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<()> { create_test_repo(); clone_test_repo(); 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(clone_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<()> { create_test_repo(); clone_test_repo(); 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(clone_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<()> { create_test_repo(); clone_test_repo(); assert!(EmitBuilder::builder() .all_git() .git_describe(true, true, None) .git_sha(true) .emit_at(clone_path()) .is_ok()); 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); if repo_exists().is_ok() && !failed { assert!(GIT_REGEX_IDEM_INST.is_match(&output)); } else { assert!(ALL_IDEM_OUTPUT.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); if repo_exists().is_ok() && !failed { assert!(GIT_REGEX_IDEM_INST.is_match(&output)); } else { assert_eq!(IDEM_QUIET_OUTPUT, output); } Ok(()) } #[test] #[serial_test::serial] fn repo_path_temp_dir_works() { if let Ok(runner_temp) = env::var("RUNNER_TEMP") { env::remove_var("RUNNER_TEMP"); assert!(repo_path().ends_with(BARE_REPO_NAME)); env::set_var("RUNNER_TEMP", runner_temp); } else { assert!(repo_path().ends_with(BARE_REPO_NAME)); } } #[test] #[serial_test::serial] fn clone_path_temp_dir_works() { if let Ok(runner_temp) = env::var("RUNNER_TEMP") { env::remove_var("RUNNER_TEMP"); assert!(clone_path().ends_with(CLONE_NAME)); env::set_var("RUNNER_TEMP", runner_temp); } else { assert!(clone_path().ends_with(CLONE_NAME)); } } #[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(()) } #[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); if repo_exists().is_ok() && !failed { assert!(GIT_REGEX_INST.is_match(&output)); } else { assert!(ALL_IDEM_OUTPUT.is_match(&output)); } Ok(()) } } vergen-8.2.6/tests/rustc_output.rs000064400000000000000000000047531046102023000154300ustar 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.2.6/tests/sysinfo_output.rs000064400000000000000000000126011046102023000157510ustar 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(()) } }