Compare commits

...

6 Commits

Author SHA1 Message Date
Sebastian Ullrich
3c32607020 fix: incorrect borrow annotation on demangleBtLinCStr leading to segfault on panic (#12939) 2026-03-17 09:24:57 +00:00
Eric Wieser
6714601ee4 fix: remove accidental type monomorphism in Id.run_seqLeft (#12936)
This PR fixes `Id.run_seqLeft` and `Id.run_seqRight` to apply when the
two monad results are different.
2026-03-17 06:43:51 +00:00
damiano
6b604625f2 fix: add missing pp-spaces in grind_pattern (#11686)
This PR adds a pretty-printed space in `grind_pattern`.

[#lean4 > Some pretty printing quirks @
💬](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Some.20pretty.20printing.20quirks/near/563848793)

Co-authored-by: Kim Morrison <kim@tqft.net>
2026-03-17 04:15:02 +00:00
Kim Morrison
e96b0ff39c fix: use response files on all platforms to avoid ARG_MAX (#12540)
This PR extends Lake's use of response files (`@file`) from Windows-only
to all platforms, avoiding `ARG_MAX` limits when invoking `clang`/`ar`
with many object files.

Lake already uses response files on Windows to avoid exceeding CLI
length limits. On macOS and Linux, linking Mathlib's ~15,000 object
files into a shared library can exceed macOS's `ARG_MAX` (262,144
bytes). Both `clang` and `gcc` support `@file` response files on all
platforms, so this is safe to enable unconditionally.

Reported as a macOS issue at
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/The.20clang.20command.20line.20with.20all.20~15.2C000.20Mathlib.20.2Ec.2Eo.2Eexport/near/574369912:
the Mathlib cache ships Linux `.so` shared libs but not macOS `.dylib`
files, so `precompileModules` on macOS triggers a full re-link that
exceeds `ARG_MAX`.

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 04:14:37 +00:00
Kim Morrison
50ee6dff0a chore: update leantar to v0.1.19 (#12938) 2026-03-17 03:55:21 +00:00
Mac Malone
9e0aa14b6f feat: lake: fixedToolchain package configuration (#12935)
This PR adds the `fixedToolchain` Lake package configuration option.
Setting this to `true` informs Lake that the package is only expected to
function on a single toolchain (like Mathlib). This causes Lake's
toolchain update procedure to prioritize its toolchain and avoids the
need to separate input-to-output mappings for the package by toolchain
version in the Lake cache.
2026-03-17 02:37:55 +00:00
25 changed files with 219 additions and 86 deletions

View File

@@ -79,7 +79,7 @@ if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
endif() endif()
find_program(LEANTAR leantar) find_program(LEANTAR leantar)
if(NOT LEANTAR) if(NOT LEANTAR)
set(LEANTAR_VERSION v0.1.18) set(LEANTAR_VERSION v0.1.19)
if(CMAKE_SYSTEM_NAME MATCHES "Windows") if(CMAKE_SYSTEM_NAME MATCHES "Windows")
set(LEANTAR_ARCHIVE_SUFFIX .zip) set(LEANTAR_ARCHIVE_SUFFIX .zip)
set(LEANTAR_TARGET x86_64-pc-windows-msvc) set(LEANTAR_TARGET x86_64-pc-windows-msvc)

View File

@@ -254,8 +254,8 @@ instance : LawfulMonad Id := by
@[simp, grind =] theorem run_bind (x : Id α) (f : α Id β) : (x >>= f).run = (f x.run).run := rfl @[simp, grind =] theorem run_bind (x : Id α) (f : α Id β) : (x >>= f).run = (f x.run).run := rfl
@[simp, grind =] theorem run_pure (a : α) : (pure a : Id α).run = a := rfl @[simp, grind =] theorem run_pure (a : α) : (pure a : Id α).run = a := rfl
@[simp, grind =] theorem pure_run (a : Id α) : pure a.run = a := rfl @[simp, grind =] theorem pure_run (a : Id α) : pure a.run = a := rfl
@[simp] theorem run_seqRight (x y : Id α) : (x *> y).run = y.run := rfl @[simp] theorem run_seqRight (x : Id α) (y : Id β) : (x *> y).run = y.run := rfl
@[simp] theorem run_seqLeft (x y : Id α) : (x <* y).run = x.run := rfl @[simp] theorem run_seqLeft (x : Id α) (y : Id β) : (x <* y).run = x.run := rfl
@[simp] theorem run_seq (f : Id (α β)) (x : Id α) : (f <*> x).run = f.run x.run := rfl @[simp] theorem run_seq (f : Id (α β)) (x : Id α) : (f <*> x).run = f.run x.run := rfl
end Id end Id

View File

@@ -333,11 +333,7 @@ public def demangleBtLine (line : String) : Option String := do
return pfx ++ demangled ++ sfx return pfx ++ demangled ++ sfx
@[export lean_demangle_bt_line_cstr] @[export lean_demangle_bt_line_cstr]
def demangleBtLineCStr (line : @& String) : String := def demangleBtLineCStr (line : String) : String :=
(demangleBtLine line).getD "" (demangleBtLine line).getD ""
@[export lean_demangle_symbol_cstr]
def demangleSymbolCStr (symbol : @& String) : String :=
(demangleSymbol symbol).getD ""
end Lean.Name.Demangle end Lean.Name.Demangle

View File

@@ -138,7 +138,7 @@ example : f b = f c → a ≤ f a → f (f a) ≤ f (f (f a)) := by
``` ```
-/ -/
@[builtin_command_parser] def grindPattern := leading_parser @[builtin_command_parser] def grindPattern := leading_parser
Term.attrKind >> "grind_pattern " >> optional ("[" >> ident >> "]") >> ident >> darrow >> sepBy1 termParser "," >> optional grindPatternCnstrs Term.attrKind >> "grind_pattern " >> optional ("[" >> ident >> "]") >> ident >> darrow >> sepBy1 termParser ", " >> optional grindPatternCnstrs
@[builtin_command_parser] def initGrindNorm := leading_parser @[builtin_command_parser] def initGrindNorm := leading_parser
"init_grind_norm " >> many ident >> "| " >> many ident "init_grind_norm " >> many ident >> "| " >> many ident

View File

@@ -87,21 +87,20 @@ public def compileO
} }
public def mkArgs (basePath : FilePath) (args : Array String) : LogIO (Array String) := do public def mkArgs (basePath : FilePath) (args : Array String) : LogIO (Array String) := do
if Platform.isWindows then -- Use response file to avoid potentially exceeding CLI length limits.
-- Use response file to avoid potentially exceeding CLI length limits. -- On Windows this is always needed; on macOS/Linux this is needed for large
let rspFile := basePath.addExtension "rsp" -- projects like Mathlib where the number of object files exceeds ARG_MAX.
let h IO.FS.Handle.mk rspFile .write let rspFile := basePath.addExtension "rsp"
args.forM fun arg => let h IO.FS.Handle.mk rspFile .write
-- Escape special characters args.forM fun arg =>
let arg := arg.foldl (init := "") fun s c => -- Escape special characters
if c == '\\' || c == '"' then let arg := arg.foldl (init := "") fun s c =>
s.push '\\' |>.push c if c == '\\' || c == '"' then
else s.push '\\' |>.push c
s.push c else
h.putStr s!"\"{arg}\"\n" s.push c
return #[s!"@{rspFile}"] h.putStr s!"\"{arg}\"\n"
else return #[s!"@{rspFile}"]
return args
public def compileStaticLib public def compileStaticLib
(libFile : FilePath) (oFiles : Array FilePath) (libFile : FilePath) (oFiles : Array FilePath)

View File

@@ -392,7 +392,7 @@ def serviceNotFound (service : String) (configuredServices : Array CacheServiceC
configuredServices.foldl (· ++ s!" {·.name}") msg configuredServices.foldl (· ++ s!" {·.name}") msg
@[inline] private def cacheToolchain (pkg : Package) (toolchain : CacheToolchain) : CacheToolchain := @[inline] private def cacheToolchain (pkg : Package) (toolchain : CacheToolchain) : CacheToolchain :=
if pkg.bootstrap then .none else toolchain if pkg.fixedToolchain || pkg.bootstrap then .none else toolchain
@[inline] private def cachePlatform (pkg : Package) (platform : CachePlatform) : CachePlatform := @[inline] private def cachePlatform (pkg : Package) (platform : CachePlatform) : CachePlatform :=
if pkg.isPlatformIndependent then .none else platform if pkg.isPlatformIndependent then .none else platform

View File

@@ -45,7 +45,7 @@ public structure Package where
/-- The path to the package's configuration file (relative to `dir`). -/ /-- The path to the package's configuration file (relative to `dir`). -/
relConfigFile : FilePath relConfigFile : FilePath
/-- The path to the package's JSON manifest of remote dependencies (relative to `dir`). -/ /-- The path to the package's JSON manifest of remote dependencies (relative to `dir`). -/
relManifestFile : FilePath := config.manifestFile.getD defaultManifestFile |>.normalize relManifestFile : FilePath := defaultManifestFile
/-- The package's scope (e.g., in Reservoir). -/ /-- The package's scope (e.g., in Reservoir). -/
scope : String scope : String
/-- The URL to this package's Git remote. -/ /-- The URL to this package's Git remote. -/
@@ -244,6 +244,10 @@ public def id? (self : Package) : Option PkgId :=
@[inline] public def isPlatformIndependent (self : Package) : Bool := @[inline] public def isPlatformIndependent (self : Package) : Bool :=
self.config.platformIndependent == some true self.config.platformIndependent == some true
/-- The package's `fixedToolchain` configuration. -/
@[inline] public def fixedToolchain (self : Package) : Bool :=
self.config.fixedToolchain
/-- The package's `releaseRepo`/`releaseRepo?` configuration. -/ /-- The package's `releaseRepo`/`releaseRepo?` configuration. -/
@[inline] public def releaseRepo? (self : Package) : Option String := @[inline] public def releaseRepo? (self : Package) : Option String :=
self.config.releaseRepo self.config.releaseRepo

View File

@@ -29,16 +29,6 @@ public configuration PackageConfig (p : Name) (n : Name) extends WorkspaceConfig
/-- **For internal use.** Whether this package is Lean itself. -/ /-- **For internal use.** Whether this package is Lean itself. -/
bootstrap : Bool := false bootstrap : Bool := false
/--
**This field is deprecated.**
The path of a package's manifest file, which stores the exact versions
of its resolved dependencies.
Defaults to `defaultManifestFile` (i.e., `lake-manifest.json`).
-/
manifestFile : Option FilePath := none
/-- An `Array` of target names to build whenever the package is used. -/ /-- An `Array` of target names to build whenever the package is used. -/
extraDepTargets : Array Name := #[] extraDepTargets : Array Name := #[]
@@ -331,6 +321,18 @@ public configuration PackageConfig (p : Name) (n : Name) extends WorkspaceConfig
-/ -/
allowImportAll : Bool := false allowImportAll : Bool := false
/--
Whether this package is expected to function only on a single toolchain
(the package's toolchain).
This informs Lake's toolchain update procedure (in `lake update`) to prioritize
this package's toolchain. It also avoids the need to separate input-to-output mappings
for this package by toolchain version in the Lake cache.
Defaults to `false`.
-/
fixedToolchain : Bool := false
deriving Inhabited deriving Inhabited
/-- The package's name as specified by the author. -/ /-- The package's name as specified by the author. -/

View File

@@ -166,10 +166,6 @@ public def Package.loadFromEnv
else else
pure self.config.lintDriver pure self.config.lintDriver
-- Deprecation warnings
unless self.config.manifestFile.isNone do
logWarning s!"{self.prettyName}: package configuration option 'manifestFile' is deprecated"
-- Fill in the Package -- Fill in the Package
return {self with return {self with
depConfigs, targetDecls, targetDeclMap depConfigs, targetDecls, targetDeclMap

View File

@@ -50,8 +50,9 @@ That is, Lake ignores the `-` suffix.
**v1.x.x** (versioned by a string) **v1.x.x** (versioned by a string)
- `"1.0.0"`: Switches to a semantic versioning scheme - `"1.0.0"`: Switches to a semantic versioning scheme
- `"1.1.0"`: Add optional `scope` package entry field - `"1.1.0"`: Add optional `scope` package entry field
- `"1.2.0"`: Add optional `fixedToolchain` manifest field
-/ -/
@[inline] public def Manifest.version : StdVer := {major := 1, minor := 1} @[inline] public def Manifest.version : StdVer := {major := 1, minor := 2}
/-- Manifest version `0.6.0` package entry. For backwards compatibility. -/ /-- Manifest version `0.6.0` package entry. For backwards compatibility. -/
private inductive PackageEntryV6 private inductive PackageEntryV6
@@ -84,7 +85,9 @@ public structure PackageEntry where
name : Name name : Name
scope : String := "" scope : String := ""
inherited : Bool inherited : Bool
/-- The relative path within the package directory to the Lake configuration file. -/
configFile : FilePath := defaultConfigFile configFile : FilePath := defaultConfigFile
/-- The relative path within the package directory to the Lake manifest file. -/
manifestFile? : Option FilePath := none manifestFile? : Option FilePath := none
src : PackageEntrySrc src : PackageEntrySrc
deriving Inhabited deriving Inhabited
@@ -139,7 +142,7 @@ public protected def fromJson? (json : Json) : Except String PackageEntry := do
| _ => | _ =>
throw s!"unknown package entry type '{type}'" throw s!"unknown package entry type '{type}'"
return { return {
name, scope, inherited, name, scope, inherited
configFile, manifestFile? := manifestFile, src configFile, manifestFile? := manifestFile, src
: PackageEntry : PackageEntry
} }
@@ -172,6 +175,7 @@ end PackageEntry
public structure Manifest where public structure Manifest where
name : Name name : Name
lakeDir : FilePath lakeDir : FilePath
fixedToolchain : Bool
packagesDir? : Option FilePath := none packagesDir? : Option FilePath := none
packages : Array PackageEntry := #[] packages : Array PackageEntry := #[]
@@ -184,6 +188,7 @@ public def addPackage (entry : PackageEntry) (self : Manifest) : Manifest :=
public protected def toJson (self : Manifest) : Json := public protected def toJson (self : Manifest) : Json :=
Json.mkObj [ Json.mkObj [
("version", toJson version), ("version", toJson version),
("fixedToolchain", toJson self.fixedToolchain),
("name", toJson self.name), ("name", toJson self.name),
("lakeDir", toJson self.lakeDir), ("lakeDir", toJson self.lakeDir),
("packagesDir", toJson self.packagesDir?), ("packagesDir", toJson self.packagesDir?),
@@ -217,11 +222,12 @@ private def getPackages (ver : StdVer) (obj : JsonObject) : Except String (Array
public protected def fromJson? (json : Json) : Except String Manifest := do public protected def fromJson? (json : Json) : Except String Manifest := do
let obj JsonObject.fromJson? json let obj JsonObject.fromJson? json
let ver getVersion obj let ver getVersion obj
let fixedToolchain obj.getD "fixedToolchain" false
let name obj.getD "name" Name.anonymous let name obj.getD "name" Name.anonymous
let lakeDir obj.getD "lakeDir" defaultLakeDir let lakeDir obj.getD "lakeDir" defaultLakeDir
let packagesDir? obj.get? "packagesDir" let packagesDir? obj.get? "packagesDir"
let packages getPackages ver obj let packages getPackages ver obj
return {name, lakeDir, packagesDir?, packages} return {name, lakeDir, packagesDir?, packages, fixedToolchain}
public instance : FromJson Manifest := Manifest.fromJson? public instance : FromJson Manifest := Manifest.fromJson?

View File

@@ -89,26 +89,33 @@ public structure MaterializedDep where
Used as the endpoint from which to fetch cloud releases for the package. Used as the endpoint from which to fetch cloud releases for the package.
-/ -/
remoteUrl : String remoteUrl : String
/-- The manifest for the dependency or the error produced when trying to load it. -/
manifest? : Except IO.Error Manifest
/-- The manifest entry for the dependency. -/ /-- The manifest entry for the dependency. -/
manifestEntry : PackageEntry manifestEntry : PackageEntry
deriving Inhabited deriving Inhabited
namespace MaterializedDep namespace MaterializedDep
@[inline] public def name (self : MaterializedDep) := @[inline] public def name (self : MaterializedDep) : Name :=
self.manifestEntry.name self.manifestEntry.name
@[inline] public def scope (self : MaterializedDep) := @[inline] public def scope (self : MaterializedDep) : String :=
self.manifestEntry.scope self.manifestEntry.scope
/-- Path to the dependency's configuration file (relative to `relPkgDir`). -/ /-- Path to the dependency's configuration file (relative to `relPkgDir`). -/
@[inline] public def manifestFile? (self : MaterializedDep) := @[inline] public def manifestFile? (self : MaterializedDep) : Option FilePath :=
self.manifestEntry.manifestFile? self.manifestEntry.manifestFile?
/-- Path to the dependency's configuration file (relative to `relPkgDir`). -/ /-- Path to the dependency's configuration file (relative to `relPkgDir`). -/
@[inline] public def configFile (self : MaterializedDep) := @[inline] public def configFile (self : MaterializedDep) : FilePath :=
self.manifestEntry.configFile self.manifestEntry.configFile
public def fixedToolchain (self : MaterializedDep) : Bool :=
match self.manifest? with
| .ok manifest => manifest.fixedToolchain
| _ => false
end MaterializedDep end MaterializedDep
inductive InputVer inductive InputVer
@@ -148,7 +155,7 @@ public def Dependency.materialize
match src with match src with
| .path dir => | .path dir =>
let relPkgDir := relParentDir / dir let relPkgDir := relParentDir / dir
return mkDep relPkgDir "" (.path relPkgDir) mkDep (wsDir / relPkgDir) relPkgDir "" (.path relPkgDir)
| .git url inputRev? subDir? => do | .git url inputRev? subDir? => do
let sname := dep.name.toString (escape := false) let sname := dep.name.toString (escape := false)
let repoUrl := Git.filterUrl? url |>.getD "" let repoUrl := Git.filterUrl? url |>.getD ""
@@ -196,16 +203,19 @@ public def Dependency.materialize
| _ => error s!"{pkg.fullName}: Git source not found on Reservoir" | _ => error s!"{pkg.fullName}: Git source not found on Reservoir"
where where
materializeGit name relPkgDir gitUrl remoteUrl inputRev? subDir? : LoggerIO MaterializedDep := do materializeGit name relPkgDir gitUrl remoteUrl inputRev? subDir? : LoggerIO MaterializedDep := do
let repo := GitRepo.mk (wsDir / relPkgDir) let pkgDir := wsDir / relPkgDir
let repo := GitRepo.mk pkgDir
let gitUrl := lakeEnv.pkgUrlMap.find? dep.name |>.getD gitUrl let gitUrl := lakeEnv.pkgUrlMap.find? dep.name |>.getD gitUrl
materializeGitRepo name repo gitUrl inputRev? materializeGitRepo name repo gitUrl inputRev?
let rev repo.getHeadRevision let rev repo.getHeadRevision
let relPkgDir := if let some subDir := subDir? then relPkgDir / subDir else relPkgDir let relPkgDir := if let some subDir := subDir? then relPkgDir / subDir else relPkgDir
return mkDep relPkgDir remoteUrl <| .git gitUrl rev inputRev? subDir? mkDep pkgDir relPkgDir remoteUrl <| .git gitUrl rev inputRev? subDir?
@[inline] mkDep relPkgDir remoteUrl src : MaterializedDep := { @[inline] mkDep pkgDir relPkgDir remoteUrl src : LoggerIO MaterializedDep := do
relPkgDir, remoteUrl, return {
manifestEntry := {name := dep.name, scope := dep.scope, inherited, src} relPkgDir, remoteUrl
} manifest? := Manifest.load (pkgDir / defaultManifestFile) |>.toBaseIO
manifestEntry := {name := dep.name, scope := dep.scope, inherited, src}
}
/-- /--
Materializes a manifest package entry, cloning and/or checking it out as necessary. Materializes a manifest package entry, cloning and/or checking it out as necessary.
@@ -216,7 +226,7 @@ public def PackageEntry.materialize
: LoggerIO MaterializedDep := : LoggerIO MaterializedDep :=
match manifestEntry.src with match manifestEntry.src with
| .path (dir := relPkgDir) .. => | .path (dir := relPkgDir) .. =>
return mkDep relPkgDir "" mkDep (wsDir / relPkgDir) relPkgDir ""
| .git (url := url) (rev := rev) (subDir? := subDir?) .. => do | .git (url := url) (rev := rev) (subDir? := subDir?) .. => do
let sname := manifestEntry.name.toString (escape := false) let sname := manifestEntry.name.toString (escape := false)
let relGitDir := relPkgsDir / sname let relGitDir := relPkgsDir / sname
@@ -239,7 +249,12 @@ public def PackageEntry.materialize
let url := lakeEnv.pkgUrlMap.find? manifestEntry.name |>.getD url let url := lakeEnv.pkgUrlMap.find? manifestEntry.name |>.getD url
cloneGitPkg sname repo url rev cloneGitPkg sname repo url rev
let relPkgDir := match subDir? with | .some subDir => relGitDir / subDir | .none => relGitDir let relPkgDir := match subDir? with | .some subDir => relGitDir / subDir | .none => relGitDir
return mkDep relPkgDir (Git.filterUrl? url |>.getD "") mkDep gitDir relPkgDir (Git.filterUrl? url |>.getD "")
where where
@[inline] mkDep relPkgDir remoteUrl : MaterializedDep := @[inline] mkDep pkgDir relPkgDir remoteUrl : LoggerIO MaterializedDep := do
{relPkgDir, remoteUrl, manifestEntry} let manifest? id do
if let some manifestFile := manifestEntry.manifestFile? then
Manifest.load (pkgDir / manifestFile) |>.toBaseIO
else
return .error (.noFileOrDirectory "" 0 "")
return {relPkgDir, remoteUrl, manifest?, manifestEntry}

View File

@@ -171,8 +171,8 @@ private def reuseManifest
logWarning s!"{rootName}: ignoring previous manifest because it failed to load: {e}" logWarning s!"{rootName}: ignoring previous manifest because it failed to load: {e}"
/-- Add a package dependency's manifest entries to the update state. -/ /-- Add a package dependency's manifest entries to the update state. -/
private def addDependencyEntries (pkg : Package) : UpdateT LoggerIO PUnit := do private def addDependencyEntries (pkg : Package) (matDep : MaterializedDep) : UpdateT LoggerIO PUnit := do
match ( Manifest.load pkg.manifestFile |>.toBaseIO) with match matDep.manifest? with
| .ok manifest => | .ok manifest =>
manifest.packages.forM fun entry => do manifest.packages.forM fun entry => do
unless ( getThe (NameMap PackageEntry)).contains entry.name do unless ( getThe (NameMap PackageEntry)).contains entry.name do
@@ -210,6 +210,36 @@ Used, for instance, if the toolchain is updated and no Elan is detected.
-/ -/
def restartCode : ExitCode := 4 def restartCode : ExitCode := 4
/-- The toolchain information of a package. -/
private structure ToolchainCandidate where
/-- The name of the package which provided the toolchain candidate. -/
src : Name
/-- The version of the toolchain candidate. -/
ver : ToolchainVer
/-- Whether the candidate toolchain been fixed to particular version. -/
fixed : Bool := false
private structure ToolchainState where
/-- The name of depedency which provided the current candidate toolchain. -/
src : Name
/-- The current candidate toolchain version (if any). -/
tc? : Option ToolchainVer
/-- Incompatible candidate toolchains (if any). -/
clashes : Array ToolchainCandidate
/--
Whether the candidate toolchain been fixed to particular version.
If `false`, the search will update the toolchain further where possible.
-/
fixed : Bool
@[inline] def ToolchainState.replace
(src : Name) (tc? : Option ToolchainVer) (fixed : Bool) (self : ToolchainState)
: ToolchainState := {self with src, tc?, fixed}
@[inline] def ToolchainState.addClash
(src : Name) (ver : ToolchainVer) (fixed : Bool) (self : ToolchainState)
: ToolchainState := {self with clashes := self.clashes.push {src, ver, fixed}}
/-- /--
Update the workspace's `lean-toolchain` if necessary. Update the workspace's `lean-toolchain` if necessary.
@@ -222,23 +252,38 @@ def Workspace.updateToolchain
: LoggerIO PUnit := do : LoggerIO PUnit := do
let rootToolchainFile := ws.root.dir / toolchainFileName let rootToolchainFile := ws.root.dir / toolchainFileName
let rootTc? ToolchainVer.ofDir? ws.dir let rootTc? ToolchainVer.ofDir? ws.dir
let (src, tc?, tcs) rootDeps.foldlM (init := (ws.root.baseName, rootTc?, #[])) fun s dep => do let s : ToolchainState := ws.root.baseName, rootTc?, #[], ws.root.fixedToolchain
let src, tc?, tcs, fixed rootDeps.foldlM (init := s) fun s dep => do
let depTc? ToolchainVer.ofDir? (ws.dir / dep.relPkgDir) let depTc? ToolchainVer.ofDir? (ws.dir / dep.relPkgDir)
let some depTc := depTc? let some depTc := depTc?
| return s | return s
let (src, tc?, tcs) := s let some tc := s.tc?
let some tc := tc? | return s.replace dep.name depTc? dep.fixedToolchain
| return (dep.name, depTc?, tcs) if dep.fixedToolchain then
if depTc tc then if s.fixed then
return (src, tc, tcs) if tc = depTc then
else if tc < depTc then return s
return (dep.name, depTc, tcs) else
return s.addClash dep.name depTc dep.fixedToolchain -- true
else
if tc depTc then
return s.replace dep.name depTc dep.fixedToolchain -- true
else
return s.addClash dep.name depTc dep.fixedToolchain -- true
else else
return (src, tc, tcs.push (dep.name, depTc)) if depTc tc then
return s
else if !s.fixed && tc < depTc then
return s.replace dep.name depTc dep.fixedToolchain -- false
else
return s.addClash dep.name depTc dep.fixedToolchain -- false
if 0 < tcs.size then if 0 < tcs.size then
let s := "toolchain not updated; multiple toolchain candidates:" let s := "toolchain not updated; multiple toolchain candidates:"
let s := if let some tc := tc? then s!"{s}\n {tc}\n from {src}" else s let addEntry s tc src fixed :=
let s := tcs.foldl (init := s) fun s (d, tc) => s!"{s}\n {tc}\n from {d}" let fixed := if fixed then " (fixed toolchain)" else ""
s!"{s}\n {tc}\n from {src}{fixed}"
let s := if let some tc := tc? then addEntry s tc src fixed else s
let s := tcs.foldl (init := s) fun s src, tc, fixed => addEntry s tc src fixed
logWarning s logWarning s
else if let some tc := tc? then else if let some tc := tc? then
if rootTc?.any (· == tc) then if rootTc?.any (· == tc) then
@@ -333,7 +378,7 @@ where
loadUpdatedDep wsIdx dep matDep loadUpdatedDep wsIdx dep matDep
@[inline] loadUpdatedDep wsIdx dep matDep : StateT Workspace (UpdateT LoggerIO) Package := do @[inline] loadUpdatedDep wsIdx dep matDep : StateT Workspace (UpdateT LoggerIO) Package := do
let depPkg loadDepPackage wsIdx matDep dep.opts leanOpts true let depPkg loadDepPackage wsIdx matDep dep.opts leanOpts true
addDependencyEntries depPkg addDependencyEntries depPkg matDep
return depPkg return depPkg
/-- Write package entries to the workspace manifest. -/ /-- Write package entries to the workspace manifest. -/
@@ -347,6 +392,7 @@ def Workspace.writeManifest
| none => arr -- should only be the case for the root | none => arr -- should only be the case for the root
let manifest : Manifest := { let manifest : Manifest := {
name := ws.root.baseName name := ws.root.baseName
fixedToolchain := ws.root.fixedToolchain
lakeDir := ws.relLakeDir lakeDir := ws.relLakeDir
packagesDir? := ws.relPkgsDir packagesDir? := ws.relPkgsDir
packages := manifestEntries packages := manifestEntries

View File

@@ -462,6 +462,11 @@
"default": false, "default": false,
"description": "Whether downstream packages can `import all` modules of this package.\n\nIf enabled, downstream users will be able to access the `private` internals of modules, including definition bodies not marked as `@[expose]`. This may also, in the future, prevent compiler optimization which rely on `private` definitions being inaccessible outside their own package." "description": "Whether downstream packages can `import all` modules of this package.\n\nIf enabled, downstream users will be able to access the `private` internals of modules, including definition bodies not marked as `@[expose]`. This may also, in the future, prevent compiler optimization which rely on `private` definitions being inaccessible outside their own package."
}, },
"fixedToolchain": {
"type": "boolean",
"default": false,
"description": "Whether this package is expected to only function on a single toolchain (the package's toolchain).\n\nThis informs Lake's toolchain update procedure (in `lake update`) to prioritize this package's toolchain. It also avoids the need to separate input-to-output mappings for this package by toolchain version in the Lake cache."
},
"enableArtifactCache": { "enableArtifactCache": {
"type": "boolean", "type": "boolean",
"description": "Whether to enables Lake's local, offline artifact cache for the package.\n\nArtifacts (i.e., build products) of packages will be shared across local copies by storing them in a cache associated with the Lean toolchain.\nThis can significantly reduce initial build times and disk space usage when working with multiple copies of large projects or large dependencies.\n\nAs a caveat, build targets which support the artifact cache will not be stored in their usual location within the build directory. Thus, projects with custom build scripts that rely on specific location of artifacts may wish to disable this feature.\n\nIf `none` (the default), this will fallback to (in order):\n* The `LAKE_ARTIFACT_CACHE` environment variable (if set).\n* The workspace root's `enableArtifactCache` configuration (if set and this package is a dependency).\n* **Lake's default**: The package can use artifacts from the cache, but cannot write to it." "description": "Whether to enables Lake's local, offline artifact cache for the package.\n\nArtifacts (i.e., build products) of packages will be shared across local copies by storing them in a cache associated with the Lean toolchain.\nThis can significantly reduce initial build times and disk space usage when working with multiple copies of large projects or large dependencies.\n\nAs a caveat, build targets which support the artifact cache will not be stored in their usual location within the build directory. Thus, projects with custom build scripts that rely on specific location of artifacts may wish to disable this feature.\n\nIf `none` (the default), this will fallback to (in order):\n* The `LAKE_ARTIFACT_CACHE` environment variable (if set).\n* The workspace root's `enableArtifactCache` configuration (if set and this package is a dependency).\n* **Lake's default**: The package can use artifacts from the cache, but cannot write to it."

View File

@@ -34,7 +34,7 @@ Author: Leonardo de Moura
// Lean-exported demangler from Lean.Compiler.NameDemangling. // Lean-exported demangler from Lean.Compiler.NameDemangling.
// Declared as a weak symbol so leanrt doesn't require libLean at link time. // Declared as a weak symbol so leanrt doesn't require libLean at link time.
// When the Lean demangler is linked in, it overrides this stub. // When the Lean demangler is linked in, it overrides this stub.
extern "C" __attribute__((weak)) lean_object * lean_demangle_bt_line_cstr(lean_object * s) { extern "C" __attribute__((weak)) lean_obj_res lean_demangle_bt_line_cstr(lean_obj_arg s) {
lean_dec(s); lean_dec(s);
return lean_mk_string(""); return lean_mk_string("");
} }
@@ -150,11 +150,9 @@ static void print_backtrace(bool force_stderr) {
if (result_str[0] != '\0') { if (result_str[0] != '\0') {
panic_eprintln(result_str, force_stderr); panic_eprintln(result_str, force_stderr);
lean_dec(result); lean_dec(result);
lean_dec(line_obj);
continue; continue;
} }
lean_dec(result); lean_dec(result);
lean_dec(line_obj);
} }
panic_eprintln(symbols[i], force_stderr); panic_eprintln(symbols[i], force_stderr);
} }
@@ -791,7 +789,7 @@ class task_manager {
// idle before picking up new work. // idle before picking up new work.
// But during shutdown, we skip this throttling: // But during shutdown, we skip this throttling:
// because the finalizer might have called m_queue_cv.notify_all() for the last // because the finalizer might have called m_queue_cv.notify_all() for the last
// time, we don't want to get stuck behind the wait(). // time, we don't want to get stuck behind the wait().
if (!m_shutting_down && if (!m_shutting_down &&
m_std_workers.size() - m_idle_std_workers >= m_max_std_workers) { m_std_workers.size() - m_idle_std_workers >= m_max_std_workers) {
m_queue_cv.wait(lock); m_queue_cv.wait(lock);

View File

@@ -87,4 +87,9 @@ example : True := by {
trivial trivial
} }
/-- info: grind_pattern A => B, C -/
#guard_msgs in
#pp
grind_pattern A => B, C
end Damiano1 end Damiano1

View File

@@ -1,4 +1,4 @@
{"version": "1.1.0", {"version": "1.2.0",
"packagesDir": ".lake/packages", "packagesDir": ".lake/packages",
"packages": "packages":
[{"type": "path", [{"type": "path",
@@ -30,4 +30,5 @@
"dir": "../foo/../b/../root", "dir": "../foo/../b/../root",
"configFile": "lakefile.lean"}], "configFile": "lakefile.lean"}],
"name": "bar", "name": "bar",
"lakeDir": ".lake"} "lakeDir": ".lake",
"fixedToolchain": false}

View File

@@ -1,4 +1,4 @@
{"version": "1.1.0", {"version": "1.2.0",
"packagesDir": ".lake/packages", "packagesDir": ".lake/packages",
"packages": "packages":
[{"url": "bar", [{"url": "bar",
@@ -19,4 +19,5 @@
"dir": "foo", "dir": "foo",
"configFile": "lakefile.lean"}], "configFile": "lakefile.lean"}],
"name": "«foo-bar»", "name": "«foo-bar»",
"lakeDir": ".lake"} "lakeDir": ".lake",
"fixedToolchain": false}

View File

@@ -0,0 +1,23 @@
{"version": "1.2.0",
"packagesDir": ".lake/packages",
"packages":
[{"url": "bar",
"type": "git",
"subDir": null,
"scope": "foo",
"rev": "0538596b94a0510f55dc820cabd3bde41ad93c3e",
"name": "bar",
"manifestFile": "lake-manifest.json",
"inputRev": null,
"inherited": false,
"configFile": "lakefile.lean"},
{"type": "path",
"scope": "",
"name": "foo",
"manifestFile": "lake-manifest.json",
"inherited": false,
"dir": "foo",
"configFile": "lakefile.lean"}],
"name": "«foo-bar»",
"lakeDir": ".lake",
"fixedToolchain": false}

View File

@@ -18,7 +18,7 @@ LOCKED_REV='0538596b94a0510f55dc820cabd3bde41ad93c3e'
test_update() { test_update() {
test_run update test_run update
sed_i "s/$GIT_REV/$LOCKED_REV/g" lake-manifest.json sed_i "s/$GIT_REV/$LOCKED_REV/g" lake-manifest.json
diff --strip-trailing-cr lake-manifest-latest.json lake-manifest.json test_cmd diff -u --strip-trailing-cr lake-manifest-latest.json lake-manifest.json
} }
# --- # ---
@@ -53,6 +53,7 @@ test_manifest v6
test_manifest v7 test_manifest v7
test_manifest v1.0.0 test_manifest v1.0.0
test_manifest v1.1.0 test_manifest v1.1.0
test_manifest v1.2.0
# cleanup # cleanup
rm -rf bar/.git rm -rf bar/.git

View File

@@ -0,0 +1,8 @@
import Lake
open System Lake DSL
def fixedToolchain : Bool :=
get_config? fixedToolchain |>.bind envToBool? |>.getD false
package a where
fixedToolchain := fixedToolchain

View File

@@ -1 +0,0 @@
name = "a"

View File

@@ -0,0 +1,8 @@
import Lake
open System Lake DSL
def fixedToolchain : Bool :=
get_config? fixedToolchain |>.bind envToBool? |>.getD false
package b where
fixedToolchain := fixedToolchain

View File

@@ -1 +0,0 @@
name = "b"

View File

@@ -1 +1,4 @@
rm -f produced.out lean-toolchain a/lean-toolchain b/lean-toolchain rm -f produced.out
rm -rf a/.lake b/.lake
rm -f lean-toolchain a/lean-toolchain b/lean-toolchain
rm -f lake-manifest.json a/lake-manifest.json b/lake-manifest.json

View File

@@ -1,9 +1,13 @@
#!/usr/bin/env bash #!/usr/bin/env bash
source ../common.sh source ../common.sh
./clean.sh
# Ensure Lake thinks there is a elan environment configured # Ensure Lake thinks there is a Elan environment configured
export ELAN_HOME= export ELAN_HOME=
# Ensure Lake does not run a system Elan
export ELAN=false
# Tests the toolchain update functionality of `lake update` # Tests the toolchain update functionality of `lake update`
RESTART_CODE=4 RESTART_CODE=4
@@ -61,6 +65,20 @@ echo lean-a > a/lean-toolchain
echo lean-b > b/lean-toolchain echo lean-b > b/lean-toolchain
test_out "toolchain not updated; multiple toolchain candidates" update test_out "toolchain not updated; multiple toolchain candidates" update
# Test a fixed toolchain and a greater version
./clean.sh
test_run -d a update -R -KfixedToolchain=true
echo v4.4.0 > a/lean-toolchain
echo v4.8.0 > b/lean-toolchain
test_out "(fixed toolchain)" update
# Test a fixed toolchain and a lower version
./clean.sh
test_run -d b update -R -KfixedToolchain=true
echo v4.4.0 > a/lean-toolchain
echo v4.8.0 > b/lean-toolchain
test_update leanprover/lean4:v4.8.0
# Test manual restart # Test manual restart
./clean.sh ./clean.sh
echo lean-a > a/lean-toolchain echo lean-a > a/lean-toolchain
@@ -72,4 +90,4 @@ echo lean-a > a/lean-toolchain
ELAN=echo test_out "run --install lean-a lake update" update ELAN=echo test_out "run --install lean-a lake update" update
# Cleanup # Cleanup
rm -f produced.out ./clean.sh