mirror of https://github.com/sbt/sbt.git
Output better `sbt.ModuleReport`s
This commit is contained in:
parent
ebd97b8340
commit
d33ab9da26
|
|
@ -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])])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -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._
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,8 @@ object FromSbt {
|
||||||
Nil,
|
Nil,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Nil
|
Nil,
|
||||||
|
Info.empty
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,8 @@ package object test {
|
||||||
profiles,
|
profiles,
|
||||||
versions,
|
versions,
|
||||||
snapshotVersioning,
|
snapshotVersioning,
|
||||||
publications
|
publications,
|
||||||
|
Info.empty
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue