By using this site, you agree to the Privacy Policy and Terms of Use.
Accept
World of SoftwareWorld of SoftwareWorld of Software
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Search
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
Reading: Rust’s Build.rs Might Be the Most Underrated Feature in Cargo | HackerNoon
Share
Sign In
Notification Show More
Font ResizerAa
World of SoftwareWorld of Software
Font ResizerAa
  • Software
  • Mobile
  • Computing
  • Gadget
  • Gaming
  • Videos
Search
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Have an existing account? Sign In
Follow US
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
World of Software > Computing > Rust’s Build.rs Might Be the Most Underrated Feature in Cargo | HackerNoon
Computing

Rust’s Build.rs Might Be the Most Underrated Feature in Cargo | HackerNoon

News Room
Last updated: 2025/04/17 at 5:02 PM
News Room Published 17 April 2025
Share
SHARE

While working on my demo on WebAssembly and Kubernetes, I wanted to create three different binaries based on the same code:

  • Native: compile the Rust code to regular native code as a baseline
  • Embed: compile to WebAssembly and use the WasmEdge runtime image as the base Docker image
  • Runtime: compile to WebAssembly, use a base scratch image as my base image, and set the runtime when running the code

The code itself is an HTTP server that offers a single endpoint. For the sake of the demo, I wanted it to return the flavour of the underlying image.

curl localhost:3000/get
{"source": "native", "data": {}}

The idea is to have a single codebase that I can compile to native or WebAssembly. I solved this requirement by using a cfg compile flag.

#[cfg(flavor = "native")]
const FLAVOR: &str = "native";

#[cfg(flavor = "embed")]
const FLAVOR: &str = "embed";

#[cfg(flavor = "runtime")]
const FLAVOR: &str = "runtime";

// Use FLAVOR later in the returned HTTP response

In my case, I can live with three different values. But what if I had to deal with a high cardinality, say, fifteen? It would be quite a bore to define them manually. I searched for a solution and found Build Scripts.

Some packages need to compile third-party non-Rust code, for example C libraries. Other packages need to link to C libraries which can either be located on the system or possibly need to be built from source. Others still need facilities for functionality such as code generation before building (think parser generators).

Cargo does not aim to replace other tools that are well-optimized for these tasks, but it does integrate with them with custom build scripts. Placing a file named build.rs in the root of a package will cause Cargo to compile that script and execute it just before building the package.

— Build Scripts

Let’s try with a simple build.script at the root of the crate:

fn main() {
    println!("Hello from build script!");
}
cargo build
Compiling high-cardinality-cfg-compile v0.1.0 (/Users/nico/projects/private/high-cardinality-cfg-compile)
 Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s

There isn’t any log–nothing seems to happen. The documentation reveals the reason:

The output of the script is hidden from the terminal during normal compilation. If you would like to see the output directly in your terminal, invoke Cargo as “very verbose” with the -vv flag.

Let’s compile again with the verbose flag:

cargo build --vv

The output is what we expect:

...
[high-cardinality-cfg-compile 0.1.0] Hello from build script!
...

Now is the time to do something useful: I’ll use the build script to replace the hard-coded constants above. For that, I’ll generate a Rust code file that contains the value passed for flavor.

Note the build script runs before compilation. Hence, it doesn’t have access to the cfg flags. Instead, I’ll pass the value as a Cargo.toml package metadata key value.

[package.metadata]
flavor = "foobar"

For our build script to read the Cargo.toml metadata, we need to add a build dependency:

[build-dependencies]
cargo_metadata = "0.19.1"

The code is straightforward:

fn main() {
    let metadata = MetadataCommand::new()                                         //1
        .exec()
        .expect("Failed to fetch cargo metadata");

    let package = metadata.root_package().expect("No root package found");        //2

    let flavor = package                                                          //3
        .metadata
        .get("flavor")
        .and_then(|f| f.as_str())
        .expect("flavor is not set in Cargo.toml under [package.metadata]");

    let dest_path = Path::new("src").join("flavor.rs");                           //4

    fs::write(&dest_path, format!("pub const FLAVOR: &str = "{}";n", flavor))  //5
        .expect("Failed to write flavor.rs");

    println!("cargo:rerun-if-changed=Cargo.toml");
    println!("cargo:warning=FLAVOR written to {}", dest_path.display());
}
  1. Get the metadata
  2. Get the package where we want to create the new file
  3. Read the flavor value
  4. Reference src/flavor.rs
  5. Write pub const FLAVOR: &str = <flavor>

On the code side, we only need to use the FLAVOR const from the flavor module:

mod flavor;

fn main() {
    println!("Hello from flavor {}", flavor::FLAVOR);
}

Running the program outputs the flavor configured in the Cargo.toml file:

cargo run
Hello from flavor foobar

Conclusion

In this, I’ve shown how to use high-cardinality build parameters from the package metadata. I could have achieved the same with environment variables, but I wanted to learn about the metadata section of the Cargo.toml file.

The build.rs file is a handy trick for achieving goals that aren’t possible with regular Cargo features.

To go further:


Originally published at A Java Geek on April 13th, 2025

Sign Up For Daily Newsletter

Be keep up! Get the latest breaking news delivered straight to your inbox.
By signing up, you agree to our Terms of Use and acknowledge the data practices in our Privacy Policy. You may unsubscribe at any time.
Share This Article
Facebook Twitter Email Print
Share
What do you think?
Love0
Sad0
Happy0
Sleepy0
Angry0
Dead0
Wink0
Previous Article How to Ship to and Use an Amazon Locker and Keep Your Packages Safe
Next Article New Jersey Sues Discord for Allegedly Failing to Protect Children
Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Stay Connected

248.1k Like
69.1k Follow
134k Pin
54.3k Follow

Latest News

Belichick agrees new move involving Michael Strahan after CBS interview disaster
News
Today’s Wormle -Hints and Answer Puzzle #1423, May 12
News
Create a Website and Start Selling Products Fast With AI
Computing
Panasonic’s 2025 TVs look promising but are they enough?
Gadget

You Might also Like

Computing

Create a Website and Start Selling Products Fast With AI

31 Min Read
Computing

Updated Nouveau Driver Patches For NVIDIA Hopper & Blackwell GPUs

2 Min Read
Computing

Rethinking what banks are for

8 Min Read
Computing

👨🏿‍🚀 Daily –  Safaricom reaches for the sky |

21 Min Read
//

World of Software is your one-stop website for the latest tech news and updates, follow us now to get the news that matters to you.

Quick Link

  • Privacy Policy
  • Terms of use
  • Advertise
  • Contact

Topics

  • Computing
  • Software
  • Press Release
  • Trending

Sign Up for Our Newsletter

Subscribe to our newsletter to get our newest articles instantly!

World of SoftwareWorld of Software
Follow US
Copyright © All Rights Reserved. World of Software.
Welcome Back!

Sign in to your account

Lost your password?