From b0f3cb0a8e9beb520684c8ec516d772b081cbdf0 Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Thu, 28 Mar 2024 11:59:55 +0100 Subject: [PATCH] Fix actions/completions When loading a scripted test, sbt creates a jar file and loads it. The path of the jar file is the same for all the batched tests. We must prevent the JDK from caching this jar file to force a reload after each test. Otherwise sbt tries to load the auto-plugins of a previous test and fails. --- .../java/sbt/internal/XMainConfiguration.java | 26 ++++++++++++++++++- main/src/main/scala/sbt/EvaluateTask.scala | 3 +-- .../scala/sbt/internal/PluginDiscovery.scala | 7 ++--- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/main/src/main/java/sbt/internal/XMainConfiguration.java b/main/src/main/java/sbt/internal/XMainConfiguration.java index d6cf69af2..674d39008 100644 --- a/main/src/main/java/sbt/internal/XMainConfiguration.java +++ b/main/src/main/java/sbt/internal/XMainConfiguration.java @@ -8,10 +8,14 @@ package sbt.internal; import java.io.File; +import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.MalformedURLException; import java.net.URL; +import java.net.URLConnection; +import java.nio.file.Paths; import xsbti.*; /** @@ -25,6 +29,10 @@ public class XMainConfiguration { public xsbti.MainResult run(String moduleName, xsbti.AppConfiguration configuration) throws Throwable { try { + boolean isScripted = Boolean.parseBoolean(System.getProperty("sbt.scripted")); + // in batch scripted tests, we disable caching of JAR URL connections to avoid interference + // between tests + if (isScripted) disableCachingOfURLConnections(); ClassLoader topLoader = configuration.provider().scalaProvider().launcher().topLoader(); xsbti.AppConfiguration updatedConfiguration = null; try { @@ -56,7 +64,7 @@ public class XMainConfiguration { clw.getMethod("warmup").invoke(clw.getField("MODULE$").get(null)); return (xsbti.MainResult) runMethod.invoke(instance, updatedConfiguration); } catch (InvocationTargetException e) { - // This propogates xsbti.FullReload to the launcher + // This propagates xsbti.FullReload to the launcher throw e.getCause(); } } catch (ReflectiveOperationException e) { @@ -104,6 +112,22 @@ public class XMainConfiguration { } } + private class FakeURLConnection extends URLConnection { + public FakeURLConnection(URL url) { + super(url); + } + + public void connect() throws IOException {} + } + + private void disableCachingOfURLConnections() { + try { + URLConnection conn = new FakeURLConnection(Paths.get(".").toUri().toURL()); + conn.setDefaultUseCaches(false); + } catch (MalformedURLException e) { + } + } + /* * Replaces the AppProvider.loader method with a new loader that puts the sbt test interface * jar ahead of the rest of the sbt classpath in the classloading hierarchy. diff --git a/main/src/main/scala/sbt/EvaluateTask.scala b/main/src/main/scala/sbt/EvaluateTask.scala index 8c8f42a66..06e5b9105 100644 --- a/main/src/main/scala/sbt/EvaluateTask.scala +++ b/main/src/main/scala/sbt/EvaluateTask.scala @@ -337,10 +337,9 @@ object EvaluateTask { def evalPluginDef(pluginDef: BuildStructure, state: State): PluginData = { val root = ProjectRef(pluginDef.root, Load.getRootProject(pluginDef.units)(pluginDef.root)) - val pluginKey = pluginData val config = extractedTaskConfig(Project.extract(state), pluginDef, state) val evaluated = - apply(pluginDef, ScopedKey(pluginKey.scope, pluginKey.key), state, root, config) + apply(pluginDef, ScopedKey(pluginData.scope, pluginData.key), state, root, config) val (newS, result) = evaluated getOrElse sys.error( "Plugin data does not exist for plugin definition at " + pluginDef.root ) diff --git a/main/src/main/scala/sbt/internal/PluginDiscovery.scala b/main/src/main/scala/sbt/internal/PluginDiscovery.scala index 79f3aba11..35afdd3fb 100644 --- a/main/src/main/scala/sbt/internal/PluginDiscovery.scala +++ b/main/src/main/scala/sbt/internal/PluginDiscovery.scala @@ -136,9 +136,10 @@ object PluginDiscovery: .getResources(resourceName) .asScala .toSeq - .filter(onClasspath(classpath, converter)) flatMap { u => - IO.readLinesURL(u).map(_.trim).filter(!_.isEmpty) - } + .filter(onClasspath(classpath, converter)) + .flatMap { u => + IO.readLinesURL(u).map(_.trim).filter(!_.isEmpty) + } /** Returns `true` if `url` is an entry in `classpath`. */ def onClasspath(classpath: Def.Classpath, converter: FileConverter)(url: URL): Boolean =