From 85c04b3ab1d8446b0a8337ad738ea11aa94f45b4 Mon Sep 17 00:00:00 2001 From: Samuel Burnham <45365069+samuelburnham@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:08:45 -0500 Subject: [PATCH] ci: Add comparative GPU benchmarks on `merge_group` (#238) * ci: Add comparative benchmarks on `merge_group` (WIP) * Add other benchmarks and refactor * Remove caching for now and test action on `pull_request` --- .github/PERF_REGRESSION.md | 7 ++ .github/tables.toml | 6 ++ .github/workflows/gpu-bench.yml | 131 ++++++++++++++++++++++++++ Cargo.toml | 4 + benches/common/mod.rs | 43 +++++++++ benches/compressed-snark-supernova.rs | 32 ++++--- benches/compressed-snark.rs | 26 +++-- benches/justfile | 36 +++++++ benches/recursive-snark-supernova.rs | 23 +++-- benches/recursive-snark.rs | 16 +++- build.rs | 8 ++ 11 files changed, 299 insertions(+), 33 deletions(-) create mode 100644 .github/PERF_REGRESSION.md create mode 100644 .github/tables.toml create mode 100644 .github/workflows/gpu-bench.yml create mode 100644 benches/common/mod.rs create mode 100644 benches/justfile create mode 100644 build.rs diff --git a/.github/PERF_REGRESSION.md b/.github/PERF_REGRESSION.md new file mode 100644 index 000000000..090be25bd --- /dev/null +++ b/.github/PERF_REGRESSION.md @@ -0,0 +1,7 @@ +--- +title: ":rotating_light: Performance regression in #{{ env.PR_NUMBER }}" +labels: P-Performance, automated issue +--- +Regression >= {{ env.NOISE_THRESHOLD }} found during merge of: #{{ env.PR_NUMBER }} +Commit: {{ env.GIT_SHA }} +Triggered by: {{ env.WORKFLOW_URL }} \ No newline at end of file diff --git a/.github/tables.toml b/.github/tables.toml new file mode 100644 index 000000000..cb4e6595e --- /dev/null +++ b/.github/tables.toml @@ -0,0 +1,6 @@ +[table_comments] + +[top_comments] +Overview = """ +This benchmark report shows the Arecibo GPU benchmarks. +""" \ No newline at end of file diff --git a/.github/workflows/gpu-bench.yml b/.github/workflows/gpu-bench.yml new file mode 100644 index 000000000..db826474f --- /dev/null +++ b/.github/workflows/gpu-bench.yml @@ -0,0 +1,131 @@ +# Run final tests only when attempting to merge, shown as skipped status checks beforehand +name: GPU benchmark regression test + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + branches: [dev] + merge_group: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + # Run comparative benchmark against dev, open issue on regression + gpu-benchmark: + if: github.event_name != 'pull_request' || github.event.action == 'enqueued' + name: Run benchmarks on GPU + runs-on: [self-hosted, gpu-bench] + steps: + - uses: actions/checkout@v4 + with: + repository: lurk-lab/ci-workflows + - uses: ./.github/actions/gpu-setup + with: + gpu-framework: 'cuda' + - uses: ./.github/actions/ci-env + - uses: actions/checkout@v4 + # Install dependencies + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@v2 + with: + tool: just@1.22 + - name: Install criterion + run: | + cargo install cargo-criterion + cargo install criterion-table + - name: Set bench output format and base SHA + run: | + echo "ARECIBO_BENCH_OUTPUT=commit-comment" | tee -a $GITHUB_ENV + echo "BASE_COMMIT=${{ github.event.merge_group.base_sha }}" | tee -a $GITHUB_ENV + GPU_NAME=$(nvidia-smi --query-gpu=gpu_name --format=csv,noheader,nounits | tail -n1) + echo "GPU_ID=$(echo $GPU_NAME | awk '{ print $NF }')" | tee -a $GITHUB_ENV + echo "GPU_NAME=$GPU_NAME" | tee -a $GITHUB_ENV + # Checkout base branch for comparative bench + - uses: actions/checkout@v4 + with: + ref: dev + path: dev + # Copy the script so the base can bench with the same parameters + - name: Run GPU bench on base branch + run: | + # Copy justfile to dev, overwriting existing config with that of PR branch + cp ../benches/justfile . + # Run benchmark + just gpu-bench-ci recursive-snark recursive-snark-supernova compressed-snark compressed-snark-supernova + # Copy bench output to PR branch + cp *-${{ env.BASE_COMMIT }}.json .. + working-directory: ${{ github.workspace }}/dev + - name: Run GPU bench on PR branch + run: | + just gpu-bench-ci recursive-snark recursive-snark-supernova compressed-snark compressed-snark-supernova + cp *-${{ github.sha }}.json .. + working-directory: ${{ github.workspace }}/benches + - name: copy the benchmark template and prepare it with data + run: | + cp .github/tables.toml . + # Get CPU model + CPU_MODEL=$(grep '^model name' /proc/cpuinfo | head -1 | awk -F ': ' '{ print $2 }') + # Get vCPU count + NUM_VCPUS=$(nproc --all) + # Get total RAM in GB + TOTAL_RAM=$(grep MemTotal /proc/meminfo | awk '{$2=$2/(1024^2); print int($2), "GB RAM";}') + WORKFLOW_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + + # Use conditionals to ensure that only non-empty variables are inserted + [[ ! -z "${{ env.GPU_NAME }}" ]] && sed -i "/^\"\"\"$/i ${{ env.GPU_NAME }}" tables.toml + [[ ! -z "$CPU_MODEL" ]] && sed -i "/^\"\"\"$/i $CPU_MODEL" tables.toml + [[ ! -z "$NUM_VCPUS" ]] && sed -i "/^\"\"\"$/i $NUM_VCPUS" tables.toml + [[ ! -z "$TOTAL_RAM" ]] && sed -i "/^\"\"\"$/i $TOTAL_RAM" tables.toml + sed -i "/^\"\"\"$/i Workflow run: $WORKFLOW_URL" tables.toml + echo "WORKFLOW_URL=$WORKFLOW_URL" | tee -a $GITHUB_ENV + working-directory: ${{ github.workspace }} + # Create a `criterion-table` and write in commit comment + - name: Run `criterion-table` + run: | + cat recursive-snark-${{ env.BASE_COMMIT }}.json recursive-snark-${{ github.sha }}.json \ + recursive-snark-supernova-${{ env.BASE_COMMIT }}.json recursive-snark-supernova- ${{ github.sha }}.json \ + compressed-snark-${{ env.BASE_COMMIT }}.json compressed-snark-${{ github.sha }}.json \ + compressed-snark-supernova-${{ env.BASE_COMMIT }}.json compressed-snark-supernova- ${{ github.sha }}.json \ + | criterion-table > BENCHMARKS.md + - name: Write bench on commit comment + uses: peter-evans/commit-comment@v3 + with: + body-path: BENCHMARKS.md + # Check for a slowdown >= `$ARECIBO_NOISE_THRESHOLD` (fallback is 5%). If so, open an issue but don't block merge + - name: Check for perf regression + id: regression-check + run: | + REGRESSIONS=$(awk -F'[*x]' '/slower/{print $12}' BENCHMARKS.md) + echo $regressions + + if [ ! -z "${{ env.ARECIBO_NOISE_THRESHOLD}}" ]; then + NOISE_THRESHOLD=$(echo "1+${{ env.ARECIBO_NOISE_THRESHOLD }}" | bc) + else + NOISE_THRESHOLD=1.05 + fi + + for r in $REGRESSIONS + do + if (( $(echo "$r >= $NOISE_THRESHOLD" | bc -l) )) + then + exit 1 + fi + done + echo "NOISE_THRESHOLD=$NOISE_THRESHOLD" | tee -a $GITHUB_ENV + continue-on-error: true + # Not possible to use ${{ github.event.number }} with the `merge_group` trigger + - name: Get PR number from merge branch + run: | + echo "PR_NUMBER=$(echo ${{ github.event.merge_group.head_ref }} | sed -e 's/.*pr-\(.*\)-.*/\1/')" | tee -a $GITHUB_ENV + - uses: JasonEtco/create-an-issue@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ env.PR_NUMBER }} + GIT_SHA: ${{ github.sha }} + WORKFLOW_URL: ${{ env.WORKFLOW_URL }} + NOISE_THRESHOLD: $${{ env.NOISE_THRESHOLD }} + with: + filename: .github/PERF_REGRESSION.md \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index d29406c9b..5829d8742 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,10 @@ hex = "0.4.3" sha2 = "0.10.7" tracing-test = "0.2.4" expect-test = "1.4.1" +anyhow = "1.0.72" + +[build-dependencies] +vergen = { version = "8", features = ["build", "git", "gitcl"] } [[bench]] name = "recursive-snark" diff --git a/benches/common/mod.rs b/benches/common/mod.rs new file mode 100644 index 000000000..43ec5ac13 --- /dev/null +++ b/benches/common/mod.rs @@ -0,0 +1,43 @@ +use anyhow::anyhow; +use criterion::BenchmarkId; + +// TODO: Why Copy and &'static str over String? +#[derive(Clone, Debug, Copy)] +pub(crate) struct BenchParams { + pub step_size: usize, + pub date: &'static str, + pub sha: &'static str, +} +impl BenchParams { + pub(crate) fn bench_id(&self, name: &str) -> BenchmarkId { + let output_type = bench_output_env().unwrap_or("stdout".into()); + match output_type.as_ref() { + "pr-comment" => BenchmarkId::new(name, format!("StepCircuitSize-{}", self.step_size)), + "commit-comment" => BenchmarkId::new( + format!("ref={}", self.sha), + format!("{}-StepCircuitSize-{}", name, self.step_size), + ), + // TODO: refine "gh-pages" + _ => BenchmarkId::new( + name, + format!( + "StepCircuitSize-{}-{}-{}", + self.step_size, self.sha, self.date + ), + ), + } + } +} + +fn bench_output_env() -> anyhow::Result { + std::env::var("ARECIBO_BENCH_OUTPUT").map_err(|e| anyhow!("Bench output env var isn't set: {e}")) +} + +pub(crate) fn noise_threshold_env() -> anyhow::Result { + std::env::var("ARECIBO_BENCH_NOISE_THRESHOLD") + .map_err(|e| anyhow!("Noise threshold env var isn't set: {e}")) + .and_then(|nt| { + nt.parse::() + .map_err(|e| anyhow!("Failed to parse noise threshold: {e}")) + }) +} diff --git a/benches/compressed-snark-supernova.rs b/benches/compressed-snark-supernova.rs index 9c1e73484..44a4607b6 100644 --- a/benches/compressed-snark-supernova.rs +++ b/benches/compressed-snark-supernova.rs @@ -11,14 +11,17 @@ use criterion::{measurement::WallTime, *}; use ff::PrimeField; use std::time::Duration; +mod common; +use common::{noise_threshold_env, BenchParams}; + type E1 = arecibo::provider::PallasEngine; type E2 = arecibo::provider::VestaEngine; type EE1 = arecibo::provider::ipa_pc::EvaluationEngine; type EE2 = arecibo::provider::ipa_pc::EvaluationEngine; -// SNARKs without computation commitmnets +// SNARKs without computation commitments type S1 = arecibo::spartan::batched::BatchedRelaxedR1CSSNARK; type S2 = arecibo::spartan::snark::RelaxedR1CSSNARK; -// SNARKs with computation commitmnets +// SNARKs with computation commitments type SS1 = arecibo::spartan::batched_ppsnark::BatchedRelaxedR1CSSNARK; type SS2 = arecibo::spartan::ppsnark::RelaxedR1CSSNARK; @@ -162,8 +165,14 @@ fn bench_compressed_snark_internal_with_arity< let (prover_key, verifier_key) = CompressedSNARK::<_, _, _, _, S1, S2>::setup(&pp).unwrap(); + let bench_params = BenchParams { + step_size: num_cons, + date: env!("VERGEN_GIT_COMMIT_DATE"), + sha: env!("VERGEN_GIT_SHA"), + }; + // Benchmark the prove time - group.bench_function("Prove", |b| { + group.bench_function(bench_params.bench_id("Prove"), |b| { b.iter(|| { assert!(CompressedSNARK::<_, _, _, _, S1, S2>::prove( black_box(&pp), @@ -180,7 +189,7 @@ fn bench_compressed_snark_internal_with_arity< let compressed_snark = res.unwrap(); // Benchmark the verification time - group.bench_function("Verify", |b| { + group.bench_function(bench_params.bench_id("Verify"), |b| { b.iter(|| { assert!(black_box(&compressed_snark) .verify( @@ -211,10 +220,9 @@ fn bench_one_augmented_circuit_compressed_snark(c: &mut Criterion) { // number of constraints in the step circuit let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY; - let mut group = c.benchmark_group(format!( - "CompressedSNARKSuperNova-1circuit-StepCircuitSize-{num_cons}" - )); + let mut group = c.benchmark_group("CompressedSNARKSuperNova-1circuit"); group.sample_size(NUM_SAMPLES); + group.noise_threshold(noise_threshold_env().unwrap_or(0.05)); bench_compressed_snark_internal_with_arity::(&mut group, 1, num_cons); @@ -239,10 +247,9 @@ fn bench_two_augmented_circuit_compressed_snark(c: &mut Criterion) { // number of constraints in the step circuit let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY; - let mut group = c.benchmark_group(format!( - "CompressedSNARKSuperNova-2circuit-StepCircuitSize-{num_cons}" - )); + let mut group = c.benchmark_group("CompressedSNARKSuperNova-2circuit"); group.sample_size(NUM_SAMPLES); + group.noise_threshold(noise_threshold_env().unwrap_or(0.05)); bench_compressed_snark_internal_with_arity::(&mut group, 2, num_cons); @@ -267,10 +274,9 @@ fn bench_two_augmented_circuit_compressed_snark_with_computational_commitments(c // number of constraints in the step circuit let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY; - let mut group = c.benchmark_group(format!( - "CompressedSNARKSuperNova-Commitments-2circuit-StepCircuitSize-{num_cons}" - )); + let mut group = c.benchmark_group("CompressedSNARKSuperNova-Commitments-2circuit"); group.sample_size(NUM_SAMPLES); + group.noise_threshold(noise_threshold_env().unwrap_or(0.05)); bench_compressed_snark_internal_with_arity::(&mut group, 2, num_cons); diff --git a/benches/compressed-snark.rs b/benches/compressed-snark.rs index a620c93cb..c3eab2a17 100644 --- a/benches/compressed-snark.rs +++ b/benches/compressed-snark.rs @@ -14,6 +14,9 @@ use criterion::{measurement::WallTime, *}; use ff::PrimeField; use std::time::Duration; +mod common; +use common::{noise_threshold_env, BenchParams}; + type E1 = PallasEngine; type E2 = VestaEngine; type EE1 = arecibo::provider::ipa_pc::EvaluationEngine; @@ -101,8 +104,14 @@ fn bench_compressed_snark_internal, S2: RelaxedR1C assert!(res.is_ok()); } + let bench_params = BenchParams { + step_size: num_cons, + date: env!("VERGEN_GIT_COMMIT_DATE"), + sha: env!("VERGEN_GIT_SHA"), + }; + // Bench time to produce a compressed SNARK - group.bench_function("Prove", |b| { + group.bench_function(bench_params.bench_id("Prove"), |b| { b.iter(|| { assert!(CompressedSNARK::<_, _, _, _, S1, S2>::prove( black_box(&pp), @@ -117,7 +126,7 @@ fn bench_compressed_snark_internal, S2: RelaxedR1C let compressed_snark = res.unwrap(); // Benchmark the verification time - group.bench_function("Verify", |b| { + group.bench_function(bench_params.bench_id("Verify"), |b| { b.iter(|| { assert!(black_box(&compressed_snark) .verify( @@ -148,8 +157,9 @@ fn bench_compressed_snark(c: &mut Criterion) { // number of constraints in the step circuit let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY; - let mut group = c.benchmark_group(format!("CompressedSNARK-StepCircuitSize-{num_cons}")); + let mut group = c.benchmark_group("CompressedSNARK"); group.sample_size(NUM_SAMPLES); + group.noise_threshold(noise_threshold_env().unwrap_or(0.05)); bench_compressed_snark_internal::(&mut group, num_cons); @@ -172,12 +182,10 @@ fn bench_compressed_snark_with_computational_commitments(c: &mut Criterion) { // number of constraints in the step circuit let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY; - let mut group = c.benchmark_group(format!( - "CompressedSNARK-Commitments-StepCircuitSize-{num_cons}" - )); - group - .sampling_mode(SamplingMode::Flat) - .sample_size(NUM_SAMPLES); + let mut group = c.benchmark_group("CompressedSNARK-Commitments"); + group.sampling_mode(SamplingMode::Flat); + group.sample_size(NUM_SAMPLES); + group.noise_threshold(noise_threshold_env().unwrap_or(0.05)); bench_compressed_snark_internal::(&mut group, num_cons); diff --git a/benches/justfile b/benches/justfile new file mode 100644 index 000000000..4e74be610 --- /dev/null +++ b/benches/justfile @@ -0,0 +1,36 @@ +# Install with `cargo install just` +# Usage: `just ` +set dotenv-load +set dotenv-filename := "bench.env" +set ignore-comments := true + +commit := `git rev-parse HEAD` + +# Run CPU benchmarks +bench +benches: + #!/bin/sh + for bench in {{benches}}; do + cargo criterion --bench $bench + done + +gpu-env: + # The `compute`/`sm` number corresponds to the Nvidia GPU architecture + # In this case, the self-hosted machine uses the Ampere architecture, but we want this to be configurable + # See https://arnon.dk/matching-sm-architectures-arch-and-gencode-for-various-nvidia-cards/ + export CUDA_ARCH := `nvidia-smi --query-gpu=compute_cap --format=csv,noheader | sed 's/\.//g'` + export EC_GPU_CUDA_NVCC_ARGS := "--fatbin --gpu-architecture=sm_$CUDA_ARCH --generate-code=arch=compute_$CUDA_ARCH,code=sm_$CUDA_ARCH" + export EC_GPU_FRAMEWORK := "cuda" + +# Run CUDA benchmarks on GPU +gpu-bench +benches: gpu-env + #!/bin/sh + for bench in {{benches}}; do + cargo criterion --bench $bench --features "cuda" + done + +# Run CUDA benchmarks on GPU, tuned for CI +gpu-bench-ci +benches: + #!/bin/sh + for bench in {{benches}}; do + cargo criterion --bench $bench --features "cuda" --message-format=json > "$bench-{{commit}}".json + done \ No newline at end of file diff --git a/benches/recursive-snark-supernova.rs b/benches/recursive-snark-supernova.rs index e2bd83e11..23b0d4c2b 100644 --- a/benches/recursive-snark-supernova.rs +++ b/benches/recursive-snark-supernova.rs @@ -12,6 +12,9 @@ use criterion::{measurement::WallTime, *}; use ff::PrimeField; use std::time::Duration; +mod common; +use common::{noise_threshold_env, BenchParams}; + // To run these benchmarks, first download `criterion` with `cargo install cargo-criterion`. // Then `cargo criterion --bench recursive-snark-supernova`. The results are located in `target/criterion/data/`. // For flamegraphs, run `cargo criterion --bench recursive-snark-supernova --features flamegraph -- --profile-time `. @@ -152,8 +155,14 @@ fn bench_recursive_snark_internal_with_arity( assert!(recursive_snark_option.is_some()); let recursive_snark = recursive_snark_option.unwrap(); + let bench_params = BenchParams { + step_size: num_cons, + date: env!("VERGEN_GIT_COMMIT_DATE"), + sha: env!("VERGEN_GIT_SHA"), + }; + // Benchmark the prove time - group.bench_function("Prove", |b| { + group.bench_function(bench_params.bench_id("Prove"), |b| { b.iter(|| { // produce a recursive SNARK for a step of the recursion assert!(black_box(&mut recursive_snark.clone()) @@ -167,7 +176,7 @@ fn bench_recursive_snark_internal_with_arity( }); // Benchmark the verification time - group.bench_function("Verify", |b| { + group.bench_function(bench_params.bench_id("Verify"), |b| { b.iter(|| { assert!(black_box(&mut recursive_snark.clone()) .verify( @@ -197,10 +206,9 @@ fn bench_one_augmented_circuit_recursive_snark(c: &mut Criterion) { // number of constraints in the step circuit let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY; - let mut group = c.benchmark_group(format!( - "RecursiveSNARKSuperNova-1circuit-StepCircuitSize-{num_cons}" - )); + let mut group = c.benchmark_group("RecursiveSNARKSuperNova-1circuit"); group.sample_size(NUM_SAMPLES); + group.noise_threshold(noise_threshold_env().unwrap_or(0.05)); bench_recursive_snark_internal_with_arity(&mut group, 1, num_cons); group.finish(); @@ -224,10 +232,9 @@ fn bench_two_augmented_circuit_recursive_snark(c: &mut Criterion) { // number of constraints in the step circuit let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY; - let mut group = c.benchmark_group(format!( - "RecursiveSNARKSuperNova-2circuit-StepCircuitSize-{num_cons}" - )); + let mut group = c.benchmark_group("RecursiveSNARKSuperNova-2circuit"); group.sample_size(NUM_SAMPLES); + group.noise_threshold(noise_threshold_env().unwrap_or(0.05)); bench_recursive_snark_internal_with_arity(&mut group, 2, num_cons); group.finish(); diff --git a/benches/recursive-snark.rs b/benches/recursive-snark.rs index 353b7fa56..08d3ffac4 100644 --- a/benches/recursive-snark.rs +++ b/benches/recursive-snark.rs @@ -14,6 +14,9 @@ use criterion::*; use ff::PrimeField; use std::time::Duration; +mod common; +use common::{noise_threshold_env, BenchParams}; + type E1 = PallasEngine; type E2 = VestaEngine; type C1 = NonTrivialCircuit<::Scalar>; @@ -62,8 +65,9 @@ fn bench_recursive_snark(c: &mut Criterion) { // number of constraints in the step circuit let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY; - let mut group = c.benchmark_group(format!("RecursiveSNARK-StepCircuitSize-{num_cons}")); + let mut group = c.benchmark_group("RecursiveSNARK"); group.sample_size(NUM_SAMPLES); + group.noise_threshold(noise_threshold_env().unwrap_or(0.05)); let c_primary = NonTrivialCircuit::new(num_cons); let c_secondary = TrivialCircuit::default(); @@ -104,7 +108,13 @@ fn bench_recursive_snark(c: &mut Criterion) { assert!(res.is_ok()); } - group.bench_function("Prove", |b| { + let bench_params = BenchParams { + step_size: num_cons, + date: env!("VERGEN_GIT_COMMIT_DATE"), + sha: env!("VERGEN_GIT_SHA"), + }; + + group.bench_function(bench_params.bench_id("Prove"), |b| { b.iter(|| { // produce a recursive SNARK for a step of the recursion assert!(black_box(&mut recursive_snark.clone()) @@ -118,7 +128,7 @@ fn bench_recursive_snark(c: &mut Criterion) { }); // Benchmark the verification time - group.bench_function("Verify", |b| { + group.bench_function(bench_params.bench_id("Verify"), |b| { b.iter(|| { assert!(black_box(&recursive_snark) .verify( diff --git a/build.rs b/build.rs new file mode 100644 index 000000000..e39d2522e --- /dev/null +++ b/build.rs @@ -0,0 +1,8 @@ +use std::error::Error; +use vergen::EmitBuilder; + +fn main() -> Result<(), Box> { + // Emit the instructions + EmitBuilder::builder().all_git().emit()?; + Ok(()) +}