Skip to content

Commit fac6ec2

Browse files
kim-emclaude
andcommitted
chore(scripts): work around #guard_msgs / aesop failures in expose pipeline
When `build_with_diagnostics.py` enables `set_option diagnostics true` globally via `leanOptions`, five files in Mathlib fail to elaborate: four `#guard_msgs` mismatches caused by extra `[diag]` info messages, and one `aesop`/`naturality` synth failure in `Mathlib/CategoryTheory/Discrete/Basic.lean` that empirically also goes away with diagnostics off. The script now temporarily inserts `set_option diagnostics false` after the imports of each affected file before running `lake build`, and restores the originals via the same `try / finally` that restores `lakefile.lean`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3dae18c commit fac6ec2

2 files changed

Lines changed: 86 additions & 10 deletions

File tree

scripts/build_with_diagnostics.py

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,22 +74,43 @@
7474
PATCH_MARKER_BEGIN = "-- BEGIN expose_report diagnostics patch"
7575
PATCH_MARKER_END = "-- END expose_report diagnostics patch"
7676

77-
# The extra LeanOptions lines inserted into `mathlibLeanOptions`.
77+
# Files that fail to elaborate when `diagnostics=true` is enabled globally.
78+
# We turn diagnostics off in each one for the duration of the build, then
79+
# restore the original source. Four are `#guard_msgs` mismatches caused by
80+
# the extra `[diag]` info messages; the fifth is an `aesop`/`naturality`
81+
# tactic failure that empirically also goes away with diagnostics off.
82+
DIAGNOSTICS_OFF_FILES: list[str] = [
83+
"Mathlib/Tactic/Linter/ValidatePRTitle.lean",
84+
"Mathlib/Order/Interval/Lex.lean",
85+
"Mathlib/Tactic/NormNum/Ordinal.lean",
86+
"Mathlib/Probability/ConditionalProbability.lean",
87+
"Mathlib/CategoryTheory/Discrete/Basic.lean",
88+
]
89+
FILE_PATCH_LINE = (
90+
"-- expose_report: locally disable diagnostics so this file builds "
91+
"under global `diagnostics=true`\nset_option diagnostics false\n"
92+
)
93+
94+
# Diagnostics options are inserted into `mathlibLeanOptions`. This DOES
95+
# affect Lake's olean hash, so the build rebuilds Mathlib from scratch. The
96+
# tradeoff is necessary: `weakLeanArgs` doesn't change the hash, but Lake
97+
# then sees the existing oleans as up-to-date and skips re-elaboration —
98+
# no re-elaboration means no diagnostics output. After this script
99+
# finishes, run `lake exe cache get` to restore cached oleans (their
100+
# unchanged hashes are still on the cache server).
78101
PATCH_BLOCK = f""" {PATCH_MARKER_BEGIN}
79102
⟨`diagnostics, true⟩,
80103
⟨`diagnostics.threshold, (0 : Nat)⟩,
81104
{PATCH_MARKER_END}
82105
"""
83106

84-
# Insertion anchor inside `mathlibLeanOptions := #[ ... ]`. We insert *after*
85-
# this line (which has no trailing comment, so preserving trailing text is
86-
# not an issue).
107+
# Insertion anchor inside `mathlibLeanOptions := #[ ... ]`.
87108
ANCHOR = " ⟨`autoImplicit, false⟩,\n"
88109

89110

