Skip to content

Commit

Permalink
Auto merge of #3510 - froydnj:cross-compilation-test-checking, r=alex…
Browse files Browse the repository at this point in the history
…crichton

handle cross-compilation test failure more gracefully

The current cross-compilation test module fails every test if an appropriate cross standard library is not found.  This behavior is unhelpful, because the failures are verbose, unexpected, and there's no
obvious way to make them go away.

Instead, it would be better to check once before all the tests that an appropriate cross-compilation setup is available.  Once that check has been done, a single test can fail with an appropriate `panic!` message while the other tests silently pass.  The `panic!` message can inform the user about their options, either setting an appropriate environment variable, or using rustup to install the necessary cross standard library.

Assuming the user has rustup installed, the single failure now looks
something like:

```
	thread 'plugin_deps' panicked at 'Cannot cross compile to i686-unknown-linux-gnu.

This failure can be safely ignored. If you would prefer to not see this
failure, you can set the environment variable CFG_DISABLE_CROSS_TESTS to "1".

Alternatively, you can install the necessary libraries for cross-compilation with

    rustup toolchain install stable-i686-unknown-linux-gnu
', tests/cross-compile.rs:87
note: Run with `RUST_BACKTRACE=1` for a backtrace.
```

The code is admittedly gnarly, with synchronization and `unsafe` sections, but I wasn't sure how to make it any better. Suggestions welcome!

Fixes #3086.
  • Loading branch information
bors committed Jan 9, 2017
2 parents 40a4ce6 + bbc1079 commit f70194a
Showing 1 changed file with 80 additions and 4 deletions.
84 changes: 80 additions & 4 deletions tests/cross-compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ extern crate cargotest;
extern crate hamcrest;

use std::env;
use std::process::Command;
use std::sync::{Once, ONCE_INIT};
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

use cargo::util::process;
use cargotest::{is_nightly, rustc_host};
use cargotest::support::{project, execs, basic_bin_manifest};
use cargotest::support::{project, execs, main_file, basic_bin_manifest};
use hamcrest::{assert_that, existing_file};

fn disabled() -> bool {
Expand All @@ -19,9 +22,82 @@ fn disabled() -> bool {
// Right now the windows bots cannot cross compile due to the mingw setup,
// so we disable ourselves on all but macos/linux setups where the rustc
// install script ensures we have both architectures
!(cfg!(target_os = "macos") ||
cfg!(target_os = "linux") ||
cfg!(target_env = "msvc"))
if !(cfg!(target_os = "macos") ||
cfg!(target_os = "linux") ||
cfg!(target_env = "msvc")) {
return true;
}

// It's not particularly common to have a cross-compilation setup, so
// try to detect that before we fail a bunch of tests through no fault
// of the user.
static mut CAN_RUN_CROSS_TESTS: bool = false;
static CHECK: Once = ONCE_INIT;

let cross_target = alternate();

CHECK.call_once(|| {
let p = project("cross_test")
.file("Cargo.toml", &basic_bin_manifest("cross_test"))
.file("src/cross_test.rs", &main_file(r#""testing!""#, &[]));

let result = p.cargo_process("build")
.arg("--target").arg(&cross_target)
.exec_with_output();

if result.is_ok() {
unsafe {
CAN_RUN_CROSS_TESTS = true;
}
}
});

if unsafe { CAN_RUN_CROSS_TESTS } {
// We were able to compile a simple project, so the user has the
// necessary std:: bits installed. Therefore, tests should not
// be disabled.
return false;
}

// We can't compile a simple cross project. We want to warn the user
// by failing a single test and having the remainder of the cross tests
// pass. We don't use std::sync::Once here because panicing inside its
// call_once method would poison the Once instance, which is not what
// we want.
static HAVE_WARNED: AtomicBool = ATOMIC_BOOL_INIT;

if HAVE_WARNED.swap(true, Ordering::SeqCst) {
// We are some other test and somebody else is handling the warning.
// Just disable the current test.
return true;
}

// We are responsible for warning the user, which we do by panicing.
let rustup_available = Command::new("rustup").output().is_ok();

let linux_help = if cfg!(target_os = "linux") {
"
You may need to install runtime libraries for your Linux distribution as well.".to_string()
} else {
"".to_string()
};

let rustup_help = if rustup_available {
format!("
Alternatively, you can install the necessary libraries for cross-compilation with
rustup target add {}{}", cross_target, linux_help)
} else {
"".to_string()
};

panic!("Cannot cross compile to {}.
This failure can be safely ignored. If you would prefer to not see this
failure, you can set the environment variable CFG_DISABLE_CROSS_TESTS to \"1\".{}
", cross_target, rustup_help);
}

fn alternate() -> String {
Expand Down

0 comments on commit f70194a

Please sign in to comment.