Skip to content
This repository was archived by the owner on Oct 25, 2023. It is now read-only.

Commit aa10cea

Browse files
committed
feat: preliminary custom module facets
1 parent b082443 commit aa10cea

13 files changed

+205
-84
lines changed

Diff for: Lake/Build/Index.lean

+7
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,13 @@ the initial set of Lake package facets (e.g., `extraDep`).
153153
| .module mod facet =>
154154
if let some build := moduleBuildMap.find? facet then
155155
build mod
156+
else if let some config := (← getWorkspace).findModuleFacetConfig? facet then
157+
if h : facet = config.facet then
158+
have : DynamicType ModuleData facet (ActiveBuildTarget config.resultType) :=
159+
by simp [h, eq_dynamic_type]⟩
160+
mkModuleFacetBuild config.build mod
161+
else
162+
error "module facet's name in the configuration does not match the name it was registered with"
156163
else
157164
error s!"do not know how to build module facet `{facet}`"
158165
| .package pkg facet =>

Diff for: Lake/CLI/Build.lean

+16-14
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def parsePackageSpec (ws : Workspace) (spec : String) : Except CliError Package
2525
| none => throw <| CliError.unknownPackage spec
2626

2727
open Module in
28-
def resolveModuleTarget (mod : Module) (facet : String) : Except CliError OpaqueTarget :=
28+
def resolveModuleTarget (ws : Workspace) (mod : Module) (facet : String) : Except CliError OpaqueTarget :=
2929
if facet.isEmpty || facet == "bin" then
3030
return mod.facetTarget binFacet
3131
else if facet == "ilean" then
@@ -38,6 +38,8 @@ def resolveModuleTarget (mod : Module) (facet : String) : Except CliError Opaque
3838
return mod.facetTarget oFacet
3939
else if facet == "dynlib" then
4040
return mod.facetTarget dynlibFacet
41+
else if let some config := ws.findModuleFacetConfig? facet then
42+
return mod.facetTarget config.facet
4143
else
4244
throw <| CliError.unknownFacet "module" facet
4345

@@ -57,7 +59,7 @@ def resolveExeTarget (exe : LeanExe) (facet : String) : Except CliError OpaqueTa
5759
else
5860
throw <| CliError.unknownFacet "executable" facet
5961

60-
def resolveTargetInPackage (pkg : Package) (target : Name) (facet : String) : Except CliError OpaqueTarget :=
62+
def resolveTargetInPackage (ws : Workspace) (pkg : Package) (target : Name) (facet : String) : Except CliError OpaqueTarget :=
6163
if let some exe := pkg.findLeanExe? target then
6264
resolveExeTarget exe facet
6365
else if let some lib := pkg.findExternLib? target then
@@ -68,20 +70,20 @@ def resolveTargetInPackage (pkg : Package) (target : Name) (facet : String) : Ex
6870
else if let some lib := pkg.findLeanLib? target then
6971
resolveLibTarget lib facet
7072
else if let some mod := pkg.findModule? target then
71-
resolveModuleTarget mod facet
73+
resolveModuleTarget ws mod facet
7274
else
7375
throw <| CliError.missingTarget pkg.name (target.toString false)
7476

75-
def resolveDefaultPackageTarget (pkg : Package) : Except CliError OpaqueTarget :=
77+
def resolveDefaultPackageTarget (ws : Workspace) (pkg : Package) : Except CliError OpaqueTarget :=
7678
if pkg.defaultTargets.isEmpty then
7779
return pkg.defaultTarget
7880
else
7981
return Target.collectOpaqueArray <| ←
80-
pkg.defaultTargets.mapM (resolveTargetInPackage pkg · "")
82+
pkg.defaultTargets.mapM (resolveTargetInPackage ws pkg · "")
8183

82-
def resolvePackageTarget (pkg : Package) (facet : String) : Except CliError OpaqueTarget :=
84+
def resolvePackageTarget (ws : Workspace) (pkg : Package) (facet : String) : Except CliError OpaqueTarget :=
8385
if facet.isEmpty then
84-
resolveDefaultPackageTarget pkg
86+
resolveDefaultPackageTarget ws pkg
8587
else if facet == "exe" || facet == "bin" then
8688
return pkg.exeTarget.withoutInfo
8789
else if facet == "staticLib" then
@@ -104,24 +106,24 @@ def resolveTargetInWorkspace (ws : Workspace) (spec : String) (facet : String) :
104106
else if let some lib := ws.findLeanLib? spec.toName then
105107
resolveLibTarget lib facet
106108
else if let some pkg := ws.findPackage? spec.toName then
107-
resolvePackageTarget pkg facet
109+
resolvePackageTarget ws pkg facet
108110
else if let some mod := ws.findModule? spec.toName then
109-
resolveModuleTarget mod facet
111+
resolveModuleTarget ws mod facet
110112
else
111113
throw <| CliError.unknownTarget spec
112114

113115
def resolveTargetBaseSpec (ws : Workspace) (spec : String) (facet := "") : Except CliError OpaqueTarget := do
114116
match spec.splitOn "/" with
115117
| [spec] =>
116118
if spec.isEmpty then
117-
resolvePackageTarget ws.root facet
119+
resolvePackageTarget ws ws.root facet
118120
else if spec.startsWith "@" then
119121
let pkg ← parsePackageSpec ws <| spec.drop 1
120-
resolvePackageTarget pkg facet
122+
resolvePackageTarget ws pkg facet
121123
else if spec.startsWith "+" then
122124
let mod := spec.drop 1 |>.toName
123125
if let some mod := ws.findModule? mod then
124-
resolveModuleTarget mod facet
126+
resolveModuleTarget ws mod facet
125127
else
126128
throw <| CliError.unknownModule mod
127129
else
@@ -132,11 +134,11 @@ def resolveTargetBaseSpec (ws : Workspace) (spec : String) (facet := "") : Excep
132134
if targetSpec.startsWith "+" then
133135
let mod := targetSpec.drop 1 |>.toName
134136
if let some mod := pkg.findModule? mod then
135-
resolveModuleTarget mod facet
137+
resolveModuleTarget ws mod facet
136138
else
137139
throw <| CliError.unknownModule mod
138140
else
139-
resolveTargetInPackage pkg spec facet
141+
resolveTargetInPackage ws pkg spec facet
140142
| _ =>
141143
throw <| CliError.invalidTargetSpec spec '/'
142144

Diff for: Lake/CLI/Main.lean

+1-1
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ def command : (cmd : String) → CliM PUnit
313313
let target ← show Except _ _ from do
314314
let targets ← targetSpecs.mapM <| parseTargetSpec ws
315315
if targets.isEmpty then
316-
resolveDefaultPackageTarget ws.root
316+
resolveDefaultPackageTarget ws ws.root
317317
else
318318
return Target.collectOpaqueList targets
319319
let ctx ← mkBuildContext ws config.leanInstall config.lakeInstall

Diff for: Lake/Config/Load.lean

+23-13
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Authors: Mac Malone
66
import Lean.Elab.Frontend
77
import Lake.DSL.Attributes
88
import Lake.DSL.Extensions
9-
import Lake.Config.Package
9+
import Lake.Config.ModuleFacetConfig
1010

