Skip to content

Commit cb4c1e7

Browse files
authored
Merge pull request sbt#6246 from eed3si9n/wip/2.13-pre1
Port XMainConfiguration to Java
2 parents d687cf1 + 2f392ac commit cb4c1e7

File tree

9 files changed

+362
-203
lines changed

9 files changed

+362
-203
lines changed

build.sbt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ ThisBuild / Test / scalafmtOnCompile := !(Global / insideCI).value
1717
ThisBuild / turbo := true
1818
ThisBuild / usePipelining := false // !(Global / insideCI).value
1919

20+
Global / semanticdbEnabled := !(Global / insideCI).value
2021
val excludeLint = SettingKey[Set[Def.KeyedInitialize[_]]]("excludeLintKeys")
2122
Global / excludeLint := (Global / excludeLint).?.value.getOrElse(Set.empty)
2223
Global / excludeLint += componentID
@@ -1029,7 +1030,11 @@ lazy val mainProj = (project in file("main"))
10291030
// internal logging apis,
10301031
exclude[IncompatibleSignatureProblem]("sbt.internal.LogManager*"),
10311032
exclude[MissingTypesProblem]("sbt.internal.RelayAppender"),
1032-
exclude[MissingClassProblem]("sbt.internal.TaskProgress$ProgressThread")
1033+
exclude[MissingClassProblem]("sbt.internal.TaskProgress$ProgressThread"),
1034+
// internal implementation
1035+
exclude[MissingClassProblem](
1036+
"sbt.internal.XMainConfiguration$ModifiedConfiguration$ModifiedAppProvider$ModifiedScalaProvider$"
1037+
),
10331038
)
10341039
)
10351040
.configure(
@@ -1317,6 +1322,7 @@ def runNpm(command: String, base: File, log: sbt.internal.util.ManagedLogger) =
13171322

13181323
lazy val vscodePlugin = (project in file("vscode-sbt-scala"))
13191324
.settings(
1325+
bspEnabled := false,
13201326
crossPaths := false,
13211327
crossScalaVersions := Seq(baseScalaVersion),
13221328
skip in publish := true,

main-settings/src/main/scala/sbt/std/InputWrapper.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,32 +33,32 @@ object InputWrapper {
3333
@compileTimeOnly(
3434
"`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task."
3535
)
36-
def wrapTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
36+
def `wrapTask_\u2603\u2603`[T](@deprecated("unused", "") in: Any): T = implDetailError
3737

3838
@compileTimeOnly(
3939
"`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting."
4040
)
41-
def wrapInit_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
41+
def `wrapInit_\u2603\u2603`[T](@deprecated("unused", "") in: Any): T = implDetailError
4242

4343
@compileTimeOnly(
4444
"`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task."
4545
)
46-
def wrapInitTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
46+
def `wrapInitTask_\u2603\u2603`[T](@deprecated("unused", "") in: Any): T = implDetailError
4747

4848
@compileTimeOnly(
4949
"`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask."
5050
)
51-
def wrapInputTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
51+
def `wrapInputTask_\u2603\u2603`[T](@deprecated("unused", "") in: Any): T = implDetailError
5252

5353
@compileTimeOnly(
5454
"`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask."
5555
)
56-
def wrapInitInputTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
56+
def `wrapInitInputTask_\u2603\u2603`[T](@deprecated("unused", "") in: Any): T = implDetailError
5757

5858
@compileTimeOnly(
5959
"`previous` can only be called on a task within a task or input task definition macro, such as :=, +=, ++=, Def.task, or Def.inputTask."
6060
)
61-
def wrapPrevious_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
61+
def `wrapPrevious_\u2603\u2603`[T](@deprecated("unused", "") in: Any): T = implDetailError
6262

6363
private[this] def implDetailError =
6464
sys.error("This method is an implementation detail and should not be referenced.")
@@ -240,13 +240,13 @@ object ParserInput {
240240
@compileTimeOnly(
241241
"`parsed` can only be used within an input task macro, such as := or Def.inputTask."
242242
)
243-
def parser_\u2603\u2603[T](@deprecated("unused", "") i: Any): T =
243+
def `parser_\u2603\u2603`[T](@deprecated("unused", "") i: Any): T =
244244
sys.error("This method is an implementation detail and should not be referenced.")
245245

246246
@compileTimeOnly(
247247
"`parsed` can only be used within an input task macro, such as := or Def.inputTask."
248248
)
249-
def initParser_\u2603\u2603[T](@deprecated("unused", "") i: Any): T =
249+
def `initParser_\u2603\u2603`[T](@deprecated("unused", "") i: Any): T =
250250
sys.error("This method is an implementation detail and should not be referenced.")
251251

252252
private[std] def wrap[T: c.WeakTypeTag](
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
/*
2+
* sbt
3+
* Copyright 2011 - 2018, Lightbend, Inc.
4+
* Copyright 2008 - 2010, Mark Harrah
5+
* Licensed under Apache License 2.0 (see LICENSE)
6+
*/
7+
8+
package sbt.internal;
9+
10+
import java.io.File;
11+
import java.lang.reflect.Constructor;
12+
import java.lang.reflect.InvocationTargetException;
13+
import java.lang.reflect.Method;
14+
import java.net.URL;
15+
import xsbti.*;
16+
17+
/**
18+
* Generates a new app configuration and invokes xMainImpl.run. For AppConfigurations generated by
19+
* recent launchers, it is unnecessary to modify the original configuration, but configurations
20+
* generated by older launchers need to be modified to place the test interface jar higher in the
21+
* class hierarchy. The methods this object are implemented without using the scala library so that
22+
* we can avoid loading any classes from the old scala provider.
23+
*/
24+
public class XMainConfiguration {
25+
public xsbti.MainResult run(String moduleName, xsbti.AppConfiguration configuration) {
26+
try {
27+
ClassLoader topLoader = configuration.provider().scalaProvider().launcher().topLoader();
28+
xsbti.AppConfiguration updatedConfiguration = null;
29+
try {
30+
Method method = topLoader.getClass().getMethod("getJLineJars");
31+
URL[] jars = (URL[]) method.invoke(topLoader);
32+
boolean canReuseConfiguration = jars.length == 3;
33+
int j = 0;
34+
while (j < jars.length && canReuseConfiguration) {
35+
String s = jars[j].toString();
36+
canReuseConfiguration = s.contains("jline") || s.contains("jansi");
37+
j += 1;
38+
}
39+
if (canReuseConfiguration && j == 3) {
40+
updatedConfiguration = configuration;
41+
} else {
42+
updatedConfiguration = makeConfiguration(configuration);
43+
}
44+
} catch (NoSuchMethodException e) {
45+
updatedConfiguration = makeConfiguration(configuration);
46+
}
47+
48+
ClassLoader loader = updatedConfiguration.provider().loader();
49+
Thread.currentThread().setContextClassLoader(loader);
50+
Class<?> clazz = loader.loadClass("sbt." + moduleName + "$");
51+
Object instance = clazz.getField("MODULE$").get(null);
52+
Method runMethod = clazz.getMethod("run", xsbti.AppConfiguration.class);
53+
try {
54+
Class<?> clw = loader.loadClass("sbt.internal.ClassLoaderWarmup$");
55+
clw.getMethod("warmup").invoke(clw.getField("MODULE$").get(null));
56+
return (xsbti.MainResult) runMethod.invoke(instance, updatedConfiguration);
57+
} catch (InvocationTargetException e) {
58+
// This propogates xsbti.FullReload to the launcher
59+
throw (xsbti.FullReload) e.getCause();
60+
}
61+
} catch (ReflectiveOperationException e) {
62+
throw new RuntimeException(e);
63+
}
64+
}
65+
66+
private xsbti.AppConfiguration makeConfiguration(xsbti.AppConfiguration configuration) {
67+
try {
68+
ClassLoader baseLoader = XMainConfiguration.class.getClassLoader();
69+
String className = "sbt/internal/XMainConfiguration.class";
70+
URL url = baseLoader.getResource(className);
71+
String path = url.toString().replaceAll(className.concat("$"), "");
72+
URL[] urlArray = new URL[1];
73+
urlArray[0] = new URL(path);
74+
ClassLoader topLoader = configuration.provider().scalaProvider().launcher().topLoader();
75+
// This loader doesn't have the scala library in it so it's critical that none of the code
76+
// in this file use the scala library.
77+
ClassLoader modifiedLoader = new XMainClassLoader(urlArray, topLoader);
78+
Class<?> xMainConfigurationClass =
79+
modifiedLoader.loadClass("sbt.internal.XMainConfiguration");
80+
Object instance = (Object) xMainConfigurationClass.getConstructor().newInstance();
81+
Class<?> metaBuildLoaderClass = modifiedLoader.loadClass("sbt.internal.MetaBuildLoader");
82+
Method method = metaBuildLoaderClass.getMethod("makeLoader", AppProvider.class);
83+
84+
ClassLoader loader = (ClassLoader) method.invoke(null, configuration.provider());
85+
86+
Thread.currentThread().setContextClassLoader(loader);
87+
Class<?> modifiedConfigurationClass =
88+
modifiedLoader.loadClass("sbt.internal.XMainConfiguration$ModifiedConfiguration");
89+
Constructor<?> cons = modifiedConfigurationClass.getConstructors()[0];
90+
ClassLoaderClose.close(configuration.provider().loader());
91+
ScalaProvider scalaProvider = configuration.provider().scalaProvider();
92+
Class<? extends ScalaProvider> providerClass = scalaProvider.getClass();
93+
try {
94+
Method method2 = providerClass.getMethod("loaderLibraryOnly");
95+
ClassLoaderClose.close((ClassLoader) method2.invoke(scalaProvider));
96+
} catch (NoSuchMethodException e) {
97+
}
98+
ClassLoaderClose.close(scalaProvider.loader());
99+
ClassLoaderClose.close(configuration.provider().loader());
100+
return (xsbti.AppConfiguration) cons.newInstance(instance, configuration, loader);
101+
} catch (Throwable e) {
102+
throw new RuntimeException(e);
103+
}
104+
}
105+
106+
/*
107+
* Replaces the AppProvider.loader method with a new loader that puts the sbt test interface
108+
* jar ahead of the rest of the sbt classpath in the classloading hierarchy.
109+
*/
110+
public class ModifiedConfiguration implements xsbti.AppConfiguration {
111+
private xsbti.AppConfiguration configuration;
112+
private ClassLoader metaLoader;
113+
114+
public ModifiedConfiguration(xsbti.AppConfiguration configuration, ClassLoader metaLoader) {
115+
this.configuration = configuration;
116+
this.metaLoader = metaLoader;
117+
}
118+
119+
public class ModifiedAppProvider implements AppProvider {
120+
private AppProvider appProvider;
121+
private ScalaProvider instance;
122+
123+
public ModifiedAppProvider(AppProvider appProvider) throws ClassNotFoundException {
124+
this.appProvider = appProvider;
125+
ScalaProvider delegate = configuration.provider().scalaProvider();
126+
this.instance =
127+
new ScalaProvider() {
128+
public Launcher _launcher =
129+
new Launcher() {
130+
private Launcher delegateLauncher = delegate.launcher();
131+
private ClassLoader interfaceLoader =
132+
metaLoader.loadClass("sbt.testing.Framework").getClassLoader();
133+
134+
@Override
135+
public ScalaProvider getScala(String version) {
136+
return getScala(version, "");
137+
}
138+
139+
@Override
140+
public ScalaProvider getScala(String version, String reason) {
141+
return getScala(version, reason, "org.scala-lang");
142+
}
143+
144+
@Override
145+
public ScalaProvider getScala(String version, String reason, String scalaOrg) {
146+
return delegateLauncher.getScala(version, reason, scalaOrg);
147+
}
148+
149+
@Override
150+
public AppProvider app(xsbti.ApplicationID id, String version) {
151+
return delegateLauncher.app(id, version);
152+
}
153+
154+
@Override
155+
public ClassLoader topLoader() {
156+
return interfaceLoader;
157+
}
158+
159+
@Override
160+
public GlobalLock globalLock() {
161+
return delegateLauncher.globalLock();
162+
}
163+
164+
@Override
165+
public File bootDirectory() {
166+
return delegateLauncher.bootDirectory();
167+
}
168+
169+
@Override
170+
public xsbti.Repository[] ivyRepositories() {
171+
return delegateLauncher.ivyRepositories();
172+
}
173+
174+
@Override
175+
public xsbti.Repository[] appRepositories() {
176+
return delegateLauncher.appRepositories();
177+
}
178+
179+
@Override
180+
public boolean isOverrideRepositories() {
181+
return delegateLauncher.isOverrideRepositories();
182+
}
183+
184+
@Override
185+
public File ivyHome() {
186+
return delegateLauncher.ivyHome();
187+
}
188+
189+
@Override
190+
public String[] checksums() {
191+
return delegateLauncher.checksums();
192+
}
193+
};
194+
195+
@Override
196+
public Launcher launcher() {
197+
return this._launcher;
198+
}
199+
200+
@Override
201+
public String version() {
202+
return delegate.version();
203+
}
204+
205+
@Override
206+
public ClassLoader loader() {
207+
return metaLoader.getParent();
208+
}
209+
210+
@Override
211+
public File[] jars() {
212+
return delegate.jars();
213+
}
214+
215+
@Override
216+
@Deprecated()
217+
public File libraryJar() {
218+
return delegate.libraryJar();
219+
}
220+
221+
@Override
222+
@Deprecated()
223+
public File compilerJar() {
224+
return delegate.compilerJar();
225+
}
226+
227+
@Override
228+
public AppProvider app(xsbti.ApplicationID id) {
229+
return delegate.app(id);
230+
}
231+
232+
private ClassLoader loaderLibraryOnly() {
233+
return metaLoader.getParent().getParent();
234+
}
235+
};
236+
}
237+
238+
@Override
239+
public ScalaProvider scalaProvider() {
240+
return instance;
241+
}
242+
243+
@Override
244+
public xsbti.ApplicationID id() {
245+
return appProvider.id();
246+
}
247+
248+
@Override
249+
public ClassLoader loader() {
250+
return metaLoader;
251+
}
252+
253+
@Override
254+
@Deprecated()
255+
public Class<? extends AppMain> mainClass() {
256+
return appProvider.mainClass();
257+
}
258+
259+
@Override
260+
public Class<?> entryPoint() {
261+
return appProvider.entryPoint();
262+
}
263+
264+
@Override
265+
public AppMain newMain() {
266+
return appProvider.newMain();
267+
}
268+
269+
@Override
270+
public File[] mainClasspath() {
271+
return appProvider.mainClasspath();
272+
}
273+
274+
@Override
275+
public ComponentProvider components() {
276+
return appProvider.components();
277+
}
278+
}
279+
280+
@Override
281+
public String[] arguments() {
282+
return configuration.arguments();
283+
}
284+
285+
@Override
286+
public File baseDirectory() {
287+
return configuration.baseDirectory();
288+
}
289+
290+
@Override
291+
public AppProvider provider() {
292+
try {
293+
return new ModifiedAppProvider(configuration.provider());
294+
} catch (Throwable e) {
295+
throw new RuntimeException(e);
296+
}
297+
}
298+
}
299+
}

0 commit comments

Comments
 (0)