Skip to content

Commit 28fb4bb

Browse files
authored
feat: lake cache (& remote cache support) (#10188)
This PR adds support for remote artifact caches (e.g., Reservoir) to Lake. As part of this support, a new suite of `lake cache` CLI commands has been introduced to help manage Lake's cache. Also, the existing local cache support has been overhauled for better interplay with the new remote support. **Cache CLI** Artifacts are uploaded to a remote cache via `lake cache put`. This command takes a JSON Lines input-to-outputs file which describes the output artifacts for a build (indexed by its input hash). This file can be produced by a run of `lake build` with the new `-o` option. Lake will write the input-to-outputs mappings of thee root package artifacts traversed by the build to the file specified via `-o`. This file can then be passed to `lake cache put` to upload both it and the built artifacts from the local cache to the remote cache. The remote cache service can be customized using the following environment variables: * `LAKE_CACHE_KEY`: This is the authorization key for the remote cache. Lake uploads artifacts via `curl` using the AWS Signature Version 4 protocol, so this should be the S3 `<key>:<secret>` pair expected by `curl`. * `LAKE_CACHE_ARTIFACT_ENDPOINT`: This is the base URL to upload (or download) artifacts to a given remote cache. Artifacts will be stored at `<endpoint>/<scope/<content-hash>.art`. * `LAKE_CACHE_REVISION_ENDPOINT`: This is the base URL to upload (or download) input-to-output mappings to a given remote cache. Mappings are indexed by the Git revision of the package, and are stored at `<endpoint>/<scope/<rev>.jsonl`. The `<scope>` is provided through the `--scope` option to `lake cache put`. This option is used to prevent one package from overwriting the artifacts/mappings of another. Lake artifact hashes and Git revisions hashes are not cryptographically secure, so it is not safe for a service to store untrusted files across packages in a single flat store. Once artifacts are available in a remote cache, the `lake cache get` command can be used to retrieve them. By default, it will fetch artifacts for the root package's dependencies from Reservoir using its API. But, like `cache put`, it can be configured to use a custom endpoint with the above environment variables and an explicit `--scope`. When so configured, `cache get` will instead download artifacts for the root package. Lake only downloads artifacts for a single package in this case, because it cannot deduce the necessary package scopes without Reservoir. **Significant local cache changes** * Lake now always has a cache directory. If Lake cannot find a good candidate directory on the system for the cache, it will instead store the cache at `.lake/cache` within the workspace. * If the local cache is disabled, Lake will not save built artifacts to the cache. However, Lake will, nonetheless, always attempt to lookup build artifacts in the cache. If found, the cached artifact will be copied to the the build location ("restored"). * Input-to-outputs mappings in the local cache are no longer stored in a single file for a package, but rather in individual files per input (in the `outputs` subdirectory of the cache). * Outputs in a trace file, outputs file, or mappings file are now an `ArtifactDescr`, which is currently composed of both the content hash and the file extension. * Trace files now contain a date-based `schemaVersion` to help make version to version migration easier. Hashes in JSON and in artifacts names now use a 16-digit hexadecimal encoding (instead of a variable decimal encoding). * `buildArtifactUnlessUpToDate` now returns an `Artifact` instead of a `FilePath`. **NOTE:** The Lake local cache is still disabled by default. This means that built artifacts, by default, will not be placed in the cache directory, and thus will not be available for `lake cache put` to upload. Users must first explicitly enable the cache by either setting the `LAKE_ARTIFACT_CACHE` environment variable to a truthy value or by setting the `enableArtifactCache` package configuration option to `true`.
1 parent 2231d9b commit 28fb4bb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1616
-501
lines changed

src/lake/Lake/Build/Common.lean

Lines changed: 203 additions & 120 deletions
Large diffs are not rendered by default.

src/lake/Lake/Build/Context.lean

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public structure BuildConfig where
4242
ansiMode : AnsiMode := .auto
4343
/-- Whether to print a message when the build finishes successfully (if not quiet). -/
4444
showSuccess : Bool := false
45+
/-- File to save input-to-output mappings from the build of the worksoace's root -/
46+
outputsFile? : Option FilePath := none
4547

4648
/--
4749
Whether the build should show progress information.

src/lake/Lake/Build/Module.lean

Lines changed: 103 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,16 @@ public def Module.setupFacetConfig : ModuleFacetConfig setupFacet :=
511511
public def Module.depsFacetConfig : ModuleFacetConfig depsFacet :=
512512
mkFacetJobConfig fun mod => (·.toOpaque) <$> mod.setup.fetch
513513

514+
/-- Remove all existing artifacts produced by the Lean build of the module. -/
515+
public def Module.clearOutputArtifacts (mod : Module) : IO PUnit := do
516+
removeFileIfExists mod.oleanFile
517+
removeFileIfExists mod.oleanServerFile
518+
removeFileIfExists mod.oleanPrivateFile
519+
removeFileIfExists mod.ileanFile
520+
removeFileIfExists mod.irFile
521+
removeFileIfExists mod.cFile
522+
removeFileIfExists mod.bcFile
523+
514524
/-- Remove any cached file hashes of the module build outputs (in `.hash` files). -/
515525
public def Module.clearOutputHashes (mod : Module) : IO PUnit := do
516526
clearFileHash mod.oleanFile
@@ -535,31 +545,34 @@ public def Module.cacheOutputHashes (mod : Module) : IO PUnit := do
535545
if Lean.Internal.hasLLVMBackend () then
536546
cacheFileHash mod.bcFile
537547

538-
private def ModuleOutputHashes.getArtifactsFrom?
539-
(cache : Cache) (hashes : ModuleOutputHashes)
548+
private def ModuleOutputDescrs.getArtifactsFrom?
549+
(cache : Cache) (descrs : ModuleOutputDescrs)
540550
: BaseIO (Option ModuleOutputArtifacts) := OptionT.run do
541551
let mut arts : ModuleOutputArtifacts := {
542-
olean := ← cache.getArtifact? hashes.olean "olean"
543-
ilean := ← cache.getArtifact? hashes.ilean "ilean"
544-
c :=← cache.getArtifact? hashes.c "c"
552+
olean := ← cache.getArtifact? descrs.olean
553+
ilean := ← cache.getArtifact? descrs.ilean
554+
c :=← cache.getArtifact? descrs.c
545555
}
546-
if let some hash := hashes.oleanServer? then
547-
arts := {arts with oleanServer? := some (← cache.getArtifact? hash "olean.server")}
548-
if let some hash := hashes.oleanPrivate? then
549-
arts := {arts with oleanPrivate? := some (← cache.getArtifact? hash "olean.private")}
550-
if let some hash := hashes.ir? then
551-
arts := {arts with ir? := some (← cache.getArtifact? hash "ir")}
556+
if let some hash := descrs.oleanServer? then
557+
arts := {arts with oleanServer? := some (← cache.getArtifact? hash)}
558+
if let some hash := descrs.oleanPrivate? then
559+
arts := {arts with oleanPrivate? := some (← cache.getArtifact? hash)}
560+
if let some hash := descrs.ir? then
561+
arts := {arts with ir? := some (← cache.getArtifact? hash)}
552562
if Lean.Internal.hasLLVMBackend () then
553-
arts := {arts with bc? := some (← cache.getArtifact? (← hashes.bc?) "bc")}
563+
arts := {arts with bc? := some (← cache.getArtifact? (← descrs.bc?))}
554564
return arts
555565

556-
@[inline] private def ModuleOutputHashes.getArtifacts?
557-
[MonadLakeEnv m] [MonadLiftT BaseIO m] [Monad m] (hashes : ModuleOutputHashes)
558-
: m (Option ModuleOutputArtifacts) := do hashes.getArtifactsFrom? (← getLakeCache)
566+
@[inline] def resolveModuleOutputs?
567+
[MonadWorkspace m] [MonadLiftT BaseIO m] [MonadError m] [Monad m] (outputs : Json)
568+
: m (Option ModuleOutputArtifacts) := do
569+
match fromJson? outputs with
570+
| .ok (descrs : ModuleOutputDescrs) => descrs.getArtifactsFrom? (← getLakeCache)
571+
| .error e => error e
559572

560-
private instance
561-
[MonadLakeEnv m] [MonadLiftT BaseIO m] [Monad m]
562-
: ResolveArtifacts m ModuleOutputHashes ModuleOutputArtifacts := ⟨ ModuleOutputHashes.getArtifacts?⟩
573+
instance
574+
[MonadWorkspace m] [MonadLiftT BaseIO m] [MonadError m] [Monad m]
575+
: ResolveOutputs m ModuleOutputArtifacts := ⟨resolveModuleOutputs?⟩
563576

564577
/-- Save module build artifacts to the local Lake cache. Requires the artifact cache to be enabled. -/
565578
private def Module.cacheOutputArtifacts (mod : Module) : JobM ModuleOutputArtifacts := do
@@ -578,22 +591,38 @@ where
578591
@[inline] cacheIfExists? art ext := do
579592
cacheIf? (← art.pathExists) art ext
580593

594+
private def restoreModuleArtifact (file : FilePath) (art : Artifact) : JobM Artifact := do
595+
unless (← file.pathExists) do
596+
logVerbose s!"restored artifact from cache to: {file}"
597+
createParentDirs file
598+
copyFile art.path file
599+
writeFileHash file art.hash
600+
return art.useLocalFile file
601+
581602
/--
582603
Some module build artifacts must be located in the build directory (e.g., ILeans).
583604
This copies the required artifacts from the local Lake cache to the build directory and
584605
updates the data structure with the new paths.
585606
-/
586-
private def Module.restoreArtifacts (mod : Module) (cached : ModuleOutputArtifacts) : JobM ModuleOutputArtifacts := do
607+
private def Module.restoreNeededArtifacts (mod : Module) (cached : ModuleOutputArtifacts) : JobM ModuleOutputArtifacts := do
608+
return {cached with
609+
ilean := ← restoreModuleArtifact mod.ileanFile cached.ilean
610+
}
611+
612+
private def Module.restoreAllArtifacts (mod : Module) (cached : ModuleOutputArtifacts) : JobM ModuleOutputArtifacts := do
587613
return {cached with
588-
ilean := ← restore mod.ileanFile cached.ilean
614+
olean := ← restoreModuleArtifact mod.oleanFile cached.olean
615+
oleanServer? := ← restoreSome mod.oleanServerFile cached.oleanServer?
616+
oleanPrivate? := ← restoreSome mod.oleanPrivateFile cached.oleanPrivate?
617+
ilean := ← restoreModuleArtifact mod.ileanFile cached.ilean
618+
ir? := ← restoreSome mod.oleanFile cached.ir?
619+
c := ← restoreModuleArtifact mod.cFile cached.c
620+
bc? := ← restoreSome mod.oleanFile cached.bc?
589621
}
590622
where
591-
restore file art := do
592-
unless (← file.pathExists) do
593-
logVerbose s!"restored artifact from cache to: {file}"
594-
copyFile art.path file
595-
writeFileHash file art.hash
596-
return art.useLocalFile file
623+
@[inline] restoreSome file art? :=
624+
art?.mapM (restoreModuleArtifact file)
625+
597626

598627
private def Module.mkArtifacts (mod : Module) (srcFile : FilePath) (isModule : Bool) : ModuleArtifacts where
599628
lean? := srcFile
@@ -605,31 +634,27 @@ private def Module.mkArtifacts (mod : Module) (srcFile : FilePath) (isModule : B
605634
c? := mod.cFile
606635
bc? := if Lean.Internal.hasLLVMBackend () then some mod.bcFile else none
607636

608-
private def Module.computeOutputHashes (mod : Module) (isModule : Bool) : FetchM ModuleOutputHashes :=
637+
private def Module.computeArtifacts (mod : Module) (isModule : Bool) : FetchM ModuleOutputArtifacts :=
609638
return {
610-
olean := ← computeFileHash mod.oleanFile
611-
oleanServer? := ← if isModule then some <$> computeFileHash mod.oleanServerFile else pure none
612-
oleanPrivate? := ← if isModule then some <$> computeFileHash mod.oleanPrivateFile else pure none
613-
ilean := ← computeFileHash mod.ileanFile
614-
ir? := ← if isModule then some <$> computeFileHash mod.irFile else pure none
615-
c := ← computeFileHash mod.cFile
616-
bc? := ← if Lean.Internal.hasLLVMBackend () then some <$> computeFileHash mod.bcFile else pure none
639+
olean := ← compute mod.oleanFile "olean"
640+
oleanServer? := ← computeIf isModule mod.oleanServerFile "olean.server"
641+
oleanPrivate? := ← computeIf isModule mod.oleanPrivateFile "olean.private"
642+
ilean := ← compute mod.ileanFile "ilean"
643+
ir? := ← computeIf isModule mod.irFile "ir"
644+
c := ← compute mod.cFile "c"
645+
bc? := ← computeIf (Lean.Internal.hasLLVMBackend ()) mod.bcFile "bc"
617646
}
647+
where
648+
@[inline] compute file ext := do
649+
computeArtifact file ext
650+
computeIf c file ext := do
651+
if c then return some (← compute file ext) else return none
618652

619-
private def Module.fetchLocalArtifacts (mod : Module) (isModule : Bool) : FetchM ModuleOutputArtifacts :=
620-
return {
621-
olean := ← fetchLocalArtifact mod.oleanFile
622-
oleanServer? := ← if isModule then some <$> fetchLocalArtifact mod.oleanServerFile else pure none
623-
oleanPrivate? := ← if isModule then some <$> fetchLocalArtifact mod.oleanPrivateFile else pure none
624-
ilean := ← fetchLocalArtifact mod.ileanFile
625-
ir? := ← if isModule then some <$> fetchLocalArtifact mod.irFile else pure none
626-
c := ← fetchLocalArtifact mod.cFile
627-
bc? := ← if Lean.Internal.hasLLVMBackend () then some <$> fetchLocalArtifact mod.bcFile else pure none
628-
}
653+
instance : ToOutputJson ModuleOutputArtifacts := ⟨(toJson ·.descrs)⟩
629654

630655
private def Module.buildLean
631656
(mod : Module) (depTrace : BuildTrace) (srcFile : FilePath) (setup : ModuleSetup)
632-
: JobM ModuleOutputHashes := buildAction depTrace mod.traceFile do
657+
: JobM ModuleOutputArtifacts := buildAction depTrace mod.traceFile do
633658
let args := mod.weakLeanArgs ++ mod.leanArgs
634659
let relSrcFile := relPathFrom mod.pkg.dir srcFile
635660
let directImports := (← (← mod.input.fetch).await).imports
@@ -639,7 +664,7 @@ private def Module.buildLean
639664
compileLeanModule srcFile relSrcFile setup mod.setupFile arts args
640665
(← getLeanPath) (← getLean)
641666
mod.clearOutputHashes
642-
mod.computeOutputHashes setup.isModule
667+
mod.computeArtifacts setup.isModule
643668

644669
private def traceOptions (opts : LeanOptions) (caption := "opts") : BuildTrace :=
645670
opts.values.foldl (init := .nil caption) fun t n v =>
@@ -671,18 +696,38 @@ private def Module.recBuildLean (mod : Module) : FetchM (Job ModuleOutputArtifac
671696
let depTrace ← getTrace
672697
let inputHash := depTrace.hash
673698
let savedTrace ← readTraceFile mod.traceFile
674-
if let some ref := mod.pkg.cacheRef? then
675-
if let some arts ← resolveArtifactsUsing? ModuleOutputHashes inputHash mod.traceFile savedTrace ref then
676-
return ← mod.restoreArtifacts arts
677-
let upToDate ← savedTrace.replayIfUpToDate (oldTrace := srcTrace.mtime) mod depTrace
678-
unless upToDate do
679-
discard <| mod.buildLean depTrace srcFile setup
680-
if let some ref := mod.pkg.cacheRef? then
681-
let arts ← mod.cacheOutputArtifacts
682-
ref.insert inputHash arts.hashes
683-
return arts
684-
else
685-
mod.fetchLocalArtifacts setup.isModule
699+
let cache ← getLakeCache
700+
let fetchArtsFromCache? restoreAll := do
701+
let arts? ← getArtifacts? inputHash mod.traceFile savedTrace cache mod.pkg
702+
if let some arts := arts? then
703+
if savedTrace.isDifferentFrom inputHash then
704+
mod.clearOutputArtifacts
705+
if restoreAll then
706+
some <$> mod.restoreAllArtifacts arts
707+
else
708+
some <$> mod.restoreNeededArtifacts arts
709+
else
710+
return none
711+
let arts ← id do
712+
if (← mod.pkg.isArtifactCacheEnabled) then
713+
if let some arts ← fetchArtsFromCache? false then
714+
return arts
715+
else
716+
unless (← savedTrace.replayIfUpToDate (oldTrace := srcTrace.mtime) mod depTrace) do
717+
discard <| mod.buildLean depTrace srcFile setup
718+
let arts ← mod.cacheOutputArtifacts
719+
cache.writeOutputs mod.pkg.cacheScope inputHash arts.descrs
720+
return arts
721+
else
722+
if (← savedTrace.replayIfUpToDate (oldTrace := srcTrace.mtime) mod depTrace) then
723+
mod.computeArtifacts setup.isModule
724+
else if let some arts ← fetchArtsFromCache? true then
725+
return arts
726+
else
727+
mod.buildLean depTrace srcFile setup
728+
if let some ref := mod.pkg.outputsRef? then
729+
ref.insert inputHash arts.descrs
730+
return arts
686731

687732
/-- The `ModuleFacetConfig` for the builtin `leanArtsFacet`. -/
688733
public def Module.leanArtsFacetConfig : ModuleFacetConfig leanArtsFacet :=

src/lake/Lake/Build/ModuleArtifacts.lean

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,42 +14,42 @@ open Lean (Json ToJson FromJson)
1414

1515
namespace Lake
1616

17-
/-- The content hashes of the output artifacts of a module build. -/
18-
public structure ModuleOutputHashes where
19-
olean : Hash := .nil
20-
oleanServer? : Option Hash := none
21-
oleanPrivate? : Option Hash := none
22-
ilean : Hash := .nil
23-
ir? : Option Hash := none
24-
c : Hash := .nil
25-
bc? : Option Hash := none
17+
/-- The descriptions of the output artifacts of a module build. -/
18+
public structure ModuleOutputDescrs where
19+
olean : ArtifactDescr
20+
oleanServer? : Option ArtifactDescr := none
21+
oleanPrivate? : Option ArtifactDescr := none
22+
ilean : ArtifactDescr
23+
ir? : Option ArtifactDescr := none
24+
c : ArtifactDescr
25+
bc? : Option ArtifactDescr := none
2626

27-
public def ModuleOutputHashes.oleanHashes (hashes : ModuleOutputHashes) : Array Hash := Id.run do
28-
let mut oleanHashes := #[hashes.olean]
29-
if let some hash := hashes.oleanServer? then
30-
oleanHashes := oleanHashes.push hash
31-
if let some hash := hashes.oleanPrivate? then
32-
oleanHashes := oleanHashes.push hash
33-
return oleanHashes
27+
public def ModuleOutputDescrs.oleanParts (self : ModuleOutputDescrs) : Array ArtifactDescr := Id.run do
28+
let mut descrs := #[self.olean]
29+
if let some hash := self.oleanServer? then
30+
descrs := descrs.push hash
31+
if let some hash := self.oleanPrivate? then
32+
descrs := descrs.push hash
33+
return descrs
3434

35-
public protected def ModuleOutputHashes.toJson (hashes : ModuleOutputHashes) : Json := Id.run do
35+
public protected def ModuleOutputDescrs.toJson (self : ModuleOutputDescrs) : Json := Id.run do
3636
let mut obj : JsonObject := {}
37-
obj := obj.insert "o" hashes.oleanHashes
38-
obj := obj.insert "i" hashes.ilean
39-
if let some ir := hashes.ir? then
37+
obj := obj.insert "o" self.oleanParts
38+
obj := obj.insert "i" self.ilean
39+
if let some ir := self.ir? then
4040
obj := obj.insert "r" ir
41-
obj := obj.insert "c" hashes.c
42-
if let some bc := hashes.bc? then
41+
obj := obj.insert "c" self.c
42+
if let some bc := self.bc? then
4343
obj := obj.insert "b" bc
4444
return obj
4545

46-
public instance : ToJson ModuleOutputHashes := ⟨ModuleOutputHashes.toJson⟩
46+
public instance : ToJson ModuleOutputDescrs := ⟨ModuleOutputDescrs.toJson⟩
4747

48-
public protected def ModuleOutputHashes.fromJson? (val : Json) : Except String ModuleOutputHashes := do
48+
public protected def ModuleOutputDescrs.fromJson? (val : Json) : Except String ModuleOutputDescrs := do
4949
let obj ← JsonObject.fromJson? val
50-
let oleanHashes : Array Hash ← obj.get "o"
50+
let oleanHashes : Array ArtifactDescr ← obj.get "o"
5151
let some olean := oleanHashes[0]?
52-
| throw "expected a least one 'o' (OLean) hash"
52+
| throw "expected a least one 'o' (.olean) hash"
5353
return {
5454
olean := olean
5555
oleanServer? := oleanHashes[1]?
@@ -60,7 +60,7 @@ public protected def ModuleOutputHashes.fromJson? (val : Json) : Except String M
6060
bc? := ← obj.get? "b"
6161
}
6262

63-
public instance : FromJson ModuleOutputHashes := ⟨ModuleOutputHashes.fromJson?⟩
63+
public instance : FromJson ModuleOutputDescrs := ⟨ModuleOutputDescrs.fromJson?⟩
6464

6565
/-- The output artifacts of a module build. -/
6666
public structure ModuleOutputArtifacts where
@@ -73,11 +73,11 @@ public structure ModuleOutputArtifacts where
7373
bc? : Option Artifact := none
7474

7575
/-- Content hashes of the artifacts. -/
76-
public def ModuleOutputArtifacts.hashes (arts : ModuleOutputArtifacts) : ModuleOutputHashes where
77-
olean := arts.olean.hash
78-
oleanServer? := arts.oleanServer?.map (·.hash)
79-
oleanPrivate? := arts.oleanPrivate?.map (·.hash)
80-
ilean := arts.ilean.hash
81-
ir? := arts.ir?.map (·.hash)
82-
c := arts.c.hash
83-
bc? := arts.bc?.map (·.hash)
76+
public def ModuleOutputArtifacts.descrs (arts : ModuleOutputArtifacts) : ModuleOutputDescrs where
77+
olean := arts.olean.descr
78+
oleanServer? := arts.oleanServer?.map (·.descr)
79+
oleanPrivate? := arts.oleanPrivate?.map (·.descr)
80+
ilean := arts.ilean.descr
81+
ir? := arts.ir?.map (·.descr)
82+
c := arts.c.descr
83+
bc? := arts.bc?.map (·.descr)

src/lake/Lake/Build/Package.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public import Lake.Config.FacetConfig
1111
public import Lake.Build.Job.Monad
1212
public import Lake.Build.Infos
1313
import Lake.Util.Git
14+
import Lake.Util.Url
1415
import Lake.Util.Proc
1516
import Lake.Build.Actions
1617
import Lake.Build.Common

src/lake/Lake/Build/Run.lean

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -222,14 +222,6 @@ public def monitorJobs
222222
didBuild := s.didBuild
223223
}
224224

225-
/-- Save input mappings to the local Lake artifact cache (if enabled). -/
226-
public def Workspace.saveInputs (ws : Workspace) : LogIO Unit := do
227-
unless ws.lakeCache.isDisabled do
228-
ws.packages.forM fun pkg => do
229-
if let some ref := pkg.cacheRef? then
230-
let inputsFile := pkg.inputsFileIn ws.lakeCache
231-
(← ref.get).save inputsFile
232-
233225
/-- Exit code to return if `--no-build` is set and a build is required. -/
234226
public def noBuildCode : ExitCode := 3
235227

@@ -260,16 +252,25 @@ public def Workspace.runFetchM
260252
let showTime := isVerbose || !useAnsi
261253
let {failures, numJobs, didBuild} ← monitorJobs #[job] ctx.registeredJobs
262254
out failLv outLv minAction showOptional useAnsi showProgress showTime
263-
-- Save input mappings to cache
264-
match (← ws.saveInputs.run {}) with
265-
| .ok _ log =>
266-
if !log.isEmpty && isVerbose then
267-
print! out "There were issues saving input mappings to the local artifact cache:\n"
268-
log.replay (logger := .stream out outLv useAnsi)
269-
| .error _ log =>
270-
print! out "Failed to save input mappings to the local artifact cache.\n"
271-
if isVerbose then
272-
log.replay (logger := .stream out outLv useAnsi)
255+
-- Save input-to-output mappings
256+
if let some outputsFile := cfg.outputsFile? then
257+
let logger := .stream out outLv useAnsi
258+
unless ws.isRootArtifactCacheEnabled do
259+
logger.logEntry <| .warning s!"{ws.root.name}: \
260+
the artifact cache is not enabled for this package, so the artifacts described \
261+
by the mappings produced by `-o` will not necessarily be available in the cache."
262+
if let some ref := ws.root.outputsRef? then
263+
match (← (← ref.get).writeFile outputsFile {}) with
264+
| .ok _ log =>
265+
if !log.isEmpty && isVerbose then
266+
print! out "There were issues saving input-to-output mappings from the build:\n"
267+
log.replay (logger := logger)
268+
| .error _ log =>
269+
print! out "Failed to save input-to-output mappings from the build.\n"
270+
if isVerbose then
271+
log.replay (logger := logger)
272+
else
273+
print! out "Workspace missing input-to-output mappings from build. (This is likely a bug in Lake.)\n"
273274
-- Report
274275
let isNoBuild := cfg.noBuild
275276
if failures.isEmpty then

0 commit comments

Comments
 (0)