How to set cfg options to compile conditionally?
Asked Answered
S

2

6

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?

Sartorial answered 3/8, 2019 at 12:51 Comment(4)
According to this answer I have to use features. Is that correct?Sartorial
Why do you need to do this? Is there any reason you cannot expect user to pass a slice as a buffer instead of this Buffer object? This looks very cumbersome, especially when you might want to have two different buffer sizes at once.Radke
There are multiple factors at play here: first Bufferis just a wrapper for a statically sized array. I could have used a type alias eg. type Buffer = [f32; 256] and implement the necessary methods on it. The Buffer will eventually be used in some concurrent context / will be processed in multiple threads. Working with correct lifetimes seems to be more cumbersome here, but I get your point, that the provided example is not very elegant.Sartorial
That's fair, what about multiple buffer size problem? I'd suggest just going with multiple structs, or a generic struct and let user choose what size they need before creating the buffer, not at compile time, even if they will ever need a single buffer type. (user is general, even if you're writing code just for yourself)Radke
I
15

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
Interest answered 8/8, 2019 at 17:6 Comment(0)
S
2

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.

Sartorial answered 22/4, 2020 at 9:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.