134 lines
5.3 KiB
Text
134 lines
5.3 KiB
Text
/-
|
||
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Sebastian Ullrich, Mac Malone
|
||
-/
|
||
import Lean.Elab.Import
|
||
import Lake.Build.Info
|
||
import Lake.Build.Store
|
||
import Lake.Build.Targets
|
||
|
||
open Std System
|
||
open Lean hiding SearchPath
|
||
|
||
namespace Lake
|
||
|
||
abbrev ModuleSet := RBTree Module (·.name.quickCmp ·.name)
|
||
@[inline] def ModuleSet.empty : ModuleSet := RBTree.empty
|
||
|
||
abbrev ModuleMap (α) := RBMap Module α (·.name.quickCmp ·.name)
|
||
@[inline] def ModuleMap.empty : ModuleMap α := RBMap.empty
|
||
|
||
-- # Solo Module Targets
|
||
|
||
def Module.soloTarget (mod : Module)
|
||
(dynlibs : Array FilePath) (depTarget : BuildTarget x) (c := true) : OpaqueTarget :=
|
||
Target.opaque <| depTarget.bindOpaqueSync fun depTrace => do
|
||
let argTrace : BuildTrace := pureHash mod.leanArgs
|
||
let srcTrace : BuildTrace ← computeTrace mod.leanFile
|
||
let modTrace := (← getLeanTrace).mix <| argTrace.mix <| srcTrace.mix depTrace
|
||
let modUpToDate ← modTrace.checkAgainstFile mod mod.traceFile
|
||
if c then
|
||
let cUpToDate ← modTrace.checkAgainstFile mod.cFile mod.cTraceFile
|
||
unless modUpToDate && cUpToDate do
|
||
compileLeanModule mod.leanFile mod.oleanFile mod.ileanFile mod.cFile
|
||
(← getOleanPath) mod.pkg.rootDir dynlibs mod.leanArgs (← getLean)
|
||
modTrace.writeToFile mod.cTraceFile
|
||
else
|
||
unless modUpToDate do
|
||
compileLeanModule mod.leanFile mod.oleanFile mod.ileanFile none
|
||
(← getOleanPath) mod.pkg.rootDir dynlibs mod.leanArgs (← getLean)
|
||
modTrace.writeToFile mod.traceFile
|
||
return depTrace
|
||
|
||
def Module.mkCTarget (modTarget : BuildTarget x) (self : Module) : FileTarget :=
|
||
Target.mk self.cFile <| modTarget.bindOpaqueSync fun _ =>
|
||
return mixTrace (← computeTrace self.cFile) (← getLeanTrace)
|
||
|
||
@[inline]
|
||
def Module.mkOTarget (cTarget : FileTarget) (self : Module) : FileTarget :=
|
||
leanOFileTarget self.oFile cTarget self.leancArgs
|
||
|
||
@[inline]
|
||
def Module.mkDynlibTarget (linkTargets : Array FileTarget) (self : Module) : FileTarget :=
|
||
leanSharedLibTarget self.dynlib linkTargets self.linkArgs
|
||
|
||
-- # Recursive Building
|
||
|
||
section
|
||
variable [Monad m] [MonadLiftT BuildM m] [MonadBuildStore m]
|
||
|
||
/--
|
||
Recursively build a module and its (transitive, local) imports,
|
||
optionally outputting a `.c` file as well if `c` is set to `true`.
|
||
-/
|
||
@[specialize] def Module.recBuildLean (mod : Module) (c : Bool)
|
||
(recurse : IndexBuildFn m) : m ActiveOpaqueTarget := do
|
||
have : MonadLift BuildM m := ⟨liftM⟩
|
||
let extraDepTarget ← mod.pkg.recBuildFacet &`extraDep recurse
|
||
let (imports, transImports) ← mod.recBuildFacet &`lean.imports recurse
|
||
let dynlibsTarget ←
|
||
if mod.shouldPrecompile then
|
||
ActiveTarget.collectArray
|
||
<| ← transImports.mapM (·.recBuildFacet &`lean.dynlib recurse)
|
||
else
|
||
pure <| ActiveTarget.nil.withInfo #[]
|
||
let importTarget ←
|
||
if c then
|
||
ActiveTarget.collectOpaqueArray
|
||
<| ← imports.mapM (·.recBuildFacet &`lean.c recurse)
|
||
else
|
||
ActiveTarget.collectOpaqueArray
|
||
<| ← imports.mapM (·.recBuildFacet &`lean recurse)
|
||
let depTarget := Target.active <| ← extraDepTarget.mixOpaqueAsync
|
||
<| ← dynlibsTarget.mixOpaqueAsync importTarget
|
||
let modTarget ← mod.soloTarget dynlibsTarget.info depTarget c |>.activate
|
||
store (mod.mkBuildKey &`lean) modTarget
|
||
store (mod.mkBuildKey &`olean) modTarget
|
||
store (mod.mkBuildKey &`ilean) modTarget
|
||
if c then
|
||
let cTarget ← mod.mkCTarget (Target.active modTarget) |>.activate
|
||
store (mod.mkBuildKey &`lean.c) cTarget
|
||
return cTarget.withoutInfo
|
||
else
|
||
return modTarget
|
||
|
||
/--
|
||
Recursively parse the Lean files of a module and its imports
|
||
building an `Array` product of its direct and transitive local imports.
|
||
-/
|
||
@[specialize] def Module.recParseImports (mod : Module)
|
||
(recurse : IndexBuildFn m) : m (Array Module × Array Module) := do
|
||
have : MonadLift BuildM m := ⟨liftM⟩
|
||
let contents ← IO.FS.readFile mod.leanFile
|
||
let (imports, _, _) ← Elab.parseImports contents mod.leanFile.toString
|
||
let importSet ← imports.foldlM (init := ModuleSet.empty) fun a imp => do
|
||
if let some mod ← findModule? imp.module then return a.insert mod else return a
|
||
let mut imports := #[]
|
||
let mut transImports := ModuleSet.empty
|
||
for imp in importSet do
|
||
let (_, impTransImports) ← imp.recBuildFacet &`lean.imports recurse
|
||
for imp in impTransImports do
|
||
transImports := transImports.insert imp
|
||
transImports := transImports.insert imp
|
||
imports := imports.push imp
|
||
return (imports, transImports.toArray)
|
||
|
||
/--
|
||
Recursively build the shared library of a module (e.g., for `--load-dynlib`).
|
||
-/
|
||
@[specialize] def Module.recBuildDynLib (mod : Module)
|
||
(recurse : IndexBuildFn m) : m ActiveFileTarget := do
|
||
have : MonadLift BuildM m := ⟨liftM⟩
|
||
let oTarget ← mod.recBuildFacet &`lean.o recurse
|
||
let dynlibTargets ←
|
||
if mod.shouldPrecompile then
|
||
let (_, transImports) ← mod.recBuildFacet &`lean.imports recurse
|
||
transImports.mapM fun mod => mod.recBuildFacet &`lean.dynlib recurse
|
||
else
|
||
pure #[]
|
||
-- TODO: Link in external libraries
|
||
-- let externLibTargets ← mod.pkg.externLibTargets.mapM (·.activate)
|
||
-- let linkTargets := #[oTarget] ++ sharedLibTargets ++ externLibTargets |>.map Target.active
|
||
let linkTargets := #[oTarget] ++ dynlibTargets |>.map Target.active
|
||
mod.mkDynlibTarget linkTargets |>.activate
|