Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CARGO_TARGET_TMPDIR env var for integration tests & benches #9375

Merged
merged 2 commits into from
May 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions crates/cargo-test-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ pub fn cargo_test(attr: TokenStream, item: TokenStream) -> TokenStream {
}
};

let mut new_body =
to_token_stream("let _test_guard = cargo_test_support::paths::init_root();");
let mut new_body = to_token_stream(
r#"let _test_guard = {
let tmp_dir = option_env!("CARGO_TARGET_TMPDIR");
cargo_test_support::paths::init_root(tmp_dir)
};"#,
);

// If this is a `build_std` test (aka `tests/build-std/*.rs`) then they
// only run on nightly and they only run when specifically instructed to
Expand Down
5 changes: 3 additions & 2 deletions crates/cargo-test-support/src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,12 @@ pub fn init(path: &Path) -> git2::Repository {
}

fn default_search_path() {
use crate::paths::GLOBAL_ROOT;
use crate::paths::global_root;
use git2::{opts::set_search_path, ConfigLevel};

static INIT: Once = Once::new();
INIT.call_once(|| unsafe {
let path = GLOBAL_ROOT.join("blank_git_search_path");
let path = global_root().join("blank_git_search_path");
t!(set_search_path(ConfigLevel::System, &path));
t!(set_search_path(ConfigLevel::Global, &path));
t!(set_search_path(ConfigLevel::XDG, &path));
Expand Down
62 changes: 41 additions & 21 deletions crates/cargo-test-support/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,44 @@ use std::sync::Mutex;
static CARGO_INTEGRATION_TEST_DIR: &str = "cit";

lazy_static! {
pub static ref GLOBAL_ROOT: PathBuf = {
let mut path = t!(env::current_exe());
path.pop(); // chop off exe name
path.pop(); // chop off 'debug'

// If `cargo test` is run manually then our path looks like
// `target/debug/foo`, in which case our `path` is already pointing at
// `target`. If, however, `cargo test --target $target` is used then the
// output is `target/$target/debug/foo`, so our path is pointing at
// `target/$target`. Here we conditionally pop the `$target` name.
if path.file_name().and_then(|s| s.to_str()) != Some("target") {
path.pop();
}

path.push(CARGO_INTEGRATION_TEST_DIR);
path.mkdir_p();
path
};
// TODO: Use `SyncOnceCell` when stable
static ref GLOBAL_ROOT: Mutex<Option<PathBuf>> = Mutex::new(None);

static ref TEST_ROOTS: Mutex<HashMap<String, PathBuf>> = Default::default();
}

/// This is used when running cargo is pre-CARGO_TARGET_TMPDIR
/// TODO: Remove when CARGO_TARGET_TMPDIR grows old enough.
fn global_root_legacy() -> PathBuf {
let mut path = t!(env::current_exe());
path.pop(); // chop off exe name
path.pop(); // chop off "deps"
path.push("tmp");
path.mkdir_p();
path
}

fn set_global_root(tmp_dir: Option<&'static str>) {
let mut lock = GLOBAL_ROOT.lock().unwrap();
if lock.is_none() {
let mut root = match tmp_dir {
Some(tmp_dir) => PathBuf::from(tmp_dir),
None => global_root_legacy(),
};

root.push(CARGO_INTEGRATION_TEST_DIR);
*lock = Some(root);
}
}

pub fn global_root() -> PathBuf {
let lock = GLOBAL_ROOT.lock().unwrap();
match lock.as_ref() {
Some(p) => p.clone(),
None => unreachable!("GLOBAL_ROOT not set yet"),
}
}

// We need to give each test a unique id. The test name could serve this
// purpose, but the `test` crate doesn't have a way to obtain the current test
// name.[*] Instead, we used the `cargo-test-macro` crate to automatically
Expand All @@ -52,14 +68,15 @@ pub struct TestIdGuard {
_private: (),
}

pub fn init_root() -> TestIdGuard {
pub fn init_root(tmp_dir: Option<&'static str>) -> TestIdGuard {
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);

let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
let id = NEXT_ID.fetch_add(1, Ordering::SeqCst);
TEST_ID.with(|n| *n.borrow_mut() = Some(id));

let guard = TestIdGuard { _private: () };

set_global_root(tmp_dir);
let r = root();
r.rm_rf();
r.mkdir_p();
Expand All @@ -80,7 +97,10 @@ pub fn root() -> PathBuf {
order to be able to use the crate root.",
)
});
GLOBAL_ROOT.join(&format!("t{}", id))

let mut root = global_root();
root.push(&format!("t{}", id));
root
}

pub fn home() -> PathBuf {
Expand Down
8 changes: 8 additions & 0 deletions src/cargo/core/compiler/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ pub struct Layout {
examples: PathBuf,
/// The directory for rustdoc output: `$root/doc`
doc: PathBuf,
/// The directory for temporary data of integration tests and benches: `$dest/tmp`
tmp: PathBuf,
/// The lockfile for a build (`.cargo-lock`). Will be unlocked when this
/// struct is `drop`ped.
_lock: FileLock,
Expand Down Expand Up @@ -170,6 +172,7 @@ impl Layout {
fingerprint: dest.join(".fingerprint"),
examples: dest.join("examples"),
doc: root.join("doc"),
tmp: dest.join("tmp"),
root,
dest,
_lock: lock,
Expand Down Expand Up @@ -219,4 +222,9 @@ impl Layout {
pub fn build(&self) -> &Path {
&self.build
}
/// Create and return the tmp path.
pub fn prepare_tmp(&self) -> CargoResult<&Path> {
paths::create_dir_all(&self.tmp)?;
Ok(&self.tmp)
}
}
5 changes: 5 additions & 0 deletions src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,11 @@ fn prepare_rustc(
base.env("CARGO_PRIMARY_PACKAGE", "1");
}

if unit.target.is_test() || unit.target.is_bench() {
let tmp = cx.files().layout(unit.kind).prepare_tmp()?;
base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
}

if cx.bcx.config.cli_unstable().jobserver_per_rustc {
let client = cx.new_jobserver()?;
base.inherit_jobserver(&client);
Expand Down
6 changes: 6 additions & 0 deletions src/doc/src/reference/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,12 @@ corresponding environment variable is set to the empty string, `""`.
on the current directory and the default workspace members. This environment
variable will not be set when building dependencies. This is only set when
compiling the package (not when running binaries or tests).
* `CARGO_TARGET_TMPDIR` — Only set when building [integration test] or benchmark code.
This is a path to a directory inside the target directory
where integration tests or benchmarks are free to put any data needed by
the tests/benches. Cargo initially creates this directory but doesn't
manage its content in any way, this is the responsibility of the test code.
There are separate directories for `debug` and `release` profiles.

[integration test]: cargo-targets.md#integration-tests
[`env` macro]: ../../std/macro.env.html
Expand Down
64 changes: 63 additions & 1 deletion tests/testsuite/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1334,12 +1334,18 @@ fn crate_env_vars() {
let s = format!("{}.{}.{}-{}", VERSION_MAJOR,
VERSION_MINOR, VERSION_PATCH, VERSION_PRE);
assert_eq!(s, VERSION);

// Verify CARGO_TARGET_TMPDIR isn't set for bins
assert!(option_env!("CARGO_TARGET_TMPDIR").is_none());
}
"#,
)
.file(
"src/lib.rs",
r#"
use std::env;
use std::path::PathBuf;

pub fn version() -> String {
format!("{}-{}-{} @ {} in {}",
env!("CARGO_PKG_VERSION_MAJOR"),
Expand All @@ -1348,9 +1354,60 @@ fn crate_env_vars() {
env!("CARGO_PKG_VERSION_PRE"),
env!("CARGO_MANIFEST_DIR"))
}

pub fn check_no_int_test_env() {
env::var("CARGO_TARGET_DIR").unwrap_err();
}

pub fn check_tmpdir(tmp: Option<&'static str>) {
let tmpdir: PathBuf = tmp.unwrap().into();

let exe: PathBuf = env::current_exe().unwrap().into();
let mut expected: PathBuf = exe.parent().unwrap().parent().unwrap().into();
expected.push("tmp");
assert_eq!(tmpdir, expected);

// Check that CARGO_TARGET_TMPDIR isn't set for lib code
assert!(option_env!("CARGO_TARGET_TMPDIR").is_none());
env::var("CARGO_TARGET_TMPDIR").unwrap_err();
}

#[test]
fn env() {
// Check that CARGO_TARGET_TMPDIR isn't set for unit tests
assert!(option_env!("CARGO_TARGET_TMPDIR").is_none());
env::var("CARGO_TARGET_TMPDIR").unwrap_err();
}
"#,
)
.build();
.file(
"tests/env.rs",
r#"
#[test]
fn env() {
foo::check_tmpdir(option_env!("CARGO_TARGET_TMPDIR"));
}
"#,
);

let p = if is_nightly() {
p.file(
"benches/env.rs",
r#"
#![feature(test)]
extern crate test;
use test::Bencher;

#[bench]
fn env(_: &mut Bencher) {
foo::check_tmpdir(option_env!("CARGO_TARGET_TMPDIR"));
}
"#,
)
.build()
} else {
p.build()
};

println!("build");
p.cargo("build -v").run();
Expand All @@ -1362,6 +1419,11 @@ fn crate_env_vars() {

println!("test");
p.cargo("test -v").run();

if is_nightly() {
println!("bench");
p.cargo("bench -v").run();
}
}

#[cargo_test]
Expand Down