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