sbt.internal.librarymanagement.IvyXml.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of main_2.12 Show documentation
Show all versions of main_2.12 Show documentation
sbt is an interactive build tool
The newest version!
/*
* 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
package librarymanagement
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.Files
import lmcoursier.definitions.{ Configuration, Project }
import org.apache.ivy.core.module.id.ModuleRevisionId
import Def.Setting
import sbt.Keys.{
csrProject,
csrPublications,
publishLocalConfiguration,
publishConfiguration,
useCoursier
}
import sbt.librarymanagement.PublishConfiguration
import scala.collection.JavaConverters._
import scala.xml.{ Node, PrefixedAttribute }
object IvyXml {
import sbt.Project._
private def rawContent(
currentProject: Project,
shadedConfigOpt: Option[Configuration]
): String = {
// Important: width = Int.MaxValue, so that no tag gets truncated.
// In particular, that prevents things like to be split to
//
//
// by the pretty-printer.
// See https://github.com/sbt/sbt/issues/3412.
val printer = new scala.xml.PrettyPrinter(Int.MaxValue, 2)
"""""" + '\n' +
printer.format(content(currentProject, shadedConfigOpt))
}
// These are required for publish to be fine, later on.
private[sbt] def writeFiles(
currentProject: Project,
shadedConfigOpt: Option[Configuration],
ivySbt: IvySbt,
log: sbt.util.Logger
): Unit = {
val ivyCacheManager = ivySbt.withIvy(log)(ivy => ivy.getResolutionCacheManager)
val ivyModule = ModuleRevisionId.newInstance(
currentProject.module.organization.value,
currentProject.module.name.value,
currentProject.version,
currentProject.module.attributes.asJava
)
val cacheIvyFile = ivyCacheManager.getResolvedIvyFileInCache(ivyModule)
val cacheIvyPropertiesFile = ivyCacheManager.getResolvedIvyPropertiesInCache(ivyModule)
val content0 = rawContent(currentProject, shadedConfigOpt)
cacheIvyFile.getParentFile.mkdirs()
log.debug(s"writing Ivy file $cacheIvyFile")
Files.write(cacheIvyFile.toPath, content0.getBytes(UTF_8))
// Just writing an empty file here... Are these only used?
cacheIvyPropertiesFile.getParentFile.mkdirs()
Files.write(cacheIvyPropertiesFile.toPath, Array.emptyByteArray)
()
}
private def content(project0: Project, shadedConfigOpt: Option[Configuration]): Node = {
val filterOutDependencies =
shadedConfigOpt.toSet[Configuration].flatMap { shadedConfig =>
project0.dependencies
.collect { case (conf, dep) if conf.value == shadedConfig.value => dep }
}
val project: Project = project0.withDependencies(project0.dependencies.collect {
case p @ (_, dep) if !filterOutDependencies(dep) => p
})
val infoAttrs =
(project.module.attributes.toSeq ++ project.properties).foldLeft[xml.MetaData](xml.Null) {
case (acc, (k, v)) =>
new PrefixedAttribute("e", k, v, acc)
}
val licenseElems = project.info.licenses.map {
case (name, urlOpt) =>
val n =
urlOpt.fold(n) { url =>
n % .attributes
}
}
val descriptionElem = {
val n = {project.info.description}
if (project.info.homePage.nonEmpty)
n % .attributes
else
n
}
val infoElem = {
{licenseElems}
{descriptionElem}
} % infoAttrs
val confElems = project.configurations.toVector.collect {
case (name, extends0) if !shadedConfigOpt.exists(_.value == name.value) =>
val extends1 = shadedConfigOpt.fold(extends0)(c => extends0.filter(_.value != c.value))
val n =
if (extends1.nonEmpty)
n % .attributes
else
n
}
val publications = project.publications
.groupBy { case (_, p) => p }
.mapValues { _.map { case (cfg, _) => cfg } }
val publicationElems = publications.map {
case (pub, configs) =>
val n =
if (pub.classifier.value.nonEmpty)
n % .attributes
else
n
}
val dependencyElems = project.dependencies.toVector.map {
case (conf, dep) =>
val classifier = {
val pub = dep.publication
if (pub.classifier.value.nonEmpty)
Seq(
)
else
Seq.empty
}
val excludes = dep.exclusions.toSeq.map {
case (org, name) =>
}
val n =
${dep.configuration.value}"}>
{classifier}
{excludes}
val moduleAttrs = dep.module.attributes.foldLeft[xml.MetaData](xml.Null) {
case (acc, (k, v)) =>
new PrefixedAttribute("e", k, v, acc)
}
n % moduleAttrs
}
{infoElem}
{confElems}
{publicationElems}
{dependencyElems}
}
private def makeIvyXmlBefore[T](
task: TaskKey[T],
shadedConfigOpt: Option[Configuration]
): Setting[Task[T]] =
task := task.dependsOn {
Def.taskDyn {
val doGen = useCoursier.value
if (doGen)
Def.task {
val currentProject = {
val proj = csrProject.value
val publications = csrPublications.value
proj.withPublications(publications)
}
IvyXml.writeFiles(
currentProject,
shadedConfigOpt,
sbt.Keys.ivySbt.value,
sbt.Keys.streams.value.log
)
} else
Def.task(())
}
}.value
private lazy val needsIvyXmlLocal = Seq(publishLocalConfiguration) ++ getPubConf(
"makeIvyXmlLocalConfiguration"
)
private lazy val needsIvyXml = Seq(publishConfiguration) ++ getPubConf(
"makeIvyXmlConfiguration"
)
private[this] def getPubConf(method: String): List[TaskKey[PublishConfiguration]] =
try {
val cls = sbt.Keys.getClass
val m = cls.getMethod(method)
val task = m.invoke(sbt.Keys).asInstanceOf[TaskKey[PublishConfiguration]]
List(task)
} catch {
case _: Throwable => // FIXME Too wide
Nil
}
def generateIvyXmlSettings(
shadedConfigOpt: Option[Configuration] = None
): Seq[Setting[_]] =
(needsIvyXml ++ needsIvyXmlLocal).map(makeIvyXmlBefore(_, shadedConfigOpt))
}