Skip to content

Commit

Permalink
Test wasm32-wasip1 in CI, not wasm32-unknown-unknown
Browse files Browse the repository at this point in the history
This commit changes CI to no longer test the `wasm32-unknown-unknown`
target and instead test the `wasm32-wasip1` target. There was some
discussion of this in a [Zulip thread], and the motivations for this PR
are:

* Runtime failures on `wasm32-unknown-unknown` print nothing, meaning
  all you get is "something failed". In contrast `wasm32-wasip1` can
  print to stdout/stderr.

* The unknown-unknown target is missing lots of pieces of libstd, and
  while `wasm32-wasip1` is also missing some pieces (e.g. threads) it's
  missing fewer pieces. This means that many more tests can be run.

Overall my hope is to improve the debuggability of wasm failures on CI
and ideally be a bit less of a maintenance burden.

This commit specifically removes the testing of `wasm32-unknown-unknown`
and replaces it with testing of `wasm32-wasip1`. Along the way there
were a number of other archiectural changes made as well, including:

* A new `target.*.runtool` option can now be specified in `config.toml`
  which is passed as `--runtool` to `compiletest`. This is used to
  reimplement execution of WebAssembly in a less-wasm-specific fashion.

* The default value for `runtool` is an ambiently located WebAssembly
  runtime found on the system, if any. I've implemented logic for
  Wasmtime.

* Existing testing support for `wasm32-unknown-unknown` and Emscripten
  has been removed. I'm not aware of Emscripten testing being run any
  time recently and otherwise `wasm32-wasip1` is in theory the focus now.

* I've added a new `//@ needs-threads` directive for `compiletest` and
  classified a bunch of wasm-ignored tests as needing threads. In theory
  these tests can run on `wasm32-wasi-preview1-threads`, for example.

* I've tried to audit all existing tests that are either
  `ignore-emscripten` or `ignore-wasm*`. Many now run on `wasm32-wasip1`
  due to being able to emit error messages, for example. Many are
  updated with comments as to why they can't run as well.

* The `compiletest` output matching for `wasm32-wasip1` automatically
  uses "match a subset" mode implemented in `compiletest`. This is
  because WebAssembly runtimes often add extra information on failure,
  such as the `unreachable` instruction in `panic!`, which isn't able to
  be matched against the golden output from native platforms.

* I've ported most existing `run-make` tests that use custom Node.js
  wrapper scripts to the new run-make-based-in-Rust infrastructure. To
  do this I added `wasmparser` as a dependency of `run-make-support` for
  the various wasm tests to use that parse wasm files. The one test that
  executed WebAssembly now uses `wasmtime`-the-CLI to execute the test
  instead. I have not ported over an exception-handling test as Wasmtime
  doesn't implement this yet.

* I've updated the `test` crate to print out timing information for WASI
  targets as it can do that (gets a previously ignored test now
  passing).

* The `test-various` image now builds a WASI sysroot for the WASI target
  and additionally downloads a fixed release of Wasmtime, currently the
  latest one at 18.0.2, and uses that for testing.

[Zulip thread]: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Have.20wasm.20tests.20ever.20caused.20problems.20on.20CI.3F/near/424317944
  • Loading branch information
