net.minecraftforge.gradle.mcp.function.ExecuteFunction Maven / Gradle / Ivy
/*
* ForgeGradle
* Copyright (C) 2018 Forge Development LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
package net.minecraftforge.gradle.mcp.function;
import net.minecraftforge.gradle.common.util.HashStore;
import net.minecraftforge.gradle.common.util.Utils;
import net.minecraftforge.gradle.mcp.util.MCPEnvironment;
import org.gradle.jvm.toolchain.JavaToolchainService;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
class ExecuteFunction implements MCPFunction {
private static final Pattern REPLACE_PATTERN = Pattern.compile("^\\{(\\w+)\\}$");
protected final File jar;
protected final String[] jvmArgs;
protected String[] runArgs;
protected final Map envVars;
private Map data;
public ExecuteFunction(File jar, String[] jvmArgs, String[] runArgs, Map envVars) {
this.jar = jar;
this.jvmArgs = jvmArgs;
this.runArgs = runArgs;
this.envVars = envVars;
}
@Override
public void loadData(Map data) {
this.data = data;
}
@Override
public void initialize(MCPEnvironment environment, ZipFile zip) throws IOException {
analyzeAndExtract(environment, zip, jvmArgs);
analyzeAndExtract(environment, zip, runArgs);
}
@Override
public File execute(MCPEnvironment environment) throws IOException, InterruptedException, ExecutionException {
// Add an output and log argument if there wasn't one
Map arguments = environment.getArguments();
String outputExtension = (String)arguments.getOrDefault("outputExtension", "jar");
arguments.computeIfAbsent("output", k -> environment.getFile("output." + outputExtension));
arguments.computeIfAbsent("log", k -> environment.getFile("log.log"));
// Get input and output files
File output = (File)environment.getArguments().get("output");
// Find out what the inputs are
Map replacedArgs = new HashMap<>();
List jvmArgList = applyVariableSubstitutions(environment, Arrays.asList(jvmArgs), arguments, replacedArgs);
List runArgList = applyVariableSubstitutions(environment, Arrays.asList(runArgs), arguments, replacedArgs);
replacedArgs.remove("output");
replacedArgs.remove("log");
HashStore hashStore = new HashStore(environment.project).load(environment.getFile("lastinput.sha1"));
hashStore.add("args", String.join(" ", runArgs));
hashStore.add("jvmargs", String.join(" ", jvmArgs));
hashStore.add("jar", jar);
replacedArgs.forEach((key, value) -> {
if (value instanceof File) {
hashStore.add(key, (File)value);
} else if (value instanceof String) {
hashStore.add(key, (String)value);
}
});
addInputs(hashStore);
if (hashStore.isSame() && output.exists()) return output;
// Delete previous output
if (output.exists()) output.delete();
// Set up working directory
File workingDir = environment.getWorkingDir();
workingDir.mkdirs();
// Locate main class in jar file
JarFile jarFile = new JarFile(jar);
String mainClass = jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
jarFile.close();
// Do not implicitly use the Java version that Gradle itself is using.
// Instead use a launcher compatible with the version required by MCP.
JavaToolchainService toolchainService = environment.project.getExtensions().getByType(JavaToolchainService.class);
String launcher = toolchainService.launcherFor(spec -> spec.getLanguageVersion().set(environment.getJavaVersion()))
.get()
.getExecutablePath()
.getAsFile()
.getAbsolutePath();
// Execute command
try (BufferedOutputStream log_out = new BufferedOutputStream(new FileOutputStream(environment.getFile("console.log")))) {
environment.project.javaexec(java -> {
PrintWriter writer = new PrintWriter(log_out);
Function quote = s -> '"' + s + '"';
writer.println("JVM: " + launcher);
writer.println("JVM Args: " + jvmArgList.stream().map(quote).collect(Collectors.joining(", ")));
writer.println("Run Args: " + runArgList.stream().map(quote).collect(Collectors.joining(", ")));
writer.println("Classpath: " + jar.getAbsolutePath());
writer.println("Working Dir: " + workingDir.getAbsolutePath());
writer.println("Main Class: " + mainClass);
writer.flush();
java.executable(launcher);
java.setJvmArgs(jvmArgList);
java.setArgs(runArgList);
java.setClasspath(environment.project.files(jar));
java.setWorkingDir(workingDir);
java.getMainClass().set(mainClass);
java.setStandardOutput(log_out);
}).rethrowFailure().assertNormalExitValue();
}
// Return the output file
hashStore.save();
return output;
}
private List applyVariableSubstitutions(MCPEnvironment environment, List list, Map arguments, Map inputs) {
return list.stream().map(s -> applyVariableSubstitutions(environment, s, arguments, inputs)).collect(Collectors.toList());
}
private String applyVariableSubstitutions(MCPEnvironment environment, String value, Map arguments, Map inputs) {
Matcher matcher = REPLACE_PATTERN.matcher(value);
if (!matcher.find()) return value; // Not a replaceable string
String argName = matcher.group(1);
if (argName != null) {
Object argument = arguments.get(argName);
if (argument instanceof File) {
inputs.put(argName, argument);
return ((File)argument).getAbsolutePath();
} else if (argument instanceof String) {
inputs.put(argName, argument);
return (String)argument;
}
String dataElement = data.get(argName);
if (dataElement != null) {
inputs.put(argName, environment.getFile(dataElement));
return dataElement;
}
}
throw new IllegalStateException("The string '" + value + "' did not return a valid substitution match!");
}
private void analyzeAndExtract(MCPEnvironment environment, ZipFile zip, String[] args) throws IOException {
for (String arg : args) {
Matcher matcher = REPLACE_PATTERN.matcher(arg);
if (!matcher.find()) continue;
String argName = matcher.group(1);
if (argName == null) continue;
String referencedData = data.get(argName);
if (referencedData == null) continue;
ZipEntry entry = zip.getEntry(referencedData);
if (entry == null) continue;
String entryName = entry.getName();
if (entry.isDirectory()) {
Utils.extractDirectory(environment::getFile, zip, entryName);
} else {
Utils.extractFile(zip, entry, environment.getFile(entryName));
}
}
}
protected void addInputs(HashStore cache) {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy