/- Copyright (c) 2021 Mac Malone. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Mac Malone -/ import Lake.Task import Lake.Trace import Lake.RealM import Lake.LogMonad import Lake.InstallPath open System namespace Lake -------------------------------------------------------------------------------- -- # Build Monad Definition -------------------------------------------------------------------------------- structure BuildContext where leanTrace : BuildTrace leanInstall : LeanInstall lakeInstall : LakeInstall deriving Inhabited abbrev BuildCoreM := LogMethodsT RealM <| EIO PUnit abbrev BuildM := ReaderT BuildContext BuildCoreM -------------------------------------------------------------------------------- -- # Build Monad Utilities -------------------------------------------------------------------------------- def getLeanInstall : BuildM LeanInstall := (·.leanInstall) <$> read def getLeanIncludeDir : BuildM FilePath := (·.includeDir) <$> getLeanInstall def getLean : BuildM FilePath := (·.lean) <$> getLeanInstall def getLeanTrace : BuildM BuildTrace := do (·.leanTrace) <$> read def getLeanc : BuildM FilePath := (·.leanc) <$> getLeanInstall def getLakeInstall : BuildM LakeInstall := (·.lakeInstall) <$> read namespace BuildCoreM def run (logMethods : LogMethods RealM) (self : BuildCoreM α) : IO α := ReaderT.run self logMethods |>.toIO fun _ => IO.userError "build failed" def runWith (logMethods : LogMethods RealM) (self : BuildCoreM α) : EIO PUnit α := ReaderT.run self logMethods protected def failure : BuildCoreM α := throw () protected def orElse (self : BuildCoreM α) (f : Unit → BuildCoreM α) : BuildCoreM α := tryCatch self f instance : Alternative BuildCoreM where failure := BuildCoreM.failure orElse := BuildCoreM.orElse def runIO (x : IO α) : BuildCoreM α := do match (← RealM.runIO' x) with | Except.ok a => pure a | Except.error e => do logError (toString e) failure instance : MonadLift IO BuildCoreM := ⟨runIO⟩ instance : MonadLift (LogT IO) BuildCoreM where monadLift x := fun meths => runIO (x.run meths.lift) meths end BuildCoreM def BuildM.run (logMethods : LogMethods RealM) (ctx : BuildContext) (self : BuildM α) : IO α := self ctx |>.run logMethods def failOnBuildCycle [ToString k] : Except (List k) α → BuildM α | Except.ok a => a | Except.error cycle => do let cycle := cycle.map (s!" {·}") logError s!"build cycle detected:\n{"\n".intercalate cycle}" failure /-- TODO: Replace with an `EIOTask PUnit` once Lean supports such a thing. -/ abbrev BuildTask := IOTask namespace BuildTask def spawn (act : BuildCoreM α) (prio := Task.Priority.dedicated) : BuildCoreM (BuildTask α) := fun meths => BuildCoreM.runWith meths do IO.asTask (act.run meths) prio instance : Async BuildCoreM BuildTask := ⟨spawn⟩ protected def await (self : BuildTask α) : BuildCoreM α := do match (← IO.wait self) with | Except.ok a => pure a | Except.error e => logError (toString e); failure instance : Await BuildTask BuildCoreM := ⟨BuildTask.await⟩ protected def mapAsync (f : α → BuildCoreM β) (self : BuildTask α) (prio := Task.Priority.dedicated) : BuildCoreM (BuildTask β) := fun meths => BuildCoreM.runWith meths do self.mapAsync (fun a => f a |>.run meths) prio instance : MapAsync BuildCoreM BuildTask := ⟨BuildTask.mapAsync⟩ protected def bindAsync (self : BuildTask α) (f : α → BuildCoreM (BuildTask β)) (prio := Task.Priority.dedicated) : BuildCoreM (BuildTask β) := fun meths => BuildCoreM.runWith meths do self.bindAsync (fun a => f a |>.run meths) prio instance : BindAsync BuildCoreM BuildTask := ⟨BuildTask.bindAsync⟩ protected def seqLeftAsync (self : BuildTask α) (act : BuildCoreM β) (prio := Task.Priority.dedicated) : BuildCoreM (BuildTask α) := fun meths => BuildCoreM.runWith meths do self.seqLeftAsync (act.run meths) prio instance : SeqLeftAsync BuildCoreM BuildTask := ⟨BuildTask.seqLeftAsync⟩ protected def seqRightAsync (self : BuildTask α) (act : BuildCoreM β) (prio := Task.Priority.dedicated) : BuildCoreM (BuildTask β) := fun meths => BuildCoreM.runWith meths do self.seqRightAsync (act.run meths) prio instance : SeqRightAsync BuildCoreM BuildTask := ⟨BuildTask.seqRightAsync⟩ end BuildTask