feat: implement facet
parent
6534a71cca
commit
cdfd8ff49c
|
@ -19,20 +19,13 @@ Sets up a lake workspace for the current project. Furthermore initialize
|
||||||
the Lean search path with the path to the proper compiler from lean-toolchain
|
the Lean search path with the path to the proper compiler from lean-toolchain
|
||||||
as well as all the dependencies.
|
as well as all the dependencies.
|
||||||
-/
|
-/
|
||||||
def lakeSetup (imports : List String) : IO (Except UInt32 Lake.Workspace) := do
|
def lakeSetup : IO (Except UInt32 Lake.Workspace) := do
|
||||||
let (leanInstall?, lakeInstall?) ← Lake.findInstall?
|
let (leanInstall?, lakeInstall?) ← Lake.findInstall?
|
||||||
match ←(EIO.toIO' <| Lake.mkLoadConfig {leanInstall?, lakeInstall?}) with
|
match ←(EIO.toIO' <| Lake.mkLoadConfig {leanInstall?, lakeInstall?}) with
|
||||||
| .ok config =>
|
| .ok config =>
|
||||||
let ws : Lake.Workspace ← Lake.loadWorkspace config
|
let ws : Lake.Workspace ← Lake.loadWorkspace config
|
||||||
|>.run Lake.MonadLog.eio
|
|>.run Lake.MonadLog.eio
|
||||||
|>.toIO (λ _ => IO.userError "Failed to load Lake workspace")
|
|>.toIO (λ _ => IO.userError "Failed to load Lake workspace")
|
||||||
let libraryLeanGitHash := ws.lakeEnv.lean.githash
|
|
||||||
if libraryLeanGitHash ≠ Lean.githash then
|
|
||||||
IO.println s!"WARNING: This doc-gen was built with Lean: {Lean.githash} but the project is running on: {libraryLeanGitHash}"
|
|
||||||
let _libs ← ws.runBuild (Lake.buildImportsAndDeps imports) false
|
|
||||||
|>.run (Lake.MonadLog.eio config.verbosity)
|
|
||||||
|>.toIO (λ _ => IO.userError "Failed to compile imports via Lake")
|
|
||||||
initSearchPath (←findSysroot) (ws.packageList.map (·.oleanDir))
|
|
||||||
pure <| Except.ok ws
|
pure <| Except.ok ws
|
||||||
| .error err =>
|
| .error err =>
|
||||||
throw <| IO.userError err.toString
|
throw <| IO.userError err.toString
|
||||||
|
@ -62,4 +55,7 @@ def load (task : Process.AnalyzeTask) : IO (Process.AnalyzerResult × Hierarchy)
|
||||||
|
|
||||||
Prod.fst <$> Meta.MetaM.toIO (Process.process task) config { env := env } {} {}
|
Prod.fst <$> Meta.MetaM.toIO (Process.process task) config { env := env } {} {}
|
||||||
|
|
||||||
|
def loadCore : IO (Process.AnalyzerResult × Hierarchy) := do
|
||||||
|
load <| .loadAll [`Init, `Std, `Lean]
|
||||||
|
|
||||||
end DocGen4
|
end DocGen4
|
||||||
|
|
57
Main.lean
57
Main.lean
|
@ -11,17 +11,16 @@ def getTopLevelModules (p : Parsed) : IO (List String) := do
|
||||||
pure topLevelModules
|
pure topLevelModules
|
||||||
|
|
||||||
def runSingleCmd (p : Parsed) : IO UInt32 := do
|
def runSingleCmd (p : Parsed) : IO UInt32 := do
|
||||||
let relevantModules := [p.positionalArg! "module" |>.as! String]
|
let relevantModules := [p.positionalArg! "module" |>.as! String |> String.toName]
|
||||||
let res ← lakeSetup (relevantModules)
|
let res ← lakeSetup
|
||||||
match res with
|
match res with
|
||||||
| Except.ok ws =>
|
| Except.ok ws =>
|
||||||
let relevantModules := relevantModules.map String.toName
|
let (doc, hierarchy) ← load <| .loadAllLimitAnalysis relevantModules
|
||||||
let (doc, hierarchy) ← load (.loadAllLimitAnalysis relevantModules)
|
IO.println "Outputting HTML"
|
||||||
IO.println "Outputting HTML"
|
let baseConfig := getSimpleBaseContext hierarchy
|
||||||
let baseConfig := getSimpleBaseContext hierarchy
|
htmlOutputResults baseConfig doc ws (p.hasFlag "ink")
|
||||||
htmlOutputResults baseConfig doc ws (p.hasFlag "ink")
|
pure 0
|
||||||
pure 0
|
| Except.error rc => pure rc
|
||||||
| Except.error rc => pure rc
|
|
||||||
|
|
||||||
def runIndexCmd (_p : Parsed) : IO UInt32 := do
|
def runIndexCmd (_p : Parsed) : IO UInt32 := do
|
||||||
let hierarchy ← Hierarchy.fromDirectory Output.basePath
|
let hierarchy ← Hierarchy.fromDirectory Output.basePath
|
||||||
|
@ -29,22 +28,22 @@ def runIndexCmd (_p : Parsed) : IO UInt32 := do
|
||||||
htmlOutputIndex baseConfig
|
htmlOutputIndex baseConfig
|
||||||
pure 0
|
pure 0
|
||||||
|
|
||||||
def runDocGenCmd (p : Parsed) : IO UInt32 := do
|
def runGenCoreCmd (_p : Parsed) : IO UInt32 := do
|
||||||
let modules : List String := p.variableArgsAs! String |>.toList
|
let res ← lakeSetup
|
||||||
if modules.length == 0 then
|
|
||||||
throw <| IO.userError "No modules provided."
|
|
||||||
|
|
||||||
let res ← lakeSetup modules
|
|
||||||
match res with
|
match res with
|
||||||
| Except.ok ws =>
|
| Except.ok ws =>
|
||||||
IO.println s!"Loading modules from: {←searchPathRef.get}"
|
let (doc, hierarchy) ← loadCore
|
||||||
let modules := modules.map String.toName
|
|
||||||
let (doc, hierarchy) ← load (.loadAll modules)
|
|
||||||
IO.println "Outputting HTML"
|
IO.println "Outputting HTML"
|
||||||
htmlOutput doc hierarchy ws (p.hasFlag "ink")
|
let baseConfig := getSimpleBaseContext hierarchy
|
||||||
|
htmlOutputResults baseConfig doc ws (ink := False)
|
||||||
pure 0
|
pure 0
|
||||||
| Except.error rc => pure rc
|
| Except.error rc => pure rc
|
||||||
|
|
||||||
|
def runDocGenCmd (_p : Parsed) : IO UInt32 := do
|
||||||
|
IO.println "You most likely want to use me via Lake now, check my README on Github on how to:"
|
||||||
|
IO.println "https://github.com/leanprover/doc-gen4"
|
||||||
|
return 0
|
||||||
|
|
||||||
def singleCmd := `[Cli|
|
def singleCmd := `[Cli|
|
||||||
single VIA runSingleCmd;
|
single VIA runSingleCmd;
|
||||||
"Only generate the documentation for the module it was given, might contain broken links unless all documentation is generated."
|
"Only generate the documentation for the module it was given, might contain broken links unless all documentation is generated."
|
||||||
|
@ -63,19 +62,19 @@ def indexCmd := `[Cli|
|
||||||
...topLevelModule : String; "The top level modules this documentation will be for."
|
...topLevelModule : String; "The top level modules this documentation will be for."
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def genCoreCmd := `[Cli|
|
||||||
|
genCore VIA runGenCoreCmd;
|
||||||
|
"Generate documentation for the core Lean modules: Init, Std and Lean since they are not Lake projects"
|
||||||
|
]
|
||||||
|
|
||||||
def docGenCmd : Cmd := `[Cli|
|
def docGenCmd : Cmd := `[Cli|
|
||||||
"doc-gen4" VIA runDocGenCmd; ["0.0.1"]
|
"doc-gen4" VIA runDocGenCmd; ["0.1.0"]
|
||||||
"A documentation generator for Lean 4."
|
"A documentation generator for Lean 4."
|
||||||
|
|
||||||
FLAGS:
|
|
||||||
ink; "Render the files with LeanInk in addition"
|
|
||||||
|
|
||||||
ARGS:
|
|
||||||
...modules : String; "The modules to generate the HTML for."
|
|
||||||
|
|
||||||
SUBCOMMANDS:
|
SUBCOMMANDS:
|
||||||
singleCmd;
|
singleCmd;
|
||||||
indexCmd
|
indexCmd;
|
||||||
|
genCoreCmd
|
||||||
]
|
]
|
||||||
|
|
||||||
def main (args : List String) : IO UInt32 :=
|
def main (args : List String) : IO UInt32 :=
|
||||||
|
|
49
README.md
49
README.md
|
@ -2,48 +2,15 @@
|
||||||
Document Generator for Lean 4
|
Document Generator for Lean 4
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
You can call `doc-gen4` from the top of a Lake project like this:
|
`doc-gen4` is the easiest to use via its custom Lake facet, in order
|
||||||
```sh
|
to do this you have to add it to your `lakefile.lean` like this:
|
||||||
$ /path/to/doc-gen4 Module
|
|
||||||
```
|
```
|
||||||
|
meta if get_config? env = some "dev" then -- dev is so not everyone has to build it
|
||||||
where `Module` is one or more of the top level modules you want to document.
|
require «doc-gen4» from git "https://github.com/leanprover/doc-gen4" @ "main"
|
||||||
The tool will then proceed to compile the project using lake (if that hasn't happened yet),
|
|
||||||
analyze it and put the result in `./build/doc`.
|
|
||||||
|
|
||||||
You can optionally provide the path to a `LeanInk` binary using the `--ink` flag which will make
|
|
||||||
the tool produce `Alectryon` style rendered output along the usual documentation.
|
|
||||||
|
|
||||||
You could e.g. host the files locally with the built-in Python webserver:
|
|
||||||
```sh
|
|
||||||
$ cd build/doc && python -m http.server
|
|
||||||
```
|
```
|
||||||
|
Then you can generate documentation for an entire library using:
|
||||||
### Multi stage
|
|
||||||
You can also use `doc-gen4` in multiple separate stages to generate the whole documentation.
|
|
||||||
For example `mathlib4` consists out of 4 modules, the 3 Lean compiler ones and itself:
|
|
||||||
- `Init`
|
|
||||||
- `Std`
|
|
||||||
- `Lean`
|
|
||||||
- `Mathlib`
|
|
||||||
|
|
||||||
The first build stage is to run doc-gen for all modules separately:
|
|
||||||
|
|
||||||
1. `doc-gen4 single Init`
|
|
||||||
2. `doc-gen4 single Std`
|
|
||||||
3. `doc-gen4 single Lean`
|
|
||||||
4. `doc-gen4 single Mathlib`
|
|
||||||
|
|
||||||
Note that you can also just make a call to submodules so `Mathlib.Algebra`
|
|
||||||
will work standalone as well. Furthermore one can use the `--ink` flag
|
|
||||||
here to also generate LeanInk documentation in addition.
|
|
||||||
|
|
||||||
The second and last stage is the index one which zips up some
|
|
||||||
information relevant for the search:
|
|
||||||
```sh
|
|
||||||
$ doc-gen4 index Mathlib
|
|
||||||
```
|
```
|
||||||
Now `build/doc` should contain the same files with the same context as if one had run
|
lake -Kenv=dev build Test:docs
|
||||||
```
|
|
||||||
$ doc-gen4 Mathlib
|
|
||||||
```
|
```
|
||||||
|
If you have multiple libraries you want to generate documentation for
|
||||||
|
the recommended way right now is to run it for each library.
|
||||||
|
|
|
@ -26,3 +26,92 @@ require lake from git
|
||||||
|
|
||||||
require leanInk from git
|
require leanInk from git
|
||||||
"https://github.com/hargonix/LeanInk" @ "doc-gen"
|
"https://github.com/hargonix/LeanInk" @ "doc-gen"
|
||||||
|
|
||||||
|
module_facet docs (mod) : FilePath := do
|
||||||
|
let some docGen4 ← findLeanExe? `«doc-gen4»
|
||||||
|
| error "no doc-gen4 executable configuration found in workspace"
|
||||||
|
let exeJob ← docGen4.exe.fetch
|
||||||
|
let modJob ← mod.leanBin.fetch
|
||||||
|
let buildDir := (← getWorkspace).root.buildDir
|
||||||
|
let docFile := mod.filePath (buildDir / "doc") "html"
|
||||||
|
exeJob.bindAsync fun exeFile exeTrace => do
|
||||||
|
modJob.bindSync fun _ modTrace => do
|
||||||
|
let depTrace := exeTrace.mix modTrace
|
||||||
|
let trace ← buildFileUnlessUpToDate docFile depTrace do
|
||||||
|
logInfo s!"Documenting module: {mod.name}"
|
||||||
|
proc {
|
||||||
|
cmd := exeFile.toString
|
||||||
|
args := #["single", mod.name.toString, "--ink"]
|
||||||
|
env := #[("LEAN_PATH", (← getAugmentedLeanPath).toString)]
|
||||||
|
}
|
||||||
|
return (docFile, trace)
|
||||||
|
|
||||||
|
-- TODO: technically speaking this facet does not show all file dependencies
|
||||||
|
target coreDocs : FilePath := do
|
||||||
|
let some docGen4 ← findLeanExe? `«doc-gen4»
|
||||||
|
| error "no doc-gen4 executable configuration found in workspace"
|
||||||
|
let exeJob ← docGen4.exe.fetch
|
||||||
|
let basePath := (←getWorkspace).root.buildDir / "doc"
|
||||||
|
let dataFile := basePath / "declarations" / "declaration-data-Lean.bmp"
|
||||||
|
exeJob.bindSync fun exeFile exeTrace => do
|
||||||
|
let trace ← buildFileUnlessUpToDate dataFile exeTrace do
|
||||||
|
logInfo "Documenting Lean core: Init, Std, Lean"
|
||||||
|
proc {
|
||||||
|
cmd := exeFile.toString
|
||||||
|
args := #["genCore"]
|
||||||
|
env := #[("LEAN_PATH", (← getAugmentedLeanPath).toString)]
|
||||||
|
}
|
||||||
|
return (dataFile, trace)
|
||||||
|
|
||||||
|
library_facet docs (lib) : FilePath := do
|
||||||
|
let some docGen4Pkg ← findPackage? `«doc-gen4»
|
||||||
|
| error "no doc-gen4 package found in workspace"
|
||||||
|
let some docGen4 := docGen4Pkg.findLeanExe? `«doc-gen4»
|
||||||
|
| error "no doc-gen4 executable configuration found in workspace"
|
||||||
|
let exeJob ← docGen4.exe.fetch
|
||||||
|
|
||||||
|
-- XXX: Workaround remove later
|
||||||
|
let coreJob ← if h : docGen4Pkg.name = _package.name then
|
||||||
|
have : Fact (docGen4Pkg.name = _package.name) := Fact.mk h
|
||||||
|
let job := fetch <| docGen4Pkg.target `coreDocs
|
||||||
|
job
|
||||||
|
else
|
||||||
|
error "wrong package"
|
||||||
|
|
||||||
|
let moduleJobs ← BuildJob.mixArray (α := FilePath) <| ← lib.recBuildLocalModules #[`docs]
|
||||||
|
-- Shared with DocGen4.Output
|
||||||
|
let basePath := (←getWorkspace).root.buildDir / "doc"
|
||||||
|
let dataFile := basePath / "declarations" / "declaration-data.bmp"
|
||||||
|
let staticFiles := #[
|
||||||
|
basePath / "style.css",
|
||||||
|
basePath / "declaration-data.js",
|
||||||
|
basePath / "nav.js",
|
||||||
|
basePath / "how-about.js",
|
||||||
|
basePath / "search.js",
|
||||||
|
basePath / "mathjax-config.js",
|
||||||
|
basePath / "instances.js",
|
||||||
|
basePath / "importedBy.js",
|
||||||
|
basePath / "index.html",
|
||||||
|
basePath / "404.html",
|
||||||
|
basePath / "navbar.html",
|
||||||
|
basePath / "find" / "index.html",
|
||||||
|
basePath / "find" / "find.js",
|
||||||
|
basePath / "src" / "alectryon.css",
|
||||||
|
basePath / "src" / "alectryon.js",
|
||||||
|
basePath / "src" / "docutils_basic.css",
|
||||||
|
basePath / "src" / "pygments.css"
|
||||||
|
]
|
||||||
|
coreJob.bindAsync fun _ coreInputTrace => do
|
||||||
|
exeJob.bindAsync fun exeFile exeTrace => do
|
||||||
|
moduleJobs.bindSync fun _ inputTrace => do
|
||||||
|
let depTrace := mixTraceArray #[inputTrace, exeTrace, coreInputTrace]
|
||||||
|
let trace ← buildFileUnlessUpToDate dataFile depTrace do
|
||||||
|
logInfo "Documentation indexing"
|
||||||
|
proc {
|
||||||
|
cmd := exeFile.toString
|
||||||
|
args := #["index"]
|
||||||
|
}
|
||||||
|
let traces ← staticFiles.mapM computeTrace
|
||||||
|
let indexTrace := mixTraceArray traces
|
||||||
|
|
||||||
|
return (dataFile, trace.mix indexTrace)
|
||||||
|
|
Loading…
Reference in New Issue