Does CARGO_BIN_EXE_<name> work in workspace across packages?

Hi,

I have used env!(CARGO_BIN_EXE_<name>) to get the path of the executable for integration testing inside a package. But it seems like it did not work if the <name> refers to an executable from a sibling package in the same workspace. Is that expected?

basically my project has structure like this:

\package_1
\package_2
Cargo.toml

where Cargo.toml is basically this:

[workspace]
members = [
    "package_1",
    "package_2",
]

My goal is to run an integration test that brings up executables from both package_1 and package_2. What's the recommended way to identify the path of executables? Is it CARGO_BIN_EXE_<name> or something else?

Thanks.

The Cargo test suite does the following to find its own binary:

(from here)

pub fn cargo_dir() -> PathBuf {
    env::var_os("CARGO_BIN_PATH")
        .map(PathBuf::from)
        .or_else(|| {
            env::current_exe().ok().map(|mut path| {
                path.pop();
                if path.ends_with("deps") {
                    path.pop();
                }
                path
            })
        })
        .unwrap_or_else(|| panic!("CARGO_BIN_PATH wasn't set. Cannot continue running test"))
}

Not sure if CARGO_BIN_PATH is something Cargo usually sets. But it falls back here to finding the current exe path (the test executable), and going up a directory (removing deps). This directory is the directory where the binaries would be found. Usually target/debug/ but sometimes different if --target is passed to Cargo.

Simplifying this a bit. You can have a workspace like this:

[workspace]
members = [
    "package_1",
    "package_2",
    "tests",
]

With a tests/src/lib.rs file that looks like this

use std::env;
use std::path::PathBuf;

pub fn bin_dir() -> PathBuf {
    let mut path = env::current_exe().unwrap();
    path.pop();
    path
}

#[test]
fn hello() {
    let dir = bin_dir();
    assert!(dir.join("package_1").exists());
    assert!(dir.join("package_2").exists());

   // now can run binaries with `std::process::Command::new(dir.join("package_1"))` etc
}

The only caveat is that you need to run cargo build --workspace before running cargo test --workspace otherwise the binaries won't be present.

1 Like

I'm not sure about having tests as its own package in the workspace. That aside, In this case, I guess I can do a similar thing inside, say package_2, right? i.e. use bin_dir() as you described to help get the path of other executables, and use env!(CARGO_BIN_EXE_package_2 to get its own path.

Btw, I also found another way, a bit hacky, but works :wink: : Because package_1 and package_2 are always using the same target output directory, their path only diffs in the name part. We can do this:

suppose we are writing a test under package_2/tests/:

    let pkg2_bin_path = env!("CARGO_BIN_EXE_package_2");
    let pkg1_bin_path = pkg2_bin_path.replace("package_2", "package_1");