Move ivy.xml generation stuff to sbt-coursier-shared

This commit is contained in:
Alexandre Archambault 2018-11-20 17:17:53 +01:00
parent 92bde55a5a
commit 6cd9b7e2b4
12 changed files with 223 additions and 194 deletions

View File

@ -45,7 +45,9 @@ lazy val `sbt-coursier-shared` = project
.enablePlugins(ScriptedPlugin)
.dependsOn(`lm-coursier`)
.settings(
plugin
plugin,
libraryDependencies += "com.lihaoyi" %% "utest" % "0.6.4" % Test,
testFrameworks += new TestFramework("utest.runner.Framework")
)
lazy val `sbt-lm-coursier` = project
@ -71,8 +73,6 @@ lazy val `sbt-coursier` = project
.dependsOn(`sbt-coursier-shared`)
.settings(
plugin,
libraryDependencies += "com.lihaoyi" %% "utest" % "0.6.4" % Test,
testFrameworks += new TestFramework("utest.runner.Framework"),
libraryDependencies +="io.get-coursier" %% "coursier-scalaz-interop" % coursierVersion,
scriptedDependencies := {
scriptedDependencies.value

View File

@ -0,0 +1,143 @@
package coursier.sbtcoursiershared
import coursier.core._
import coursier.lmcoursier._
import coursier.sbtcoursiershared.Structure._
import sbt.librarymanagement.{Artifact => _, Configuration => _, _}
import sbt.Def
import sbt.Keys._
object ArtifactsTasks {
def coursierPublicationsTask(
configsMap: (sbt.librarymanagement.Configuration, Configuration)*
): Def.Initialize[sbt.Task[Seq[(Configuration, Publication)]]] =
Def.task {
val state = sbt.Keys.state.value
val projectRef = sbt.Keys.thisProjectRef.value
val projId = sbt.Keys.projectID.value
val sv = sbt.Keys.scalaVersion.value
val sbv = sbt.Keys.scalaBinaryVersion.value
val ivyConfs = sbt.Keys.ivyConfigurations.value
val sourcesConfigOpt =
if (ivyConfigurations.value.exists(_.name == "sources"))
Some(Configuration("sources"))
else
None
val docsConfigOpt =
if (ivyConfigurations.value.exists(_.name == "docs"))
Some(Configuration("docs"))
else
None
val sbtBinArtifacts =
for ((config, targetConfig) <- configsMap) yield {
val publish = publishArtifact
.in(projectRef)
.in(packageBin)
.in(config)
.getOrElse(state, false)
if (publish)
artifact
.in(projectRef)
.in(packageBin)
.in(config)
.find(state)
.map(targetConfig -> _)
else
None
}
val sbtSourceArtifacts =
for ((config, targetConfig) <- configsMap) yield {
val publish = publishArtifact
.in(projectRef)
.in(packageSrc)
.in(config)
.getOrElse(state, false)
if (publish)
artifact
.in(projectRef)
.in(packageSrc)
.in(config)
.find(state)
.map(sourcesConfigOpt.getOrElse(targetConfig) -> _)
else
None
}
val sbtDocArtifacts =
for ((config, targetConfig) <- configsMap) yield {
val publish = publishArtifact
.in(projectRef)
.in(packageDoc)
.in(config)
.getOrElse(state, false)
if (publish)
artifact
.in(projectRef)
.in(packageDoc)
.in(config)
.find(state)
.map(docsConfigOpt.getOrElse(targetConfig) -> _)
else
None
}
val sbtArtifacts = sbtBinArtifacts ++ sbtSourceArtifacts ++ sbtDocArtifacts
def artifactPublication(artifact: sbt.Artifact) = {
val name = FromSbt.sbtCrossVersionName(
artifact.name,
projId.crossVersion,
sv,
sbv
)
Publication(
name,
Type(artifact.`type`),
Extension(artifact.extension),
artifact.classifier.fold(Classifier.empty)(Classifier(_))
)
}
val sbtArtifactsPublication = sbtArtifacts.collect {
case Some((config, artifact)) =>
config -> artifactPublication(artifact)
}
val stdArtifactsSet = sbtArtifacts.flatMap(_.map { case (_, a) => a }.toSeq).toSet
// Second-way of getting artifacts from SBT
// No obvious way of getting the corresponding publishArtifact value for the ones
// only here, it seems.
val extraSbtArtifacts = sbt.Keys.artifacts.in(projectRef).getOrElse(state, Nil)
.filterNot(stdArtifactsSet)
// Seems that SBT does that - if an artifact has no configs,
// it puts it in all of them. See for example what happens to
// the standalone JAR artifact of the coursier cli module.
def allConfigsIfEmpty(configs: Iterable[ConfigRef]): Iterable[ConfigRef] =
if (configs.isEmpty) ivyConfs.filter(_.isPublic).map(c => ConfigRef(c.name)) else configs
val extraSbtArtifactsPublication = for {
artifact <- extraSbtArtifacts
config <- allConfigsIfEmpty(artifact.configurations.map(x => ConfigRef(x.name)))
// FIXME If some configurations from artifact.configurations are not public, they may leak here :\
} yield Configuration(config.name) -> artifactPublication(artifact)
sbtArtifactsPublication ++ extraSbtArtifactsPublication
}
}

View File

@ -1,20 +1,22 @@
package coursier.sbtcoursier
package coursier.sbtcoursiershared
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.Files
import coursier.core.{Configuration, Project}
import org.apache.ivy.core.module.id.ModuleRevisionId
import sbt.{Def, Setting, Task, TaskKey}
import sbt.internal.librarymanagement.IvySbt
import sbt.librarymanagement.PublishConfiguration
import scala.collection.JavaConverters._
import scala.xml.{Node, PrefixedAttribute}
import sbt.internal.librarymanagement.IvySbt
object IvyXml {
def rawContent(
currentProject: Project,
shadedConfigOpt: Option[(String, Configuration)]
shadedConfigOpt: Option[Configuration]
): String = {
// Important: width = Int.MaxValue, so that no tag gets truncated.
@ -26,15 +28,15 @@ object IvyXml {
val printer = new scala.xml.PrettyPrinter(Int.MaxValue, 2)
"""<?xml version="1.0" encoding="UTF-8"?>""" + '\n' +
printer.format(content(currentProject, shadedConfigOpt.map(_._2)))
printer.format(content(currentProject, shadedConfigOpt))
}
// These are required for publish to be fine, later on.
def writeFiles(
currentProject: Project,
shadedConfigOpt: Option[(String, Configuration)],
shadedConfigOpt: Option[Configuration],
ivySbt: IvySbt,
log: sbt.Logger
log: sbt.util.Logger
): Unit = {
val ivyCacheManager = ivySbt.withIvy(log)(ivy =>
@ -153,4 +155,45 @@ object IvyXml {
</ivy-module>
}
def makeIvyXmlBefore[T](
task: TaskKey[T],
shadedConfigOpt: Option[Configuration]
): Setting[Task[T]] =
task := task.dependsOn {
Def.taskDyn {
import SbtCoursierShared.autoImport._
val doGen = coursierGenerateIvyXml.value
if (doGen)
Def.task {
val currentProject = {
val proj = coursierProject.value
val publications = coursierPublications.value
proj.copy(publications = publications)
}
IvyXml.writeFiles(currentProject, shadedConfigOpt, sbt.Keys.ivySbt.value, sbt.Keys.streams.value.log)
}
else
Def.task(())
}
}.value
private lazy val needsIvyXmlLocal = Seq(sbt.Keys.publishLocalConfiguration) ++ getPubConf("makeIvyXmlLocalConfiguration")
private lazy val needsIvyXml = Seq(sbt.Keys.publishConfiguration) ++ getPubConf("makeIvyXmlConfiguration")
private[this] def getPubConf(method: String): List[TaskKey[PublishConfiguration]] =
try {
val cls = sbt.Keys.getClass
val m = cls.getMethod(method)
val task = m.invoke(sbt.Keys).asInstanceOf[TaskKey[PublishConfiguration]]
List(task)
} catch {
case _: Throwable => // FIXME Too wide
Nil
}
def generateIvyXmlSettings(
shadedConfigOpt: Option[Configuration] = None
): Seq[Setting[_]] =
(needsIvyXml ++ needsIvyXmlLocal).map(IvyXml.makeIvyXmlBefore(_, shadedConfigOpt))
}

View File

@ -1,7 +1,7 @@
package coursier.sbtcoursiershared
import coursier.core.Project
import sbt.{AutoPlugin, TaskKey}
import coursier.core.{Configuration, Project, Publication}
import sbt.{AutoPlugin, Compile, Setting, TaskKey, Test, settingKey}
object SbtCoursierShared extends AutoPlugin {
@ -10,15 +10,24 @@ object SbtCoursierShared extends AutoPlugin {
override def requires = sbt.plugins.JvmPlugin
object autoImport {
val coursierGenerateIvyXml = settingKey[Boolean]("")
val coursierProject = TaskKey[Project]("coursier-project")
val coursierInterProjectDependencies = TaskKey[Seq[Project]]("coursier-inter-project-dependencies", "Projects the current project depends on, possibly transitively")
val coursierPublications = TaskKey[Seq[(Configuration, Publication)]]("coursier-publications")
}
import autoImport._
override def projectSettings = Seq(
coursierProject := InputsTasks.coursierProjectTask.value,
coursierInterProjectDependencies := InputsTasks.coursierInterProjectDependenciesTask.value
)
def publicationsSetting(packageConfigs: Seq[(sbt.Configuration, Configuration)]): Setting[_] =
coursierPublications := ArtifactsTasks.coursierPublicationsTask(packageConfigs: _*).value
override def projectSettings =
Seq[Setting[_]](
coursierGenerateIvyXml := true,
coursierProject := InputsTasks.coursierProjectTask.value,
coursierInterProjectDependencies := InputsTasks.coursierInterProjectDependenciesTask.value,
publicationsSetting(Seq(Compile, Test).map(c => c -> Configuration(c.name)))
) ++
IvyXml.generateIvyXmlSettings()
}

View File

@ -1,4 +1,4 @@
package coursier.sbtcoursier
package coursier.sbtcoursiershared
import coursier.core.Configuration
import coursier.{Info, Module, Project, moduleNameString, organizationString}

View File

@ -6,144 +6,11 @@ import coursier.{Artifact, FileError}
import coursier.core._
import coursier.lmcoursier._
import coursier.sbtcoursier.Keys._
import coursier.sbtcoursiershared.Structure._
import sbt.librarymanagement.{Artifact => _, Configuration => _, _}
import sbt.Def
import sbt.Keys._
object ArtifactsTasks {
def coursierPublicationsTask(
configsMap: (sbt.librarymanagement.Configuration, Configuration)*
): Def.Initialize[sbt.Task[Seq[(Configuration, Publication)]]] =
Def.task {
val state = sbt.Keys.state.value
val projectRef = sbt.Keys.thisProjectRef.value
val projId = sbt.Keys.projectID.value
val sv = sbt.Keys.scalaVersion.value
val sbv = sbt.Keys.scalaBinaryVersion.value
val ivyConfs = sbt.Keys.ivyConfigurations.value
val sourcesConfigOpt =
if (ivyConfigurations.value.exists(_.name == "sources"))
Some(Configuration("sources"))
else
None
val docsConfigOpt =
if (ivyConfigurations.value.exists(_.name == "docs"))
Some(Configuration("docs"))
else
None
val sbtBinArtifacts =
for ((config, targetConfig) <- configsMap) yield {
val publish = publishArtifact
.in(projectRef)
.in(packageBin)
.in(config)
.getOrElse(state, false)
if (publish)
artifact
.in(projectRef)
.in(packageBin)
.in(config)
.find(state)
.map(targetConfig -> _)
else
None
}
val sbtSourceArtifacts =
for ((config, targetConfig) <- configsMap) yield {
val publish = publishArtifact
.in(projectRef)
.in(packageSrc)
.in(config)
.getOrElse(state, false)
if (publish)
artifact
.in(projectRef)
.in(packageSrc)
.in(config)
.find(state)
.map(sourcesConfigOpt.getOrElse(targetConfig) -> _)
else
None
}
val sbtDocArtifacts =
for ((config, targetConfig) <- configsMap) yield {
val publish = publishArtifact
.in(projectRef)
.in(packageDoc)
.in(config)
.getOrElse(state, false)
if (publish)
artifact
.in(projectRef)
.in(packageDoc)
.in(config)
.find(state)
.map(docsConfigOpt.getOrElse(targetConfig) -> _)
else
None
}
val sbtArtifacts = sbtBinArtifacts ++ sbtSourceArtifacts ++ sbtDocArtifacts
def artifactPublication(artifact: sbt.Artifact) = {
val name = FromSbt.sbtCrossVersionName(
artifact.name,
projId.crossVersion,
sv,
sbv
)
Publication(
name,
Type(artifact.`type`),
Extension(artifact.extension),
artifact.classifier.fold(Classifier.empty)(Classifier(_))
)
}
val sbtArtifactsPublication = sbtArtifacts.collect {
case Some((config, artifact)) =>
config -> artifactPublication(artifact)
}
val stdArtifactsSet = sbtArtifacts.flatMap(_.map { case (_, a) => a }.toSeq).toSet
// Second-way of getting artifacts from SBT
// No obvious way of getting the corresponding publishArtifact value for the ones
// only here, it seems.
val extraSbtArtifacts = sbt.Keys.artifacts.in(projectRef).getOrElse(state, Nil)
.filterNot(stdArtifactsSet)
// Seems that SBT does that - if an artifact has no configs,
// it puts it in all of them. See for example what happens to
// the standalone JAR artifact of the coursier cli module.
def allConfigsIfEmpty(configs: Iterable[ConfigRef]): Iterable[ConfigRef] =
if (configs.isEmpty) ivyConfs.filter(_.isPublic).map(c => ConfigRef(c.name)) else configs
val extraSbtArtifactsPublication = for {
artifact <- extraSbtArtifacts
config <- allConfigsIfEmpty(artifact.configurations.map(x => ConfigRef(x.name)))
// FIXME If some configurations from artifact.configurations are not public, they may leak here :\
} yield Configuration(config.name) -> artifactPublication(artifact)
sbtArtifactsPublication ++ extraSbtArtifactsPublication
}
def artifactsTask(
withClassifiers: Boolean,
sbtClassifiers: Boolean = false,

View File

@ -6,7 +6,6 @@ import coursier.{Cache, CachePolicy, TermDisplay}
import coursier.core.{Configuration, ResolutionProcess}
import coursier.lmcoursier.SbtCoursierCache
import coursier.sbtcoursiershared.SbtCoursierShared
import sbt.librarymanagement.{Configuration => _, Resolver => _, _}
import sbt.{Cache => _, Configuration => _, _}
import sbt.Keys._
@ -35,7 +34,6 @@ object CoursierPlugin extends AutoPlugin {
val coursierFallbackDependencies = Keys.coursierFallbackDependencies
val coursierCache = Keys.coursierCache
val coursierConfigGraphs = Keys.coursierConfigGraphs
val coursierPublications = Keys.coursierPublications
val coursierSbtClassifiersModule = Keys.coursierSbtClassifiersModule
val coursierConfigurations = Keys.coursierConfigurations
@ -63,7 +61,6 @@ object CoursierPlugin extends AutoPlugin {
}
import autoImport._
import SbtCoursierShared.autoImport._
lazy val treeSettings = Seq(
coursierDependencyTree := DisplayTasks.coursierDependencyTreeTask(
@ -79,19 +76,6 @@ object CoursierPlugin extends AutoPlugin {
}.evaluated
)
def makeIvyXmlBefore[T](
task: TaskKey[T],
shadedConfigOpt: Option[(String, Configuration)]
): Setting[Task[T]] =
task := task.dependsOn(Def.task {
val currentProject = {
val proj = coursierProject.value
val publications = coursierPublications.value
proj.copy(publications = publications)
}
IvyXml.writeFiles(currentProject, shadedConfigOpt, ivySbt.value, streams.value.log)
}).value
private val pluginIvySnapshotsBase = Resolver.SbtRepositoryRoot.stripSuffix("/") + "/ivy-snapshots"
// allows to get the actual repo list when sbt starts up
@ -162,9 +146,8 @@ object CoursierPlugin extends AutoPlugin {
)
def coursierSettings(
shadedConfigOpt: Option[(String, Configuration)],
packageConfigs: Seq[(sbt.Configuration, Configuration)]
) = hackHack ++ Seq(
shadedConfigOpt: Option[(String, Configuration)] = None
): Seq[Setting[_]] = hackHack ++ Seq(
clean := {
val noWarningPlz = clean.value
SbtCoursierCache.default.clear()
@ -228,7 +211,6 @@ object CoursierPlugin extends AutoPlugin {
ignoreArtifactErrors = true
).value,
coursierConfigGraphs := InputsTasks.ivyGraphsTask.value,
coursierPublications := ArtifactsTasks.coursierPublicationsTask(packageConfigs: _*).value,
coursierSbtClassifiersModule := classifiersModule.in(updateSbtClassifiers).value,
coursierConfigurations := InputsTasks.coursierConfigurationsTask(None).value,
coursierParentProjectCache := InputsTasks.parentProjectCacheTask.value,
@ -292,8 +274,7 @@ object CoursierPlugin extends AutoPlugin {
// Tests artifacts from Maven repositories are given this type.
// Adding it here so that these work straightaway.
classpathTypes += "test-jar"
) ++
(needsIvyXml ++ needsIvyXmlLocal).map(makeIvyXmlBefore(_, shadedConfigOpt))
)
override lazy val buildSettings = super.buildSettings ++ Seq(
coursierParallelDownloads := 6,
@ -312,23 +293,8 @@ object CoursierPlugin extends AutoPlugin {
coursierCreateLogger := { () => new TermDisplay(new OutputStreamWriter(System.err)) }
)
override lazy val projectSettings = coursierSettings(None, Seq(Compile, Test).map(c => c -> Configuration(c.name))) ++
override lazy val projectSettings = coursierSettings() ++
inConfig(Compile)(treeSettings) ++
inConfig(Test)(treeSettings)
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 = Keys.getClass
val m = cls.getMethod(method)
val task = m.invoke(Keys).asInstanceOf[TaskKey[PublishConfiguration]]
List(task)
} catch {
case _: Throwable => // FIXME Too wide
Nil
}
}

View File

@ -39,7 +39,6 @@ object Keys {
val coursierFallbackDependencies = TaskKey[Seq[(Module, String, URL, Boolean)]]("coursier-fallback-dependencies")
val coursierConfigGraphs = TaskKey[Seq[Set[Configuration]]]("coursier-config-graphs")
val coursierPublications = TaskKey[Seq[(Configuration, Publication)]]("coursier-publications")
val coursierSbtClassifiersModule = TaskKey[GetClassifiersModule]("coursier-sbt-classifiers-module")

View File

@ -9,7 +9,7 @@ object MyPlugin extends AutoPlugin {
val FooConfig = config("foo")
override def projectSettings = Seq(
override def projectSettings = Seq[Setting[_]](
libraryDependencies ++= Seq(
"org.slf4j" % "slf4j-api" % "1.7.0",
"ch.qos.logback" % "logback-classic" % "1.1.7"

View File

@ -2,7 +2,7 @@ package coursier.sbtlmcoursier
import coursier.lmcoursier.{CoursierConfiguration, CoursierDependencyResolution}
import coursier.sbtcoursiershared.SbtCoursierShared
import sbt.{AutoPlugin, Classpaths, Def, Task, taskKey}
import sbt.{AutoPlugin, Classpaths, Def, Setting, Task, taskKey}
import sbt.KeyRanks.DTask
import sbt.Keys.{dependencyResolution, fullResolvers, otherResolvers, streams}
import sbt.librarymanagement.DependencyResolution
@ -24,7 +24,7 @@ object LmCoursierPlugin extends AutoPlugin {
override def requires = SbtCoursierShared
// putting this in projectSettings like sbt.plugins.IvyPlugin does :|
override def projectSettings = Seq(
override def projectSettings = Seq[Setting[_]](
dependencyResolution := mkDependencyResolution.value,
coursierConfiguration := mkCoursierConfiguration.value
)

View File

@ -5,6 +5,7 @@ import java.io.File
import coursier.core.{Configuration, Type}
import coursier.ivy.IvyXml.{mappings => ivyXmlMappings}
import coursier.sbtcoursier.{CoursierPlugin, InputsTasks, Keys}
import coursier.sbtcoursiershared.{IvyXml, SbtCoursierShared}
import sbt.Keys._
import sbt.{AutoPlugin, Compile, SettingKey, TaskKey, inConfig}
@ -97,9 +98,10 @@ object ShadingPlugin extends AutoPlugin {
sbt.Classpaths.ivyPublishSettings ++
shadingJvmPublishSettings ++
CoursierPlugin.coursierSettings(
Some(baseDependencyConfiguration.value -> Configuration(Shaded.name)),
Seq(Shading -> Configuration.compile)
Some(baseDependencyConfiguration.value -> Configuration(Shaded.name))
) ++
IvyXml.generateIvyXmlSettings(Some(Configuration(Shaded.name))) ++
Seq(SbtCoursierShared.publicationsSetting(Seq(Shading -> Configuration.compile))) ++
CoursierPlugin.treeSettings ++
Seq(
configuration := baseSbtConfiguration, // wuw

View File

@ -29,7 +29,7 @@ runLmCoursierTests() {
runSbtCoursierTests() {
./metadata/scripts/with-test-repo.sh sbt \
++$TRAVIS_SCALA_VERSION \
sbt-coursier/test \
sbt-coursier-shared/test \
"sbt-coursier/scripted shared-$TEST_GROUP/* sbt-coursier-group-$TEST_GROUP/*"
}