diff --git a/core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala index 2a4c6e876..4194d1866 100644 --- a/core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala +++ b/core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala @@ -356,16 +356,18 @@ trait Cont: // wrap body in between output var declarations and var references def letOutput[A1: Type]( outputs: List[Output] - )(body: Expr[A1]): Expr[(A1, Seq[VirtualFile])] = + )(body: Expr[A1]): Expr[ActionCache.InternalActionResult[A1]] = Block( outputs.map(_.toVarDef), '{ - ( - $body, - List(${ Varargs[VirtualFile](outputs.map(_.toRef.asExprOf[VirtualFile])) }: _*) + ActionCache.InternalActionResult( + value = $body, + outputs = List(${ + Varargs[VirtualFile](outputs.map(_.toRef.asExprOf[VirtualFile])) + }: _*), ) }.asTerm - ).asExprOf[(A1, Seq[VirtualFile])] + ).asExprOf[ActionCache.InternalActionResult[A1]] val WrapOutputName = "wrapOutput_\u2603\u2603" // Called when transforming the tree to add an input. diff --git a/util-cache/src/main/scala/sbt/util/ActionCache.scala b/util-cache/src/main/scala/sbt/util/ActionCache.scala index 663dad341..62b1c8a70 100644 --- a/util-cache/src/main/scala/sbt/util/ActionCache.scala +++ b/util-cache/src/main/scala/sbt/util/ActionCache.scala @@ -33,7 +33,7 @@ object ActionCache: extraHash: Digest, tags: List[CacheLevelTag], )( - action: I => (O, Seq[VirtualFile]) + action: I => InternalActionResult[O], )( config: BuildWideCacheConfiguration ): O = @@ -44,8 +44,8 @@ object ActionCache: def organicTask: O = // run action(...) and combine the newResult with outputs - val (result, outputs) = - try action(key) + val InternalActionResult(result, outputs) = + try action(key): @unchecked catch case e: Exception => cacheEventLog.append(ActionCacheEvent.Error) @@ -90,6 +90,21 @@ object ActionCache: if paths.isEmpty then organicTask else valueFromStr(IO.read(paths.head.toFile()), result.origin) case Left(_) => organicTask + + /** + * Represents a value and output files, used internally by the macro. + */ + class InternalActionResult[A1] private ( + val value: A1, + val outputs: Seq[VirtualFile], + ) + end InternalActionResult + object InternalActionResult: + def apply[A1](value: A1, outputs: Seq[VirtualFile]): InternalActionResult[A1] = + new InternalActionResult(value, outputs) + private[sbt] def unapply[A1](r: InternalActionResult[A1]): Option[(A1, Seq[VirtualFile])] = + Some(r.value, r.outputs) + end InternalActionResult end ActionCache class BuildWideCacheConfiguration( diff --git a/util-cache/src/test/scala/sbt/util/ActionCacheTest.scala b/util-cache/src/test/scala/sbt/util/ActionCacheTest.scala index 9d895a795..ea676cead 100644 --- a/util-cache/src/test/scala/sbt/util/ActionCacheTest.scala +++ b/util-cache/src/test/scala/sbt/util/ActionCacheTest.scala @@ -11,6 +11,7 @@ import xsbti.VirtualFileRef import java.nio.file.Path import java.nio.file.Paths import java.nio.file.Files +import ActionCache.InternalActionResult object ActionCacheTest extends BasicTestSuite: val tags = CacheLevelTag.all.toList @@ -35,9 +36,9 @@ object ActionCacheTest extends BasicTestSuite: def testActionCacheBasic(cache: ActionCacheStore): Unit = import sjsonnew.BasicJsonProtocol.* var called = 0 - val action: ((Int, Int)) => (Int, Seq[VirtualFile]) = { case (a, b) => + val action: ((Int, Int)) => InternalActionResult[Int] = { case (a, b) => called += 1 - (a + b, Nil) + InternalActionResult(a + b, Nil) } IO.withTemporaryDirectory: (tempDir) => val config = getCacheConfig(cache, tempDir) @@ -57,10 +58,10 @@ object ActionCacheTest extends BasicTestSuite: import sjsonnew.BasicJsonProtocol.* IO.withTemporaryDirectory: (tempDir) => var called = 0 - val action: ((Int, Int)) => (Int, Seq[VirtualFile]) = { case (a, b) => + val action: ((Int, Int)) => InternalActionResult[Int] = { case (a, b) => called += 1 val out = StringVirtualFile1(s"$tempDir/a.txt", (a + b).toString) - (a + b, Seq(out)) + InternalActionResult(a + b, Seq(out)) } val config = getCacheConfig(cache, tempDir) val v1 =