90111
def patch_lakefile() -> str:
91-
"""Insert the diagnostics options into mathlibLeanOptions. Returns the
92-
original contents (for restoration)."""
112+
"""Insert the diagnostics weakLeanArgs into the mathlib package config.
113+
Returns the original contents for restoration."""
93114
original = LAKEFILE.read_text()
94115
if PATCH_MARKER_BEGIN in original:
95116
raise RuntimeError(
@@ -109,6 +130,42 @@ def restore_lakefile(original: str) -> None:
109130
LAKEFILE.write_text(original)
110131

111132

133+
def patch_diagnostics_off_files() -> dict[str, str]:
134+
"""Insert `set_option diagnostics false` after the imports of each file
135+
in `DIAGNOSTICS_OFF_FILES`. Returns originals for restoration.
136+
137+
Insertion point: the line *after* the last `import` / `public import`
138+
line of the file. Works for both module-mode files (which have
139+
`module` followed by imports) and non-module files. The copyright
140+
comment block stays at the top.
141+
"""
142+
originals: dict[str, str] = {}
143+
for rel in DIAGNOSTICS_OFF_FILES:
144+
path = REPO_ROOT / rel
145+
text = path.read_text()
146+
originals[rel] = text
147+
if FILE_PATCH_LINE in text:
148+
continue
149+
lines = text.splitlines(keepends=True)
150+
last_import = -1
151+
for i, line in enumerate(lines):
152+
stripped = line.lstrip()
153+
if stripped.startswith("import ") or stripped.startswith("public import "):
154+
last_import = i
155+
if last_import < 0:
156+
raise RuntimeError(f"no import line found in {rel}; "
157+
f"don't know where to insert set_option")
158+
insert_at = last_import + 1
159+
new_lines = lines[:insert_at] + ["\n", FILE_PATCH_LINE] + lines[insert_at:]
160+
path.write_text("".join(new_lines))
161+
return originals
162+
163+
164+
def restore_diagnostics_off_files(originals: dict[str, str]) -> None:
165+
for rel, text in originals.items():
166+
(REPO_ROOT / rel).write_text(text)
167+
168+
112169
def run_build(log_path: Path) -> int:
113170
"""Stream `lake build Mathlib` to stdout and to `log_path`. Returns exit."""
114171
log_path.parent.mkdir(parents=True, exist_ok=True)
@@ -248,11 +305,13 @@ def main() -> int:
248305

249306
rc = 0
250307
if not args.skip_build:
251-
original = patch_lakefile()
308+
original_lakefile = patch_lakefile()
309+
original_files = patch_diagnostics_off_files()
252310
try:
253311
rc = run_build(args.log)
254312
finally:
255-
restore_lakefile(original)
313+
restore_lakefile(original_lakefile)
314+
restore_diagnostics_off_files(original_files)
256315
if rc != 0:
257316
print(f"[build_with_diagnostics] lake build exited {rc}; "
258317
f"log preserved at {args.log}. "

scripts/expose_enumerate.lean

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,27 @@ def collect : CoreM (Array DeclRecord) := do
159159

160160
end Mathlib.ExposeReport
161161

162+
/-- Read module names to import from a file, one per line, blank lines and
163+
`#`-comments ignored. -/
164+
def readModulesFile (path : System.FilePath) : IO (Array Name) := do
165+
let text ← IO.FS.readFile path
166+
let mut out : Array Name := #[]
167+
for line in text.splitOn "\n" do
168+
let line := line.trim
169+
if line.isEmpty || line.startsWith "#" then continue
170+
out := out.push line.toName
171+
return out
172+
162173
open Mathlib.ExposeReport in
163-
unsafe def main (_args : List String) : IO UInt32 := do
174+
unsafe def main (args : List String) : IO UInt32 := do
164175
let searchPath ← addSearchPathFromEnv (← getBuiltinSearchPath (← findSysroot))
165-
CoreM.withImportModules #[`Mathlib] (searchPath := searchPath) (trustLevel := 1024) do
176+
-- If a file path is given as the first argument, import the modules listed
177+
-- there (one per line). Otherwise, default to the single `Mathlib` module.
178+
let modules ← match args with
179+
| path :: _ => readModulesFile path
180+
| _ => pure #[`Mathlib]
181+
IO.eprintln s!"[expose_enumerate] importing {modules.size} module(s)..."
182+
CoreM.withImportModules modules (searchPath := searchPath) (trustLevel := 1024) do
166183
let records ← collect
167184
let stdout ← IO.getStdout
168185
for r in records do

0 commit comments

Comments
 (0)