mirror of https://github.com/sbt/sbt.git
Add Def.declareOutputDirectory function
This commit is contained in:
parent
c8ddbaed0e
commit
987ab5f214
|
|
@ -15,7 +15,7 @@ import sbt.util.{
|
|||
Digest,
|
||||
Monad,
|
||||
}
|
||||
import xsbti.VirtualFile
|
||||
import xsbti.{ VirtualFile, VirtualFileRef }
|
||||
|
||||
/**
|
||||
* Implementation of a macro that provides a direct syntax for applicative functors and monads. It
|
||||
|
|
@ -337,7 +337,7 @@ trait Cont:
|
|||
}.asExprOf[HashWriter[A2]]
|
||||
else summonHashWriter[A2]
|
||||
val tagsExpr = '{ List(${ Varargs(tags.map(Expr[CacheLevelTag](_))) }: _*) }
|
||||
val block = letOutput(outputs)(body)
|
||||
val block = letOutput(outputs, cacheConfigExpr)(body)
|
||||
'{
|
||||
given HashWriter[A2] = $inputHashWriter
|
||||
given JsonFormat[A1] = $aJsonFormat
|
||||
|
|
@ -353,9 +353,28 @@ trait Cont:
|
|||
})($cacheConfigExpr)
|
||||
}
|
||||
|
||||
// wrap body in between output var declarations and var references
|
||||
def toVirtualFileExpr(
|
||||
cacheConfigExpr: Expr[BuildWideCacheConfiguration]
|
||||
)(out: Output): Expr[VirtualFile] =
|
||||
if out.isFile then out.toRef.asExprOf[VirtualFile]
|
||||
else
|
||||
'{
|
||||
ActionCache.packageDirectory(
|
||||
dir = ${ out.toRef.asExprOf[VirtualFileRef] },
|
||||
conv = $cacheConfigExpr.fileConverter,
|
||||
)
|
||||
}
|
||||
|
||||
// This will generate following code for Def.declareOutput(...):
|
||||
// var $o1: VirtualFile = null
|
||||
// ActionCache.ActionResult({
|
||||
// body...
|
||||
// $o1 = out // Def.declareOutput(out)
|
||||
// result
|
||||
// }, List($o1))
|
||||
def letOutput[A1: Type](
|
||||
outputs: List[Output]
|
||||
outputs: List[Output],
|
||||
cacheConfigExpr: Expr[BuildWideCacheConfiguration],
|
||||
)(body: Expr[A1]): Expr[ActionCache.InternalActionResult[A1]] =
|
||||
Block(
|
||||
outputs.map(_.toVarDef),
|
||||
|
|
@ -363,29 +382,38 @@ trait Cont:
|
|||
ActionCache.InternalActionResult(
|
||||
value = $body,
|
||||
outputs = List(${
|
||||
Varargs[VirtualFile](outputs.map(_.toRef.asExprOf[VirtualFile]))
|
||||
Varargs[VirtualFile](outputs.map(toVirtualFileExpr(cacheConfigExpr)))
|
||||
}: _*),
|
||||
)
|
||||
}.asTerm
|
||||
).asExprOf[ActionCache.InternalActionResult[A1]]
|
||||
|
||||
val WrapOutputName = "wrapOutput_\u2603\u2603"
|
||||
val WrapOutputDirectoryName = "wrapOutputDirectory_\u2603\u2603"
|
||||
// Called when transforming the tree to add an input.
|
||||
// For `qual` of type F[A], and a `selection` qual.value.
|
||||
val record = [a] =>
|
||||
(name: String, tpe: Type[a], qual: Term, oldTree: Term) =>
|
||||
given t: Type[a] = tpe
|
||||
convert[a](name, qual) transform { (replacement: Term) =>
|
||||
if name != WrapOutputName then
|
||||
// todo cache opt-out attribute
|
||||
inputBuf += Input(TypeRepr.of[a], qual, replacement, freshName("q"))
|
||||
oldTree
|
||||
else
|
||||
val output = Output(TypeRepr.of[a], qual, freshName("o"), Symbol.spliceOwner)
|
||||
outputBuf += output
|
||||
if cacheConfigExprOpt.isDefined then output.toAssign
|
||||
else oldTree
|
||||
end if
|
||||
name match
|
||||
case WrapOutputName | WrapOutputDirectoryName =>
|
||||
val output = Output(
|
||||
tpe = TypeRepr.of[a],
|
||||
term = qual,
|
||||
name = freshName("o"),
|
||||
parent = Symbol.spliceOwner,
|
||||
outputType = name match
|
||||
case WrapOutputName => OutputType.File
|
||||
case WrapOutputDirectoryName => OutputType.Directory,
|
||||
)
|
||||
outputBuf += output
|
||||
if cacheConfigExprOpt.isDefined then output.toAssign
|
||||
else oldTree
|
||||
case _ =>
|
||||
// todo cache opt-out attribute
|
||||
inputBuf += Input(TypeRepr.of[a], qual, replacement, freshName("q"))
|
||||
oldTree
|
||||
}
|
||||
val exprWithConfig =
|
||||
cacheConfigExprOpt.map(config => '{ $config; $expr }).getOrElse(expr)
|
||||
|
|
|
|||
|
|
@ -101,6 +101,10 @@ trait ContextUtil[C <: Quotes & scala.Singleton](val valStart: Int):
|
|||
case Apply(_, List(arg)) => extractTags(arg)
|
||||
case _ => extractTags0(tree)
|
||||
|
||||
enum OutputType:
|
||||
case File
|
||||
case Directory
|
||||
|
||||
/**
|
||||
* Represents an output expression via Def.declareOutput
|
||||
*/
|
||||
|
|
@ -109,9 +113,10 @@ trait ContextUtil[C <: Quotes & scala.Singleton](val valStart: Int):
|
|||
val term: Term,
|
||||
val name: String,
|
||||
val parent: Symbol,
|
||||
val outputType: OutputType,
|
||||
):
|
||||
override def toString: String =
|
||||
s"Output($tpe, $term, $name)"
|
||||
s"Output($tpe, $term, $name, $outputType)"
|
||||
val placeholder: Symbol =
|
||||
tpe.asType match
|
||||
case '[a] =>
|
||||
|
|
@ -126,6 +131,7 @@ trait ContextUtil[C <: Quotes & scala.Singleton](val valStart: Int):
|
|||
ValDef(placeholder, rhs = Some('{ null }.asTerm))
|
||||
def toAssign: Term = Assign(toRef, term)
|
||||
def toRef: Ref = Ref(placeholder)
|
||||
def isFile: Boolean = outputType == OutputType.File
|
||||
end Output
|
||||
|
||||
def applyTuple(tupleTerm: Term, tpe: TypeRepr, idx: Int): Term =
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
package sbt
|
||||
|
||||
import java.net.URI
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.annotation.targetName
|
||||
import sbt.KeyRanks.{ DTask, Invisible }
|
||||
|
|
@ -20,7 +19,7 @@ import sbt.internal.util.{ Terminal => ITerminal, * }
|
|||
import sbt.util.{ ActionCacheStore, AggregateActionCacheStore, BuildWideCacheConfiguration, cacheLevel , DiskActionCacheStore }
|
||||
import Util._
|
||||
import sbt.util.Show
|
||||
import xsbti.{ HashedVirtualFileRef, VirtualFile }
|
||||
import xsbti.{ HashedVirtualFileRef, VirtualFile, VirtualFileRef }
|
||||
import sjsonnew.JsonFormat
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
|
|
@ -330,6 +329,8 @@ object Def extends Init[Scope] with TaskMacroExtra with InitializeImplicits:
|
|||
inline def declareOutput(inline vf: VirtualFile): Unit =
|
||||
InputWrapper.`wrapOutput_\u2603\u2603`[VirtualFile](vf)
|
||||
|
||||
inline def declareOutputDirectory(inline vf: VirtualFileRef): Unit =
|
||||
InputWrapper.`wrapOutputDirectory_\u2603\u2603`[VirtualFileRef](vf)
|
||||
|
||||
// The following conversions enable the types Initialize[T], Initialize[Task[T]], and Task[T] to
|
||||
// be used in task and setting macros as inputs with an ultimate result of type T
|
||||
|
|
|
|||
|
|
@ -74,12 +74,13 @@ class FullConvert[C <: Quotes & scala.Singleton](override val qctx: C, valStart:
|
|||
|
||||
override def convert[A: Type](nme: String, in: Term): Converted =
|
||||
nme match
|
||||
case InputWrapper.WrapInitTaskName => Converted.success(in)
|
||||
case InputWrapper.WrapPreviousName => Converted.success(in)
|
||||
case InputWrapper.WrapInitName => wrapInit[A](in)
|
||||
case InputWrapper.WrapTaskName => wrapTask[A](in)
|
||||
case InputWrapper.WrapOutputName => Converted.success(in)
|
||||
case _ => Converted.NotApplicable()
|
||||
case InputWrapper.WrapInitTaskName => Converted.success(in)
|
||||
case InputWrapper.WrapPreviousName => Converted.success(in)
|
||||
case InputWrapper.WrapInitName => wrapInit[A](in)
|
||||
case InputWrapper.WrapTaskName => wrapTask[A](in)
|
||||
case InputWrapper.WrapOutputName => Converted.success(in)
|
||||
case InputWrapper.WrapOutputDirectoryName => Converted.success(in)
|
||||
case _ => Converted.NotApplicable()
|
||||
|
||||
private def wrapInit[A: Type](tree: Term): Converted =
|
||||
val expr = tree.asExprOf[Initialize[A]]
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ object InputWrapper:
|
|||
private[std] final val WrapTaskName = "wrapTask_\u2603\u2603"
|
||||
private[std] final val WrapInitName = "wrapInit_\u2603\u2603"
|
||||
private[std] final val WrapOutputName = "wrapOutput_\u2603\u2603"
|
||||
private[std] final val WrapOutputDirectoryName = "wrapOutputDirectory_\u2603\u2603"
|
||||
private[std] final val WrapInitTaskName = "wrapInitTask_\u2603\u2603"
|
||||
private[std] final val WrapInitInputName = "wrapInitInputTask_\u2603\u2603"
|
||||
private[std] final val WrapInputName = "wrapInputTask_\u2603\u2603"
|
||||
|
|
@ -42,6 +43,11 @@ object InputWrapper:
|
|||
)
|
||||
def `wrapOutput_\u2603\u2603`[A](@deprecated("unused", "") in: Any): A = implDetailError
|
||||
|
||||
@compileTimeOnly(
|
||||
"`declareOutputDirectory` can only be used within a task macro, such as Def.cachedTask."
|
||||
)
|
||||
def `wrapOutputDirectory_\u2603\u2603`[A](@deprecated("unused", "") in: Any): A = implDetailError
|
||||
|
||||
@compileTimeOnly(
|
||||
"`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task."
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package sbt.util
|
||||
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
import sbt.internal.util.{ ActionCacheEvent, CacheEventLog, StringVirtualFile1 }
|
||||
import sbt.io.IO
|
||||
import scala.reflect.ClassTag
|
||||
|
|
@ -7,7 +9,7 @@ import scala.annotation.{ meta, StaticAnnotation }
|
|||
import sjsonnew.{ HashWriter, JsonFormat }
|
||||
import sjsonnew.support.murmurhash.Hasher
|
||||
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter, Parser }
|
||||
import xsbti.{ FileConverter, VirtualFile }
|
||||
import xsbti.{ FileConverter, VirtualFile, VirtualFileRef }
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Path
|
||||
import scala.quoted.{ Expr, FromExpr, ToExpr, Quotes }
|
||||
|
|
@ -91,6 +93,16 @@ object ActionCache:
|
|||
else valueFromStr(IO.read(paths.head.toFile()), result.origin)
|
||||
case Left(_) => organicTask
|
||||
|
||||
def packageDirectory(dir: VirtualFileRef, conv: FileConverter): VirtualFile =
|
||||
import sbt.io.syntax.*
|
||||
val dirPath = conv.toPath(dir)
|
||||
val dirFile = dirPath.toFile()
|
||||
val zipPath = Paths.get(dirPath.toString + ".dirzip")
|
||||
val rebase: File => Seq[(File, String)] =
|
||||
f => if f != dirFile then (f -> dirPath.relativize(f.toPath).toString) :: Nil else Nil
|
||||
IO.zip(dirFile.allPaths.get().flatMap(rebase), zipPath.toFile(), None)
|
||||
conv.toVirtualFile(zipPath)
|
||||
|
||||
/**
|
||||
* Represents a value and output files, used internally by the macro.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue