Skip to content
Merged
30 changes: 19 additions & 11 deletions templates/family-root/scripts/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -1009,12 +1009,12 @@ def run_member_target(root: Path, row: dict, target: str) -> str | None:
#: make's does not carry, since make exits 2 for any command that fails
#: (#839); `*** No rule to make target ...`, or `*** [Makefile:44:
#: bootstrap] Killed`, where make fails on its own or a signal ends what it
#: ran, with none; and `[Makefile:44: bootstrap] Error 2 (ignored)`, with no
#: `***`, where a `-` before a command lets it fail with a make's code
#: (#884).
#: ran, with none; and `[Makefile:44: bootstrap] Error 1 (ignored)`, or
#: `Killed (ignored)` where a signal ended it, with no `***`, where a `-`
#: before a command lets it fail, whatever ended it (#884, #890).
MAKE_FAILED_RE = re.compile(
rb"(?:\[(\d+)\])?: (?:\*\*\* (?:\[[^\n]*\] Error (\d+)\s*$)?"
rb"|\[[^\n]*\] Error 2 \((ignored)\)\s*$)")
rb"|\[[^\n]*\] [^\n]* \((ignored)\)\s*$)")
#: The bytes `MakeErrors` keeps of a line of make's stderr not yet ended:
#: an `Error <code>` line ends there, so the end of a line is enough.
LINE_KEPT = 4096
Expand All @@ -1035,15 +1035,23 @@ class MakeErrors:
and is passed over: where the member's make runs another (`$(MAKE)
inner`), the code is the innermost command's, and where the inner make
failed on its own there is none, as for make's own failure. Where a
`-` let that make fail (`Error 2 (ignored)`), the depth is the
parent's again, so an `Error 2` of the parent's own after it is named.
`-` let a command fail (`Error 1 (ignored)`, or `Killed (ignored)`,
whatever ended it), the make that says so goes on, and would print a
`***` line of its own if it failed after, so no `Error 2` is passed
over again until a `***` line comes: a parent's own `Error 2` after a
make a `-` let fail is named, however far below it the `-` was.

WHERE THE CODE IS NOT THE INNERMOST COMMAND'S, since make's lines do
not tell it:
- a recipe that runs a make, lets its failure pass in the shell (`||
true`, or `;`), or with a `-` in a make run with `-s`, which then
prints no `(ignored)` line, and exits 2 itself: its `Error 2` is
read as that make's report, and the inner command's code is named;
- a recipe that runs a make and lets its failure pass in the shell
(`|| true`, or `;`), or with a `-` in a make run with `-s`, which
prints no `(ignored)` line, then exits 2 itself with no `(ignored)`
line between: its `Error 2` is read as that make's report, and the
inner command's code is named;
- under `-k`, a make a `-` lets fail after a command that failed (`a:
$(MAKE) innerA`, whose command exits 1, then `b: -$(MAKE) innerB`,
whose command exits 3): the last `Error` line is the command's in
the make the `-` let fail, and its code, 3, is named;
- an inner make run with `env -u MAKELEVEL`, which prints its parent's
depth: the parent's `Error 2` is named;
- under `-j`, a job of the parent's that fails while the inner make is
Expand Down Expand Up @@ -1073,7 +1081,7 @@ def said(self, line: bytes) -> None:
depth_said, code, ignored = found[-1]
depth = int(depth_said or 0)
if ignored:
self.depth = min(self.depth, depth)
self.depth = -1
Comment on lines 1083 to +1084

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve nesting after same-depth ignored errors

When a recursive member target has already failed and a later - recipe is ignored in that same sub-make—for example, bootstrap invokes $(MAKE) -k inner, where one prerequisite exits 1 and a subsequent prerequisite runs -false—GNU make prints a depth-1 Error 1, a depth-1 (ignored) line, and then the parent's depth-0 Error 2. Resetting the depth unconditionally makes that final parent report overwrite code with 2, so family bootstrap reports the member's finding as an exit-2 refusal. A same-depth ignored line must preserve the outstanding failure nesting; only an ignored line that closes a deeper failure chain should reset it.

Useful? React with 👍 / 👎.

return
reports_a_make = code == b"2" and depth < self.depth
self.depth = depth
Expand Down
52 changes: 36 additions & 16 deletions tests/test_family_holder_bootstrap_texts.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,10 @@ def made(holder: Path, tmp_path: Path, *,
check=False)


#: A recipe's command that prints a finding and exits 1, and one that
#: prints a refusal and exits 2, as a member's checks do.
FINDS = b"\t@echo 'Find: a finding' >&2; exit 1\n"
REFUSES = b"\t@echo 'Refuse: refused' >&2; exit 2\n"
#: Members whose make runs another make, or whose `Error` line is followed
#: by more than 4096 bytes on stderr, each with what the line says of it:
#: the innermost command's code, read from each line as it passes (#884's
Expand All @@ -450,31 +454,47 @@ def made(holder: Path, tmp_path: Path, *,
#: named, and one that ignores a failure of its own (without `-s`, which
#: keeps make from saying so) reports nothing; and
#: make's `Error` line after a member's own `: *** ` on one line is read
#: (rv884's X11, X7 and X12, and NIT 1 and NIT 2, on #886).
#: (rv884's X11, X7 and X12, and NIT 1 and NIT 2, on #886). A `-` that
#: lets a command fail, whatever ended it, leaves nothing passed over
#: after it: a member's own `Error 2` is named where the `-` was in a make
#: one or two below it, or after a make whose failure an `|| true` let
#: pass, a signal's `(ignored)` line included, SIGKILL's or SIGTERM's, and
#: the code named before it stands (rv884's re-check, on #886, and its
#: reviews of #891).
INNERMOST = {
"Recurse": (b"bootstrap:\n\t@$(MAKE) -s inner\n"
b"inner:\n\t@echo 'Find: a finding' >&2; exit 1\n", 1),
"RecurseRefuse": (b"bootstrap:\n\t@$(MAKE) -s inner\n"
b"inner:\n\t@echo 'Refuse: refused' >&2; exit 2\n", 2),
"Recurse": (b"bootstrap:\n\t@$(MAKE) -s inner\ninner:\n" + FINDS, 1),
"RecurseRefuse": (b"bootstrap:\n\t@$(MAKE) -s inner\ninner:\n" + REFUSES,
2),
"InnerOwn": (b"bootstrap:\n\t@$(MAKE) -s nothere\n", None),
"InnerThenOwn": (b"bootstrap:\n\t@$(MAKE) -s nothere; exit 3\n", 3),
"InnerThenFinding": (b"bootstrap:\n\t@$(MAKE) -s nothere || exit 1\n",
1),
"Long": (b"bootstrap: one two\n"
b"one:\n\t@echo 'Find: a finding' >&2; exit 1\n"
b"two:\n\t@printf '%05000d' 0 >&2; echo >&2\n", 1),
"Long": (b"bootstrap: one two\none:\n" + FINDS
+ b"two:\n\t@printf '%05000d' 0 >&2; echo >&2\n", 1),
"KeepsGoing": (b"bootstrap:\n\t@$(MAKE) -s -k inner\ninner: one two\n"
b"one:\n\t@echo 'Find: a finding' >&2; exit 1\n"
b"two:\n\t@echo 'two: done' >&2\n", 1),
"ThenKilled": (b"bootstrap: one two\n"
b"one:\n\t@echo 'Find: a finding' >&2; exit 1\n"
b"two:\n\t@kill -KILL $$$$\n", 1),
"LetFail": (b"bootstrap:\n\t-@$(MAKE) -s inner\n"
b"\t@echo 'Refuse: refused' >&2; exit 2\n"
b"inner:\n\t@echo 'Find: a finding' >&2; exit 1\n", 2),
b"one:\n" + FINDS + b"two:\n\t@echo 'two: done' >&2\n", 1),
"ThenKilled": (b"bootstrap: one two\none:\n" + FINDS
+ b"two:\n\t@kill -KILL $$$$\n", 1),
"LetFail": (b"bootstrap:\n\t-@$(MAKE) -s inner\n" + REFUSES
+ b"inner:\n" + FINDS, 2),
"InnerLets": (b"bootstrap:\n\t@$(MAKE) inner; exit 2\n"
b"inner:\n\t-@exit 2\n\t@echo 'inner: done'\n", 2),
"OwnStars": (b"bootstrap:\n\t@printf 'x: *** y' >&2; exit 1\n", 1),
"LetsBelow": (b"bootstrap:\n\t@$(MAKE) mid\n" + REFUSES
+ b"mid:\n\t-@$(MAKE) inner\ninner:\n" + FINDS, 2),
"PassesThenLets": (b"bootstrap:\n\t@$(MAKE) inner || true\n\t-@exit 10\n"
+ REFUSES + b"inner:\n" + FINDS, 2),
"ThenLets": (b"bootstrap: one two\none:\n" + FINDS
+ b"two:\n\t-@false\n", 1),
"LetsTwoBelow": (b"bootstrap:\n\t@$(MAKE) mid\n" + REFUSES
+ b"mid:\n\t@$(MAKE) mid2\nmid2:\n\t-@$(MAKE) inner\n"
b"inner:\n" + FINDS, 2),
"PassesThenKilled": (b"bootstrap:\n\t@$(MAKE) inner || true\n"
b"\t-@kill -KILL $$$$\n" + REFUSES
+ b"inner:\n" + FINDS, 2),
"PassesThenTerminated": (b"bootstrap:\n\t@$(MAKE) inner || true\n"
b"\t-@kill -TERM $$$$\n" + REFUSES
+ b"inner:\n" + FINDS, 2),
}
#: A member whose two commands fail, under `-k`: the last `Error` line
#: names the code (rv876's R1), and a member's finding, both under a
Expand Down
Loading