Skip to content

Commit 826d6b1

Browse files
committed
feat: use module setup in lake lean & setup-file
1 parent 463bb86 commit 826d6b1

File tree

15 files changed

+104
-61
lines changed

15 files changed

+104
-61
lines changed

src/Lean/Elab/ParseImportsFast.lean

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,16 @@ partial def moduleIdent : Parser := fun input s =>
188188
let s := p input s
189189
match s.error? with
190190
| none => many p input s
191-
| some _ => { pos, error? := none, imports := s.imports.shrink size }
191+
| some _ => { s with pos, error? := none, imports := s.imports.shrink size }
192192

193193
def setIsExported (isExported : Bool) : Parser := fun _ s =>
194-
{ s with isExported := isExported }
194+
{ s with isExported }
195195

196196
def setImportAll (importAll : Bool) : Parser := fun _ s =>
197197
{ s with importAll }
198198

199199
def main : Parser :=
200-
keywordCore "module" (fun _ s => { s with isModule := true }) (fun _ s => s) >>
200+
keywordCore "module" (fun _ s => s) (fun _ s => { s with isModule := true }) >>
201201
keywordCore "prelude" (fun _ s => s.pushImport `Init) (fun _ s => s) >>
202202
many (keywordCore "private" (setIsExported true) (setIsExported false) >>
203203
keyword "import" >>

src/lake/Lake/Build/Facets.lean

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,14 @@ The facet which builds all of a module's dependencies
5454
(i.e., transitive local imports and `--load-dynlib` shared libraries).
5555
Returns the list of shared libraries to load along with their search path.
5656
-/
57-
builtin_facet deps : Module => ModuleDeps
57+
builtin_facet setup : Module => ModuleSetup
58+
59+
/--
60+
The facet which builds all of a module's dependencies
61+
(i.e., transitive local imports and `--load-dynlib` shared libraries).
62+
Returns the list of shared libraries to load along with their search path.
63+
-/
64+
builtin_facet deps : Module => Opaque
5865

5966
/--
6067
The core build facet of a Lean file.

src/lake/Lake/Build/Info.lean

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ namespace Module
192192
@[inherit_doc precompileImportsFacet] abbrev precompileImports (self : Module) :=
193193
self.facetCore precompileImportsFacet
194194

195+
@[inherit_doc setupFacet] abbrev setup (self : Module) :=
196+
self.facetCore setupFacet
197+
195198
@[inherit_doc depsFacet] abbrev deps (self : Module) :=
196199
self.facetCore depsFacet
197200

src/lake/Lake/Build/Module.lean

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def Module.srcFacetConfig : ModuleFacetConfig srcFacet :=
3737
def Module.recParseHeader (mod : Module) : FetchM (Job ModuleHeader) := do
3838
(← mod.src.fetch).mapM fun srcFile => do
3939
let contents ← IO.FS.readFile srcFile
40-
Lean.parseImports' contents mod.leanFile.toString
40+
Lean.parseImports' contents srcFile.toString
4141

4242
/-- The `ModuleFacetConfig` for the builtin `headerFacet`. -/
4343
def Module.headerFacetConfig : ModuleFacetConfig headerFacet :=
@@ -211,14 +211,15 @@ Recursively build a module's dependencies, including:
211211
* Shared libraries (e.g., `extern_lib` targets or precompiled modules)
212212
* `extraDepTargets` of its library
213213
-/
214-
def Module.recBuildDeps (mod : Module) : FetchM (Job ModuleDeps) := ensureJob do
214+
def Module.recFetchSetup (mod : Module) : FetchM (Job ModuleSetup) := ensureJob do
215215
let extraDepJob ← mod.lib.extraDep.fetch
216216

217217
/-
218218
Remark: We must build direct imports before we fetch the transitive
219219
precompiled imports so that errors in the import block of transitive imports
220220
will not kill this job before the direct imports are built.
221221
-/
222+
let headerJob ← mod.header.fetch
222223
let directImports ← (← mod.imports.fetch).await
223224
let importJobs ← directImports.mapM fun imp => do
224225
if imp.name = mod.name then
@@ -240,6 +241,7 @@ def Module.recBuildDeps (mod : Module) : FetchM (Job ModuleDeps) := ensureJob do
240241
let pluginsJob ← mod.plugins.fetchIn mod.pkg "module plugins"
241242

242243
extraDepJob.bindM fun _ => do
244+
headerJob.bindM fun header => do
243245
importJob.bindM (sync := true) fun _ => do
244246
let depTrace ← takeTrace
245247
impLibsJob.bindM (sync := true) fun impLibs => do
@@ -254,11 +256,24 @@ def Module.recBuildDeps (mod : Module) : FetchM (Job ModuleDeps) := ensureJob do
254256
| none => addTrace depTrace; addTrace libTrace
255257
| some false => addTrace depTrace; addTrace libTrace; addPlatformTrace
256258
| some true => addTrace depTrace
257-
computeModuleDeps impLibs externLibs dynlibs plugins
259+
let {dynlibs, plugins} ← computeModuleDeps impLibs externLibs dynlibs plugins
260+
return {
261+
name := mod.name
262+
isModule := header.isModule
263+
imports := header.imports
264+
modules := {} -- TODO
265+
dynlibs := dynlibs.map (·.path.toString)
266+
plugins := plugins.map (·.path.toString)
267+
options := mod.leanOptions
268+
}
269+
270+
/-- The `ModuleFacetConfig` for the builtin `setupFacet`. -/
271+
def Module.setupFacetConfig : ModuleFacetConfig setupFacet :=
272+
mkFacetJobConfig recFetchSetup
258273

259274
/-- The `ModuleFacetConfig` for the builtin `depsFacet`. -/
260275
def Module.depsFacetConfig : ModuleFacetConfig depsFacet :=
261-
mkFacetJobConfig recBuildDeps
276+
mkFacetJobConfig fun mod => (·.toOpaque) <$> mod.setup.fetch
262277

263278
/-- Remove cached file hashes of the module build outputs (in `.hash` files). -/
264279
def Module.clearOutputHashes (mod : Module) : IO PUnit := do
@@ -290,22 +305,12 @@ def Module.recBuildLean (mod : Module) : FetchM (Job Unit) := do
290305
withRegisterJob mod.name.toString do
291306
(← mod.src.fetch).bindM fun srcFile => do
292307
let srcTrace ← getTrace
293-
(← mod.header.fetch).bindM fun header => do
294-
(← mod.deps.fetch).mapM fun {dynlibs, plugins} => do
308+
(← mod.setup.fetch).mapM fun setup => do
295309
addLeanTrace
296-
addTrace <| traceOptions mod.leanOptions "Module.leanOptions"
310+
addTrace <| traceOptions setup.options "options"
297311
addPureTrace mod.leanArgs "Module.leanArgs"
298312
setTraceCaption s!"{mod.name.toString}:leanArts"
299313
let upToDate ← buildUnlessUpToDate? (oldTrace := srcTrace.mtime) mod (← getTrace) mod.traceFile do
300-
let setup : ModuleSetup := {
301-
name := mod.name
302-
isModule := header.isModule
303-
imports := header.imports
304-
modules := {} -- TODO
305-
dynlibs := dynlibs.map (·.path.toString)
306-
plugins := plugins.map (·.path.toString)
307-
options := mod.leanOptions
308-
}
309314
let args := mod.weakLeanArgs ++ mod.leanArgs
310315
compileLeanModule srcFile setup mod.setupFile mod.arts args
311316
(← getLeanPath) mod.rootDir (← getLean)
@@ -327,7 +332,7 @@ def Module.leanArtsFacetConfig : ModuleFacetConfig leanArtsFacet :=
327332
transitive imports. However, they are independent of their module sources.
328333
-/
329334
newTrace s!"{mod.name.toString}:olean"
330-
addTrace (← mod.deps.fetch).getTrace.withoutInputs
335+
addTrace (← mod.setup.fetch).getTrace.withoutInputs
331336
addTrace (← fetchFileTrace file)
332337
return file
333338

@@ -486,6 +491,7 @@ def Module.initFacetConfigs : DNameMap ModuleFacetConfig :=
486491
|>.insert importsFacet importsFacetConfig
487492
|>.insert transImportsFacet transImportsFacetConfig
488493
|>.insert precompileImportsFacet precompileImportsFacetConfig
494+
|>.insert setupFacet setupFacetConfig
489495
|>.insert depsFacet depsFacetConfig
490496
|>.insert leanArtsFacet leanArtsFacetConfig
491497
|>.insert oleanFacet oleanFacetConfig

src/lake/Lake/CLI/Main.lean

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,10 @@ protected def setupFile : CliM PUnit := do
446446
let loadConfig ← mkLoadConfig opts
447447
let buildConfig := mkBuildConfig opts
448448
let filePath ← takeArg "file path"
449-
let imports ← takeArgs
450-
setupFile loadConfig filePath imports buildConfig
449+
-- Additional arguments (imports) are ignored
450+
-- TODO: Forbid them once the language server is updated
451+
-- noArgsRem do
452+
setupFile loadConfig filePath buildConfig
451453

452454
protected def test : CliM PUnit := do
453455
processOptions lakeOption
@@ -537,15 +539,15 @@ private def evalLeanFile
537539
: LoggerIO UInt32 := do
538540
let some path ← resolvePath? leanFile
539541
| error s!"file not found: {leanFile}"
540-
let args ← id do
542+
let args ← do
541543
if let some mod := ws.findModuleBySrc? path then
542-
let deps ← ws.runBuild (withRegisterJob s!"setup ({mod.name})" do mod.deps.fetch) buildConfig
543-
return mkArgs path deps (some mod.rootDir) mod.leanArgs mod.leanOptions
544+
let setup ← ws.runBuild (cfg := buildConfig) do
545+
withRegisterJob s!"{mod.name}:setup" do mod.setup.fetch
546+
mkArgs path setup (some mod.rootDir) mod.leanArgs
544547
else
545-
let res ← Lean.parseImports' (← IO.FS.readFile path) leanFile.toString
546-
let imports := res.imports.filterMap (ws.findModule? ·.module)
547-
let deps ← ws.runBuild (buildImportsAndDeps leanFile imports) buildConfig
548-
return mkArgs path deps none ws.root.moreLeanArgs ws.root.leanOptions
548+
let setup ← mkModuleSetup
549+
ws leanFile.toString (← IO.FS.readFile path) ws.leanOptions buildConfig
550+
mkArgs path setup none ws.root.moreLeanArgs
549551
let spawnArgs : IO.Process.SpawnArgs := {
550552
args := args ++ moreArgs
551553
cmd := ws.lakeEnv.lean.lean.toString
@@ -555,17 +557,15 @@ private def evalLeanFile
555557
let child ← IO.Process.spawn spawnArgs
556558
child.wait
557559
where
558-
mkArgs leanFile deps rootDir? cfgArgs opts := Id.run do
560+
mkArgs leanFile setup rootDir? cfgArgs := do
559561
let mut args := cfgArgs.push leanFile.toString
560562
if let some rootDir := rootDir? then
561563
args := args ++ #["-R", rootDir.toString]
562-
let {dynlibs, plugins} := deps
563-
for dynlib in dynlibs do
564-
args := args ++ #["--load-dynlib", dynlib.path.toString]
565-
for plugin in plugins do
566-
args := args ++ #["--plugin", plugin.path.toString]
567-
for (name, val) in opts.values do
568-
args := args.push s!"-D{name}={val.asCliFlagValue}"
564+
let (h, setupFile) ← IO.FS.createTempFile
565+
let contents := (toJson setup).compress
566+
logVerbose s!"module setup: {contents}"
567+
h.putStr contents
568+
args := args ++ #["--setup", setupFile.toString]
569569
return args
570570

571571
protected def lean : CliM PUnit := do

src/lake/Lake/CLI/Serve.lean

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,28 @@ and falls back to plain `lean --server`.
2323
-/
2424
def invalidConfigEnvVar := "LAKE_INVALID_CONFIG"
2525

26-
private def mkLeanPaths (ws : Workspace) (deps : ModuleDeps) : LeanPaths where
26+
private def mkLeanPaths (ws : Workspace) (setup : ModuleSetup) : LeanPaths where
2727
oleanPath := ws.leanPath
2828
srcPath := ws.leanSrcPath
29-
loadDynlibPaths := deps.dynlibs.map (·.path)
30-
pluginPaths := deps.plugins.map (·.path)
29+
loadDynlibPaths := setup.dynlibs
30+
pluginPaths := setup.plugins
31+
32+
def mkModuleSetup
33+
(ws : Workspace) (fileName : String) (input : String) (opts : LeanOptions)
34+
(buildConfig : BuildConfig)
35+
: IO ModuleSetup := do
36+
let header ← Lean.parseImports' input fileName
37+
let imports := header.imports.filterMap (ws.findModule? ·.module)
38+
let {dynlibs, plugins} ← ws.runBuild (buildImportsAndDeps fileName imports) buildConfig
39+
return {
40+
name := ← Lean.moduleNameOfFileName fileName none
41+
isModule := header.isModule
42+
imports := header.imports
43+
modules := {} -- TODO
44+
dynlibs := dynlibs.map (·.path.toString)
45+
plugins := plugins.map (·.path.toString)
46+
options := opts
47+
}
3148

3249
/--
3350
Build a list of imports of a file and print the `.olean` and source directories
@@ -37,8 +54,7 @@ If no configuration file exists, exit silently with `noConfigFileCode` (i.e, 2).
3754
The `setup-file` command is used internally by the Lean 4 server.
3855
-/
3956
def setupFile
40-
(loadConfig : LoadConfig) (path : FilePath) (imports : List String := [])
41-
(buildConfig : BuildConfig := {})
57+
(loadConfig : LoadConfig) (path : FilePath) (buildConfig : BuildConfig := {})
4258
: MainM PUnit := do
4359
let path ← resolvePath path
4460
let configFile ← realConfigFile loadConfig.configFile
@@ -62,19 +78,19 @@ def setupFile
6278
let some ws ← loadWorkspace loadConfig |>.toBaseIO buildConfig.outLv buildConfig.ansiMode
6379
| error "failed to load workspace"
6480
if let some mod := ws.findModuleBySrc? path then
65-
let deps ← ws.runBuild (withRegisterJob s!"setup ({mod.name})" do mod.deps.fetch) buildConfig
81+
let setup ← ws.runBuild (cfg := buildConfig) do
82+
withRegisterJob s!"{mod.name}:setup" do mod.setup.fetch
6683
let info : FileSetupInfo := {
67-
paths := mkLeanPaths ws deps
84+
paths := mkLeanPaths ws setup
6885
setupOptions := mod.serverOptions
6986
}
7087
IO.println (toJson info).compress
7188
else
72-
let imports := imports.foldl (init := #[]) fun imps imp =>
73-
if let some mod := ws.findModule? imp.toName then imps.push mod else imps
74-
let deps ← ws.runBuild (buildImportsAndDeps path imports) buildConfig
89+
let setup ← mkModuleSetup
90+
ws path.toString (← IO.FS.readFile path) ws.serverOptions buildConfig
7591
let info : FileSetupInfo := {
76-
paths := mkLeanPaths ws deps
77-
setupOptions := ⟨∅⟩
92+
paths := mkLeanPaths ws setup
93+
setupOptions := setup.options
7894
}
7995
IO.println (toJson info).compress
8096

src/lake/Lake/Config/Package.lean

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,8 +513,8 @@ namespace Package
513513
self.config.moreGlobalServerArgs
514514

515515
/-- The package's `moreServerOptions` configuration appended to its `leanOptions` configuration. -/
516-
@[inline] def moreServerOptions (self : Package) : Array LeanOption :=
517-
self.config.leanOptions ++ self.config.moreServerOptions
516+
@[inline] def moreServerOptions (self : Package) : LeanOptions :=
517+
LeanOptions.ofArray self.config.leanOptions ++ self.config.moreServerOptions
518518

519519
/-- The package's `buildType` configuration. -/
520520
@[inline] def buildType (self : Package) : BuildType :=

src/lake/Lake/Config/Workspace.lean

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import Lake.Config.Env
1111
import Lake.Util.Log
1212

1313
open System
14+
open Lean (Name LeanOptions)
1415

1516
namespace Lake
16-
open Lean (Name)
1717

1818
/-- A Lake workspace -- the top-level package directory. -/
1919
structure Workspace : Type where
@@ -66,6 +66,14 @@ namespace Workspace
6666
@[inline] def pkgsDir (self : Workspace) : FilePath :=
6767
self.root.pkgsDir
6868

69+
/-- Options to pass to `lean` for files outside a library (e.g., via `lake lean`). -/
70+
@[inline] def leanOptions (self : Workspace) : LeanOptions :=
71+
self.root.leanOptions
72+
73+
/-- Options to pass to the Lean server when editing Lean files outside a library. -/
74+
@[inline] def serverOptions (self : Workspace) : LeanOptions :=
75+
self.root.moreServerOptions
76+
6977
/-- The workspace's Lake manifest. -/
7078
@[inline] def manifestFile (self : Workspace) : FilePath :=
7179
self.root.manifestFile

src/lake/tests/lean/test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ test_out 'Hello Bob!' lean Test.lean -- --run Test.lean Bob
1212

1313
# Test that Lake uses module-specific configuration
1414
# if the source file is a module in the workspace
15-
test_out '-Dweak.foo="bar"' -v lean Lib/Basic.lean
15+
test_out '"options":{"weak.foo":"bar"}' -v lean Lib/Basic.lean
1616

1717
# cleanup
1818
rm -f produced.out
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import Test

0 commit comments

Comments
 (0)