[2.x] refactor: Extract lm-ivy to separate sbt-ivy plugin module (#8873)

Step 5 of #7640 — removes the compile-time dependency on lm-ivy from main/ by creating a standalone sbt-ivy plugin module.

- Create new sbt-ivy/ subproject with IvyDependencyPlugin AutoPlugin that provides all Ivy-specific functionality (ivySbt, ivyModule, ivyConfiguration, publisher, projectDescriptors, deliver/makeIvyXml)
- Move IvyXml.scala from main/ to sbt-ivy/
This commit is contained in:
Dream 2026-03-10 23:30:06 -04:00 committed by GitHub
parent 4f4bc374f6
commit d71fe5b7a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 818 additions and 288 deletions

View File

@ -156,6 +156,7 @@ jobs:
./sbt -v --client "scripted dependency-management/* project-load/* project-matrix/* java/* run/*" ./sbt -v --client "scripted dependency-management/* project-load/* project-matrix/* java/* run/*"
./sbt -v --client "scripted plugins/*" ./sbt -v --client "scripted plugins/*"
./sbt -v --client "scripted nio/*" ./sbt -v --client "scripted nio/*"
./sbt -v --client "scripted ivy/*"
- name: Build and test (4) - name: Build and test (4)
if: ${{ matrix.jobtype == 4 }} if: ${{ matrix.jobtype == 4 }}
shell: bash shell: bash

View File

@ -690,7 +690,7 @@ lazy val buildFileProj = (project in file("buildfile"))
libraryDependencies ++= Seq(scalaCompiler), libraryDependencies ++= Seq(scalaCompiler),
mimaSettings, mimaSettings,
) )
.dependsOn(lmCore, lmIvy) .dependsOn(lmCore)
.configure(addSbtIO, addSbtCompilerInterface, addSbtZincCompileCore) .configure(addSbtIO, addSbtCompilerInterface, addSbtZincCompileCore)
// The main integration project for sbt. It brings all of the projects together, configures them, and provides for overriding conventions. // The main integration project for sbt. It brings all of the projects together, configures them, and provides for overriding conventions.
@ -734,11 +734,30 @@ lazy val mainProj = (project in file("main"))
Compile / doc / sources := Nil, Compile / doc / sources := Nil,
mimaSettings, mimaSettings,
mimaBinaryIssueFilters ++= Vector( mimaBinaryIssueFilters ++= Vector(
// Moved to sbt-ivy module (Step 5 of sbt#7640)
exclude[DirectMissingMethodProblem]("sbt.Classpaths.depMap"),
exclude[DirectMissingMethodProblem]("sbt.Classpaths.ivySbt0"),
exclude[DirectMissingMethodProblem]("sbt.Classpaths.mkIvyConfiguration"),
exclude[MissingClassProblem]("sbt.internal.librarymanagement.IvyXml"),
exclude[MissingClassProblem]("sbt.internal.librarymanagement.IvyXml$"),
), ),
) )
.dependsOn(lmCore, lmIvy, lmCoursierShadedPublishing) .dependsOn(lmCore, lmCoursierShadedPublishing)
.configure(addSbtIO, addSbtCompilerInterface, addSbtZincCompileCore) .configure(addSbtIO, addSbtCompilerInterface, addSbtZincCompileCore)
lazy val sbtIvyProj = (project in file("sbt-ivy"))
.dependsOn(sbtProj, lmIvy)
.settings(
testedBaseSettings,
name := "sbt-ivy",
sbtPlugin := true,
pluginCrossBuild / sbtVersion := version.value,
// TODO: Fix doc
Compile / doc / sources := Nil,
mimaPreviousArtifacts := Set.empty, // new module, no previous artifacts
)
.configure(addSbtIO)
// Strictly for bringing implicits and aliases from subsystems into the top-level sbt namespace through a single package object // Strictly for bringing implicits and aliases from subsystems into the top-level sbt namespace through a single package object
// technically, we need a dependency on all of mainProj's dependencies, but we don't do that since this is strictly an integration project // technically, we need a dependency on all of mainProj's dependencies, but we don't do that since this is strictly an integration project
// with the sole purpose of providing certain identifiers without qualification (with a package object) // with the sole purpose of providing certain identifiers without qualification (with a package object)
@ -993,6 +1012,7 @@ def allProjects =
mainSettingsProj, mainSettingsProj,
zincLmIntegrationProj, zincLmIntegrationProj,
mainProj, mainProj,
sbtIvyProj,
sbtProj, sbtProj,
bundledLauncherProj, bundledLauncherProj,
sbtClientProj, sbtClientProj,

View File

