Implement pickler for UpdateReport. #1763

This commit is contained in:
Eugene Yokota 2015-03-04 04:31:31 -05:00
parent 45344d751b
commit ef1ec99bd0
13 changed files with 411 additions and 112 deletions

View File

@ -262,7 +262,7 @@ lazy val ivyProj = (project in file("ivy")).
settings(baseSettings: _*). settings(baseSettings: _*).
settings( settings(
name := "Ivy", name := "Ivy",
libraryDependencies ++= Seq(ivy, jsch, json4sNative, jawnParser, jawnJson4s), libraryDependencies ++= Seq(ivy, jsch, sbtSerialization),
testExclusive) testExclusive)
// Runner for uniform test interface // Runner for uniform test interface

View File

@ -5,6 +5,7 @@ package sbt
import java.io.File import java.io.File
import java.net.URL import java.net.URL
import sbt.serialization._
final case class Artifact(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Iterable[Configuration], url: Option[URL], extraAttributes: Map[String, String]) { final case class Artifact(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Iterable[Configuration], url: Option[URL], extraAttributes: Map[String, String]) {
def extra(attributes: (String, String)*) = Artifact(name, `type`, extension, classifier, configurations, url, extraAttributes ++ ModuleID.checkE(attributes)) def extra(attributes: (String, String)*) = Artifact(name, `type`, extension, classifier, configurations, url, extraAttributes ++ ModuleID.checkE(attributes))
@ -71,4 +72,73 @@ object Artifact {
def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier.stripPrefix(TestsClassifier + "-"), DefaultType) def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier.stripPrefix(TestsClassifier + "-"), DefaultType)
def classified(name: String, classifier: String): Artifact = def classified(name: String, classifier: String): Artifact =
Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), classifierConf(classifier) :: Nil, None) Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), classifierConf(classifier) :: Nil, None)
private val optStringPickler = implicitly[Pickler[Option[String]]]
private val optStringUnpickler = implicitly[Unpickler[Option[String]]]
private val vectorConfigurationPickler = implicitly[Pickler[Vector[Configuration]]]
private val vectorConfigurationUnpickler = implicitly[Unpickler[Vector[Configuration]]]
private val stringStringMapPickler = implicitly[Pickler[Map[String, String]]]
private val stringStringMapUnpickler = implicitly[Unpickler[Map[String, String]]]
implicit val pickler: Pickler[Artifact] = new Pickler[Artifact] {
val tag = implicitly[FastTypeTag[Artifact]]
val stringTag = implicitly[FastTypeTag[String]]
val optionStringTag = implicitly[FastTypeTag[Option[String]]]
val vectorConfigurationTag = implicitly[FastTypeTag[Vector[Configuration]]]
val stringStringMapTag = implicitly[FastTypeTag[Map[String, String]]]
def pickle(a: Artifact, builder: PBuilder): Unit = {
builder.pushHints()
builder.hintTag(tag)
builder.beginEntry(a)
builder.putField("name", { b =>
b.hintTag(stringTag)
stringPickler.pickle(a.name, b)
})
builder.putField("type", { b =>
b.hintTag(stringTag)
stringPickler.pickle(a.`type`, b)
})
builder.putField("extension", { b =>
b.hintTag(stringTag)
stringPickler.pickle(a.extension, b)
})
builder.putField("classifier", { b =>
b.hintTag(optionStringTag)
optStringPickler.pickle(a.classifier, b)
})
builder.putField("configurations", { b =>
b.hintTag(vectorConfigurationTag)
vectorConfigurationPickler.pickle(a.configurations.toVector, b)
})
builder.putField("url", { b =>
b.hintTag(optionStringTag)
optStringPickler.pickle(a.url map { _.toString }, b)
})
builder.putField("extraAttributes", { b =>
b.hintTag(stringStringMapTag)
stringStringMapPickler.pickle(a.extraAttributes, b)
})
builder.endEntry()
builder.popHints()
}
}
implicit val unpickler: Unpickler[Artifact] = new Unpickler[Artifact] {
val tag = implicitly[FastTypeTag[Artifact]]
def unpickle(tpe: String, reader: PReader): Any = {
reader.pushHints()
reader.hintTag(tag)
reader.beginEntry()
val name = stringPickler.unpickleEntry(reader.readField("name")).asInstanceOf[String]
val tp = stringPickler.unpickleEntry(reader.readField("type")).asInstanceOf[String]
val extension = stringPickler.unpickleEntry(reader.readField("extension")).asInstanceOf[String]
val classifier = optStringUnpickler.unpickleEntry(reader.readField("classifier")).asInstanceOf[Option[String]]
val configurations = vectorConfigurationUnpickler.unpickleEntry(reader.readField("configurations")).asInstanceOf[Vector[Configuration]]
val u = optStringUnpickler.unpickleEntry(reader.readField("url")).asInstanceOf[Option[String]] map { new URL(_) }
val extraAttributes = stringStringMapUnpickler.unpickleEntry(reader.readField("extraAttributes")).asInstanceOf[Map[String, String]]
val result = Artifact(name, tp, extension, classifier, configurations, u, extraAttributes)
reader.endEntry()
reader.popHints()
result
}
}
} }

View File

@ -3,6 +3,8 @@
*/ */
package sbt package sbt
import sbt.serialization._
object Configurations { object Configurations {
def config(name: String) = new Configuration(name) def config(name: String) = new Configuration(name)
def default: Seq[Configuration] = defaultMavenConfigurations def default: Seq[Configuration] = defaultMavenConfigurations
@ -61,3 +63,6 @@ final case class Configuration(name: String, description: String, isPublic: Bool
def hide = Configuration(name, description, false, extendsConfigs, transitive) def hide = Configuration(name, description, false, extendsConfigs, transitive)
override def toString = name override def toString = name
} }
object Configuration {
implicit val pickler: Pickler[Configuration] with Unpickler[Configuration] = PicklerUnpickler.generate[Configuration]
}

View File

@ -1,6 +1,7 @@
package sbt package sbt
import cross.CrossVersionUtil import cross.CrossVersionUtil
import sbt.serialization._
final case class ScalaVersion(full: String, binary: String) final case class ScalaVersion(full: String, binary: String)
@ -35,6 +36,42 @@ object CrossVersion {
override def toString = "Full" override def toString = "Full"
} }
private val disabledTag = implicitly[FastTypeTag[Disabled.type]]
private val binaryTag = implicitly[FastTypeTag[Binary]]
private val fullTag = implicitly[FastTypeTag[Full]]
implicit val pickler: Pickler[CrossVersion] = new Pickler[CrossVersion] {
val tag = implicitly[FastTypeTag[CrossVersion]]
def pickle(a: CrossVersion, builder: PBuilder): Unit = {
builder.pushHints()
builder.hintTag(a match {
case Disabled => disabledTag
case x: Binary => binaryTag
case x: Full => fullTag
})
builder.beginEntry(a)
builder.endEntry()
builder.popHints()
}
}
implicit val unpickler: Unpickler[CrossVersion] = new Unpickler[CrossVersion] {
val tag = implicitly[FastTypeTag[CrossVersion]]
def unpickle(tpe: String, reader: PReader): Any = {
reader.pushHints()
reader.hintTag(tag)
val tpeStr = reader.beginEntry()
val tpe = scala.pickling.FastTypeTag(tpeStr)
// sys.error(tpe.toString)
val result = tpe match {
case t if t == disabledTag => Disabled
case t if t == binaryTag => binary
case t if t == fullTag => full
}
reader.endEntry()
reader.popHints()
result
}
}
/** Cross-versions a module with the full version (typically the full Scala version). */ /** Cross-versions a module with the full version (typically the full Scala version). */
def full: CrossVersion = new Full(idFun) def full: CrossVersion = new Full(idFun)

View File

@ -0,0 +1,35 @@
package sbt
import sbt.serialization._
import java.{ util => ju }
private[sbt] object DatePicklers {
private val longTag = implicitly[FastTypeTag[Long]]
implicit val datePickler: Pickler[ju.Date] = new Pickler[ju.Date] {
val tag = implicitly[FastTypeTag[ju.Date]]
def pickle(a: ju.Date, builder: PBuilder): Unit = {
builder.pushHints()
builder.hintTag(tag)
builder.beginEntry(a)
builder.putField("value", { b =>
b.hintTag(longTag)
longPickler.pickle(a.getTime, b)
})
builder.endEntry()
builder.popHints()
}
}
implicit val dateUnpickler: Unpickler[ju.Date] = new Unpickler[ju.Date] {
val tag = implicitly[FastTypeTag[ju.Date]]
def unpickle(tpe: String, reader: PReader): Any = {
reader.pushHints()
reader.hintTag(tag)
reader.beginEntry()
val a0 = longPickler.unpickleEntry(reader.readField("value")).asInstanceOf[Long]
val result = new ju.Date(a0)
reader.endEntry()
reader.popHints()
result
}
}
}

View File

@ -0,0 +1,22 @@
package sbt
import java.io.File
import sbt.serialization._
import java.net.URI
object FileMapPicklers {
implicit def fileMapPickler[A: Pickler: Unpickler: FastTypeTag]: Pickler[Map[File, A]] with Unpickler[Map[File, A]] = new Pickler[Map[File, A]] with Unpickler[Map[File, A]] {
val tag = implicitly[FastTypeTag[Map[File, A]]]
val stringAMapPickler = implicitly[Pickler[Map[String, A]]]
val stringAMapUnpickler = implicitly[Unpickler[Map[String, A]]]
def pickle(m: Map[File, A], builder: PBuilder): Unit =
stringAMapPickler.pickle(Map(m.toSeq map { case (k, v) => (k.toURI.toASCIIString, v) }: _*), builder)
def unpickle(tpe: String, reader: PReader): Any =
Map(stringAMapUnpickler.unpickle(tpe, reader).asInstanceOf[Map[String, A]].toSeq map {
case (k, v) =>
(new File(new URI(k)), v)
}: _*).asInstanceOf[Map[File, A]]
}
}

View File

@ -8,6 +8,7 @@ import java.net.{ URI, URL }
import scala.xml.NodeSeq import scala.xml.NodeSeq
import org.apache.ivy.plugins.resolver.{ DependencyResolver, IBiblioResolver } import org.apache.ivy.plugins.resolver.{ DependencyResolver, IBiblioResolver }
import org.apache.ivy.util.url.CredentialsStore import org.apache.ivy.util.url.CredentialsStore
import sbt.serialization._
/** Additional information about a project module */ /** Additional information about a project module */
final case class ModuleInfo(nameFormal: String, description: String = "", homepage: Option[URL] = None, startYear: Option[Int] = None, licenses: Seq[(String, URL)] = Nil, organizationName: String = "", organizationHomepage: Option[URL] = None, scmInfo: Option[ScmInfo] = None, developers: Seq[Developer] = Seq()) { final case class ModuleInfo(nameFormal: String, description: String = "", homepage: Option[URL] = None, startYear: Option[Int] = None, licenses: Seq[(String, URL)] = Nil, organizationName: String = "", organizationHomepage: Option[URL] = None, scmInfo: Option[ScmInfo] = None, developers: Seq[Developer] = Seq()) {
@ -26,6 +27,9 @@ final case class Developer(id: String, name: String, email: String, url: URL)
/** Rule to exclude unwanted dependencies pulled in transitively by a module. */ /** Rule to exclude unwanted dependencies pulled in transitively by a module. */
final case class ExclusionRule(organization: String = "*", name: String = "*", artifact: String = "*", configurations: Seq[String] = Nil) final case class ExclusionRule(organization: String = "*", name: String = "*", artifact: String = "*", configurations: Seq[String] = Nil)
object ExclusionRule {
implicit val pickler: Pickler[ExclusionRule] with Unpickler[ExclusionRule] = PicklerUnpickler.generate[ExclusionRule]
}
final case class ModuleConfiguration(organization: String, name: String, revision: String, resolver: Resolver) final case class ModuleConfiguration(organization: String, name: String, revision: String, resolver: Resolver)
object ModuleConfiguration { object ModuleConfiguration {

View File

@ -2,22 +2,17 @@ package sbt
import java.io.File import java.io.File
import java.net.URL import java.net.URL
import org.json4s._
import org.apache.ivy.core import org.apache.ivy.core
import core.module.descriptor.ModuleDescriptor import core.module.descriptor.ModuleDescriptor
import sbt.serialization._
private[sbt] object JsonUtil { private[sbt] object JsonUtil {
def parseUpdateReport(md: ModuleDescriptor, path: File, cachedDescriptor: File, log: Logger): UpdateReport = def parseUpdateReport(md: ModuleDescriptor, path: File, cachedDescriptor: File, log: Logger): UpdateReport =
{ {
import org.json4s._
implicit val formats = native.Serialization.formats(NoTypeHints) +
new ConfigurationSerializer +
new ArtifactSerializer +
new FileSerializer +
new URLSerializer
try { try {
val json = jawn.support.json4s.Parser.parseFromFile(path) val s = IO.read(path, IO.utf8)
fromLite(json.get.extract[UpdateReportLite], cachedDescriptor) val lite = fromJsonString[UpdateReportLite](s).get
fromLite(lite, cachedDescriptor)
} catch { } catch {
case e: Throwable => case e: Throwable =>
log.error("Unable to parse mini graph: " + path.toString) log.error("Unable to parse mini graph: " + path.toString)
@ -26,12 +21,7 @@ private[sbt] object JsonUtil {
} }
def writeUpdateReport(ur: UpdateReport, graphPath: File): Unit = def writeUpdateReport(ur: UpdateReport, graphPath: File): Unit =
{ {
implicit val formats = native.Serialization.formats(NoTypeHints) + val str = toJsonString(toLite(ur))
new ConfigurationSerializer +
new ArtifactSerializer +
new FileSerializer
import native.Serialization.write
val str = write(toLite(ur))
IO.write(graphPath, str, IO.utf8) IO.write(graphPath, str, IO.utf8)
} }
def toLite(ur: UpdateReport): UpdateReportLite = def toLite(ur: UpdateReport): UpdateReportLite =
@ -60,61 +50,11 @@ private[sbt] object JsonUtil {
} }
private[sbt] case class UpdateReportLite(configurations: Seq[ConfigurationReportLite]) private[sbt] case class UpdateReportLite(configurations: Seq[ConfigurationReportLite])
private[sbt] object UpdateReportLite {
implicit val pickler: Pickler[UpdateReportLite] with Unpickler[UpdateReportLite] = PicklerUnpickler.generate[UpdateReportLite]
}
private[sbt] case class ConfigurationReportLite(configuration: String, details: Seq[OrganizationArtifactReport]) private[sbt] case class ConfigurationReportLite(configuration: String, details: Seq[OrganizationArtifactReport])
private[sbt] object ConfigurationReportLite {
private[sbt] class URLSerializer extends CustomSerializer[URL](format => ( implicit val pickler: Pickler[ConfigurationReportLite] with Unpickler[ConfigurationReportLite] = PicklerUnpickler.generate[ConfigurationReportLite]
{ }
case JString(s) => new URL(s)
},
{
case x: URL => JString(x.toString)
}
))
private[sbt] class FileSerializer extends CustomSerializer[File](format => (
{
case JString(s) => new File(s)
},
{
case x: File => JString(x.toString)
}
))
private[sbt] class ConfigurationSerializer extends CustomSerializer[Configuration](format => (
{
case JString(s) => new Configuration(s)
},
{
case x: Configuration => JString(x.name)
}
))
private[sbt] class ArtifactSerializer extends CustomSerializer[Artifact](format => (
{
case json: JValue =>
implicit val fmt = format
Artifact(
(json \ "name").extract[String],
(json \ "type").extract[String],
(json \ "extension").extract[String],
(json \ "classifier").extract[Option[String]],
(json \ "configurations").extract[List[Configuration]],
(json \ "url").extract[Option[URL]],
(json \ "extraAttributes").extract[Map[String, String]]
)
},
{
case x: Artifact =>
import DefaultJsonFormats.{ OptionWriter, StringWriter, mapWriter }
val optStr = implicitly[Writer[Option[String]]]
val mw = implicitly[Writer[Map[String, String]]]
JObject(JField("name", JString(x.name)) ::
JField("type", JString(x.`type`)) ::
JField("extension", JString(x.extension)) ::
JField("classifier", optStr.write(x.classifier)) ::
JField("configurations", JArray(x.configurations.toList map { x => JString(x.name) })) ::
JField("url", optStr.write(x.url map { _.toString })) ::
JField("extraAttributes", mw.write(x.extraAttributes)) ::
Nil)
}
))

View File

@ -6,6 +6,7 @@ package sbt
import java.net.URL import java.net.URL
import sbt.mavenint.SbtPomExtraProperties import sbt.mavenint.SbtPomExtraProperties
import sbt.serialization._
final case class ModuleID(organization: String, name: String, revision: String, configurations: Option[String] = None, isChanging: Boolean = false, isTransitive: Boolean = true, isForce: Boolean = false, explicitArtifacts: Seq[Artifact] = Nil, exclusions: Seq[ExclusionRule] = Nil, extraAttributes: Map[String, String] = Map.empty, crossVersion: CrossVersion = CrossVersion.Disabled) { final case class ModuleID(organization: String, name: String, revision: String, configurations: Option[String] = None, isChanging: Boolean = false, isTransitive: Boolean = true, isForce: Boolean = false, explicitArtifacts: Seq[Artifact] = Nil, exclusions: Seq[ExclusionRule] = Nil, extraAttributes: Map[String, String] = Map.empty, crossVersion: CrossVersion = CrossVersion.Disabled) {
override def toString: String = override def toString: String =
@ -119,6 +120,8 @@ final case class ModuleID(organization: String, name: String, revision: String,
def jar() = artifacts(Artifact(name)) def jar() = artifacts(Artifact(name))
} }
object ModuleID { object ModuleID {
implicit val pickler: Pickler[ModuleID] with Unpickler[ModuleID] = PicklerUnpickler.generate[ModuleID]
/** Prefixes all keys with `e:` if they are not already so prefixed. */ /** Prefixes all keys with `e:` if they are not already so prefixed. */
def checkE(attributes: Seq[(String, String)]) = def checkE(attributes: Seq[(String, String)]) =
for ((key, value) <- attributes) yield if (key.startsWith("e:")) (key, value) else ("e:" + key, value) for ((key, value) <- attributes) yield if (key.startsWith("e:")) (key, value) else ("e:" + key, value)

View File

@ -0,0 +1,44 @@
package sbt
import sbt.serialization._
private[sbt] object PairPicklers {
implicit def pairPickler[A1: FastTypeTag: Pickler: Unpickler, A2: FastTypeTag: Pickler: Unpickler](): Pickler[(A1, A2)] with Unpickler[(A1, A2)] =
new Pickler[(A1, A2)] with Unpickler[(A1, A2)] {
val a1Tag = implicitly[FastTypeTag[A1]]
val a2Tag = implicitly[FastTypeTag[A2]]
val tag = implicitly[FastTypeTag[(A1, A2)]]
val a1Pickler = implicitly[Pickler[A1]]
val a1Unpickler = implicitly[Unpickler[A1]]
val a2Pickler = implicitly[Pickler[A2]]
val a2Unpickler = implicitly[Unpickler[A2]]
def pickle(a: (A1, A2), builder: PBuilder): Unit = {
builder.pushHints()
builder.hintTag(tag)
builder.beginEntry(a)
builder.putField("_1", { b =>
b.hintTag(a1Tag)
a1Pickler.pickle(a._1, b)
})
builder.putField("_2", { b =>
b.hintTag(a2Tag)
a2Pickler.pickle(a._2, b)
})
builder.endEntry()
builder.popHints()
}
def unpickle(tpe: String, reader: PReader): Any = {
reader.pushHints()
reader.hintTag(tag)
reader.beginEntry()
val a0 = a1Unpickler.unpickleEntry(reader.readField("_1")).asInstanceOf[A1]
val a1 = a2Unpickler.unpickleEntry(reader.readField("_2")).asInstanceOf[A2]
val result = (a0, a1)
reader.endEntry()
reader.popHints()
result
}
}
}

View File

@ -6,41 +6,7 @@ package sbt
import java.io.File import java.io.File
import java.net.URL import java.net.URL
import java.{ util => ju } import java.{ util => ju }
import sbt.serialization._
/**
* Provides information about dependency resolution.
* It does not include information about evicted modules, only about the modules ultimately selected by the conflict manager.
* This means that for a given configuration, there should only be one revision for a given organization and module name.
* @param cachedDescriptor the location of the resolved module descriptor in the cache
* @param configurations a sequence containing one report for each configuration resolved.
* @param stats information about the update that produced this report
* @see sbt.RichUpdateReport
*/
final class UpdateReport(val cachedDescriptor: File, val configurations: Seq[ConfigurationReport], val stats: UpdateStats, private[sbt] val stamps: Map[File, Long]) {
@deprecated("Use the variant that provides timestamps of files.", "0.13.0")
def this(cachedDescriptor: File, configurations: Seq[ConfigurationReport], stats: UpdateStats) =
this(cachedDescriptor, configurations, stats, Map.empty)
override def toString = "Update report:\n\t" + stats + "\n" + configurations.mkString
/** All resolved modules in all configurations. */
def allModules: Seq[ModuleID] = configurations.flatMap(_.allModules).distinct
def retrieve(f: (String, ModuleID, Artifact, File) => File): UpdateReport =
new UpdateReport(cachedDescriptor, configurations map { _ retrieve f }, stats, stamps)
/** Gets the report for the given configuration, or `None` if the configuration was not resolved.*/
def configuration(s: String) = configurations.find(_.configuration == s)
/** Gets the names of all resolved configurations. This `UpdateReport` contains one `ConfigurationReport` for each configuration in this list. */
def allConfigurations: Seq[String] = configurations.map(_.configuration)
private[sbt] def withStats(us: UpdateStats): UpdateReport =
new UpdateReport(this.cachedDescriptor,
this.configurations,
us,
this.stamps)
}
/** /**
* Provides information about resolution of a single configuration. * Provides information about resolution of a single configuration.
@ -71,6 +37,9 @@ final class ConfigurationReport(
def retrieve(f: (String, ModuleID, Artifact, File) => File): ConfigurationReport = def retrieve(f: (String, ModuleID, Artifact, File) => File): ConfigurationReport =
new ConfigurationReport(configuration, modules map { _.retrieve((mid, art, file) => f(configuration, mid, art, file)) }, details, evicted) new ConfigurationReport(configuration, modules map { _.retrieve((mid, art, file) => f(configuration, mid, art, file)) }, details, evicted)
} }
object ConfigurationReport {
implicit val pickler: Pickler[ConfigurationReport] with Unpickler[ConfigurationReport] = PicklerUnpickler.generate[ConfigurationReport]
}
/** /**
* OrganizationArtifactReport represents an organization+name entry in Ivy resolution report. * OrganizationArtifactReport represents an organization+name entry in Ivy resolution report.
@ -93,6 +62,8 @@ final class OrganizationArtifactReport private[sbt] (
} }
} }
object OrganizationArtifactReport { object OrganizationArtifactReport {
implicit val pickler: Pickler[OrganizationArtifactReport] with Unpickler[OrganizationArtifactReport] = PicklerUnpickler.generate[OrganizationArtifactReport]
def apply(organization: String, name: String, modules: Seq[ModuleReport]): OrganizationArtifactReport = def apply(organization: String, name: String, modules: Seq[ModuleReport]): OrganizationArtifactReport =
new OrganizationArtifactReport(organization, name, modules) new OrganizationArtifactReport(organization, name, modules)
} }
@ -188,6 +159,14 @@ object ModuleReport {
def apply(module: ModuleID, artifacts: Seq[(Artifact, File)], missingArtifacts: Seq[Artifact]): ModuleReport = def apply(module: ModuleID, artifacts: Seq[(Artifact, File)], missingArtifacts: Seq[Artifact]): ModuleReport =
new ModuleReport(module, artifacts, missingArtifacts, None, None, None, None, new ModuleReport(module, artifacts, missingArtifacts, None, None, None, None,
false, None, None, None, None, Map(), None, None, Nil, Nil, Nil) false, None, None, None, None, Map(), None, None, Nil, Nil, Nil)
import PairPicklers._
import DatePicklers._
private implicit val afPairPickler: Pickler[(Artifact, File)] with Unpickler[(Artifact, File)] =
pairPickler[Artifact, File]
private implicit def tuplePickler2: Pickler[(String, Option[String])] with Unpickler[(String, Option[String])] =
pairPickler[String, Option[String]]
implicit val pickler: Pickler[ModuleReport] with Unpickler[ModuleReport] = PicklerUnpickler.generate[ModuleReport]
} }
final class Caller( final class Caller(
@ -201,6 +180,44 @@ final class Caller(
override def toString: String = override def toString: String =
s"$caller" s"$caller"
} }
object Caller {
implicit val pickler: Pickler[Caller] with Unpickler[Caller] = PicklerUnpickler.generate[Caller]
}
/**
* Provides information about dependency resolution.
* It does not include information about evicted modules, only about the modules ultimately selected by the conflict manager.
* This means that for a given configuration, there should only be one revision for a given organization and module name.
* @param cachedDescriptor the location of the resolved module descriptor in the cache
* @param configurations a sequence containing one report for each configuration resolved.
* @param stats information about the update that produced this report
* @see sbt.RichUpdateReport
*/
final class UpdateReport(val cachedDescriptor: File, val configurations: Seq[ConfigurationReport], val stats: UpdateStats, private[sbt] val stamps: Map[File, Long]) {
@deprecated("Use the variant that provides timestamps of files.", "0.13.0")
def this(cachedDescriptor: File, configurations: Seq[ConfigurationReport], stats: UpdateStats) =
this(cachedDescriptor, configurations, stats, Map.empty)
override def toString = "Update report:\n\t" + stats + "\n" + configurations.mkString
/** All resolved modules in all configurations. */
def allModules: Seq[ModuleID] = configurations.flatMap(_.allModules).distinct
def retrieve(f: (String, ModuleID, Artifact, File) => File): UpdateReport =
new UpdateReport(cachedDescriptor, configurations map { _ retrieve f }, stats, stamps)
/** Gets the report for the given configuration, or `None` if the configuration was not resolved.*/
def configuration(s: String) = configurations.find(_.configuration == s)
/** Gets the names of all resolved configurations. This `UpdateReport` contains one `ConfigurationReport` for each configuration in this list. */
def allConfigurations: Seq[String] = configurations.map(_.configuration)
private[sbt] def withStats(us: UpdateStats): UpdateReport =
new UpdateReport(this.cachedDescriptor,
this.configurations,
us,
this.stamps)
}
object UpdateReport { object UpdateReport {
implicit def richUpdateReport(report: UpdateReport): RichUpdateReport = new RichUpdateReport(report) implicit def richUpdateReport(report: UpdateReport): RichUpdateReport = new RichUpdateReport(report)
@ -271,7 +288,61 @@ object UpdateReport {
new UpdateReport(report.cachedDescriptor, newConfigurations, report.stats, report.stamps) new UpdateReport(report.cachedDescriptor, newConfigurations, report.stats, report.stamps)
} }
} }
import FileMapPicklers._
private val vectorConfigurationReportPickler = implicitly[Pickler[Vector[ConfigurationReport]]]
private val vectorConfigurationReportUnpickler = implicitly[Unpickler[Vector[ConfigurationReport]]]
private val updateStatsPickler = implicitly[Pickler[UpdateStats]]
private val updateStatsUnpickler = implicitly[Unpickler[UpdateStats]]
private val flMapPickler = implicitly[Pickler[Map[File, Long]]]
private val flMapUnpickler = implicitly[Unpickler[Map[File, Long]]]
implicit val pickler: Pickler[UpdateReport] with Unpickler[UpdateReport] = new Pickler[UpdateReport] with Unpickler[UpdateReport] {
val tag = implicitly[FastTypeTag[UpdateReport]]
val fileTag = implicitly[FastTypeTag[File]]
val vectorConfigurationReportTag = implicitly[FastTypeTag[Vector[ConfigurationReport]]]
val updateStatsTag = implicitly[FastTypeTag[UpdateStats]]
val flMapTag = implicitly[FastTypeTag[Map[File, Long]]]
def pickle(a: UpdateReport, builder: PBuilder): Unit = {
builder.pushHints()
builder.hintTag(tag)
builder.beginEntry(a)
builder.putField("cachedDescriptor", { b =>
b.hintTag(fileTag)
filePickler.pickle(a.cachedDescriptor, b)
})
builder.putField("configurations", { b =>
b.hintTag(vectorConfigurationReportTag)
vectorConfigurationReportPickler.pickle(a.configurations.toVector, b)
})
builder.putField("stats", { b =>
b.hintTag(updateStatsTag)
updateStatsPickler.pickle(a.stats, b)
})
builder.putField("stamps", { b =>
b.hintTag(flMapTag)
flMapPickler.pickle(a.stamps, b)
})
builder.endEntry()
builder.popHints()
}
def unpickle(tpe: String, reader: PReader): Any = {
reader.pushHints()
reader.hintTag(tag)
reader.beginEntry()
val cachedDescriptor = filePickler.unpickleEntry(reader.readField("cachedDescriptor")).asInstanceOf[File]
val configurations = vectorConfigurationReportUnpickler.unpickleEntry(reader.readField("configurations")).asInstanceOf[Vector[ConfigurationReport]]
val stats = updateStatsUnpickler.unpickleEntry(reader.readField("stats")).asInstanceOf[UpdateStats]
val stamps = flMapUnpickler.unpickleEntry(reader.readField("stamps")).asInstanceOf[Map[File, Long]]
val result = new UpdateReport(cachedDescriptor, configurations, stats, stamps)
reader.endEntry()
reader.popHints()
result
}
}
} }
final class UpdateStats(val resolveTime: Long, val downloadTime: Long, val downloadSize: Long, val cached: Boolean) { final class UpdateStats(val resolveTime: Long, val downloadTime: Long, val downloadSize: Long, val cached: Boolean) {
override def toString = Seq("Resolve time: " + resolveTime + " ms", "Download time: " + downloadTime + " ms", "Download size: " + downloadSize + " bytes").mkString(", ") override def toString = Seq("Resolve time: " + resolveTime + " ms", "Download time: " + downloadTime + " ms", "Download size: " + downloadSize + " bytes").mkString(", ")
private[sbt] def withCached(c: Boolean): UpdateStats = private[sbt] def withCached(c: Boolean): UpdateStats =
@ -280,3 +351,6 @@ final class UpdateStats(val resolveTime: Long, val downloadTime: Long, val downl
downloadSize = this.downloadSize, downloadSize = this.downloadSize,
cached = c) cached = c)
} }
object UpdateStats {
implicit val pickler: Pickler[UpdateStats] with Unpickler[UpdateStats] = PicklerUnpickler.generate[UpdateStats]
}

View File

@ -0,0 +1,67 @@
package sbt
import org.specs2._
import matcher.MatchResult
import java.net.URL
import java.io.File
import sbt.serialization._
class DMSerializationSpec extends Specification {
def is = sequential ^ s2"""
This is a specification to check the serialization of dependency graph.
CrossVersion.full should
${roundtripStr(CrossVersion.full: sbt.CrossVersion)}
CrossVersion.binary should
${roundtripStr(CrossVersion.binary: sbt.CrossVersion)}
CrossVersion.Disabled should
${roundtrip(CrossVersion.Disabled: sbt.CrossVersion)}
Artifact("foo") should
${roundtrip(Artifact("foo"))}
Artifact("foo", "sources") should
${roundtrip(Artifact("foo", "sources"))}
Artifact.pom("foo") should
${roundtrip(Artifact.pom("foo"))}
Artifact("foo", url("http://example.com/")) should
${roundtrip(Artifact("foo", new URL("http://example.com/")))}
Artifact("foo").extra(("key", "value")) should
${roundtrip(Artifact("foo").extra(("key", "value")))}
ModuleID("org", "name", "1.0") should
${roundtrip(ModuleID("org", "name", "1.0"))}
ModuleReport(ModuleID("org", "name", "1.0"), Nil, Nil) should
${roundtripStr(ModuleReport(ModuleID("org", "name", "1.0"), Nil, Nil))}
Organization artifact report should
${roundtripStr(organizationArtifactReportExample)}
Configuration report should
${roundtripStr(configurationReportExample)}
Update report should
${roundtripStr(updateReportExample)}
"""
lazy val updateReportExample =
new UpdateReport(new File("./foo"), Vector(configurationReportExample),
new UpdateStats(0, 0, 0, false), Map(new File("./foo") -> 0))
lazy val configurationReportExample =
new ConfigurationReport("compile", Vector(moduleReportExample),
Vector(organizationArtifactReportExample), Nil)
lazy val organizationArtifactReportExample =
new OrganizationArtifactReport("org", "name", Vector(moduleReportExample))
lazy val moduleReportExample =
ModuleReport(ModuleID("org", "name", "1.0"), Nil, Nil)
def roundtrip[A: Pickler: Unpickler](a: A) =
roundtripBuilder(a) { _ must_== _ }
def roundtripStr[A: Pickler: Unpickler](a: A) =
roundtripBuilder(a) { _.toString must_== _.toString }
def roundtripBuilder[A: Pickler: Unpickler](a: A)(f: (A, A) => MatchResult[Any]): MatchResult[Any] =
{
val json = toJsonString(a)
println(json)
val obj = fromJsonString[A](json).get
f(a, obj)
}
}

View File

@ -12,9 +12,7 @@ object Dependencies {
lazy val ivy = "org.scala-sbt.ivy" % "ivy" % "2.3.0-sbt-fccfbd44c9f64523b61398a0155784dcbaeae28f" lazy val ivy = "org.scala-sbt.ivy" % "ivy" % "2.3.0-sbt-fccfbd44c9f64523b61398a0155784dcbaeae28f"
lazy val jsch = "com.jcraft" % "jsch" % "0.1.46" intransitive () lazy val jsch = "com.jcraft" % "jsch" % "0.1.46" intransitive ()
lazy val sbinary = "org.scala-tools.sbinary" %% "sbinary" % "0.4.2" lazy val sbinary = "org.scala-tools.sbinary" %% "sbinary" % "0.4.2"
lazy val json4sNative = "org.json4s" %% "json4s-native" % "3.2.10" lazy val sbtSerialization = "org.scala-sbt" %% "serialization" % "0.1.0"
lazy val jawnParser = "org.spire-math" %% "jawn-parser" % "0.6.0"
lazy val jawnJson4s = "org.spire-math" %% "json4s-support" % "0.6.0"
lazy val scalaCompiler = Def.setting { "org.scala-lang" % "scala-compiler" % scalaVersion.value } lazy val scalaCompiler = Def.setting { "org.scala-lang" % "scala-compiler" % scalaVersion.value }
lazy val testInterface = "org.scala-sbt" % "test-interface" % "1.0" lazy val testInterface = "org.scala-sbt" % "test-interface" % "1.0"
lazy val scalaCheck = "org.scalacheck" %% "scalacheck" % "1.11.4" lazy val scalaCheck = "org.scalacheck" %% "scalacheck" % "1.11.4"