Getting executable name from environment variable / macro?


#1

I’d like to be able to run a binary generated by cargo directly in integration tests. I know that cargo places binaries (at least ones in a main.rs) in target/(debug or release)/myapp(.exe) . Is there a cleaner way to get that location, though? Like in an environment variable or macro? I didn’t see exactly what I want in http://doc.crates.io/environment-variables.html . I can use CARGO_MANIFEST_DIR , detect whether it’s debug or release, then sort of guess the executable name with CARGO_PKG_NAME, but that doesn’t seem very clean and a little unreliable.

Also, tangentially, is there a place where all the possible values for cfg! can be found? I didn’t see anything in https://rustbyexample.com/attribute/cfg.html , https://doc.rust-lang.org/std/macro.cfg.html , or https://doc.rust-lang.org/book/first-edition/conditional-compilation.html .

Thanks.


#2

If you are scripting your ITs is it enough to know that --release compiles to target/release and without goes to target/debug?

You could also combine RUSTFLAGS and CARGO_TARGET_DIR to get the desired output.


#3

Thanks for the response. Unless I’m misinterpreting the docs, those two variables are passed to cargo, rather than set by cargo. Whatever the case, I tried them and they weren’t found.

I ended up doing this, but it’s less than pretty:

	let mut program_path_buf = PathBuf::new();
	program_path_buf.push(env!("CARGO_MANIFEST_DIR"));
	program_path_buf.push("target");
	program_path_buf.push(if cfg!(debug_assertions) { "debug" } else { "release" });
	program_path_buf.push(if cfg!(windows) { concat!(env!("CARGO_PKG_NAME"), ".exe") } else { env!("CARGO_PKG_NAME") });
	println!("program_path_buf: {:?}", program_path_buf);

Does anyone have a nicer way?


#4

I know you can use std::env::current_exe() to get the location of the currently running binary at runtime. Could you calculate the location of your other binary relative to the integration test binary being run?


#5

Hmm, I think I might like that better. It avoids the if/else on cfg!(debug_assertions). Also, it looks like std::env::consts can be used instead of hard-coded “.exe”. So almost there :slight_smile:. To get what I really want I might ultimately need a build script…


#6

Just out of curiosity, do you know if cargo adds the output directory (e.g. target/debug/) to your PATH when running tests? If that’s the case you wouldn’t have to stuff around with any of this, you can just do Command::new("my_program").output().unwrap() in your test.


#7

I’ll give that a try when I get home from work, that’s a good suggestion.

I’m thinking something like Expect/DejaGnu might be more appropriate to what I’m trying to do, so I’m going to look into that. Maybe a 3rd-party cargo module would be interesting to work on.


#8

I just had a google and apparently there’s a rexpect crate. Looking at the README it seems to be fairly useful and actively maintained. Would that be useful for running tests?


#9

Cool, I think it might. When I googled “rust expect” I got nothing but pages talking about Option and Result. Thanks.


#10

what you are trying to do is exactly the same I’m planning. Nice coincidence :slight_smile:
I designed rexpect as a standalone crate which can execute programs where you can interact with them, send ctrl-c etc. and expect output (you’re right: expect is already a reserved keyword in rust, that is one of a drawback of such a crate…).

That said, I just created shell_doctest (unfortunately it’s only a stub and not usable at all yet) to be able to add doctests to executables (and so create a kind of integration test which starts the executable and does some input/output tests):

$ my_program
expected_output

Ultimately it would need the same things as you say, find out if the current target is debug or release, but I’m planning to put that into a build script so I’m sure that available is available at build/test time. I would be happy if we could cooperate on that quest…!