alexcrichton committed Mar 5, 2024
1 parent 50e77f1 commit 1725edc
Show file tree
Hide file tree
Showing 314 changed files with 836 additions and 860 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3305,6 +3305,9 @@ dependencies = [
[[package]]
name = "run_make_support"
version = "0.0.0"
dependencies = [
"wasmparser",
]

[[package]]
name = "rust-demangler"
Expand Down
10 changes: 10 additions & 0 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,16 @@
# See that option for more info.
#codegen-backends = rust.codegen-backends (array)

# This is a "runtool" to pass to `compiletest` when executing tests. Tests will
# execute this tool where the binary-to-test is passed as an argument. Can
# be useful for situations such as when WebAssembly is being tested and a
# runtime needs to be configured.
#
# This configuration is a space-separated list of arguments so `foo bar` would
# execute the program `foo` with the first argument as `bar` and the second
# argument as the test binary.
#runtime = <none> (string)

# =============================================================================
# Distribution options
#
Expand Down
7 changes: 4 additions & 3 deletions library/test/src/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,11 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
// Prevent the usage of `Instant` in some cases:
// - It's currently not supported for wasm targets.
// - We disable it for miri because it's not available when isolation is enabled.
let is_instant_supported =
!cfg!(target_family = "wasm") && !cfg!(target_os = "zkvm") && !cfg!(miri);
let is_instant_unsupported = (cfg!(target_family = "wasm") && !cfg!(target_os = "wasi"))
|| cfg!(target_os = "zkvm")
|| cfg!(miri);

let start_time = is_instant_supported.then(Instant::now);
let start_time = (!is_instant_unsupported).then(Instant::now);
run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?;
st.exec_time = start_time.map(|t| TestSuiteExecTime(t.elapsed()));

Expand Down
19 changes: 7 additions & 12 deletions src/bootstrap/src/core/build_steps/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1656,8 +1656,8 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
// ensure that `libproc_macro` is available on the host.
builder.ensure(compile::Std::new(compiler, compiler.host));

// As well as the target, except for plain wasm32, which can't build it
if suite != "mir-opt" && !target.contains("wasm") && !target.contains("emscripten") {
// As well as the target
if suite != "mir-opt" {
builder.ensure(TestHelpers { target });
}

Expand Down Expand Up @@ -1970,6 +1970,8 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the

if builder.remote_tested(target) {
cmd.arg("--remote-test-client").arg(builder.tool_exe(Tool::RemoteTestClient));
} else if let Some(tool) = builder.runtool(target) {
cmd.arg("--runtool").arg(tool);
}

if suite != "mir-opt" {
Expand Down Expand Up @@ -2505,20 +2507,13 @@ fn prepare_cargo_test(
dylib_path.insert(0, PathBuf::from(&*builder.sysroot_libdir(compiler, target)));
cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());

if target.contains("emscripten") {
cargo.env(
format!("CARGO_TARGET_{}_RUNNER", envify(&target.triple)),
builder.config.nodejs.as_ref().expect("nodejs not configured"),
);
} else if target.starts_with("wasm32") {
let node = builder.config.nodejs.as_ref().expect("nodejs not configured");
let runner = format!("{} {}/src/etc/wasm32-shim.js", node.display(), builder.src.display());
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target.triple)), &runner);
} else if builder.remote_tested(target) {
if builder.remote_tested(target) {
cargo.env(
format!("CARGO_TARGET_{}_RUNNER", envify(&target.triple)),
format!("{} run 0", builder.tool_exe(Tool::RemoteTestClient).display()),
);
} else if let Some(tool) = builder.runtool(target) {
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target.triple)), tool);
}

cargo
Expand Down
3 changes: 3 additions & 0 deletions src/bootstrap/src/core/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ pub struct Target {
pub musl_libdir: Option<PathBuf>,
pub wasi_root: Option<PathBuf>,
pub qemu_rootfs: Option<PathBuf>,
pub runtool: Option<String>,
pub no_std: bool,
pub codegen_backends: Option<Vec<Interned<String>>>,
}
Expand Down Expand Up @@ -1140,6 +1141,7 @@ define_config! {
qemu_rootfs: Option<String> = "qemu-rootfs",
no_std: Option<bool> = "no-std",
codegen_backends: Option<Vec<String>> = "codegen-backends",
runtool: Option<String> = "runtool",
}
}

