Skip to content

Commit

Permalink
add Miri build script (#745)
Browse files Browse the repository at this point in the history
add Miri build script
  • Loading branch information
RalfJung authored May 28, 2019
2 parents 4e329eb + 543e579 commit 3c930e4
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 55 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ env:
global:
- RUST_TEST_NOCAPTURE=1
- RUST_BACKTRACE=1
- RUSTFLAGS="-C debug-assertions"

before_script:
# Linux: install extra stuff for cross-compilation
Expand Down
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,15 @@ rustc_version = { version = "0.2.3", optional = true }
env_logger = "0.6"
log = "0.4"
shell-escape = "0.1.4"
hex = "0.3.2"
rand = "0.6"
# A noop dependency that changes in the Rust repository, it's a bit of a hack.
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
# for more information.
rustc-workspace-hack = "1.0.0"
hex = "0.3.2"
rand = "0.6"
# Depend on num-traits with default features to avoid having to rebuild
# between "cargo build" and "cargo intall".
num-traits = "*"

[build-dependencies]
vergen = "3"
Expand Down
51 changes: 14 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,42 +141,30 @@ version of `rustc` that, instead of compiling your code, runs it. It accepts
all the same flags as `rustc` (though the ones only affecting code generation
and linking obviously will have no effect) [and more][miri-flags].

To run the Miri driver, you need to have the `MIRI_SYSROOT` environment variable
set to an appropriate sysroot. You can generate such a sysroot with the
following incantation:

```
cargo run --bin cargo-miri -- miri setup
```

This basically runs the `cargo-miri` binary (which backs the `cargo miri`
subcommand) with `cargo`, and asks it to `setup`. It should in the end print
the directory where the libstd was built. In the following, we will assume it
is `~/.cache/miri/HOST`; you may have to adjust that if you are not using Linux.

Now you can run the driver directly using
Running the Miri driver requires some fiddling with environment variables, so
the `miri` script helps you do that. For example, you can run the driver on a
particular file by doing

```sh
MIRI_SYSROOT=~/.cache/miri/HOST cargo run tests/run-pass/format.rs # or whatever test you like
./miri run tests/run-pass/format.rs
./miri run tests/run-pass/hello.rs --target i686-unknown-linux-gnu
```

and you can run the test suite using
and you can run the test suite using:

```
cargo test
./miri test
```

We recommend adding the `--release` flag to make tests run faster.

`cargo test --release FILTER` only runs those tests that contain `FILTER` in
their filename (including the base directory, e.g. `cargo test --release fail`
will run all compile-fail tests).
`./miri test FILTER` only runs those tests that contain `FILTER` in their
filename (including the base directory, e.g. `./miri test fail` will run all
compile-fail tests).

You can get a trace of which MIR statements are being executed by setting the
`MIRI_LOG` environment variable. For example:

```sh
MIRI_LOG=info cargo run tests/run-pass/vecs.rs
MIRI_LOG=info ./miri run tests/run-pass/vecs.rs
```

Setting `MIRI_LOG` like this will configure logging for Miri itself as well as
Expand All @@ -185,7 +173,7 @@ can also do more targeted configuration, e.g. the following helps debug the
stacked borrows implementation:

```sh
MIRI_LOG=rustc_mir::interpret=info,miri::stacked_borrows cargo run tests/run-pass/vecs.rs
MIRI_LOG=rustc_mir::interpret=info,miri::stacked_borrows ./miri run tests/run-pass/vecs.rs
```

In addition, you can set `MIRI_BACKTRACE=1` to get a backtrace of where an
Expand All @@ -199,7 +187,7 @@ is probably easier to test it with the cargo wrapper. You can install your
development version of Miri using

```
cargo install --path . --force
./miri install
```

and then you can use it as if it was installed by `rustup`. Make sure you use
Expand Down Expand Up @@ -235,18 +223,7 @@ rustup override set custom
```

With this, you should now have a working development setup! See
[above][testing-miri] for how to proceed working with the Miri driver. Notice
that rustc's sysroot is already built for Miri in this case, so you can set
`MIRI_SYSROOT=$(rustc --print sysroot)`.

Running `cargo miri` in this setup is a bit more complicated, because the Miri
binary you just created needs help to find the libraries it links against. On
Linux, you can set the rpath to make this "just work":

```sh
export RUSTFLAGS="-C link-args=-Wl,-rpath,$(rustc --print sysroot)/lib/rustlib/x86_64-unknown-linux-gnu/lib"
cargo install --path . --force
```
[above][testing-miri] for how to proceed working with the Miri driver.

### Miri `-Z` flags and environment variables
[miri-flags]: #miri--z-flags-and-environment-variables
Expand Down
83 changes: 83 additions & 0 deletions miri
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/bin/sh
set -e
# I'd love to use `jq` for parsing the JSON properly, but macOS is totally underequipped for this kind of work.
TARGET=$(rustc --print target-spec-json -Z unstable-options | grep llvm-target | cut -d '"' -f 4)
SYSROOT=$(rustc --print sysroot)
# We set the rpath so that Miri finds the private rustc libraries it needs.
# We enable debug-assertions to get tracing.
# We enable line-only debuginfo for backtraces.
export RUSTFLAGS="-C link-args=-Wl,-rpath,$SYSROOT/lib/rustlib/$TARGET/lib -C debug-assertions -C debuginfo=1"

## Helper functions

# Build a sysroot and set MIRI_SYSROOT to use it. Arguments are passed to `cargo miri setup`.
build_sysroot() {
# Build once, for the user to see.
cargo run --release --bin cargo-miri -- miri setup "$@"
# Call again, to just set env var.
eval $(cargo run --release -q --bin cargo-miri -- miri setup --env "$@")
export MIRI_SYSROOT
}

# Prepare and set MIRI_SYSROOT. Respects `MIRI_TEST_TARGET` and takes into account
# locally built vs. distributed rustc.
find_sysroot() {
# Get ourselves a sysroot
if [ -n "$MIRI_SYSROOT" ]; then
# Sysroot already set, use that.
true
elif echo "$SYSROOT" | egrep -q 'build/[^/]+/stage'; then
# A local rustc build.
if [ -n "$MIRI_TEST_TARGET" ]; then
# Foreign targets still need a build. Use the rustc sources.
export XARGO_RUST_SRC="$SYSROOT/../../../src"
build_sysroot --target "$MIRI_TEST_TARGET"
else
# Assume we have a proper host libstd in $SYSROOT.
true
fi
else
# A normal toolchain. We have to build a sysroot either way.
if [ -n "$MIRI_TEST_TARGET" ]; then
build_sysroot --target "$MIRI_TEST_TARGET"
else
build_sysroot
fi
fi
}

## Main

COMMAND="$1"
shift

case "$COMMAND" in
install)
# "--locked" to respect the Cargo.lock file if it exists,
# "--offline" to avoid querying the registry (for yanked packages).
exec cargo "$COMMAND" --path "$(dirname "$0")" --force --locked --offline "$@"
;;
build)
# Build, and let caller control flags.
exec cargo "$COMMAND" --release "$@"
;;
test|run)
# In "run" mode, scan for "--target" to set the "MIRI_TEST_TARGET" env var so
# that we set the MIRI_SYSROOT up the right way.
if [ "$COMMAND" = "run" ] && [ -z "$MIRI_TEST_TARGET" ]; then
for ARG in "$@"; do
if [ "$LAST_ARG" = "--target" ]; then
# Found it!
export MIRI_TEST_TARGET="$ARG"
break
fi
LAST_ARG="$ARG"
done
fi
# First build and get a sysroot.
cargo build --release
find_sysroot
# Then run the actual command.
exec cargo "$COMMAND" --release "$@"
;;
esac
5 changes: 4 additions & 1 deletion src/bin/cargo-miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ path = "lib.rs"
File::create(dir.join("lib.rs")).unwrap();
// Run xargo.
let target = get_arg_flag_value("--target");
let print_env = !ask_user && has_arg_flag("--env"); // whether we just print the necessary environment variable
let mut command = Command::new("xargo");
command.arg("build").arg("-q")
.current_dir(&dir)
Expand All @@ -265,7 +266,9 @@ path = "lib.rs"
};
let sysroot = if is_host { dir.join("HOST") } else { PathBuf::from(dir) };
std::env::set_var("MIRI_SYSROOT", &sysroot);
if !ask_user {
if print_env {
println!("MIRI_SYSROOT={}", sysroot.display());
} else if !ask_user {
println!("A libstd for Miri is now available in `{}`", sysroot.display());
}
}
Expand Down
21 changes: 7 additions & 14 deletions travis.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,29 @@ set -euo pipefail

# Determine configuration
if [ "$TRAVIS_OS_NAME" == osx ]; then
MIRI_SYSROOT_BASE=~/Library/Caches/miri.miri.miri/
FOREIGN_TARGET=i686-apple-darwin
else
MIRI_SYSROOT_BASE=~/.cache/miri/
FOREIGN_TARGET=i686-unknown-linux-gnu
fi

# Prepare
echo "Build and install miri"
cargo build --release --all-features --all-targets
cargo install --all-features --force --path .
echo

echo "Get ourselves a MIR-full libstd for the host and a foreign architecture"
cargo miri setup
cargo miri setup --target "$FOREIGN_TARGET"
./miri build --all-features --all-targets
./miri install
echo

# Test
function run_tests {
cargo test --release --all-features
test-cargo-miri/run-test.py
./miri test
# "miri test" has built the sysroot for us, now this should pass without
# any interactive questions.
test-cargo-miri/run-test.py
}

echo "Test host architecture"
export MIRI_SYSROOT="$MIRI_SYSROOT_BASE"/HOST
run_tests
echo

echo "Test foreign architecture ($FOREIGN_TARGET)"
export MIRI_SYSROOT="$MIRI_SYSROOT_BASE" MIRI_TEST_TARGET="$FOREIGN_TARGET"
run_tests
MIRI_TEST_TARGET="$FOREIGN_TARGET" run_tests
echo

0 comments on commit 3c930e4

Please sign in to comment.