mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-29 04:30:00 -05:00
WIP AT -> JarProcessor
This commit is contained in:
@@ -26,15 +26,24 @@ package net.fabricmc.loom.api.decompilers;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.task.GenerateSourcesTask;
|
||||
|
||||
public class DecompilationMetadata {
|
||||
public final int numberOfThreads;
|
||||
public final Path javaDocs;
|
||||
public final Collection<Path> libraries;
|
||||
@Nullable
|
||||
public final Predicate<String> classFilter;
|
||||
|
||||
public DecompilationMetadata(int numberOfThreads, Path javaDocs, Collection<Path> libraries) {
|
||||
public DecompilationMetadata(int numberOfThreads, Path javaDocs, Collection<Path> libraries, Function<String, GenerateSourcesTask.SkipState> classFilter) {
|
||||
this.numberOfThreads = numberOfThreads;
|
||||
this.javaDocs = javaDocs;
|
||||
this.libraries = libraries;
|
||||
this.classFilter = s -> GenerateSourcesTask.SkipState.SKIP != classFilter.apply(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
package net.fabricmc.loom.configuration.accesstransformer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import me.shedaniel.architectury.refmapremapper.remapper.SimpleReferenceRemapper;
|
||||
import net.minecraftforge.accesstransformer.AccessTransformerEngine;
|
||||
import net.minecraftforge.accesstransformer.TransformerProcessor;
|
||||
import net.minecraftforge.accesstransformer.parser.AccessTransformerList;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.zeroturnaround.zip.ZipUtil;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.processors.JarProcessor;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
import net.fabricmc.loom.util.JarUtil;
|
||||
import net.fabricmc.loom.util.srg.AtRemapper;
|
||||
import net.fabricmc.mapping.tree.TinyTree;
|
||||
|
||||
public class AccessTransformerJarProcessor implements JarProcessor {
|
||||
private final Path projectAt;
|
||||
private final Project project;
|
||||
private final Set<String> affectedClasses = new HashSet<>();
|
||||
private Path remappedProjectAt;
|
||||
|
||||
public AccessTransformerJarProcessor(Path projectAt, Project project) {
|
||||
this.projectAt = projectAt;
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
public static void addTo(Project project, LoomGradleExtension extension) {
|
||||
SourceSet main = project.getConvention().findPlugin(JavaPluginConvention.class).getSourceSets().getByName("main");
|
||||
|
||||
for (File srcDir : main.getResources().getSrcDirs()) {
|
||||
File projectAt = new File(srcDir, "META-INF/accesstransformer.cfg");
|
||||
|
||||
if (projectAt.exists()) {
|
||||
extension.addJarProcessor(new AccessTransformerJarProcessor(projectAt.toPath(), project));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
affectedClasses.clear();
|
||||
remappedProjectAt = null;
|
||||
getRemappedAt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(File file) {
|
||||
try {
|
||||
accessTransformForge(file, file, false, getRemappedAt());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvalid(File file) {
|
||||
byte[] hash = ZipUtil.unpackEntry(file, "loom-at.sha256");
|
||||
|
||||
if (hash == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !Arrays.equals(getInputHash(false, getRemappedAt()), hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesProcessClass(String classInternalName) {
|
||||
getRemappedAt();
|
||||
return affectedClasses.contains(classInternalName);
|
||||
}
|
||||
|
||||
private File getRemappedAt() {
|
||||
if (remappedProjectAt == null) {
|
||||
try {
|
||||
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
|
||||
MappingsProvider mappingsProvider = extension.getMappingsProvider();
|
||||
TinyTree mappings = extension.isForge() ? mappingsProvider.getMappingsWithSrg() : mappingsProvider.getMappings();
|
||||
|
||||
File file = File.createTempFile("at-remapped-", ".at");
|
||||
file.delete();
|
||||
String sourceAt = FileUtils.readFileToString(projectAt.toFile(), StandardCharsets.UTF_8);
|
||||
String remappedAt = AtRemapper.remapAt(project.getLogger(), sourceAt, new SimpleReferenceRemapper.Remapper() {
|
||||
@Override
|
||||
@Nullable
|
||||
public String mapClass(String value) {
|
||||
return mappings.getClasses().stream()
|
||||
.filter(classDef -> Objects.equals(classDef.getName("srg"), value))
|
||||
.findFirst()
|
||||
.map(classDef -> classDef.getName("named"))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String mapMethod(@Nullable String className, String methodName, String methodDescriptor) {
|
||||
return mappings.getClasses().stream()
|
||||
.flatMap(classDef -> classDef.getMethods().stream())
|
||||
.filter(methodDef -> Objects.equals(methodDef.getName("srg"), methodName))
|
||||
.filter(methodDef -> Objects.equals(methodDef.getDescriptor("srg"), methodDescriptor))
|
||||
.findFirst()
|
||||
.map(classDef -> classDef.getName("named"))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String mapField(@Nullable String className, String fieldName, String fieldDescriptor) {
|
||||
return mappings.getClasses().stream()
|
||||
.flatMap(classDef -> classDef.getFields().stream())
|
||||
.filter(methodDef -> Objects.equals(methodDef.getName("srg"), fieldName))
|
||||
.findFirst()
|
||||
.map(classDef -> classDef.getName("named"))
|
||||
.orElse(null);
|
||||
}
|
||||
});
|
||||
|
||||
for (String line : remappedAt.split("\n")) {
|
||||
{
|
||||
int indexOf = line.indexOf('#');
|
||||
|
||||
if (indexOf != -1) {
|
||||
line = line.substring(0, indexOf);
|
||||
}
|
||||
|
||||
line = line.trim();
|
||||
}
|
||||
|
||||
if (!Strings.isBlank(line)) {
|
||||
affectedClasses.add(line.split("\\s+")[1].replace('.', '/'));
|
||||
}
|
||||
}
|
||||
|
||||
FileUtils.write(file, remappedAt, StandardCharsets.UTF_8);
|
||||
file.deleteOnExit();
|
||||
return (remappedProjectAt = file.toPath()).toFile();
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return remappedProjectAt.toFile();
|
||||
}
|
||||
|
||||
public static byte[] getInputHash(boolean addEmbeddedAt, File... extraAts) {
|
||||
byte[] inputHash = new byte[] {(byte) (addEmbeddedAt ? 1 : 0)};
|
||||
|
||||
for (File extraAt : extraAts) {
|
||||
inputHash = concat(inputHash, Checksum.sha256(extraAt));
|
||||
}
|
||||
|
||||
return inputHash;
|
||||
}
|
||||
|
||||
public static byte[] concat(byte[] a, byte[] b) {
|
||||
byte[] out = new byte[a.length + b.length];
|
||||
System.arraycopy(a, 0, out, 0, a.length);
|
||||
System.arraycopy(b, 0, out, a.length, b.length);
|
||||
return out;
|
||||
}
|
||||
|
||||
public static void accessTransformForge(File input, File target, boolean addEmbeddedAt, File... extraAts) throws Exception {
|
||||
byte[] inputHash = getInputHash(addEmbeddedAt, extraAts);
|
||||
File inputCopied = File.createTempFile("at-input-", ".jar");
|
||||
File at = File.createTempFile("at-", ".cfg");
|
||||
FileUtils.copyFile(input, inputCopied);
|
||||
|
||||
if (target.exists()) {
|
||||
byte[] hash = ZipUtil.unpackEntry(target, "loom-at.sha256");
|
||||
|
||||
if (hash != null && Arrays.equals(inputHash, hash)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
target.delete();
|
||||
String[] args = new String[] {
|
||||
"--inJar", inputCopied.getAbsolutePath(),
|
||||
"--outJar", target.getAbsolutePath()
|
||||
};
|
||||
|
||||
if (addEmbeddedAt) {
|
||||
JarUtil.extractFile(inputCopied, "META-INF/accesstransformer.cfg", at);
|
||||
args = Arrays.copyOf(args, args.length + 2);
|
||||
args[args.length - 2] = "--atFile";
|
||||
args[args.length - 1] = at.getAbsolutePath();
|
||||
}
|
||||
|
||||
for (File extraAt : extraAts) {
|
||||
args = Arrays.copyOf(args, args.length + 2);
|
||||
args[args.length - 2] = "--atFile";
|
||||
args[args.length - 1] = extraAt.getAbsolutePath();
|
||||
}
|
||||
|
||||
resetAccessTransformerEngine();
|
||||
TransformerProcessor.main(args);
|
||||
inputCopied.delete();
|
||||
at.delete();
|
||||
resetAccessTransformerEngine();
|
||||
|
||||
ZipUtil.addEntry(target, "loom-at.sha256", inputHash);
|
||||
}
|
||||
|
||||
private static void resetAccessTransformerEngine() throws Exception {
|
||||
// Thank you Forge, I love you
|
||||
Field field = AccessTransformerEngine.class.getDeclaredField("masterList");
|
||||
field.setAccessible(true);
|
||||
AccessTransformerList list = (AccessTransformerList) field.get(AccessTransformerEngine.INSTANCE);
|
||||
field = AccessTransformerList.class.getDeclaredField("accessTransformers");
|
||||
field.setAccessible(true);
|
||||
((Map<?, ?>) field.get(list)).clear();
|
||||
}
|
||||
}
|
||||
@@ -117,6 +117,11 @@ public class AccessWidenerJarProcessor implements JarProcessor {
|
||||
ZipUtil.addEntry(file, "aw.sha256", inputHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesProcessClass(String classInternalName) {
|
||||
return accessWidener.getTargets().contains(classInternalName.replace('/', '.'));
|
||||
}
|
||||
|
||||
private ZipEntryTransformerEntry[] getTransformers(Set<String> classes) {
|
||||
return classes.stream()
|
||||
.map(string -> new ZipEntryTransformerEntry(string.replaceAll("\\.", "/") + ".class", getTransformer(string)))
|
||||
|
||||
@@ -211,7 +211,7 @@ public class ModProcessor {
|
||||
}
|
||||
|
||||
if (extension.isForge()) {
|
||||
AtRemapper.remap(project.getLogger(), info.getRemappedOutput().toPath(), mappings);
|
||||
AtRemapper.remapSrgToNamed(project.getLogger(), info.getRemappedOutput().toPath(), mappings);
|
||||
CoreModClassRemapper.remapJar(info.getRemappedOutput().toPath(), mappings, project.getLogger());
|
||||
|
||||
if (ZipUtil.containsEntry(info.getRemappedOutput(), "META-INF/MANIFEST.MF")) {
|
||||
|
||||
@@ -38,4 +38,8 @@ public interface JarProcessor {
|
||||
* Return true to make all jar processors run again, return false to use the existing results of jar processing.
|
||||
*/
|
||||
boolean isInvalid(File file);
|
||||
|
||||
default boolean doesProcessClass(String classInternalName) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,10 @@ public class JarProcessorManager {
|
||||
return jarProcessors.stream().anyMatch(jarProcessor -> jarProcessor.isInvalid(file));
|
||||
}
|
||||
|
||||
public boolean doesProcessClass(String classInternalName) {
|
||||
return jarProcessors.stream().anyMatch(jarProcessor -> jarProcessor.doesProcessClass(classInternalName));
|
||||
}
|
||||
|
||||
public void process(File file) {
|
||||
for (JarProcessor jarProcessor : jarProcessors) {
|
||||
jarProcessor.process(file);
|
||||
|
||||
@@ -50,9 +50,7 @@ public class MinecraftProcessedProvider extends MinecraftMappedProvider {
|
||||
|
||||
@Override
|
||||
protected void addDependencies(DependencyInfo dependency, Consumer<Runnable> postPopulationScheduler) {
|
||||
boolean isForgeAtDirty = getExtension().isForge() && getExtension().getMappingsProvider().patchedProvider.isAtDirty();
|
||||
|
||||
if (jarProcessorManager.isInvalid(projectMappedJar) || isRefreshDeps() || isForgeAtDirty) {
|
||||
if (jarProcessorManager.isInvalid(projectMappedJar) || isRefreshDeps()) {
|
||||
getProject().getLogger().info(":processing mapped jar");
|
||||
invalidateJars();
|
||||
|
||||
|
||||
@@ -26,15 +26,12 @@ package net.fabricmc.loom.configuration.providers.forge;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
@@ -44,7 +41,7 @@ import java.nio.file.StandardCopyOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
@@ -54,18 +51,13 @@ import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.JsonParser;
|
||||
import de.oceanlabs.mcp.mcinjector.adaptors.ParameterAnnotationFixer;
|
||||
import net.minecraftforge.accesstransformer.AccessTransformerEngine;
|
||||
import net.minecraftforge.accesstransformer.TransformerProcessor;
|
||||
import net.minecraftforge.accesstransformer.parser.AccessTransformerList;
|
||||
import net.minecraftforge.binarypatcher.ConsoleTool;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.output.NullOutputStream;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
@@ -73,14 +65,12 @@ import org.objectweb.asm.tree.ClassNode;
|
||||
import org.zeroturnaround.zip.ZipUtil;
|
||||
|
||||
import net.fabricmc.loom.configuration.DependencyProvider;
|
||||
import net.fabricmc.loom.configuration.accesstransformer.AccessTransformerJarProcessor;
|
||||
import net.fabricmc.loom.configuration.providers.MinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.DownloadUtil;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
import net.fabricmc.loom.util.JarUtil;
|
||||
import net.fabricmc.loom.util.ThreadingUtils;
|
||||
import net.fabricmc.loom.util.TinyRemapperMappingsHelper;
|
||||
import net.fabricmc.loom.util.function.FsPathConsumer;
|
||||
@@ -98,18 +88,11 @@ public class MinecraftPatchedProvider extends DependencyProvider {
|
||||
// Step 2: Binary Patch
|
||||
private File minecraftClientPatchedSrgJar;
|
||||
private File minecraftServerPatchedSrgJar;
|
||||
// Step 3: Access Transform
|
||||
private File minecraftClientPatchedSrgATJar;
|
||||
private File minecraftServerPatchedSrgATJar;
|
||||
// Step 4: Remap Patched AT to Official
|
||||
// Step 3: Remap Patched Jar to Official
|
||||
private File minecraftClientPatchedOfficialJar;
|
||||
private File minecraftServerPatchedOfficialJar;
|
||||
// Step 5: Merge
|
||||
// Step 4: Merge
|
||||
private File minecraftMergedPatchedJar;
|
||||
private File projectAtHash;
|
||||
@Nullable
|
||||
private File projectAt = null;
|
||||
private boolean atDirty = false;
|
||||
|
||||
public MinecraftPatchedProvider(MappingsProvider mappingsProvider, Project project) {
|
||||
super(project);
|
||||
@@ -117,34 +100,6 @@ public class MinecraftPatchedProvider extends DependencyProvider {
|
||||
}
|
||||
|
||||
public void initFiles() throws IOException {
|
||||
projectAtHash = new File(getExtension().getProjectPersistentCache(), "at.sha256");
|
||||
|
||||
SourceSet main = getProject().getConvention().findPlugin(JavaPluginConvention.class).getSourceSets().getByName("main");
|
||||
|
||||
for (File srcDir : main.getResources().getSrcDirs()) {
|
||||
File projectAt = new File(srcDir, "META-INF/accesstransformer.cfg");
|
||||
|
||||
if (projectAt.exists()) {
|
||||
this.projectAt = projectAt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isRefreshDeps() || !projectAtHash.exists()) {
|
||||
writeAtHash();
|
||||
atDirty = projectAt != null;
|
||||
} else {
|
||||
byte[] expected = com.google.common.io.Files.asByteSource(projectAtHash).read();
|
||||
byte[] current = projectAt != null ? Checksum.sha256(projectAt) : Checksum.sha256("");
|
||||
boolean mismatched = !Arrays.equals(current, expected);
|
||||
|
||||
if (mismatched) {
|
||||
writeAtHash();
|
||||
}
|
||||
|
||||
atDirty = mismatched;
|
||||
}
|
||||
|
||||
MinecraftProvider minecraftProvider = getExtension().getMinecraftProvider();
|
||||
PatchProvider patchProvider = getExtension().getPatchProvider();
|
||||
String minecraftVersion = minecraftProvider.getMinecraftVersion();
|
||||
@@ -157,21 +112,18 @@ public class MinecraftPatchedProvider extends DependencyProvider {
|
||||
minecraftProvider.setJarSuffix(jarSuffix);
|
||||
|
||||
File globalCache = getExtension().getUserCache();
|
||||
File cache = usesProjectCache() ? getExtension().getProjectPersistentCache() : globalCache;
|
||||
|
||||
minecraftClientSrgJar = new File(globalCache, "minecraft-" + minecraftVersion + "-client-srg.jar");
|
||||
minecraftServerSrgJar = new File(globalCache, "minecraft-" + minecraftVersion + "-server-srg.jar");
|
||||
minecraftClientPatchedSrgJar = new File(globalCache, "minecraft-" + minecraftVersion + "-client-srg" + jarSuffix + ".jar");
|
||||
minecraftServerPatchedSrgJar = new File(globalCache, "minecraft-" + minecraftVersion + "-server-srg" + jarSuffix + ".jar");
|
||||
minecraftClientPatchedSrgATJar = new File(cache, "minecraft-" + minecraftVersion + "-client-srg-at" + jarSuffix + ".jar");
|
||||
minecraftServerPatchedSrgATJar = new File(cache, "minecraft-" + minecraftVersion + "-server-srg-at" + jarSuffix + ".jar");
|
||||
minecraftClientPatchedOfficialJar = new File(cache, "minecraft-" + minecraftVersion + "-client" + jarSuffix + ".jar");
|
||||
minecraftServerPatchedOfficialJar = new File(cache, "minecraft-" + minecraftVersion + "-server" + jarSuffix + ".jar");
|
||||
minecraftMergedPatchedJar = new File(cache, "minecraft-" + minecraftVersion + "-merged" + jarSuffix + ".jar");
|
||||
minecraftClientPatchedSrgJar = new File(globalCache, "minecraft-" + minecraftVersion + "-client-srg-patched" + jarSuffix + ".jar");
|
||||
minecraftServerPatchedSrgJar = new File(globalCache, "minecraft-" + minecraftVersion + "-server-srg-patched" + jarSuffix + ".jar");
|
||||
minecraftClientPatchedOfficialJar = new File(globalCache, "minecraft-" + minecraftVersion + "-client-patched" + jarSuffix + ".jar");
|
||||
minecraftServerPatchedOfficialJar = new File(globalCache, "minecraft-" + minecraftVersion + "-server-patched" + jarSuffix + ".jar");
|
||||
minecraftMergedPatchedJar = new File(globalCache, "minecraft-" + minecraftVersion + "-merged-patched" + jarSuffix + ".jar");
|
||||
|
||||
if (isRefreshDeps() || Stream.of(getGlobalCaches()).anyMatch(Predicates.not(File::exists))) {
|
||||
cleanAllCache();
|
||||
} else if (atDirty || Stream.of(getProjectCache()).anyMatch(Predicates.not(File::exists))) {
|
||||
} else if (Stream.of(getProjectCache()).anyMatch(Predicates.not(File::exists))) {
|
||||
cleanProjectCache();
|
||||
}
|
||||
}
|
||||
@@ -201,8 +153,6 @@ public class MinecraftPatchedProvider extends DependencyProvider {
|
||||
|
||||
private File[] getProjectCache() {
|
||||
return new File[] {
|
||||
minecraftClientPatchedSrgATJar,
|
||||
minecraftServerPatchedSrgATJar,
|
||||
minecraftClientPatchedOfficialJar,
|
||||
minecraftServerPatchedOfficialJar,
|
||||
minecraftMergedPatchedJar
|
||||
@@ -213,10 +163,6 @@ public class MinecraftPatchedProvider extends DependencyProvider {
|
||||
public void provide(DependencyInfo dependency, Consumer<Runnable> postPopulationScheduler) throws Exception {
|
||||
initFiles();
|
||||
|
||||
if (atDirty) {
|
||||
getProject().getLogger().lifecycle(":found dirty access transformers");
|
||||
}
|
||||
|
||||
boolean dirty = false;
|
||||
|
||||
if (!minecraftClientSrgJar.exists() || !minecraftServerSrgJar.exists()) {
|
||||
@@ -229,10 +175,6 @@ public class MinecraftPatchedProvider extends DependencyProvider {
|
||||
dirty = true;
|
||||
patchJars(getProject().getLogger());
|
||||
injectForgeClasses(getProject().getLogger());
|
||||
}
|
||||
|
||||
if (atDirty || !minecraftClientPatchedSrgATJar.exists() || !minecraftServerPatchedSrgATJar.exists()) {
|
||||
dirty = true;
|
||||
accessTransformForge(getProject().getLogger());
|
||||
}
|
||||
|
||||
@@ -245,16 +187,6 @@ public class MinecraftPatchedProvider extends DependencyProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private void writeAtHash() throws IOException {
|
||||
try (FileOutputStream out = new FileOutputStream(projectAtHash)) {
|
||||
if (projectAt != null) {
|
||||
out.write(Checksum.sha256(projectAt));
|
||||
} else {
|
||||
out.write(Checksum.sha256(""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createSrgJars(Logger logger) throws Exception {
|
||||
McpConfigProvider mcpProvider = getExtension().getMcpConfigProvider();
|
||||
|
||||
@@ -280,8 +212,10 @@ public class MinecraftPatchedProvider extends DependencyProvider {
|
||||
throw new IllegalStateException("Failed to find mappings '" + mappingsPath[0] + "' in " + mcpProvider.getMcp().getAbsolutePath() + "!");
|
||||
}
|
||||
|
||||
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);
|
||||
Dependency dependency = getProject().getDependencies().create("net.md-5:SpecialSource:1.8.3:shaded");
|
||||
Set<File> specialSourceJars = getProject().getConfigurations().detachedConfiguration(dependency).resolve();
|
||||
if (specialSourceJars.isEmpty()) throw new IllegalStateException("Failed to resolve net.md-5:SpecialSource:1.8.3:shaded");
|
||||
File specialSourceJar = specialSourceJars.iterator().next();
|
||||
|
||||
ThreadingUtils.run(() -> {
|
||||
Files.copy(SpecialSourceExecutor.produceSrgJar(getProject(), mappingsProvider, "client", specialSourceJar, minecraftProvider.minecraftClientJar.toPath(), tmpSrg[0]), minecraftClientSrgJar.toPath());
|
||||
@@ -348,65 +282,30 @@ public class MinecraftPatchedProvider extends DependencyProvider {
|
||||
String side = environment.side();
|
||||
logger.lifecycle(":access transforming minecraft (" + side + ")");
|
||||
|
||||
File input = environment.patchedSrgJar.apply(this);
|
||||
File inputCopied = File.createTempFile("at" + side, ".jar");
|
||||
FileUtils.copyFile(input, inputCopied);
|
||||
File target = environment.patchedSrgATJar.apply(this);
|
||||
target.delete();
|
||||
File at = File.createTempFile("at" + side, ".cfg");
|
||||
JarUtil.extractFile(inputCopied, "META-INF/accesstransformer.cfg", at);
|
||||
String[] args = new String[] {
|
||||
"--inJar", inputCopied.getAbsolutePath(),
|
||||
"--outJar", target.getAbsolutePath(),
|
||||
"--atFile", at.getAbsolutePath()
|
||||
};
|
||||
|
||||
if (usesProjectCache()) {
|
||||
args = Arrays.copyOf(args, args.length + 2);
|
||||
args[args.length - 2] = "--atFile";
|
||||
args[args.length - 1] = projectAt.getAbsolutePath();
|
||||
}
|
||||
|
||||
resetAccessTransformerEngine();
|
||||
TransformerProcessor.main(args);
|
||||
inputCopied.delete();
|
||||
File file = environment.patchedSrgJar.apply(this);
|
||||
AccessTransformerJarProcessor.accessTransformForge(file, file, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetAccessTransformerEngine() throws Exception {
|
||||
// Thank you Forge, I love you
|
||||
Field field = AccessTransformerEngine.class.getDeclaredField("masterList");
|
||||
field.setAccessible(true);
|
||||
AccessTransformerList list = (AccessTransformerList) field.get(AccessTransformerEngine.INSTANCE);
|
||||
field = AccessTransformerList.class.getDeclaredField("accessTransformers");
|
||||
field.setAccessible(true);
|
||||
((Map<?, ?>) field.get(list)).clear();
|
||||
}
|
||||
|
||||
private enum Environment {
|
||||
CLIENT(provider -> provider.minecraftClientSrgJar,
|
||||
provider -> provider.minecraftClientPatchedSrgJar,
|
||||
provider -> provider.minecraftClientPatchedSrgATJar,
|
||||
provider -> provider.minecraftClientPatchedOfficialJar
|
||||
),
|
||||
SERVER(provider -> provider.minecraftServerSrgJar,
|
||||
provider -> provider.minecraftServerPatchedSrgJar,
|
||||
provider -> provider.minecraftServerPatchedSrgATJar,
|
||||
provider -> provider.minecraftServerPatchedOfficialJar
|
||||
);
|
||||
|
||||
final Function<MinecraftPatchedProvider, File> srgJar;
|
||||
final Function<MinecraftPatchedProvider, File> patchedSrgJar;
|
||||
final Function<MinecraftPatchedProvider, File> patchedSrgATJar;
|
||||
final Function<MinecraftPatchedProvider, File> patchedOfficialJar;
|
||||
|
||||
Environment(Function<MinecraftPatchedProvider, File> srgJar,
|
||||
Function<MinecraftPatchedProvider, File> patchedSrgJar,
|
||||
Function<MinecraftPatchedProvider, File> patchedSrgATJar,
|
||||
Function<MinecraftPatchedProvider, File> patchedOfficialJar) {
|
||||
this.srgJar = srgJar;
|
||||
this.patchedSrgJar = patchedSrgJar;
|
||||
this.patchedSrgATJar = patchedSrgATJar;
|
||||
this.patchedOfficialJar = patchedOfficialJar;
|
||||
}
|
||||
|
||||
@@ -422,7 +321,7 @@ public class MinecraftPatchedProvider extends DependencyProvider {
|
||||
logger.lifecycle(":remapping minecraft (TinyRemapper, " + environment.side() + ", srg -> official)");
|
||||
TinyTree mappingsWithSrg = getExtension().getMappingsProvider().getMappingsWithSrg();
|
||||
|
||||
Path input = environment.patchedSrgATJar.apply(this).toPath();
|
||||
Path input = environment.patchedSrgJar.apply(this).toPath();
|
||||
Path output = environment.patchedOfficialJar.apply(this).toPath();
|
||||
|
||||
Files.deleteIfExists(output);
|
||||
@@ -570,14 +469,6 @@ public class MinecraftPatchedProvider extends DependencyProvider {
|
||||
return minecraftMergedPatchedJar;
|
||||
}
|
||||
|
||||
public boolean usesProjectCache() {
|
||||
return projectAt != null;
|
||||
}
|
||||
|
||||
public boolean isAtDirty() {
|
||||
return atDirty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTargetConfig() {
|
||||
return Constants.Configurations.MINECRAFT;
|
||||
|
||||
@@ -47,6 +47,8 @@ import org.apache.commons.io.FileUtils;
|
||||
import org.apache.tools.ant.util.StringUtils;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.zeroturnaround.zip.FileSource;
|
||||
import org.zeroturnaround.zip.ZipEntrySource;
|
||||
import org.zeroturnaround.zip.ZipUtil;
|
||||
@@ -54,6 +56,7 @@ import org.zeroturnaround.zip.ZipUtil;
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.configuration.DependencyProvider;
|
||||
import net.fabricmc.loom.configuration.accesstransformer.AccessTransformerJarProcessor;
|
||||
import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor;
|
||||
import net.fabricmc.loom.configuration.processors.JarProcessorManager;
|
||||
import net.fabricmc.loom.configuration.processors.MinecraftProcessedProvider;
|
||||
@@ -240,6 +243,10 @@ public class MappingsProvider extends DependencyProvider {
|
||||
extension.addJarProcessor(new AccessWidenerJarProcessor(getProject()));
|
||||
}
|
||||
|
||||
if (extension.isForge()) {
|
||||
AccessTransformerJarProcessor.addTo(getProject(), extension);
|
||||
}
|
||||
|
||||
JarProcessorManager processorManager = new JarProcessorManager(extension.getJarProcessors());
|
||||
extension.setJarProcessorManager(processorManager);
|
||||
processorManager.setupProcessors();
|
||||
@@ -249,7 +256,7 @@ public class MappingsProvider extends DependencyProvider {
|
||||
patchedProvider.provide(dependency, postPopulationScheduler);
|
||||
}
|
||||
|
||||
if (processorManager.active() || (extension.isForge() && patchedProvider.usesProjectCache())) {
|
||||
if (processorManager.active()) {
|
||||
mappedProvider = new MinecraftProcessedProvider(getProject(), processorManager);
|
||||
getProject().getLogger().lifecycle("Using project based jar storage");
|
||||
} else {
|
||||
|
||||
@@ -89,9 +89,7 @@ public class MinecraftMappedProvider extends DependencyProvider {
|
||||
throw new RuntimeException("input merged jar not found");
|
||||
}
|
||||
|
||||
boolean isForgeAtDirty = getExtension().isForge() && getExtension().getMappingsProvider().patchedProvider.isAtDirty();
|
||||
|
||||
if (!minecraftMappedJar.exists() || !getIntermediaryJar().exists() || (getExtension().isForge() && !getSrgJar().exists()) || isRefreshDeps() || isForgeAtDirty) {
|
||||
if (!minecraftMappedJar.exists() || !getIntermediaryJar().exists() || (getExtension().isForge() && !getSrgJar().exists()) || isRefreshDeps()) {
|
||||
if (minecraftMappedJar.exists()) {
|
||||
minecraftMappedJar.delete();
|
||||
}
|
||||
@@ -199,7 +197,7 @@ public class MinecraftMappedProvider extends DependencyProvider {
|
||||
}
|
||||
|
||||
TinyTree yarnWithSrg = getExtension().getMappingsProvider().getMappingsWithSrg();
|
||||
AtRemapper.remap(getProject().getLogger(), output, yarnWithSrg);
|
||||
AtRemapper.remapSrgToNamed(getProject().getLogger(), output, yarnWithSrg);
|
||||
CoreModClassRemapper.remapJar(output, yarnWithSrg, getProject().getLogger());
|
||||
}
|
||||
}
|
||||
@@ -281,6 +279,10 @@ public class MinecraftMappedProvider extends DependencyProvider {
|
||||
return minecraftMappedJar;
|
||||
}
|
||||
|
||||
public final File getUnprocessedMappedJar() {
|
||||
return minecraftMappedJar;
|
||||
}
|
||||
|
||||
public File getUnpickedJar() {
|
||||
return new File(getJarDirectory(getExtension().getUserCache(), "mapped"), "minecraft-" + getJarVersionString("unpicked") + ".jar");
|
||||
}
|
||||
|
||||
@@ -177,6 +177,22 @@ public class FabricCFRDecompiler implements LoomDecompiler {
|
||||
List<Future<?>> futures = new LinkedList<>();
|
||||
|
||||
for (String clazz : classes) {
|
||||
if (metaData.classFilter != null) {
|
||||
String classInternalName = clazz;
|
||||
|
||||
if (classInternalName.length() >= 1 && classInternalName.charAt(0) == '/') {
|
||||
classInternalName = classInternalName.substring(1);
|
||||
}
|
||||
|
||||
classInternalName = classInternalName.substring(0, classInternalName.length() - 6);
|
||||
|
||||
if (!metaData.classFilter.test(classInternalName)) {
|
||||
return;
|
||||
} else {
|
||||
System.out.println(classInternalName);
|
||||
}
|
||||
}
|
||||
|
||||
futures.add(executorService.submit(() -> {
|
||||
loggerMap.computeIfAbsent(Thread.currentThread().getId(), createLogger).progress(clazz);
|
||||
driver.analyse(Collections.singletonList(clazz));
|
||||
|
||||
@@ -26,13 +26,25 @@ package net.fabricmc.loom.decompilers.fernflower;
|
||||
|
||||
import static java.text.MessageFormat.format;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.internal.project.ProjectInternal;
|
||||
@@ -77,7 +89,7 @@ public abstract class AbstractFernFlowerDecompiler implements LoomDecompiler {
|
||||
List<String> args = new ArrayList<>();
|
||||
|
||||
options.forEach((k, v) -> args.add(format("-{0}={1}", k, v)));
|
||||
args.add(absolutePathOf(compiledJar));
|
||||
args.add(absolutePathOf(transformBinary(compiledJar, metaData)));
|
||||
args.add("-o=" + absolutePathOf(sourcesDestination));
|
||||
args.add("-l=" + absolutePathOf(linemapDestination));
|
||||
args.add("-m=" + absolutePathOf(metaData.javaDocs));
|
||||
@@ -150,6 +162,52 @@ public abstract class AbstractFernFlowerDecompiler implements LoomDecompiler {
|
||||
result.assertNormalExitValue();
|
||||
}
|
||||
|
||||
private Path transformBinary(Path compiledJar, DecompilationMetadata metaData) {
|
||||
if (metaData.classFilter == null) return compiledJar;
|
||||
|
||||
try {
|
||||
Path tempFile = Files.createTempFile("loom-", null);
|
||||
Files.deleteIfExists(tempFile);
|
||||
tempFile.toFile().deleteOnExit();
|
||||
|
||||
try (ZipOutputStream tempZipOut = new ZipOutputStream(new FileOutputStream(tempFile.toFile()))) {
|
||||
try (FileSystem compiledJarSystem = FileSystems.newFileSystem(URI.create("jar:" + compiledJar.toUri()), new HashMap<>())) {
|
||||
Files.walkFileTree(compiledJarSystem.getPath("/"), new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (file.toString().endsWith(".class")) {
|
||||
String sourcePath = file.toString();
|
||||
|
||||
if (sourcePath.length() >= 1 && sourcePath.charAt(0) == '/') {
|
||||
sourcePath = sourcePath.substring(1);
|
||||
}
|
||||
|
||||
sourcePath = sourcePath.substring(0, sourcePath.length() - 6);
|
||||
|
||||
if (!metaData.classFilter.test(sourcePath)) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
} else {
|
||||
System.out.println(sourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
String resourcePath = file.toString();
|
||||
tempZipOut.putNextEntry(new ZipEntry(resourcePath));
|
||||
tempZipOut.write(Files.readAllBytes(file));
|
||||
tempZipOut.closeEntry();
|
||||
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return tempFile;
|
||||
} catch (IOException exception) {
|
||||
throw new UncheckedIOException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static String absolutePathOf(Path path) {
|
||||
return path.toAbsolutePath().toString();
|
||||
}
|
||||
|
||||
@@ -28,10 +28,11 @@ import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.tasks.Internal;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
public abstract class AbstractLoomTask extends DefaultTask {
|
||||
public AbstractLoomTask() {
|
||||
setGroup("fabric");
|
||||
setGroup(Constants.TASK_CATEGORY);
|
||||
}
|
||||
|
||||
@Internal
|
||||
|
||||
@@ -35,13 +35,14 @@ import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.JavaExec;
|
||||
|
||||
import net.fabricmc.loom.configuration.ide.RunConfig;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
public abstract class AbstractRunTask extends JavaExec {
|
||||
private final RunConfig config;
|
||||
|
||||
public AbstractRunTask(Function<Project, RunConfig> configProvider) {
|
||||
super();
|
||||
setGroup("fabric");
|
||||
setGroup(Constants.TASK_CATEGORY);
|
||||
this.config = configProvider.apply(getProject());
|
||||
|
||||
setClasspath(config.sourceSet.getRuntimeClasspath());
|
||||
|
||||
15
src/main/java/net/fabricmc/loom/task/CleanSourcesTask.java
Normal file
15
src/main/java/net/fabricmc/loom/task/CleanSourcesTask.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package net.fabricmc.loom.task;
|
||||
|
||||
import static net.fabricmc.loom.task.GenerateSourcesTask.getMappedJarFileWithSuffix;
|
||||
|
||||
import java.nio.file.Files;
|
||||
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
public class CleanSourcesTask extends AbstractLoomTask {
|
||||
@TaskAction
|
||||
public void doTask() throws Throwable {
|
||||
Files.deleteIfExists(getMappedJarFileWithSuffix(getProject(), "-sources.jar", false).toPath());
|
||||
Files.deleteIfExists(getMappedJarFileWithSuffix(getProject(), "-sources.jar", true).toPath());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package net.fabricmc.loom.task;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import net.fabricmc.loom.api.decompilers.LoomDecompiler;
|
||||
import net.fabricmc.loom.configuration.processors.JarProcessorManager;
|
||||
import net.fabricmc.loom.configuration.processors.MinecraftProcessedProvider;
|
||||
import net.fabricmc.stitch.util.StitchUtil;
|
||||
|
||||
public class GenerateIncrementalSourcesTask extends AbstractLoomTask {
|
||||
public final LoomDecompiler decompiler;
|
||||
|
||||
@Inject
|
||||
public GenerateIncrementalSourcesTask(LoomDecompiler decompiler) {
|
||||
this.decompiler = decompiler;
|
||||
|
||||
getOutputs().upToDateWhen((o) -> false);
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void doTask() throws Throwable {
|
||||
GenerateSourcesTask.generateSources(getProject(), getClass(), decompiler, null, false, true);
|
||||
|
||||
if (getExtension().getMappingsProvider().mappedProvider instanceof MinecraftProcessedProvider) {
|
||||
JarProcessorManager processorManager = getExtension().getJarProcessorManager();
|
||||
Path unprocessedCompiledJar = GenerateSourcesTask.getMappedJarFileWithSuffix(getProject(), null, false).toPath();
|
||||
Path compiledJar = GenerateSourcesTask.getMappedJarFileWithSuffix(getProject(), null, true).toPath();
|
||||
Path unprocessedSourcesDestination = GenerateSourcesTask.getMappedJarFileWithSuffix(getProject(), "-sources.jar", false).toPath();
|
||||
Map<String, byte[]> unprocessedSources = new HashMap<>();
|
||||
Set<String> affectedSources = new HashSet<>();
|
||||
|
||||
try (FileSystem system = FileSystems.newFileSystem(URI.create("jar:" + unprocessedSourcesDestination.toUri()), new HashMap<>())) {
|
||||
Files.walkFileTree(system.getPath("/"), new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (file.toString().endsWith(".java")) {
|
||||
String sourcePath = file.toString();
|
||||
|
||||
if (sourcePath.length() >= 1 && sourcePath.charAt(0) == '/') {
|
||||
sourcePath = sourcePath.substring(1);
|
||||
}
|
||||
|
||||
sourcePath = sourcePath.substring(0, sourcePath.length() - 5);
|
||||
unprocessedSources.put(sourcePath, Files.readAllBytes(file));
|
||||
|
||||
if (processorManager.doesProcessClass(sourcePath)) {
|
||||
affectedSources.add(sourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
} catch (IOException exception) {
|
||||
exception.printStackTrace();
|
||||
unprocessedSources.clear();
|
||||
}
|
||||
|
||||
Consumer<Path> linemapConsumer = linemap -> {
|
||||
try {
|
||||
try (StitchUtil.FileSystemDelegate unprocessedFs = StitchUtil.getJarFileSystem(unprocessedCompiledJar.toFile(), true);
|
||||
StitchUtil.FileSystemDelegate processedFs = StitchUtil.getJarFileSystem(compiledJar.toFile(), true)) {
|
||||
int i = 0;
|
||||
|
||||
for (Path path : (Iterable<? extends Path>) Files.walk(processedFs.get().getPath("/"))::iterator) {
|
||||
if (path.toString().endsWith(".class")) {
|
||||
String sourcePath = path.toString();
|
||||
|
||||
if (sourcePath.length() >= 1 && sourcePath.charAt(0) == '/') {
|
||||
sourcePath = sourcePath.substring(1);
|
||||
}
|
||||
|
||||
sourcePath = sourcePath.substring(0, sourcePath.length() - 6);
|
||||
|
||||
Path unprocessedPath = unprocessedFs.get().getPath(path.toString());
|
||||
|
||||
if (Files.exists(unprocessedPath) && !processorManager.doesProcessClass(sourcePath)) {
|
||||
i++;
|
||||
Files.copy(unprocessedPath, path, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getLogger().lifecycle(":copied {} linemap remapped classes from unprocessed jar", i);
|
||||
}
|
||||
|
||||
GenerateSourcesTask.remapLinemap(getProject(), getClass(), compiledJar, linemap, true);
|
||||
} catch (IOException exception) {
|
||||
throw new UncheckedIOException(exception);
|
||||
}
|
||||
};
|
||||
|
||||
getLogger().lifecycle(":skipping {} unprocessed classes (excluding {} processed classes)", unprocessedSources.size() - affectedSources.size(), affectedSources.size());
|
||||
GenerateSourcesTask.generateSources(getProject(), getClass(), decompiler, className -> {
|
||||
int i = className.indexOf('$');
|
||||
if (i >= 0) className = className.substring(0, i);
|
||||
if (affectedSources.contains(className)) return GenerateSourcesTask.SkipState.GENERATE;
|
||||
if (unprocessedSources.containsKey(className)) return GenerateSourcesTask.SkipState.SKIP;
|
||||
return null;
|
||||
}, true, true, linemapConsumer);
|
||||
}
|
||||
}
|
||||
|
||||
public GenerateIncrementalSourcesTask setInputJar(File inputJar) {
|
||||
this.inputJar = inputJar;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -26,19 +26,34 @@ package net.fabricmc.loom.task;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.api.decompilers.DecompilationMetadata;
|
||||
import net.fabricmc.loom.api.decompilers.LoomDecompiler;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider;
|
||||
@@ -56,25 +71,100 @@ public class GenerateSourcesTask extends AbstractLoomTask {
|
||||
public GenerateSourcesTask(LoomDecompiler decompiler) {
|
||||
this.decompiler = decompiler;
|
||||
|
||||
setGroup("fabric");
|
||||
getOutputs().upToDateWhen((o) -> false);
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void doTask() throws Throwable {
|
||||
int threads = Runtime.getRuntime().availableProcessors();
|
||||
Path javaDocs = getExtension().getMappingsProvider().tinyMappings.toPath();
|
||||
Collection<Path> libraries = getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES).getFiles()
|
||||
.stream().map(File::toPath).collect(Collectors.toSet());
|
||||
generateSources(getProject(), getClass(), decompiler, null, true, false);
|
||||
}
|
||||
|
||||
DecompilationMetadata metadata = new DecompilationMetadata(threads, javaDocs, libraries);
|
||||
Path runtimeJar = getExtension().getMappingsProvider().mappedProvider.getMappedJar().toPath();
|
||||
Path sourcesDestination = getMappedJarFileWithSuffix("-sources.jar").toPath();
|
||||
Path linemap = getMappedJarFileWithSuffix("-sources.lmap").toPath();
|
||||
public static void generateSources(Project project, Class<?> taskClass, LoomDecompiler decompiler, Function<String, SkipState> additionalClassFilter, boolean processed, boolean incremental)
|
||||
throws IOException {
|
||||
generateSources(project, taskClass, decompiler, additionalClassFilter, processed, incremental, linemap -> {
|
||||
try {
|
||||
Path compiledJar = getMappedJarFileWithSuffix(project, null, processed).toPath();
|
||||
remapLinemap(project, taskClass, compiledJar, linemap, processed);
|
||||
} catch (IOException exception) {
|
||||
throw new UncheckedIOException(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void generateSources(Project project, Class<?> taskClass, LoomDecompiler decompiler, Function<String, SkipState> additionalClassFilter, boolean processed, boolean incremental, Consumer<Path> linemapConsumer)
|
||||
throws IOException {
|
||||
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
|
||||
int threads = Runtime.getRuntime().availableProcessors();
|
||||
Path javaDocs = extension.getMappingsProvider().tinyMappings.toPath();
|
||||
Collection<Path> libraries = project.getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES).getFiles()
|
||||
.stream().map(File::toPath).collect(Collectors.toSet());
|
||||
|
||||
Path runtimeJar = getMappedJarFileWithSuffix(project, null, processed).toPath();
|
||||
Path sourcesDestination = getMappedJarFileWithSuffix(project, "-sources.jar", processed).toPath();
|
||||
Path linemap = getMappedJarFileWithSuffix(project, "-sources.lmap", processed).toPath();
|
||||
Map<String, byte[]> remappedClasses = new HashMap<>();
|
||||
|
||||
if (!LoomGradlePlugin.refreshDeps && incremental && Files.exists(sourcesDestination)) {
|
||||
try (FileSystem system = FileSystems.newFileSystem(URI.create("jar:" + sourcesDestination.toUri()), new HashMap<>())) {
|
||||
Files.walkFileTree(system.getPath("/"), new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (file.toString().endsWith(".java")) {
|
||||
String sourcePath = file.toString();
|
||||
|
||||
if (sourcePath.length() >= 1 && sourcePath.charAt(0) == '/') {
|
||||
sourcePath = sourcePath.substring(1);
|
||||
}
|
||||
|
||||
remappedClasses.put(sourcePath.substring(0, sourcePath.length() - 5), Files.readAllBytes(file));
|
||||
}
|
||||
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
} catch (IOException exception) {
|
||||
exception.printStackTrace();
|
||||
remappedClasses.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Files.deleteIfExists(sourcesDestination);
|
||||
|
||||
if (incremental) {
|
||||
project.getLogger().lifecycle(":incremental source generation: skipping {} classes", remappedClasses.size());
|
||||
}
|
||||
|
||||
Function<String, SkipState> function = MoreObjects.firstNonNull(additionalClassFilter, s -> null);
|
||||
Function<String, SkipState> classFilter = remappedClasses.isEmpty() ? function : s -> {
|
||||
SkipState apply = function.apply(s);
|
||||
if (apply != null) return apply;
|
||||
int i = s.indexOf('$');
|
||||
if (i >= 0) s = s.substring(0, i);
|
||||
return remappedClasses.containsKey(s) ? SkipState.SKIP : SkipState.GENERATE;
|
||||
};
|
||||
DecompilationMetadata metadata = new DecompilationMetadata(threads, javaDocs, libraries, classFilter);
|
||||
decompiler.decompile(inputJar.toPath(), sourcesDestination, linemap, metadata);
|
||||
|
||||
if (incremental && !remappedClasses.isEmpty()) {
|
||||
try (FileSystem system = FileSystems.newFileSystem(URI.create("jar:" + sourcesDestination.toUri()), new HashMap<String, String>() {
|
||||
{
|
||||
put("create", "true");
|
||||
}
|
||||
})) {
|
||||
for (Map.Entry<String, byte[]> entry : remappedClasses.entrySet()) {
|
||||
if (additionalClassFilter != null && SkipState.SKIP != additionalClassFilter.apply(entry.getKey())) continue;
|
||||
Path path = system.getPath(entry.getKey() + ".java");
|
||||
Path parent = path.getParent();
|
||||
if (parent != null) Files.createDirectories(parent);
|
||||
Files.write(path, entry.getValue(), StandardOpenOption.CREATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void remapLinemap(Project project, Class<?> taskClass, Path compiledJar, Path linemap, boolean processed) throws IOException {
|
||||
if (Files.exists(linemap)) {
|
||||
Path linemappedJarDestination = getMappedJarFileWithSuffix("-linemapped.jar").toPath();
|
||||
Path linemappedJarDestination = getMappedJarFileWithSuffix(project, "-linemapped.jar", processed).toPath();
|
||||
|
||||
// Line map the actually jar used to run the game, not the one used to decompile
|
||||
remapLineNumbers(runtimeJar, linemap, linemappedJarDestination);
|
||||
@@ -84,12 +174,13 @@ public class GenerateSourcesTask extends AbstractLoomTask {
|
||||
}
|
||||
}
|
||||
|
||||
private void remapLineNumbers(Path oldCompiledJar, Path linemap, Path linemappedJarDestination) throws IOException {
|
||||
getProject().getLogger().info(":adjusting line numbers");
|
||||
private static void remapLineNumbers(Project project, Class<?> taskClass, Path oldCompiledJar, Path linemap, Path linemappedJarDestination)
|
||||
throws IOException {
|
||||
project.getLogger().info(":adjusting line numbers");
|
||||
LineNumberRemapper remapper = new LineNumberRemapper();
|
||||
remapper.readMappings(linemap.toFile());
|
||||
|
||||
ProgressLogger progressLogger = ProgressLogger.getProgressFactory(getProject(), getClass().getName());
|
||||
ProgressLogger progressLogger = ProgressLogger.getProgressFactory(project, taskClass.getName());
|
||||
progressLogger.start("Adjusting line numbers", "linemap");
|
||||
|
||||
try (StitchUtil.FileSystemDelegate inFs = StitchUtil.getJarFileSystem(oldCompiledJar.toFile(), true);
|
||||
@@ -100,19 +191,27 @@ public class GenerateSourcesTask extends AbstractLoomTask {
|
||||
progressLogger.completed();
|
||||
}
|
||||
|
||||
private File getMappedJarFileWithSuffix(String suffix) {
|
||||
LoomGradleExtension extension = getProject().getExtensions().getByType(LoomGradleExtension.class);
|
||||
public static File getMappedJarFileWithSuffix(Project project, String suffix, boolean processed) {
|
||||
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
|
||||
MappingsProvider mappingsProvider = extension.getMappingsProvider();
|
||||
File mappedJar = mappingsProvider.mappedProvider.getMappedJar();
|
||||
File mappedJar = processed ? mappingsProvider.mappedProvider.getMappedJar() : mappingsProvider.mappedProvider.getUnprocessedMappedJar();
|
||||
String path = mappedJar.getAbsolutePath();
|
||||
|
||||
if (!path.toLowerCase(Locale.ROOT).endsWith(".jar")) {
|
||||
throw new RuntimeException("Invalid mapped JAR path: " + path);
|
||||
}
|
||||
|
||||
if (suffix == null) {
|
||||
return new File(path);
|
||||
}
|
||||
|
||||
return new File(path.substring(0, path.length() - 4) + suffix);
|
||||
}
|
||||
|
||||
public enum SkipState {
|
||||
SKIP, GENERATE;
|
||||
}
|
||||
|
||||
@InputFile
|
||||
public File getInputJar() {
|
||||
return inputJar;
|
||||
|
||||
@@ -35,6 +35,7 @@ import net.fabricmc.loom.api.decompilers.LoomDecompiler;
|
||||
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider;
|
||||
import net.fabricmc.loom.decompilers.fernflower.FabricFernFlowerDecompiler;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
|
||||
public final class LoomTasks {
|
||||
private LoomTasks() {
|
||||
@@ -50,7 +51,7 @@ public final class LoomTasks {
|
||||
|
||||
tasks.register("remapJar", RemapJarTask.class, t -> {
|
||||
t.setDescription("Remaps the built project jar to intermediary mappings.");
|
||||
t.setGroup("fabric");
|
||||
t.setGroup(Constants.TASK_CATEGORY);
|
||||
});
|
||||
|
||||
tasks.register("downloadAssets", DownloadAssetsTask.class, t -> t.setDescription("Downloads required assets for Fabric."));
|
||||
@@ -59,6 +60,8 @@ public final class LoomTasks {
|
||||
registerIDETasks(tasks);
|
||||
registerRunTasks(tasks, project);
|
||||
registerDecompileTasks(tasks, project);
|
||||
|
||||
tasks.register("cleanSources", CleanSourcesTask.class);
|
||||
}
|
||||
|
||||
private static void registerIDETasks(TaskContainer tasks) {
|
||||
@@ -97,7 +100,6 @@ public final class LoomTasks {
|
||||
|
||||
tasks.register(taskName, RunGameTask.class, config).configure(t -> {
|
||||
t.setDescription("Starts the '" + config.getConfigName() + "' run configuration");
|
||||
t.setGroup("fabric");
|
||||
|
||||
if (config.getEnvironment().equals("client")) {
|
||||
t.dependsOn("downloadAssets");
|
||||
@@ -130,6 +132,7 @@ public final class LoomTasks {
|
||||
|
||||
for (LoomDecompiler decompiler : extension.getDecompilers()) {
|
||||
String taskName = decompiler instanceof FabricFernFlowerDecompiler ? "genSources" : "genSourcesWith" + decompiler.name();
|
||||
String incrementalTaskName = decompiler instanceof FabricFernFlowerDecompiler ? "genIncrementalSources" : "genIncrementalSourcesWith" + decompiler.name();
|
||||
// decompiler will be passed to the constructor of GenerateSourcesTask
|
||||
GenerateSourcesTask generateSourcesTask = tasks.register(taskName, GenerateSourcesTask.class, decompiler).get();
|
||||
generateSourcesTask.setInputJar(inputJar);
|
||||
@@ -137,6 +140,13 @@ public final class LoomTasks {
|
||||
if (mappingsProvider.hasUnpickDefinitions()) {
|
||||
generateSourcesTask.dependsOn(tasks.getByName("unpickJar"));
|
||||
}
|
||||
|
||||
GenerateIncrementalSourcesTask generateIncrementalSourcesTask = tasks.register(incrementalTaskName, GenerateIncrementalSourcesTask.class, decompiler).get();
|
||||
generateIncrementalSourcesTask.setInputJar(inputJar);
|
||||
|
||||
if (mappingsProvider.hasUnpickDefinitions()) {
|
||||
generateIncrementalSourcesTask.dependsOn(tasks.getByName("unpickJar"));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import net.fabricmc.loom.util.gradle.GradleSupport;
|
||||
|
||||
public class Constants {
|
||||
public static final String PLUGIN_ID = "forgified-fabric-loom";
|
||||
public static final String TASK_CATEGORY = "loom";
|
||||
public static final String LIBRARIES_BASE = "https://libraries.minecraft.net/";
|
||||
public static final String RESOURCES_BASE = "http://resources.download.minecraft.net/";
|
||||
public static final String VERSION_MANIFESTS = "https://launchermeta.mojang.com/mc/game/version_manifest_v2.json";
|
||||
|
||||
@@ -24,111 +24,108 @@
|
||||
|
||||
package net.fabricmc.loom.util.srg;
|
||||
|
||||
import static me.shedaniel.architectury.refmapremapper.utils.DescriptorRemapper.remapDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.Objects;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import me.shedaniel.architectury.refmapremapper.remapper.SimpleReferenceRemapper;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.zeroturnaround.zip.ZipUtil;
|
||||
import org.zeroturnaround.zip.transform.StringZipEntryTransformer;
|
||||
import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry;
|
||||
|
||||
import net.fabricmc.loom.util.function.CollectionUtil;
|
||||
import net.fabricmc.mapping.tree.TinyTree;
|
||||
|
||||
/**
|
||||
* Remaps AT classes from SRG to Yarn.
|
||||
* Remaps AT contents.
|
||||
*
|
||||
* @author Juuz
|
||||
*/
|
||||
public final class AtRemapper {
|
||||
public static void remap(Logger logger, Path jar, TinyTree mappings) throws IOException {
|
||||
ZipUtil.transformEntries(jar.toFile(), new ZipEntryTransformerEntry[] {(new ZipEntryTransformerEntry("META-INF/accesstransformer.cfg", new StringZipEntryTransformer() {
|
||||
public static void remapSrgToNamed(Logger logger, Path jar, TinyTree mappings) throws IOException {
|
||||
ZipUtil.transformEntry(jar.toFile(), "META-INF/accesstransformer.cfg", new StringZipEntryTransformer() {
|
||||
@Override
|
||||
protected String transform(ZipEntry zipEntry, String input) {
|
||||
String[] lines = input.split("\n");
|
||||
List<String> output = new ArrayList<>(lines.length);
|
||||
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
String line = lines[i].trim();
|
||||
|
||||
if (line.startsWith("#") || Strings.isBlank(line)) {
|
||||
output.add(i, line);
|
||||
continue;
|
||||
return remapAt(logger, input, new SimpleReferenceRemapper.Remapper() {
|
||||
@Override
|
||||
@Nullable
|
||||
public String mapClass(String value) {
|
||||
return mappings.getClasses().stream()
|
||||
.filter(classDef -> Objects.equals(classDef.getName("srg"), value))
|
||||
.findFirst()
|
||||
.map(classDef -> classDef.getName("named"))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
String[] parts = line.split("\\s+");
|
||||
|
||||
if (parts.length < 2) {
|
||||
logger.warn("Invalid AT Line: " + line);
|
||||
output.add(i, line);
|
||||
continue;
|
||||
@Override
|
||||
@Nullable
|
||||
public String mapMethod(@Nullable String className, String methodName, String methodDescriptor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String name = parts[1].replace('.', '/');
|
||||
parts[1] = CollectionUtil.find(
|
||||
mappings.getClasses(),
|
||||
def -> def.getName("srg").equals(name)
|
||||
).map(def -> def.getName("named")).orElse(name).replace('/', '.');
|
||||
|
||||
if (parts.length >= 3) {
|
||||
if (parts[2].contains("(")) {
|
||||
parts[2] = parts[2].substring(0, parts[2].indexOf('(')) + remapDescriptor(parts[2].substring(parts[2].indexOf('(')), s -> {
|
||||
return CollectionUtil.find(
|
||||
mappings.getClasses(),
|
||||
def -> def.getName("srg").equals(s)
|
||||
).map(def -> def.getName("named")).orElse(s);
|
||||
});
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public String mapField(@Nullable String className, String fieldName, String fieldDescriptor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
output.add(i, String.join(" ", parts));
|
||||
}
|
||||
|
||||
return String.join("\n", output);
|
||||
});
|
||||
}
|
||||
}))});
|
||||
});
|
||||
}
|
||||
|
||||
private static String remapDescriptor(String original, UnaryOperator<String> classMappings) {
|
||||
try {
|
||||
StringReader reader = new StringReader(original);
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean insideClassName = false;
|
||||
StringBuilder className = new StringBuilder();
|
||||
public static String remapAt(Logger logger, String sourceAt, SimpleReferenceRemapper.Remapper remapper) {
|
||||
String[] lines = sourceAt.split("\n");
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
while (true) {
|
||||
int c = reader.read();
|
||||
for (String line : lines) {
|
||||
{
|
||||
int indexOf = line.indexOf('#');
|
||||
|
||||
if (c == -1) {
|
||||
break;
|
||||
if (indexOf != -1) {
|
||||
line = line.substring(0, indexOf);
|
||||
}
|
||||
|
||||
if ((char) c == ';') {
|
||||
insideClassName = false;
|
||||
result.append(classMappings.apply(className.toString()));
|
||||
}
|
||||
line = line.trim();
|
||||
}
|
||||
|
||||
if (insideClassName) {
|
||||
className.append((char) c);
|
||||
if (Strings.isBlank(line)) {
|
||||
builder.append(line).append('\n');
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] parts = line.split("\\s+");
|
||||
|
||||
if (parts.length < 2) {
|
||||
logger.warn("Invalid AT Line: " + line);
|
||||
builder.append(line).append('\n');
|
||||
continue;
|
||||
}
|
||||
|
||||
String originalClassName = parts[1].replace('.', '/');
|
||||
parts[1] = either(remapper.mapClass(originalClassName), parts[1]).replace('/', '.');
|
||||
|
||||
if (parts.length >= 3) {
|
||||
if (parts[2].contains("(")) {
|
||||
String methodName = parts[2].substring(0, parts[2].indexOf('('));
|
||||
String methodDescriptor = parts[2].substring(parts[2].indexOf('('));
|
||||
parts[2] = either(remapper.mapMethod(originalClassName, methodName, methodDescriptor), methodName)
|
||||
+ remapDescriptor(methodDescriptor, it -> either(remapper.mapClass(it), it));
|
||||
} else {
|
||||
result.append((char) c);
|
||||
}
|
||||
|
||||
if (!insideClassName && (char) c == 'L') {
|
||||
insideClassName = true;
|
||||
className.setLength(0);
|
||||
parts[2] = either(remapper.mapField(originalClassName, parts[2], null), parts[2]);
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
builder.append(String.join(" ", parts)).append('\n');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static <T> T either(@Nullable T first, @Nullable T second) {
|
||||
return first == null ? second : first;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user