Coursier dependency resolution integration

This adds dependency to LM implemented using Coursier.
I had to copy paste a bunch of code from sbt-coursier-shared to break the dependency to sbt.

`Global / useCoursier := false` or `-Dsbt.coursier=false` be used to opt-out of using Coursier for the dependency resolution.
This commit is contained in:
Eugene Yokota 2019-03-08 20:15:04 -05:00
parent 42bc2ea04d
commit 38f94a6e31
8 changed files with 888 additions and 28 deletions

View File

@ -590,7 +590,12 @@ lazy val mainProj = (project in file("main"))
if (xs exists { s => s.contains(s""""$sv"""") }) ()
else sys.error("PluginCross.scala does not match up with the scalaVersion " + sv)
},
libraryDependencies ++= scalaXml.value ++ Seq(launcherInterface) ++ log4jDependencies ++ Seq(scalaCacheCaffeine),
libraryDependencies ++= {
scalaXml.value ++
Seq(launcherInterface) ++
log4jDependencies ++
Seq(scalaCacheCaffeine, lmCousier)
},
Compile / scalacOptions -= "-Xfatal-warnings",
managedSourceDirectories in Compile +=
baseDirectory.value / "src" / "main" / "contraband-scala",
@ -650,6 +655,7 @@ lazy val sbtProj = (project in file("sbt"))
)
.configure(addSbtIO, addSbtCompilerBridge)
/*
lazy val sbtBig = (project in file(".big"))
.dependsOn(sbtProj)
.settings(
@ -685,6 +691,7 @@ lazy val sbtBig = (project in file(".big"))
}).transform(node).head
},
)
*/
lazy val sbtIgnoredProblems = {
Vector(

View File

@ -12,6 +12,7 @@ import java.net.{ URI, URL, URLClassLoader }
import java.util.Optional
import java.util.concurrent.{ Callable, TimeUnit }
import coursier.core.{ Configuration => CConfiguration }
import org.apache.ivy.core.module.descriptor.ModuleDescriptor
import org.apache.ivy.core.module.id.ModuleRevisionId
import sbt.Def.{ Initialize, ScopedKey, Setting, SettingsDefinition }
@ -200,6 +201,7 @@ object Defaults extends BuildCommon {
exportJars :== false,
trackInternalDependencies :== TrackLevel.TrackAlways,
exportToInternal :== TrackLevel.TrackAlways,
useCoursier :== LibraryManagement.defaultUseCoursier,
retrieveManaged :== false,
retrieveManagedSync :== false,
configurationsToRetrieve :== None,
@ -224,7 +226,12 @@ object Defaults extends BuildCommon {
pomAllRepositories :== false,
pomIncludeRepository :== Classpaths.defaultRepositoryFilter,
updateOptions := UpdateOptions(),
forceUpdatePeriod :== None
forceUpdatePeriod :== None,
// coursier settings
csrExtraCredentials :== Nil,
csrLogger :== None,
csrCachePath :== coursier.cache.CacheDefaults.location,
csrMavenProfiles :== Set.empty,
)
/** Core non-plugin settings for sbt builds. These *must* be on every build or the sbt engine will fail to run at all. */
@ -2125,6 +2132,14 @@ object Classpaths {
}).value,
moduleName := normalizedName.value,
ivyPaths := IvyPaths(baseDirectory.value, bootIvyHome(appConfiguration.value)),
csrCachePath := {
val old = csrCachePath.value
val ip = ivyPaths.value
val defaultIvyCache = bootIvyHome(appConfiguration.value)
if (old != coursier.cache.CacheDefaults.location) old
else if (ip.ivyHome == defaultIvyCache) old
else ip.ivyHome.getOrElse(old)
},
dependencyCacheDirectory := {
val st = state.value
BuildPaths.getDependencyDirectory(st, BuildPaths.getGlobalBase(st))
@ -2181,10 +2196,7 @@ object Classpaths {
)
else None
},
dependencyResolution := IvyDependencyResolution(
ivyConfiguration.value,
CustomHttp.okhttpClient.value
),
dependencyResolution := LibraryManagement.dependencyResolutionTask.value,
publisher := IvyPublisher(ivyConfiguration.value, CustomHttp.okhttpClient.value),
ivyConfiguration := mkIvyConfiguration.value,
ivyConfigurations := {
@ -2198,6 +2210,44 @@ object Classpaths {
if (managedScalaInstance.value && scalaHome.value.isEmpty) Configurations.ScalaTool :: Nil
else Nil
},
// Coursier needs these
ivyConfigurations := {
val confs = ivyConfigurations.value
val names = confs.map(_.name).toSet
val extraSources =
if (names("sources"))
None
else
Some(
Configuration.of(
id = "Sources",
name = "sources",
description = "",
isPublic = true,
extendsConfigs = Vector.empty,
transitive = false
)
)
val extraDocs =
if (names("docs"))
None
else
Some(
Configuration.of(
id = "Docs",
name = "docs",
description = "",
isPublic = true,
extendsConfigs = Vector.empty,
transitive = false
)
)
val use = useCoursier.value
if (use) confs ++ extraSources.toSeq ++ extraDocs.toSeq
else confs
},
moduleSettings := moduleSettings0.value,
makePomConfiguration := MakePomConfiguration()
.withFile((artifactPath in makePom).value)
@ -2342,8 +2392,17 @@ object Classpaths {
case Right(ur) => ur
}
}
} tag (Tags.Update, Tags.Network)).value
)
} tag (Tags.Update, Tags.Network)).value,
csrProject := LMCoursier.coursierProjectTask.value,
csrConfiguration := LMCoursier.coursierConfigurationTask(false, false).value,
csrResolvers := LMCoursier.coursierResolversTask.value,
csrRecursiveResolvers := LMCoursier.coursierRecursiveResolversTask.value,
csrSbtResolvers := LMCoursier.coursierSbtResolversTask.value,
csrInterProjectDependencies := LMCoursier.coursierInterProjectDependenciesTask.value,
csrFallbackDependencies := LMCoursier.coursierFallbackDependenciesTask.value,
) ++
IvyXml.generateIvyXmlSettings() ++
LMCoursier.publicationsSetting(Seq(Compile, Test).map(c => c -> CConfiguration(c.name)))
val jvmBaseSettings: Seq[Setting[_]] = Seq(
libraryDependencies ++= autoLibraryDependency(

View File

@ -32,6 +32,8 @@ import sbt.librarymanagement.ivy.{ Credentials, IvyConfiguration, IvyPaths, Upda
import sbt.testing.Framework
import sbt.util.{ Level, Logger }
import xsbti.compile._
import coursier.cache.CacheLogger
import coursier.lmcoursier.{ CoursierConfiguration, FallbackDependency }
import scala.concurrent.duration.{ Duration, FiniteDuration }
import scala.xml.{ NodeSeq, Node => XNode }
@ -351,6 +353,20 @@ object Keys {
val fullClasspathAsJars = taskKey[Classpath]("The exported classpath, consisting of build products and unmanaged and managed, internal and external dependencies, all as JARs.")
val internalDependencyConfigurations = settingKey[Seq[(ProjectRef, Set[String])]]("The project configurations that this configuration depends on")
val useCoursier = settingKey[Boolean]("Use Coursier for dependency resolution.").withRank(BSetting)
val csrCachePath = settingKey[File]("Coursier cache path").withRank(CSetting)
private[sbt] val csrConfiguration = taskKey[CoursierConfiguration]("General dependency management (Coursier) settings, such as the resolvers and options to use.").withRank(DTask)
private[sbt] val csrProject = taskKey[coursier.core.Project]("")
private[sbt] val csrResolvers = taskKey[Seq[Resolver]]("")
private[sbt] val csrRecursiveResolvers = taskKey[Seq[Resolver]]("Resolvers of the current project, plus those of all from its inter-dependency projects")
private[sbt] val csrSbtResolvers = taskKey[Seq[Resolver]]("Resolvers used for sbt artifacts.")
private[sbt] val csrInterProjectDependencies = taskKey[Seq[coursier.core.Project]]("Projects the current project depends on, possibly transitively")
private[sbt] val csrFallbackDependencies = taskKey[Seq[FallbackDependency]]("")
private[sbt] val csrMavenProfiles = settingKey[Set[String]]("")
private[sbt] val csrLogger = taskKey[Option[CacheLogger]]("")
private[sbt] val csrExtraCredentials = taskKey[Seq[coursier.credentials.Credentials]]("")
private[sbt] val csrPublications = taskKey[Seq[(coursier.core.Configuration, coursier.core.Publication)]]("")
val internalConfigurationMap = settingKey[Configuration => Configuration]("Maps configurations to the actual configuration used to define the classpath.").withRank(CSetting)
val classpathConfiguration = taskKey[Configuration]("The configuration used to define the classpath.").withRank(CTask)
val ivyConfiguration = taskKey[IvyConfiguration]("General dependency management (Ivy) settings, such as the resolvers and paths to use.").withRank(DTask)

View File

@ -0,0 +1,524 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
import coursier.core.{
Attributes => CAttributes,
Classifier,
Configuration => CConfiguration,
Dependency => CDependency,
Extension => CExtension,
Info => CInfo,
Module,
ModuleName,
Organization => COrganization,
Project => CProject,
Publication => CPublication,
Type => CType
}
import coursier.credentials.DirectCredentials
import coursier.lmcoursier._
import sbt.librarymanagement._
import Keys._
import sbt.librarymanagement.ivy.{
FileCredentials,
Credentials,
DirectCredentials => IvyDirectCredentials
}
import sbt.ScopeFilter.Make._
import scala.collection.JavaConverters._
private[sbt] object LMCoursier {
def coursierProjectTask: Def.Initialize[sbt.Task[CProject]] =
Def.task {
Inputs.coursierProject(
projectID.value,
allDependencies.value,
excludeDependencies.value,
// should projectID.configurations be used instead?
ivyConfigurations.value,
scalaVersion.value,
scalaBinaryVersion.value,
streams.value.log
)
}
def coursierConfigurationTask(
withClassifiers: Boolean,
sbtClassifiers: Boolean
): Def.Initialize[Task[CoursierConfiguration]] =
Def.taskDyn {
val resolversTask =
if (sbtClassifiers)
csrSbtResolvers
else
csrRecursiveResolvers
val classifiersTask: sbt.Def.Initialize[sbt.Task[Option[Seq[Classifier]]]] =
if (withClassifiers && !sbtClassifiers)
Def.task(Some(sbt.Keys.transitiveClassifiers.value.map(Classifier(_))))
else
Def.task(None)
Def.task {
val rs = resolversTask.value
val scalaOrg = scalaOrganization.value
val scalaVer = scalaVersion.value
val interProjectDependencies = csrInterProjectDependencies.value
val excludeDeps = Inputs.exclusions(
excludeDependencies.value,
scalaVer,
scalaBinaryVersion.value,
streams.value.log
)
val fallbackDeps = csrFallbackDependencies.value
val autoScalaLib = autoScalaLibrary.value && scalaModuleInfo.value.forall(
_.overrideScalaVersion
)
val profiles = csrMavenProfiles.value
val credentials = credentialsTask.value
val createLogger = csrLogger.value
val cache = csrCachePath.value
val internalSbtScalaProvider = appConfiguration.value.provider.scalaProvider
val sbtBootJars = internalSbtScalaProvider.jars()
val sbtScalaVersion = internalSbtScalaProvider.version()
val sbtScalaOrganization = "org.scala-lang" // always assuming sbt uses mainline scala
val classifiers = classifiersTask.value
val s = streams.value
Classpaths.warnResolversConflict(rs, s.log)
CoursierConfiguration()
.withResolvers(rs.toVector)
.withInterProjectDependencies(interProjectDependencies.toVector)
.withFallbackDependencies(fallbackDeps.toVector)
.withExcludeDependencies(
excludeDeps.toVector.sorted
.map {
case (o, n) =>
(o.value, n.value)
}
)
.withAutoScalaLibrary(autoScalaLib)
.withSbtScalaJars(sbtBootJars.toVector)
.withSbtScalaVersion(sbtScalaVersion)
.withSbtScalaOrganization(sbtScalaOrganization)
.withClassifiers(classifiers.toVector.flatten.map(_.value))
.withHasClassifiers(classifiers.nonEmpty)
.withMavenProfiles(profiles.toVector.sorted)
.withScalaOrganization(scalaOrg)
.withScalaVersion(scalaVer)
.withCredentials(credentials)
.withLogger(createLogger)
.withCache(cache)
.withLog(s.log)
}
}
val credentialsTask = Def.task {
val log = streams.value.log
val creds = sbt.Keys.credentials.value
.flatMap {
case dc: IvyDirectCredentials => List(dc)
case fc: FileCredentials =>
Credentials.loadCredentials(fc.path) match {
case Left(err) =>
log.warn(s"$err, ignoring it")
Nil
case Right(dc) => List(dc)
}
}
.map { c =>
DirectCredentials()
.withHost(c.host)
.withUsername(c.userName)
.withPassword(c.passwd)
.withRealm(Some(c.realm).filter(_.nonEmpty))
}
creds ++ csrExtraCredentials.value
}
def coursierRecursiveResolversTask: Def.Initialize[sbt.Task[Seq[Resolver]]] =
Def.taskDyn {
val state = sbt.Keys.state.value
val projectRef = sbt.Keys.thisProjectRef.value
val projects = allRecursiveInterDependencies(state, projectRef)
Def.task {
csrResolvers.all(ScopeFilter(inProjects(projectRef +: projects: _*))).value.flatten
}
}
def coursierResolversTask: Def.Initialize[sbt.Task[Seq[Resolver]]] =
Def.taskDyn {
val bootResOpt = bootResolvers.value
val overrideFlag = overrideBuildResolvers.value
Def.task {
val result = resultTask(bootResOpt, overrideFlag).value
val reorderResolvers = true // coursierReorderResolvers.value
val keepPreloaded = false // coursierKeepPreloaded.value
val result0 =
if (reorderResolvers)
ResolutionParams.reorderResolvers(result)
else
result
if (keepPreloaded)
result0
else
result0.filter { r =>
!r.name.startsWith("local-preloaded")
}
}
}
private val pluginIvySnapshotsBase = Resolver.SbtRepositoryRoot.stripSuffix("/") + "/ivy-snapshots"
def coursierSbtResolversTask: Def.Initialize[sbt.Task[Seq[Resolver]]] = Def.task {
val resolvers =
sbt.Classpaths
.bootRepositories(appConfiguration.value)
.toSeq
.flatten ++ // required because of the hack above it seems
externalResolvers.in(updateSbtClassifiers).value
val pluginIvySnapshotsFound = resolvers.exists {
case repo: URLRepository =>
repo.patterns.artifactPatterns.headOption
.exists(_.startsWith(pluginIvySnapshotsBase))
case _ => false
}
val resolvers0 =
if (pluginIvySnapshotsFound && !resolvers.contains(Classpaths.sbtPluginReleases))
resolvers :+ Classpaths.sbtPluginReleases
else
resolvers
val keepPreloaded = true // coursierKeepPreloaded.value
if (keepPreloaded)
resolvers0
else
resolvers0.filter { r =>
!r.name.startsWith("local-preloaded")
}
}
def coursierInterProjectDependenciesTask: Def.Initialize[sbt.Task[Seq[CProject]]] =
Def.taskDyn {
val state = sbt.Keys.state.value
val projectRef = sbt.Keys.thisProjectRef.value
val projectRefs = allRecursiveInterDependencies(state, projectRef)
Def.task {
val projects = csrProject.all(ScopeFilter(inProjects(projectRefs: _*))).value
val projectModules = projects.map(_.module).toSet
// this includes org.scala-sbt:global-plugins referenced from meta-builds in particular
val extraProjects = sbt.Keys.projectDescriptors.value
.map {
case (k, v) =>
moduleFromIvy(k) -> v
}
.filter {
case (module, _) =>
!projectModules(module)
}
.toVector
.map {
case (module, v) =>
val configurations = v.getConfigurations.map { c =>
CConfiguration(c.getName) -> c.getExtends.map(CConfiguration(_)).toSeq
}.toMap
val deps = v.getDependencies.flatMap(dependencyFromIvy)
CProject(
module,
v.getModuleRevisionId.getRevision,
deps,
configurations,
None,
Nil,
Nil,
Nil,
None,
None,
None,
relocated = false,
None,
Nil,
CInfo.empty
)
}
projects ++ extraProjects
}
}
def coursierFallbackDependenciesTask: Def.Initialize[sbt.Task[Seq[FallbackDependency]]] =
Def.taskDyn {
val s = state.value
val projectRef = sbt.Keys.thisProjectRef.value
val projects = allRecursiveInterDependencies(s, projectRef)
Def.task {
val allDeps =
allDependencies.all(ScopeFilter(inProjects(projectRef +: projects: _*))).value.flatten
FromSbt.fallbackDependencies(
allDeps,
scalaVersion.in(projectRef).value,
scalaBinaryVersion.in(projectRef).value
)
}
}
def publicationsSetting(packageConfigs: Seq[(Configuration, CConfiguration)]): Def.Setting[_] = {
csrPublications := coursierPublicationsTask(packageConfigs: _*).value
}
def coursierPublicationsTask(
configsMap: (Configuration, CConfiguration)*
): Def.Initialize[sbt.Task[Seq[(CConfiguration, CPublication)]]] =
Def.task {
val s = sbt.Keys.state.value
val projectRef = sbt.Keys.thisProjectRef.value
val projId = sbt.Keys.projectID.value
val sv = sbt.Keys.scalaVersion.value
val sbv = sbt.Keys.scalaBinaryVersion.value
val ivyConfs = sbt.Keys.ivyConfigurations.value
val extracted = Project.extract(s)
import extracted._
val sourcesConfigOpt =
if (ivyConfigurations.value.exists(_.name == "sources"))
Some(CConfiguration("sources"))
else
None
val docsConfigOpt =
if (ivyConfigurations.value.exists(_.name == "docs"))
Some(CConfiguration("docs"))
else
None
val sbtBinArtifacts =
for ((config, targetConfig) <- configsMap) yield {
val publish = getOpt(
publishArtifact
.in(projectRef)
.in(packageBin)
.in(config)
).getOrElse(false)
if (publish)
getOpt(
artifact
.in(projectRef)
.in(packageBin)
.in(config)
).map(targetConfig -> _)
else
None
}
val sbtSourceArtifacts =
for ((config, targetConfig) <- configsMap) yield {
val publish = getOpt(
publishArtifact
.in(projectRef)
.in(packageSrc)
.in(config)
).getOrElse(false)
if (publish)
getOpt(
artifact
.in(projectRef)
.in(packageSrc)
.in(config)
).map(sourcesConfigOpt.getOrElse(targetConfig) -> _)
else
None
}
val sbtDocArtifacts =
for ((config, targetConfig) <- configsMap) yield {
val publish =
getOpt(
publishArtifact
.in(projectRef)
.in(packageDoc)
.in(config)
).getOrElse(false)
if (publish)
getOpt(
artifact
.in(projectRef)
.in(packageDoc)
.in(config)
).map(docsConfigOpt.getOrElse(targetConfig) -> _)
else
None
}
val sbtArtifacts = sbtBinArtifacts ++ sbtSourceArtifacts ++ sbtDocArtifacts
def artifactPublication(artifact: Artifact) = {
val name = FromSbt.sbtCrossVersionName(
artifact.name,
projId.crossVersion,
sv,
sbv
)
CPublication(
name,
CType(artifact.`type`),
CExtension(artifact.extension),
artifact.classifier.fold(Classifier.empty)(Classifier(_))
)
}
val sbtArtifactsPublication = sbtArtifacts.collect {
case Some((config, artifact)) =>
config -> artifactPublication(artifact)
}
val stdArtifactsSet = sbtArtifacts.flatMap(_.map { case (_, a) => a }.toSeq).toSet
// Second-way of getting artifacts from SBT
// No obvious way of getting the corresponding publishArtifact value for the ones
// only here, it seems.
val extraSbtArtifacts = getOpt(
sbt.Keys.artifacts
.in(projectRef)
).getOrElse(Nil)
.filterNot(stdArtifactsSet)
// Seems that SBT does that - if an artifact has no configs,
// it puts it in all of them. See for example what happens to
// the standalone JAR artifact of the coursier cli module.
def allConfigsIfEmpty(configs: Iterable[ConfigRef]): Iterable[ConfigRef] =
if (configs.isEmpty) ivyConfs.filter(_.isPublic).map(c => ConfigRef(c.name)) else configs
val extraSbtArtifactsPublication = for {
artifact <- extraSbtArtifacts
config <- allConfigsIfEmpty(artifact.configurations.map(x => ConfigRef(x.name)))
// FIXME If some configurations from artifact.configurations are not public, they may leak here :\
} yield CConfiguration(config.name) -> artifactPublication(artifact)
sbtArtifactsPublication ++ extraSbtArtifactsPublication
}
private def moduleFromIvy(id: org.apache.ivy.core.module.id.ModuleRevisionId): Module =
Module(
COrganization(id.getOrganisation),
ModuleName(id.getName),
id.getExtraAttributes.asScala.map {
case (k0, v0) => k0.asInstanceOf[String] -> v0.asInstanceOf[String]
}.toMap
)
private def dependencyFromIvy(
desc: org.apache.ivy.core.module.descriptor.DependencyDescriptor
): Seq[(CConfiguration, CDependency)] = {
val id = desc.getDependencyRevisionId
val module = moduleFromIvy(id)
val exclusions = desc.getAllExcludeRules.map { rule =>
// we're ignoring rule.getConfigurations and rule.getMatcher here
val modId = rule.getId.getModuleId
// we're ignoring modId.getAttributes here
(COrganization(modId.getOrganisation), ModuleName(modId.getName))
}.toSet
val configurations = desc.getModuleConfigurations.toVector
.flatMap(s => coursier.ivy.IvyXml.mappings(s))
def dependency(conf: CConfiguration, attr: CAttributes) = CDependency(
module,
id.getRevision,
conf,
exclusions,
attr,
optional = false,
desc.isTransitive
)
val attributes: CConfiguration => CAttributes = {
val artifacts = desc.getAllDependencyArtifacts
val m = artifacts.toVector.flatMap { art =>
val attr = CAttributes(CType(art.getType), Classifier.empty)
art.getConfigurations.map(CConfiguration(_)).toVector.map { conf =>
conf -> attr
}
}.toMap
c => m.getOrElse(c, CAttributes.empty)
}
configurations.map {
case (from, to) =>
from -> dependency(to, attributes(to))
}
}
private def resultTask(
bootResOpt: Option[Seq[Resolver]],
overrideFlag: Boolean
): Def.Initialize[sbt.Task[Seq[Resolver]]] =
bootResOpt.filter(_ => overrideFlag) match {
case Some(r) => Def.task(r)
case None =>
Def.taskDyn {
val extRes = externalResolvers.value
val isSbtPlugin = sbtPlugin.value
if (isSbtPlugin)
Def.task {
Seq(
sbtResolver.value,
Classpaths.sbtPluginReleases
) ++ extRes
} else
Def.task(extRes)
}
}
def allRecursiveInterDependencies(state: sbt.State, projectRef: sbt.ProjectRef) = {
def dependencies(map: Map[String, Seq[String]], id: String): Set[String] = {
def helper(map: Map[String, Seq[String]], acc: Set[String]): Set[String] =
if (acc.exists(map.contains)) {
val (kept, rem) = map.partition { case (k, _) => acc(k) }
helper(rem, acc ++ kept.valuesIterator.flatten)
} else
acc
helper(map - id, map.getOrElse(id, Nil).toSet)
}
val allProjectsDeps =
for (p <- Project.structure(state).allProjects)
yield p.id -> p.dependencies.map(_.project.project)
val deps = dependencies(allProjectsDeps.toMap, projectRef.project)
Project.structure(state).allProjectRefs.filter(p => deps(p.project))
}
}

View File

@ -5,12 +5,15 @@
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal
package sbt
package internal
import coursier.lmcoursier.CoursierDependencyResolution
import java.io.File
import sbt.internal.librarymanagement._
import sbt.internal.util.{ ConsoleAppender, LogOption }
import sbt.librarymanagement._
import sbt.librarymanagement.ivy.IvyDependencyResolution
import sbt.librarymanagement.syntax._
import sbt.util.{ CacheStore, CacheStoreFactory, Logger, Tracked }
import sbt.io.IO
@ -19,6 +22,43 @@ private[sbt] object LibraryManagement {
private type UpdateInputs = (Long, ModuleSettings, UpdateConfiguration)
def defaultUseCoursier: Boolean = {
val coursierOpt = sys.props
.get("sbt.coursier")
.flatMap(
str =>
ConsoleAppender.parseLogOption(str) match {
case LogOption.Always => Some(true)
case LogOption.Never => Some(false)
case _ => None
}
)
val ivyOpt = sys.props
.get("sbt.ivy")
.flatMap(
str =>
ConsoleAppender.parseLogOption(str) match {
case LogOption.Always => Some(true)
case LogOption.Never => Some(false)
case _ => None
}
)
val notIvyOpt = ivyOpt map { !_ }
coursierOpt.orElse(notIvyOpt).getOrElse(true)
}
def dependencyResolutionTask: Def.Initialize[Task[DependencyResolution]] = Def.taskDyn {
if (Keys.useCoursier.value) {
Def.task { CoursierDependencyResolution(Keys.csrConfiguration.value) }
} else
Def.task {
IvyDependencyResolution(
Keys.ivyConfiguration.value,
CustomHttp.okhttpClient.value
)
}
}
def cachedUpdate(
lm: DependencyResolution,
module: ModuleDescriptor,

View File

@ -0,0 +1,224 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
package librarymanagement
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.Files
import coursier.core.{ Configuration, Project }
import org.apache.ivy.core.module.id.ModuleRevisionId
import Def.Setting
import sbt.Keys.{
csrProject,
csrPublications,
publishLocalConfiguration,
publishConfiguration,
useCoursier
}
import sbt.librarymanagement.PublishConfiguration
import scala.collection.JavaConverters._
import scala.xml.{ Node, PrefixedAttribute }
object IvyXml {
import sbt.Project._
def rawContent(
currentProject: Project,
shadedConfigOpt: Option[Configuration]
): String = {
// Important: width = Int.MaxValue, so that no tag gets truncated.
// In particular, that prevents things like <foo /> to be split to
// <foo>
// </foo>
// by the pretty-printer.
// See https://github.com/sbt/sbt/issues/3412.
val printer = new scala.xml.PrettyPrinter(Int.MaxValue, 2)
"""<?xml version="1.0" encoding="UTF-8"?>""" + '\n' +
printer.format(content(currentProject, shadedConfigOpt))
}
// These are required for publish to be fine, later on.
def writeFiles(
currentProject: Project,
shadedConfigOpt: Option[Configuration],
ivySbt: IvySbt,
log: sbt.util.Logger
): Unit = {
val ivyCacheManager = ivySbt.withIvy(log)(ivy => ivy.getResolutionCacheManager)
val ivyModule = ModuleRevisionId.newInstance(
currentProject.module.organization.value,
currentProject.module.name.value,
currentProject.version,
currentProject.module.attributes.asJava
)
val cacheIvyFile = ivyCacheManager.getResolvedIvyFileInCache(ivyModule)
val cacheIvyPropertiesFile = ivyCacheManager.getResolvedIvyPropertiesInCache(ivyModule)
val content0 = rawContent(currentProject, shadedConfigOpt)
cacheIvyFile.getParentFile.mkdirs()
log.info(s"Writing Ivy file $cacheIvyFile")
Files.write(cacheIvyFile.toPath, content0.getBytes(UTF_8))
// Just writing an empty file here... Are these only used?
cacheIvyPropertiesFile.getParentFile.mkdirs()
Files.write(cacheIvyPropertiesFile.toPath, Array.emptyByteArray)
()
}
def content(project0: Project, shadedConfigOpt: Option[Configuration]): Node = {
val filterOutDependencies =
shadedConfigOpt.toSet[Configuration].flatMap { shadedConfig =>
project0.dependencies
.collect { case (`shadedConfig`, dep) => dep }
}
val project: Project = project0.copy(
dependencies = project0.dependencies.collect {
case p @ (_, dep) if !filterOutDependencies(dep) => p
}
)
val infoAttrs = project.module.attributes.foldLeft[xml.MetaData](xml.Null) {
case (acc, (k, v)) =>
new PrefixedAttribute("e", k, v, acc)
}
val licenseElems = project.info.licenses.map {
case (name, urlOpt) =>
val n = <license name={name} />
urlOpt.fold(n) { url =>
n % <x url={url} />.attributes
}
}
val infoElem = {
<info
organisation={project.module.organization.value}
module={project.module.name.value}
revision={project.version}
>
{licenseElems}
<description>{project.info.description}</description>
</info>
} % infoAttrs
val confElems = project.configurations.toVector.collect {
case (name, extends0) if !shadedConfigOpt.contains(name) =>
val extends1 = shadedConfigOpt.fold(extends0)(c => extends0.filter(_ != c))
val n = <conf name={name.value} visibility="public" description="" />
if (extends1.nonEmpty)
n % <x extends={extends1.map(_.value).mkString(",")} />.attributes
else
n
}
val publications = project.publications
.groupBy { case (_, p) => p }
.mapValues { _.map { case (cfg, _) => cfg } }
val publicationElems = publications.map {
case (pub, configs) =>
val n =
<artifact name={pub.name} type={pub.`type`.value} ext={pub.ext.value} conf={
configs.map(_.value).mkString(",")
} />
if (pub.classifier.nonEmpty)
n % <x e:classifier={pub.classifier.value} />.attributes
else
n
}
val dependencyElems = project.dependencies.toVector.map {
case (conf, dep) =>
val excludes = dep.exclusions.toSeq.map {
case (org, name) =>
<exclude org={org.value} module={name.value} name="*" type="*" ext="*" conf="" matcher="exact"/>
}
val n =
<dependency org={dep.module.organization.value} name={dep.module.name.value} rev={
dep.version
} conf={s"${conf.value}->${dep.configuration.value}"}>
{excludes}
</dependency>
val moduleAttrs = dep.module.attributes.foldLeft[xml.MetaData](xml.Null) {
case (acc, (k, v)) =>
new PrefixedAttribute("e", k, v, acc)
}
n % moduleAttrs
}
<ivy-module version="2.0" xmlns:e="http://ant.apache.org/ivy/extra">
{infoElem}
<configurations>{confElems}</configurations>
<publications>{publicationElems}</publications>
<dependencies>{dependencyElems}</dependencies>
</ivy-module>
}
def makeIvyXmlBefore[T](
task: TaskKey[T],
shadedConfigOpt: Option[Configuration]
): Setting[Task[T]] =
task := task.dependsOn {
Def.taskDyn {
val doGen = useCoursier.value
if (doGen)
Def.task {
val currentProject = {
val proj = csrProject.value
val publications = csrPublications.value
proj.copy(publications = publications)
}
IvyXml.writeFiles(
currentProject,
shadedConfigOpt,
sbt.Keys.ivySbt.value,
sbt.Keys.streams.value.log
)
} else
Def.task(())
}
}.value
private lazy val needsIvyXmlLocal = Seq(publishLocalConfiguration) ++ getPubConf(
"makeIvyXmlLocalConfiguration"
)
private lazy val needsIvyXml = Seq(publishConfiguration) ++ getPubConf(
"makeIvyXmlConfiguration"
)
private[this] def getPubConf(method: String): List[TaskKey[PublishConfiguration]] =
try {
val cls = sbt.Keys.getClass
val m = cls.getMethod(method)
val task = m.invoke(sbt.Keys).asInstanceOf[TaskKey[PublishConfiguration]]
List(task)
} catch {
case _: Throwable => // FIXME Too wide
Nil
}
def generateIvyXmlSettings(
shadedConfigOpt: Option[Configuration] = None
): Seq[Setting[_]] =
(needsIvyXml ++ needsIvyXmlLocal).map(IvyXml.makeIvyXmlBefore(_, shadedConfigOpt))
}

View File

@ -29,22 +29,7 @@ object Dependencies {
private val utilScripted = "org.scala-sbt" %% "util-scripted" % utilVersion
private val libraryManagementCore = "org.scala-sbt" %% "librarymanagement-core" % lmVersion
private val libraryManagementImpl = {
val lmOrganization =
sys.props.get("sbt.build.lm.organization") match {
case Some(impl) => impl
case _ => "org.scala-sbt"
}
val lmModuleName =
sys.props.get("sbt.build.lm.moduleName") match {
case Some(impl) => impl
case _ => "librarymanagement-ivy"
}
lmOrganization %% lmModuleName % lmVersion
}
private val libraryManagementImpl = "org.scala-sbt" %% "librarymanagement-ivy" % lmVersion
val launcherVersion = "1.0.4"
val launcherInterface = "org.scala-sbt" % "launcher-interface" % launcherVersion
@ -115,6 +100,9 @@ object Dependencies {
def addSbtZincCompile(p: Project): Project =
addSbtModule(p, sbtZincPath, "zincCompile", zincCompile)
val lmCousierVersion = "1.1.0-M14"
val lmCousier = "io.get-coursier" %% "lm-coursier" % lmCousierVersion
val sjsonNewScalaJson = Def.setting {
"com.eed3si9n" %% "sjson-new-scalajson" % contrabandSjsonNewVersion.value
}

View File

@ -35,7 +35,9 @@ def retrieveID = org % "test-retrieve" % "2.0"
// check that the test class is on the compile classpath, either because it was compiled or because it was properly retrieved
def checkTask(classpath: TaskKey[Classpath]) = Def task {
val loader = ClasspathUtilities.toLoader((classpath in Compile).value.files, scalaInstance.value.loader)
val deps = libraryDependencies.value
val cp = (classpath in Compile).value.files
val loader = ClasspathUtilities.toLoader(cp, scalaInstance.value.loader)
try { Class.forName("test.Test", false, loader); () }
catch { case _: ClassNotFoundException | _: NoClassDefFoundError => sys.error("Dependency not retrieved properly") }
catch { case _: ClassNotFoundException | _: NoClassDefFoundError => sys.error(s"Dependency not retrieved properly: $deps, $cp") }
}