# Generating a versions.rs at build time

**URL:** <https://users.rust-lang.org/t/generating-a-versions-rs-at-build-time/48976>\
**Category:** uncategorized\
**Created:** [September 18, 2020, 8:58pm UTC](https://users.rust-lang.org/t/generating-a-versions-rs-at-build-time/48976 "2020-09-18T20:58:22Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![federicomenaquintero](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/federicomenaquintero/32/3813_2.png) [@federicomenaquintero](https://users.rust-lang.org/u/federicomenaquintero)\
**Post date:** [September 18, 2020, 8:58pm UTC](https://users.rust-lang.org/t/generating-a-versions-rs-at-build-time/48976/1 "2020-09-18T20:58:23Z")

</div>

The C ABI of librsvg exports a few `uint_t` variables like `rsvg_major_version`, `rsvg_minor_version`, etc.

We are in the process of moving this to Rust so that the entire `librsvg.so` can be built with `cargo-c` (right now all the code in the library is in Rust, and is wrapped by stub functions in C. Those variables are also in C files).

Ideally those version numbers would be defined once, in the `configure.ac` script, and propagated down to a generated `versions.rs` source file. (Commentary: librsvg is fine with not letting `cargo build` run standalone for the C library; we need to do some postprocessing for it anyway, which is not cargo-c's business.)

I see two options:

1. Set some VERSION\_MAJOR, VERSION\_MINOR environment variables in the build scripts. Have a `build.rs` pick these up and generate a `versions.rs` in the build directory, which is later included somewhere in the library.
2. Have a `versions.rs.in` onto which the build scripts do textual substitution to generate `versions.rs` in the source directory.

I suppose (1) is the "conventionally build.rs way", and (2) is the "keep the existing build scripts mostly unchanged".

Has anyone had to do something similar to this? Any preferred approaches? Thanks!

---

<div class="post-metadata">

**Author:** ![notriddle](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/notriddle/32/51307_2.png) [@notriddle](https://users.rust-lang.org/u/notriddle)\
**Post date:** [September 18, 2020, 9:32pm UTC](https://users.rust-lang.org/t/generating-a-versions-rs-at-build-time/48976/2 "2020-09-18T21:32:19Z")

</div>

You could be totally unprincipled and just parse `configure.ac` from your `build.rs` script.

```rust
let mut file = File::open("../configure.ac").expect("builds must take place within the librsvg source tree");
let major_regex = Regex::new(r#"^m4_define\(\[rsvg_major_version\],\[(\d+)\]\)"#).unwrap();
let minor_regex = Regex::new(r#"^m4_define\(\[rsvg_minor_version\],\[(\d+)\]\)"#).unwrap();
let micro_regex = Regex::new(r#"^m4_define\(\[rsvg_micro_version\],\[(\d+)\]\)"#).unwrap();
let mut major = None;
let mut minor = None;
let mut micro = None;
for line in BufRead::new(file).lines() {
    if let Some(nums) = major_regex.captures(line) {
        major = Some(nums.get(1).expect("major_regex has one capture group").as_str());
    } else if let Some(nums) = minor_regex.captures(line) {
        minor = Some(nums.get(1).expect("minor_regex has one capture group").as_str());
    } else if let Some(nums) = micro_regex.captures(line) {
        micro = Some(nums.get(1).expect("micro_regex has one capture group").as_str());
    }
}
mem::drop(file);
let mut file = File::create(format!("{}/version.rs", env::get("OUT_DIR"))).expect("open version.rs for writing");
file.write_all(format!(
  r#"fn major_version() -> u32 { {} } fn minor_version() -> u32 { {} } fn micro_version() -> u32 { {} }"#,
  major.expect("major"),
  minor.expect("minor"),
  micro.expect("micro"),
).as_bytes()).expect("write_all");

```

A lot of Rust-based websites do something similar to include the git commit in a generated source file, [like this](https://github.com/rust-lang/docs.rs/blob/master/build.rs), because they use continuous deployment and don't really _have_ version numbers.

---

<div class="post-metadata">

**Author:** ![2e71828](https://avatars.discourse-cdn.com/v4/letter/2/3e96dc/32.png) [@2e71828](https://users.rust-lang.org/u/2e71828)\
**Post date:** [September 18, 2020, 9:54pm UTC](https://users.rust-lang.org/t/generating-a-versions-rs-at-build-time/48976/3 "2020-09-18T21:54:11Z")

</div>

> [@federicomenaquintero](#):
>
> Set some VERSION\_MAJOR, VERSION\_MINOR environment variables in the build scripts. Have a `build.rs` pick these up and generate a `versions.rs` in the build directory, which is later included somewhere in the library.

If the values you need are already in environment variables, you can use [`env!`](https://doc.rust-lang.org/std/macro.env.html) to import them as `str`s at compile time, without needing anything in `build.rs`.

---

<div class="post-metadata">

**Author:** ![federicomenaquintero](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/federicomenaquintero/32/3813_2.png) [@federicomenaquintero](https://users.rust-lang.org/u/federicomenaquintero)\
**Post date:** [September 18, 2020, 10:01pm UTC](https://users.rust-lang.org/t/generating-a-versions-rs-at-build-time/48976/4 "2020-09-18T22:01:41Z")

</div>

This is pure evil and I love it 😄 Thank you!

---

<div class="post-metadata">

**Author:** ![federicomenaquintero](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/federicomenaquintero/32/3813_2.png) [@federicomenaquintero](https://users.rust-lang.org/u/federicomenaquintero)\
**Post date:** [September 18, 2020, 10:03pm UTC](https://users.rust-lang.org/t/generating-a-versions-rs-at-build-time/48976/5 "2020-09-18T22:03:03Z")

</div>

Oh, yeah, `env!` was my first approach, but unfortunately I need the values as numbers. (For historical reasons they are numbers in the C code; for a new library I'd probably just export the version as a string.)

---

<div class="post-metadata">

**Author:** ![2e71828](https://avatars.discourse-cdn.com/v4/letter/2/3e96dc/32.png) [@2e71828](https://users.rust-lang.org/u/2e71828)\
**Post date:** [September 18, 2020, 10:42pm UTC](https://users.rust-lang.org/t/generating-a-versions-rs-at-build-time/48976/6 "2020-09-18T22:42:20Z")

</div>

> [@federicomenaquintero](#):
>
> Oh, yeah, `env!` was my first approach, but unfortunately I need the values as numbers.

That’s doable, too, but is complicated by `const fn` being relatively weak right now:

```rust
const VERSION: &[u8] = "2.13.4".as_bytes();

const fn get_ver_field(mut x:u8)->u16 {
    let mut i:usize = 0;

    while x > 0 {
        if VERSION[i] == '.' as u8 { x -= 1; }
        i += 1;
    }

    let mut result:u16 = 0;

    while i < VERSION.len() && VERSION[i] != '.' as u8 {
        result *= 10;
        result += VERSION[i] as u16 - '0' as u16;
        i += 1;
    }

    result
}
    
pub static MAJOR:u16 = get_ver_field(0);
pub static MINOR:u16 = get_ver_field(1);
pub static PATCH:u16 = get_ver_field(2);

fn main() {
    println!("{}.{}.{}", MAJOR,MINOR,PATCH);
}

```

([Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1b14e1dd61d890604755d849fcfac91e))

---

<div class="post-metadata">

**Author:** ![system](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/system/32/51372_2.png) [@system](https://users.rust-lang.org/u/system)\
**Post date:** [December 17, 2020, 10:42pm UTC](https://users.rust-lang.org/t/generating-a-versions-rs-at-build-time/48976/7 "2020-12-17T22:42:22Z")

</div>

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.
