forked from leanprover/doc-gen4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlakefile.lean
214 lines (191 loc) · 7.82 KB
/
lakefile.lean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
import Lake
open System Lake DSL
package «doc-gen4»
lean_lib DocGen4
@[default_target]
lean_exe «doc-gen4» {
root := `Main
supportInterpreter := true
}
require CMark from git
"https://github.com/xubaiw/CMark.lean" @ "main"
require «UnicodeBasic» from git
"https://github.com/fgdorais/lean4-unicode-basic" @ "main"
require Cli from git
"https://github.com/mhuisi/lean4-cli" @ "nightly"
require leanInk from git
"https://github.com/hargonix/LeanInk" @ "doc-gen"
/--
Obtain the Github URL of a project by parsing the origin remote.
-/
def getGitRemoteUrl (directory : System.FilePath := "." ) (remote : String := "origin") : IO String := do
let out ← IO.Process.output {
cmd := "git",
args := #["remote", "get-url", remote],
cwd := directory
}
if out.exitCode != 0 then
let explanation := "Failed to find a git remote in your project, consider reading: https://github.com/leanprover/doc-gen4#source-locations"
let err := s!"git exited with code {out.exitCode} while looking for the git remote in {directory}"
throw <| IO.userError <| explanation ++ "\n" ++ err
return out.stdout.trimRight
/--
Obtain the git commit hash of the project that is currently getting analyzed.
-/
def getProjectCommit (directory : System.FilePath := "." ) : IO String := do
let out ← IO.Process.output {
cmd := "git",
args := #["rev-parse", "HEAD"]
cwd := directory
}
if out.exitCode != 0 then
throw <| IO.userError <| s!"git exited with code {out.exitCode} while looking for the current commit in {directory}"
return out.stdout.trimRight
def filteredPath (path : FilePath) : List String := path.components.filter (· != ".")
/--
Append the module path to a string with the separator used for name components.
-/
def appendModPath (libUri : String) (pathSep : Char) (mod : Module) : String :=
mod.name.components.foldl (init := libUri) (·.push pathSep ++ ·.toString) ++ ".lean"
/--
Append the library and mod path to the given Uri referring to the package source.
-/
def appendLibModPath (pkgUri : String) (pathSep : Char) (lib : LeanLibConfig) (mod : Module) : String :=
let libPath := filteredPath lib.srcDir
appendModPath (pathSep.toString.intercalate (pkgUri :: libPath)) pathSep mod
/--
Turns a Github git remote URL into an HTTPS Github URL.
Three link types from git supported:
- https://github.com/org/repo
- https://github.com/org/repo.git
- [email protected]:org/repo.git
-/
def getGithubBaseUrl (url : String) : Option String :=
if url.startsWith "[email protected]:" && url.endsWith ".git" then
let url := url.drop "[email protected]:".length
let url := url.dropRight ".git".length
.some s!"https://github.com/{url}"
else if url.startsWith "https://github.com/" then
if url.endsWith ".git" then
.some <| url.dropRight ".git".length
else
.some url
else
.none
def getGithubUrl (pkg : Package) (lib : LeanLibConfig) (mod : Module) : IO String := do
let url ← getGitRemoteUrl pkg.dir "origin"
let .some baseUrl := getGithubBaseUrl url
| throw <| IO.userError <|
s!"Could not interpret Git remote uri {url} as a Github source repo.\n"
++ "See README on source URIs for more details."
let commit ← getProjectCommit pkg.dir
let srcDir := filteredPath pkg.config.srcDir
let pkgUri := "/".intercalate <| baseUrl :: "blob" :: commit :: srcDir
pure <| appendLibModPath pkgUri '/' lib mod
/--
Return a File uri for the module.
-/
def getFileUri (pkg : Package) (lib : LeanLibConfig) (mod : Module) := do
let dir ← IO.FS.realPath (pkg.dir / pkg.config.srcDir)
pure <| appendLibModPath s!"file://{dir}" FilePath.pathSeparator lib mod
/--
Return a VSCode uri for the module.
-/
def getVSCodeUri (pkg : Package) (lib : LeanLibConfig) (mod : Module) : IO String := do
let dir ← IO.FS.realPath (pkg.dir / pkg.config.srcDir)
pure <| appendLibModPath s!"vscode://file/{dir}" FilePath.pathSeparator lib mod
/--
Attempt to determine URI to use for the module source file.
-/
def getSrcUri (pkg : Package) (lib : LeanLibConfig) (mod : Module) : IO String := do
match ←IO.getEnv "DOCGEN_SRC" with
| .none | .some "github" | .some "" => getGithubUrl pkg lib mod
| .some "vscode" => getVSCodeUri pkg lib mod
| .some "file" => getFileUri pkg lib mod
| .some _ => throw <| IO.userError "$DOCGEN_SRC should be github, file, or vscode."
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.leanArts.fetch
let ws ← getWorkspace
let pkg ← ws.packages.find? (·.isLocalModule mod.name)
let libConfig ← pkg.leanLibConfigs.toArray.find? (·.isLocalModule mod.name)
-- Build all documentation imported modules
let imports ← mod.imports.fetch
let depDocJobs ← BuildJob.mixArray <| ← imports.mapM fun mod => fetch <| mod.facet `docs
let srcUri ← getSrcUri pkg libConfig mod
let buildDir := ws.root.buildDir
let docFile := mod.filePath (buildDir / "doc") "html"
depDocJobs.bindAsync fun _ depDocTrace => do
exeJob.bindAsync fun exeFile exeTrace => do
modJob.bindSync fun _ modTrace => do
let depTrace := mixTraceArray #[exeTrace, modTrace, depDocTrace]
let trace ← buildFileUnlessUpToDate docFile depTrace do
logStep s!"Documenting module: {mod.name}"
proc {
cmd := exeFile.toString
args := #["single", mod.name.toString, srcUri]
env := ← getAugmentedEnv
}
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
logStep "Documenting Lean core: Init and Lean"
proc {
cmd := exeFile.toString
args := #["genCore"]
env := ← getAugmentedEnv
}
return (dataFile, trace)
library_facet docs (lib) : FilePath := do
let mods ← lib.modules.fetch
let moduleJobs ← BuildJob.mixArray <| ← mods.mapM (fetch <| ·.facet `docs)
let coreJob : BuildJob FilePath ← coreDocs.fetch
let exeJob ← «doc-gen4».fetch
-- 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 / "color-scheme.js",
basePath / "nav.js",
basePath / "jump-src.js",
basePath / "expand-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 / "search.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)