1111
namespace Lake
1212
open Lean System
@@ -95,6 +95,7 @@ unsafe def loadUnsafe (dir : FilePath) (args : List String := [])
9595
match packageAttr.ext.getState env |>.toList with
9696
| [] => error s!"configuration file is missing a `package` declaration"
9797
| [pkgDeclName] =>
98+
-- Load Package Configuration
9899
let config ← evalPackageDecl env pkgDeclName dir args leanOpts
99100
unless config.dependencies.isEmpty do
100101
logWarning "Syntax for package dependencies has changed. Use the new `require` syntax."
@@ -105,24 +106,33 @@ unsafe def loadUnsafe (dir : FilePath) (args : List String := [])
105106
if config.defaultFacet = PackageFacet.oleans then
106107
logWarning "The `oleans` package facet has been deprecated in favor of `leanLib`."
107108
let config := {config with dependencies := depsExt.getState env ++ config.dependencies}
108-
let scripts ← scriptAttr.ext.getState env |>.foldM (init := {})
109-
fun m d => return m.insert d <| ← evalScriptDecl env d leanOpts
110-
let leanLibConfigs ← leanLibAttr.ext.getState env |>.foldM (init := {}) fun m d =>
111-
let eval := env.evalConstCheck LeanLibConfig leanOpts ``LeanLibConfig d
112-
return m.insert d <| ← IO.ofExcept eval.run.run
113-
let leanExeConfigs ← leanExeAttr.ext.getState env |>.foldM (init := {}) fun m d =>
114-
let eval := env.evalConstCheck LeanExeConfig leanOpts ``LeanExeConfig d
115-
return m.insert d <| ← IO.ofExcept eval.run.run
116-
let externLibConfigs ← externLibAttr.ext.getState env |>.foldM (init := {}) fun m d =>
117-
let eval := env.evalConstCheck ExternLibConfig leanOpts ``ExternLibConfig d
118-
return m.insert d <| ← IO.ofExcept eval.run.run
109+
-- Load Target & Script Configurations
110+
let mkTagMap {α} (attr) (f : Name → IO α) : IO (NameMap α) :=
111+
attr.ext.getState env |>.foldM (init := {}) fun map declName =>
112+
return map.insert declName <| ← f declName
113+
let evalConst (α typeName declName) : IO α :=
114+
IO.ofExcept (env.evalConstCheck α leanOpts typeName declName).run.run
115+
let scripts ← mkTagMap scriptAttr
116+
(evalScriptDecl env · leanOpts)
117+
let leanLibConfigs ← mkTagMap leanLibAttr
118+
(evalConst LeanLibConfig ``LeanLibConfig)
119+
let leanExeConfigs ← mkTagMap leanExeAttr
120+
(evalConst LeanExeConfig ``LeanExeConfig)
121+
let externLibConfigs ← mkTagMap externLibAttr
122+
(evalConst ExternLibConfig ``ExternLibConfig)
123+
let opaqueModuleFacetConfigs ← mkTagMap moduleFacetAttr fun declName =>
124+
match env.evalConst ModuleFacetConfig leanOpts declName with
125+
| .ok config => pure <| OpaqueModuleFacetConfig.mk config
126+
| .error e => throw <| IO.userError e
127+
let defaultTargets := defaultTargetAttr.ext.getState env |>.toArray
128+
-- Construct the Package
119129
if leanLibConfigs.isEmpty && leanExeConfigs.isEmpty && config.defaultFacet ≠ .none then
120130
logWarning <| "Package targets are deprecated. " ++
121131
"Add a `lean_exe` and/or `lean_lib` default target to the package instead."
122-
let defaultTargets := defaultTargetAttr.ext.getState env |>.toArray
123132
return {
124133
dir, config, scripts,
125134
leanLibConfigs, leanExeConfigs, externLibConfigs,
135+
opaqueModuleFacetConfigs,
126136
defaultTargets
127137
}
128138
| _ => error s!"configuration file has multiple `package` declarations"

