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 // Ivy-specific
// First String is configuration // First String is configuration
publications: Seq[(String, Publication)] publications: Seq[(String, Publication)],
// Extra infos, not used during resolution
info: Info
) { ) {
def moduleVersion = (module, version) def moduleVersion = (module, version)
@ -85,6 +88,25 @@ case class Project(
Orders.allConfigurations(configurations) 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 // Maven-specific
case class Activation(properties: Seq[(String, Option[String])]) case class Activation(properties: Seq[(String, Option[String])])

View File

@ -106,7 +106,25 @@ object IvyXml {
publicationsOpt = publicationsNodeOpt.map(publications) 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( Project(
module, module,
version, version,
@ -128,7 +146,15 @@ object IvyXml {
configurations0.flatMap { case (conf, _) => configurations0.flatMap { case (conf, _) =>
(publicationsOpt.flatMap(_.get(conf)).getOrElse(Nil) ++ inAllConfs).map(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 (mod.copy(attributes = Map.empty), ver) -> mod.attributes
}.toMap }.toMap
} yield Project( } yield {
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
)
}
def parseDateTime(s: String): Option[Versions.DateTime] = val description = pom.children
if (s.length == 14 && s.forall(_.isDigit)) .find(_.label == "description")
Some(Versions.DateTime( .map(_.textContent)
s.substring(0, 4).toInt, .getOrElse("")
s.substring(4, 6).toInt,
s.substring(6, 8).toInt, val homePage = pom.children
s.substring(8, 10).toInt, .find(_.label == "url")
s.substring(10, 12).toInt, .map(_.textContent)
s.substring(12, 14).toInt .getOrElse("")
))
else val licenses = pom.children
None .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 = { def versions(node: Node): String \/ Versions = {
import Scalaz._ import Scalaz._

View File

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

View File

@ -1,5 +1,7 @@
package coursier.util package coursier.util
import coursier.core.Versions
import scalaz.{\/-, -\/, \/, Scalaz} import scalaz.{\/-, -\/, \/, Scalaz}
object Xml { object Xml {
@ -55,4 +57,17 @@ object Xml {
.toRightDisjunction(s"$description not found") .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, Nil,
None, None,
None, None,
Nil Nil,
Info.empty
) )
} }

View File

@ -1,5 +1,7 @@
package coursier package coursier
import java.util.GregorianCalendar
import sbt._ import sbt._
object ToSbt { object ToSbt {
@ -24,33 +26,61 @@ object ToSbt {
Map.empty 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( new sbt.ModuleReport(
ToSbt.moduleId(dependency), module = ToSbt.moduleId(dependency),
artifacts.collect { artifacts = sbtArtifacts,
case (artifact, Some(file)) => missingArtifacts = sbtMissingArtifacts,
(ToSbt.artifact(dependency.module, artifact), file) status = None,
}, publicationDate = publicationDate,
artifacts.collect { resolver = None,
case (artifact, None) => artifactResolver = None,
ToSbt.artifact(dependency.module, artifact) evicted = false,
}, evictedData = None,
None, evictedReason = None,
None, problem = None,
None, homepage = Some(project.info.homePage).filter(_.nonEmpty),
None, extraAttributes = dependency.module.attributes ++ project.properties,
false, isDefault = None,
None, branch = None,
None, configurations = project.configurations.keys.toVector,
None, licenses = project.info.licenses,
None, callers = callers
Map.empty,
None,
None,
Nil,
Nil,
Nil
) )
}
private def grouped[K, V](map: Seq[(K, V)]): Map[K, Seq[V]] = private def grouped[K, V](map: Seq[(K, V)]): Map[K, Seq[V]] =
map.groupBy { case (k, _) => k }.map { map.groupBy { case (k, _) => k }.map {
@ -71,9 +101,47 @@ object ToSbt {
val groupedDepArtifacts = grouped(depArtifacts) 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 { groupedDepArtifacts.map {
case (dep, artifacts) => 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, profiles,
versions, versions,
snapshotVersioning, snapshotVersioning,
publications publications,
Info.empty
) )
} }
} }