mirror of https://github.com/sbt/sbt.git
Tweak SBT boot jar handling
Fixes https://github.com/alexarchambault/coursier/issues/402
This commit is contained in:
parent
ea5169cc0a
commit
b7ce6d042f
|
|
@ -0,0 +1,18 @@
|
||||||
|
package coursier
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object SbtBootJars {
|
||||||
|
def apply(
|
||||||
|
scalaOrg: String,
|
||||||
|
scalaVersion: String,
|
||||||
|
jars: Seq[File]
|
||||||
|
): Map[(Module, String), File] =
|
||||||
|
jars.collect {
|
||||||
|
case jar if jar.getName.endsWith(".jar") =>
|
||||||
|
val name = jar.getName.stripSuffix(".jar")
|
||||||
|
val mod = Module(scalaOrg, name)
|
||||||
|
|
||||||
|
(mod, scalaVersion) -> jar
|
||||||
|
}.toMap
|
||||||
|
}
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
package coursier
|
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
import coursier.core.Publication
|
|
||||||
|
|
||||||
import scalaz.{ EitherT, Monad }
|
|
||||||
import scalaz.Scalaz.ToEitherOps
|
|
||||||
|
|
||||||
object SbtScalaJarsRepository {
|
|
||||||
|
|
||||||
// Will break in 2.11, where scala-parser-combinators_2.11 and scala-xml_2.11, with different
|
|
||||||
// org and versions, are thrown into the mix.
|
|
||||||
// To handle these well, we would need to fetch actual infos about the scala-* dependencies
|
|
||||||
// from actual repos, and use that from SbtScalaJarsRepository.
|
|
||||||
|
|
||||||
val looseDependencies = Map(
|
|
||||||
"scala-compiler" -> Set(
|
|
||||||
"scala-library",
|
|
||||||
"scala-reflect"
|
|
||||||
),
|
|
||||||
"scala-reflect" -> Set(
|
|
||||||
"scala-library"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
case class SbtScalaJarsRepository(
|
|
||||||
scalaOrg: String,
|
|
||||||
scalaVersion: String,
|
|
||||||
jars: Seq[File]
|
|
||||||
) extends Repository { repo =>
|
|
||||||
|
|
||||||
val foundNames = jars.collect {
|
|
||||||
case jar if jar.getName.endsWith(".jar") =>
|
|
||||||
jar.getName.stripSuffix(".jar")
|
|
||||||
}.toSet
|
|
||||||
|
|
||||||
val dependencies = SbtScalaJarsRepository.looseDependencies
|
|
||||||
.filterKeys(foundNames)
|
|
||||||
.mapValues(_.filter(foundNames))
|
|
||||||
|
|
||||||
val artifacts = jars.collect {
|
|
||||||
case jar if jar.getName.endsWith(".jar") =>
|
|
||||||
val name = jar.getName.stripSuffix(".jar")
|
|
||||||
val mod = Module(scalaOrg, name)
|
|
||||||
|
|
||||||
val proj = Project(
|
|
||||||
mod,
|
|
||||||
scalaVersion,
|
|
||||||
dependencies.getOrElse(name, Set.empty[String]).toVector.map { depName =>
|
|
||||||
val dep = Dependency(Module(scalaOrg, depName), scalaVersion)
|
|
||||||
"compile" -> dep
|
|
||||||
},
|
|
||||||
MavenRepository.defaultConfigurations,
|
|
||||||
None,
|
|
||||||
Nil,
|
|
||||||
Nil,
|
|
||||||
Nil,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Seq("compile" -> Publication(name, "jar", "jar", "")),
|
|
||||||
Info("", "", Nil, Nil, None)
|
|
||||||
)
|
|
||||||
|
|
||||||
(mod, scalaVersion) -> ((proj, jar))
|
|
||||||
}.toMap
|
|
||||||
|
|
||||||
val source: Artifact.Source = new Artifact.Source {
|
|
||||||
def artifacts(
|
|
||||||
dependency: Dependency,
|
|
||||||
project: Project,
|
|
||||||
overrideClassifiers: Option[Seq[String]]
|
|
||||||
) =
|
|
||||||
if (overrideClassifiers.isEmpty)
|
|
||||||
repo.artifacts.get(project.moduleVersion) match {
|
|
||||||
case Some((_, f)) =>
|
|
||||||
Seq(
|
|
||||||
Artifact(
|
|
||||||
f.toURI.toString,
|
|
||||||
Map.empty,
|
|
||||||
Map.empty,
|
|
||||||
Attributes("jar", ""),
|
|
||||||
changing = true,
|
|
||||||
None
|
|
||||||
)
|
|
||||||
)
|
|
||||||
case None =>
|
|
||||||
Nil
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Nil
|
|
||||||
}
|
|
||||||
|
|
||||||
def find[F[_]](
|
|
||||||
module: Module,
|
|
||||||
version: String,
|
|
||||||
fetch: Fetch.Content[F]
|
|
||||||
)(implicit
|
|
||||||
F: Monad[F]
|
|
||||||
): EitherT[F, String, (Artifact.Source, Project)] = {
|
|
||||||
|
|
||||||
val res = artifacts.get((module, version)) match {
|
|
||||||
case None =>
|
|
||||||
s"not found in internal SBT scala JARs: $module:$version".left
|
|
||||||
case Some((p, _)) =>
|
|
||||||
(source, p).right
|
|
||||||
}
|
|
||||||
|
|
||||||
EitherT(F.point(res))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -435,17 +435,6 @@ object Tasks {
|
||||||
|
|
||||||
val interProjectRepo = InterProjectRepository(interProjectDependencies)
|
val interProjectRepo = InterProjectRepository(interProjectDependencies)
|
||||||
|
|
||||||
val internalSbtScalaProvider = appConfiguration.value.provider.scalaProvider
|
|
||||||
val internalSbtScalaJarsRepo = SbtScalaJarsRepository(
|
|
||||||
so, // this seems plain wrong - this assumes that the scala org of the project is the same
|
|
||||||
// as the one that started SBT. This will scrap the scala org specific JARs by the ones
|
|
||||||
// that booted SBT, even if the latter come from the standard org.scala-lang org.
|
|
||||||
// But SBT itself does it this way, and not doing so may make two different versions
|
|
||||||
// of the scala JARs land in the classpath...
|
|
||||||
internalSbtScalaProvider.version(),
|
|
||||||
internalSbtScalaProvider.jars()
|
|
||||||
)
|
|
||||||
|
|
||||||
val ivyHome = sys.props.getOrElse(
|
val ivyHome = sys.props.getOrElse(
|
||||||
"ivy.home",
|
"ivy.home",
|
||||||
new File(sys.props("user.home")).toURI.getPath + ".ivy2"
|
new File(sys.props("user.home")).toURI.getPath + ".ivy2"
|
||||||
|
|
@ -521,7 +510,7 @@ object Tasks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val internalRepositories = Seq(globalPluginsRepo, interProjectRepo, internalSbtScalaJarsRepo)
|
val internalRepositories = Seq(globalPluginsRepo, interProjectRepo)
|
||||||
|
|
||||||
val repositories =
|
val repositories =
|
||||||
internalRepositories ++
|
internalRepositories ++
|
||||||
|
|
@ -674,6 +663,18 @@ object Tasks {
|
||||||
// Downloads are already parallel, no need to parallelize further anyway
|
// Downloads are already parallel, no need to parallelize further anyway
|
||||||
synchronized {
|
synchronized {
|
||||||
|
|
||||||
|
val so = scalaOrganization.value
|
||||||
|
val internalSbtScalaProvider = appConfiguration.value.provider.scalaProvider
|
||||||
|
val sbtBootJarOverrides = SbtBootJars(
|
||||||
|
so, // this seems plain wrong - this assumes that the scala org of the project is the same
|
||||||
|
// as the one that started SBT. This will scrap the scala org specific JARs by the ones
|
||||||
|
// that booted SBT, even if the latter come from the standard org.scala-lang org.
|
||||||
|
// But SBT itself does it this way, and not doing so may make two different versions
|
||||||
|
// of the scala JARs land in the classpath...
|
||||||
|
internalSbtScalaProvider.version(),
|
||||||
|
internalSbtScalaProvider.jars()
|
||||||
|
)
|
||||||
|
|
||||||
lazy val cm = coursierSbtClassifiersModule.value
|
lazy val cm = coursierSbtClassifiersModule.value
|
||||||
|
|
||||||
lazy val projectName = thisProjectRef.value.project
|
lazy val projectName = thisProjectRef.value.project
|
||||||
|
|
@ -862,10 +863,22 @@ object Tasks {
|
||||||
artifact
|
artifact
|
||||||
}.toSet
|
}.toSet
|
||||||
|
|
||||||
def artifactFileOpt(artifact: Artifact) = {
|
def artifactFileOpt(module: Module, version: String, artifact: Artifact) = {
|
||||||
|
|
||||||
val artifact0 = artifact
|
val artifact0 = artifact
|
||||||
.copy(attributes = Attributes()) // temporary hack :-(
|
.copy(attributes = Attributes()) // temporary hack :-(
|
||||||
val res = artifactFiles.get(artifact0)
|
|
||||||
|
// Under some conditions, SBT puts the scala JARs of its own classpath
|
||||||
|
// in the application classpath. Ensuring we return SBT's jars rather than
|
||||||
|
// JARs from the coursier cache, so that a same JAR doesn't land twice in the
|
||||||
|
// application classpath (once via SBT jars, once via coursier cache).
|
||||||
|
val fromBootJars =
|
||||||
|
if (artifact.classifier.isEmpty && artifact.`type` == "jar")
|
||||||
|
sbtBootJarOverrides.get((module, version))
|
||||||
|
else
|
||||||
|
None
|
||||||
|
|
||||||
|
val res = fromBootJars.orElse(artifactFiles.get(artifact0))
|
||||||
|
|
||||||
if (res.isEmpty && !erroredArtifacts(artifact0))
|
if (res.isEmpty && !erroredArtifacts(artifact0))
|
||||||
log.error(s"${artifact.url} not downloaded (should not happen)")
|
log.error(s"${artifact.url} not downloaded (should not happen)")
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ object ToSbt {
|
||||||
def moduleReports(
|
def moduleReports(
|
||||||
res: Resolution,
|
res: Resolution,
|
||||||
classifiersOpt: Option[Seq[String]],
|
classifiersOpt: Option[Seq[String]],
|
||||||
artifactFileOpt: Artifact => Option[File]
|
artifactFileOpt: (Module, String, Artifact) => Option[File]
|
||||||
) = {
|
) = {
|
||||||
val depArtifacts =
|
val depArtifacts =
|
||||||
classifiersOpt match {
|
classifiersOpt match {
|
||||||
|
|
@ -145,7 +145,7 @@ object ToSbt {
|
||||||
dep,
|
dep,
|
||||||
dependees,
|
dependees,
|
||||||
proj,
|
proj,
|
||||||
artifacts.map(a => a -> artifactFileOpt(a))
|
artifacts.map(a => a -> artifactFileOpt(proj.module, proj.version, a))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -155,7 +155,7 @@ object ToSbt {
|
||||||
resolution: Resolution,
|
resolution: Resolution,
|
||||||
configs: Map[String, Set[String]],
|
configs: Map[String, Set[String]],
|
||||||
classifiersOpt: Option[Seq[String]],
|
classifiersOpt: Option[Seq[String]],
|
||||||
artifactFileOpt: Artifact => Option[File]
|
artifactFileOpt: (Module, String, Artifact) => Option[File]
|
||||||
): sbt.UpdateReport = {
|
): sbt.UpdateReport = {
|
||||||
|
|
||||||
val configReports = configs.map {
|
val configReports = configs.map {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
scalaVersion := appConfiguration.value.provider.scalaProvider.version
|
||||||
|
|
||||||
|
coursierCachePolicies := {
|
||||||
|
if (sys.props("os.name").startsWith("Windows"))
|
||||||
|
coursierCachePolicies.value
|
||||||
|
else
|
||||||
|
Seq(coursier.CachePolicy.ForceDownload)
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy val updateClassifiersCheck = TaskKey[Unit]("updateClassifiersCheck")
|
||||||
|
|
||||||
|
updateClassifiersCheck := {
|
||||||
|
|
||||||
|
val configReport = updateClassifiers.value
|
||||||
|
.configuration("compile")
|
||||||
|
.getOrElse {
|
||||||
|
throw new Exception(
|
||||||
|
"compile configuration not found in updateClassifiers report"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val scalaLibraryArtifacts = configReport
|
||||||
|
.modules
|
||||||
|
.collectFirst {
|
||||||
|
case moduleReport
|
||||||
|
if moduleReport.module.organization == "org.scala-lang" &&
|
||||||
|
moduleReport.module.name == "scala-library" =>
|
||||||
|
moduleReport.artifacts
|
||||||
|
}
|
||||||
|
.toSeq
|
||||||
|
.flatten
|
||||||
|
|
||||||
|
def classifierArtifact(classifier: String) =
|
||||||
|
scalaLibraryArtifacts.collectFirst {
|
||||||
|
case (a, _) if a.classifier == Some(classifier) => a
|
||||||
|
}
|
||||||
|
|
||||||
|
def ensureHasClassifierArtifact(classifier: String) =
|
||||||
|
assert(
|
||||||
|
classifierArtifact(classifier).nonEmpty,
|
||||||
|
s"scala-library $classifier not found"
|
||||||
|
)
|
||||||
|
|
||||||
|
ensureHasClassifierArtifact("javadoc")
|
||||||
|
ensureHasClassifierArtifact("sources")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
val pluginVersion = sys.props.getOrElse(
|
||||||
|
"plugin.version",
|
||||||
|
throw new RuntimeException(
|
||||||
|
"""|The system property 'plugin.version' is not defined.
|
||||||
|
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
addSbtPlugin("io.get-coursier" % "sbt-coursier" % pluginVersion)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Files
|
||||||
|
|
||||||
|
object Main extends App {
|
||||||
|
Files.write(new File("output").toPath, "OK".getBytes("UTF-8"))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
$ delete output
|
||||||
|
> run
|
||||||
|
$ exists output
|
||||||
|
> updateClassifiersCheck
|
||||||
Loading…
Reference in New Issue