From 67df72ab019e32935588e1a58e636e07f4ed65ce Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Thu, 2 May 2019 14:01:17 -0700 Subject: [PATCH] Properly close a number of classloaders I discovered there were a number of places where closing a ClassLoader didn't work correctly because I was assuming it was a URLClassLoader when it was actually a ClasspathFilter. I also incorrectly imported the wrong kind of URLClassLoader in Run.scala. Finally, I close the SbtMetaBuildClassLoader when xMain exits now. --- main/src/main/scala/sbt/Defaults.scala | 9 +++++++-- main/src/main/scala/sbt/Main.scala | 5 +++++ .../main/scala/sbt/internal/ClassLoaders.scala | 5 +++++ run/src/main/scala/sbt/Run.scala | 17 +++++++++-------- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 4cb413ea1..c056393e3 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -29,6 +29,7 @@ import sbt.Scope.{ GlobalScope, ThisScope, fillTaskAxis } import sbt.internal.CommandStrings.ExportStream import sbt.internal._ import sbt.internal.inc.JavaInterfaceUtil._ +import sbt.internal.inc.classpath.ClasspathFilter import sbt.internal.inc.{ ZincLmUtil, ZincUtil } import sbt.internal.io.{ Source, WatchState } import sbt.internal.librarymanagement.{ CustomHttp => _, _ } @@ -795,13 +796,17 @@ object Defaults extends BuildCommon { // ((streams in test, loadedTestFrameworks, testLoader, testGrouping in test, testExecution in test, fullClasspath in test, javaHome in test, testForkedParallel, javaOptions in test) flatMap allTestGroupsTask).value, testResultLogger in (Test, test) :== TestResultLogger.SilentWhenNoTests, // https://github.com/sbt/sbt/issues/1185 test := { - val loader = testLoader.value match { case u: URLClassLoader => Some(u); case _ => None } + val close = testLoader.value match { + case u: URLClassLoader => Some(() => u.close()) + case c: ClasspathFilter => Some(() => c.close()) + case _ => None + } val trl = (testResultLogger in (Test, test)).value val taskName = Project.showContextKey(state.value).show(resolvedScoped.value) try { trl.run(streams.value.log, executeTests.value, taskName) } finally { - loader.foreach(_.close()) + close.foreach(_.apply()) } }, testOnly := inputTests(testOnly).evaluated, diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 943a17736..6a4596ad1 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -50,6 +50,11 @@ final class xMain extends xsbti.AppMain { case e: InvocationTargetException => // This propogates xsbti.FullReload to the launcher throw e.getCause + } finally { + loader match { + case a: AutoCloseable => a.close() + case _ => + } } } /* diff --git a/main/src/main/scala/sbt/internal/ClassLoaders.scala b/main/src/main/scala/sbt/internal/ClassLoaders.scala index ecd4fc50a..89e24227c 100644 --- a/main/src/main/scala/sbt/internal/ClassLoaders.scala +++ b/main/src/main/scala/sbt/internal/ClassLoaders.scala @@ -226,6 +226,11 @@ private[sbt] object SbtMetaBuildClassLoader { } new URLClassLoader(rest, updatedLibraryLoader) { override def toString: String = s"SbtMetaBuildClassLoader" + override def close(): Unit = { + super.close() + updatedLibraryLoader.close() + interfaceLoader.close() + } } } } diff --git a/run/src/main/scala/sbt/Run.scala b/run/src/main/scala/sbt/Run.scala index 22883d43d..0ae16f58b 100644 --- a/run/src/main/scala/sbt/Run.scala +++ b/run/src/main/scala/sbt/Run.scala @@ -8,19 +8,19 @@ package sbt import java.io.File -import java.lang.reflect.{ Method, Modifier } -import Modifier.{ isPublic, isStatic } +import java.lang.reflect.Method +import java.lang.reflect.Modifier.{ isPublic, isStatic } +import java.net.URLClassLoader -import sbt.internal.inc.classpath.ClasspathUtilities import sbt.internal.inc.ScalaInstance +import sbt.internal.inc.classpath.{ ClasspathFilter, ClasspathUtilities } import sbt.internal.util.MessageOnlyException import sbt.io.Path import sbt.util.Logger -import scala.reflect.internal.util.ScalaClassLoader.URLClassLoader -import scala.util.{ Failure, Success, Try } -import scala.util.control.NonFatal import scala.sys.process.Process +import scala.util.control.NonFatal +import scala.util.{ Failure, Success, Try } sealed trait ScalaRun { def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Try[Unit] @@ -98,8 +98,9 @@ class Run(newLoader: Seq[File] => ClassLoader, trapExit: Boolean) extends ScalaR invokeMain(loader, main, options) } finally { loader match { - case u: URLClassLoader => u.close() - case _ => + case u: URLClassLoader => u.close() + case c: ClasspathFilter => c.close() + case _ => } } }