Add sbt-launcher

This commit is contained in:
Alexandre Archambault 2017-02-21 15:57:41 +01:00
parent e5a6a609e3
commit 88f54cc356
13 changed files with 1108 additions and 0 deletions

View File

@ -382,6 +382,19 @@ lazy val `sbt-shading` = project
libraryDependencies += "org.anarres.jarjar" % "jarjar-core" % "1.0.0"
)
lazy val `sbt-launcher` = project
.dependsOn(cache)
.settings(commonSettings)
.settings(packAutoSettings)
.settings(
libraryDependencies ++= Seq(
"com.github.alexarchambault" %% "case-app" % "1.1.3",
"org.scala-sbt" % "launcher-interface" % "1.0.0",
"com.typesafe" % "config" % "1.3.1"
),
packExcludeArtifactTypes += "pom"
)
val http4sVersion = "0.8.6"
lazy val `http-server` = project
@ -426,6 +439,7 @@ lazy val `coursier` = project.in(file("."))
cli,
`sbt-coursier`,
`sbt-shading`,
`sbt-launcher`,
web,
doc,
`http-server`,

View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e
COURSIER_VERSION=1.0.0-SNAPSHOT
"$(dirname "$0")/../coursier" bootstrap \
"io.get-coursier:sbt-launcher_2.12:$COURSIER_VERSION" \
-i launcher \
-I launcher:org.scala-sbt:launcher-interface:1.0.0 \
-o csbt \
-J -Djline.shutdownhook=false

View File

@ -0,0 +1,9 @@
package coursier.sbtlauncher
import java.io.File
final case class AppConfiguration(
arguments: Array[String],
baseDirectory: File,
provider: xsbti.AppProvider
) extends xsbti.AppConfiguration

View File

@ -0,0 +1,18 @@
package coursier.sbtlauncher
import java.io.File
final case class AppProvider(
scalaProvider: xsbti.ScalaProvider,
id: xsbti.ApplicationID,
loader: ClassLoader,
mainClass: Class[_ <: xsbti.AppMain],
createMain: () => xsbti.AppMain,
mainClasspath: Array[File],
components: xsbti.ComponentProvider
) extends xsbti.AppProvider {
def entryPoint: Class[_] =
mainClass
def newMain(): xsbti.AppMain =
createMain()
}

View File

@ -0,0 +1,54 @@
package coursier.sbtlauncher
import java.io.File
final case class ApplicationID(
groupID: String,
name: String,
version: String,
mainClass: String,
mainComponents: Array[String],
crossVersioned: Boolean,
crossVersionedValue: xsbti.CrossValue,
classpathExtra: Array[File]
) extends xsbti.ApplicationID {
assert(crossVersioned == (crossVersionedValue != xsbti.CrossValue.Disabled))
def disableCrossVersion(scalaVersion: String): ApplicationID =
crossVersionedValue match {
case xsbti.CrossValue.Disabled =>
this
case xsbti.CrossValue.Binary =>
val scalaBinaryVersion = scalaVersion.split('.').take(2).mkString(".")
copy(
crossVersioned = false,
crossVersionedValue = xsbti.CrossValue.Disabled,
version = s"${version}_$scalaBinaryVersion"
)
case xsbti.CrossValue.Full =>
copy(
crossVersioned = false,
crossVersionedValue = xsbti.CrossValue.Disabled,
version = s"${version}_$scalaVersion"
)
}
}
object ApplicationID {
def apply(id: xsbti.ApplicationID): ApplicationID =
id match {
case id0: ApplicationID => id0
case _ =>
ApplicationID(
id.groupID(),
id.name(),
id.version(),
id.mainClass(),
id.mainComponents(),
id.crossVersionedValue() != xsbti.CrossValue.Disabled,
id.crossVersionedValue(),
id.classpathExtra()
)
}
}

View File

@ -0,0 +1,70 @@
package coursier.sbtlauncher
import java.io.File
import java.nio.file.{Files, StandardCopyOption}
import scala.collection.mutable
final class ComponentProvider(cacheDir: File) extends xsbti.ComponentProvider {
private val components0 = new mutable.HashMap[String, Array[File]]
def componentLocation(id: String): File =
new File(cacheDir, id)
def component(componentId: String): Array[File] = {
val res = components0.getOrElse[Array[File]](
componentId,
{
val dir = componentLocation(componentId)
if (dir.exists())
Option(dir.listFiles()).getOrElse(Array())
else
Array()
}
)
res
}
private def clear(componentId: String): Unit = {
def deleteRecursively(f: File): Unit =
if (f.isFile)
f.delete()
else
Option(f.listFiles())
.getOrElse(Array())
.foreach(deleteRecursively)
val dir = componentLocation(componentId)
deleteRecursively(dir)
}
private def copying(componentId: String, f: File): File = {
// TODO Use some locking mechanisms here
val dir = componentLocation(componentId)
dir.mkdirs()
val dest = new File(dir, f.getName)
Files.copy(f.toPath, dest.toPath, StandardCopyOption.REPLACE_EXISTING)
dest
}
def defineComponentNoCopy(componentId: String, components: Array[File]): Unit = {
components0 += componentId -> components.distinct
}
def defineComponent(componentId: String, components: Array[File]): Unit = {
clear(componentId)
components0 += componentId -> components.distinct.map(copying(componentId, _))
}
def addToComponent(componentId: String, components: Array[File]): Boolean = {
val previousFiles = components0.getOrElse(componentId, Array.empty[File])
val newFiles = (previousFiles ++ components.distinct.map(copying(componentId, _))).distinct
components0 += componentId -> newFiles
newFiles.length != previousFiles.length
}
def lockFile: File = new File("/component-lock")
}

View File

@ -0,0 +1,21 @@
package coursier.sbtlauncher
import java.io.File
import java.util.concurrent.{Callable, ConcurrentHashMap}
case object DummyGlobalLock extends xsbti.GlobalLock {
private val locks = new ConcurrentHashMap[File, AnyRef]
def apply[T](lockFile: File, run: Callable[T]): T =
Option(lockFile) match {
case None => run.call()
case Some(lockFile0) =>
val lock0 = new AnyRef
val lock = Option(locks.putIfAbsent(lockFile0, lock0)).getOrElse(lock0)
lock.synchronized {
run.call()
}
}
}

View File

