refactor: simplify module target code + related cleanup

closes leanprover/lake#75
This commit is contained in:
tydeu 2022-06-16 01:31:51 -04:00
parent 8dc3133244
commit 02ee011a0e
16 changed files with 266 additions and 331 deletions

View file

@ -13,21 +13,21 @@ namespace Lake
-- # Build Package .o Files
def Package.oFileTargetOf
(mod : Name) (target : ActiveOleanAndCTarget) (self : Package) : FileTarget :=
let oFile := self.modToO mod
let cTarget := Target.active <| ActiveOleanAndCTarget.cTarget target
leanOFileTarget oFile cTarget self.moreLeancArgs
def Package.oFileTargetsOf
(targetMap : NameMap ActiveOleanAndCTarget) (self : Package) : Array FileTarget :=
targetMap.fold (fun arr k v => arr.push (k, v)) #[] |>.filterMap fun (mod, target) =>
if self.isLocalModule mod then self.oFileTargetOf mod target else none
(targetMap : ModuleMap ActiveOpaqueTarget) (self : Package) : Array FileTarget :=
targetMap.fold (fun arr k v => arr.push (k, v)) #[] |>.filterMap fun (key, target) =>
if key.facet == `lean.c && self.isLocalModule key.module then
let mod : Module := ⟨self, key.module⟩
let target := Target.active <| target.withInfo mod.cFile
leanOFileTarget mod.oFile target self.moreLeancArgs
else
none
def Package.moduleOTarget (mod : Name) (self : Package) : FileTarget :=
let oFile := self.modToO mod
let cTarget := self.moduleOleanAndCTarget mod |>.cTarget
leanOFileTarget oFile cTarget self.moreLeancArgs
def Module.mkOTarget (modTarget : OpaqueTarget) (self : Module) : FileTarget :=
leanOFileTarget self.oFile (self.mkCTarget modTarget) self.leancArgs
def Module.oTarget (self : Module) : FileTarget :=
self.mkOTarget <| self.leanBinTarget (c := true)
-- # Build Package Static Lib
@ -35,9 +35,9 @@ def Package.mkStaticLibTarget (self : Package) (lib : LeanLibConfig) : FileTarge
let libFile := self.libDir / lib.staticLibFileName
BuildTarget.mk' libFile do
let depTarget ← self.buildExtraDepsTarget
let infos := (← lib.getModuleArray self.srcDir).map (Module.mk self)
let moduleTargetMap ← buildModuleMap infos $
recBuildModuleOleanAndCTargetWithLocalImports depTarget
let mods ← self.getLibModuleArray lib
let moduleTargetMap ← buildModuleMap mods $
recBuildModuleTargetWithDeps depTarget (c := true)
let oFileTargets := self.oFileTargetsOf moduleTargetMap
staticLibTarget libFile oFileTargets |>.materializeAsync
@ -47,13 +47,13 @@ protected def Package.staticLibTarget (self : Package) : FileTarget :=
-- # Build Package Shared Lib
def Package.linkTargetsOf
(targetMap : NameMap ActiveOleanAndCTarget) (self : Package) : LakeM (Array FileTarget) := do
(targetMap : ModuleMap ActiveOpaqueTarget) (self : Package) : LakeM (Array FileTarget) := do
let collect dep recurse := do
let pkg := (← getPackageByName? dep.name).get!
let pkg := (← findPackage? dep.name).get!
pkg.dependencies.forM fun dep => discard <| recurse dep
return pkg.oFileTargetsOf targetMap ++ pkg.externLibTargets
let ⟨x, map⟩ ← RBTopT.run <| self.dependencies.forM fun dep =>
discard <| buildRBTop (cmp := Name.quickCmp) collect Dependency.name dep
let ⟨x, map⟩ ← RBTopT.run (cmp := Name.quickCmp) <| self.dependencies.forM fun dep =>
discard <| buildTop collect Dependency.name dep
match x with
| Except.ok _ =>
let ts := map.fold (fun acc _ vs => acc ++ vs) #[]
@ -64,9 +64,9 @@ def Package.mkSharedLibTarget (self : Package) (lib : LeanLibConfig) : FileTarge
let libFile := self.libDir / lib.sharedLibFileName
BuildTarget.mk' libFile do
let depTarget ← self.buildExtraDepsTarget
let infos := (← lib.getModuleArray self.srcDir).map (Module.mk self)
let moduleTargetMap ← buildModuleMap infos $
recBuildModuleOleanAndCTargetWithLocalImports depTarget
let mods ← self.getLibModuleArray lib
let moduleTargetMap ← buildModuleMap mods $
recBuildModuleTargetWithDeps depTarget (c := true)
let linkTargets ← self.linkTargetsOf moduleTargetMap
let target := leanSharedLibTarget libFile linkTargets lib.linkArgs
target.materializeAsync
@ -79,18 +79,19 @@ protected def Package.sharedLibTarget (self : Package) : FileTarget :=
def Package.mkExeTarget (self : Package) (exe : LeanExeConfig) : FileTarget :=
let exeFile := self.binDir / exe.fileName
BuildTarget.mk' exeFile do
let root : Module := ⟨self, exe.root⟩
let depTarget ← self.buildExtraDepsTarget
let moduleTargetMap ← buildModuleMap #[⟨self, exe.root] $
recBuildModuleOleanAndCTargetWithLocalImports depTarget
let moduleTargetMap ← buildModuleMap #[root] $
recBuildModuleTargetWithDeps depTarget (c := true)
let pkgLinkTargets ← self.linkTargetsOf moduleTargetMap
let linkTargets :=
if self.isLocalModule exe.root then
pkgLinkTargets
else
let rootTarget := moduleTargetMap.find? exe.root |>.get!
let rootLinkTarget := self.oFileTargetOf exe.root rootTarget
let rootTarget := moduleTargetMap.find? (root.key `lean) |>.get!
let rootLinkTarget := root.mkOTarget <| Target.active rootTarget
#[rootLinkTarget] ++ pkgLinkTargets
let target := leanBinTarget exeFile linkTargets exe.linkArgs
let target := leanExeTarget exeFile linkTargets exe.linkArgs
target.materializeAsync
protected def Package.exeTarget (self : Package) : FileTarget :=

