diff --git a/modules/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala b/modules/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala index 82c33db52..41c69fc74 100644 --- a/modules/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala +++ b/modules/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala @@ -27,29 +27,34 @@ private[internal] object SbtUpdateReport { } } + private def infoProperties(project: Project): Seq[(String, String)] = + project.properties.filter(_._1.startsWith("info.")) + private val moduleId = caching[(Dependency, String, Map[String, String]), ModuleID] { case (dependency, version, extraProperties) => - sbt.librarymanagement.ModuleID( + val mod = sbt.librarymanagement.ModuleID( dependency.module.organization.value, dependency.module.name.value, version - ).withConfigurations( - Some(dependency.configuration.value) - ).withExtraAttributes( - dependency.module.attributes ++ extraProperties - ).withExclusions( - dependency - .exclusions - .toVector - .map { - case (org, name) => - sbt.librarymanagement.InclExclRule() - .withOrganization(org.value) - .withName(name.value) - } - ).withIsTransitive( - dependency.transitive ) + mod + .withConfigurations( + Some(dependency.configuration.value) + .filter(_.nonEmpty) // ??? + ) + .withExtraAttributes(dependency.module.attributes ++ extraProperties) + .withExclusions( + dependency + .exclusions + .toVector + .map { + case (org, name) => + sbt.librarymanagement.InclExclRule() + .withOrganization(org.value) + .withName(name.value) + } + ) + .withIsTransitive(dependency.transitive) } private val artifact = caching[(Module, Map[String, String], Publication, Artifact), sbt.librarymanagement.Artifact] { @@ -73,36 +78,39 @@ private[internal] object SbtUpdateReport { val sbtArtifacts = artifacts.collect { case (pub, artifact0, Some(file)) => - (artifact((dependency.module, project.properties.toMap, pub, artifact0)), file) + (artifact((dependency.module, infoProperties(project).toMap, pub, artifact0)), file) } val sbtMissingArtifacts = artifacts.collect { case (pub, artifact0, None) => - artifact((dependency.module, project.properties.toMap, pub, artifact0)) + artifact((dependency.module, infoProperties(project).toMap, pub, artifact0)) } val publicationDate = project.info.publication.map { dt => new GregorianCalendar(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second) } - val callers = dependees.map { + val callers = dependees.distinct.map { case (dependee, dependeeProj) => Caller( - moduleId((dependee, dependeeProj.version, dependeeProj.properties.toMap)), + moduleId((dependee, dependeeProj.version, Map.empty)), + // FIXME Shouldn't we only keep the configurations pulling dependency? dependeeProj.configurations.keys.toVector.map(c => ConfigRef(c.value)), dependee.module.attributes ++ dependeeProj.properties, // FIXME Set better values here isForceDependency = false, isChangingDependency = false, - isTransitiveDependency = false, + isTransitiveDependency = dependency.transitive, isDirectlyForceDependency = false ) } - ModuleReport( - moduleId((dependency, project.version, project.properties.toMap)), + val rep = ModuleReport( + moduleId((dependency, project.version, infoProperties(project).toMap)), sbtArtifacts.toVector, sbtMissingArtifacts.toVector ) + + rep // .withStatus(None) .withPublicationDate(publicationDate) // .withResolver(None) @@ -112,7 +120,8 @@ private[internal] object SbtUpdateReport { // .withEvictedReason(None) // .withProblem(None) .withHomepage(Some(project.info.homePage).filter(_.nonEmpty)) - .withExtraAttributes(dependency.module.attributes ++ project.properties) + .withLicenses(project.info.licenses.toVector) + .withExtraAttributes(dependency.module.attributes ++ infoProperties(project)) // .withIsDefault(None) // .withBranch(None) .withConfigurations(project.configurations.keys.toVector.map(c => ConfigRef(c.value))) @@ -243,7 +252,7 @@ private[internal] object SbtUpdateReport { // appears first in the update report, see https://github.com/coursier/coursier/issues/650 val dep = subRes.rootDependencies.head val (_, proj) = subRes.projectCache(dep.moduleVersion) - val mod = moduleId((dep, proj.version, proj.properties.toMap)) + val mod = moduleId((dep, proj.version, infoProperties(proj).toMap)) val (main, other) = reports.partition { r => r.module.organization == mod.organization && r.module.name == mod.name && @@ -253,10 +262,46 @@ private[internal] object SbtUpdateReport { } else reports.toVector + val mainReportDetails = reports0.map { rep => + OrganizationArtifactReport(rep.module.organization, rep.module.name, Vector(rep)) + } + + val evicted = coursier.graph.Conflict(subRes).flatMap { c => + // FIXME The project for c.wantedVersion is possibly not around (it's likely it was just not fetched) + val projOpt = subRes.projectCache.get((c.module, c.wantedVersion)) + .orElse(subRes.projectCache.get((c.module, c.version))) + projOpt.toSeq.map { + case (_, proj) => + // likely misses some details (transitive, exclusions, …) + val dep = Dependency(c.module, c.wantedVersion) + val dependee = Dependency(c.dependeeModule, c.dependeeVersion) + val dependeeProj = subRes.projectCache + .get((c.dependeeModule, c.dependeeVersion)) + .map(_._2) + .getOrElse { + // should not happen + Project(c.dependeeModule, c.dependeeVersion, Nil, Map(), None, Nil, Nil, Nil, None, None, None, false, None, Nil, coursier.core.Info.empty) + } + val rep = moduleReport((dep, Seq((dependee, dependeeProj)), proj.copy(version = c.wantedVersion), Nil)) + .withEvicted(true) + .withEvictedData(Some("version selection")) // ??? put latest-revision like sbt/ivy here? + OrganizationArtifactReport(c.module.organization.value, c.module.name.value, Vector(rep)) + } + } + + val details = (mainReportDetails ++ evicted) + .groupBy(r => (r.organization, r.name)) + .toVector // order? + .map { + case ((org, name), l) => + val modules = l.flatMap(_.modules) + OrganizationArtifactReport(org, name, modules) + } + ConfigurationReport( ConfigRef(config.value), reports0, - Vector() + details ) } diff --git a/modules/sbt-coursier/src/sbt-test/sbt-lm-coursier/evicted/build.sbt b/modules/sbt-coursier/src/sbt-test/sbt-lm-coursier/evicted/build.sbt new file mode 100644 index 000000000..4cd9ffc8d --- /dev/null +++ b/modules/sbt-coursier/src/sbt-test/sbt-lm-coursier/evicted/build.sbt @@ -0,0 +1,47 @@ + +// examples adapted from https://github.com/coursier/sbt-coursier/pull/75#issuecomment-497128870 + +lazy val a = project + .settings( + scalaVersion := "2.11.8", + libraryDependencies ++= Seq( + "org.typelevel" %% "cats-effect" % "1.3.1", + "org.typelevel" %% "cats-core" % "1.5.0" + ) +) + +lazy val b = project + .settings( + scalaVersion := "2.11.8", + libraryDependencies ++= Seq( + "org.slf4s" %% "slf4s-api" % "1.7.12", // depends on org.slf4j:slf4j-api:1.7.12 + "ch.qos.logback" % "logback-classic" % "1.1.2" // depends on org.slf4j:slf4j-api:1.7.6 + ) +) + +lazy val check = taskKey[Unit]("") + +check := { + + val aReport = update.in(a).value + val bReport = update.in(b).value + + def doCheck(report: UpdateReport): Unit = { + + val compileReport = report + .configurations + .find(_.configuration.name == "compile") + .getOrElse { + sys.error("compile report not found") + } + + val foundEvictions = compileReport.details.exists(_.modules.exists(_.evicted)) + if (!foundEvictions) + compileReport.details.foreach(println) + assert(foundEvictions) + } + + // needs https://github.com/coursier/coursier/pull/1217 + // doCheck(aReport) + doCheck(bReport) +} diff --git a/modules/sbt-coursier/src/sbt-test/sbt-lm-coursier/evicted/project/plugins.sbt b/modules/sbt-coursier/src/sbt-test/sbt-lm-coursier/evicted/project/plugins.sbt new file mode 100644 index 000000000..503ac2871 --- /dev/null +++ b/modules/sbt-coursier/src/sbt-test/sbt-lm-coursier/evicted/project/plugins.sbt @@ -0,0 +1,13 @@ +addSbtPlugin { + + val name = sys.props.getOrElse( + "plugin.name", + sys.error("plugin.name Java property not set") + ) + val version = sys.props.getOrElse( + "plugin.version", + sys.error("plugin.version Java property not set") + ) + + "io.get-coursier" % name % version +} diff --git a/modules/sbt-coursier/src/sbt-test/sbt-lm-coursier/evicted/test b/modules/sbt-coursier/src/sbt-test/sbt-lm-coursier/evicted/test new file mode 100644 index 000000000..454ddff24 --- /dev/null +++ b/modules/sbt-coursier/src/sbt-test/sbt-lm-coursier/evicted/test @@ -0,0 +1,2 @@ +> evicted +> check diff --git a/scripts/travis.sh b/scripts/travis.sh index f5af6fd2c..0efb3a565 100755 --- a/scripts/travis.sh +++ b/scripts/travis.sh @@ -14,13 +14,19 @@ sbtShading() { } runLmCoursierTests() { + if [ "$TEST_GROUP" = 1 ]; then + SCRIPTED_EXTRA="sbt-lm-coursier/*" + else + SCRIPTED_EXTRA="" + fi + # publishing locally to ensure shading runs fine ./metadata/scripts/with-test-repo.sh sbt \ ++$TRAVIS_SCALA_VERSION! \ mimaReportBinaryIssues \ lm-coursier-shaded/publishLocal \ lm-coursier/test \ - "sbt-lm-coursier/scripted shared-$TEST_GROUP/*" + "sbt-lm-coursier/scripted shared-$TEST_GROUP/* $SCRIPTED_EXTRA" } runSbtCoursierTests() {