user8725011
user8725011

Reputation:

How to set cfg options to compile conditionally?

I am working on some code where a buffer is backed by a statically sized array. Since Rust and the build tools provided by it offer the possibilities to compile conditionally, I can do something like this:

struct Buffer {
    // default case, if none is set
    #[cfg(not(buffersize))]
    buffer: [f32; 16],

    #[cfg(buffersize = "32")]
    buffer: [f32; 32],

    #[cfg(buffersize = "64")]
    buffer: [f32; 64],
}

impl Buffer {
    fn new() -> Buffer {
        Buffer {
            #[cfg(not(buffersize))]
            buffer: [0.0; 16],

            #[cfg(buffersize = "32")]
            buffer: [0.0; 32],

            #[cfg(buffersize = "64")]
            buffer: [0.0; 64],
        }
    }
}

There is another question that uses features to compile the code conditionally. Using features alone, I would have to combine buffersize and the actual value e.g. buffersize16. Is it possible to provide the cfg flags to Cargo, or would I need to provide them directly to rustc?

Upvotes: 6

Views: 8029

Answers (2)

user8725011
user8725011

Reputation:

I want to post an update to my question as an additional option on how to pass (numeric) configuration values at compile time, that's possible through a build script.

Suppose you have following build script inside your project:

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

fn main() {
    println!("cargo:rerun-if-env-changed=SIZE");
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest = Path::new(&out_dir).join("consts.rs");

    let mut out_file = File::create(&dest).expect("Cannot create file");
    let size: usize = env!("SIZE").parse().unwrap();

    write!(&out_file, "pub const S : usize = {};", size);
}

It reads an environment variable at compile time, parses it as usize and writes a rust file (consts.rs) containing only the constant. Now, in your application code you can include this file and use the constant to eg. allocate memory on the stack:

include!(concat!(env!("OUT_DIR"), "/consts.rs"));

fn main() {
    let array = [0.0f32; S];
    println!("array len= {:?}", array.len());
}

The downside of this trick is, that you have to recompile the whole project (or parts of it) whenever the value of the environment variable changes, since cargo:rerun-if-env-changed=SIZE doesn't get captured. It also implies to always keep knowledge of this configuration option, but this could be wrapped in an additional build script like a makefile. Even if this is not the most elegant path to choose, it might be an option on certain occasions.

It would be nice to have this as macro option, though.

Upvotes: 2

mpromonet
mpromonet

Reputation: 11952

You can set the environnment variable RUSTFLAGS or set rustflags variable in .cargo/config.
From environment-variables

RUSTFLAGS — A space-separated list of custom flags to pass to all compiler invocations that Cargo performs. In contrast with cargo rustc, this is useful for passing a flag to all compiler instances.

In your example, you could use :

RUSTFLAGS='--cfg buffersize="32"' cargo build

Upvotes: 15

Related Questions