@ -0,0 +1,638 @@
package coursier.sbtlauncher
import java.io.{File, OutputStreamWriter}
import java.net.{URL, URLClassLoader}
import java.util.concurrent.ConcurrentHashMap
import coursier.Cache.Logger
import coursier._
import coursier.ivy.IvyRepository
import coursier.maven.MavenSource
import scala.annotation.tailrec
import scalaz.{-\/, \/-}
import scalaz.concurrent.Task
class Launcher(
scalaVersion: String,
componentsCache: File,
val ivyHome: File
) extends xsbti.Launcher {
val componentProvider = new ComponentProvider(componentsCache)
lazy val baseLoader = {
@tailrec
def rootLoader(cl: ClassLoader): ClassLoader =
if (cl == null)
sys.error("Cannot find base loader")
else {
val isLauncherLoader =
try {
cl
.asInstanceOf[AnyRef { def getIsolationTargets: Array[String] }]
.getIsolationTargets
.contains("launcher")
} catch {
case _: Throwable => false
}
if (isLauncherLoader)
cl
else
rootLoader(cl.getParent)
}
rootLoader(Thread.currentThread().getContextClassLoader)
}
val repositoryIdPrefix = "coursier-launcher-"
val repositories = Seq(
// mmh, ID "local" seems to be required for publishLocal to be fine if we're launching sbt
"local" -> Cache.ivy2Local,
s"${repositoryIdPrefix}central" -> MavenRepository("https://repo1.maven.org/maven2", sbtAttrStub = true),
s"${repositoryIdPrefix}typesafe-ivy-releases" -> IvyRepository.parse(
"https://repo.typesafe.com/typesafe/ivy-releases/[organization]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext]"
).leftMap(sys.error).merge,
s"${repositoryIdPrefix}sbt-plugin-releases" -> IvyRepository.parse(
"https://repo.scala-sbt.org/scalasbt/sbt-plugin-releases/[organization]/[module](/scala_[scalaVersion])(/sbt_[sbtVersion])/[revision]/[type]s/[artifact](-[classifier]).[ext]"
).leftMap(sys.error).merge
)
assert(!repositories.groupBy(_._1).exists(_._2.lengthCompare(1) > 0))
val cachePolicies = CachePolicy.default
def fetch(logger: Option[Logger]) = {
def helper(policy: CachePolicy) =
Cache.fetch(cachePolicy = policy, logger = logger)
val f = cachePolicies.map(helper)
Fetch.from(repositories.map(_._2), f.head, f.tail: _*)
}
val keepArtifactTypes = Set("jar", "bundle")
def tasks(res: Resolution, logger: Option[Logger], classifiersOpt: Option[Seq[String]] = None) = {
val a = classifiersOpt
.fold(res.dependencyArtifacts.map(_._2))(res.dependencyClassifiersArtifacts(_).map(_._2))
val keepArtifactTypes = classifiersOpt.fold(Set("jar", "bundle"))(c => c.map(c => MavenSource.classifierExtensionDefaultTypes.getOrElse((c, "jar"), ???)).toSet)
a.collect {
case artifact if keepArtifactTypes(artifact.`type`) =>
def file(policy: CachePolicy) = Cache.file(
artifact,
cachePolicy = policy,
logger = logger
)
(file(cachePolicies.head) /: cachePolicies.tail)(_ orElse file(_))
.run
.map(artifact.->)
}
}
def isOverrideRepositories = false // ???
def bootDirectory: File = ???
def getScala(version: String): xsbti.ScalaProvider =
getScala(version, "")
def getScala(version: String, reason: String): xsbti.ScalaProvider =
getScala(version, reason, "org.scala-lang")
def getScala(version: String, reason: String, scalaOrg: String): xsbti.ScalaProvider = {
val key = (version, scalaOrg)
Option(scalaProviderCache.get(key)).getOrElse {
val prov = getScala0(version, reason, scalaOrg)
val previous = Option(scalaProviderCache.putIfAbsent(key, prov))
previous.getOrElse(prov)
}
}
private val scalaProviderCache = new ConcurrentHashMap[(String, String), xsbti.ScalaProvider]
private def getScala0(version: String, reason: String, scalaOrg: String): xsbti.ScalaProvider = {
val files = getScalaFiles(version, reason, scalaOrg)
val libraryJar = files.find(_.getName.startsWith("scala-library")).getOrElse {
throw new NoSuchElementException("scala-library JAR")
}
val compilerJar = files.find(_.getName.startsWith("scala-compiler")).getOrElse {
throw new NoSuchElementException("scala-compiler JAR")
}
val scalaLoader = new URLClassLoader(files.map(_.toURI.toURL).toArray, baseLoader)
ScalaProvider(
this,
version,
scalaLoader,
files.toArray,
libraryJar,
compilerJar,
id => app(id, id.version())
)
}
private def getScalaFiles(version: String, reason: String, scalaOrg: String): Seq[File] = {
val initialRes = Resolution(
Set(
Dependency(Module(scalaOrg, "scala-library"), version),
Dependency(Module(scalaOrg, "scala-compiler"), version)
),
forceVersions = Map(
Module(scalaOrg, "scala-library") -> version,
Module(scalaOrg, "scala-compiler") -> version,
Module(scalaOrg, "scala-reflect") -> version
)
)
val logger =
Some(new TermDisplay(
new OutputStreamWriter(System.err)
))
logger.foreach(_.init {
System.err.println(s"Resolving Scala $version (organization $scalaOrg)")
})
val res = initialRes.process.run(fetch(logger)).unsafePerformSync
logger.foreach { l =>
if (l.stopDidPrintSomething())
System.err.println(s"Resolved Scala $version (organization $scalaOrg)")
}
if (res.errors.nonEmpty) {
Console.err.println(s"Errors:\n${res.errors.map(" " + _).mkString("\n")}")
sys.exit(1)
}
if (res.conflicts.nonEmpty) {
Console.err.println(s"Conflicts:\n${res.conflicts.map(" " + _).mkString("\n")}")
sys.exit(1)
}
if (!res.isDone) {
Console.err.println("Did not converge")
sys.exit(1)
}
val artifactLogger =
Some(new TermDisplay(
new OutputStreamWriter(System.err)
))
artifactLogger.foreach(_.init {
System.err.println(s"Fetching Scala $version artifacts (organization $scalaOrg)")
})
val results = Task.gatherUnordered(tasks(res, artifactLogger)).unsafePerformSync
artifactLogger.foreach { l =>
if (l.stopDidPrintSomething())
System.err.println(s"Fetched Scala $version artifacts (organization $scalaOrg)")
}
val errors = results.collect { case (a, -\/(err)) => (a, err) }
val files = results.collect { case (_, \/-(f)) => f }
if (errors.nonEmpty) {
Console.err.println(s"Error downloading artifacts:\n${errors.map(" " + _).mkString("\n")}")
sys.exit(1)
}
files
}
def topLoader: ClassLoader = baseLoader
def appRepositories: Array[xsbti.Repository] =
repositories.map {
case (id, m: MavenRepository) =>
Repository.Maven(id, new URL(m.root))
case (id, i: IvyRepository) =>
assert(i.metadataPatternOpt.forall(_ == i.pattern))
val (base, pat) = i.pattern.string.span(c => c != '[' && c != '$' && c != '(')
assert(base.nonEmpty, i.pattern.string)
Repository.Ivy(
id,
new URL(base),
pat,
pat,
mavenCompatible = false,
skipConsistencyCheck = true, // ???
descriptorOptional = true // ???
)
}.toArray
def ivyRepositories: Array[xsbti.Repository] =
appRepositories // ???
def globalLock = DummyGlobalLock
// See https://github.com/sbt/ivy/blob/2cf13e211b2cb31f0d3b317289dca70eca3362f6/src/java/org/apache/ivy/util/ChecksumHelper.java
def checksums: Array[String] = Array("sha1", "md5")
def app(id: xsbti.ApplicationID, version: String): xsbti.AppProvider =
app(ApplicationID(id).copy(version = version))
def app(id: xsbti.ApplicationID, extra: Dependency*): xsbti.AppProvider = {
val (scalaFiles, files) = appFiles(id, extra: _*)
val scalaLoader = new URLClassLoader(scalaFiles.map(_.toURI.toURL).toArray, baseLoader)
val libraryJar = scalaFiles.find(_.getName.startsWith("scala-library")).getOrElse {
throw new NoSuchElementException("scala-library JAR")
}
val compilerJar = scalaFiles.find(_.getName.startsWith("scala-compiler")).getOrElse {
throw new NoSuchElementException("scala-compiler JAR")
}
val scalaProvider = ScalaProvider(
this,
scalaVersion,
scalaLoader,
scalaFiles.toArray,
libraryJar,
compilerJar,
id => app(id, id.version())
)
val loader = new URLClassLoader(files.filterNot(scalaFiles.toSet).map(_.toURI.toURL).toArray, scalaLoader)
val mainClass0 = loader.loadClass(id.mainClass).asSubclass(classOf[xsbti.AppMain])
AppProvider(
scalaProvider,
id,
loader,
mainClass0,
() => mainClass0.newInstance(),
files.toArray,
componentProvider
)
}
private def appFiles(id: xsbti.ApplicationID, extra: Dependency*): (Seq[File], Seq[File]) = {
val id0 = ApplicationID(id).disableCrossVersion(scalaVersion)
val initialRes = Resolution(
Set(
Dependency(Module("org.scala-lang", "scala-library"), scalaVersion),
Dependency(Module("org.scala-lang", "scala-compiler"), scalaVersion),
Dependency(Module(id0.groupID, id0.name), id0.version)
) ++ extra,
forceVersions = Map(
Module("org.scala-lang", "scala-library") -> scalaVersion,
Module("org.scala-lang", "scala-compiler") -> scalaVersion,
Module("org.scala-lang", "scala-reflect") -> scalaVersion
)
)
val logger =
Some(new TermDisplay(
new OutputStreamWriter(System.err)
))
val extraMsg =
if (extra.isEmpty)
""
else
s" (plus ${extra.length} dependencies)"
logger.foreach(_.init {
System.err.println(s"Resolving ${id0.groupID}:${id0.name}:${id0.version}$extraMsg")
})
val res = initialRes.process.run(fetch(logger)).unsafePerformSync
logger.foreach { l =>
if (l.stopDidPrintSomething())
System.err.println(s"Resolved ${id0.groupID}:${id0.name}:${id0.version}$extraMsg")
}
if (res.errors.nonEmpty) {
Console.err.println(s"Errors:\n${res.errors.map(" " + _).mkString("\n")}")
sys.exit(1)
}
if (res.conflicts.nonEmpty) {
Console.err.println(s"Conflicts:\n${res.conflicts.map(" " + _).mkString("\n")}")
sys.exit(1)
}
if (!res.isDone) {
Console.err.println("Did not converge")
sys.exit(1)
}
val artifactLogger =
Some(new TermDisplay(
new OutputStreamWriter(System.err)
))
artifactLogger.foreach(_.init {
System.err.println(s"Fetching ${id0.groupID}:${id0.name}:${id0.version} artifacts")
})
val results = Task.gatherUnordered(tasks(res, artifactLogger)).unsafePerformSync
artifactLogger.foreach { l =>
if (l.stopDidPrintSomething())
System.err.println(s"Fetched ${id0.groupID}:${id0.name}:${id0.version} artifacts")
}
val errors = results.collect { case (a, -\/(err)) => (a, err) }
val files = results.collect { case (_, \/-(f)) => f }
if (errors.nonEmpty) {
Console.err.println(s"Error downloading artifacts:\n${errors.map(" " + _).mkString("\n")}")
sys.exit(1)
}
val scalaSubRes = res.subset(
Set(
Dependency(Module("org.scala-lang", "scala-library"), scalaVersion),
Dependency(Module("org.scala-lang", "scala-compiler"), scalaVersion)
)
)
val scalaArtifactLogger =
Some(new TermDisplay(
new OutputStreamWriter(System.err)
))
scalaArtifactLogger.foreach(_.init {
System.err.println(s"Fetching ${id0.groupID}:${id0.name}:${id0.version} Scala artifacts")
})
val scalaResults = Task.gatherUnordered(tasks(scalaSubRes, scalaArtifactLogger)).unsafePerformSync
scalaArtifactLogger.foreach { l =>
if (l.stopDidPrintSomething())
System.err.println(s"Fetched ${id0.groupID}:${id0.name}:${id0.version} Scala artifacts")
}
val scalaErrors = scalaResults.collect { case (a, -\/(err)) => (a, err) }
val scalaFiles = scalaResults.collect { case (_, \/-(f)) => f }
if (scalaErrors.nonEmpty) {
Console.err.println(s"Error downloading artifacts:\n${scalaErrors.map(" " + _).mkString("\n")}")
sys.exit(1)
}
(scalaFiles, files)
}
def registerScalaComponents(scalaVersion: String = scalaVersion): Unit = {
lazy val prov = getScala(scalaVersion)
lazy val jars = prov.jars()
lazy val libraryJar = jars.find(_.getName.startsWith("scala-library")).getOrElse {
throw new NoSuchElementException("scala-library JAR")
}
lazy val compilerJar = jars.find(_.getName.startsWith("scala-compiler")).getOrElse {
throw new NoSuchElementException("scala-compiler JAR")
}
lazy val reflectJar = jars.find(_.getName.startsWith("scala-reflect")).getOrElse {
throw new NoSuchElementException("scala-reflect JAR")
}
if (componentProvider.component("library").isEmpty)
componentProvider.defineComponentNoCopy("library", Array(libraryJar))
if (componentProvider.component("compiler").isEmpty)
componentProvider.defineComponentNoCopy("compiler", Array(compilerJar))
if (componentProvider.component("reflect").isEmpty)
componentProvider.defineComponentNoCopy("reflect", Array(reflectJar))
}
def registerSbtInterfaceComponents(sbtVersion: String): Unit = {
lazy val (interfaceJar, _) = sbtInterfaceComponentFiles(sbtVersion)
lazy val compilerInterfaceSourceJar = sbtCompilerInterfaceSrcComponentFile(sbtVersion)
if (componentProvider.component("xsbti").isEmpty)
componentProvider.defineComponentNoCopy("xsbti", Array(interfaceJar))
if (componentProvider.component("compiler-interface").isEmpty)
componentProvider.defineComponentNoCopy("compiler-interface", Array(compilerInterfaceSourceJar))
if (componentProvider.component("compiler-interface-src").isEmpty)
componentProvider.defineComponentNoCopy("compiler-interface-src", Array(compilerInterfaceSourceJar))
}
private def sbtInterfaceComponentFiles(sbtVersion: String): (File, File) = {
lazy val res = {
val initialRes = Resolution(
Set(
Dependency(Module("org.scala-sbt", "interface"), sbtVersion, transitive = false)
)
)
val logger =
Some(new TermDisplay(
new OutputStreamWriter(System.err)
))
logger.foreach(_.init {
System.err.println(s"Resolving org.scala-sbt:interface:$sbtVersion")
})
val res = initialRes.process.run(fetch(logger)).unsafePerformSync
logger.foreach { l =>
if (l.stopDidPrintSomething())
System.err.println(s"Resolved org.scala-sbt:interface:$sbtVersion")
}
if (res.errors.nonEmpty) {
Console.err.println(s"Errors:\n${res.errors.map(" " + _).mkString("\n")}")
sys.exit(1)
}
if (res.conflicts.nonEmpty) {
Console.err.println(s"Conflicts:\n${res.conflicts.map(" " + _).mkString("\n")}")
sys.exit(1)
}
if (!res.isDone) {
Console.err.println("Did not converge")
sys.exit(1)
}
res
}
lazy val interfaceJar = {
val artifactLogger =
Some(new TermDisplay(
new OutputStreamWriter(System.err)
))
artifactLogger.foreach(_.init {
System.err.println(s"Fetching org.scala-sbt:interface:$sbtVersion artifacts")
})
val results = Task.gatherUnordered(tasks(res, artifactLogger)).unsafePerformSync
artifactLogger.foreach { l =>
if (l.stopDidPrintSomething())
System.err.println(s"Fetched org.scala-sbt:interface:$sbtVersion artifacts")
}
val errors = results.collect { case (a, -\/(err)) => (a, err) }
val files = results.collect { case (_, \/-(f)) => f }
if (errors.nonEmpty) {
Console.err.println(s"Error downloading artifacts:\n${errors.map(" " + _).mkString("\n")}")
sys.exit(1)
}
files match {
case Nil =>
throw new NoSuchElementException(s"interface JAR for sbt $sbtVersion")
case List(jar) =>
jar
case _ =>
sys.error(s"Too many interface JAR for sbt $sbtVersion: ${files.mkString(", ")}")
}
}
lazy val compilerInterfaceSourcesJar = {
val artifactLogger =
Some(new TermDisplay(
new OutputStreamWriter(System.err)
))
artifactLogger.foreach(_.init {
System.err.println(s"Fetching org.scala-sbt:interface:$sbtVersion source artifacts")
})
val results = Task.gatherUnordered(tasks(res, artifactLogger, Some(Seq("sources")))).unsafePerformSync
artifactLogger.foreach { l =>
if (l.stopDidPrintSomething())
System.err.println(s"Fetched org.scala-sbt:interface:$sbtVersion source artifacts")
}
val errors = results.collect { case (a, -\/(err)) => (a, err) }
val files = results.collect { case (_, \/-(f)) => f }
if (errors.nonEmpty) {
Console.err.println(s"Error downloading artifacts:\n${errors.map(" " + _).mkString("\n")}")
sys.exit(1)
}
files match {
case Nil =>
throw new NoSuchElementException(s"compiler-interface source JAR for sbt $sbtVersion")
case List(jar) =>
jar
case _ =>
sys.error(s"Too many compiler-interface source JAR for sbt $sbtVersion: ${files.mkString(", ")}")
}
}
(interfaceJar, compilerInterfaceSourcesJar)
}
private def sbtCompilerInterfaceSrcComponentFile(sbtVersion: String): File = {
val res = {
val initialRes = Resolution(
Set(
Dependency(Module("org.scala-sbt", "compiler-interface"), sbtVersion, transitive = false)
)
)
val logger =
Some(new TermDisplay(
new OutputStreamWriter(System.err)
))
logger.foreach(_.init {
System.err.println(s"Resolving org.scala-sbt:compiler-interface:$sbtVersion")
})
val res = initialRes.process.run(fetch(logger)).unsafePerformSync
logger.foreach { l =>
if (l.stopDidPrintSomething())
System.err.println(s"Resolved org.scala-sbt:compiler-interface:$sbtVersion")
}
if (res.errors.nonEmpty) {
Console.err.println(s"Errors:\n${res.errors.map(" " + _).mkString("\n")}")
sys.exit(1)
}
if (res.conflicts.nonEmpty) {
Console.err.println(s"Conflicts:\n${res.conflicts.map(" " + _).mkString("\n")}")
sys.exit(1)
}
if (!res.isDone) {
Console.err.println("Did not converge")
sys.exit(1)
}
res
}
val files = {
val artifactLogger =
Some(new TermDisplay(
new OutputStreamWriter(System.err)
))
artifactLogger.foreach(_.init {
System.err.println(s"Fetching org.scala-sbt:compiler-interface:$sbtVersion source artifacts")
})
val results = Task.gatherUnordered(
tasks(res, artifactLogger, None) ++
tasks(res, artifactLogger, Some(Seq("sources")))
).unsafePerformSync
artifactLogger.foreach { l =>
if (l.stopDidPrintSomething())
System.err.println(s"Fetched org.scala-sbt:compiler-interface:$sbtVersion source artifacts")
}
val errors = results.collect { case (a, -\/(err)) => (a, err) }
if (errors.nonEmpty) {
Console.err.println(s"Error downloading artifacts:\n${errors.map(" " + _).mkString("\n")}")
sys.exit(1)
}
results.collect { case (_, \/-(f)) => f }
}
files.find(f => f.getName == "compiler-interface-src.jar" || f.getName == "compiler-interface-sources.jar").getOrElse {
sys.error("compiler-interface-src not found")
}
}
}

