mirror of https://github.com/sbt/sbt.git
commit
73321d5447
|
|
@ -16,16 +16,24 @@ case class Bootstrap(
|
|||
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
if (options.mainClass.isEmpty) {
|
||||
Console.err.println(s"Error: no main class specified. Specify one with -M or --main")
|
||||
sys.exit(255)
|
||||
}
|
||||
val helper = new Helper(
|
||||
options.common,
|
||||
remainingArgs,
|
||||
isolated = options.isolated,
|
||||
warnBaseLoaderNotFound = false
|
||||
)
|
||||
|
||||
if (!options.standalone && options.downloadDir.isEmpty) {
|
||||
Console.err.println(s"Error: no download dir specified. Specify one with -D or --download-dir")
|
||||
Console.err.println("E.g. -D \"\\$HOME/.app-name/jars\"")
|
||||
sys.exit(255)
|
||||
}
|
||||
lazy val downloadDir =
|
||||
if (options.downloadDir.isEmpty)
|
||||
helper.baseDependencies.headOption match {
|
||||
case Some(dep) =>
|
||||
s"\\$$HOME/.coursier/bootstrap/${dep.module.organization}/${dep.module.name}"
|
||||
case None =>
|
||||
Console.err.println("Error: no dependencies specified.")
|
||||
sys.exit(255)
|
||||
}
|
||||
else
|
||||
options.downloadDir
|
||||
|
||||
val (validProperties, wrongProperties) = options.property.partition(_.contains("="))
|
||||
if (wrongProperties.nonEmpty) {
|
||||
|
|
@ -73,8 +81,6 @@ case class Bootstrap(
|
|||
}
|
||||
|
||||
|
||||
val helper = new Helper(options.common, remainingArgs)
|
||||
|
||||
val isolatedDeps = options.isolated.isolatedDeps(options.common.defaultArtifactType)
|
||||
|
||||
val (_, isolatedArtifactFiles) =
|
||||
|
|
@ -121,7 +127,9 @@ case class Bootstrap(
|
|||
if (nonHttpUrls.nonEmpty)
|
||||
Console.err.println(s"Warning: non HTTP URLs:\n${nonHttpUrls.mkString("\n")}")
|
||||
|
||||
val buffer = new ByteArrayOutputStream()
|
||||
val mainClass = helper.retainedMainClass
|
||||
|
||||
val buffer = new ByteArrayOutputStream
|
||||
|
||||
val bootstrapZip = new ZipInputStream(new ByteArrayInputStream(bootstrapJar))
|
||||
val outputZip = new ZipOutputStream(buffer)
|
||||
|
|
@ -176,10 +184,10 @@ case class Bootstrap(
|
|||
val propsEntry = new ZipEntry("bootstrap.properties")
|
||||
propsEntry.setTime(time)
|
||||
|
||||
val properties = new Properties()
|
||||
properties.setProperty("bootstrap.mainClass", options.mainClass)
|
||||
val properties = new Properties
|
||||
properties.setProperty("bootstrap.mainClass", mainClass)
|
||||
if (!options.standalone)
|
||||
properties.setProperty("bootstrap.jarDir", options.downloadDir)
|
||||
properties.setProperty("bootstrap.jarDir", downloadDir)
|
||||
|
||||
outputZip.putNextEntry(propsEntry)
|
||||
properties.store(outputZip, "")
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package coursier
|
|||
package cli
|
||||
|
||||
import java.io.{ OutputStreamWriter, File }
|
||||
import java.net.URL
|
||||
import java.net.{ URL, URLClassLoader }
|
||||
import java.util.jar.{ Manifest => JManifest }
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
|
|
@ -11,6 +11,7 @@ import coursier.util.{Print, Parse}
|
|||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration.Duration
|
||||
import scala.util.Try
|
||||
|
||||
import scalaz.{Failure, Success, \/-, -\/}
|
||||
import scalaz.concurrent.{ Task, Strategy }
|
||||
|
|
@ -75,7 +76,9 @@ class Helper(
|
|||
common: CommonOptions,
|
||||
rawDependencies: Seq[String],
|
||||
printResultStdout: Boolean = false,
|
||||
ignoreErrors: Boolean = false
|
||||
ignoreErrors: Boolean = false,
|
||||
isolated: IsolatedLoaderOptions = IsolatedLoaderOptions(),
|
||||
warnBaseLoaderNotFound: Boolean = true
|
||||
) {
|
||||
import common._
|
||||
import Helper.errPrintln
|
||||
|
|
@ -577,4 +580,116 @@ class Helper(
|
|||
|
||||
files0
|
||||
}
|
||||
|
||||
lazy val (parentLoader, filteredFiles) = {
|
||||
|
||||
val contextLoader = Thread.currentThread().getContextClassLoader
|
||||
|
||||
val files0 = fetch(sources = false, javadoc = false)
|
||||
|
||||
val parentLoader0: ClassLoader =
|
||||
Launch.mainClassLoader(contextLoader)
|
||||
.flatMap(cl => Option(cl.getParent))
|
||||
.getOrElse {
|
||||
// proguarded -> no risk of conflicts, no absolute need to find a specific ClassLoader
|
||||
val isProguarded = Try(contextLoader.loadClass("coursier.cli.Launch")).isFailure
|
||||
if (warnBaseLoaderNotFound && !isProguarded && common.verbosityLevel >= 0)
|
||||
Console.err.println(
|
||||
"Warning: cannot find the main ClassLoader that launched coursier.\n" +
|
||||
"Was coursier launched by its main launcher? " +
|
||||
"The ClassLoader of the application that is about to be launched will be intertwined " +
|
||||
"with the one of coursier, which may be a problem if their dependencies conflict."
|
||||
)
|
||||
contextLoader
|
||||
}
|
||||
|
||||
if (isolated.isolated.isEmpty)
|
||||
(parentLoader0, files0)
|
||||
else {
|
||||
|
||||
val isolatedDeps = isolated.isolatedDeps(common.defaultArtifactType)
|
||||
|
||||
val (isolatedLoader, filteredFiles0) = isolated.targets.foldLeft((parentLoader0, files0)) {
|
||||
case ((parent, files0), target) =>
|
||||
|
||||
// FIXME These were already fetched above
|
||||
val isolatedFiles = fetch(
|
||||
sources = false,
|
||||
javadoc = false,
|
||||
subset = isolatedDeps.getOrElse(target, Seq.empty).toSet
|
||||
)
|
||||
|
||||
if (common.verbosityLevel >= 2) {
|
||||
Console.err.println(s"Isolated loader files:")
|
||||
for (f <- isolatedFiles.map(_.toString).sorted)
|
||||
Console.err.println(s" $f")
|
||||
}
|
||||
|
||||
val isolatedLoader = new IsolatedClassLoader(
|
||||
isolatedFiles.map(_.toURI.toURL).toArray,
|
||||
parent,
|
||||
Array(target)
|
||||
)
|
||||
|
||||
val filteredFiles0 = files0.filterNot(isolatedFiles.toSet)
|
||||
|
||||
(isolatedLoader, filteredFiles0)
|
||||
}
|
||||
|
||||
if (common.verbosityLevel >= 2) {
|
||||
Console.err.println(s"Remaining files:")
|
||||
for (f <- filteredFiles0.map(_.toString).sorted)
|
||||
Console.err.println(s" $f")
|
||||
}
|
||||
|
||||
(isolatedLoader, filteredFiles0)
|
||||
}
|
||||
}
|
||||
|
||||
lazy val loader = new URLClassLoader(
|
||||
filteredFiles.map(_.toURI.toURL).toArray,
|
||||
parentLoader
|
||||
)
|
||||
|
||||
|
||||
lazy val retainedMainClass = {
|
||||
|
||||
val mainClasses = Helper.mainClasses(loader)
|
||||
|
||||
if (common.verbosityLevel >= 2) {
|
||||
Console.err.println("Found main classes:")
|
||||
for (((vendor, title), mainClass) <- mainClasses)
|
||||
Console.err.println(s" $mainClass (vendor: $vendor, title: $title)")
|
||||
Console.err.println("")
|
||||
}
|
||||
|
||||
val mainClass =
|
||||
if (mainClasses.isEmpty) {
|
||||
Helper.errPrintln("No main class found. Specify one with -M or --main.")
|
||||
sys.exit(255)
|
||||
} else if (mainClasses.size == 1) {
|
||||
val (_, mainClass) = mainClasses.head
|
||||
mainClass
|
||||
} else {
|
||||
// Trying to get the main class of the first artifact
|
||||
val mainClassOpt = for {
|
||||
(module, _, _) <- moduleVersionConfigs.headOption
|
||||
mainClass <- mainClasses.collectFirst {
|
||||
case ((org, name), mainClass)
|
||||
if org == module.organization && (
|
||||
module.name == name ||
|
||||
module.name.startsWith(name + "_") // Ignore cross version suffix
|
||||
) =>
|
||||
mainClass
|
||||
}
|
||||
} yield mainClass
|
||||
|
||||
mainClassOpt.getOrElse {
|
||||
Helper.errPrintln(s"Cannot find default main class. Specify one with -M or --main.")
|
||||
sys.exit(255)
|
||||
}
|
||||
}
|
||||
|
||||
mainClass
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,140 +76,30 @@ case class Launch(
|
|||
|
||||
val helper = new Helper(
|
||||
options.common,
|
||||
remainingArgs ++ options.isolated.rawIsolated.map { case (_, dep) => dep }
|
||||
remainingArgs ++ options.isolated.rawIsolated.map { case (_, dep) => dep },
|
||||
isolated = options.isolated
|
||||
)
|
||||
|
||||
|
||||
val files0 = helper.fetch(sources = false, javadoc = false)
|
||||
|
||||
val contextLoader = Thread.currentThread().getContextClassLoader
|
||||
|
||||
val parentLoader0: ClassLoader =
|
||||
Launch.mainClassLoader(contextLoader)
|
||||
.flatMap(cl => Option(cl.getParent))
|
||||
.getOrElse {
|
||||
// proguarded -> no risk of conflicts, no absolute need to find a specific ClassLoader
|
||||
val isProguarded = Try(contextLoader.loadClass("coursier.cli.Launch")).isFailure
|
||||
if (!isProguarded && options.common.verbosityLevel >= 0)
|
||||
Console.err.println(
|
||||
"Warning: cannot find the main ClassLoader that launched coursier.\n" +
|
||||
"Was coursier launched by its main launcher? " +
|
||||
"The ClassLoader of the application that is about to be launched will be intertwined " +
|
||||
"with the one of coursier, which may be a problem if their dependencies conflict."
|
||||
)
|
||||
contextLoader
|
||||
}
|
||||
|
||||
val (parentLoader, filteredFiles) =
|
||||
if (options.isolated.isolated.isEmpty)
|
||||
(parentLoader0, files0)
|
||||
else {
|
||||
|
||||
val isolatedDeps = options.isolated.isolatedDeps(options.common.defaultArtifactType)
|
||||
|
||||
val (isolatedLoader, filteredFiles0) = options.isolated.targets.foldLeft((parentLoader0, files0)) {
|
||||
case ((parent, files0), target) =>
|
||||
|
||||
// FIXME These were already fetched above
|
||||
val isolatedFiles = helper.fetch(
|
||||
sources = false,
|
||||
javadoc = false,
|
||||
subset = isolatedDeps.getOrElse(target, Seq.empty).toSet
|
||||
)
|
||||
|
||||
if (options.common.verbosityLevel >= 2) {
|
||||
Console.err.println(s"Isolated loader files:")
|
||||
for (f <- isolatedFiles.map(_.toString).sorted)
|
||||
Console.err.println(s" $f")
|
||||
}
|
||||
|
||||
val isolatedLoader = new IsolatedClassLoader(
|
||||
isolatedFiles.map(_.toURI.toURL).toArray,
|
||||
parent,
|
||||
Array(target)
|
||||
)
|
||||
|
||||
val filteredFiles0 = files0.filterNot(isolatedFiles.toSet)
|
||||
|
||||
(isolatedLoader, filteredFiles0)
|
||||
}
|
||||
|
||||
if (options.common.verbosityLevel >= 2) {
|
||||
Console.err.println(s"Remaining files:")
|
||||
for (f <- filteredFiles0.map(_.toString).sorted)
|
||||
Console.err.println(s" $f")
|
||||
}
|
||||
|
||||
(isolatedLoader, filteredFiles0)
|
||||
}
|
||||
|
||||
val loader = new URLClassLoader(
|
||||
filteredFiles.map(_.toURI.toURL).toArray,
|
||||
parentLoader
|
||||
)
|
||||
|
||||
val mainClass0 =
|
||||
if (options.mainClass.nonEmpty) options.mainClass
|
||||
else {
|
||||
val mainClasses = Helper.mainClasses(loader)
|
||||
|
||||
if (options.common.verbosityLevel >= 2) {
|
||||
Console.err.println("Found main classes:")
|
||||
for (((vendor, title), mainClass) <- mainClasses)
|
||||
Console.err.println(s" $mainClass (vendor: $vendor, title: $title)")
|
||||
Console.err.println("")
|
||||
}
|
||||
|
||||
val mainClass =
|
||||
if (mainClasses.isEmpty) {
|
||||
Helper.errPrintln("No main class found. Specify one with -M or --main.")
|
||||
sys.exit(255)
|
||||
} else if (mainClasses.size == 1) {
|
||||
val (_, mainClass) = mainClasses.head
|
||||
mainClass
|
||||
} else {
|
||||
// Trying to get the main class of the first artifact
|
||||
val mainClassOpt = for {
|
||||
(module, _, _) <- helper.moduleVersionConfigs.headOption
|
||||
mainClass <- mainClasses.collectFirst {
|
||||
case ((org, name), mainClass)
|
||||
if org == module.organization && (
|
||||
module.name == name ||
|
||||
module.name.startsWith(name + "_") // Ignore cross version suffix
|
||||
) =>
|
||||
mainClass
|
||||
}
|
||||
} yield mainClass
|
||||
|
||||
mainClassOpt.getOrElse {
|
||||
Helper.errPrintln(s"Cannot find default main class. Specify one with -M or --main.")
|
||||
sys.exit(255)
|
||||
}
|
||||
}
|
||||
|
||||
mainClass
|
||||
}
|
||||
|
||||
val cls =
|
||||
try loader.loadClass(mainClass0)
|
||||
try helper.loader.loadClass(helper.retainedMainClass)
|
||||
catch { case e: ClassNotFoundException =>
|
||||
Helper.errPrintln(s"Error: class $mainClass0 not found")
|
||||
Helper.errPrintln(s"Error: class ${helper.retainedMainClass} not found")
|
||||
sys.exit(255)
|
||||
}
|
||||
val method =
|
||||
try cls.getMethod("main", classOf[Array[String]])
|
||||
catch { case e: NoSuchMethodException =>
|
||||
Helper.errPrintln(s"Error: method main not found in $mainClass0")
|
||||
Helper.errPrintln(s"Error: method main not found in ${helper.retainedMainClass}")
|
||||
sys.exit(255)
|
||||
}
|
||||
method.setAccessible(true)
|
||||
|
||||
if (options.common.verbosityLevel >= 2)
|
||||
Helper.errPrintln(s"Launching $mainClass0 ${userArgs.mkString(" ")}")
|
||||
Helper.errPrintln(s"Launching ${helper.retainedMainClass} ${userArgs.mkString(" ")}")
|
||||
else if (options.common.verbosityLevel == 1)
|
||||
Helper.errPrintln(s"Launching")
|
||||
|
||||
Thread.currentThread().setContextClassLoader(loader)
|
||||
Thread.currentThread().setContextClassLoader(helper.loader)
|
||||
try method.invoke(null, userArgs.toArray)
|
||||
catch {
|
||||
case e: java.lang.reflect.InvocationTargetException =>
|
||||
|
|
|
|||
|
|
@ -98,10 +98,10 @@ case class CacheOptions(
|
|||
case class IsolatedLoaderOptions(
|
||||
@Value("target:dependency")
|
||||
@Short("I")
|
||||
isolated: List[String],
|
||||
isolated: List[String] = Nil,
|
||||
@Help("Comma-separated isolation targets")
|
||||
@Short("i")
|
||||
isolateTarget: List[String]
|
||||
isolateTarget: List[String] = Nil
|
||||
) {
|
||||
|
||||
def anyIsolatedDep = isolateTarget.nonEmpty || isolated.nonEmpty
|
||||
|
|
|
|||
|
|
@ -29,17 +29,32 @@ object Parse {
|
|||
None
|
||||
|
||||
def versionInterval(s: String): Option[VersionInterval] = {
|
||||
|
||||
def parseBounds(fromIncluded: Boolean, toIncluded: Boolean, s: String) = {
|
||||
|
||||
val commaIdx = s.indexOf(',')
|
||||
|
||||
if (commaIdx >= 0) {
|
||||
val strFrom = s.take(commaIdx)
|
||||
val strTo = s.drop(commaIdx + 1)
|
||||
|
||||
for {
|
||||
from <- if (strFrom.isEmpty) Some(None) else version(strFrom).map(Some(_))
|
||||
to <- if (strTo.isEmpty) Some(None) else version(strTo).map(Some(_))
|
||||
} yield VersionInterval(from.filterNot(_.isEmpty), to.filterNot(_.isEmpty), fromIncluded, toIncluded)
|
||||
} else if (s.nonEmpty && fromIncluded && toIncluded)
|
||||
for (v <- version(s) if !v.isEmpty)
|
||||
yield VersionInterval(Some(v), Some(v), fromIncluded, toIncluded)
|
||||
else
|
||||
None
|
||||
}
|
||||
|
||||
for {
|
||||
fromIncluded <- if (s.startsWith("[")) Some(true) else if (s.startsWith("(")) Some(false) else None
|
||||
toIncluded <- if (s.endsWith("]")) Some(true) else if (s.endsWith(")")) Some(false) else None
|
||||
s0 = s.drop(1).dropRight(1)
|
||||
commaIdx = s0.indexOf(',')
|
||||
if commaIdx >= 0
|
||||
strFrom = s0.take(commaIdx)
|
||||
strTo = s0.drop(commaIdx + 1)
|
||||
from <- if (strFrom.isEmpty) Some(None) else version(strFrom).map(Some(_))
|
||||
to <- if (strTo.isEmpty) Some(None) else version(strTo).map(Some(_))
|
||||
} yield VersionInterval(from.filterNot(_.isEmpty), to.filterNot(_.isEmpty), fromIncluded, toIncluded)
|
||||
itv <- parseBounds(fromIncluded, toIncluded, s0)
|
||||
} yield itv
|
||||
}
|
||||
|
||||
def versionConstraint(s: String): Option[VersionConstraint] = {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.8.0")
|
||||
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.10")
|
||||
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.11")
|
||||
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")
|
||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.1.0")
|
||||
addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.2")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
com.google.code.findbugs:jsr305:3.0.0:compile
|
||||
com.google.guava:guava:19.0:compile
|
||||
io.grpc:grpc-core:0.14.1:compile
|
||||
io.grpc:grpc-netty:0.14.1:compile
|
||||
io.netty:netty-buffer:4.1.1.Final:compile
|
||||
io.netty:netty-codec:4.1.1.Final:compile
|
||||
io.netty:netty-codec-http:4.1.1.Final:compile
|
||||
io.netty:netty-codec-http2:4.1.1.Final:compile
|
||||
io.netty:netty-common:4.1.1.Final:compile
|
||||
io.netty:netty-handler:4.1.1.Final:compile
|
||||
io.netty:netty-resolver:4.1.1.Final:compile
|
||||
io.netty:netty-transport:4.1.1.Final:compile
|
||||
|
|
@ -280,6 +280,13 @@ object CentralTests extends TestSuite {
|
|||
}
|
||||
}
|
||||
|
||||
'fixedVersionDependency - {
|
||||
val mod = Module("io.grpc", "grpc-netty")
|
||||
val version = "0.14.1"
|
||||
|
||||
resolutionCheck(mod, version)
|
||||
}
|
||||
|
||||
'mavenScopes - {
|
||||
def check(config: String) = resolutionCheck(
|
||||
Module("com.android.tools", "sdklib"),
|
||||
|
|
|
|||
|
|
@ -163,10 +163,6 @@ object VersionIntervalTests extends TestSuite {
|
|||
|
||||
'parse{
|
||||
'malformed{
|
||||
val s1 = "[1.1]"
|
||||
val itv1 = Parse.versionInterval(s1)
|
||||
assert(itv1 == None)
|
||||
|
||||
val s2 = "(1.1)"
|
||||
val itv2 = Parse.versionInterval(s2)
|
||||
assert(itv2 == None)
|
||||
|
|
@ -263,6 +259,38 @@ object VersionIntervalTests extends TestSuite {
|
|||
assert(itv4 == Some(VersionInterval(None, None, false, true)))
|
||||
assert(!itv4.get.isValid)
|
||||
}
|
||||
|
||||
'fixedVersion - {
|
||||
* - {
|
||||
val itv = Parse.versionInterval("[1.2]")
|
||||
assert(itv == Some(VersionInterval(Some(Version("1.2")), Some(Version("1.2")), true, true)))
|
||||
}
|
||||
|
||||
* - {
|
||||
val itv = Parse.versionInterval("[1.2)")
|
||||
assert(itv.isEmpty)
|
||||
}
|
||||
|
||||
* - {
|
||||
val itv = Parse.versionInterval("(1.2]")
|
||||
assert(itv.isEmpty)
|
||||
}
|
||||
|
||||
* - {
|
||||
val itv = Parse.versionInterval("(1.2)")
|
||||
assert(itv.isEmpty)
|
||||
}
|
||||
|
||||
* - {
|
||||
val itv = Parse.versionInterval("[]")
|
||||
assert(itv.isEmpty)
|
||||
}
|
||||
|
||||
* - {
|
||||
val itv = Parse.versionInterval("[0.0]")
|
||||
assert(itv.isEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
'constraint{
|
||||
|
|
|
|||
Loading…
Reference in New Issue