├── .gitignore ├── .taplo.toml ├── sample ├── build.rs ├── Cargo.toml └── src │ └── main.rs ├── .editorconfig ├── Cargo.toml ├── .vscode └── settings.json ├── dependency-tree ├── build.rs ├── Cargo.toml └── src │ └── main.rs ├── tag_new_version.sh ├── .rustfmt.toml ├── .github └── workflows │ └── rust-ci.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.taplo.toml: -------------------------------------------------------------------------------- 1 | include = ["**/*.toml"] 2 | 3 | [formatting] 4 | column_width = 120 5 | indent_string = "\t" 6 | reorder_arrays = true 7 | reorder_keys = true 8 | -------------------------------------------------------------------------------- /sample/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Calling `build_info_build::build_script` collects all data and makes it available to `build_info::build_info!` 3 | // and `build_info::format!` in the main program. 4 | build_info_build::build_script(); 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | 6 | [.{gitignore,editorconfig}] 7 | insert_final_newline = true 8 | 9 | [*.{rs,toml,json,md}] 10 | indent_style = tab 11 | indent_size = 2 12 | insert_final_newline = true 13 | 14 | [*.yml] 15 | indent_style = space 16 | indent_size = 2 17 | insert_final_newline = true 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["build-info", "build-info-build", "build-info-common", "build-info-proc", "dependency-tree", "sample"] 3 | resolver = "3" 4 | 5 | [workspace.package] 6 | edition = "2024" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/danielschemmel/build-info/" 9 | rust-version = "1.86.0" 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "bincode", 4 | "chrono", 5 | "clippy", 6 | "concat", 7 | "datelike", 8 | "deps", 9 | "Deque", 10 | "endianness", 11 | "eval", 12 | "flate", 13 | "hasher", 14 | "nanos", 15 | "paren", 16 | "reparsing", 17 | "rustc", 18 | "semver", 19 | "serde" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /dependency-tree/build.rs: -------------------------------------------------------------------------------- 1 | use build_info_build::DependencyDepth; 2 | 3 | fn main() { 4 | // Calling `build_info_build::build_script` collects all data and makes it available to `build_info::build_info!` 5 | // and `build_info::format!` in the main program. 6 | // 7 | // Dependency collection needs to be enabled specifically. 8 | build_info_build::build_script().collect_dependencies(DependencyDepth::Full); 9 | } 10 | -------------------------------------------------------------------------------- /tag_new_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -u 5 | set -o pipefail 6 | 7 | cargo clippy -- -D warnings 8 | cargo test 9 | cargo test --all-features 10 | git diff --exit-code # check if unstaged changes exist 11 | git diff --cached --exit-code # check if staged, uncommitted changes exist 12 | for x in build-info build-info-build build-info-common build-info-proc ; do 13 | pushd $x 14 | cargo msrv verify 15 | popd 16 | done 17 | exec cargo workspaces version --all --exact --no-individual-tags --allow-branch main --force \* 18 | -------------------------------------------------------------------------------- /sample/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Daniel Schemmel "] 3 | description = "A sample program that uses the build-info crate." 4 | name = "sample" 5 | publish = false 6 | readme = "../README.md" 7 | version = "0.0.42" 8 | 9 | edition.workspace = true 10 | license.workspace = true 11 | repository.workspace = true 12 | rust-version.workspace = true 13 | 14 | [dependencies] 15 | build-info = { version = "=0.0.42", path = "../build-info" } 16 | 17 | [build-dependencies] 18 | build-info-build = { version = "=0.0.42", path = "../build-info-build" } 19 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | # See https://rust-lang.github.io/rustfmt/ for all possible settings & documentation. 2 | 3 | # When running `cargo fmt`, we don't need to set this option because it will be picked up from cargo. However, this 4 | # explicit setting is useful for (IDE) tools that run `rustfmt` directly, which otherwise default to "2015". 5 | edition = "2024" 6 | 7 | hard_tabs = true 8 | max_width = 120 9 | newline_style = "Unix" 10 | tab_spaces = 2 11 | 12 | comment_width = 120 13 | wrap_comments = true 14 | 15 | group_imports = "StdExternalCrate" 16 | imports_granularity = "Crate" 17 | 18 | use_field_init_shorthand = true 19 | -------------------------------------------------------------------------------- /dependency-tree/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Daniel Schemmel "] 3 | description = "A sample program that uses the build-info crate to print a tree of all dependencies." 4 | name = "dependency-tree" 5 | publish = false 6 | readme = "../README.md" 7 | version = "0.0.42" 8 | 9 | edition.workspace = true 10 | license.workspace = true 11 | repository.workspace = true 12 | rust-version.workspace = true 13 | 14 | [dependencies] 15 | build-info = { version = "=0.0.42", path = "../build-info" } 16 | 17 | [build-dependencies] 18 | build-info-build = { version = "=0.0.42", path = "../build-info-build" } 19 | -------------------------------------------------------------------------------- /dependency-tree/src/main.rs: -------------------------------------------------------------------------------- 1 | build_info::build_info!(fn version); 2 | 3 | fn print_crate_info(ci: &build_info::CrateInfo, self_indent: &str, nest_indent: &str) { 4 | println!( 5 | "{}{} v{} [{}]", 6 | self_indent, 7 | ci.name, 8 | ci.version, 9 | ci.enabled_features.join(", ") 10 | ); 11 | 12 | let nested_self = format!("{nest_indent}├─"); 13 | let nested_self_last = format!("{nest_indent}└─"); 14 | let nested_nest = format!("{nest_indent}│ "); 15 | let nested_nest_last = format!("{nest_indent} "); 16 | for (i, dep) in ci.dependencies.iter().enumerate() { 17 | if i + 1 < ci.dependencies.len() { 18 | print_crate_info(dep, &nested_self, &nested_nest); 19 | } else { 20 | print_crate_info(dep, &nested_self_last, &nested_nest_last); 21 | } 22 | } 23 | } 24 | 25 | fn main() { 26 | print_crate_info(&version().crate_info, "", ""); 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/rust-ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | push: 4 | schedule: 5 | - cron: '1 1 1 * *' 6 | 7 | jobs: 8 | lint: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: dtolnay/rust-toolchain@master 14 | with: 15 | toolchain: stable 16 | components: rustfmt, clippy 17 | - name: format 18 | run: cargo fmt -- --check 19 | - name: clippy 20 | run: cargo clippy --all-features --all-targets -- --deny warnings 21 | 22 | test: 23 | runs-on: ${{ matrix.os }} 24 | strategy: 25 | matrix: 26 | os: [ubuntu-latest, windows-latest, macos-latest] 27 | toolchain: [stable, beta, nightly] 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: dtolnay/rust-toolchain@master 32 | with: 33 | toolchain: ${{ matrix.toolchain }} 34 | components: clippy 35 | - name: Build 36 | run: cargo build --verbose 37 | - name: Run tests 38 | run: cargo test --verbose 39 | - name: Run clippy 40 | run: cargo clippy --all-targets 41 | 42 | taplo: 43 | name: Check toml file formatting 44 | runs-on: ubuntu-latest 45 | steps: 46 | - uses: taiki-e/install-action@v2 47 | with: 48 | tool: taplo 49 | - uses: actions/checkout@v4 50 | - run: taplo fmt --check 51 | -------------------------------------------------------------------------------- /sample/src/main.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code)] 2 | 3 | // Use the `build_info!` macro to generate a function `crate::build_info` that returns on object with the data that 4 | // is collected in the build script. 5 | // This macro supports visibility-specifiers, like `build_info!(pub fn how_this_crate_was_built)`. 6 | build_info::build_info!(fn build_info); 7 | 8 | // The function can have visibility specifiers, doc-comments and attributes. The choice of curly braces versus 9 | // parentheses is somewhat arbitrary, but will impact whether a terminating semicolon is expected, or not. 10 | build_info::build_info! { 11 | /// Public functions should really have a doc comment. 12 | #[inline] 13 | pub fn pub_build_info 14 | } 15 | 16 | fn main() { 17 | // We can now either use the `build_info` function to work with the collected data at runtime... 18 | println!("{:#?}", build_info()); 19 | 20 | // ... or format it directly to a single `&'static str` at compile time 21 | println!( 22 | "{}", 23 | build_info::format!("{{{} v{} built with {} at {}}}", $.crate_info.name, $.crate_info.version, $.compiler, $.timestamp) 24 | ); 25 | 26 | // Most types have a sensible default for formatting. Just printing the BuildInfo type directly is quick and easy: 27 | println!("{}", build_info::format!("{}", $)); 28 | 29 | // In fact, there is even a shorter shortcut: 30 | println!("{}", build_info::format!()); 31 | 32 | // Your milage *will* vary, but you can attempt to "do stuff" inside `format!` 33 | println!( 34 | "{}", 35 | build_info::format!("Copyright 2020-{compilation_year}", compilation_year = $.timestamp.format("%Y")) 36 | ); 37 | 38 | // Some macros can also be called inside `format!` 39 | println!( 40 | "{}", 41 | build_info::format!( 42 | concat!("We are using a RUSTC_WRAPPER: ", "{}"), 43 | option_env!("RUSTC_WRAPPER").is_some() 44 | ) 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | Begin by adding `build-info` as a `[dependency]` and `build-info-build` as a `[build-dependency]` to your [`Cargo.toml`](sample/Cargo.toml). 3 | By separating those two crates, pure compile-time dependencies, such as `git2` are not compiled into your final program. 4 | For this to work properly in workspaces, [ensure your resolver is set to at least version 2](https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html#cargos-new-feature-resolver). 5 | 6 | If it does not already exist, add a [`build.rs`](https://github.com/danielschemmel/build-info/tree/main/sample/build.rs) to your project's root, where you call `build_info_build::build_script()`. 7 | This will collect build information at compile time. 8 | 9 | Then, either use the `build_info!` macro to add a function that returns version information at runtime: 10 | ```rust,ignore 11 | build_info::build_info!(fn version); 12 | ``` 13 | or use `build_info::format!` to generate a string at compile time: 14 | ```rust,ignore 15 | // sample output: "{sample v0.0.13 built with rustc 1.45.0-nightly (4bd32c980 2020-05-29) at 2020-05-30 11:22:46Z}" 16 | build_info::format!("{{{} v{} built with {} at {}}}", $.crate_info.name, $.crate_info.version, $.compiler, $.timestamp) 17 | ``` 18 | 19 | The [sample](https://github.com/danielschemmel/build-info/tree/main/sample) project shows both variants. 20 | 21 | ## Features 22 | The `build_info` crate supports several feature flags: 23 | - The `runtime` feature enables `build_info::build_info!`. It is enabled by default, but if you intend to only use `build_info::format!`, it is safe to disable this flag. Disable this feature to use `build_info` in a `no_std` context. 24 | - The `chrono` feature enables the default features of the `chrono` package, which is used by `build_info::build_info!`. It is disabled by default. 25 | - The `serde` feature adds `Serialize`/`Deserialize` support to the types used by `build_info::build_info!`. It is disabled by default. 26 | 27 | # Caveats 28 | The build script will ask cargo to rerun it whenever the project or the currently checked out commit changes. 29 | It will not necessarily be rerun if only the dependencies change (`build_info_build::build_script` will try to find the lockfile and depend on it, but it is not really aware of any of the more intricate features, such as, cargo workspaces). 30 | Please open an issue if your specific use case requires a more strict rerun policy for `build.rs` and include a short description what additional files should trigger a rebuild when changed. 31 | 32 | # Copyright and license 33 | This project is copyright its authors and licensed under either of 34 | 35 | - Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) 36 | - MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) 37 | 38 | at your option. 39 | 40 | ## Contributions 41 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 42 | --------------------------------------------------------------------------------