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

Fetch miri flags from cargo config #2451

Closed
wants to merge 3 commits into from
Closed
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
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ their name.

You can pass arguments to Miri via `MIRIFLAGS`. For example,
`MIRIFLAGS="-Zmiri-disable-stacked-borrows" cargo miri run` runs the program
without checking the aliasing of references.
without checking the aliasing of references. Also, you can set the `miri.flags`
setting in [cargo configuration](https://doc.rust-lang.org/cargo/reference/config.html).

When compiling code via `cargo miri`, the `cfg(miri)` config flag is set for code
that will be interpret under Miri. You can use this to ignore test cases that fail
Expand Down Expand Up @@ -262,7 +263,22 @@ up the sysroot. If you are using `miri` (the Miri driver) directly, see the
[miri-flags]: #miri--z-flags-and-environment-variables

Miri adds its own set of `-Z` flags, which are usually set via the `MIRIFLAGS`
environment variable. We first document the most relevant and most commonly used flags:
environment variable or using the `miri.flags` setting in
[cargo configuration](https://doc.rust-lang.org/cargo/reference/config.html),
they are mutually exclusive with priority of `MIRIFLAGS` environment variable:

```bash
MIRIFLAGS="-Zmiri-disable-stacked-borrows" cargo miri run
```

```toml
# .cargo/config.toml

[miri]
flags = ["-Zmiri-disable-isolation", "-Zmiri-report-progress"]
```

We first document the most relevant and most commonly used flags:

* `-Zmiri-compare-exchange-weak-failure-rate=<rate>` changes the failure rate of
`compare_exchange_weak` operations. The default is `0.8` (so 4 out of 5 weak ops will fail).
Expand Down
31 changes: 26 additions & 5 deletions cargo-miri/src/phases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::fs::{self, File};
use std::io::BufReader;
use std::path::PathBuf;
use std::process::Command;
use std::str;

use crate::{setup::*, util::*};

Expand Down Expand Up @@ -517,11 +518,10 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
cmd.arg(arg);
}
}
// Respect `MIRIFLAGS`.
if let Ok(a) = env::var("MIRIFLAGS") {
// This code is taken from `RUSTFLAGS` handling in cargo.
let args = a.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string);
cmd.args(args);

// Respect flags.
if let Some(flags) = get_miriflags() {
cmd.args(flags);
}

// Then pass binary arguments.
Expand All @@ -541,6 +541,27 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
}
}

fn get_miriflags() -> Option<Vec<String>> {
avrong marked this conversation as resolved.
Show resolved Hide resolved
avrong marked this conversation as resolved.
Show resolved Hide resolved
// Fetch miri flags from cargo config.
let mut cmd = cargo();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to do a bit more work here: the user might have passed --config ... to cargo miri run, which would be ignored this way. See get_cargo_metadata for how to forward that. Also they might have set the manifest path, which even get_cargo_metadata gets wrong.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once #2474 lands there will be a function cargo_extra_flags you can call to get all the flags you need.

However, you can only call that function in phase_cargo_miri; the arguments are lost here in phase_runner. So you'll need to do the flag handling there, and then put the results into an env var that is picked up again in phase_runner.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the "right" way to do that is to have a variable like CARGO_ENCODED_MIRIFLAGS. Then phase_cargo_miri can determine the flags (from either MIRIFLAGS or via the config -- or from CARGO_ENCODED_MIRIFLAGS if that is already set -- and then store the flags losslessly (without spaces causing trouble) into CARGO_ENCODED_MIRIFLAGS here phase_runner can retreive it.

If that's more work than what you signed up for, then storing it into MIRIFLAGS and not supporting spaces within arguments for now would also work.

cmd.args(["-Zunstable-options", "config", "get", "miri.flags", "--format=json-value"]);
let output = cmd.output().expect("failed to run `cargo config`");
let config_miriflags =
str::from_utf8(&output.stdout).expect("failed to get `cargo config` output");

// Respect `MIRIFLAGS` and `miri.flags` setting in cargo config.
// If MIRIFLAGS is present, flags from cargo config are ignored.
// This matches cargo behavior for RUSTFLAGS.
if let Ok(a) = env::var("MIRIFLAGS") {
// This code is taken from `RUSTFLAGS` handling in cargo.
Some(a.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect())
} else if let Ok(args) = serde_json::from_str::<Vec<String>>(config_miriflags) {
Some(args)
} else {
None
}
}

pub fn phase_rustdoc(mut args: impl Iterator<Item = String>) {
let verbose = std::env::var("MIRI_VERBOSE")
.map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer"));
Expand Down
46 changes: 45 additions & 1 deletion test-cargo-miri/run-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def test(name, cmd, stdout_ref, stderr_ref, stdin=b'', env={}):

stdout_matches = check_output(stdout, stdout_ref, "stdout")
stderr_matches = check_output(stderr, stderr_ref, "stderr")

if p.returncode == 0 and stdout_matches and stderr_matches:
# All good!
return
Expand Down Expand Up @@ -90,6 +90,12 @@ def test_no_rebuild(name, cmd, env={}):
fail("Something was being rebuilt when it should not be (or we got no output)");

def test_cargo_miri_run():
# Try to remove cargo config to avoid unexpected settings
try:
os.remove('.cargo/config.toml')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized this seems to edit the same file as

miri/ci.sh

Line 49 in aa53f3f

echo 'build.rustc-wrapper = "thisdoesnotexist"' > .cargo/config.toml
...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah, the path should be absolute here

Copy link
Member

@RalfJung RalfJung Aug 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not what I mean. It can be relative, the other paths in the python file are also relative.

What I mean is that removing that file removes testing the build.rustc-wrapper = "thisdoesnotexist" configuration!

except OSError:
pass

test("`cargo miri run` (no isolation)",
cargo_miri("run"),
"run.default.stdout.ref", "run.default.stderr.ref",
Expand All @@ -99,6 +105,44 @@ def test_cargo_miri_run():
'MIRITESTVAR': "wrongval", # make sure the build.rs value takes precedence
},
)

# Create a config with isolation disabled
try:
os.mkdir('.cargo')
except OSError:
pass

with open(".cargo/config.toml", "w") as f:
f.write('[miri]\nflags = ["-Zmiri-disable-isolation"]')

# Testing miri.flags are taken in account
test("`cargo miri run` (no isolation, set from cargo config)",
cargo_miri("run"),
"run.default.stdout.ref", "run.default.stderr.ref",
stdin=b'12\n21\n',
env={
'MIRITESTVAR': "wrongval", # make sure the build.rs value takes precedence
},
)

# Create an empty config
with open(".cargo/config.toml", "w") as f:
f.write('[miri]\nflags = [""]')
avrong marked this conversation as resolved.
Show resolved Hide resolved

# Check that MIRIFLAGS envar has higher precedence tha cargo config
test("`cargo miri run` (no isolation, ignoring cargo config)",
cargo_miri("run"),
"run.default.stdout.ref", "run.default.stderr.ref",
stdin=b'12\n21\n',
env={
'MIRIFLAGS': "-Zmiri-disable-isolation",
'MIRITESTVAR': "wrongval", # make sure the build.rs value takes precedence
},
)

# Cleaning up config after all config-related tests
os.remove('.cargo/config.toml')

# Special test: run it again *without* `-q` to make sure nothing is being rebuilt (Miri issue #1722)
test_no_rebuild("`cargo miri run` (no rebuild)",
cargo_miri("run", quiet=False) + ["--", ""],
Expand Down