Improve run config argument handling, should work with spaces a bit better. (#524)
This commit is contained in:
		
							parent
							
								
									7359dc4e98
								
							
						
					
					
						commit
						61b5cfa733
					
				
					 7 changed files with 76 additions and 63 deletions
				
			
		|  | @ -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 a new issue