Search code examples
rustrust-cargo

Return version got from Cargo.toml in a static libreary generated with Rust


I have developed a library using Rust which exposes a function to be used by a C program which returns the library version based in Cargo metadata.

This is the function:

#[no_mangle]
pub extern "C" fn get_version() ->  *const libc::c_char {
    // Get Cargo metadata
    let metadata = cargo_metadata::MetadataCommand::new()
        .exec()
        .expect("Failed to get Cargo metadata");
    let root = metadata.root_package().unwrap();
    let version = root.version.to_string();

    return CString::new(version).unwrap().into_raw();
}

This is the Cargo.toml file (simplified):

[package]
name = "acme"
version = "0.1.0"
edition = "2021"

[dependencies]
...
libc = "0.2"
cargo_metadata = "0.18.1"

[lib]
crate-type=["staticlib"]

I generate the library using cargo build --release, which produces a binary file named libacme.a which I copy into my /usr/local/lib directory (so my C compiler can link properly my C program using it).

So far, so good.

I can compile and link my C program without problems but when I run it I get this error:

thread '<unnamed>' panicked at src/lib.rs:376:10:
Failed to get Cargo metadata: CargoMetadata { stderr: "error: could not find `Cargo.toml` in `/home/fermin/src/mycproject` or any parent directory\n" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fatal runtime error: failed to initiate panic, error 5
Abort

I understand this is due to the current approach expects to get the version at runtime, from the Cargo.toml file. But I don't have a Cargo.toml file at this point (I only have the statically linked libacme.a binary file).

Is there any way of solving this problem (e.g. implement get_version() in a way the version number gets included in the static library)?


Solution

  • Cargo sets the CARGO_PKG_VERSION environment variable for compiled crates, and you can retrieve its value using env!(). You can even create a constant &CStr:

    use core::ffi::CStr;
    
    #[no_mangle]
    pub extern "C" fn get_version() -> *const libc::c_char {
        const VERSION: &CStr =
            match CStr::from_bytes_with_nul(concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes()) {
                Ok(version) => version,
                Err(_) => panic!("package version contains null bytes??"),
            };
    
        VERSION.as_ptr()
    }