View File

@ -0,0 +1,110 @@
package coursier.sbtlauncher
import java.io.File
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import caseapp._
import com.typesafe.config.ConfigFactory
import coursier.Dependency
final case class MainApp(
@ExtraName("org")
organization: String,
name: String,
version: String,
scalaVersion: String,
sbtVersion: String,
mainClass: String,
mainComponents: List[String],
classpathExtra: List[String],
extra: List[String]
) extends App {
val sbtPropFile = new File(sys.props("user.dir") + "/sbt.properties")
val buildPropFile = new File(sys.props("user.dir") + "/project/build.properties")
val propFileOpt = Some(sbtPropFile).filter(_.exists())
.orElse(Some(buildPropFile).filter(_.exists()))
val (org0, name0, ver0, scalaVer0, extraDeps0, mainClass0, sbtVersion0) =
propFileOpt match {
case Some(propFile) =>
// can't get ConfigFactory.parseFile to work fine here
val conf = ConfigFactory.parseString(new String(Files.readAllBytes(propFile.toPath), StandardCharsets.UTF_8))
.withFallback(ConfigFactory.defaultReference(Thread.currentThread().getContextClassLoader))
.resolve()
val sbtConfig = SbtConfig.fromConfig(conf)
(sbtConfig.organization, sbtConfig.moduleName, sbtConfig.version, sbtConfig.scalaVersion, sbtConfig.dependencies, sbtConfig.mainClass, sbtConfig.version)
case None =>
require(scalaVersion.nonEmpty, "No scala version specified")
(organization, name, version, scalaVersion, Nil, mainClass, sbtVersion)
}
val (extraParseErrors, extraModuleVersions) = coursier.util.Parse.moduleVersions(extra, scalaVersion)
if (extraParseErrors.nonEmpty) {
???
}
val extraDeps = extraModuleVersions.map {
case (mod, ver) =>
Dependency(mod, ver)
}
val launcher = new Launcher(
scalaVer0,
// FIXME Add org & moduleName in this path
new File(s"${sys.props("user.dir")}/target/sbt-components/components_scala$scalaVer0${if (sbtVersion0.isEmpty) "" else "_sbt" + sbtVersion0}"),
new File(s"${sys.props("user.dir")}/target/ivy2")
)
launcher.registerScalaComponents()
if (sbtVersion0.nonEmpty)
launcher.registerSbtInterfaceComponents(sbtVersion0)
val appId = ApplicationID(
org0,
name0,
ver0,
mainClass0,
mainComponents.toArray,
crossVersioned = false,
xsbti.CrossValue.Disabled,
classpathExtra.map(new File(_)).toArray
)
val appProvider = launcher.app(appId, extraDeps0 ++ extraDeps: _*)
val appMain = appProvider.newMain()
val appConfig = AppConfiguration(
remainingArgs.toArray,
new File(sys.props("user.dir")),
appProvider
)
val thread = Thread.currentThread()
val previousLoader = thread.getContextClassLoader
val result =
try {
thread.setContextClassLoader(appProvider.loader())
appMain.run(appConfig)
} finally {
thread.setContextClassLoader(previousLoader)
}
result match {
case _: xsbti.Continue =>
case e: xsbti.Exit =>
sys.exit(e.code())
case _: xsbti.Reboot =>
sys.error("Not able to reboot yet")
}
}
object Main extends AppOf[MainApp]

