mirror of https://github.com/sbt/sbt.git
Merge pull request #3149 from scalacenter/clean-update
Clean up `update` in `Defaults`
This commit is contained in:
commit
6cd3cd2323
|
|
@ -2051,271 +2051,93 @@ object Classpaths {
|
|||
)
|
||||
}
|
||||
|
||||
def updateTask: Initialize[Task[UpdateReport]] = Def.task {
|
||||
val depsUpdated = transitiveUpdate.value.exists(!_.stats.cached)
|
||||
val isRoot = executionRoots.value contains resolvedScoped.value
|
||||
val forceUpdate = forceUpdatePeriod.value
|
||||
val s = streams.value
|
||||
val fullUpdateOutput = s.cacheDirectory / "out"
|
||||
val forceUpdateByTime = forceUpdate match {
|
||||
case None => false
|
||||
case Some(period) =>
|
||||
val elapsedDuration =
|
||||
new FiniteDuration(System.currentTimeMillis() - fullUpdateOutput.lastModified(),
|
||||
TimeUnit.MILLISECONDS)
|
||||
fullUpdateOutput.exists() && elapsedDuration > period
|
||||
}
|
||||
val scalaProvider = appConfiguration.value.provider.scalaProvider
|
||||
|
||||
// Only substitute unmanaged jars for managed jars when the major.minor parts of the versions the same for:
|
||||
// the resolved Scala version and the scalaHome version: compatible (weakly- no qualifier checked)
|
||||
// the resolved Scala version and the declared scalaVersion: assume the user intended scalaHome to override anything with scalaVersion
|
||||
def subUnmanaged(subVersion: String, jars: Seq[File]) =
|
||||
(sv: String) =>
|
||||
(partialVersion(sv), partialVersion(subVersion), partialVersion(scalaVersion.value)) match {
|
||||
case (Some(res), Some(sh), _) if res == sh => jars
|
||||
case (Some(res), _, Some(decl)) if res == decl => jars
|
||||
case _ => Nil
|
||||
/**
|
||||
* Substitute unmanaged jars for managed jars when the major.minor parts of
|
||||
* the version are the same for:
|
||||
* 1. The Scala version and the `scalaHome` (unmanaged) version are equal.
|
||||
* 2. The Scala version and the `declared` (managed) version are equal.
|
||||
*
|
||||
* Equality is weak, that is, no version qualifier is checked.
|
||||
*/
|
||||
private def unmanagedJarsTask(scalaVersion: String, unmanagedVersion: String, jars: Seq[File]) = {
|
||||
(subVersion0: String) =>
|
||||
val scalaV = partialVersion(scalaVersion)
|
||||
val managedV = partialVersion(subVersion0)
|
||||
val unmanagedV = partialVersion(unmanagedVersion)
|
||||
(managedV, unmanagedV, scalaV) match {
|
||||
case (Some(mv), Some(uv), _) if mv == uv => jars
|
||||
case (Some(mv), _, Some(sv)) if mv == sv => jars
|
||||
case _ => Nil
|
||||
}
|
||||
}
|
||||
|
||||
def updateTask: Initialize[Task[UpdateReport]] = Def.task {
|
||||
val s = streams.value
|
||||
val cacheDirectory = streams.value.cacheDirectory
|
||||
|
||||
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 - fullUpdateOutput.lastModified()
|
||||
val elapsedDuration = new FiniteDuration(diff, TimeUnit.MILLISECONDS)
|
||||
fullUpdateOutput.exists() && elapsedDuration > period
|
||||
}
|
||||
val subScalaJars: String => Seq[File] = Defaults.unmanagedScalaInstanceOnly.value match {
|
||||
case Some(si) => subUnmanaged(si.version, si.allJars)
|
||||
case None =>
|
||||
sv =>
|
||||
if (scalaProvider.version == sv) scalaProvider.jars else Nil
|
||||
}
|
||||
val transform: UpdateReport => UpdateReport =
|
||||
r => substituteScalaFiles(scalaOrganization.value, r)(subScalaJars)
|
||||
val uwConfig = (unresolvedWarningConfiguration in update).value
|
||||
val show = Reference.display(thisProjectRef.value)
|
||||
val st = state.value
|
||||
val logicalClock = LogicalClock(st.hashCode)
|
||||
val depDir = dependencyCacheDirectory.value
|
||||
val uc0 = updateConfiguration.value
|
||||
val ms = publishMavenStyle.value
|
||||
val cw = compatibilityWarningOptions.value
|
||||
// Normally, log would capture log messages at all levels.
|
||||
// Ivy logs are treated specially using sbt.UpdateConfiguration.logging.
|
||||
// This code bumps up the sbt.UpdateConfiguration.logging to Full when logLevel is Debug.
|
||||
import UpdateLogging.{ Full, DownloadOnly, Default }
|
||||
val uc = (logLevel in update).?.value orElse st.get(logLevel.key) match {
|
||||
case Some(Level.Debug) if uc0.logging == Default => uc0.withLogging(Full)
|
||||
case Some(x) if uc0.logging == Default => uc0.withLogging(DownloadOnly)
|
||||
case _ => uc0
|
||||
|
||||
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 ewo =
|
||||
if (executionRoots.value exists { _.key == evicted.key }) EvictionWarningOptions.empty
|
||||
|
||||
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.{ Full, DownloadOnly, Default }
|
||||
val conf = updateConfiguration.value
|
||||
val maybeUpdateLevel = (logLevel in update).?.value
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
val evictionOptions = {
|
||||
if (executionRoots.value.exists(_.key == evicted.key))
|
||||
EvictionWarningOptions.empty
|
||||
else (evictionWarningOptions in update).value
|
||||
cachedUpdate(
|
||||
s.cacheStoreFactory sub updateCacheName.value,
|
||||
show,
|
||||
}
|
||||
|
||||
LibraryManagement.cachedUpdate(
|
||||
s.cacheStoreFactory.sub(updateCacheName.value),
|
||||
Reference.display(thisProjectRef.value),
|
||||
ivyModule.value,
|
||||
uc,
|
||||
transform,
|
||||
updateConf,
|
||||
substituteScalaFiles(scalaOrganization.value, _)(providedScalaJars),
|
||||
skip = (skip in update).value,
|
||||
force = isRoot || forceUpdateByTime,
|
||||
depsUpdated = depsUpdated,
|
||||
uwConfig = uwConfig,
|
||||
logicalClock = logicalClock,
|
||||
depDir = Some(depDir),
|
||||
ewo = ewo,
|
||||
mavenStyle = ms,
|
||||
compatWarning = cw,
|
||||
force = shouldForce,
|
||||
depsUpdated = transitiveUpdate.value.exists(!_.stats.cached),
|
||||
uwConfig = (unresolvedWarningConfiguration in update).value,
|
||||
logicalClock = LogicalClock(state0.hashCode),
|
||||
depDir = Some(dependencyCacheDirectory.value),
|
||||
ewo = evictionOptions,
|
||||
mavenStyle = publishMavenStyle.value,
|
||||
compatWarning = compatibilityWarningOptions.value,
|
||||
log = s.log
|
||||
)
|
||||
}
|
||||
|
||||
object AltLibraryManagementCodec extends LibraryManagementCodec {
|
||||
type In0 = ModuleSettings :+: UpdateConfiguration :+: HNil
|
||||
type In = IvyConfiguration :+: In0
|
||||
|
||||
object NullLogger extends sbt.internal.util.BasicLogger {
|
||||
override def control(event: sbt.util.ControlEvent.Value, message: ⇒ String): Unit = ()
|
||||
override def log(level: Level.Value, message: ⇒ String): Unit = ()
|
||||
override def logAll(events: Seq[sbt.util.LogEvent]): Unit = ()
|
||||
override def success(message: ⇒ String): Unit = ()
|
||||
override def trace(t: ⇒ Throwable): Unit = ()
|
||||
}
|
||||
|
||||
implicit val altRawRepositoryJsonFormat: JsonFormat[RawRepository] =
|
||||
project(_.name, FakeRawRepository.create)
|
||||
|
||||
// Redefine to add RawRepository, and switch to unionFormat
|
||||
override lazy implicit val ResolverFormat: JsonFormat[Resolver] =
|
||||
unionFormat8[Resolver,
|
||||
ChainedResolver,
|
||||
MavenRepo,
|
||||
MavenCache,
|
||||
FileRepository,
|
||||
URLRepository,
|
||||
SshRepository,
|
||||
SftpRepository,
|
||||
RawRepository]
|
||||
|
||||
type InlineIvyHL = (IvyPaths :+: Vector[Resolver] :+: Vector[Resolver] :+: Vector[
|
||||
ModuleConfiguration] :+: Boolean :+: Vector[String] :+: HNil)
|
||||
def inlineIvyToHL(i: InlineIvyConfiguration): InlineIvyHL = (
|
||||
i.paths :+: i.resolvers :+: i.otherResolvers :+: i.moduleConfigurations :+: i.localOnly
|
||||
:+: i.checksums :+: HNil
|
||||
)
|
||||
|
||||
type ExternalIvyHL = PlainFileInfo :+: Array[Byte] :+: HNil
|
||||
def externalIvyToHL(e: ExternalIvyConfiguration): ExternalIvyHL =
|
||||
FileInfo.exists(e.baseDirectory) :+: Hash.contentsIfLocal(e.uri) :+: HNil
|
||||
|
||||
// Redefine to use a subset of properties, that are serialisable
|
||||
override lazy implicit val InlineIvyConfigurationFormat: JsonFormat[InlineIvyConfiguration] = {
|
||||
def hlToInlineIvy(i: InlineIvyHL): InlineIvyConfiguration = {
|
||||
val (paths :+: resolvers :+: otherResolvers :+: moduleConfigurations :+: localOnly
|
||||
:+: checksums :+: HNil) = i
|
||||
InlineIvyConfiguration(None,
|
||||
IO.createTemporaryDirectory,
|
||||
NullLogger,
|
||||
UpdateOptions(),
|
||||
paths,
|
||||
resolvers,
|
||||
otherResolvers,
|
||||
moduleConfigurations,
|
||||
localOnly,
|
||||
checksums,
|
||||
None)
|
||||
}
|
||||
|
||||
project[InlineIvyConfiguration, InlineIvyHL](inlineIvyToHL, hlToInlineIvy)
|
||||
}
|
||||
|
||||
// Redefine to use a subset of properties, that are serialisable
|
||||
override lazy implicit val ExternalIvyConfigurationFormat
|
||||
: JsonFormat[ExternalIvyConfiguration] = {
|
||||
def hlToExternalIvy(e: ExternalIvyHL): ExternalIvyConfiguration = {
|
||||
val baseDirectory :+: _ /* uri */ :+: HNil = e
|
||||
ExternalIvyConfiguration(
|
||||
None,
|
||||
baseDirectory.file,
|
||||
NullLogger,
|
||||
UpdateOptions(),
|
||||
IO.createTemporaryDirectory.toURI /* the original uri is destroyed.. */,
|
||||
Vector.empty)
|
||||
}
|
||||
|
||||
project[ExternalIvyConfiguration, ExternalIvyHL](externalIvyToHL, hlToExternalIvy)
|
||||
}
|
||||
|
||||
// Redefine to switch to unionFormat
|
||||
override implicit lazy val IvyConfigurationFormat: JsonFormat[IvyConfiguration] =
|
||||
unionFormat2[IvyConfiguration, InlineIvyConfiguration, ExternalIvyConfiguration]
|
||||
|
||||
def forHNil[A <: HNil]: Equiv[A] = new Equiv[A] { def equiv(x: A, y: A) = true }
|
||||
implicit val lnilEquiv1: Equiv[HNil] = forHNil[HNil]
|
||||
implicit val lnilEquiv2: Equiv[HNil.type] = forHNil[HNil.type]
|
||||
|
||||
implicit def hconsEquiv[H, T <: HList](implicit he: Equiv[H], te: Equiv[T]): Equiv[H :+: T] =
|
||||
new Equiv[H :+: T] {
|
||||
def equiv(x: H :+: T, y: H :+: T) = he.equiv(x.head, y.head) && te.equiv(x.tail, y.tail)
|
||||
}
|
||||
|
||||
implicit object altIvyConfigurationEquiv extends Equiv[IvyConfiguration] {
|
||||
def equiv(x: IvyConfiguration, y: IvyConfiguration): Boolean = (x, y) match {
|
||||
case (x: InlineIvyConfiguration, y: InlineIvyConfiguration) =>
|
||||
implicitly[Equiv[InlineIvyHL]].equiv(inlineIvyToHL(x), inlineIvyToHL(y))
|
||||
case (x: ExternalIvyConfiguration, y: ExternalIvyConfiguration) =>
|
||||
implicitly[Equiv[ExternalIvyHL]].equiv(externalIvyToHL(x), externalIvyToHL(y))
|
||||
case (x: Any, y: Any) => sys error s"Trying to compare ${x.getClass} with ${y.getClass}"
|
||||
}
|
||||
}
|
||||
|
||||
implicit object altInSingletonCache extends SingletonCache[In] {
|
||||
def write(to: Output, value: In) = to.write(value)
|
||||
def read(from: Input) = from.read[In]()
|
||||
def equiv = hconsEquiv(altIvyConfigurationEquiv, implicitly[Equiv[In0]])
|
||||
}
|
||||
}
|
||||
|
||||
private[sbt] def cachedUpdate(cacheStoreFactory: CacheStoreFactory,
|
||||
label: String,
|
||||
module: IvySbt#Module,
|
||||
config: UpdateConfiguration,
|
||||
transform: UpdateReport => UpdateReport,
|
||||
skip: Boolean,
|
||||
force: Boolean,
|
||||
depsUpdated: Boolean,
|
||||
uwConfig: UnresolvedWarningConfiguration,
|
||||
logicalClock: LogicalClock,
|
||||
depDir: Option[File],
|
||||
ewo: EvictionWarningOptions,
|
||||
mavenStyle: Boolean,
|
||||
compatWarning: CompatibilityWarningOptions,
|
||||
log: Logger): UpdateReport = {
|
||||
type In = IvyConfiguration :+: ModuleSettings :+: UpdateConfiguration :+: HNil
|
||||
|
||||
def work = (_: In) match {
|
||||
case conf :+: settings :+: config :+: HNil =>
|
||||
import ShowLines._
|
||||
log.info("Updating " + label + "...")
|
||||
val r =
|
||||
IvyActions.updateEither(module, config, uwConfig, logicalClock, depDir, log) match {
|
||||
case Right(ur) => ur
|
||||
case Left(uw) =>
|
||||
uw.lines foreach { log.warn(_) }
|
||||
throw uw.resolveException
|
||||
}
|
||||
log.info("Done updating.")
|
||||
val result = transform(r)
|
||||
val ew = EvictionWarning(module, ewo, result, log)
|
||||
ew.lines foreach { log.warn(_) }
|
||||
ew.infoAllTheThings foreach { log.info(_) }
|
||||
CompatibilityWarning.run(compatWarning, module, mavenStyle, log)
|
||||
result
|
||||
}
|
||||
def uptodate(inChanged: Boolean, out: UpdateReport): Boolean =
|
||||
!force &&
|
||||
!depsUpdated &&
|
||||
!inChanged &&
|
||||
out.allFiles.forall(f => fileUptodate(f, out.stamps)) &&
|
||||
fileUptodate(out.cachedDescriptor, out.stamps)
|
||||
|
||||
val outStore = cacheStoreFactory make "output"
|
||||
def skipWork: In => UpdateReport = {
|
||||
import LibraryManagementCodec._
|
||||
Tracked.lastOutput[In, UpdateReport](outStore) {
|
||||
case (_, Some(out)) => out
|
||||
case _ =>
|
||||
sys.error("Skipping update requested, but update has not previously run successfully.")
|
||||
}
|
||||
}
|
||||
def doWorkInternal = { (inChanged: Boolean, in: In) =>
|
||||
import LibraryManagementCodec._
|
||||
val outCache = Tracked.lastOutput[In, UpdateReport](outStore) {
|
||||
case (_, Some(out)) if uptodate(inChanged, out) => out
|
||||
case _ => work(in)
|
||||
}
|
||||
try {
|
||||
outCache(in)
|
||||
} catch {
|
||||
case e: NullPointerException =>
|
||||
val r = work(in)
|
||||
log.warn("Update task has failed to cache the report due to null.")
|
||||
log.warn("Report the following output to sbt:")
|
||||
r.toString.lines foreach { log.warn(_) }
|
||||
log.trace(e)
|
||||
r
|
||||
case e: OutOfMemoryError =>
|
||||
val r = work(in)
|
||||
log.warn("Update task has failed to cache the report due to OutOfMemoryError.")
|
||||
log.trace(e)
|
||||
r
|
||||
}
|
||||
}
|
||||
def doWork: In => UpdateReport = {
|
||||
import AltLibraryManagementCodec._
|
||||
Tracked.inputChanged(cacheStoreFactory make "inputs")(doWorkInternal)
|
||||
}
|
||||
val f = if (skip && !force) skipWork else doWork
|
||||
f(module.owner.configuration :+: module.moduleSettings :+: config :+: HNil)
|
||||
}
|
||||
|
||||
private[this] def fileUptodate(file: File, stamps: Map[File, Long]): Boolean =
|
||||
stamps.get(file).forall(_ == file.lastModified)
|
||||
|
||||
private[sbt] def dependencyPositionsTask: Initialize[Task[Map[ModuleID, SourcePosition]]] =
|
||||
Def.task {
|
||||
val projRef = thisProjectRef.value
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
package sbt.internal
|
||||
|
||||
import sbt.internal.librarymanagement._
|
||||
import sbt.internal.util.Types._
|
||||
import sbt.internal.util.{ HList, HNil }
|
||||
import sbt.io.{ Hash, IO }
|
||||
import sbt.librarymanagement._
|
||||
import sbt.util.CacheImplicits._
|
||||
import sbt.util._
|
||||
import sjsonnew.JsonFormat
|
||||
|
||||
object AltLibraryManagementCodec extends LibraryManagementCodec {
|
||||
type In0 = ModuleSettings :+: UpdateConfiguration :+: HNil
|
||||
type In = IvyConfiguration :+: In0
|
||||
|
||||
object NullLogger extends sbt.internal.util.BasicLogger {
|
||||
override def control(event: sbt.util.ControlEvent.Value, message: ⇒ String): Unit = ()
|
||||
override def log(level: Level.Value, message: ⇒ String): Unit = ()
|
||||
override def logAll(events: Seq[sbt.util.LogEvent]): Unit = ()
|
||||
override def success(message: ⇒ String): Unit = ()
|
||||
override def trace(t: ⇒ Throwable): Unit = ()
|
||||
}
|
||||
|
||||
implicit val altRawRepositoryJsonFormat: JsonFormat[RawRepository] =
|
||||
project(_.name, FakeRawRepository.create)
|
||||
|
||||
// Redefine to add RawRepository, and switch to unionFormat
|
||||
override lazy implicit val ResolverFormat: JsonFormat[Resolver] =
|
||||
unionFormat8[Resolver,
|
||||
ChainedResolver,
|
||||
MavenRepo,
|
||||
MavenCache,
|
||||
FileRepository,
|
||||
URLRepository,
|
||||
SshRepository,
|
||||
SftpRepository,
|
||||
RawRepository]
|
||||
|
||||
type InlineIvyHL = (IvyPaths :+: Vector[Resolver] :+: Vector[Resolver] :+: Vector[
|
||||
ModuleConfiguration] :+: Boolean :+: Vector[String] :+: HNil)
|
||||
def inlineIvyToHL(i: InlineIvyConfiguration): InlineIvyHL = (
|
||||
i.paths :+: i.resolvers :+: i.otherResolvers :+: i.moduleConfigurations :+: i.localOnly
|
||||
:+: i.checksums :+: HNil
|
||||
)
|
||||
|
||||
type ExternalIvyHL = PlainFileInfo :+: Array[Byte] :+: HNil
|
||||
def externalIvyToHL(e: ExternalIvyConfiguration): ExternalIvyHL =
|
||||
FileInfo.exists(e.baseDirectory) :+: Hash.contentsIfLocal(e.uri) :+: HNil
|
||||
|
||||
// Redefine to use a subset of properties, that are serialisable
|
||||
override lazy implicit val InlineIvyConfigurationFormat: JsonFormat[InlineIvyConfiguration] = {
|
||||
def hlToInlineIvy(i: InlineIvyHL): InlineIvyConfiguration = {
|
||||
val (paths :+: resolvers :+: otherResolvers :+: moduleConfigurations :+: localOnly
|
||||
:+: checksums :+: HNil) = i
|
||||
InlineIvyConfiguration(None,
|
||||
IO.createTemporaryDirectory,
|
||||
NullLogger,
|
||||
UpdateOptions(),
|
||||
paths,
|
||||
resolvers,
|
||||
otherResolvers,
|
||||
moduleConfigurations,
|
||||
localOnly,
|
||||
checksums,
|
||||
None)
|
||||
}
|
||||
|
||||
project[InlineIvyConfiguration, InlineIvyHL](inlineIvyToHL, hlToInlineIvy)
|
||||
}
|
||||
|
||||
// Redefine to use a subset of properties, that are serialisable
|
||||
override lazy implicit val ExternalIvyConfigurationFormat
|
||||
: JsonFormat[ExternalIvyConfiguration] = {
|
||||
def hlToExternalIvy(e: ExternalIvyHL): ExternalIvyConfiguration = {
|
||||
val baseDirectory :+: _ /* uri */ :+: HNil = e
|
||||
ExternalIvyConfiguration(
|
||||
None,
|
||||
baseDirectory.file,
|
||||
NullLogger,
|
||||
UpdateOptions(),
|
||||
IO.createTemporaryDirectory.toURI /* the original uri is destroyed.. */,
|
||||
Vector.empty)
|
||||
}
|
||||
|
||||
project[ExternalIvyConfiguration, ExternalIvyHL](externalIvyToHL, hlToExternalIvy)
|
||||
}
|
||||
|
||||
// Redefine to switch to unionFormat
|
||||
override implicit lazy val IvyConfigurationFormat: JsonFormat[IvyConfiguration] =
|
||||
unionFormat2[IvyConfiguration, InlineIvyConfiguration, ExternalIvyConfiguration]
|
||||
|
||||
def forHNil[A <: HNil]: Equiv[A] = (_: A, _: A) => true
|
||||
implicit val lnilEquiv1: Equiv[HNil] = forHNil[HNil]
|
||||
implicit val lnilEquiv2: Equiv[HNil.type] = forHNil[HNil.type]
|
||||
|
||||
implicit def hconsEquiv[H, T <: HList](implicit he: Equiv[H], te: Equiv[T]): Equiv[H :+: T] =
|
||||
(x: H :+: T, y: H :+: T) => he.equiv(x.head, y.head) && te.equiv(x.tail, y.tail)
|
||||
|
||||
implicit object altIvyConfigurationEquiv extends Equiv[IvyConfiguration] {
|
||||
def equiv(x: IvyConfiguration, y: IvyConfiguration): Boolean = (x, y) match {
|
||||
case (x: InlineIvyConfiguration, y: InlineIvyConfiguration) =>
|
||||
implicitly[Equiv[InlineIvyHL]].equiv(inlineIvyToHL(x), inlineIvyToHL(y))
|
||||
case (x: ExternalIvyConfiguration, y: ExternalIvyConfiguration) =>
|
||||
implicitly[Equiv[ExternalIvyHL]].equiv(externalIvyToHL(x), externalIvyToHL(y))
|
||||
case (x: Any, y: Any) => sys error s"Trying to compare ${x.getClass} with ${y.getClass}"
|
||||
}
|
||||
}
|
||||
|
||||
implicit object altInSingletonCache extends SingletonCache[In] {
|
||||
def write(to: Output, value: In) = to.write(value)
|
||||
def read(from: Input) = from.read[In]()
|
||||
def equiv = hconsEquiv(altIvyConfigurationEquiv, implicitly[Equiv[In0]])
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package sbt.internal
|
||||
|
||||
import java.io.File
|
||||
|
||||
import sbt.internal.librarymanagement._
|
||||
import sbt.internal.util.HNil
|
||||
import sbt.internal.util.Types._
|
||||
import sbt.librarymanagement._
|
||||
import sbt.librarymanagement.syntax._
|
||||
import sbt.util.CacheImplicits._
|
||||
import sbt.util.{ CacheStore, CacheStoreFactory, Logger, Tracked }
|
||||
|
||||
object LibraryManagement {
|
||||
|
||||
private type UpdateInputs = IvyConfiguration :+: ModuleSettings :+: UpdateConfiguration :+: HNil
|
||||
|
||||
private[sbt] def cachedUpdate(
|
||||
cacheStoreFactory: CacheStoreFactory,
|
||||
label: String,
|
||||
module: IvySbt#Module,
|
||||
updateConfig: UpdateConfiguration,
|
||||
transform: UpdateReport => UpdateReport,
|
||||
skip: Boolean,
|
||||
force: Boolean,
|
||||
depsUpdated: Boolean,
|
||||
uwConfig: UnresolvedWarningConfiguration,
|
||||
logicalClock: LogicalClock,
|
||||
depDir: Option[File],
|
||||
ewo: EvictionWarningOptions,
|
||||
mavenStyle: Boolean,
|
||||
compatWarning: CompatibilityWarningOptions,
|
||||
log: Logger
|
||||
): UpdateReport = {
|
||||
|
||||
/* Resolve the module settings from the inputs. */
|
||||
def resolve(inputs: UpdateInputs): UpdateReport = {
|
||||
import sbt.util.ShowLines._
|
||||
|
||||
log.info(s"Updating $label...")
|
||||
val reportOrUnresolved: Either[UnresolvedWarning, UpdateReport] =
|
||||
IvyActions.updateEither(module, updateConfig, uwConfig, logicalClock, depDir, log)
|
||||
|
||||
val report = reportOrUnresolved match {
|
||||
case Right(report0) => report0
|
||||
case Left(unresolvedWarning) =>
|
||||
unresolvedWarning.lines.foreach(log.warn(_))
|
||||
throw unresolvedWarning.resolveException
|
||||
}
|
||||
log.info("Done updating.")
|
||||
val finalReport = transform(report)
|
||||
|
||||
// Warn of any eviction and compatibility warnings
|
||||
val ew = EvictionWarning(module, ewo, finalReport, log)
|
||||
ew.lines.foreach(log.warn(_))
|
||||
ew.infoAllTheThings.foreach(log.info(_))
|
||||
CompatibilityWarning.run(compatWarning, module, mavenStyle, log)
|
||||
|
||||
finalReport
|
||||
}
|
||||
|
||||
/* Check if a update report is still up to date or we must resolve again. */
|
||||
def upToDate(inChanged: Boolean, out: UpdateReport): Boolean = {
|
||||
!force &&
|
||||
!depsUpdated &&
|
||||
!inChanged &&
|
||||
out.allFiles.forall(f => fileUptodate(f, out.stamps)) &&
|
||||
fileUptodate(out.cachedDescriptor, out.stamps)
|
||||
}
|
||||
|
||||
/* Skip resolve if last output exists, otherwise error. */
|
||||
def skipResolve(cache: CacheStore): UpdateInputs => UpdateReport = {
|
||||
import sbt.librarymanagement.LibraryManagementCodec._
|
||||
Tracked.lastOutput[UpdateInputs, UpdateReport](cache) {
|
||||
case (_, Some(out)) => out
|
||||
case _ =>
|
||||
sys.error("Skipping update requested, but update has not previously run successfully.")
|
||||
}
|
||||
}
|
||||
|
||||
def doResolve(cache: CacheStore): UpdateInputs => UpdateReport = {
|
||||
val doCachedResolve = { (inChanged: Boolean, updateInputs: UpdateInputs) =>
|
||||
import sbt.librarymanagement.LibraryManagementCodec._
|
||||
val cachedResolve = Tracked.lastOutput[UpdateInputs, UpdateReport](cache) {
|
||||
case (_, Some(out)) if upToDate(inChanged, out) => out
|
||||
case _ => resolve(updateInputs)
|
||||
}
|
||||
import scala.util.control.Exception.catching
|
||||
catching(classOf[NullPointerException], classOf[OutOfMemoryError])
|
||||
.withApply { t =>
|
||||
val resolvedAgain = resolve(updateInputs)
|
||||
val culprit = t.getClass.getSimpleName
|
||||
log.warn(s"Update task caching failed due to $culprit.")
|
||||
log.warn("Report the following output to sbt:")
|
||||
resolvedAgain.toString.lines.foreach(log.warn(_))
|
||||
log.trace(t)
|
||||
resolvedAgain
|
||||
}
|
||||
.apply(cachedResolve(updateInputs))
|
||||
}
|
||||
import AltLibraryManagementCodec._
|
||||
Tracked.inputChanged(cacheStoreFactory.make("inputs"))(doCachedResolve)
|
||||
}
|
||||
|
||||
// Get the handler to use and feed it in the inputs
|
||||
val ivyConfig = module.owner.configuration
|
||||
val settings = module.moduleSettings
|
||||
val outStore = cacheStoreFactory.make("output")
|
||||
val handler = if (skip && !force) skipResolve(outStore) else doResolve(outStore)
|
||||
handler(ivyConfig :+: settings :+: updateConfig :+: HNil)
|
||||
}
|
||||
|
||||
private[this] def fileUptodate(file: File, stamps: Map[File, Long]): Boolean =
|
||||
stamps.get(file).forall(_ == file.lastModified)
|
||||
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ TaskKey[Unit]("check") := {
|
|||
type In = IvyConfiguration :+: ModuleSettings :+: UpdateConfiguration :+: HNil
|
||||
|
||||
import sbt.util.CacheImplicits._
|
||||
import sbt.Classpaths.AltLibraryManagementCodec._
|
||||
import sbt.internal.AltLibraryManagementCodec._
|
||||
|
||||
val f: In => Unit =
|
||||
Tracked.inputChanged(cacheStoreFactory make "inputs") { (inChanged: Boolean, in: In) =>
|
||||
|
|
|
|||
Loading…
Reference in New Issue