shadow-rs-1.1.1/.cargo_vcs_info.json0000644000000001360000000000100127430ustar { "git": { "sha1": "c413cc88974fb524be03f193fd4267270fae3c75" }, "path_in_vcs": "" }shadow-rs-1.1.1/.github/dependabot.yml000064400000000000000000000005431046102023000157250ustar 00000000000000version: 2 updates: - package-ecosystem: cargo directory: "/" schedule: interval: daily open-pull-requests-limit: 10 labels: - "\U0001F4E6 dependencies" - package-ecosystem: github-actions directory: "/" schedule: interval: daily open-pull-requests-limit: 10 labels: - "\U0001F4E6 dependencies" shadow-rs-1.1.1/.github/workflows/audit.yml000064400000000000000000000013031046102023000167560ustar 00000000000000name: Security audit on: pull_request: paths: # Run if workflow changes - '.github/workflows/audit.yml' # Run on changed dependencies - '**/Cargo.toml' - '**/Cargo.lock' # Run if the configuration file changes - '**/audit.toml' push: paths: # Run if workflow changes - '.github/workflows/audit.yml' # Run on changed dependencies - '**/Cargo.toml' - '**/Cargo.lock' # Run if the configuration file changes - '**/audit.toml' jobs: security_audit: runs-on: ubuntu-latest steps: - name: Set up Rust uses: actions/checkout@v4 - name: Run audit uses: actions-rust-lang/audit@v1shadow-rs-1.1.1/.github/workflows/check.yml000064400000000000000000000072761046102023000167440ustar 00000000000000name: check on: push: branches: - "*" tags: - "*" pull_request: branches: - "*" jobs: build: strategy: matrix: os: - ubuntu-latest - macos-latest - windows-latest runs-on: ${{ matrix.os }} steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - uses: actions-rs/toolchain@v1 with: toolchain: stable override: true components: clippy, rustfmt - name: Check format run: cargo fmt --all -- --check - name: Check fix run: cargo fix && cargo fix - name: Check with clippy run: cargo clippy --all-targets --all-features -- -D warnings - name: Build Release run: cargo build --release - name: Run tests run: cargo test # Run examples with debug - name: Run examples with debug run: cargo run --example builtin_fn # example_shadow check - name: Check example_shadow run: | cargo fmt --all -- --check cargo clippy --all-targets --all-features -- -D warnings cargo run working-directory: ./example_shadow # example_shadow_hook check - name: Check example_shadow_hook run: | cargo fmt --all -- --check cargo clippy --all-targets --all-features -- -D warnings cargo run working-directory: ./example_shadow_hook # example_wasm check - uses: jetli/wasm-pack-action@v0.4.0 with: version: latest - name: Setup wasm-pack run: | cargo b wasm-pack build --target bundler wasm-pack build --target web working-directory: ./example_wasm - uses: dtolnay/rust-toolchain@v1 with: target: riscv32imc-unknown-none-elf toolchain: stable components: rust-src - name: Run no_std example run: | cargo fmt --all -- --check cargo clippy --release -- -D warnings cargo b --release working-directory: ./example_no_std # build on nightly - uses: actions-rs/toolchain@v1 with: toolchain: nightly override: true components: clippy - name: Build on nightly run: | cargo build --release cargo +nightly clippy --all-features -- -D warnings -A clippy::literal_string_with_formatting_args test: strategy: matrix: rust: [ stable, beta, nightly ] runs-on: ubuntu-latest needs: [ build ] steps: - name: Setup Rust uses: hecrj/setup-rust-action@v2 with: rust-version: ${{ matrix.rust }} - name: Install Tarpaulin uses: actions-rs/install@v0.1 with: crate: cargo-tarpaulin version: 0.31.5 use-tool-cache: true - name: Checkout uses: actions/checkout@v4 - name: Test run: | cargo test --all-features cargo test --examples - name: Coverage if: matrix.rust == 'stable' run: cargo tarpaulin -o Lcov --output-dir ./coverage - name: Coveralls if: matrix.rust == 'stable' continue-on-error: true uses: coverallsapp/github-action@master with: github-token: ${{ secrets.GITHUB_TOKEN }} publish-crate: if: startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest needs: [ test ] steps: - name: Set up Rust uses: hecrj/setup-rust-action@v2 - uses: actions/checkout@v4 - name: Publish shell: bash run: | cargo publish --token ${{ secrets.CRATES_GITHUB_TOKEN }} shadow-rs-1.1.1/.gitignore000064400000000000000000000005211046102023000135210ustar 00000000000000# Generated by Cargo # will have compiled files and executables /target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk .idea /shadow.rs shadow-rs-1.1.1/Cargo.lock0000644000000556600000000000100107320ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[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 = "bitflags" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bumpalo" version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "camino" version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" dependencies = [ "serde", ] [[package]] name = "cargo-platform" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] [[package]] name = "cargo_metadata" version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" dependencies = [ "camino", "cargo-platform", "semver", "serde", "serde_json", "thiserror", ] [[package]] name = "cc" version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" 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 = "const_format" version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "deranged" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] [[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 = "document-features" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" dependencies = [ "litrs", ] [[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 = "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 = "iana-time-zone" version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" [[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 = "is_debug" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fe266d2e243c931d8190177f20bf7f24eed45e96f39e87dc49a27b32d12d407" [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "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.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[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 = "litemap" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "litrs" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "log" version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num_threads" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] [[package]] name = "once_cell" version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "proc-macro2" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 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 = "rustversion" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[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_json" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "shadow-rs" version = "1.1.1" dependencies = [ "cargo_metadata", "const_format", "document-features", "git2", "is_debug", "serde_json", "time", "tzdb", "winnow", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "smallvec" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[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.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "synstructure" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "thiserror" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "time" version = "0.3.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d9c75b47bdff86fa3334a3db91356b8d7d86a9b839dab7d0bdc5c3d3a077618" dependencies = [ "deranged", "itoa", "libc", "num-conv", "num_threads", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29aa485584182073ed57fd5004aa09c371f021325014694e432313345865fd04" dependencies = [ "num-conv", "time-core", ] [[package]] name = "tinystr" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "tz-rs" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1450bf2b99397e72070e7935c89facaa80092ac812502200375f1f7d33c71a1" [[package]] name = "tzdb" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0be2ea5956f295449f47c0b825c5e109022ff1a6a53bb4f77682a87c2341fbf5" dependencies = [ "iana-time-zone", "tz-rs", "tzdb_data", ] [[package]] name = "tzdb_data" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0604b35c1f390a774fdb138cac75a99981078895d24bcab175987440bbff803b" dependencies = [ "tz-rs", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[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 = "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.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 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.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] [[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", ] shadow-rs-1.1.1/Cargo.toml0000644000000044160000000000100107460ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "shadow-rs" version = "1.1.1" authors = ["baoyachi "] build = false exclude = [ "shadow-rs.png", "build_module.png", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "A build-time information stored in your rust project" homepage = "https://github.com/baoyachi/shadow-rs" documentation = "https://docs.rs/shadow-rs" readme = "README.md" keywords = [ "cargo", "build-script", "build", "shadow", "compile", ] categories = [ "development-tools", "development-tools::build-utils", ] license = "MIT AND Apache-2.0" repository = "https://github.com/baoyachi/shadow-rs" [package.metadata.docs.rs] all-features = true [features] build = [ "time", "tzdb", "is_debug/std", "std", ] default = [ "git2", "tzdb", "build", ] metadata = [ "cargo_metadata", "serde_json", ] no_std = [] std = [] [lib] name = "shadow_rs" path = "src/lib.rs" [[example]] name = "builtin_fn" path = "examples/builtin_fn.rs" [dependencies.cargo_metadata] version = "0.19.1" optional = true default-features = false [dependencies.const_format] version = "0.2.22" default-features = false [dependencies.document-features] version = "0.2" optional = true [dependencies.git2] version = "0.20.0" optional = true default-features = false [dependencies.is_debug] version = "1.1.0" default-features = false [dependencies.serde_json] version = "1" optional = true default-features = false [dependencies.time] version = "0.3.36" features = [ "formatting", "local-offset", "parsing", ] optional = true default-features = false [dependencies.tzdb] version = "0.7.2" features = [ "local", "now", ] optional = true default-features = false [dev-dependencies.winnow] version = "0.7" shadow-rs-1.1.1/Cargo.toml.orig000064400000000000000000000032371046102023000144270ustar 00000000000000[package] name = "shadow-rs" version = "1.1.1" authors = ["baoyachi "] edition = "2021" description = "A build-time information stored in your rust project" keywords = ["cargo", "build-script", "build", "shadow", "compile"] readme = "README.md" categories = ["development-tools", "development-tools::build-utils"] repository = "https://github.com/baoyachi/shadow-rs" documentation = "https://docs.rs/shadow-rs" homepage = "https://github.com/baoyachi/shadow-rs" license = "MIT AND Apache-2.0" exclude = ["shadow-rs.png", "build_module.png"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [package.metadata.docs.rs] all-features = true [dependencies] is_debug = { version = "1.1.0", default-features = false } const_format = { version = "0.2.22", default-features = false } time = { version = "0.3.36", features = ["formatting", "local-offset", "parsing"], default-features = false, optional = true } #! Optional Dependencies: ## Use `libgit2` as a backend for git operations git2 = { version = "0.20.0", default-features = false, optional = true } ## Better support for querying the local system time tzdb = { version = "0.7.2", optional = true, default-features = false, features = ["local", "now"] } document-features = { version = "0.2", optional = true } cargo_metadata = { version = "0.19.1", optional = true, default-features = false } serde_json = { version = "1", default-features = false, optional = true } [features] default = ["git2", "tzdb", "build"] metadata = ["cargo_metadata", "serde_json"] std = [] no_std = [] build = ["time", "tzdb", "is_debug/std", "std"] [dev-dependencies] winnow = "0.7" shadow-rs-1.1.1/LICENSE000064400000000000000000000020511046102023000125360ustar 00000000000000MIT License Copyright (c) 2020 baoyachi 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. shadow-rs-1.1.1/README.md000064400000000000000000000273651046102023000130270ustar 00000000000000[`shadow-rs`][docsrs]: Build-time information stored in your Rust project (binary, lib, cdylib, dylib,wasm). ========================================

shadow-rs build tool

