Merge pull request #312 from alexarchambault/topic/develop

Fix
This commit is contained in:
Alexandre Archambault 2016-08-04 22:58:43 -04:00 committed by GitHub
commit 73321d5447
9 changed files with 223 additions and 148 deletions

View File

@ -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, "")

View File

@ -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
}
}

View File

@ -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 =>

View File

@ -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

View File

@ -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] = {

View File

@ -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")

View File

@ -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

View File

@ -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"),

View File

@ -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{