Use ClassTag instead of Manifest

This commit is contained in:
Eugene Yokota 2022-10-17 22:53:07 -04:00
parent ecd59bd3e1
commit fbc98ed72e
10 changed files with 141 additions and 107 deletions

View File

@ -16,7 +16,7 @@ import sbt.ConcurrentRestrictions.Tag
import sbt.Def.{ Initialize, ScopedKey, Setting, setting }
import std.TaskMacro
import std.TaskExtra.{ task => mktask, _ }
import scala.reflect.ManifestFactory
import scala.reflect.{ ClassTag, ManifestFactory }
/** An abstraction on top of Settings for build configuration and task definition. */
sealed trait Scoped extends Equals:
@ -261,6 +261,11 @@ object Scoped:
implicit def inputScopedToKey[T](s: InputKey[T]): ScopedKey[InputTask[T]] =
ScopedKey(s.scope, s.key)
private[sbt] def coerceTag[A1: ClassTag]: Manifest[A1] =
summon[ClassTag[A1]] match
case mf: Manifest[A1] => mf
case tag => ManifestFactory.classType[A1](tag.runtimeClass)
/**
* Mixin trait for adding convenience vocabulary associated with specifying the [[Scope]] of a setting.
* Allows specification of the Scope or part of the [[Scope]] of a setting being referenced.
@ -593,6 +598,12 @@ object Scoped:
// format: off
type ST[X] = Taskable[X]
final class RichTaskable1[A1](t1: ST[A1]) extends RichTaskables[[F[_]] =>> F[A1]](t1)(using AList.single[A1]):
type Fun[M[_], Ret] = M[A1] => Ret
def identityMap = mapN(identity)
protected def convert[M[_], R](f: M[A1] => R) = f
end RichTaskable1
final class RichTaskable2[A, B](t2: (ST[A], ST[B])) extends RichTaskables[AList.Tuple2K[A, B]](t2)(using AList.tuple2[A, B]) {
type Fun[M[_], Ret] = (M[A], M[B]) => Ret
def identityMap = mapN(mkTuple2)
@ -736,6 +747,7 @@ trait TupleSyntax:
// this is the least painful arrangement I came up with
type ST[T] = Taskable[T]
implicit def taskableToTable1[A1](t1: ST[A1]): RichTaskable1[A1] = new RichTaskable1(t1)
implicit def t2ToTable2[A, B](t2: (ST[A], ST[B])): RichTaskable2[A, B] = new RichTaskable2(t2)
implicit def t3ToTable3[A, B, C](t3: (ST[A], ST[B], ST[C])): RichTaskable3[A, B, C] = new RichTaskable3(t3)
implicit def t4ToTable4[A, B, C, D](t4: (ST[A], ST[B], ST[C], ST[D])): RichTaskable4[A, B, C, D] = new RichTaskable4(t4)
@ -763,35 +775,36 @@ end TupleSyntax
object TupleSyntax extends TupleSyntax
import Scoped.extendScoped
import Scoped.{ coerceTag, extendScoped }
/** Constructs InputKeys, which are associated with input tasks to define a setting. */
object InputKey:
def apply[A1: Manifest](
def apply[A1: ClassTag](
label: String,
description: String = "",
rank: Int = KeyRanks.DefaultInputRank
): InputKey[A1] =
given mf: Manifest[InputTask[A1]] =
ManifestFactory.classType[InputTask[A1]](classOf[InputTask[A1]], manifest[A1])
given mf: ClassTag[InputTask[A1]] =
ManifestFactory.classType[InputTask[A1]](classOf[InputTask[A1]], coerceTag[A1])
apply(AttributeKey[InputTask[A1]](label, description, rank))
def apply[A1: Manifest](
def apply[A1: ClassTag](
label: String,
description: String,
extend1: Scoped,
extendN: Scoped*
): InputKey[A1] = apply(label, description, KeyRanks.DefaultInputRank, extend1, extendN: _*)
def apply[A1: Manifest](
def apply[A1: ClassTag](
label: String,
description: String,
rank: Int,
extend1: Scoped,
extendN: Scoped*
): InputKey[A1] =
given mf: Manifest[InputTask[A1]] =
ManifestFactory.classType[InputTask[A1]](classOf[InputTask[A1]], manifest[A1])
given mf: ClassTag[InputTask[A1]] =
ManifestFactory.classType[InputTask[A1]](classOf[InputTask[A1]], coerceTag[A1])
apply(AttributeKey[InputTask[A1]](label, description, extendScoped(extend1, extendN), rank))
def apply[A1](akey: AttributeKey[InputTask[A1]]): InputKey[A1] =
@ -801,49 +814,63 @@ end InputKey
/** Constructs TaskKeys, which are associated with tasks to define a setting. */
object TaskKey:
def apply[A1: Manifest](
def apply[A1: ClassTag](label: String): TaskKey[A1] =
apply[A1](
label = label,
description = "",
rank = Int.MaxValue,
)
def apply[A1: ClassTag](label: String, description: String): TaskKey[A1] =
apply[A1](
label = label,
description = description,
rank = Int.MaxValue,
)
def apply[A1: ClassTag](
label: String,
description: String = "",
rank: Int = KeyRanks.DefaultTaskRank
description: String,
rank: Int,
): TaskKey[A1] =
given mf: Manifest[Task[A1]] =
ManifestFactory.classType[Task[A1]](classOf[Task[A1]], manifest[A1])
given mf: ClassTag[Task[A1]] =
ManifestFactory.classType[Task[A1]](classOf[Task[A1]], coerceTag[A1])
apply(AttributeKey[Task[A1]](label, description, rank))
def apply[A1: Manifest](
def apply[A1: ClassTag](
label: String,
description: String,
extend1: Scoped,
extendN: Scoped*
): TaskKey[A1] =
given mf: Manifest[Task[A1]] =
ManifestFactory.classType[Task[A1]](classOf[Task[A1]], manifest[A1])
given mf: ClassTag[Task[A1]] =
ManifestFactory.classType[Task[A1]](classOf[Task[A1]], coerceTag[A1])
apply(AttributeKey[Task[A1]](label, description, extendScoped(extend1, extendN)))
def apply[A1: Manifest](
def apply[A1: ClassTag](
label: String,
description: String,
rank: Int,
extend1: Scoped,
extendN: Scoped*
): TaskKey[A1] =
given mf: Manifest[Task[A1]] =
ManifestFactory.classType[Task[A1]](classOf[Task[A1]], manifest[A1])
given mf: ClassTag[Task[A1]] =
ManifestFactory.classType[Task[A1]](classOf[Task[A1]], coerceTag[A1])
apply(AttributeKey[Task[A1]](label, description, extendScoped(extend1, extendN), rank))
def apply[A1](akey: AttributeKey[Task[A1]]): TaskKey[A1] =
Scoped.scopedTask(Scope.ThisScope, akey)
def local[A1: Manifest]: TaskKey[A1] =
given mf: Manifest[Task[A1]] =
ManifestFactory.classType[Task[A1]](classOf[Task[A1]], manifest[A1])
def local[A1: ClassTag]: TaskKey[A1] =
given mf: ClassTag[Task[A1]] =
ManifestFactory.classType[Task[A1]](classOf[Task[A1]], coerceTag[A1])
apply[A1](AttributeKey.local[Task[A1]])
end TaskKey
/** Constructs SettingKeys, which are associated with a value to define a basic setting. */
object SettingKey:
def apply[A1: Manifest: OptJsonWriter](
def apply[A1: ClassTag: OptJsonWriter](
label: String,
): SettingKey[A1] =
apply[A1](
@ -852,7 +879,7 @@ object SettingKey:
rank = KeyRanks.DefaultSettingRank
)
def apply[A1: Manifest: OptJsonWriter](
def apply[A1: ClassTag: OptJsonWriter](
label: String,
description: String,
): SettingKey[A1] =
@ -862,14 +889,14 @@ object SettingKey:
rank = KeyRanks.DefaultSettingRank,
)
def apply[A1: Manifest: OptJsonWriter](
def apply[A1: ClassTag: OptJsonWriter](
label: String,
description: String,
rank: Int
): SettingKey[A1] =
apply(AttributeKey[A1](label, description, rank))
def apply[A1: Manifest: OptJsonWriter](
def apply[A1: ClassTag: OptJsonWriter](
label: String,
description: String,
extend1: Scoped,
@ -877,7 +904,7 @@ object SettingKey:
): SettingKey[A1] =
apply(AttributeKey[A1](label, description, extendScoped(extend1, extendN)))
def apply[A1: Manifest: OptJsonWriter](
def apply[A1: ClassTag: OptJsonWriter](
label: String,
description: String,
rank: Int,
@ -889,7 +916,8 @@ object SettingKey:
def apply[A1](akey: AttributeKey[A1]): SettingKey[A1] =
Scoped.scopedSetting(Scope.ThisScope, akey)
def local[A1: Manifest: OptJsonWriter]: SettingKey[A1] = apply[A1](AttributeKey.local[A1])
def local[A1: ClassTag: OptJsonWriter]: SettingKey[A1] =
apply[A1](AttributeKey.local[A1])
end SettingKey

View File

@ -11,6 +11,7 @@ package std
import java.io.File
import scala.annotation.tailrec
import scala.quoted.*
import scala.reflect.ClassTag
import sbt.util.OptJsonWriter
@ -42,21 +43,21 @@ private[sbt] object KeyMacro:
}
private def keyImpl[A1: Type, A2: Type](methodName: String)(
f: (String, Expr[Manifest[A1]]) => Expr[A2]
f: (String, Expr[ClassTag[A1]]) => Expr[A2]
)(using qctx: Quotes): Expr[A2] =
val tpe = summon[Type[A1]]
f(
definingValName(errorMsg(methodName)),
Expr.summon[Manifest[A1]].getOrElse(sys.error("Manifest[A] not found for $tpe"))
Expr.summon[ClassTag[A1]].getOrElse(sys.error("ClassTag[A] not found for $tpe"))
)
private def keyImpl2[A1: Type, A2: Type](methodName: String)(
f: (String, Expr[Manifest[A1]], Expr[OptJsonWriter[A1]]) => Expr[A2]
f: (String, Expr[ClassTag[A1]], Expr[OptJsonWriter[A1]]) => Expr[A2]
)(using qctx: Quotes): Expr[A2] =
val tpe = summon[Type[A1]]
f(
definingValName(errorMsg(methodName)),
Expr.summon[Manifest[A1]].getOrElse(sys.error("Manifest[A] not found for $tpe")),
Expr.summon[ClassTag[A1]].getOrElse(sys.error("ClassTag[A] not found for $tpe")),
Expr.summon[OptJsonWriter[A1]].getOrElse(sys.error("OptJsonWriter[A] not found for $tpe")),
)

View File

@ -39,6 +39,7 @@ import xsbti.compile.CompilerCache
import scala.annotation.{ nowarn, tailrec }
import scala.concurrent.ExecutionContext
import scala.concurrent.duration.Duration
import scala.reflect.ClassTag
import scala.util.control.NonFatal
/** This class is the entry point for sbt. */
@ -517,11 +518,14 @@ object BuiltinCommands {
def sortByRank(keys: Seq[AttributeKey[_]]): Seq[AttributeKey[_]] = keys.sortBy(_.rank)
def withDescription(keys: Seq[AttributeKey[_]]): Seq[AttributeKey[_]] =
keys.filter(_.description.isDefined)
def isTask(
mf: Manifest[_]
)(implicit taskMF: Manifest[Task[_]], inputMF: Manifest[InputTask[_]]): Boolean =
mf: ClassTag[_]
)(using taskMF: ClassTag[Task[_]], inputMF: ClassTag[InputTask[_]]): Boolean =
mf.runtimeClass == taskMF.runtimeClass || mf.runtimeClass == inputMF.runtimeClass
def topNRanked(n: Int) = (keys: Seq[AttributeKey[_]]) => sortByRank(keys).take(n)
def highPass(rankCutoff: Int) =
(keys: Seq[AttributeKey[_]]) => sortByRank(keys).takeWhile(_.rank <= rankCutoff)

View File

@ -22,11 +22,10 @@ final case class ScopedKeyData[A](scoped: ScopedKey[A], value: Any) {
"Setting: %s = %s" format (key.manifest.toString, value.toString)
)
def fold[T](targ: OptManifest[_] => T, itarg: OptManifest[_] => T, s: => T): T =
key.manifest.runtimeClass match {
key.manifest.runtimeClass match
case TaskClass => targ(key.manifest.typeArguments.head)
case InputTaskClass => itarg(key.manifest.typeArguments.head)
case _ => s
}
def fmtMf(s: String): OptManifest[_] => String = s format _
private val TaskClass = classOf[Task[_]]

View File

@ -19,6 +19,7 @@ import Types.idFun
import complete._
import DefaultParsers._
import scala.annotation.nowarn
import scala.reflect.ClassTag
/**
* The resulting `session` and verbose and quiet summaries of the result of a set operation.
@ -352,32 +353,33 @@ private[sbt] object SettingCompletions {
def configScalaID(c: String): String = Util.quoteIfKeyword(c.capitalize)
/** Applies a function on the underlying manifest for T for `key` depending if it is for a `Setting[T]`, `Task[T]`, or `InputTask[T]`. */
@nowarn
def keyType[S](key: AttributeKey[_])(
onSetting: Manifest[_] => S,
onTask: Manifest[_] => S,
onInput: Manifest[_] => S
)(implicit tm: Manifest[Task[_]], im: Manifest[InputTask[_]]): S = {
def argTpe = key.manifest.typeArguments.head
onSetting: ClassTag[_] => S,
onTask: ClassTag[_] => S,
onInput: ClassTag[_] => S
)(using tm: ClassTag[Task[_]], im: ClassTag[InputTask[_]]): S =
def argTpe = key.manifest.typeArguments.head match
case m: Manifest[_] => m
case _ => sys.error(s"Manifest not found for ${key} typeArgument")
val TaskClass = tm.runtimeClass
val InputTaskClass = im.runtimeClass
key.manifest.runtimeClass match {
key.manifest.runtimeClass match
case TaskClass => onTask(argTpe)
case InputTaskClass => onInput(argTpe)
case _ => onSetting(key.manifest)
}
}
/** For a Task[T], InputTask[T], or Setting[T], this returns the manifest for T. */
def keyUnderlyingType(key: AttributeKey[_]): Manifest[_] = keyType(key)(idFun, idFun, idFun)
def keyUnderlyingType(key: AttributeKey[_]): ClassTag[_] =
keyType(key)(idFun, idFun, idFun)
/**
* Returns a string representation of the underlying type T for a `key` representing a `Setting[T]`, `Task[T]`, or `InputTask[T]`.
* This string representation is currently a cleaned up toString of the underlying Manifest.
* This string representation is currently a cleaned up toString of the underlying ClassTag.
*/
def keyTypeString[T](key: AttributeKey[_]): String = {
val mfToString = (mf: Manifest[_]) => complete.TypeString.cleanup(mf.toString)
keyType(key)(mfToString, mfToString, mfToString)
}
def keyTypeString[T](key: AttributeKey[_]): String =
val tagToString = (tag: ClassTag[_]) => complete.TypeString.cleanup(tag.toString)
keyType(key)(tagToString, tagToString, tagToString)
/** True if the `key` represents a setting or task that may be appended using an assignment method such as `+=`. */
def appendable(key: AttributeKey[_]): Boolean = {

View File

@ -93,15 +93,15 @@ private[sbt] object Settings {
addTaskDefinition(Def.setting[Task[Seq[Path]]](key, init, setting.pos)) ::
outputsAndStamps(taskKey)
}
ak.manifest.typeArguments match {
case t :: Nil if seqClass.isAssignableFrom(t.runtimeClass) =>
ak.manifest.typeArguments match
case (t: Manifest[_]) :: Nil if seqClass.isAssignableFrom(t.runtimeClass) =>
t.typeArguments match {
case p :: Nil if pathClass.isAssignableFrom(p.runtimeClass) => mkSetting[Seq[Path]]
case _ => default
}
case t :: Nil if pathClass.isAssignableFrom(t.runtimeClass) => mkSetting[Path]
case _ => default
}
case (t: Manifest[_]) :: Nil if pathClass.isAssignableFrom(t.runtimeClass) =>
mkSetting[Path]
case _ => default
case _ => Nil
}
}

View File

@ -82,7 +82,7 @@ object Dependencies {
// val lmCoursierShaded = "io.get-coursier" %% "lm-coursier-shaded" % "2.0.10"
val lmCoursierShaded = "org.scala-sbt" %% "librarymanagement-coursier" % "2.0.0-alpha2"
lazy val sjsonNewVersion = "0.10.0"
lazy val sjsonNewVersion = "0.11.0"
def sjsonNew(n: String) = Def.setting(
"com.eed3si9n" %% n % sjsonNewVersion
) // contrabandSjsonNewVersion.value

View File

@ -1,19 +1,21 @@
import sbt._, Keys._
import Def.Initialize
import sbt.TupleSyntax.*
object Marker extends AutoPlugin {
object Marker extends AutoPlugin:
override def trigger = allRequirements
override def requires = sbt.plugins.JvmPlugin
object autoImport {
final lazy val Mark = TaskKey[Unit]("mark")
final def mark: Initialize[Task[Unit]] = mark(baseDirectory)
final def mark(project: Reference): Initialize[Task[Unit]] = mark(baseDirectory in project)
final def mark(baseKey: SettingKey[File]): Initialize[Task[Unit]] = baseKey map { base =>
val toMark = base / "ran"
if(toMark.exists)
sys.error("Already ran (" + toMark + " exists)")
else
IO touch toMark
final def mark(project: Reference): Initialize[Task[Unit]] = mark(project / baseDirectory)
final def mark(baseKey: SettingKey[File]): Initialize[Task[Unit]] = baseKey.toTaskable mapN {
base =>
val toMark = base / "ran"
if (toMark.exists)
sys.error("Already ran (" + toMark + " exists)")
else
IO touch toMark
}
}
}
end Marker

View File

@ -11,13 +11,13 @@ $ exists ran
$ delete ran
# single project, aggregate = true on Mark
> set aggregate in Mark := true
> set Mark / aggregate := true
> mark
$ exists ran
$ delete ran
# single project, aggregate = false on Mark
> set aggregate in Mark := false
> set Mark / aggregate := false
> mark
$ exists ran
$ delete ran
@ -58,8 +58,8 @@ $ touch aggregate
> reload
# add tasks to each subproject
> set Mark in sub := mark(sub).value
> set Mark in sub2 := mark(sub2).value
> set sub / Mark := mark(sub).value
> set sub2 / Mark := mark(sub2).value
# check that aggregation works when root project has no task
> mark
@ -77,15 +77,15 @@ $ delete sub/ran sub/sub/ran
> set Mark := mark.value
# disable aggregation for sub/mark so that sub2/mark doesn't run
> set aggregate in (sub,Mark) := false
> set sub / Mark / aggregate := false
> mark
$ exists ran sub/ran
$ absent sub/sub/ran
$ delete ran sub/ran
# the aggregation setting in a leaf shouldn't affect whether it can be run directly
> set aggregate in (sub2, Mark) := false
> set sub2 / Mark / aggregate := false
> sub2/mark
$ exists sub/sub/ran
$ absent ran sub/ran
$ delete sub/sub/ran
$ delete sub/sub/ran

View File

@ -22,7 +22,7 @@ import sbt.util.OptJsonWriter
sealed trait AttributeKey[A]:
/** The runtime evidence for `A`. */
def manifest: Manifest[A]
def manifest: ClassTag[A]
// def classTag: ClassTag[A]
/** The label is the identifier for the key and is camelCase by convention. */
@ -52,7 +52,7 @@ sealed trait AttributeKey[A]:
end AttributeKey
private[sbt] abstract class SharedAttributeKey[T] extends AttributeKey[T] {
private[sbt] abstract class SharedAttributeKey[A] extends AttributeKey[A]:
override final def toString = label
override final def hashCode = label.hashCode
override final def equals(o: Any) =
@ -61,74 +61,72 @@ private[sbt] abstract class SharedAttributeKey[T] extends AttributeKey[T] {
case _ => false
})
final def isLocal: Boolean = false
}
end SharedAttributeKey
object AttributeKey {
def apply[T: Manifest: OptJsonWriter](name: String): AttributeKey[T] =
def apply[A: ClassTag: OptJsonWriter](name: String): AttributeKey[A] =
make(name, None, Nil, Int.MaxValue)
def apply[T: Manifest: OptJsonWriter](name: String, rank: Int): AttributeKey[T] =
def apply[A: ClassTag: OptJsonWriter](name: String, rank: Int): AttributeKey[A] =
make(name, None, Nil, rank)
def apply[T: Manifest: OptJsonWriter](name: String, description: String): AttributeKey[T] =
def apply[A: ClassTag: OptJsonWriter](name: String, description: String): AttributeKey[A] =
apply(name, description, Nil)
def apply[T: Manifest: OptJsonWriter](
def apply[A: ClassTag: OptJsonWriter](
name: String,
description: String,
rank: Int
): AttributeKey[T] =
): AttributeKey[A] =
apply(name, description, Nil, rank)
def apply[T: Manifest: OptJsonWriter](
def apply[A: ClassTag: OptJsonWriter](
name: String,
description: String,
extend: Seq[AttributeKey[_]]
): AttributeKey[T] =
): AttributeKey[A] =
apply(name, description, extend, Int.MaxValue)
def apply[T: Manifest: OptJsonWriter](
def apply[A: ClassTag: OptJsonWriter](
name: String,
description: String,
extend: Seq[AttributeKey[_]],
rank: Int
): AttributeKey[T] =
): AttributeKey[A] =
make(name, Some(description), extend, rank)
private[sbt] def copyWithRank[T](a: AttributeKey[T], rank: Int): AttributeKey[T] =
make(a.label, a.description, a.extend, rank)(a.manifest, a.optJsonWriter)
private[sbt] def copyWithRank[A](a: AttributeKey[A], rank: Int): AttributeKey[A] =
make(a.label, a.description, a.extend, rank)(using a.manifest, a.optJsonWriter)
private[this] def make[T](
private[this] def make[A](
name: String,
description0: Option[String],
extend0: Seq[AttributeKey[_]],
rank0: Int
)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] =
new SharedAttributeKey[T] {
)(using mf: ClassTag[A], ojw: OptJsonWriter[A]): AttributeKey[A] =
new SharedAttributeKey[A]:
require(
name.headOption.exists(_.isLower),
s"A named attribute key must start with a lowercase letter: $name"
)
def manifest = mf
val label = Util.hyphenToCamel(name)
def description = description0
def extend = extend0
def rank = rank0
def optJsonWriter = ojw
}
override def manifest: ClassTag[A] = mf
override val label: String = Util.hyphenToCamel(name)
override def description: Option[String] = description0
override def extend: Seq[AttributeKey[_]] = extend0
override def rank: Int = rank0
override def optJsonWriter: OptJsonWriter[A] = ojw
private[sbt] def local[T](implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] =
new AttributeKey[T] {
def manifest = mf
def label = LocalLabel
def description = None
def extend = Nil
private[sbt] def local[A](using ct: ClassTag[A], ojw: OptJsonWriter[A]): AttributeKey[A] =
new AttributeKey[A]:
override def manifest: ClassTag[A] = ct
override def label: String = LocalLabel
override def description: Option[String] = None
override def extend: Seq[AttributeKey[_]] = Nil
override def toString = label
def isLocal: Boolean = true
def rank = Int.MaxValue
val optJsonWriter = ojw
}
override def isLocal: Boolean = true
override def rank: Int = Int.MaxValue
override val optJsonWriter: OptJsonWriter[A] = ojw
private[sbt] final val LocalLabel = "$" + "local"