This PR adds support for plugins to the frontend and server. Implementation-wise, this adds a `plugins` argument to `runFrontend`, `processHeader`, amd `importModules`, a `plugins` field to `SetupImportsResult` and `FileSetupResult`. and a `pluginsPath` field to `LeanPaths`, and then threads the value through these.
139 lines
5.2 KiB
Text
139 lines
5.2 KiB
Text
/-
|
|
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Authors: Sebastian Ullrich, Marc Huisinga
|
|
-/
|
|
prelude
|
|
import Init.System.IO
|
|
import Lean.Server.Utils
|
|
import Lean.Util.FileSetupInfo
|
|
import Lean.Util.LakePath
|
|
import Lean.LoadDynlib
|
|
|
|
namespace Lean.Server.FileWorker
|
|
|
|
open IO
|
|
|
|
structure LakeSetupFileOutput where
|
|
spawnArgs : Process.SpawnArgs
|
|
exitCode : UInt32
|
|
stdout : String
|
|
stderr : String
|
|
|
|
partial def runLakeSetupFile
|
|
(m : DocumentMeta)
|
|
(lakePath filePath : System.FilePath)
|
|
(imports : Array Import)
|
|
(handleStderr : String → IO Unit)
|
|
: IO LakeSetupFileOutput := do
|
|
let mut args := #["setup-file", filePath.toString] ++ imports.map (toString ·.module)
|
|
if m.dependencyBuildMode matches .never then
|
|
args := args.push "--no-build" |>.push "--no-cache"
|
|
let spawnArgs : Process.SpawnArgs := {
|
|
stdin := Process.Stdio.null
|
|
stdout := Process.Stdio.piped
|
|
stderr := Process.Stdio.piped
|
|
cmd := lakePath.toString
|
|
args
|
|
}
|
|
let lakeProc ← Process.spawn spawnArgs
|
|
|
|
let rec processStderr (acc : String) : IO String := do
|
|
let line ← lakeProc.stderr.getLine
|
|
if line == "" then
|
|
return acc
|
|
else
|
|
handleStderr line
|
|
processStderr (acc ++ line)
|
|
let stderr ← IO.asTask (processStderr "") Task.Priority.dedicated
|
|
|
|
let stdout := String.trim (← lakeProc.stdout.readToEnd)
|
|
let stderr ← IO.ofExcept stderr.get
|
|
let exitCode ← lakeProc.wait
|
|
return ⟨spawnArgs, exitCode, stdout, stderr⟩
|
|
|
|
/-- Categorizes possible outcomes of running `lake setup-file`. -/
|
|
inductive FileSetupResultKind where
|
|
/-- File configuration loaded and dependencies updated successfully. -/
|
|
| success
|
|
/-- No Lake project found, no setup was done. -/
|
|
| noLakefile
|
|
/-- Imports must be rebuilt but `--no-build` was specified. -/
|
|
| importsOutOfDate
|
|
/-- Other error during Lake invocation. -/
|
|
| error (msg : String)
|
|
|
|
/-- Result of running `lake setup-file`. -/
|
|
structure FileSetupResult where
|
|
/-- Kind of outcome. -/
|
|
kind : FileSetupResultKind
|
|
/-- Search path from successful setup, or else empty. -/
|
|
srcSearchPath : SearchPath
|
|
/-- Additional options from successful setup, or else empty. -/
|
|
fileOptions : Options
|
|
/-- Lean plugins from successful setup, or else empty. -/
|
|
plugins : Array System.FilePath
|
|
|
|
def FileSetupResult.ofSuccess (pkgSearchPath : SearchPath) (fileOptions : Options)
|
|
(plugins : Array System.FilePath) : IO FileSetupResult := do return {
|
|
kind := FileSetupResultKind.success
|
|
srcSearchPath := ← initSrcSearchPath pkgSearchPath,
|
|
fileOptions, plugins
|
|
}
|
|
|
|
def FileSetupResult.ofNoLakefile : IO FileSetupResult := do return {
|
|
kind := FileSetupResultKind.noLakefile
|
|
srcSearchPath := ← initSrcSearchPath
|
|
fileOptions := Options.empty
|
|
plugins := #[]
|
|
}
|
|
|
|
def FileSetupResult.ofImportsOutOfDate : IO FileSetupResult := do return {
|
|
kind := FileSetupResultKind.importsOutOfDate
|
|
srcSearchPath := ← initSrcSearchPath
|
|
fileOptions := Options.empty
|
|
plugins := #[]
|
|
}
|
|
|
|
def FileSetupResult.ofError (msg : String) : IO FileSetupResult := do return {
|
|
kind := FileSetupResultKind.error msg
|
|
srcSearchPath := ← initSrcSearchPath
|
|
fileOptions := Options.empty
|
|
plugins := #[]
|
|
}
|
|
|
|
/-- Uses `lake setup-file` to compile dependencies on the fly and add them to `LEAN_PATH`.
|
|
Compilation progress is reported to `handleStderr`. Returns the search path for
|
|
source files and the options for the file. -/
|
|
partial def setupFile (m : DocumentMeta) (imports : Array Import) (handleStderr : String → IO Unit) : IO FileSetupResult := do
|
|
let some filePath := System.Uri.fileUriToPath? m.uri
|
|
| return ← FileSetupResult.ofNoLakefile -- untitled files have no lakefile
|
|
|
|
-- NOTE: we assume for now that `lakefile.lean` does not have any non-core-Lean deps
|
|
-- NOTE: lake does not exist in stage 0 (yet?)
|
|
if filePath.fileName == "lakefile.lean" then
|
|
return ← FileSetupResult.ofNoLakefile -- the lakefile itself has no lakefile
|
|
|
|
let lakePath ← determineLakePath
|
|
if !(← System.FilePath.pathExists lakePath) then
|
|
return ← FileSetupResult.ofNoLakefile
|
|
|
|
let result ← runLakeSetupFile m lakePath filePath imports handleStderr
|
|
|
|
let cmdStr := " ".intercalate (toString result.spawnArgs.cmd :: result.spawnArgs.args.toList)
|
|
|
|
match result.exitCode with
|
|
| 0 =>
|
|
let Except.ok (info : FileSetupInfo) := Json.parse result.stdout >>= fromJson?
|
|
| return ← FileSetupResult.ofError s!"Invalid output from `{cmdStr}`:\n{result.stdout}\nstderr:\n{result.stderr}"
|
|
initSearchPath (← getBuildDir) info.paths.oleanPath
|
|
info.paths.loadDynlibPaths.forM loadDynlib
|
|
let pkgSearchPath ← info.paths.srcPath.mapM realPathNormalized
|
|
let pluginPaths ← info.paths.pluginPaths.mapM realPathNormalized
|
|
FileSetupResult.ofSuccess pkgSearchPath info.setupOptions.toOptions pluginPaths
|
|
| 2 => -- exit code for lake reporting that there is no lakefile
|
|
FileSetupResult.ofNoLakefile
|
|
| 3 => -- exit code for `--no-build`
|
|
FileSetupResult.ofImportsOutOfDate
|
|
| _ =>
|
|
FileSetupResult.ofError s!"`{cmdStr}` failed:\n{result.stdout}\nstderr:\n{result.stderr}"
|