lean4-htt/Lake/Build/Module.lean

248 lines
11 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
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 Lake.Util.OrdHashSet
import Lean.Elab.ParseImportsFast
import Lake.Build.Common
open System
namespace Lake
/-- Fetch the build result of a module facet. -/
@[inline] protected def ModuleFacetDecl.fetch (mod : Module)
(self : ModuleFacetDecl) [FamilyOut ModuleData self.name α] : IndexBuildM α := do
fetch <| mod.facet self.name
/-- Fetch the build job of a module facet. -/
def ModuleFacetConfig.fetchJob (mod : Module)
(self : ModuleFacetConfig name) : IndexBuildM (BuildJob Unit) := do
let some getJob := self.getJob?
| error "module facet '{self.name}' has no associated build job"
return getJob <| ← fetch <| mod.facet self.name
/-- Fetch the build job of a module facet. -/
def Module.fetchFacetJob
(name : Name) (self : Module) : IndexBuildM (BuildJob Unit) := do
let some config := (← getWorkspace).moduleFacetConfigs.find? name
| error "library facet '{name}' does not exist in workspace"
inline <| config.fetchJob self
def Module.buildUnlessUpToDate (mod : Module)
(dynlibPath : SearchPath) (dynlibs : Array FilePath)
(depTrace : BuildTrace) : BuildM PUnit := do
let isOldMode ← getIsOldMode
let argTrace : BuildTrace := pureHash mod.leanArgs
let srcTrace : BuildTrace ← computeTrace { path := mod.leanFile : TextFilePath }
let modTrace := (← getLeanTrace).mix <| argTrace.mix <| srcTrace.mix depTrace
let modUpToDate ← do
if isOldMode then
srcTrace.checkAgainstTime mod
else
modTrace.checkAgainstFile mod mod.traceFile
let name := mod.name.toString
unless modUpToDate do
compileLeanModule name mod.leanFile mod.oleanFile mod.ileanFile mod.cFile
(← getLeanPath) mod.rootDir dynlibs dynlibPath (mod.leanArgs ++ mod.weakLeanArgs) (← getLean)
unless isOldMode do
modTrace.writeToFile mod.traceFile
/-- Compute library directories and build external library Jobs of the given packages. -/
def recBuildExternDynlibs (pkgs : Array Package)
: IndexBuildM (Array (BuildJob Dynlib) × Array FilePath) := do
let mut libDirs := #[]
let mut jobs : Array (BuildJob Dynlib) := #[]
for pkg in pkgs do
libDirs := libDirs.push pkg.nativeLibDir
jobs := jobs.append <| ← pkg.externLibs.mapM (·.dynlib.fetch)
return (jobs, libDirs)
/--
Build the dynlibs of the transitive imports that want precompilation
and the dynlibs of *their* imports.
-/
partial def recBuildPrecompileDynlibs (imports : Array Module)
: IndexBuildM (Array (BuildJob Dynlib) × Array (BuildJob Dynlib) × Array FilePath) := do
let (pkgs, _, jobs) ←
go imports OrdPackageSet.empty ModuleSet.empty #[] false
return (jobs, ← recBuildExternDynlibs pkgs.toArray)
where
go imports pkgs modSet jobs shouldPrecompile := do
let mut pkgs := pkgs
let mut modSet := modSet
let mut jobs := jobs
for mod in imports do
if modSet.contains mod then
continue
modSet := modSet.insert mod
let shouldPrecompile := shouldPrecompile || mod.shouldPrecompile
if shouldPrecompile then
pkgs := pkgs.insert mod.pkg
jobs := jobs.push <| (← mod.dynlib.fetch)
let recImports ← mod.imports.fetch
(pkgs, modSet, jobs) ← go recImports pkgs modSet jobs shouldPrecompile
return (pkgs, modSet, jobs)
variable [MonadLiftT BuildM m]
/--
Recursively parse the Lean files of a module and its imports
building an `Array` product of its direct local imports.
-/
def Module.recParseImports (mod : Module) : IndexBuildM (Array Module) := do
let contents ← IO.FS.readFile mod.leanFile
let imports ← Lean.parseImports' contents mod.leanFile.toString
let mods ← imports.foldlM (init := OrdModuleSet.empty) fun set imp =>
findModule? imp.module <&> fun | some mod => set.insert mod | none => set
return mods.toArray
/-- The `ModuleFacetConfig` for the builtin `importsFacet`. -/
def Module.importsFacetConfig : ModuleFacetConfig importsFacet :=
mkFacetConfig (·.recParseImports)
/-- Recursively compute a module's transitive imports. -/
def Module.recComputeTransImports (mod : Module) : IndexBuildM (Array Module) := do
(·.toArray) <$> (← mod.imports.fetch).foldlM (init := OrdModuleSet.empty) fun set imp => do
return set.appendArray (← imp.transImports.fetch) |>.insert imp
/-- The `ModuleFacetConfig` for the builtin `transImportsFacet`. -/
def Module.transImportsFacetConfig : ModuleFacetConfig transImportsFacet :=
mkFacetConfig (·.recComputeTransImports)
/-- Recursively compute a module's precompiled imports. -/
def Module.recComputePrecompileImports (mod : Module) : IndexBuildM (Array Module) := do
(·.toArray) <$> (← mod.imports.fetch).foldlM (init := OrdModuleSet.empty) fun set imp => do
if imp.shouldPrecompile then
return set.appendArray (← imp.transImports.fetch) |>.insert imp
else
return set.appendArray (← imp.precompileImports.fetch)
/-- The `ModuleFacetConfig` for the builtin `precompileImportsFacet`. -/
def Module.precompileImportsFacetConfig : ModuleFacetConfig precompileImportsFacet :=
mkFacetConfig (·.recComputePrecompileImports)
/--
Recursively build a module and its (transitive, local) imports.
-/
private def Module.recBuildLeanCore (mod : Module) : IndexBuildM (BuildJob Unit) := do
-- Compute and build dependencies
let imports ← mod.imports.fetch
let extraDepJob ← mod.pkg.extraDep.fetch
let precompileImports ← mod.precompileImports.fetch
let modJobs ← precompileImports.mapM (·.dynlib.fetch)
let pkgs := precompileImports.foldl (·.insert ·.pkg)
OrdPackageSet.empty |>.insert mod.pkg |>.toArray
let (externJobs, libDirs) ← recBuildExternDynlibs pkgs
let importJob ← BuildJob.mixArray <| ← imports.mapM (·.importBin.fetch)
let externDynlibsJob ← BuildJob.collectArray externJobs
let modDynlibsJob ← BuildJob.collectArray modJobs
extraDepJob.bindAsync fun _ _ => do
importJob.bindAsync fun _ importTrace => do
modDynlibsJob.bindAsync fun modDynlibs modTrace => do
externDynlibsJob.bindSync fun externDynlibs externTrace => do
let depTrace := importTrace.mix <| modTrace.mix externTrace
/-
Requirements:
* Lean wants the external library symbols before module symbols.
* Unix requires the file extension of the dynlib.
* For some reason, building from the Lean server requires full paths.
Everything else loads fine with just the augmented library path.
* Linux still needs the augmented path to resolve nested dependencies in dynlibs.
-/
let dynlibPath := libDirs ++ externDynlibs.filterMap (·.dir?) |>.toList
let dynlibs := externDynlibs.map (·.path) ++ modDynlibs.map (·.path)
mod.buildUnlessUpToDate dynlibPath dynlibs depTrace
return ((), depTrace)
/-- The `ModuleFacetConfig` for the builtin `leanBinFacet`. -/
def Module.leanBinFacetConfig : ModuleFacetConfig leanBinFacet :=
mkFacetJobConfig (·.recBuildLeanCore)
/-- The `ModuleFacetConfig` for the builtin `importBinFacet`. -/
def Module.importBinFacetConfig : ModuleFacetConfig importBinFacet :=
mkFacetJobConfigSmall fun mod => do
(← mod.leanBin.fetch).bindSync fun _ depTrace =>
return ((), mixTrace (← computeTrace mod) depTrace)
/-- The `ModuleFacetConfig` for the builtin `oleanFacet`. -/
def Module.oleanFacetConfig : ModuleFacetConfig oleanFacet :=
mkFacetJobConfigSmall fun mod => do
(← mod.leanBin.fetch).bindSync fun _ depTrace =>
return (mod.oleanFile, mixTrace (← computeTrace mod.oleanFile) depTrace)
/-- The `ModuleFacetConfig` for the builtin `ileanFacet`. -/
def Module.ileanFacetConfig : ModuleFacetConfig ileanFacet :=
mkFacetJobConfigSmall fun mod => do
(← mod.leanBin.fetch).bindSync fun _ depTrace =>
return (mod.ileanFile, mixTrace (← computeTrace mod.ileanFile) depTrace)
/-- The `ModuleFacetConfig` for the builtin `cFacet`. -/
def Module.cFacetConfig : ModuleFacetConfig cFacet :=
mkFacetJobConfigSmall fun mod => do
(← mod.leanBin.fetch).bindSync fun _ _ =>
-- do content-aware hashing so that we avoid recompiling unchanged C files
return (mod.cFile, ← computeTrace mod.cFile)
/-- Recursively build the module's object file from its C file produced by `lean`. -/
def Module.recBuildLeanO (self : Module) : IndexBuildM (BuildJob FilePath) := do
buildLeanO self.name.toString self.oFile (← self.c.fetch) self.leancArgs
/-- The `ModuleFacetConfig` for the builtin `oFacet`. -/
def Module.oFacetConfig : ModuleFacetConfig oFacet :=
mkFacetJobConfig Module.recBuildLeanO
-- TODO: Return `BuildJob OrdModuleSet × OrdPackageSet` or `OrdRBSet Dynlib`
/-- Recursively build the shared library of a module (e.g., for `--load-dynlib`). -/
def Module.recBuildDynlib (mod : Module) : IndexBuildM (BuildJob Dynlib) := do
-- Compute dependencies
let transImports ← mod.transImports.fetch
let modJobs ← transImports.mapM (·.dynlib.fetch)
let pkgs := transImports.foldl (·.insert ·.pkg)
OrdPackageSet.empty |>.insert mod.pkg |>.toArray
let (externJobs, pkgLibDirs) ← recBuildExternDynlibs pkgs
let linkJobs ← mod.nativeFacets.mapM (fetch <| mod.facet ·.name)
-- Collect Jobs
let linksJob ← BuildJob.collectArray linkJobs
let modDynlibsJob ← BuildJob.collectArray modJobs
let externDynlibsJob ← BuildJob.collectArray externJobs
-- Build dynlib
show SchedulerM _ from do
linksJob.bindAsync fun links oTrace => do
modDynlibsJob.bindAsync fun modDynlibs libTrace => do
externDynlibsJob.bindSync fun externDynlibs externTrace => do
let libNames := modDynlibs.map (·.name) ++ externDynlibs.map (·.name)
let libDirs := pkgLibDirs ++ externDynlibs.filterMap (·.dir?)
let depTrace := oTrace.mix <| libTrace.mix externTrace
let trace ← buildFileUnlessUpToDate mod.dynlibFile depTrace do
let args := links.map toString ++
libDirs.map (s!"-L{·}") ++ libNames.map (s!"-l{·}")
compileSharedLib mod.name.toString mod.dynlibFile args (← getLeanc)
return (⟨mod.dynlibFile, mod.dynlibName⟩, trace)
/-- The `ModuleFacetConfig` for the builtin `dynlibFacet`. -/
def Module.dynlibFacetConfig : ModuleFacetConfig dynlibFacet :=
mkFacetJobConfig Module.recBuildDynlib
open Module in
/--
A name-configuration map for the initial set of
Lake module facets (e.g., `lean.{imports, c, o, dynlib]`).
-/
def initModuleFacetConfigs : DNameMap ModuleFacetConfig :=
DNameMap.empty
|>.insert importsFacet importsFacetConfig
|>.insert transImportsFacet transImportsFacetConfig
|>.insert precompileImportsFacet precompileImportsFacetConfig
|>.insert leanBinFacet leanBinFacetConfig
|>.insert importBinFacet importBinFacetConfig
|>.insert oleanFacet oleanFacetConfig
|>.insert ileanFacet ileanFacetConfig
|>.insert cFacet cFacetConfig
|>.insert oFacet oFacetConfig
|>.insert dynlibFacet dynlibFacetConfig