From f354a626c71e22766b716b2fc643ec1561db6f4b Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 26 Apr 2019 17:33:14 -0400 Subject: [PATCH] use lm-coursier-shaded This uses lm-coursier-shaded, and follows along the changes in https://github.com/coursier/sbt-coursier/pull/58. --- build.sbt | 2 +- main/src/main/scala/sbt/Defaults.scala | 16 +- main/src/main/scala/sbt/Keys.scala | 12 +- main/src/main/scala/sbt/Project.scala | 22 + .../scala/sbt/internal/BuildStructure.scala | 6 + .../main/scala/sbt/internal/LMCoursier.scala | 443 +----------------- .../sbt/internal/LibraryManagement.scala | 2 +- .../CoursierArtifactsTasks.scala | 157 +++++++ .../CoursierInputsTasks.scala | 235 ++++++++++ .../CoursierRepositoriesTasks.scala | 111 +++++ .../internal/librarymanagement/IvyXml.scala | 20 +- project/Dependencies.scala | 4 +- 12 files changed, 570 insertions(+), 460 deletions(-) create mode 100644 main/src/main/scala/sbt/internal/librarymanagement/CoursierArtifactsTasks.scala create mode 100644 main/src/main/scala/sbt/internal/librarymanagement/CoursierInputsTasks.scala create mode 100644 main/src/main/scala/sbt/internal/librarymanagement/CoursierRepositoriesTasks.scala diff --git a/build.sbt b/build.sbt index f517ad661..2bec08fac 100644 --- a/build.sbt +++ b/build.sbt @@ -594,7 +594,7 @@ lazy val mainProj = (project in file("main")) scalaXml.value ++ Seq(launcherInterface) ++ log4jDependencies ++ - Seq(scalaCacheCaffeine, lmCousier) + Seq(scalaCacheCaffeine, lmCoursierShaded) }, Compile / scalacOptions -= "-Xfatal-warnings", managedSourceDirectories in Compile += diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index ceb89bff2..4e0a41e65 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -12,7 +12,7 @@ import java.net.{ URI, URL, URLClassLoader } import java.util.Optional import java.util.concurrent.{ Callable, TimeUnit } -import coursier.core.{ Configuration => CConfiguration } +import lmcoursier.definitions.{ 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 } @@ -230,7 +230,7 @@ object Defaults extends BuildCommon { // coursier settings csrExtraCredentials :== Nil, csrLogger :== None, - csrCachePath :== coursier.cache.CacheDefaults.location, + csrCachePath :== LMCoursier.defaultCacheLocation, csrMavenProfiles :== Set.empty, ) @@ -2136,7 +2136,7 @@ object Classpaths { val old = csrCachePath.value val ip = ivyPaths.value val defaultIvyCache = bootIvyHome(appConfiguration.value) - if (old != coursier.cache.CacheDefaults.location) old + if (old != LMCoursier.defaultCacheLocation) old else if (ip.ivyHome == defaultIvyCache) old else ip.ivyHome match { @@ -2406,13 +2406,13 @@ object Classpaths { } tag (Tags.Update, Tags.Network)).value, ) ) ++ Seq( - csrProject := LMCoursier.coursierProjectTask.value, + csrProject := CoursierInputsTasks.coursierProjectTask.value, csrConfiguration := LMCoursier.coursierConfigurationTask(false, false).value, - csrResolvers := LMCoursier.coursierResolversTask.value, - csrRecursiveResolvers := LMCoursier.coursierRecursiveResolversTask.value, + csrResolvers := CoursierRepositoriesTasks.coursierResolversTask.value, + csrRecursiveResolvers := CoursierRepositoriesTasks.coursierRecursiveResolversTask.value, csrSbtResolvers := LMCoursier.coursierSbtResolversTask.value, - csrInterProjectDependencies := LMCoursier.coursierInterProjectDependenciesTask.value, - csrFallbackDependencies := LMCoursier.coursierFallbackDependenciesTask.value, + csrInterProjectDependencies := CoursierInputsTasks.coursierInterProjectDependenciesTask.value, + csrFallbackDependencies := CoursierInputsTasks.coursierFallbackDependenciesTask.value, ) ++ IvyXml.generateIvyXmlSettings() ++ LMCoursier.publicationsSetting(Seq(Compile, Test).map(c => c -> CConfiguration(c.name))) diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 7464c2f0f..500e02f7c 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -32,8 +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 lmcoursier.definitions.CacheLogger +import lmcoursier.{ CoursierConfiguration, FallbackDependency } import scala.concurrent.duration.{ Duration, FiniteDuration } import scala.xml.{ NodeSeq, Node => XNode } @@ -357,15 +357,15 @@ object Keys { val csrCachePath = settingKey[File]("Coursier cache path").withRank(CSetting) val csrMavenProfiles = settingKey[Set[String]]("").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 csrProject = taskKey[lmcoursier.definitions.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 csrInterProjectDependencies = taskKey[Seq[lmcoursier.definitions.Project]]("Projects the current project depends on, possibly transitively") private[sbt] val csrFallbackDependencies = taskKey[Seq[FallbackDependency]]("") 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)]]("") + private[sbt] val csrExtraCredentials = taskKey[Seq[lmcoursier.credentials.Credentials]]("") + private[sbt] val csrPublications = taskKey[Seq[(lmcoursier.definitions.Configuration, lmcoursier.definitions.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) diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index 9a55a760a..d570c3432 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -758,6 +758,28 @@ object Project extends ProjectExtra { def updateExtraBuilds(s: State, f: List[URI] => List[URI]): State = setExtraBuilds(s, f(extraBuilds(s))) + // used by Coursier integration + private[sbt] def transitiveInterDependencies( + state: State, + projectRef: ProjectRef + ): Seq[ProjectRef] = { + def dependencies(map: Map[ProjectRef, Seq[ProjectRef]], id: ProjectRef): Set[ProjectRef] = { + def helper(map: Map[ProjectRef, Seq[ProjectRef]], acc: Set[ProjectRef]): Set[ProjectRef] = + 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: Map[ProjectRef, Seq[ProjectRef]] = + (for { + (p, ref) <- Project.structure(state).allProjectPairs + } yield ref -> p.dependencies.map(_.project)).toMap + val deps = dependencies(allProjectsDeps.toMap, projectRef) + Project.structure(state).allProjectRefs.filter(p => deps(p)) + } + object LoadAction extends Enumeration { val Return, Current, Plugins = Value } diff --git a/main/src/main/scala/sbt/internal/BuildStructure.scala b/main/src/main/scala/sbt/internal/BuildStructure.scala index a1b556fe5..309e72844 100644 --- a/main/src/main/scala/sbt/internal/BuildStructure.scala +++ b/main/src/main/scala/sbt/internal/BuildStructure.scala @@ -38,6 +38,12 @@ final class BuildStructure( case (build, unit) => refs(build, unit.defined.values.toSeq) } def allProjectRefs(build: URI): Seq[ProjectRef] = refs(build, allProjects(build)) + def allProjectPairs: Seq[(ResolvedProject, ProjectRef)] = + for { + (build, unit) <- units.toSeq + p: ResolvedProject <- unit.defined.values.toSeq + } yield (p, ProjectRef(build, p.id)) + val extra: BuildUtil[ResolvedProject] = BuildUtil(root, units, index.keyIndex, data) private[this] def refs(build: URI, projects: Seq[ResolvedProject]): Seq[ProjectRef] = projects.map { p => diff --git a/main/src/main/scala/sbt/internal/LMCoursier.scala b/main/src/main/scala/sbt/internal/LMCoursier.scala index 57c3505b4..7dec70905 100644 --- a/main/src/main/scala/sbt/internal/LMCoursier.scala +++ b/main/src/main/scala/sbt/internal/LMCoursier.scala @@ -8,54 +8,15 @@ 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.io.IO +import java.io.File +import lmcoursier.definitions.{ Classifier, Configuration => CConfiguration } +import lmcoursier._ import sbt.librarymanagement._ import Keys._ -import sbt.librarymanagement.ivy.{ - FileCredentials, - Credentials, - DirectCredentials => IvyDirectCredentials -} -import sbt.ScopeFilter.Make._ -import scala.collection.JavaConverters._ +import sbt.internal.librarymanagement.{ CoursierArtifactsTasks, CoursierInputsTasks } private[sbt] object LMCoursier { - - def coursierProjectTask: Def.Initialize[sbt.Task[CProject]] = - Def.task { - val auOpt = apiURL.value - val proj = Inputs.coursierProject( - projectID.value, - allDependencies.value, - excludeDependencies.value, - ivyConfigurations.value, - scalaVersion.value, - scalaBinaryVersion.value, - streams.value.log - ) - auOpt match { - case Some(au) => - val props = proj.properties :+ ("info.apiURL" -> au.toString) - proj.copy(properties = props) - case _ => proj - } - } + def defaultCacheLocation: File = CoursierDependencyResolution.defaultCacheLocation def coursierConfigurationTask( withClassifiers: Boolean, @@ -90,7 +51,7 @@ private[sbt] object LMCoursier { _.overrideScalaVersion ) val profiles = csrMavenProfiles.value - val credentials = credentialsTask.value + val credentials = CoursierInputsTasks.credentialsTask.value val createLogger = csrLogger.value @@ -107,11 +68,10 @@ private[sbt] object LMCoursier { .withInterProjectDependencies(interProjectDependencies.toVector) .withFallbackDependencies(fallbackDeps.toVector) .withExcludeDependencies( - excludeDeps.toVector.sorted - .map { - case (o, n) => - (o.value, n.value) - } + excludeDeps.toVector.map { + case (o, n) => + (o.value, n.value) + }.sorted ) .withAutoScalaLibrary(autoScalaLib) .withSbtScalaJars(sbtBootJars.toVector) @@ -129,80 +89,6 @@ private[sbt] object LMCoursier { } } - 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 result0 = resultTask(bootResOpt, overrideFlag).value - val reorderResolvers = true // coursierReorderResolvers.value - val keepPreloaded = false // coursierKeepPreloaded.value - val paths = ivyPaths.value - val result1 = - if (reorderResolvers) ResolutionParams.reorderResolvers(result0) - else result0 - val result2 = - paths.ivyHome match { - case Some(ivyHome) => - val ivyHomeUri = IO.toURI(ivyHome).getSchemeSpecificPart - result1 map { - case r: FileRepository => - val ivyPatterns = r.patterns.ivyPatterns map { - _.replaceAllLiterally("$" + "{ivy.home}", ivyHomeUri) - } - val artifactPatterns = r.patterns.artifactPatterns map { - _.replaceAllLiterally("$" + "{ivy.home}", ivyHomeUri) - } - val p = - r.patterns.withIvyPatterns(ivyPatterns).withArtifactPatterns(artifactPatterns) - r.withPatterns(p) - case r => r - } - case _ => result1 - } - if (keepPreloaded) result2 - else - result2.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 { @@ -234,314 +120,7 @@ private[sbt] object LMCoursier { } } - 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)) + csrPublications := CoursierArtifactsTasks.coursierPublicationsTask(packageConfigs: _*).value } } diff --git a/main/src/main/scala/sbt/internal/LibraryManagement.scala b/main/src/main/scala/sbt/internal/LibraryManagement.scala index eaed30432..a6f12b680 100644 --- a/main/src/main/scala/sbt/internal/LibraryManagement.scala +++ b/main/src/main/scala/sbt/internal/LibraryManagement.scala @@ -8,7 +8,7 @@ package sbt package internal -import coursier.lmcoursier.CoursierDependencyResolution +import lmcoursier.CoursierDependencyResolution import java.io.File import sbt.internal.librarymanagement._ import sbt.internal.util.{ ConsoleAppender, LogOption } diff --git a/main/src/main/scala/sbt/internal/librarymanagement/CoursierArtifactsTasks.scala b/main/src/main/scala/sbt/internal/librarymanagement/CoursierArtifactsTasks.scala new file mode 100644 index 000000000..23e64aae2 --- /dev/null +++ b/main/src/main/scala/sbt/internal/librarymanagement/CoursierArtifactsTasks.scala @@ -0,0 +1,157 @@ +/* + * 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 lmcoursier.definitions.{ + Classifier, + Configuration => CConfiguration, + Extension => CExtension, + Publication => CPublication, + Type => CType +} +import sbt.librarymanagement._ +import sbt.Keys._ + +object CoursierArtifactsTasks { + def coursierPublicationsTask( + configsMap: (sbt.librarymanagement.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 = CrossVersion(projId.crossVersion, sv, sbv) + .fold(artifact.name)(_(artifact.name)) + + CPublication( + name, + CType(artifact.`type`), + CExtension(artifact.extension), + artifact.classifier.fold(Classifier(""))(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 + } +} diff --git a/main/src/main/scala/sbt/internal/librarymanagement/CoursierInputsTasks.scala b/main/src/main/scala/sbt/internal/librarymanagement/CoursierInputsTasks.scala new file mode 100644 index 000000000..e50b1a874 --- /dev/null +++ b/main/src/main/scala/sbt/internal/librarymanagement/CoursierInputsTasks.scala @@ -0,0 +1,235 @@ +/* + * 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 sbt.librarymanagement._ +import sbt.util.Logger +import sbt.Keys._ +import lmcoursier.definitions.{ + Attributes => CAttributes, + Classifier, + Configuration => CConfiguration, + Dependency => CDependency, + // Extension => CExtension, + Info => CInfo, + Module, + ModuleName, + Organization => COrganization, + Project => CProject, + // Publication => CPublication, + Type => CType +} +import lmcoursier.credentials.DirectCredentials +import lmcoursier.{ FallbackDependency, FromSbt, Inputs } +import sbt.librarymanagement.ivy.{ + FileCredentials, + Credentials, + DirectCredentials => IvyDirectCredentials +} +import sbt.ScopeFilter.Make._ +import scala.collection.JavaConverters._ + +private[sbt] object CoursierInputsTasks { + private def coursierProject0( + projId: ModuleID, + dependencies: Seq[ModuleID], + excludeDeps: Seq[InclExclRule], + configurations: Seq[sbt.librarymanagement.Configuration], + sv: String, + sbv: String, + log: Logger + ): CProject = { + + val exclusions0 = Inputs.exclusions(excludeDeps, sv, sbv, log) + + val configMap = Inputs.configExtends(configurations) + + val proj = FromSbt.project( + projId, + dependencies, + configMap, + sv, + sbv + ) + + proj.copy( + dependencies = proj.dependencies.map { + case (config, dep) => + (config, dep.copy(exclusions = dep.exclusions ++ exclusions0)) + } + ) + } + + private[sbt] def coursierProjectTask: Def.Initialize[sbt.Task[CProject]] = + Def.task { + val auOpt = apiURL.value + val proj = coursierProject0( + projectID.value, + allDependencies.value, + allExcludeDependencies.value, + // should projectID.configurations be used instead? + ivyConfigurations.value, + scalaVersion.value, + scalaBinaryVersion.value, + streams.value.log + ) + auOpt match { + case Some(au) => + val props = proj.properties :+ ("info.apiURL" -> au.toString) + proj.copy(properties = props) + case _ => proj + } + } + + 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(Inputs.ivyXmlMappings) + + 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("")) + art.getConfigurations.map(CConfiguration(_)).toVector.map { conf => + conf -> attr + } + }.toMap + + c => m.getOrElse(c, CAttributes(CType(""), Classifier(""))) + } + + configurations.map { + case (from, to) => + from -> dependency(to, attributes(to)) + } + } + + private[sbt] def coursierInterProjectDependenciesTask: Def.Initialize[sbt.Task[Seq[CProject]]] = + Def.taskDyn { + + val state = sbt.Keys.state.value + val projectRef = sbt.Keys.thisProjectRef.value + + val projectRefs = Project.transitiveInterDependencies(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, + Nil, + None, + Nil, + CInfo("", "", Nil, Nil, None) + ) + } + + projects ++ extraProjects + } + } + + private[sbt] def coursierFallbackDependenciesTask + : Def.Initialize[sbt.Task[Seq[FallbackDependency]]] = + Def.taskDyn { + val state = sbt.Keys.state.value + val projectRef = sbt.Keys.thisProjectRef.value + + val projects = Project.transitiveInterDependencies(state, projectRef) + + Def.task { + val allDeps = + allDependencies.all(ScopeFilter(inProjects(projectRef +: projects: _*))).value.flatten + + FromSbt.fallbackDependencies( + allDeps, + scalaVersion.value, + scalaBinaryVersion.value + ) + } + } + + 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)) + .withHttpsOnly(false) + .withMatchHost(true) + } + creds ++ csrExtraCredentials.value + } +} diff --git a/main/src/main/scala/sbt/internal/librarymanagement/CoursierRepositoriesTasks.scala b/main/src/main/scala/sbt/internal/librarymanagement/CoursierRepositoriesTasks.scala new file mode 100644 index 000000000..bccf63fe6 --- /dev/null +++ b/main/src/main/scala/sbt/internal/librarymanagement/CoursierRepositoriesTasks.scala @@ -0,0 +1,111 @@ +/* + * 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 sbt.librarymanagement._ +import sbt.Keys._ +import sbt.ScopeFilter.Make._ + +private[sbt] object CoursierRepositoriesTasks { + private object CResolvers { + private val slowReposBase = Seq( + "https://repo.typesafe.com/", + "https://repo.scala-sbt.org/", + "http://repo.typesafe.com/", + "http://repo.scala-sbt.org/" + ) + + private val fastReposBase = Seq( + "http://repo1.maven.org/", + "https://repo1.maven.org/" + ) + + private def url(res: Resolver): Option[String] = + res match { + case m: sbt.librarymanagement.MavenRepository => + Some(m.root) + case u: URLRepository => + u.patterns.artifactPatterns.headOption + .orElse(u.patterns.ivyPatterns.headOption) + case _ => + None + } + + private def fastRepo(res: Resolver): Boolean = + url(res).exists(u => fastReposBase.exists(u.startsWith)) + + private def slowRepo(res: Resolver): Boolean = + url(res).exists(u => slowReposBase.exists(u.startsWith)) + + def reorderResolvers(resolvers: Seq[Resolver]): Seq[Resolver] = + if (resolvers.exists(fastRepo) && resolvers.exists(slowRepo)) { + val (slow, other) = resolvers.partition(slowRepo) + other ++ slow + } else + resolvers + } + + 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 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 = true // coursierKeepPreloaded.value + + val result0 = + if (reorderResolvers) + CResolvers.reorderResolvers(result) + else + result + + if (keepPreloaded) + result0 + else + result0.filter { r => + !r.name.startsWith("local-preloaded") + } + } + } + + def coursierRecursiveResolversTask: Def.Initialize[sbt.Task[Seq[Resolver]]] = + Def.taskDyn { + val state = sbt.Keys.state.value + val projectRef = sbt.Keys.thisProjectRef.value + val projects = Project.transitiveInterDependencies(state, projectRef) + Def.task { + csrResolvers.all(ScopeFilter(inProjects(projectRef +: projects: _*))).value.flatten + } + } +} diff --git a/main/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala b/main/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala index 7cb58451a..966d0fb6e 100644 --- a/main/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala +++ b/main/src/main/scala/sbt/internal/librarymanagement/IvyXml.scala @@ -12,7 +12,7 @@ package librarymanagement import java.nio.charset.StandardCharsets.UTF_8 import java.nio.file.Files -import coursier.core.{ Configuration, Project } +import lmcoursier.definitions.{ Configuration, Project } import org.apache.ivy.core.module.id.ModuleRevisionId import Def.Setting import sbt.Keys.{ @@ -29,7 +29,7 @@ import scala.xml.{ Node, PrefixedAttribute } object IvyXml { import sbt.Project._ - def rawContent( + private def rawContent( currentProject: Project, shadedConfigOpt: Option[Configuration] ): String = { @@ -47,7 +47,7 @@ object IvyXml { } // These are required for publish to be fine, later on. - def writeFiles( + private def writeFiles( currentProject: Project, shadedConfigOpt: Option[Configuration], ivySbt: IvySbt, @@ -77,12 +77,12 @@ object IvyXml { () } - def content(project0: Project, shadedConfigOpt: Option[Configuration]): Node = { + private def content(project0: Project, shadedConfigOpt: Option[Configuration]): Node = { val filterOutDependencies = shadedConfigOpt.toSet[Configuration].flatMap { shadedConfig => project0.dependencies - .collect { case (`shadedConfig`, dep) => dep } + .collect { case (conf, dep) if conf.value == shadedConfig.value => dep } } val project: Project = project0.copy( @@ -118,8 +118,8 @@ object IvyXml { } % infoAttrs val confElems = project.configurations.toVector.collect { - case (name, extends0) if !shadedConfigOpt.contains(name) => - val extends1 = shadedConfigOpt.fold(extends0)(c => extends0.filter(_ != c)) + case (name, extends0) if !shadedConfigOpt.exists(_.value == name.value) => + val extends1 = shadedConfigOpt.fold(extends0)(c => extends0.filter(_.value != c.value)) val n = if (extends1.nonEmpty) n % .attributes @@ -138,7 +138,7 @@ object IvyXml { configs.map(_.value).mkString(",") } /> - if (pub.classifier.nonEmpty) + if (pub.classifier.value.nonEmpty) n % .attributes else n @@ -174,7 +174,7 @@ object IvyXml { } - def makeIvyXmlBefore[T]( + private def makeIvyXmlBefore[T]( task: TaskKey[T], shadedConfigOpt: Option[Configuration] ): Setting[Task[T]] = @@ -220,6 +220,6 @@ object IvyXml { def generateIvyXmlSettings( shadedConfigOpt: Option[Configuration] = None ): Seq[Setting[_]] = - (needsIvyXml ++ needsIvyXmlLocal).map(IvyXml.makeIvyXmlBefore(_, shadedConfigOpt)) + (needsIvyXml ++ needsIvyXmlLocal).map(makeIvyXmlBefore(_, shadedConfigOpt)) } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 64c9a8f26..ebc440cca 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -100,8 +100,8 @@ 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 lmCoursierVersion = "1.1.0-M14-1" + val lmCoursierShaded = "io.get-coursier" %% "lm-coursier-shaded" % lmCoursierVersion val sjsonNewScalaJson = Def.setting { "com.eed3si9n" %% "sjson-new-scalajson" % contrabandSjsonNewVersion.value