diff --git a/templates/family-root/scripts/bootstrap.py b/templates/family-root/scripts/bootstrap.py index 96645003..c9db04cf 100755 --- a/templates/family-root/scripts/bootstrap.py +++ b/templates/family-root/scripts/bootstrap.py @@ -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 ` line ends there, so the end of a line is enough. LINE_KEPT = 4096 @@ -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 @@ -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 return reports_a_make = code == b"2" and depth < self.depth self.depth = depth diff --git a/tests/test_family_holder_bootstrap_texts.py b/tests/test_family_holder_bootstrap_texts.py index e388cc42..a0436d68 100644 --- a/tests/test_family_holder_bootstrap_texts.py +++ b/tests/test_family_holder_bootstrap_texts.py @@ -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 @@ -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