-
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
Check build target supports std when building with -Zbuild-std=std #14183
base: master
Are you sure you want to change the base?
Check build target supports std when building with -Zbuild-std=std #14183
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @ehuss (or someone else) some time within the next two weeks. Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (
|
r? @ehuss |
Could not assign reviewer from: |
c09ba9c
to
d79de0d
Compare
Thanks for the PR! Unfortunately I'm not sure this is something we can move forward with in the short term. The There is some more information at rust-lang/rust#38338 and rust-lang/wg-cargo-std-aware#6. I would suggest getting in contact with the compiler team to suss out the viability of that. There might be other alternatives, such as providing other rustc options for querying/extracting information like this. |
I didn't think that target-spec-json would need to be stable because Cargo and Rustc are versioned together - this creates a contract between the two, but one that can be changed, similar to the contract between Rustc and std (or stdarch as a closer example). Would a test in Rust that tests this integration alleviate your concerns? We only need to parse part of the spec. Otherwise I doubt the spec can be stabilised, so I think we'd need some other mechanism of getting target info from Rustc. I think this might be useful for things like getting the default panic or unwind strategy too. |
They aren't exactly tightly coupled with each other. Cargo is expected to work on the previous two versions of rustc (although build-std makes that complicated). Also, since cargo is developed in a separate repository, it makes it difficult to synchronize changes, since a change in rustc could immediately break development in cargo. I think it might be possible to support this on an advisory basis, but not something we can strictly rely upon. It would likely need a different approach than what is taken in this PR, though. A few things to consider:
However, I'm reluctant to do that since I think it introduces risk of breaking cargo's CI and development (since cargo's testsuite could fail). I'm not sure how a test in rust-lang/rust would help, since if the compiler needs to make a change to the target-spec-json output or CLI option, there would be a problem where you can't update both rustc and cargo at the same time. That has been a fundamental problem that has prevented us from adding build-std tests to rust-lang/rust. I would suggest opening a dialog with the compiler team to see if they have any input on how cargo and rustc can coordinate to obtain this information in a way that would be reliable. |
☔ The latest upstream changes (presumably #14185) made this pull request unmergeable. Please resolve the merge conflicts. |
I appreciate the detailed answer, thanks. Agreed on the RUSTC_BOOTSTRAP=1 hack. On checking the target-spec-json tracking issue it does seem like there's some support for stabilising the print option which surprised me - I think I had it mixed up in my head with the idea of providing the JSON as an input target. Considering the case where it was stabilised with all fields, current and future, optional. In the case of this PR, it would mean that Cargo handles the "not found" case for the This seems not ideal, but might it be good enough? The alternative would be a stricter form of stabilisation that makes all current fields mandatory, any future fields optional and all enums non-exhaustive and strings have an undefined format. |
d79de0d
to
db383e7
Compare
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.
Regardless of my review, Eric's concern in #14183 (comment) should be deeply considered.
db383e7
to
e34950e
Compare
@weihanglo Regarding Eric's concerns about the use of the The second rustc query wouldn't be needed either if this RFC is implemented, we could revert back to collecting everything with a single query. |
To elaborate on this we tried to make this patch improve things without waiting for the RFC's proposal to become stable by removing RUSTC_BOOTSTRAP and making Cargo tolerate all failure cases. If you'd feel more comfortable waiting for that solution then we're fine with that of course. |
Running cargo with "-Zbuild-std=std" should check whether the build target supports building the standard library. This information can be obtained from rustc with the target-spec-json "--print" option. When 'std' is false for the build target, cargo should not attempt to build the standard library. This avoids the "use of unstable library" errors, as this check is performed before Cargo starts trying to build restricted_std code. Cargo will now emit a warning if the requested target does not support building the standard library, or if there was an issue when collecting the necessary information via rustc
Add a new test case to check cargo handles building a target which doesn't support the standard library properly.
An additional rustc query is made in Cargo to collect the target-spec-json from rustc to determine if the build target supports compiling the standard library. As this functionality relies on a nightly rustc, Cargo does not output any errors when parsing, and continues as normal. This commit adds a new test case to bad_cfg_discovery to ensure that Cargo handles this properly. Unlike the other tests, there is no expected output and an exit code 0.
e34950e
to
37a326e
Compare
#[derive(Deserialize)] | ||
pub struct Metadata { | ||
pub std: Option<bool>, | ||
} | ||
|
||
#[derive(Deserialize)] | ||
pub struct TargetSpec { |
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.
No need to be pub.
Also, while not really a blocker, personally I may move these closer to where they are used, Like inside the gctx.cli_unstable().build_std.is_some() {}
block.
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.
See also the suggestion in #14183 (comment)
// try to fetch this information if rustc allows nightly features. Additionally, | ||
// to avoid making two rustc queries when not required, only try to fetch the | ||
// target-spec when the '-Zbuild-std' option is passed. | ||
if gctx.nightly_features_allowed && gctx.cli_unstable().build_std.is_some() { |
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.
You cannot set build_std = Some(…)
when not on nightly, so it is redundant.
if gctx.nightly_features_allowed && gctx.cli_unstable().build_std.is_some() { | |
if gctx.cli_unstable().build_std.is_some() { |
@@ -183,6 +184,11 @@ impl BuildStd for Execs { | |||
self.arg("--target").arg(rustc_host()); | |||
self | |||
} | |||
|
|||
fn target(&mut self, target: &str) -> &mut Self { |
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.
While this method looks quite convenient, I consider it as a refactor. There are a lot of places just calling cargo.run("build --target).arg("<triple>")
now. I may put off this change a bit if it is only used by one place.
for (target, target_info) in target_data.target_info() { | ||
if target_info.supports_std == Some(false) { | ||
anyhow::bail!( | ||
"building std is not supported on this target: {:?}", | ||
target.short_name() | ||
); | ||
} | ||
} |
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 exposing fn target_info()
, should we have a method say RustcTargetData::supports_std
instead?
for (target, target_info) in target_data.target_info() { | |
if target_info.supports_std == Some(false) { | |
anyhow::bail!( | |
"building std is not supported on this target: {:?}", | |
target.short_name() | |
); | |
} | |
} | |
target_data.supports_std()?; |
A perhaps could show all unsupported targets instead of the first one found.
(though I don't know if people really build multi-target std in one invocation)
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 think this is a good idea. I'll have a think about how to handle listing all unsupported targets.
|
||
let target_spec_cached_output = | ||
rustc.cached_output(&target_spec_process, extra_fingerprint); | ||
|
||
if target_spec_cached_output.is_ok() { | ||
let (stdout, _stderr) = target_spec_cached_output.unwrap(); | ||
match serde_json::from_str(&stdout) { | ||
Result::Ok(val) => { | ||
let target_spec: TargetSpec = val; | ||
supports_std = target_spec.metadata.std; | ||
} | ||
Result::Err(_) => (), | ||
} | ||
} |
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.
rustc.cached_output
caches the rustc output to target/.rustc_info.json
file. I guess it is better not to cache it. A direct call to ProcessBuilder::output
is probably good enough. Would be something like:
let target_spec_cached_output = | |
rustc.cached_output(&target_spec_process, extra_fingerprint); | |
if target_spec_cached_output.is_ok() { | |
let (stdout, _stderr) = target_spec_cached_output.unwrap(); | |
match serde_json::from_str(&stdout) { | |
Result::Ok(val) => { | |
let target_spec: TargetSpec = val; | |
supports_std = target_spec.metadata.std; | |
} | |
Result::Err(_) => (), | |
} | |
} | |
#[derive(Deserialize)] | |
pub struct Metadata { | |
pub std: Option<bool>, | |
} | |
#[derive(Deserialize)] | |
pub struct TargetSpec { | |
pub metadata: Metadata, | |
} | |
if let Ok(output) = target_spec_process.output() { | |
if let Ok(spec) = serde_json::from_slice::<TargetSpec>(&output.stdout) { | |
supports_std = spec.metadata.std; | |
} | |
} |
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.
As Eric has noted, target-spec-json is not going to be stabilized in a near feature, I would suggest not adding a new mode to this test. It is already complicated. The test_std_on_unsupported_target
test should have covered the parsing end-to-end pretty much.
What does this PR try to resolve?
Ensures that Cargo first verifies whether a given target supports building the standard library when the
-Zbuild-std=std
option is passed to Cargo (see issue here). This information can be obtained by queryingrustc --print=target-spec-json
. The target spec "metadata" contains anOption<bool>
determining whether the target supports building std.In the case this value is
false
, cargo will stop the build. This avoids the numerous "use of unstable library" errors, giving a cleaner, and simpler, "building std is not supported on this target".How should we test and review this PR?
It can be manually tested by running
cargo build --target <target> -Zbuild-std=std
. If a target who's target-spec marks std as false is passed, cargo will exit. This works with multiple--target
's, and if any of them don't support std, cargo will exit.Additional Information
This change relies on two things:
aarch64-unknown-none
having it's target-spec metadata completed.