Improve run config argument handling, should work with spaces a bit better. (#524)
parent
7359dc4e98
commit
61b5cfa733
|
@ -28,10 +28,11 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
@ -44,7 +45,6 @@ import org.w3c.dom.Node;
|
||||||
|
|
||||||
import net.fabricmc.loom.LoomGradleExtension;
|
import net.fabricmc.loom.LoomGradleExtension;
|
||||||
import net.fabricmc.loom.configuration.InstallerData;
|
import net.fabricmc.loom.configuration.InstallerData;
|
||||||
import net.fabricmc.loom.util.OperatingSystem;
|
|
||||||
|
|
||||||
public class RunConfig {
|
public class RunConfig {
|
||||||
public String configName;
|
public String configName;
|
||||||
|
@ -53,8 +53,8 @@ public class RunConfig {
|
||||||
public String mainClass;
|
public String mainClass;
|
||||||
public String runDirIdeaUrl;
|
public String runDirIdeaUrl;
|
||||||
public String runDir;
|
public String runDir;
|
||||||
public String vmArgs;
|
public List<String> vmArgs = new ArrayList<>();
|
||||||
public String programArgs;
|
public List<String> programArgs = new ArrayList<>();
|
||||||
public SourceSet sourceSet;
|
public SourceSet sourceSet;
|
||||||
|
|
||||||
public Element genRuns(Element doc) {
|
public Element genRuns(Element doc) {
|
||||||
|
@ -65,12 +65,12 @@ public class RunConfig {
|
||||||
this.addXml(root, "option", ImmutableMap.of("name", "MAIN_CLASS_NAME", "value", mainClass));
|
this.addXml(root, "option", ImmutableMap.of("name", "MAIN_CLASS_NAME", "value", mainClass));
|
||||||
this.addXml(root, "option", ImmutableMap.of("name", "WORKING_DIRECTORY", "value", runDirIdeaUrl));
|
this.addXml(root, "option", ImmutableMap.of("name", "WORKING_DIRECTORY", "value", runDirIdeaUrl));
|
||||||
|
|
||||||
if (!Strings.isNullOrEmpty(vmArgs)) {
|
if (!vmArgs.isEmpty()) {
|
||||||
this.addXml(root, "option", ImmutableMap.of("name", "VM_PARAMETERS", "value", vmArgs));
|
this.addXml(root, "option", ImmutableMap.of("name", "VM_PARAMETERS", "value", joinArguments(vmArgs)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Strings.isNullOrEmpty(programArgs)) {
|
if (!programArgs.isEmpty()) {
|
||||||
this.addXml(root, "option", ImmutableMap.of("name", "PROGRAM_PARAMETERS", "value", programArgs));
|
this.addXml(root, "option", ImmutableMap.of("name", "PROGRAM_PARAMETERS", "value", joinArguments(programArgs)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
|
@ -106,11 +106,10 @@ public class RunConfig {
|
||||||
private static void populate(Project project, LoomGradleExtension extension, RunConfig runConfig, String environment) {
|
private static void populate(Project project, LoomGradleExtension extension, RunConfig runConfig, String environment) {
|
||||||
runConfig.configName += extension.isRootProject() ? "" : " (" + project.getPath() + ")";
|
runConfig.configName += extension.isRootProject() ? "" : " (" + project.getPath() + ")";
|
||||||
runConfig.eclipseProjectName = project.getExtensions().getByType(EclipseModel.class).getProject().getName();
|
runConfig.eclipseProjectName = project.getExtensions().getByType(EclipseModel.class).getProject().getName();
|
||||||
runConfig.vmArgs = "";
|
|
||||||
runConfig.programArgs = "";
|
|
||||||
|
|
||||||
runConfig.mainClass = "net.fabricmc.devlaunchinjector.Main";
|
runConfig.mainClass = "net.fabricmc.devlaunchinjector.Main";
|
||||||
runConfig.vmArgs = "-Dfabric.dli.config=" + encodeEscaped(extension.getFiles().getDevLauncherConfig().getAbsolutePath()) + " -Dfabric.dli.env=" + environment.toLowerCase();
|
runConfig.vmArgs.add("-Dfabric.dli.config=" + encodeEscaped(extension.getFiles().getDevLauncherConfig().getAbsolutePath()));
|
||||||
|
runConfig.vmArgs.add("-Dfabric.dli.env=" + environment.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turns camelCase/PascalCase into Capital Case
|
// Turns camelCase/PascalCase into Capital Case
|
||||||
|
@ -165,19 +164,9 @@ public class RunConfig {
|
||||||
runConfig.sourceSet = sourceSet;
|
runConfig.sourceSet = sourceSet;
|
||||||
|
|
||||||
// Custom parameters
|
// Custom parameters
|
||||||
for (String progArg : settings.getProgramArgs()) {
|
runConfig.programArgs.addAll(settings.getProgramArgs());
|
||||||
runConfig.programArgs += " " + progArg;
|
runConfig.vmArgs.addAll(settings.getVmArgs());
|
||||||
}
|
runConfig.vmArgs.add("-Dfabric.dli.main=" + getMainClass(environment, extension, defaultMain));
|
||||||
|
|
||||||
for (String vmArg : settings.getVmArgs()) {
|
|
||||||
runConfig.vmArgs += " " + vmArg;
|
|
||||||
}
|
|
||||||
|
|
||||||
runConfig.vmArgs += " -Dfabric.dli.main=" + getMainClass(environment, extension, defaultMain);
|
|
||||||
|
|
||||||
// Remove unnecessary leading/trailing whitespaces we might have generated
|
|
||||||
runConfig.programArgs = runConfig.programArgs.trim();
|
|
||||||
runConfig.vmArgs = runConfig.vmArgs.trim();
|
|
||||||
|
|
||||||
return runConfig;
|
return runConfig;
|
||||||
}
|
}
|
||||||
|
@ -204,18 +193,26 @@ public class RunConfig {
|
||||||
dummyConfig = dummyConfig.replace("%ECLIPSE_PROJECT%", eclipseProjectName);
|
dummyConfig = dummyConfig.replace("%ECLIPSE_PROJECT%", eclipseProjectName);
|
||||||
dummyConfig = dummyConfig.replace("%IDEA_MODULE%", ideaModuleName);
|
dummyConfig = dummyConfig.replace("%IDEA_MODULE%", ideaModuleName);
|
||||||
dummyConfig = dummyConfig.replace("%RUN_DIRECTORY%", runDir);
|
dummyConfig = dummyConfig.replace("%RUN_DIRECTORY%", runDir);
|
||||||
dummyConfig = dummyConfig.replace("%PROGRAM_ARGS%", programArgs.replaceAll("\"", """));
|
dummyConfig = dummyConfig.replace("%PROGRAM_ARGS%", joinArguments(programArgs).replaceAll("\"", """));
|
||||||
dummyConfig = dummyConfig.replace("%VM_ARGS%", vmArgs.replaceAll("\"", """));
|
dummyConfig = dummyConfig.replace("%VM_ARGS%", joinArguments(vmArgs).replaceAll("\"", """));
|
||||||
|
|
||||||
return dummyConfig;
|
return dummyConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getOSClientJVMArgs() {
|
public static String joinArguments(List<String> args) {
|
||||||
if (OperatingSystem.getOS().equalsIgnoreCase("osx")) {
|
final var sb = new StringBuilder();
|
||||||
return " -XstartOnFirstThread";
|
boolean first = true;
|
||||||
|
|
||||||
|
for (String arg : args) {
|
||||||
|
if (!first) {
|
||||||
|
sb.append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
first = false;
|
||||||
|
sb.append("\"").append(arg).append("\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getMainClass(String side, LoomGradleExtension extension, String defaultMainClass) {
|
private static String getMainClass(String side, LoomGradleExtension extension, String defaultMainClass) {
|
||||||
|
|
|
@ -26,7 +26,6 @@ package net.fabricmc.loom.task;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
@ -46,39 +45,11 @@ public abstract class AbstractRunTask extends JavaExec {
|
||||||
this.config = configProvider.apply(getProject());
|
this.config = configProvider.apply(getProject());
|
||||||
|
|
||||||
setClasspath(config.sourceSet.getRuntimeClasspath());
|
setClasspath(config.sourceSet.getRuntimeClasspath());
|
||||||
|
args(config.programArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exec() {
|
public void exec() {
|
||||||
List<String> argsSplit = new ArrayList<>();
|
|
||||||
String[] args = config.programArgs.split(" ");
|
|
||||||
int partPos = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
|
||||||
if (partPos < 0) {
|
|
||||||
if (args[i].startsWith("\"")) {
|
|
||||||
if (args[i].endsWith("\"")) {
|
|
||||||
argsSplit.add(args[i].substring(1, args[i].length() - 1));
|
|
||||||
} else {
|
|
||||||
partPos = i;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
argsSplit.add(args[i]);
|
|
||||||
}
|
|
||||||
} else if (args[i].endsWith("\"")) {
|
|
||||||
StringBuilder builder = new StringBuilder(args[partPos].substring(1));
|
|
||||||
|
|
||||||
for (int j = partPos + 1; j < i; j++) {
|
|
||||||
builder.append(" ").append(args[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.append(" ").append(args[i], 0, args[i].length() - 1);
|
|
||||||
argsSplit.add(builder.toString());
|
|
||||||
partPos = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args(argsSplit);
|
|
||||||
setWorkingDir(new File(getProject().getRootDir(), config.runDir));
|
setWorkingDir(new File(getProject().getRootDir(), config.runDir));
|
||||||
|
|
||||||
super.exec();
|
super.exec();
|
||||||
|
@ -102,7 +73,7 @@ public abstract class AbstractRunTask extends JavaExec {
|
||||||
public List<String> getJvmArgs() {
|
public List<String> getJvmArgs() {
|
||||||
List<String> superArgs = super.getJvmArgs();
|
List<String> superArgs = super.getJvmArgs();
|
||||||
List<String> args = new ArrayList<>(superArgs != null ? superArgs : Collections.emptyList());
|
List<String> args = new ArrayList<>(superArgs != null ? superArgs : Collections.emptyList());
|
||||||
args.addAll(Arrays.asList(config.vmArgs.split(" ")));
|
args.addAll(config.vmArgs);
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,8 +103,8 @@ public class GenVsCodeProjectTask extends AbstractLoomTask {
|
||||||
VsCodeConfiguration(RunConfig runConfig) {
|
VsCodeConfiguration(RunConfig runConfig) {
|
||||||
this.name = runConfig.configName;
|
this.name = runConfig.configName;
|
||||||
this.mainClass = runConfig.mainClass;
|
this.mainClass = runConfig.mainClass;
|
||||||
this.vmArgs = runConfig.vmArgs;
|
this.vmArgs = RunConfig.joinArguments(runConfig.vmArgs);
|
||||||
this.args = runConfig.programArgs;
|
this.args = RunConfig.joinArguments(runConfig.programArgs);
|
||||||
this.cwd = "${workspaceFolder}/" + runConfig.runDir;
|
this.cwd = "${workspaceFolder}/" + runConfig.runDir;
|
||||||
|
|
||||||
if (getProject().getRootProject() != getProject()) {
|
if (getProject().getRootProject() != getProject()) {
|
||||||
|
|
|
@ -42,6 +42,7 @@ class RunConfigTest extends Specification implements GradleProjectTestTrait {
|
||||||
|
|
||||||
then:
|
then:
|
||||||
result.task(":${task}").outcome == SUCCESS
|
result.task(":${task}").outcome == SUCCESS
|
||||||
|
result.output.contains("This contains a space")
|
||||||
|
|
||||||
where:
|
where:
|
||||||
task | _
|
task | _
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 FabricMC
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.fabricmc.loom.test.unit
|
||||||
|
|
||||||
|
import net.fabricmc.loom.configuration.ide.RunConfig
|
||||||
|
import spock.lang.Specification
|
||||||
|
|
||||||
|
class RunConfigUnitTest extends Specification {
|
||||||
|
def "escape arguments"() {
|
||||||
|
when:
|
||||||
|
def args = RunConfig.joinArguments(["-Dfabric.test=123", "-Dfabric.test=abc 123"])
|
||||||
|
|
||||||
|
then:
|
||||||
|
args == '"-Dfabric.test=123" "-Dfabric.test=abc 123"'
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,10 @@ loom {
|
||||||
vmArg "-Dfabric.autoTest"
|
vmArg "-Dfabric.autoTest"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runConfigs.configureEach {
|
||||||
|
vmArg "-Dfabric.loom.test.space=This contains a space"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
archivesBaseName = "fabric-example-mod"
|
archivesBaseName = "fabric-example-mod"
|
||||||
|
|
|
@ -7,6 +7,8 @@ public class ExampleMod implements ModInitializer {
|
||||||
public void onInitialize() {
|
public void onInitialize() {
|
||||||
System.out.println("Hello Fabric world!");
|
System.out.println("Hello Fabric world!");
|
||||||
|
|
||||||
|
System.out.println(System.getProperty("fabric.loom.test.space"));
|
||||||
|
|
||||||
// Quit now, we dont need to load the whole game to know the run configs are works
|
// Quit now, we dont need to load the whole game to know the run configs are works
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue