-1

I have a procedural macro crate Proc and binary crate Bin. Bin has a dependency on Proc. Proc needs a filled environment variable to function properly.

This is some code inside my build.rs in Bin. Proc can successfully find the env value when using the following code:

fn main() {
    println!("cargo:rustc-env=SOME_ENV_VALUE=somevalue");
}

However, Proc fails to find the environment variable when using this code inside my build.rs in Bin (note: when checking the existence right after the dotenv call, I can verify the key is actually present):

fn main() {
    dotenv::dotenv().unwrap();
}

This is my Proc crate:

use proc_macro::TokenStream;

#[proc_macro_derive(MyProcMacro)]
pub fn my_proc_macro(input: TokenStream) -> TokenStream {
    if std::env::var("SOME_ENV_VALUE").is_err() {
        panic!("Failed to retrieve env value")
    }

    TokenStream::new()
}

Why won't it fail with the println! command? Can it work with dotenv? Else I need to write some code that copies the keys from my env file to the println! command...

All the code is in my minimal reproduction project.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
J. Doe
  • 12,159
  • 9
  • 60
  • 114
  • *In my build.rs in Bin, Proc can successfully find the env value* — this doesn't make sense. What are you showing with this code? There's no usage of dotenv or a procedural macro. – Shepmaster Jul 22 '20 at 19:39
  • What do you believe will load the dotenv environment? You are *compiling* your program, which means that the active process is `rustc`. `rustc` isn't going to load your dotenv file. – Shepmaster Jul 22 '20 at 19:41
  • @Shepmaster Literally there is this line of code: `dotenv::dotenv().unwrap();` and the procedural macro, well that's just below the sentence: This is my `Proc` crate: – J. Doe Jul 22 '20 at 19:42
  • @Shepmaster Then why do I have access on my environment variables right after I called `dotenv::dotenv().unwrap();`? It's just that the `Proc` crate can not access the environment variable, but surely they are loaded right after my `dotenv()` call – J. Doe Jul 22 '20 at 19:43
  • Do you have experience with any other compiled languages? It seems like you might not fully appreciate how compilation of a program and execution of that program are different steps. – Shepmaster Jul 22 '20 at 19:44
  • @Shepmaster I don't know anything about `rustc`, but I when I call `dotenv::dotenv().unwrap();`, the keys are loaded inside the environment variables. That's what I know. I don't understand why `Proc` can not read my environment variables, but it can when using the `println` command, but I am noticing I am repeating myself all the time – J. Doe Jul 22 '20 at 19:47

1 Answers1

4

I encourage you to re-read the Build Scripts chapter of the Cargo docs. A build script is a separate executable that Cargo builds and executes before starting to build your source code.

  1. Cargo starts
  2. Cargo executes rustc to build the build script
  3. Cargo executes the build script
  4. Cargo executes rustc to build your code
  5. Cargo exits

[dotenv] loads environment variables from a .env file, if available, and mashes those with the actual environment variables provided by the operating system.

Variables that it loads are placed into the current processes environment variables. In your example, that's the build script executable.

[cargo:rustc-env] tells Cargo to set the given environment variable when compiling the package

The stdout of the build script interacts with Cargo, which then modifies how the code is compiled, including what environment variables to set.


You need to load the dotenv file and set the environment variables for the subsequent compilation. Something like this compiling-but-untested example:

fn main() {
    if let Ok(env) = dotenv::dotenv_iter() {
        for (k,v) in env.flatten() {
            println!("cargo:rustc-env={}={}", k, v);
        }
    }
}

Don't worry that this method is marked as deprecated. The maintainers changed their minds.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366