Fix setup errors + Multi-thread forge patching more.

This commit is contained in:
shedaniel
2021-01-11 14:36:15 +08:00
parent 5317263e58
commit fed9b0a4e7
4 changed files with 239 additions and 78 deletions

View File

@@ -57,10 +57,9 @@ import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.Arrays;
import java.util.Collections;
import java.util.Locale;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -220,8 +219,11 @@ public class MinecraftPatchedProvider extends DependencyProvider {
File specialSourceJar = new File(getExtension().getUserCache(), "SpecialSource-1.8.3-shaded.jar");
DownloadUtil.downloadIfChanged(new URL("https://repo1.maven.org/maven2/net/md-5/SpecialSource/1.8.3/SpecialSource-1.8.3-shaded.jar"), specialSourceJar, getProject().getLogger(), true);
Files.copy(SpecialSourceExecutor.produceSrgJar(getProject(), specialSourceJar,minecraftProvider.minecraftClientJar.toPath(), tmpSrg[0]), minecraftClientSrgJar.toPath());
Files.copy(SpecialSourceExecutor.produceSrgJar(getProject(), specialSourceJar, minecraftProvider.minecraftServerJar.toPath(), tmpSrg[0]), minecraftServerSrgJar.toPath());
ThreadingUtils.run(() -> {
Files.copy(SpecialSourceExecutor.produceSrgJar(getProject(), specialSourceJar, minecraftProvider.minecraftClientJar.toPath(), tmpSrg[0]), minecraftClientSrgJar.toPath());
}, () -> {
Files.copy(SpecialSourceExecutor.produceSrgJar(getProject(), specialSourceJar, minecraftProvider.minecraftServerJar.toPath(), tmpSrg[0]), minecraftServerSrgJar.toPath());
});
}
private void fixParameterAnnotation(File jarFile) throws Exception {
@@ -250,11 +252,13 @@ public class MinecraftPatchedProvider extends DependencyProvider {
private void injectForgeClasses(Logger logger) throws IOException {
logger.lifecycle(":injecting forge classes into minecraft");
copyAll(getExtension().getForgeUniversalProvider().getForge(), minecraftClientPatchedSrgJar);
copyAll(getExtension().getForgeUniversalProvider().getForge(), minecraftServerPatchedSrgJar);
copyUserdevFiles(getExtension().getForgeUserdevProvider().getUserdevJar(), minecraftClientPatchedSrgJar);
copyUserdevFiles(getExtension().getForgeUserdevProvider().getUserdevJar(), minecraftServerPatchedSrgJar);
ThreadingUtils.run(() -> {
copyAll(getExtension().getForgeUniversalProvider().getForge(), minecraftClientPatchedSrgJar);
copyUserdevFiles(getExtension().getForgeUserdevProvider().getUserdevJar(), minecraftClientPatchedSrgJar);
}, () -> {
copyAll(getExtension().getForgeUniversalProvider().getForge(), minecraftServerPatchedSrgJar);
copyUserdevFiles(getExtension().getForgeUserdevProvider().getUserdevJar(), minecraftServerPatchedSrgJar);
});
logger.lifecycle(":injecting loom classes into minecraft");
File injection = File.createTempFile("loom-injection", ".jar");
@@ -263,13 +267,12 @@ public class MinecraftPatchedProvider extends DependencyProvider {
FileUtils.copyInputStreamToFile(in, injection);
}
walkFileSystems(injection, minecraftClientPatchedSrgJar, it -> !it.getFileName().toString().equals("MANIFEST.MF"), this::copyReplacing);
walkFileSystems(injection, minecraftServerPatchedSrgJar, it -> !it.getFileName().toString().equals("MANIFEST.MF"), this::copyReplacing);
logger.lifecycle(":access transforming minecraft");
for (Environment environment : Environment.values()) {
ThreadingUtils.run(Arrays.asList(Environment.values()), environment -> {
String side = environment.side();
File target = environment.patchedSrgJar.apply(this);
walkFileSystems(injection, target, it -> !it.getFileName().toString().equals("MANIFEST.MF"), this::copyReplacing);
logger.lifecycle(":access transforming minecraft (" + side + ")");
File atJar = File.createTempFile("at" + side, ".jar");
File at = File.createTempFile("at" + side, ".cfg");
@@ -288,7 +291,7 @@ public class MinecraftPatchedProvider extends DependencyProvider {
}
TransformerProcessor.main(args);
}
});
}
private enum Environment {
@@ -317,63 +320,49 @@ public class MinecraftPatchedProvider extends DependencyProvider {
.map(File::toPath)
.toArray(Path[]::new);
ExecutorService service = Executors.newFixedThreadPool(2);
List<Future<?>> futures = new LinkedList<>();
ThreadingUtils.run(Arrays.asList(Environment.values()), environment -> {
logger.lifecycle(":remapping minecraft (TinyRemapper, " + environment.side() + ", srg -> official)");
TinyTree mappingsWithSrg = getExtension().getMappingsProvider().getMappingsWithSrg();
for (Environment environment : Environment.values()) {
futures.add(service.submit(() -> {
try {
logger.lifecycle(":remapping minecraft (TinyRemapper, " + environment.side() + ", srg -> official)");
TinyTree mappingsWithSrg = getExtension().getMappingsProvider().getMappingsWithSrg();
Path input = environment.patchedSrgJar.apply(this).toPath();
Path output = environment.patchedOfficialJar.apply(this).toPath();
Path input = environment.patchedSrgJar.apply(this).toPath();
Path output = environment.patchedOfficialJar.apply(this).toPath();
Files.deleteIfExists(output);
Files.deleteIfExists(output);
TinyRemapper remapper = TinyRemapper.newRemapper()
.withMappings(TinyRemapperMappingsHelper.create(mappingsWithSrg, "srg", "official", true))
.withMappings(InnerClassRemapper.of(input, mappingsWithSrg, "srg", "official"))
.renameInvalidLocals(true)
.rebuildSourceFilenames(true)
.fixPackageAccess(true)
.build();
TinyRemapper remapper = TinyRemapper.newRemapper()
.withMappings(TinyRemapperMappingsHelper.create(mappingsWithSrg, "srg", "official", true))
.withMappings(InnerClassRemapper.of(input, mappingsWithSrg, "srg", "official"))
.renameInvalidLocals(true)
.rebuildSourceFilenames(true)
.fixPackageAccess(true)
.build();
try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(output).build()) {
outputConsumer.addNonClassFiles(input);
try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(output).build()) {
outputConsumer.addNonClassFiles(input);
remapper.readClassPath(libraries);
remapper.readInputs(input);
remapper.apply(outputConsumer);
} finally {
remapper.finish();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}));
}
for (Future<?> future : futures) {
future.get();
}
remapper.readClassPath(libraries);
remapper.readInputs(input);
remapper.apply(outputConsumer);
} finally {
remapper.finish();
}
});
}
private void patchJars(Logger logger) throws Exception {
private void patchJars(Logger logger) throws IOException {
logger.lifecycle(":patching jars");
PatchProvider patchProvider = getExtension().getPatchProvider();
patchJars(minecraftClientSrgJar, minecraftClientPatchedSrgJar, patchProvider.clientPatches);
patchJars(minecraftServerSrgJar, minecraftServerPatchedSrgJar, patchProvider.serverPatches);
logger.lifecycle(":copying missing classes into patched jars");
copyMissingClasses(minecraftClientSrgJar, minecraftClientPatchedSrgJar);
copyMissingClasses(minecraftServerSrgJar, minecraftServerPatchedSrgJar);
logger.lifecycle(":fixing parameter annotations for patched jars");
fixParameterAnnotation(minecraftClientPatchedSrgJar);
fixParameterAnnotation(minecraftServerPatchedSrgJar);
ThreadingUtils.run(() -> {
copyMissingClasses(minecraftClientSrgJar, minecraftClientPatchedSrgJar);
fixParameterAnnotation(minecraftClientPatchedSrgJar);
}, () -> {
copyMissingClasses(minecraftServerSrgJar, minecraftServerPatchedSrgJar);
fixParameterAnnotation(minecraftServerPatchedSrgJar);
});
}
private void patchJars(File clean, File output, Path patches) throws IOException {
@@ -407,9 +396,9 @@ public class MinecraftPatchedProvider extends DependencyProvider {
private void walkFileSystems(File source, File target, Predicate<Path> filter, Function<FileSystem, Iterable<Path>> toWalk, FsPathConsumer action)
throws IOException {
try (FileSystem sourceFs = FileSystems.newFileSystem(new URI("jar:" + source.toURI()), ImmutableMap.of("create", false));
FileSystem targetFs = FileSystems.newFileSystem(new URI("jar:" + target.toURI()), ImmutableMap.of("create", false))) {
for (Path sourceDir : toWalk.apply(sourceFs)) {
try (FileSystemUtil.FileSystemDelegate sourceFs = FileSystemUtil.getJarFileSystem(source, false);
FileSystemUtil.FileSystemDelegate targetFs = FileSystemUtil.getJarFileSystem(target, false)) {
for (Path sourceDir : toWalk.apply(sourceFs.get())) {
Path dir = sourceDir.toAbsolutePath();
Files.walk(dir)
.filter(Files::isRegularFile)
@@ -419,15 +408,13 @@ public class MinecraftPatchedProvider extends DependencyProvider {
try {
Path relativeSource = root ? it : dir.relativize(it);
Path targetPath = targetFs.getPath(relativeSource.toString());
action.accept(sourceFs, targetFs, it, targetPath);
Path targetPath = targetFs.get().getPath(relativeSource.toString());
action.accept(sourceFs.get(), targetFs.get(), it, targetPath);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
} catch (URISyntaxException e) {
throw new IOException(e);
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2016 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.loom.util;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemAlreadyExistsException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.*;
public final class FileSystemUtil {
public static class FileSystemDelegate implements AutoCloseable {
private final FileSystem fileSystem;
private final boolean owner;
public FileSystemDelegate(FileSystem fileSystem, boolean owner) {
this.fileSystem = fileSystem;
this.owner = owner;
}
public FileSystem get() {
return fileSystem;
}
@Override
public void close() throws IOException {
if (owner) {
fileSystem.close();
}
}
}
private FileSystemUtil() {
}
private static final Map<String, String> jfsArgsCreate = new HashMap<>();
private static final Map<String, String> jfsArgsEmpty = new HashMap<>();
static {
jfsArgsCreate.put("create", "true");
}
public static FileSystemDelegate getJarFileSystem(File file, boolean create) throws IOException {
return getJarFileSystem(file.toURI(), create);
}
public static FileSystemDelegate getJarFileSystem(Path path, boolean create) throws IOException {
return getJarFileSystem(path.toUri(), create);
}
public static FileSystemDelegate getJarFileSystem(URI uri, boolean create) throws IOException {
URI jarUri;
try {
jarUri = new URI("jar:" + uri.getScheme(), uri.getHost(), uri.getPath(), uri.getFragment());
} catch (URISyntaxException e) {
throw new IOException(e);
}
try {
return new FileSystemDelegate(FileSystems.newFileSystem(jarUri, create ? jfsArgsCreate : jfsArgsEmpty), true);
} catch (FileSystemAlreadyExistsException e) {
return new FileSystemDelegate(FileSystems.getFileSystem(jarUri), false);
}
}
}

View File

@@ -0,0 +1,91 @@
package net.fabricmc.loom.util;
import org.gradle.internal.impldep.com.google.api.client.repackaged.com.google.common.base.Function;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
public class ThreadingUtils {
public static <T> void run(Collection<T> values, UnsafeConsumer<T> action) {
run(values.stream()
.<UnsafeRunnable>map(t -> () -> action.accept(t))
.collect(Collectors.toList()));
}
public static void run(UnsafeRunnable... jobs) {
run(Arrays.asList(jobs));
}
public static void run(Collection<UnsafeRunnable> jobs) {
try {
ExecutorService service = Executors.newFixedThreadPool(Math.min(jobs.size(), Runtime.getRuntime().availableProcessors() / 2));
List<Future<?>> futures = new LinkedList<>();
for (UnsafeRunnable runnable : jobs) {
futures.add(service.submit(() -> {
try {
runnable.run();
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}));
}
for (Future<?> future : futures) {
future.get();
}
service.shutdownNow();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public static <T, R> List<R> get(Collection<T> values, Function<T, R> action) {
return get(values.stream()
.<UnsafeCallable<R>>map(t -> () -> action.apply(t))
.collect(Collectors.toList()));
}
@SafeVarargs
public static <T> List<T> get(UnsafeCallable<T>... jobs) {
return get(Arrays.asList(jobs));
}
public static <T> List<T> get(Collection<UnsafeCallable<T>> jobs) {
try {
ExecutorService service = Executors.newFixedThreadPool(Math.min(jobs.size(), Runtime.getRuntime().availableProcessors() / 2));
List<Future<T>> futures = new LinkedList<>();
for (UnsafeCallable<T> runnable : jobs) {
futures.add(service.submit(() -> {
try {
return runnable.call();
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}));
}
List<T> result = new ArrayList<>();
for (Future<T> future : futures) {
result.add(future.get());
}
service.shutdownNow();
return result;
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public interface UnsafeRunnable {
void run() throws Throwable;
}
public interface UnsafeCallable<T> {
T call() throws Throwable;
}
public interface UnsafeConsumer<T> {
void accept(T value) throws Throwable;
}
}

View File

@@ -1,6 +1,5 @@
package net.fabricmc.loom.util.srg;
import com.google.common.collect.ImmutableMap;
import net.fabricmc.loom.LoomGradleExtension;
import org.apache.commons.io.IOUtils;
import org.gradle.api.Project;
@@ -10,13 +9,15 @@ import org.zeroturnaround.zip.ZipUtil;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Set;
import java.util.UUID;
import java.util.jar.JarOutputStream;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
public class SpecialSourceExecutor {
public static Path produceSrgJar(Project project, File specialSourceJar, Path officialJar, Path srgPath) throws Exception {
@@ -24,16 +25,14 @@ public class SpecialSourceExecutor {
.filter(s -> !s.startsWith("\t"))
.map(s -> s.split(" ")[0] + ".class")
.collect(Collectors.toSet());
Path stripped = project.getExtensions().getByType(LoomGradleExtension.class).getProjectBuildCache().toPath().resolve(officialJar.getFileName().toString().substring(0, officialJar.getFileName().toString().length() - 3) + "-filtered.jar");
Path stripped = project.getExtensions().getByType(LoomGradleExtension.class).getProjectBuildCache().toPath().resolve(officialJar.getFileName().toString().substring(0, officialJar.getFileName().toString().length() - 4) + "-filtered.jar");
Files.deleteIfExists(stripped);
try (FileSystem strippedFs = FileSystems.newFileSystem(URI.create("jar:" + stripped.toUri()), ImmutableMap.of("create", true))) {
try (JarOutputStream output = new JarOutputStream(Files.newOutputStream(stripped))) {
ZipUtil.iterate(officialJar.toFile(), (in, zipEntry) -> {
if (filter.contains(zipEntry.getName())) {
Path path = strippedFs.getPath(zipEntry.getName());
if (path.getParent() != null) {
Files.createDirectories(path.getParent());
}
Files.write(path, IOUtils.toByteArray(in), StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
output.putNextEntry((ZipEntry) zipEntry.clone());
IOUtils.write(IOUtils.toByteArray(in), output);
output.closeEntry();
}
});
}