View file

@ -29,6 +29,9 @@ abbrev BuildM := BuildT <| MonadLogT BaseIO OptionIO
/-- The `Task` monad for Lake builds. -/
abbrev BuildTask := OptionIOTask
/-- A Lake build job. -/
abbrev Job := BuildTask BuildTrace
instance : MonadError BuildM := ⟨MonadLog.error⟩
instance : MonadLift IO BuildM := ⟨MonadError.runIO⟩

View file

@ -9,224 +9,120 @@ import Lake.Build.Target
import Lake.Build.Actions
import Lake.Build.Recursive
import Lake.Build.TargetTypes
import Lake.Config.Package
import Lake.Config.Module
open System
open Lean hiding SearchPath
namespace Lake
-- # Info
-- # Build Key
structure Module where
pkg : Package
name : Name
deriving Inhabited
structure ModuleBuildKey where
module : Name
facet : Name
deriving Inhabited, Repr, BEq, Hashable
namespace Module
namespace ModuleBuildKey
@[inline] def srcFile (self : Module) : FilePath :=
self.pkg.modToSrc self.name
def quickCmp (lhs rhs : ModuleBuildKey) :=
match lhs.module.quickCmp rhs.module with
| .eq => lhs.facet.quickCmp rhs.facet
| ord => ord
@[inline] def oleanFile (self : Module) : FilePath :=
self.pkg.modToOlean self.name
protected def toString (self : ModuleBuildKey) :=
s!"{self.module}:{self.facet}"
@[inline] def ileanFile (self : Module) : FilePath :=
self.pkg.modToIlean self.name
instance : ToString ModuleBuildKey := ⟨(·.toString)⟩
@[inline] def cFile (self : Module) : FilePath :=
self.pkg.modToC self.name
end ModuleBuildKey
@[inline] def traceFile (self : Module) : FilePath :=
self.pkg.modToTraceFile self.name
def Module.key (facet : Name) (self : Module) : ModuleBuildKey :=
⟨self.name, facet⟩
end Module
-- # Single Module Target
-- # Targets
structure OleanAndC where
oleanFile : FilePath
cFile : FilePath
deriving Inhabited, Repr
namespace OleanAndC
protected def getMTime (self : OleanAndC) : IO MTime := do
return mixTrace (← getMTime self.oleanFile) (← getMTime self.cFile)
instance : GetMTime OleanAndC := ⟨OleanAndC.getMTime⟩
protected def computeHash (self : OleanAndC) : IO Hash := do
return mixTrace (← computeHash self.oleanFile) (← computeHash self.cFile)
instance : ComputeHash OleanAndC IO := ⟨OleanAndC.computeHash⟩
protected def checkExists (self : OleanAndC) : BaseIO Bool := do
return (← checkExists self.oleanFile) && (← checkExists self.cFile)
instance : CheckExists OleanAndC := ⟨OleanAndC.checkExists⟩
end OleanAndC
abbrev OleanAndCTarget := BuildTarget OleanAndC
namespace OleanAndCTarget
abbrev oleanFile (self : OleanAndCTarget) := self.info.oleanFile
def oleanTarget (self : OleanAndCTarget) : FileTarget :=
Target.mk self.oleanFile do self.bindSync fun i _ => computeTrace i.oleanFile
abbrev cFile (self : OleanAndCTarget) := self.info.cFile
def cTarget (self : OleanAndCTarget) : FileTarget :=
Target.mk self.cFile do self.bindSync fun i _ => computeTrace i.cFile
end OleanAndCTarget
structure ActiveOleanAndCTargets where
oleanTarget : ActiveFileTarget
cTarget : ActiveFileTarget
deriving Inhabited
namespace ActiveOleanAndCTargets
abbrev oleanFile (self : ActiveOleanAndCTargets) := self.oleanTarget.info
abbrev cFile (self : ActiveOleanAndCTargets) := self.cTarget.info
end ActiveOleanAndCTargets
/--
An active module `.olean` and `.c` target consists of a single task that
builds both with two dependent targets that compute their individual traces.
-/
abbrev ActiveOleanAndCTarget := ActiveBuildTarget ActiveOleanAndCTargets
namespace ActiveOleanAndCTarget
abbrev oleanFile (self : ActiveOleanAndCTarget) := self.info.oleanFile
abbrev oleanTarget (self : ActiveOleanAndCTarget) := self.info.oleanTarget
abbrev cFile (self : ActiveOleanAndCTarget) := self.info.cFile
abbrev cTarget (self : ActiveOleanAndCTarget) := self.info.cTarget
end ActiveOleanAndCTarget
def OleanAndCTarget.activate (self : OleanAndCTarget) : SchedulerM ActiveOleanAndCTarget := do
let t ← BuildTarget.activate self
let oleanTask ← t.bindSync fun info depTrace => do
return mixTrace (← computeTrace info.oleanFile) depTrace
let cTask ← t.bindSync fun info _ => do
return mixTrace (← computeTrace info.cFile) (← getLeanTrace)
return t.withInfo {
oleanTarget := ActiveTarget.mk self.oleanFile oleanTask
cTarget := ActiveTarget.mk self.cFile cTask
}
-- # Module Builders
def moduleTarget
[CheckExists i] [GetMTime i] [ComputeHash i m] [MonadLiftT m BuildM] (info : i)
(leanFile traceFile : FilePath) (contents : String) (depTarget : BuildTarget x)
(argTrace : BuildTrace) (build : BuildM PUnit) : BuildTarget i :=
Target.mk info <| depTarget.bindOpaqueSync fun depTrace => do
let srcTrace : BuildTrace := ⟨Hash.ofString contents, ← getMTime leanFile⟩
let fullTrace := (← getLeanTrace).mix <| argTrace.mix <| srcTrace.mix depTrace
let upToDate ← fullTrace.checkAgainstFile info traceFile
unless upToDate do
build
fullTrace.writeToFile traceFile
def moduleTarget (mod : Module)
(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 mod.leanArgs (← getLean)
modTrace.writeToFile mod.cTraceFile
else
unless modUpToDate do
compileLeanModule mod.leanFile mod.oleanFile mod.ileanFile none
(← getOleanPath) mod.pkg.rootDir mod.leanArgs (← getLean)
modTrace.writeToFile mod.traceFile
return depTrace
def moduleOleanAndCTarget
(leanFile cFile oleanFile traceFile : FilePath)
(contents : String) (depTarget : BuildTarget x)
(rootDir : FilePath := ".") (leanArgs : Array String := #[]) : OleanAndCTarget :=
let info := OleanAndC.mk oleanFile cFile
let ileanFile := oleanFile.withExtension "ilean"
moduleTarget info leanFile traceFile contents depTarget (pureHash leanArgs) do
compileLeanModule leanFile oleanFile ileanFile cFile (← getOleanPath) rootDir leanArgs (← getLean)
def moduleOleanTarget
(leanFile oleanFile traceFile : FilePath)
(contents : String) (depTarget : BuildTarget x)
(rootDir : FilePath := ".") (leanArgs : Array String := #[]) : FileTarget :=
let ileanFile := oleanFile.withExtension "ilean"
let target := moduleTarget oleanFile leanFile traceFile contents depTarget (pureHash leanArgs) do
compileLeanModule leanFile oleanFile ileanFile none (← getOleanPath) rootDir leanArgs (← getLean)
target.withTask do target.bindSync fun oleanFile depTrace => do
return mixTrace (← computeTrace oleanFile) depTrace
protected def Package.moduleOleanAndCTargetOnly (self : Package)
(mod : Name) (leanFile : FilePath) (contents : String) (depTarget : BuildTarget x) :=
let cFile := self.modToC mod
let oleanFile := self.modToOlean mod
let traceFile := self.modToTraceFile mod
moduleOleanAndCTarget leanFile cFile oleanFile traceFile contents
depTarget self.rootDir self.moreLeanArgs
protected def Package.moduleOleanTargetOnly (self : Package)
(mod : Name) (leanFile : FilePath) (contents : String) (depTarget : BuildTarget x) :=
let oleanFile := self.modToOlean mod
let traceFile := self.modToTraceFile mod
moduleOleanTarget leanFile oleanFile traceFile contents depTarget
self.rootDir self.moreLeanArgs
-- # Recursive Building
/-
Produces a recursive module target builder that
builds the target module after recursively building its local imports
/--
Produces a recursive module target builder that builds the target module
with the `build` function after recursively building its local imports
(relative to the workspace).
-/
def recBuildModuleWithLocalImports
[Monad m] [MonadLiftT BuildM m] [MonadFunctorT BuildM m]
(build : Package → Name → FilePath → String → List α → BuildM α)
: RecBuild Module α m := fun info recurse => do
def recBuildModuleWithLocalImports [Monad m] [MonadLiftT BuildM m]
(build : Module → List α → m α) : RecBuild Module α m := fun mod recurse => do
have : MonadLift BuildM m := ⟨liftM⟩
have : MonadFunctor BuildM m := ⟨fun f => monadMap (m := BuildM) f⟩
let contents ← IO.FS.readFile info.srcFile
let (imports, _, _) ← Elab.parseImports contents info.srcFile.toString
let contents ← IO.FS.readFile mod.leanFile
let (imports, _, _) ← Elab.parseImports contents mod.leanFile.toString
-- we construct the import targets even if a rebuild is not required
-- because other build processes (ex. `.o`) rely on the map being complete
let importTargets ← imports.filterMapM fun imp => OptionT.run do
let mod := imp.module
let pkg ← OptionT.mk <| getPackageForModule? mod
recurse ⟨pkg, mod⟩
build info.pkg info.name info.srcFile contents importTargets
recurse <| ← OptionT.mk <| findModule? imp.module
build mod importTargets
def recBuildModuleOleanAndCTargetWithLocalImports
[Monad m] [MonadLiftT BuildM m] [MonadFunctorT BuildM m] (depTarget : ActiveBuildTarget x)
: RecBuild Module ActiveOleanAndCTarget m :=
recBuildModuleWithLocalImports fun pkg mod leanFile contents importTargets => do
let importTarget ← ActiveTarget.collectOpaqueList <| importTargets.map (·.oleanTarget)
let allDepsTarget := Target.active <| ← depTarget.mixOpaqueAsync importTarget
pkg.moduleOleanAndCTargetOnly mod leanFile contents allDepsTarget |>.activate
def recBuildModuleOleanTargetWithLocalImports
[Monad m] [MonadLiftT BuildM m] [MonadFunctorT BuildM m] (depTarget : ActiveBuildTarget x)
: RecBuild Module ActiveFileTarget m :=
recBuildModuleWithLocalImports fun pkg mod leanFile contents importTargets => do
/--
Produces a recursive module target builder that builds the
`olean`, `ilean`, and (optionally) `c` files of a module and its local imports
after `extraDepTarget` finishes building.
-/
def recBuildModuleTargetWithDeps
[Monad m] [MonadLiftT BuildM m] [MonadStore ModuleBuildKey ActiveOpaqueTarget m]
(extraDepTarget : ActiveBuildTarget x) (c := true) : RecBuild Module ActiveOpaqueTarget m :=
recBuildModuleWithLocalImports fun mod importTargets => do
have : MonadLift BuildM m := ⟨liftM⟩
let importTarget ← ActiveTarget.collectOpaqueList importTargets
let allDepsTarget := Target.active <| ← depTarget.mixOpaqueAsync importTarget
pkg.moduleOleanTargetOnly mod leanFile contents allDepsTarget |>.activate
let allDepsTarget := Target.active <| ← extraDepTarget.mixOpaqueAsync importTarget
let modTarget := (← moduleTarget mod allDepsTarget c |>.activate).withoutInfo
if c then
let cTarget ← liftM (m := SchedulerM) do
return ActiveTarget.opaque <| ← modTarget.bindOpaqueSync (m := BuildM) fun _ => do
return mixTrace (← computeTrace mod.cFile) (← getLeanTrace)
store (mod.key `lean.c) cTarget
return modTarget
-- ## Definitions
abbrev ModuleMap (α) :=
Std.RBMap ModuleBuildKey α ModuleBuildKey.quickCmp
abbrev ModuleBuildM (α) :=
-- equivalent to `RBTopT (cmp := Name.quickCmp) Name α BuildM`.
-- phrased this way to use `NameMap`
EStateT (List Name) (NameMap α) BuildM
-- equivalent to `RBTopT ModuleBuildKey α BuildM ModuleBuildKey.quickCmp`
-- phrased this way to use `ModuleMap`
EStateT (List ModuleBuildKey) (ModuleMap α) BuildM
abbrev RecModuleBuild (o) :=
RecBuild Module o (ModuleBuildM o)
abbrev RecModuleTargetBuild (i) :=
RecModuleBuild (ActiveBuildTarget i)
abbrev RecModuleTargetBuild :=
RecModuleBuild ActiveOpaqueTarget
-- ## Builders
-- ## Multi-Module Builders
/-- Build a single module using the recursive module build function `build`. -/
def buildModule (mod : Module)
[Inhabited o] (build : RecModuleBuild o) : BuildM o := do
failOnBuildCycle <| ← RBTopT.run' do
buildRBTop (cmp := Name.quickCmp) build Module.name mod
buildTop build (·.key `lean) mod
/--
Build each module using the recursive module function `build`,
@ -235,16 +131,15 @@ constructing an `Array` of the results.
def buildModuleArray (mods : Array Module)
[Inhabited o] (build : RecModuleBuild o) : BuildM (Array o) := do
failOnBuildCycle <| ← RBTopT.run' <| mods.mapM <|
buildRBTop (cmp := Name.quickCmp) build Module.name
buildTop build (·.key `lean)
/--
Build each module using the recursive module function `build`,
constructing a module-target `NameMap` of the results.
-/
def buildModuleMap [Inhabited o]
(infos : Array Module) (build : RecModuleBuild o)
: BuildM (NameMap o) := do
let (x, objMap) ← RBTopT.run do
infos.forM fun info => discard <| buildRBTop build Module.name info
def buildModuleMap (mods : Array Module)
[Inhabited o] (build : RecModuleBuild o) : BuildM (ModuleMap o) := do
let (x, map) ← RBTopT.run <| mods.forM fun mod =>
discard <| buildTop build (·.key `lean) mod
failOnBuildCycle x
return objMap
return map

View file

@ -3,9 +3,6 @@ Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Gabriel Ebner, Sebastian Ullrich, Mac Malone
-/
import Lean.Data.Name
import Lean.Elab.Import
import Lake.Config.Package
import Lake.Build.Module
open System
@ -22,11 +19,11 @@ protected def Package.buildExtraDepsTarget (self : Package) : SchedulerM ActiveO
let depTarget ← ActiveTarget.collectOpaqueArray depTargets
extraDepTarget.mixOpaqueAsync depTarget
let build dep recurse := do
let pkg := (← getPackageByName? dep.name).get!
let pkg := (← findPackage? dep.name).get!
let depTargets ← pkg.dependencies.mapM recurse
liftM <| collect pkg depTargets
let targetsE ← RBTopT.run' <| self.dependencies.mapM fun dep =>
buildRBTop (cmp := Name.quickCmp) build Dependency.name dep
let targetsE ← RBTopT.run' (cmp := Name.quickCmp) <| self.dependencies.mapM fun dep =>
buildTop build Dependency.name dep
match targetsE with
| Except.ok targets => collect self targets
| Except.error _ => panic! "dependency cycle emerged after resolution"
@ -38,39 +35,49 @@ def buildExtraDepsTarget : SchedulerM ActiveOpaqueTarget := do
-- # Build Package Modules
def Package.getLibModuleArray (lib : LeanLibConfig) (self : Package) : IO (Array Module) := do
return (← lib.getModuleArray self.srcDir).map (Module.mk self)
/--
Build the `extraDepTarget` of a package and its (transitive) dependencies
and then build the library's modules recursively using the `build` function,
constructing a single opaque target for the whole build.
-/
def Package.buildLibModules (self : Package) (lib : LeanLibConfig)
[Inhabited i] (build : ActiveOpaqueTarget → RecModuleTargetBuild i) : SchedulerM (BuildTask BuildTrace) := do
(build : ActiveOpaqueTarget → RecModuleTargetBuild) : SchedulerM Job := do
let depTarget ← self.buildExtraDepsTarget
let buildMods : BuildM _ := do
let infos := (← lib.getModuleArray self.srcDir).map (Module.mk self)
let modTargets ← buildModuleArray infos (build depTarget)
let mods ← self.getLibModuleArray lib
let modTargets ← buildModuleArray mods (build depTarget)
(·.task) <$> ActiveTarget.collectOpaqueArray modTargets
buildMods.catchFailure fun _ => pure <| failure
def Package.mkLibTarget (self : Package) (lib : LeanLibConfig) : OpaqueTarget :=
Target.opaque <| self.buildLibModules lib recBuildModuleOleanTargetWithLocalImports
Target.opaque <| self.buildLibModules lib
(recBuildModuleTargetWithDeps · (c := false))
def Package.libTarget (self : Package) : OpaqueTarget :=
self.mkLibTarget self.builtinLibConfig
-- # Build Specific Modules of the Package
def Package.moduleOleanTarget (mod : Name) (self : Package) : FileTarget :=
BuildTarget.mk' (self.modToOlean mod) do
let depTarget ← self.buildExtraDepsTarget
let build := recBuildModuleOleanTargetWithLocalImports depTarget
return (← buildModule self, mod⟩ build).task
def Module.leanBinTarget (c := false) (self : Module) : OpaqueTarget :=
BuildTarget.mk' () do
let depTarget ← self.pkg.buildExtraDepsTarget
let build := recBuildModuleTargetWithDeps depTarget (c := c)
return (← buildModule self build).task
def Package.moduleOleanAndCTarget (mod : Name) (self : Package) : OleanAndCTarget :=
BuildTarget.mk' ⟨self.modToOlean mod, self.modToC mod⟩ do
let depTarget ← self.buildExtraDepsTarget
let build := recBuildModuleOleanAndCTargetWithLocalImports depTarget
return (← buildModule ⟨self, mod⟩ build).task
@[inline] def Module.oleanTarget (self : Module) : FileTarget :=
self.leanBinTarget false |>.withInfo self.oleanFile
@[inline] def Module.ileanTarget (self : Module) : FileTarget :=
self.leanBinTarget false |>.withInfo self.ileanFile
def Module.mkCTarget (modTarget : OpaqueTarget) (self : Module) : FileTarget :=
Target.mk self.cFile <| modTarget.bindOpaqueSync fun _ => computeTrace self.cFile
@[inline] def Module.cTarget (self : Module) : FileTarget :=
self.mkCTarget <| self.leanBinTarget (c := true)
-- # Build Imports
@ -82,9 +89,8 @@ def Workspace.processImportList
(imports : List String) (self : Workspace) : Array Module := Id.run do
let mut localImports := #[]
for imp in imports do
let mod := imp.toName
if let some pkg := self.packageForModule? mod then
localImports := localImports.push ⟨pkg, mod⟩
if let some mod := self.findModule? imp.toName then
localImports := localImports.push mod
return localImports
/--
@ -101,12 +107,12 @@ def Package.buildImportsAndDeps (imports : List String) (self : Package) : Build
depTarget.buildOpaque
else
-- build local imports from list
let infos := (← getWorkspace).processImportList imports
let mods := (← getWorkspace).processImportList imports
if self.leanExes.isEmpty && self.defaultFacet matches .none | .leanLib | .oleans then
let build := recBuildModuleOleanTargetWithLocalImports depTarget
let targets ← buildModuleArray infos build
let build := recBuildModuleTargetWithDeps depTarget (c := false)
let targets ← buildModuleArray mods build
targets.forM (·.buildOpaque)
else
let build := recBuildModuleOleanAndCTargetWithLocalImports depTarget
let targets ← buildModuleArray infos build
let build := recBuildModuleTargetWithDeps depTarget (c := true)
let targets ← buildModuleArray mods build
targets.forM (·.buildOpaque)

View file

@ -102,8 +102,3 @@ def run' [Monad m] (self : RBTopT k o cmp m α) :=
Functor.map (·.1) self.run
end RBTopT
/-- The `RBMap` version of `buildTop`. -/
def buildRBTop {k o} {cmp} {m} [BEq k] [Inhabited o] [Monad m]
(build : RecBuild i o (RBTopT k o cmp m)) (keyOf : i → k) (info : i) : RBTopT k o cmp m o :=
buildTop build keyOf info

View file

@ -21,10 +21,10 @@ abbrev ActiveBuildTarget i := ActiveTarget i BuildTask BuildTrace
namespace BuildTarget
abbrev mk (info : i) (task : SchedulerM (BuildTask BuildTrace)) : BuildTarget i :=
abbrev mk (info : i) (task : SchedulerM Job) : BuildTarget i :=
Target.mk info task
abbrev mk' (info : i) (task : BuildM (BuildTask BuildTrace)) : BuildTarget i :=
abbrev mk' (info : i) (task : BuildM Job) : BuildTarget i :=
Target.mk info <| task.catchFailure fun _ => pure failure
abbrev activate (self : BuildTarget i) : SchedulerM (ActiveBuildTarget i) :=
@ -70,10 +70,10 @@ abbrev ActiveOpaqueTarget := ActiveBuildTarget PUnit
namespace OpaqueTarget
abbrev mk (task : SchedulerM (BuildTask BuildTrace)) : OpaqueTarget :=
abbrev mk (task : SchedulerM Job) : OpaqueTarget :=
Target.opaque task
abbrev mk' (task : BuildM (BuildTask BuildTrace)) : OpaqueTarget :=
abbrev mk' (task : BuildM Job) : OpaqueTarget :=
Target.opaque <| task.catchFailure fun _ => pure failure
abbrev async (act : BuildM BuildTrace) : OpaqueTarget :=

View file

@ -64,12 +64,12 @@ def leanSharedLibTarget (binFile : FilePath)
fileTargetWithDepArray binFile linkTargets fun links => do
compileSharedLib binFile links linkArgs (← getLeanc)
def binTarget (binFile : FilePath) (linkTargets : Array FileTarget)
def cExeTarget (binFile : FilePath) (linkTargets : Array FileTarget)
(linkArgs : Array String := #[]) (linker : FilePath := "cc") : FileTarget :=
fileTargetWithDepArray binFile linkTargets (extraDepTrace := computeHash linkArgs) fun links => do
compileBin binFile links linkArgs linker
def leanBinTarget (binFile : FilePath)
def leanExeTarget (binFile : FilePath)
(linkTargets : Array FileTarget) (linkArgs : Array String := #[]) : FileTarget :=
fileTargetWithDepArray binFile linkTargets
(extraDepTrace := getLeanTrace <&> (·.mix <| pureHash linkArgs)) fun links => do

View file

@ -22,22 +22,19 @@ def parsePackageSpec (ws : Workspace) (spec : String) : Except CliError Package
if spec.isEmpty then
return ws.root
else
match ws.packageByName? spec.toName with
match ws.findPackage? spec.toName with
| some pkg => return pkg
| none => throw <| CliError.unknownPackage spec
def resolveModuleTarget (pkg : Package) (mod : Name) (facet : String) : Except CliError OpaqueTarget :=
if pkg.hasModule mod then
if facet.isEmpty || facet == "olean" then
return pkg.moduleOleanTarget mod |>.withoutInfo
else if facet == "c" then
return pkg.moduleOleanAndCTarget mod |>.withoutInfo
else if facet == "o" then
return pkg.moduleOTarget mod |>.withoutInfo
else
throw <| CliError.unknownFacet "module" facet
def resolveModuleTarget (mod : Module) (facet : String) : Except CliError OpaqueTarget :=
if facet.isEmpty || facet == "bin" || facet == "ilean" || facet == "olean" then
return mod.leanBinTarget (c := false)
else if facet == "c" then
return mod.leanBinTarget (c := true)
else if facet == "o" then
return mod.oTarget.withoutInfo
else
throw <| CliError.missingModule pkg.name mod
throw <| CliError.unknownFacet "module" facet
def resolveLibTarget (pkg : Package) (lib : LeanLibConfig) (facet : String) : Except CliError OpaqueTarget :=
if facet.isEmpty || facet == "lean" || facet == "oleans" then
@ -65,8 +62,8 @@ def resolveTargetInPackage (pkg : Package) (target : Name) (facet : String) : Ex
throw <| CliError.invalidFacet target facet
else if let some lib := pkg.findLeanLib? target then
resolveLibTarget pkg lib facet
else if pkg.hasModule target then
resolveModuleTarget pkg target facet
else if let some mod := pkg.findModule? target then
resolveModuleTarget mod facet
else
throw <| CliError.missingTarget pkg.name (target.toString false)
@ -92,19 +89,19 @@ def resolvePackageTarget (pkg : Package) (facet : String) : Except CliError Opaq
throw <| CliError.unknownFacet "package" facet
def resolveTargetInWorkspace (ws : Workspace) (spec : String) (facet : String) : Except CliError OpaqueTarget :=
if let some (pkg, exe) := ws.findLeanExe? spec then
if let some (pkg, exe) := ws.findLeanExe? spec.toName then
resolveExeTarget pkg exe facet
else if let some (_, lib) := ws.findExternLib? spec then
else if let some (_, lib) := ws.findExternLib? spec.toName then
if facet.isEmpty then
return lib.target.withoutInfo
else
throw <| CliError.invalidFacet spec facet
else if let some (pkg, lib) := ws.findLeanLib? spec then
else if let some (pkg, lib) := ws.findLeanLib? spec.toName then
resolveLibTarget pkg lib facet
else if let some pkg := ws.packageByName? spec then
else if let some pkg := ws.findPackage? spec.toName then
resolvePackageTarget pkg facet
else if let some pkg := ws.packageForModule? spec then
resolveModuleTarget pkg spec facet
else if let some mod := ws.findModule? spec.toName then
resolveModuleTarget mod facet
else
throw <| CliError.unknownTarget spec
@ -118,8 +115,8 @@ def resolveTargetBaseSpec (ws : Workspace) (spec : String) (facet := "") : Excep
resolvePackageTarget pkg facet
else if spec.startsWith "+" then
let mod := spec.drop 1 |>.toName
if let some pkg := ws.packageForModule? mod then
resolveModuleTarget pkg mod facet
if let some mod := ws.findModule? mod then
resolveModuleTarget mod facet
else
throw <| CliError.unknownModule mod
else
@ -129,7 +126,10 @@ def resolveTargetBaseSpec (ws : Workspace) (spec : String) (facet := "") : Excep
let pkg ← parsePackageSpec ws pkgSpec
if targetSpec.startsWith "+" then
let mod := targetSpec.drop 1 |>.toName
resolveModuleTarget pkg mod facet
if let some mod := pkg.findModule? mod then
resolveModuleTarget mod facet
else
throw <| CliError.unknownModule mod
else
resolveTargetInPackage pkg spec facet
| _ =>

View file

@ -8,5 +8,6 @@ import Lake.Config.InstallPath
import Lake.Config.Workspace
import Lake.Config.Script
import Lake.Config.Package
import Lake.Config.Module
import Lake.Config.Monad
import Lake.Config.SearchPath

66
Lake/Config/Module.lean Normal file
View file

@ -0,0 +1,66 @@
/-
Copyright (c) 2022 Mac Malone. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mac Malone
-/
import Lake.Build.Trace
import Lake.Config.Package
namespace Lake
open System Lean
/-- A buildable Lean module of a `Package`. -/
structure Module where
pkg : Package
name : Name
deriving Inhabited
/-- Locate the named module in the package (if it is local to it). -/
def Package.findModule? (mod : Name) (self : Package) : Option Module :=
if self.isBuildableModule mod then some ⟨self, mod⟩ else none
namespace Module
def leanFile (self : Module) : FilePath :=
Lean.modToFilePath self.pkg.srcDir self.name "lean"
def oleanFile (self : Module) : FilePath :=
Lean.modToFilePath self.pkg.oleanDir self.name "olean"
def ileanFile (self : Module) : FilePath :=
Lean.modToFilePath self.pkg.oleanDir self.name "ilean"
def traceFile (self : Module) : FilePath :=
Lean.modToFilePath self.pkg.oleanDir self.name "trace"
def cFile (self : Module) : FilePath :=
Lean.modToFilePath self.pkg.irDir self.name "c"
def cTraceFile (self : Module) : FilePath :=
Lean.modToFilePath self.pkg.irDir self.name "c.trace"
def oFile (self : Module) : FilePath :=
Lean.modToFilePath self.pkg.irDir self.name "o"
@[inline] def leanArgs (self : Module) : Array String :=
self.pkg.moreLeanArgs
@[inline] def leancArgs (self : Module) : Array String :=
self.pkg.moreLeancArgs
-- ## Trace Helpers
protected def getMTime (self : Module) : IO MTime := do
return mixTrace (← getMTime self.oleanFile) (← getMTime self.ileanFile)
instance : GetMTime Module := ⟨Module.getMTime⟩
protected def computeHash (self : Module) : IO Hash := do
return mixTrace (← computeHash self.oleanFile) (← computeHash self.ileanFile)
instance : ComputeHash Module IO := ⟨Module.computeHash⟩
protected def checkExists (self : Module) : BaseIO Bool := do
return (← checkExists self.oleanFile) && (← checkExists self.ileanFile)
instance : CheckExists Module := ⟨Module.checkExists⟩

View file

@ -49,11 +49,11 @@ def getLeanEnv [Monad m] : m (Array (String × Option String)) := do
variable [Functor m]
@[inline] def getPackageByName? (name : Name) : m (Option Package) :=
(·.packageByName? name) <$> getWorkspace
@[inline] def findPackage? (name : Name) : m (Option Package) :=
(·.findPackage? name) <$> getWorkspace
@[inline] def getPackageForModule? (mod : Name) : m (Option Package) :=
(·.packageForModule? mod) <$> getWorkspace
@[inline] def findModule? (mod : Name) : m (Option Module) :=
(·.findModule? mod) <$> getWorkspace
@[inline] def getOleanPath : m SearchPath :=
(·.oleanPath) <$> getWorkspace

View file

@ -331,10 +331,6 @@ def extraDepTarget (self : Package) : OpaqueTarget :=
def defaultFacet (self : Package) : PackageFacet :=
self.config.defaultFacet
/-- The package's `moreLibTargets` configuration. -/
def moreLibTargets (self : Package) : Array FileTarget :=
self.config.moreLibTargets
/-- Get the package's library configuration with the given name. -/
def findLeanLib? (name : Name) (self : Package) : Option LeanLibConfig :=
self.leanLibs.find? name
@ -349,7 +345,8 @@ def findExternLib? (name : Name) (self : Package) : Option ExternLibConfig :=
/-- Get an `Array` of the package's external library targets. -/
def externLibTargets (self : Package) : Array FileTarget :=
self.externLibs.fold (fun xs _ x => xs.push x.target) #[] ++ self.moreLibTargets
self.externLibs.fold (fun xs _ x => xs.push x.target) #[] ++
self.config.moreLibTargets
/-- The package's `moreServerArgs` configuration. -/
def moreServerArgs (self : Package) : Array String :=
@ -359,14 +356,10 @@ def moreServerArgs (self : Package) : Array String :=
def srcDir (self : Package) : FilePath :=
self.dir / self.config.srcDir
/-- The package's root directory for Lean (i.e., `srcDir`). -/
/-- The package's root directory for `lean` (i.e., `srcDir`). -/
def rootDir (self : Package) : FilePath :=
self.srcDir
/-- The path to a module's `.lean` source file within the package. -/
def modToSrc (mod : Name) (self : Package) : FilePath :=
Lean.modToFilePath self.srcDir mod "lean"
/-- The package's `dir` joined with its `buildDir` configuration. -/
def buildDir (self : Package) : FilePath :=
self.dir / self.config.buildDir
@ -379,18 +372,6 @@ def oleanDir (self : Package) : FilePath :=
def moreLeanArgs (self : Package) : Array String :=
self.config.moreLeanArgs
/-- The path to a module's `.olean` file within the package. -/
def modToOlean (mod : Name) (self : Package) : FilePath :=
Lean.modToFilePath self.oleanDir mod "olean"
/-- The path to a module's `.ilean` file within the package. -/
def modToIlean (mod : Name) (self : Package) : FilePath :=
Lean.modToFilePath self.oleanDir mod "ilean"
/-- The path to module's `.trace` file within the package. -/
def modToTraceFile (mod : Name) (self : Package) : FilePath :=
Lean.modToFilePath self.oleanDir mod "trace"
/- `-O3`, `-DNDEBUG`, and `moreLeancArgs` -/
def moreLeancArgs (self : Package) : Array String :=
#["-O3", "-DNDEBUG"] ++ self.config.moreLeancArgs
@ -399,22 +380,6 @@ def moreLeancArgs (self : Package) : Array String :=
def irDir (self : Package) : FilePath :=
self.buildDir / self.config.irDir
/-- To which Lake should output the package's `.c` files (.e., `irDir`). -/
def cDir (self : Package) : FilePath :=
self.irDir
/-- The path to a module's `.c` file within the package. -/
def modToC (mod : Name) (self : Package) : FilePath :=
Lean.modToFilePath self.cDir mod "c"
/-- To which Lake should output the package's `.o` files (.e., `irDir`). -/
def oDir (self : Package) : FilePath :=
self.irDir
/-- The path to a module's `.o` file within the package. -/
def modToO (mod : Name) (self : Package) : FilePath :=
Lean.modToFilePath self.oDir mod "o"
/-- The package's `buildDir` joined with its `libDir` configuration. -/
def libDir (self : Package) : FilePath :=
self.buildDir / self.config.libDir
@ -441,8 +406,8 @@ def isLocalModule (mod : Name) (self : Package) : Bool :=
self.builtinLibConfig.isLocalModule mod
/-- Whether the given module is in the package (i.e., can build it). -/
def hasModule (mod : Name) (self : Package) : Bool :=
self.leanLibs.any (fun _ lib => lib.hasModule mod) ||
def isBuildableModule (mod : Name) (self : Package) : Bool :=
self.leanLibs.any (fun _ lib => lib.isBuildableModule mod) ||
self.leanExes.any (fun _ exe => exe.root == mod) ||
self.builtinLibConfig.hasModule mod ||
self.builtinLibConfig.isBuildableModule mod ||
self.config.binRoot == mod

View file

@ -120,8 +120,8 @@ def resolveDeps (ws : Workspace) (pkg : Package)
let pkg ← resolveDep ws pkg dep shouldUpdate
pkg.dependencies.forM fun dep => discard <| resolve dep
return pkg
let (res, map) ← RBTopT.run <| pkg.dependencies.forM fun dep =>
discard <| buildRBTop (cmp := Name.quickCmp) resolve Dependency.name dep
let (res, map) ← RBTopT.run (cmp := Name.quickCmp) <| pkg.dependencies.forM fun dep =>
discard <| buildTop resolve Dependency.name dep
match res with
| Except.ok _ => return map
| Except.error cycle => do

View file

@ -61,8 +61,8 @@ def isLocalModule (mod : Name) (self : LeanLibConfig) : Bool :=
self.roots.any (fun root => root.isPrefixOf mod) ||
self.globs.any (fun glob => glob.matches mod)
/-- Whether the given module is in the library (i.e., is built as part of it). -/
def hasModule (mod : Name) (self : LeanLibConfig) : Bool :=
/-- Whether the given module is a buildable part of the library. -/
def isBuildableModule (mod : Name) (self : LeanLibConfig) : Bool :=
self.globs.any (fun glob => glob.matches mod) ||
self.roots.any (fun root => root.isPrefixOf mod && self.globs.any (·.matches root))

View file

@ -7,6 +7,7 @@ import Lean.Util.Paths
import Lake.Config.Opaque
import Lake.Config.WorkspaceConfig
import Lake.Config.Package
import Lake.Config.Module
open System
open Lean (Name NameMap LeanPaths)
@ -80,20 +81,16 @@ def addPackage (pkg : Package) (self : Workspace) : Workspace :=
{self with packageMap := self.packageMap.insert pkg.name pkg}
/-- Get a package within the workspace by name. -/
def packageByName? (pkg : Name) (self : Workspace) : Option Package :=
def findPackage? (pkg : Name) (self : Workspace) : Option Package :=
self.packageMap.find? pkg
/-- Find a package in the workspace satisfying the given predicate (if one exists). -/
def findPackage? (f : Package → Bool) (self : Workspace) : Option Package :=
self.packageArray.find? f
/-- Check if the module is local to any package in the workspace. -/
def isLocalModule (mod : Name) (self : Workspace) : Bool :=
self.packageMap.any fun _ pkg => pkg.isLocalModule mod
/-- Get the package for the module in the workspace (if it is local to one). -/
def packageForModule? (mod : Name) (self : Workspace) : Option Package :=
self.findPackage? (·.isLocalModule mod)
/-- Locate the named module in the workspace (if it is local to it). -/
def findModule? (mod : Name) (self : Workspace) : Option Module :=
self.packageArray.findSome? (·.findModule? mod)
/-- Get the workspace's library configuration with the given name. -/
def findLeanLib? (name : Name) (self : Workspace) : Option (Package × LeanLibConfig) :=

View file

@ -15,8 +15,14 @@ fi
./clean.sh
$LAKE build +Foo.Test
test -f ./build/lib/Foo/Test.olean
test ! -f ./build/lib/Foo/Test.c
$LAKE build Bar:o
test -f ./build/ir/Bar.o
$LAKE build
./build/bin/c