Merge pull request #7490 from adpi2/sbt2-remove-manifest

[sbt 2] Remove usage of Manifest in Structure.scala
This commit is contained in:
adpi2 2024-02-12 16:34:41 +01:00 committed by GitHub
commit a607e54188
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 87 additions and 133 deletions

View File

@ -388,18 +388,15 @@ object Index {
)(label: AttributeKey[_] => String): Map[String, AttributeKey[_]] = {
val multiMap = settings.groupBy(label)
val duplicates = multiMap.iterator
.collect { case (k, xs) if xs.size > 1 => (k, xs.map(_.manifest)) }
.collect {
case (k, xs) if xs.size > 1 => (k, xs)
}
.collect { case (k, xs) if xs.size > 1 => (k, xs.map(_.tag)) }
.collect { case (k, xs) if xs.size > 1 => (k, xs) }
.toVector
if duplicates.isEmpty then multiMap.collect { case (k, v) if validID(k) => (k, v.head) }.toMap
else
sys.error(
duplicates map { case (k, tps) =>
"'" + k + "' (" + tps.mkString(", ") + ")"
} mkString ("Some keys were defined with the same name but different types: ", ", ", "")
)
val duplicateStr = duplicates
.map { case (k, tps) => s"'$k' (${tps.mkString(", ")})" }
.mkString(",")
sys.error(s"Some keys were defined with the same name but different types: $duplicateStr")
}
private[this] type TriggerMap = collection.mutable.HashMap[TaskId[?], Seq[TaskId[?]]]

View File

@ -56,7 +56,7 @@ object Previous {
// private[sbt] def task: ScopedKey[Task[T]] = key.task
lazy val stamped: JsonFormat[T] =
StampedFormat.withStamp(key.task.key.manifest.toString)(format)
StampedFormat.withStamp(key.task.key.tag.toString)(format)
def setTask(newTask: ScopedKey[Task[T]]) = new Referenced(newTask, format)
private[sbt] def read(streams: Streams): Option[T] =

View File

@ -10,7 +10,7 @@ package sbt
import scala.annotation.targetName
import sbt.internal.util.Types.*
import sbt.internal.util.{ AttributeKey, Settings, SourcePosition }
import sbt.internal.util.{ AttributeKey, KeyTag, Settings, SourcePosition }
import sbt.internal.util.TupleMapExtension.*
import sbt.util.OptJsonWriter
import sbt.ConcurrentRestrictions.Tag
@ -259,11 +259,6 @@ 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.
@ -736,10 +731,12 @@ end TupleSyntax
object TupleSyntax extends TupleSyntax
import Scoped.{ coerceTag, extendScoped }
import Scoped.extendScoped
/** Constructs InputKeys, which are associated with input tasks to define a setting. */
object InputKey:
private given [A: ClassTag]: KeyTag[InputTask[A]] =
KeyTag.InputTask(summon[ClassTag[A]].runtimeClass)
def apply[A1: ClassTag](label: String): InputKey[A1] =
apply[A1](label, "", KeyRanks.DefaultInputRank)
@ -755,8 +752,6 @@ object InputKey:
description: String,
rank: Int,
): InputKey[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: ClassTag](
@ -773,8 +768,6 @@ object InputKey:
extend1: Scoped,
extendN: Scoped*
): InputKey[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] =
@ -784,27 +777,19 @@ end InputKey
/** Constructs TaskKeys, which are associated with tasks to define a setting. */
object TaskKey:
private given [A: ClassTag]: KeyTag[Task[A]] = KeyTag.Task(summon[ClassTag[A]].runtimeClass)
def apply[A1: ClassTag](label: String): TaskKey[A1] =
apply[A1](
label = label,
description = "",
rank = Int.MaxValue,
)
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,
)
apply[A1](label = label, description = description, rank = Int.MaxValue)
def apply[A1: ClassTag](
label: String,
description: String,
rank: Int,
): TaskKey[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: ClassTag](
@ -813,8 +798,6 @@ object TaskKey:
extend1: Scoped,
extendN: Scoped*
): TaskKey[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: ClassTag](
@ -824,25 +807,18 @@ object TaskKey:
extend1: Scoped,
extendN: Scoped*
): TaskKey[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: ClassTag]: TaskKey[A1] =
given mf: ClassTag[Task[A1]] =
ManifestFactory.classType[Task[A1]](classOf[Task[A1]], coerceTag[A1])
apply[A1](AttributeKey.local[Task[A1]])
def local[A1: ClassTag]: TaskKey[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: ClassTag: OptJsonWriter](
label: String,
): SettingKey[A1] =
def apply[A1: ClassTag: OptJsonWriter](label: String): SettingKey[A1] =
apply[A1](
label = label,
description = "",

View File

@ -440,14 +440,14 @@ object BuiltinCommands {
private[this] def quiet[T](t: => T): Option[T] =
try Some(t)
catch { case _: Exception => None }
catch case _: Exception => None
def settingsCommand: Command =
showSettingLike(
SettingsCommand,
settingsPreamble,
KeyRanks.MainSettingCutoff,
key => !isTask(key.manifest)
key => key.tag.isSetting
)
def tasks: Command =
@ -455,7 +455,7 @@ object BuiltinCommands {
TasksCommand,
tasksPreamble,
KeyRanks.MainTaskCutoff,
key => isTask(key.manifest)
key => key.tag.isTaskOrInputTask
)
def showSettingLike(
@ -518,11 +518,6 @@ object BuiltinCommands {
def withDescription(keys: Seq[AttributeKey[_]]): Seq[AttributeKey[_]] =
keys.filter(_.description.isDefined)
def isTask(
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) =

View File

@ -8,26 +8,20 @@
package sbt
import Def.ScopedKey
import sbt.internal.util.KeyTag
final case class ScopedKeyData[A](scoped: ScopedKey[A], value: Any) {
import sbt.internal.util.Types.const
val key = scoped.key
val scope = scoped.scope
def typeName: String = fold(fmtMf("Task[%s]"), fmtMf("InputTask[%s]"), key.manifest.toString)
def settingValue: Option[Any] = fold(const(None), const(None), Some(value))
def typeName: String = key.tag.toString
def settingValue: Option[Any] =
key.tag match
case KeyTag.Setting(_) => Some(value)
case _ => None
def description: String =
fold(
fmtMf("Task: %s"),
fmtMf("Input task: %s"),
"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
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[_]]
private val InputTaskClass = classOf[InputTask[_]]
key.tag match
case KeyTag.Task(typeArg) => s"Task: $typeArg"
case KeyTag.InputTask(typeArg) => s"Input task: $typeArg"
case KeyTag.Setting(typeArg) => s"Setting: $typeArg = $value"
}

View File

@ -8,7 +8,7 @@
package sbt
package internal
import sbt.internal.util.{ AttributeKey, complete, Relation, Settings, Types, Util }
import sbt.internal.util.{ AttributeKey, complete, KeyTag, Relation, Settings, Types, Util }
import sbt.util.Show
import sbt.librarymanagement.Configuration
@ -352,40 +352,16 @@ 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: 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
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[_]): 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 ClassTag.
*/
def keyTypeString[T](key: AttributeKey[_]): String =
val tagToString = (tag: ClassTag[_]) => complete.TypeString.cleanup(tag.toString)
keyType(key)(tagToString, tagToString, tagToString)
complete.TypeString.cleanup(key.tag.typeArg.toString)
/** True if the `key` represents a setting or task that may be appended using an assignment method such as `+=`. */
def appendable(key: AttributeKey[_]): Boolean = {
val underlying = keyUnderlyingType(key).runtimeClass
appendableClasses.exists(_ isAssignableFrom underlying)
}
def appendable(key: AttributeKey[_]): Boolean =
appendableClasses.exists(_.isAssignableFrom(key.tag.typeArg))
/** The simple name of the Global scope, which can be used to reference it in the default setting context. */
final val GlobalID = Scope.Global.getClass.getSimpleName.stripSuffix("$")

View File

@ -104,7 +104,7 @@ object SettingQuery {
def getJsonWriter[A](key: AttributeKey[A]): Either[String, JsonWriter[A]] =
key.optJsonWriter match {
case SomeJsonWriter(jw) => Right(jw)
case NoJsonWriter() => Left(s"JsonWriter for ${key.manifest} not found")
case NoJsonWriter() => Left(s"JsonWriter for ${key.tag} not found")
}
def toJson[A: JsonWriter](x: A): JValue = Converter toJsonUnsafe x
@ -128,7 +128,7 @@ object SettingQuery {
for {
key <- key
json <- getSettingJsonValue(structure, key)
} yield SettingQuerySuccess(json, key.key.manifest.toString)
} yield SettingQuerySuccess(json, key.key.tag.toString)
}
def handleSettingQuery(req: SettingQuery, structure: BuildStructure): SettingQueryResponse =

View File

@ -14,7 +14,7 @@ import java.util.concurrent.ConcurrentHashMap
import sbt.Keys._
import sbt.internal.Clean.ToSeqPath
import sbt.internal.Continuous.FileStampRepository
import sbt.internal.util.AttributeKey
import sbt.internal.util.KeyTag
import sbt.internal.{ Clean, Continuous, DynamicInput, WatchTransitiveDependencies }
import sbt.nio.FileStamp.Formats._
import sbt.nio.FileStamper.{ Hash, LastModified }
@ -53,7 +53,7 @@ private[sbt] object Settings {
/**
* This method checks if the setting is for a task with a return type in:
* `File`, `Seq[File]`, `Path`, `Seq[Path`. If it does, then we inject a number of
* `File`, `Seq[File]`, `Path`, `Seq[Path]`. If it does, then we inject a number of
* task definition settings that allow the user to check if the output paths of
* the task have changed. It also adds a custom clean task that will delete the
* paths returned by the task, provided that they are in the task's target directory. We also
@ -68,10 +68,11 @@ private[sbt] object Settings {
setting: Def.Setting[_],
fileOutputScopes: Set[Scope]
): List[Def.Setting[_]] = {
setting.key.key match {
case ak: AttributeKey[_] if taskClass.isAssignableFrom(ak.manifest.runtimeClass) =>
val attributeKey = setting.key.key
attributeKey.tag match {
case tag: KeyTag.Task[?] =>
def default: List[Def.Setting[_]] = {
val scope = setting.key.scope.copy(task = Select(ak))
val scope = setting.key.scope.copy(task = Select(attributeKey))
if (fileOutputScopes.contains(scope)) {
val sk = setting.asInstanceOf[Def.Setting[Task[Any]]].key
val scopedKey = Keys.dynamicFileOutputs in (sk.scope in sk.key)
@ -93,15 +94,15 @@ private[sbt] object Settings {
addTaskDefinition(Def.setting[Task[Seq[Path]]](key, init, setting.pos)) ::
outputsAndStamps(taskKey)
}
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: Manifest[_]) :: Nil if pathClass.isAssignableFrom(t.runtimeClass) =>
mkSetting[Path]
case _ => default
if seqClass.isAssignableFrom(tag.typeArg) then
// TODO fix this: maybe using the taskKey macro to convey the information
// t.typeArguments match {
// case p :: Nil if pathClass.isAssignableFrom(p.runtimeClass) => mkSetting[Seq[Path]]
// case _ => default
// }
default
else if pathClass.isAssignableFrom(tag.typeArg) then mkSetting[Path]
else default
case _ => Nil
}
}
@ -161,7 +162,6 @@ private[sbt] object Settings {
}) :: fileStamps(scopedKey) :: allFilesImpl(scope) :: changedInputFilesImpl(scope)
}
private[this] val taskClass = classOf[Task[_]]
private[this] val seqClass = classOf[Seq[_]]
private[this] val pathClass = classOf[java.nio.file.Path]

View File

@ -12,15 +12,32 @@ import scala.reflect.ClassTag
import sbt.util.OptJsonWriter
import sjsonnew.*
// T must be invariant to work properly.
enum KeyTag[A]:
case Setting[A](typeArg: Class[?]) extends KeyTag[A]
case Task[A](typeArg: Class[?]) extends KeyTag[A]
case InputTask[A](typeArg: Class[?]) extends KeyTag[A]
override def toString: String = this match
case Setting(typeArg) => typeArg.toString
case Task(typeArg) => s"Task[$typeArg]"
case InputTask(typeArg) => s"InputTask[$typeArg]"
def typeArg: Class[?]
def isSetting: Boolean = isInstanceOf[Setting[?]]
def isTaskOrInputTask: Boolean = !isSetting
end KeyTag
object KeyTag:
given [A: ClassTag]: KeyTag[A] = Setting[A](summon[ClassTag[A]].runtimeClass)
// A must be invariant to work properly.
// Because it is sealed and the only instances go through AttributeKey.apply,
// a single AttributeKey instance cannot conform to AttributeKey[T] for different Ts
// a single AttributeKey instance cannot conform to AttributeKey[A] for different As
sealed trait AttributeKey[A]:
/** The runtime evidence for `A`. */
def manifest: ClassTag[A]
// def classTag: ClassTag[A]
def tag: KeyTag[A]
/** The label is the identifier for the key and is camelCase by convention. */
def label: String
@ -59,37 +76,37 @@ private[sbt] abstract class SharedAttributeKey[A] extends AttributeKey[A]:
override final def hashCode = label.hashCode
override final def equals(o: Any) =
(this eq o.asInstanceOf[AnyRef]) || (o match {
case a: SharedAttributeKey[t] => a.label == this.label && a.manifest == this.manifest
case a: SharedAttributeKey[t] => a.label == this.label && a.tag == this.tag
case _ => false
})
final def isLocal: Boolean = false
end SharedAttributeKey
object AttributeKey {
def apply[A: ClassTag: OptJsonWriter](name: String): AttributeKey[A] =
def apply[A: KeyTag: OptJsonWriter](name: String): AttributeKey[A] =
make(name, None, Nil, Int.MaxValue)
def apply[A: ClassTag: OptJsonWriter](name: String, rank: Int): AttributeKey[A] =
def apply[A: KeyTag: OptJsonWriter](name: String, rank: Int): AttributeKey[A] =
make(name, None, Nil, rank)
def apply[A: ClassTag: OptJsonWriter](name: String, description: String): AttributeKey[A] =
def apply[A: KeyTag: OptJsonWriter](name: String, description: String): AttributeKey[A] =
apply(name, description, Nil)
def apply[A: ClassTag: OptJsonWriter](
def apply[A: KeyTag: OptJsonWriter](
name: String,
description: String,
rank: Int
): AttributeKey[A] =
apply(name, description, Nil, rank)
def apply[A: ClassTag: OptJsonWriter](
def apply[A: KeyTag: OptJsonWriter](
name: String,
description: String,
extend: Seq[AttributeKey[_]]
): AttributeKey[A] =
apply(name, description, extend, Int.MaxValue)
def apply[A: ClassTag: OptJsonWriter](
def apply[A: KeyTag: OptJsonWriter](
name: String,
description: String,
extend: Seq[AttributeKey[_]],
@ -98,40 +115,39 @@ object AttributeKey {
make(name, Some(description), extend, rank)
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)
make(a.label, a.description, a.extend, rank)(using a.tag, a.optJsonWriter)
private[this] def make[A](
private[this] def make[A: KeyTag: OptJsonWriter](
name: String,
description0: Option[String],
extend0: Seq[AttributeKey[_]],
rank0: Int
)(using mf: ClassTag[A], ojw: OptJsonWriter[A]): AttributeKey[A] =
): AttributeKey[A] =
new SharedAttributeKey[A]:
require(
name.headOption.exists(_.isLower),
s"A named attribute key must start with a lowercase letter: $name"
)
override def manifest: ClassTag[A] = mf
override def tag: KeyTag[A] = summon
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
override def optJsonWriter: OptJsonWriter[A] = summon
private[sbt] def local[A](using ct: ClassTag[A], ojw: OptJsonWriter[A]): AttributeKey[A] =
private[sbt] def local[A: KeyTag: OptJsonWriter]: AttributeKey[A] =
new AttributeKey[A]:
override def manifest: ClassTag[A] = ct
override def tag: KeyTag[A] = summon
override def label: String = LocalLabel
override def description: Option[String] = None
override def extend: Seq[AttributeKey[_]] = Nil
override def toString = label
override def isLocal: Boolean = true
override def rank: Int = Int.MaxValue
override val optJsonWriter: OptJsonWriter[A] = ojw
override val optJsonWriter: OptJsonWriter[A] = summon
private[sbt] final val LocalLabel = "$" + "local"
}
/**