mirror of https://github.com/sbt/sbt.git
Detect main class in bootstrap command
This commit is contained in:
parent
419ff74a98
commit
c89dc684c1
|
|
@ -16,11 +16,6 @@ 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)
|
||||
}
|
||||
|
||||
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\"")
|
||||
|
|
@ -73,7 +68,12 @@ case class Bootstrap(
|
|||
}
|
||||
|
||||
|
||||
val helper = new Helper(options.common, remainingArgs)
|
||||
val helper = new Helper(
|
||||
options.common,
|
||||
remainingArgs,
|
||||
isolated = options.isolated,
|
||||
warnBaseLoaderNotFound = false
|
||||
)
|
||||
|
||||
val isolatedDeps = options.isolated.isolatedDeps(options.common.defaultArtifactType)
|
||||
|
||||
|
|
@ -121,7 +121,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,8 +178,8 @@ 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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue