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(
name := "Ivy",
libraryDependencies ++= Seq(ivy, jsch, json4sNative, jawnParser, jawnJson4s),
libraryDependencies ++= Seq(ivy, jsch, sbtSerialization),
testExclusive)
// Runner for uniform test interface

View File

@ -5,6 +5,7 @@ package sbt
import java.io.File
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]) {
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 classified(name: String, classifier: String): Artifact =
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
import sbt.serialization._
object Configurations {
def config(name: String) = new Configuration(name)
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)
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
import cross.CrossVersionUtil
import sbt.serialization._
final case class ScalaVersion(full: String, binary: String)
@ -35,6 +36,42 @@ object CrossVersion {
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). */
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 org.apache.ivy.plugins.resolver.{ DependencyResolver, IBiblioResolver }
import org.apache.ivy.util.url.CredentialsStore
import sbt.serialization._
/** 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()) {
@ -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. */
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)
object ModuleConfiguration {

View File

@ -2,22 +2,17 @@ package sbt
import java.io.File
import java.net.URL
import org.json4s._
import org.apache.ivy.core
import core.module.descriptor.ModuleDescriptor
import sbt.serialization._
private[sbt] object JsonUtil {
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 {
val json = jawn.support.json4s.Parser.parseFromFile(path)
fromLite(json.get.extract[UpdateReportLite], cachedDescriptor)
val s = IO.read(path, IO.utf8)
val lite = fromJsonString[UpdateReportLite](s).get
fromLite(lite, cachedDescriptor)
} catch {
case e: Throwable =>
log.error("Unable to parse mini graph: " + path.toString)
@ -26,12 +21,7 @@ private[sbt] object JsonUtil {
}
def writeUpdateReport(ur: UpdateReport, graphPath: File): Unit =
{
implicit val formats = native.Serialization.formats(NoTypeHints) +
new ConfigurationSerializer +
new ArtifactSerializer +
new FileSerializer
import native.Serialization.write
val str = write(toLite(ur))
val str = toJsonString(toLite(ur))
IO.write(graphPath, str, IO.utf8)
}
def toLite(ur: UpdateReport): UpdateReportLite =
@ -60,61 +50,11 @@ private[sbt] object JsonUtil {
}
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] class URLSerializer extends CustomSerializer[URL](format => (
{
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)
}
))
private[sbt] object ConfigurationReportLite {
implicit val pickler: Pickler[ConfigurationReportLite] with Unpickler[ConfigurationReportLite] = PicklerUnpickler.generate[ConfigurationReportLite]
}

View File

@ -6,6 +6,7 @@ package sbt
import java.net.URL
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) {
override def toString: String =
@ -119,6 +120,8 @@ final case class ModuleID(organization: String, name: String, revision: String,
def jar() = artifacts(Artifact(name))
}
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. */
def checkE(attributes: Seq[(String, String)]) =
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.net.URL
import java.{ util => ju }
/**
* 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)
}
import sbt.serialization._
/**
* Provides information about resolution of a single configuration.
@ -71,6 +37,9 @@ final class 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)
}
object ConfigurationReport {
implicit val pickler: Pickler[ConfigurationReport] with Unpickler[ConfigurationReport] = PicklerUnpickler.generate[ConfigurationReport]
}
/**
* OrganizationArtifactReport represents an organization+name entry in Ivy resolution report.
@ -93,6 +62,8 @@ final class OrganizationArtifactReport private[sbt] (
}
}
object OrganizationArtifactReport {
implicit val pickler: Pickler[OrganizationArtifactReport] with Unpickler[OrganizationArtifactReport] = PicklerUnpickler.generate[OrganizationArtifactReport]
def apply(organization: String, name: String, modules: Seq[ModuleReport]): OrganizationArtifactReport =
new OrganizationArtifactReport(organization, name, modules)
}
@ -188,6 +159,14 @@ object ModuleReport {
def apply(module: ModuleID, artifacts: Seq[(Artifact, File)], missingArtifacts: Seq[Artifact]): ModuleReport =
new ModuleReport(module, artifacts, missingArtifacts, None, None, None, None,
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(
@ -201,6 +180,44 @@ final class Caller(
override def toString: String =
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 {
implicit def richUpdateReport(report: UpdateReport): RichUpdateReport = new RichUpdateReport(report)
@ -271,7 +288,61 @@ object UpdateReport {
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) {
override def toString = Seq("Resolve time: " + resolveTime + " ms", "Download time: " + downloadTime + " ms", "Download size: " + downloadSize + " bytes").mkString(", ")
private[sbt] def withCached(c: Boolean): UpdateStats =
@ -279,4 +350,7 @@ final class UpdateStats(val resolveTime: Long, val downloadTime: Long, val downl
downloadTime = this.downloadTime,
downloadSize = this.downloadSize,
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 jsch = "com.jcraft" % "jsch" % "0.1.46" intransitive ()
lazy val sbinary = "org.scala-tools.sbinary" %% "sbinary" % "0.4.2"
lazy val json4sNative = "org.json4s" %% "json4s-native" % "3.2.10"
lazy val jawnParser = "org.spire-math" %% "jawn-parser" % "0.6.0"
lazy val jawnJson4s = "org.spire-math" %% "json4s-support" % "0.6.0"
lazy val sbtSerialization = "org.scala-sbt" %% "serialization" % "0.1.0"
lazy val scalaCompiler = Def.setting { "org.scala-lang" % "scala-compiler" % scalaVersion.value }
lazy val testInterface = "org.scala-sbt" % "test-interface" % "1.0"
lazy val scalaCheck = "org.scalacheck" %% "scalacheck" % "1.11.4"