[docsrs]: https://docs.rs/shadow-rs [![GitHub Actions](https://github.com/baoyachi/shadow-rs/workflows/check/badge.svg)](https://github.com/baoyachi/shadow-rs/actions?query=workflow%3Acheck) [![Crates.io](https://img.shields.io/crates/v/shadow-rs.svg)](https://crates.io/crates/shadow-rs) [![Docs.rs](https://docs.rs/shadow-rs/badge.svg)](https://docs.rs/shadow-rs) [![Download](https://img.shields.io/crates/d/shadow-rs)](https://crates.io/crates/shadow-rs) [![DepStatus](https://deps.rs/repo/github/baoyachi/shadow-rs/status.svg)](https://deps.rs/repo/github/baoyachi/shadow-rs) [![Gitter](https://badges.gitter.im/shadow-rs/community.svg)](https://gitter.im/shadow-rs/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Coverage Status](https://coveralls.io/repos/github/baoyachi/shadow-rs/badge.svg)](https://coveralls.io/github/baoyachi/shadow-rs) `shadow-rs` allows you to access properties of the build process and environment at runtime, including: * `Cargo.toml` information, such as the project version * Dependency information * Git information, such as the commit that produced the build artifact * What version of the Rust toolchain was used in compilation * The build variant, e.g. `debug` or `release` * ... And more! Strongly recommend using **shadow-rs** on the [LSP](https://microsoft.github.io/language-server-protocol/) service developed in Rust. You can use this crate to programmatically check where a binary came from and how it was built. Currently, integration into **wasm**,**no_std** is also supported. For detailed settings, please refer to the link: * [example_wasm](https://github.com/baoyachi/shadow-rs/tree/master/example_wasm) * [example_no_std](https://github.com/baoyachi/shadow-rs/tree/master/example_no_std) ![build_module](./build_module.png) # BuildPattern The BuildPattern enum defines strategies for triggering package rebuilding. **Default mode is `Lazy`**. * `Lazy`: The lazy mode. In this mode, if the current Rust environment is set to `debug`, the rebuild package will not run every time the build script is triggered. If the environment is set to `release`, it behaves the same as the `RealTime` mode. * `RealTime`: The real-time mode. It will always trigger rebuilding a package upon any change, regardless of whether the Rust environment is set to `debug` or `release`. * `Custom`: The custom build mode, an enhanced version of `RealTime` mode, allowing for user-defined conditions to trigger rebuilding a package. # Examples * Check out the [example_shadow](https://github.com/baoyachi/shadow-rs/tree/master/example_shadow) for a simple demonstration of how `shadow-rs` might be used to provide build-time information at run-time. * Check out the [example_shadow_hook](https://github.com/baoyachi/shadow-rs/tree/master/example_shadow_hook) for a demonstration of how custom hooks can be used to add extra information to `shadow-rs`'s output. * Check out the [`builtin_fn` example](https://github.com/baoyachi/shadow-rs/tree/master/examples/builtin_fn.rs) for a simple demonstration of the built-in functions that `shadow-rs` provides. # Setup ### 1) Modify `Cargo.toml` fields Modify your `Cargo.toml` like so: ```toml [package] build = "build.rs" [dependencies] shadow-rs = { version = "{latest version}", default-features = false } [build-dependencies] shadow-rs = "{latest version}" ``` > About `build = "build.rs"`,this is an optional addition where, by default, build points to the build.rs file. It is > recommended to use it as such. However, if your build script file is not named build.rs, please manually specify it. > For > example: `build = "gen.rs"`. ### 2) Create `build.rs` file Now in the root of your project (same directory as `Cargo.toml`) add a file `build.rs`: ```rust fn main() { ShadowBuilder::builder().build().unwrap(); } ``` If you want to exclude some build constants, you can use [`new_deny`] instead of [`new`]. ### 3) Integrate Shadow In your main Rust file (usually `main.rs` or `lib.rs`), add this: ```rust use shadow_rs::shadow; shadow!(build); ``` The `shadow!` macro uses the given identifier to create a module with that name. ### 4) Use Shadow Constants You can now use the module defined with `shadow!` to access build-time information. ```rust fn main() { println!("debug:{}", shadow_rs::is_debug()); // check if this is a debug build. e.g 'true/false' println!("branch:{}", shadow_rs::branch()); // get current project branch. e.g 'master/develop' println!("tag:{}", shadow_rs::tag()); // get current project tag. e.g 'v1.3.5' println!("git_clean:{}", shadow_rs::git_clean()); // get current project clean. e.g 'true/false' println!("git_status_file:{}", shadow_rs::git_status_file()); // get current project statue file. e.g ' * examples/builtin_fn.rs (dirty)' println!("{}", build::VERSION); //print version const println!("{}", build::CLAP_LONG_VERSION); //print CLAP_LONG_VERSION const println!("{}", build::BRANCH); //master println!("{}", build::SHORT_COMMIT); //8405e28e println!("{}", build::COMMIT_HASH); //8405e28e64080a09525a6cf1b07c22fcaf71a5c5 println!("{}", build::COMMIT_DATE); //2021-08-04 12:34:03 +00:00 println!("{}", build::COMMIT_AUTHOR); //baoyachi println!("{}", build::COMMIT_EMAIL); //xxx@gmail.com println!("{}", build::BUILD_OS); //macos-x86_64 println!("{}", build::RUST_VERSION); //rustc 1.45.0 (5c1f21c3b 2020-07-13) println!("{}", build::RUST_CHANNEL); //stable-x86_64-apple-darwin (default) println!("{}", build::CARGO_VERSION); //cargo 1.45.0 (744bd1fbb 2020-06-15) println!("{}", build::PKG_VERSION); //0.3.13 println!("{}", build::CARGO_TREE); //like command:cargo tree println!("{}", build::CARGO_MANIFEST_DIR); // /User/baoyachi/shadow-rs/ | println!("{}", build::PROJECT_NAME); //shadow-rs println!("{}", build::BUILD_TIME); //2020-08-16 14:50:25 println!("{}", build::BUILD_RUST_CHANNEL); //debug println!("{}", build::GIT_CLEAN); //false println!("{}", build::GIT_STATUS_FILE); //* src/lib.rs (dirty) } ``` ## Setup for `no_std` Add this to your Cargo.toml ```toml [dependencies] shadow-rs = { version = "{latest version}", default-features = false } [build-dependencies] shadow-rs = { version = "{latest version}", features = ["no_std"] } ``` #### Reproducibility This tool includes the current time in the binary which would normally make it non-reproducible. However, it respects the [`SOURCE_DATE_EPOCH` variable](https://reproducible-builds.org/docs/source-date-epoch/) - if set to a Unix timestamp it will override the value of build time. ## Clap You can also use `shadow-rs` to provide information to command-line interface crates such as [`clap`](https://docs.rs/clap/latest/clap/). An example of this can be found in [`example_shadow`](https://github.com/baoyachi/shadow-rs/blob/master/example_shadow/src/main.rs). ## List of Constants and Functions #### Functions | Function | Description | |---------------------|---------------------------------------------------------------------| | `is_debug()` | `true` if this is a build with debug assertions. | | `branch()` | Git branch at build time. | | `tag()` | Current Git tag at build time. | | `git_clean()` | Whether Git working tree was clean at build time. | | `git_status_file()` | `git status`-like output, e.g. ` * examples/builtin_fn.rs (dirty)` | #### Constants | Constant | Example | |--------------------|------------------------------------------------------------------------------------------------------| | VERSION | 3.4.5 | | CLAP_LONG_VERSION | (A multi-line string containing branch, commit hash, build time, Rust version and toolchain channel) | | BRANCH | master | | TAG | v1.0.0 | | SHORT_COMMIT | 8405e28e | | COMMIT_HASH | 8405e28e64080a09525a6cf1b07c22fcaf71a5c5 | | COMMIT_DATE | 2021-08-04 12:34:03 +00:00 | | COMMIT_DATE_2822 | Thu, 24 Jun 2021 21:33:59 +0800 | | COMMIT_DATE_3339 | 2021-06-24T21:33:59.972494+08:00 | | COMMIT_AUTHOR | baoyachi | | COMMIT_EMAIL | xxx@gmail.com | | BUILD_OS | macos-x86_64 | | BUILD_TARGET | x86_64-apple-darwin | | BUILD_TARGET_ARCH | x86_64 | | RUST_VERSION | rustc 1.45.0 (5c1f21c3b 2020-07-13) | | RUST_CHANNEL | stable-x86_64-apple-darwin (default) | | CARGO_VERSION | cargo 1.45.0 (744bd1fbb 2020-06-15) | | PKG_VERSION | 0.3.13 | | CARGO_TREE | (Output of `cargo tree`) | | CARGO_MANIFEST_DIR | /User/baoyachi/shadow-rs/ | | PROJECT_NAME | shadow-rs | | BUILD_TIME | 2021-06-24 21:33:59 | | BUILD_TIME_2822 | Thu, 24 Jun 2021 21:33:59 +0800 | | BUILD_TIME_3339 | 2021-06-24T15:53:55+08:00 | | BUILD_RUST_CHANNEL | release | | GIT_CLEAN | true | | GIT_STATUS_FILE | * src/lib.rs (dirty) | If you have any questions, please create an [issue](https://github.com/baoyachi/shadow-rs/issues/new) so we may improve the documentation where it may be unclear. ## People using shadow-rs If you are using `shadow-rs`, please tell me! Or instead, consider making a note here: [Shadow Users Collection](https://github.com/baoyachi/shadow-rs/issues/19). shadow-rs-1.1.1/examples/builtin_fn.rs000064400000000000000000000011001046102023000160400ustar 00000000000000// cargo run --example builtin_fn fn main() { println!("debug:{}", shadow_rs::is_debug()); // check if this is a debug build. e.g 'true/false' println!("branch:{}", shadow_rs::branch()); // get current project branch. e.g 'master/develop' println!("tag:{}", shadow_rs::tag()); // get current project tag. e.g 'v1.3.5' println!("git_clean:{}", shadow_rs::git_clean()); // get current project clean. e.g 'true/false' println!("git_status_file:{}", shadow_rs::git_status_file()); // get current project statue file. e.g ' * examples/builtin_fn.rs (dirty)' } shadow-rs-1.1.1/src/build.rs000064400000000000000000000253741046102023000140020ustar 00000000000000use crate::date_time::DEFINE_SOURCE_DATE_EPOCH; use crate::hook::HookExt; use crate::shadow::DEFINE_SHADOW_RS; use crate::{SdResult, Shadow, CARGO_METADATA}; use is_debug::is_debug; use std::collections::BTreeSet; use std::fmt::{Display, Formatter}; /// `shadow-rs` build constant identifiers. pub type ShadowConst = &'static str; /// Since [cargo metadata](https://crates.io/crates/cargo_metadata) details about workspace /// membership and resolved dependencies for the current package, storing this data can result in /// significantly larger crate sizes. As such, the CARGO_METADATA const is disabled by default. /// /// Should you choose to retain this information, you have the option to customize a deny_const /// object and override the `new_deny` method parameters accordingly. /// #[allow(clippy::all, clippy::pedantic, clippy::restriction, clippy::nursery)] pub fn default_deny() -> BTreeSet { BTreeSet::from([CARGO_METADATA]) } /// Serialized values for build constants. #[derive(Debug, Clone)] pub struct ConstVal { /// User-facing documentation for the build constant. pub desc: String, /// Serialized value of the build constant. pub v: String, /// Type of the build constant. pub t: ConstType, } impl ConstVal { pub fn new>(desc: S) -> ConstVal { // Creates a new `ConstVal` with an empty string as its value and `Str` as its type. ConstVal { desc: desc.into(), v: "".to_string(), t: ConstType::Str, } } pub fn new_bool>(desc: S) -> ConstVal { // Creates a new `ConstVal` with "true" as its value and `Bool` as its type. ConstVal { desc: desc.into(), v: "true".to_string(), t: ConstType::Bool, } } pub fn new_slice>(desc: S) -> ConstVal { // Creates a new `ConstVal` with an empty string as its value and `Slice` as its type. ConstVal { desc: desc.into(), v: "".to_string(), t: ConstType::Slice, } } } /// Supported types of build constants. #[derive(Debug, Clone)] pub enum ConstType { /// [`&str`](`str`). Str, /// [`bool`]. Bool, /// [`&[u8]`]. Slice, /// [`usize`]. Usize, } impl Display for ConstType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { ConstType::Str => write!(f, "&str"), ConstType::Bool => write!(f, "bool"), ConstType::Slice => write!(f, "&[u8]"), ConstType::Usize => write!(f, "usize"), } } } /// The BuildPattern enum defines strategies for triggering package rebuilding. /// /// Default mode is `Lazy`. /// /// * `Lazy`: The lazy mode. In this mode, if the current Rust environment is set to `debug`, /// the rebuild package will not run every time the build script is triggered. /// If the environment is set to `release`, it behaves the same as the `RealTime` mode. /// * `RealTime`: The real-time mode. It will always trigger rebuilding a package upon any change, /// regardless of whether the Rust environment is set to `debug` or `release`. /// * `Custom`: The custom build mode, an enhanced version of `RealTime` mode, allowing for user-defined conditions /// to trigger rebuilding a package. /// #[derive(Debug, Default, Clone)] pub enum BuildPattern { #[default] Lazy, RealTime, Custom { /// A list of paths that, if changed, will trigger a rebuild. /// See if_path_changed: Vec, /// A list of environment variables that, if changed, will trigger a rebuild. /// See if_env_changed: Vec, }, } impl BuildPattern { /// Determines when Cargo should rerun the build script based on the configured pattern. /// /// # Arguments /// /// * `other_keys` - An iterator over additional keys that should trigger a rebuild if they change. /// * `out_dir` - The output directory where generated files are placed. pub(crate) fn rerun_if<'a>( &self, other_keys: impl Iterator, out_dir: &str, ) { match self { BuildPattern::Lazy => { if is_debug() { return; } } BuildPattern::RealTime => {} BuildPattern::Custom { if_path_changed, if_env_changed, } => { if_env_changed .iter() .for_each(|key| println!("cargo:rerun-if-env-changed={key}")); if_path_changed .iter() .for_each(|p| println!("cargo:rerun-if-changed={p}")); } } other_keys.for_each(|key| println!("cargo:rerun-if-env-changed={key}")); println!("cargo:rerun-if-env-changed={}", DEFINE_SOURCE_DATE_EPOCH); println!("cargo:rerun-if-changed={}/{}", out_dir, DEFINE_SHADOW_RS); } } /// A builder pattern structure to construct a `Shadow` instance. /// /// This struct allows for configuring various aspects of how shadow-rs will be built into your Rust project. /// It provides methods to set up hooks, specify build patterns, define paths, and deny certain build constants. /// /// # Fields /// /// * `hook`: An optional hook that can be used during the build process. Hooks implement the `HookExt` trait. /// * `build_pattern`: Determines the strategy for triggering package rebuilds (`Lazy`, `RealTime`, or `Custom`). /// * `deny_const`: A set of build constant identifiers that should not be included in the build. /// * `src_path`: The source path from which files are read for building. /// * `out_path`: The output path where generated files will be placed. /// pub struct ShadowBuilder<'a> { hook: Option>, build_pattern: BuildPattern, deny_const: BTreeSet, src_path: Option, out_path: Option, } impl<'a> ShadowBuilder<'a> { /// Creates a new `ShadowBuilder` with default settings. /// /// Initializes the builder with the following defaults: /// - `hook`: None /// - `build_pattern`: `BuildPattern::Lazy` /// - `deny_const`: Uses the result from `default_deny()` /// - `src_path`: Attempts to get the manifest directory using `CARGO_MANIFEST_DIR` environment variable. /// - `out_path`: Attempts to get the output directory using `OUT_DIR` environment variable. /// /// # Returns /// /// A new instance of `ShadowBuilder`. pub fn builder() -> Self { let default_src_path = std::env::var("CARGO_MANIFEST_DIR").ok(); let default_out_path = std::env::var("OUT_DIR").ok(); Self { hook: None, build_pattern: BuildPattern::default(), deny_const: default_deny(), src_path: default_src_path, out_path: default_out_path, } } /// Sets the build hook for this builder. /// /// # Arguments /// /// * `hook` - An object implementing the `HookExt` trait that defines custom behavior for the build process. /// /// # Returns /// /// A new `ShadowBuilder` instance with the specified hook applied. pub fn hook(mut self, hook: impl HookExt + 'a) -> Self { self.hook = Some(Box::new(hook)); self } /// Sets the source path for this builder. /// /// # Arguments /// /// * `src_path` - A string reference that specifies the source directory for the build. /// /// # Returns /// /// A new `ShadowBuilder` instance with the specified source path. pub fn src_path>(mut self, src_path: P) -> Self { self.src_path = Some(src_path.as_ref().to_owned()); self } /// Sets the output path for this builder. /// /// # Arguments /// /// * `out_path` - A string reference that specifies the output directory for the build. /// /// # Returns /// /// A new `ShadowBuilder` instance with the specified output path. pub fn out_path>(mut self, out_path: P) -> Self { self.out_path = Some(out_path.as_ref().to_owned()); self } /// Sets the build pattern for this builder. /// /// # Arguments /// /// * `pattern` - A `BuildPattern` that determines when the package should be rebuilt. /// /// # Returns /// /// A new `ShadowBuilder` instance with the specified build pattern. pub fn build_pattern(mut self, pattern: BuildPattern) -> Self { self.build_pattern = pattern; self } /// Sets the denied constants for this builder. /// /// # Arguments /// /// * `deny_const` - A set of `ShadowConst` that should be excluded from the build. /// /// # Returns /// /// A new `ShadowBuilder` instance with the specified denied constants. pub fn deny_const(mut self, deny_const: BTreeSet) -> Self { self.deny_const = deny_const; self } /// Builds a `Shadow` instance based on the current configuration. /// /// # Returns /// /// A `SdResult` that represents the outcome of the build operation. pub fn build(self) -> SdResult { Shadow::build_inner(self) } /// Gets the source path if it has been set. /// /// # Returns /// /// A `SdResult<&String>` containing the source path or an error if the path is missing. pub fn get_src_path(&self) -> SdResult<&String> { let src_path = self.src_path.as_ref().ok_or("missing `src_path`")?; Ok(src_path) } /// Gets the output path if it has been set. /// /// # Returns /// /// A `SdResult<&String>` containing the output path or an error if the path is missing. pub fn get_out_path(&self) -> SdResult<&String> { let out_path = self.out_path.as_ref().ok_or("missing `out_path`")?; Ok(out_path) } /// Gets the build pattern. /// /// # Returns /// /// A reference to the `BuildPattern` currently configured for this builder. pub fn get_build_pattern(&self) -> &BuildPattern { &self.build_pattern } /// Gets the denied constants. /// /// # Returns /// /// A reference to the set of `ShadowConst` that are denied for this build. pub fn get_deny_const(&self) -> &BTreeSet { &self.deny_const } /// Gets the build hook if it has been set. /// /// # Returns /// /// An option containing a reference to the hook if one is present. pub fn get_hook(&'a self) -> Option<&'a (dyn HookExt + 'a)> { self.hook.as_deref() } } shadow-rs-1.1.1/src/ci.rs000064400000000000000000000011331046102023000132610ustar 00000000000000use std::fmt::{Display, Formatter}; /// [`CiType`] holds the types of CI environment that `shadow-rs` can detect. #[derive(Debug)] pub enum CiType { Github, Gitlab, // TODO: Recognize other CI types, especially Travis and Jenkins None, } impl Default for CiType { fn default() -> Self { Self::None } } impl Display for CiType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { CiType::Github => write!(f, "github"), CiType::Gitlab => write!(f, "gitlab"), _ => write!(f, "none"), } } } shadow-rs-1.1.1/src/date_time.rs000064400000000000000000000162111046102023000146240ustar 00000000000000use crate::{Format, SdResult, ShadowError}; use std::error::Error; use time::format_description::well_known::{Rfc2822, Rfc3339}; #[cfg(feature = "tzdb")] use time::UtcOffset; use time::{format_description, OffsetDateTime}; pub enum DateTime { Local(OffsetDateTime), Utc(OffsetDateTime), } pub(crate) const DEFINE_SOURCE_DATE_EPOCH: &str = "SOURCE_DATE_EPOCH"; pub fn now_date_time() -> DateTime { // Enable reproducibility for uses of `now_date_time` by respecting the // `SOURCE_DATE_EPOCH` env variable. // // https://reproducible-builds.org/docs/source-date-epoch/ match std::env::var_os(DEFINE_SOURCE_DATE_EPOCH) { None => DateTime::now(), Some(timestamp) => { let epoch = timestamp .into_string() .unwrap_or_else(|_| { panic!("Input {} could not be parsed", DEFINE_SOURCE_DATE_EPOCH) }) .parse::() .unwrap_or_else(|_| { panic!( "Input {} could not be cast to a number", DEFINE_SOURCE_DATE_EPOCH ) }); DateTime::Utc(OffsetDateTime::from_unix_timestamp(epoch).unwrap()) } } } impl Default for DateTime { fn default() -> Self { Self::now() } } impl DateTime { pub fn now() -> Self { Self::local_now().unwrap_or_else(|_| DateTime::Utc(OffsetDateTime::now_utc())) } pub fn offset_datetime() -> OffsetDateTime { let date_time = Self::now(); match date_time { DateTime::Local(time) | DateTime::Utc(time) => time, } } #[cfg(not(feature = "tzdb"))] pub fn local_now() -> Result> { // Warning: This attempts to create a new OffsetDateTime with the current date and time in the local offset, which may fail. // Currently, it always fails on MacOS. // This issue does not exist with the "tzdb" feature (see below), which should be used instead. OffsetDateTime::now_local() .map(DateTime::Local) .map_err(|e| e.into()) } #[cfg(feature = "tzdb")] pub fn local_now() -> Result> { let local_time = tzdb::now::local()?; let time_zone_offset = UtcOffset::from_whole_seconds(local_time.local_time_type().ut_offset())?; let local_date_time = OffsetDateTime::from_unix_timestamp(local_time.unix_time())? .to_offset(time_zone_offset); Ok(DateTime::Local(local_date_time)) } pub fn timestamp_2_utc(time_stamp: i64) -> SdResult { let time = OffsetDateTime::from_unix_timestamp(time_stamp).map_err(ShadowError::new)?; Ok(DateTime::Utc(time)) } pub fn to_rfc2822(&self) -> String { match self { DateTime::Local(dt) | DateTime::Utc(dt) => dt.format(&Rfc2822).unwrap_or_default(), } } pub fn to_rfc3339(&self) -> String { match self { DateTime::Local(dt) | DateTime::Utc(dt) => dt.format(&Rfc3339).unwrap_or_default(), } } } impl Format for DateTime { fn human_format(&self) -> String { match self { DateTime::Local(dt) | DateTime::Utc(dt) => dt.human_format(), } } } impl Format for OffsetDateTime { fn human_format(&self) -> String { let fmt = format_description::parse( "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \ sign:mandatory]:[offset_minute]", ) .unwrap(); self.format(&fmt).unwrap() } } #[cfg(test)] mod tests { use super::*; use human_format_validate::parse_human_format; mod human_format_validate { use std::num::{NonZeroU32, NonZeroU8}; use winnow::ascii::{digit1, space1}; use winnow::error::{ContextError, ParseError}; use winnow::token::{literal, take}; use winnow::{ModalResult, Parser}; fn u8_len2(input: &mut &str) -> ModalResult { take(2_usize).try_map(str::parse).parse_next(input) } fn non_zero_u8_len2(input: &mut &str) -> ModalResult { take(2_usize) .try_map(str::parse) .verify(|x| *x <= unsafe { NonZeroU8::new_unchecked(LIMIT) }) .parse_next(input) } // fn non_zero_u32(input: &mut &str) -> ModalResult { digit1.try_map(str::parse).parse_next(input) } // 2022-07-14 00:40:05 +08:00 pub(crate) fn parse_human_format( input: &str, ) -> Result<(), ParseError<&str, ContextError>> { ( non_zero_u32, literal('-'), non_zero_u8_len2::<12>, literal('-'), non_zero_u8_len2::<31>, space1, u8_len2, literal(':'), u8_len2, literal(':'), u8_len2, space1, literal('+'), u8_len2, literal(':'), u8_len2, ) .parse(input)?; Ok(()) } #[test] fn test_parse() { assert!(parse_human_format("2022-07-14 00:40:05 +08:00").is_ok()); assert!(parse_human_format("2022-12-14 00:40:05 +08:00").is_ok()); assert!(parse_human_format("2022-13-14 00:40:05 +08:00").is_err()); assert!(parse_human_format("2022-12-31 00:40:05 +08:00").is_ok()); assert!(parse_human_format("2022-12-32 00:40:05 +08:00").is_err()); assert!(parse_human_format("2022-07-14 00:40:05 +08:0").is_err()); assert!(parse_human_format("2022-07-14 00:40:05 -08:0").is_err()); assert!(parse_human_format("2022-07-00 00:40:05 +08:00").is_err()); assert!(parse_human_format("2022-00-01 00:40:05 +08:00").is_err()); assert!(parse_human_format("2022-00-01 00:40:05 08:00").is_err()); assert!(parse_human_format("2022-00-01 00:40:05+08:00").is_err()); assert!(parse_human_format("20221-00-01 00:40:05+08:00").is_err()); assert!(parse_human_format("20221-01-01 00:40:05 +08:00").is_ok()); } } #[test] fn test_source_date_epoch() { std::env::set_var(DEFINE_SOURCE_DATE_EPOCH, "1628080443"); let time = now_date_time(); assert_eq!(time.human_format(), "2021-08-04 12:34:03 +00:00"); } #[test] fn test_local_now_human_format() { let time = DateTime::local_now().unwrap().human_format(); #[cfg(unix)] assert!(!std::fs::read("/etc/localtime").unwrap().is_empty()); assert!(parse_human_format(&time).is_ok()); println!("local now:{time}"); // 2022-07-14 00:40:05 +08:00 assert_eq!(time.len(), 26); } #[test] fn test_timestamp_2_utc() { let time = DateTime::timestamp_2_utc(1628080443).unwrap(); assert_eq!(time.to_rfc2822(), "Wed, 04 Aug 2021 12:34:03 +0000"); assert_eq!(time.to_rfc3339(), "2021-08-04T12:34:03Z"); assert_eq!(time.human_format(), "2021-08-04 12:34:03 +00:00"); } } shadow-rs-1.1.1/src/env.rs000064400000000000000000000425761046102023000134760ustar 00000000000000use crate::build::*; use crate::date_time::now_date_time; use crate::env::dep_source_replace::filter_cargo_tree; use crate::err::SdResult; use crate::{Format, Shadow}; use is_debug::build_channel; use std::collections::BTreeMap; use std::env; use std::env as std_env; use std::process::Command; #[derive(Default, Debug)] pub struct SystemEnv { map: BTreeMap, } /// Returns the contents of [`std::env::vars`] as an ordered map. pub(crate) fn get_std_env() -> BTreeMap { let mut env_map = BTreeMap::new(); for (k, v) in std_env::vars() { env_map.insert(k, v); } env_map } const BUILD_OS_DOC: &str = r#" Operating system and architecture on which the project was build. The format of this variable is always `os-arch`, where `os` is the operating system name as returned by [`std::env::consts::OS`], and `arch` is the computer architecture as returned by [`std::env::consts::ARCH`]."#; pub const BUILD_OS: ShadowConst = "BUILD_OS"; const RUST_VERSION_DOC: &str = r#" Rust version with which the project was built. The version always uses the canonical Rust version format, and is therefore identical to the output of the build toolchain's `rustc --version`."#; pub const RUST_VERSION: ShadowConst = "RUST_VERSION"; const RUST_CHANNEL_DOC: &str = r#" The [Rustup toolchain](https://rust-lang.github.io/rustup/concepts/toolchains.html) with which the project was built. Note that as per Rustup toolchain format, this variable may or may not contain host and date information, but it will always contain [channel](https://rust-lang.github.io/rustup/concepts/channels.html) information (stable, beta or nightly)."#; pub const RUST_CHANNEL: ShadowConst = "RUST_CHANNEL"; pub const CARGO_METADATA: ShadowConst = "CARGO_METADATA"; const CARGO_METADATA_DOC: ShadowConst = r#" The information about the workspace members and resolved dependencies of the current package. See the [cargo_metadata](https://crates.io/crates/cargo_metadata) crate for a Rust API for reading the metadata."#; const CARGO_VERSION_DOC: &str = r#" The cargo version which which the project was built, as output by `cargo --version`."#; pub const CARGO_VERSION: ShadowConst = "CARGO_VERSION"; const CARGO_TREE_DOC: &str = r#" The dependency tree of the project, as output by `cargo tree`. Note that this variable may contain local file system paths for path dependencies, and may therefore contain sensitive information and not be reproducible."#; pub const CARGO_TREE: ShadowConst = "CARGO_TREE"; const BUILD_TARGET_DOC: &str = r#" The [target](https://doc.rust-lang.org/rustc/targets/index.html) for this build. This is possibly distinct from the host target during build, in which case this project build was created via cross-compilation."#; pub const BUILD_TARGET: ShadowConst = "BUILD_TARGET"; const BUILD_TARGET_ARCH_DOC: &str = r#" The architecture of the target for this build. This is the "architecture" part of the [`BUILD_TARGET`] constant."#; pub const BUILD_TARGET_ARCH: ShadowConst = "BUILD_TARGET_ARCH"; const CARGO_MANIFEST_DIR_DOC: &str = r#" The directory of the Cargo.toml manifest file of the project during build. Note that this variable will contain a full local file system path, and will therefore contain sensitive information and not be reproducible."#; pub const CARGO_MANIFEST_DIR: ShadowConst = "CARGO_MANIFEST_DIR"; const PKG_VERSION_DOC: &str = r#" The project's full version string, as determined by the Cargo.toml manifest."#; pub const PKG_VERSION: ShadowConst = "PKG_VERSION"; const PKG_DESCRIPTION_DOC: &str = r#" The project's description, as determined by the Cargo.toml manifest."#; pub const PKG_DESCRIPTION: ShadowConst = "PKG_DESCRIPTION"; const PKG_VERSION_MAJOR_DOC: &str = r#" The project's semver major version, as determined by the Cargo.toml manifest."#; pub const PKG_VERSION_MAJOR: ShadowConst = "PKG_VERSION_MAJOR"; const PKG_VERSION_MINOR_DOC: &str = r#" The project's semver minor version, as determined by the Cargo.toml manifest."#; pub const PKG_VERSION_MINOR: ShadowConst = "PKG_VERSION_MINOR"; const PKG_VERSION_PATCH_DOC: &str = r#" The project's semver patch version, as determined by the Cargo.toml manifest."#; pub const PKG_VERSION_PATCH: ShadowConst = "PKG_VERSION_PATCH"; const PKG_VERSION_PRE_DOC: &str = r#" The project's semver pre-release version, as determined by the Cargo.toml manifest."#; pub const PKG_VERSION_PRE: ShadowConst = "PKG_VERSION_PRE"; impl SystemEnv { fn init(&mut self, shadow: &Shadow) -> SdResult<()> { let std_env = &shadow.std_env; let mut update_val = |c: ShadowConst, v: String| { if let Some(val) = self.map.get_mut(c) { val.v = v; } }; if let Some(v) = std_env.get("RUSTUP_TOOLCHAIN") { update_val(RUST_CHANNEL, v.to_string()); } if let Ok(out) = Command::new("rustc").arg("-V").output() { update_val( RUST_VERSION, String::from_utf8(out.stdout)?.trim().to_string(), ); } if let Ok(out) = Command::new("cargo").arg("-V").output() { update_val( CARGO_VERSION, String::from_utf8(out.stdout)?.trim().to_string(), ); } // If the build constant `CARGO_TREE` is not in the deny list, // See discussions and issues related to this functionality: // - https://github.com/baoyachi/shadow-rs/issues/184 // - https://github.com/baoyachi/shadow-rs/issues/135 // - https://github.com/rust-lang/cargo/issues/12195 // - https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#lockfile-path if !shadow.deny_contains(CARGO_TREE) { if let Ok(out) = Command::new("cargo").arg("tree").output() { let input = String::from_utf8(out.stdout)?; if let Some(index) = input.find('\n') { let lines = filter_cargo_tree( input.get(index..).unwrap_or_default().split('\n').collect(), ); update_val(CARGO_TREE, lines); } } } // If the build constant `CARGO_METADATA` is not in the deny list, // See discussions and issues related to this functionality: // - https://github.com/baoyachi/shadow-rs/issues/184 // - https://github.com/baoyachi/shadow-rs/issues/135 // - https://github.com/rust-lang/cargo/issues/12195 // - https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#lockfile-path if !shadow.deny_contains(CARGO_METADATA) { // Attempt to run the `cargo metadata --format-version 1` command. if let Ok(output) = Command::new("cargo") .args(["metadata", "--format-version", "1"]) .output() { // If successful, parse the output and update the value associated with `CARGO_METADATA`. update_val( CARGO_METADATA, String::from_utf8(output.stdout)?.trim().to_string(), ); } } if let Some(v) = std_env.get("TARGET") { update_val(BUILD_TARGET, v.to_string()); } if let Some(v) = std_env.get("CARGO_CFG_TARGET_ARCH") { update_val(BUILD_TARGET_ARCH, v.to_string()); } if let Some(v) = std_env.get("CARGO_PKG_VERSION") { update_val(PKG_VERSION, v.to_string()); } if let Some(v) = std_env.get("CARGO_PKG_DESCRIPTION") { update_val(PKG_DESCRIPTION, v.to_string()); } if let Some(v) = std_env.get("CARGO_PKG_VERSION_MAJOR") { update_val(PKG_VERSION_MAJOR, v.to_string()); } if let Some(v) = std_env.get("CARGO_PKG_VERSION_MINOR") { update_val(PKG_VERSION_MINOR, v.to_string()); } if let Some(v) = std_env.get("CARGO_PKG_VERSION_PATCH") { update_val(PKG_VERSION_PATCH, v.to_string()); } if let Some(v) = std_env.get("CARGO_PKG_VERSION_PRE") { update_val(PKG_VERSION_PRE, v.to_string()); } if let Some(v) = std_env.get("CARGO_MANIFEST_DIR") { update_val(CARGO_MANIFEST_DIR, v.to_string()); } Ok(()) } } mod dep_source_replace { use std::fs; fn path_exists(path: &str) -> bool { fs::metadata(path).is_ok() } const DEP_REPLACE_NONE: &str = ""; const DEP_REPLACE_PATH: &str = " (* path)"; const DEP_REPLACE_GIT: &str = " (* git)"; const DEP_REPLACE_REGISTRY: &str = " (* registry)"; /// filter cargo tree dependencies source /// /// Why do this? /// /// Sometimes, the private registry or private git url that our cargo relies on will carry this information /// with the cargo tree command output we use. In order to protect the privacy of dependence, we need to shield it. /// /// This can protect us from the security issues we rely on environmental information. /// /// I think it is very necessary.So we need to do fuzzy replacement of dependent output. /// /// for examples: /// /// - dep by git: shadow-rs = { git = "https://github.com/baoyachi/shadow-rs", branch="master" } /// - dep by registry: shadow-rs = { version = "0.5.23",registry="private-crates" } /// - dep by path: shadow-rs = { path = "/Users/baoyachi/shadow-rs" } /// /// before exec: cargo tree output by difference dependencies source: /// /// - git: └── shadow-rs v0.5.23 (https://github.com/baoyachi/shadow-rs?branch=master#eb712990) /// - registry: └── shadow-rs v0.5.23 (registry ssh://git@github.com/baoyachi/shadow-rs.git) /// - path: └── shadow-rs v0.5.23 ((/Users/baoyachi/shadow-rs)) /// /// after filter dependencies source /// /// - git: └── shadow-rs v0.5.23 (* git) /// - registry: └── shadow-rs v0.5.23 (* registry) /// - path: └── shadow-rs v0.5.23 (* path) /// pub fn filter_dep_source(input: &str) -> String { let (val, index) = if let Some(index) = input.find(" (/") { (DEP_REPLACE_PATH, index) } else if let Some(index) = input.find(" (registry ") { (DEP_REPLACE_REGISTRY, index) } else if let Some(index) = input.find(" (http") { (DEP_REPLACE_GIT, index) } else if let Some(index) = input.find(" (https") { (DEP_REPLACE_GIT, index) } else if let Some(index) = input.find(" (ssh") { (DEP_REPLACE_GIT, index) } else if let (Some(start), Some(end)) = (input.find(" ("), input.find(')')) { let path = input.get(start + 2..end).unwrap_or_default().trim(); if path_exists(path) { (DEP_REPLACE_PATH, start) } else { (DEP_REPLACE_NONE, input.len()) } } else { (DEP_REPLACE_NONE, input.len()) }; format!("{}{}", &input.get(..index).unwrap_or_default(), val) } pub fn filter_cargo_tree(lines: Vec<&str>) -> String { let mut tree = "\n".to_string(); for line in lines { let val = filter_dep_source(line); if tree.trim().is_empty() { tree.push_str(&val); } else { tree = format!("{tree}\n{val}"); } } tree } } /// Create all `shadow-rs` constants which are determined by the build environment. /// The data for these constants is provided by the `std_env` argument. pub(crate) fn new_system_env(shadow: &Shadow) -> BTreeMap { let mut env = SystemEnv::default(); env.map.insert( BUILD_OS, ConstVal { desc: BUILD_OS_DOC.to_string(), v: format!("{}-{}", env::consts::OS, env::consts::ARCH), t: ConstType::Str, }, ); env.map .insert(RUST_CHANNEL, ConstVal::new(RUST_CHANNEL_DOC)); env.map .insert(CARGO_METADATA, ConstVal::new_slice(CARGO_METADATA_DOC)); env.map .insert(RUST_VERSION, ConstVal::new(RUST_VERSION_DOC)); env.map .insert(CARGO_VERSION, ConstVal::new(CARGO_VERSION_DOC)); env.map.insert(CARGO_TREE, ConstVal::new(CARGO_TREE_DOC)); env.map .insert(BUILD_TARGET, ConstVal::new(BUILD_TARGET_DOC)); env.map .insert(BUILD_TARGET_ARCH, ConstVal::new(BUILD_TARGET_ARCH_DOC)); env.map.insert(PKG_VERSION, ConstVal::new(PKG_VERSION_DOC)); env.map .insert(PKG_DESCRIPTION, ConstVal::new(PKG_DESCRIPTION_DOC)); env.map .insert(PKG_VERSION_MAJOR, ConstVal::new(PKG_VERSION_MAJOR_DOC)); env.map .insert(PKG_VERSION_MINOR, ConstVal::new(PKG_VERSION_MINOR_DOC)); env.map .insert(PKG_VERSION_PATCH, ConstVal::new(PKG_VERSION_PATCH_DOC)); env.map .insert(PKG_VERSION_PRE, ConstVal::new(PKG_VERSION_PRE_DOC)); env.map .insert(CARGO_MANIFEST_DIR, ConstVal::new(CARGO_MANIFEST_DIR_DOC)); if let Err(e) = env.init(shadow) { println!("{e}"); } env.map } #[derive(Default, Debug)] pub struct Project { map: BTreeMap, } const PROJECT_NAME_DOC: &str = r#" The project name, as determined by the Cargo.toml manifest."#; const PROJECT_NAME: ShadowConst = "PROJECT_NAME"; const BUILD_TIME_DOC: &str = r#" The project build time, formatted in modified ISO 8601 format (`YYYY-MM-DD HH-MM ±hh-mm` where hh-mm is the offset from UTC)."#; const BUILD_TIME: ShadowConst = "BUILD_TIME"; const BUILD_TIME_2822_DOC: &str = r#" The project build time, formatted according to [RFC 2822](https://datatracker.ietf.org/doc/html/rfc2822#section-3.3) (e.g. HTTP Headers)."#; const BUILD_TIME_2822: ShadowConst = "BUILD_TIME_2822"; const BUILD_TIME_3339_DOC: &str = r#" The project build time, formatted according to [RFC 3339 and ISO 8601](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6)."#; const BUILD_TIME_3339: ShadowConst = "BUILD_TIME_3339"; const BUILD_RUST_CHANNEL_DOC: &str = r#" The debug configuration with which the project was built. Note that this is not the Rust channel, but either `debug` or `release`, depending on whether debug assertions were enabled in the build or not. "#; const BUILD_RUST_CHANNEL: ShadowConst = "BUILD_RUST_CHANNEL"; pub(crate) fn build_time(project: &mut Project) { // Enable reproducible builds: https://reproducible-builds.org/docs/source-date-epoch/ let time = now_date_time(); project.map.insert( BUILD_TIME, ConstVal { desc: BUILD_TIME_DOC.to_string(), v: time.human_format(), t: ConstType::Str, }, ); project.map.insert( BUILD_TIME_2822, ConstVal { desc: BUILD_TIME_2822_DOC.to_string(), v: time.to_rfc2822(), t: ConstType::Str, }, ); project.map.insert( BUILD_TIME_3339, ConstVal { desc: BUILD_TIME_3339_DOC.to_string(), v: time.to_rfc3339(), t: ConstType::Str, }, ); } pub(crate) fn new_project(std_env: &BTreeMap) -> BTreeMap { let mut project = Project::default(); build_time(&mut project); project.map.insert( BUILD_RUST_CHANNEL, ConstVal { desc: BUILD_RUST_CHANNEL_DOC.to_string(), v: build_channel().to_string(), t: ConstType::Str, }, ); project .map .insert(PROJECT_NAME, ConstVal::new(PROJECT_NAME_DOC)); if let (Some(v), Some(val)) = ( std_env.get("CARGO_PKG_NAME"), project.map.get_mut(PROJECT_NAME), ) { val.t = ConstType::Str; val.v = v.to_string(); } project.map } #[cfg(test)] mod tests { use crate::env::dep_source_replace::filter_dep_source; #[test] fn test_filter_dep_source_none() { let input = "shadow-rs v0.5.23"; let ret = filter_dep_source(input); assert_eq!(input, ret) } #[test] fn test_filter_dep_source_multi() { let input = "shadow-rs v0.5.23 (*)"; let ret = filter_dep_source(input); assert_eq!(input, ret) } #[test] fn test_filter_dep_source_path() { let input = "shadow-rs v0.5.23 (/Users/baoyachi/shadow-rs)"; let ret = filter_dep_source(input); assert_eq!("shadow-rs v0.5.23 (* path)", ret) } #[test] fn test_filter_dep_source_registry() { let input = "shadow-rs v0.5.23 (registry `ssh://git@github.com/baoyachi/shadow-rs.git`)"; let ret = filter_dep_source(input); assert_eq!("shadow-rs v0.5.23 (* registry)", ret) } #[test] fn test_filter_dep_source_git_https() { let input = "shadow-rs v0.5.23 (https://github.com/baoyachi/shadow-rs#13572c90)"; let ret = filter_dep_source(input); assert_eq!("shadow-rs v0.5.23 (* git)", ret) } #[test] fn test_filter_dep_source_git_http() { let input = "shadow-rs v0.5.23 (http://github.com/baoyachi/shadow-rs#13572c90)"; let ret = filter_dep_source(input); assert_eq!("shadow-rs v0.5.23 (* git)", ret) } #[test] fn test_filter_dep_source_git() { let input = "shadow-rs v0.5.23 (ssh://git@github.com/baoyachi/shadow-rs)"; let ret = filter_dep_source(input); assert_eq!("shadow-rs v0.5.23 (* git)", ret) } #[test] fn test_filter_dep_windows_path() { let input = r"shadow-rs v0.5.23 (FD:\a\shadow-rs\shadow-rs)"; let ret = filter_dep_source(input); assert_eq!(input, ret) } } shadow-rs-1.1.1/src/err.rs000064400000000000000000000031661046102023000134660ustar 00000000000000use std::error::Error; use std::error::Error as StdError; use std::fmt::{Display, Formatter}; use std::string::FromUtf8Error; /// Results returned by the `shadow-rs` build process. /// For more information see [`ShadowError`]. pub type SdResult = Result; /// `shadow-rs` build process errors. /// This type wraps multiple kinds of underlying errors that can occur downstream of `shadow-rs`, such as [`std::io::Error`]. #[derive(Debug)] pub enum ShadowError { String(String), } impl ShadowError { pub fn new(err: impl Error) -> Self { ShadowError::String(err.to_string()) } } impl From for ShadowError { fn from(e: FromUtf8Error) -> Self { ShadowError::String(e.to_string()) } } impl From for ShadowError { fn from(e: std::io::Error) -> Self { ShadowError::String(e.to_string()) } } impl From for ShadowError { fn from(e: String) -> Self { ShadowError::String(e) } } impl From<&str> for ShadowError { fn from(e: &str) -> Self { ShadowError::String(e.to_string()) } } impl From for ShadowError { fn from(e: std::env::VarError) -> Self { ShadowError::String(e.to_string()) } } impl From for ShadowError { fn from(e: std::num::ParseIntError) -> Self { ShadowError::String(e.to_string()) } } impl Display for ShadowError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { ShadowError::String(err) => f.write_str(err), } } } impl StdError for ShadowError {} shadow-rs-1.1.1/src/gen_const.rs000064400000000000000000000103151046102023000146470ustar 00000000000000use crate::{Shadow, CARGO_CLIPPY_ALLOW_ALL, CARGO_METADATA}; macro_rules! gen_const { ($fn_name:ident, $fn_body:expr) => { pub fn $fn_name() -> String { let (doc, content) = $fn_body; format!( "{}\n{}\n{}\n{}\n", doc, "#[allow(dead_code,missing_docs)]", $crate::CARGO_CLIPPY_ALLOW_ALL, content ) } }; } const VERSION_BRANCH_CONST: (&str, &str) = ( r#"/// A long version string describing the project. /// The version string contains the package version, branch, commit hash, build time, and build environment on separate lines. /// This constant is suitable for printing to the user."#, r##"pub const VERSION:&str = shadow_rs::formatcp!(r#" pkg_version:{} branch:{} commit_hash:{} build_time:{} build_env:{},{}"#,PKG_VERSION, BRANCH, SHORT_COMMIT, BUILD_TIME, RUST_VERSION, RUST_CHANNEL );"##, ); const VERSION_TAG_CONST: (&str, &str) = ( r#"/// A long version string describing the project. /// The version string contains the package version, current Git tag, commit hash, build time, and build environment on separate lines. /// This constant is suitable for printing to the user."#, r##"pub const VERSION:&str = shadow_rs::formatcp!(r#" pkg_version:{} tag:{} commit_hash:{} build_time:{} build_env:{},{}"#,PKG_VERSION, TAG, SHORT_COMMIT, BUILD_TIME, RUST_VERSION, RUST_CHANNEL );"##, ); const CLAP_LONG_VERSION_BRANCH_CONST: (&str, &str) = ( r#"/// A long version string describing the project. /// The version string contains the package version, branch, commit hash, build time, and build environment on separate lines. /// This constant is intended to be used by clap or other CLI tools as a long version string."#, r##"pub const CLAP_LONG_VERSION:&str = shadow_rs::formatcp!(r#"{} branch:{} commit_hash:{} build_time:{} build_env:{},{}"#,PKG_VERSION, BRANCH, SHORT_COMMIT, BUILD_TIME, RUST_VERSION, RUST_CHANNEL );"##, ); const CLAP_LONG_VERSION_TAG_CONST: (&str, &str) = ( r#"/// A long version string describing the project. /// The version string contains the package version, current Git tag, commit hash, build time, and build environment on separate lines. /// This constant is intended to be used by clap or other CLI tools as a long version string."#, r##"pub const CLAP_LONG_VERSION:&str = shadow_rs::formatcp!(r#"{} tag:{} commit_hash:{} build_time:{} build_env:{},{}"#,PKG_VERSION, TAG, SHORT_COMMIT, BUILD_TIME, RUST_VERSION, RUST_CHANNEL );"##, ); gen_const!(version_branch_const, VERSION_BRANCH_CONST); gen_const!(version_tag_const, VERSION_TAG_CONST); gen_const!( clap_long_version_branch_const, CLAP_LONG_VERSION_BRANCH_CONST ); gen_const!(clap_long_version_tag_const, CLAP_LONG_VERSION_TAG_CONST); pub(crate) const BUILD_CONST_VERSION: &str = "VERSION"; pub(crate) const BUILD_CONST_CLAP_LONG_VERSION: &str = "CLAP_LONG_VERSION"; #[allow(dead_code)] pub(crate) fn cargo_metadata_fn(shadow: &Shadow) -> String { if !shadow.map.contains_key(CARGO_METADATA) { return "".to_string(); } format!( r#" use shadow_rs::cargo_metadata::Metadata; use shadow_rs::serde_json; /// Attempts to parse the Cargo package metadata from the generated constant CARGO_METADATA. /// /// Returns a `Metadata` struct containing information about the Cargo workspace, /// such as details about the packages and their dependencies. /// /// # Return Values /// - `Ok(Metadata)`: Contains the parsed metadata if successful. /// - `Err(String)`: Returns an error message if converting the environment variable to a UTF-8 string or parsing JSON fails. #[allow(dead_code)] {} pub fn cargo_metadata() -> Result {{ let metadata_json = std::str::from_utf8(CARGO_METADATA.as_ref()).map_err(|err| format!("generate 'CARGO_METADATA' value from UTF8 error:{{}}",err))?; let meta: Metadata = serde_json::from_str(metadata_json).map_err(|err| err.to_string())?; Ok(meta) }}"#, CARGO_CLIPPY_ALLOW_ALL ) } #[cfg(test)] mod tests { use super::*; #[test] fn test_version_fn() { assert!(version_tag_const().contains(VERSION_TAG_CONST.0)); assert!(clap_long_version_branch_const().contains(CLAP_LONG_VERSION_BRANCH_CONST.1)); } } shadow-rs-1.1.1/src/git.rs000064400000000000000000000635751046102023000134730ustar 00000000000000use crate::build::{ConstType, ConstVal, ShadowConst}; use crate::ci::CiType; use crate::err::*; use crate::{DateTime, Format}; use std::collections::BTreeMap; use std::io::{BufReader, Read}; use std::path::Path; use std::process::{Command, Stdio}; const BRANCH_DOC: &str = r#" The name of the Git branch that this project was built from. This constant will be empty if the branch cannot be determined."#; pub const BRANCH: ShadowConst = "BRANCH"; const TAG_DOC: &str = r#" The name of the Git tag that this project was built from. Note that this will be empty if there is no tag for the HEAD at the time of build."#; pub const TAG: ShadowConst = "TAG"; const LAST_TAG_DOC: &str = r#" The name of the last Git tag on the branch that this project was built from. As opposed to [`TAG`], this does not require the current commit to be tagged, just one of its parents. This constant will be empty if the last tag cannot be determined."#; pub const LAST_TAG: ShadowConst = "LAST_TAG"; pub const COMMITS_SINCE_TAG_DOC: &str = r#" The number of commits since the last Git tag on the branch that this project was built from. This value indicates how many commits have been made after the last tag and before the current commit. If there are no additional commits after the last tag (i.e., the current commit is exactly at a tag), this value will be `0`. This constant will be empty or `0` if the last tag cannot be determined or if there are no commits after it. "#; pub const COMMITS_SINCE_TAG: &str = "COMMITS_SINCE_TAG"; const SHORT_COMMIT_DOC: &str = r#" The short hash of the Git commit that this project was built from. Note that this will always truncate [`COMMIT_HASH`] to 8 characters if necessary. Depending on the amount of commits in your project, this may not yield a unique Git identifier ([see here for more details on hash abbreviation](https://git-scm.com/docs/git-describe#_examples)). This constant will be empty if the last commit cannot be determined."#; pub const SHORT_COMMIT: ShadowConst = "SHORT_COMMIT"; const COMMIT_HASH_DOC: &str = r#" The full commit hash of the Git commit that this project was built from. An abbreviated, but not necessarily unique, version of this is [`SHORT_COMMIT`]. This constant will be empty if the last commit cannot be determined."#; pub const COMMIT_HASH: ShadowConst = "COMMIT_HASH"; const COMMIT_DATE_DOC: &str = r#"The time of the Git commit that this project was built from. The time is formatted in modified ISO 8601 format (`YYYY-MM-DD HH-MM ±hh-mm` where hh-mm is the offset from UTC). This constant will be empty if the last commit cannot be determined."#; pub const COMMIT_DATE: ShadowConst = "COMMIT_DATE"; const COMMIT_DATE_2822_DOC: &str = r#" The name of the Git branch that this project was built from. The time is formatted according to [RFC 2822](https://datatracker.ietf.org/doc/html/rfc2822#section-3.3) (e.g. HTTP Headers). This constant will be empty if the last commit cannot be determined."#; pub const COMMIT_DATE_2822: ShadowConst = "COMMIT_DATE_2822"; const COMMIT_DATE_3339_DOC: &str = r#" The name of the Git branch that this project was built from. The time is formatted according to [RFC 3339 and ISO 8601](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6). This constant will be empty if the last commit cannot be determined."#; pub const COMMIT_DATE_3339: ShadowConst = "COMMIT_DATE_3339"; const COMMIT_AUTHOR_DOC: &str = r#" The author of the Git commit that this project was built from. This constant will be empty if the last commit cannot be determined."#; pub const COMMIT_AUTHOR: ShadowConst = "COMMIT_AUTHOR"; const COMMIT_EMAIL_DOC: &str = r#" The e-mail address of the author of the Git commit that this project was built from. This constant will be empty if the last commit cannot be determined."#; pub const COMMIT_EMAIL: ShadowConst = "COMMIT_EMAIL"; const GIT_CLEAN_DOC: &str = r#" Whether the Git working tree was clean at the time of project build (`true`), or not (`false`). This constant will be `false` if the last commit cannot be determined."#; pub const GIT_CLEAN: ShadowConst = "GIT_CLEAN"; const GIT_STATUS_FILE_DOC: &str = r#" The Git working tree status as a list of files with their status, similar to `git status`. Each line of the list is preceded with ` * `, followed by the file name. Files marked `(dirty)` have unstaged changes. Files marked `(staged)` have staged changes. This constant will be empty if the working tree status cannot be determined."#; pub const GIT_STATUS_FILE: ShadowConst = "GIT_STATUS_FILE"; #[derive(Default, Debug)] pub struct Git { map: BTreeMap, ci_type: CiType, } impl Git { fn update_str(&mut self, c: ShadowConst, v: String) { if let Some(val) = self.map.get_mut(c) { *val = ConstVal { desc: val.desc.clone(), v, t: ConstType::Str, } } } fn update_bool(&mut self, c: ShadowConst, v: bool) { if let Some(val) = self.map.get_mut(c) { *val = ConstVal { desc: val.desc.clone(), v: v.to_string(), t: ConstType::Bool, } } } fn update_usize(&mut self, c: ShadowConst, v: usize) { if let Some(val) = self.map.get_mut(c) { *val = ConstVal { desc: val.desc.clone(), v: v.to_string(), t: ConstType::Usize, } } } fn init(&mut self, path: &Path, std_env: &BTreeMap) -> SdResult<()> { // First, try executing using the git command. if let Err(err) = self.init_git() { println!("{err}"); } // If the git2 feature is enabled, then replace the corresponding values with git2. self.init_git2(path)?; // use command branch if let Some(x) = find_branch_in(path) { self.update_str(BRANCH, x) }; // use command tag if let Some(x) = command_current_tag() { self.update_str(TAG, x) } // use command get last tag let describe = command_git_describe(); if let Some(x) = describe.0 { self.update_str(LAST_TAG, x) } if let Some(x) = describe.1 { self.update_usize(COMMITS_SINCE_TAG, x) } // try use ci branch,tag self.ci_branch_tag(std_env); Ok(()) } fn init_git(&mut self) -> SdResult<()> { // check git status let x = command_git_clean(); self.update_bool(GIT_CLEAN, x); let x = command_git_status_file(); self.update_str(GIT_STATUS_FILE, x); let git_info = command_git_head(); self.update_str(COMMIT_EMAIL, git_info.email); self.update_str(COMMIT_AUTHOR, git_info.author); self.update_str(SHORT_COMMIT, git_info.short_commit); self.update_str(COMMIT_HASH, git_info.commit); let time_stamp = git_info.date.parse::()?; if let Ok(date_time) = DateTime::timestamp_2_utc(time_stamp) { self.update_str(COMMIT_DATE, date_time.human_format()); self.update_str(COMMIT_DATE_2822, date_time.to_rfc2822()); self.update_str(COMMIT_DATE_3339, date_time.to_rfc3339()); } Ok(()) } #[allow(unused_variables)] fn init_git2(&mut self, path: &Path) -> SdResult<()> { #[cfg(feature = "git2")] { use crate::date_time::DateTime; use crate::git::git2_mod::git_repo; use crate::Format; let repo = git_repo(path).map_err(ShadowError::new)?; let reference = repo.head().map_err(ShadowError::new)?; //get branch let branch = reference .shorthand() .map(|x| x.trim().to_string()) .or_else(command_current_branch) .unwrap_or_default(); //get HEAD branch let tag = command_current_tag().unwrap_or_default(); self.update_str(BRANCH, branch); self.update_str(TAG, tag); // use command get last tag let describe = command_git_describe(); if let Some(x) = describe.0 { self.update_str(LAST_TAG, x) } if let Some(x) = describe.1 { self.update_usize(COMMITS_SINCE_TAG, x) } if let Some(v) = reference.target() { let commit = v.to_string(); self.update_str(COMMIT_HASH, commit.clone()); let mut short_commit = commit.as_str(); if commit.len() > 8 { short_commit = short_commit.get(0..8).unwrap(); } self.update_str(SHORT_COMMIT, short_commit.to_string()); } let commit = reference.peel_to_commit().map_err(ShadowError::new)?; let author = commit.author(); if let Some(v) = author.email() { self.update_str(COMMIT_EMAIL, v.to_string()); } if let Some(v) = author.name() { self.update_str(COMMIT_AUTHOR, v.to_string()); } let status_file = Self::git2_dirty_stage(&repo); if status_file.trim().is_empty() { self.update_bool(GIT_CLEAN, true); } else { self.update_bool(GIT_CLEAN, false); } self.update_str(GIT_STATUS_FILE, status_file); let time_stamp = commit.time().seconds().to_string().parse::()?; if let Ok(date_time) = DateTime::timestamp_2_utc(time_stamp) { self.update_str(COMMIT_DATE, date_time.human_format()); self.update_str(COMMIT_DATE_2822, date_time.to_rfc2822()); self.update_str(COMMIT_DATE_3339, date_time.to_rfc3339()); } } Ok(()) } //use git2 crates git repository 'dirty or stage' status files. #[cfg(feature = "git2")] pub fn git2_dirty_stage(repo: &git2::Repository) -> String { let mut repo_opts = git2::StatusOptions::new(); repo_opts.include_ignored(false); if let Ok(statue) = repo.statuses(Some(&mut repo_opts)) { let mut dirty_files = Vec::new(); let mut staged_files = Vec::new(); for status in statue.iter() { if let Some(path) = status.path() { match status.status() { git2::Status::CURRENT => (), git2::Status::INDEX_NEW | git2::Status::INDEX_MODIFIED | git2::Status::INDEX_DELETED | git2::Status::INDEX_RENAMED | git2::Status::INDEX_TYPECHANGE => staged_files.push(path.to_string()), _ => dirty_files.push(path.to_string()), }; } } filter_git_dirty_stage(dirty_files, staged_files) } else { "".into() } } #[allow(clippy::manual_strip)] fn ci_branch_tag(&mut self, std_env: &BTreeMap) { let mut branch: Option = None; let mut tag: Option = None; match self.ci_type { CiType::Gitlab => { if let Some(v) = std_env.get("CI_COMMIT_TAG") { tag = Some(v.to_string()); } else if let Some(v) = std_env.get("CI_COMMIT_REF_NAME") { branch = Some(v.to_string()); } } CiType::Github => { if let Some(v) = std_env.get("GITHUB_REF") { let ref_branch_prefix: &str = "refs/heads/"; let ref_tag_prefix: &str = "refs/tags/"; if v.starts_with(ref_branch_prefix) { branch = Some( v.get(ref_branch_prefix.len()..) .unwrap_or_default() .to_string(), ) } else if v.starts_with(ref_tag_prefix) { tag = Some( v.get(ref_tag_prefix.len()..) .unwrap_or_default() .to_string(), ) } } } _ => {} } if let Some(x) = branch { self.update_str(BRANCH, x); } if let Some(x) = tag { self.update_str(TAG, x.clone()); self.update_str(LAST_TAG, x); } } } pub(crate) fn new_git( path: &Path, ci: CiType, std_env: &BTreeMap, ) -> BTreeMap { let mut git = Git { map: Default::default(), ci_type: ci, }; git.map.insert(BRANCH, ConstVal::new(BRANCH_DOC)); git.map.insert(TAG, ConstVal::new(TAG_DOC)); git.map.insert(LAST_TAG, ConstVal::new(LAST_TAG_DOC)); git.map .insert(COMMITS_SINCE_TAG, ConstVal::new(COMMITS_SINCE_TAG_DOC)); git.map.insert(COMMIT_HASH, ConstVal::new(COMMIT_HASH_DOC)); git.map .insert(SHORT_COMMIT, ConstVal::new(SHORT_COMMIT_DOC)); git.map .insert(COMMIT_AUTHOR, ConstVal::new(COMMIT_AUTHOR_DOC)); git.map .insert(COMMIT_EMAIL, ConstVal::new(COMMIT_EMAIL_DOC)); git.map.insert(COMMIT_DATE, ConstVal::new(COMMIT_DATE_DOC)); git.map .insert(COMMIT_DATE_2822, ConstVal::new(COMMIT_DATE_2822_DOC)); git.map .insert(COMMIT_DATE_3339, ConstVal::new(COMMIT_DATE_3339_DOC)); git.map.insert(GIT_CLEAN, ConstVal::new_bool(GIT_CLEAN_DOC)); git.map .insert(GIT_STATUS_FILE, ConstVal::new(GIT_STATUS_FILE_DOC)); if let Err(e) = git.init(path, std_env) { println!("{e}"); } git.map } #[cfg(feature = "git2")] pub mod git2_mod { use git2::Error as git2Error; use git2::Repository; use std::path::Path; pub fn git_repo>(path: P) -> Result { Repository::discover(path) } pub fn git2_current_branch(repo: &Repository) -> Option { repo.head() .map(|x| x.shorthand().map(|x| x.to_string())) .unwrap_or(None) } } /// get current repository git branch. /// /// When current repository exists git folder. /// /// It's use default feature.This function try use [git2] crates get current branch. /// If not use git2 feature,then try use [Command] to get. pub fn branch() -> String { #[cfg(feature = "git2")] { use crate::git::git2_mod::{git2_current_branch, git_repo}; git_repo(".") .map(|x| git2_current_branch(&x)) .unwrap_or_else(|_| command_current_branch()) .unwrap_or_default() } #[cfg(not(feature = "git2"))] { command_current_branch().unwrap_or_default() } } /// get current repository git tag. /// /// When current repository exists git folder. /// I's use [Command] to get. pub fn tag() -> String { command_current_tag().unwrap_or_default() } /// Check current git Repository status without nothing(dirty or stage) /// /// if nothing,It means clean:true. On the contrary, it is 'dirty':false pub fn git_clean() -> bool { #[cfg(feature = "git2")] { use crate::git::git2_mod::git_repo; git_repo(".") .map(|x| Git::git2_dirty_stage(&x)) .map(|x| x.trim().is_empty()) .unwrap_or(true) } #[cfg(not(feature = "git2"))] { command_git_clean() } } /// List current git Repository statue(dirty or stage) contain file changed /// /// Refer to the 'cargo fix' result output when git statue(dirty or stage) changed. /// /// Example output:` * examples/builtin_fn.rs (dirty)` pub fn git_status_file() -> String { #[cfg(feature = "git2")] { use crate::git::git2_mod::git_repo; git_repo(".") .map(|x| Git::git2_dirty_stage(&x)) .unwrap_or_default() } #[cfg(not(feature = "git2"))] { command_git_status_file() } } struct GitHeadInfo { commit: String, short_commit: String, email: String, author: String, date: String, } struct GitCommandExecutor<'a> { path: &'a Path, } impl Default for GitCommandExecutor<'_> { fn default() -> Self { Self::new(Path::new(".")) } } impl<'a> GitCommandExecutor<'a> { fn new(path: &'a Path) -> Self { GitCommandExecutor { path } } fn exec(&self, args: &[&str]) -> Option { Command::new("git") .env("GIT_OPTIONAL_LOCKS", "0") .current_dir(self.path) .args(args) .output() .map(|x| { String::from_utf8(x.stdout) .map(|x| x.trim().to_string()) .ok() }) .unwrap_or(None) } } fn command_git_head() -> GitHeadInfo { let cli = |args: &[&str]| GitCommandExecutor::default().exec(args).unwrap_or_default(); GitHeadInfo { commit: cli(&["rev-parse", "HEAD"]), short_commit: cli(&["rev-parse", "--short", "HEAD"]), author: cli(&["log", "-1", "--pretty=format:%an"]), email: cli(&["log", "-1", "--pretty=format:%ae"]), date: cli(&["show", "--pretty=format:%ct", "--date=raw", "-s"]), } } /// Command exec git current tag fn command_current_tag() -> Option { GitCommandExecutor::default().exec(&["tag", "-l", "--contains", "HEAD"]) } /// git describe --tags HEAD /// Command exec git describe fn command_git_describe() -> (Option, Option, Option) { let last_tag = GitCommandExecutor::default().exec(&["describe", "--tags", "--abbrev=0", "HEAD"]); if last_tag.is_none() { return (None, None, None); } let tag = last_tag.unwrap(); let describe = GitCommandExecutor::default().exec(&["describe", "--tags", "HEAD"]); if let Some(desc) = describe { match parse_git_describe(&tag, &desc) { Ok((tag, commits, hash)) => { return (Some(tag), commits, hash); } Err(_) => { return (Some(tag), None, None); } } } (Some(tag), None, None) } fn parse_git_describe( last_tag: &str, describe: &str, ) -> SdResult<(String, Option, Option)> { if !describe.starts_with(last_tag) { return Err(ShadowError::String("git describe result error".to_string())); } if last_tag == describe { return Ok((describe.to_string(), None, None)); } let parts: Vec<&str> = describe.rsplit('-').collect(); if parts.is_empty() || parts.len() == 2 { return Err(ShadowError::String( "git describe result error,expect:--g".to_string(), )); } if parts.len() > 2 { let short_hash = parts[0]; // last part if !short_hash.starts_with('g') { return Err(ShadowError::String( "git describe result error,expect commit hash end with:-g".to_string(), )); } let short_hash = short_hash.trim_start_matches('g'); // Full example:v1.0.0-alpha0-5-ga1b2c3d let num_commits_str = parts[1]; let num_commits = num_commits_str .parse::() .map_err(|e| ShadowError::String(e.to_string()))?; let last_tag = parts[2..] .iter() .rev() .copied() .collect::>() .join("-"); return Ok((last_tag, Some(num_commits), Some(short_hash.to_string()))); } Ok((describe.to_string(), None, None)) } /// git clean:git status --porcelain /// check repository git status is clean fn command_git_clean() -> bool { GitCommandExecutor::default() .exec(&["status", "--porcelain"]) .map(|x| x.is_empty()) .unwrap_or(true) } /// check git repository 'dirty or stage' status files. /// git dirty:git status --porcelain | grep '^\sM.' |awk '{print $2}' /// git stage:git status --porcelain --untracked-files=all | grep '^[A|M|D|R]'|awk '{print $2}' fn command_git_status_file() -> String { let git_status_files = move |args: &[&str], grep: &[&str], awk: &[&str]| -> SdResult> { let git_shell = Command::new("git") .args(args) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn()?; let git_out = git_shell.stdout.ok_or("Failed to exec git stdout")?; let grep_shell = Command::new("grep") .args(grep) .stdin(Stdio::from(git_out)) .stdout(Stdio::piped()) .spawn()?; let grep_out = grep_shell.stdout.ok_or("Failed to exec grep stdout")?; let mut awk_shell = Command::new("awk") .args(awk) .stdin(Stdio::from(grep_out)) .stdout(Stdio::piped()) .spawn()?; let mut awk_out = BufReader::new( awk_shell .stdout .as_mut() .ok_or("Failed to exec awk stdout")?, ); let mut line = String::new(); awk_out.read_to_string(&mut line)?; Ok(line.lines().map(|x| x.into()).collect()) }; let dirty = git_status_files(&["status", "--porcelain"], &[r"^\sM."], &["{print $2}"]) .unwrap_or_default(); let stage = git_status_files( &["status", "--porcelain", "--untracked-files=all"], &[r#"^[A|M|D|R]"#], &["{print $2}"], ) .unwrap_or_default(); filter_git_dirty_stage(dirty, stage) } /// Command exec git current branch fn command_current_branch() -> Option { find_branch_in(Path::new(".")) } fn find_branch_in(path: &Path) -> Option { GitCommandExecutor::new(path).exec(&["symbolic-ref", "--short", "HEAD"]) } fn filter_git_dirty_stage(dirty_files: Vec, staged_files: Vec) -> String { let mut concat_file = String::new(); for file in dirty_files { concat_file.push_str(" * "); concat_file.push_str(&file); concat_file.push_str(" (dirty)\n"); } for file in staged_files { concat_file.push_str(" * "); concat_file.push_str(&file); concat_file.push_str(" (staged)\n"); } concat_file } #[cfg(test)] mod tests { use super::*; use crate::get_std_env; #[test] fn test_git() { let env_map = get_std_env(); let map = new_git(Path::new("./"), CiType::Github, &env_map); for (k, v) in map { assert!(!v.desc.is_empty()); if !k.eq(TAG) && !k.eq(LAST_TAG) && !k.eq(COMMITS_SINCE_TAG) && !k.eq(BRANCH) && !k.eq(GIT_STATUS_FILE) { assert!(!v.v.is_empty()); continue; } //assert github tag always exist value if let Some(github_ref) = env_map.get("GITHUB_REF") { if github_ref.starts_with("refs/tags/") && k.eq(TAG) { assert!(!v.v.is_empty(), "not empty"); } else if github_ref.starts_with("refs/heads/") && k.eq(BRANCH) { assert!(!v.v.is_empty()); } } } } #[test] fn test_current_branch() { if get_std_env().contains_key("GITHUB_REF") { return; } #[cfg(feature = "git2")] { use crate::git::git2_mod::{git2_current_branch, git_repo}; let git2_branch = git_repo(".") .map(|x| git2_current_branch(&x)) .unwrap_or(None); let command_branch = command_current_branch(); assert!(git2_branch.is_some()); assert!(command_branch.is_some()); assert_eq!(command_branch, git2_branch); } assert_eq!(Some(branch()), command_current_branch()); } #[test] fn test_parse_git_describe() { let commit_hash = "24skp4489"; let describe = "v1.0.0"; assert_eq!( parse_git_describe("v1.0.0", describe).unwrap(), (describe.into(), None, None) ); let describe = "v1.0.0-0-g24skp4489"; assert_eq!( parse_git_describe("v1.0.0", describe).unwrap(), ("v1.0.0".into(), Some(0), Some(commit_hash.into())) ); let describe = "v1.0.0-1-g24skp4489"; assert_eq!( parse_git_describe("v1.0.0", describe).unwrap(), ("v1.0.0".into(), Some(1), Some(commit_hash.into())) ); let describe = "v1.0.0-alpha-0-g24skp4489"; assert_eq!( parse_git_describe("v1.0.0-alpha", describe).unwrap(), ("v1.0.0-alpha".into(), Some(0), Some(commit_hash.into())) ); let describe = "v1.0.0.alpha-0-g24skp4489"; assert_eq!( parse_git_describe("v1.0.0.alpha", describe).unwrap(), ("v1.0.0.alpha".into(), Some(0), Some(commit_hash.into())) ); let describe = "v1.0.0-alpha"; assert_eq!( parse_git_describe("v1.0.0-alpha", describe).unwrap(), ("v1.0.0-alpha".into(), None, None) ); let describe = "v1.0.0-alpha-99-0-g24skp4489"; assert_eq!( parse_git_describe("v1.0.0-alpha-99", describe).unwrap(), ("v1.0.0-alpha-99".into(), Some(0), Some(commit_hash.into())) ); let describe = "v1.0.0-alpha-99-024skp4489"; assert!(parse_git_describe("v1.0.0-alpha-99", describe).is_err()); let describe = "v1.0.0-alpha-024skp4489"; assert!(parse_git_describe("v1.0.0-alpha", describe).is_err()); let describe = "v1.0.0-alpha-024skp4489"; assert!(parse_git_describe("v1.0.0-alpha", describe).is_err()); let describe = "v1.0.0-alpha-g024skp4489"; assert!(parse_git_describe("v1.0.0-alpha", describe).is_err()); let describe = "v1.0.0----alpha-g024skp4489"; assert!(parse_git_describe("v1.0.0----alpha", describe).is_err()); } } shadow-rs-1.1.1/src/hook.rs000064400000000000000000000041701046102023000136320ustar 00000000000000use crate::{default_deny, SdResult, ShadowConst}; use std::collections::BTreeSet; use std::fs::File; /// A trait that extends the functionality of hooks. /// It provides methods to get the default deny list and the inner hook function. pub trait HookExt { /// Returns the default deny list. fn default_deny(&self) -> BTreeSet; /// Returns a reference to the inner hook function. fn hook_inner(&self) -> &dyn Fn(&File) -> SdResult<()>; } /// Implement the `HookExt` trait for any function that takes a `&File` and returns a `SdResult<()>`. impl HookExt for F where F: Fn(&File) -> SdResult<()>, { /// Returns the default deny list using the `default_deny` function from the crate. fn default_deny(&self) -> BTreeSet { default_deny() } /// Returns a reference to the function itself. fn hook_inner(&self) -> &dyn Fn(&File) -> SdResult<()> { self } } /// Implement the `HookExt` trait for a tuple containing a function and a deny list. impl HookExt for (F, BTreeSet) where F: Fn(&File) -> SdResult<()>, { /// Returns the deny list stored in the second element of the tuple. fn default_deny(&self) -> BTreeSet { self.1.clone() } /// Returns a reference to the function stored in the first element of the tuple. fn hook_inner(&self) -> &dyn Fn(&File) -> SdResult<()> { &self.0 } } /// A struct representing a shadow hook with an inner function and a deny list. pub struct ShadowHook { /// The inner function that will be used as the hook. pub hook: F, /// The deny list associated with this hook. pub deny: BTreeSet, } /// Implement the `HookExt` trait for the `ShadowHook` struct. impl HookExt for ShadowHook where F: Fn(&File) -> SdResult<()>, { /// Returns the deny list associated with this `ShadowHook`. fn default_deny(&self) -> BTreeSet { self.deny.clone() } /// Returns a reference to the inner function of this `ShadowHook`. fn hook_inner(&self) -> &dyn Fn(&File) -> SdResult<()> { &self.hook } } shadow-rs-1.1.1/src/lib.rs000064400000000000000000000210571046102023000134430ustar 00000000000000#![doc(html_logo_url = "https://raw.githubusercontent.com/baoyachi/shadow-rs/master/shadow-rs.png")] //! `shadow-rs`: Build-time information stored in your Rust project (binary, lib, cdylib, dylib). //! //! `shadow-rs` allows you to access properties of the build process and environment at runtime, including: //! //! * `Cargo.toml` information, such as the project version //! * Dependency information //! * Git information, such as the commit that produced the build artifact //! * What version of the Rust toolchain was used in compilation //! * The build variant, e.g. `debug` or `release` //! * ... And more! //! //! You can use this crate to programmatically check where a binary came from and how it was built. //! //! # Examples //! * Check out the [example_shadow](https://github.com/baoyachi/shadow-rs/tree/master/example_shadow) for a simple demonstration of how `shadow-rs` might be used to provide build-time information at run-time. //! * Check out the [example_shadow_hook](https://github.com/baoyachi/shadow-rs/tree/master/example_shadow_hook) for a demonstration of how custom hooks can be used to add extra information to `shadow-rs`'s output. //! * Check out the [`builtin_fn` example](https://github.com/baoyachi/shadow-rs/tree/master/examples/builtin_fn.rs) for a simple demonstration of the built-in functions that `shadow-rs` provides. //! //! # Setup //! //! ### 1) Modify `Cargo.toml` fields //! Modify your `Cargo.toml` like so: //! //! ```toml //! [package] //! build = "build.rs" //! //! [dependencies] //! shadow-rs = { version = "{latest version}", default-features = false } //! //! [build-dependencies] //! shadow-rs = "{latest version}" //! ``` #![cfg_attr(feature = "document-features", doc = document_features::document_features!())] //! //! ### 2) Create `build.rs` file //! Now in the root of your project (same directory as `Cargo.toml`) add a file `build.rs`: //! //! ```ignore //! fn main() { //! ShadowBuilder::builder() //! .build_pattern(BuildPattern::RealTime) //! .build().unwrap(); //! } //! ``` //! //! //! ### 3) Integrate Shadow //! In your main Rust file (usually `main.rs` or `lib.rs`), add this: //! //! ```ignore //! use shadow_rs::shadow; //! //! shadow!(build); //! ``` //! //! The `shadow!` macro uses the given identifier to create a module with that name. //! //! ### 4) Use Shadow Constants //! You can now use the module defined with `shadow!` to access build-time information. //! //! ```ignore //! fn main(){ //! println!("debug:{}", shadow_rs::is_debug()); // check if this is a debug build. e.g 'true/false' //! println!("branch:{}", shadow_rs::branch()); // get current project branch. e.g 'master/develop' //! println!("tag:{}", shadow_rs::tag()); // get current project tag. e.g 'v1.3.5' //! println!("git_clean:{}", shadow_rs::git_clean()); // get current project clean. e.g 'true/false' //! println!("git_status_file:{}", shadow_rs::git_status_file()); // get current project statue file. e.g ' * examples/builtin_fn.rs (dirty)' //! //! println!("{}", build::VERSION); //print version const //! println!("{}", build::CLAP_LONG_VERSION); //print CLAP_LONG_VERSION const //! println!("{}", build::BRANCH); //master //! println!("{}", build::SHORT_COMMIT);//8405e28e //! println!("{}", build::COMMIT_HASH);//8405e28e64080a09525a6cf1b07c22fcaf71a5c5 //! println!("{}", build::COMMIT_DATE);//2021-08-04 12:34:03 +00:00 //! println!("{}", build::COMMIT_AUTHOR);//baoyachi //! println!("{}", build::COMMIT_EMAIL);//xxx@gmail.com //! //! println!("{}", build::BUILD_OS);//macos-x86_64 //! println!("{}", build::RUST_VERSION);//rustc 1.45.0 (5c1f21c3b 2020-07-13) //! println!("{}", build::RUST_CHANNEL);//stable-x86_64-apple-darwin (default) //! println!("{}", build::CARGO_VERSION);//cargo 1.45.0 (744bd1fbb 2020-06-15) //! println!("{}", build::PKG_VERSION);//0.3.13 //! println!("{}", build::CARGO_TREE); //like command:cargo tree //! println!("{}", build::CARGO_MANIFEST_DIR); // /User/baoyachi/shadow-rs/ | //! //! println!("{}", build::PROJECT_NAME);//shadow-rs //! println!("{}", build::BUILD_TIME);//2020-08-16 14:50:25 //! println!("{}", build::BUILD_RUST_CHANNEL);//debug //! println!("{}", build::GIT_CLEAN);//false //! println!("{}", build::GIT_STATUS_FILE);//* src/lib.rs (dirty) //! } //! ``` //! //! ## Clap //! You can also use `shadow-rs` to provide information to command-line interface crates such as [`clap`](https://docs.rs/clap/latest/clap/). An example of this can be found in [`example_shadow`](https://github.com/baoyachi/shadow-rs/blob/master/example_shadow/src/main.rs). //! //! For the user guide and further documentation, see the [README of `shadow-rs`](https://github.com/baoyachi/shadow-rs). //! //! # List of Generated Output Constants //! //! All constants produced by `shadow-rs` are documented in the module created with [`shadow!`], so `rustdoc` and your IDE will pick it up. //! //! ``` //! pub const PKG_VERSION: &str = "1.3.8-beta3"; //! pub const PKG_VERSION_MAJOR: &str = "1"; //! pub const PKG_VERSION_MINOR: &str = "3"; //! pub const PKG_VERSION_PATCH: &str = "8"; //! pub const PKG_VERSION_PRE: &str = "beta3"; //! pub const RUST_VERSION: &str = "rustc 1.45.0 (5c1f21c3b 2020-07-13)"; //! pub const BUILD_RUST_CHANNEL: &str = "debug"; //! pub const COMMIT_AUTHOR: &str = "baoyachi"; //! pub const BUILD_TIME: &str = "2020-08-16 13:48:52"; //! pub const BUILD_TIME_2822: &str = "Thu, 24 Jun 2021 21:44:14 +0800"; //! pub const BUILD_TIME_3339: &str = "2021-06-24T15:53:55+08:00"; //! pub const COMMIT_DATE: &str = "2021-08-04 12:34:03 +00:00"; //! pub const COMMIT_DATE_2822: &str = "Thu, 24 Jun 2021 21:44:14 +0800"; //! pub const COMMIT_DATE_3339: &str = "2021-06-24T21:44:14.473058+08:00"; //! pub const COMMIT_EMAIL: &str = "xxx@gmail.com"; //! pub const PROJECT_NAME: &str = "shadow-rs"; //! pub const RUST_CHANNEL: &str = "stable-x86_64-apple-darwin (default)"; //! pub const BRANCH: &str = "master"; //! pub const CARGO_LOCK: &str = r#" //! ├── chrono v0.4.19 //! │ ├── libc v0.2.80 //! │ ├── num-integer v0.1.44 //! │ │ └── num-traits v0.2.14 //! │ │ [build-dependencies] //! │ │ └── autocfg v1.0.1 //! │ ├── num-traits v0.2.14 (*) //! │ └── time v0.1.44 //! │ └── libc v0.2.80 //! └── git2 v0.13.12 //! ├── log v0.4.11 //! │ └── cfg-if v0.1.10 //! └── url v2.2.0 //! ├── form_urlencoded v1.0.0 //! │ └── percent-encoding v2.1.0 //! └── percent-encoding v2.1.0"#; //! pub const CARGO_VERSION: &str = "cargo 1.45.0 (744bd1fbb 2020-06-15)"; //! pub const BUILD_OS: &str = "macos-x86_64"; //! pub const COMMIT_HASH: &str = "386741540d73c194a3028b96b92fdeb53ca2788a"; //! pub const GIT_CLEAN: bool = true; //! pub const GIT_STATUS_FILE: &str = "* src/lib.rs (dirty)"; //! ``` //! #![cfg_attr(not(feature = "std"), no_std)] #[cfg(feature = "metadata")] pub extern crate cargo_metadata; #[cfg(feature = "metadata")] pub extern crate serde_json; #[cfg(feature = "build")] mod build; #[cfg(feature = "build")] mod ci; #[cfg(feature = "build")] mod date_time; #[cfg(feature = "build")] mod env; #[cfg(feature = "build")] mod err; #[cfg(feature = "build")] mod gen_const; #[cfg(feature = "build")] mod git; #[cfg(feature = "build")] mod hook; #[cfg(feature = "build")] mod shadow; /// Re-exported from the const_format crate pub use const_format::*; /// Re-exported from the is_debug crate pub use is_debug::*; #[cfg(feature = "build")] mod pub_export { pub use crate::build::{BuildPattern, ShadowBuilder}; pub use crate::date_time::DateTime; pub use crate::err::{SdResult, ShadowError}; pub use crate::shadow::Shadow; pub use {crate::build::default_deny, crate::build::ShadowConst, crate::env::*, crate::git::*}; pub trait Format { fn human_format(&self) -> String; } } #[cfg(feature = "build")] pub use pub_export::*; pub const CARGO_CLIPPY_ALLOW_ALL: &str = "#[allow(clippy::all, clippy::pedantic, clippy::restriction, clippy::nursery)]"; /// Add a module with the provided name which contains the build information generated by `shadow-rs`. /// /// # Example /// /// ```ignore /// use shadow_rs::shadow; /// /// shadow!(my_build_information); /// /// fn main() { /// println!("I'm version {}!", my_build_information::VERSION); /// } /// ``` /// /// The convention, however, is to use `shadow!(build);`. #[macro_export] macro_rules! shadow { ($build_mod:ident) => { #[doc = r#"shadow-rs mod"#] pub mod $build_mod { include!(concat!(env!("OUT_DIR"), "/shadow.rs")); } }; } shadow-rs-1.1.1/src/shadow.rs000064400000000000000000000310451046102023000141600ustar 00000000000000use crate::build::{ConstType, ConstVal}; use crate::ci::CiType; use crate::env::{new_project, new_system_env}; use crate::gen_const::{ clap_long_version_branch_const, clap_long_version_tag_const, version_branch_const, version_tag_const, BUILD_CONST_CLAP_LONG_VERSION, BUILD_CONST_VERSION, }; use crate::git::new_git; use crate::{ get_std_env, BuildPattern, DateTime, SdResult, ShadowBuilder, ShadowConst, CARGO_CLIPPY_ALLOW_ALL, TAG, }; use std::collections::{BTreeMap, BTreeSet}; use std::fs::File; use std::io::Write; use std::path::Path; pub(crate) const DEFINE_SHADOW_RS: &str = "shadow.rs"; /// `shadow-rs` configuration. /// /// This struct encapsulates the configuration for the `shadow-rs` build process. It allows for fine-grained control over /// various aspects of the build, including file output, build constants, environment variables, deny lists, and build patterns. /// /// While it is possible to construct a [`Shadow`] instance manually, it is highly recommended to use the [`ShadowBuilder`] builder pattern structure /// provided by `shadow-rs`. The builder pattern simplifies the setup process and ensures that all necessary configurations are properly set up, /// allowing you to customize multiple aspects simultaneously, such as using a denylist and a hook function at the same time. /// /// # Fields /// /// * `f`: The file that `shadow-rs` writes build information to. This file will contain serialized build constants and other metadata. /// * `map`: A map of build constant identifiers to their corresponding `ConstVal`. These are the values that will be written into the file. /// * `std_env`: A map of environment variables obtained through [`std::env::vars`]. These variables can influence the build process. /// * `deny_const`: A set of build constant identifiers that should be excluded from the build process. This can be populated via [`ShadowBuilder::deny_const`]. /// * `out_path`: The path where the generated files will be placed. This is usually derived from the `OUT_DIR` environment variable but can be customized via [`ShadowBuilder::out_path`]. /// * `build_pattern`: Determines the strategy for triggering package rebuilds (`Lazy`, `RealTime`, or `Custom`). This affects when Cargo will rerun the build script and can be configured via [`ShadowBuilder::build_pattern`]. /// /// # Example /// /// ```no_run /// use std::collections::BTreeSet; /// use shadow_rs::{ShadowBuilder, BuildPattern, CARGO_TREE, CARGO_METADATA}; /// /// ShadowBuilder::builder() /// .build_pattern(BuildPattern::RealTime) /// .deny_const(BTreeSet::from([CARGO_TREE, CARGO_METADATA])) /// .build().unwrap(); /// ``` /// #[derive(Debug)] pub struct Shadow { /// The file that `shadow-rs` writes build information to. /// /// This file will contain all the necessary information about the build, including serialized build constants and other metadata. pub f: File, /// The values of build constants to be written. /// /// This is a mapping from `ShadowConst` identifiers to their corresponding `ConstVal` objects. Each entry in this map represents a build constant that will be included in the final build. pub map: BTreeMap, /// Build environment variables, obtained through [`std::env::vars`]. /// /// These environment variables can affect the build process and are captured here for consistency and reproducibility. pub std_env: BTreeMap, /// Constants in the deny list, passed through [`ShadowBuilder::deny_const`]. /// /// This set contains build constant identifiers that should be excluded from the build process. By specifying these, you can prevent certain constants from being written into the build file. pub deny_const: BTreeSet, /// The output path where generated files will be placed. /// /// This specifies the directory where the build script will write its output. It's typically set using the `OUT_DIR` environment variable but can be customized using [`ShadowBuilder::out_path`]. pub out_path: String, /// Determines the strategy for triggering package rebuilds. /// /// This field sets the pattern for how often the package should be rebuilt. Options include `Lazy`, `RealTime`, and `Custom`, each with its own implications on the build frequency and conditions under which a rebuild is triggered. /// It can be configured using [`ShadowBuilder::build_pattern`]. pub build_pattern: BuildPattern, } impl Shadow { /// Write the build configuration specified by this [`Shadow`] instance. /// The hook function is run as well, allowing it to append to `shadow-rs`'s output. pub fn hook(&self, f: F) -> SdResult<()> where F: Fn(&File) -> SdResult<()>, { let desc = r#"// Below code generated by project custom from by build.rs"#; writeln!(&self.f, "\n{desc}\n")?; f(&self.f)?; Ok(()) } /// Try to infer the CI system that we're currently running under. /// /// TODO: Recognize other CI types, especially Travis and Jenkins. fn try_ci(&self) -> CiType { if let Some(c) = self.std_env.get("GITLAB_CI") { if c == "true" { return CiType::Gitlab; } } if let Some(c) = self.std_env.get("GITHUB_ACTIONS") { if c == "true" { return CiType::Github; } } CiType::None } /// Checks if the specified build constant is in the deny list. /// /// # Arguments /// * `deny_const` - A value of type `ShadowConst` representing the build constant to check. /// /// # Returns /// * `true` if the build constant is present in the deny list; otherwise, `false`. pub fn deny_contains(&self, deny_const: ShadowConst) -> bool { self.deny_const.contains(&deny_const) } pub(crate) fn build_inner(builder: ShadowBuilder) -> SdResult { let out_path = builder.get_out_path()?; let src_path = builder.get_src_path()?; let build_pattern = builder.get_build_pattern().clone(); let deny_const = builder.get_deny_const().clone(); let out = { let path = Path::new(out_path); if !out_path.ends_with('/') { path.join(format!("{out_path}/{DEFINE_SHADOW_RS}")) } else { path.join(DEFINE_SHADOW_RS) } }; let mut shadow = Shadow { f: File::create(out)?, map: Default::default(), std_env: Default::default(), deny_const, out_path: out_path.to_string(), build_pattern, }; shadow.std_env = get_std_env(); let ci_type = shadow.try_ci(); let src_path = Path::new(src_path.as_str()); let mut map = new_git(src_path, ci_type, &shadow.std_env); for (k, v) in new_project(&shadow.std_env) { map.insert(k, v); } for (k, v) in new_system_env(&shadow) { map.insert(k, v); } shadow.map = map; // deny const shadow.filter_deny(); shadow.write_all()?; // handle hook if let Some(h) = builder.get_hook() { shadow.hook(h.hook_inner())? } Ok(shadow) } fn filter_deny(&mut self) { self.deny_const.iter().for_each(|x| { self.map.remove(x); }) } fn write_all(&mut self) -> SdResult<()> { self.gen_header()?; self.gen_const()?; //write version function let gen_version = self.gen_version()?; self.gen_build_in(gen_version)?; Ok(()) } fn gen_const(&mut self) -> SdResult<()> { let out_dir = &self.out_path; self.build_pattern.rerun_if(self.map.keys(), out_dir); for (k, v) in &self.map { self.write_const(k, v)?; } Ok(()) } fn gen_header(&self) -> SdResult<()> { let desc = format!( r#"// Code automatically generated by `shadow-rs` (https://github.com/baoyachi/shadow-rs), do not edit. // Author: https://www.github.com/baoyachi // Generation time: {} "#, DateTime::now().to_rfc2822() ); writeln!(&self.f, "{desc}\n\n")?; Ok(()) } fn write_const(&self, shadow_const: ShadowConst, val: &ConstVal) -> SdResult<()> { let desc = format!("#[doc=r#\"{}\"#]", val.desc); let define = match val.t { ConstType::Str => format!( "#[allow(dead_code)]\n\ {}\n\ pub const {} :{} = r#\"{}\"#;", CARGO_CLIPPY_ALLOW_ALL, shadow_const.to_ascii_uppercase(), ConstType::Str, val.v ), ConstType::Bool => format!( "#[allow(dead_code)]\n\ {}\n\ pub const {} :{} = {};", CARGO_CLIPPY_ALLOW_ALL, shadow_const.to_ascii_uppercase(), ConstType::Bool, val.v.parse::().unwrap() ), ConstType::Slice => format!( "#[allow(dead_code)]\n\ {}\n\ pub const {} :{} = &{:?};", CARGO_CLIPPY_ALLOW_ALL, shadow_const.to_ascii_uppercase(), ConstType::Slice, val.v.as_bytes() ), ConstType::Usize => format!( "#[allow(dead_code)]\n\ {}\n\ pub const {} :{} = {};", CARGO_CLIPPY_ALLOW_ALL, shadow_const.to_ascii_uppercase(), ConstType::Usize, val.v.parse::().unwrap_or_default() ), }; writeln!(&self.f, "{desc}")?; writeln!(&self.f, "{define}\n")?; Ok(()) } fn gen_version(&mut self) -> SdResult> { let (ver_fn, clap_long_ver_fn) = match self.map.get(TAG) { None => (version_branch_const(), clap_long_version_branch_const()), Some(tag) => { if !tag.v.is_empty() { (version_tag_const(), clap_long_version_tag_const()) } else { (version_branch_const(), clap_long_version_branch_const()) } } }; writeln!(&self.f, "{ver_fn}\n")?; writeln!(&self.f, "{clap_long_ver_fn}\n")?; Ok(vec![BUILD_CONST_VERSION, BUILD_CONST_CLAP_LONG_VERSION]) } fn gen_build_in(&self, gen_const: Vec<&'static str>) -> SdResult<()> { let mut print_val = String::from("\n"); // append gen const for (k, v) in &self.map { let tmp = match v.t { ConstType::Str | ConstType::Bool | ConstType::Usize => { format!(r#"{}println!("{k}:{{{k}}}\n");{}"#, "\t", "\n") } ConstType::Slice => { format!(r#"{}println!("{k}:{{:?}}\n",{});{}"#, "\t", k, "\n",) } }; print_val.push_str(tmp.as_str()); } // append gen fn for k in gen_const { let tmp = format!(r#"{}println!("{k}:{{{k}}}\n");{}"#, "\t", "\n"); print_val.push_str(tmp.as_str()); } #[cfg(not(feature = "no_std"))] { let everything_define = format!( "/// Prints all built-in `shadow-rs` build constants to standard output.\n\ #[allow(dead_code)]\n\ {CARGO_CLIPPY_ALLOW_ALL}\n\ pub fn print_build_in() {\ {{print_val}}\ }\n", ); writeln!(&self.f, "{everything_define}")?; use crate::gen_const::cargo_metadata_fn; writeln!(&self.f, "{}", cargo_metadata_fn(self))?; } Ok(()) } } #[cfg(test)] mod tests { use super::*; use crate::CARGO_TREE; use std::fs; #[test] fn test_build() -> SdResult<()> { ShadowBuilder::builder() .src_path("./") .out_path("./") .build()?; let shadow = fs::read_to_string(DEFINE_SHADOW_RS)?; assert!(!shadow.is_empty()); assert!(shadow.lines().count() > 0); fs::remove_file(DEFINE_SHADOW_RS)?; ShadowBuilder::builder() .src_path("./") .out_path("./") .deny_const(BTreeSet::from([CARGO_TREE])) .build()?; let content = fs::read_to_string(DEFINE_SHADOW_RS)?; assert!(!content.is_empty()); assert!(content.lines().count() > 0); let expect = "pub const CARGO_TREE :&str"; assert!(!content.contains(expect)); Ok(()) } }