Diff for: Lake/Config/ModuleFacetConfig.lean

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
import Lake.Build.Info
7+
import Lake.Build.Store
8+
9+
namespace Lake
10+
open Lean System
11+
12+
/-- A Module facet's declarative configuration. -/
13+
structure ModuleFacetConfig where
14+
/-- The name of the facet. -/
15+
facet : WfName
16+
/-- The type of the facet's build result. -/
17+
resultType : Type
18+
/-- The module facet's build function. -/
19+
build : {m : TypeType} →
20+
[Monad m] → [MonadLift BuildM m] → [MonadBuildStore m] →
21+
Module → IndexT m (ActiveBuildTarget resultType)
22+
/-- Proof that module facet's build result is the correctly typed target.-/
23+
data_eq_target : ModuleData facet = ActiveBuildTarget resultType
24+
25+
instance : Inhabited ModuleFacetConfig := ⟨{
26+
facet := &`lean.bin
27+
resultType := PUnit
28+
build := default
29+
data_eq_target := eq_dynamic_type
30+
}⟩
31+
32+
hydrate_opaque_type OpaqueModuleFacetConfig ModuleFacetConfig
33+
34+
instance {cfg : ModuleFacetConfig}
35+
: DynamicType ModuleData cfg.facet (ActiveBuildTarget cfg.resultType) :=
36+
⟨cfg.data_eq_target⟩
37+
38+
/-- Try to find a module facet configuration in the package with the given name . -/
39+
def Package.findModuleFacetConfig? (name : Name) (self : Package) : Option ModuleFacetConfig :=
40+
self.opaqueModuleFacetConfigs.find? name |>.map (·.get)

Diff for: Lake/Config/Monad.lean

+9
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ variable [Functor m]
5555
@[inline] def findModule? (mod : Name) : m (Option Module) :=
5656
(·.findModule? mod) <$> getWorkspace
5757

58+
@[inline] def findLeanExe? (mod : Name) : m (Option LeanExe) :=
59+
(·.findLeanExe? mod) <$> getWorkspace
60+
61+
@[inline] def findLeanLib? (mod : Name) : m (Option LeanLib) :=
62+
(·.findLeanLib? mod) <$> getWorkspace
63+
64+
@[inline] def findExternLib? (mod : Name) : m (Option ExternLib) :=
65+
(·.findExternLib? mod) <$> getWorkspace
66+
5867
@[inline] def getOleanPath : m SearchPath :=
5968
(·.oleanPath) <$> getWorkspace
6069

Diff for: Lake/Config/Opaque.lean

+6-9
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,15 @@ Copyright (c) 2021 Mac Malone. All rights reserved.
33
Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Mac Malone
55
-/
6-
namespace Lake
6+
import Lake.Util.Opaque
77

8-
opaque OpaquePackagePointed : NonemptyType.{0}
8+
namespace Lake
99

1010
/-- Opaque reference to a `Package` used for forward declaration. -/
11-
def OpaquePackage : Type := OpaquePackagePointed.type
12-
13-
instance : Nonempty OpaquePackage := OpaquePackagePointed.property
14-
15-
opaque OpaqueWorkspacePointed : NonemptyType.{0}
11+
declare_opaque_type OpaquePackage
1612

1713
/-- Opaque reference to a `Workspace` used for forward declaration. -/
18-
def OpaqueWorkspace : Type := OpaqueWorkspacePointed.type
14+
declare_opaque_type OpaqueWorkspace
1915

20-
instance : Nonempty OpaqueWorkspace := OpaqueWorkspacePointed.property
16+
/-- Opaque reference to a `ModuleFacetConfig` used for forward declaration. -/
17+
declare_opaque_type OpaqueModuleFacetConfig

Diff for: Lake/Config/Package.lean

+4-21
Original file line numberDiff line numberDiff line change
@@ -263,37 +263,20 @@ structure Package where
263263
leanExeConfigs : NameMap LeanExeConfig := {}
264264
/-- External library targets for the package. -/
265265
externLibConfigs : NameMap ExternLibConfig := {}
266+
/-- (Opaque) module facets defined in the package. -/
267+
opaqueModuleFacetConfigs : NameMap OpaqueModuleFacetConfig := {}
266268
/--
267269
The names of the package's targets to build by default
268270
(i.e., on a bare `lake build` of the package).
269271
-/
270272
defaultTargets : Array Name := #[]
271273
deriving Inhabited
272274

275+
hydrate_opaque_type OpaquePackage Package
276+
273277
abbrev PackageSet := RBTree Package (·.name.quickCmp ·.name)
274278
@[inline] def PackageSet.empty : PackageSet := RBTree.empty
275279

276-
namespace OpaquePackage
277-
278-
unsafe def unsafeMk (pkg : Package) : OpaquePackage :=
279-
unsafeCast pkg
280-
281-
@[implementedBy unsafeMk] opaque mk (pkg : Package) : OpaquePackage
282-
283-
instance : Coe Package OpaquePackage := ⟨mk⟩
284-
instance : Inhabited OpaquePackage := ⟨mk Inhabited.default⟩
285-
286-
unsafe def unsafeGet (self : OpaquePackage) : Package :=
287-
unsafeCast self
288-
289-
@[implementedBy unsafeGet] opaque get (self : OpaquePackage) : Package
290-
291-
instance : Coe OpaquePackage Package := ⟨get⟩
292-
293-
@[simp] axiom get_mk {pkg : Package} : get (mk pkg) = pkg
294-
295-
end OpaquePackage
296-
297280
/--
298281
An alternate signature for package configurations
299282
that permits more dynamic configurations, but is still pure.

Diff for: Lake/Config/Workspace.lean

+14-26
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Mac Malone
55
-/
66
import Lean.Util.Paths
7-
import Lake.Config.LeanExe
8-
import Lake.Config.ExternLib
7+
import Lake.Config.ModuleFacetConfig
98

109
open System
1110
open Lean (LeanPaths)
@@ -23,26 +22,7 @@ structure Workspace where
2322
packageMap : NameMap Package := {}
2423
deriving Inhabited
2524

26-
namespace OpaqueWorkspace
27-
28-
unsafe def unsafeMk (ws : Workspace) : OpaqueWorkspace :=
29-
unsafeCast ws
30-
31-
@[implementedBy unsafeMk] opaque mk (ws : Workspace) : OpaqueWorkspace
32-
33-
instance : Coe Workspace OpaqueWorkspace := ⟨mk⟩
34-
instance : Inhabited OpaqueWorkspace := ⟨mk Inhabited.default⟩
35-
36-
unsafe def unsafeGet (self : OpaqueWorkspace) : Workspace :=
37-
unsafeCast self
38-
39-
@[implementedBy unsafeGet] opaque get (self : OpaqueWorkspace) : Workspace
40-
41-
instance : Coe OpaqueWorkspace Workspace := ⟨get⟩
42-
43-
@[simp] axiom get_mk {ws : Workspace} : get (mk ws) = ws
44-
45-
end OpaqueWorkspace
25+
hydrate_opaque_type OpaqueWorkspace Workspace
4626

4727
namespace Workspace
4828

@@ -86,6 +66,10 @@ def findPackage? (pkg : Name) (self : Workspace) : Option Package :=
8666
def isLocalModule (mod : Name) (self : Workspace) : Bool :=
8767
self.packageMap.any fun _ pkg => pkg.isLocalModule mod
8868

69+
/-- Check if the module is buildable by any package in the workspace. -/
70+
def isBuildableModule (mod : Name) (self : Workspace) : Bool :=
71+
self.packageMap.any fun _ pkg => pkg.isBuildableModule mod
72+
8973
/-- Locate the named module in the workspace (if it is local to it). -/
9074
def findModule? (mod : Name) (self : Workspace) : Option Module :=
9175
self.packageArray.findSome? (·.findModule? mod)
@@ -98,10 +82,14 @@ def findLeanLib? (name : Name) (self : Workspace) : Option LeanLib :=
9882
def findLeanExe? (name : Name) (self : Workspace) : Option LeanExe :=
9983
self.packageArray.findSome? fun pkg => pkg.findLeanExe? name
10084

101-
/-- Get the workspace's external library with the given name. -/
85+
/-- Try to find an external library in the workspace with the given name. -/
10286
def findExternLib? (name : Name) (self : Workspace) : Option ExternLib :=
10387
self.packageArray.findSome? fun pkg => pkg.findExternLib? name
10488

89+
/-- Try to find a module facet configuration in the workspace with the given name. -/
90+
def findModuleFacetConfig? (name : Name) (self : Workspace) : Option ModuleFacetConfig :=
91+
self.packageArray.findSome? fun pkg => pkg.findModuleFacetConfig? name
92+
10593
/-- The `LEAN_PATH` of the workspace. -/
10694
def oleanPath (self : Workspace) : SearchPath :=
10795
self.packageList.map (·.oleanDir)
@@ -111,6 +99,6 @@ def leanSrcPath (self : Workspace) : SearchPath :=
11199
self.packageList.map (·.srcDir)
112100

113101
/-- The `LeanPaths` of the workspace. -/
114-
def leanPaths (self : Workspace) : LeanPaths :=
115-
let pkgs := self.packageList
116-
{ oleanPath := pkgs.map (·.oleanDir), srcPath := pkgs.map (·.srcDir) }
102+
def leanPaths (self : Workspace) : LeanPaths where
103+
oleanPath := self.packageList.map (·.oleanDir)
104+
srcPath := self.packageList.map (·.srcDir)

Diff for: Lake/DSL.lean

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ import Lake.DSL.Package
1111
import Lake.DSL.Script
1212
import Lake.DSL.Require
1313
import Lake.DSL.Targets
14+
import Lake.DSL.ModuleFacet

Diff for: Lake/DSL/Attributes.lean

+3
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,6 @@ initialize defaultTargetAttr : TagAttribute ←
3232
externLibAttr.hasTag env name
3333
unless valid do
3434
throwError "attribute `defaultTarget` can only be used on a target (e.g., `lean_lib`, `lean_exe`)"
35+
36+
initialize moduleFacetAttr : TagAttribute ←
37+
registerTagAttribute `moduleFacet "mark a definition as a Lake module facet"

0 commit comments

Comments
 (0)