built-0.8.0/.cargo_vcs_info.json0000644000000001360000000000100121600ustar { "git": { "sha1": "2d84b2362c9cde5e0054be103190de0cbc065d85" }, "path_in_vcs": "" }built-0.8.0/.devcontainer/devcontainer.json000064400000000000000000000002021046102023000170550ustar 00000000000000{ "image": "mcr.microsoft.com/devcontainers/universal:2", "features": { "ghcr.io/devcontainers/features/rust:1": {} } } built-0.8.0/.github/workflows/check.yml000064400000000000000000000054361046102023000161550ustar 00000000000000name: Clippy, Format & Test on: [pull_request, push, workflow_dispatch] jobs: fmt: name: rustfmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: components: rustfmt - run: cargo fmt --all -- --check - run: cargo fmt --manifest-path=example_project/Cargo.toml --all -- --check check: name: cargo check runs-on: ubuntu-latest strategy: matrix: toolchain: [stable, "1.81"] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ matrix.toolchain }} - run: cargo check --no-default-features - run: cargo check --no-default-features --features cargo-lock - run: cargo check --no-default-features --features dependency-tree - run: cargo check --no-default-features --features git2 - run: cargo check --no-default-features --features semver - run: cargo check --no-default-features --features chrono - run: cargo check --all-features - run: cargo check --manifest-path=example_project/Cargo.toml semver_crate: name: cargo check of internal crate runs-on: ubuntu-latest strategy: matrix: toolchain: [stable, "1.83"] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ matrix.toolchain }} - run: cargo check --manifest-path=semver_check/Cargo.toml clippy: name: cargo clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: components: clippy - run: cargo clippy --all --tests -- -D warnings - run: cargo clippy --all-features --all --tests -- -D warnings - run: cargo clippy --manifest-path=example_project/Cargo.toml --all --tests -- --warn clippy::pedantic -D warnings test: name: cargo test strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] toolchain: [stable, nightly] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: targets: x86_64-unknown-none toolchain: ${{ matrix.toolchain }} - if: matrix.toolchain == 'nightly' run: echo "RUSTFLAGS=-Zfmt-debug=none" >> $GITHUB_ENV - run: cargo test --no-default-features - run: cargo test --all-features - run: cargo test -- --ignored nostd_testbox - run: cargo run --manifest-path=example_project/Cargo.toml semver: name: Semver check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: sudo apt update && sudo apt install -y cmake - uses: obi1kenobi/cargo-semver-checks-action@v2 built-0.8.0/.gitignore000064400000000000000000000000241046102023000127340ustar 00000000000000/target /Cargo.lock built-0.8.0/CHANGELOG.md000064400000000000000000000074471046102023000125750ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.8.0] - Add override-variables - Bump MSRV to 1.81 (due to dependencies) ## [0.7.7] - Fix `NUM_JOB` to 1 if `SOURCE_DATE_EPOCH` is set, in order to better support reproducible builds - Set MSRV to 1.74 (due to dependencies) ## [0.7.6] - Do not depend on `fmt::Debug`-output (`fmt-debug=none`) - Bump `git2` to 0.20 - Use `static`- instead of `const`-items throughout ## [0.7.5] - 2024-10-17 ### Changed - Bump `cargo-lock` to 10.0 ## [0.7.4] - 2024-07-07 ### Added - Honor `SOURCE_DATE_EPOCH` in `BUILT_TIME_UTC` ### Changed - Bump `git2` to 0.19 ## [0.7.3] - 2024-05-21 ### Added - Search for lockfile in manifest's parent directory (crates in workspaces) ## [0.7.2] - 2024-04-09 ### Changed - Fixed hard error in case `rustdoc` is missing ## [0.7.1] - 2023-10-14 ### Changed - Fixed `no_std` builds ## [0.7.0] - 2023-08-09 ### Changed - The `Options`-type has been removed in favor of controlling `built`'s behavior by means of feature-flags. - `cargo-lock` is now an optional dependency - Bump `git2` to 0.18 ## [0.6.1] - 2023-06-19 ### Changed - Bump `git2` to 0.17 - Bump `cargo-lock` to 9.0 ### Added - `FEATURES_LOWERCASE` and `FEATURES_LOWERCASE_STR` ## [0.6.0] - 2023-02-09 ### Changed - Identical re-release after yanking 0.5.3, due to semver failure ## [0.5.3] - 2023-02-08 ### Changed - Bump `git2` to 0.16, mitigating GHSA-8643-3wh5-rmjq ### Added - Add `GIT_COMMIT_HASH_SHORT` ## [0.5.2] - 2022-12-03 ### Changed - Removed unused transitive dependency on `time` - Bump `cargo-lock` to 8.0 - Bump `git2` to 0.15 - Fix unescaped quotes in literals ### Added - Added GitHub Actions to the list of detected CI platforms ## [0.5.1] - 2021-05-27 ### Changed - Bump `cargo-lock` to 7.0 - Bump `semver` to 1.0 ## [0.5.0] - 2021-05-01 ### Fixed - Fix possibly wrong `CFG_` values in cross-compilation scenarios - Fix handling of backspaces in doc-attributes ### Changed - Switch deprecated `tempdir` to `tempfile` - Add `#allow(dead_code)` to generated code - Bump `cargo-lock` to 6.0 - Bump `semver` to 0.11 - Publicly re-export dependencies ## [0.4.4] - 2020-12-01 ### Added - Added `PKG_LICENSE` and `PKG_REPOSITORY` ## [0.4.3] - 2020-08-19 ### Fixed - Fix handling of unescaped special characters ## [0.4.2] - 2020-05-27 ### Added - Add `GIT_DIRTY` - Add `GIT_COMMIT_HASH` and `GIT_HEAD_REF` ### Changed - Bump `semver` to 0.10 [unreleased]: https://github.com/lukaslueg/built/compare/0.8.0...master [0.8.0]: https://github.com/lukaslueg/built/compare/0.7.7...0.8.0 [0.7.7]: https://github.com/lukaslueg/built/compare/0.7.6...0.7.7 [0.7.6]: https://github.com/lukaslueg/built/compare/0.7.5...0.7.6 [0.7.5]: https://github.com/lukaslueg/built/compare/0.7.4...0.7.5 [0.7.4]: https://github.com/lukaslueg/built/compare/0.7.3...0.7.4 [0.7.3]: https://github.com/lukaslueg/built/compare/0.7.2...0.7.3 [0.7.2]: https://github.com/lukaslueg/built/compare/0.7.1...0.7.2 [0.7.1]: https://github.com/lukaslueg/built/compare/0.7.0...0.7.1 [0.7.0]: https://github.com/lukaslueg/built/compare/0.6.1...0.7.0 [0.6.1]: https://github.com/lukaslueg/built/compare/0.6.0...0.6.1 [0.6.0]: https://github.com/lukaslueg/built/compare/0.5.3...0.6.0 [0.5.3]: https://github.com/lukaslueg/built/compare/0.5.2...0.5.3 [0.5.2]: https://github.com/lukaslueg/built/compare/0.5.1...0.5.2 [0.5.1]: https://github.com/lukaslueg/built/compare/0.5.0...0.5.1 [0.5.0]: https://github.com/lukaslueg/built/compare/0.4.4...0.5.0 [0.4.4]: https://github.com/lukaslueg/built/compare/0.4.3...0.4.4 [0.4.3]: https://github.com/lukaslueg/built/compare/0.4.2...0.4.3 [0.4.2]: https://github.com/lukaslueg/built/compare/0.4.1...0.4.2 built-0.8.0/Cargo.lock0000644000000606620000000000100101450ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "built" version = "0.8.0" dependencies = [ "cargo-lock", "chrono", "git2", "semver", "tempfile", ] [[package]] name = "bumpalo" version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "cargo-lock" version = "10.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06acb4f71407ba205a07cb453211e0e6a67b21904e47f6ba1f9589e38f2e454" dependencies = [ "petgraph", "semver", "serde", "toml", "url", ] [[package]] name = "cc" version = "1.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" dependencies = [ "jobserver", "libc", "shlex", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "windows-link", ] [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fixedbitset" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "getrandom" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", "r-efi", "wasi", ] [[package]] name = "git2" version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5220b8ba44c68a9a7f7a7659e864dd73692e417ef0211bea133c7b74e031eeb9" dependencies = [ "bitflags", "libc", "libgit2-sys", "log", "url", ] [[package]] name = "hashbrown" version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" [[package]] name = "iana-time-zone" version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "log", "wasm-bindgen", "windows-core", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ "cc", ] [[package]] name = "icu_collections" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_locid" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", "tinystr", "writeable", "zerovec", ] [[package]] name = "icu_locid_transform" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ "displaydoc", "icu_locid", "icu_locid_transform_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_locid_transform_data" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", "utf16_iter", "utf8_iter", "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ "displaydoc", "icu_collections", "icu_locid_transform", "icu_properties_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_properties_data" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", "icu_locid", "icu_provider_macros", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_provider_macros" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "idna" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ "idna_adapter", "smallvec", "utf8_iter", ] [[package]] name = "idna_adapter" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ "icu_normalizer", "icu_properties", ] [[package]] name = "indexmap" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "jobserver" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ "getrandom", "libc", ] [[package]] name = "js-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "libc" version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libgit2-sys" version = "0.18.1+1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1dcb20f84ffcdd825c7a311ae347cce604a6f084a767dec4a4929829645290e" dependencies = [ "cc", "libc", "libz-sys", "pkg-config", ] [[package]] name = "libz-sys" version = "1.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "linux-raw-sys" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", "indexmap", ] [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "proc-macro2" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "rustix" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "rustversion" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "semver" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_spanned" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "smallvec" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "synstructure" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tempfile" version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ "fastrand", "getrandom", "once_cell", "rustix", "windows-sys", ] [[package]] name = "tinystr" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "toml" version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "toml_write", "winnow", ] [[package]] name = "toml_write" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "url" version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] [[package]] name = "utf16_iter" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] [[package]] name = "wasm-bindgen" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] [[package]] name = "windows-core" version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ "windows-implement", "windows-interface", "windows-link", "windows-result", "windows-strings", ] [[package]] name = "windows-implement" version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "windows-interface" version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "windows-link" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" [[package]] name = "windows-result" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" dependencies = [ "windows-link", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e27d6ad3dac991091e4d35de9ba2d2d00647c5d0fc26c5496dee55984ae111b" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen-rt" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] [[package]] name = "write16" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "yoke" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zerofrom" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zerovec" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ "yoke", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", "syn", ] built-0.8.0/Cargo.toml0000644000000031220000000000100101540ustar # 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" rust-version = "1.81" name = "built" version = "0.8.0" authors = ["Lukas Lueg "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Provides a crate with information from the time it was built." documentation = "https://docs.rs/built" readme = "README.md" keywords = [ "cargo", "build", ] license = "MIT" repository = "https://github.com/lukaslueg/built" [package.metadata.docs.rs] features = [ "cargo-lock", "chrono", "dependency-tree", "git2", "semver", ] [features] dependency-tree = ["cargo-lock/dependency-tree"] [lib] name = "built" path = "src/lib.rs" [[test]] name = "testbox_tests" path = "tests/testbox_tests.rs" [dependencies.cargo-lock] version = "10.0" optional = true default-features = false [dependencies.chrono] version = "0.4" features = ["clock"] optional = true default-features = false [dependencies.git2] version = "0.20" features = [] optional = true default-features = false [dependencies.semver] version = "1.0" optional = true [dev-dependencies.tempfile] version = "3" built-0.8.0/Cargo.toml.orig000064400000000000000000000015651046102023000136460ustar 00000000000000[package] name = "built" version = "0.8.0" description = "Provides a crate with information from the time it was built." repository = "https://github.com/lukaslueg/built" documentation = "https://docs.rs/built" authors = ["Lukas Lueg "] license = "MIT" readme = "README.md" keywords = ["cargo", "build"] edition = "2021" rust-version = "1.81" [dependencies] cargo-lock = { version = "10.0", optional = true, default-features = false } semver = { version = "1.0", optional = true } chrono = { version = "0.4", optional = true, default-features = false, features = ["clock"] } git2 = { version = "0.20", optional = true, default-features = false, features = [] } [dev-dependencies] tempfile = "3" [features] dependency-tree = [ "cargo-lock/dependency-tree" ] [package.metadata.docs.rs] features = [ "cargo-lock", "chrono", "dependency-tree", "git2", "semver" ] built-0.8.0/LICENSE000064400000000000000000000020601046102023000117530ustar 00000000000000MIT License Copyright (c) 2017-2023 Lukas Lueg 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. built-0.8.0/README.md000064400000000000000000000103721046102023000122320ustar 00000000000000```built``` provides a crate with information from the time it was built. [![Crates.io Version](https://img.shields.io/crates/v/built.svg)](https://crates.io/crates/built) [![Docs](https://docs.rs/built/badge.svg)](https://docs.rs/built) [![Clippy, Format & Test](https://github.com/lukaslueg/built/actions/workflows/check.yml/badge.svg)](https://github.com/lukaslueg/built/actions/workflows/check.yml) [![Downloads](https://img.shields.io/crates/d/built)](https://crates.io/crates/built) `built` is used as a build-time dependency to collect various information about the build-environment, serialize this information into Rust-code and provide that to the crate. The information collected by `built` include: * Various metadata like version, authors, homepage etc. as set by `Cargo.toml` * The tag or commit id if the crate was being compiled from within a Git repository. * The values of `CARGO_CFG_*` build script environment variables, like `CARGO_CFG_TARGET_OS` and `CARGO_CFG_TARGET_ARCH`. * The features the crate was compiled with. * The various dependencies, dependencies of dependencies and their versions Cargo ultimately chose to compile. * The presence of a CI-platform like `Github Actions`, `Travis CI` and `AppVeyor`. * The compiler and it's version; the documentation-generator and it's version. See [the example](https://github.com/lukaslueg/built/tree/master/example_project) or the [docs](https://docs.rs/built) for more information. ### MSRV `built-0.8` itself currently requires Rust 1.81 with all features enabled. --- ```rust,ignore // In build.rs fn main() { built::write_built_file().expect("Failed to acquire build-time information") } ``` ```rust,ignore // In lib.rs or main.rs // Include the generated-file as a separate module pub mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } println!( "This is version {}, built for {} by {}.", built_info::PKG_VERSION, built_info::TARGET, built_info::RUSTC_VERSION ); match built_info::CI_PLATFORM { None => print!("It seems I've not been built on a continuous integration platform,"), Some(ci) => print!("I've been built on CI-platform {},", ci), } if built::util::detect_ci().is_some() { println!(" but I'm currently executing on one!"); } else { println!(" and I'm currently not executing on one!"); } //.... ``` > This is version 0.1.0, built for x86_64-unknown-linux-gnu by rustc 1.77.2 (25ef9e3d8 2024-04-09). > > I was built from git `0.7.2-3-g4014a2e`, commit 4014a2eb4e8575bec9a1f17ae7b92d8956ecd1fd, short_commit 4014a2e; the working directory was "clean". The branch was `refs/heads/master`. > > I was built for a x86_64-CPU, which is a little-endian architecture. I was compiled to run on linux (a unix-breed) and my runtime should be gnu. > > It seems I've not been built on a continuous integration platform, and I'm currently not executing on one! > > I was built with profile "debug", features "" on 2024-04-14 14:28:31 +00:00 (38 seconds ago) using `built 0.7.2` (with help from `android-tzdata 0.1.1, android_system_properties 0.1.5, autocfg 1.2.0, bitflags 2.5.0, bumpalo 3.16.0, cargo-lock 9.0.0, cc 1.0.94, cfg-if 1.0.0, chrono 0.4.37, core-foundation-sys 0.8.6, equivalent 1.0.1, fixedbitset 0.4.2, form_urlencoded 1.2.1, git2 0.18.3, hashbrown 0.14.3, iana-time-zone 0.1.60, iana-time-zone-haiku 0.1.2, idna 0.5.0, indexmap 2.2.6, jobserver 0.1.30, js-sys 0.3.69, libc 0.2.153, libgit2-sys 0.16.2+1.7.2, libz-sys 1.1.16, log 0.4.21, memchr 2.7.2, num-traits 0.2.18, once_cell 1.19.0, percent-encoding 2.3.1, petgraph 0.6.4, pkg-config 0.3.30, proc-macro2 1.0.79, quote 1.0.36, semver 1.0.22, serde 1.0.197, serde_derive 1.0.197, serde_spanned 0.6.5, syn 2.0.58, tinyvec 1.6.0, tinyvec_macros 0.1.1, toml 0.7.8, toml_datetime 0.6.5, toml_edit 0.19.15, unicode-bidi 0.3.15, unicode-ident 1.0.12, unicode-normalization 0.1.23, url 2.5.0, vcpkg 0.2.15, wasm-bindgen 0.2.92, wasm-bindgen-backend 0.2.92, wasm-bindgen-macro 0.2.92, wasm-bindgen-macro-support 0.2.92, wasm-bindgen-shared 0.2.92, windows-core 0.52.0, windows-targets 0.52.5, windows_aarch64_gnullvm 0.52.5, windows_aarch64_msvc 0.52.5, windows_i686_gnu 0.52.5, windows_i686_gnullvm 0.52.5, windows_i686_msvc 0.52.5, windows_x86_64_gnu 0.52.5, windows_x86_64_gnullvm 0.52.5, windows_x86_64_msvc 0.52.5, winnow 0.5.40`). built-0.8.0/src/dependencies.rs000064400000000000000000000203751046102023000145420ustar 00000000000000use crate::util::TupleArrayDisplay; use crate::{write_str_variable, write_variable}; use std::{collections, fs, io, path}; fn package_names<'a, I>(packages: I) -> Vec<(String, String)> where I: IntoIterator, { let mut res = packages .into_iter() .map(|package| (package.name.to_string(), package.version.to_string())) .collect::>() .into_iter() .collect::>(); res.sort_unstable(); res } fn find_lockfile(base: &path::Path) -> io::Result { base.ancestors() .find_map(|p| { let lockfile = p.join("Cargo.lock"); lockfile.exists().then(|| lockfile.to_owned()) }) .ok_or(io::Error::other("Cargo.lock not found")) } #[cfg(feature = "dependency-tree")] struct Dependencies { deps: Vec<(String, String)>, direct_deps: Vec<(String, String)>, indirect_deps: Vec<(String, String)>, } #[cfg(feature = "dependency-tree")] impl Dependencies { fn new(lockfile: &cargo_lock::Lockfile) -> Self { use cargo_lock::dependency::graph::EdgeDirection; let tree = lockfile .dependency_tree() .expect("properly formed lockfile"); let graph = tree.graph(); let root_pkg_idx = graph .externals(EdgeDirection::Incoming) .collect::>(); let deps = package_names(graph.node_indices().filter_map(|idx| { if root_pkg_idx.contains(&idx) { None } else { Some(&graph[idx]) } })); let direct_deps_idx = root_pkg_idx .iter() .flat_map(|idx| graph.neighbors_directed(*idx, EdgeDirection::Outgoing)) .collect::>(); let direct_deps = package_names(direct_deps_idx.iter().map(|dep_idx| &graph[*dep_idx])); let indirect_deps = package_names(graph.node_indices().filter_map(|idx| { if root_pkg_idx.contains(&idx) | direct_deps_idx.contains(&idx) { None } else { Some(&graph[idx]) } })); Self { deps, direct_deps, indirect_deps, } } } #[cfg(feature = "dependency-tree")] pub fn write_dependencies(manifest_location: &path::Path, mut w: &fs::File) -> io::Result<()> { use io::{Read, Write}; let mut lock_buf = String::new(); fs::File::open(find_lockfile(manifest_location)?)?.read_to_string(&mut lock_buf)?; let lockfile = lock_buf.parse().expect("Failed to parse lockfile"); let dependencies = Dependencies::new(&lockfile); write_variable!( w, "DEPENDENCIES", format_args!("[(&str, &str); {}]", dependencies.deps.len()), TupleArrayDisplay(&dependencies.deps), "An array of effective dependencies as documented by `Cargo.lock`." ); write_str_variable!( w, "DEPENDENCIES_STR", dependencies .deps .iter() .map(|(n, v)| format!("{n} {v}")) .collect::>() .join(", "), "The effective dependencies as a comma-separated string." ); write_variable!( w, "DIRECT_DEPENDENCIES", format_args!("[(&str, &str); {}]", dependencies.direct_deps.len()), TupleArrayDisplay(&dependencies.direct_deps), "An array of direct dependencies as documented by `Cargo.lock`." ); write_str_variable!( w, "DIRECT_DEPENDENCIES_STR", dependencies .direct_deps .iter() .map(|(n, v)| format!("{n} {v}")) .collect::>() .join(", "), "The direct dependencies as a comma-separated string." ); write_variable!( w, "INDIRECT_DEPENDENCIES", format_args!("[(&str, &str); {}]", dependencies.indirect_deps.len()), TupleArrayDisplay(&dependencies.indirect_deps), "An array of indirect dependencies as documented by `Cargo.lock`." ); write_str_variable!( w, "INDIRECT_DEPENDENCIES_STR", dependencies .indirect_deps .iter() .map(|(n, v)| format!("{n} {v}")) .collect::>() .join(", "), "The indirect dependencies as a comma-separated string." ); Ok(()) } #[cfg(not(feature = "dependency-tree"))] pub fn write_dependencies(manifest_location: &path::Path, mut w: &fs::File) -> io::Result<()> { use io::{Read, Write}; let mut lock_buf = String::new(); fs::File::open(find_lockfile(manifest_location)?)?.read_to_string(&mut lock_buf)?; let lockfile: cargo_lock::Lockfile = lock_buf.parse().expect("Failed to parse lockfile"); let deps = package_names(&lockfile.packages); write_variable!( w, "DEPENDENCIES", format_args!("[(&str, &str); {}]", deps.len()), TupleArrayDisplay(&deps), "An array of effective dependencies as documented by `Cargo.lock`." ); write_str_variable!( w, "DEPENDENCIES_STR", deps.iter() .map(|(n, v)| format!("{n} {v}")) .collect::>() .join(", "), "The effective dependencies as a comma-separated string." ); Ok(()) } #[cfg(test)] mod tests { static LOCK_TOML_BUFFER: &str = r#" # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "foo" version = "0.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7dbb6acfeff1d490fba693a402456f76b344fea77a5e7cae43b5970c3332b8f" [[package]] name = "foobar" version = "0.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c0d152c1d2a9673211b9f3c02a4786715ce730dbd5f94f2f895fc0bb9eed63" [[package]] name = "memchr" version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "dummy" version = "0.1.0" dependencies = [ "foo", "foobar", "nom", ] "#; #[test] fn parse_deps() { let lockfile: cargo_lock::Lockfile = LOCK_TOML_BUFFER.parse().expect("Failed to parse lockfile"); let deps = super::package_names(&lockfile.packages); assert_eq!( deps, [ ("dummy".to_owned(), "0.1.0".to_owned()), ("foo".to_owned(), "0.0.0".to_owned()), ("foobar".to_owned(), "0.0.0".to_owned()), ("memchr".to_owned(), "2.6.3".to_owned()), ("minimal-lexical".to_owned(), "0.2.1".to_owned()), ("nom".to_owned(), "7.1.3".to_owned()), ] ); } #[test] #[cfg(feature = "dependency-tree")] fn direct_deps() { let lockfile = LOCK_TOML_BUFFER.parse().expect("Failed to parse lockfile"); let dependencies = super::Dependencies::new(&lockfile); assert_eq!( dependencies.deps, [ ("foo".to_owned(), "0.0.0".to_owned()), ("foobar".to_owned(), "0.0.0".to_owned()), ("memchr".to_owned(), "2.6.3".to_owned()), ("minimal-lexical".to_owned(), "0.2.1".to_owned()), ("nom".to_owned(), "7.1.3".to_owned()), ] ); assert_eq!( dependencies.direct_deps, [ ("foo".to_owned(), "0.0.0".to_owned()), ("foobar".to_owned(), "0.0.0".to_owned()), ("nom".to_owned(), "7.1.3".to_owned()), ] ); assert_eq!( dependencies.indirect_deps, [ ("memchr".to_owned(), "2.6.3".to_owned()), ("minimal-lexical".to_owned(), "0.2.1".to_owned()), ] ); } } built-0.8.0/src/environment.rs000064400000000000000000000403211046102023000144510ustar 00000000000000use crate::util::{self, ArrayDisplay}; use crate::{fmt_option_str, write_str_variable, write_variable}; use std::{cell, collections, env, ffi, fmt, fs, io, process}; const BUILT_OVERRIDE_PREFIX: &str = "BUILT_OVERRIDE_"; #[derive(Debug, Default)] enum EnvironmentValue { #[default] Unused, Queried, Used, } impl EnvironmentValue { fn upgrade_to_queried(&mut self) { if matches!(self, EnvironmentValue::Unused) { *self = EnvironmentValue::Queried; } } fn upgrade_to_used(&mut self) { *self = EnvironmentValue::Used; } pub fn is_unused(&self) -> bool { matches!(self, EnvironmentValue::Unused) } pub fn is_used(&self) -> bool { matches!(self, EnvironmentValue::Used) } } pub struct EnvironmentMap { map: collections::HashMap)>, override_prefix: String, } fn get_version_from_cmd(executable: &ffi::OsStr) -> io::Result { let output = process::Command::new(executable).arg("-V").output()?; let mut v = String::from_utf8(output.stdout).unwrap(); v.pop(); // remove newline Ok(v) } impl EnvironmentMap { pub fn new() -> Self { let map = env::vars_os() .filter_map(|(k, v)| match (k.into_string(), v.into_string()) { (Ok(k), Ok(v)) => Some((k, (v, cell::RefCell::default()))), _ => None, }) .collect::>(); let override_prefix = format!("{}{}_", BUILT_OVERRIDE_PREFIX, map["CARGO_PKG_NAME"].0); Self { map, override_prefix, } } fn override_key(&self, key: &str) -> String { let mut prefixed_key = self.override_prefix.clone(); prefixed_key.push_str(key); prefixed_key } pub fn get(&self, key: &str) -> Option<&str> { self.map.get(key).map(|v| { v.1.borrow_mut().upgrade_to_queried(); v.0.as_str() }) } pub fn contains_key(&self, key: &str) -> bool { self.get(key).is_some() } pub fn filter_map_keys(&self, mut f: F) -> impl Iterator where F: FnMut(&str) -> Option<&str>, { self.map.iter().filter_map(move |v| match f(v.0.as_str()) { Some(w) => { v.1 .1.borrow_mut().upgrade_to_queried(); Some(w) } None => None, }) } pub fn get_override_var<'a, T>(&'a self, key: &str) -> Option where T: util::ParseFromEnv<'a>, { self.map.get(&self.override_key(key)).map(|v| { v.1.borrow_mut().upgrade_to_queried(); match T::parse_from_env(v.0.as_str()) { Ok(t) => { v.1.borrow_mut().upgrade_to_used(); t } Err(e) => { panic!("Failed to parse `{key}`=`{0}`: {e:?}", v.0); } } }) } pub fn unused_override_vars(&self) -> impl Iterator { self.map.iter().filter_map(|(k, v)| { if k.starts_with(&self.override_prefix) && v.1.borrow().is_unused() { Some(k.as_str()) } else { None } }) } pub fn used_override_vars(&self) -> impl Iterator { self.map.iter().filter_map(|(k, v)| { if v.1.borrow().is_used() { Some(k.strip_prefix(&self.override_prefix).unwrap()) } else { None } }) } pub fn write_ci(&self, mut w: &fs::File) -> io::Result<()> { use io::Write; let ci = match self.get_override_var("CI_PLATFORM") { Some(v) => v, None => self.detect_ci().map(|ci| ci.to_string()), }; write_variable!( w, "CI_PLATFORM", "Option<&str>", fmt_option_str(ci), "The Continuous Integration platform detected during compilation." ); Ok(()) } pub fn write_env(&self, mut w: &fs::File) -> io::Result<()> { use io::Write; macro_rules! write_env_str { ($(($name:ident, $env_name:expr, $doc:expr)),*) => {$( let v = match self.get_override_var(stringify!($name)) { Some(v) => v, None => self.get($env_name).expect(stringify!(Missing expected environment variable $env_name)), }; write_str_variable!( w, stringify!($name), v, $doc ); )*} } write_env_str!( (PKG_VERSION, "CARGO_PKG_VERSION", "The full version."), ( PKG_VERSION_MAJOR, "CARGO_PKG_VERSION_MAJOR", "The major version." ), ( PKG_VERSION_MINOR, "CARGO_PKG_VERSION_MINOR", "The minor version." ), ( PKG_VERSION_PATCH, "CARGO_PKG_VERSION_PATCH", "The patch version." ), ( PKG_VERSION_PRE, "CARGO_PKG_VERSION_PRE", "The pre-release version." ), ( PKG_AUTHORS, "CARGO_PKG_AUTHORS", "A colon-separated list of authors." ), (PKG_NAME, "CARGO_PKG_NAME", "The name of the package."), (PKG_DESCRIPTION, "CARGO_PKG_DESCRIPTION", "The description."), (PKG_HOMEPAGE, "CARGO_PKG_HOMEPAGE", "The homepage."), (PKG_LICENSE, "CARGO_PKG_LICENSE", "The license."), ( PKG_REPOSITORY, "CARGO_PKG_REPOSITORY", "The source repository as advertised in Cargo.toml." ), ( TARGET, "TARGET", "The target triple that was being compiled for." ), (HOST, "HOST", "The host triple of the rust compiler."), ( PROFILE, "PROFILE", "`release` for release builds, `debug` for other builds." ), (RUSTC, "RUSTC", "The compiler that cargo resolved to use."), ( RUSTDOC, "RUSTDOC", "The documentation generator that cargo resolved to use." ) ); write_str_variable!( w, "OPT_LEVEL", self.get_override_var("OPT_LEVEL") .unwrap_or_else(|| env::var("OPT_LEVEL").unwrap()), "Value of OPT_LEVEL for the profile used during compilation." ); write_variable!( w, "NUM_JOBS", "u32", self.get_override_var("NUM_JOBS").unwrap_or_else(|| { if env::var(crate::SOURCE_DATE_EPOCH).is_ok() { 1u32 } else { env::var("NUM_JOBS").unwrap().parse().unwrap() } }), "The parallelism that was specified during compilation." ); write_variable!( w, "DEBUG", "bool", self.get_override_var("DEBUG") .unwrap_or_else(|| env::var("DEBUG").unwrap() == "true"), "Value of DEBUG for the profile used during compilation." ); Ok(()) } pub fn write_features(&self, mut w: &fs::File) -> io::Result<()> { use io::Write; let mut features = self.get_override_var("FEATURES").unwrap_or_else(|| { self.filter_map_keys(|k| k.strip_prefix("CARGO_FEATURE_")) .map(|f| f.to_owned()) .collect::>() }); features.sort_unstable(); write_variable!( w, "FEATURES", format_args!("[&str; {}]", features.len()), ArrayDisplay(&features, |t, f| write!(f, "\"{}\"", t.escape_default())), "The features that were enabled during compilation." ); let features_str = features.join(", "); write_str_variable!( w, "FEATURES_STR", features_str, "The features as a comma-separated string." ); let mut lowercase_features = features .iter() .map(|name| name.to_lowercase()) .collect::>(); lowercase_features.sort_unstable(); write_variable!( w, "FEATURES_LOWERCASE", format_args!("[&str; {}]", lowercase_features.len()), ArrayDisplay(&lowercase_features, |val, fmt| write!( fmt, "\"{}\"", val.escape_default() )), "The features as above, as lowercase strings." ); let lowercase_features_str = lowercase_features.join(", "); write_str_variable!( w, "FEATURES_LOWERCASE_STR", lowercase_features_str, "The feature-string as above, from lowercase strings." ); Ok(()) } pub fn write_cfg(&self, mut w: &fs::File) -> io::Result<()> { use io::Write; write_str_variable!( w, "CFG_TARGET_ARCH", self.get_override_var("CFG_TARGET_ARCH") .unwrap_or_else(|| self.get("CARGO_CFG_TARGET_ARCH").unwrap()), "The target architecture, given by `CARGO_CFG_TARGET_ARCH`." ); write_str_variable!( w, "CFG_ENDIAN", self.get_override_var("CFG_ENDIAN") .unwrap_or_else(|| self.get("CARGO_CFG_TARGET_ENDIAN").unwrap()), "The endianness, given by `CARGO_CFG_TARGET_ENDIAN`." ); write_str_variable!( w, "CFG_ENV", self.get_override_var("CFG_ENV") .unwrap_or_else(|| self.get("CARGO_CFG_TARGET_ENV").unwrap()), "The toolchain-environment, given by `CARGO_CFG_TARGET_ENV`." ); write_str_variable!( w, "CFG_FAMILY", self.get_override_var("CFG_FAMILY") .unwrap_or_else(|| self.get("CARGO_CFG_TARGET_FAMILY").unwrap_or_default()), "The OS-family, given by `CARGO_CFG_TARGET_FAMILY`." ); write_str_variable!( w, "CFG_OS", self.get_override_var("CFG_OS") .unwrap_or_else(|| self.get("CARGO_CFG_TARGET_OS").unwrap()), "The operating system, given by `CARGO_CFG_TARGET_OS`." ); write_str_variable!( w, "CFG_POINTER_WIDTH", self.get_override_var("CFG_POINTER_WIDTH") .unwrap_or_else(|| self.get("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap()), "The pointer width, given by `CARGO_CFG_TARGET_POINTER_WIDTH`." ); Ok(()) } pub fn write_compiler_version(&self, mut w: &fs::File) -> io::Result<()> { use std::io::Write; let rustc; let rustc_version; match self.get_override_var("RUSTC") { Some(v) => { rustc = v; rustc_version = self .get_override_var("RUSTC_VERSION") .expect("RUSTC_VERSION must be overridden if RUSTC is") } None => { rustc = self.get("RUSTC").unwrap(); rustc_version = get_version_from_cmd(rustc.as_ref())?; } } let rustdoc; let rustdoc_version; match self.get_override_var("RUSTDOC") { Some(v) => { rustdoc = v; rustdoc_version = self.get_override_var("RUSTDOC_VERSION").unwrap_or_default(); } None => { rustdoc = self.get("RUSTDOC").unwrap(); rustdoc_version = get_version_from_cmd(rustdoc.as_ref()).unwrap_or_default(); } } write_str_variable!( w, "RUSTC_VERSION", rustc_version, format_args!("The output of `{rustc} -V`") ); write_str_variable!( w, "RUSTDOC_VERSION", rustdoc_version, format_args!( "The output of `{rustdoc} -V`; empty string if `{rustdoc} -V` failed to execute" ) ); Ok(()) } pub fn detect_ci(&self) -> Option { macro_rules! detect { ($(($k:expr, $v:expr, $i:ident)),*) => {$( if self.get($k).map_or(false, |v| v == $v) { return Some(CIPlatform::$i); } )*}; ($(($k:expr, $i:ident)),*) => {$( if self.contains_key($k) { return Some(CIPlatform::$i); } )*}; ($($k:expr),*) => {$( if self.contains_key($k) { return Some(CIPlatform::Generic); } )*}; } // Variable names collected by watson/ci-info detect!( ("TRAVIS", Travis), ("CIRCLECI", Circle), ("GITLAB_CI", GitLab), ("APPVEYOR", AppVeyor), ("DRONE", Drone), ("MAGNUM", Magnum), ("SEMAPHORE", Semaphore), ("JENKINS_URL", Jenkins), ("bamboo_planKey", Bamboo), ("TF_BUILD", TFS), ("TEAMCITY_VERSION", TeamCity), ("BUILDKITE", Buildkite), ("HUDSON_URL", Hudson), ("GO_PIPELINE_LABEL", GoCD), ("BITBUCKET_COMMIT", BitBucket), ("GITHUB_ACTIONS", GitHubActions) ); if self.contains_key("TASK_ID") && self.contains_key("RUN_ID") { return Some(CIPlatform::TaskCluster); } detect!(("CI_NAME", "codeship", Codeship)); detect!( "CI", // Could be Travis, Circle, GitLab, AppVeyor or CodeShip "CONTINUOUS_INTEGRATION", // Probably Travis "BUILD_NUMBER" // Jenkins, TeamCity ); None } } /// Various Continuous Integration platforms whose presence can be detected. pub enum CIPlatform { /// Travis, /// Circle, /// GitLab, /// AppVeyor, /// Codeship, /// Drone, /// Magnum, /// Semaphore, /// Jenkins, /// Bamboo, /// TFS, /// TeamCity, /// Buildkite, /// Hudson, /// TaskCluster, /// GoCD, /// BitBucket, /// GitHubActions, /// Unspecific Generic, } impl fmt::Display for CIPlatform { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(match *self { CIPlatform::Travis => "Travis CI", CIPlatform::Circle => "CircleCI", CIPlatform::GitLab => "GitLab", CIPlatform::AppVeyor => "AppVeyor", CIPlatform::Codeship => "CodeShip", CIPlatform::Drone => "Drone", CIPlatform::Magnum => "Magnum", CIPlatform::Semaphore => "Semaphore", CIPlatform::Jenkins => "Jenkins", CIPlatform::Bamboo => "Bamboo", CIPlatform::TFS => "Team Foundation Server", CIPlatform::TeamCity => "TeamCity", CIPlatform::Buildkite => "Buildkite", CIPlatform::Hudson => "Hudson", CIPlatform::TaskCluster => "TaskCluster", CIPlatform::GoCD => "GoCD", CIPlatform::BitBucket => "BitBucket", CIPlatform::GitHubActions => "GitHub Actions", CIPlatform::Generic => "Generic CI", }) } } built-0.8.0/src/git.rs000064400000000000000000000250131046102023000126710ustar 00000000000000use crate::{environment, fmt_option_str, write_variable}; use std::{fs, io, path}; pub fn write_git_version( manifest_location: &path::Path, envmap: &environment::EnvironmentMap, mut w: &fs::File, ) -> io::Result<()> { use io::Write; // CIs will do shallow clones of repositories, causing libgit2 to error // out. We try to detect if we are running on a CI and ignore the // error. let (mut tag, mut dirty) = ( envmap.get_override_var("GIT_VERSION"), envmap.get_override_var("GIT_DIRTY"), ); if tag.is_none() || dirty.is_none() { if let Some((git_tag, git_dirty)) = get_repo_description(manifest_location).ok().flatten() { if tag.is_none() { tag = Some(git_tag); } if dirty.is_none() { dirty = Some(git_dirty); } }; } write_variable!( w, "GIT_VERSION", "Option<&str>", fmt_option_str(tag), "If the crate was compiled from within a git-repository, \ `GIT_VERSION` contains HEAD's tag. The short commit id is used if HEAD is not tagged." ); write_variable!( w, "GIT_DIRTY", "Option", match dirty { Some(true) => "Some(true)", Some(false) => "Some(false)", None => "None", }, "If the repository had dirty/staged files." ); let (mut branch, mut commit, mut commit_short) = ( envmap.get_override_var("GIT_HEAD_REF"), envmap.get_override_var::("GIT_COMMIT_HASH"), envmap.get_override_var("GIT_COMMIT_HASH_SHORT"), ); if branch.is_none() || commit.is_none() || commit_short.is_none() { if let Some((git_branch, git_commit, git_commit_short)) = get_repo_head(manifest_location).ok().flatten() { if branch.is_none() { branch = git_branch; } if commit.is_none() { commit = Some(git_commit); } if commit_short.is_none() { commit_short = Some(git_commit_short); } } } if let (Some(h), None) = (&commit, &commit_short) { commit_short = Some(h.chars().take(8).collect()) } let doc = "If the crate was compiled from within a git-repository, `GIT_HEAD_REF` \ contains full name to the reference pointed to by HEAD \ (e.g.: `refs/heads/master`). If HEAD is detached or the branch name is not \ valid UTF-8 `None` will be stored.\n"; write_variable!( w, "GIT_HEAD_REF", "Option<&str>", fmt_option_str(branch), doc ); write_variable!( w, "GIT_COMMIT_HASH", "Option<&str>", fmt_option_str(commit), "If the crate was compiled from within a git-repository, `GIT_COMMIT_HASH` \ contains HEAD's full commit SHA-1 hash." ); write_variable!( w, "GIT_COMMIT_HASH_SHORT", "Option<&str>", fmt_option_str(commit_short), "If the crate was compiled from within a git-repository, `GIT_COMMIT_HASH_SHORT` \ contains HEAD's short commit SHA-1 hash." ); Ok(()) } /// Retrieves the git-tag or hash describing the exact version and a boolean /// that indicates if the repository currently has dirty/staged files. /// /// If a valid git-repo can't be discovered at or above the given path, /// `Ok(None)` is returned instead of an `Err`-value. /// /// # Errors /// Errors from `git2` are returned if the repository does exists at all. #[cfg(feature = "git2")] pub fn get_repo_description(root: &std::path::Path) -> Result, git2::Error> { match git2::Repository::discover(root) { Ok(repo) => { let mut desc_opt = git2::DescribeOptions::new(); desc_opt.describe_tags().show_commit_oid_as_fallback(true); let tag = repo .describe(&desc_opt) .and_then(|desc| desc.format(None))?; let mut st_opt = git2::StatusOptions::new(); st_opt.include_ignored(false); st_opt.include_untracked(false); let dirty = repo .statuses(Some(&mut st_opt))? .iter() .any(|status| !matches!(status.status(), git2::Status::CURRENT)); Ok(Some((tag, dirty))) } Err(ref e) if e.class() == git2::ErrorClass::Repository && e.code() == git2::ErrorCode::NotFound => { Ok(None) } Err(e) => Err(e), } } /// Retrieves the branch name and hash of HEAD. /// /// The returned value is a tuple of head's reference-name, long-hash and short-hash. The /// branch name will be `None` if the head is detached, or it's not valid UTF-8. /// /// If a valid git-repo can't be discovered at or above the given path, /// `Ok(None)` is returned instead of an `Err`-value. /// /// # Errors /// Errors from `git2` are returned if the repository does exists at all. #[cfg(feature = "git2")] pub fn get_repo_head( root: &std::path::Path, ) -> Result, String, String)>, git2::Error> { match git2::Repository::discover(root) { Ok(repo) => { // Supposed to be the reference pointed to by HEAD, but it's HEAD // itself, if detached let head_ref = repo.head()?; let branch = { // Check whether `head` is really the pointed to reference and // not HEAD itself. if repo.head_detached()? { None } else { head_ref.name() } }; let head = head_ref.peel_to_commit()?; let commit = head.id(); let commit_short = head.into_object().short_id()?; Ok(Some(( branch.map(ToString::to_string), format!("{commit}"), commit_short.as_str().unwrap_or_default().to_string(), ))) } Err(ref e) if e.class() == git2::ErrorClass::Repository && e.code() == git2::ErrorCode::NotFound => { Ok(None) } Err(e) => Err(e), } } #[cfg(test)] mod tests { #[test] fn parse_git_repo() { use std::fs; use std::path; let repo_root = tempfile::tempdir().unwrap(); assert_eq!(super::get_repo_description(repo_root.as_ref()), Ok(None)); let repo = git2::Repository::init_opts( &repo_root, git2::RepositoryInitOptions::new() .external_template(false) .mkdir(false) .no_reinit(true) .mkpath(false), ) .unwrap(); let cruft_file = repo_root.path().join("cruftfile"); std::fs::write(&cruft_file, "Who? Me?").unwrap(); let project_root = repo_root.path().join("project_root"); fs::create_dir(&project_root).unwrap(); let sig = git2::Signature::now("foo", "bar").unwrap(); let mut idx = repo.index().unwrap(); idx.add_path(path::Path::new("cruftfile")).unwrap(); idx.write().unwrap(); let commit_oid = repo .commit( Some("HEAD"), &sig, &sig, "Testing testing 1 2 3", &repo.find_tree(idx.write_tree().unwrap()).unwrap(), &[], ) .unwrap(); let binding = repo .find_commit(commit_oid) .unwrap() .into_object() .short_id() .unwrap(); let commit_oid_short = binding.as_str().unwrap(); let commit_hash = commit_oid.to_string(); let commit_hash_short = commit_oid_short.to_string(); assert!(commit_hash.starts_with(&commit_hash_short)); // The commit, the commit-id is something and the repo is not dirty let (tag, dirty) = super::get_repo_description(&project_root).unwrap().unwrap(); assert!(!tag.is_empty()); assert!(!dirty); // Tag the commit, it should be retrieved repo.tag( "foobar", &repo .find_object(commit_oid, Some(git2::ObjectType::Commit)) .unwrap(), &sig, "Tagged foobar", false, ) .unwrap(); let (tag, dirty) = super::get_repo_description(&project_root).unwrap().unwrap(); assert_eq!(tag, "foobar"); assert!(!dirty); // Make some dirt std::fs::write(cruft_file, "now dirty").unwrap(); let (tag, dirty) = super::get_repo_description(&project_root).unwrap().unwrap(); assert_eq!(tag, "foobar"); assert!(dirty); let branch_short_name = "baz"; let branch_name = "refs/heads/baz"; let commit = repo.find_commit(commit_oid).unwrap(); repo.branch(branch_short_name, &commit, true).unwrap(); repo.set_head(branch_name).unwrap(); assert_eq!( super::get_repo_head(&project_root), Ok(Some(( Some(branch_name.to_owned()), commit_hash, commit_hash_short ))) ); } #[test] fn detached_head_repo() { let repo_root = tempfile::tempdir().unwrap(); let repo = git2::Repository::init_opts( &repo_root, git2::RepositoryInitOptions::new() .external_template(false) .mkdir(false) .no_reinit(true) .mkpath(false), ) .unwrap(); let sig = git2::Signature::now("foo", "bar").unwrap(); let commit_oid = repo .commit( Some("HEAD"), &sig, &sig, "Testing", &repo .find_tree(repo.index().unwrap().write_tree().unwrap()) .unwrap(), &[], ) .unwrap(); let binding = repo .find_commit(commit_oid) .unwrap() .into_object() .short_id() .unwrap(); let commit_oid_short = binding.as_str().unwrap(); let commit_hash = commit_oid.to_string(); let commit_hash_short = commit_oid_short.to_string(); assert!(commit_hash.starts_with(&commit_hash_short)); repo.set_head_detached(commit_oid).unwrap(); assert_eq!( super::get_repo_head(repo_root.as_ref()), Ok(Some((None, commit_hash, commit_hash_short))) ); } } built-0.8.0/src/krono.rs000064400000000000000000000041221046102023000132340ustar 00000000000000use crate::{environment, util, write_str_variable, write_variable}; use std::{fs, io}; impl<'a> util::ParseFromEnv<'a> for chrono::DateTime { type Err = chrono::ParseError; fn parse_from_env(s: &'a str) -> Result { Ok(chrono::DateTime::parse_from_rfc2822(s)?.with_timezone(&chrono::offset::Utc)) } } /// Parse a time-string as formatted by `built`. /// /// ``` /// use chrono::Datelike; /// /// pub mod build_info { /// pub static BUILT_TIME_UTC: &'static str = "Tue, 14 Feb 2017 05:21:41 GMT"; /// } /// /// assert_eq!(built::util::strptime(&build_info::BUILT_TIME_UTC).year(), 2017); /// ``` /// /// # Panics /// If the string can't be parsed. This should never happen with input provided /// by `built`. #[must_use] pub fn strptime(s: &str) -> chrono::DateTime { chrono::DateTime::parse_from_rfc2822(s) .unwrap() .with_timezone(&chrono::offset::Utc) } fn get_source_date_epoch_from_env() -> Option> { match std::env::var(crate::SOURCE_DATE_EPOCH) { Ok(val) => { let ts = match val.parse::() { Ok(ts) => ts, Err(_) => { eprintln!("SOURCE_DATE_EPOCH defined, but not a i64"); return None; } }; match chrono::DateTime::from_timestamp(ts, 0) { Some(now) => Some(now), None => { eprintln!("SOURCE_DATE_EPOCH can't be represented as a UTC-time"); None } } } Err(_) => None, } } pub fn write_time(mut w: &fs::File, envmap: &environment::EnvironmentMap) -> io::Result<()> { use io::Write; let now = match envmap.get_override_var("BUILT_TIME_UTC") { Some(v) => v, None => get_source_date_epoch_from_env().unwrap_or_else(chrono::offset::Utc::now), }; write_str_variable!( w, "BUILT_TIME_UTC", now.to_rfc2822(), "The build time in RFC2822, UTC." ); Ok(()) } built-0.8.0/src/lib.rs000064400000000000000000000643651046102023000126710ustar 00000000000000// MIT License // // Copyright (c) Lukas Lueg // // 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. // #![allow(clippy::needless_doctest_main)] //! Provides a crate with information from the time it was built. //! //! `built` is used as a build-time dependency to collect various information //! about the build-environment, serialize this information into Rust-code and //! provide that to the crate. The information collected by `built` include: //! //! * Various metadata like version, authors, homepage etc. as set by `Cargo.toml` //! * The tag or commit id if the crate was being compiled from within a Git repository. //! * The values of `CARGO_CFG_*` build script environment variables, like `CARGO_CFG_TARGET_OS` and `CARGO_CFG_TARGET_ARCH`. //! * The features the crate was compiled with. //! * The various dependencies, dependencies of dependencies and their versions Cargo ultimately chose to compile. //! * The presence of a CI-platform like `Github Actions`, `Travis CI` and `AppVeyor`. //! * The compiler and it's version; the documentation-generator and it's version. //! //! `built` does not add any further runtime-dependencies to a crate; all information //! is serialized as types from `stdlib`. One can include `built` as a //! runtime-dependency and use it's convenience functions. //! //! To add `built` to a crate, add it as a build-time dependency, use a build-script //! to collect and serialize the build-time information, and `include!` the generated code. //! //! Add this to `Cargo.toml`: //! //! ```toml //! [package] //! build = "build.rs" //! //! [build-dependencies] //! built = "0.7" //! ``` //! //! Add or modify a build-script. In `build.rs`: //! //! ```rust,no_run //! fn main() { //! built::write_built_file().expect("Failed to acquire build-time information"); //! } //! ``` //! //! The build-script will by default write a file named `built.rs` into Cargo's output //! directory. It can be picked up in `main.rs` (or anywhere else) like this: //! //! ```rust,ignore //! // Use of a mod or pub mod is not actually necessary. //! pub mod built_info { //! // The file has been placed there by the build script. //! include!(concat!(env!("OUT_DIR"), "/built.rs")); //! } //! ``` //! //! ...and then used somewhere in the crate's code: //! //! ```rust //! # mod built_info { //! # pub static PKG_VERSION_PRE: &str = ""; //! # pub static CI_PLATFORM: Option<&str> = None; //! # pub static GIT_VERSION: Option<&str> = None; //! # pub static DEPENDENCIES: [(&str, &str); 0] = []; //! # pub static BUILT_TIME_UTC: &str = "Tue, 14 Feb 2017 05:21:41 GMT"; //! # } //! # //! # enum LogLevel { TRACE, ERROR }; //! /// Determine if current version is a pre-release or was built from a git-repo //! fn release_is_unstable() -> bool { //! return !built_info::PKG_VERSION_PRE.is_empty() || built_info::GIT_VERSION.is_some() //! } //! //! /// Default log-level, enhanced on CI //! fn default_log_level() -> LogLevel { //! if built_info::CI_PLATFORM.is_some() { //! LogLevel::TRACE //! } else { //! LogLevel::ERROR //! } //! } //! //! /// The time this crate was built //! #[cfg(feature = "chrono")] //! fn built_time() -> built::chrono::DateTime { //! built::util::strptime(built_info::BUILT_TIME_UTC) //! .with_timezone(&built::chrono::offset::Local) //! } //! //! /// If another crate pulls in a dependency we don't like, print a warning //! #[cfg(feature = "semver")] //! fn check_sane_dependencies() { //! if built::util::parse_versions(&built_info::DEPENDENCIES) //! .any(|(name, ver)| name == "DeleteAllMyFiles" //! && ver < built::semver::Version::parse("1.1.4").unwrap()) { //! eprintln!("DeleteAllMyFiles < 1.1.4 may not delete all your files. Beware!"); //! } //! } //! ``` //! //! //! ## Overrides //! //! Most values otherwise detected by `built` can be manually set using environment variables. The //! primary use-case for this is to allow automated build-systems and package-managers to enforce //! certain values, e.g. to hide the presence of a CI-platform and/or allow for reproducible //! builds. //! //! The values set via the environment take precedence over what `built` would otherwise detect. //! There is no mechanism to ensure that values derived from override-variables are sensible, //! besides enforcing the correct type. //! //! Override-variables are prefixed by `BUILT_OVERRIDE_{PKG_NAME}_`, where `{PKG_NAME}` is the name //! of the package as reported by `cargo`. For example, if the package is named "mypkg", and the value //! to be overridden is named "CI_PLATFORM", then the override variable is named //! "BUILT_OVERRIDE_mypkg_CI_PLATFORM". Remember that more than one package in a dependency-graph //! might use `built` internally, so you might have to override multiple instances of the same //! value. //! Unused override variables result in a warning at compile time, albeit cargo only reports those //! for path-specific (e.g. local) packages. //! //! An override-variable's text-presentation must parse to the value's respective type: //! * Strings are used as-is. //! * Integers and `bool` must parse via their `std::str::FromStr`-implementation. //! * `std::option::Option` parses the string `BUILT_OVERRIDE_NONE` as `Option::None`; every other value //! must parse as `T`. For example, `BUILT_OVERRIDE_mypkg_CI_PLATFORM=BUILT_OVERRIDE_NONE` forces //! `CI_PLATFORM.is_none()`. //! * List-like values are parsed as comma-separated values. //! //! If an override-variable can't be parsed, the build-process will abort with a `panic!()`. //! //! Notice that values that were overridden are recorded in `OVERRIDE_VARIABLES_USED`. //! //! Please refer to the respective item's documentation for more information on overrides. //! //! ## Feature flags //! The information that `built` collects and makes available in `built.rs` depends //! on the features that were enabled on the build-time dependency. //! //! ### _Always available_ //! The following information is available regardless of feature-flags. //! //! ``` //! /// The Continuous Integration platform detected during compilation. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_CI_PLATFORM`. //! pub static CI_PLATFORM: Option<&str> = None; //! //! /// The full version. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_PKG_VERSION`. //! pub static PKG_VERSION: &str = "0.1.0"; //! /// The major version. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_PKG_MAJOR`. //! pub static PKG_VERSION_MAJOR: &str = "0"; //! /// The minor version. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_PKG_MINOR`. //! pub static PKG_VERSION_MINOR: &str = "1"; //! /// "The patch version. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_PKG_PATCH`. //! pub static PKG_VERSION_PATCH: &str = "0"; //! /// "The pre-release version. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_PKG_PRE`. //! pub static PKG_VERSION_PRE: &str = ""; //! //! /// "A colon-separated list of authors. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_PKG_AUTHORS`. //! pub static PKG_AUTHORS: &str = "Lukas Lueg "; //! //! /// The name of the package. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_PKG_NAME`. //! pub static PKG_NAME: &str = "example_project"; //! /// "The description. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_PKG_DESCRIPTION`. //! pub static PKG_DESCRIPTION: &str = ""; //! /// "The homepage. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_PKG_HOMEPAGE`. //! pub static PKG_HOMEPAGE: &str = ""; //! /// "The license. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_PKG_LICENSE`. //! pub static PKG_LICENSE: &str = "MIT"; //! /// The source repository as advertised in Cargo.toml. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_PKG_REPOSITORY`. //! pub static PKG_REPOSITORY: &str = ""; //! //! /// The target triple that was being compiled for. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_TARGET`. //! pub static TARGET: &str = "x86_64-unknown-linux-gnu"; //! /// The host triple of the rust compiler. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_HOST`. //! pub static HOST: &str = "x86_64-unknown-linux-gnu"; //! /// `release` for release builds, `debug` for other builds. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_PROFILE`. //! pub static PROFILE: &str = "debug"; //! //! /// The compiler that cargo resolved to use. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_RUSTC`; notice that //! /// if `RUSTC` is overridden, `RUSTC_VERSION` *must* also be overridden. //! pub static RUSTC: &str = "rustc"; //! /// The documentation-generator that cargo resolved to use. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_RUSTDOC`; notice that if `RUSTDOC` //! /// is overridden, `RUSTDOC_VERSION` *must* also be overridden. //! pub static RUSTDOC: &str = "rustdoc"; //! /// The output of `rustc -V` //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_RUSTC_VERSION`. //! pub static RUSTC_VERSION: &str = "rustc 1.43.1 (8d69840ab 2020-05-04)"; //! /// The output of `rustdoc -V` //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_RUSTDOC_VERSION`. //! pub static RUSTDOC_VERSION: &str = "rustdoc 1.43.1 (8d69840ab 2020-05-04)"; //! //! /// Value of OPT_LEVEL for the profile used during compilation. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_OPT_LEVEL`. //! pub static OPT_LEVEL: &str = "0"; //! /// The parallelism that was specified during compilation. //! /// If `SOURCE_DATE_EPOCH` is set, `NUM_JOBS` is forced to `1`. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_NUM_JOBS`. //! pub static NUM_JOBS: u32 = 8; //! /// "Value of DEBUG for the profile used during compilation. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_NUM_DEBUG`. //! pub static DEBUG: bool = true; //! //! /// The features that were enabled during compilation. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_FEATURES`. //! pub static FEATURES: [&str; 0] = []; //! /// The features as a comma-separated string. //! pub static FEATURES_STR: &str = ""; //! /// The features as above, as lowercase strings. //! pub static FEATURES_LOWERCASE: [&str; 0] = []; //! /// The feature-string as above, from lowercase strings. //! pub static FEATURES_LOWERCASE_STR: &str = ""; //! //! /// The target architecture, given by `CARGO_CFG_TARGET_ARCH`. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_CFG_TARGET_ARCH`. //! pub static CFG_TARGET_ARCH: &str = "x86_64"; //! /// The endianness, given by `CARGO_CFG_TARGET_ENDIAN`. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_CFG_ENDIAN`. //! pub static CFG_ENDIAN: &str = "little"; //! /// The toolchain-environment, given by `CARGO_CFG_TARGET_ENV`. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_CFG_ENV`. //! pub static CFG_ENV: &str = "gnu"; //! /// The OS-family, given by `CARGO_CFG_TARGET_FAMILY`. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_CFG_FAMILY`. //! pub static CFG_FAMILY: &str = "unix"; //! /// The operating system, given by `CARGO_CFG_TARGET_OS`. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_CFG_OS`. //! pub static CFG_OS: &str = "linux"; //! /// The pointer width, given by `CARGO_CFG_TARGET_POINTER_WIDTH`. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_CFG_POINTER_WIDTH`. //! pub static CFG_POINTER_WIDTH: &str = "64"; //! //! /// The override-variables that were used during compilation. //! pub static OVERRIDE_VARIABLES_USED: [&str; 0] = []; //! ``` //! //! ### `cargo-lock` //! Parses `Cargo.lock`and generates representations of dependencies and their versions. //! //! For this to work, `Cargo.lock` needs to actually be there; this is (usually) //! only true for executables and not for libraries. Cargo will only create a //! `Cargo.lock` for the top-level crate in a dependency-tree. In case //! of a library, the top-level crate will decide which crate/version //! combination to compile and there will be no `Cargo.lock` while the library //! gets compiled as a dependency. //! //! Parsing `Cargo.lock` instead of `Cargo.toml` allows to serialize the //! precise versions Cargo chose to compile. One can't, however, distinguish //! `build-dependencies`, `dev-dependencies` and `dependencies`. Furthermore, //! some dependencies never show up if Cargo had not been forced to //! actually use them (e.g. `dev-dependencies` with `cargo test` never //! having been executed). //! //! Note that if the `dependency-tree`-feature is not active, the list of dependencies //! contains the root-package(s) as well. //! //! ``` //! /// An array of effective dependencies as documented by `Cargo.lock`. //! pub static DEPENDENCIES: [(&str, &str); 37] = [("autocfg", "1.0.0"), ("bitflags", "1.2.1"), ("built", "0.4.1"), ("cargo-lock", "4.0.1"), ("cc", "1.0.54"), ("cfg-if", "0.1.10"), ("chrono", "0.4.11"), ("example_project", "0.1.0"), ("git2", "0.13.6"), ("idna", "0.2.0"), ("jobserver", "0.1.21"), ("libc", "0.2.71"), ("libgit2-sys", "0.12.6+1.0.0"), ("libz-sys", "1.0.25"), ("log", "0.4.8"), ("matches", "0.1.8"), ("num-integer", "0.1.42"), ("num-traits", "0.2.11"), ("percent-encoding", "2.1.0"), ("pkg-config", "0.3.17"), ("proc-macro2", "1.0.17"), ("quote", "1.0.6"), ("semver", "1.0.0"), ("serde", "1.0.110"), ("serde_derive", "1.0.110"), ("smallvec", "1.4.0"), ("syn", "1.0.25"), ("time", "0.1.43"), ("toml", "0.5.6"), ("unicode-bidi", "0.3.4"), ("unicode-normalization", "0.1.12"), ("unicode-xid", "0.2.0"), ("url", "2.1.1"), ("vcpkg", "0.2.8"), ("winapi", "0.3.8"), ("winapi-i686-pc-windows-gnu", "0.4.0"), ("winapi-x86_64-pc-windows-gnu", "0.4.0")]; //! /// The effective dependencies as a comma-separated string. //! pub static DEPENDENCIES_STR: &str = "autocfg 1.0.0, bitflags 1.2.1, built 0.4.1, cargo-lock 4.0.1, cc 1.0.54, cfg-if 0.1.10, chrono 0.4.11, example_project 0.1.0, git2 0.13.6, idna 0.2.0, jobserver 0.1.21, libc 0.2.71, libgit2-sys 0.12.6+1.0.0, libz-sys 1.0.25, log 0.4.8, matches 0.1.8, num-integer 0.1.42, num-traits 0.2.11, percent-encoding 2.1.0, pkg-config 0.3.17, proc-macro2 1.0.17, quote 1.0.6, semver 1.0.0, serde 1.0.110, serde_derive 1.0.110, smallvec 1.4.0, syn 1.0.25, time 0.1.43, toml 0.5.6, unicode-bidi 0.3.4, unicode-normalization 0.1.12, unicode-xid 0.2.0, url 2.1.1, vcpkg 0.2.8, winapi 0.3.8, winapi-i686-pc-windows-gnu 0.4.0, winapi-x86_64-pc-windows-gnu 0.4.0"; //! ``` //! //! ### `dependency-tree` (implies `cargo-lock`) //! Solve the dependency-graph in `Cargo.lock` to discern direct and indirect //! dependencies. //! //! "Direct" dependencies are those which the root-package(s) depends on. //! "Indirect" dependencies are those which are not direct dependencies. //! //! ``` //! /// An array of direct dependencies as documented by `Cargo.lock`. //! pub static DIRECT_DEPENDENCIES: [(&str, &str); 1] = [("built", "0.6.1")]; //! /// The direct dependencies as a comma-separated string. //! pub static DIRECT_DEPENDENCIES_STR: &str = r"built 0.6.1"; //! //! /// An array of indirect dependencies as documented by `Cargo.lock`. //! pub static INDIRECT_DEPENDENCIES: [(&str, &str); 64] = [("android-tzdata", "0.1.1"), ("android_system_properties", "0.1.5"), ("autocfg", "1.1.0"), ("bitflags", "2.4.0"), ("bumpalo", "3.13.0"), ("cargo-lock", "9.0.0"), ("cc", "1.0.83"), ("cfg-if", "1.0.0"), ("chrono", "0.4.29"), ("core-foundation-sys", "0.8.4"), ("equivalent", "1.0.1"), ("example_project", "0.1.0"), ("fixedbitset", "0.4.2"), ("form_urlencoded", "1.2.0"), ("git2", "0.18.0"), ("hashbrown", "0.14.0"), ("iana-time-zone", "0.1.57"), ("iana-time-zone-haiku", "0.1.2"), ("idna", "0.4.0"), ("indexmap", "2.0.0"), ("jobserver", "0.1.26"), ("js-sys", "0.3.64"), ("libc", "0.2.147"), ("libgit2-sys", "0.16.1+1.7.1"), ("libz-sys", "1.1.12"), ("log", "0.4.20"), ("memchr", "2.6.3"), ("num-traits", "0.2.16"), ("once_cell", "1.18.0"), ("percent-encoding", "2.3.0"), ("petgraph", "0.6.4"), ("pkg-config", "0.3.27"), ("proc-macro2", "1.0.66"), ("quote", "1.0.33"), ("semver", "1.0.18"), ("serde", "1.0.188"), ("serde_derive", "1.0.188"), ("serde_spanned", "0.6.3"), ("syn", "2.0.31"), ("tinyvec", "1.6.0"), ("tinyvec_macros", "0.1.1"), ("toml", "0.7.6"), ("toml_datetime", "0.6.3"), ("toml_edit", "0.19.14"), ("unicode-bidi", "0.3.13"), ("unicode-ident", "1.0.11"), ("unicode-normalization", "0.1.22"), ("url", "2.4.1"), ("vcpkg", "0.2.15"), ("wasm-bindgen", "0.2.87"), ("wasm-bindgen-backend", "0.2.87"), ("wasm-bindgen-macro", "0.2.87"), ("wasm-bindgen-macro-support", "0.2.87"), ("wasm-bindgen-shared", "0.2.87"), ("windows", "0.48.0"), ("windows-targets", "0.48.5"), ("windows_aarch64_gnullvm", "0.48.5"), ("windows_aarch64_msvc", "0.48.5"), ("windows_i686_gnu", "0.48.5"), ("windows_i686_msvc", "0.48.5"), ("windows_x86_64_gnu", "0.48.5"), ("windows_x86_64_gnullvm", "0.48.5"), ("windows_x86_64_msvc", "0.48.5"), ("winnow", "0.5.15")]; //! /// The indirect dependencies as a comma-separated string. //! pub static INDIRECT_DEPENDENCIES_STR: &str = r"android-tzdata 0.1.1, android_system_properties 0.1.5, autocfg 1.1.0, bitflags 2.4.0, bumpalo 3.13.0, cargo-lock 9.0.0, cc 1.0.83, cfg-if 1.0.0, chrono 0.4.29, core-foundation-sys 0.8.4, equivalent 1.0.1, example_project 0.1.0, fixedbitset 0.4.2, form_urlencoded 1.2.0, git2 0.18.0, hashbrown 0.14.0, iana-time-zone 0.1.57, iana-time-zone-haiku 0.1.2, idna 0.4.0, indexmap 2.0.0, jobserver 0.1.26, js-sys 0.3.64, libc 0.2.147, libgit2-sys 0.16.1+1.7.1, libz-sys 1.1.12, log 0.4.20, memchr 2.6.3, num-traits 0.2.16, once_cell 1.18.0, percent-encoding 2.3.0, petgraph 0.6.4, pkg-config 0.3.27, proc-macro2 1.0.66, quote 1.0.33, semver 1.0.18, serde 1.0.188, serde_derive 1.0.188, serde_spanned 0.6.3, syn 2.0.31, tinyvec 1.6.0, tinyvec_macros 0.1.1, toml 0.7.6, toml_datetime 0.6.3, toml_edit 0.19.14, unicode-bidi 0.3.13, unicode-ident 1.0.11, unicode-normalization 0.1.22, url 2.4.1, vcpkg 0.2.15, wasm-bindgen 0.2.87, wasm-bindgen-backend 0.2.87, wasm-bindgen-macro 0.2.87, wasm-bindgen-macro-support 0.2.87, wasm-bindgen-shared 0.2.87, windows 0.48.0, windows-targets 0.48.5, windows_aarch64_gnullvm 0.48.5, windows_aarch64_msvc 0.48.5, windows_i686_gnu 0.48.5, windows_i686_msvc 0.48.5, windows_x86_64_gnu 0.48.5, windows_x86_64_gnullvm 0.48.5, windows_x86_64_msvc 0.48.5, winnow 0.5.15"; //! ``` //! //! ### `git2` //! Try to open the git-repository at `manifest_location` and retrieve `HEAD` //! tag or commit id. //! //! Notice that `GIT_HEAD_REF` is `None` if `HEAD` is detached or not valid UTF-8. //! //! Continuous Integration platforms like `Travis` and `AppVeyor` will //! do shallow clones, causing `libgit2` to be unable to get a meaningful //! result. `GIT_VERSION` and `GIT_DIRTY` will therefore always be `None` if //! a CI-platform is detected. //! ``` //! /// If the crate was compiled from within a git-repository, //! /// `GIT_VERSION` contains HEAD's tag. The short commit id is used //! /// if HEAD is not tagged. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_GIT_VERSION`. //! pub static GIT_VERSION: Option<&str> = Some("0.4.1-10-gca2af4f"); //! //! /// If the repository had dirty/staged files. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_GIT_DIRTY`. //! pub static GIT_DIRTY: Option = Some(true); //! //! /// If the crate was compiled from within a git-repository, //! /// `GIT_HEAD_REF` contains full name to the reference pointed to by //! /// HEAD (e.g.: `refs/heads/master`). If HEAD is detached or the branch //! /// name is not valid UTF-8 `None` will be stored. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_GIT_HEAD_REF`. //! pub static GIT_HEAD_REF: Option<&str> = Some("refs/heads/master"); //! //! /// If the crate was compiled from within a git-repository, //! /// `GIT_COMMIT_HASH` contains HEAD's full commit SHA-1 hash. //! /// Can be overridden with `BUILT_OVERRIDE_{pkg_name}_GIT_GIT_COMMIT_HASH`. //! pub static GIT_COMMIT_HASH: Option<&str> = Some("ca2af4f11bb8f4f6421c4cccf428bf4862573daf"); //! //! /// If the crate was compiled from within a git-repository, //! /// `GIT_COMMIT_HASH_SHORT` contains HEAD's short commit SHA-1 hash. //! /// Can be overriden using `BUILT_OVERRIDE_{pkg_name}_GIT_COMMIT_HASH_SHORT`. //! pub static GIT_COMMIT_HASH_SHORT: Option<&str> = Some("ca2af4f"); //! ``` //! //! ### `chrono` //! //! The build-time is recorded as `BUILT_TIME_UTC`. If `built` is included as a runtime-dependency, //! it can parse the string-representation into a `time:Tm` with the help of //! `built::util::strptime()`. //! //! `built` honors the environment variable `SOURCE_DATE_EPOCH`. If the variable is defined and //! parses to a valid UTC timestamp, that build-time is used instead of the current local time. //! The variable is silently ignored if defined but but does not parse to a valid UTC timestamp. //! //! ``` //! /// The built-time in RFC2822, UTC //! /// Can be overridden with`BUILT_OVERRIDE_BUILT_TIME_UTC`; the override takes precedence //! /// over `SOURCE_DATE_EPOCH`; it *must* parse via `chrono::DateTime::parse_from_rfc2822()`. //! pub static BUILT_TIME_UTC: &str = "Wed, 27 May 2020 18:12:39 +0000"; //! ``` #[cfg(feature = "cargo-lock")] mod dependencies; mod environment; #[cfg(feature = "git2")] mod git; #[cfg(feature = "chrono")] mod krono; pub mod util; use std::{env, fmt, fs, io, io::Write, path}; #[cfg(feature = "semver")] pub use semver; #[cfg(feature = "chrono")] pub use chrono; pub use environment::CIPlatform; #[doc = include_str!("../README.md")] #[allow(dead_code)] type _READMETEST = (); /// If `SOURCE_DATE_EPOCH` is defined, it's value is used instead of /// `chrono::..::now()` as `BUILT_TIME_UTC`. /// The presence of `SOURCE_DATE_EPOCH` also soft-indicates that a /// reproducible build is desired, which we may or may not be able /// to honor. const SOURCE_DATE_EPOCH: &str = "SOURCE_DATE_EPOCH"; macro_rules! write_variable { ($writer:expr, $name:expr, $datatype:expr, $value:expr, $doc:expr) => { writeln!( $writer, "#[allow(clippy::needless_raw_string_hashes)]\n#[doc=r#\"{}\"#]\n#[allow(dead_code)]\npub static {}: {} = {};", $doc, $name, $datatype, $value )?; }; } pub(crate) use write_variable; macro_rules! write_str_variable { ($writer:expr, $name:expr, $value:expr, $doc:expr) => { write_variable!( $writer, $name, "&str", format_args!("\"{}\"", $value.escape_default()), $doc ); }; } pub(crate) use write_str_variable; pub(crate) fn fmt_option_str(o: Option) -> String { match o { Some(s) => format!("Some(\"{s}\")"), None => "None".to_owned(), } } /// Writes rust-code describing the crate at `manifest_location` to a new file named `dst`. /// /// # Errors /// The function returns an error if the file at `dst` already exists or can't /// be written to. This should not be a concern if the filename points to /// `OUR_DIR`. pub fn write_built_file_with_opts( #[cfg(any(feature = "cargo-lock", feature = "git2"))] manifest_location: Option<&path::Path>, dst: &path::Path, ) -> io::Result<()> { let mut built_file = fs::File::create(dst)?; built_file.write_all( r#"// // EVERYTHING BELOW THIS POINT WAS AUTO-GENERATED DURING COMPILATION. DO NOT MODIFY. // "# .as_ref(), )?; let envmap = environment::EnvironmentMap::new(); envmap.write_ci(&built_file)?; envmap.write_env(&built_file)?; envmap.write_features(&built_file)?; envmap.write_compiler_version(&built_file)?; envmap.write_cfg(&built_file)?; #[cfg(feature = "git2")] { if let Some(manifest_location) = manifest_location { git::write_git_version(manifest_location, &envmap, &built_file)?; } } #[cfg(feature = "cargo-lock")] if let Some(manifest_location) = manifest_location { dependencies::write_dependencies(manifest_location, &built_file)?; } #[cfg(feature = "chrono")] krono::write_time(&built_file, &envmap)?; let mut used_override_vars = envmap.used_override_vars().collect::>(); used_override_vars.sort_unstable(); write_variable!( built_file, "OVERRIDE_VARIABLES_USED", format_args!("[&str; {}]", used_override_vars.len()), util::ArrayDisplay(&used_override_vars, |t, f| write!( f, "\"{}\"", t.escape_default() )), "The override-variables that were used during compilation." ); built_file.write_all( r#"// // EVERYTHING ABOVE THIS POINT WAS AUTO-GENERATED DURING COMPILATION. DO NOT MODIFY. // "# .as_ref(), )?; let unused_override_vars = envmap.unused_override_vars().collect::>().join(", "); if !unused_override_vars.is_empty() { println!("cargo::warning=At least one environment variable looks like an override-variable but was ignored by built: `{unused_override_vars}`. Typo?"); } Ok(()) } /// A shorthand for calling `write_built_file_with_opts()` with `CARGO_MANIFEST_DIR` and /// `[OUT_DIR]/built.rs`. /// /// # Errors /// Same as `write_built_file_with_opts()`. /// /// # Panics /// If `CARGO_MANIFEST_DIR` or `OUT_DIR` are not set. pub fn write_built_file() -> io::Result<()> { let dst = path::Path::new(&env::var("OUT_DIR").expect("OUT_DIR not set")).join("built.rs"); write_built_file_with_opts( #[cfg(any(feature = "cargo-lock", feature = "git2"))] Some( env::var("CARGO_MANIFEST_DIR") .expect("CARGO_MANIFEST_DIR") .as_ref(), ), &dst, )?; Ok(()) } built-0.8.0/src/util.rs000064400000000000000000000146161046102023000130720ustar 00000000000000//! Various convenience functions for `built` at runtime. use std::fmt; use std::fmt::Write; #[cfg(feature = "git2")] pub use crate::git::{get_repo_description, get_repo_head}; #[cfg(feature = "chrono")] pub use crate::krono::strptime; /// Parses version-strings with `semver::Version::parse()`. /// /// This function is only available if `built` was compiled with the /// `semver` feature. /// /// The function takes a reference to an array of names and version numbers as /// serialized by `built` and returns an iterator over the unchanged names /// and parsed version numbers. /// /// ``` /// pub mod build_info { /// pub static DEPENDENCIES: [(&'static str, &'static str); 1] = [("built", "0.1.0")]; /// } /// /// let deps = build_info::DEPENDENCIES; /// assert!(built::util::parse_versions(&deps) /// .any(|(name, ver)| name == "built" && /// ver >= semver::Version::parse("0.1.0").unwrap())); /// ``` /// /// # Panics /// If a version can't be parsed by `semver::Version::parse()`. This should never /// happen with version strings provided by Cargo and `built`. #[cfg(feature = "semver")] pub fn parse_versions<'a, T>( name_and_versions: T, ) -> impl Iterator where T: IntoIterator, { fn parse_version<'a>(t: &'a (&'a str, &'a str)) -> (&'a str, semver::Version) { (t.0, t.1.parse().unwrap()) } name_and_versions.into_iter().map(parse_version) } /// Detect execution on various Continuous Integration platforms. /// /// CI-platforms are detected by the presence of known environment variables. /// This allows to detect specific CI-platform (like `GitLab`); various /// generic environment variables are also checked, which may result in /// `CIPlatform::Generic`. /// /// Since some platforms have fairly generic environment variables to begin with /// (e.g. `TASK_ID`), this function may have false positives. #[must_use] pub fn detect_ci() -> Option { crate::environment::EnvironmentMap::new().detect_ci() } pub(crate) trait ParseFromEnv<'a> where Self: Sized, { type Err: std::fmt::Debug; fn parse_from_env(s: &'a str) -> Result; } impl<'a> ParseFromEnv<'a> for &'a str { type Err = std::convert::Infallible; fn parse_from_env(s: &'a str) -> Result { Ok(s) } } macro_rules! parsefromenv_impl { ($tie:ty) => { impl ParseFromEnv<'_> for $tie { type Err = ::Err; fn parse_from_env(s: &str) -> Result { s.parse() } } }; ($($tie:ty),+) => { $( parsefromenv_impl!($tie); )+ }; } parsefromenv_impl!(i64, i32, i16, i8, u64, u32, u16, u8, bool, String); impl<'a, T> ParseFromEnv<'a> for Vec where T: ParseFromEnv<'a>, { type Err = >::Err; fn parse_from_env(s: &'a str) -> Result { s.split(',') .map(|chunk| T::parse_from_env(chunk.trim())) .collect() } } impl<'a, T> ParseFromEnv<'a> for Option where T: ParseFromEnv<'a>, { type Err = >::Err; fn parse_from_env(s: &'a str) -> Result { if s == "BUILT_OVERRIDE_NONE" { Ok(None) } else { Ok(Some(T::parse_from_env(s)?)) } } } pub(crate) struct ArrayDisplay<'a, T, F>(pub &'a [T], pub F) where F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result; impl fmt::Display for ArrayDisplay<'_, T, F> where F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_char('[')?; for (i, v) in self.0.iter().enumerate() { if i != 0 { f.write_str(", ")?; } (self.1)(v, f)?; } f.write_char(']') } } #[cfg(feature = "cargo-lock")] pub(crate) struct TupleArrayDisplay<'a, T>(pub &'a [(T, T)]); #[cfg(feature = "cargo-lock")] impl fmt::Display for TupleArrayDisplay<'_, T> where T: AsRef, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{}", ArrayDisplay(self.0, |(a, b), fmt| write!( fmt, r#"("{}", "{}")"#, a.as_ref().escape_default(), b.as_ref().escape_default() )) ) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_env() { assert_eq!(String::parse_from_env("foo"), Ok("foo".to_owned())); assert_eq!(<&str>::parse_from_env("foo"), Ok("foo")); assert_eq!(i64::parse_from_env("123"), Ok(123)); assert_eq!(i32::parse_from_env("123"), Ok(123)); assert_eq!(i16::parse_from_env("123"), Ok(123)); assert_eq!(i8::parse_from_env("123"), Ok(123)); assert_eq!(u64::parse_from_env("123"), Ok(123)); assert_eq!(u32::parse_from_env("123"), Ok(123)); assert_eq!(u16::parse_from_env("123"), Ok(123)); assert_eq!(u8::parse_from_env("123"), Ok(123)); assert_eq!(i8::parse_from_env("-123"), Ok(-123)); assert!(u8::parse_from_env("-123").is_err()); assert!(i32::parse_from_env("foo").is_err()); assert!(i32::parse_from_env("").is_err()); assert_eq!(bool::parse_from_env("true"), Ok(true)); assert_eq!(bool::parse_from_env("false"), Ok(false)); assert!(bool::parse_from_env("").is_err()); assert!(bool::parse_from_env("foo").is_err()); assert!(Option::::parse_from_env("").is_err()); assert_eq!(Option::<&str>::parse_from_env(""), Ok(Some(""))); assert_eq!( Option::parse_from_env("BUILT_OVERRIDE_NONE"), Ok(Option::::None) ); assert_eq!(Option::parse_from_env("123"), Ok(Some(123u8))); assert_eq!(Option::::parse_from_env("true"), Ok(Some(true))); assert!(Option::::parse_from_env("123").is_err()); assert_eq!( Vec::<&str>::parse_from_env("foo, b a r , foo"), Ok(vec!["foo", "b a r", "foo"]) ); assert_eq!( Vec::::parse_from_env("123,456,789"), Ok(vec![123, 456, 789]) ); assert_eq!( Option::parse_from_env("123,456"), Ok(Some(vec![123u32, 456u32])) ); } } built-0.8.0/tests/testbox_tests.rs000064400000000000000000000622201046102023000153740ustar 00000000000000use std::{ collections, env, ffi, fs, io::{self, Write}, path, process, }; struct Project { root: tempfile::TempDir, files: Vec<(path::PathBuf, Vec)>, env: collections::HashMap, } impl Project { fn new() -> Project { Project { root: tempfile::tempdir().unwrap(), files: Default::default(), env: Default::default(), } } fn add_file, C: Into>>( &mut self, name: N, content: C, ) -> &mut Self { self.files.push((name.into(), content.into())); self } fn set_env(&mut self, k: K, v: V) -> &mut Self where K: Into, V: Into, { self.env.insert(k.into(), v.into()); self } fn bootstrap(&mut self) -> &mut Self { let built_root = get_built_root(); let features = if cfg!(feature = "git2") { r#"["git2"]"# } else { "[]" }; self.add_file( "Cargo.toml", format!( r#" [package] name = "testbox" version = "0.0.1" build = "build.rs" [build-dependencies] built = {{ path = "{}", features = {} }}"#, built_root.display().to_string().escape_default(), &features, ), ) .add_file( "build.rs", r#" fn main() { built::write_built_file().expect("writing failed"); }"#, ); self } /// Hold on to the tempdir, it will be removed when dropped! fn create(self) -> io::Result { fs::DirBuilder::new() .create(self.root.path().join("src")) .unwrap(); for (name, content) in self.files { let fname = self.root.path().join(name); assert!(fname.is_absolute()); fs::create_dir_all(fname.parent().unwrap()).unwrap(); let mut file = fs::File::create(fname)?; file.write_all(&content)?; } Ok(self.root) } fn create_and_run(mut self, extra_args: &[&str]) -> (String, String) { let env = std::mem::take(&mut self.env); let root = self.create().expect("Creating the project failed"); Self::run(root.as_ref(), extra_args, env) } fn create_and_build(self, extra_args: &[&str]) { let root = self.create().expect("Creating the project failed"); Self::build(root.as_ref(), extra_args); } fn run(root: &std::path::Path, extra_args: &[&str], env: I) -> (String, String) where I: IntoIterator, K: AsRef, V: AsRef, { let cargo_result = process::Command::new("cargo") .current_dir(root) .arg("run") .args(extra_args) .envs(env) .output() .expect("cargo failed"); let stderr = String::from_utf8_lossy(&cargo_result.stderr); let stdout = String::from_utf8_lossy(&cargo_result.stdout); assert!( cargo_result.status.success(), "cargo failed with {}", stderr ); assert!(stdout.contains("builttestsuccess")); (stderr.into_owned(), stdout.into_owned()) } fn build(root: &std::path::Path, extra_args: &[&str]) { let cargo_result = process::Command::new("cargo") .current_dir(root) .arg("build") .args(extra_args) .output() .expect("cargo failed"); assert!( cargo_result.status.success(), "cargo failed with {}", String::from_utf8_lossy(&cargo_result.stderr) ); } #[cfg(feature = "git2")] fn init_git(&self) -> git2::Repository { git2::Repository::init_opts( &self.root, git2::RepositoryInitOptions::new() .external_template(false) .mkdir(false) .no_reinit(true) .mkpath(false), ) .expect("git-init failed") } } /// Tries to find built's Cargo.toml, panics if it ends up in / fn get_built_root() -> path::PathBuf { env::current_exe() .map(|path| { let mut path = path; loop { if path.join("Cargo.toml").exists() { break; } path = path::PathBuf::from(path.parent().unwrap()); } path }) .unwrap() } #[test] #[ignore = "requires target x86_64-unknown-none"] fn nostd_testbox() { let mut p = Project::new(); let built_root = get_built_root(); p.add_file( "Cargo.toml", format!( r#" [package] name = "nostd_testbox" version = "1.2.3-rc1" authors = ["Joe", "Bob"] build = "build.rs" description = "xobtset" homepage = "localhost" repository = "https://dev.example.com/sources/testbox/" license = "MIT" [build-dependencies] built = {{ path = "{}", default_features=false }} [profile.dev] panic = "abort" [profile.release] panic = "abort" [features] default = ["SuperAwesome", "MegaAwesome"] SuperAwesome = [] MegaAwesome = []"#, built_root.display().to_string().escape_default(), ), ) .add_file( "build.rs", r#" fn main() { built::write_built_file().unwrap(); }"#, ) .add_file( "src/main.rs", r#" #![no_main] #![no_std] use core::panic::PanicInfo; mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } #[panic_handler] fn panic(_panic: &PanicInfo<'_>) -> ! { loop {} } #[no_mangle] pub unsafe extern "C" fn main() -> ! { loop {} } "#, ); p.create_and_build(&["--target", "x86_64-unknown-none"]); } #[test] fn unused_override() { let mut p = Project::new(); p.bootstrap() .add_file( "src/main.rs", r#" mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } fn main() { println!("builttestsuccess"); } "#, ) .set_env("BUILT_OVERRIDE_testbox_FOO", "BAR") .set_env("BUILT_OVERRIDE_testbox1_FOO", "BAR"); let (stderr, _) = p.create_and_run(&[]); assert!(stderr.contains("but was ignored by built: `BUILT_OVERRIDE_testbox_FOO`.")); assert!(!stderr.contains("but was ignored by built: `BUILT_OVERRIDE_testbox1_FOO`.")); } #[test] fn minimal_testbox() { let mut p = Project::new(); let built_root = get_built_root(); p.add_file( "Cargo.toml", format!( r#" [package] name = "minimal_testbox" version = "1.2.3-rc1" authors = ["Joe", "Bob"] build = "build.rs" description = "xobtset" homepage = "localhost" repository = "https://dev.example.com/sources/testbox/" license = "MIT" [dependencies] built = {{ path = "{built_root}", default_features=false }} [build-dependencies] built = {{ path = "{built_root}", default_features=false }} [features] default = ["SuperAwesome", "MegaAwesome"] SuperAwesome = [] MegaAwesome = []"#, built_root = built_root.display().to_string().escape_default() ), ); p.add_file( "build.rs", r#" fn main() { built::write_built_file().unwrap(); }"#, ); p.add_file( "src/main.rs", r#" //! The minimal testbox. mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } fn main() { assert_eq!(built_info::PKG_VERSION, "1.2.3-rc1"); assert_eq!(built_info::PKG_VERSION_MAJOR, "1"); assert_eq!(built_info::PKG_VERSION_MINOR, "2"); assert_eq!(built_info::PKG_VERSION_PATCH, "3"); assert_eq!(built_info::PKG_VERSION_PRE, "rc1"); assert_eq!(built_info::PKG_AUTHORS, "Joe:Bob"); assert_eq!(built_info::PKG_NAME, "minimal_testbox"); assert_eq!(built_info::PKG_DESCRIPTION, "xobtset"); assert_eq!(built_info::PKG_HOMEPAGE, "localhost"); assert_eq!(built_info::PKG_LICENSE, "MIT"); assert_eq!(built_info::PKG_REPOSITORY, "https://dev.example.com/sources/testbox/"); assert!(built_info::NUM_JOBS > 0); assert!(built_info::OPT_LEVEL == "0"); assert!(built_info::DEBUG); assert_eq!(built_info::PROFILE, "debug"); assert_eq!(built_info::FEATURES, ["DEFAULT", "MEGAAWESOME", "SUPERAWESOME"]); assert_eq!(built_info::FEATURES_STR, "DEFAULT, MEGAAWESOME, SUPERAWESOME"); assert_eq!(built_info::FEATURES_LOWERCASE, ["default", "megaawesome", "superawesome"]); assert_eq!(built_info::FEATURES_LOWERCASE_STR, "default, megaawesome, superawesome"); assert_ne!(built_info::RUSTC_VERSION, ""); assert_ne!(built_info::RUSTDOC_VERSION, ""); assert_ne!(built_info::HOST, ""); assert_ne!(built_info::TARGET, ""); assert_ne!(built_info::RUSTC, ""); assert_ne!(built_info::RUSTDOC, ""); assert_ne!(built_info::CFG_TARGET_ARCH, ""); assert_ne!(built_info::CFG_ENDIAN, ""); assert_ne!(built_info::CFG_FAMILY, ""); assert_ne!(built_info::CFG_OS, ""); assert_ne!(built_info::CFG_POINTER_WIDTH, ""); // For CFG_ENV, empty string is a possible value. let _: &'static str = built_info::CFG_ENV; println!("builttestsuccess"); }"#, ); p.create_and_run(&[]); } #[test] fn simple_workspace() { let mut p = Project::new(); let built_root = get_built_root(); p.add_file("Cargo.toml", "[workspace]\nmembers = ['foobar']"); p.add_file( "foobar/Cargo.toml", format!( r#"[package] name = "foobar" version = "5.6.7" build = "build.rs" [build-dependencies] built = {{ path = "{}" }}"#, built_root.display().to_string().escape_default() ), ); p.add_file( "foobar/build.rs", "fn main() { built::write_built_file().unwrap(); }", ); p.add_file( "foobar/src/main.rs", r#" mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } fn main() { assert_eq!(built_info::PKG_VERSION, "5.6.7"); println!("builttestsuccess"); } "#, ); p.create_and_run(&[]); } #[test] #[cfg(all( feature = "cargo-lock", feature = "dependency-tree", feature = "git2", feature = "chrono", feature = "semver" ))] fn full_testbox() { let mut p = Project::new(); let built_root = get_built_root(); p.add_file( "Cargo.toml", format!( r#" [package] name = "testbox" version = "1.2.3-rc1" authors = ["Joe", "Bob", "Harry:Potter"] build = "build.rs" description = "xobtset" homepage = "localhost" repository = "https://dev.example.com/sources/testbox/" license = "MIT" [dependencies] built = {{ path = "{built_root}", features=["cargo-lock", "dependency-tree", "git2", "chrono", "semver"] }} [build-dependencies] built = {{ path = "{built_root}", features=["cargo-lock", "dependency-tree", "git2", "chrono", "semver"] }} [features] default = ["SuperAwesome", "MegaAwesome"] SuperAwesome = [] MegaAwesome = []"#, built_root = built_root.display().to_string().escape_default() ), ) .add_file( "build.rs", r#" fn main() { built::write_built_file().unwrap(); }"#, ) .set_env("CONTINUOUS_INTEGRATION", "1") .add_file( "src/main.rs", r#" //! The testbox. mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } fn main() { assert_eq!(built_info::GIT_VERSION, None); assert_eq!(built_info::GIT_DIRTY, None); assert_eq!(built_info::GIT_COMMIT_HASH, None); assert_eq!(built_info::GIT_COMMIT_HASH_SHORT, None); assert_eq!(built_info::GIT_HEAD_REF, None); assert!(built_info::CI_PLATFORM.is_some()); assert_eq!(built_info::PKG_VERSION, "1.2.3-rc1"); assert_eq!(built_info::PKG_VERSION_MAJOR, "1"); assert_eq!(built_info::PKG_VERSION_MINOR, "2"); assert_eq!(built_info::PKG_VERSION_PATCH, "3"); assert_eq!(built_info::PKG_VERSION_PRE, "rc1"); assert_eq!(built_info::PKG_AUTHORS, "Joe:Bob:Harry:Potter"); assert_eq!(built_info::PKG_NAME, "testbox"); assert_eq!(built_info::PKG_DESCRIPTION, "xobtset"); assert_eq!(built_info::PKG_HOMEPAGE, "localhost"); assert_eq!(built_info::PKG_LICENSE, "MIT"); assert_eq!(built_info::PKG_REPOSITORY, "https://dev.example.com/sources/testbox/"); assert!(built_info::NUM_JOBS > 0); assert!(built_info::OPT_LEVEL == "0"); assert!(built_info::DEBUG); assert_eq!(built_info::PROFILE, "debug"); assert_eq!(built_info::FEATURES, ["DEFAULT", "MEGAAWESOME", "SUPERAWESOME"]); assert_eq!(built_info::FEATURES_STR, "DEFAULT, MEGAAWESOME, SUPERAWESOME"); assert_eq!(built_info::FEATURES_LOWERCASE, ["default", "megaawesome", "superawesome"]); assert_eq!(built_info::FEATURES_LOWERCASE_STR, "default, megaawesome, superawesome"); assert_ne!(built_info::RUSTC_VERSION, ""); assert_ne!(built_info::RUSTDOC_VERSION, ""); assert_ne!(built_info::DEPENDENCIES_STR, ""); assert_ne!(built_info::DIRECT_DEPENDENCIES_STR, ""); assert_ne!(built_info::INDIRECT_DEPENDENCIES_STR, ""); assert_ne!(built_info::HOST, ""); assert_ne!(built_info::TARGET, ""); assert_ne!(built_info::RUSTC, ""); assert_ne!(built_info::RUSTDOC, ""); assert_ne!(built_info::CFG_TARGET_ARCH, ""); assert_ne!(built_info::CFG_ENDIAN, ""); assert_ne!(built_info::CFG_FAMILY, ""); assert_ne!(built_info::CFG_OS, ""); assert_ne!(built_info::CFG_POINTER_WIDTH, ""); // For CFG_ENV, empty string is a possible value. let _: &'static str = built_info::CFG_ENV; assert!(built::util::parse_versions(built_info::DEPENDENCIES.iter()) .any(|(name, ver)| name == "toml" && ver >= built::semver::Version::parse("0.1.0").unwrap())); assert_eq!(built_info::DIRECT_DEPENDENCIES.len(), 1); assert_eq!(built_info::DIRECT_DEPENDENCIES[0].0, "built"); assert!((built::chrono::offset::Utc::now() - built::util::strptime(built_info::BUILT_TIME_UTC)).num_days() <= 1); assert!(built_info::OVERRIDE_VARIABLES_USED.is_empty()); println!("builttestsuccess"); }"#, ); p.create_and_run(&[]); } #[test] #[cfg(all(feature = "git2", feature = "chrono", feature = "semver"))] fn overridden_testbox() { let mut p = Project::new(); let built_root = get_built_root(); p.add_file( "Cargo.toml", format!( r#" [package] name = "testbox" version = "1.2.3-rc1" authors = ["Joe", "Bob", "Harry:Potter"] build = "build.rs" description = "xobtset" homepage = "localhost" repository = "https://dev.example.com/sources/testbox/" license = "MIT" [dependencies] built = {{ path = "{built_root}", features=["git2", "chrono", "semver"] }} [build-dependencies] built = {{ path = "{built_root}", features=["git2", "chrono", "semver"] }} [features] default = ["SuperAwesome", "MegaAwesome"] SuperAwesome = [] MegaAwesome = []"#, built_root = built_root.display().to_string().escape_default() ), ) .add_file( "build.rs", r#" fn main() { built::write_built_file().unwrap(); }"#, ) .set_env("BUILT_OVERRIDE_testbox_GIT_VERSION", "GIT_VERSION1") .set_env("BUILT_OVERRIDE_testbox_GIT_DIRTY", "false") .set_env("BUILT_OVERRIDE_testbox_GIT_COMMIT_HASH", "1234567890") .set_env("BUILT_OVERRIDE_testbox_GIT_HEAD_REF", "GIT_REF") .set_env("BUILT_OVERRIDE_testbox_CI_PLATFORM", "TESTBOXCI") .set_env("BUILT_OVERRIDE_testbox_PKG_VERSION", "1.2.3.4") .set_env("BUILT_OVERRIDE_testbox_PKG_VERSION_MAJOR", "abc") .set_env("BUILT_OVERRIDE_testbox_PKG_VERSION_MINOR", "def") .set_env("BUILT_OVERRIDE_testbox_PKG_VERSION_PATCH", "ghi") .set_env("BUILT_OVERRIDE_testbox_PKG_VERSION_PRE", "jkl") .set_env("BUILT_OVERRIDE_testbox_PKG_AUTHORS", "The council") .set_env("BUILT_OVERRIDE_testbox_PKG_NAME", "OVERRIDEBOX") .set_env("BUILT_OVERRIDE_testbox_PKG_DESCRIPTION", "TEST") .set_env("BUILT_OVERRIDE_testbox_PKG_HOMEPAGE", "foreignhost") .set_env("BUILT_OVERRIDE_testbox_PKG_LICENSE", "MITv2") .set_env("BUILT_OVERRIDE_testbox_PKG_REPOSITORY", "8.8.8.8") .set_env("BUILT_OVERRIDE_testbox_NUM_JOBS", "999") .set_env( "BUILT_OVERRIDE_testbox_OPT_LEVEL", "not_too_fast_not_too_slow", ) .set_env("BUILT_OVERRIDE_testbox_DEBUG", "false") .set_env("BUILT_OVERRIDE_testbox_PROFILE", "MEDIUM") .set_env( "BUILT_OVERRIDE_testbox_FEATURES", "cup_holder,Stereo Sound, dynamic range", ) .set_env("BUILT_OVERRIDE_testbox_RUSTC", "overridec") .set_env("BUILT_OVERRIDE_testbox_RUSTC_VERSION", "overridec v1") .set_env("BUILT_OVERRIDE_testbox_RUSTDOC", "overridedoc") .set_env("BUILT_OVERRIDE_testbox_RUSTDOC_VERSION", "overridedoc v1") .set_env("BUILT_OVERRIDE_testbox_HOST", "overridehost") .set_env("BUILT_OVERRIDE_testbox_TARGET", "potato") .set_env("BUILT_OVERRIDE_testbox_CFG_TARGET_ARCH", "potatoes") .set_env("BUILT_OVERRIDE_testbox_CFG_ENDIAN", "random") .set_env("BUILT_OVERRIDE_testbox_CFG_FAMILY", "v0") .set_env("BUILT_OVERRIDE_testbox_CFG_OS", "none") .set_env("BUILT_OVERRIDE_testbox_CFG_POINTER_WIDTH", "63.9998") .set_env("BUILT_OVERRIDE_testbox_CFG_ENV", "calm") .set_env( "BUILT_OVERRIDE_testbox_BUILT_TIME_UTC", "Sat, 25 May 2024 12:15:59 +0000", ) .add_file( "src/main.rs", r#" //! The testbox. mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } fn main() { assert_eq!(built_info::GIT_VERSION, Some("GIT_VERSION1")); assert_eq!(built_info::GIT_DIRTY, Some(false)); assert_eq!(built_info::GIT_COMMIT_HASH, Some("1234567890")); assert_eq!(built_info::GIT_COMMIT_HASH_SHORT, Some("12345678")); assert_eq!(built_info::GIT_HEAD_REF, Some("GIT_REF")); assert_eq!(built_info::CI_PLATFORM, Some("TESTBOXCI")); assert_eq!(built_info::PKG_VERSION, "1.2.3.4"); assert_eq!(built_info::PKG_VERSION_MAJOR, "abc"); assert_eq!(built_info::PKG_VERSION_MINOR, "def"); assert_eq!(built_info::PKG_VERSION_PATCH, "ghi"); assert_eq!(built_info::PKG_VERSION_PRE, "jkl"); assert_eq!(built_info::PKG_AUTHORS, "The council"); assert_eq!(built_info::PKG_NAME, "OVERRIDEBOX"); assert_eq!(built_info::PKG_DESCRIPTION, "TEST"); assert_eq!(built_info::PKG_HOMEPAGE, "foreignhost"); assert_eq!(built_info::PKG_LICENSE, "MITv2"); assert_eq!(built_info::PKG_REPOSITORY, "8.8.8.8"); assert_eq!(built_info::NUM_JOBS, 999); assert_eq!(built_info::OPT_LEVEL, "not_too_fast_not_too_slow"); assert!(!built_info::DEBUG); assert_eq!(built_info::PROFILE, "MEDIUM"); assert_eq!(built_info::FEATURES, ["Stereo Sound", "cup_holder", "dynamic range"]); assert_eq!(built_info::FEATURES_STR, "Stereo Sound, cup_holder, dynamic range"); assert_eq!(built_info::FEATURES_LOWERCASE, ["cup_holder", "dynamic range", "stereo sound"]); assert_eq!(built_info::FEATURES_LOWERCASE_STR, "cup_holder, dynamic range, stereo sound"); assert_eq!(built_info::RUSTC, "overridec"); assert_eq!(built_info::RUSTC_VERSION, "overridec v1"); assert_eq!(built_info::RUSTDOC, "overridedoc"); assert_eq!(built_info::RUSTDOC_VERSION, "overridedoc v1"); assert_eq!(built_info::HOST, "overridehost"); assert_eq!(built_info::TARGET, "potato"); assert_eq!(built_info::CFG_TARGET_ARCH, "potatoes"); assert_eq!(built_info::CFG_ENDIAN, "random"); assert_eq!(built_info::CFG_FAMILY, "v0"); assert_eq!(built_info::CFG_OS, "none"); assert_eq!(built_info::CFG_POINTER_WIDTH, "63.9998"); assert_eq!(built_info::CFG_ENV, "calm"); assert_eq!(built::util::strptime(built_info::BUILT_TIME_UTC).to_rfc2822(), "Sat, 25 May 2024 12:15:59 +0000"); assert_eq!(built_info::OVERRIDE_VARIABLES_USED, [ "BUILT_TIME_UTC", "CFG_ENDIAN", "CFG_ENV", "CFG_FAMILY", "CFG_OS", "CFG_POINTER_WIDTH", "CFG_TARGET_ARCH", "CI_PLATFORM", "DEBUG", "FEATURES", "GIT_COMMIT_HASH", "GIT_DIRTY", "GIT_HEAD_REF", "GIT_VERSION", "HOST", "NUM_JOBS", "OPT_LEVEL", "PKG_AUTHORS", "PKG_DESCRIPTION", "PKG_HOMEPAGE", "PKG_LICENSE", "PKG_NAME", "PKG_REPOSITORY", "PKG_VERSION", "PKG_VERSION_MAJOR", "PKG_VERSION_MINOR", "PKG_VERSION_PATCH", "PKG_VERSION_PRE", "PROFILE", "RUSTC", "RUSTC_VERSION", "RUSTDOC", "RUSTDOC_VERSION", "TARGET", ]); println!("builttestsuccess"); }"#, ); p.create_and_run(&[]); } #[test] #[cfg(feature = "chrono")] fn source_date_epoch() { let mut p = Project::new(); let built_root = get_built_root(); p.add_file( "Cargo.toml", format!( r#" [package] name = "testbox" version = "1.2.3-rc1" authors = ["Joe", "Bob", "Harry:Potter"] build = "build.rs" description = "xobtset" [dependencies] built = {{ path = "{built_root}", features=["chrono"] }} [build-dependencies] built = {{ path = "{built_root}", features=["chrono"] }}"#, built_root = built_root.display().to_string().escape_default() ), ) .add_file( "build.rs", r#" fn main() { built::write_built_file().unwrap(); }"#, ) .add_file( "src/main.rs", r#" mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } fn main() { assert_eq!(built::util::strptime(built_info::BUILT_TIME_UTC).to_rfc2822(), "Sat, 25 May 2024 12:15:59 +0000"); assert_eq!(built_info::NUM_JOBS, 1); println!("builttestsuccess"); }"#, ) .set_env("SOURCE_DATE_EPOCH", "1716639359"); p.create_and_run(&[]); } #[test] #[cfg(feature = "git2")] fn git_no_git() { // `root` isn't even a git-repo let mut p = Project::new(); p.bootstrap().add_file( "src/main.rs", r#" mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } fn main() { assert_eq!(built_info::GIT_DIRTY, None); println!("builttestsuccess"); } "#, ); p.create_and_run(&[]); } #[test] #[cfg(feature = "git2")] fn clean_then_dirty_git() { let mut p = Project::new(); p.bootstrap().add_file( "src/main.rs", r#" mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } fn main() { assert_eq!(built_info::GIT_DIRTY, Some(false)); println!("builttestsuccess"); } "#, ); let repo = p.init_git(); let root = p.create().expect("Creating the project failed"); let sig = git2::Signature::now("foo", "bar").unwrap(); let mut idx = repo.index().unwrap(); for p in &["src/main.rs", "build.rs"] { idx.add_path(path::Path::new(p)).unwrap(); } idx.write().unwrap(); repo.commit( Some("HEAD"), &sig, &sig, "Testing testing 1 2 3", &repo.find_tree(idx.write_tree().unwrap()).unwrap(), &[], ) .unwrap(); Project::run( root.as_ref(), &[], std::iter::empty::<(ffi::OsString, ffi::OsString)>(), ); let mut f = std::fs::OpenOptions::new() .write(true) .truncate(true) .open(root.path().join("src/main.rs")) .unwrap(); f.write_all( r#" mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } fn main() { assert_eq!(built_info::GIT_DIRTY, Some(true)); assert!(built_info::GIT_COMMIT_HASH.is_some()); assert!(built_info::GIT_COMMIT_HASH_SHORT.is_some()); assert!(built_info::GIT_COMMIT_HASH.unwrap().starts_with(built_info::GIT_COMMIT_HASH_SHORT.unwrap())); println!("builttestsuccess"); } "# .as_bytes(), ) .unwrap(); Project::run( root.as_ref(), &[], std::iter::empty::<(ffi::OsString, ffi::OsString)>(), ); } #[test] #[cfg(feature = "git2")] fn empty_git() { // Issue #7, git can be there and still fail let mut p = Project::new(); p.bootstrap().add_file( "src/main.rs", r#" mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } fn main() { println!("builttestsuccess"); } "#, ); p.init_git(); p.create_and_run(&[]); } #[cfg(target_os = "windows")] #[test] fn absolute_paths() { // Issue #35. Usually binaries we refer to are simply executables names but sometimes they are // absolute paths, containing backslashes, and everything gets sad on this devilish platform. let mut p = Project::new(); p.bootstrap().add_file( "src/main.rs", r#" mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } fn main() { println!("builttestsuccess"); } "#, ); let rustc_exe_buf = String::from_utf8( process::Command::new("where") .arg("rustc") .output() .expect("Unable to locate absolute path to rustc using `where`") .stdout, ) .unwrap(); let rustc_exe = rustc_exe_buf.split("\r\n").next().unwrap(); // There should at least be `C:\` assert!(rustc_exe.contains('\\')); let root = p.create().expect("Creating the project failed"); let cargo_result = process::Command::new("cargo") .current_dir(&root) .arg("run") .env("RUSTC", &rustc_exe) .output() .expect("cargo failed"); if !cargo_result.status.success() { panic!( "cargo failed with {}", String::from_utf8_lossy(&cargo_result.stderr) ); } }