diff --git a/build.sbt b/build.sbt
index 0d223c21b..f517ad661 100644
--- a/build.sbt
+++ b/build.sbt
@@ -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(
diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala
index 00d53e26e..d52b6605e 100755
--- a/main/src/main/scala/sbt/Defaults.scala
+++ b/main/src/main/scala/sbt/Defaults.scala
@@ -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(
diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala
index ac2711453..52eff84cc 100644
--- a/main/src/main/scala/sbt/Keys.scala
+++ b/main/src/main/scala/sbt/Keys.scala
@@ -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)
diff --git a/main/src/main/scala/sbt/internal/LMCoursier.scala b/main/src/main/scala/sbt/internal/LMCoursier.scala
new file mode 100644
index 000000000..cf1a65088
--- /dev/null
+++ b/main/src/main/scala/sbt/internal/LMCoursier.scala
@@ -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))
+ }
+}
diff --git a/main/src/main/scala/sbt/internal/LibraryManagement.scala b/main/src/main/scala/sbt/internal/LibraryManagement.scala
index e6366446d..eaed30432 100644
--- a/main/src/main/scala/sbt/internal/LibraryManagement.scala
+++ b/main/src/main/scala/sbt/internal/LibraryManagement.scala
@@ -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,
diff --git a/main/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala b/main/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala
new file mode 100644
index 000000000..99db26f58
--- /dev/null
+++ b/main/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala
@@ -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 to be split to
+ //
+ //
+ // by the pretty-printer.
+ // See https://github.com/sbt/sbt/issues/3412.
+ val printer = new scala.xml.PrettyPrinter(Int.MaxValue, 2)
+
+ """""" + '\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 =
+
+ urlOpt.fold(n) { url =>
+ n % .attributes
+ }
+ }
+
+ val infoElem = {
+
+ {licenseElems}
+ {project.info.description}
+
+ } % 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 =
+ if (extends1.nonEmpty)
+ n % .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 =
+
+
+ if (pub.classifier.nonEmpty)
+ n % .attributes
+ else
+ n
+ }
+
+ val dependencyElems = project.dependencies.toVector.map {
+ case (conf, dep) =>
+ val excludes = dep.exclusions.toSeq.map {
+ case (org, name) =>
+
+ }
+
+ val n =
+ ${dep.configuration.value}"}>
+ {excludes}
+
+
+ val moduleAttrs = dep.module.attributes.foldLeft[xml.MetaData](xml.Null) {
+ case (acc, (k, v)) =>
+ new PrefixedAttribute("e", k, v, acc)
+ }
+
+ n % moduleAttrs
+ }
+
+
+ {infoElem}
+ {confElems}
+ {publicationElems}
+ {dependencyElems}
+
+ }
+
+ 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))
+
+}
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index 226b6567f..64c9a8f26 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -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
}
diff --git a/sbt/src/sbt-test/dependency-management/artifact/build.sbt b/sbt/src/sbt-test/dependency-management/artifact/build.sbt
index 7a737bbec..5ba1238bf 100644
--- a/sbt/src/sbt-test/dependency-management/artifact/build.sbt
+++ b/sbt/src/sbt-test/dependency-management/artifact/build.sbt
@@ -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") }
}