Migrate updateTask via tuple syntax

This commit is contained in:
Eugene Yokota 2022-09-20 02:08:28 -04:00
parent 0c6eb093ac
commit 0ee7d11afe
4 changed files with 334 additions and 177 deletions

View File

@ -718,7 +718,7 @@ end Scoped
* *
* See https://www.scala-sbt.org/1.x/docs/Migrating-from-sbt-013x.html#Migrating+from+sbt+0.12+style for how to migrate. * See https://www.scala-sbt.org/1.x/docs/Migrating-from-sbt-013x.html#Migrating+from+sbt+0.12+style for how to migrate.
*/ */
trait TupleSyntax { trait TupleSyntax:
import Scoped._ import Scoped._
// format: off // format: off
@ -748,7 +748,7 @@ trait TupleSyntax {
implicit def t11ToApp11[A, B, C, D, E, F, G, H, I, J, K](t11: (Initialize[A], Initialize[B], Initialize[C], Initialize[D], Initialize[E], Initialize[F], Initialize[G], Initialize[H], Initialize[I], Initialize[J], Initialize[K])): Apply11[A, B, C, D, E, F, G, H, I, J, K] = new Apply11(t11) implicit def t11ToApp11[A, B, C, D, E, F, G, H, I, J, K](t11: (Initialize[A], Initialize[B], Initialize[C], Initialize[D], Initialize[E], Initialize[F], Initialize[G], Initialize[H], Initialize[I], Initialize[J], Initialize[K])): Apply11[A, B, C, D, E, F, G, H, I, J, K] = new Apply11(t11)
// format: on // format: on
} end TupleSyntax
object TupleSyntax extends TupleSyntax object TupleSyntax extends TupleSyntax
@ -849,3 +849,12 @@ object SettingKey:
def local[A1: Manifest: OptJsonWriter]: SettingKey[A1] = apply[A1](AttributeKey.local[A1]) def local[A1: Manifest: OptJsonWriter]: SettingKey[A1] = apply[A1](AttributeKey.local[A1])
end SettingKey end SettingKey
class TupleWrap[Tup <: Tuple](value: Tuple.Map[Tup, Taskable]):
type InitTask[A2] = Initialize[Task[A2]]
lazy val alist = AList.tuple[Tup]
lazy val initTasks =
alist.transform[Taskable, InitTask](value)([a] => (t: Taskable[a]) => t.toTask)
def mapN[A1](f: Tup => A1): Def.Initialize[Task[A1]] =
import std.FullInstance.initializeTaskMonad
alist.mapN[InitTask, A1](initTasks)(f.asInstanceOf[Tuple.Map[Tup, Id] => A1])

View File

@ -9,12 +9,19 @@ package sbt.test
import sbt._ import sbt._
import sbt.Def.Initialize import sbt.Def.Initialize
import sbt.TupleSyntax._ import sbt.internal.util.AList
import sbt.internal.util.Types.Id
object TupleSyntaxTest: object TupleSyntaxTest:
def t1[A](a: SettingKey[A], b: TaskKey[A], c: Def.Initialize[A], d: Def.Initialize[Task[A]]) = { def t1[A](a: SettingKey[A], b: TaskKey[A], c: Def.Initialize[A], d: Def.Initialize[Task[A]]) = {
import sbt.TupleSyntax._
(a, b, c.toTaskable, d.toTaskable).mapN { (x: A, y: A, z: A, w: A) => (a, b, c.toTaskable, d.toTaskable).mapN { (x: A, y: A, z: A, w: A) =>
"" + x + y + z + w "" + x + y + z + w
} }
} }
def t2[A](a: SettingKey[A], b: TaskKey[A], c: Def.Initialize[A], d: Def.Initialize[Task[A]]) =
TupleWrap[(A, A, A, A)]((a, b, c.toTaskable, d)).mapN { case (x: A, y: A, z: A, w: A) =>
"" + x + y + z + w
}
end TupleSyntaxTest end TupleSyntaxTest

View File

@ -3633,92 +3633,173 @@ object Classpaths {
cacheLabel: String, cacheLabel: String,
includeCallers: Boolean, includeCallers: Boolean,
includeDetails: Boolean includeDetails: Boolean
): Initialize[Task[UpdateReport]] = Def.task { ): Initialize[Task[UpdateReport]] =
val s = streams.value TupleWrap[
val cacheDirectory = crossTarget.value / cacheLabel / updateCacheName.value (
DependencyResolution,
TaskStreams,
UpdateConfiguration,
Option[Level.Value],
String,
State,
String,
xsbti.AppConfiguration,
Option[ScalaInstance],
File,
File,
Seq[ScopedKey[_]],
ScopedKey[_],
Option[FiniteDuration],
Boolean,
ProjectRef,
IvySbt#Module,
String,
Boolean,
Seq[UpdateReport],
UnresolvedWarningConfiguration,
Level.Value,
Seq[ModuleID],
Level.Value,
String,
String,
Boolean,
CompatibilityWarningOptions,
)
](
dependencyResolution,
streams,
updateConfiguration.toTaskable,
(update / logLevel).?.toTaskable,
updateCacheName.toTaskable,
state,
scalaVersion.toTaskable,
appConfiguration.toTaskable,
Defaults.unmanagedScalaInstanceOnly.toTaskable,
dependencyCacheDirectory.toTaskable,
crossTarget.toTaskable,
executionRoots.toTaskable,
resolvedScoped.toTaskable,
forceUpdatePeriod.toTaskable,
sbtPlugin.toTaskable,
thisProjectRef.toTaskable,
ivyModule.toTaskable,
scalaOrganization.toTaskable,
(update / skip).toTaskable,
transitiveUpdate.toTaskable,
(update / unresolvedWarningConfiguration).toTaskable,
evictionErrorLevel.toTaskable,
libraryDependencySchemes.toTaskable,
assumedEvictionErrorLevel.toTaskable,
assumedVersionScheme.toTaskable,
assumedVersionSchemeJava.toTaskable,
publishMavenStyle.toTaskable,
compatibilityWarningOptions.toTaskable,
).mapN {
case (
lm,
s,
conf,
maybeUpdateLevel,
ucn,
state0,
sv,
ac,
usiOnly,
dcd,
ct,
er,
rs,
fup,
isPlugin,
thisRef,
im,
so,
sk,
tu,
uwConfig,
eel,
lds,
aeel,
avs,
avsj,
mavenStyle,
cwo,
) =>
val cacheDirectory = ct / cacheLabel / ucn
val cacheStoreFactory: CacheStoreFactory = {
val factory =
state0.get(Keys.cacheStoreFactoryFactory).getOrElse(InMemoryCacheStore.factory(0))
factory(cacheDirectory.toPath)
}
val cacheStoreFactory: CacheStoreFactory = { val isRoot = er.contains(rs)
val factory = val shouldForce = isRoot || {
state.value.get(Keys.cacheStoreFactoryFactory).getOrElse(InMemoryCacheStore.factory(0)) fup match
factory(cacheDirectory.toPath) case None => false
case Some(period) =>
val fullUpdateOutput = cacheDirectory / "out"
val now = System.currentTimeMillis
val diff = now - IO.getModifiedTimeOrZero(fullUpdateOutput)
val elapsedDuration = new FiniteDuration(diff, TimeUnit.MILLISECONDS)
fullUpdateOutput.exists() && elapsedDuration > period
}
val providedScalaJars: String => Seq[File] = {
val scalaProvider = ac.provider.scalaProvider
usiOnly match
case Some(instance) =>
unmanagedJarsTask(sv, instance.version, instance.allJars)
case None =>
(subVersion: String) =>
if (scalaProvider.version == subVersion) scalaProvider.jars else Nil
}
val updateConf = {
// Log captures log messages at all levels, except ivy logs.
// Use full level when debug is enabled so that ivy logs are shown.
import UpdateLogging.{ Default, DownloadOnly, Full }
val conf1 = maybeUpdateLevel.orElse(state0.get(logLevel.key)) match {
case Some(Level.Debug) if conf.logging == Default => conf.withLogging(logging = Full)
case Some(_) if conf.logging == Default => conf.withLogging(logging = DownloadOnly)
case _ => conf
}
// logical clock is folded into UpdateConfiguration
conf1
.withLogicalClock(LogicalClock(state0.hashCode))
.withMetadataDirectory(dcd)
}
val extracted = Project.extract(state0)
val label =
if (isPlugin) Reference.display(thisRef)
else Def.displayRelativeReference(extracted.currentRef, thisRef)
LibraryManagement.cachedUpdate(
// LM API
lm = lm,
// Ivy-free ModuleDescriptor
module = im,
cacheStoreFactory = cacheStoreFactory,
label = label,
updateConf,
substituteScalaFiles(so, _)(providedScalaJars),
skip = sk,
force = shouldForce,
depsUpdated = tu.exists(!_.stats.cached),
uwConfig = uwConfig,
evictionLevel = eel,
versionSchemeOverrides = lds,
assumedEvictionErrorLevel = aeel,
assumedVersionScheme = avs,
assumedVersionSchemeJava = avsj,
mavenStyle = mavenStyle,
compatWarning = cwo,
includeCallers = includeCallers,
includeDetails = includeDetails,
log = s.log
)
} }
val isRoot = executionRoots.value contains resolvedScoped.value
val shouldForce = isRoot || {
forceUpdatePeriod.value match {
case None => false
case Some(period) =>
val fullUpdateOutput = cacheDirectory / "out"
val now = System.currentTimeMillis
val diff = now - IO.getModifiedTimeOrZero(fullUpdateOutput)
val elapsedDuration = new FiniteDuration(diff, TimeUnit.MILLISECONDS)
fullUpdateOutput.exists() && elapsedDuration > period
}
}
val providedScalaJars: String => Seq[File] = {
val scalaProvider = appConfiguration.value.provider.scalaProvider
Defaults.unmanagedScalaInstanceOnly.value match {
case Some(instance) =>
unmanagedJarsTask(scalaVersion.value, instance.version, instance.allJars)
case None =>
(subVersion: String) =>
if (scalaProvider.version == subVersion) scalaProvider.jars else Nil
}
}
val state0 = state.value
val updateConf = {
// Log captures log messages at all levels, except ivy logs.
// Use full level when debug is enabled so that ivy logs are shown.
import UpdateLogging.{ Default, DownloadOnly, Full }
val conf = updateConfiguration.value
val maybeUpdateLevel = (update / logLevel).?.value
val conf1 = maybeUpdateLevel.orElse(state0.get(logLevel.key)) match {
case Some(Level.Debug) if conf.logging == Default => conf.withLogging(logging = Full)
case Some(_) if conf.logging == Default => conf.withLogging(logging = DownloadOnly)
case _ => conf
}
// logical clock is folded into UpdateConfiguration
conf1
.withLogicalClock(LogicalClock(state0.hashCode))
.withMetadataDirectory(dependencyCacheDirectory.value)
}
val extracted = Project.extract(state0)
val isPlugin = sbtPlugin.value
val thisRef = thisProjectRef.value
val label =
if (isPlugin) Reference.display(thisRef)
else Def.displayRelativeReference(extracted.currentRef, thisRef)
LibraryManagement.cachedUpdate(
// LM API
lm = dependencyResolution.value,
// Ivy-free ModuleDescriptor
module = ivyModule.value,
cacheStoreFactory = cacheStoreFactory,
label = label,
updateConf,
substituteScalaFiles(scalaOrganization.value, _)(providedScalaJars),
skip = (update / skip).value,
force = shouldForce,
depsUpdated = transitiveUpdate.value.exists(!_.stats.cached),
uwConfig = (update / unresolvedWarningConfiguration).value,
evictionLevel = evictionErrorLevel.value,
versionSchemeOverrides = libraryDependencySchemes.value,
assumedEvictionErrorLevel = assumedEvictionErrorLevel.value,
assumedVersionScheme = assumedVersionScheme.value,
assumedVersionSchemeJava = assumedVersionSchemeJava.value,
mavenStyle = publishMavenStyle.value,
compatWarning = compatibilityWarningOptions.value,
includeCallers = includeCallers,
includeDetails = includeDetails,
log = s.log
)
}
private[sbt] def dependencyPositionsTask: Initialize[Task[Map[ModuleID, SourcePosition]]] = private[sbt] def dependencyPositionsTask: Initialize[Task[Map[ModuleID, SourcePosition]]] =
Def.task { Def.task {
val projRef = thisProjectRef.value val projRef = thisProjectRef.value

View File

@ -11,6 +11,7 @@ package internal
import java.io.File import java.io.File
import java.util.concurrent.Callable import java.util.concurrent.Callable
import sbt.Def.ScopedKey
import sbt.SlashSyntax0._ import sbt.SlashSyntax0._
import sbt.internal.librarymanagement._ import sbt.internal.librarymanagement._
import sbt.librarymanagement._ import sbt.librarymanagement._
@ -21,6 +22,7 @@ import sbt.io.syntax._
import sbt.Project.richInitializeTask import sbt.Project.richInitializeTask
import sjsonnew.JsonFormat import sjsonnew.JsonFormat
import scala.compat.Platform.EOL import scala.compat.Platform.EOL
import scala.concurrent.duration.FiniteDuration
private[sbt] object LibraryManagement { private[sbt] object LibraryManagement {
implicit val linter: sbt.dsl.LinterLevel.Ignore.type = sbt.dsl.LinterLevel.Ignore implicit val linter: sbt.dsl.LinterLevel.Ignore.type = sbt.dsl.LinterLevel.Ignore
@ -242,100 +244,158 @@ private[sbt] object LibraryManagement {
* for dependency definitions, transitively. * for dependency definitions, transitively.
*/ */
def updateClassifiersTask: Def.Initialize[Task[UpdateReport]] = def updateClassifiersTask: Def.Initialize[Task[UpdateReport]] =
(Def.task { TupleWrap[
import Keys._ (
val s = streams.value DependencyResolution,
val cacheDirectory = streams.value.cacheDirectory State,
val csr = useCoursier.value Keys.TaskStreams,
val lm = dependencyResolution.value UpdateConfiguration,
Option[Level.Value],
if (csr) { Boolean,
// following copied from https://github.com/coursier/sbt-coursier/blob/9173406bb399879508aa481fed16efda72f55820/modules/sbt-lm-coursier/src/main/scala/sbt/hack/Foo.scala Seq[ScopedKey[_]],
val isRoot = executionRoots.value contains resolvedScoped.value ScopedKey[_],
val shouldForce = isRoot || { Option[FiniteDuration],
forceUpdatePeriod.value match { IvySbt#Module,
case None => false String,
case Some(period) => ProjectRef,
val fullUpdateOutput = cacheDirectory / "out" Boolean,
val now = System.currentTimeMillis Seq[UpdateReport],
val diff = now - fullUpdateOutput.lastModified() UnresolvedWarningConfiguration,
val elapsedDuration = new scala.concurrent.duration.FiniteDuration( Boolean,
diff, CompatibilityWarningOptions,
java.util.concurrent.TimeUnit.MILLISECONDS IvySbt,
) GetClassifiersModule,
fullUpdateOutput.exists() && elapsedDuration > period File,
} xsbti.AppConfiguration,
} Seq[String],
val state0 = state.value Seq[String],
val updateConf = { )
import UpdateLogging.{ Full, DownloadOnly, Default } ](
val conf = updateConfiguration.value Keys.dependencyResolution,
val maybeUpdateLevel = (update / logLevel).?.value Keys.state,
val conf1 = maybeUpdateLevel.orElse(state0.get(logLevel.key)) match { Keys.streams,
case Some(Level.Debug) if conf.logging == Default => conf.withLogging(logging = Full) Keys.updateConfiguration.toTaskable,
case Some(_) if conf.logging == Default => conf.withLogging(logging = DownloadOnly) (Keys.update / Keys.logLevel).?.toTaskable,
case _ => conf Keys.useCoursier.toTaskable,
} Keys.executionRoots,
// logical clock is folded into UpdateConfiguration Keys.resolvedScoped.toTaskable,
conf1.withLogicalClock(LogicalClock(state0.hashCode)) Keys.forceUpdatePeriod.toTaskable,
} Keys.ivyModule.toTaskable,
cachedUpdate( Keys.updateCacheName.toTaskable,
// LM API Keys.thisProjectRef.toTaskable,
lm = lm, (Keys.update / Keys.skip).toTaskable,
// Ivy-free ModuleDescriptor Keys.transitiveUpdate,
module = ivyModule.value, (Keys.update / Keys.unresolvedWarningConfiguration).toTaskable,
s.cacheStoreFactory.sub(updateCacheName.value), Keys.publishMavenStyle.toTaskable,
Reference.display(thisProjectRef.value), Keys.compatibilityWarningOptions.toTaskable,
updateConf, Keys.ivySbt,
identity, Keys.classifiersModule,
skip = (update / skip).value, Keys.dependencyCacheDirectory,
force = shouldForce, Keys.appConfiguration.toTaskable,
depsUpdated = transitiveUpdate.value.exists(!_.stats.cached), Keys.sourceArtifactTypes.toTaskable,
uwConfig = (update / unresolvedWarningConfiguration).value, Keys.docArtifactTypes.toTaskable,
evictionLevel = Level.Debug, ).mapN {
versionSchemeOverrides = Nil, case (
assumedEvictionErrorLevel = Level.Debug, lm,
assumedVersionScheme = VersionScheme.Always, state0,
assumedVersionSchemeJava = VersionScheme.Always, s,
mavenStyle = publishMavenStyle.value, conf,
compatWarning = compatibilityWarningOptions.value, maybeUpdateLevel,
includeCallers = false, csr,
includeDetails = false, er,
log = s.log rs,
) fup,
} else { im,
val is = ivySbt.value ucn,
val mod = classifiersModule.value thisRef,
val updateConfig0 = updateConfiguration.value sk,
lazy val updateConfig = updateConfig0 tu,
.withMetadataDirectory(dependencyCacheDirectory.value)
.withArtifactFilter(
updateConfig0.artifactFilter.map(af => af.withInverted(!af.inverted))
)
val app = appConfiguration.value
val srcTypes = sourceArtifactTypes.value
val docTypes = docArtifactTypes.value
val uwConfig = (update / unresolvedWarningConfiguration).value
val out = is.withIvy(s.log)(_.getSettings.getDefaultIvyUserDir)
withExcludes(out, mod.classifiers, lock(app)) { excludes =>
lm.updateClassifiers(
GetClassifiersConfiguration(
mod,
excludes.toVector,
updateConfig,
srcTypes.toVector,
docTypes.toVector
),
uwConfig, uwConfig,
Vector.empty, mavenStyle,
s.log cwo,
) match { ivySbt0,
case Left(_) => ??? mod,
case Right(ur) => ur dcd,
app,
srcTypes,
docTypes,
) =>
import Keys._
val cacheDirectory = s.cacheDirectory
val isRoot = er.contains(rs)
if csr then {
// following copied from https://github.com/coursier/sbt-coursier/blob/9173406bb399879508aa481fed16efda72f55820/modules/sbt-lm-coursier/src/main/scala/sbt/hack/Foo.scala
val shouldForce = isRoot || {
fup match
case None => false
case Some(period) =>
val fullUpdateOutput = cacheDirectory / "out"
val now = System.currentTimeMillis
val diff = now - fullUpdateOutput.lastModified()
val elapsedDuration = new FiniteDuration(
diff,
java.util.concurrent.TimeUnit.MILLISECONDS
)
fullUpdateOutput.exists() && elapsedDuration > period
}
val updateConf = {
import UpdateLogging.{ Full, DownloadOnly, Default }
val conf1 = maybeUpdateLevel.orElse(state0.get(logLevel.key)) match
case Some(Level.Debug) if conf.logging == Default => conf.withLogging(logging = Full)
case Some(_) if conf.logging == Default => conf.withLogging(logging = DownloadOnly)
case _ => conf
// logical clock is folded into UpdateConfiguration
conf1.withLogicalClock(LogicalClock(state0.hashCode))
}
cachedUpdate(
// LM API
lm = lm,
// Ivy-free ModuleDescriptor
module = im,
s.cacheStoreFactory.sub(ucn),
Reference.display(thisRef),
updateConf,
identity,
skip = sk,
force = shouldForce,
depsUpdated = tu.exists(!_.stats.cached),
uwConfig = uwConfig,
evictionLevel = Level.Debug,
versionSchemeOverrides = Nil,
assumedEvictionErrorLevel = Level.Debug,
assumedVersionScheme = VersionScheme.Always,
assumedVersionSchemeJava = VersionScheme.Always,
mavenStyle = mavenStyle,
compatWarning = cwo,
includeCallers = false,
includeDetails = false,
log = s.log
)
} else {
lazy val updateConfig = conf
.withMetadataDirectory(dcd)
.withArtifactFilter(
conf.artifactFilter.map(af => af.withInverted(!af.inverted))
)
val out = ivySbt0.withIvy(s.log)(_.getSettings.getDefaultIvyUserDir)
withExcludes(out, mod.classifiers, lock(app)) { excludes =>
lm.updateClassifiers(
GetClassifiersConfiguration(
mod,
excludes.toVector,
updateConfig,
srcTypes.toVector,
docTypes.toVector
),
uwConfig,
Vector.empty,
s.log
) match
case Left(_) => ???
case Right(ur) => ur
} }
} }
} }.tag(Tags.Update, Tags.Network)
} tag (Tags.Update, Tags.Network))
def withExcludes(out: File, classifiers: Seq[String], lock: xsbti.GlobalLock)( def withExcludes(out: File, classifiers: Seq[String], lock: xsbti.GlobalLock)(
f: Map[ModuleID, Vector[ConfigRef]] => UpdateReport f: Map[ModuleID, Vector[ConfigRef]] => UpdateReport