MappingsProvider: Add mapping file augmented with SRG when Forge support is enabled

This commit is contained in:
Juuxel
2020-07-30 16:03:35 +03:00
parent 85eb839db1
commit 3ee61ced8e
4 changed files with 215 additions and 0 deletions

View File

@@ -47,6 +47,7 @@ import org.zeroturnaround.zip.ZipUtil;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.DependencyProvider;
import net.fabricmc.loom.util.DownloadUtil;
import net.fabricmc.loom.util.srg.SrgMerger;
import net.fabricmc.mapping.reader.v2.TinyV2Factory;
import net.fabricmc.mapping.tree.TinyTree;
import net.fabricmc.stitch.Command;
@@ -72,6 +73,7 @@ public class MappingsProvider extends DependencyProvider {
public File tinyMappings;
public File tinyMappingsJar;
public File mappingsMixinExport;
private Path tinyMappingsWithSrg;
public MappingsProvider(Project project) {
super(project);
@@ -85,6 +87,14 @@ public class MappingsProvider extends DependencyProvider {
return MappingsCache.INSTANCE.get(tinyMappings.toPath());
}
public TinyTree getMappingsWithSrg() throws IOException {
if (getExtension().isForge()) {
return MappingsCache.INSTANCE.get(tinyMappingsWithSrg);
}
throw new UnsupportedOperationException("Not running with Forge support.");
}
@Override
public void provide(DependencyInfo dependency, Consumer<Runnable> postPopulationScheduler) throws Exception {
MinecraftProvider minecraftProvider = getDependencyManager().getProvider(MinecraftProvider.class);
@@ -129,6 +139,7 @@ public class MappingsProvider extends DependencyProvider {
tinyMappings = mappingsDir.resolve(StringUtils.removeSuffix(mappingsJar.getName(), ".jar") + ".tiny").toFile();
tinyMappingsJar = new File(getExtension().getUserCache(), mappingsJar.getName().replace(".jar", "-" + jarClassifier + ".jar"));
tinyMappingsWithSrg = mappingsDir.resolve(StringUtils.removeSuffix(mappingsJar.getName(), ".jar") + "-srg.tiny");
if (!tinyMappings.exists() || isRefreshDeps()) {
storeMappings(getProject(), minecraftProvider, mappingsJar.toPath());
@@ -138,6 +149,10 @@ public class MappingsProvider extends DependencyProvider {
ZipUtil.pack(new ZipEntrySource[] {new FileSource("mappings/mappings.tiny", tinyMappings)}, tinyMappingsJar);
}
if (getExtension().isForge() && (Files.notExists(tinyMappingsWithSrg) || isRefreshDeps())) {
SrgMerger.mergeSrg(getExtension().getMcpConfigProvider().getSrg().toPath(), tinyMappings.toPath(), tinyMappingsWithSrg);
}
addDependency(tinyMappingsJar, Constants.MAPPINGS_FINAL);
JarProcessorManager processorManager = new JarProcessorManager(getProject());

View File

@@ -0,0 +1,51 @@
package net.fabricmc.loom.util.function;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* Stream-like utilities for working with collections.
*
* @author Juuz
*/
public final class CollectionUtil {
/**
* Finds the first element matching the predicate.
*
* @param collection the collection to be searched
* @param filter the predicate to be matched
* @param <E> the element type
* @return the first matching element, or empty if none match
*/
public static <E> Optional<E> find(Iterable<? extends E> collection, Predicate<? super E> filter) {
for (E e : collection) {
if (filter.test(e)) {
return Optional.of(e);
}
}
return Optional.empty();
}
/**
* Transforms the collection with a function.
*
* @param collection the source collection
* @param transform the transformation function
* @param <A> the source type
* @param <B> the target type
* @return a mutable list with the transformed entries
*/
public static <A, B> List<B> map(Iterable<? extends A> collection, Function<? super A, ? extends B> transform) {
ArrayList<B> result = new ArrayList<>();
for (A a : collection) {
result.add(transform.apply(a));
}
return result;
}
}

View File

@@ -0,0 +1,12 @@
package net.fabricmc.loom.util.srg;
/**
* An exception that occurs when processing obfuscation mappings.
*
* @author Juuz
*/
public class MappingException extends RuntimeException {
public MappingException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,137 @@
package net.fabricmc.loom.util.srg;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import org.cadixdev.lorenz.MappingSet;
import org.cadixdev.lorenz.io.srg.tsrg.TSrgReader;
import org.cadixdev.lorenz.model.ClassMapping;
import org.cadixdev.lorenz.model.FieldMapping;
import org.cadixdev.lorenz.model.InnerClassMapping;
import org.cadixdev.lorenz.model.MethodMapping;
import org.cadixdev.lorenz.model.TopLevelClassMapping;
import net.fabricmc.loom.util.function.CollectionUtil;
import net.fabricmc.mapping.tree.ClassDef;
import net.fabricmc.mapping.tree.FieldDef;
import net.fabricmc.mapping.tree.MethodDef;
import net.fabricmc.mapping.tree.TinyMappingFactory;
import net.fabricmc.mapping.tree.TinyTree;
import net.fabricmc.stitch.commands.tinyv2.TinyClass;
import net.fabricmc.stitch.commands.tinyv2.TinyField;
import net.fabricmc.stitch.commands.tinyv2.TinyFile;
import net.fabricmc.stitch.commands.tinyv2.TinyHeader;
import net.fabricmc.stitch.commands.tinyv2.TinyMethod;
import net.fabricmc.stitch.commands.tinyv2.TinyV2Writer;
/**
* Utilities for merging SRG mappings.
*
* @author Juuz
*/
public final class SrgMerger {
/**
* Merges SRG mappings with a tiny mappings tree through the obf names.
*
* @param srg the SRG file in .tsrg format
* @param tiny the tiny file
* @param out the output file, will be in tiny v2
* @throws IOException if an IO error occurs while reading or writing the mappings
* @throws MappingException if the input tiny tree's default namespace is not 'official'
* or if an element mentioned in the SRG file does not have tiny mappings
*/
public static void mergeSrg(Path srg, Path tiny, Path out) throws IOException, MappingException {
MappingSet arr;
TinyTree foss;
try (TSrgReader reader = new TSrgReader(Files.newBufferedReader(srg))) {
arr = reader.read();
}
try (BufferedReader reader = Files.newBufferedReader(tiny)) {
foss = TinyMappingFactory.loadWithDetection(reader);
}
List<String> namespaces = new ArrayList<>(foss.getMetadata().getNamespaces());
namespaces.add(1, "srg");
if (!"official".equals(namespaces.get(0))) {
throw new MappingException("Mapping file " + tiny + " does not have the 'official' namespace as the default!");
}
TinyHeader header = new TinyHeader(namespaces, 2, 0, Collections.emptyMap());
List<TinyClass> classes = new ArrayList<>();
for (TopLevelClassMapping klass : arr.getTopLevelClassMappings()) {
classToTiny(foss, namespaces, klass, classes::add);
}
TinyFile file = new TinyFile(header, classes);
TinyV2Writer.write(file, out);
}
private static void classToTiny(TinyTree foss, List<String> namespaces, ClassMapping<?, ?> klass, Consumer<TinyClass> classConsumer) {
String obf = klass.getFullObfuscatedName();
String srg = klass.getFullDeobfuscatedName();
ClassDef classDef = foss.getDefaultNamespaceClassMap().get(obf);
if (classDef == null) {
throw new MappingException("Missing class: " + obf + " (srg: " + srg + ")");
}
List<String> classNames = CollectionUtil.map(
namespaces,
namespace -> "srg".equals(namespace) ? srg : classDef.getName(namespace)
);
List<TinyMethod> methods = new ArrayList<>();
List<TinyField> fields = new ArrayList<>();
for (MethodMapping method : klass.getMethodMappings()) {
MethodDef def = CollectionUtil.find(
classDef.getMethods(),
m -> m.getName("official").equals(method.getObfuscatedName()) && m.getDescriptor("official").equals(method.getObfuscatedDescriptor())
).orElseThrow(() -> new MappingException("Missing method: " + method.getFullObfuscatedName() + " (srg: " + method.getFullDeobfuscatedName() + ")"));
List<String> methodNames = CollectionUtil.map(
namespaces,
namespace -> "srg".equals(namespace) ? method.getDeobfuscatedName() : def.getName(namespace)
);
methods.add(new TinyMethod(
def.getDescriptor("official"), methodNames,
/* parameters */ Collections.emptyList(),
/* locals */ Collections.emptyList(),
/* comments */ Collections.emptyList()
));
}
for (FieldMapping field : klass.getFieldMappings()) {
FieldDef def = CollectionUtil.find(
classDef.getFields(),
f -> f.getName("official").equals(field.getObfuscatedName())
).orElseThrow(() -> new MappingException("Missing field: " + field.getFullObfuscatedName() + " (srg: " + field.getFullDeobfuscatedName() + ")"));
List<String> fieldNames = CollectionUtil.map(
namespaces,
namespace -> "srg".equals(namespace) ? field.getDeobfuscatedName() : field.getObfuscatedName()
);
fields.add(new TinyField(def.getDescriptor("official"), fieldNames, Collections.emptyList()));
}
TinyClass tinyClass = new TinyClass(classNames, methods, fields, Collections.emptyList());
classConsumer.accept(tinyClass);
for (InnerClassMapping innerKlass : klass.getInnerClassMappings()) {
classToTiny(foss, namespaces, innerKlass, classConsumer);
}
}
}