Multiple isolation levels

This commit is contained in:
Alexandre Archambault 2016-01-10 21:32:30 +01:00
parent 1ebced021b
commit 3259fec276
2 changed files with 80 additions and 72 deletions

View File

@ -9,7 +9,7 @@ import java.util.Properties
import java.util.zip.{ ZipEntry, ZipOutputStream, ZipInputStream }
import caseapp.{ HelpMessage => Help, ValueDescription => Value, ExtraName => Short, _ }
import coursier.util.ClasspathFilter
import coursier.util.{ Parse, ClasspathFilter }
case class CommonOptions(
@Help("Keep optional dependencies (Maven)")
@ -101,9 +101,11 @@ case class Launch(
@Short("M")
@Short("main")
mainClass: String,
@ExtraName("I")
@Value("target:dependency")
@Short("I")
isolated: List[String],
@ExtraName("i")
@Help("Comma-separated isolation targets")
@Short("i")
isolateTarget: List[String],
@Recurse
common: CommonOptions
@ -118,62 +120,65 @@ case class Launch(
}
}
val helper = new Helper(
common.copy(forceVersion = common.forceVersion),
rawDependencies ++ isolated
)
// FIXME Some duplication with similar things in Helper
val (splitIsolated, malformedIsolated) = isolated
.toVector
.map(_.split(":", 3).toSeq)
.partition(_.length == 3)
if (malformedIsolated.nonEmpty) {
if (malformedIsolated.nonEmpty) {
Console.err.println("Malformed dependency(ies), should be like org:name:version")
for (s <- malformedIsolated)
Console.err.println(s" ${s.mkString(":")}")
}
sys.exit(1)
}
val isolatedModuleVersions = splitIsolated.map{
case Seq(org, namePart, version) =>
val p = namePart.split(';')
val name = p.head
val splitAttributes = p.tail.map(_.split("=", 2).toSeq).toSeq
val malformedAttributes = splitAttributes.filter(_.length != 2)
if (malformedAttributes.nonEmpty) {
Console.err.println(s"Malformed attributes in ${splitIsolated.mkString(":")}")
// :(
sys.exit(255)
}
val attributes = splitAttributes.collect {
case Seq(k, v) => k -> v
}
(Module(org, name, attributes.toMap), version)
}
val isolatedDeps = isolatedModuleVersions.map{case (mod, ver) =>
Dependency(mod, ver, configuration = "runtime")
}
val isolateTargets = {
val l = isolateTarget.flatMap(_.split(',')).filter(_.nonEmpty)
if (l.isEmpty)
val (invalid, valid) = l.partition(_.contains(":"))
if (invalid.nonEmpty) {
Console.err.println(s"Invalid target IDs:")
for (t <- invalid)
Console.err.println(s" $t")
sys.exit(255)
}
if (valid.isEmpty)
Array("default")
else
l.toArray
valid.toArray
}
val (validIsolated, unrecognizedIsolated) = isolated.partition(s => isolateTargets.exists(t => s.startsWith(t + ":")))
if (unrecognizedIsolated.nonEmpty) {
Console.err.println(s"Unrecognized isolation targets in:")
for (i <- unrecognizedIsolated)
Console.err.println(s" $i")
sys.exit(255)
}
val rawIsolated = validIsolated.map { s =>
val Array(target, dep) = s.split(":", 2)
target -> dep
}
val isolatedModuleVersions = rawIsolated.groupBy { case (t, _) => t }.map {
case (t, l) =>
val (errors, modVers) = Parse.moduleVersions(l.map { case (_, d) => d })
if (errors.nonEmpty) {
errors.foreach(Console.err.println)
sys.exit(255)
}
t -> modVers
}
val isolatedDeps = isolatedModuleVersions.map {
case (t, l) =>
t -> l.map {
case (mod, ver) =>
Dependency(mod, ver, configuration = "runtime")
}
}
val helper = new Helper(
common.copy(forceVersion = common.forceVersion),
rawDependencies ++ rawIsolated.map { case (_, dep) => dep }
)
val files0 = helper.fetch(sources = false, javadoc = false)
val parentLoader0 = new ClasspathFilter(
val parentLoader0: ClassLoader = new ClasspathFilter(
Thread.currentThread().getContextClassLoader,
Coursier.baseCp.map(new File(_)).toSet,
exclude = true
@ -183,22 +188,34 @@ case class Launch(
if (isolated.isEmpty)
(parentLoader0, files0)
else {
// FIXME These were already fetched above
val isolatedFiles =
helper.fetch(sources = false, javadoc = false, subset = isolatedDeps.toSet)
val (isolatedLoader, filteredFiles0) = isolateTargets.foldLeft((parentLoader0, files0)) {
case ((parent, files0), target) =>
val isolatedLoader = new IsolatedClassLoader(
isolatedFiles.map(_.toURI.toURL).toArray,
parentLoader0,
isolateTargets
)
// FIXME These were already fetched above
val isolatedFiles = helper.fetch(
sources = false,
javadoc = false,
subset = isolatedDeps.getOrElse(target, Seq.empty).toSet
)
val filteredFiles0 = files0.filterNot(isolatedFiles.toSet)
if (common.verbose0 >= 1) {
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.verbose0 >= 1) {
Console.err.println(s"Isolated loader files:")
for (f <- isolatedFiles.map(_.toString).sorted)
Console.err.println(s" $f")
Console.err.println(s"Remaining files:")
for (f <- filteredFiles0.map(_.toString).sorted)
Console.err.println(s" $f")

View File

@ -108,16 +108,7 @@ class Helper(
}
val (rawDependencies, extraArgs) = {
val idxOpt = Some(remainingArgs.indexOf("--")).filter(_ >= 0)
idxOpt.fold((remainingArgs, Seq.empty[String])) { idx =>
val (l, r) = remainingArgs.splitAt(idx)
assert(r.nonEmpty)
(l, r.tail)
}
}
val (modVerErrors, moduleVersions) = Parse.moduleVersions(remainingArgs)
val (modVerErrors, moduleVersions) = Parse.moduleVersions(rawDependencies)
prematureExitIf(modVerErrors.nonEmpty) {
s"Cannot parse dependencies:\n" + modVerErrors.map(" "+_).mkString("\n")