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

Adjusting -Zon-broken-pipe=kill to only be applied to rustc/rustdoc binary wrappers break two cargo tests #131059

Closed
jieyouxu opened this issue Sep 30, 2024 · 14 comments · Fixed by #131155
Labels
A-testsuite Area: The testsuite used to check the correctness of rustc C-bug Category: This is a bug. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-cargo Relevant to the cargo team, which will review and decide on the PR/issue.

Comments

@jieyouxu
Copy link
Member

jieyouxu commented Sep 30, 2024

In https://rust-lang.zulipchat.com/#narrow/stream/326414-t-infra.2Fbootstrap/topic/Modifying.20run-make.20tests.20unnecessarily.20rebuild.20stage.201.20cargo we identified the root cause of #130980 (unnecessary stage 1 cargo rebuilds) being

// `-Zon-broken-pipe=kill` breaks cargo tests
if !path.ends_with("cargo") {
// If the output is piped to e.g. `head -n1` we want the process to be killed,
// rather than having an error bubble up and cause a panic.
cargo.rustflag("-Zon-broken-pipe=kill");
}

The other instance of this is

cargo.rustflag("-Zon-broken-pipe=kill");

As @onur-ozkan (thank you so much) pointed out:

It's not just about cargo builds. If you run x build clippy and then x build cargo, the build cache for clippy will be broken because bootstrap sets -Zon-broken-pipe=kill for every invocation except for cargo. Which means building cargo will invalidate any tool build cache which was built previously.


I tried to limit application of -Zon-broken-pipe=kill to rustc and rustdoc binary wrappers in src/bootstrap/src/bin/{rustc,rustdoc}.rs themselves, and unfortunately this leads to two cargo test failures:

---- build::close_output stdout ----
thread 'build::close_output' panicked at tests/testsuite/build.rs:6680:18:

---- expected: tests/testsuite/build.rs:6682:9
++++ actual:   In-memory
   1    1 | [COMPILING] foo v0.1.0 ([ROOT]/foo)
   2    2 | hello stderr!
   3      - [ERROR] [BROKEN_PIPE]
   4      - [WARNING] build failed, waiting for other jobs to finish...

Update with SNAPSHOTS=overwrite

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- cargo_command::closed_output_ok stdout ----
thread 'cargo_command::closed_output_ok' panicked at tests/testsuite/cargo_command.rs:549:5:
assertion failed: status.success()


failures:
    build::close_output
    cargo_command::closed_output_ok
Previous history

I'm putting up a PR (#131060) to remove conditionally adding -Zon-broken-pipe=kill, and as @saethlin pointed out we expect two cargo tests to be broken with RUSTFLAGS=-Zon-broken-pipe=kill ./x test src/tools/cargo if that is unconditionally passed to bootstrap.

---- build::close_output stdout ----
thread 'build::close_output' panicked at tests/testsuite/build.rs:6680:18:

---- expected: tests/testsuite/build.rs:6682:9
++++ actual:   In-memory
   1    1 | [COMPILING] foo v0.1.0 ([ROOT]/foo)
   2    2 | hello stderr!
   3      - [ERROR] [BROKEN_PIPE]
   4      - [WARNING] build failed, waiting for other jobs to finish...

Update with SNAPSHOTS=overwrite

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- cargo_command::closed_output_ok stdout ----
thread 'cargo_command::closed_output_ok' panicked at tests/testsuite/cargo_command.rs:549:5:
assertion failed: status.success()


failures:
    build::close_output
    cargo_command::closed_output_ok

I don't know if this breaks someone's workflow regarding to piping cargo tool tests, but in the interest of not making everyone rebuild stage1 cargo on every single invocation of ./x test run-make, I'm considering this as "known breakage" for the time being. As @saethlin suggested:

If someone wants to both set -Zon-broken-pipe and also run the Cargo test suite, I think they should file an issue and be part of a discussion about how to support that and what the costs are.

@jieyouxu jieyouxu added A-testsuite Area: The testsuite used to check the correctness of rustc C-bug Category: This is a bug. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) labels Sep 30, 2024
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Sep 30, 2024
@jieyouxu jieyouxu removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Sep 30, 2024
@jieyouxu
Copy link
Member Author

cc @rust-lang/cargo team in case anyone has any advice regarding these two tests.

@jieyouxu
Copy link
Member Author

jieyouxu commented Sep 30, 2024

cc @Enselic if you have any advice since this was added in #124480 for #97889 (it didn't occur to me that adding -Zon-broken-pipe=kill would cause build cache invalidation problems), presumably to workaround the cargo test failures.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Sep 30, 2024
Drop conditionally applied cargo `-Zon-broken-pipe=kill` flags to fix stage 1 cargo rebuilds

The conditionally applied `-Zon-broken-pipe=kill` flag trigger rebuilds because they can invalidate previous tool build cache due to differing flags. This PR removes those flags to stop tool build cache invalidation.

> The build cache for clippy will be broken because bootstrap sets `-Zon-broken-pipe=kill` for every invocation except for cargo. Which means building cargo will invalidate any tool build cache which was built previously.
>
> *From <https://rust-lang.zulipchat.com/#narrow/stream/326414-t-infra.2Fbootstrap/topic/Modifying.20run-make.20tests.20unnecessarily.20rebuild.20stage.201.20cargo/near/473486972>*

Fixes rust-lang#130980.
But introduces rust-lang#131059 (breaks 2 cargo tests that relied upon some behavior related to `-Zon-broken-pipe=kill` being set).

r? `@onur-ozkan` (or bootstrap)
@Enselic
Copy link
Member

Enselic commented Sep 30, 2024

Thanks for the ping. Unfortunately I currently do not have the time to look into this.

@jieyouxu
Copy link
Member Author

No worries that is fine, pinged just in case you had immediate context or advice.

@weihanglo
Copy link
Member

Maybe we should only run those two tests in rust-lang/cargo CI?

@ehuss
Copy link
Contributor

ehuss commented Sep 30, 2024

Can you give some more context? What are the benefits and drawbacks of -Zon-broken-pipe=kill? What does that look like to the user of rustc and cargo?

I don't quite understand #131060. It looks like it regresses something, but I'm not clear what. If it is breaking cargo tests, how is it possible for that PR to land? It seems like removing -Zon-broken-pipe=kill would be regressing whatever issue it was added to fix in the first place, but I don't know what that is.

@saethlin
Copy link
Member

The linked PR does not break Cargo tests, it possibly breaks someone's contributor workflow that relies on bootstrap implicitly setting -Zon-broken-pipe=kill. Bootstrap can't implicitly set that flag for everything, or it breaks Cargo's tests.

The bug here is that none of the numerous people who looked at the original PR (including me) noticed that the right thing to do here is not set the flag at all, because inconsistent flag-setting creates spurious rebuilds.

rust-timer added a commit to rust-lang-ci/rust that referenced this issue Sep 30, 2024
Rollup merge of rust-lang#131060 - jieyouxu:rmake-rebuild, r=onur-ozkan

Drop conditionally applied cargo `-Zon-broken-pipe=kill` flags to fix stage 1 cargo rebuilds

The conditionally applied `-Zon-broken-pipe=kill` flag trigger rebuilds because they can invalidate previous tool build cache due to differing flags. This PR removes those flags to stop tool build cache invalidation.

> The build cache for clippy will be broken because bootstrap sets `-Zon-broken-pipe=kill` for every invocation except for cargo. Which means building cargo will invalidate any tool build cache which was built previously.
>
> *From <https://rust-lang.zulipchat.com/#narrow/stream/326414-t-infra.2Fbootstrap/topic/Modifying.20run-make.20tests.20unnecessarily.20rebuild.20stage.201.20cargo/near/473486972>*

Fixes rust-lang#130980.
But introduces rust-lang#131059 (breaks 2 cargo tests that relied upon some behavior related to `-Zon-broken-pipe=kill` being set).

r? `@onur-ozkan` (or bootstrap)
@jieyouxu
Copy link
Member Author

jieyouxu commented Sep 30, 2024

Yeah sorry I didn't know how to word the issue title accurately (please rename this issue if there's an more accurate description), #131060 will remove conditionally passed -Zon-broken-pipe=kill flags passed to cargo from bootstrap, which might break someone's workflow related to running cargo tool tests through bootstrap if they wanted to do something like ./x test src/tools/cargo | head -n 1 (AFAICT) because now we don't pass -Zon-broken-pipe=kill at all, but unconditionally setting RUSTFLAGS=-Zon-broken-pipe=kill will cause the aforementioned test failures.

EDIT: I tried to fixup the issue description and title to be more accurate.

@jieyouxu jieyouxu changed the title Fixing stage1 cargo rebuilds by removing RUSTFLAGS=-Zon-broken-pipe=kill will break two cargo tests Fixing stage1 cargo rebuilds by removing RUSTFLAGS=-Zon-broken-pipe=kill might break some cargo tool testing workflow related to piping Sep 30, 2024
@ehuss
Copy link
Contributor

ehuss commented Sep 30, 2024

Sorry, I'm still not clear what is being changed or what is being proposed. Also, sorry for many questions, but I don't have context on what is going on.

What happens after #131060 when someone does ./x test src/tools/cargo | head -n 1? What happened before?

Is the concern after #131060 specifically about ./x test src/tools/cargo, or is it all bootstrap commands? Some of them?

Does #131060 change the dist artifacts? If so, how? How much of this is specific to using ./x versus using rustc and cargo?

Is there an original issue related to bootstrap workflows that initiated the introduction of -Zon-broken-pipe=kill? Who is asking for a different behavior?

Looking at #124480, it seems like this stems back to #49606. How does rustc now handle sigpipe? My naive thinking is that removing -Zon-broken-pipe=kill causes that to regress, but it doesn't seem like that is what you are proposing, and clearly no tests are being broken. But I don't see how all these pieces fit together.

@ChrisDenton
Copy link
Member

-Zon-broken-pipe=kill only affects the entry point. Setting it on libraries is useless.

I think cargo rustc could be used to add flags that only effect the final build and not any dependency.

@jieyouxu
Copy link
Member Author

jieyouxu commented Oct 1, 2024

Sorry, I'm still not clear what is being changed or what is being proposed. Also, sorry for many questions, but I don't have context on what is going on.

... Actually thanks for asking these questions, it seems that simply removing these flags to fix rebuilds will regress rustc (and probably rustdoc) behavior with respect to broken pipes. rustc will ICE locally (e.g. rustc +stage1 --print=sysroot | false, i.e. reintroduces #49606) without being built with that flag being set.

The context is that I was trying to make ./x test run-make --stage 1 not have to rebuild stage1 cargo even if nothing changed, because -Zon-broken-pipe=kill was not universally applied to tool builds (specifically excluding cargo), which causes tool build cache invalidation because stage1 cargo has to be rebuilt due to the differing flags.

I will revert #131060, investigate why regression test for rustc doesn't catch the broken pipe ICE, and reinvestigate what is going on with -Zon-broken-pipe.

@jieyouxu
Copy link
Member Author

jieyouxu commented Oct 1, 2024

Does #131060 change the dist artifacts? If so, how? How much of this is specific to using ./x versus using rustc and cargo?

Apparently it does, I'm putting up a revert now. This unfortunately affects how rustc itself is built.

Is there an original issue related to bootstrap workflows that initiated the introduction of -Zon-broken-pipe=kill? Who is asking for a different behavior?

This was originally related to #124480 where we changed from the in-source attribute of #[unix_sigpipe = "sig_dfl"] UI to a command-line UI -Zon-broken-pipe=kill, so they were added in an attempt to preserve the existing behavior of kill process if pipe is broken, instead of panicking (and possibly leading to an ICE for rustc specifically).

Looking at #124480, it seems like this stems back to #49606. How does rustc now handle sigpipe? My naive thinking is that removing -Zon-broken-pipe=kill causes that to regress, but it doesn't seem like that is what you are proposing, and clearly no tests are being broken. But I don't see how all these pieces fit together.

How does rustc now handle sigpipe?

Apparently through that -Zon-broken-pipe=kill flag when the rustc main binary is being built.

My naive thinking is that removing -Zon-broken-pipe=kill causes that to regress, but it doesn't seem like that is what you are proposing, and clearly no tests are being broken. But I don't see how all these pieces fit together.

AFAICT your suspicions are correct, and indeed removing the flag will regress that. I don't know why no tests are being broken, which probably means no tests were actually exercising this behavior.

@jieyouxu
Copy link
Member Author

jieyouxu commented Oct 1, 2024

@ehuss I tried to do some digging, and here's what I came up with.


Note: the following investigation is to the best of my understanding and most likely contains inaccuracies and misleading representations, reader discretion is required

Context and Archaeology

// `-Zon-broken-pipe=kill` breaks cargo tests
if !path.ends_with("cargo") {
    // If the output is piped to e.g. `head -n1` we 
    // want the process to be killed,
    // rather than having an error bubble up and cause a panic.
cargo.rustflag("-Zon-broken-pipe=kill");
}
  • In fix broken build cache caused by rustdoc builds #126934 we moved -Zon-broken-pipe=kill being set only for Rustdoc tool build from impl Step for Rustdoc to prepare_tool_cargo to help mitigate rebuilds for Miri tests (because setting only -Zon-broken-pipe=kill for rustdoc means if you build another tool, the tool build cache will get invalidated due to differing RUSTFLAGS).
    • Note that this particular instance of -Zon-broken-pipe=kill is for tool builds and cargo, there is a different instance of -Zon-broken-pipe=kill for building the rustc binary itself in /src/core/build_steps/compile.rs.
    • Conditionally omitting -Zon-broken-pipe=kill for cargo itself does not cause tool build cache invalidation before Fix cargo staging for run-make tests #130739 (and Pass the current cargo to run-make tests #130642) because we were simply using bootstrap cargo (or rather, initial_cargo in bootstrap terms) and not try to build stage 1 cargo ourselves. Only run-make tests want to build stage 1 cargo, even miri tests just use initial cargo. Now that run-make tests builds stage 1 cargo, the omitted -Zon-broken-pipes=kill for cargo tool build specifically will lead to tool build cache invalidation due to differing RUSTFLAGS.
    • Note that I don't fully understand how tool build caches work and why differing RUSTFLAGS for one of the tools can lead to tool build cache invalidation for all of the tools built from the same stage compiler.

What about #131060?

  • I tried to remove both instances of -Zon-broken-pipe=kill:
    1. The -Zon-broken-pipe=kill instance in prepare_tool_cargo: this I suspect was intended for rustdoc binary. Now I think I understand I cannot remove this flag, because to at least rustdoc binary I believe -Zon-broken-pipe=kill is load-bearing for kill-process-on-broken-pipe behavior due to #[unix_sigpipe] UI changes to command-line flag as aforementioned. Not sure how cargo tests are affected, which is why the flag is not unconditionally always set in the first place.
    2. The -Zon-broken-pipe=kill instance in src/core/build_steps/compile.rs: this I definitely should not have removed because as I described previously is now load-bearing for the rustc binary for kill-process-on-broken-pipe, otherwise it will reintroduce ICE on "rustc --help | false" #34376.

How do we fix this?

  • I intend to revert Drop conditionally applied cargo -Zon-broken-pipe=kill flags to fix stage 1 cargo rebuilds #131060 now that I have a better understanding of why these flags are even there in the first place, and not having the -Zon-broken-pipes=kill flags set regresses broken-pipe behavior for rustc and rustdoc at the minimum. This effect includes dist builds of them so I practically introduced a P-critical regression to rustc and rustdoc if not also for other tools.
  • I intend to try unconditionally set -Zon-broken-pipe=kill for all tool builds in prepare_tool_cargo so that (1) it preserves desired kill-process-on-broken-pipe behavior for rustdoc and other tools that are typical of cli tools and (2) it should not cause tool build cache invalidation because it is not conditionally absent from cargo. I intend to investigate/fix a few things:
    1. Work with T-cargo and T-bootstrap to figure out why some cargo tests may fail on -Zon-broken-pipe=kill and either fix those tests and/or fix how cargo is building itself.
    2. Investigate why zero tests failed in full CI and locally, in both rustc and rustdoc test suites, even if I reintroduce the ICE on "rustc --help | false" #34376 ICE for rustc and a similar panic for rustdoc. I intend to add regression tests for rustc and rustdoc for this behavior specifically, or fix the existing tests that didn't test what they intended to test.

jieyouxu added a commit to jieyouxu/rust that referenced this issue Oct 1, 2024
This reverts commit 5a7058c.

In [rust-lang#131059] we found out that `-Zon-broken-pipe=kill` is actually
**load-bearing** [1] for (at least) `rustc` and `rustdoc` to have the
kill-process-on-broken-pipe behavior, e.g. `rustc --print=sysroot |
false` will ICE and `rustdoc --print=sysroot | false` will panic on a
broken pipe.

[rust-lang#131059]: rust-lang#131059
[1]: rust-lang#131059 (comment)
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Oct 2, 2024
…r-ozkan

Revert rust-lang#131060 "Drop conditionally applied cargo `-Zon-broken-pipe=kill` flags"

In [rust-lang#131059] we found out that `-Zon-broken-pipe=kill` is actually **load-bearing**[^1] for
(at least) `rustc` and `rustdoc` to have the kill-process-on-broken-pipe behavior, e.g. `rustc
--print=sysroot | false` will ICE and `rustdoc --print=sysroot | false` will panic on a broken pipe.

This PR reverts 5a7058c (reverts PR rust-lang#131060) in favor of a future
fix to *unconditionally* apply `-Zon-broken-pipe=kill` to tool builds and also not drop the
`-Zon-broken-pipe=kill` flag for rustc binary builds.

I could not figure out how to write a regression test for the `rustc --print=sysroot | false`
behavior on Unix, so this is a plain revert for now.

This revert will unfortunately reintroduce rust-lang#130980 until we fix it again with the different approach.

See more details at <rust-lang#131059 (comment)> and in the timeline below.

### Timeline of kill-process-on-broken-pipe behavior changes

See [`unix_sigpipe` tracking issue rust-lang#97889][rust-lang#97889] for more context around unix sigpipe handling.

- From the very beginning since 2014, Rust binaries by default use `sig_ign`. This meant that if
  output pipe is broken yet the program tries to use `println!` and such, there will be a broken
  pipe panic from std. This lead to ICEs from e.g. `rustc --help | false` [rust-lang#34376].
- [rust-lang#49606] mitigated [rust-lang#34376] by adding an explicit signal handler to `rustc_driver` register a
  sigpipe handler with `SIG_DFL` which will cause the binary using `rustc_driver` to terminate if
  `rustc_driver::set_sigpipe_handler()` is called. `rustc`'s main binary wrapper uses
  `rustc_driver::set_sigpipe_handler()`, and so does `rustdoc`.
- A more universal way to set sigpipe behavior for Unix was introduced as part of [rust-lang#97889], i.e. `#
  [unix_sigpipe = "sig_dfl"]` attribute.
- [rust-lang#102587] migrated `rustc` to use `#[unix_sigpipe = "sig_dfl"]` instead of
  `rustc_driver::set_sigpipe_handler`.
- [rust-lang#103495] migrated `rustdoc` to use `#[unix_sigpipe = "sig_dfl"]` instead of
  `rustc_driver::set_sigpipe_handler`. `rustc_driver::set_sigpipe_handler` was removed.
- Following concerns about sigpipe setting UI in [rust-lang#97889], the UI for specifying sigpipe behavior
  was changed in [rust-lang#124480] from `#[unix_sigpipe = "sig_dfl"]` attribute to the commmand line flag
  `-Zon-broken-pipe=kill`.
    - In the same PR, `#[unix_sigpipe = "sig_dfl"]` were removed from `rustc` and `rustdoc` main
      binary crate entry points in favor of the command line flag. Kill-process-on-broken-pipe
      behavior was preserved by adding `-Zon-broken-pipe=kill` for `rustdoc` tool build step and
      `rustc` during compile steps.
- [rust-lang#126934] added `-Zon-broken-pipe=kill` for tool builds *except* for cargo to help with some miri
  tests because at the time the PR was written, this would lead to a couple of cargo test failures.
  Conditionally setting `RUSTFLAGS` can lead to tool build invalidation, e.g. building `cargo`
  without `-Zon-broken-pipe=kill` but `clippy` with the flag can lead to invalidation of the tool
  build cache. This is not a problem at the time, because nothing (not even miri) tests built stage
  1 cargo (all used initial cargo).
- In [rust-lang#130634] we found out that `run-make` tests like `compiler-builtins` needed stage 1 cargo, not
  just beta bootstrap cargo, because there can be changes that are present in stage 1 cargo but
  absent in beta cargo, which was blocking a beta backport.
- [rust-lang#130642] and later [rust-lang#130739] now build stage 1 cargo. And as previously mentioned, since
  `-Zon-broken-pipe=kill` was specifically *not* set for cargo, this caused tool build cache
  invalidation meaning rebuilds of stage 1 even if nothing in source was changed due to differing
  `RUSTFLAGS` since `run-make` also builds `rustdoc` and such [rust-lang#130980].

[rust-lang#34376]: rust-lang#34376
[rust-lang#49606]: rust-lang#49606
[rust-lang#97889]: rust-lang#97889
[rust-lang#102587]: rust-lang#102587
[rust-lang#103495]: rust-lang#103495
[rust-lang#124480]: rust-lang#124480
[rust-lang#130634]: rust-lang#130634
[rust-lang#130642]: rust-lang#130642
[rust-lang#130739]: rust-lang#130739
[rust-lang#130980]: rust-lang#130980
[rust-lang#131059]: rust-lang#131059
[^1]: rust-lang#131059 (comment)

r? `@onur-ozkan` (or bootstrap)
workingjubilee added a commit to workingjubilee/rustc that referenced this issue Oct 2, 2024
…r-ozkan

Revert rust-lang#131060 "Drop conditionally applied cargo `-Zon-broken-pipe=kill` flags"

In [rust-lang#131059] we found out that `-Zon-broken-pipe=kill` is actually **load-bearing**[^1] for
(at least) `rustc` and `rustdoc` to have the kill-process-on-broken-pipe behavior, e.g. `rustc
--print=sysroot | false` will ICE and `rustdoc --print=sysroot | false` will panic on a broken pipe.

This PR reverts 5a7058c (reverts PR rust-lang#131060) in favor of a future
fix to *unconditionally* apply `-Zon-broken-pipe=kill` to tool builds and also not drop the
`-Zon-broken-pipe=kill` flag for rustc binary builds.

I could not figure out how to write a regression test for the `rustc --print=sysroot | false`
behavior on Unix, so this is a plain revert for now.

This revert will unfortunately reintroduce rust-lang#130980 until we fix it again with the different approach.

See more details at <rust-lang#131059 (comment)> and in the timeline below.

### Timeline of kill-process-on-broken-pipe behavior changes

See [`unix_sigpipe` tracking issue rust-lang#97889][rust-lang#97889] for more context around unix sigpipe handling.

- From the very beginning since 2014, Rust binaries by default use `sig_ign`. This meant that if
  output pipe is broken yet the program tries to use `println!` and such, there will be a broken
  pipe panic from std. This lead to ICEs from e.g. `rustc --help | false` [rust-lang#34376].
- [rust-lang#49606] mitigated [rust-lang#34376] by adding an explicit signal handler to `rustc_driver` register a
  sigpipe handler with `SIG_DFL` which will cause the binary using `rustc_driver` to terminate if
  `rustc_driver::set_sigpipe_handler()` is called. `rustc`'s main binary wrapper uses
  `rustc_driver::set_sigpipe_handler()`, and so does `rustdoc`.
- A more universal way to set sigpipe behavior for Unix was introduced as part of [rust-lang#97889], i.e. `#
  [unix_sigpipe = "sig_dfl"]` attribute.
- [rust-lang#102587] migrated `rustc` to use `#[unix_sigpipe = "sig_dfl"]` instead of
  `rustc_driver::set_sigpipe_handler`.
- [rust-lang#103495] migrated `rustdoc` to use `#[unix_sigpipe = "sig_dfl"]` instead of
  `rustc_driver::set_sigpipe_handler`. `rustc_driver::set_sigpipe_handler` was removed.
- Following concerns about sigpipe setting UI in [rust-lang#97889], the UI for specifying sigpipe behavior
  was changed in [rust-lang#124480] from `#[unix_sigpipe = "sig_dfl"]` attribute to the commmand line flag
  `-Zon-broken-pipe=kill`.
    - In the same PR, `#[unix_sigpipe = "sig_dfl"]` were removed from `rustc` and `rustdoc` main
      binary crate entry points in favor of the command line flag. Kill-process-on-broken-pipe
      behavior was preserved by adding `-Zon-broken-pipe=kill` for `rustdoc` tool build step and
      `rustc` during compile steps.
- [rust-lang#126934] added `-Zon-broken-pipe=kill` for tool builds *except* for cargo to help with some miri
  tests because at the time the PR was written, this would lead to a couple of cargo test failures.
  Conditionally setting `RUSTFLAGS` can lead to tool build invalidation, e.g. building `cargo`
  without `-Zon-broken-pipe=kill` but `clippy` with the flag can lead to invalidation of the tool
  build cache. This is not a problem at the time, because nothing (not even miri) tests built stage
  1 cargo (all used initial cargo).
- In [rust-lang#130634] we found out that `run-make` tests like `compiler-builtins` needed stage 1 cargo, not
  just beta bootstrap cargo, because there can be changes that are present in stage 1 cargo but
  absent in beta cargo, which was blocking a beta backport.
- [rust-lang#130642] and later [rust-lang#130739] now build stage 1 cargo. And as previously mentioned, since
  `-Zon-broken-pipe=kill` was specifically *not* set for cargo, this caused tool build cache
  invalidation meaning rebuilds of stage 1 even if nothing in source was changed due to differing
  `RUSTFLAGS` since `run-make` also builds `rustdoc` and such [rust-lang#130980].

[rust-lang#34376]: rust-lang#34376
[rust-lang#49606]: rust-lang#49606
[rust-lang#97889]: rust-lang#97889
[rust-lang#102587]: rust-lang#102587
[rust-lang#103495]: rust-lang#103495
[rust-lang#124480]: rust-lang#124480
[rust-lang#130634]: rust-lang#130634
[rust-lang#130642]: rust-lang#130642
[rust-lang#130739]: rust-lang#130739
[rust-lang#130980]: rust-lang#130980
[rust-lang#131059]: rust-lang#131059
[^1]: rust-lang#131059 (comment)

r? ``@onur-ozkan`` (or bootstrap)
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Oct 2, 2024
Rollup merge of rust-lang#131108 - jieyouxu:revert-broken-pipe, r=onur-ozkan

Revert rust-lang#131060 "Drop conditionally applied cargo `-Zon-broken-pipe=kill` flags"

In [rust-lang#131059] we found out that `-Zon-broken-pipe=kill` is actually **load-bearing**[^1] for
(at least) `rustc` and `rustdoc` to have the kill-process-on-broken-pipe behavior, e.g. `rustc
--print=sysroot | false` will ICE and `rustdoc --print=sysroot | false` will panic on a broken pipe.

This PR reverts 5a7058c (reverts PR rust-lang#131060) in favor of a future
fix to *unconditionally* apply `-Zon-broken-pipe=kill` to tool builds and also not drop the
`-Zon-broken-pipe=kill` flag for rustc binary builds.

I could not figure out how to write a regression test for the `rustc --print=sysroot | false`
behavior on Unix, so this is a plain revert for now.

This revert will unfortunately reintroduce rust-lang#130980 until we fix it again with the different approach.

See more details at <rust-lang#131059 (comment)> and in the timeline below.

### Timeline of kill-process-on-broken-pipe behavior changes

See [`unix_sigpipe` tracking issue rust-lang#97889][rust-lang#97889] for more context around unix sigpipe handling.

- From the very beginning since 2014, Rust binaries by default use `sig_ign`. This meant that if
  output pipe is broken yet the program tries to use `println!` and such, there will be a broken
  pipe panic from std. This lead to ICEs from e.g. `rustc --help | false` [rust-lang#34376].
- [rust-lang#49606] mitigated [rust-lang#34376] by adding an explicit signal handler to `rustc_driver` register a
  sigpipe handler with `SIG_DFL` which will cause the binary using `rustc_driver` to terminate if
  `rustc_driver::set_sigpipe_handler()` is called. `rustc`'s main binary wrapper uses
  `rustc_driver::set_sigpipe_handler()`, and so does `rustdoc`.
- A more universal way to set sigpipe behavior for Unix was introduced as part of [rust-lang#97889], i.e. `#
  [unix_sigpipe = "sig_dfl"]` attribute.
- [rust-lang#102587] migrated `rustc` to use `#[unix_sigpipe = "sig_dfl"]` instead of
  `rustc_driver::set_sigpipe_handler`.
- [rust-lang#103495] migrated `rustdoc` to use `#[unix_sigpipe = "sig_dfl"]` instead of
  `rustc_driver::set_sigpipe_handler`. `rustc_driver::set_sigpipe_handler` was removed.
- Following concerns about sigpipe setting UI in [rust-lang#97889], the UI for specifying sigpipe behavior
  was changed in [rust-lang#124480] from `#[unix_sigpipe = "sig_dfl"]` attribute to the commmand line flag
  `-Zon-broken-pipe=kill`.
    - In the same PR, `#[unix_sigpipe = "sig_dfl"]` were removed from `rustc` and `rustdoc` main
      binary crate entry points in favor of the command line flag. Kill-process-on-broken-pipe
      behavior was preserved by adding `-Zon-broken-pipe=kill` for `rustdoc` tool build step and
      `rustc` during compile steps.
- [rust-lang#126934] added `-Zon-broken-pipe=kill` for tool builds *except* for cargo to help with some miri
  tests because at the time the PR was written, this would lead to a couple of cargo test failures.
  Conditionally setting `RUSTFLAGS` can lead to tool build invalidation, e.g. building `cargo`
  without `-Zon-broken-pipe=kill` but `clippy` with the flag can lead to invalidation of the tool
  build cache. This is not a problem at the time, because nothing (not even miri) tests built stage
  1 cargo (all used initial cargo).
- In [rust-lang#130634] we found out that `run-make` tests like `compiler-builtins` needed stage 1 cargo, not
  just beta bootstrap cargo, because there can be changes that are present in stage 1 cargo but
  absent in beta cargo, which was blocking a beta backport.
- [rust-lang#130642] and later [rust-lang#130739] now build stage 1 cargo. And as previously mentioned, since
  `-Zon-broken-pipe=kill` was specifically *not* set for cargo, this caused tool build cache
  invalidation meaning rebuilds of stage 1 even if nothing in source was changed due to differing
  `RUSTFLAGS` since `run-make` also builds `rustdoc` and such [rust-lang#130980].

[rust-lang#34376]: rust-lang#34376
[rust-lang#49606]: rust-lang#49606
[rust-lang#97889]: rust-lang#97889
[rust-lang#102587]: rust-lang#102587
[rust-lang#103495]: rust-lang#103495
[rust-lang#124480]: rust-lang#124480
[rust-lang#130634]: rust-lang#130634
[rust-lang#130642]: rust-lang#130642
[rust-lang#130739]: rust-lang#130739
[rust-lang#130980]: rust-lang#130980
[rust-lang#131059]: rust-lang#131059
[^1]: rust-lang#131059 (comment)

r? ``@onur-ozkan`` (or bootstrap)
@jieyouxu jieyouxu changed the title Fixing stage1 cargo rebuilds by removing RUSTFLAGS=-Zon-broken-pipe=kill might break some cargo tool testing workflow related to piping Adjusting -Zon-broken-pipe=kill to only be applied to rustc/rustdoc binary wrappers break two cargo tests Oct 2, 2024
@jieyouxu jieyouxu added the T-cargo Relevant to the cargo team, which will review and decide on the PR/issue. label Oct 2, 2024
@jieyouxu
Copy link
Member Author

jieyouxu commented Oct 2, 2024

I tried to update PR title and PR description to better reflect the current understanding regarding the two cargo test failures related to changing where -Zon-broken-pipe=kill is applied in bootstrap.

RalfJung pushed a commit to RalfJung/miri that referenced this issue Oct 3, 2024
Revert #131060 "Drop conditionally applied cargo `-Zon-broken-pipe=kill` flags"

In [#131059] we found out that `-Zon-broken-pipe=kill` is actually **load-bearing**[^1] for
(at least) `rustc` and `rustdoc` to have the kill-process-on-broken-pipe behavior, e.g. `rustc
--print=sysroot | false` will ICE and `rustdoc --print=sysroot | false` will panic on a broken pipe.

This PR reverts 5a7058c5a542ec42d1fa9b524f7b4f7d6845d1e9 (reverts PR #131060) in favor of a future
fix to *unconditionally* apply `-Zon-broken-pipe=kill` to tool builds and also not drop the
`-Zon-broken-pipe=kill` flag for rustc binary builds.

I could not figure out how to write a regression test for the `rustc --print=sysroot | false`
behavior on Unix, so this is a plain revert for now.

This revert will unfortunately reintroduce #130980 until we fix it again with the different approach.

See more details at <rust-lang/rust#131059 (comment)> and in the timeline below.

### Timeline of kill-process-on-broken-pipe behavior changes

See [`unix_sigpipe` tracking issue #97889][#97889] for more context around unix sigpipe handling.

- From the very beginning since 2014, Rust binaries by default use `sig_ign`. This meant that if
  output pipe is broken yet the program tries to use `println!` and such, there will be a broken
  pipe panic from std. This lead to ICEs from e.g. `rustc --help | false` [#34376].
- [#49606] mitigated [#34376] by adding an explicit signal handler to `rustc_driver` register a
  sigpipe handler with `SIG_DFL` which will cause the binary using `rustc_driver` to terminate if
  `rustc_driver::set_sigpipe_handler()` is called. `rustc`'s main binary wrapper uses
  `rustc_driver::set_sigpipe_handler()`, and so does `rustdoc`.
- A more universal way to set sigpipe behavior for Unix was introduced as part of [#97889], i.e. `#
  [unix_sigpipe = "sig_dfl"]` attribute.
- [#102587] migrated `rustc` to use `#[unix_sigpipe = "sig_dfl"]` instead of
  `rustc_driver::set_sigpipe_handler`.
- [#103495] migrated `rustdoc` to use `#[unix_sigpipe = "sig_dfl"]` instead of
  `rustc_driver::set_sigpipe_handler`. `rustc_driver::set_sigpipe_handler` was removed.
- Following concerns about sigpipe setting UI in [#97889], the UI for specifying sigpipe behavior
  was changed in [#124480] from `#[unix_sigpipe = "sig_dfl"]` attribute to the commmand line flag
  `-Zon-broken-pipe=kill`.
    - In the same PR, `#[unix_sigpipe = "sig_dfl"]` were removed from `rustc` and `rustdoc` main
      binary crate entry points in favor of the command line flag. Kill-process-on-broken-pipe
      behavior was preserved by adding `-Zon-broken-pipe=kill` for `rustdoc` tool build step and
      `rustc` during compile steps.
- [#126934] added `-Zon-broken-pipe=kill` for tool builds *except* for cargo to help with some miri
  tests because at the time the PR was written, this would lead to a couple of cargo test failures.
  Conditionally setting `RUSTFLAGS` can lead to tool build invalidation, e.g. building `cargo`
  without `-Zon-broken-pipe=kill` but `clippy` with the flag can lead to invalidation of the tool
  build cache. This is not a problem at the time, because nothing (not even miri) tests built stage
  1 cargo (all used initial cargo).
- In [#130634] we found out that `run-make` tests like `compiler-builtins` needed stage 1 cargo, not
  just beta bootstrap cargo, because there can be changes that are present in stage 1 cargo but
  absent in beta cargo, which was blocking a beta backport.
- [#130642] and later [#130739] now build stage 1 cargo. And as previously mentioned, since
  `-Zon-broken-pipe=kill` was specifically *not* set for cargo, this caused tool build cache
  invalidation meaning rebuilds of stage 1 even if nothing in source was changed due to differing
  `RUSTFLAGS` since `run-make` also builds `rustdoc` and such [#130980].

[#34376]: rust-lang/rust#34376
[#49606]: rust-lang/rust#49606
[#97889]: rust-lang/rust#97889
[#102587]: rust-lang/rust#102587
[#103495]: rust-lang/rust#103495
[#124480]: rust-lang/rust#124480
[#130634]: rust-lang/rust#130634
[#130642]: rust-lang/rust#130642
[#130739]: rust-lang/rust#130739
[#130980]: rust-lang/rust#130980
[#131059]: rust-lang/rust#131059
[^1]: rust-lang/rust#131059 (comment)

r? ``@onur-ozkan`` (or bootstrap)
bors added a commit to rust-lang-ci/rust that referenced this issue Oct 7, 2024
Prevent building cargo from invalidating build cache of other tools due to conditionally applied `-Zon-broken-pipe=kill` via tracked `RUSTFLAGS`

This PR fixes rust-lang#130980 where building cargo invalidated the tool build caches of other tools (such as rustdoc) because `-Zon-broken-pipe=kill` was conditionally passed via `RUSTFLAGS` for other tools *except* for cargo. The differing `RUSTFLAGS` triggered tool build cache invalidation as `RUSTFLAGS` is a tracked env var -- any changes in `RUSTFLAGS` requires a rebuild.

`-Zon-broken-pipe=kill` is load-bearing for rustc and rustdoc to not ICE on broken pipes due to usages of raw std `println!` that panics without the flag being set, which manifests in ICEs.

I can't say I like the changes here, but it is what it is...

See detailed discussions and history of `-Zon-broken-pipe=kill` usage in https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Internal.20lint.20for.20raw.20.60print!.60.20and.20.60println!.60.3F/near/474593815.

## Approach

This PR fixes the tool build cache invalidation by informing the `rustc` binary shim when to apply `-Zon-broken-pipe=kill` (i.e. when the rustc binary shim is not used to build cargo). This information is not communicated by `RUSTFLAGS`, which is an env var tracked by cargo, and instead uses an untracked env var `UNTRACKED_BROKEN_PIPE_FLAG` so we won't trigger tool build cache invalidation. We preserve bootstrap's behavior of not setting that flag for cargo by conditionally omitting setting `UNTRACKED_BROKEN_PIPE_FLAG` when building cargo.

Notably, the `-Zon-broken-pipe=kill` instance in https://github.com/rust-lang/rust/blob/1e5719bdc40bb553089ce83525f07dfe0b2e71e9/src/bootstrap/src/core/build_steps/compile.rs#L1058 is not modified because that is used to build rustc only and not cargo itself.

Thanks to `@cuviper` for the idea!

## Testing

### Integration testing

This PR introduces a run-make test for rustc and rustdoc that checks that when they do not ICE/panic when they encounter a broken pipe of the stdout stream.

I checked this test will catch the broken pipe ICE regression for rustc on Linux (at least) by commenting out https://github.com/rust-lang/rust/blob/1e5719bdc40bb553089ce83525f07dfe0b2e71e9/src/bootstrap/src/core/build_steps/compile.rs#L1058, and the test failed because rustc ICE'd.

### Manual testing

I have manually tried:

1. `./x clean && `./x test build --stage 1` -> `rustc +stage1 --print=sysroot | false`: no ICE.
2. `./x clean` -> `./x test run-make` twice: no stage 1 cargo rebuilds.
3. `./x clean` -> `./x build rustdoc` -> `rustdoc +stage1 --version | false`: no panics.
4. `./x test src/tools/cargo`: tests pass, notably `build::close_output` and `cargo_command::closed_output_ok` do not fail which would fail if cargo was built with `-Zon-broken-pipe=kill`.

## Related discussions

Thanks to everyone who helped!
- https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo/topic/Applying.20.60-Zon-broken-pipe.3Dkill.60.20flags.20in.20bootstrap.3F
- https://rust-lang.zulipchat.com/#narrow/stream/326414-t-infra.2Fbootstrap/topic/Modifying.20run-make.20tests.20unnecessarily.20rebuild.20stage.201.20.2E.2E.2E
- https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Internal.20lint.20for.20raw.20.60print!.60.20and.20.60println!.60.3F

Fixes rust-lang#130980
Closes rust-lang#131059

---

try-job: aarch64-apple
try-job: x86_64-msvc
try-job: x86_64-mingw
@bors bors closed this as completed in 18deb53 Oct 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-testsuite Area: The testsuite used to check the correctness of rustc C-bug Category: This is a bug. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-cargo Relevant to the cargo team, which will review and decide on the PR/issue.
Projects
None yet
7 participants