Expand Down Expand Up @@ -1858,6 +1860,7 @@ impl Config {
target.musl_libdir = cfg.musl_libdir.map(PathBuf::from);
target.wasi_root = cfg.wasi_root.map(PathBuf::from);
target.qemu_rootfs = cfg.qemu_rootfs.map(PathBuf::from);
target.runtool = cfg.runtool;
target.sanitizers = cfg.sanitizers;
target.profiler = cfg.profiler;
target.rpath = cfg.rpath;
Expand Down
38 changes: 38 additions & 0 deletions src/bootstrap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,44 @@ impl Build {
|| env::var_os("TEST_DEVICE_ADDR").is_some()
}

/// Returns an optional "runtool" to pass to `compiletest` when executing
/// test binaries.
///
/// An example of this would be a WebAssembly runtime when testing the wasm
/// targets.
fn runtool(&self, target: TargetSelection) -> Option<String> {
let configured_runtool =
self.config.target_config.get(&target).and_then(|t| t.runtool.as_ref()).map(|p| &**p);
if let Some(runtool) = configured_runtool {
return Some(runtool.to_owned());
}

if target.starts_with("wasm") && target.contains("wasi") {
self.default_wasi_runtool()
} else {
None
}
}

/// When a `runtool` configuration is not provided and a WASI-looking target
/// is being tested this is consulted to prove the environment to see if
/// there's a runtime already lying around that seems reasonable to use.
fn default_wasi_runtool(&self) -> Option<String> {
let mut finder = crate::core::sanity::Finder::new();

// Look for Wasmtime, and for its default options be sure to disable
// its caching system since we're executing quite a lot of tests and
// ideally shouldn't pollute the cache too much.
if let Some(path) = finder.maybe_have("wasmtime") {
if let Ok(mut path) = path.into_os_string().into_string() {
path.push_str(" run -C cache=n --dir .");
return Some(path);
}
}

None
}

/// Returns the root of the "rootfs" image that this target will be using,
/// if one was configured.
///
Expand Down
17 changes: 14 additions & 3 deletions src/ci/docker/host-x86_64/test-various/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
clang-11 \
llvm-11 \
g++ \
make \
ninja-build \
Expand Down Expand Up @@ -38,10 +39,14 @@ WORKDIR /
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh

COPY host-x86_64/dist-various-2/build-wasi-toolchain.sh /tmp/
RUN /tmp/build-wasi-toolchain.sh

ENV RUST_CONFIGURE_ARGS \
--musl-root-x86_64=/usr/local/x86_64-linux-musl \
--set build.nodejs=/node-v18.12.0-linux-x64/bin/node \
--set rust.lld
--set rust.lld \
--set target.wasm32-wasip1.wasi-root=/wasm32-wasip1

# Some run-make tests have assertions about code size, and enabling debug
# assertions in libstd causes the binary to be much bigger than it would
Expand All @@ -50,7 +55,11 @@ ENV RUST_CONFIGURE_ARGS \
ENV NO_DEBUG_ASSERTIONS=1
ENV NO_OVERFLOW_CHECKS=1

ENV WASM_TARGETS=wasm32-unknown-unknown
RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v18.0.2/wasmtime-v18.0.2-x86_64-linux.tar.xz | \
tar -xJ
ENV PATH "$PATH:/wasmtime-v18.0.2-x86_64-linux"

ENV WASM_TARGETS=wasm32-wasip1
ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_TARGETS \
tests/run-make \
tests/ui \
Expand All @@ -59,7 +68,9 @@ ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_T
tests/codegen \
tests/assembly \
library/core
ENV CC_wasm32_unknown_unknown=clang-11
ENV CC_wasm32_wasip1=clang-11 \
CFLAGS_wasm32_wasip1="--sysroot /wasm32-wasip1" \
AR_wasm32_wasip1=llvm-ar-11

ENV NVPTX_TARGETS=nvptx64-nvidia-cuda
ENV NVPTX_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $NVPTX_TARGETS \
Expand Down
24 changes: 0 additions & 24 deletions src/etc/wasm32-shim.js

This file was deleted.

9 changes: 9 additions & 0 deletions src/tools/compiletest/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,15 @@ impl Config {
self.target_cfg().panic == PanicStrategy::Unwind
}

pub fn has_threads(&self) -> bool {
// Wasm targets don't have threads unless `-threads` is in the target
// name, such as `wasm32-wasip1-threads`.
if self.target.starts_with("wasm") {
return self.target.contains("threads");
}
true
}

pub fn has_asm_support(&self) -> bool {
static ASM_SUPPORTED_ARCHS: &[&str] = &[
"x86", "x86_64", "arm", "aarch64", "riscv32",
Expand Down
1 change: 1 addition & 0 deletions src/tools/compiletest/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,7 @@ const DIAGNOSTICS_DIRECTIVE_NAMES: &[&str] = &[
"needs-sanitizer-shadow-call-stack",
"needs-sanitizer-support",
"needs-sanitizer-thread",
"needs-threads",
"needs-unwind",
"needs-xray",
"no-prefer-dynamic",
Expand Down
5 changes: 5 additions & 0 deletions src/tools/compiletest/src/header/needs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ pub(super) fn handle_needs(
condition: config.run_enabled(),
ignore_reason: "ignored when running the resulting test binaries is disabled",
},
Need {
name: "needs-threads",
condition: config.has_threads(),
ignore_reason: "ignored on targets without threading support",
},
Need {
name: "needs-unwind",
condition: config.can_unwind(),
Expand Down
Loading

0 comments on commit 1725edc

Please sign in to comment.