Merge pull request #4602 from eatkins/layer-config

Improve error reporting for classloading issues
This commit is contained in:
eugene yokota 2019-04-04 22:32:15 -04:00 committed by GitHub
commit b40084f6fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 36 additions and 9 deletions

View File

@ -780,7 +780,8 @@ object Defaults extends BuildCommon {
(fullClasspath in test).value, (fullClasspath in test).value,
testForkedParallel.value, testForkedParallel.value,
(javaOptions in test).value, (javaOptions in test).value,
(classLoaderLayeringStrategy).value (classLoaderLayeringStrategy).value,
projectId = s"${thisProject.value.id} / ",
) )
} }
).value, ).value,
@ -952,7 +953,8 @@ object Defaults extends BuildCommon {
fullClasspath.value, fullClasspath.value,
testForkedParallel.value, testForkedParallel.value,
javaOptions.value, javaOptions.value,
classLoaderLayeringStrategy.value classLoaderLayeringStrategy.value,
projectId = s"${thisProject.value.id} / ",
) )
val taskName = display.show(resolvedScoped.value) val taskName = display.show(resolvedScoped.value)
val trl = testResultLogger.value val trl = testResultLogger.value
@ -997,6 +999,7 @@ object Defaults extends BuildCommon {
forkedParallelExecution = false, forkedParallelExecution = false,
javaOptions = Nil, javaOptions = Nil,
strategy = ClassLoaderLayeringStrategy.TestDependencies, strategy = ClassLoaderLayeringStrategy.TestDependencies,
projectId = "",
) )
} }
@ -1019,6 +1022,7 @@ object Defaults extends BuildCommon {
forkedParallelExecution, forkedParallelExecution,
javaOptions = Nil, javaOptions = Nil,
strategy = ClassLoaderLayeringStrategy.TestDependencies, strategy = ClassLoaderLayeringStrategy.TestDependencies,
projectId = "",
) )
} }
@ -1032,6 +1036,7 @@ object Defaults extends BuildCommon {
forkedParallelExecution: Boolean, forkedParallelExecution: Boolean,
javaOptions: Seq[String], javaOptions: Seq[String],
strategy: ClassLoaderLayeringStrategy, strategy: ClassLoaderLayeringStrategy,
projectId: String
): Initialize[Task[Tests.Output]] = { ): Initialize[Task[Tests.Output]] = {
val runners = createTestRunners(frameworks, loader, config) val runners = createTestRunners(frameworks, loader, config)
val groupTasks = groups map { val groupTasks = groups map {
@ -1061,17 +1066,39 @@ object Defaults extends BuildCommon {
val result = output map { out => val result = output map { out =>
out.events.foreach { out.events.foreach {
case (suite, e) => case (suite, e) =>
if (strategy != ClassLoaderLayeringStrategy.Flat) { if (strategy != ClassLoaderLayeringStrategy.Flat ||
e.throwables strategy != ClassLoaderLayeringStrategy.ScalaInstance) {
(e.throwables ++ e.throwables.flatMap(t => Option(t.getCause)))
.find { t => .find { t =>
t.isInstanceOf[NoClassDefFoundError] || t.isInstanceOf[IllegalAccessError] t.isInstanceOf[NoClassDefFoundError] ||
t.isInstanceOf[IllegalAccessError] ||
t.isInstanceOf[ClassNotFoundException]
} }
.foreach { t => .foreach { t =>
s.log.error( s.log.error(
s"Test suite $suite failed with $t. This may be due to the ClassLoaderLayeringStrategy" s"Test suite $suite failed with $t.\nThis may be due to the "
+ s" ($strategy) used by your task. This issue may be resolved by changing the" + s"ClassLoaderLayeringStrategy ($strategy) used by your task.\n"
+ " ClassLoaderLayeringStrategy in your configuration (generally Test or IntegrationTest)," + "To improve performance and reduce memory, sbt attempts to cache the"
+ " e.g.:\nTest / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat\n" + " class loaders used to load the project dependencies.\n"
+ "The project class files are loaded in a separate class loader that is"
+ " created for each test run.\nThe test class loader accesses the project"
+ " dependency classes using the cached project dependency classloader.\nWith"
+ " this approach, class loading may fail under the following conditions:\n\n"
+ " * Dependencies use reflection to access classes in your project's"
+ " classpath.\n Java serialization/deserialization may cause this.\n"
+ " * An open package is accessed across layers. If the project's classes"
+ " access or extend\n jvm package private classes defined in a"
+ " project dependency, it may cause an IllegalAccessError\n because the"
+ " jvm enforces package private at the classloader level.\n\n"
+ "These issues, along with others that were not enumerated above, may be"
+ " resolved by changing the class loader layering strategy.\n"
+ "The Flat and ScalaInstance strategies bundle the full project classpath in"
+ " the same class loader.\nTo use one of these strategies, set the "
+ " ClassLoaderLayeringStrategy key\nin your configuration, for example:\n\n"
+ s"set ${projectId}Test / classLoaderLayeringStrategy :="
+ " ClassLoaderLayeringStrategy.ScalaInstance\n"
+ s"set ${projectId}Test / classLoaderLayeringStrategy :="
+ " ClassLoaderLayeringStrategy.Flat\n\n"
+ "See ClassLoaderLayeringStrategy.scala for the full list of options." + "See ClassLoaderLayeringStrategy.scala for the full list of options."
) )
} }