How do I get the version of workspace member using a method from the parent member?

This question has a trash repo with minimum reproducible code.

How to reproduce

I have created the project like this:

cargo new foo --bin
cd foo
cargo new bar --bin

Then, I have updated Cargo.toml as below:

[package]
name = "foo"
version = "0.1.0" # notice the version
edition = "2018"

[dependencies]

[workspace]
members = ["bar"]

And then, I have updated bar/Cargo.toml seen as below:

[package]
name = "bar"
version = "0.2.0" # notice the version
edition = "2018"

[dependencies]
foo = { path = ".." }

Notice the versions of both Cargo.toml.

Then, I have created a function in src/lib.rs like this:

pub fn get_version<'a>() -> &'a str {
    env!("CARGO_PKG_VERSION")
}

Then, in bar/src/main.rs, I used it as below:

use foo::get_version;

fn main() {
    println!("{}", get_version());
}

Then I run cargo run -p bar.

The Problem

I will possibly have multiple workspace members that will use foo::get_version. I expect them to print out their own versions. In this example, foo is 0.1.0 and bar is 0.2.0.

So, I want the main function of bar to print out 0.2.0, yet it prints out 0.1.0, which is expected because env! macro takes environment variable and directly passes it into final binary. When I compile, it compiles foo::get_version, which is inside foo and CARGO_PKG_VERSION is 0.1.0 on compile time.

However, this is not the behavior I desire. I desire it to get the version of the crate that get_version is used and I want to keep it in foo because, again, I'd like to reuse it again and again on possible different workspace members.

Hence the question, how do I get the version of workspace member using a method from the parent member?


Environment

  • rustc 1.55.0

You can't do this with a function, but you could define a macro instead?

Time to learn, my greatest fear of all, how to write a macro, then. :laughing:

Keeping it open just in case somebody might have an easier solution.

It's not particularly difficult:

macro_rules! get_version {
    () => { env!("CARGO_PKG_VERSION") };
}

So, the final code in src/lib.rs is:

macro_rules! get_version {
    () => {
        env!("CARGO_PKG_VERSION")
    };
}

pub fn get_version<'a>() -> &'a str {
    get_version!()
}

However, this still gets the version of foo, which is 0.1.0. I'd like to get 0.2.0, which is of bar crate, a member of workspace. I'd like to keep get_version function in foo because, as I've said in the question:

So, this is not what I'm going for. :pray:

BTW, I've just looked at a couple of tutorials on macros. Declarative ones do not seem to be that hard. So, I've wanted to write a macro which basically (i) goes up parent directories, (ii) looks for Cargo.toml there, (iii) parse the TOML and finally (iv) get the results. However, I presume this will also result in 0.1.0 version since the final value will be compiled inside foo binary. So, it seems I'm kinda stuck here somewhere, idk.

You can't have a function at all. The other crates should be using the macro directly. The version it returns is the version of the crate the macro is used in.

Okay, now I understand.

The reason I have given a plain function was due to minimum reproducible code.

On the real codebase, I have a struct called Metadata and Metadata::default() method, which should get the version of the each workspace member and was supposed to be reusable.

So it seems that is impossible and I have to do Metadata::new(name, version, whatever) for each workspace member by hand.

Thanks for the insight.

You could also define a macro that returns a Metadata object.

1 Like

Oh wait, that's genius!

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.