@ -2,6 +2,7 @@ package sbt.internal.librarymanagement
import java.io.File import java.io.File
import sbt.librarymanagement.* import sbt.librarymanagement.*
import sbt.librarymanagement.syntax.*
object UpdateClassifiersUtil { object UpdateClassifiersUtil {
@ -76,4 +77,26 @@ object UpdateClassifiersUtil {
.withExplicitArtifacts(arts) .withExplicitArtifacts(arts)
.withInclusions(Vector(InclExclRule.everything)) .withInclusions(Vector(InclExclRule.everything))
def extractExcludes(report: UpdateReport): Map[ModuleID, Set[String]] =
report.allMissing
.flatMap { case (_, mod, art) =>
art.classifier.map { c =>
(restrictedCopy(mod, false), c)
}
}
.groupBy(_._1)
.map { (mod, pairs) => (mod, pairs.map(_._2).toSet) }
def addExcluded(
report: UpdateReport,
classifiers: Vector[String],
exclude: Map[ModuleID, Set[String]]
): UpdateReport =
report.addMissing { id =>
classifiedArtifacts(id.name, classifiers.filter(getExcluded(id, exclude)))
}
private def getExcluded(id: ModuleID, exclude: Map[ModuleID, Set[String]]): Set[String] =
exclude.getOrElse(restrictedCopy(id, false), Set.empty[String])
} }

View File

@ -0,0 +1,63 @@
/*
* sbt
* Copyright 2023, Scala center
* Copyright 2011 - 2022, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package librarymanagement
import java.io.File
import sbt.io.IO
/** Credential-loading utilities that work with sbt.librarymanagement.Credentials without Ivy. */
object CredentialUtils:
def forHost(sc: Seq[Credentials], host: String): Option[Credentials.DirectCredentials] =
allDirect(sc).find(_.host == host)
def allDirect(sc: Seq[Credentials]): Seq[Credentials.DirectCredentials] =
sc.map(toDirect)
def toDirect(c: Credentials): Credentials.DirectCredentials = c match
case dc: Credentials.DirectCredentials => dc
case fc: Credentials.FileCredentials =>
loadCredentials(fc.path) match
case Left(err) => sys.error(err)
case Right(dc) => dc
def loadCredentials(path: File): Either[String, Credentials.DirectCredentials] =
if !path.exists then Left("Credentials file " + path + " does not exist")
else
val props = read(path)
for
host <- lookup(props, HostKeys, path)
user <- lookup(props, UserKeys, path)
pass <- lookup(props, PasswordKeys, path)
yield
val realm = props.keysIterator.find(RealmKeys.contains).flatMap(props.get).orNull
new Credentials.DirectCredentials(realm, host, user, pass)
private val RealmKeys = Set("realm")
private val HostKeys = List("host", "hostname")
private val UserKeys = List("user", "user.name", "username")
private val PasswordKeys = List("password", "pwd", "pass", "passwd")
private def lookup(
props: Map[String, String],
keys: List[String],
path: File
): Either[String, String] =
keys
.flatMap(props.get)
.headOption
.toRight(s"${keys.head} not specified in credentials file: $path")
import scala.jdk.CollectionConverters.*
private def read(from: File): Map[String, String] =
val properties = new java.util.Properties
IO.load(properties, from)
properties.asScala.map((k, v) => (k, v.trim)).toMap
end CredentialUtils

View File

@ -14,8 +14,6 @@ import java.util.{ Optional, UUID }
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import lmcoursier.CoursierDependencyResolution import lmcoursier.CoursierDependencyResolution
import lmcoursier.definitions.{ Configuration as CConfiguration } import lmcoursier.definitions.{ Configuration as CConfiguration }
import org.apache.ivy.core.module.descriptor.ModuleDescriptor
import org.apache.ivy.core.module.id.ModuleRevisionId
import org.scalasbt.ipcsocket.Win32SecurityLevel import org.scalasbt.ipcsocket.Win32SecurityLevel
import sbt.Def.{ Initialize, ScopedKey, Setting, SettingsDefinition, parsed } import sbt.Def.{ Initialize, ScopedKey, Setting, SettingsDefinition, parsed }
import sbt.Keys.* import sbt.Keys.*
@ -33,7 +31,6 @@ import sbt.internal.classpath.AlternativeZincUtil
import sbt.internal.inc.JavaInterfaceUtil.* import sbt.internal.inc.JavaInterfaceUtil.*
import sbt.internal.inc.classpath.ClasspathFilter import sbt.internal.inc.classpath.ClasspathFilter
import sbt.internal.inc.{ CompileOutput, MappedFileConverter, Stamps, ZincLmUtil, ZincUtil } import sbt.internal.inc.{ CompileOutput, MappedFileConverter, Stamps, ZincLmUtil, ZincUtil }
import sbt.internal.librarymanagement.ivy.*
import sbt.internal.librarymanagement.mavenint.{ import sbt.internal.librarymanagement.mavenint.{
PomExtraDependencyAttributes, PomExtraDependencyAttributes,
SbtPomExtraProperties SbtPomExtraProperties
@ -281,7 +278,7 @@ object Defaults extends BuildCommon {
pomPostProcess :== idFun, pomPostProcess :== idFun,
pomAllRepositories :== false, pomAllRepositories :== false,
pomIncludeRepository :== Classpaths.defaultRepositoryFilter, pomIncludeRepository :== Classpaths.defaultRepositoryFilter,
updateOptions := UpdateOptions(), updateOptions := None, // overridden by IvyDependencyPlugin with UpdateOptions()
forceUpdatePeriod :== None, forceUpdatePeriod :== None,
platform :== Platform.jvm, platform :== Platform.jvm,
// coursier settings // coursier settings
@ -2851,10 +2848,10 @@ object Classpaths {
val nameWithCross = crossVersion(artifact.value.name) val nameWithCross = crossVersion(artifact.value.name)
val version = Keys.version.value val version = Keys.version.value
val pomFile = config.file.get.getParentFile / s"$nameWithCross-$version.pom" val pomFile = config.file.get.getParentFile / s"$nameWithCross-$version.pom"
val publisher = Keys.publisher.value val pub = Keys.publisher.value
val ivySbt = Keys.ivySbt.value val module =
val module = new ivySbt.Module(moduleSettings.value, appendSbtCrossVersion = true) pub.moduleDescriptor(moduleSettings.value.asInstanceOf[ModuleDescriptorConfiguration])
publisher.makePomFile(module, config.withFile(pomFile), streams.value.log) pub.makePomFile(module, config.withFile(pomFile), streams.value.log)
converter.toVirtualFile(pomFile.toPath) converter.toVirtualFile(pomFile.toPath)
def ivyPublishSettings: Seq[Setting[?]] = publishGlobalDefaults ++ Seq( def ivyPublishSettings: Seq[Setting[?]] = publishGlobalDefaults ++ Seq(
@ -2863,8 +2860,10 @@ object Classpaths {
makePom := Def.uncached { makePom := Def.uncached {
val converter = fileConverter.value val converter = fileConverter.value
val config = makePomConfiguration.value val config = makePomConfiguration.value
val publisher = Keys.publisher.value val pub = Keys.publisher.value
publisher.makePomFile(ivyModule.value, config, streams.value.log) val module =
pub.moduleDescriptor(moduleSettings.value.asInstanceOf[ModuleDescriptorConfiguration])
pub.makePomFile(module, config, streams.value.log)
converter.toVirtualFile(config.file.get.toPath()) converter.toVirtualFile(config.file.get.toPath())
}, },
(makePom / packagedArtifact) := Def.uncached((makePom / artifact).value -> makePom.value), (makePom / packagedArtifact) := Def.uncached((makePom / artifact).value -> makePom.value),
@ -3135,7 +3134,7 @@ object Classpaths {
makePom / artifact := Artifact.pom(moduleName.value), makePom / artifact := Artifact.pom(moduleName.value),
projectID := defaultProjectID.value, projectID := defaultProjectID.value,
projectID := pluginProjectID.value, projectID := pluginProjectID.value,
projectDescriptors := Def.uncached(depMap.value), projectDescriptors := Def.uncached(Map.empty[Any, Any]),
updateConfiguration := { updateConfiguration := {
// Tell the UpdateConfiguration which artifact types are special (for sources and javadocs) // Tell the UpdateConfiguration which artifact types are special (for sources and javadocs)
val specialArtifactTypes = sourceArtifactTypes.value.toSet union docArtifactTypes.value.toSet val specialArtifactTypes = sourceArtifactTypes.value.toSet union docArtifactTypes.value.toSet
@ -3158,8 +3157,7 @@ object Classpaths {
else None else None
}, },
dependencyResolution := Def.uncached(dependencyResolutionTask.value), dependencyResolution := Def.uncached(dependencyResolutionTask.value),
publisher := Def.uncached(IvyPublisher(ivyConfiguration.value)), ivyConfiguration := Def.uncached((): Any),
ivyConfiguration := Def.uncached(mkIvyConfiguration.value),
ivyConfigurations := { ivyConfigurations := {
val confs = thisProject.value.configurations val confs = thisProject.value.configurations
(confs ++ confs.map(internalConfigurationMap.value) ++ (if (autoCompilerPlugins.value) (confs ++ confs.map(internalConfigurationMap.value) ++ (if (autoCompilerPlugins.value)
@ -3302,8 +3300,11 @@ object Classpaths {
overwrite = isSnapshot.value overwrite = isSnapshot.value
) )
}, },
ivySbt := Def.uncached(ivySbt0.value), ivySbt := Def.uncached((): Any),
ivyModule := Def.uncached { val is = ivySbt.value; new is.Module(moduleSettings.value) }, ivyModule := Def.uncached((): Any),
publisher := Def.uncached(
Classpaths.defaultPublisher(dependencyResolution.value, fullResolvers.value.toVector)
),
allCredentials := Def.uncached(LMCoursier.allCredentialsTask.value), allCredentials := Def.uncached(LMCoursier.allCredentialsTask.value),
transitiveUpdate := Def.uncached(transitiveUpdateTask.value), transitiveUpdate := Def.uncached(transitiveUpdateTask.value),
updateCacheName := { updateCacheName := {
@ -3380,7 +3381,6 @@ object Classpaths {
CoursierInputsTasks.coursierFallbackDependenciesTask.value CoursierInputsTasks.coursierFallbackDependenciesTask.value
), ),
) ++ ) ++
IvyXml.generateIvyXmlSettings() ++
LMCoursier.publicationsSetting(Seq(Compile, Test).map(c => c -> CConfiguration(c.name))) LMCoursier.publicationsSetting(Seq(Compile, Test).map(c => c -> CConfiguration(c.name)))
def jvmBaseSettings: Seq[Setting[?]] = Seq( def jvmBaseSettings: Seq[Setting[?]] = Seq(
@ -3496,11 +3496,6 @@ object Classpaths {
) )
else projectID.value else projectID.value
} }
private[sbt] lazy val ivySbt0: Initialize[Task[IvySbt]] =
Def.task {
IvyCredentials.register(credentials.value, streams.value.log)
new IvySbt(ivyConfiguration.value)
}
def moduleSettings0: Initialize[Task[ModuleSettings]] = Def.task { def moduleSettings0: Initialize[Task[ModuleSettings]] = Def.task {
val deps = allDependencies.value.toVector val deps = allDependencies.value.toVector
errorInsecureProtocolInModules(deps, streams.value.log) errorInsecureProtocolInModules(deps, streams.value.log)
@ -3536,21 +3531,6 @@ object Classpaths {
.resolvers .resolvers
explicit orElse boot getOrElse externalResolvers.value explicit orElse boot getOrElse externalResolvers.value
}, },
ivyConfiguration := Def.uncached(
InlineIvyConfiguration(
lock = Option(lock(appConfiguration.value)),
log = Option(streams.value.log),
updateOptions = UpdateOptions(),
paths = Option(ivyPaths.value),
resolvers = externalResolvers.value.toVector,
otherResolvers = Vector.empty,
moduleConfigurations = Vector.empty,
checksums = checksums.value.toVector,
managedChecksums = false,
resolutionCacheDir = Some(target.value / "resolution-cache"),
)
),
ivySbt := Def.uncached(ivySbt0.value),
classifiersModule := Def.uncached(classifiersModuleTask.value), classifiersModule := Def.uncached(classifiersModuleTask.value),
// Redefine scalaVersion and scalaBinaryVersion specifically for the dependency graph used for updateSbtClassifiers task. // Redefine scalaVersion and scalaBinaryVersion specifically for the dependency graph used for updateSbtClassifiers task.
// to fix https://github.com/sbt/sbt/issues/2686 // to fix https://github.com/sbt/sbt/issues/2686
@ -3583,7 +3563,6 @@ object Classpaths {
.task { .task {
val lm = dependencyResolution.value val lm = dependencyResolution.value
val s = streams.value val s = streams.value
val is = ivySbt.value
val mod = classifiersModule.value val mod = classifiersModule.value
val updateConfig0 = updateConfiguration.value val updateConfig0 = updateConfiguration.value
val updateConfig = updateConfig0 val updateConfig = updateConfig0
@ -3596,7 +3575,9 @@ object Classpaths {
val srcTypes = sourceArtifactTypes.value val srcTypes = sourceArtifactTypes.value
val docTypes = docArtifactTypes.value val docTypes = docArtifactTypes.value
val log = s.log val log = s.log
val out = is.withIvy(log)(_.getSettings.getDefaultIvyUserDir) val out = ivyPaths.value.ivyHome
.map(new File(_))
.getOrElse(new File(System.getProperty("user.home"), ".ivy2"))
val uwConfig = (update / unresolvedWarningConfiguration).value val uwConfig = (update / unresolvedWarningConfiguration).value
withExcludes(out, mod.classifiers, lock(app)) { excludes => withExcludes(out, mod.classifiers, lock(app)) { excludes =>
// val noExplicitCheck = ivy.map(_.withCheckExplicit(false)) // val noExplicitCheck = ivy.map(_.withCheckExplicit(false))
@ -3703,9 +3684,9 @@ object Classpaths {
def deliverTask(config: TaskKey[PublishConfiguration]): Initialize[Task[File]] = def deliverTask(config: TaskKey[PublishConfiguration]): Initialize[Task[File]] =
Def.task { Def.task {
Def.unit(update.value) sys.error(
if !useIvy.value then sys.error("deliver/makeIvyXml requires useIvy := true") "deliver/makeIvyXml requires the sbt-ivy plugin. Add IvyDependencyPlugin to your project."
IvyActions.deliver(ivyModule.value, config.value, streams.value.log) )
} }
@deprecated("Use variant without delivery key", "1.1.1") @deprecated("Use variant without delivery key", "1.1.1")
@ -3736,16 +3717,10 @@ object Classpaths {
val log = streams.value.log val log = streams.value.log
val ref = thisProjectRef.value val ref = thisProjectRef.value
logSkipPublish(log, ref) logSkipPublish(log, ref)
} else if (!useIvy.value) {
sys.error(
"publishOrSkip requires useIvy := true. Use publish/publishLocal for ivyless publishing."
)
} else { } else {
val conf = config.value sys.error(
val log = streams.value.log "publishOrSkip requires the sbt-ivy plugin. Use publish/publishLocal for ivyless publishing."
val module = ivyModule.value )
val publisherInterface = publisher.value
publisherInterface.publish(module, conf, log)
} }
} }
.tag(Tags.Publish, Tags.Network) .tag(Tags.Publish, Tags.Network)
@ -4062,17 +4037,6 @@ object Classpaths {
f(libraryDependencies.value) f(libraryDependencies.value)
} }
/*
// can't cache deliver/publish easily since files involved are hidden behind patterns. publish will be difficult to verify target-side anyway
def cachedPublish(cacheFile: File)(g: (IvySbt#Module, PublishConfiguration) => Unit, module: IvySbt#Module, config: PublishConfiguration) => Unit =
{ case module :+: config :+: HNil =>
/* implicit val publishCache = publishIC
val f = cached(cacheFile) { (conf: IvyConfiguration, settings: ModuleSettings, config: PublishConfiguration) =>*/
g(module, config)
/*}
f(module.owner.configuration :+: module.moduleSettings :+: config :+: HNil)*/
}*/
def defaultRepositoryFilter: MavenRepository => Boolean = repo => !repo.root.startsWith("file:") def defaultRepositoryFilter: MavenRepository => Boolean = repo => !repo.root.startsWith("file:")
def getPublishTo(repo: Option[Resolver]): Resolver = def getPublishTo(repo: Option[Resolver]): Resolver =
@ -4195,31 +4159,62 @@ object Classpaths {
depProjId.withConfigurations(dep.configuration).withExplicitArtifacts(Vector.empty) depProjId.withConfigurations(dep.configuration).withExplicitArtifacts(Vector.empty)
} }
private[sbt] def depMap: Initialize[Task[Map[ModuleRevisionId, ModuleDescriptor]]] =
import sbt.TupleSyntax.*
(buildDependencies.toTaskable, thisProjectRef.toTaskable, settingsData, streams)
.flatMapN { (bd, thisProj, data, s) =>
depMap(bd.classpathTransitiveRefs(thisProj), data, s.log)
}
private[sbt] def depMap(
projects: Seq[ProjectRef],
data: Def.Settings,
log: Logger
): Task[Map[ModuleRevisionId, ModuleDescriptor]] =
val ivyModules = projects.flatMap { proj =>
(proj / ivyModule).get(data)
}.join
ivyModules.mapN { mod =>
mod.map { _.dependencyMapping(log) }.toMap
}
def projectResolverTask: Initialize[Task[Resolver]] = def projectResolverTask: Initialize[Task[Resolver]] =
projectDescriptors.map { m => Def.task {
val resolver = new ProjectResolver(ProjectResolver.InterProject, m) // Stub resolver Coursier handles inter-project deps via csrInterProjectDependencies.
new RawRepository(resolver, resolver.getName) // Overridden by IvyDependencyPlugin with a real ProjectResolver.
new RawRepository(NoOpResolver, NoOpResolver.name)
} }
private object NoOpResolver:
val name = "inter-project"
override def toString: String = name
/** Default publisher that delegates moduleDescriptor to Coursier and generates POM without Ivy. */
private[sbt] def defaultPublisher(
lm: DependencyResolution,
resolvers: Vector[Resolver] = Vector.empty,
): Publisher =
Publisher(new PublisherInterface {
def moduleDescriptor(moduleSetting: ModuleDescriptorConfiguration): ModuleDescriptor =
lm.moduleDescriptor(moduleSetting)
def publish(
module: ModuleDescriptor,
configuration: PublishConfiguration,
log: Logger
): Unit =
sys.error("Ivy-based publish requires the sbt-ivy plugin or useIvy := true")
def makePomFile(
module: ModuleDescriptor,
configuration: MakePomConfiguration,
log: Logger
): java.io.File =
val file = configuration.file.getOrElse(sys.error("makePom file must be specified."))
val ms = module.moduleSettings.asInstanceOf[ModuleDescriptorConfiguration]
val mid = ms.module
val info = configuration.moduleInfo.orElse(Option(ms.moduleInfo))
val deps = module.directDependencies
val extra = configuration.extra.getOrElse(scala.xml.NodeSeq.Empty)
val confs = configuration.configurations
val scalaInfo = ms.scalaModuleInfo
val pomXml =
sbt.internal.PomGenerator.makePom(
mid,
info,
deps,
confs,
extra,
scalaInfo,
resolvers,
configuration.filterRepositories,
configuration.allRepositories,
)
val processed = configuration.process(pomXml)
scala.xml.XML.save(file.getAbsolutePath, processed, "UTF-8", xmlDecl = true)
log.info("Wrote " + file.getAbsolutePath)
file
})
def makeProducts: Initialize[Task[Seq[File]]] = Def.task { def makeProducts: Initialize[Task[Seq[File]]] = Def.task {
val c = fileConverter.value val c = fileConverter.value
val resourceDirs = resourceDirectories.value val resourceDirs = resourceDirectories.value
@ -4247,23 +4242,6 @@ object Classpaths {
def internalDependencyJarsTask: Initialize[Task[Classpath]] = def internalDependencyJarsTask: Initialize[Task[Classpath]] =
ClasspathImpl.internalDependencyJarsTask ClasspathImpl.internalDependencyJarsTask
lazy val mkIvyConfiguration: Initialize[Task[InlineIvyConfiguration]] =
Def.task {
val (rs, other) = (fullResolvers.value.toVector, otherResolvers.value.toVector)
val s = streams.value
warnResolversConflict(rs ++: other, s.log)
errorInsecureProtocol(rs ++: other, s.log)
InlineIvyConfiguration()
.withPaths(ivyPaths.value)
.withResolvers(rs)
.withOtherResolvers(other)
.withModuleConfigurations(moduleConfigurations.value.toVector)
.withLock(lock(appConfiguration.value))
.withChecksums((update / checksums).value.toVector)
.withResolutionCacheDir(target.value / "resolution-cache")
.withUpdateOptions(updateOptions.value)
.withLog(s.log)
}
def interSort( def interSort(
projectRef: ProjectRef, projectRef: ProjectRef,
@ -4474,7 +4452,7 @@ object Classpaths {
try { try {
app.provider.scalaProvider.launcher.checksums.toVector app.provider.scalaProvider.launcher.checksums.toVector
} catch { } catch {
case _: NoSuchMethodError => IvySbt.DefaultChecksums case _: NoSuchMethodError => Vector("sha1", "md5")
} }
def isOverrideRepositories(app: xsbti.AppConfiguration): Boolean = def isOverrideRepositories(app: xsbti.AppConfiguration): Boolean =

View File

@ -13,16 +13,13 @@ import java.io.File
import java.net.URI import java.net.URI
import lmcoursier.definitions.{ CacheLogger, ModuleMatchers, Reconciliation } import lmcoursier.definitions.{ CacheLogger, ModuleMatchers, Reconciliation }
import lmcoursier.{ CoursierConfiguration, FallbackDependency } import lmcoursier.{ CoursierConfiguration, FallbackDependency }
import org.apache.ivy.core.module.descriptor.ModuleDescriptor
import org.apache.ivy.core.module.id.ModuleRevisionId
import sbt.Def.* import sbt.Def.*
import sbt.KeyRanks.* import sbt.KeyRanks.*
import sbt.internal.InMemoryCacheStore.CacheStoreFactoryFactory import sbt.internal.InMemoryCacheStore.CacheStoreFactoryFactory
import sbt.internal.* import sbt.internal.*
import sbt.internal.bsp.* import sbt.internal.bsp.*
import sbt.internal.inc.ScalaInstance import sbt.internal.inc.ScalaInstance
import sbt.internal.librarymanagement.{ CompatibilityWarningOptions, IvySbt } import sbt.internal.librarymanagement.CompatibilityWarningOptions
import sbt.internal.librarymanagement.ivy.{ IvyConfiguration, UpdateOptions }
import sbt.internal.server.BuildServerProtocol.BspFullWorkspace import sbt.internal.server.BuildServerProtocol.BspFullWorkspace
import sbt.internal.server.{ BspCompileTask, BuildServerReporter, ServerHandler } import sbt.internal.server.{ BspCompileTask, BuildServerReporter, ServerHandler }
import sbt.internal.util.{ AttributeKey, ProgressState, SourcePosition } import sbt.internal.util.{ AttributeKey, ProgressState, SourcePosition }
@ -519,16 +516,16 @@ object Keys {
val internalConfigurationMap = settingKey[Configuration => Configuration]("Maps configurations to the actual configuration used to define the classpath.").withRank(CSetting) 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 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) val ivyConfiguration = taskKey[Any]("General dependency management (Ivy) settings, such as the resolvers and paths to use.").withRank(DTask)
val ivyConfigurations = settingKey[Seq[Configuration]]("The defined configurations for dependency management. This may be different from the configurations for Project settings.").withRank(BSetting) val ivyConfigurations = settingKey[Seq[Configuration]]("The defined configurations for dependency management. This may be different from the configurations for Project settings.").withRank(BSetting)
// This setting was created to work around the limitation of derived tasks not being able to use task-scoped task: ivyConfiguration in updateSbtClassifiers // This setting was created to work around the limitation of derived tasks not being able to use task-scoped task: ivyConfiguration in updateSbtClassifiers
val bootIvyConfiguration = taskKey[IvyConfiguration]("General dependency management (Ivy) settings, configured to retrieve sbt's components.").withRank(DTask) val bootIvyConfiguration = taskKey[Any]("General dependency management (Ivy) settings, configured to retrieve sbt's components.").withRank(DTask)
val bootDependencyResolution = taskKey[DependencyResolution]("Dependency resolution to retrieve sbt's components.").withRank(CTask) val bootDependencyResolution = taskKey[DependencyResolution]("Dependency resolution to retrieve sbt's components.").withRank(CTask)
val scalaCompilerBridgeDependencyResolution = taskKey[DependencyResolution]("Dependency resolution to retrieve the compiler bridge.").withRank(CTask) val scalaCompilerBridgeDependencyResolution = taskKey[DependencyResolution]("Dependency resolution to retrieve the compiler bridge.").withRank(CTask)
val moduleSettings = taskKey[ModuleSettings]("Module settings, which configure dependency management for a specific module, such as a project.").withRank(DTask) val moduleSettings = taskKey[ModuleSettings]("Module settings, which configure dependency management for a specific module, such as a project.").withRank(DTask)
val unmanagedBase = settingKey[File]("The default directory for manually managed libraries.").withRank(ASetting) val unmanagedBase = settingKey[File]("The default directory for manually managed libraries.").withRank(ASetting)
val updateConfiguration = settingKey[UpdateConfiguration]("Configuration for resolving and retrieving managed dependencies.").withRank(DSetting) val updateConfiguration = settingKey[UpdateConfiguration]("Configuration for resolving and retrieving managed dependencies.").withRank(DSetting)
val updateOptions = settingKey[UpdateOptions]("Options for resolving managed dependencies.").withRank(DSetting) val updateOptions = settingKey[Any]("Options for resolving managed dependencies.").withRank(DSetting)
@transient @transient
val unresolvedWarningConfiguration = taskKey[UnresolvedWarningConfiguration]("Configuration for unresolved dependency warning.").withRank(DTask) val unresolvedWarningConfiguration = taskKey[UnresolvedWarningConfiguration]("Configuration for unresolved dependency warning.").withRank(DTask)
@ -537,8 +534,8 @@ object Keys {
@transient @transient
val dependencyResolution = taskKey[DependencyResolution]("Provides the sbt interface to dependency resolution.").withRank(CTask) val dependencyResolution = taskKey[DependencyResolution]("Provides the sbt interface to dependency resolution.").withRank(CTask)
val publisher = taskKey[Publisher]("Provides the sbt interface to publisher") val publisher = taskKey[Publisher]("Provides the sbt interface to publisher")
val ivySbt = taskKey[IvySbt]("Provides the sbt interface to Ivy.").withRank(CTask) val ivySbt = taskKey[Any]("Provides the sbt interface to Ivy.").withRank(CTask)
val ivyModule = taskKey[IvySbt#Module]("Provides the sbt interface to a configured Ivy module.").withRank(CTask) val ivyModule = taskKey[Any]("Provides the sbt interface to a configured Ivy module.").withRank(CTask)
val updateCacheName = taskKey[String]("Defines the directory name used to store the update cache files (inside the streams cacheDirectory).").withRank(DTask) val updateCacheName = taskKey[String]("Defines the directory name used to store the update cache files (inside the streams cacheDirectory).").withRank(DTask)
val update = taskKey[UpdateReport]("Resolves and optionally retrieves dependencies, producing a report.").withRank(ATask) val update = taskKey[UpdateReport]("Resolves and optionally retrieves dependencies, producing a report.").withRank(ATask)
val updateFull = taskKey[UpdateReport]("Resolves and optionally retrieves dependencies, producing a full report with callers.").withRank(CTask) val updateFull = taskKey[UpdateReport]("Resolves and optionally retrieves dependencies, producing a full report with callers.").withRank(CTask)
@ -638,7 +635,7 @@ object Keys {
@transient @transient
val publishTo = taskKey[Option[Resolver]]("The resolver to publish to.").withRank(ASetting) val publishTo = taskKey[Option[Resolver]]("The resolver to publish to.").withRank(ASetting)
val artifacts = settingKey[Seq[Artifact]]("The artifact definitions for the current module. Must be consistent with " + packagedArtifacts.key.label + ".").withRank(BSetting) val artifacts = settingKey[Seq[Artifact]]("The artifact definitions for the current module. Must be consistent with " + packagedArtifacts.key.label + ".").withRank(BSetting)
val projectDescriptors = taskKey[Map[ModuleRevisionId, ModuleDescriptor]]("Project dependency map for the inter-project resolver.").withRank(DTask) val projectDescriptors = taskKey[Map[Any, Any]]("Project dependency map for the inter-project resolver.").withRank(DTask)
val autoUpdate = settingKey[Boolean]("<unimplemented>").withRank(Invisible) val autoUpdate = settingKey[Boolean]("<unimplemented>").withRank(Invisible)
val retrieveManaged = settingKey[Boolean]("If true, enables retrieving dependencies to the current build. Otherwise, dependencies are used directly from the cache.").withRank(BSetting) val retrieveManaged = settingKey[Boolean]("If true, enables retrieving dependencies to the current build. Otherwise, dependencies are used directly from the cache.").withRank(BSetting)
val retrieveManagedSync = settingKey[Boolean]("If true, enables synchronizing the dependencies retrieved to the current build by removed unneeded files.").withRank(BSetting) val retrieveManagedSync = settingKey[Boolean]("If true, enables synchronizing the dependencies retrieved to the current build by removed unneeded files.").withRank(BSetting)

View File

@ -13,26 +13,12 @@ import java.net.URI
import sbt.librarymanagement.{ Credentials as IvyCredentials, * } import sbt.librarymanagement.{ Credentials as IvyCredentials, * }
import sbt.util.Logger import sbt.util.Logger
import sbt.Keys.* import sbt.Keys.*
import lmcoursier.definitions.{ import lmcoursier.definitions.{ Project as CProject, Strict as CStrict }
Classifier as CClassifier,
Configuration as CConfiguration,
Dependency as CDependency,
Extension as CExtension,
Info as CInfo,
Module as CModule,
ModuleName as CModuleName,
Organization as COrganization,
Project as CProject,
Publication as CPublication,
Type as CType,
Strict as CStrict,
}
import lmcoursier.credentials.DirectCredentials import lmcoursier.credentials.DirectCredentials
import lmcoursier.{ FallbackDependency, FromSbt, Inputs } import lmcoursier.{ FallbackDependency, FromSbt, Inputs }
import sbt.internal.librarymanagement.mavenint.SbtPomExtraProperties import sbt.internal.librarymanagement.mavenint.SbtPomExtraProperties
import sbt.ProjectExtra.transitiveInterDependencies import sbt.ProjectExtra.transitiveInterDependencies
import sbt.ScopeFilter.Make.* import sbt.ScopeFilter.Make.*
import scala.jdk.CollectionConverters.*
object CoursierInputsTasks { object CoursierInputsTasks {
private def coursierProject0( private def coursierProject0(
@ -100,61 +86,6 @@ object CoursierInputsTasks {
) )
} }
private def moduleFromIvy(id: org.apache.ivy.core.module.id.ModuleRevisionId): CModule =
CModule(
COrganization(id.getOrganisation),
CModuleName(id.getName),
id.getExtraAttributes.asScala.map { (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), CModuleName(modId.getName))
}.toSet
val configurations = desc.getModuleConfigurations.toVector
.flatMap(Inputs.ivyXmlMappings)
def dependency(conf: CConfiguration, pub: CPublication) = CDependency(
module,
id.getRevision,
conf,
exclusions,
pub,
optional = false,
desc.isTransitive
)
val publications: CConfiguration => CPublication = {
val artifacts = desc.getAllDependencyArtifacts
val m = artifacts.toVector.flatMap { art =>
val pub =
CPublication(art.getName, CType(art.getType), CExtension(art.getExt()), CClassifier(""))
art.getConfigurations.map(CConfiguration(_)).toVector.map { conf =>
conf -> pub
}
}.toMap
c => m.getOrElse(c, CPublication("", CType(""), CExtension(""), CClassifier("")))
}
configurations.map { (from, to) =>
from -> dependency(to, publications(to))
}
}
private[sbt] def coursierInterProjectDependenciesTask: Def.Initialize[sbt.Task[Seq[CProject]]] = private[sbt] def coursierInterProjectDependenciesTask: Def.Initialize[sbt.Task[Seq[CProject]]] =
(Def (Def
.task { .task {
@ -171,34 +102,9 @@ object CoursierInputsTasks {
private[sbt] def coursierExtraProjectsTask: Def.Initialize[sbt.Task[Seq[CProject]]] = { private[sbt] def coursierExtraProjectsTask: Def.Initialize[sbt.Task[Seq[CProject]]] = {
Def.task { Def.task {
val projects = csrInterProjectDependencies.value // Coursier handles inter-project dependencies natively via csrInterProjectDependencies.
val projectModules = projects.map(_.module).toSet // The Ivy-typed projectDescriptors are only populated when IvyDependencyPlugin is enabled.
Seq.empty[CProject]
// this includes org.scala-sbt:global-plugins referenced from meta-builds in particular
sbt.Keys.projectDescriptors.value
.map { (k, v) =>
moduleFromIvy(k) -> v
}
.filter { case (module, _) =>
!projectModules(module)
}
.toVector
.map { (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.toSeq,
configurations,
Nil,
None,
Nil,
CInfo("", "", Nil, Nil, None)
)
}
} }
} }
@ -230,7 +136,7 @@ object CoursierInputsTasks {
.flatMap { .flatMap {
case dc: IvyCredentials.DirectCredentials => List(dc) case dc: IvyCredentials.DirectCredentials => List(dc)
case fc: IvyCredentials.FileCredentials => case fc: IvyCredentials.FileCredentials =>
sbt.internal.librarymanagement.ivy.IvyCredentials.loadCredentials(fc.path) match { sbt.librarymanagement.CredentialUtils.loadCredentials(fc.path) match {
case Left(err) => case Left(err) =>
log.warn(s"$err, ignoring it") log.warn(s"$err, ignoring it")
Nil Nil

View File

@ -278,7 +278,7 @@ object LMCoursier {
case dc: IvyCredentials.DirectCredentials => case dc: IvyCredentials.DirectCredentials =>
Right[String, IvyCredentials.DirectCredentials](dc) Right[String, IvyCredentials.DirectCredentials](dc)
case fc: IvyCredentials.FileCredentials => case fc: IvyCredentials.FileCredentials =>
sbt.internal.librarymanagement.ivy.IvyCredentials.loadCredentials(fc.path) sbt.librarymanagement.CredentialUtils.loadCredentials(fc.path)
}) match { }) match {
case Left(err) => st.log.warn(err) case Left(err) => st.log.warn(err)
case Right(d) => case Right(d) =>

View File

@ -24,8 +24,6 @@ import sbt.ProjectExtra.{ extract, runUnloadHooks, setProject }
import sbt.SlashSyntax0.* import sbt.SlashSyntax0.*
import sbt.librarymanagement.LibraryManagementCodec.given import sbt.librarymanagement.LibraryManagementCodec.given
import java.io.File import java.io.File
import org.apache.ivy.core.module.{ descriptor, id }
import descriptor.ModuleDescriptor, id.ModuleRevisionId
object GlobalPlugin { object GlobalPlugin {
// constructs a sequence of settings that may be appended to a project's settings to // constructs a sequence of settings that may be appended to a project's settings to
@ -79,9 +77,7 @@ object GlobalPlugin {
val taskInit = Def.task { val taskInit = Def.task {
val intcp = (Runtime / internalDependencyClasspath).value val intcp = (Runtime / internalDependencyClasspath).value
val prods = (Runtime / exportedProducts).value val prods = (Runtime / exportedProducts).value
val depMap = val depMap = projectDescriptors.value
if useIvy.value then projectDescriptors.value + ivyModule.value.dependencyMapping(state.log)
else projectDescriptors.value
GlobalPluginData( GlobalPluginData(
projectID.value, projectID.value,
@ -125,7 +121,7 @@ object GlobalPlugin {
final case class GlobalPluginData( final case class GlobalPluginData(
projectID: ModuleID, projectID: ModuleID,
dependencies: Seq[ModuleID], dependencies: Seq[ModuleID],
descriptors: Map[ModuleRevisionId, ModuleDescriptor], descriptors: Map[Any, Any],
resolvers: Vector[Resolver], resolvers: Vector[Resolver],
fullClasspath: Classpath, fullClasspath: Classpath,
internalClasspath: Classpath internalClasspath: Classpath

View File

@ -310,7 +310,7 @@ private[sbt] object LibraryManagement {
Seq[ScopedKey[?]], Seq[ScopedKey[?]],
ScopedKey[?], ScopedKey[?],
Option[FiniteDuration], Option[FiniteDuration],
IvySbt#Module, ModuleSettings,
String, String,
ProjectRef, ProjectRef,
Boolean, Boolean,
@ -318,7 +318,6 @@ private[sbt] object LibraryManagement {
UnresolvedWarningConfiguration, UnresolvedWarningConfiguration,
Boolean, Boolean,
CompatibilityWarningOptions, CompatibilityWarningOptions,
IvySbt,
GetClassifiersModule, GetClassifiersModule,
File, File,
xsbti.AppConfiguration, xsbti.AppConfiguration,
@ -334,7 +333,7 @@ private[sbt] object LibraryManagement {
Keys.executionRoots, Keys.executionRoots,
Keys.resolvedScoped.toTaskable, Keys.resolvedScoped.toTaskable,
Keys.forceUpdatePeriod.toTaskable, Keys.forceUpdatePeriod.toTaskable,
Keys.ivyModule.toTaskable, Keys.moduleSettings.toTaskable,
Keys.updateCacheName.toTaskable, Keys.updateCacheName.toTaskable,
Keys.thisProjectRef.toTaskable, Keys.thisProjectRef.toTaskable,
(Keys.update / Keys.skip).toTaskable, (Keys.update / Keys.skip).toTaskable,
@ -342,7 +341,6 @@ private[sbt] object LibraryManagement {
(Keys.update / Keys.unresolvedWarningConfiguration).toTaskable, (Keys.update / Keys.unresolvedWarningConfiguration).toTaskable,
Keys.publishMavenStyle.toTaskable, Keys.publishMavenStyle.toTaskable,
Keys.compatibilityWarningOptions.toTaskable, Keys.compatibilityWarningOptions.toTaskable,
Keys.ivySbt,
Keys.classifiersModule, Keys.classifiersModule,
Keys.dependencyCacheDirectory, Keys.dependencyCacheDirectory,
Keys.appConfiguration.toTaskable, Keys.appConfiguration.toTaskable,
@ -358,7 +356,7 @@ private[sbt] object LibraryManagement {
er, er,
rs, rs,
fup, fup,
im, ms,
ucn, ucn,
thisRef, thisRef,
sk, sk,
@ -366,7 +364,6 @@ private[sbt] object LibraryManagement {
uwConfig, uwConfig,
mavenStyle, mavenStyle,
cwo, cwo,
ivySbt0,
mod, mod,
dcd, dcd,
app, app,
@ -400,10 +397,8 @@ private[sbt] object LibraryManagement {
conf1.withLogicalClock(LogicalClock(state0.hashCode)) conf1.withLogicalClock(LogicalClock(state0.hashCode))
} }
cachedUpdate( cachedUpdate(
// LM API
lm = lm, lm = lm,
// Ivy-free ModuleDescriptor module = lm.moduleDescriptor(ms.asInstanceOf[ModuleDescriptorConfiguration]),
module = im,
s.cacheStoreFactory.sub(ucn), s.cacheStoreFactory.sub(ucn),
Reference.display(thisRef), Reference.display(thisRef),
updateConf, updateConf,
@ -446,12 +441,12 @@ private[sbt] object LibraryManagement {
) )
val report = f(excludes) val report = f(excludes)
val allExcludes: Map[ModuleID, Vector[ConfigRef]] = excludes ++ val allExcludes: Map[ModuleID, Vector[ConfigRef]] = excludes ++
IvyActions UpdateClassifiersUtil
.extractExcludes(report) .extractExcludes(report)
.view .view
.mapValues(cs => cs.map(c => ConfigRef(c)).toVector) .mapValues(cs => cs.map(c => ConfigRef(c)).toVector)
store.write(allExcludes) store.write(allExcludes)
IvyActions UpdateClassifiersUtil
.addExcluded( .addExcluded(
report, report,
classifiers.toVector, classifiers.toVector,
@ -986,7 +981,7 @@ private[sbt] object LibraryManagement {
Def.task { Def.task {
val log = streams.value.log val log = streams.value.log
val conf = publishConfiguration.value val conf = publishConfiguration.value
val module = ivyModule.value val module = ivyModule.value.asInstanceOf[ModuleDescriptor]
val publisherInterface = publisher.value val publisherInterface = publisher.value
publisherInterface.publish(module, conf, log) publisherInterface.publish(module, conf, log)
} }
@ -1114,7 +1109,7 @@ private[sbt] object LibraryManagement {
Def.task { Def.task {
val log = streams.value.log val log = streams.value.log
val conf = publishLocalConfiguration.value val conf = publishLocalConfiguration.value
val module = ivyModule.value val module = ivyModule.value.asInstanceOf[ModuleDescriptor]
val publisherInterface = publisher.value val publisherInterface = publisher.value
publisherInterface.publish(module, conf, log) publisherInterface.publish(module, conf, log)
} }
@ -1157,7 +1152,7 @@ private[sbt] object LibraryManagement {
Def.task { Def.task {
val log = streams.value.log val log = streams.value.log
val conf = publishM2Configuration.value val conf = publishM2Configuration.value
val module = ivyModule.value val module = ivyModule.value.asInstanceOf[ModuleDescriptor]
val publisherInterface = publisher.value val publisherInterface = publisher.value
publisherInterface.publish(module, conf, log) publisherInterface.publish(module, conf, log)
} }

View File

@ -0,0 +1,285 @@
/*
* sbt
* Copyright 2023, Scala center
* Copyright 2011 - 2022, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
import sbt.librarymanagement.*
import scala.xml.{ Elem, Node, NodeSeq }
/**
* Generates Maven POM XML from sbt's own types, without requiring Ivy.
* This is used by the default publisher when the sbt-ivy plugin is not loaded.
*/
private[sbt] object PomGenerator:
def makePom(
mid: ModuleID,
info: Option[ModuleInfo],
deps: Vector[ModuleID],
configurations: Option[Vector[Configuration]],
extra: NodeSeq,
scalaModuleInfo: Option[ScalaModuleInfo] = None,
resolvers: Vector[Resolver] = Vector.empty,
filterRepositories: MavenRepository => Boolean = _ => true,
allRepositories: Boolean = false,
): Node =
val crossMid = crossVersionDep(mid, scalaModuleInfo)
val keepConfs: Set[String] =
configurations.map(_.map(_.name).toSet).getOrElse(Set.empty)
val crossVersioned = deps.map(crossVersionDep(_, scalaModuleInfo))
val filteredDeps =
if keepConfs.isEmpty then crossVersioned
else crossVersioned.filter(d => d.configurations.forall(c => confIntersects(c, keepConfs)))
val (bomDeps, regularDeps) = filteredDeps.partition: d =>
d.explicitArtifacts.nonEmpty && d.explicitArtifacts.forall(_.`type` == Artifact.PomType)
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
{makeModuleID(crossMid)}
{info.map(i => <name>{i.nameFormal}</name>).getOrElse(NodeSeq.Empty)}
{info.map(makeStartYear).getOrElse(NodeSeq.Empty)}
{info.map(makeOrganization).getOrElse(NodeSeq.Empty)}
{info.map(makeScmInfo).getOrElse(NodeSeq.Empty)}
{info.map(makeDeveloperInfo).getOrElse(NodeSeq.Empty)}
{info.map(makeLicenses).getOrElse(NodeSeq.Empty)}
{makeProperties(crossMid)}
{extra}
{makeRepositories(resolvers, filterRepositories, allRepositories)}
{makeDependencyManagement(bomDeps)}
{makeDependencies(regularDeps)}
</project>
private def crossVersionDep(dep: ModuleID, scalaInfo: Option[ScalaModuleInfo]): ModuleID =
val crossFn = CrossVersion(dep, scalaInfo)
val crossDep = crossFn match
case Some(fn) => dep.withName(fn(dep.name)).withCrossVersion(CrossVersion.disabled)
case None => dep
if crossDep.exclusions.isEmpty || scalaInfo.isEmpty then crossDep
else
val si = scalaInfo.get
val crossedExclusions = crossDep.exclusions.map: excl =>
CrossVersion(excl.crossVersion, si.scalaFullVersion, si.scalaBinaryVersion) match
case Some(fn) =>
excl.withName(fn(excl.name)).withCrossVersion(CrossVersion.disabled)
case None => excl
crossDep.withExclusions(crossedExclusions)
private def confIntersects(confStr: String, keepConfs: Set[String]): Boolean =
confStr
.split(';')
.exists: mapping =>
val from = mapping.split("->").head.trim
from.split(',').exists(c => keepConfs.contains(c.trim) || c.trim == "*")
private def makeModuleID(mid: ModuleID): NodeSeq =
val packaging =
if mid.explicitArtifacts.isEmpty then "jar"
else
val types = mid.explicitArtifacts.map(_.`type`).filterNot(IgnoreTypes)
if types.isEmpty then Artifact.PomType
else if types.contains(Artifact.DefaultType) then Artifact.DefaultType
else types.head
(<groupId>{mid.organization}</groupId>
<artifactId>{mid.name}</artifactId>
<version>{mid.revision}</version>
<packaging>{packaging}</packaging>)
private val IgnoreTypes: Set[String] =
Set(Artifact.SourceType, Artifact.DocType, Artifact.PomType)
private def makeStartYear(info: ModuleInfo): NodeSeq =
info.startYear match
case Some(y) => <inceptionYear>{y}</inceptionYear>
case _ => NodeSeq.Empty
private def makeOrganization(info: ModuleInfo): NodeSeq =
<organization>
<name>{info.organizationName}</name>
{info.organizationHomepage.map(h => <url>{h}</url>).getOrElse(NodeSeq.Empty)}
</organization>
private def makeScmInfo(info: ModuleInfo): NodeSeq =
info.scmInfo match
case Some(s) =>
<scm>
<url>{s.browseUrl}</url>
<connection>{s.connection}</connection>
{
s.devConnection
.map(d => <developerConnection>{d}</developerConnection>)
.getOrElse(NodeSeq.Empty)
}
</scm>
case _ => NodeSeq.Empty
private def makeDeveloperInfo(info: ModuleInfo): NodeSeq =
if info.developers.nonEmpty then
<developers>
{
info.developers.map: dev =>
<developer>
<id>{dev.id}</id>
<name>{dev.name}</name>
<url>{dev.url}</url>
{
if dev.email != null && dev.email.nonEmpty then <email>{dev.email}</email>
else NodeSeq.Empty
}
</developer>
}
</developers>
else NodeSeq.Empty
private def makeLicenses(info: ModuleInfo): NodeSeq =
if info.licenses.nonEmpty then
<licenses>
{
info.licenses.map: lic =>
<license>
<name>{lic.spdxId}</name>
<url>{lic.uri}</url>
<distribution>repo</distribution>
</license>
}
</licenses>
else NodeSeq.Empty
private def makeProperties(mid: ModuleID): NodeSeq =
val props = mid.extraAttributes
.collect:
case (k, v) if k.startsWith("e:info.") => (k.stripPrefix("e:"), v)
case (k, v) if k.startsWith("info.") => (k, v)
if props.isEmpty then NodeSeq.Empty
else
<properties>
{
props.toSeq
.sortBy(_._1)
.map: (k, v) =>
Elem(null, k, scala.xml.Null, scala.xml.TopScope, false, scala.xml.Text(v))
}
</properties>
private def makeRepositories(
resolvers: Vector[Resolver],
filter: MavenRepository => Boolean,
allRepositories: Boolean,
): NodeSeq =
val repos = resolvers.collect:
case r: MavenRepository if r.name != "public" && (allRepositories || filter(r)) =>
<repository>
<id>{r.name}</id>
<name>{r.name}</name>
<url>{r.root}</url>
</repository>
if repos.isEmpty then NodeSeq.Empty
else <repositories>{repos}</repositories>
private def makeDependencyManagement(deps: Vector[ModuleID]): NodeSeq =
if deps.isEmpty then NodeSeq.Empty
else
<dependencyManagement>
<dependencies>
{
deps.map: dep =>
<dependency>
<groupId>{dep.organization}</groupId>
<artifactId>{dep.name}</artifactId>
<version>{dep.revision}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
}
</dependencies>
</dependencyManagement>
private def makeDependencies(deps: Vector[ModuleID]): NodeSeq =
if deps.isEmpty then NodeSeq.Empty
else <dependencies>
{deps.map(makeDependencyElem)}
</dependencies>
private def makeDependencyElem(dep: ModuleID): Elem =
val (scope, optional) = getScopeAndOptional(dep.configurations)
val mavenVersion = convertVersion(dep.revision)
val versionNode: NodeSeq =
if mavenVersion == null || mavenVersion == "*" || mavenVersion.isEmpty then NodeSeq.Empty
else <version>{mavenVersion}</version>
val result: Elem =
<dependency>
<groupId>{dep.organization}</groupId>
<artifactId>{dep.name}</artifactId>
{versionNode}
{scopeElem(scope)}
{optionalElem(optional)}
{classifierElem(dep)}
{exclusions(dep)}
</dependency>
result
private def getScopeAndOptional(configurations: Option[String]): (Option[String], Boolean) =
configurations match
case None => (None, false)
case Some(confStr) =>
val confs =
confStr.split(';').flatMap(_.split("->").head.trim.split(',')).map(_.trim).toSet
val optional = confs.contains(Configurations.Optional.name)
val notOptional = confs - Configurations.Optional.name
val scope = Configurations.defaultMavenConfigurations
.find(c => notOptional.contains(c.name))
.map(_.name)
(scope, optional)
/** Convert Ivy-style dynamic versions to Maven range format. */
private def convertVersion(version: String): String =
if version == null then null
else if version.endsWith("+") then
val base = version.stripSuffix("+").stripSuffix(".")
val parts = base.split('.')
if parts.nonEmpty then
val last =
try parts.last.toInt + 1
catch case _: NumberFormatException => return version
val upper = (parts.init :+ last.toString).mkString(".")
s"[$base,$upper)"
else version
else if version == "latest.integration" || version == "latest.release" then ""
else version
private def scopeElem(scope: Option[String]): NodeSeq =
scope match
case None | Some("compile") => NodeSeq.Empty
case Some(s) => <scope>{s}</scope>
private def optionalElem(opt: Boolean): NodeSeq =
if opt then <optional>true</optional> else NodeSeq.Empty
private def classifierElem(dep: ModuleID): NodeSeq =
dep.explicitArtifacts.headOption.flatMap(_.classifier) match
case Some(c) => <classifier>{c}</classifier>
case None => NodeSeq.Empty
private def exclusions(dep: ModuleID): NodeSeq =
if dep.exclusions.isEmpty then NodeSeq.Empty
else
val elems = dep.exclusions.flatMap { excl =>
val g = excl.organization
val a = excl.name
if g.nonEmpty && g != "*" && a.nonEmpty && a != "*" then Some(<exclusion>
<groupId>{g}</groupId>
<artifactId>{a}</artifactId>
</exclusion>)
else None
}
if elems.isEmpty then NodeSeq.Empty
else <exclusions>{elems}</exclusions>
end PomGenerator

View File

@ -15,7 +15,7 @@ import sbt.internal.util.MessageOnlyException
import sbt.io.IO import sbt.io.IO
import sbt.io.Path.contentOf import sbt.io.Path.contentOf
import sbt.librarymanagement.Credentials import sbt.librarymanagement.Credentials
import sbt.internal.librarymanagement.ivy.IvyCredentials import sbt.librarymanagement.CredentialUtils
import sona.{ PublishingType, Sona } import sona.{ PublishingType, Sona }
import scala.concurrent.duration.FiniteDuration import scala.concurrent.duration.FiniteDuration
@ -65,7 +65,7 @@ see https://www.scala-sbt.org/1.x/docs/Using-Sonatype.html for details.""")
} }
private def fromCreds(creds: Seq[Credentials], uploadRequestTimeout: FiniteDuration): Sona = { private def fromCreds(creds: Seq[Credentials], uploadRequestTimeout: FiniteDuration): Sona = {
val cred = IvyCredentials val cred = CredentialUtils
.forHost(creds, Sona.host) .forHost(creds, Sona.host)
.getOrElse(throw new MessageOnlyException(s"no credentials are found for ${Sona.host}")) .getOrElse(throw new MessageOnlyException(s"no credentials are found for ${Sona.host}"))
Sona.oauthClient(cred.userName, cred.passwd, uploadRequestTimeout) Sona.oauthClient(cred.userName, cred.passwd, uploadRequestTimeout)

View File

@ -18,7 +18,6 @@ import sbt.ProjectExtra.*
import sbt.internal.graph.* import sbt.internal.graph.*
import sbt.internal.graph.backend.SbtUpdateReport import sbt.internal.graph.backend.SbtUpdateReport
import sbt.internal.graph.rendering.{ DagreHTML, TreeView } import sbt.internal.graph.rendering.{ DagreHTML, TreeView }
import sbt.internal.librarymanagement.*
import sbt.internal.util.complete.{ Parser, Parsers } import sbt.internal.util.complete.{ Parser, Parsers }
import sbt.internal.util.complete.DefaultParsers.* import sbt.internal.util.complete.DefaultParsers.*
import sbt.io.IO import sbt.io.IO
@ -110,29 +109,12 @@ OPTIONS
*/ */
def coreSettings = def coreSettings =
Seq( Seq(
// disable the cached resolution engine (exposing a scoped `ivyModule` used directly by `updateTask`), as it
// generates artificial module descriptors which are internal to sbt, making it hard to reconstruct the
// dependency tree
dependencyTreeIgnoreMissingUpdate / updateOptions := updateOptions.value
.withCachedResolution(false),
dependencyTreeIgnoreMissingUpdate / ivyConfiguration := Def.uncached {
// inTask will make sure the new definition will pick up `updateOptions in dependencyTreeIgnoreMissingUpdate`
Project.inTask(dependencyTreeIgnoreMissingUpdate, Classpaths.mkIvyConfiguration).value
},
dependencyTreeIgnoreMissingUpdate / ivyModule := Def.uncached {
// concatenating & inlining ivySbt & ivyModule default task implementations, as `SbtAccess.inTask` does
// NOT correctly force the scope when applied to `TaskKey.toTask` instances (as opposed to raw
// implementations like `Classpaths.mkIvyConfiguration` or `Classpaths.updateTask`)
val is = new IvySbt((dependencyTreeIgnoreMissingUpdate / ivyConfiguration).value)
new is.Module(moduleSettings.value)
},
// don't fail on missing dependencies or eviction errors // don't fail on missing dependencies or eviction errors
dependencyTreeIgnoreMissingUpdate / updateConfiguration := updateConfiguration.value dependencyTreeIgnoreMissingUpdate / updateConfiguration := updateConfiguration.value
.withMissingOk(true), .withMissingOk(true),
dependencyTreeIgnoreMissingUpdate / evictionErrorLevel := Level.Warn, dependencyTreeIgnoreMissingUpdate / evictionErrorLevel := Level.Warn,
dependencyTreeIgnoreMissingUpdate / assumedEvictionErrorLevel := Level.Warn, dependencyTreeIgnoreMissingUpdate / assumedEvictionErrorLevel := Level.Warn,
dependencyTreeIgnoreMissingUpdate := Def.uncached { dependencyTreeIgnoreMissingUpdate := Def.uncached {
// inTask will make sure the new definition will pick up `ivyModule/updateConfiguration in ignoreMissingUpdate`
Project.inTask(dependencyTreeIgnoreMissingUpdate, Classpaths.updateTask).value Project.inTask(dependencyTreeIgnoreMissingUpdate, Classpaths.updateTask).value
}, },
) )
@ -390,8 +372,8 @@ OPTIONS
type HasModule = { type HasModule = {
val module: ModuleID val module: ModuleID
} }
def crossName(ivyModule: IvySbt#Module) = def crossName(module: Any) =
ivyModule.moduleSettings match { module match {
case ic: ModuleDescriptorConfiguration => ic.module.name case ic: ModuleDescriptorConfiguration => ic.module.name
case _ => case _ =>
throw new IllegalStateException( throw new IllegalStateException(

View File

@ -1,17 +0,0 @@
scalaVersion := "2.12.21"
libraryDependencies += "org.slf4j" % "slf4j-api" % "1.7.28"
updateOptions := updateOptions.value.withCachedResolution(true)
TaskKey[Unit]("check") := {
val report = (Test / updateFull).value
val graph = (Test / dependencyTree).toTask(" --quiet").value
def sanitize(str: String): String = str.split('\n').drop(1).mkString("\n")
val expectedGraph =
"""default:cachedresolution_2.12:0.1.0-SNAPSHOT
| +-org.slf4j:slf4j-api:1.7.28
| """.stripMargin
require(sanitize(graph) == sanitize(expectedGraph), "Graph for report %s was '\n%s' but should have been '\n%s'" format (report, sanitize(graph), sanitize(expectedGraph)))
()
}

View File

@ -1,3 +1,4 @@
ThisBuild / useIvy := true
lazy val root = (project in file(".")) lazy val root = (project in file("."))
lazy val a = project lazy val a = project
lazy val b = project lazy val b = project

View File

@ -0,0 +1,4 @@
libraryDependencies += {
val sbtV = sbtVersion.value
("org.scala-sbt" % s"sbt-ivy_sbt${sbtV}_${scalaBinaryVersion.value}" % sbtV).intransitive()
}

View File

@ -0,0 +1,4 @@
libraryDependencies += {
val sbtV = sbtVersion.value
("org.scala-sbt" % s"sbt-ivy_sbt${sbtV}_${scalaBinaryVersion.value}" % sbtV).intransitive()
}

View File

@ -2,6 +2,8 @@ import scala.xml.{ Node, _ }
import scala.xml.Utility.trim import scala.xml.Utility.trim
import sbt.internal.librarymanagement.{ IvySbt, MakePom } import sbt.internal.librarymanagement.{ IvySbt, MakePom }
ThisBuild / useIvy := true
lazy val check = taskKey[Unit]("check") lazy val check = taskKey[Unit]("check")
val dispatch = "net.databinder.dispatch" %% "dispatch-core" % "0.11.2" val dispatch = "net.databinder.dispatch" %% "dispatch-core" % "0.11.2"
@ -36,7 +38,7 @@ lazy val root = (project in file(".")).
sys.error("dispatch-core_2.11-0.11.1.jar found when it should NOT be included: " + bcp.toString) sys.error("dispatch-core_2.11-0.11.1.jar found when it should NOT be included: " + bcp.toString)
} }
val bPomXml = makePomXml(streams.value.log, (b / makePomConfiguration).value, (b / ivyModule).value) val bPomXml = makePomXml(streams.value.log, (b / makePomConfiguration).value, (b / ivyModule).value.asInstanceOf[IvySbt#Module])
val repatchTwitterXml = bPomXml \ "dependencies" \ "dependency" find { d => val repatchTwitterXml = bPomXml \ "dependencies" \ "dependency" find { d =>
(d \ "groupId").text == "com.eed3si9n" && (d \ "artifactId").text == "repatch-twitter-core_2.11" (d \ "groupId").text == "com.eed3si9n" && (d \ "artifactId").text == "repatch-twitter-core_2.11"

View File

@ -0,0 +1,4 @@
libraryDependencies += {
val sbtV = sbtVersion.value
("org.scala-sbt" % s"sbt-ivy_sbt${sbtV}_${scalaBinaryVersion.value}" % sbtV).intransitive()
}

View File

@ -0,0 +1,4 @@
libraryDependencies += {
val sbtV = sbtVersion.value
("org.scala-sbt" % s"sbt-ivy_sbt${sbtV}_${scalaBinaryVersion.value}" % sbtV).intransitive()
}

View File

@ -1,4 +1,5 @@
ThisBuild / scalaVersion := "2.12.21" ThisBuild / scalaVersion := "2.12.21"
ThisBuild / useIvy := true
def configIvyScala = def configIvyScala =
scalaModuleInfo ~= (_ map (_ withCheckExplicit false)) scalaModuleInfo ~= (_ map (_ withCheckExplicit false))

View File

@ -0,0 +1,4 @@
libraryDependencies += {
val sbtV = sbtVersion.value
("org.scala-sbt" % s"sbt-ivy_sbt${sbtV}_${scalaBinaryVersion.value}" % sbtV).intransitive()
}

View File

@ -210,6 +210,7 @@ object IvyXml {
shadedConfigOpt: Option[Configuration] shadedConfigOpt: Option[Configuration]
): Setting[Task[T]] = ): Setting[Task[T]] =
task := Def.uncached(task.dependsOnTask { task := Def.uncached(task.dependsOnTask {
Def.ifS(Def.task { sbt.Keys.useIvy.value })(
Def.task { Def.task {
val currentProject = { val currentProject = {
val proj = csrProject.value val proj = csrProject.value
@ -220,11 +221,14 @@ object IvyXml {
IvyXml.writeFiles( IvyXml.writeFiles(
currentProject, currentProject,
shadedConfigOpt, shadedConfigOpt,
sbt.Keys.ivySbt.value, sbt.Keys.ivySbt.value.asInstanceOf[IvySbt],
sbt.Keys.streams.value.log, sbt.Keys.streams.value.log,
resolved resolved
) )
} }
)(
Def.task { () }
)
}.value) }.value)
private lazy val needsIvyXmlLocal = Seq(publishLocalConfiguration) ++ getPubConf( private lazy val needsIvyXmlLocal = Seq(publishLocalConfiguration) ++ getPubConf(

View File

@ -0,0 +1,274 @@
/*
* sbt
* Copyright 2023, Scala center
* Copyright 2011 - 2022, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package plugins
import java.io.File
import org.apache.ivy.core.module.descriptor.{ DependencyDescriptor, ModuleDescriptor }
import org.apache.ivy.core.module.id.ModuleRevisionId
import sbt.Def.{ Initialize, Setting }
import sbt.Keys.*
import sbt.ProjectExtra.*
import sbt.internal.LibraryManagement
import sbt.internal.librarymanagement.{ IvyActions, IvySbt, IvyXml, ProjectResolver }
import sbt.internal.librarymanagement.ivy.*
import sbt.io.syntax.*
import sbt.librarymanagement.*
import sbt.std.TaskExtra.*
import lmcoursier.definitions.{
Classifier as CClassifier,
Configuration as CConfiguration,
Dependency as CDependency,
Extension as CExtension,
Info as CInfo,
Module as CModule,
ModuleName as CModuleName,
Organization as COrganization,
Project as CProject,
Publication as CPublication,
Type as CType,
}
import lmcoursier.Inputs
import scala.jdk.CollectionConverters.*
/**
* AutoPlugin that provides all Ivy-specific functionality.
* This plugin overrides the stub defaults in main/ with real Ivy implementations.
*/
object IvyDependencyPlugin extends AutoPlugin:
override def requires = IvyPlugin
override def trigger = allRequirements
override lazy val globalSettings: Seq[Setting[?]] = Seq(
updateOptions := UpdateOptions(),
)
override lazy val projectSettings: Seq[Setting[?]] = Seq(
ivyConfiguration := Def.uncached(
Def
.ifS(Def.task { useIvy.value })(
Def.task { mkIvyConfiguration.value: Any }
)(
Def.task { (): Any }
)
.value
),
publisher := Def.uncached(
Def
.ifS(Def.task { useIvy.value })(
Def.task {
IvyPublisher(ivyConfiguration.value.asInstanceOf[IvyConfiguration])
}
)(
Def.task {
Classpaths.defaultPublisher(dependencyResolution.value, fullResolvers.value.toVector)
}
)
.value
),
ivySbt := Def.uncached(
Def
.ifS(Def.task { useIvy.value })(
Def.task { ivySbt0.value: Any }
)(
Def.task { (): Any }
)
.value
),
ivyModule := Def.uncached(
Def
.ifS(Def.task { useIvy.value })(
Def.task {
val is = ivySbt.value.asInstanceOf[IvySbt]
new is.Module(moduleSettings.value): Any
}
)(
Def.task { (): Any }
)
.value
),
projectDescriptors := Def.uncached(
Def
.ifS(Def.task { useIvy.value })(
Def.task { depMap.value.asInstanceOf[Map[Any, Any]] }
)(
Def.task { Map.empty[Any, Any] }
)
.value
),
projectResolver := Def.uncached(
Def
.ifS(Def.task { useIvy.value })(
projectResolverTask
)(
Classpaths.projectResolverTask
)
.value
),
csrExtraProjects := Def.uncached(
Def
.ifS(Def.task { useIvy.value })(
coursierExtraProjectsTask
)(
Def.task { Nil }
)
.value
),
) ++ IvyXml.generateIvyXmlSettings() ++ ivyPublishOrSkipSettings
private lazy val ivySbt0: Initialize[Task[IvySbt]] =
Def.task {
IvyCredentials.register(credentials.value, streams.value.log)
new IvySbt(ivyConfiguration.value.asInstanceOf[IvyConfiguration])
}
private lazy val mkIvyConfiguration: Initialize[Task[InlineIvyConfiguration]] =
Def.task {
val (rs, other) = (fullResolvers.value.toVector, otherResolvers.value.toVector)
val s = streams.value
Classpaths.warnResolversConflict(rs ++: other, s.log)
Classpaths.errorInsecureProtocol(rs ++: other, s.log)
InlineIvyConfiguration()
.withPaths(ivyPaths.value)
.withResolvers(rs)
.withOtherResolvers(other)
.withModuleConfigurations(moduleConfigurations.value.toVector)
.withLock(LibraryManagement.lock(appConfiguration.value))
.withChecksums((update / checksums).value.toVector)
.withResolutionCacheDir(target.value / "resolution-cache")
.withUpdateOptions(updateOptions.value.asInstanceOf[UpdateOptions])
.withLog(s.log)
}
private def depMap: Initialize[Task[Map[ModuleRevisionId, ModuleDescriptor]]] =
import sbt.TupleSyntax.*
(buildDependencies.toTaskable, thisProjectRef.toTaskable, settingsData, streams)
.flatMapN { (bd, thisProj, data, s) =>
depMap(bd.classpathTransitiveRefs(thisProj), data, s.log)
}
private def depMap(
projects: Seq[ProjectRef],
data: Def.Settings,
log: sbt.util.Logger
): Task[Map[ModuleRevisionId, ModuleDescriptor]] =
val ivyModules = projects.flatMap { proj =>
(proj / ivyModule).get(data)
}.join
ivyModules.mapN { mod =>
mod.map { m => m.asInstanceOf[IvySbt#Module].dependencyMapping(log) }.toMap
}
private def projectResolverTask: Initialize[Task[Resolver]] =
projectDescriptors.map { m =>
val typedMap = m.asInstanceOf[Map[ModuleRevisionId, ModuleDescriptor]]
val resolver = new ProjectResolver(ProjectResolver.InterProject, typedMap)
new RawRepository(resolver, resolver.getName)
}
private def ivyPublishOrSkipSettings: Seq[Setting[?]] =
Seq(
deliver := deliverTask(makeIvyXmlConfiguration).value,
deliverLocal := deliverTask(makeIvyXmlLocalConfiguration).value,
makeIvyXml := deliverTask(makeIvyXmlConfiguration).value,
)
private def deliverTask(config: TaskKey[PublishConfiguration]): Initialize[Task[File]] =
Def.task {
Def.unit(update.value)
if !useIvy.value then sys.error("deliver/makeIvyXml requires useIvy := true")
IvyActions.deliver(
ivyModule.value.asInstanceOf[IvySbt#Module],
config.value,
streams.value.log
)
}
private lazy val coursierExtraProjectsTask: Initialize[Task[Seq[CProject]]] =
Def.task {
val projects = csrInterProjectDependencies.value
val projectModules = projects.map(_.module).toSet
projectDescriptors.value
.map { (k, v) =>
val id = k.asInstanceOf[ModuleRevisionId]
val desc = v.asInstanceOf[ModuleDescriptor]
moduleFromIvy(id) -> desc
}
.filter { case (module, _) =>
!projectModules(module)
}
.toVector
.map { (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.toSeq,
configurations,
Nil,
None,
Nil,
CInfo("", "", Nil, Nil, None)
)
}
}
private def moduleFromIvy(id: ModuleRevisionId): CModule =
CModule(
COrganization(id.getOrganisation),
CModuleName(id.getName),
id.getExtraAttributes.asScala.map { (k0, v0) =>
k0.asInstanceOf[String] -> v0.asInstanceOf[String]
}.toMap
)
private def dependencyFromIvy(
desc: DependencyDescriptor
): Seq[(CConfiguration, CDependency)] =
val id = desc.getDependencyRevisionId
val module = moduleFromIvy(id)
val exclusions = desc.getAllExcludeRules.map { rule =>
val modId = rule.getId.getModuleId
(COrganization(modId.getOrganisation), CModuleName(modId.getName))
}.toSet
val configurations = desc.getModuleConfigurations.toVector
.flatMap(Inputs.ivyXmlMappings)
def dependency(conf: CConfiguration, pub: CPublication) = CDependency(
module,
id.getRevision,
conf,
exclusions,
pub,
optional = false,
desc.isTransitive
)
val publications: CConfiguration => CPublication =
val artifacts = desc.getAllDependencyArtifacts
val m = artifacts.toVector.flatMap { art =>
val pub = CPublication(
art.getName,
CType(art.getType),
CExtension(art.getExt()),
CClassifier("")
)
art.getConfigurations.map(CConfiguration(_)).toVector.map { conf =>
conf -> pub
}
}.toMap
c => m.getOrElse(c, CPublication("", CType(""), CExtension(""), CClassifier("")))
configurations.map { (from, to) =>
from -> dependency(to, publications(to))
}
end IvyDependencyPlugin