-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Allow configuring unstable flags via config file #8393
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @alexcrichton (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like a reasonable feature to me, I find the description of #8127 pretty compelling! I'm curious though if we could rely on serde for deserialization here?
Additionally can you add a test where a flag is specified but we're on the stable channel? (an error should be returned). Ideally the stable/beta channel would error if anything exists in the unstable
table as well, not just the known keys.
src/cargo/core/features.rs
Outdated
let v = parts.next(); | ||
/// Read unstable options from a hashmap. | ||
/// Intended for consuming unstable settings from config files | ||
pub fn from_table(&mut self, flags: &HashMap<String, String>) -> CargoResult<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of from_table
, perhaps CliUnstable
could implement Deserialize
? That way we could do config.get::<CliUnstable>("unstable")?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That'd be my preference too -- I didn't add it initially for fear of making too large an unsolicited diff, but I'll see what it looks like to swap over the little bespoke deserializer table.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, on closer inspection I think kicking this over to serde is orthogonal -- both the parse
and from_table
functions are updating-in-place, rather than deserializing to construct a new instance. I think that makes a difference here because we rely on being able to differentiate between "false because that's the default" and "explicitly set to false" to give the CLI the ability to override a config file in turning off a Zflag.
I gather it's probably fair game since we're strictly talking about unstable flags / features here, but a hard error on Zflags in config would be a break with how mtime-on-use is configured today in that stable cargo simply ignores the setting. I can see a reasonable argument either way -- Pro: A hard error matches the behavior of setting these same flags at the command line, and reduces the chances of a nasty surprise change-in-behavior if you switch channels and recompile. Con: A hard error means you can't leave unstable options in config and toggle back and forth between stable and nightly, or configure CI runs on nightlies to assert that a new feature does not break a build that works on stable. Let me know how you'd like to see it work, and I'll get it changed. |
Oh right nevermind, I forgot that we ignore it on stable/beta! That seems fine for now! |
@alexcrichton would you still like me to include a Deserialize impl in this PR, given it won't slot into the update path it's modifying (I think, anyway)? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally think that when using .cargo/config.toml
we should go through serde as much as possible. That gives us env vars, good error mesages, etc. Avoiding it will likely result in us quickly having a list of issues to fix and I think it's best to head that off.
src/cargo/util/config/mod.rs
Outdated
// NB. It sucks to parse these twice, but doing it again here | ||
// allows the CLI to override config files for both enabling | ||
// and disabling. | ||
self.unstable_flags.parse(unstable_flags)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this perhaps move from the beginning of the function to the end to avoid doing this twice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can, but it'll introduce a couple funky edge cases around bits of the config deserializer that are themselves gated on Zflags. In particular I'm thinking of advanced_env
(used twice in that file).
EDIT: I haven't gone through the whole call graph from higher up in the initialization function to see where else we might wind up leaning on the cli parsing happening first, but I will, once I've got your other comments addressed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah either way is fine, if it needs to go near the beginning it's gotta go near the beginning, alas!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gave this another go and I think (unfortunately) parsing the cli flags twice is the simplest answer for allowing explicit disables at the command line for flags that are otherwise enabled in config.
EDIT: also I rebased on master, out of force of habit. Just a quick update — I'm still working on this, I just hit an oddity in the Deserializer that's treating missing fields as an error even with
fn null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: serde::Deserializer<'de>,
T: Deserialize<'de> + Default,
{
let option = Option::deserialize(deserializer)?;
Ok(option.unwrap_or_default())
} I'm digging through the Deserializer now to see what's going on there. |
Thinking out loud a bit here: After talking it over with a buddy (thx @packysauce!) I think there's a bit of a layering problem in the As I understand it, that means there are two problems:
While I could work around this for CliUnstable by annotating every non- Alternatively, I can make the change suggested in 1 above — tweak the map accessor to report all fields in the joined set of env/config — but I suspect it'll lead to a bunch of funky corner case bugs around required/unknown fields and env vars. EDIT: added alternative. |
I'd definitely believe that the config bits with serde aren't exactly the best they could be, I've never really fully grok'd how to write a serde deserializer. That being said I'm happy with whatever works here, I think switching all the fields to |
This makes it easier to populate unstable options from configuration files. Might also help make unstable option tests easier to write.
Obviously this only works with nightlies and all that, but if you're e.g. testing resolver v2, want to always have Ztiming on in your workspace, or something like that, this makes it easier to do.
This makes it a little easier to match whatever the natural formatting is for where you're setting unstable options -- CLI or config file.
Co-authored-by: Eric Huss <[email protected]>
These masquerade like construction functions, but they're in-place mutators since they're meant to be used one after another in a precedence chain.
Per comments on the PR, a couple changes need to be backed out.
Tests are currently failing, looks like there's something in the Deserializer impl that's forcing field-missing errors even when the serde `default` annotation is applied.
This is a workaround in case y'all would like to land this change before I'm able to finish up a Deserializer refactor.
The current behavior, with the default-false workaround in place, is not able to identify extra members of the `unstable` table, so it can't error on unexpected members. I'll add this test back in with a Deserializer refactor to clean up the extra logic added to CliUnstable.
I've never totally understood the Changes in this round:
I'm gonna keep hacking on top of this to flip the Deserializer impl around to allow serde's In the meantime please don't hesitate to ask if you'd like me to make some more changes to this first. |
Looks good to me! I'd be fine landing this as-is. FWIW I don't think we want to add |
This patch changes how ConfigMapAccess iterates k/v pairs when deserializing structs. Previously we produced keys for exactly the set of fields needed for a struct, and errored out in the deserializer if we can't find anything for that field. This patch makes us produces keys from the union of two sets: 1. All fields that are both needed for the struct and can be found in the environment. 2. All fields in the config table. This change allows serde's codegen to handle both missing and unknown fields via the usual derive annotations (default or deny_unknown_fields respectively)
Serde's errors for missing fields are a lil' different than the ones from our Deserializer.
Just kidding, this was way simpler than I was making it out to be — the new_map ctor for ConfigMapAccess does the right thing already, it's just the struct behavior is trying to be a little too clever in how it's prefiltering fields. I patched new_struct and dropped the workaround. If this still looks good I think it's ready to rock! Otherwise I'm happy to revert this Deserializer change and ship the |
Happy to help - thanks for the reviews! |
💔 Test failed - checks-azure |
Test failure, bummer. I couldn't find a way to pull the proptest codegen'd case off the builder host, so I've left my workstation running a copy of the failing proptest ( EDIT: 128000 iterations down, no repro yet. Kicking off 5120000 more. |
@bors retry |
thx ehuss! I just got a repro case locally, but skimming the linked thread it looks like y'all already have a handle on where the edge case proptest is turning up is so I'll let it lie. |
☀️ Test successful - checks-azure |
Technically there have to be an infinite supply of edge cases. The problem is NP-Complete so there have to be exponencial cases. So far (at least recently) the proptest are finding particularly un-realistic examples and my investigations have not found ways to make them fast that are worth the additional code complexity. But each one is a new adventure, that can be completely unrelated. |
Update cargo 4 commits in 4f74d9b2a771c58b7ef4906b2668afd075bc8081..43cf77395cad5b79887b20b7cf19d418bbd703a9 2020-07-08 17:13:00 +0000 to 2020-07-13 17:35:42 +0000 - fix: add space to comments (rust-lang/cargo#8476) - Allow configuring unstable flags via config file (rust-lang/cargo#8393) - Add support for rustc's `-Z terminal-width`. (rust-lang/cargo#8427) - Avoid colliding with older Cargo fingerprint changes (rust-lang/cargo#8473)
I just tried this feature with the latest nightly and it seems to work great overall. Thanks a lot @bearcage for implementing this! One minor thing I noticed was that the |
This fixes an issue where the deserializer for `-Zbuild-std` was a bit fancier than the `unstable.build-std` directive. cc rust-lang#8393
@phil-opp looks like that's a bug |
Thanks a lot for the quick investigation and fix! |
…h2406 Ensure `unstable.build-std` works like `-Zbuild-std` This fixes an issue where the deserializer for `-Zbuild-std` was a bit fancier than the `unstable.build-std` directive. cc #8393
Thanks to this PR, now we can use the rust-analyzer with the unstable cargo option! Yay |
…er build If a user adds `-Zbuild-std` to the command line to rebuild the std it is only propagated to the build of each version, but not the runner. These flags, however, can also be configured with [environment variables](rust-lang/cargo#8393). So if a user uses the `CARGO_UNSTABLE_BUILD_STD` environment variable to rebuild the std of the crate, it is propagated to the build of the runner. Since the runner adds `panic=abort`, if the user does not add `CARGO_UNSTABLE_BUILD_STD=std,panic_abort`, there are duplicate lang item errors. These errors might be confusing since the build of the runner is not necessarily known, and its profile even less. See #7
Summary
This fixes #8127 by mapping the
unstable
key in.cargo/config
to Z flags.It should have no impact on stable/beta cargo, and on nightlies it gives folks the ability to configure Z flags for an entire project or workspace. This is meant to make it easier to try things like the new features resolver behavior, mtime-on-use, build-std, or timing reports across a whole project.
I've also included a small (but entirely independent) ergonomics change -- treating dashes and underscores identically in Z flags. That's along for the ride in this PR as the last commit, and is included here because it makes for more idiomatic toml file keys (
print_im_a_teapot = yes
vsprint-im-a-teapot = yes
). Please let me know if y'all would prefer that be in a separate PR, or not happen at all.Test Plan
Apologies if I've missed anything -- this is my first cargo contrib and I've tried to hew to the contributing guide. If I've slipped up, please let me know how and I'll fix it.
NB. My linux machine doesn't have multilib set up, so I disabled cross tests.
cargo test
passes for each commit in the stackrustfmt
Keys in
unstable
which do not correspond with a Z flag will trigger an error indicating the invalid flag came from a config file read:If you'd like to see a test case which isn't represented here, I'm happy to add it. Just let me know.
Documentation
I've included commits in this stack updating the only docs page that seemed relevant to me, skimming the book --
unstable.md
.