View File

@ -0,0 +1,19 @@
package coursier.sbtlauncher
import java.net.URL
object Repository {
final case class Maven(id: String, url: URL) extends xsbti.MavenRepository
final case class Ivy(
id: String,
url: URL,
ivyPattern: String,
artifactPattern: String,
mavenCompatible: Boolean,
skipConsistencyCheck: Boolean,
descriptorOptional: Boolean
) extends xsbti.IvyRepository
}

View File

@ -0,0 +1,112 @@
package coursier.sbtlauncher
import com.typesafe.config.Config
import coursier.Dependency
import coursier.util.Parse
import scala.collection.JavaConverters._
final case class SbtConfig(
organization: String,
moduleName: String,
version: String,
scalaVersion: String,
mainClass: String,
dependencies: Seq[Dependency]
)
object SbtConfig {
def defaultOrganization = "org.scala-sbt"
def defaultModuleName = "sbt"
def defaultMainClass = "sbt.xMain"
def fromConfig(config: Config): SbtConfig = {
val version = config.getString("sbt.version")
val scalaVersion =
if (config.hasPath("scala.version"))
config.getString("scala.version")
else if (version.startsWith("0.13."))
"2.10.6"
else if (version.startsWith("1.0."))
"2.12.1"
else
throw new Exception(s"Don't know what Scala version should be used for sbt version '$version'")
val org =
if (config.hasPath("sbt.organization"))
config.getString("sbt.organization")
else
defaultOrganization
val name =
if (config.hasPath("sbt.module-name"))
config.getString("sbt.module-name")
else
defaultModuleName
val mainClass =
if (config.hasPath("sbt.main-class"))
config.getString("sbt.main-class")
else
defaultMainClass
val scalaBinaryVersion = scalaVersion.split('.').take(2).mkString(".")
val sbtBinaryVersion = version.split('.').take(2).mkString(".")
val rawPlugins =
if (config.hasPath("plugins"))
config.getStringList("plugins").asScala
else
Nil
val (pluginErrors, pluginsModuleVersions) = Parse.moduleVersions(rawPlugins, scalaVersion)
if (pluginErrors.nonEmpty) {
???
}
val pluginDependencies =
pluginsModuleVersions.map {
case (mod, ver) =>
Dependency(
mod.copy(
attributes = mod.attributes ++ Seq(
"scalaVersion" -> scalaBinaryVersion,
"sbtVersion" -> sbtBinaryVersion
)
),
ver
)
}
val rawDeps =
if (config.hasPath("dependencies"))
config.getStringList("dependencies").asScala
else
Nil
val (depsErrors, depsModuleVersions) = Parse.moduleVersions(rawDeps, scalaVersion)
if (depsErrors.nonEmpty) {
???
}
val dependencies =
depsModuleVersions.map {
case (mod, ver) =>
Dependency(mod, ver)
}
SbtConfig(
org,
name,
version,
scalaVersion,
mainClass,
pluginDependencies ++ dependencies
)
}
}

View File

@ -0,0 +1,16 @@
package coursier.sbtlauncher
import java.io.File
final case class ScalaProvider(
launcher: xsbti.Launcher,
version: String,
loader: ClassLoader,
jars: Array[File],
libraryJar: File,
compilerJar: File,
createApp: xsbti.ApplicationID => xsbti.AppProvider
) extends xsbti.ScalaProvider {
def app(id: xsbti.ApplicationID): xsbti.AppProvider =
createApp(id)
}

16
sbt.properties Normal file
View File

@ -0,0 +1,16 @@
sbt.version=0.13.8
plugins = [
"io.get-coursier:sbt-coursier:1.0.0-M15-1"
"org.xerial.sbt:sbt-pack:0.8.2"
"org.scala-js:sbt-scalajs:0.6.14"
"com.jsuereth:sbt-pgp:1.0.0"
"org.scoverage:sbt-scoverage:1.4.0"
"org.tpolecat:tut-plugin:0.4.8"
"com.typesafe.sbt:sbt-proguard:0.2.2"
"com.typesafe:sbt-mima-plugin:0.1.13"
]
dependencies = [
"org.scala-sbt:scripted-plugin:"${sbt.version}
]