mirror of https://github.com/sbt/sbt.git
[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:
parent
4f4bc374f6
commit
d71fe5b7a3
|
|
@ -156,6 +156,7 @@ jobs:
|
|||
./sbt -v --client "scripted dependency-management/* project-load/* project-matrix/* java/* run/*"
|
||||
./sbt -v --client "scripted plugins/*"
|
||||
./sbt -v --client "scripted nio/*"
|
||||
./sbt -v --client "scripted ivy/*"
|
||||
- name: Build and test (4)
|
||||
if: ${{ matrix.jobtype == 4 }}
|
||||
shell: bash
|
||||
|
|
|
|||
24
build.sbt
24
build.sbt
|
|
@ -690,7 +690,7 @@ lazy val buildFileProj = (project in file("buildfile"))
|
|||
libraryDependencies ++= Seq(scalaCompiler),
|
||||
mimaSettings,
|
||||
)
|
||||
.dependsOn(lmCore, lmIvy)
|
||||
.dependsOn(lmCore)
|
||||
.configure(addSbtIO, addSbtCompilerInterface, addSbtZincCompileCore)
|
||||
|
||||
// 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,
|
||||
mimaSettings,
|
||||
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)
|
||||
|
||||
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
|
||||
// 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)
|
||||
|
|
@ -993,6 +1012,7 @@ def allProjects =
|
|||
mainSettingsProj,
|
||||
zincLmIntegrationProj,
|
||||
mainProj,
|
||||
sbtIvyProj,
|
||||
sbtProj,
|
||||
bundledLauncherProj,
|
||||
sbtClientProj,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package sbt.internal.librarymanagement
|
|||
|
||||
import java.io.File
|
||||
import sbt.librarymanagement.*
|
||||
import sbt.librarymanagement.syntax.*
|
||||
|
||||
object UpdateClassifiersUtil {
|
||||
|
||||
|
|
@ -76,4 +77,26 @@ object UpdateClassifiersUtil {
|
|||
.withExplicitArtifacts(arts)
|
||||
.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])
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -14,8 +14,6 @@ import java.util.{ Optional, UUID }
|
|||
import java.util.concurrent.TimeUnit
|
||||
import lmcoursier.CoursierDependencyResolution
|
||||
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 sbt.Def.{ Initialize, ScopedKey, Setting, SettingsDefinition, parsed }
|
||||
import sbt.Keys.*
|
||||
|
|
@ -33,7 +31,6 @@ import sbt.internal.classpath.AlternativeZincUtil
|
|||
import sbt.internal.inc.JavaInterfaceUtil.*
|
||||
import sbt.internal.inc.classpath.ClasspathFilter
|
||||
import sbt.internal.inc.{ CompileOutput, MappedFileConverter, Stamps, ZincLmUtil, ZincUtil }
|
||||
import sbt.internal.librarymanagement.ivy.*
|
||||
import sbt.internal.librarymanagement.mavenint.{
|
||||
PomExtraDependencyAttributes,
|
||||
SbtPomExtraProperties
|
||||
|
|
@ -281,7 +278,7 @@ object Defaults extends BuildCommon {
|
|||
pomPostProcess :== idFun,
|
||||
pomAllRepositories :== false,
|
||||
pomIncludeRepository :== Classpaths.defaultRepositoryFilter,
|
||||
updateOptions := UpdateOptions(),
|
||||
updateOptions := None, // overridden by IvyDependencyPlugin with UpdateOptions()
|
||||
forceUpdatePeriod :== None,
|
||||
platform :== Platform.jvm,
|
||||
// coursier settings
|
||||
|
|
@ -2851,10 +2848,10 @@ object Classpaths {
|
|||
val nameWithCross = crossVersion(artifact.value.name)
|
||||
val version = Keys.version.value
|
||||
val pomFile = config.file.get.getParentFile / s"$nameWithCross-$version.pom"
|
||||
val publisher = Keys.publisher.value
|
||||
val ivySbt = Keys.ivySbt.value
|
||||
val module = new ivySbt.Module(moduleSettings.value, appendSbtCrossVersion = true)
|
||||
publisher.makePomFile(module, config.withFile(pomFile), streams.value.log)
|
||||
val pub = Keys.publisher.value
|
||||
val module =
|
||||
pub.moduleDescriptor(moduleSettings.value.asInstanceOf[ModuleDescriptorConfiguration])
|
||||
pub.makePomFile(module, config.withFile(pomFile), streams.value.log)
|
||||
converter.toVirtualFile(pomFile.toPath)
|
||||
|
||||
def ivyPublishSettings: Seq[Setting[?]] = publishGlobalDefaults ++ Seq(
|
||||
|
|
@ -2863,8 +2860,10 @@ object Classpaths {
|
|||
makePom := Def.uncached {
|
||||
val converter = fileConverter.value
|
||||
val config = makePomConfiguration.value
|
||||
val publisher = Keys.publisher.value
|
||||
publisher.makePomFile(ivyModule.value, config, streams.value.log)
|
||||
val pub = Keys.publisher.value
|
||||
val module =
|
||||
pub.moduleDescriptor(moduleSettings.value.asInstanceOf[ModuleDescriptorConfiguration])
|
||||
pub.makePomFile(module, config, streams.value.log)
|
||||
converter.toVirtualFile(config.file.get.toPath())
|
||||
},
|
||||
(makePom / packagedArtifact) := Def.uncached((makePom / artifact).value -> makePom.value),
|
||||
|
|
@ -3135,7 +3134,7 @@ object Classpaths {
|
|||
makePom / artifact := Artifact.pom(moduleName.value),
|
||||
projectID := defaultProjectID.value,
|
||||
projectID := pluginProjectID.value,
|
||||
projectDescriptors := Def.uncached(depMap.value),
|
||||
projectDescriptors := Def.uncached(Map.empty[Any, Any]),
|
||||
updateConfiguration := {
|
||||
// Tell the UpdateConfiguration which artifact types are special (for sources and javadocs)
|
||||
val specialArtifactTypes = sourceArtifactTypes.value.toSet union docArtifactTypes.value.toSet
|
||||
|
|
@ -3158,8 +3157,7 @@ object Classpaths {
|
|||
else None
|
||||
},
|
||||
dependencyResolution := Def.uncached(dependencyResolutionTask.value),
|
||||
publisher := Def.uncached(IvyPublisher(ivyConfiguration.value)),
|
||||
ivyConfiguration := Def.uncached(mkIvyConfiguration.value),
|
||||
ivyConfiguration := Def.uncached((): Any),
|
||||
ivyConfigurations := {
|
||||
val confs = thisProject.value.configurations
|
||||
(confs ++ confs.map(internalConfigurationMap.value) ++ (if (autoCompilerPlugins.value)
|
||||
|
|
@ -3302,8 +3300,11 @@ object Classpaths {
|
|||
overwrite = isSnapshot.value
|
||||
)
|
||||
},
|
||||
ivySbt := Def.uncached(ivySbt0.value),
|
||||
ivyModule := Def.uncached { val is = ivySbt.value; new is.Module(moduleSettings.value) },
|
||||
ivySbt := Def.uncached((): Any),
|
||||
ivyModule := Def.uncached((): Any),
|
||||
publisher := Def.uncached(
|
||||
Classpaths.defaultPublisher(dependencyResolution.value, fullResolvers.value.toVector)
|
||||
),
|
||||
allCredentials := Def.uncached(LMCoursier.allCredentialsTask.value),
|
||||
transitiveUpdate := Def.uncached(transitiveUpdateTask.value),
|
||||
updateCacheName := {
|
||||
|
|
@ -3380,7 +3381,6 @@ object Classpaths {
|
|||
CoursierInputsTasks.coursierFallbackDependenciesTask.value
|
||||
),
|
||||
) ++
|
||||
IvyXml.generateIvyXmlSettings() ++
|
||||
LMCoursier.publicationsSetting(Seq(Compile, Test).map(c => c -> CConfiguration(c.name)))
|
||||
|
||||
def jvmBaseSettings: Seq[Setting[?]] = Seq(
|
||||
|
|
@ -3496,11 +3496,6 @@ object Classpaths {
|
|||
)
|
||||
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 {
|
||||
val deps = allDependencies.value.toVector
|
||||
errorInsecureProtocolInModules(deps, streams.value.log)
|
||||
|
|
@ -3536,21 +3531,6 @@ object Classpaths {
|
|||
.resolvers
|
||||
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),
|
||||
// Redefine scalaVersion and scalaBinaryVersion specifically for the dependency graph used for updateSbtClassifiers task.
|
||||
// to fix https://github.com/sbt/sbt/issues/2686
|
||||
|
|
@ -3583,7 +3563,6 @@ object Classpaths {
|
|||
.task {
|
||||
val lm = dependencyResolution.value
|
||||
val s = streams.value
|
||||
val is = ivySbt.value
|
||||
val mod = classifiersModule.value
|
||||
val updateConfig0 = updateConfiguration.value
|
||||
val updateConfig = updateConfig0
|
||||
|
|
@ -3596,7 +3575,9 @@ object Classpaths {
|
|||
val srcTypes = sourceArtifactTypes.value
|
||||
val docTypes = docArtifactTypes.value
|
||||
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
|
||||
withExcludes(out, mod.classifiers, lock(app)) { excludes =>
|
||||
// val noExplicitCheck = ivy.map(_.withCheckExplicit(false))
|
||||
|
|
@ -3703,9 +3684,9 @@ object Classpaths {
|
|||
|
||||
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, config.value, streams.value.log)
|
||||
sys.error(
|
||||
"deliver/makeIvyXml requires the sbt-ivy plugin. Add IvyDependencyPlugin to your project."
|
||||
)
|
||||
}
|
||||
|
||||
@deprecated("Use variant without delivery key", "1.1.1")
|
||||
|
|
@ -3736,16 +3717,10 @@ object Classpaths {
|
|||
val log = streams.value.log
|
||||
val ref = thisProjectRef.value
|
||||
logSkipPublish(log, ref)
|
||||
} else if (!useIvy.value) {
|
||||
sys.error(
|
||||
"publishOrSkip requires useIvy := true. Use publish/publishLocal for ivyless publishing."
|
||||
)
|
||||
} else {
|
||||
val conf = config.value
|
||||
val log = streams.value.log
|
||||
val module = ivyModule.value
|
||||
val publisherInterface = publisher.value
|
||||
publisherInterface.publish(module, conf, log)
|
||||
sys.error(
|
||||
"publishOrSkip requires the sbt-ivy plugin. Use publish/publishLocal for ivyless publishing."
|
||||
)
|
||||
}
|
||||
}
|
||||
.tag(Tags.Publish, Tags.Network)
|
||||
|
|
@ -4062,17 +4037,6 @@ object Classpaths {
|
|||
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 getPublishTo(repo: Option[Resolver]): Resolver =
|
||||
|
|
@ -4195,31 +4159,62 @@ object Classpaths {
|
|||
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]] =
|
||||
projectDescriptors.map { m =>
|
||||
val resolver = new ProjectResolver(ProjectResolver.InterProject, m)
|
||||
new RawRepository(resolver, resolver.getName)
|
||||
Def.task {
|
||||
// Stub resolver — Coursier handles inter-project deps via csrInterProjectDependencies.
|
||||
// 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 {
|
||||
val c = fileConverter.value
|
||||
val resourceDirs = resourceDirectories.value
|
||||
|
|
@ -4247,23 +4242,6 @@ object Classpaths {
|
|||
|
||||
def internalDependencyJarsTask: Initialize[Task[Classpath]] =
|
||||
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(
|
||||
projectRef: ProjectRef,
|
||||
|
|
@ -4474,7 +4452,7 @@ object Classpaths {
|
|||
try {
|
||||
app.provider.scalaProvider.launcher.checksums.toVector
|
||||
} catch {
|
||||
case _: NoSuchMethodError => IvySbt.DefaultChecksums
|
||||
case _: NoSuchMethodError => Vector("sha1", "md5")
|
||||
}
|
||||
|
||||
def isOverrideRepositories(app: xsbti.AppConfiguration): Boolean =
|
||||
|
|
|
|||
|
|
@ -13,16 +13,13 @@ import java.io.File
|
|||
import java.net.URI
|
||||
import lmcoursier.definitions.{ CacheLogger, ModuleMatchers, Reconciliation }
|
||||
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.KeyRanks.*
|
||||
import sbt.internal.InMemoryCacheStore.CacheStoreFactoryFactory
|
||||
import sbt.internal.*
|
||||
import sbt.internal.bsp.*
|
||||
import sbt.internal.inc.ScalaInstance
|
||||
import sbt.internal.librarymanagement.{ CompatibilityWarningOptions, IvySbt }
|
||||
import sbt.internal.librarymanagement.ivy.{ IvyConfiguration, UpdateOptions }
|
||||
import sbt.internal.librarymanagement.CompatibilityWarningOptions
|
||||
import sbt.internal.server.BuildServerProtocol.BspFullWorkspace
|
||||
import sbt.internal.server.{ BspCompileTask, BuildServerReporter, ServerHandler }
|
||||
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 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)
|
||||
// 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 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 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 updateOptions = settingKey[UpdateOptions]("Options for resolving managed dependencies.").withRank(DSetting)
|
||||
val updateOptions = settingKey[Any]("Options for resolving managed dependencies.").withRank(DSetting)
|
||||
|
||||
@transient
|
||||
val unresolvedWarningConfiguration = taskKey[UnresolvedWarningConfiguration]("Configuration for unresolved dependency warning.").withRank(DTask)
|
||||
|
|
@ -537,8 +534,8 @@ object Keys {
|
|||
@transient
|
||||
val dependencyResolution = taskKey[DependencyResolution]("Provides the sbt interface to dependency resolution.").withRank(CTask)
|
||||
val publisher = taskKey[Publisher]("Provides the sbt interface to publisher")
|
||||
val ivySbt = taskKey[IvySbt]("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 ivySbt = taskKey[Any]("Provides the sbt interface to Ivy.").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 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)
|
||||
|
|
@ -638,7 +635,7 @@ object Keys {
|
|||
@transient
|
||||
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 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 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)
|
||||
|
|
|
|||
|
|
@ -13,26 +13,12 @@ import java.net.URI
|
|||
import sbt.librarymanagement.{ Credentials as IvyCredentials, * }
|
||||
import sbt.util.Logger
|
||||
import sbt.Keys.*
|
||||
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,
|
||||
Strict as CStrict,
|
||||
}
|
||||
import lmcoursier.definitions.{ Project as CProject, Strict as CStrict }
|
||||
import lmcoursier.credentials.DirectCredentials
|
||||
import lmcoursier.{ FallbackDependency, FromSbt, Inputs }
|
||||
import sbt.internal.librarymanagement.mavenint.SbtPomExtraProperties
|
||||
import sbt.ProjectExtra.transitiveInterDependencies
|
||||
import sbt.ScopeFilter.Make.*
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
object CoursierInputsTasks {
|
||||
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]]] =
|
||||
(Def
|
||||
.task {
|
||||
|
|
@ -171,34 +102,9 @@ object CoursierInputsTasks {
|
|||
|
||||
private[sbt] def coursierExtraProjectsTask: Def.Initialize[sbt.Task[Seq[CProject]]] = {
|
||||
Def.task {
|
||||
val projects = csrInterProjectDependencies.value
|
||||
val projectModules = projects.map(_.module).toSet
|
||||
|
||||
// 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)
|
||||
)
|
||||
}
|
||||
// Coursier handles inter-project dependencies natively via csrInterProjectDependencies.
|
||||
// The Ivy-typed projectDescriptors are only populated when IvyDependencyPlugin is enabled.
|
||||
Seq.empty[CProject]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -230,7 +136,7 @@ object CoursierInputsTasks {
|
|||
.flatMap {
|
||||
case dc: IvyCredentials.DirectCredentials => List(dc)
|
||||
case fc: IvyCredentials.FileCredentials =>
|
||||
sbt.internal.librarymanagement.ivy.IvyCredentials.loadCredentials(fc.path) match {
|
||||
sbt.librarymanagement.CredentialUtils.loadCredentials(fc.path) match {
|
||||
case Left(err) =>
|
||||
log.warn(s"$err, ignoring it")
|
||||
Nil
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ object LMCoursier {
|
|||
case dc: IvyCredentials.DirectCredentials =>
|
||||
Right[String, IvyCredentials.DirectCredentials](dc)
|
||||
case fc: IvyCredentials.FileCredentials =>
|
||||
sbt.internal.librarymanagement.ivy.IvyCredentials.loadCredentials(fc.path)
|
||||
sbt.librarymanagement.CredentialUtils.loadCredentials(fc.path)
|
||||
}) match {
|
||||
case Left(err) => st.log.warn(err)
|
||||
case Right(d) =>
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ import sbt.ProjectExtra.{ extract, runUnloadHooks, setProject }
|
|||
import sbt.SlashSyntax0.*
|
||||
import sbt.librarymanagement.LibraryManagementCodec.given
|
||||
import java.io.File
|
||||
import org.apache.ivy.core.module.{ descriptor, id }
|
||||
import descriptor.ModuleDescriptor, id.ModuleRevisionId
|
||||
|
||||
object GlobalPlugin {
|
||||
// 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 intcp = (Runtime / internalDependencyClasspath).value
|
||||
val prods = (Runtime / exportedProducts).value
|
||||
val depMap =
|
||||
if useIvy.value then projectDescriptors.value + ivyModule.value.dependencyMapping(state.log)
|
||||
else projectDescriptors.value
|
||||
val depMap = projectDescriptors.value
|
||||
|
||||
GlobalPluginData(
|
||||
projectID.value,
|
||||
|
|
@ -125,7 +121,7 @@ object GlobalPlugin {
|
|||
final case class GlobalPluginData(
|
||||
projectID: ModuleID,
|
||||
dependencies: Seq[ModuleID],
|
||||
descriptors: Map[ModuleRevisionId, ModuleDescriptor],
|
||||
descriptors: Map[Any, Any],
|
||||
resolvers: Vector[Resolver],
|
||||
fullClasspath: Classpath,
|
||||
internalClasspath: Classpath
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ private[sbt] object LibraryManagement {
|
|||
Seq[ScopedKey[?]],
|
||||
ScopedKey[?],
|
||||
Option[FiniteDuration],
|
||||
IvySbt#Module,
|
||||
ModuleSettings,
|
||||
String,
|
||||
ProjectRef,
|
||||
Boolean,
|
||||
|
|
@ -318,7 +318,6 @@ private[sbt] object LibraryManagement {
|
|||
UnresolvedWarningConfiguration,
|
||||
Boolean,
|
||||
CompatibilityWarningOptions,
|
||||
IvySbt,
|
||||
GetClassifiersModule,
|
||||
File,
|
||||
xsbti.AppConfiguration,
|
||||
|
|
@ -334,7 +333,7 @@ private[sbt] object LibraryManagement {
|
|||
Keys.executionRoots,
|
||||
Keys.resolvedScoped.toTaskable,
|
||||
Keys.forceUpdatePeriod.toTaskable,
|
||||
Keys.ivyModule.toTaskable,
|
||||
Keys.moduleSettings.toTaskable,
|
||||
Keys.updateCacheName.toTaskable,
|
||||
Keys.thisProjectRef.toTaskable,
|
||||
(Keys.update / Keys.skip).toTaskable,
|
||||
|
|
@ -342,7 +341,6 @@ private[sbt] object LibraryManagement {
|
|||
(Keys.update / Keys.unresolvedWarningConfiguration).toTaskable,
|
||||
Keys.publishMavenStyle.toTaskable,
|
||||
Keys.compatibilityWarningOptions.toTaskable,
|
||||
Keys.ivySbt,
|
||||
Keys.classifiersModule,
|
||||
Keys.dependencyCacheDirectory,
|
||||
Keys.appConfiguration.toTaskable,
|
||||
|
|
@ -358,7 +356,7 @@ private[sbt] object LibraryManagement {
|
|||
er,
|
||||
rs,
|
||||
fup,
|
||||
im,
|
||||
ms,
|
||||
ucn,
|
||||
thisRef,
|
||||
sk,
|
||||
|
|
@ -366,7 +364,6 @@ private[sbt] object LibraryManagement {
|
|||
uwConfig,
|
||||
mavenStyle,
|
||||
cwo,
|
||||
ivySbt0,
|
||||
mod,
|
||||
dcd,
|
||||
app,
|
||||
|
|
@ -400,10 +397,8 @@ private[sbt] object LibraryManagement {
|
|||
conf1.withLogicalClock(LogicalClock(state0.hashCode))
|
||||
}
|
||||
cachedUpdate(
|
||||
// LM API
|
||||
lm = lm,
|
||||
// Ivy-free ModuleDescriptor
|
||||
module = im,
|
||||
module = lm.moduleDescriptor(ms.asInstanceOf[ModuleDescriptorConfiguration]),
|
||||
s.cacheStoreFactory.sub(ucn),
|
||||
Reference.display(thisRef),
|
||||
updateConf,
|
||||
|
|
@ -446,12 +441,12 @@ private[sbt] object LibraryManagement {
|
|||
)
|
||||
val report = f(excludes)
|
||||
val allExcludes: Map[ModuleID, Vector[ConfigRef]] = excludes ++
|
||||
IvyActions
|
||||
UpdateClassifiersUtil
|
||||
.extractExcludes(report)
|
||||
.view
|
||||
.mapValues(cs => cs.map(c => ConfigRef(c)).toVector)
|
||||
store.write(allExcludes)
|
||||
IvyActions
|
||||
UpdateClassifiersUtil
|
||||
.addExcluded(
|
||||
report,
|
||||
classifiers.toVector,
|
||||
|
|
@ -986,7 +981,7 @@ private[sbt] object LibraryManagement {
|
|||
Def.task {
|
||||
val log = streams.value.log
|
||||
val conf = publishConfiguration.value
|
||||
val module = ivyModule.value
|
||||
val module = ivyModule.value.asInstanceOf[ModuleDescriptor]
|
||||
val publisherInterface = publisher.value
|
||||
publisherInterface.publish(module, conf, log)
|
||||
}
|
||||
|
|
@ -1114,7 +1109,7 @@ private[sbt] object LibraryManagement {
|
|||
Def.task {
|
||||
val log = streams.value.log
|
||||
val conf = publishLocalConfiguration.value
|
||||
val module = ivyModule.value
|
||||
val module = ivyModule.value.asInstanceOf[ModuleDescriptor]
|
||||
val publisherInterface = publisher.value
|
||||
publisherInterface.publish(module, conf, log)
|
||||
}
|
||||
|
|
@ -1157,7 +1152,7 @@ private[sbt] object LibraryManagement {
|
|||
Def.task {
|
||||
val log = streams.value.log
|
||||
val conf = publishM2Configuration.value
|
||||
val module = ivyModule.value
|
||||
val module = ivyModule.value.asInstanceOf[ModuleDescriptor]
|
||||
val publisherInterface = publisher.value
|
||||
publisherInterface.publish(module, conf, log)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -15,7 +15,7 @@ import sbt.internal.util.MessageOnlyException
|
|||
import sbt.io.IO
|
||||
import sbt.io.Path.contentOf
|
||||
import sbt.librarymanagement.Credentials
|
||||
import sbt.internal.librarymanagement.ivy.IvyCredentials
|
||||
import sbt.librarymanagement.CredentialUtils
|
||||
import sona.{ PublishingType, Sona }
|
||||
|
||||
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 = {
|
||||
val cred = IvyCredentials
|
||||
val cred = CredentialUtils
|
||||
.forHost(creds, Sona.host)
|
||||
.getOrElse(throw new MessageOnlyException(s"no credentials are found for ${Sona.host}"))
|
||||
Sona.oauthClient(cred.userName, cred.passwd, uploadRequestTimeout)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import sbt.ProjectExtra.*
|
|||
import sbt.internal.graph.*
|
||||
import sbt.internal.graph.backend.SbtUpdateReport
|
||||
import sbt.internal.graph.rendering.{ DagreHTML, TreeView }
|
||||
import sbt.internal.librarymanagement.*
|
||||
import sbt.internal.util.complete.{ Parser, Parsers }
|
||||
import sbt.internal.util.complete.DefaultParsers.*
|
||||
import sbt.io.IO
|
||||
|
|
@ -110,29 +109,12 @@ OPTIONS
|
|||
*/
|
||||
def coreSettings =
|
||||
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
|
||||
dependencyTreeIgnoreMissingUpdate / updateConfiguration := updateConfiguration.value
|
||||
.withMissingOk(true),
|
||||
dependencyTreeIgnoreMissingUpdate / evictionErrorLevel := Level.Warn,
|
||||
dependencyTreeIgnoreMissingUpdate / assumedEvictionErrorLevel := Level.Warn,
|
||||
dependencyTreeIgnoreMissingUpdate := Def.uncached {
|
||||
// inTask will make sure the new definition will pick up `ivyModule/updateConfiguration in ignoreMissingUpdate`
|
||||
Project.inTask(dependencyTreeIgnoreMissingUpdate, Classpaths.updateTask).value
|
||||
},
|
||||
)
|
||||
|
|
@ -390,8 +372,8 @@ OPTIONS
|
|||
type HasModule = {
|
||||
val module: ModuleID
|
||||
}
|
||||
def crossName(ivyModule: IvySbt#Module) =
|
||||
ivyModule.moduleSettings match {
|
||||
def crossName(module: Any) =
|
||||
module match {
|
||||
case ic: ModuleDescriptorConfiguration => ic.module.name
|
||||
case _ =>
|
||||
throw new IllegalStateException(
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
()
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
> check
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
ThisBuild / useIvy := true
|
||||
lazy val root = (project in file("."))
|
||||
lazy val a = project
|
||||
lazy val b = project
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
libraryDependencies += {
|
||||
val sbtV = sbtVersion.value
|
||||
("org.scala-sbt" % s"sbt-ivy_sbt${sbtV}_${scalaBinaryVersion.value}" % sbtV).intransitive()
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
libraryDependencies += {
|
||||
val sbtV = sbtVersion.value
|
||||
("org.scala-sbt" % s"sbt-ivy_sbt${sbtV}_${scalaBinaryVersion.value}" % sbtV).intransitive()
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ import scala.xml.{ Node, _ }
|
|||
import scala.xml.Utility.trim
|
||||
import sbt.internal.librarymanagement.{ IvySbt, MakePom }
|
||||
|
||||
ThisBuild / useIvy := true
|
||||
|
||||
lazy val check = taskKey[Unit]("check")
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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 =>
|
||||
(d \ "groupId").text == "com.eed3si9n" && (d \ "artifactId").text == "repatch-twitter-core_2.11"
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
libraryDependencies += {
|
||||
val sbtV = sbtVersion.value
|
||||
("org.scala-sbt" % s"sbt-ivy_sbt${sbtV}_${scalaBinaryVersion.value}" % sbtV).intransitive()
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
libraryDependencies += {
|
||||
val sbtV = sbtVersion.value
|
||||
("org.scala-sbt" % s"sbt-ivy_sbt${sbtV}_${scalaBinaryVersion.value}" % sbtV).intransitive()
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
ThisBuild / scalaVersion := "2.12.21"
|
||||
ThisBuild / useIvy := true
|
||||
def configIvyScala =
|
||||
scalaModuleInfo ~= (_ map (_ withCheckExplicit false))
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
libraryDependencies += {
|
||||
val sbtV = sbtVersion.value
|
||||
("org.scala-sbt" % s"sbt-ivy_sbt${sbtV}_${scalaBinaryVersion.value}" % sbtV).intransitive()
|
||||
}
|
||||
|
|
@ -210,21 +210,25 @@ object IvyXml {
|
|||
shadedConfigOpt: Option[Configuration]
|
||||
): Setting[Task[T]] =
|
||||
task := Def.uncached(task.dependsOnTask {
|
||||
Def.task {
|
||||
val currentProject = {
|
||||
val proj = csrProject.value
|
||||
val publications = csrPublications.value
|
||||
proj.withPublications(publications)
|
||||
Def.ifS(Def.task { sbt.Keys.useIvy.value })(
|
||||
Def.task {
|
||||
val currentProject = {
|
||||
val proj = csrProject.value
|
||||
val publications = csrPublications.value
|
||||
proj.withPublications(publications)
|
||||
}
|
||||
val resolved = sbt.Keys.resolvedDependencies.value
|
||||
IvyXml.writeFiles(
|
||||
currentProject,
|
||||
shadedConfigOpt,
|
||||
sbt.Keys.ivySbt.value.asInstanceOf[IvySbt],
|
||||
sbt.Keys.streams.value.log,
|
||||
resolved
|
||||
)
|
||||
}
|
||||
val resolved = sbt.Keys.resolvedDependencies.value
|
||||
IvyXml.writeFiles(
|
||||
currentProject,
|
||||
shadedConfigOpt,
|
||||
sbt.Keys.ivySbt.value,
|
||||
sbt.Keys.streams.value.log,
|
||||
resolved
|
||||
)
|
||||
}
|
||||
)(
|
||||
Def.task { () }
|
||||
)
|
||||
}.value)
|
||||
|
||||
private lazy val needsIvyXmlLocal = Seq(publishLocalConfiguration) ++ getPubConf(
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue