Skip to content

Commit c5779b6

Browse files
robin4002LexManos
authored andcommitted
Add support for VSCode run configs (#589)
1 parent 50821de commit c5779b6

File tree

1 file changed

+175
-79
lines changed
  • src/common/java/net/minecraftforge/gradle/common/util

1 file changed

+175
-79
lines changed

src/common/java/net/minecraftforge/gradle/common/util/IDEUtils.java

Lines changed: 175 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222

2323
import com.google.common.collect.ImmutableList;
2424
import com.google.common.collect.ImmutableMap;
25+
import com.google.gson.Gson;
26+
import com.google.gson.GsonBuilder;
27+
import com.google.gson.JsonArray;
28+
import com.google.gson.JsonObject;
29+
2530
import org.apache.commons.lang3.tuple.ImmutableTriple;
2631
import org.apache.commons.lang3.tuple.Triple;
2732
import org.gradle.api.Project;
@@ -46,6 +51,9 @@
4651
import javax.xml.transform.dom.DOMSource;
4752
import javax.xml.transform.stream.StreamResult;
4853
import java.io.File;
54+
import java.io.FileWriter;
55+
import java.io.IOException;
56+
import java.io.Writer;
4957
import java.util.Collections;
5058
import java.util.LinkedHashMap;
5159
import java.util.List;
@@ -60,11 +68,13 @@ public final class IDEUtils {
6068
public static void createIDEGenRunsTasks(@Nonnull final MinecraftExtension minecraft, @Nonnull final TaskProvider<Task> prepareRuns, @Nonnull final TaskProvider<Task> makeSourceDirs) {
6169
final Project project = minecraft.getProject();
6270

63-
final Map<String, Triple<List<Object>, File, RunConfigurationGenerator>> ideConfigurationGenerators = ImmutableMap.<String, Triple<List<Object>, File, RunConfigurationGenerator>>builder()
71+
final Map<String, Triple<List<Object>, File, RunConfigurationBuilder>> ideConfigurationGenerators = ImmutableMap.<String, Triple<List<Object>, File, RunConfigurationBuilder>>builder()
6472
.put("genIntellijRuns", ImmutableTriple.of(Collections.singletonList(prepareRuns.get()),
65-
new File(project.getRootProject().getRootDir(), ".idea/runConfigurations"), IDEUtils::createIntellijRunConfigurationXML))
73+
new File(project.getRootProject().getRootDir(), ".idea/runConfigurations"), new XMLConfigurationBuilder(IDEUtils::createIntellijRunConfigurationXML)))
6674
.put("genEclipseRuns", ImmutableTriple.of(ImmutableList.of(prepareRuns.get(), makeSourceDirs.get()),
67-
project.getProjectDir(), IDEUtils::createEclipseRunConfigurationXML))
75+
project.getProjectDir(), new XMLConfigurationBuilder(IDEUtils::createEclipseRunConfigurationXML)))
76+
.put("genVSCodeRuns", ImmutableTriple.of(ImmutableList.of(prepareRuns.get(), makeSourceDirs.get()),
77+
new File(project.getProjectDir(), ".vscode"), new JsonConfigurationBuilder(IDEUtils::createVSCodeRunConfiguration)))
6878
.build();
6979

