Reputation: 18069
I have the following projects in a workspace:
Workspacefolder
|
+-- Project A
| |
| +-- build.rs
|
+-- Dep
| |
| +-- test.json
|
+-Cargo.toml
In Project A
, there is build.rs
that wants to open test.json
in a way that doesn't rely on platform and that works well with CI.
I'm looking for a CARGO_WORKSPACE
environment variable, because then I can say Path::new(&workspace_dir).join("/Dep/test.json")
.
Upvotes: 10
Views: 7364
Reputation: 26727
In future, maybe CARGO_RUSTC_CURRENT_DIR
could be use to do that, doc.
A good trick from RandomInsano is to use .cargo/config.toml
:
[env]
CARGO_WORKSPACE_DIR = { value = "", relative = true }
Upvotes: 2
Reputation: 11803
There is a simpler way to do it now:
fn workspace_dir() -> PathBuf {
let output = std::process::Command::new(env!("CARGO"))
.arg("locate-project")
.arg("--workspace")
.arg("--message-format=plain")
.output()
.unwrap()
.stdout;
let cargo_path = Path::new(std::str::from_utf8(&output).unwrap().trim());
cargo_path.parent().unwrap().to_path_buf()
}
Upvotes: 11
Reputation: 31
For cargo version 1.63.0, i managed with:
use std::{env, path::PathBuf, process::Command};
pub fn get_workspace_root() -> anyhow::Result<PathBuf> {
let current_dir = env::current_dir()?;
let cmd_output = Command::new("cargo")
.args(["metadata", "--format-version=1"])
.output()?;
if !cmd_output.status.success() {
return Ok(current_dir);
}
let json =
serde_json::from_str::<serde_json::Value>(String::from_utf8(cmd_output.stdout)?.as_str())?;
let path = match json.get("workspace_root") {
Some(val) => match val.as_str() {
Some(val) => val,
None => return Ok(current_dir),
},
None => return Ok(current_dir),
};
Ok(PathBuf::from(path))
}
Upvotes: 2
Reputation: 430941
No, not for the version of Cargo bundled with Rust 1.16.0. You can verify this yourself by printing out all of the environment variables in the build script:
use std::fs::File;
use std::io::Write;
fn main() {
let mut dump = File::create("/tmp/dump").expect("unable to open");
for (k, v) in std::env::vars() {
writeln!(&mut dump, "{} -> {}", k, v).expect("unable to write")
}
}
On my machine, this produces:
$ sort /tmp/dump | grep CARGO
CARGO_CFG_DEBUG_ASSERTIONS ->
CARGO_CFG_TARGET_ARCH -> x86_64
CARGO_CFG_TARGET_ENDIAN -> little
CARGO_CFG_TARGET_ENV ->
CARGO_CFG_TARGET_FAMILY -> unix
CARGO_CFG_TARGET_OS -> macos
CARGO_CFG_TARGET_POINTER_WIDTH -> 64
CARGO_CFG_UNIX ->
CARGO_HOME -> /Users/shep/.cargo
CARGO_MANIFEST_DIR -> /private/tmp/the-workspace/project-a
CARGO_PKG_AUTHORS -> An Devloper <[email protected]>
CARGO_PKG_DESCRIPTION ->
CARGO_PKG_HOMEPAGE ->
CARGO_PKG_NAME -> project-a
CARGO_PKG_VERSION -> 0.1.0
CARGO_PKG_VERSION_MAJOR -> 0
CARGO_PKG_VERSION_MINOR -> 1
CARGO_PKG_VERSION_PATCH -> 0
CARGO_PKG_VERSION_PRE ->
I'm not sure why you can't just do
Path::new(&manifest_dir).join("..").join("Dep").join("test.json")
I've split each directory into a separate call — avoiding the need to specify the directory separator at all to be platform agnostic.
Upvotes: 4