Output better `sbt.ModuleReport`s

This commit is contained in:
Alexandre Archambault 2015-12-30 01:34:44 +01:00
parent ebd97b8340
commit d33ab9da26
8 changed files with 233 additions and 63 deletions

View File

@ -76,7 +76,10 @@ case class Project(
// Ivy-specific
// First String is configuration
publications: Seq[(String, Publication)]
publications: Seq[(String, Publication)],
// Extra infos, not used during resolution
info: Info
) {
def moduleVersion = (module, version)
@ -85,6 +88,25 @@ case class Project(
Orders.allConfigurations(configurations)
}
/** Extra project info, not used during resolution */
case class Info(
description: String,
homePage: String,
licenses: Seq[(String, Option[String])],
developers: Seq[Info.Developer],
publication: Option[Versions.DateTime]
)
object Info {
case class Developer(
id: String,
name: String,
url: String
)
val empty = Info("", "", Nil, Nil, None)
}
// Maven-specific
case class Activation(properties: Seq[(String, Option[String])])

View File

@ -106,7 +106,25 @@ object IvyXml {
publicationsOpt = publicationsNodeOpt.map(publications)
} yield
} yield {
val description = infoNode.children
.find(_.label == "description")
.map(_.textContent.trim)
.getOrElse("")
val licenses = infoNode.children
.filter(_.label == "license")
.flatMap { n =>
n.attribute("name").toOption.map { name =>
(name, n.attribute("url").toOption)
}.toSeq
}
val publicationDate = infoNode.attribute("publication")
.toOption
.flatMap(parseDateTime)
Project(
module,
version,
@ -128,7 +146,15 @@ object IvyXml {
configurations0.flatMap { case (conf, _) =>
(publicationsOpt.flatMap(_.get(conf)).getOrElse(Nil) ++ inAllConfs).map(conf -> _)
}
}
},
Info(
description,
"",
licenses,
Nil,
publicationDate
)
)
}
}

View File

@ -177,39 +177,73 @@ object Pom {
(mod.copy(attributes = Map.empty), ver) -> mod.attributes
}.toMap
} yield Project(
projModule.copy(organization = groupId),
version,
deps.map {
case (config, dep0) =>
val dep = extraAttrsMap.get(dep0.moduleVersion).fold(dep0)(attrs =>
dep0.copy(module = dep0.module.copy(attributes = attrs))
)
config -> dep
},
Map.empty,
parentModuleOpt.map((_, parentVersionOpt.getOrElse(""))),
depMgmts,
properties.toMap,
profiles,
None,
None,
Nil
)
}
} yield {
def parseDateTime(s: String): Option[Versions.DateTime] =
if (s.length == 14 && s.forall(_.isDigit))
Some(Versions.DateTime(
s.substring(0, 4).toInt,
s.substring(4, 6).toInt,
s.substring(6, 8).toInt,
s.substring(8, 10).toInt,
s.substring(10, 12).toInt,
s.substring(12, 14).toInt
))
else
None
val description = pom.children
.find(_.label == "description")
.map(_.textContent)
.getOrElse("")
val homePage = pom.children
.find(_.label == "url")
.map(_.textContent)
.getOrElse("")
val licenses = pom.children
.find(_.label == "licenses")
.toSeq
.flatMap(_.children)
.filter(_.label == "license")
.flatMap { n =>
n.attribute("name").toOption.map { name =>
(name, n.attribute("url").toOption)
}.toSeq
}
val developers = pom.children
.find(_.label == "developers")
.toSeq
.flatMap(_.children)
.filter(_.label == "developer")
.map { n =>
for {
id <- n.attribute("id")
name <- n.attribute("name")
url <- n.attribute("url")
} yield Info.Developer(id, name, url)
}
.collect {
case \/-(d) => d
}
Project(
projModule.copy(organization = groupId),
version,
deps.map {
case (config, dep0) =>
val dep = extraAttrsMap.get(dep0.moduleVersion).fold(dep0)(attrs =>
dep0.copy(module = dep0.module.copy(attributes = attrs))
)
config -> dep
},
Map.empty,
parentModuleOpt.map((_, parentVersionOpt.getOrElse(""))),
depMgmts,
properties.toMap,
profiles,
None,
None,
Nil,
Info(
description,
homePage,
licenses,
developers,
None
)
)
}
}
def versions(node: Node): String \/ Versions = {
import Scalaz._

View File

@ -37,6 +37,9 @@ package object coursier {
type Project = core.Project
val Project = core.Project
type Info = core.Info
val Info = core.Info
type Profile = core.Profile
val Profile = core.Profile

View File

@ -1,5 +1,7 @@
package coursier.util
import coursier.core.Versions
import scalaz.{\/-, -\/, \/, Scalaz}
object Xml {
@ -55,4 +57,17 @@ object Xml {
.toRightDisjunction(s"$description not found")
}
def parseDateTime(s: String): Option[Versions.DateTime] =
if (s.length == 14 && s.forall(_.isDigit))
Some(Versions.DateTime(
s.substring(0, 4).toInt,
s.substring(4, 6).toInt,
s.substring(6, 8).toInt,
s.substring(8, 10).toInt,
s.substring(10, 12).toInt,
s.substring(12, 14).toInt
))
else
None
}

View File

@ -101,7 +101,8 @@ object FromSbt {
Nil,
None,
None,
Nil
Nil,
Info.empty
)
}

View File

@ -1,5 +1,7 @@
package coursier
import java.util.GregorianCalendar
import sbt._
object ToSbt {
@ -24,33 +26,61 @@ object ToSbt {
Map.empty
)
def moduleReport(dependency: Dependency, artifacts: Seq[(Artifact, Option[File])]): sbt.ModuleReport =
def moduleReport(
dependency: Dependency,
dependees: Seq[(Dependency, Project)],
project: Project,
artifacts: Seq[(Artifact, Option[File])]
): sbt.ModuleReport = {
val sbtArtifacts = artifacts.collect {
case (artifact, Some(file)) =>
(ToSbt.artifact(dependency.module, artifact), file)
}
val sbtMissingArtifacts = artifacts.collect {
case (artifact, None) =>
ToSbt.artifact(dependency.module, artifact)
}
val publicationDate = project.info.publication.map { dt =>
new GregorianCalendar(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second).getTime
}
val callers = dependees.map {
case (dependee, dependeeProj) =>
new Caller(
ToSbt.moduleId(dependee),
dependeeProj.configurations.keys.toVector,
dependee.module.attributes ++ dependeeProj.properties,
// FIXME Set better values here
isForceDependency = false,
isChangingDependency = false,
isTransitiveDependency = false,
isDirectlyForceDependency = false
)
}
new sbt.ModuleReport(
ToSbt.moduleId(dependency),
artifacts.collect {
case (artifact, Some(file)) =>
(ToSbt.artifact(dependency.module, artifact), file)
},
artifacts.collect {
case (artifact, None) =>
ToSbt.artifact(dependency.module, artifact)
},
None,
None,
None,
None,
false,
None,
None,
None,
None,
Map.empty,
None,
None,
Nil,
Nil,
Nil
module = ToSbt.moduleId(dependency),
artifacts = sbtArtifacts,
missingArtifacts = sbtMissingArtifacts,
status = None,
publicationDate = publicationDate,
resolver = None,
artifactResolver = None,
evicted = false,
evictedData = None,
evictedReason = None,
problem = None,
homepage = Some(project.info.homePage).filter(_.nonEmpty),
extraAttributes = dependency.module.attributes ++ project.properties,
isDefault = None,
branch = None,
configurations = project.configurations.keys.toVector,
licenses = project.info.licenses,
callers = callers
)
}
private def grouped[K, V](map: Seq[(K, V)]): Map[K, Seq[V]] =
map.groupBy { case (k, _) => k }.map {
@ -71,9 +101,47 @@ object ToSbt {
val groupedDepArtifacts = grouped(depArtifacts)
val versions = res.dependencies.toVector.map { dep =>
dep.module -> dep.version
}.toMap
def clean(dep: Dependency): Dependency =
dep.copy(configuration = "", exclusions = Set.empty, optional = false)
val reverseDependencies = res.reverseDependencies
.toVector
.map { case (k, v) =>
clean(k) -> v.map(clean)
}
.groupBy { case (k, v) => k }
.mapValues { v =>
v.flatMap {
case (_, l) => l
}
}
.toVector
.toMap
groupedDepArtifacts.map {
case (dep, artifacts) =>
ToSbt.moduleReport(dep, artifacts.map(a => a -> artifactFileOpt(a)))
val (_, proj) = res.projectCache(dep.moduleVersion)
// FIXME Likely flaky...
val dependees = reverseDependencies
.getOrElse(clean(dep.copy(version = "")), Vector.empty)
.map { dependee0 =>
val version = versions(dependee0.module)
val dependee = dependee0.copy(version = version)
val (_, dependeeProj) = res.projectCache(dependee.moduleVersion)
(dependee, dependeeProj)
}
ToSbt.moduleReport(
dep,
dependees,
proj,
artifacts.map(a => a -> artifactFileOpt(a))
)
}
}

View File

@ -56,7 +56,8 @@ package object test {
profiles,
versions,
snapshotVersioning,
publications
publications,
Info.empty
)
}
}