7080
ideConfigurationGenerators.forEach((taskName, configurationGenerator) -> {
@@ -73,40 +83,12 @@ public static void createIDEGenRunsTasks(@Nonnull final MinecraftExtension minec
7383
task.dependsOn(configurationGenerator.getLeft());
7484

7585
task.doLast(t -> {
76-
try {
77-
final File runConfigurationsDir = configurationGenerator.getMiddle();
78-
79-
if (!runConfigurationsDir.exists()) {
80-
runConfigurationsDir.mkdirs();
81-
}
86+
final File runConfigurationsDir = configurationGenerator.getMiddle();
8287

83-
final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
84-
final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
85-
final TransformerFactory transformerFactory = TransformerFactory.newInstance();
86-
final Transformer transformer = transformerFactory.newTransformer();
87-
88-
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
89-
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
90-
91-
minecraft.getRuns().forEach(runConfig -> {
92-
final Stream<String> propStream = runConfig.getProperties().entrySet().stream().map(kv -> String.format("-D%s=%s", kv.getKey(), kv.getValue()));
93-
final String props = Stream.concat(propStream, runConfig.getJvmArgs().stream()).collect(Collectors.joining(" "));
94-
final Map<String, Document> documents = configurationGenerator.getRight().createRunConfigurationXML(project, runConfig, props, docBuilder);
95-
96-
documents.forEach((fileName, document) -> {
97-
final DOMSource source = new DOMSource(document);
98-
final StreamResult result = new StreamResult(new File(runConfigurationsDir, fileName));
99-
100-
try {
101-
transformer.transform(source, result);
102-
} catch (TransformerException e) {
103-
e.printStackTrace();
104-
}
105-
});
106-
});
107-
} catch (ParserConfigurationException | TransformerConfigurationException e) {
108-
e.printStackTrace();
88+
if (!runConfigurationsDir.exists()) {
89+
runConfigurationsDir.mkdirs();
10990
}
91+
configurationGenerator.getRight().createConfig(minecraft, runConfigurationsDir, project);
11092
});
11193
});
11294
});
@@ -231,49 +213,7 @@ private static Map<String, Document> createEclipseRunConfigurationXML(@Nonnull f
231213
{
232214
envs.setAttribute("key", "org.eclipse.debug.core.environmentVariables");
233215

234-
runConfig.getEnvironment().compute("MOD_CLASSES", (key, value) -> {
235-
// Only replace environment variable if it is already set
236-
if (value == null || value.isEmpty()) {
237-
return value;
238-
}
239-
240-
final EclipseModel eclipse = project.getExtensions().findByType(EclipseModel.class);
241-
242-
if (eclipse != null) {
243-
final Map<String, String> outputs = eclipse.getClasspath().resolveDependencies().stream()
244-
.filter(SourceFolder.class::isInstance)
245-
.map(SourceFolder.class::cast)
246-
.map(SourceFolder::getOutput)
247-
.distinct()
248-
.collect(Collectors.toMap(output -> output.split("/")[output.split("/").length - 1], output -> project.file(output).getAbsolutePath()));
249-
250-
if (runConfig.getMods().isEmpty()) {
251-
return runConfig.getAllSources().stream()
252-
.map(SourceSet::getName)
253-
.filter(outputs::containsKey)
254-
.map(outputs::get)
255-
.map(s -> String.join(File.pathSeparator, s, s)) // <resources>:<classes>
256-
.collect(Collectors.joining(File.pathSeparator));
257-
} else {
258-
final SourceSet main = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
259-
260-
return runConfig.getMods().stream()
261-
.map(modConfig -> {
262-
return (modConfig.getSources().isEmpty() ? Stream.of(main) : modConfig.getSources().stream())
263-
.map(SourceSet::getName)
264-
.filter(outputs::containsKey)
265-
.map(outputs::get)
266-
.map(output -> modConfig.getName() + "%%" + output)
267-
.map(s -> String.join(File.pathSeparator, s, s)); // <resources>:<classes>
268-
})
269-
.flatMap(Function.identity())
270-
.collect(Collectors.joining(File.pathSeparator));
271-
}
272-
}
273-
274-
return value;
275-
});
276-
216+
runConfig.getEnvironment().compute("MOD_CLASSES", (key, value) -> mapModClasses(key, value, project, runConfig));
277217
runConfig.getEnvironment().forEach((name, value) -> {
278218
final Element envEntry = javaDocument.createElement("mapEntry");
279219
{
@@ -292,12 +232,168 @@ private static Map<String, Document> createEclipseRunConfigurationXML(@Nonnull f
292232
return documents;
293233
}
294234

235+
@Nonnull
236+
private static JsonObject createVSCodeRunConfiguration(@Nonnull final Project project, @Nonnull final RunConfig runConfig, @Nonnull final String props) {
237+
JsonObject config = new JsonObject();
238+
config.addProperty("type", "java");
239+
config.addProperty("name", runConfig.getTaskName());
240+
config.addProperty("request", "launch");
241+
config.addProperty("mainClass", runConfig.getMain());
242+
config.addProperty("projectName", project.getName());
243+
config.addProperty("cwd", replaceRootDirBy(project, runConfig.getWorkingDirectory().toString(), "${workspaceFolder}"));
244+
config.addProperty("vmArgs", props);
245+
config.addProperty("args", String.join(" ", runConfig.getArgs()));
246+
JsonObject env = new JsonObject();
247+
runConfig.getEnvironment().compute("MOD_CLASSES", (key, value) -> replaceRootDirBy(project, mapModClasses(key, value, project, runConfig), "${workspaceFolder}"));
248+
runConfig.getEnvironment().compute("nativesDirectory", (key, value) -> replaceRootDirBy(project, value, "${workspaceFolder}"));
249+
runConfig.getEnvironment().forEach((name, value) -> {
250+
env.addProperty(name, value);
251+
});
252+
config.add("env", env);
253+
return config;
254+
}
255+
256+
private static String replaceRootDirBy(@Nonnull final Project project, String value, @Nonnull final String replacement) {
257+
if (value == null || value.isEmpty()) {
258+
return value;
259+
}
260+
return value.replace(project.getRootDir().toString(), replacement);
261+
}
262+
263+
private static String mapModClasses(String key, String value, @Nonnull final Project project, @Nonnull final RunConfig runConfig) {
264+
// Only replace environment variable if it is already set
265+
if (value == null || value.isEmpty()) {
266+
return value;
267+
}
268+
269+
final EclipseModel eclipse = project.getExtensions().findByType(EclipseModel.class);
270+
271+
if (eclipse != null) {
272+
final Map<String, String> outputs = eclipse.getClasspath().resolveDependencies().stream()
273+
.filter(SourceFolder.class::isInstance)
274+
.map(SourceFolder.class::cast)
275+
.map(SourceFolder::getOutput)
276+
.distinct()
277+
.collect(Collectors.toMap(output -> output.split("/")[output.split("/").length - 1], output -> project.file(output).getAbsolutePath()));
278+
279+
if (runConfig.getMods().isEmpty()) {
280+
return runConfig.getAllSources().stream()
281+
.map(SourceSet::getName)
282+
.filter(outputs::containsKey)
283+
.map(outputs::get)
284+
.map(s -> String.join(File.pathSeparator, s, s)) // <resources>:<classes>
285+
.collect(Collectors.joining(File.pathSeparator));
286+
} else {
287+
final SourceSet main = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
288+
289+
return runConfig.getMods().stream()
290+
.map(modConfig -> {
291+
return (modConfig.getSources().isEmpty() ? Stream.of(main) : modConfig.getSources().stream())
292+
.map(SourceSet::getName)
293+
.filter(outputs::containsKey)
294+
.map(outputs::get)
295+
.map(output -> modConfig.getName() + "%%" + output)
296+
.map(s -> String.join(File.pathSeparator, s, s)); // <resources>:<classes>
297+
})
298+
.flatMap(Function.identity())
299+
.collect(Collectors.joining(File.pathSeparator));
300+
}
301+
}
302+
303+
return value;
304+
}
305+
295306
@FunctionalInterface
296-
private interface RunConfigurationGenerator {
307+
private interface XMLRunConfigurationGenerator {
297308

298309
@Nonnull
299310
Map<String, Document> createRunConfigurationXML(@Nonnull final Project project, @Nonnull final RunConfig runConfig, @Nonnull final String props, @Nonnull final DocumentBuilder documentBuilder);
300311

301312
}
302313

314+
@FunctionalInterface
315+
private interface JsonRunConfigurationGenerator {
316+
317+
@Nonnull
318+
JsonObject createRunConfigurationJson(@Nonnull final Project project, @Nonnull final RunConfig runConfig, @Nonnull final String props);
319+
320+
}
321+
322+
private interface RunConfigurationBuilder {
323+
324+
void createConfig(@Nonnull final MinecraftExtension minecraft, @Nonnull final File runConfigurationsDir, @Nonnull final Project project);
325+
326+
}
327+
328+
private static class XMLConfigurationBuilder implements RunConfigurationBuilder {
329+
330+
private XMLRunConfigurationGenerator configGenerator;
331+
332+
public XMLConfigurationBuilder(XMLRunConfigurationGenerator generator) {
333+
configGenerator = generator;
334+
}
335+
336+
@Override
337+
public void createConfig(@Nonnull final MinecraftExtension minecraft, @Nonnull final File runConfigurationsDir, @Nonnull final Project project) {
338+
try {
339+
final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
340+
final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
341+
final TransformerFactory transformerFactory = TransformerFactory.newInstance();
342+
final Transformer transformer = transformerFactory.newTransformer();
343+
344+
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
345+
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
346+
347+
minecraft.getRuns().forEach(runConfig -> {
348+
final Stream<String> propStream = runConfig.getProperties().entrySet().stream().map(kv -> String.format("-D%s=%s", kv.getKey(), kv.getValue()));
349+
final String props = Stream.concat(propStream, runConfig.getJvmArgs().stream()).collect(Collectors.joining(" "));
350+
final Map<String, Document> documents = configGenerator.createRunConfigurationXML(project, runConfig, props, docBuilder);
351+
352+
documents.forEach((fileName, document) -> {
353+
final DOMSource source = new DOMSource(document);
354+
final StreamResult result = new StreamResult(new File(runConfigurationsDir, fileName));
355+
356+
try {
357+
transformer.transform(source, result);
358+
} catch (TransformerException e) {
359+
e.printStackTrace();
360+
}
361+
});
362+
});
363+
} catch (ParserConfigurationException | TransformerConfigurationException e) {
364+
e.printStackTrace();
365+
}
366+
}
367+
}
368+
369+
private static class JsonConfigurationBuilder implements RunConfigurationBuilder {
370+
371+
private JsonRunConfigurationGenerator configGenerator;
372+
373+
public JsonConfigurationBuilder(JsonRunConfigurationGenerator generator) {
374+
configGenerator = generator;
375+
}
376+
377+
@Override
378+
public void createConfig(@Nonnull final MinecraftExtension minecraft, @Nonnull final File runConfigurationsDir, @Nonnull final Project project) {
379+
final JsonObject rootObject = new JsonObject();
380+
rootObject.addProperty("version", "0.2.0");
381+
JsonArray runConfigs = new JsonArray();
382+
minecraft.getRuns().forEach(runConfig -> {
383+
final Stream<String> propStream = runConfig.getProperties().entrySet().stream().map(kv -> String.format("-D%s=%s", kv.getKey(), kv.getValue()));
384+
final String props = Stream.concat(propStream, runConfig.getJvmArgs().stream()).collect(Collectors.joining(" "));
385+
runConfigs.add(configGenerator.createRunConfigurationJson(project, runConfig, props));
386+
});
387+
rootObject.add("configurations", runConfigs);
388+
Writer writer;
389+
try {
390+
writer = new FileWriter(new File(runConfigurationsDir, "launch.json"));
391+
Gson gson = new GsonBuilder().setPrettyPrinting().create();
392+
writer.write(gson.toJson(rootObject));
393+
writer.close();
394+
} catch (IOException e) {
395+
e.printStackTrace();
396+
}
397+
}
398+
}
303399
}

0 commit comments

Comments
 (0)