Add files using upload-large-folder tool
Browse files- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Key.lean +53 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Meta.lean +63 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Package.lean +54 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Require.lean +70 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Script.lean +27 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Syntax.lean +419 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Targets.lean +257 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/VerLit.lean +47 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Config.lean +58 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Lean.lean +37 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Lean/Elab.lean +264 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Lean/Eval.lean +186 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Manifest.lean +290 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Materialize.lean +216 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Package.lean +92 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Resolve.lean +471 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Toml.lean +450 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Workspace.lean +58 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Toml/Data.lean +11 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Toml/Decode.lean +208 -0
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Key.lean
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.Build.Key
|
| 8 |
+
import Lake.DSL.Syntax
|
| 9 |
+
|
| 10 |
+
/-! # DSL for Build Key
|
| 11 |
+
Notation for specifying build keys in a package.
|
| 12 |
+
-/
|
| 13 |
+
|
| 14 |
+
open Lean
|
| 15 |
+
|
| 16 |
+
namespace Lake.DSL
|
| 17 |
+
|
| 18 |
+
private def expandFacets (tgt : Term) (facets : Array Ident) : MacroM Term := do
|
| 19 |
+
let facetLits := facets.map fun facet => Name.quoteFrom facet facet.getId
|
| 20 |
+
facetLits.foldlM (init := tgt) fun tgt lit => `(BuildKey.facet $tgt $lit)
|
| 21 |
+
|
| 22 |
+
private def expandPackageTargetLit
|
| 23 |
+
(pkg : Term) (stx : TSyntax ``packageTargetLit)
|
| 24 |
+
: MacroM Term := withRef stx do
|
| 25 |
+
let `(packageTargetLit|$[+%$mod?]?$tgt) := stx
|
| 26 |
+
| Macro.throwError "ill-formed package target literal"
|
| 27 |
+
let tgtName :=
|
| 28 |
+
if mod?.isSome then
|
| 29 |
+
tgt.getId ++ PartialBuildKey.moduleTargetIndicator
|
| 30 |
+
else
|
| 31 |
+
tgt.getId
|
| 32 |
+
let tgtLit := Name.quoteFrom tgt tgtName
|
| 33 |
+
`(BuildKey.packageTarget $pkg $tgtLit)
|
| 34 |
+
|
| 35 |
+
macro_rules
|
| 36 |
+
| `(`+%$tk$mod$[:$facets]*) => withRef tk do
|
| 37 |
+
let modLit := Name.quoteFrom mod mod.getId
|
| 38 |
+
let tgt ← `(BuildKey.module $modLit)
|
| 39 |
+
let key ← expandFacets tgt facets
|
| 40 |
+
`(PartialBuildKey.mk $key)
|
| 41 |
+
| `(`@%$tk$(pkg?)?$[/$tgt?]?$[:$facets]*) => withRef tk do
|
| 42 |
+
let pkgLit :=
|
| 43 |
+
if let some pkg := pkg? then
|
| 44 |
+
Name.quoteFrom pkg pkg.getId
|
| 45 |
+
else
|
| 46 |
+
Name.quoteFrom tk Name.anonymous
|
| 47 |
+
let tgt ←
|
| 48 |
+
if let some tgt := tgt? then
|
| 49 |
+
expandPackageTargetLit pkgLit tgt
|
| 50 |
+
else
|
| 51 |
+
`(BuildKey.package $pkgLit)
|
| 52 |
+
let key ← expandFacets tgt facets
|
| 53 |
+
`(PartialBuildKey.mk $key)
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Meta.lean
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Eval
|
| 8 |
+
import Lean.Elab.ElabRules
|
| 9 |
+
import Lake.Util.FilePath
|
| 10 |
+
import Lake.DSL.Syntax
|
| 11 |
+
|
| 12 |
+
/-!
|
| 13 |
+
Syntax for elaboration time control flow.
|
| 14 |
+
-/
|
| 15 |
+
|
| 16 |
+
namespace Lake.DSL
|
| 17 |
+
open Lean Meta Elab Command Term
|
| 18 |
+
|
| 19 |
+
@[implemented_by Term.evalTerm]
|
| 20 |
+
opaque evalTerm (α) (type : Expr) (value : Syntax) (safety := DefinitionSafety.safe) : TermElabM α
|
| 21 |
+
|
| 22 |
+
def expandCmdDo : TSyntax ``cmdDo → Array Command
|
| 23 |
+
| `(cmdDo|do $cmds*) => cmds
|
| 24 |
+
| `(cmdDo|$cmd:command) => #[cmd]
|
| 25 |
+
| _ => #[]
|
| 26 |
+
|
| 27 |
+
@[builtin_command_elab metaIf]
|
| 28 |
+
def elabMetaIf : CommandElab := fun stx => do
|
| 29 |
+
let `(meta if $c then $t $[else $e?]?) := stx
|
| 30 |
+
| throwErrorAt stx "ill-formed meta if command"
|
| 31 |
+
if (← withRef c <| runTermElabM fun _ => evalTerm Bool (toTypeExpr Bool) c .unsafe) then
|
| 32 |
+
let cmd := mkNullNode (expandCmdDo t)
|
| 33 |
+
withMacroExpansion (← getRef) cmd <| elabCommand cmd
|
| 34 |
+
else if let some e := e? then
|
| 35 |
+
let cmd := mkNullNode (expandCmdDo e)
|
| 36 |
+
withMacroExpansion (← getRef) cmd <| elabCommand cmd
|
| 37 |
+
|
| 38 |
+
@[implemented_by Meta.evalExpr]
|
| 39 |
+
opaque evalExpr (α) (expectedType : Expr) (value : Expr) (safety := DefinitionSafety.safe) : MetaM α
|
| 40 |
+
|
| 41 |
+
def toExprIO [ToExpr α] (x : IO α) : IO Expr :=
|
| 42 |
+
toExpr <$> x
|
| 43 |
+
|
| 44 |
+
@[builtin_term_elab runIO]
|
| 45 |
+
def elabRunIO : TermElab := fun stx expectedType? =>
|
| 46 |
+
match stx with
|
| 47 |
+
| `(run_io%$tk $t) => withRef t do
|
| 48 |
+
let expectedType := mkApp (mkConst ``IO) <|
|
| 49 |
+
expectedType?.getD <| ← mkFreshExprMVar <| mkSort <| .succ .zero
|
| 50 |
+
let v ← elabTermEnsuringType (← `(do $t)) expectedType
|
| 51 |
+
synthesizeSyntheticMVarsNoPostponing
|
| 52 |
+
let v ← instantiateMVars v
|
| 53 |
+
if (← logUnassignedUsingErrorInfos (← getMVars v)) then
|
| 54 |
+
throwAbortTerm
|
| 55 |
+
let v ← mkAppM ``toExprIO #[v]
|
| 56 |
+
let io ← evalExpr (IO Expr) (mkApp (mkConst ``IO) (mkConst ``Expr)) v
|
| 57 |
+
let (out, x) ← IO.FS.withIsolatedStreams io.toBaseIO
|
| 58 |
+
unless out.isEmpty do
|
| 59 |
+
logInfoAt tk out
|
| 60 |
+
match x with
|
| 61 |
+
| .ok x => return x
|
| 62 |
+
| .error e => throwErrorAt tk e.toString
|
| 63 |
+
| _ => Elab.throwUnsupportedSyntax
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Package.lean
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.Config.Package
|
| 8 |
+
import Lake.DSL.Attributes
|
| 9 |
+
import Lake.DSL.DeclUtil
|
| 10 |
+
import Lake.DSL.Syntax
|
| 11 |
+
|
| 12 |
+
open Lean Parser Elab Command
|
| 13 |
+
|
| 14 |
+
namespace Lake.DSL
|
| 15 |
+
|
| 16 |
+
/-! # Package Declarations
|
| 17 |
+
DSL definitions for packages and hooks.
|
| 18 |
+
-/
|
| 19 |
+
|
| 20 |
+
@[builtin_command_elab packageCommand]
|
| 21 |
+
def elabPackageCommand : CommandElab := fun stx => do
|
| 22 |
+
let `(packageCommand|$(doc?)? $(attrs?)? package%$kw $[$nameStx?]? $cfg) := stx
|
| 23 |
+
| throwErrorAt stx "ill-formed package declaration"
|
| 24 |
+
withRef kw do
|
| 25 |
+
let configId : Ident ← `(pkgConfig)
|
| 26 |
+
let id ← mkConfigDeclIdent nameStx?
|
| 27 |
+
let name := Name.quoteFrom id id.getId
|
| 28 |
+
let ty := Syntax.mkCApp ``PackageConfig #[name]
|
| 29 |
+
elabConfig ``PackageConfig configId ty cfg
|
| 30 |
+
let attr ← `(Term.attrInstance| «package»)
|
| 31 |
+
let attrs := #[attr] ++ expandAttrs attrs?
|
| 32 |
+
let id := mkIdentFrom id packageDeclName
|
| 33 |
+
let decl ← `({name := $name, config := $configId})
|
| 34 |
+
let cmd ← `($[$doc?]? @[$attrs,*] abbrev $id : PackageDecl := $decl)
|
| 35 |
+
withMacroExpansion stx cmd <| elabCommand cmd
|
| 36 |
+
|
| 37 |
+
abbrev PackageCommand := TSyntax ``packageCommand
|
| 38 |
+
|
| 39 |
+
instance : Coe PackageCommand Command where
|
| 40 |
+
coe x := ⟨x.raw⟩
|
| 41 |
+
|
| 42 |
+
@[builtin_macro postUpdateDecl]
|
| 43 |
+
def expandPostUpdateDecl : Macro := fun stx => do
|
| 44 |
+
match stx with
|
| 45 |
+
| `($[$doc?]? $[$attrs?]? post_update%$kw $[$pkg?]? do $seq $[$wds?:whereDecls]?) =>
|
| 46 |
+
`($[$doc?]? $[$attrs?]? post_update%$kw $[$pkg?]? := do $seq $[$wds?:whereDecls]?)
|
| 47 |
+
| `($[$doc?]? $[$attrs?]? post_update%$kw $[$pkg?]? := $defn $[$wds?:whereDecls]?) => withRef kw do
|
| 48 |
+
let pkg ← expandOptSimpleBinder pkg?
|
| 49 |
+
let pkgName := mkIdentFrom pkg `_package.name
|
| 50 |
+
let attr ← `(Term.attrInstance| «post_update»)
|
| 51 |
+
let attrs := #[attr] ++ expandAttrs attrs?
|
| 52 |
+
`($[$doc?]? @[$attrs,*] def postUpdateHook : PostUpdateHookDecl :=
|
| 53 |
+
{pkg := $pkgName, fn := fun $pkg => $defn} $[$wds?:whereDecls]?)
|
| 54 |
+
| stx => Macro.throwErrorAt stx "ill-formed post_update declaration"
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Require.lean
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Parser.Command
|
| 8 |
+
import Lake.Config.Dependency
|
| 9 |
+
import Lake.DSL.Extensions
|
| 10 |
+
import Lake.DSL.DeclUtil
|
| 11 |
+
import Lake.DSL.Syntax
|
| 12 |
+
|
| 13 |
+
open Lean Parser Command
|
| 14 |
+
|
| 15 |
+
namespace Lake.DSL
|
| 16 |
+
|
| 17 |
+
/-! # The `require` syntax
|
| 18 |
+
|
| 19 |
+
This module contains the expansion of the `require` DSL syntax used to specify package dependencies.
|
| 20 |
+
-/
|
| 21 |
+
|
| 22 |
+
@[inline] private def quoteOptTerm [Monad m] [MonadQuotation m] (term? : Option Term) : m Term :=
|
| 23 |
+
if let some term := term? then withRef term ``(some $term) else ``(none)
|
| 24 |
+
|
| 25 |
+
def expandDepSpec (stx : TSyntax ``depSpec) (doc? : Option DocComment) : MacroM Command := do
|
| 26 |
+
let `(depSpec| $fullNameStx $[@ $ver?]? $[from $src?]? $[with $opts?]?) := stx
|
| 27 |
+
| Macro.throwErrorAt stx "ill-formed require syntax"
|
| 28 |
+
let src? ← src?.mapM fun src =>
|
| 29 |
+
match src with
|
| 30 |
+
| `(fromSource|git%$tk $url $[@ $rev?]? $[/ $subDir?]?) => withRef tk do
|
| 31 |
+
let rev ← quoteOptTerm rev?
|
| 32 |
+
let subDir ← quoteOptTerm subDir?
|
| 33 |
+
``(DependencySrc.git $url $rev $subDir)
|
| 34 |
+
| `(fromSource|$path:term) => withRef src do
|
| 35 |
+
``(DependencySrc.path $path)
|
| 36 |
+
| _ => Macro.throwErrorAt src "ill-formed from syntax"
|
| 37 |
+
let `(depName|$[$scope? /]? $nameStx) := fullNameStx
|
| 38 |
+
| Macro.throwErrorAt fullNameStx "ill-formed name syntax"
|
| 39 |
+
let scope :=
|
| 40 |
+
match scope? with
|
| 41 |
+
| some scope => scope
|
| 42 |
+
| none => Syntax.mkStrLit "" (.fromRef fullNameStx)
|
| 43 |
+
let ver ←
|
| 44 |
+
if let some ver := ver? then withRef ver do
|
| 45 |
+
match ver with
|
| 46 |
+
| `(verSpec|git $ver) => ``(some ("git#" ++ $ver))
|
| 47 |
+
| `(verSpec|$ver:term) => ``(some $ver)
|
| 48 |
+
| _ => Macro.throwErrorAt ver "ill-formed version syntax"
|
| 49 |
+
else
|
| 50 |
+
``(none)
|
| 51 |
+
let name := expandIdentOrStrAsIdent nameStx
|
| 52 |
+
`($[$doc?:docComment]? @[package_dep] def $name : $(mkCIdent ``Dependency) := {
|
| 53 |
+
name := $(quote name.getId),
|
| 54 |
+
scope := $scope,
|
| 55 |
+
version? := $ver,
|
| 56 |
+
src? := $(← quoteOptTerm src?),
|
| 57 |
+
opts := $(opts?.getD <| ← `({})),
|
| 58 |
+
})
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
@[builtin_macro requireDecl]
|
| 62 |
+
def expandRequireDecl : Macro := fun stx => do
|
| 63 |
+
let `(requireDecl|$(doc?)? require%$kw $spec) := stx
|
| 64 |
+
| Macro.throwErrorAt stx "ill-formed require declaration"
|
| 65 |
+
withRef kw do expandDepSpec spec doc?
|
| 66 |
+
|
| 67 |
+
@[inherit_doc requireDecl] abbrev RequireDecl := TSyntax ``requireDecl
|
| 68 |
+
|
| 69 |
+
instance : Coe RequireDecl Command where
|
| 70 |
+
coe x := ⟨x.raw⟩
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Script.lean
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.Config.Package
|
| 8 |
+
import Lake.DSL.Attributes
|
| 9 |
+
import Lake.DSL.Syntax
|
| 10 |
+
|
| 11 |
+
/-! # Script Declarations
|
| 12 |
+
DSL definitions to define a Lake script for a package.
|
| 13 |
+
-/
|
| 14 |
+
|
| 15 |
+
namespace Lake.DSL
|
| 16 |
+
open Lean Parser Command
|
| 17 |
+
|
| 18 |
+
@[builtin_macro scriptDecl]
|
| 19 |
+
def expandScriptDecl : Macro
|
| 20 |
+
| `($[$doc?]? $[$attrs?]? script%$kw $name $[$args?]? do $seq $[$wds?:whereDecls]?) => do
|
| 21 |
+
`($[$doc?]? $[$attrs?]? script%$kw $name $[$args?]? := do $seq $[$wds?:whereDecls]?)
|
| 22 |
+
| `($[$doc?]? $[$attrs?]? script%$kw $name $[$args?]? := $defn $[$wds?:whereDecls]?) => withRef kw do
|
| 23 |
+
let id := expandIdentOrStrAsIdent name
|
| 24 |
+
let args ← expandOptSimpleBinder args?
|
| 25 |
+
let attrs := #[← `(Term.attrInstance| «script»)] ++ expandAttrs attrs?
|
| 26 |
+
`($[$doc?]? @[$attrs,*] def $id : ScriptFn := fun $args => $defn $[$wds?:whereDecls]?)
|
| 27 |
+
| stx => Macro.throwErrorAt stx "ill-formed script declaration"
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Syntax.lean
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.DSL.DeclUtil
|
| 8 |
+
import Lean.Parser.Term
|
| 9 |
+
|
| 10 |
+
open Lean Parser Elab Command
|
| 11 |
+
|
| 12 |
+
open System (FilePath)
|
| 13 |
+
|
| 14 |
+
/-!
|
| 15 |
+
This module defines the syntax of the Lake DSL. The syntax is defined separately from the elaborator
|
| 16 |
+
and/or macro definitions to allow clients to import it without crashing Lean. In particular, this
|
| 17 |
+
allows the reference manual to document the DSL syntax.
|
| 18 |
+
-/
|
| 19 |
+
|
| 20 |
+
namespace Lake.DSL
|
| 21 |
+
|
| 22 |
+
/--
|
| 23 |
+
A macro that expands to the path of package's directory
|
| 24 |
+
during the Lakefile's elaboration.
|
| 25 |
+
-/
|
| 26 |
+
scoped syntax (name := dirConst) "__dir__" : term
|
| 27 |
+
|
| 28 |
+
/--
|
| 29 |
+
A macro that expands to the specified configuration option (or `none`,
|
| 30 |
+
if the option has not been set) during the Lakefile's elaboration.
|
| 31 |
+
|
| 32 |
+
Configuration arguments are set either via the Lake CLI (by the `-K` option)
|
| 33 |
+
or via the `with` clause in a `require` statement.
|
| 34 |
+
-/
|
| 35 |
+
scoped syntax (name := getConfig) "get_config? " ident :term
|
| 36 |
+
|
| 37 |
+
/-!
|
| 38 |
+
# Package Declarations
|
| 39 |
+
DSL syntax definitions for packages and hooks.
|
| 40 |
+
-/
|
| 41 |
+
|
| 42 |
+
/--
|
| 43 |
+
Defines the configuration of a Lake package. Has many forms:
|
| 44 |
+
|
| 45 |
+
```lean
|
| 46 |
+
package «pkg-name»
|
| 47 |
+
package «pkg-name» { /- config opts -/ }
|
| 48 |
+
package «pkg-name» where /- config opts -/
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
There can only be one `package` declaration per Lake configuration file.
|
| 52 |
+
The defined package configuration will be available for reference as `_package`.
|
| 53 |
+
-/
|
| 54 |
+
scoped syntax (name := packageCommand)
|
| 55 |
+
(docComment)? (Term.attributes)? "package " (identOrStr)? optConfig : command
|
| 56 |
+
|
| 57 |
+
/--
|
| 58 |
+
Declare a post-`lake update` hook for the package.
|
| 59 |
+
Runs the monadic action is after a successful `lake update` execution
|
| 60 |
+
in this package or one of its downstream dependents.
|
| 61 |
+
|
| 62 |
+
**Example**
|
| 63 |
+
|
| 64 |
+
This feature enables Mathlib to synchronize the Lean toolchain and run
|
| 65 |
+
`cache get` after a `lake update`:
|
| 66 |
+
|
| 67 |
+
```
|
| 68 |
+
lean_exe cache
|
| 69 |
+
post_update pkg do
|
| 70 |
+
let wsToolchainFile := (← getRootPackage).dir / "lean-toolchain"
|
| 71 |
+
let mathlibToolchain ← IO.FS.readFile <| pkg.dir / "lean-toolchain"
|
| 72 |
+
IO.FS.writeFile wsToolchainFile mathlibToolchain
|
| 73 |
+
let exeFile ← runBuild cache.fetch
|
| 74 |
+
let exitCode ← env exeFile.toString #["get"]
|
| 75 |
+
if exitCode ≠ 0 then
|
| 76 |
+
error s!"{pkg.name}: failed to fetch cache"
|
| 77 |
+
```
|
| 78 |
+
-/
|
| 79 |
+
scoped syntax (name := postUpdateDecl)
|
| 80 |
+
optional(docComment) optional(Term.attributes)
|
| 81 |
+
"post_update " (ppSpace simpleBinder)? (declValSimple <|> declValDo)
|
| 82 |
+
: command
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
/-! # The `require` syntax
|
| 86 |
+
|
| 87 |
+
This is the `require` DSL syntax used to specify package dependencies.
|
| 88 |
+
-/
|
| 89 |
+
|
| 90 |
+
syntax fromPath :=
|
| 91 |
+
term
|
| 92 |
+
|
| 93 |
+
syntax fromGit :=
|
| 94 |
+
&"git " term:max ("@" term:max)? ("/" term)?
|
| 95 |
+
|
| 96 |
+
syntax fromSource :=
|
| 97 |
+
fromGit <|> fromPath
|
| 98 |
+
|
| 99 |
+
/--
|
| 100 |
+
Specifies a specific source from which to draw the package dependency.
|
| 101 |
+
Dependencies that are downloaded from a remote source will be placed
|
| 102 |
+
into the workspace's `packagesDir`.
|
| 103 |
+
|
| 104 |
+
**Path Dependencies**
|
| 105 |
+
|
| 106 |
+
```
|
| 107 |
+
from <path>
|
| 108 |
+
```
|
| 109 |
+
|
| 110 |
+
Lake loads the package located a fixed `path` relative to the
|
| 111 |
+
requiring package's directory.
|
| 112 |
+
|
| 113 |
+
**Git Dependencies**
|
| 114 |
+
|
| 115 |
+
```
|
| 116 |
+
from git <url> [@ <rev>] [/ <subDir>]
|
| 117 |
+
```
|
| 118 |
+
|
| 119 |
+
Lake clones the Git repository available at the specified fixed Git `url`,
|
| 120 |
+
and checks out the specified revision `rev`. The revision can be a commit hash,
|
| 121 |
+
branch, or tag. If none is provided, Lake defaults to `master`. After checkout,
|
| 122 |
+
Lake loads the package located in `subDir` (or the repository root if no
|
| 123 |
+
subdirectory is specified).
|
| 124 |
+
-/
|
| 125 |
+
syntax fromClause :=
|
| 126 |
+
" from " fromSource
|
| 127 |
+
|
| 128 |
+
/-
|
| 129 |
+
A `NameMap String` of Lake options used to configure the dependency.
|
| 130 |
+
This is equivalent to passing `-K` options to the dependency on the command line.
|
| 131 |
+
-/
|
| 132 |
+
syntax withClause :=
|
| 133 |
+
" with " term
|
| 134 |
+
|
| 135 |
+
syntax verSpec :=
|
| 136 |
+
&"git "? term:max
|
| 137 |
+
|
| 138 |
+
/--
|
| 139 |
+
The version of the package to require.
|
| 140 |
+
To specify a Git revision, use the syntax `@ git <rev>`.
|
| 141 |
+
-/
|
| 142 |
+
syntax verClause :=
|
| 143 |
+
" @ " verSpec
|
| 144 |
+
|
| 145 |
+
syntax depName :=
|
| 146 |
+
atomic(str " / ")? identOrStr
|
| 147 |
+
|
| 148 |
+
syntax depSpec :=
|
| 149 |
+
depName (verClause)? (fromClause)? (withClause)?
|
| 150 |
+
|
| 151 |
+
/--
|
| 152 |
+
Adds a new package dependency to the workspace. The general syntax is:
|
| 153 |
+
|
| 154 |
+
```
|
| 155 |
+
require ["<scope>" /] <pkg-name> [@ <version>]
|
| 156 |
+
[from <source>] [with <options>]
|
| 157 |
+
```
|
| 158 |
+
|
| 159 |
+
The `from` clause tells Lake where to locate the dependency.
|
| 160 |
+
See the `fromClause` syntax documentation (e.g., hover over it) to see
|
| 161 |
+
the different forms this clause can take.
|
| 162 |
+
|
| 163 |
+
Without a `from` clause, Lake will lookup the package in the default
|
| 164 |
+
registry (i.e., Reservoir) and use the information there to download the
|
| 165 |
+
package at the requested `version`. The `scope` is used to disambiguate between
|
| 166 |
+
packages in the registry with the same `pkg-name`. In Reservoir, this scope
|
| 167 |
+
is the package owner (e.g., `leanprover` of `@leanprover/doc-gen4`).
|
| 168 |
+
|
| 169 |
+
The `with` clause specifies a `NameMap String` of Lake options
|
| 170 |
+
used to configure the dependency. This is equivalent to passing `-K`
|
| 171 |
+
options to the dependency on the command line.
|
| 172 |
+
-/
|
| 173 |
+
scoped syntax (name := requireDecl)
|
| 174 |
+
(docComment)? "require " depSpec : command
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
/-!
|
| 178 |
+
# DSL for Targets & Facets
|
| 179 |
+
Syntax for declaring Lake targets and facets.
|
| 180 |
+
-/
|
| 181 |
+
|
| 182 |
+
syntax buildDeclSig :=
|
| 183 |
+
identOrStr (ppSpace simpleBinder)? Term.typeSpec declValSimple
|
| 184 |
+
|
| 185 |
+
/-!
|
| 186 |
+
## Facet Declarations
|
| 187 |
+
-/
|
| 188 |
+
|
| 189 |
+
/--
|
| 190 |
+
Define a new module facet. Has one form:
|
| 191 |
+
|
| 192 |
+
```lean
|
| 193 |
+
module_facet «facet-name» (mod : Module) : α :=
|
| 194 |
+
/- build term of type `FetchM (Job α)` -/
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
The `mod` parameter (and its type specifier) is optional.
|
| 198 |
+
-/
|
| 199 |
+
scoped syntax (name := moduleFacetDecl)
|
| 200 |
+
(docComment)? (Term.attributes)? "module_facet " buildDeclSig : command
|
| 201 |
+
|
| 202 |
+
/--
|
| 203 |
+
Define a new package facet. Has one form:
|
| 204 |
+
|
| 205 |
+
```lean
|
| 206 |
+
package_facet «facet-name» (pkg : Package) : α :=
|
| 207 |
+
/- build term of type `FetchM (Job α)` -/
|
| 208 |
+
```
|
| 209 |
+
|
| 210 |
+
The `pkg` parameter (and its type specifier) is optional.
|
| 211 |
+
-/
|
| 212 |
+
scoped syntax (name := packageFacetDecl)
|
| 213 |
+
(docComment)? (Term.attributes)? "package_facet " buildDeclSig : command
|
| 214 |
+
|
| 215 |
+
/--
|
| 216 |
+
Define a new library facet. Has one form:
|
| 217 |
+
|
| 218 |
+
```lean
|
| 219 |
+
library_facet «facet-name» (lib : LeanLib) : α :=
|
| 220 |
+
/- build term of type `FetchM (Job α)` -/
|
| 221 |
+
```
|
| 222 |
+
|
| 223 |
+
The `lib` parameter (and its type specifier) is optional.
|
| 224 |
+
-/
|
| 225 |
+
scoped syntax (name := libraryFacetDecl)
|
| 226 |
+
(docComment)? (Term.attributes)? "library_facet " buildDeclSig : command
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
/-!
|
| 230 |
+
## Custom Target Declaration
|
| 231 |
+
-/
|
| 232 |
+
|
| 233 |
+
/--
|
| 234 |
+
Define a new custom target for the package. Has one form:
|
| 235 |
+
|
| 236 |
+
```lean
|
| 237 |
+
target «target-name» (pkg : NPackage _package.name) : α :=
|
| 238 |
+
/- build term of type `FetchM (Job α)` -/
|
| 239 |
+
```
|
| 240 |
+
|
| 241 |
+
The `pkg` parameter (and its type specifier) is optional.
|
| 242 |
+
It is of type `NPackage _package.name` to provably demonstrate the package
|
| 243 |
+
provided is the package in which the target is defined.
|
| 244 |
+
-/
|
| 245 |
+
scoped syntax (name := targetCommand)
|
| 246 |
+
(docComment)? (Term.attributes)? "target " buildDeclSig : command
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
/-!
|
| 250 |
+
## Lean Library & Executable Target Declarations
|
| 251 |
+
-/
|
| 252 |
+
|
| 253 |
+
/--
|
| 254 |
+
Define a new Lean library target for the package.
|
| 255 |
+
Can optionally be provided with a configuration of type `LeanLibConfig`.
|
| 256 |
+
Has many forms:
|
| 257 |
+
|
| 258 |
+
```lean
|
| 259 |
+
lean_lib «target-name»
|
| 260 |
+
lean_lib «target-name» { /- config opts -/ }
|
| 261 |
+
lean_lib «target-name» where /- config opts -/
|
| 262 |
+
```
|
| 263 |
+
-/
|
| 264 |
+
scoped syntax (name := leanLibCommand)
|
| 265 |
+
(docComment)? (Term.attributes)? "lean_lib " (identOrStr)? optConfig : command
|
| 266 |
+
|
| 267 |
+
/--
|
| 268 |
+
Define a new Lean binary executable target for the package.
|
| 269 |
+
Can optionally be provided with a configuration of type `LeanExeConfig`.
|
| 270 |
+
Has many forms:
|
| 271 |
+
|
| 272 |
+
```lean
|
| 273 |
+
lean_exe «target-name»
|
| 274 |
+
lean_exe «target-name» { /- config opts -/ }
|
| 275 |
+
lean_exe «target-name» where /- config opts -/
|
| 276 |
+
```
|
| 277 |
+
-/
|
| 278 |
+
scoped syntax (name := leanExeCommand)
|
| 279 |
+
(docComment)? (Term.attributes)? "lean_exe " (identOrStr)? optConfig : command
|
| 280 |
+
|
| 281 |
+
/--
|
| 282 |
+
Define a new input file target for the package.
|
| 283 |
+
Can optionally be provided with a configuration of type `InputFileConfig`.
|
| 284 |
+
-/
|
| 285 |
+
scoped syntax (name := inputFileCommand)
|
| 286 |
+
(docComment)? (Term.attributes)? "input_file " (identOrStr)? optConfig : command
|
| 287 |
+
|
| 288 |
+
/--
|
| 289 |
+
Define a new input directory target for the package.
|
| 290 |
+
Can optionally be provided with a configuration of type `InputDirConfig`.
|
| 291 |
+
-/
|
| 292 |
+
scoped syntax (name := inputDirCommand)
|
| 293 |
+
(docComment)? (Term.attributes)? "input_dir " (identOrStr)? optConfig : command
|
| 294 |
+
|
| 295 |
+
/-!
|
| 296 |
+
## External Library Target Declaration
|
| 297 |
+
-/
|
| 298 |
+
|
| 299 |
+
syntax externLibDeclSpec :=
|
| 300 |
+
identOrStr (ppSpace simpleBinder)? declValSimple
|
| 301 |
+
|
| 302 |
+
/--
|
| 303 |
+
Define a new external library target for the package. Has one form:
|
| 304 |
+
|
| 305 |
+
```lean
|
| 306 |
+
extern_lib «target-name» (pkg : NPackage _package.name) :=
|
| 307 |
+
/- build term of type `FetchM (Job FilePath)` -/
|
| 308 |
+
```
|
| 309 |
+
|
| 310 |
+
The `pkg` parameter (and its type specifier) is optional.
|
| 311 |
+
It is of type `NPackage _package.name` to provably demonstrate the package
|
| 312 |
+
provided is the package in which the target is defined.
|
| 313 |
+
|
| 314 |
+
The term should build the external library's **static** library.
|
| 315 |
+
-/
|
| 316 |
+
scoped syntax (name := externLibCommand)
|
| 317 |
+
(docComment)? (Term.attributes)? "extern_lib " externLibDeclSpec : command
|
| 318 |
+
|
| 319 |
+
/-!
|
| 320 |
+
# Script Declarations
|
| 321 |
+
|
| 322 |
+
DSL definitions to define a Lake script for a package.
|
| 323 |
+
-/
|
| 324 |
+
|
| 325 |
+
syntax scriptDeclSpec :=
|
| 326 |
+
identOrStr (ppSpace simpleBinder)? (declValSimple <|> declValDo)
|
| 327 |
+
|
| 328 |
+
/--
|
| 329 |
+
Define a new Lake script for the package.
|
| 330 |
+
|
| 331 |
+
**Example**
|
| 332 |
+
|
| 333 |
+
```
|
| 334 |
+
/-- Display a greeting -/
|
| 335 |
+
script «script-name» (args) do
|
| 336 |
+
if h : 0 < args.length then
|
| 337 |
+
IO.println s!"Hello, {args[0]'h}!"
|
| 338 |
+
else
|
| 339 |
+
IO.println "Hello, world!"
|
| 340 |
+
return 0
|
| 341 |
+
```
|
| 342 |
+
-/
|
| 343 |
+
scoped syntax (name := scriptDecl)
|
| 344 |
+
(docComment)? optional(Term.attributes) "script " scriptDeclSpec : command
|
| 345 |
+
|
| 346 |
+
end DSL
|
| 347 |
+
|
| 348 |
+
/-!
|
| 349 |
+
# Version Literals
|
| 350 |
+
|
| 351 |
+
Defines the `v!"<ver>"` syntax for version literals.
|
| 352 |
+
-/
|
| 353 |
+
|
| 354 |
+
/-- A Lake version literal. -/
|
| 355 |
+
scoped syntax:max (name := verLit) "v!" noWs interpolatedStr(term) : term
|
| 356 |
+
|
| 357 |
+
namespace DSL
|
| 358 |
+
|
| 359 |
+
/-!
|
| 360 |
+
# DSL for Build Key
|
| 361 |
+
|
| 362 |
+
Notation for specifying build keys in a package.
|
| 363 |
+
-/
|
| 364 |
+
|
| 365 |
+
syntax facetSuffix := atomic(":" noWs) ident
|
| 366 |
+
syntax packageTargetLit := atomic("+" noWs)? ident
|
| 367 |
+
|
| 368 |
+
/-- A module target key literal (with optional facet). -/
|
| 369 |
+
scoped syntax:max "`+" noWs ident facetSuffix* : term
|
| 370 |
+
|
| 371 |
+
/-- A package target key literal (with optional facet). -/
|
| 372 |
+
scoped syntax:max "`@" (noWs ident)?
|
| 373 |
+
(atomic(noWs "/" noWs) packageTargetLit)? (noWs facetSuffix)* : term
|
| 374 |
+
|
| 375 |
+
/-!
|
| 376 |
+
# Elaboration-Time Control Flow
|
| 377 |
+
|
| 378 |
+
Syntax for elaboration time control flow.
|
| 379 |
+
-/
|
| 380 |
+
|
| 381 |
+
/--
|
| 382 |
+
The `do` command syntax groups multiple similarly indented commands together.
|
| 383 |
+
The group can then be passed to another command that usually only accepts a
|
| 384 |
+
single command (e.g., `meta if`).
|
| 385 |
+
-/
|
| 386 |
+
syntax cmdDo := ("do" many1Indent(command)) <|> command
|
| 387 |
+
|
| 388 |
+
/--
|
| 389 |
+
The `meta if` command has two forms:
|
| 390 |
+
|
| 391 |
+
```lean
|
| 392 |
+
meta if <c:term> then <a:command>
|
| 393 |
+
meta if <c:term> then <a:command> else <b:command>
|
| 394 |
+
```
|
| 395 |
+
|
| 396 |
+
It expands to the command `a` if the term `c` evaluates to true
|
| 397 |
+
(at elaboration time). Otherwise, it expands to command `b` (if an `else`
|
| 398 |
+
clause is provided).
|
| 399 |
+
|
| 400 |
+
One can use this command to specify, for example, external library targets
|
| 401 |
+
only available on specific platforms:
|
| 402 |
+
|
| 403 |
+
```lean
|
| 404 |
+
meta if System.Platform.isWindows then
|
| 405 |
+
extern_lib winOnlyLib := ...
|
| 406 |
+
else meta if System.Platform.isOSX then
|
| 407 |
+
extern_lib macOnlyLib := ...
|
| 408 |
+
else
|
| 409 |
+
extern_lib linuxOnlyLib := ...
|
| 410 |
+
```
|
| 411 |
+
-/
|
| 412 |
+
scoped syntax (name := metaIf)
|
| 413 |
+
"meta " "if " term " then " cmdDo (" else " cmdDo)? : command
|
| 414 |
+
|
| 415 |
+
/--
|
| 416 |
+
Executes a term of type `IO α` at elaboration-time
|
| 417 |
+
and produces an expression corresponding to the result via `ToExpr α`.
|
| 418 |
+
-/
|
| 419 |
+
scoped syntax:lead (name := runIO) "run_io " doSeq : term
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/Targets.lean
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.DSL.DeclUtil
|
| 8 |
+
import Lake.DSL.Syntax
|
| 9 |
+
import Lake.Config.FacetConfig
|
| 10 |
+
import Lake.Config.TargetConfig
|
| 11 |
+
import Lake.Build.Job
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
/-! # DSL for Targets & Facets
|
| 15 |
+
Macros for declaring Lake targets and facets.
|
| 16 |
+
-/
|
| 17 |
+
|
| 18 |
+
open System (FilePath)
|
| 19 |
+
open Lean Parser Elab Command
|
| 20 |
+
|
| 21 |
+
namespace Lake.DSL
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
--------------------------------------------------------------------------------
|
| 25 |
+
/-! ## Facet Declarations -/
|
| 26 |
+
--------------------------------------------------------------------------------
|
| 27 |
+
|
| 28 |
+
abbrev mkModuleFacetDecl
|
| 29 |
+
(α) (facet : Name)
|
| 30 |
+
[OptDataKind α] [FormatQuery α] [FamilyDef ModuleData facet α]
|
| 31 |
+
(f : Module → FetchM (Job α))
|
| 32 |
+
: ModuleFacetDecl := .mk (Module.facetKind ++ facet) <| mkFacetJobConfig fun mod => do
|
| 33 |
+
withRegisterJob (mod.facet facet |>.key.toSimpleString)
|
| 34 |
+
(f mod)
|
| 35 |
+
|
| 36 |
+
@[builtin_macro moduleFacetDecl]
|
| 37 |
+
def expandModuleFacetDecl : Macro := fun stx => do
|
| 38 |
+
let `(moduleFacetDecl|$(doc?)? $(attrs?)? module_facet%$kw $sig) := stx
|
| 39 |
+
| Macro.throwErrorAt stx "ill-formed module facet declaration"
|
| 40 |
+
let `(buildDeclSig| $nameStx $[$mod?]? : $ty := $defn $[$wds?:whereDecls]?) := sig
|
| 41 |
+
| Macro.throwErrorAt sig "ill-formed module facet declaration"
|
| 42 |
+
withRef kw do
|
| 43 |
+
let attr ← `(Term.attrInstance| «module_facet»)
|
| 44 |
+
let attrs := #[attr] ++ expandAttrs attrs?
|
| 45 |
+
let id := expandIdentOrStrAsIdent nameStx
|
| 46 |
+
let facet := Name.quoteFrom id id.getId
|
| 47 |
+
let declId := mkIdentFrom id <| id.getId.modifyBase (.str · "_modFacet")
|
| 48 |
+
let mod ← expandOptSimpleBinder mod?
|
| 49 |
+
`(module_data $id : $ty
|
| 50 |
+
$[$doc?:docComment]? @[$attrs,*] abbrev $declId :=
|
| 51 |
+
Lake.DSL.mkModuleFacetDecl $ty $facet (fun $mod => $defn)
|
| 52 |
+
$[$wds?:whereDecls]?)
|
| 53 |
+
|
| 54 |
+
abbrev mkPackageFacetDecl
|
| 55 |
+
(α) (facet : Name)
|
| 56 |
+
[OptDataKind α] [FormatQuery α] [FamilyDef PackageData facet α]
|
| 57 |
+
(f : Package → FetchM (Job α))
|
| 58 |
+
: PackageFacetDecl := .mk (Package.facetKind ++ facet) <| mkFacetJobConfig fun pkg => do
|
| 59 |
+
withRegisterJob (pkg.facet facet |>.key.toSimpleString)
|
| 60 |
+
(f pkg)
|
| 61 |
+
|
| 62 |
+
@[builtin_macro packageFacetDecl]
|
| 63 |
+
def expandPackageFacetDecl : Macro := fun stx => do
|
| 64 |
+
let `(packageFacetDecl|$(doc?)? $(attrs?)? package_facet%$kw $sig) := stx
|
| 65 |
+
| Macro.throwErrorAt stx "ill-formed package facet declaration"
|
| 66 |
+
let `(buildDeclSig| $nameStx $[$pkg?]? : $ty := $defn $[$wds?:whereDecls]?) := sig
|
| 67 |
+
| Macro.throwErrorAt sig "ill-formed package facet signature"
|
| 68 |
+
withRef kw do
|
| 69 |
+
let attr ← `(Term.attrInstance| «package_facet»)
|
| 70 |
+
let attrs := #[attr] ++ expandAttrs attrs?
|
| 71 |
+
let id := expandIdentOrStrAsIdent nameStx
|
| 72 |
+
let facet := Name.quoteFrom id id.getId
|
| 73 |
+
let declId := mkIdentFrom id <| id.getId.modifyBase (.str · "_pkgFacet")
|
| 74 |
+
let pkg ← expandOptSimpleBinder pkg?
|
| 75 |
+
`(package_data $id : $ty
|
| 76 |
+
$[$doc?]? @[$attrs,*] abbrev $declId :=
|
| 77 |
+
Lake.DSL.mkPackageFacetDecl $ty $facet (fun $pkg => $defn)
|
| 78 |
+
$[$wds?:whereDecls]?)
|
| 79 |
+
|
| 80 |
+
abbrev mkLibraryFacetDecl
|
| 81 |
+
(α) (facet : Name)
|
| 82 |
+
[OptDataKind α] [FormatQuery α] [FamilyDef LibraryData facet α]
|
| 83 |
+
(f : LeanLib → FetchM (Job α))
|
| 84 |
+
: LibraryFacetDecl := .mk (LeanLib.facetKind ++ facet) <| mkFacetJobConfig fun lib => do
|
| 85 |
+
withRegisterJob (lib.facet facet |>.key.toSimpleString)
|
| 86 |
+
(f lib)
|
| 87 |
+
|
| 88 |
+
@[builtin_macro libraryFacetDecl]
|
| 89 |
+
def expandLibraryFacetDecl : Macro := fun stx => do
|
| 90 |
+
let `(libraryFacetDecl|$(doc?)? $(attrs?)? library_facet%$kw $sig) := stx
|
| 91 |
+
| Macro.throwErrorAt stx "ill-formed library facet declaration"
|
| 92 |
+
let `(buildDeclSig| $nameStx $[$lib?]? : $ty := $defn $[$wds?:whereDecls]?) := sig
|
| 93 |
+
| Macro.throwErrorAt sig "ill-formed library facet signature"
|
| 94 |
+
withRef kw do
|
| 95 |
+
let attr ← `(Term.attrInstance| «library_facet»)
|
| 96 |
+
let attrs := #[attr] ++ expandAttrs attrs?
|
| 97 |
+
let id := expandIdentOrStrAsIdent nameStx
|
| 98 |
+
let facet := Name.quoteFrom id id.getId
|
| 99 |
+
let declId := mkIdentFrom id <| id.getId.modifyBase (.str · "_libFacet")
|
| 100 |
+
let lib ← expandOptSimpleBinder lib?
|
| 101 |
+
`(library_data $id : $ty
|
| 102 |
+
$[$doc?]? @[$attrs,*] abbrev $declId : LibraryFacetDecl :=
|
| 103 |
+
Lake.DSL.mkLibraryFacetDecl $ty $facet (fun $lib => $defn)
|
| 104 |
+
$[$wds?:whereDecls]?)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
--------------------------------------------------------------------------------
|
| 108 |
+
/-! ## Custom Target Declaration -/
|
| 109 |
+
--------------------------------------------------------------------------------
|
| 110 |
+
|
| 111 |
+
abbrev mkTargetDecl
|
| 112 |
+
(α) (pkgName «target» : Name)
|
| 113 |
+
[OptDataKind α] [FormatQuery α] [FamilyDef (CustomData pkgName) «target» α]
|
| 114 |
+
(f : NPackage pkgName → FetchM (Job α))
|
| 115 |
+
: TargetDecl :=
|
| 116 |
+
let cfg := mkTargetJobConfig fun pkg => do
|
| 117 |
+
withRegisterJob (pkg.target «target» |>.key.toSimpleString) do
|
| 118 |
+
f pkg
|
| 119 |
+
.mk (.mk pkgName «target» .anonymous (.mk cfg) (by simp [Name.isAnonymous])) rfl
|
| 120 |
+
|
| 121 |
+
@[builtin_macro targetCommand]
|
| 122 |
+
def expandTargetCommand : Macro := fun stx => do
|
| 123 |
+
let `(targetCommand|$(doc?)? $(attrs?)? target%$kw $sig) := stx
|
| 124 |
+
| Macro.throwErrorAt stx "ill-formed target declaration"
|
| 125 |
+
let `(buildDeclSig|$id:ident $[$pkg?]? : $ty := $defn $[$wds?:whereDecls]?) := sig
|
| 126 |
+
| Macro.throwErrorAt sig "ill-formed target signature"
|
| 127 |
+
withRef kw do
|
| 128 |
+
let attr ← `(Term.attrInstance| «target»)
|
| 129 |
+
let attrs := #[attr] ++ expandAttrs attrs?
|
| 130 |
+
let name := Name.quoteFrom id id.getId
|
| 131 |
+
let pkgName := mkIdentFrom id (packageDeclName.str "name")
|
| 132 |
+
let pkg ← expandOptSimpleBinder pkg?
|
| 133 |
+
`(family_def $id : CustomOut ($pkgName, $name) := $ty
|
| 134 |
+
$[$doc?]? abbrev $id :=
|
| 135 |
+
Lake.DSL.mkTargetDecl $ty $pkgName $name (fun $pkg => $defn)
|
| 136 |
+
$[$wds?:whereDecls]?
|
| 137 |
+
@[$attrs,*] def configDecl : ConfigDecl := $(id).toConfigDecl)
|
| 138 |
+
|
| 139 |
+
--------------------------------------------------------------------------------
|
| 140 |
+
/-! ## Lean Library & Executable Target Declarations -/
|
| 141 |
+
--------------------------------------------------------------------------------
|
| 142 |
+
|
| 143 |
+
abbrev mkConfigDecl
|
| 144 |
+
(pkg name kind : Name)
|
| 145 |
+
(config : ConfigType kind pkg name)
|
| 146 |
+
[FamilyDef (CustomData pkg) name (ConfigTarget kind)]
|
| 147 |
+
[FamilyDef DataType kind (ConfigTarget kind)]
|
| 148 |
+
: KConfigDecl kind :=
|
| 149 |
+
{pkg, name, kind, config, wf_data := fun _ => by simp, kind_eq := rfl}
|
| 150 |
+
|
| 151 |
+
def mkConfigDeclDef
|
| 152 |
+
(tyName attr kind : Name)
|
| 153 |
+
[ConfigInfo tyName] [delTyName : TypeName (KConfigDecl kind)]
|
| 154 |
+
(doc? : Option DocComment) (attrs? : Option Attributes)
|
| 155 |
+
(nameStx? : Option IdentOrStr) (cfg : OptConfig)
|
| 156 |
+
: CommandElabM Command := do
|
| 157 |
+
let configId : Ident ← `(config)
|
| 158 |
+
let id ← mkConfigDeclIdent nameStx?
|
| 159 |
+
let name := Name.quoteFrom id id.getId
|
| 160 |
+
let ty := Syntax.mkCApp tyName #[name]
|
| 161 |
+
elabConfig tyName configId ty cfg
|
| 162 |
+
let attrId ← mkIdentFromRef attr
|
| 163 |
+
let targetAttr ← `(Term.attrInstance| «target»)
|
| 164 |
+
let kindAttr ← `(Term.attrInstance| $attrId:ident)
|
| 165 |
+
let attrs := #[targetAttr, kindAttr] ++ expandAttrs attrs?
|
| 166 |
+
let pkg ← mkIdentFromRef (packageDeclName.str "name")
|
| 167 |
+
let declTy ← mkIdentFromRef delTyName.typeName
|
| 168 |
+
let kind := Name.quoteFrom (← getRef) kind
|
| 169 |
+
`(family_def $id : CustomOut ($pkg, $name) := ConfigTarget $kind
|
| 170 |
+
$[$doc?]? abbrev $id : $declTy :=
|
| 171 |
+
Lake.DSL.mkConfigDecl $pkg $name $kind $configId
|
| 172 |
+
@[$attrs,*] def configDecl : ConfigDecl := $(id).toConfigDecl
|
| 173 |
+
)
|
| 174 |
+
|
| 175 |
+
@[builtin_command_elab leanLibCommand]
|
| 176 |
+
def elabLeanLibCommand : CommandElab := fun stx => do
|
| 177 |
+
let `(leanLibCommand|$(doc?)? $(attrs?)? lean_lib%$kw $(nameStx?)? $cfg) := stx
|
| 178 |
+
| throwErrorAt stx "ill-formed lean_lib declaration"
|
| 179 |
+
withRef kw do
|
| 180 |
+
let cmd ← mkConfigDeclDef ``LeanLibConfig LeanLib.keyword LeanLib.configKind doc? attrs? nameStx? cfg
|
| 181 |
+
withMacroExpansion stx cmd <| elabCommand cmd
|
| 182 |
+
|
| 183 |
+
@[inherit_doc leanLibCommand] abbrev LeanLibCommand := TSyntax ``leanLibCommand
|
| 184 |
+
|
| 185 |
+
instance : Coe LeanLibCommand Command where
|
| 186 |
+
coe x := ⟨x.raw⟩
|
| 187 |
+
|
| 188 |
+
@[builtin_command_elab leanExeCommand]
|
| 189 |
+
def elabLeanExeCommand : CommandElab := fun stx => do
|
| 190 |
+
let `(leanExeCommand|$(doc?)? $(attrs?)? lean_exe%$kw $(nameStx?)? $cfg) := stx
|
| 191 |
+
| throwErrorAt stx "ill-formed lean_exe declaration"
|
| 192 |
+
withRef kw do
|
| 193 |
+
let cmd ← mkConfigDeclDef ``LeanExeConfig LeanExe.keyword LeanExe.configKind doc? attrs? nameStx? cfg
|
| 194 |
+
withMacroExpansion stx cmd <| elabCommand cmd
|
| 195 |
+
|
| 196 |
+
@[inherit_doc leanExeCommand] abbrev LeanExeCommand := TSyntax ``leanExeCommand
|
| 197 |
+
|
| 198 |
+
instance : Coe LeanExeCommand Command where
|
| 199 |
+
coe x := ⟨x.raw⟩
|
| 200 |
+
|
| 201 |
+
@[builtin_command_elab inputFileCommand]
|
| 202 |
+
def elabInputfileCommand : CommandElab := fun stx => do
|
| 203 |
+
let `(inputFileCommand|$(doc?)? $(attrs?)? input_file%$kw $(nameStx?)? $cfg) := stx
|
| 204 |
+
| throwErrorAt stx "ill-formed input_file declaration"
|
| 205 |
+
withRef kw do
|
| 206 |
+
let cmd ← mkConfigDeclDef ``InputFileConfig InputFile.keyword InputFile.configKind doc? attrs? nameStx? cfg
|
| 207 |
+
withMacroExpansion stx cmd <| elabCommand cmd
|
| 208 |
+
|
| 209 |
+
@[inherit_doc inputFileCommand] abbrev InputFileCommand := TSyntax ``inputFileCommand
|
| 210 |
+
|
| 211 |
+
instance : Coe InputFileCommand Command where
|
| 212 |
+
coe x := ⟨x.raw⟩
|
| 213 |
+
|
| 214 |
+
@[builtin_command_elab inputDirCommand]
|
| 215 |
+
def elabInputDirCommand : CommandElab := fun stx => do
|
| 216 |
+
let `(inputDirCommand|$(doc?)? $(attrs?)? input_dir%$kw $(nameStx?)? $cfg) := stx
|
| 217 |
+
| throwErrorAt stx "ill-formed input_dir declaration"
|
| 218 |
+
withRef kw do
|
| 219 |
+
let cmd ← mkConfigDeclDef ``InputDirConfig InputDir.keyword InputDir.configKind doc? attrs? nameStx? cfg
|
| 220 |
+
withMacroExpansion stx cmd <| elabCommand cmd
|
| 221 |
+
|
| 222 |
+
@[inherit_doc inputDirCommand] abbrev InputDirCommand := TSyntax ``inputDirCommand
|
| 223 |
+
|
| 224 |
+
instance : Coe InputDirCommand Command where
|
| 225 |
+
coe x := ⟨x.raw⟩
|
| 226 |
+
|
| 227 |
+
--------------------------------------------------------------------------------
|
| 228 |
+
/-! ## External Library Target Declaration -/
|
| 229 |
+
--------------------------------------------------------------------------------
|
| 230 |
+
|
| 231 |
+
abbrev mkExternLibDecl
|
| 232 |
+
(pkgName name : Name)
|
| 233 |
+
[FamilyDef (CustomData pkgName) (.str name "static") FilePath]
|
| 234 |
+
[FamilyDef (CustomData pkgName) name (ConfigTarget ExternLib.configKind)]
|
| 235 |
+
: ExternLibDecl :=
|
| 236 |
+
mkConfigDecl pkgName name ExternLib.configKind {getPath := cast (by simp)}
|
| 237 |
+
|
| 238 |
+
@[builtin_macro externLibCommand]
|
| 239 |
+
def expandExternLibCommand : Macro := fun stx => do
|
| 240 |
+
let `(externLibCommand|$(doc?)? $(attrs?)? extern_lib%$kw $spec) := stx
|
| 241 |
+
| Macro.throwErrorAt stx "ill-formed external library declaration"
|
| 242 |
+
let `(externLibDeclSpec| $nameStx $[$pkg?]? := $defn $[$wds?:whereDecls]?) := spec
|
| 243 |
+
| Macro.throwErrorAt spec "ill-formed external library signature"
|
| 244 |
+
withRef kw do
|
| 245 |
+
let attr1 ← `(Term.attrInstance| «target»)
|
| 246 |
+
let attr2 ← `(Term.attrInstance| «extern_lib»)
|
| 247 |
+
let attrs := #[attr1, attr2] ++ expandAttrs attrs?
|
| 248 |
+
let id := expandIdentOrStrAsIdent nameStx
|
| 249 |
+
let pkgName := mkIdentFrom kw (packageDeclName.str "name")
|
| 250 |
+
let targetId := mkIdentFrom id <| id.getId.modifyBase (· ++ `static)
|
| 251 |
+
let name := Name.quoteFrom id id.getId
|
| 252 |
+
let kind := Name.quoteFrom kw ExternLib.configKind
|
| 253 |
+
`(target $targetId:ident $[$pkg?]? : FilePath := $defn $[$wds?:whereDecls]?
|
| 254 |
+
family_def $id : CustomOut ($pkgName, $name) := ConfigTarget $kind
|
| 255 |
+
$[$doc?:docComment]? def $id : ExternLibDecl :=
|
| 256 |
+
Lake.DSL.mkExternLibDecl $pkgName $name
|
| 257 |
+
@[$attrs,*] def configDecl : ConfigDecl := $(id).toConfigDecl)
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/DSL/VerLit.lean
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Eval
|
| 8 |
+
import Lake.Util.Version
|
| 9 |
+
import Lake.DSL.Syntax
|
| 10 |
+
|
| 11 |
+
open Lean Elab Term Meta
|
| 12 |
+
|
| 13 |
+
/-! # Version Literals
|
| 14 |
+
|
| 15 |
+
Defines the `v!"<ver>"` syntax for version literals.
|
| 16 |
+
The elaborator attempts to synthesize an instance of `DecodeVersion` for the
|
| 17 |
+
expected type and then applies it to the string literal.
|
| 18 |
+
-/
|
| 19 |
+
|
| 20 |
+
namespace Lake
|
| 21 |
+
|
| 22 |
+
instance : ToExpr SemVerCore where
|
| 23 |
+
toExpr ver := mkAppN (mkConst ``SemVerCore.mk)
|
| 24 |
+
#[toExpr ver.major, toExpr ver.minor, toExpr ver.patch]
|
| 25 |
+
toTypeExpr := mkConst ``SemVerCore
|
| 26 |
+
|
| 27 |
+
instance : ToExpr StdVer where
|
| 28 |
+
toExpr ver := mkAppN (mkConst ``StdVer.mk)
|
| 29 |
+
#[toExpr ver.toSemVerCore, toExpr ver.specialDescr]
|
| 30 |
+
toTypeExpr := mkConst ``StdVer
|
| 31 |
+
|
| 32 |
+
private def toResultExpr [ToExpr α] (x : Except String α) : Except String Expr :=
|
| 33 |
+
Functor.map toExpr x
|
| 34 |
+
|
| 35 |
+
@[builtin_term_elab verLit]
|
| 36 |
+
def elabVerLit : TermElab := fun stx expectedType? => do
|
| 37 |
+
let `(v!$v) := stx | throwUnsupportedSyntax
|
| 38 |
+
tryPostponeIfNoneOrMVar expectedType?
|
| 39 |
+
let some expectedType := expectedType?
|
| 40 |
+
| throwError "expected type is not known"
|
| 41 |
+
let ofVerT? ← mkAppM ``Except #[mkConst ``String, expectedType]
|
| 42 |
+
let ofVerE ← elabTermEnsuringType (← ``(decodeVersion s!$v)) ofVerT?
|
| 43 |
+
let resT ← mkAppM ``Except #[mkConst ``String, mkConst ``Expr]
|
| 44 |
+
let resE ← mkAppM ``toResultExpr #[ofVerE]
|
| 45 |
+
match (← unsafe evalExpr (Except String Expr) resT resE) with
|
| 46 |
+
| .ok ver => return ver
|
| 47 |
+
| .error e => throwError e
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Config.lean
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Data.Name
|
| 8 |
+
import Lean.Data.Options
|
| 9 |
+
import Lake.Config.Env
|
| 10 |
+
import Lake.Load.Manifest
|
| 11 |
+
import Lake.Util.FilePath
|
| 12 |
+
|
| 13 |
+
namespace Lake
|
| 14 |
+
open System Lean
|
| 15 |
+
|
| 16 |
+
/-- Context for loading a Lake configuration. -/
|
| 17 |
+
structure LoadConfig where
|
| 18 |
+
/-- The Lake environment of the load process. -/
|
| 19 |
+
lakeEnv : Lake.Env
|
| 20 |
+
/--
|
| 21 |
+
The CLI arguments Lake was run with.
|
| 22 |
+
Used to perform a restart of Lake on a toolchain update.
|
| 23 |
+
A value of `none` means that Lake is not restartable via the CLI.
|
| 24 |
+
-/
|
| 25 |
+
lakeArgs? : Option (Array String) := none
|
| 26 |
+
/-- The absolute path to the root directory of the Lake workspace. -/
|
| 27 |
+
wsDir : FilePath
|
| 28 |
+
/-- The loaded package's directory (relative to the workspace directory). -/
|
| 29 |
+
relPkgDir : FilePath := "."
|
| 30 |
+
/-- The absolute path to the loaded package's directory. -/
|
| 31 |
+
pkgDir : FilePath := wsDir / relPkgDir
|
| 32 |
+
/-- The package's Lake configuration file (relative to its directory). -/
|
| 33 |
+
relConfigFile : FilePath := defaultConfigFile
|
| 34 |
+
/-- The full path to the loaded package's Lake configuration file. -/
|
| 35 |
+
configFile : FilePath := pkgDir / relConfigFile
|
| 36 |
+
/-- Additional package overrides for this workspace load. -/
|
| 37 |
+
packageOverrides : Array PackageEntry := #[]
|
| 38 |
+
/-- A set of key-value Lake configuration options (i.e., `-K` settings). -/
|
| 39 |
+
lakeOpts : NameMap String := {}
|
| 40 |
+
/-- The Lean options with which to elaborate the configuration file. -/
|
| 41 |
+
leanOpts : Options := {}
|
| 42 |
+
/-- Whether Lake should re-elaborate configuration files instead of using cached OLeans. -/
|
| 43 |
+
reconfigure : Bool := false
|
| 44 |
+
/-- Whether to update dependencies when loading the workspace. -/
|
| 45 |
+
updateDeps : Bool := false
|
| 46 |
+
/--
|
| 47 |
+
Whether to update the workspace's `lean-toolchain` when dependencies are updated.
|
| 48 |
+
If `true` and a toolchain update occurs, Lake will need to be restarted.
|
| 49 |
+
-/
|
| 50 |
+
updateToolchain : Bool := true
|
| 51 |
+
/-- The package's scope (e.g., in Reservoir). -/
|
| 52 |
+
scope : String := ""
|
| 53 |
+
/-- The URL to this package's Git remote (if any). -/
|
| 54 |
+
remoteUrl : String := ""
|
| 55 |
+
|
| 56 |
+
/-- The package's Lake directory (for Lake temporary files). -/
|
| 57 |
+
@[inline] def LoadConfig.lakeDir (cfg : LoadConfig) : FilePath :=
|
| 58 |
+
cfg.pkgDir / defaultLakeDir
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Lean.lean
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.Load.Lean.Elab
|
| 8 |
+
import Lake.Load.Lean.Eval
|
| 9 |
+
|
| 10 |
+
/-! # Lean Loader
|
| 11 |
+
|
| 12 |
+
This module contains the main definitions to load a package from a
|
| 13 |
+
Lake configuration file written in Lake's Lean DSL.
|
| 14 |
+
-/
|
| 15 |
+
|
| 16 |
+
open Lean
|
| 17 |
+
|
| 18 |
+
namespace Lake
|
| 19 |
+
|
| 20 |
+
/--
|
| 21 |
+
Elaborate a Lean configuration file into a `Package`.
|
| 22 |
+
The resulting package does not yet include any dependencies.
|
| 23 |
+
-/
|
| 24 |
+
def loadLeanConfig (cfg : LoadConfig)
|
| 25 |
+
: LogIO (Package × Environment) := do
|
| 26 |
+
let configEnv ← importConfigFile cfg
|
| 27 |
+
let {name, config} ← IO.ofExcept <| PackageDecl.loadFromEnv configEnv cfg.leanOpts
|
| 28 |
+
let pkg : Package := {
|
| 29 |
+
name, config
|
| 30 |
+
dir := cfg.pkgDir
|
| 31 |
+
relDir := cfg.relPkgDir
|
| 32 |
+
configFile := cfg.configFile
|
| 33 |
+
relConfigFile := cfg.relConfigFile
|
| 34 |
+
scope := cfg.scope
|
| 35 |
+
remoteUrl := cfg.remoteUrl
|
| 36 |
+
}
|
| 37 |
+
return (← pkg.loadFromEnv configEnv cfg.leanOpts, configEnv)
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Lean/Elab.lean
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Frontend
|
| 8 |
+
import Lake.DSL.Extensions
|
| 9 |
+
import Lake.DSL.Attributes
|
| 10 |
+
import Lake.Load.Config
|
| 11 |
+
import Lake.Build.Trace
|
| 12 |
+
import Lake.Util.Log
|
| 13 |
+
|
| 14 |
+
/-! # Lean Configuration Elaborator
|
| 15 |
+
|
| 16 |
+
This module contains the definitions to elaborate a Lake configuration file.
|
| 17 |
+
-/
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
open System Lean
|
| 21 |
+
|
| 22 |
+
namespace Lake
|
| 23 |
+
|
| 24 |
+
deriving instance BEq, Hashable for Import
|
| 25 |
+
|
| 26 |
+
/- Cache for the imported header environment of Lake configuration files. -/
|
| 27 |
+
builtin_initialize importEnvCache : IO.Ref (Std.HashMap (Array Import) Environment) ← IO.mkRef {}
|
| 28 |
+
|
| 29 |
+
/-- Like `importModules`, but fetch the resulting import state from the cache if possible. -/
|
| 30 |
+
def importModulesUsingCache (imports : Array Import) (opts : Options) (trustLevel : UInt32) : IO Environment := do
|
| 31 |
+
if let some env := (← importEnvCache.get)[imports]? then
|
| 32 |
+
return env
|
| 33 |
+
let env ← importModules (loadExts := true) imports opts trustLevel
|
| 34 |
+
importEnvCache.modify (·.insert imports env)
|
| 35 |
+
return env
|
| 36 |
+
|
| 37 |
+
/-- Like `Lean.Elab.processHeader`, but using `importEnvCache`. -/
|
| 38 |
+
def processHeader (header : TSyntax ``Parser.Module.header) (opts : Options)
|
| 39 |
+
(inputCtx : Parser.InputContext) : StateT MessageLog IO Environment := do
|
| 40 |
+
try
|
| 41 |
+
let imports := Elab.headerToImports header
|
| 42 |
+
importModulesUsingCache imports opts 1024
|
| 43 |
+
catch e =>
|
| 44 |
+
let pos := inputCtx.fileMap.toPosition <| header.raw.getPos?.getD 0
|
| 45 |
+
modify (·.add { fileName := inputCtx.fileName, data := toString e, pos })
|
| 46 |
+
mkEmptyEnvironment
|
| 47 |
+
|
| 48 |
+
/-- Main module `Name` of a Lake configuration file. -/
|
| 49 |
+
def configModuleName : Name := `lakefile
|
| 50 |
+
|
| 51 |
+
/-- Elaborate `configFile` with the given package directory and options. -/
|
| 52 |
+
def elabConfigFile (pkgDir : FilePath) (lakeOpts : NameMap String)
|
| 53 |
+
(leanOpts := Options.empty) (configFile := pkgDir / defaultLeanConfigFile) : LogIO Environment := do
|
| 54 |
+
|
| 55 |
+
-- Read file and initialize environment
|
| 56 |
+
let input ← IO.FS.readFile configFile
|
| 57 |
+
let inputCtx := Parser.mkInputContext input configFile.toString
|
| 58 |
+
let (header, parserState, messages) ← Parser.parseHeader inputCtx
|
| 59 |
+
let (env, messages) ← processHeader header leanOpts inputCtx messages
|
| 60 |
+
let env := env.setMainModule configModuleName
|
| 61 |
+
|
| 62 |
+
-- Configure extensions
|
| 63 |
+
let env := dirExt.setState env pkgDir
|
| 64 |
+
let env := optsExt.setState env lakeOpts
|
| 65 |
+
|
| 66 |
+
-- Elaborate File
|
| 67 |
+
let commandState := Elab.Command.mkState env messages leanOpts
|
| 68 |
+
let s ← Elab.IO.processCommands inputCtx parserState commandState
|
| 69 |
+
|
| 70 |
+
-- Log messages
|
| 71 |
+
s.commandState.messages.forM (logMessage ·)
|
| 72 |
+
|
| 73 |
+
-- Check result
|
| 74 |
+
if s.commandState.messages.hasErrors then
|
| 75 |
+
error s!"{configFile}: package configuration has errors"
|
| 76 |
+
else
|
| 77 |
+
return s.commandState.env
|
| 78 |
+
|
| 79 |
+
/--
|
| 80 |
+
`Lean.Kernel.Environment.add` is now private, this is an exported helper wrapping it for
|
| 81 |
+
`Lean.Environment`.
|
| 82 |
+
-/
|
| 83 |
+
@[extern "lake_environment_add"]
|
| 84 |
+
private opaque addToEnv (env : Environment) (_ : ConstantInfo) : Environment
|
| 85 |
+
|
| 86 |
+
/--
|
| 87 |
+
Import a configuration `.olean` (e.g., `lakefile.olean`).
|
| 88 |
+
Auxiliary definition for `importConfigFile`.
|
| 89 |
+
-/
|
| 90 |
+
def importConfigFileCore (olean : FilePath) (leanOpts : Options) : IO Environment := do
|
| 91 |
+
/-
|
| 92 |
+
We could import the olean together with its imports using one
|
| 93 |
+
`importModules` call, but that would mean we pay for a full
|
| 94 |
+
`finalizeImports` each time, which is linear in the number of imported
|
| 95 |
+
constants and extension entries (in fact, it is paid two more times: when
|
| 96 |
+
marking the `Environment` object as multi-threaded, and when releasing
|
| 97 |
+
it). As most lakefiles use the same set of imports, we instead construct
|
| 98 |
+
an `Environment` for it only once, and then apply the lakefile contents
|
| 99 |
+
on top of it like the elaborator would. Thus the non-shared part of the
|
| 100 |
+
`Environment` is very small.
|
| 101 |
+
-/
|
| 102 |
+
let (mod, _) ← readModuleData olean
|
| 103 |
+
let env ← importModulesUsingCache mod.imports leanOpts 1024
|
| 104 |
+
-- Apply constants (does not go through the kernel, so order is irrelevant)
|
| 105 |
+
let env := mod.constants.foldl addToEnv env
|
| 106 |
+
/-
|
| 107 |
+
Below, we pass module environment extension entries to
|
| 108 |
+
`PersistentEnvExtension.addEntryFn` - but for an extension of
|
| 109 |
+
non-erased type `PersistentEnvExtension α β σ`, the former are of type
|
| 110 |
+
`α` while `addEntryFn` expects `β`s (the type-erased
|
| 111 |
+
`persistentEnvExtensionsRef` ought to be `unsafe` to prevent this from
|
| 112 |
+
simply compiling but it isn't right now). Fortunately, all extensions
|
| 113 |
+
relevant for workspace loading, which we filter for here, have `α = β`;
|
| 114 |
+
entries for any other extensions can safely be ignored.
|
| 115 |
+
-/
|
| 116 |
+
let extDescrs ← persistentEnvExtensionsRef.get
|
| 117 |
+
let extNameIdx ← mkExtNameMap 0
|
| 118 |
+
let env := mod.entries.foldl (init := env) fun env (extName, ents) =>
|
| 119 |
+
if lakeExts.contains extName then
|
| 120 |
+
match extNameIdx[extName]? with
|
| 121 |
+
| some entryIdx => ents.foldl extDescrs[entryIdx]!.addEntry env
|
| 122 |
+
| none => env
|
| 123 |
+
else
|
| 124 |
+
env
|
| 125 |
+
return env
|
| 126 |
+
where
|
| 127 |
+
lakeExts :=
|
| 128 |
+
NameSet.empty
|
| 129 |
+
-- Lake Persistent Extensions
|
| 130 |
+
|>.insert ``packageAttr
|
| 131 |
+
|>.insert ``packageDepAttr
|
| 132 |
+
|>.insert ``postUpdateAttr
|
| 133 |
+
|>.insert ``scriptAttr
|
| 134 |
+
|>.insert ``defaultScriptAttr
|
| 135 |
+
|>.insert ``leanLibAttr
|
| 136 |
+
|>.insert ``leanExeAttr
|
| 137 |
+
|>.insert ``externLibAttr
|
| 138 |
+
|>.insert ``targetAttr
|
| 139 |
+
|>.insert ``defaultTargetAttr
|
| 140 |
+
|>.insert ``testDriverAttr
|
| 141 |
+
|>.insert ``lintDriverAttr
|
| 142 |
+
|>.insert ``moduleFacetAttr
|
| 143 |
+
|>.insert ``packageFacetAttr
|
| 144 |
+
|>.insert ``libraryFacetAttr
|
| 145 |
+
-- Docstring Extension (e.g., for scripts)
|
| 146 |
+
|>.insert ``docStringExt
|
| 147 |
+
-- IR Extension (for constant evaluation)
|
| 148 |
+
|>.insert ``IR.declMapExt
|
| 149 |
+
|
| 150 |
+
structure ConfigTrace where
|
| 151 |
+
platform : String
|
| 152 |
+
leanHash : String
|
| 153 |
+
configHash : Hash
|
| 154 |
+
options : NameMap String
|
| 155 |
+
deriving ToJson, FromJson
|
| 156 |
+
|
| 157 |
+
/--
|
| 158 |
+
Import the `.olean` for the configuration file if `reconfigure` is not set and
|
| 159 |
+
an up-to-date one exists (i.e., one with matching configuration and on the same
|
| 160 |
+
toolchain). Otherwise, elaborate the configuration and save it to the `.olean`.
|
| 161 |
+
-/
|
| 162 |
+
def importConfigFile (cfg : LoadConfig) : LogIO Environment := do
|
| 163 |
+
let some configName := FilePath.mk <$> cfg.configFile.fileName
|
| 164 |
+
| error "invalid configuration file name"
|
| 165 |
+
let olean := cfg.lakeDir / configName.withExtension "olean"
|
| 166 |
+
let traceFile := cfg.lakeDir / configName.withExtension "olean.trace"
|
| 167 |
+
let lockFile := cfg.lakeDir / configName.withExtension "olean.lock"
|
| 168 |
+
/-
|
| 169 |
+
Remark:
|
| 170 |
+
To prevent race conditions between the `.olean` and its trace file
|
| 171 |
+
(i.e., one process writes a new configuration while another reads an old one
|
| 172 |
+
and vice versa), Lake performs file locking to ensure exclusive access.
|
| 173 |
+
|
| 174 |
+
To check if the trace is up-to-date, Lake opens a read-only handle on the
|
| 175 |
+
trace file and acquires a shared lock on it. The lock is held while the
|
| 176 |
+
trace is read and compared to the expected value. If it matches, the olean is
|
| 177 |
+
imported and the (shared) lock is then released.
|
| 178 |
+
|
| 179 |
+
If the trace is out-of-date, Lake upgrades the trace to read-write handle
|
| 180 |
+
with an exclusive lock. Lake does this by first acquiring a exclusive lock to
|
| 181 |
+
configuration's lock file (i.e. `olean.lock`). While holding this lock, Lake
|
| 182 |
+
releases the shared lock on the trace, re-opens the trace with a read-write
|
| 183 |
+
handle, and acquires an exclusive lock on the trace. It then releases its
|
| 184 |
+
lock on the lock file. writes the new lock data.
|
| 185 |
+
-/
|
| 186 |
+
let acquireTrace h : IO IO.FS.Handle := id do
|
| 187 |
+
let hLock ← IO.FS.Handle.mk lockFile .write
|
| 188 |
+
/-
|
| 189 |
+
Remark:
|
| 190 |
+
We do not wait for a lock here, because that can lead to deadlock.
|
| 191 |
+
|
| 192 |
+
This is because we are already holding a shared lock on the trace.
|
| 193 |
+
If multiple process race for this lock, one will get it and then
|
| 194 |
+
wait for an exclusive lock one the trace file, but be blocked by the
|
| 195 |
+
other process with a shared lock waiting on this file.
|
| 196 |
+
|
| 197 |
+
While there is likely a way to sequence this to avoid erroring,
|
| 198 |
+
simultaneous reconfigures are likely to produce unexpected results
|
| 199 |
+
anyway, so the error seems wise nonetheless.
|
| 200 |
+
-/
|
| 201 |
+
if (← hLock.tryLock) then
|
| 202 |
+
h.unlock
|
| 203 |
+
let h ← IO.FS.Handle.mk traceFile .readWrite
|
| 204 |
+
h.lock
|
| 205 |
+
hLock.unlock
|
| 206 |
+
return h
|
| 207 |
+
else
|
| 208 |
+
h.unlock
|
| 209 |
+
error <| s!"could not acquire an exclusive configuration lock; " ++
|
| 210 |
+
"another process may already be reconfiguring the package"
|
| 211 |
+
let configHash ← computeTextFileHash cfg.configFile
|
| 212 |
+
let elabConfig h (lakeOpts : NameMap String) : LogIO Environment := id do
|
| 213 |
+
/-
|
| 214 |
+
Remark:
|
| 215 |
+
We write the trace before elaborating the configuration file
|
| 216 |
+
to enable automatic reconfiguration on the next `lake` invocation if
|
| 217 |
+
elaboration fails. To ensure a failure triggers a reconfigure, we must also
|
| 218 |
+
remove any previous out-of-date `.olean`. Otherwise, Lake will treat the
|
| 219 |
+
older `.olean` as matching the new trace.
|
| 220 |
+
|
| 221 |
+
See the related PR and Zulip discussion for more details:
|
| 222 |
+
[leanprover/lean4#3069](https://github.com/leanprover/lean4/pull/3069).
|
| 223 |
+
-/
|
| 224 |
+
match (← IO.FS.removeFile olean |>.toBaseIO) with
|
| 225 |
+
| .ok _ | .error (.noFileOrDirectory ..) =>
|
| 226 |
+
h.putStrLn <| Json.pretty <| toJson
|
| 227 |
+
{platform := System.Platform.target, leanHash := cfg.lakeEnv.leanGithash,
|
| 228 |
+
configHash, options := lakeOpts : ConfigTrace}
|
| 229 |
+
h.truncate
|
| 230 |
+
let env ← elabConfigFile cfg.pkgDir lakeOpts cfg.leanOpts cfg.configFile
|
| 231 |
+
Lean.writeModule env olean
|
| 232 |
+
h.unlock
|
| 233 |
+
return env
|
| 234 |
+
| .error e => errorWithLog do
|
| 235 |
+
logError <| toString e
|
| 236 |
+
h.unlock
|
| 237 |
+
IO.FS.removeFile traceFile
|
| 238 |
+
let validateTrace h : LogIO Environment := id do
|
| 239 |
+
if cfg.reconfigure then
|
| 240 |
+
elabConfig (← acquireTrace h) cfg.lakeOpts
|
| 241 |
+
else
|
| 242 |
+
h.lock (exclusive := false)
|
| 243 |
+
let contents ← h.readToEnd
|
| 244 |
+
let .ok (trace : ConfigTrace) := Json.parse contents >>= fromJson?
|
| 245 |
+
| error "compiled configuration is invalid; run with '-R' to reconfigure"
|
| 246 |
+
let upToDate :=
|
| 247 |
+
(← olean.pathExists) ∧ trace.platform = System.Platform.target ∧
|
| 248 |
+
trace.leanHash = cfg.lakeEnv.leanGithash ∧ trace.configHash = configHash
|
| 249 |
+
if upToDate then
|
| 250 |
+
let env ← importConfigFileCore olean cfg.leanOpts
|
| 251 |
+
h.unlock
|
| 252 |
+
return env
|
| 253 |
+
else
|
| 254 |
+
elabConfig (← acquireTrace h) trace.options
|
| 255 |
+
if (← traceFile.pathExists) then
|
| 256 |
+
validateTrace <| ← IO.FS.Handle.mk traceFile .read
|
| 257 |
+
else
|
| 258 |
+
IO.FS.createDirAll cfg.lakeDir
|
| 259 |
+
match (← IO.FS.Handle.mk traceFile .writeNew |>.toBaseIO) with
|
| 260 |
+
| .ok h =>
|
| 261 |
+
h.lock; elabConfig h cfg.lakeOpts
|
| 262 |
+
| .error (.alreadyExists ..) =>
|
| 263 |
+
validateTrace <| ← IO.FS.Handle.mk traceFile .read
|
| 264 |
+
| .error e => error e.toString
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Lean/Eval.lean
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.DSL.Attributes
|
| 8 |
+
import Lake.Config.Workspace
|
| 9 |
+
import Lean.DocString
|
| 10 |
+
|
| 11 |
+
/-! # Lean Configuration Evaluator
|
| 12 |
+
|
| 13 |
+
This module contains the definitions to load Lake configuration objects from
|
| 14 |
+
a Lean environment elaborated from a Lake configuration file.
|
| 15 |
+
-/
|
| 16 |
+
|
| 17 |
+
open System Lean
|
| 18 |
+
|
| 19 |
+
namespace Lake
|
| 20 |
+
|
| 21 |
+
/-- Unsafe implementation of `evalConstCheck`. -/
|
| 22 |
+
unsafe def unsafeEvalConstCheck
|
| 23 |
+
(env : Environment) (opts : Options) (α) [TypeName α] (const : Name)
|
| 24 |
+
: Except String α :=
|
| 25 |
+
match env.find? const with
|
| 26 |
+
| none => throw s!"unknown constant '{const}'"
|
| 27 |
+
| some info =>
|
| 28 |
+
match info.type with
|
| 29 |
+
| Expr.const c _ =>
|
| 30 |
+
if c != TypeName.typeName α then
|
| 31 |
+
throwUnexpectedType
|
| 32 |
+
else
|
| 33 |
+
env.evalConst α opts const
|
| 34 |
+
| _ => throwUnexpectedType
|
| 35 |
+
where
|
| 36 |
+
throwUnexpectedType : Except String α :=
|
| 37 |
+
throw s!"unexpected type at '{const}', `{TypeName.typeName α}` expected"
|
| 38 |
+
|
| 39 |
+
/--
|
| 40 |
+
Like `Lean.Environment.evalConstCheck`,
|
| 41 |
+
but with plain universe-polymorphic `Except`.
|
| 42 |
+
-/
|
| 43 |
+
@[implemented_by unsafeEvalConstCheck]
|
| 44 |
+
opaque evalConstCheck
|
| 45 |
+
(env : Environment) (opts : Options) (α) [TypeName α] (const : Name)
|
| 46 |
+
: Except String α
|
| 47 |
+
|
| 48 |
+
/-- Construct a `DNameMap` from the declarations tagged with `attr`. -/
|
| 49 |
+
def mkTagMap
|
| 50 |
+
(env : Environment) (attr : OrderedTagAttribute)
|
| 51 |
+
[Monad m] (f : (n : Name) → m (β n))
|
| 52 |
+
: m (DNameMap β) :=
|
| 53 |
+
let entries := attr.getAllEntries env
|
| 54 |
+
entries.foldlM (init := {}) fun map declName =>
|
| 55 |
+
return map.insert declName <| ← f declName
|
| 56 |
+
|
| 57 |
+
/-- Construct a `OrdNameMap` from the declarations tagged with `attr`. -/
|
| 58 |
+
def mkOrdTagMap
|
| 59 |
+
(env : Environment) (attr : OrderedTagAttribute)
|
| 60 |
+
[Monad m] (f : (n : Name) → m β)
|
| 61 |
+
: m (OrdNameMap β) :=
|
| 62 |
+
let entries := attr.getAllEntries env
|
| 63 |
+
entries.foldlM (init := .mkEmpty entries.size) fun map declName =>
|
| 64 |
+
return map.insert declName <| ← f declName
|
| 65 |
+
|
| 66 |
+
/-- Load a `PackageDecl` from a configuration environment. -/
|
| 67 |
+
def PackageDecl.loadFromEnv
|
| 68 |
+
(env : Environment) (opts := Options.empty)
|
| 69 |
+
: Except String PackageDecl := do
|
| 70 |
+
let declName ←
|
| 71 |
+
match packageAttr.getAllEntries env |>.toList with
|
| 72 |
+
| [] => error s!"configuration file is missing a `package` declaration"
|
| 73 |
+
| [name] => pure name
|
| 74 |
+
| _ => error s!"configuration file has multiple `package` declarations"
|
| 75 |
+
evalConstCheck env opts _ declName
|
| 76 |
+
|
| 77 |
+
/--
|
| 78 |
+
Load the optional elements of a `Package` from the Lean environment.
|
| 79 |
+
This is done after loading its core configuration but before resolving
|
| 80 |
+
its dependencies.
|
| 81 |
+
-/
|
| 82 |
+
def Package.loadFromEnv
|
| 83 |
+
(self : Package) (env : Environment) (opts : Options)
|
| 84 |
+
: LogIO Package := do
|
| 85 |
+
let strName := self.name.toString (escape := false)
|
| 86 |
+
|
| 87 |
+
-- load target, script, hook, and driver configurations
|
| 88 |
+
let constTargetMap ← IO.ofExcept <| mkOrdTagMap env targetAttr fun name => do
|
| 89 |
+
evalConstCheck env opts ConfigDecl name
|
| 90 |
+
let targetDecls ← constTargetMap.toArray.mapM fun decl => do
|
| 91 |
+
if h : decl.pkg = self.name then
|
| 92 |
+
return .mk decl h
|
| 93 |
+
else
|
| 94 |
+
error s!"\
|
| 95 |
+
target '{decl.name}' was defined in package '{decl.pkg}', \
|
| 96 |
+
but registered under '{self.name}'"
|
| 97 |
+
let targetDeclMap ← targetDecls.foldlM (init := {}) fun m decl => do
|
| 98 |
+
if let some orig := m.find? decl.name then
|
| 99 |
+
error s!"\
|
| 100 |
+
{self.name}: target '{decl.name}' was already defined as a '{orig.kind}', \
|
| 101 |
+
but then redefined as a '{decl.kind}'"
|
| 102 |
+
else
|
| 103 |
+
return m.insert decl.name (.mk decl rfl)
|
| 104 |
+
let defaultTargets ← defaultTargetAttr.getAllEntries env |>.mapM fun name =>
|
| 105 |
+
if let some decl := constTargetMap.find? name then pure decl.name else
|
| 106 |
+
error s!"{self.name}: package is missing target '{name}' marked as a default"
|
| 107 |
+
let scripts ← mkTagMap env scriptAttr fun scriptName => do
|
| 108 |
+
let name := strName ++ "/" ++ scriptName.toString (escape := false)
|
| 109 |
+
let fn ← IO.ofExcept <| evalConstCheck env opts ScriptFn scriptName
|
| 110 |
+
return {name, fn, doc? := ← findDocString? env scriptName : Script}
|
| 111 |
+
let defaultScripts ← defaultScriptAttr.getAllEntries env |>.mapM fun name =>
|
| 112 |
+
if let some script := scripts.find? name then pure script else
|
| 113 |
+
error s!"{self.name}: package is missing script '{name}' marked as a default"
|
| 114 |
+
let postUpdateHooks ← postUpdateAttr.getAllEntries env |>.mapM fun name =>
|
| 115 |
+
match evalConstCheck env opts PostUpdateHookDecl name with
|
| 116 |
+
| .ok decl =>
|
| 117 |
+
if h : decl.pkg = self.name then
|
| 118 |
+
return OpaquePostUpdateHook.mk ⟨h ▸ decl.fn⟩
|
| 119 |
+
else
|
| 120 |
+
error s!"post-update hook was defined in '{decl.pkg}', but was registered in '{self.name}'"
|
| 121 |
+
| .error e => error e
|
| 122 |
+
let depConfigs ← IO.ofExcept <| packageDepAttr.getAllEntries env |>.mapM fun name =>
|
| 123 |
+
evalConstCheck env opts Dependency name
|
| 124 |
+
let testDrivers ← testDriverAttr.getAllEntries env |>.mapM fun name =>
|
| 125 |
+
if let some decl := constTargetMap.find? name then
|
| 126 |
+
pure decl.name
|
| 127 |
+
else if scripts.contains name then
|
| 128 |
+
pure name
|
| 129 |
+
else
|
| 130 |
+
error s!"{self.name}: package is missing script or target '{name}' marked as a test driver"
|
| 131 |
+
let testDriver ←
|
| 132 |
+
if testDrivers.size > 1 then
|
| 133 |
+
error s!"{self.name}: only one script, executable, or library can be tagged @[test_driver]"
|
| 134 |
+
else if h : testDrivers.size > 0 then
|
| 135 |
+
if self.config.testDriver.isEmpty then
|
| 136 |
+
pure (testDrivers[0]'h |>.toString)
|
| 137 |
+
else
|
| 138 |
+
error s!"{self.name}: cannot both set testDriver and use @[test_driver]"
|
| 139 |
+
else
|
| 140 |
+
pure self.config.testDriver
|
| 141 |
+
let lintDrivers ← lintDriverAttr.getAllEntries env |>.mapM fun name =>
|
| 142 |
+
if let some decl := constTargetMap.find? name then
|
| 143 |
+
pure decl.name
|
| 144 |
+
else if scripts.contains name then
|
| 145 |
+
pure name
|
| 146 |
+
else
|
| 147 |
+
error s!"{self.name}: package is missing script or target '{name}' marked as a lint driver"
|
| 148 |
+
let lintDriver ←
|
| 149 |
+
if lintDrivers.size > 1 then
|
| 150 |
+
error s!"{self.name}: only one script or executable can be tagged @[lint_driver]"
|
| 151 |
+
else if h : lintDrivers.size > 0 then
|
| 152 |
+
if self.config.lintDriver.isEmpty then
|
| 153 |
+
pure (lintDrivers[0]'h |>.toString)
|
| 154 |
+
else
|
| 155 |
+
error s!"{self.name}: cannot both set lintDriver and use @[lint_driver]"
|
| 156 |
+
else
|
| 157 |
+
pure self.config.lintDriver
|
| 158 |
+
|
| 159 |
+
-- Deprecation warnings
|
| 160 |
+
unless self.config.manifestFile.isNone do
|
| 161 |
+
logWarning s!"{self.name}: package configuration option 'manifestFile' is deprecated"
|
| 162 |
+
|
| 163 |
+
-- Fill in the Package
|
| 164 |
+
return {self with
|
| 165 |
+
depConfigs, targetDecls, targetDeclMap
|
| 166 |
+
defaultTargets, scripts, defaultScripts
|
| 167 |
+
testDriver, lintDriver, postUpdateHooks
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
/--
|
| 171 |
+
Load module/package facets into a `Workspace` from a configuration environment.
|
| 172 |
+
-/
|
| 173 |
+
def Workspace.addFacetsFromEnv
|
| 174 |
+
(env : Environment) (opts : Options) (self : Workspace)
|
| 175 |
+
: Except String Workspace := do
|
| 176 |
+
let mut ws := self
|
| 177 |
+
for name in moduleFacetAttr.getAllEntries env do
|
| 178 |
+
let decl ← evalConstCheck env opts ModuleFacetDecl name
|
| 179 |
+
ws := ws.addModuleFacetConfig decl.config
|
| 180 |
+
for name in packageFacetAttr.getAllEntries env do
|
| 181 |
+
let decl ← evalConstCheck env opts PackageFacetDecl name
|
| 182 |
+
ws := ws.addPackageFacetConfig decl.config
|
| 183 |
+
for name in libraryFacetAttr.getAllEntries env do
|
| 184 |
+
let decl ← evalConstCheck env opts LibraryFacetDecl name
|
| 185 |
+
ws := ws.addLibraryFacetConfig decl.config
|
| 186 |
+
return ws
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Manifest.lean
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone, Gabriel Ebner
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.Util.Log
|
| 8 |
+
import Lake.Util.Name
|
| 9 |
+
import Lake.Util.FilePath
|
| 10 |
+
import Lake.Util.JsonObject
|
| 11 |
+
import Lake.Util.Version
|
| 12 |
+
import Lake.Config.Defaults
|
| 13 |
+
|
| 14 |
+
open System Lean
|
| 15 |
+
|
| 16 |
+
/-! # Lake Manifest
|
| 17 |
+
The data structure of a Lake manifest and the definitions needed
|
| 18 |
+
to create, modify, serialize, and deserialize it.
|
| 19 |
+
-/
|
| 20 |
+
|
| 21 |
+
namespace Lake
|
| 22 |
+
|
| 23 |
+
/--
|
| 24 |
+
The current version of the manifest format.
|
| 25 |
+
|
| 26 |
+
Three-part semantic versions were introduced in `v1.0.0`.
|
| 27 |
+
Major version increments indicate breaking changes
|
| 28 |
+
(e.g., new required fields and semantic changes to existing fields).
|
| 29 |
+
Minor version increments (after `0.x`) indicate backwards-compatible extensions
|
| 30 |
+
(e.g., adding optional fields, removing fields).
|
| 31 |
+
|
| 32 |
+
Lake supports reading manifests with versions that have `-` suffixes.
|
| 33 |
+
When checking for version compatibility, Lake expects a manifest with version
|
| 34 |
+
`x.y.z-foo` to have all the features of the official manifest version `x.y.z`.
|
| 35 |
+
That is, Lake ignores the `-` suffix.
|
| 36 |
+
|
| 37 |
+
**VERSION HISTORY**
|
| 38 |
+
|
| 39 |
+
**v0.x.0** (versioned by a natural number)
|
| 40 |
+
- `1`: First version
|
| 41 |
+
- `2`: Adds optional `inputRev` package entry field
|
| 42 |
+
- `3`: Changes entry to inductive (with `path`/`git`)
|
| 43 |
+
- `4`: Adds required `packagesDir` manifest field
|
| 44 |
+
- `5`: Adds optional `inherited` package entry field (and removed `opts`)
|
| 45 |
+
- `6`: Adds optional package root `name` manifest field
|
| 46 |
+
- `7`: `type` refactor, custom to/fromJson
|
| 47 |
+
|
| 48 |
+
**v1.x.x** (versioned by a string)
|
| 49 |
+
- `"1.0.0"`: Switches to a semantic versioning scheme
|
| 50 |
+
- `"1.1.0"`: Add optional `scope` package entry field
|
| 51 |
+
-/
|
| 52 |
+
@[inline] def Manifest.version : StdVer := {major := 1, minor := 1}
|
| 53 |
+
|
| 54 |
+
/-- Manifest version `0.6.0` package entry. For backwards compatibility. -/
|
| 55 |
+
inductive PackageEntryV6
|
| 56 |
+
| path (name : Name) (opts : NameMap String) (inherited : Bool) (dir : FilePath)
|
| 57 |
+
| git (name : Name) (opts : NameMap String) (inherited : Bool) (url : String) (rev : String)
|
| 58 |
+
(inputRev? : Option String) (subDir? : Option FilePath)
|
| 59 |
+
deriving FromJson, ToJson, Inhabited
|
| 60 |
+
|
| 61 |
+
/--
|
| 62 |
+
The package source for an entry in the manifest.
|
| 63 |
+
Describes exactly how Lake should materialize the package.
|
| 64 |
+
-/
|
| 65 |
+
inductive PackageEntrySrc
|
| 66 |
+
/--
|
| 67 |
+
A local filesystem package. `dir` is relative to the package directory
|
| 68 |
+
of the package containing the manifest.
|
| 69 |
+
-/
|
| 70 |
+
| path
|
| 71 |
+
(dir : FilePath)
|
| 72 |
+
/-- A remote Git package. -/
|
| 73 |
+
| git
|
| 74 |
+
(url : String)
|
| 75 |
+
(rev : String)
|
| 76 |
+
(inputRev? : Option String)
|
| 77 |
+
(subDir? : Option FilePath)
|
| 78 |
+
deriving Inhabited
|
| 79 |
+
|
| 80 |
+
/-- An entry for a package stored in the manifest. -/
|
| 81 |
+
structure PackageEntry where
|
| 82 |
+
name : Name
|
| 83 |
+
scope : String := ""
|
| 84 |
+
inherited : Bool
|
| 85 |
+
configFile : FilePath := defaultConfigFile
|
| 86 |
+
manifestFile? : Option FilePath := none
|
| 87 |
+
src : PackageEntrySrc
|
| 88 |
+
deriving Inhabited
|
| 89 |
+
|
| 90 |
+
namespace PackageEntry
|
| 91 |
+
|
| 92 |
+
protected def toJson (entry : PackageEntry) : Json :=
|
| 93 |
+
let fields := [
|
| 94 |
+
("name", toJson entry.name),
|
| 95 |
+
("scope", toJson entry.scope),
|
| 96 |
+
("configFile" , toJson entry.configFile),
|
| 97 |
+
("manifestFile", toJson entry.manifestFile?),
|
| 98 |
+
("inherited", toJson entry.inherited),
|
| 99 |
+
]
|
| 100 |
+
let fields :=
|
| 101 |
+
match entry.src with
|
| 102 |
+
| .path dir =>
|
| 103 |
+
("type", "path") :: fields.append [
|
| 104 |
+
("dir", toJson dir),
|
| 105 |
+
]
|
| 106 |
+
| .git url rev inputRev? subDir? =>
|
| 107 |
+
("type", "git") :: fields.append [
|
| 108 |
+
("url", toJson url),
|
| 109 |
+
("rev", toJson rev),
|
| 110 |
+
("inputRev", toJson inputRev?),
|
| 111 |
+
("subDir", toJson subDir?),
|
| 112 |
+
]
|
| 113 |
+
Json.mkObj fields
|
| 114 |
+
|
| 115 |
+
instance : ToJson PackageEntry := ⟨PackageEntry.toJson⟩
|
| 116 |
+
|
| 117 |
+
protected def fromJson? (json : Json) : Except String PackageEntry := do
|
| 118 |
+
let obj ← JsonObject.fromJson? json |>.mapError (s!"package entry: {·}")
|
| 119 |
+
let name ← obj.get "name" |>.mapError (s!"package entry: {·}")
|
| 120 |
+
let scope ← obj.getD "scope" ""
|
| 121 |
+
try
|
| 122 |
+
let type ← obj.get "type"
|
| 123 |
+
let inherited ← obj.get "inherited"
|
| 124 |
+
let configFile ← obj.getD "configFile" defaultConfigFile
|
| 125 |
+
let manifestFile ← obj.getD "manifestFile" defaultManifestFile
|
| 126 |
+
let src : PackageEntrySrc ← id do
|
| 127 |
+
match type with
|
| 128 |
+
| "path" =>
|
| 129 |
+
let dir ← obj.get "dir"
|
| 130 |
+
return .path dir
|
| 131 |
+
| "git" =>
|
| 132 |
+
let url ← obj.get "url"
|
| 133 |
+
let rev ← obj.get "rev"
|
| 134 |
+
let inputRev? ← obj.get? "inputRev"
|
| 135 |
+
let subDir? ← obj.get? "subDir"
|
| 136 |
+
return .git url rev inputRev? subDir?
|
| 137 |
+
| _ =>
|
| 138 |
+
throw s!"unknown package entry type '{type}'"
|
| 139 |
+
return {
|
| 140 |
+
name, scope, inherited,
|
| 141 |
+
configFile, manifestFile? := manifestFile, src
|
| 142 |
+
: PackageEntry
|
| 143 |
+
}
|
| 144 |
+
catch e =>
|
| 145 |
+
throw s!"package entry '{name}': {e}"
|
| 146 |
+
|
| 147 |
+
instance : FromJson PackageEntry := ⟨PackageEntry.fromJson?⟩
|
| 148 |
+
|
| 149 |
+
@[inline] def setInherited (entry : PackageEntry) : PackageEntry :=
|
| 150 |
+
{entry with inherited := true}
|
| 151 |
+
|
| 152 |
+
@[inline] def setConfigFile (path : FilePath) (entry : PackageEntry) : PackageEntry :=
|
| 153 |
+
{entry with configFile := path}
|
| 154 |
+
|
| 155 |
+
@[inline] def setManifestFile (path? : Option FilePath) (entry : PackageEntry) : PackageEntry :=
|
| 156 |
+
{entry with manifestFile? := path?}
|
| 157 |
+
|
| 158 |
+
@[inline] def inDirectory (pkgDir : FilePath) (entry : PackageEntry) : PackageEntry :=
|
| 159 |
+
{entry with src := match entry.src with | .path dir => .path (pkgDir / dir) | s => s}
|
| 160 |
+
|
| 161 |
+
def ofV6 : PackageEntryV6 → PackageEntry
|
| 162 |
+
| .path name _opts inherited dir =>
|
| 163 |
+
{name, inherited, src := .path dir}
|
| 164 |
+
| .git name _opts inherited url rev inputRev? subDir? =>
|
| 165 |
+
{name, inherited, src := .git url rev inputRev? subDir?}
|
| 166 |
+
|
| 167 |
+
end PackageEntry
|
| 168 |
+
|
| 169 |
+
/-- Manifest data structure that is serialized to the file. -/
|
| 170 |
+
structure Manifest where
|
| 171 |
+
name : Name
|
| 172 |
+
lakeDir : FilePath
|
| 173 |
+
packagesDir? : Option FilePath := none
|
| 174 |
+
packages : Array PackageEntry := #[]
|
| 175 |
+
|
| 176 |
+
namespace Manifest
|
| 177 |
+
|
| 178 |
+
/-- Add a package entry to the end of a manifest. -/
|
| 179 |
+
def addPackage (entry : PackageEntry) (self : Manifest) : Manifest :=
|
| 180 |
+
{self with packages := self.packages.push entry}
|
| 181 |
+
|
| 182 |
+
protected def toJson (self : Manifest) : Json :=
|
| 183 |
+
Json.mkObj [
|
| 184 |
+
("version", toJson version),
|
| 185 |
+
("name", toJson self.name),
|
| 186 |
+
("lakeDir", toJson self.lakeDir),
|
| 187 |
+
("packagesDir", toJson self.packagesDir?),
|
| 188 |
+
("packages", toJson self.packages),
|
| 189 |
+
]
|
| 190 |
+
|
| 191 |
+
instance : ToJson Manifest := ⟨Manifest.toJson⟩
|
| 192 |
+
|
| 193 |
+
def getVersion (obj : JsonObject) : Except String SemVerCore := do
|
| 194 |
+
let ver : Json ← obj.get "version" <|> obj.get "schemaVersion"
|
| 195 |
+
let ver : SemVerCore ←
|
| 196 |
+
match ver with
|
| 197 |
+
| (n : Nat) => pure {minor := n}
|
| 198 |
+
| (s : String) => StdVer.parse s
|
| 199 |
+
| ver => throw s!"invalid version '{ver}'; \
|
| 200 |
+
you may need to update your 'lean-toolchain'"
|
| 201 |
+
if ver.major > 1 then
|
| 202 |
+
throw s!"schema version '{ver}' is of a higher major version than this \
|
| 203 |
+
Lake's '{Manifest.version}'; you may need to update your 'lean-toolchain'"
|
| 204 |
+
else if ver < {minor := 5} then
|
| 205 |
+
throw s!"incompatible manifest version '{ver}'"
|
| 206 |
+
else
|
| 207 |
+
return ver
|
| 208 |
+
|
| 209 |
+
def getPackages (ver : StdVer) (obj : JsonObject) : Except String (Array PackageEntry) := do
|
| 210 |
+
if ver < {minor := 7} then
|
| 211 |
+
(·.map PackageEntry.ofV6) <$> obj.getD "packages" #[]
|
| 212 |
+
else
|
| 213 |
+
obj.getD "packages" #[]
|
| 214 |
+
|
| 215 |
+
protected def fromJson? (json : Json) : Except String Manifest := do
|
| 216 |
+
let obj ← JsonObject.fromJson? json
|
| 217 |
+
let ver ← getVersion obj
|
| 218 |
+
let name ← obj.getD "name" Name.anonymous
|
| 219 |
+
let lakeDir ← obj.getD "lakeDir" defaultLakeDir
|
| 220 |
+
let packagesDir? ← obj.get? "packagesDir"
|
| 221 |
+
let packages ← getPackages ver obj
|
| 222 |
+
return {name, lakeDir, packagesDir?, packages}
|
| 223 |
+
|
| 224 |
+
instance : FromJson Manifest := ⟨Manifest.fromJson?⟩
|
| 225 |
+
|
| 226 |
+
/-- Parse a `Manifest` from a string. -/
|
| 227 |
+
def parse (data : String) : Except String Manifest := do
|
| 228 |
+
match Json.parse data with
|
| 229 |
+
| .ok json => fromJson? json
|
| 230 |
+
| .error e => throw s!"invalid JSON: {e}"
|
| 231 |
+
|
| 232 |
+
/-- Parse a manifest file. -/
|
| 233 |
+
def load (file : FilePath) : IO Manifest := do
|
| 234 |
+
let contents ← IO.FS.readFile file
|
| 235 |
+
match inline <| Manifest.parse contents with
|
| 236 |
+
| .ok a => return a
|
| 237 |
+
| .error e => error s!"{file}: {e}"
|
| 238 |
+
|
| 239 |
+
/--
|
| 240 |
+
Parse a manifest file. Returns `none` if the file does not exist.
|
| 241 |
+
Errors if the manifest is ill-formatted or the read files for other reasons.
|
| 242 |
+
-/
|
| 243 |
+
def load? (file : FilePath) : IO (Option Manifest) := do
|
| 244 |
+
match (← inline (load file) |>.toBaseIO) with
|
| 245 |
+
| .ok contents => return contents
|
| 246 |
+
| .error (.noFileOrDirectory ..) => return none
|
| 247 |
+
| .error e => throw e
|
| 248 |
+
|
| 249 |
+
/-- Serialize the manifest to a JSON file. -/
|
| 250 |
+
def save (self : Manifest) (manifestFile : FilePath) : IO PUnit := do
|
| 251 |
+
let contents := Json.pretty self.toJson
|
| 252 |
+
IO.FS.writeFile manifestFile <| contents.push '\n'
|
| 253 |
+
|
| 254 |
+
@[deprecated save (since := "2024-12-17")] abbrev saveToFile := @save
|
| 255 |
+
|
| 256 |
+
/-- Deserialize package entries from a (partial) JSON manifest. -/
|
| 257 |
+
def decodeEntries (data : Json) : Except String (Array PackageEntry) := do
|
| 258 |
+
let obj ← JsonObject.fromJson? data
|
| 259 |
+
getPackages (← getVersion obj) obj
|
| 260 |
+
|
| 261 |
+
/-- Deserialize manifest package entries from a JSON string. -/
|
| 262 |
+
def parseEntries (data : String) : Except String (Array PackageEntry) := do
|
| 263 |
+
match Json.parse data with
|
| 264 |
+
| .ok json => decodeEntries json
|
| 265 |
+
| .error e => throw s!"invalid JSON: {e}"
|
| 266 |
+
|
| 267 |
+
/-- Deserialize manifest package entries from a JSON file. -/
|
| 268 |
+
def loadEntries (file : FilePath) : IO (Array PackageEntry) := do
|
| 269 |
+
let contents ← IO.FS.readFile file
|
| 270 |
+
match inline <| parseEntries contents with
|
| 271 |
+
| .ok a => return a
|
| 272 |
+
| .error e => error s!"{file}: {e}"
|
| 273 |
+
|
| 274 |
+
/--
|
| 275 |
+
Deserialize manifest package entries from a JSON file.
|
| 276 |
+
Returns an empty array if the file does not exist.
|
| 277 |
+
-/
|
| 278 |
+
def tryLoadEntries (file : FilePath) : IO (Array PackageEntry) := do
|
| 279 |
+
match (← inline (loadEntries file) |>.toBaseIO) with
|
| 280 |
+
| .ok a => return a
|
| 281 |
+
| .error (.noFileOrDirectory ..) => return #[]
|
| 282 |
+
| .error e => error s!"{file}: {e}"
|
| 283 |
+
|
| 284 |
+
/-- Serialize manifest package entries to a JSON file. -/
|
| 285 |
+
def saveEntries (file : FilePath) (entries : Array PackageEntry) : IO PUnit := do
|
| 286 |
+
let contents := Json.pretty <| Json.mkObj [
|
| 287 |
+
("schemaVersion", toJson version),
|
| 288 |
+
("packages", toJson entries)
|
| 289 |
+
]
|
| 290 |
+
IO.FS.writeFile file <| contents.push '\n'
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Materialize.lean
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Gabriel Ebner, Sebastian Ullrich, Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.Util.Git
|
| 8 |
+
import Lake.Load.Manifest
|
| 9 |
+
import Lake.Config.Dependency
|
| 10 |
+
import Lake.Config.Package
|
| 11 |
+
import Lake.Reservoir
|
| 12 |
+
|
| 13 |
+
open System Lean
|
| 14 |
+
|
| 15 |
+
/-! # Dependency Materialization
|
| 16 |
+
Definitions to "materialize" a package dependency.
|
| 17 |
+
That is, clone a local copy of a Git dependency at a specific revision
|
| 18 |
+
or resolve a local path dependency.
|
| 19 |
+
-/
|
| 20 |
+
|
| 21 |
+
namespace Lake
|
| 22 |
+
|
| 23 |
+
/-- Update the Git package in `repo` to `rev` if not already at it. -/
|
| 24 |
+
def updateGitPkg
|
| 25 |
+
(name : String) (repo : GitRepo) (rev? : Option String)
|
| 26 |
+
: LogIO PUnit := do
|
| 27 |
+
let rev ← repo.findRemoteRevision rev?
|
| 28 |
+
if (← repo.getHeadRevision) = rev then
|
| 29 |
+
if (← repo.hasDiff) then
|
| 30 |
+
logWarning s!"{name}: repository '{repo.dir}' has local changes"
|
| 31 |
+
else
|
| 32 |
+
logInfo s!"{name}: checking out revision '{rev}'"
|
| 33 |
+
repo.checkoutDetach rev
|
| 34 |
+
|
| 35 |
+
/-- Clone the Git package as `repo`. -/
|
| 36 |
+
def cloneGitPkg
|
| 37 |
+
(name : String) (repo : GitRepo) (url : String) (rev? : Option String)
|
| 38 |
+
: LogIO PUnit := do
|
| 39 |
+
logInfo s!"{name}: cloning {url}"
|
| 40 |
+
repo.clone url
|
| 41 |
+
if let some rev := rev? then
|
| 42 |
+
let rev ← repo.resolveRemoteRevision rev
|
| 43 |
+
logInfo s!"{name}: checking out revision '{rev}'"
|
| 44 |
+
repo.checkoutDetach rev
|
| 45 |
+
|
| 46 |
+
/--
|
| 47 |
+
Update the Git repository from `url` in `repo` to `rev?`.
|
| 48 |
+
If `repo` is already from `url`, just checkout the new revision.
|
| 49 |
+
Otherwise, delete the local repository and clone a fresh copy from `url`.
|
| 50 |
+
-/
|
| 51 |
+
def updateGitRepo
|
| 52 |
+
(name : String) (repo : GitRepo) (url : String) (rev? : Option String)
|
| 53 |
+
: LogIO Unit := do
|
| 54 |
+
let sameUrl ← EIO.catchExceptions (h := fun _ => pure false) <| show IO Bool from do
|
| 55 |
+
let some remoteUrl ← repo.getRemoteUrl? | return false
|
| 56 |
+
if remoteUrl = url then return true
|
| 57 |
+
return (← IO.FS.realPath remoteUrl) = (← IO.FS.realPath url)
|
| 58 |
+
if sameUrl then
|
| 59 |
+
updateGitPkg name repo rev?
|
| 60 |
+
else
|
| 61 |
+
if System.Platform.isWindows then
|
| 62 |
+
-- Deleting git repositories via IO.FS.removeDirAll does not work reliably on windows
|
| 63 |
+
logInfo s!"{name}: URL has changed; you might need to delete '{repo.dir}' manually"
|
| 64 |
+
updateGitPkg name repo rev?
|
| 65 |
+
else
|
| 66 |
+
logInfo s!"{name}: URL has changed; deleting '{repo.dir}' and cloning again"
|
| 67 |
+
IO.FS.removeDirAll repo.dir
|
| 68 |
+
cloneGitPkg name repo url rev?
|
| 69 |
+
|
| 70 |
+
/--
|
| 71 |
+
Materialize the Git repository from `url` into `repo` at `rev?`.
|
| 72 |
+
Clone it if no local copy exists, otherwise update it.
|
| 73 |
+
-/
|
| 74 |
+
def materializeGitRepo
|
| 75 |
+
(name : String) (repo : GitRepo) (url : String) (rev? : Option String)
|
| 76 |
+
: LogIO Unit := do
|
| 77 |
+
if (← repo.dirExists) then
|
| 78 |
+
updateGitRepo name repo url rev?
|
| 79 |
+
else
|
| 80 |
+
cloneGitPkg name repo url rev?
|
| 81 |
+
|
| 82 |
+
structure MaterializedDep where
|
| 83 |
+
/-- Path to the materialized package relative to the workspace's root directory. -/
|
| 84 |
+
relPkgDir : FilePath
|
| 85 |
+
/--
|
| 86 |
+
URL for the materialized package.
|
| 87 |
+
Used as the endpoint from which to fetch cloud releases for the package.
|
| 88 |
+
-/
|
| 89 |
+
remoteUrl : String
|
| 90 |
+
/-- The manifest entry for the dependency. -/
|
| 91 |
+
manifestEntry : PackageEntry
|
| 92 |
+
deriving Inhabited
|
| 93 |
+
|
| 94 |
+
@[inline] def MaterializedDep.name (self : MaterializedDep) :=
|
| 95 |
+
self.manifestEntry.name
|
| 96 |
+
|
| 97 |
+
@[inline] def MaterializedDep.scope (self : MaterializedDep) :=
|
| 98 |
+
self.manifestEntry.scope
|
| 99 |
+
|
| 100 |
+
/-- Path to the dependency's configuration file (relative to `relPkgDir`). -/
|
| 101 |
+
@[inline] def MaterializedDep.manifestFile? (self : MaterializedDep) :=
|
| 102 |
+
self.manifestEntry.manifestFile?
|
| 103 |
+
|
| 104 |
+
/-- Path to the dependency's configuration file (relative to `relPkgDir`). -/
|
| 105 |
+
@[inline] def MaterializedDep.configFile (self : MaterializedDep) :=
|
| 106 |
+
self.manifestEntry.configFile
|
| 107 |
+
|
| 108 |
+
def pkgNotIndexed (scope name : String) (rev? : Option String := none) : String :=
|
| 109 |
+
let (leanRev, tomlRev) :=
|
| 110 |
+
if let some rev := rev? then
|
| 111 |
+
(s!" @ {repr rev}", s! "\n rev = {repr rev}")
|
| 112 |
+
else ("", "")
|
| 113 |
+
s!"{scope}/{name}: package not found on Reservoir.
|
| 114 |
+
|
| 115 |
+
If the package is on GitHub, you can add a Git source. For example:
|
| 116 |
+
|
| 117 |
+
require ...
|
| 118 |
+
from git \"https://github.com/{scope}/{name}\"{leanRev}
|
| 119 |
+
|
| 120 |
+
or, if using TOML:
|
| 121 |
+
|
| 122 |
+
[[require]]
|
| 123 |
+
git = \"https://github.com/{scope}/{name}\"{tomlRev}
|
| 124 |
+
...
|
| 125 |
+
"
|
| 126 |
+
|
| 127 |
+
/--
|
| 128 |
+
Materializes a configuration dependency.
|
| 129 |
+
For Git dependencies, updates it to the latest input revision.
|
| 130 |
+
-/
|
| 131 |
+
def Dependency.materialize
|
| 132 |
+
(dep : Dependency) (inherited : Bool)
|
| 133 |
+
(lakeEnv : Env) (wsDir relPkgsDir relParentDir : FilePath)
|
| 134 |
+
: LogIO MaterializedDep := do
|
| 135 |
+
if let some src := dep.src? then
|
| 136 |
+
match src with
|
| 137 |
+
| .path dir =>
|
| 138 |
+
let relPkgDir := relParentDir / dir
|
| 139 |
+
return mkDep relPkgDir "" (.path relPkgDir)
|
| 140 |
+
| .git url inputRev? subDir? => do
|
| 141 |
+
let sname := dep.name.toString (escape := false)
|
| 142 |
+
let repoUrl := Git.filterUrl? url |>.getD ""
|
| 143 |
+
materializeGit sname (relPkgsDir / sname) url repoUrl inputRev? subDir?
|
| 144 |
+
else
|
| 145 |
+
if dep.scope.isEmpty then
|
| 146 |
+
error s!"{dep.name}: ill-formed dependency: \
|
| 147 |
+
dependency is missing a source and is missing a scope for Reservoir"
|
| 148 |
+
let verRev? ← dep.version?.mapM fun ver =>
|
| 149 |
+
if ver.startsWith "git#" then
|
| 150 |
+
return ver.drop 4
|
| 151 |
+
else
|
| 152 |
+
error s!"{dep.name}: unsupported dependency version format '{ver}' (should be \"git#<rev>\")"
|
| 153 |
+
let depName := dep.name.toString (escape := false)
|
| 154 |
+
let pkg ←
|
| 155 |
+
match (← Reservoir.fetchPkg? lakeEnv dep.scope depName |>.toLogT) with
|
| 156 |
+
| .ok (some pkg) => pure pkg
|
| 157 |
+
| .ok none => error <| pkgNotIndexed dep.scope depName verRev?
|
| 158 |
+
| .error e =>
|
| 159 |
+
logError s!"{dep.scope}/{depName}: could not materialize package: \
|
| 160 |
+
this may be a transient error or a bug in Lake or Reservoir"
|
| 161 |
+
throw e
|
| 162 |
+
let relPkgDir := relPkgsDir / pkg.name
|
| 163 |
+
match pkg.gitSrc? with
|
| 164 |
+
| some (.git _ url githubUrl? defaultBranch? subDir?) =>
|
| 165 |
+
materializeGit pkg.fullName relPkgDir url
|
| 166 |
+
(githubUrl?.getD "") (verRev? <|> defaultBranch?) subDir?
|
| 167 |
+
| _ => error s!"{pkg.fullName}: Git source not found on Reservoir"
|
| 168 |
+
where
|
| 169 |
+
materializeGit name relPkgDir gitUrl remoteUrl inputRev? subDir? : LogIO MaterializedDep := do
|
| 170 |
+
let repo := GitRepo.mk (wsDir / relPkgDir)
|
| 171 |
+
let gitUrl := lakeEnv.pkgUrlMap.find? dep.name |>.getD gitUrl
|
| 172 |
+
materializeGitRepo name repo gitUrl inputRev?
|
| 173 |
+
let rev ← repo.getHeadRevision
|
| 174 |
+
let relPkgDir := if let some subDir := subDir? then relPkgDir / subDir else relPkgDir
|
| 175 |
+
return mkDep relPkgDir remoteUrl <| .git gitUrl rev inputRev? subDir?
|
| 176 |
+
@[inline] mkDep relPkgDir remoteUrl src : MaterializedDep := {
|
| 177 |
+
relPkgDir, remoteUrl,
|
| 178 |
+
manifestEntry := {name := dep.name, scope := dep.scope, inherited, src}
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
/--
|
| 182 |
+
Materializes a manifest package entry, cloning and/or checking it out as necessary.
|
| 183 |
+
-/
|
| 184 |
+
def PackageEntry.materialize
|
| 185 |
+
(manifestEntry : PackageEntry)
|
| 186 |
+
(lakeEnv : Env) (wsDir relPkgsDir : FilePath)
|
| 187 |
+
: LogIO MaterializedDep :=
|
| 188 |
+
match manifestEntry.src with
|
| 189 |
+
| .path (dir := relPkgDir) .. =>
|
| 190 |
+
return mkDep relPkgDir ""
|
| 191 |
+
| .git (url := url) (rev := rev) (subDir? := subDir?) .. => do
|
| 192 |
+
let sname := manifestEntry.name.toString (escape := false)
|
| 193 |
+
let relGitDir := relPkgsDir / sname
|
| 194 |
+
let gitDir := wsDir / relGitDir
|
| 195 |
+
let repo := GitRepo.mk gitDir
|
| 196 |
+
/-
|
| 197 |
+
Do not update (fetch remote) if already on revision
|
| 198 |
+
Avoids errors when offline, e.g., [leanprover/lake#104][104].
|
| 199 |
+
|
| 200 |
+
[104]: https://github.com/leanprover/lake/issues/104
|
| 201 |
+
-/
|
| 202 |
+
if (← repo.dirExists) then
|
| 203 |
+
if (← repo.getHeadRevision?) = rev then
|
| 204 |
+
if (← repo.hasDiff) then
|
| 205 |
+
logWarning s!"{sname}: repository '{repo.dir}' has local changes"
|
| 206 |
+
else
|
| 207 |
+
let url := lakeEnv.pkgUrlMap.find? manifestEntry.name |>.getD url
|
| 208 |
+
updateGitRepo sname repo url rev
|
| 209 |
+
else
|
| 210 |
+
let url := lakeEnv.pkgUrlMap.find? manifestEntry.name |>.getD url
|
| 211 |
+
cloneGitPkg sname repo url rev
|
| 212 |
+
let relPkgDir := match subDir? with | .some subDir => relGitDir / subDir | .none => relGitDir
|
| 213 |
+
return mkDep relPkgDir (Git.filterUrl? url |>.getD "")
|
| 214 |
+
where
|
| 215 |
+
@[inline] mkDep relPkgDir remoteUrl : MaterializedDep :=
|
| 216 |
+
{relPkgDir, remoteUrl, manifestEntry}
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Package.lean
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.Load.Lean
|
| 8 |
+
import Lake.Load.Toml
|
| 9 |
+
|
| 10 |
+
/-! # Package Loader
|
| 11 |
+
|
| 12 |
+
This module contains the main definitions to load a package
|
| 13 |
+
from Lake configuration file (either Lean or TOML).
|
| 14 |
+
-/
|
| 15 |
+
|
| 16 |
+
open Lean
|
| 17 |
+
|
| 18 |
+
namespace Lake
|
| 19 |
+
open System (FilePath)
|
| 20 |
+
|
| 21 |
+
/--
|
| 22 |
+
Return whether a configuration file with the given name
|
| 23 |
+
and/or a supported extension exists.
|
| 24 |
+
-/
|
| 25 |
+
def configFileExists (cfgFile : FilePath) : BaseIO Bool :=
|
| 26 |
+
if cfgFile.extension.isSome then
|
| 27 |
+
cfgFile.pathExists
|
| 28 |
+
else
|
| 29 |
+
let leanFile := cfgFile.addExtension "lean"
|
| 30 |
+
let tomlFile := cfgFile.addExtension "toml"
|
| 31 |
+
leanFile.pathExists <||> tomlFile.pathExists
|
| 32 |
+
|
| 33 |
+
/--
|
| 34 |
+
Returns the normalized real path of the configuration file (if it exists).
|
| 35 |
+
Otherwise, returns an empty string.
|
| 36 |
+
-/
|
| 37 |
+
def realConfigFile (cfgFile : FilePath) : BaseIO FilePath := do
|
| 38 |
+
if cfgFile.extension.isSome then
|
| 39 |
+
resolvePath cfgFile
|
| 40 |
+
else
|
| 41 |
+
if let some path ← resolvePath? (cfgFile.addExtension "lean") then
|
| 42 |
+
return path
|
| 43 |
+
else
|
| 44 |
+
resolvePath (cfgFile.addExtension "toml")
|
| 45 |
+
|
| 46 |
+
/--
|
| 47 |
+
Loads a Lake package configuration (either Lean or TOML).
|
| 48 |
+
The resulting package does not yet include any dependencies.
|
| 49 |
+
-/
|
| 50 |
+
def loadPackageCore
|
| 51 |
+
(name : String) (cfg : LoadConfig)
|
| 52 |
+
: LogIO (Package × Option Environment) := do
|
| 53 |
+
if let some ext := cfg.relConfigFile.extension then
|
| 54 |
+
let cfg ←
|
| 55 |
+
if let some configFile ← resolvePath? cfg.configFile then
|
| 56 |
+
pure {cfg with configFile}
|
| 57 |
+
else error s!"{name}: configuration file not found: {cfg.configFile}"
|
| 58 |
+
match ext with
|
| 59 |
+
| "lean" => (·.map id some) <$> loadLeanConfig cfg
|
| 60 |
+
| "toml" => ((·,none)) <$> loadTomlConfig cfg
|
| 61 |
+
| _ => error s!"{name}: configuration has unsupported file extension: {cfg.configFile}"
|
| 62 |
+
else
|
| 63 |
+
let relLeanFile := cfg.relConfigFile.addExtension "lean"
|
| 64 |
+
let relTomlFile := cfg.relConfigFile.addExtension "toml"
|
| 65 |
+
let leanFile := cfg.pkgDir / relLeanFile
|
| 66 |
+
let tomlFile := cfg.pkgDir / relTomlFile
|
| 67 |
+
if let some configFile ← resolvePath? leanFile then
|
| 68 |
+
if (← tomlFile.pathExists) then
|
| 69 |
+
logInfo s!"{name}: {relLeanFile} and {relTomlFile} are both present; using {relLeanFile}"
|
| 70 |
+
let cfg := {cfg with configFile, relConfigFile := relLeanFile}
|
| 71 |
+
let (pkg, env) ← loadLeanConfig cfg
|
| 72 |
+
return (pkg, some env)
|
| 73 |
+
else
|
| 74 |
+
if let some configFile ← resolvePath? tomlFile then
|
| 75 |
+
let cfg := {cfg with configFile, relConfigFile := relTomlFile}
|
| 76 |
+
let pkg ← loadTomlConfig cfg
|
| 77 |
+
return (pkg, none)
|
| 78 |
+
else
|
| 79 |
+
error s!"{name}: no configuration file with a supported extension:\n{leanFile}\n{tomlFile}"
|
| 80 |
+
|
| 81 |
+
/-- Loads a Lake package as a single independent object (without dependencies). -/
|
| 82 |
+
def loadPackage (config : LoadConfig) : LogIO Package := do
|
| 83 |
+
Lean.searchPathRef.set config.lakeEnv.leanSearchPath
|
| 84 |
+
(·.1) <$> loadPackageCore "[root]" config
|
| 85 |
+
|
| 86 |
+
/-- Load the package's input-to-content mapping from the Lake cache (if enabled). -/
|
| 87 |
+
protected def Package.loadInputsFrom (env : Env) (pkg : Package) : LogIO Package := do
|
| 88 |
+
if env.lakeCache.isDisabled || !(pkg.enableArtifactCache?.getD env.enableArtifactCache) then
|
| 89 |
+
return pkg
|
| 90 |
+
else
|
| 91 |
+
let inputs ← CacheMap.load (pkg.inputsFileIn env.lakeCache)
|
| 92 |
+
return {pkg with cacheRef? := ← IO.mkRef inputs}
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Resolve.lean
ADDED
|
@@ -0,0 +1,471 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone, Gabriel Ebner
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.Config.Monad
|
| 8 |
+
import Lake.Util.StoreInsts
|
| 9 |
+
import Lake.Build.Topological
|
| 10 |
+
import Lake.Load.Materialize
|
| 11 |
+
import Lake.Load.Package
|
| 12 |
+
|
| 13 |
+
open System Lean
|
| 14 |
+
|
| 15 |
+
/-! # Dependency Resolution
|
| 16 |
+
|
| 17 |
+
This module contains definitions for resolving the dependencies of a package.
|
| 18 |
+
-/
|
| 19 |
+
|
| 20 |
+
namespace Lake
|
| 21 |
+
|
| 22 |
+
def stdMismatchError (newName : String) (rev : String) :=
|
| 23 |
+
s!"the 'std' package has been renamed to '{newName}' and moved to the
|
| 24 |
+
'leanprover-community' organization; downstream packages which wish to
|
| 25 |
+
update to the new std should replace
|
| 26 |
+
|
| 27 |
+
require std from
|
| 28 |
+
git \"https://github.com/leanprover/std4\"{rev}
|
| 29 |
+
|
| 30 |
+
in their Lake configuration file with
|
| 31 |
+
|
| 32 |
+
require {newName} from
|
| 33 |
+
git \"https://github.com/leanprover-community/{newName}\"{rev}
|
| 34 |
+
"
|
| 35 |
+
|
| 36 |
+
/--
|
| 37 |
+
Loads the package configuration of a materialized dependency.
|
| 38 |
+
Adds the facets defined in the package to the `Workspace`.
|
| 39 |
+
-/
|
| 40 |
+
def loadDepPackage
|
| 41 |
+
(dep : MaterializedDep)
|
| 42 |
+
(lakeOpts : NameMap String)
|
| 43 |
+
(leanOpts : Options) (reconfigure : Bool)
|
| 44 |
+
: StateT Workspace LogIO Package := fun ws => do
|
| 45 |
+
let name := dep.name.toString (escape := false)
|
| 46 |
+
let pkgDir := ws.dir / dep.relPkgDir
|
| 47 |
+
let some pkgDir ← resolvePath? pkgDir
|
| 48 |
+
| error s!"{name}: package directory not found: {pkgDir}"
|
| 49 |
+
let (pkg, env?) ← loadPackageCore name {
|
| 50 |
+
lakeEnv := ws.lakeEnv
|
| 51 |
+
wsDir := ws.dir
|
| 52 |
+
pkgDir
|
| 53 |
+
relPkgDir := dep.relPkgDir
|
| 54 |
+
relConfigFile := dep.configFile
|
| 55 |
+
lakeOpts, leanOpts, reconfigure
|
| 56 |
+
scope := dep.scope
|
| 57 |
+
remoteUrl := dep.remoteUrl
|
| 58 |
+
}
|
| 59 |
+
let pkg ← pkg.loadInputsFrom ws.lakeEnv
|
| 60 |
+
if let some env := env? then
|
| 61 |
+
let ws ← IO.ofExcept <| ws.addFacetsFromEnv env leanOpts
|
| 62 |
+
return (pkg, ws)
|
| 63 |
+
else
|
| 64 |
+
return (pkg, ws)
|
| 65 |
+
|
| 66 |
+
/--
|
| 67 |
+
The resolver's call stack of dependencies.
|
| 68 |
+
That is, the dependency currently being resolved plus its parents.
|
| 69 |
+
-/
|
| 70 |
+
abbrev DepStack := CallStack Name
|
| 71 |
+
|
| 72 |
+
/--
|
| 73 |
+
A monad transformer for recursive dependency resolution.
|
| 74 |
+
It equips the monad with the stack of dependencies currently being resolved.
|
| 75 |
+
-/
|
| 76 |
+
abbrev DepStackT m := CallStackT Name m
|
| 77 |
+
|
| 78 |
+
@[inline] nonrec def DepStackT.run
|
| 79 |
+
(x : DepStackT m α) (stack : DepStack := {})
|
| 80 |
+
: m α :=
|
| 81 |
+
x.run stack
|
| 82 |
+
|
| 83 |
+
/-- Log dependency cycle and error. -/
|
| 84 |
+
@[specialize] def depCycleError [MonadError m] (cycle : Cycle Name) : m α :=
|
| 85 |
+
error s!"dependency cycle detected:\n{formatCycle cycle}"
|
| 86 |
+
|
| 87 |
+
instance [Monad m] [MonadError m] : MonadCycleOf Name (DepStackT m) where
|
| 88 |
+
throwCycle := depCycleError
|
| 89 |
+
|
| 90 |
+
/-- The monad of the dependency resolver. -/
|
| 91 |
+
abbrev ResolveT m := DepStackT <| StateT Workspace m
|
| 92 |
+
|
| 93 |
+
@[inline] nonrec def ResolveT.run
|
| 94 |
+
(ws : Workspace) (x : ResolveT m α) (stack : DepStack := {})
|
| 95 |
+
: m (α × Workspace) :=
|
| 96 |
+
x.run stack |>.run ws
|
| 97 |
+
|
| 98 |
+
/-- Recursively run a `ResolveT` monad starting from the workspace's root. -/
|
| 99 |
+
@[specialize] private def Workspace.runResolveT
|
| 100 |
+
[Monad m] [MonadError m] (ws : Workspace)
|
| 101 |
+
(go : RecFetchFn Package PUnit (ResolveT m))
|
| 102 |
+
(root := ws.root) (stack : DepStack := {})
|
| 103 |
+
: m Workspace := do
|
| 104 |
+
let (_, ws) ← ResolveT.run ws (stack := stack) do
|
| 105 |
+
inline <| recFetchAcyclic (·.name) go root
|
| 106 |
+
return ws
|
| 107 |
+
|
| 108 |
+
/-
|
| 109 |
+
Recursively visits each node in a package's dependency graph, starting from
|
| 110 |
+
the workspace package `root`. Each dependency missing from the workspace is
|
| 111 |
+
resolved using the `resolve` function and added into the workspace.
|
| 112 |
+
|
| 113 |
+
Recursion occurs breadth-first. Each direct dependency of a package is
|
| 114 |
+
resolved in reverse order before recursing to the dependencies' dependencies.
|
| 115 |
+
|
| 116 |
+
See `Workspace.updateAndMaterializeCore` for more details.
|
| 117 |
+
-/
|
| 118 |
+
@[inline] private def Workspace.resolveDepsCore
|
| 119 |
+
[Monad m] [MonadError m] (ws : Workspace)
|
| 120 |
+
(load : Package → Dependency → StateT Workspace m Package)
|
| 121 |
+
(root : Package := ws.root) (stack : DepStack := {})
|
| 122 |
+
: m Workspace := do
|
| 123 |
+
ws.runResolveT go root stack
|
| 124 |
+
where
|
| 125 |
+
@[specialize] go pkg recurse : ResolveT m Unit := do
|
| 126 |
+
let start := (← getWorkspace).packages.size
|
| 127 |
+
-- Materialize and load the missing direct dependencies of `pkg`
|
| 128 |
+
pkg.depConfigs.forRevM fun dep => do
|
| 129 |
+
let ws ← getWorkspace
|
| 130 |
+
if ws.packageMap.contains dep.name then
|
| 131 |
+
return -- already handled in another branch
|
| 132 |
+
if pkg.name = dep.name then
|
| 133 |
+
error s!"{pkg.name}: package requires itself (or a package with the same name)"
|
| 134 |
+
let depPkg ← load pkg dep
|
| 135 |
+
modifyThe Workspace (·.addPackage depPkg)
|
| 136 |
+
-- Recursively load the dependencies' dependencies
|
| 137 |
+
(← getWorkspace).packages.forM recurse start
|
| 138 |
+
|
| 139 |
+
/--
|
| 140 |
+
Adds monad state used to update the manifest.
|
| 141 |
+
It equips the monad with a map of locked dependencies.
|
| 142 |
+
-/
|
| 143 |
+
abbrev UpdateT := StateT (NameMap PackageEntry)
|
| 144 |
+
|
| 145 |
+
@[inline] nonrec def UpdateT.run (x : UpdateT m α) (init : NameMap PackageEntry := {}) : m (α × NameMap PackageEntry) :=
|
| 146 |
+
x.run init
|
| 147 |
+
|
| 148 |
+
/--
|
| 149 |
+
Reuse manifest versions of root packages that should not be updated.
|
| 150 |
+
Also, move the packages directory if its location has changed.
|
| 151 |
+
-/
|
| 152 |
+
private def reuseManifest
|
| 153 |
+
(ws : Workspace) (toUpdate : NameSet)
|
| 154 |
+
: UpdateT LoggerIO PUnit := do
|
| 155 |
+
let rootName := ws.root.name.toString (escape := false)
|
| 156 |
+
match (← Manifest.load ws.manifestFile |>.toBaseIO) with
|
| 157 |
+
| .ok manifest =>
|
| 158 |
+
-- Reuse manifest versions
|
| 159 |
+
unless toUpdate.isEmpty do
|
| 160 |
+
manifest.packages.forM fun entry => do
|
| 161 |
+
unless entry.inherited || toUpdate.contains entry.name do
|
| 162 |
+
store entry.name entry
|
| 163 |
+
-- Move packages directory
|
| 164 |
+
if let some oldRelPkgsDir := manifest.packagesDir? then
|
| 165 |
+
let oldPkgsDir := ws.dir / oldRelPkgsDir
|
| 166 |
+
if oldRelPkgsDir.normalize != ws.relPkgsDir.normalize && (← oldPkgsDir.pathExists) then
|
| 167 |
+
logInfo s!"workspace packages directory changed; \
|
| 168 |
+
renaming '{oldPkgsDir}' to '{ws.pkgsDir}'"
|
| 169 |
+
let doRename : IO Unit := do
|
| 170 |
+
createParentDirs ws.pkgsDir
|
| 171 |
+
IO.FS.rename oldPkgsDir ws.pkgsDir
|
| 172 |
+
if let .error e ← doRename.toBaseIO then
|
| 173 |
+
error s!"could not rename workspace packages directory: {e}"
|
| 174 |
+
| .error (.noFileOrDirectory ..) =>
|
| 175 |
+
logInfo s!"{rootName}: no previous manifest, creating one from scratch"
|
| 176 |
+
| .error e =>
|
| 177 |
+
unless toUpdate.isEmpty do
|
| 178 |
+
liftM (m := IO) <| throw e -- only ignore manifest on a bare `lake update`
|
| 179 |
+
logWarning s!"{rootName}: ignoring previous manifest because it failed to load: {e}"
|
| 180 |
+
|
| 181 |
+
/-- Add a package dependency's manifest entries to the update state. -/
|
| 182 |
+
private def addDependencyEntries (pkg : Package) : UpdateT LoggerIO PUnit := do
|
| 183 |
+
match (← Manifest.load pkg.manifestFile |>.toBaseIO) with
|
| 184 |
+
| .ok manifest =>
|
| 185 |
+
manifest.packages.forM fun entry => do
|
| 186 |
+
unless (← getThe (NameMap PackageEntry)).contains entry.name do
|
| 187 |
+
let entry := entry.setInherited.inDirectory pkg.relDir
|
| 188 |
+
store entry.name entry
|
| 189 |
+
| .error (.noFileOrDirectory ..) =>
|
| 190 |
+
logWarning s!"{pkg.name}: ignoring missing manifest '{pkg.manifestFile}'"
|
| 191 |
+
| .error e =>
|
| 192 |
+
logWarning s!"{pkg.name}: ignoring manifest because it failed to load: {e}"
|
| 193 |
+
|
| 194 |
+
/-- Materialize a single dependency, updating it if desired. -/
|
| 195 |
+
private def updateAndMaterializeDep
|
| 196 |
+
(ws : Workspace) (pkg : Package) (dep : Dependency)
|
| 197 |
+
: UpdateT LoggerIO MaterializedDep := do
|
| 198 |
+
if let some entry ← fetch? dep.name then
|
| 199 |
+
entry.materialize ws.lakeEnv ws.dir ws.relPkgsDir
|
| 200 |
+
else
|
| 201 |
+
let inherited := pkg.name ≠ ws.root.name
|
| 202 |
+
/-
|
| 203 |
+
NOTE: A path dependency inherited from another dependency's manifest
|
| 204 |
+
will always be of the form a `./<relPath>` (i.e., be relative to its
|
| 205 |
+
workspace). Thus, when relativized to this workspace, it will have the
|
| 206 |
+
path `<relPkgDir>/./<relPath>`. However, if defining dependency lacks
|
| 207 |
+
a manifest, it will instead be locked as `<relPkgDir>/<relPath>`.
|
| 208 |
+
Adding a `.` here eliminates this difference.
|
| 209 |
+
-/
|
| 210 |
+
let relPkgDir := if pkg.relDir == "." then pkg.relDir else pkg.relDir / "."
|
| 211 |
+
let matDep ← dep.materialize inherited ws.lakeEnv ws.dir ws.relPkgsDir relPkgDir
|
| 212 |
+
store matDep.name matDep.manifestEntry
|
| 213 |
+
return matDep
|
| 214 |
+
|
| 215 |
+
/-- Verify that a dependency was loaded with the correct name. -/
|
| 216 |
+
private def validateDep
|
| 217 |
+
(pkg : Package) (dep : Dependency) (matDep : MaterializedDep) (depPkg : Package)
|
| 218 |
+
: LoggerIO PUnit := do
|
| 219 |
+
if depPkg.name ≠ dep.name then
|
| 220 |
+
if dep.name = .mkSimple "std" then
|
| 221 |
+
let rev :=
|
| 222 |
+
match matDep.manifestEntry.src with
|
| 223 |
+
| .git (inputRev? := some rev) .. => s!" @ {repr rev}"
|
| 224 |
+
| _ => ""
|
| 225 |
+
logError (stdMismatchError depPkg.name.toString rev)
|
| 226 |
+
if matDep.manifestEntry.src matches .git .. then
|
| 227 |
+
if let .error e ← IO.FS.removeDirAll depPkg.dir |>.toBaseIO then -- cleanup
|
| 228 |
+
-- Deleting git repositories via IO.FS.removeDirAll does not work reliably on Windows
|
| 229 |
+
logError s!"'{dep.name}' was downloaded incorrectly; \
|
| 230 |
+
you will need to manually delete '{depPkg.dir}': {e}"
|
| 231 |
+
error s!"{pkg.name}: package '{depPkg.name}' was required as '{dep.name}'"
|
| 232 |
+
|
| 233 |
+
/--
|
| 234 |
+
Exit code returned if Lake needs a manual restart.
|
| 235 |
+
Used, for instance, if the toolchain is updated and no Elan is detected.
|
| 236 |
+
-/
|
| 237 |
+
def restartCode : ExitCode := 4
|
| 238 |
+
|
| 239 |
+
/--
|
| 240 |
+
Update the workspace's `lean-toolchain` if necessary.
|
| 241 |
+
|
| 242 |
+
Compares the root's toolchain with that of its direct dependencies to find the
|
| 243 |
+
best match. If none can be found, issue warning and return normally. If an
|
| 244 |
+
update is found
|
| 245 |
+
-/
|
| 246 |
+
def Workspace.updateToolchain
|
| 247 |
+
(ws : Workspace) (rootDeps : Array MaterializedDep)
|
| 248 |
+
: LoggerIO PUnit := do
|
| 249 |
+
let rootToolchainFile := ws.root.dir / toolchainFileName
|
| 250 |
+
let rootTc? ← ToolchainVer.ofDir? ws.dir
|
| 251 |
+
let (src, tc?, tcs) ← rootDeps.foldlM (init := (ws.root.name, rootTc?, #[])) fun s dep => do
|
| 252 |
+
let depTc? ← ToolchainVer.ofDir? (ws.dir / dep.relPkgDir)
|
| 253 |
+
let some depTc := depTc?
|
| 254 |
+
| return s
|
| 255 |
+
let (src, tc?, tcs) := s
|
| 256 |
+
let some tc := tc?
|
| 257 |
+
| return (dep.name, depTc?, tcs)
|
| 258 |
+
if depTc ≤ tc then
|
| 259 |
+
return (src, tc, tcs)
|
| 260 |
+
else if tc < depTc then
|
| 261 |
+
return (dep.name, depTc, tcs)
|
| 262 |
+
else
|
| 263 |
+
return (src, tc, tcs.push (dep.name, depTc))
|
| 264 |
+
if 0 < tcs.size then
|
| 265 |
+
let s := "toolchain not updated; multiple toolchain candidates:"
|
| 266 |
+
let s := if let some tc := tc? then s!"{s}\n {tc}\n from {src}" else s
|
| 267 |
+
let s := tcs.foldl (init := s) fun s (d, tc) => s!"{s}\n {tc}\n from {d}"
|
| 268 |
+
logWarning s
|
| 269 |
+
else if let some tc := tc? then
|
| 270 |
+
if rootTc?.any (· == tc) then
|
| 271 |
+
logInfo "toolchain not updated; already up-to-date"
|
| 272 |
+
return
|
| 273 |
+
logInfo s!"updating toolchain to '{tc}'"
|
| 274 |
+
IO.FS.writeFile rootToolchainFile tc.toString
|
| 275 |
+
let some lakeArgs := ws.lakeArgs?
|
| 276 |
+
| logInfo s!"cannot auto-restart; you will need to manually restart Lake"
|
| 277 |
+
IO.Process.exit restartCode.toUInt8
|
| 278 |
+
let some elanInstall := ws.lakeEnv.elan?
|
| 279 |
+
| logInfo s!"no Elan detected; you will need to manually restart Lake"
|
| 280 |
+
IO.Process.exit restartCode.toUInt8
|
| 281 |
+
logInfo s!"restarting Lake via Elan"
|
| 282 |
+
let child ← IO.Process.spawn {
|
| 283 |
+
cmd := elanInstall.elan.toString
|
| 284 |
+
args := #["run", "--install", tc.toString, "lake"] ++ lakeArgs
|
| 285 |
+
env := Env.noToolchainVars
|
| 286 |
+
}
|
| 287 |
+
IO.Process.exit (← child.wait).toUInt8
|
| 288 |
+
else
|
| 289 |
+
logInfo s!"toolchain not updated; no toolchain information found"
|
| 290 |
+
|
| 291 |
+
/--
|
| 292 |
+
Updates the workspace, materializing and reconfiguring dependencies.
|
| 293 |
+
|
| 294 |
+
Dependencies are updated to latest specific revision matching that in `require`
|
| 295 |
+
(e.g., if the `require` is `@master`, update to latest commit on master) or
|
| 296 |
+
removed if the `require` is removed.
|
| 297 |
+
If `tuUpdate` is empty, all direct dependencies of the workspace's root will be
|
| 298 |
+
updated and/or remove. Otherwise, only those specified will be updated.
|
| 299 |
+
|
| 300 |
+
If `updateToolchain := true`, the workspace's toolchain is also updated to the
|
| 301 |
+
latest toolchain compatible with the root and its direct dependencies.
|
| 302 |
+
If there are multiple incomparable toolchain versions across them,
|
| 303 |
+
a warning will be issued and no update performed.
|
| 304 |
+
If an update is performed, Lake will automatically restart the update on the new
|
| 305 |
+
toolchain (via `elan`). If `elan` is missing, it will instead request a manual
|
| 306 |
+
restart from the user and exit immediately with `restartCode`.
|
| 307 |
+
|
| 308 |
+
**Dependency Traversal Order**
|
| 309 |
+
|
| 310 |
+
All dependencies of a package are visited in reverse order before recursing
|
| 311 |
+
to the dependencies' dependencies. For example, given the dependency graph:
|
| 312 |
+
|
| 313 |
+
```
|
| 314 |
+
R
|
| 315 |
+
|- A
|
| 316 |
+
|- B
|
| 317 |
+
|- X
|
| 318 |
+
|- Y
|
| 319 |
+
|- C
|
| 320 |
+
```
|
| 321 |
+
|
| 322 |
+
Lake follows the order `R`, `C`, `B`, `A`, `Y`, `X`.
|
| 323 |
+
|
| 324 |
+
The reason for this is two-fold:
|
| 325 |
+
1. Like targets, later requires should shadow earlier definitions.
|
| 326 |
+
2. Requires written by a user should take priority over those inherited
|
| 327 |
+
from dependencies.
|
| 328 |
+
|
| 329 |
+
Were Lake to use a depth-first traversal, for example, Lake would follow
|
| 330 |
+
the order `R`, `A`, `B`, `X`, `Y`, `C`. If `X` and `C` are both the package
|
| 331 |
+
`foo`, Lake would use the configuration of `foo` found in `B` rather than in
|
| 332 |
+
the root `R`, which would likely confuse the user.
|
| 333 |
+
-/
|
| 334 |
+
def Workspace.updateAndMaterializeCore
|
| 335 |
+
(ws : Workspace)
|
| 336 |
+
(toUpdate : NameSet := {}) (leanOpts : Options := {})
|
| 337 |
+
(updateToolchain := true)
|
| 338 |
+
: LoggerIO (Workspace × NameMap PackageEntry) := UpdateT.run do
|
| 339 |
+
reuseManifest ws toUpdate
|
| 340 |
+
let ws := ws.addPackage ws.root
|
| 341 |
+
if updateToolchain then
|
| 342 |
+
let deps := ws.root.depConfigs.reverse
|
| 343 |
+
let matDeps ← deps.mapM fun dep => do
|
| 344 |
+
logVerbose s!"{ws.root.name}: updating '{dep.name}' with {toJson dep.opts}"
|
| 345 |
+
updateAndMaterializeDep ws ws.root dep
|
| 346 |
+
ws.updateToolchain matDeps
|
| 347 |
+
let start := ws.packages.size
|
| 348 |
+
let ws ← (deps.zip matDeps).foldlM (init := ws) fun ws (dep, matDep) => do
|
| 349 |
+
let (depPkg, ws) ← loadUpdatedDep ws.root dep matDep ws
|
| 350 |
+
let ws := ws.addPackage depPkg
|
| 351 |
+
return ws
|
| 352 |
+
ws.packages.foldlM (init := ws) (start := start) fun ws pkg =>
|
| 353 |
+
ws.resolveDepsCore (stack := [ws.root.name]) updateAndLoadDep pkg
|
| 354 |
+
else
|
| 355 |
+
ws.resolveDepsCore updateAndLoadDep
|
| 356 |
+
where
|
| 357 |
+
@[inline] updateAndLoadDep pkg dep := do
|
| 358 |
+
let matDep ← updateAndMaterializeDep (← getWorkspace) pkg dep
|
| 359 |
+
loadUpdatedDep pkg dep matDep
|
| 360 |
+
@[inline] loadUpdatedDep pkg dep matDep : StateT Workspace (UpdateT LoggerIO) Package := do
|
| 361 |
+
let depPkg ← loadDepPackage matDep dep.opts leanOpts true
|
| 362 |
+
validateDep pkg dep matDep depPkg
|
| 363 |
+
addDependencyEntries depPkg
|
| 364 |
+
return depPkg
|
| 365 |
+
|
| 366 |
+
/-- Write package entries to the workspace manifest. -/
|
| 367 |
+
def Workspace.writeManifest
|
| 368 |
+
(ws : Workspace) (entries : NameMap PackageEntry)
|
| 369 |
+
: IO PUnit := do
|
| 370 |
+
let manifestEntries := ws.packages.foldl (init := #[]) fun arr pkg =>
|
| 371 |
+
match entries.find? pkg.name with
|
| 372 |
+
| some entry => arr.push <|
|
| 373 |
+
entry.setManifestFile pkg.relManifestFile |>.setConfigFile pkg.relConfigFile
|
| 374 |
+
| none => arr -- should only be the case for the root
|
| 375 |
+
let manifest : Manifest := {
|
| 376 |
+
name := ws.root.name
|
| 377 |
+
lakeDir := ws.relLakeDir
|
| 378 |
+
packagesDir? := ws.relPkgsDir
|
| 379 |
+
packages := manifestEntries
|
| 380 |
+
}
|
| 381 |
+
manifest.save ws.manifestFile
|
| 382 |
+
|
| 383 |
+
/-- Run a package's `post_update` hooks. -/
|
| 384 |
+
def Package.runPostUpdateHooks (pkg : Package) : LakeT LoggerIO PUnit := do
|
| 385 |
+
unless pkg.postUpdateHooks.isEmpty do
|
| 386 |
+
logInfo s!"{pkg.name}: running post-update hooks"
|
| 387 |
+
pkg.postUpdateHooks.forM fun hook => hook.get.fn pkg
|
| 388 |
+
|
| 389 |
+
/--
|
| 390 |
+
Updates the workspace, writes the new Lake manifest, and runs package
|
| 391 |
+
post-update hooks.
|
| 392 |
+
|
| 393 |
+
See `Workspace.updateAndMaterializeCore` for details on the update process.
|
| 394 |
+
-/
|
| 395 |
+
def Workspace.updateAndMaterialize
|
| 396 |
+
(ws : Workspace)
|
| 397 |
+
(toUpdate : NameSet := {}) (leanOpts : Options := {})
|
| 398 |
+
(updateToolchain := true)
|
| 399 |
+
: LoggerIO Workspace := do
|
| 400 |
+
let (ws, entries) ←
|
| 401 |
+
ws.updateAndMaterializeCore toUpdate leanOpts updateToolchain
|
| 402 |
+
ws.writeManifest entries
|
| 403 |
+
ws.runLakeT do ws.packages.forM (·.runPostUpdateHooks)
|
| 404 |
+
return ws
|
| 405 |
+
|
| 406 |
+
/--
|
| 407 |
+
Check whether entries in the manifest are up-to-date,
|
| 408 |
+
reporting warnings and/or errors as appropriate.
|
| 409 |
+
-/
|
| 410 |
+
def validateManifest
|
| 411 |
+
(pkgEntries : NameMap PackageEntry) (deps : Array Dependency)
|
| 412 |
+
: LoggerIO PUnit := do
|
| 413 |
+
deps.forM fun dep => do
|
| 414 |
+
let warnOutOfDate (what : String) :=
|
| 415 |
+
logWarning <|
|
| 416 |
+
s!"manifest out of date: {what} of dependency '{dep.name}' changed; \
|
| 417 |
+
use `lake update {dep.name}` to update it"
|
| 418 |
+
let some src := dep.src? | return
|
| 419 |
+
let some entry := pkgEntries.find? dep.name | return
|
| 420 |
+
match src, entry.src with
|
| 421 |
+
| .git (url := url) (rev := rev) .., .git (url := url') (inputRev? := rev') .. =>
|
| 422 |
+
if url ≠ url' then warnOutOfDate "git url"
|
| 423 |
+
if rev ≠ rev' then warnOutOfDate "git revision"
|
| 424 |
+
| .path .., .path .. => pure ()
|
| 425 |
+
| _, _ => warnOutOfDate "source kind (git/path)"
|
| 426 |
+
|
| 427 |
+
/--
|
| 428 |
+
Resolving a workspace's dependencies using a manifest,
|
| 429 |
+
downloading and/or updating them as necessary.
|
| 430 |
+
-/
|
| 431 |
+
def Workspace.materializeDeps
|
| 432 |
+
(ws : Workspace) (manifest : Manifest)
|
| 433 |
+
(leanOpts : Options := {}) (reconfigure := false)
|
| 434 |
+
(overrides : Array PackageEntry := #[])
|
| 435 |
+
: LoggerIO Workspace := do
|
| 436 |
+
-- Load locked dependencies
|
| 437 |
+
if !manifest.packages.isEmpty && manifest.packagesDir? != some (mkRelPathString ws.relPkgsDir) then
|
| 438 |
+
logWarning <|
|
| 439 |
+
"manifest out of date: packages directory changed; \
|
| 440 |
+
use `lake update` to rebuild the manifest \
|
| 441 |
+
(warning: this will update ALL workspace dependencies)"
|
| 442 |
+
let relPkgsDir := manifest.packagesDir?.getD ws.relPkgsDir
|
| 443 |
+
let mut pkgEntries := mkNameMap PackageEntry
|
| 444 |
+
pkgEntries := manifest.packages.foldl (init := pkgEntries) fun map entry =>
|
| 445 |
+
map.insert entry.name entry
|
| 446 |
+
validateManifest pkgEntries ws.root.depConfigs
|
| 447 |
+
let wsOverrides ← Manifest.tryLoadEntries ws.packageOverridesFile
|
| 448 |
+
pkgEntries := wsOverrides.foldl (init := pkgEntries) fun map entry =>
|
| 449 |
+
map.insert entry.name entry
|
| 450 |
+
pkgEntries := overrides.foldl (init := pkgEntries) fun map entry =>
|
| 451 |
+
map.insert entry.name entry
|
| 452 |
+
if pkgEntries.isEmpty && !ws.root.depConfigs.isEmpty then
|
| 453 |
+
error "missing manifest; use `lake update` to generate one"
|
| 454 |
+
-- Materialize all dependencies
|
| 455 |
+
let ws := ws.addPackage ws.root
|
| 456 |
+
ws.resolveDepsCore fun pkg dep => do
|
| 457 |
+
let ws ← getWorkspace
|
| 458 |
+
if let some entry := pkgEntries.find? dep.name then
|
| 459 |
+
let result ← entry.materialize ws.lakeEnv ws.dir relPkgsDir
|
| 460 |
+
loadDepPackage result dep.opts leanOpts reconfigure
|
| 461 |
+
else
|
| 462 |
+
if pkg.name = ws.root.name then
|
| 463 |
+
error <|
|
| 464 |
+
s!"dependency '{dep.name}' not in manifest; \
|
| 465 |
+
use `lake update {dep.name}` to add it"
|
| 466 |
+
else
|
| 467 |
+
error <|
|
| 468 |
+
s!"dependency '{dep.name}' of '{pkg.name}' not in manifest; \
|
| 469 |
+
this suggests that the manifest is corrupt; \
|
| 470 |
+
use `lake update` to generate a new, complete file \
|
| 471 |
+
(warning: this will update ALL workspace dependencies)"
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Toml.lean
ADDED
|
@@ -0,0 +1,450 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.Toml.Load
|
| 8 |
+
import Lake.Toml.Decode
|
| 9 |
+
import Lake.Config.Package
|
| 10 |
+
import Lake.Util.Log
|
| 11 |
+
|
| 12 |
+
open Lean Parser
|
| 13 |
+
open System (FilePath)
|
| 14 |
+
|
| 15 |
+
/-! # TOML Loader
|
| 16 |
+
|
| 17 |
+
This module contains the main definitions to load a package from a
|
| 18 |
+
Lake configuration file written in TOML.
|
| 19 |
+
-/
|
| 20 |
+
|
| 21 |
+
namespace Lake
|
| 22 |
+
|
| 23 |
+
open Toml
|
| 24 |
+
|
| 25 |
+
/-! ## General Helpers -/
|
| 26 |
+
|
| 27 |
+
@[specialize] def decodeFieldCore
|
| 28 |
+
(name : Name) (decode : Toml.Value → EDecodeM α) [field : ConfigField σ name α]
|
| 29 |
+
(_ : Table) (val : Value) (cfg : σ)
|
| 30 |
+
: DecodeM σ := fun es =>
|
| 31 |
+
match decode val es with
|
| 32 |
+
| .ok a es => .ok (field.set a cfg) es
|
| 33 |
+
| .error _ es => .ok cfg es
|
| 34 |
+
|
| 35 |
+
class DecodeField (σ : Type) (name : Name) where
|
| 36 |
+
decodeField (t : Table) (val : Value) (cfg : σ) : DecodeM σ
|
| 37 |
+
|
| 38 |
+
export DecodeField (decodeField)
|
| 39 |
+
|
| 40 |
+
instance [DecodeToml α] [ConfigField σ name α] : DecodeField σ name where
|
| 41 |
+
decodeField := decodeFieldCore name decodeToml
|
| 42 |
+
|
| 43 |
+
structure TomlFieldInfo (σ : Type) where
|
| 44 |
+
decodeAndSet : Table → Value → σ → DecodeM σ
|
| 45 |
+
|
| 46 |
+
abbrev TomlFieldInfos (σ : Type) :=
|
| 47 |
+
NameMap (TomlFieldInfo σ)
|
| 48 |
+
|
| 49 |
+
def TomlFieldInfos.empty : TomlFieldInfos σ := {}
|
| 50 |
+
|
| 51 |
+
@[inline] def TomlFieldInfos.insert
|
| 52 |
+
(name : Name) [DecodeField σ name] (infos : TomlFieldInfos σ)
|
| 53 |
+
: TomlFieldInfos σ :=
|
| 54 |
+
NameMap.insert infos name ⟨decodeField name⟩
|
| 55 |
+
|
| 56 |
+
class ConfigTomlInfo (α : Type) where
|
| 57 |
+
fieldInfos : TomlFieldInfos α
|
| 58 |
+
|
| 59 |
+
def Toml.Table.decodeConfig
|
| 60 |
+
[EmptyCollection α] [ConfigTomlInfo α] (t : Table)
|
| 61 |
+
: Toml.DecodeM α :=
|
| 62 |
+
t.foldM (init := {}) fun cfg key val => do
|
| 63 |
+
if let some info := ConfigTomlInfo.fieldInfos.find? key then
|
| 64 |
+
info.decodeAndSet t val cfg
|
| 65 |
+
else
|
| 66 |
+
return cfg
|
| 67 |
+
|
| 68 |
+
@[inline] def decodeTableValue (decode : Table → DecodeM α) (v : Value) : EDecodeM α := do
|
| 69 |
+
ensureDecode <| decode (← v.decodeTable)
|
| 70 |
+
|
| 71 |
+
/-! ## Value Decoders -/
|
| 72 |
+
|
| 73 |
+
def takeNamePart (ss : Substring) (pre : Name) : (Substring × Name) :=
|
| 74 |
+
if ss.isEmpty then
|
| 75 |
+
(ss, .anonymous)
|
| 76 |
+
else
|
| 77 |
+
let curr := ss.front
|
| 78 |
+
if isIdBeginEscape curr then
|
| 79 |
+
let ss := ss.drop 1
|
| 80 |
+
let startPos := ss.startPos
|
| 81 |
+
let ss := ss.dropWhile (!isIdEndEscape ·)
|
| 82 |
+
if isIdEndEscape ss.front then
|
| 83 |
+
let id := ss.str.extract startPos ss.startPos
|
| 84 |
+
(ss, Name.str pre id)
|
| 85 |
+
else
|
| 86 |
+
(ss, .anonymous)
|
| 87 |
+
else if isIdFirst curr then
|
| 88 |
+
let startPos := ss.startPos
|
| 89 |
+
let ss := ss.drop 1 |>.dropWhile isIdRest
|
| 90 |
+
let id := ss.str.extract startPos ss.startPos
|
| 91 |
+
(ss, Name.str pre id)
|
| 92 |
+
else if curr.isDigit then
|
| 93 |
+
let startPos := ss.startPos
|
| 94 |
+
let ss := ss.drop 1 |>.dropWhile Char.isDigit
|
| 95 |
+
let digits := ss.str.extract startPos ss.startPos
|
| 96 |
+
let n := (Syntax.decodeNatLitVal? digits).get!
|
| 97 |
+
(ss, Name.num pre n)
|
| 98 |
+
else
|
| 99 |
+
(ss, .anonymous)
|
| 100 |
+
|
| 101 |
+
partial def takeName (ss : Substring) : (Substring × Name) :=
|
| 102 |
+
let rec takeRest ss pre :=
|
| 103 |
+
if ss.front == '.' then
|
| 104 |
+
let startPos := ss.startPos
|
| 105 |
+
let (ss, n) := takeNamePart (ss.drop 1) pre
|
| 106 |
+
if n.isAnonymous then ({ss with startPos}, pre) else takeRest ss n
|
| 107 |
+
else
|
| 108 |
+
(ss, pre)
|
| 109 |
+
let (ss, n) := takeNamePart ss .anonymous
|
| 110 |
+
if n.isAnonymous then (ss, .anonymous) else takeRest ss n
|
| 111 |
+
|
| 112 |
+
def Glob.ofString? (v : String) : Option Glob := do
|
| 113 |
+
let (ss, n) := takeName v.toSubstring
|
| 114 |
+
if n.isAnonymous then failure
|
| 115 |
+
if h : ss.str.atEnd ss.startPos then
|
| 116 |
+
return .one n
|
| 117 |
+
else if ss.str.get' ss.startPos h == '.' then
|
| 118 |
+
match (ss.drop 1).front with
|
| 119 |
+
| '+' => return .submodules n
|
| 120 |
+
| '*' => return .andSubmodules n
|
| 121 |
+
| _ => failure
|
| 122 |
+
else
|
| 123 |
+
failure
|
| 124 |
+
|
| 125 |
+
protected def Glob.decodeToml (v : Value) : EDecodeM Glob := do
|
| 126 |
+
match inline <| Glob.ofString? (← v.decodeString) with
|
| 127 |
+
| some v => return v
|
| 128 |
+
| none => throwDecodeErrorAt v.ref "expected glob"
|
| 129 |
+
|
| 130 |
+
instance : DecodeToml Glob := ⟨Glob.decodeToml⟩
|
| 131 |
+
instance : DecodeToml (Array Glob) := ⟨Value.decodeArrayOrSingleton⟩
|
| 132 |
+
|
| 133 |
+
protected def LeanOptionValue.decodeToml : Value → EDecodeM LeanOptionValue
|
| 134 |
+
| .string _ v => return .ofString v
|
| 135 |
+
| .boolean _ v => return .ofBool v
|
| 136 |
+
| .integer _ (.ofNat v) => return .ofNat v
|
| 137 |
+
| x => throwDecodeErrorAt x.ref "expected string, boolean, or nonnegative integer"
|
| 138 |
+
|
| 139 |
+
instance : DecodeToml LeanOptionValue := ⟨LeanOptionValue.decodeToml⟩
|
| 140 |
+
|
| 141 |
+
protected def LeanOption.decodeToml (v : Value) : EDecodeM LeanOption := do
|
| 142 |
+
match v with
|
| 143 |
+
| .array ref vs =>
|
| 144 |
+
if h : vs.size = 2 then ensureDecode do
|
| 145 |
+
let name : Name ← tryDecode <| decodeToml vs[0]
|
| 146 |
+
let value ← tryDecode <| decodeToml vs[1]
|
| 147 |
+
return {name, value}
|
| 148 |
+
else
|
| 149 |
+
throwDecodeErrorAt ref "expected array of size 2"
|
| 150 |
+
| .table ref t => ensureDecode do
|
| 151 |
+
let name ← t.tryDecode `name ref
|
| 152 |
+
let value ← t.tryDecode `value ref
|
| 153 |
+
return {name, value}
|
| 154 |
+
| v =>
|
| 155 |
+
throwDecodeErrorAt v.ref "expected array or table"
|
| 156 |
+
|
| 157 |
+
instance : DecodeToml LeanOption := ⟨LeanOption.decodeToml⟩
|
| 158 |
+
|
| 159 |
+
protected def BuildType.decodeToml (v : Value) : EDecodeM BuildType := do
|
| 160 |
+
match inline <| BuildType.ofString? (← v.decodeString) with
|
| 161 |
+
| some v => return v
|
| 162 |
+
| none => throwDecodeErrorAt v.ref "expected one of 'debug', 'relWithDebInfo', 'minSizeRel', 'release'"
|
| 163 |
+
|
| 164 |
+
instance : DecodeToml BuildType := ⟨(BuildType.decodeToml ·)⟩
|
| 165 |
+
|
| 166 |
+
protected def Backend.decodeToml (v : Value) : EDecodeM Backend := do
|
| 167 |
+
match inline <| Backend.ofString? (← v.decodeString) with
|
| 168 |
+
| some v => return v
|
| 169 |
+
| none => throwDecodeErrorAt v.ref "expected one of 'c', 'llvm', or 'default'"
|
| 170 |
+
|
| 171 |
+
instance : DecodeToml Backend := ⟨Backend.decodeToml⟩
|
| 172 |
+
|
| 173 |
+
partial def decodeLeanOptionsAux
|
| 174 |
+
(v : Value) (k : Name) (vs : EDecodeM (Array LeanOption))
|
| 175 |
+
: EDecodeM (Array LeanOption) :=
|
| 176 |
+
match v with
|
| 177 |
+
| .table _ t => t.items.foldl (init := vs) fun vs (k',v) =>
|
| 178 |
+
decodeLeanOptionsAux v (k ++ k') vs
|
| 179 |
+
| v => mergeErrors vs (decodeToml v) fun vs v => vs.push ⟨k,v⟩
|
| 180 |
+
|
| 181 |
+
def decodeLeanOptions (v : Value) : EDecodeM (Array LeanOption) :=
|
| 182 |
+
match v with
|
| 183 |
+
| .array _ vs => decodeArray vs
|
| 184 |
+
| .table _ t => t.items.foldl (init := .ok #[]) fun vs (k,v) => decodeLeanOptionsAux v k vs
|
| 185 |
+
| v => throwDecodeErrorAt v.ref "expected array or table"
|
| 186 |
+
|
| 187 |
+
instance : DecodeToml (Array LeanOption) := ⟨decodeLeanOptions⟩
|
| 188 |
+
|
| 189 |
+
protected def StdVer.decodeToml (v : Value) : EDecodeM LeanVer := do
|
| 190 |
+
match StdVer.parse (← v.decodeString) with
|
| 191 |
+
| .ok v => return v
|
| 192 |
+
| .error e => throwDecodeErrorAt v.ref e
|
| 193 |
+
|
| 194 |
+
instance : DecodeToml StdVer := ⟨StdVer.decodeToml⟩
|
| 195 |
+
|
| 196 |
+
mutual
|
| 197 |
+
|
| 198 |
+
partial def Pattern.decodeToml
|
| 199 |
+
[IsPattern β α] [DecodeToml β] (v : Value) (presets : NameMap (Pattern α β) := {})
|
| 200 |
+
: EDecodeM (Pattern α β) :=
|
| 201 |
+
have : DecodeToml (PatternDescr α β) := ⟨PatternDescr.decodeToml⟩
|
| 202 |
+
match v with
|
| 203 |
+
| .string _ s =>
|
| 204 |
+
if s == "*" then
|
| 205 |
+
return .star
|
| 206 |
+
else
|
| 207 |
+
.ofDescr <$> decodeToml v
|
| 208 |
+
| .table r t => do
|
| 209 |
+
if let some name ← t.decode? `preset then
|
| 210 |
+
if let some preset := presets.find? name then
|
| 211 |
+
return preset
|
| 212 |
+
else
|
| 213 |
+
throwDecodeErrorAt r s!"unknown preset '{name}'"
|
| 214 |
+
else
|
| 215 |
+
.ofDescr <$> decodeToml v
|
| 216 |
+
| v => .ofDescr <$> decodeToml v
|
| 217 |
+
|
| 218 |
+
partial def PatternDescr.decodeToml
|
| 219 |
+
[IsPattern β α] [DecodeToml β] (v : Value)
|
| 220 |
+
: EDecodeM (PatternDescr α β) :=
|
| 221 |
+
have : DecodeToml (Pattern α β) := ⟨Pattern.decodeToml⟩
|
| 222 |
+
match v with
|
| 223 |
+
| .table _ t => do
|
| 224 |
+
if let some p ← t.decode? `not then
|
| 225 |
+
return .not p
|
| 226 |
+
if let some p ← t.decode? `any then
|
| 227 |
+
return .any p
|
| 228 |
+
else if let some p ← t.decode? `all then
|
| 229 |
+
return .all p
|
| 230 |
+
else
|
| 231 |
+
.coe <$> decodeToml v
|
| 232 |
+
| v => .coe <$> decodeToml v
|
| 233 |
+
|
| 234 |
+
end
|
| 235 |
+
|
| 236 |
+
instance [IsPattern β α] [DecodeToml β] : DecodeToml (Pattern α β) := ⟨Pattern.decodeToml⟩
|
| 237 |
+
instance [IsPattern β α] [DecodeToml β] : DecodeToml (PatternDescr α β) := ⟨PatternDescr.decodeToml⟩
|
| 238 |
+
|
| 239 |
+
protected def StrPatDescr.decodeToml (v : Value) : EDecodeM StrPatDescr :=
|
| 240 |
+
match v with
|
| 241 |
+
| .array _ vs => .mem <$> decodeArray vs
|
| 242 |
+
| .table r t => do
|
| 243 |
+
if let some affix ← t.decode? `startsWith then
|
| 244 |
+
return .startsWith affix
|
| 245 |
+
else if let some affix ← t.decode? `endsWith then
|
| 246 |
+
return .endsWith affix
|
| 247 |
+
else
|
| 248 |
+
throwDecodeErrorAt r "expected string pattern"
|
| 249 |
+
| v => throwDecodeErrorAt v.ref "expected string pattern"
|
| 250 |
+
|
| 251 |
+
instance : DecodeToml StrPatDescr := ⟨StrPatDescr.decodeToml⟩
|
| 252 |
+
|
| 253 |
+
protected def PathPatDescr.decodeToml (v : Value) : EDecodeM PathPatDescr :=
|
| 254 |
+
match v with
|
| 255 |
+
| .table r t => do
|
| 256 |
+
if let some p ← t.decode? `path then
|
| 257 |
+
return .path p
|
| 258 |
+
else if let some p ← t.decode? `extension then
|
| 259 |
+
return .extension p
|
| 260 |
+
else if let some p ← t.decode? `fileName then
|
| 261 |
+
return .fileName p
|
| 262 |
+
else
|
| 263 |
+
throwDecodeErrorAt r "expected file path pattern"
|
| 264 |
+
| v => throwDecodeErrorAt v.ref "expected file path pattern"
|
| 265 |
+
|
| 266 |
+
instance : DecodeToml PathPatDescr := ⟨PathPatDescr.decodeToml⟩
|
| 267 |
+
|
| 268 |
+
def decodeVersionTags (v : Value) : EDecodeM StrPat :=
|
| 269 |
+
inline <| Pattern.decodeToml (presets := versionTagPresets) v
|
| 270 |
+
|
| 271 |
+
instance : DecodeField (PackageConfig n) `versionTags where
|
| 272 |
+
decodeField := decodeFieldCore `versionTags decodeVersionTags
|
| 273 |
+
|
| 274 |
+
-- for `platformIndependent`, `releaseRepo`, `buildArchive`, etc.
|
| 275 |
+
instance [DecodeToml α] : DecodeToml (Option α) := ⟨(some <$> decodeToml ·)⟩
|
| 276 |
+
|
| 277 |
+
def PartialBuildKey.decodeToml (v : Value) : EDecodeM PartialBuildKey := do
|
| 278 |
+
match PartialBuildKey.parse (← v.decodeString) with
|
| 279 |
+
| .ok k => return k
|
| 280 |
+
| .error e => throwDecodeErrorAt v.ref e
|
| 281 |
+
|
| 282 |
+
instance : DecodeToml PartialBuildKey := ⟨PartialBuildKey.decodeToml⟩
|
| 283 |
+
instance : DecodeToml (Target α) := ⟨(Target.mk <$> PartialBuildKey.decodeToml ·)⟩
|
| 284 |
+
|
| 285 |
+
def decodeFacets (kind : Name) (val : Value) : EDecodeM (Array Name) := do
|
| 286 |
+
return (← val.decodeArray).map (kind ++ ·)
|
| 287 |
+
|
| 288 |
+
instance : DecodeField (LeanLibConfig n) `defaultFacets where
|
| 289 |
+
decodeField := decodeFieldCore `defaultFacets (decodeFacets LeanLib.facetKind)
|
| 290 |
+
|
| 291 |
+
-- HACK to work around the fact the TOML decoders
|
| 292 |
+
-- do not support inter-field dependencies by default
|
| 293 |
+
def decodeRoots
|
| 294 |
+
(t : Table) (v : Value) (cfg : LeanLibConfig n)
|
| 295 |
+
: DecodeM (LeanLibConfig n) := do
|
| 296 |
+
let cfg ← decodeField `roots t v cfg
|
| 297 |
+
if t.contains `globs then
|
| 298 |
+
return cfg
|
| 299 |
+
else
|
| 300 |
+
return {cfg with globs := mkFieldDefault `globs cfg}
|
| 301 |
+
|
| 302 |
+
instance : DecodeField (LeanLibConfig n) `roots := ⟨decodeRoots⟩
|
| 303 |
+
|
| 304 |
+
/-! ## Dependency Configuration Decoders -/
|
| 305 |
+
|
| 306 |
+
protected def DependencySrc.decodeToml (t : Table) (ref := Syntax.missing) : EDecodeM DependencySrc := do
|
| 307 |
+
let typeVal ← t.decodeValue `type
|
| 308 |
+
match (← typeVal.decodeString) with
|
| 309 |
+
| "path" =>
|
| 310 |
+
return .path (← t.decode `dir)
|
| 311 |
+
| "git" => ensureDecode do
|
| 312 |
+
return .git (← t.tryDecode `url ref) (← t.tryDecode? `rev) (← t.tryDecode? `subDir)
|
| 313 |
+
| _ =>
|
| 314 |
+
throwDecodeErrorAt typeVal.ref "expected one of 'path' or 'git'"
|
| 315 |
+
|
| 316 |
+
instance : DecodeToml DependencySrc := ⟨fun v => do DependencySrc.decodeToml (← v.decodeTable) v.ref⟩
|
| 317 |
+
|
| 318 |
+
protected def Dependency.decodeToml (t : Table) (ref := Syntax.missing) : EDecodeM Dependency := ensureDecode do
|
| 319 |
+
let name ← stringToLegalOrSimpleName <$> t.tryDecode `name ref
|
| 320 |
+
let rev? ← t.tryDecode? `rev
|
| 321 |
+
let src? : Option DependencySrc ← id do
|
| 322 |
+
if let some dir ← t.tryDecode? `path then
|
| 323 |
+
return some <| .path dir
|
| 324 |
+
else if let some g := t.find? `git then
|
| 325 |
+
match g with
|
| 326 |
+
| .string _ url =>
|
| 327 |
+
return some <| .git url rev? (← t.tryDecode? `subDir)
|
| 328 |
+
| .table ref t =>
|
| 329 |
+
return some <| .git (← t.tryDecode `url ref) rev? (← t.tryDecode? `subDir)
|
| 330 |
+
| _ =>
|
| 331 |
+
modify (·.push <| .mk g.ref "expected string or table")
|
| 332 |
+
return default
|
| 333 |
+
else
|
| 334 |
+
t.tryDecode? `source
|
| 335 |
+
let scope ← t.tryDecodeD `scope ""
|
| 336 |
+
let version? ← id do
|
| 337 |
+
if let some ver ← t.tryDecode? `version then
|
| 338 |
+
return some ver
|
| 339 |
+
else if let some rev := rev? then
|
| 340 |
+
return if src?.isSome then none else some s!"git#{rev}"
|
| 341 |
+
else
|
| 342 |
+
return none
|
| 343 |
+
let opts ← t.tryDecodeD `options {}
|
| 344 |
+
return {name, scope, version?, src?, opts}
|
| 345 |
+
|
| 346 |
+
instance : DecodeToml Dependency := ⟨fun v => do Dependency.decodeToml (← v.decodeTable) v.ref⟩
|
| 347 |
+
|
| 348 |
+
/-! ## Package & Target Configuration Decoders -/
|
| 349 |
+
|
| 350 |
+
private def genDecodeToml
|
| 351 |
+
(cmds : Array Command)
|
| 352 |
+
(tyName : Name) [info : ConfigInfo tyName] (takesName : Bool)
|
| 353 |
+
(exclude : Array Name := {})
|
| 354 |
+
: MacroM (Array Command) := do
|
| 355 |
+
let init ← `(TomlFieldInfos.empty)
|
| 356 |
+
let ty := if takesName then Syntax.mkCApp tyName #[mkIdent `n] else mkCIdent tyName
|
| 357 |
+
let infos ← info.fields.foldlM (init := init) fun infos {name, parent, ..} =>
|
| 358 |
+
if parent || exclude.contains name then
|
| 359 |
+
return infos
|
| 360 |
+
else
|
| 361 |
+
`($infos |>.insert $(quote name))
|
| 362 |
+
let instId ← mkIdentFromRef <| `_root_ ++ tyName.str "instConfigTomlInfo"
|
| 363 |
+
let cmds ← cmds.push <$> `(instance $instId:ident : ConfigTomlInfo $ty := ⟨$infos⟩)
|
| 364 |
+
let decId ← mkIdentFromRef <| `_root_ ++ tyName.str "decodeToml"
|
| 365 |
+
let cmds ← cmds.push <$> `(protected def $decId (t : Table) : DecodeM $ty := t.decodeConfig)
|
| 366 |
+
let instId ← mkIdentFromRef <| `_root_ ++ tyName.str "instDecodeToml"
|
| 367 |
+
let cmds ← cmds.push <$> `(instance $instId:ident : DecodeToml $ty := ⟨decodeTableValue $decId⟩)
|
| 368 |
+
return cmds
|
| 369 |
+
|
| 370 |
+
local macro "gen_toml_decoders%" : command => do
|
| 371 |
+
let cmds := #[]
|
| 372 |
+
-- Targets
|
| 373 |
+
let cmds ← genDecodeToml cmds ``LeanConfig false
|
| 374 |
+
let cmds ← genDecodeToml cmds ``LeanLibConfig true
|
| 375 |
+
(exclude := #[`nativeFacets])
|
| 376 |
+
let cmds ← genDecodeToml cmds ``LeanExeConfig true
|
| 377 |
+
(exclude := #[`nativeFacets])
|
| 378 |
+
let cmds ← genDecodeToml cmds ``InputFileConfig true
|
| 379 |
+
let cmds ← genDecodeToml cmds ``InputDirConfig true
|
| 380 |
+
-- Package
|
| 381 |
+
let cmds ← genDecodeToml cmds ``WorkspaceConfig false
|
| 382 |
+
let cmds ← genDecodeToml cmds ``PackageConfig true
|
| 383 |
+
return ⟨mkNullNode cmds⟩
|
| 384 |
+
|
| 385 |
+
gen_toml_decoders%
|
| 386 |
+
|
| 387 |
+
def decodeTargetDecls
|
| 388 |
+
(pkg : Name) (t : Table)
|
| 389 |
+
: DecodeM (Array (PConfigDecl pkg) × DNameMap (NConfigDecl pkg)) := do
|
| 390 |
+
let r := (#[], {})
|
| 391 |
+
let r ← go r LeanLib.keyword LeanLib.configKind LeanLibConfig.decodeToml
|
| 392 |
+
let r ← go r LeanExe.keyword LeanExe.configKind LeanExeConfig.decodeToml
|
| 393 |
+
let r ← go r InputFile.keyword InputFile.configKind InputFileConfig.decodeToml
|
| 394 |
+
let r ← go r InputDir.keyword InputDir.configKind InputDirConfig.decodeToml
|
| 395 |
+
return r
|
| 396 |
+
where
|
| 397 |
+
go r kw kind (decode : {n : Name} → Table → DecodeM (ConfigType kind pkg n)) := do
|
| 398 |
+
let some tableArrayVal := t.find? kw | return r
|
| 399 |
+
let some vals ← tryDecode? tableArrayVal.decodeValueArray | return r
|
| 400 |
+
vals.foldlM (init := r) fun r val => do
|
| 401 |
+
let some t ← tryDecode? val.decodeTable | return r
|
| 402 |
+
let some name ← tryDecode? <| stringToLegalOrSimpleName <$> t.decode `name
|
| 403 |
+
| return r
|
| 404 |
+
let (decls, map) := r
|
| 405 |
+
if let some orig := map.find? name then
|
| 406 |
+
modify fun es => es.push <| .mk val.ref s!"\
|
| 407 |
+
{pkg}: target '{name}' was already defined as a '{orig.kind}', \
|
| 408 |
+
but then redefined as a '{kind}'"
|
| 409 |
+
return (decls, map)
|
| 410 |
+
else
|
| 411 |
+
let config ← @decode name t
|
| 412 |
+
let decl : NConfigDecl pkg name :=
|
| 413 |
+
-- Safety: By definition, config kind = facet kind for declarative configurations.
|
| 414 |
+
unsafe {pkg, name, kind, config, wf_data := lcProof}
|
| 415 |
+
return (decls.push decl.toPConfigDecl, map.insert name decl)
|
| 416 |
+
|
| 417 |
+
/-! ## Root Loader -/
|
| 418 |
+
|
| 419 |
+
/-- Load a `Package` from a Lake configuration file written in TOML. -/
|
| 420 |
+
def loadTomlConfig (cfg: LoadConfig) : LogIO Package := do
|
| 421 |
+
let input ← IO.FS.readFile cfg.configFile
|
| 422 |
+
let ictx := mkInputContext input cfg.relConfigFile.toString
|
| 423 |
+
match (← loadToml ictx |>.toBaseIO) with
|
| 424 |
+
| .ok table =>
|
| 425 |
+
let .ok pkg errs := EStateM.run (s := #[]) do
|
| 426 |
+
let name ← stringToLegalOrSimpleName <$> table.tryDecode `name
|
| 427 |
+
let config ← @PackageConfig.decodeToml name table
|
| 428 |
+
let (targetDecls, targetDeclMap) ← decodeTargetDecls name table
|
| 429 |
+
let defaultTargets ← table.tryDecodeD `defaultTargets #[]
|
| 430 |
+
let defaultTargets := defaultTargets.map stringToLegalOrSimpleName
|
| 431 |
+
let depConfigs ← table.tryDecodeD `require #[]
|
| 432 |
+
return {
|
| 433 |
+
name := name
|
| 434 |
+
dir := cfg.pkgDir
|
| 435 |
+
relDir := cfg.relPkgDir
|
| 436 |
+
configFile := cfg.configFile
|
| 437 |
+
relConfigFile := cfg.relConfigFile
|
| 438 |
+
scope := cfg.scope
|
| 439 |
+
remoteUrl := cfg.remoteUrl
|
| 440 |
+
config, depConfigs, targetDecls, targetDeclMap
|
| 441 |
+
defaultTargets
|
| 442 |
+
}
|
| 443 |
+
if errs.isEmpty then
|
| 444 |
+
return pkg
|
| 445 |
+
else
|
| 446 |
+
errorWithLog <| errs.forM fun {ref, msg} =>
|
| 447 |
+
let pos := ictx.fileMap.toPosition <| ref.getPos?.getD 0
|
| 448 |
+
logError <| mkErrorStringWithPos ictx.fileName pos msg
|
| 449 |
+
| .error log =>
|
| 450 |
+
errorWithLog <| log.forM fun msg => do logError (← msg.toString)
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Load/Workspace.lean
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.Load.Resolve
|
| 8 |
+
import Lake.Build.InitFacets
|
| 9 |
+
|
| 10 |
+
/-! # Workspace Loader
|
| 11 |
+
|
| 12 |
+
This module contains the main definitions for loading a complete Lake workspace.
|
| 13 |
+
-/
|
| 14 |
+
|
| 15 |
+
open Lean
|
| 16 |
+
|
| 17 |
+
namespace Lake
|
| 18 |
+
|
| 19 |
+
/--
|
| 20 |
+
Load a `Workspace` for a Lake package by elaborating its configuration file.
|
| 21 |
+
Does not resolve dependencies.
|
| 22 |
+
-/
|
| 23 |
+
def loadWorkspaceRoot (config : LoadConfig) : LogIO Workspace := do
|
| 24 |
+
Lean.searchPathRef.set config.lakeEnv.leanSearchPath
|
| 25 |
+
let (root, env?) ← loadPackageCore "[root]" config
|
| 26 |
+
let root ← root.loadInputsFrom config.lakeEnv
|
| 27 |
+
let ws : Workspace := {
|
| 28 |
+
root
|
| 29 |
+
lakeEnv := config.lakeEnv
|
| 30 |
+
lakeArgs? := config.lakeArgs?
|
| 31 |
+
facetConfigs := initFacetConfigs
|
| 32 |
+
}
|
| 33 |
+
if let some env := env? then
|
| 34 |
+
IO.ofExcept <| ws.addFacetsFromEnv env config.leanOpts
|
| 35 |
+
else
|
| 36 |
+
return ws
|
| 37 |
+
|
| 38 |
+
/--
|
| 39 |
+
Load a `Workspace` for a Lake package by
|
| 40 |
+
elaborating its configuration file and resolving its dependencies.
|
| 41 |
+
If `updateDeps` is true, updates the manifest before resolving dependencies.
|
| 42 |
+
-/
|
| 43 |
+
def loadWorkspace (config : LoadConfig) : LoggerIO Workspace := do
|
| 44 |
+
let {reconfigure, leanOpts, updateDeps, updateToolchain, packageOverrides, ..} := config
|
| 45 |
+
let ws ← loadWorkspaceRoot config
|
| 46 |
+
if updateDeps then
|
| 47 |
+
ws.updateAndMaterialize {} leanOpts updateToolchain
|
| 48 |
+
else if let some manifest ← Manifest.load? ws.manifestFile then
|
| 49 |
+
ws.materializeDeps manifest leanOpts reconfigure packageOverrides
|
| 50 |
+
else
|
| 51 |
+
ws.updateAndMaterialize {} leanOpts updateToolchain
|
| 52 |
+
|
| 53 |
+
/-- Updates the manifest for the loaded Lake workspace (see `updateAndMaterialize`). -/
|
| 54 |
+
def updateManifest (config : LoadConfig) (toUpdate : NameSet := {})
|
| 55 |
+
: LoggerIO Unit := do
|
| 56 |
+
let {leanOpts, updateToolchain, ..} := config
|
| 57 |
+
let ws ← loadWorkspaceRoot config
|
| 58 |
+
discard <| ws.updateAndMaterialize toUpdate leanOpts updateToolchain
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Toml/Data.lean
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lake.Toml.Data.Value
|
| 8 |
+
|
| 9 |
+
/-!
|
| 10 |
+
# TOML Data Types
|
| 11 |
+
-/
|
backend/core/leanprover--lean4---v4.22.0/src/lean/lake/Lake/Toml/Decode.lean
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Mac Malone. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Mac Malone
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.System.FilePath
|
| 8 |
+
import Lake.Toml.Data
|
| 9 |
+
|
| 10 |
+
open Lean
|
| 11 |
+
|
| 12 |
+
/-!
|
| 13 |
+
# TOML Decoders
|
| 14 |
+
|
| 15 |
+
This module contains definitions to assist in decoding TOML data values
|
| 16 |
+
into concrete user types.
|
| 17 |
+
-/
|
| 18 |
+
|
| 19 |
+
namespace Lake
|
| 20 |
+
|
| 21 |
+
structure Toml.DecodeError where
|
| 22 |
+
ref : Syntax
|
| 23 |
+
msg : String
|
| 24 |
+
|
| 25 |
+
abbrev Toml.DecodeM := EStateM Empty (Array DecodeError)
|
| 26 |
+
|
| 27 |
+
abbrev Toml.EDecodeM := EStateM Unit (Array DecodeError)
|
| 28 |
+
|
| 29 |
+
class DecodeToml (α : Type) where
|
| 30 |
+
decode (v : Toml.Value) : Toml.EDecodeM α
|
| 31 |
+
|
| 32 |
+
abbrev decodeToml [DecodeToml α] (v : Toml.Value) : Toml.EDecodeM α :=
|
| 33 |
+
DecodeToml.decode v
|
| 34 |
+
|
| 35 |
+
namespace Toml
|
| 36 |
+
|
| 37 |
+
/-- Run the decode action. If there were errors, throw. Otherwise, return the result. -/
|
| 38 |
+
@[inline] def ensureDecode (x : DecodeM α) : EDecodeM α := fun es =>
|
| 39 |
+
match x es with
|
| 40 |
+
| .ok a es => if es.isEmpty then .ok a es else .error () es
|
| 41 |
+
|
| 42 |
+
/-- Run the decode action. If it fails, keep the errors but return `default`. -/
|
| 43 |
+
@[inline] def tryDecodeD (default : α) (x : EDecodeM α) : DecodeM α := fun es =>
|
| 44 |
+
match x es with
|
| 45 |
+
| .ok a es => .ok a es
|
| 46 |
+
| .error _ es => .ok default es
|
| 47 |
+
|
| 48 |
+
/-- Run the decode action. If it fails, keep the errors but return `none`. -/
|
| 49 |
+
@[inline] def tryDecode? (x : EDecodeM α) : DecodeM (Option α) := fun es =>
|
| 50 |
+
match x es with
|
| 51 |
+
| .ok a es => .ok (some a) es
|
| 52 |
+
| .error _ es => .ok none es
|
| 53 |
+
|
| 54 |
+
/-- Run the decode action. If it fails, keep the errors but return `Inhabited.default`. -/
|
| 55 |
+
@[inline] def tryDecode [Inhabited α] (x : EDecodeM α) : DecodeM α :=
|
| 56 |
+
tryDecodeD default x
|
| 57 |
+
|
| 58 |
+
/--
|
| 59 |
+
If the value is not `none`, run the decode action.
|
| 60 |
+
If it fails, add the errors to the state and return `default`.
|
| 61 |
+
-/
|
| 62 |
+
@[inline] def optDecodeD (default : β) (a? : Option α) (f : α → EDecodeM β) : DecodeM β :=
|
| 63 |
+
match a? with
|
| 64 |
+
| some a => tryDecodeD default (f a)
|
| 65 |
+
| none => pure default
|
| 66 |
+
|
| 67 |
+
/--
|
| 68 |
+
If the value is not `none`, run the decode action.
|
| 69 |
+
If it fails, add the errors to the state and return `Inhabited.default`.
|
| 70 |
+
-/
|
| 71 |
+
@[inline] def optDecode [Inhabited β] (a? : Option α) (f : α → EDecodeM β) : DecodeM β :=
|
| 72 |
+
optDecodeD default a? f
|
| 73 |
+
|
| 74 |
+
/--
|
| 75 |
+
If the value is not `none`, run the decode action.
|
| 76 |
+
If it fails, add the errors to the state and return `none`.
|
| 77 |
+
Otherwise, return the result in `some`.
|
| 78 |
+
-/
|
| 79 |
+
@[inline] def optDecode? (a? : Option α) (f : α → EDecodeM β) : DecodeM (Option β) :=
|
| 80 |
+
optDecodeD none a? fun a => some <$> f a
|
| 81 |
+
|
| 82 |
+
/--
|
| 83 |
+
If either action errors, throw the concatenated errors.
|
| 84 |
+
Otherwise, if no errors, combine the results with `f`.
|
| 85 |
+
-/
|
| 86 |
+
def mergeErrors (x₁ : EDecodeM α) (x₂ : EDecodeM β) (f : α → β → γ) : EDecodeM γ := fun es =>
|
| 87 |
+
match x₁ es with
|
| 88 |
+
| .ok a es =>
|
| 89 |
+
match x₂ es with
|
| 90 |
+
| .ok b es => .ok (f a b) es
|
| 91 |
+
| .error _ es => .error () es
|
| 92 |
+
| .error _ es => .error () es
|
| 93 |
+
|
| 94 |
+
@[inline] def throwDecodeErrorAt (ref : Syntax) (msg : String) : EDecodeM α :=
|
| 95 |
+
fun es => .error () (es.push {ref, msg})
|
| 96 |
+
|
| 97 |
+
/-- Decode an array of TOML values, merging any errors from the elements into a single array. -/
|
| 98 |
+
def decodeArray [dec : DecodeToml α] (vs : Array Value) : EDecodeM (Array α) :=
|
| 99 |
+
vs.foldl (init := pure (.mkEmpty vs.size)) fun vs v =>
|
| 100 |
+
mergeErrors vs (dec.decode v) Array.push
|
| 101 |
+
|
| 102 |
+
instance : DecodeToml Value := ⟨pure⟩
|
| 103 |
+
|
| 104 |
+
namespace Value
|
| 105 |
+
|
| 106 |
+
def decodeString : Value → EDecodeM String
|
| 107 |
+
| .string _ v => .ok v
|
| 108 |
+
| x => throwDecodeErrorAt x.ref "expected string"
|
| 109 |
+
|
| 110 |
+
instance : DecodeToml String := ⟨decodeString⟩
|
| 111 |
+
instance : DecodeToml System.FilePath := ⟨(.mk <$> decodeToml ·)⟩
|
| 112 |
+
|
| 113 |
+
def decodeName (v : Value) : EDecodeM Name := do
|
| 114 |
+
match (← v.decodeString).toName with
|
| 115 |
+
| .anonymous => throwDecodeErrorAt v.ref "expected name"
|
| 116 |
+
| n => pure n
|
| 117 |
+
|
| 118 |
+
instance : DecodeToml Lean.Name := ⟨decodeName⟩
|
| 119 |
+
|
| 120 |
+
def decodeInt : Value → EDecodeM Int
|
| 121 |
+
| .integer _ v => .ok v
|
| 122 |
+
| x => throwDecodeErrorAt x.ref "expected integer"
|
| 123 |
+
|
| 124 |
+
instance : DecodeToml Int := ⟨decodeInt⟩
|
| 125 |
+
|
| 126 |
+
def decodeNat : Value → EDecodeM Nat
|
| 127 |
+
| .integer _ (.ofNat v) => .ok v
|
| 128 |
+
| x => throwDecodeErrorAt x.ref "expected nonnegative integer"
|
| 129 |
+
|
| 130 |
+
instance : DecodeToml Nat := ⟨decodeNat⟩
|
| 131 |
+
|
| 132 |
+
def decodeFloat : Value → EDecodeM Float
|
| 133 |
+
| .float _ v => .ok v
|
| 134 |
+
| x => throwDecodeErrorAt x.ref "expected float"
|
| 135 |
+
|
| 136 |
+
instance : DecodeToml Float := ⟨decodeFloat⟩
|
| 137 |
+
|
| 138 |
+
def decodeBool : Value → EDecodeM Bool
|
| 139 |
+
| .boolean _ v => .ok v
|
| 140 |
+
| x => throwDecodeErrorAt x.ref "expected boolean"
|
| 141 |
+
|
| 142 |
+
instance : DecodeToml Bool := ⟨decodeBool⟩
|
| 143 |
+
|
| 144 |
+
def decodeDateTime : Value → EDecodeM DateTime
|
| 145 |
+
| .dateTime _ v => .ok v
|
| 146 |
+
| x => throwDecodeErrorAt x.ref "expected date-time"
|
| 147 |
+
|
| 148 |
+
instance : DecodeToml DateTime := ⟨decodeDateTime⟩
|
| 149 |
+
|
| 150 |
+
def decodeValueArray : Value → EDecodeM (Array Value)
|
| 151 |
+
| .array _ vs => .ok vs
|
| 152 |
+
| x => throwDecodeErrorAt x.ref "expected array"
|
| 153 |
+
|
| 154 |
+
@[inline] protected def decodeArray [dec : DecodeToml α] (v : Value) : EDecodeM (Array α) := do
|
| 155 |
+
decodeArray (dec := dec) (← v.decodeValueArray)
|
| 156 |
+
|
| 157 |
+
instance [DecodeToml α] : DecodeToml (Array α) := ⟨Value.decodeArray⟩
|
| 158 |
+
|
| 159 |
+
@[inline] def decodeArrayOrSingleton [dec : DecodeToml α] : Value → EDecodeM (Array α)
|
| 160 |
+
| .array _ vs => decodeArray (dec := dec) vs
|
| 161 |
+
| v => Array.singleton <$> dec.decode v
|
| 162 |
+
|
| 163 |
+
def decodeTable : Value → EDecodeM Table
|
| 164 |
+
| .table _ t => .ok t
|
| 165 |
+
| x => throwDecodeErrorAt x.ref "expected table"
|
| 166 |
+
|
| 167 |
+
instance : DecodeToml Table := ⟨(·.decodeTable)⟩
|
| 168 |
+
|
| 169 |
+
end Value
|
| 170 |
+
|
| 171 |
+
def decodeKeyval [dec : DecodeToml α] (k : Name) (v : Value) : EDecodeM α := fun es =>
|
| 172 |
+
let iniPos := es.size
|
| 173 |
+
let f es := es.mapIdx fun i e =>
|
| 174 |
+
if iniPos ≤ i then {e with msg := s!"key {ppKey k}: {e.msg}"} else e
|
| 175 |
+
match dec.decode v es with
|
| 176 |
+
| .ok a es => .ok a (f es)
|
| 177 |
+
| .error e es => .error e (f es)
|
| 178 |
+
|
| 179 |
+
namespace Table
|
| 180 |
+
|
| 181 |
+
def decodeValue (t : Table) (k : Name) (ref := Syntax.missing) : EDecodeM Value := do
|
| 182 |
+
let some a := t.find? k
|
| 183 |
+
| throwDecodeErrorAt ref s!"missing required key: {ppKey k}"
|
| 184 |
+
return a
|
| 185 |
+
|
| 186 |
+
@[inline] def decode [dec : DecodeToml α] (t : Table) (k : Name) (ref := Syntax.missing) : EDecodeM α := do
|
| 187 |
+
let a ← t.decodeValue k ref
|
| 188 |
+
decodeKeyval (dec := dec) k a
|
| 189 |
+
|
| 190 |
+
@[inline] def decode? [dec : DecodeToml α] (t : Table) (k : Name) : EDecodeM (Option α) := do
|
| 191 |
+
t.find? k |>.mapM fun v => decodeKeyval (dec := dec) k v
|
| 192 |
+
|
| 193 |
+
def decodeNameMap [dec : DecodeToml α] (t : Toml.Table) : EDecodeM (NameMap α) := do
|
| 194 |
+
t.items.foldl (init := pure {}) fun m (k,v) =>
|
| 195 |
+
mergeErrors m (dec.decode v) fun m v => m.insert k v
|
| 196 |
+
|
| 197 |
+
instance [DecodeToml α] : DecodeToml (NameMap α) := ⟨(do decodeNameMap <| ← ·.decodeTable)⟩
|
| 198 |
+
|
| 199 |
+
@[inline] protected def tryDecode [Inhabited α] [dec : DecodeToml α] (t : Table) (k : Name) (ref := Syntax.missing) : DecodeM α := do
|
| 200 |
+
tryDecode <| t.decode (dec := dec) k ref
|
| 201 |
+
|
| 202 |
+
@[inline] protected def tryDecode? [dec : DecodeToml α] (t : Table) (k : Name) : DecodeM (Option α) :=
|
| 203 |
+
optDecode? (t.find? k) dec.decode
|
| 204 |
+
|
| 205 |
+
@[inline] protected def tryDecodeD [dec : DecodeToml α] (k : Name) (default : α) (t : Table) : DecodeM α :=
|
| 206 |
+
optDecodeD default (t.find? k) dec.decode
|
| 207 |
+
|
| 208 |
+
end Table
|