diff --git a/src/main/java/net/fabricmc/loom/api/mappings/layered/MappingsNamespace.java b/src/main/java/net/fabricmc/loom/api/mappings/layered/MappingsNamespace.java
index ae32f7f..4e3a628 100644
--- a/src/main/java/net/fabricmc/loom/api/mappings/layered/MappingsNamespace.java
+++ b/src/main/java/net/fabricmc/loom/api/mappings/layered/MappingsNamespace.java
@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
- * Copyright (c) 2016-2021 FabricMC
+ * Copyright (c) 2016-2022 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +26,8 @@ package net.fabricmc.loom.api.mappings.layered;
import java.util.Locale;
+import org.jetbrains.annotations.Nullable;
+
/**
* The standard namespaces used by loom.
*/
@@ -49,6 +51,21 @@ public enum MappingsNamespace {
*/
NAMED;
+ /**
+ * Gets a {@code MappingsNamespace} from a namespace string.
+ *
+ * @param namespace the name of the namespace as a lowercase string
+ * @return the {@code MappingsNamespace}, or null if not found
+ */
+ public static @Nullable MappingsNamespace of(String namespace) {
+ return switch (namespace) {
+ case "official" -> OFFICIAL;
+ case "intermediary" -> INTERMEDIARY;
+ case "named" -> NAMED;
+ default -> null;
+ };
+ }
+
@Override
public String toString() {
return name().toLowerCase(Locale.ROOT);
diff --git a/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/FileMappingsSpecBuilder.java b/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/FileMappingsSpecBuilder.java
new file mode 100644
index 0000000..1dc71f6
--- /dev/null
+++ b/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/FileMappingsSpecBuilder.java
@@ -0,0 +1,101 @@
+/*
+ * This file is part of fabric-loom, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2021-2022 FabricMC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package net.fabricmc.loom.api.mappings.layered.spec;
+
+import org.jetbrains.annotations.ApiStatus;
+
+import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
+
+/**
+ * A builder for a file mappings layer.
+ * This layer type supports any mapping type that mapping-io can read.
+ */
+@ApiStatus.Experimental
+public interface FileMappingsSpecBuilder {
+ /**
+ * Sets the mapping path inside a zip or jar.
+ * This will have no effect if the file of this mapping spec
+ * is not a zip.
+ *
+ *
Path components within the path should be separated with {@code /}.
+ *
+ *
The default mapping path is {@code mappings/mappings.tiny}, matching regular mapping dependency jars
+ * such as Yarn's.
+ *
+ * @param mappingPath the mapping path, or null if a bare file
+ * @return this builder
+ */
+ FileMappingsSpecBuilder mappingPath(String mappingPath);
+
+ /**
+ * Sets the fallback namespaces. They will be used
+ * if the mapping format itself doesn't provide namespaces with names
+ * (e.g. Enigma mappings).
+ *
+ *
The default fallback namespaces are {@code intermediary} as the source namespace
+ * and {@code named} as the target namespace as in Yarn.
+ *
+ * @param sourceNamespace the fallback source namespace
+ * @param targetNamespace the fallback target namespace
+ * @return this builder
+ */
+ FileMappingsSpecBuilder fallbackNamespaces(String sourceNamespace, String targetNamespace);
+
+ /**
+ * Marks that the file contains Enigma mappings.
+ * Because they are stored in a directory, the format cannot be auto-detected.
+ *
+ * @return this builder
+ */
+ FileMappingsSpecBuilder enigmaMappings();
+
+ /**
+ * Sets the merge namespace of this mappings spec.
+ *
+ *
The merge namespace is the namespace that is used to match up this layer's
+ * names to the rest of the mappings. For example, Yarn mappings should be merged through
+ * the intermediary names.
+ *
+ *
The default merge namespace is {@link MappingsNamespace#INTERMEDIARY}.
+ *
+ * @param namespace the new merge namespace
+ * @return this builder
+ */
+ FileMappingsSpecBuilder mergeNamespace(MappingsNamespace namespace);
+
+ /**
+ * Sets the merge namespace of this mappings spec.
+ *
+ *
The merge namespace is the namespace that is used to match up this layer's
+ * names to the rest of the mappings. For example, Yarn mappings should be merged through
+ * the intermediary names.
+ *
+ *
The default merge namespace is {@code intermediary}.
+ *
+ * @param namespace the new merge namespace
+ * @return this builder
+ */
+ FileMappingsSpecBuilder mergeNamespace(String namespace);
+}
diff --git a/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/FileSpec.java b/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/FileSpec.java
index 5af12f7..2f9bd59 100644
--- a/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/FileSpec.java
+++ b/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/FileSpec.java
@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
- * Copyright (c) 2021 FabricMC
+ * Copyright (c) 2021-2022 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -50,9 +50,10 @@ public interface FileSpec {
*
The parameter will be evaluated like this:
*
* - {@link File}, {@link Path} and {@link FileSystemLocation} will be resolved as local files
- * - {@link Provider} (including {@link org.gradle.api.provider.Property} will recursively be resolved as its current value
+ * - {@link Provider} (including {@link org.gradle.api.provider.Property}) will recursively be resolved as its current value
* - {@link CharSequence} (including {@link String} and {@link groovy.lang.GString}) will be resolved as Maven dependencies
* - {@link Dependency} will be resolved as any dependency
+ * - {@code FileSpec} will just return the spec itself
*
*
* @param o the file notation
@@ -73,6 +74,8 @@ public interface FileSpec {
return createFromFile(p);
} else if (o instanceof FileSystemLocation l) {
return createFromFile(l);
+ } else if (o instanceof FileSpec s) {
+ return s;
}
throw new UnsupportedOperationException("Cannot create FileSpec from object of type:" + o.getClass().getCanonicalName());
diff --git a/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/LayeredMappingSpecBuilder.java b/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/LayeredMappingSpecBuilder.java
index e3faef1..faaaba2 100644
--- a/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/LayeredMappingSpecBuilder.java
+++ b/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/LayeredMappingSpecBuilder.java
@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
- * Copyright (c) 2021 FabricMC
+ * Copyright (c) 2021-2022 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -77,4 +77,40 @@ public interface LayeredMappingSpecBuilder {
*/
@ApiStatus.Experimental
LayeredMappingSpecBuilder signatureFix(Object object);
+
+ /**
+ * Add a layer that uses a mappings file or directory.
+ *
+ * @param file the file notation for a {@link FileSpec}
+ * @return this builder
+ * @see FileMappingsSpecBuilder
+ */
+ @ApiStatus.Experimental
+ default LayeredMappingSpecBuilder mappings(Object file) {
+ return mappings(file, builder -> { });
+ }
+
+ /**
+ * Configure and add a layer that uses a mappings file or directory.
+ *
+ * @param file the file notation for a {@link FileSpec}
+ * @param closure the configuration action
+ * @return this builder
+ * @see FileMappingsSpecBuilder
+ */
+ @ApiStatus.Experimental
+ default LayeredMappingSpecBuilder mappings(Object file, @DelegatesTo(value = FileMappingsSpecBuilder.class, strategy = Closure.DELEGATE_FIRST) Closure> closure) {
+ return mappings(file, new ClosureAction<>(closure));
+ }
+
+ /**
+ * Configure and add a layer that uses a mappings file or directory.
+ *
+ * @param file the file notation for a {@link FileSpec}
+ * @param action the configuration action
+ * @return this builder
+ * @see FileMappingsSpecBuilder
+ */
+ @ApiStatus.Experimental
+ LayeredMappingSpecBuilder mappings(Object file, Action super FileMappingsSpecBuilder> action);
}
diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpecBuilderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpecBuilderImpl.java
index b374625..26dd21b 100644
--- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpecBuilderImpl.java
+++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpecBuilderImpl.java
@@ -30,12 +30,14 @@ import java.util.List;
import org.gradle.api.Action;
+import net.fabricmc.loom.api.mappings.layered.spec.FileMappingsSpecBuilder;
import net.fabricmc.loom.api.mappings.layered.spec.FileSpec;
import net.fabricmc.loom.api.mappings.layered.spec.LayeredMappingSpecBuilder;
import net.fabricmc.loom.api.mappings.layered.spec.MappingsSpec;
import net.fabricmc.loom.api.mappings.layered.spec.MojangMappingsSpecBuilder;
import net.fabricmc.loom.api.mappings.layered.spec.ParchmentMappingsSpecBuilder;
import net.fabricmc.loom.configuration.providers.mappings.extras.signatures.SignatureFixesSpec;
+import net.fabricmc.loom.configuration.providers.mappings.file.FileMappingsSpecBuilderImpl;
import net.fabricmc.loom.configuration.providers.mappings.intermediary.IntermediaryMappingsSpec;
import net.fabricmc.loom.configuration.providers.mappings.mojmap.MojangMappingsSpecBuilderImpl;
import net.fabricmc.loom.configuration.providers.mappings.parchment.ParchmentMappingsSpecBuilderImpl;
@@ -68,6 +70,13 @@ public class LayeredMappingSpecBuilderImpl implements LayeredMappingSpecBuilder
return addLayer(new SignatureFixesSpec(FileSpec.create(object)));
}
+ @Override
+ public LayeredMappingSpecBuilder mappings(Object file, Action super FileMappingsSpecBuilder> action) {
+ FileMappingsSpecBuilderImpl builder = FileMappingsSpecBuilderImpl.builder(FileSpec.create(file));
+ action.execute(builder);
+ return addLayer(builder.build());
+ }
+
public LayeredMappingSpec build() {
List> builtLayers = new LinkedList<>();
// Intermediary is always the base layer
diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/file/FileMappingsLayer.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/file/FileMappingsLayer.java
new file mode 100644
index 0000000..9cc0a5c
--- /dev/null
+++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/file/FileMappingsLayer.java
@@ -0,0 +1,85 @@
+/*
+ * This file is part of fabric-loom, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 FabricMC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package net.fabricmc.loom.configuration.providers.mappings.file;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+
+import net.fabricmc.loom.api.mappings.layered.MappingLayer;
+import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
+import net.fabricmc.loom.configuration.providers.mappings.intermediary.IntermediaryMappingLayer;
+import net.fabricmc.loom.util.FileSystemUtil;
+import net.fabricmc.loom.util.ZipUtils;
+import net.fabricmc.mappingio.MappingReader;
+import net.fabricmc.mappingio.MappingUtil;
+import net.fabricmc.mappingio.MappingVisitor;
+import net.fabricmc.mappingio.adapter.MappingNsRenamer;
+import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch;
+import net.fabricmc.mappingio.format.MappingFormat;
+
+public record FileMappingsLayer(
+ Path path, String mappingPath,
+ String fallbackSourceNamespace, String fallbackTargetNamespace,
+ boolean enigma, // Enigma cannot be automatically detected since it's stored in a directory.
+ String mergeNamespace
+) implements MappingLayer {
+ @Override
+ public void visit(MappingVisitor mappingVisitor) throws IOException {
+ // Bare file
+ if (!ZipUtils.isZip(path)) {
+ visit(path, mappingVisitor);
+ } else {
+ try (FileSystemUtil.Delegate fileSystem = FileSystemUtil.getJarFileSystem(path)) {
+ visit(fileSystem.get().getPath(mappingPath), mappingVisitor);
+ }
+ }
+ }
+
+ private void visit(Path path, MappingVisitor mappingVisitor) throws IOException {
+ MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(mappingVisitor, mergeNamespace.toString());
+
+ // Replace the default fallback namespaces with
+ // our fallback namespaces if potentially needed.
+ Map fallbackNamespaceReplacements = Map.of(
+ MappingUtil.NS_SOURCE_FALLBACK, fallbackSourceNamespace,
+ MappingUtil.NS_TARGET_FALLBACK, fallbackTargetNamespace
+ );
+ MappingNsRenamer renamer = new MappingNsRenamer(nsSwitch, fallbackNamespaceReplacements);
+
+ MappingReader.read(path, enigma ? MappingFormat.ENIGMA : null, renamer);
+ }
+
+ @Override
+ public MappingsNamespace getSourceNamespace() {
+ return MappingsNamespace.of(mergeNamespace);
+ }
+
+ @Override
+ public List> dependsOn() {
+ return List.of(IntermediaryMappingLayer.class);
+ }
+}
diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/file/FileMappingsSpec.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/file/FileMappingsSpec.java
new file mode 100644
index 0000000..5d8a166
--- /dev/null
+++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/file/FileMappingsSpec.java
@@ -0,0 +1,41 @@
+/*
+ * This file is part of fabric-loom, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 FabricMC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package net.fabricmc.loom.configuration.providers.mappings.file;
+
+import net.fabricmc.loom.api.mappings.layered.MappingContext;
+import net.fabricmc.loom.api.mappings.layered.spec.FileSpec;
+import net.fabricmc.loom.api.mappings.layered.spec.MappingsSpec;
+
+public record FileMappingsSpec(
+ FileSpec fileSpec, String mappingPath,
+ String fallbackSourceNamespace, String fallbackTargetNamespace,
+ boolean enigma,
+ String mergeNamespace
+) implements MappingsSpec {
+ @Override
+ public FileMappingsLayer createLayer(MappingContext context) {
+ return new FileMappingsLayer(fileSpec.get(context), mappingPath, fallbackSourceNamespace, fallbackTargetNamespace, enigma, mergeNamespace);
+ }
+}
diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/file/FileMappingsSpecBuilderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/file/FileMappingsSpecBuilderImpl.java
new file mode 100644
index 0000000..53681ad
--- /dev/null
+++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/file/FileMappingsSpecBuilderImpl.java
@@ -0,0 +1,94 @@
+/*
+ * This file is part of fabric-loom, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 FabricMC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package net.fabricmc.loom.configuration.providers.mappings.file;
+
+import java.util.Objects;
+
+import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
+import net.fabricmc.loom.api.mappings.layered.spec.FileMappingsSpecBuilder;
+import net.fabricmc.loom.api.mappings.layered.spec.FileSpec;
+
+public class FileMappingsSpecBuilderImpl implements FileMappingsSpecBuilder {
+ /**
+ * The mapping path of regular mapping dependencies.
+ */
+ private static final String DEFAULT_MAPPING_PATH = "mappings/mappings.tiny";
+
+ private final FileSpec fileSpec;
+ private String mappingPath = DEFAULT_MAPPING_PATH;
+ private String fallbackSourceNamespace = MappingsNamespace.INTERMEDIARY.toString();
+ private String fallbackTargetNamespace = MappingsNamespace.NAMED.toString();
+ private boolean enigma = false;
+ private String mergeNamespace = MappingsNamespace.INTERMEDIARY.toString();
+
+ private FileMappingsSpecBuilderImpl(FileSpec fileSpec) {
+ this.fileSpec = fileSpec;
+ }
+
+ public static FileMappingsSpecBuilderImpl builder(FileSpec fileSpec) {
+ return new FileMappingsSpecBuilderImpl(fileSpec);
+ }
+
+ @Override
+ public FileMappingsSpecBuilderImpl mappingPath(String mappingPath) {
+ this.mappingPath = Objects.requireNonNull(mappingPath, "mapping path cannot be null");
+ return this;
+ }
+
+ @Override
+ public FileMappingsSpecBuilderImpl fallbackNamespaces(String sourceNamespace, String targetNamespace) {
+ fallbackSourceNamespace = Objects.requireNonNull(sourceNamespace, "fallback source namespace cannot be null");
+ fallbackTargetNamespace = Objects.requireNonNull(targetNamespace, "fallback target namespace cannot be null");
+ return this;
+ }
+
+ @Override
+ public FileMappingsSpecBuilderImpl enigmaMappings() {
+ enigma = true;
+ return this;
+ }
+
+ @Override
+ public FileMappingsSpecBuilderImpl mergeNamespace(MappingsNamespace namespace) {
+ mergeNamespace = Objects.requireNonNull(namespace, "merge namespace cannot be null").toString();
+ return this;
+ }
+
+ @Override
+ public FileMappingsSpecBuilderImpl mergeNamespace(String namespace) {
+ Objects.requireNonNull(namespace, "merge namespace cannot be null");
+
+ if (MappingsNamespace.of(namespace) == null) {
+ throw new IllegalArgumentException("Namespace '" + namespace + "' is unsupported! It must be either 'official', 'intermediary' or 'named'.");
+ }
+
+ mergeNamespace = namespace;
+ return this;
+ }
+
+ public FileMappingsSpec build() {
+ return new FileMappingsSpec(fileSpec, mappingPath, fallbackSourceNamespace, fallbackTargetNamespace, enigma, mergeNamespace);
+ }
+}
diff --git a/src/main/java/net/fabricmc/loom/util/ZipUtils.java b/src/main/java/net/fabricmc/loom/util/ZipUtils.java
index 2843a50..d1e2f1b 100644
--- a/src/main/java/net/fabricmc/loom/util/ZipUtils.java
+++ b/src/main/java/net/fabricmc/loom/util/ZipUtils.java
@@ -25,6 +25,7 @@
package net.fabricmc.loom.util;
import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
@@ -47,6 +48,22 @@ import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.LoomGradlePlugin;
public class ZipUtils {
+ public static boolean isZip(Path zip) throws IOException {
+ if (Files.notExists(zip)) {
+ throw new NoSuchFileException("Cannot check if '" + zip + "' is a zip because it doesn't exist!");
+ }
+
+ if (Files.isRegularFile(zip)) {
+ try (DataInputStream in = new DataInputStream(Files.newInputStream(zip))) {
+ int header = in.readInt();
+ // See https://en.wikipedia.org/wiki/List_of_file_signatures
+ return header == 0x504B0304 || header == 0x504B0506 || header == 0x504B0708;
+ }
+ } else {
+ return false;
+ }
+ }
+
public static boolean contains(Path zip, String path) {
try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(zip, false)) {
Path fsPath = fs.get().getPath(path);
diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/ZipUtilsTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/ZipUtilsTest.groovy
index 921edff..80709e1 100644
--- a/src/test/groovy/net/fabricmc/loom/test/unit/ZipUtilsTest.groovy
+++ b/src/test/groovy/net/fabricmc/loom/test/unit/ZipUtilsTest.groovy
@@ -121,4 +121,32 @@ class ZipUtilsTest extends Specification {
outputFile.exists()
outputFile.text == "This is a test of unpacking all"
}
+
+ def "is zip"() {
+ setup:
+ // Create zip
+ def dir = Files.createTempDirectory("loom-zip-test")
+ def zip = Files.createTempFile("loom-zip-test", ".zip")
+ def fileInside = dir.resolve("text.txt")
+ Files.writeString(fileInside, "hello world")
+ ZipUtils.pack(dir, zip)
+
+ when:
+ def result = ZipUtils.isZip(zip)
+
+ then:
+ result
+ }
+
+ def "is not zip"() {
+ setup:
+ def textFile = Files.createTempFile("loom-zip-test", ".txt")
+ Files.writeString(textFile, "hello world")
+
+ when:
+ def result = ZipUtils.isZip(textFile)
+
+ then:
+ !result
+ }
}
diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/FileMappingLayerTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/FileMappingLayerTest.groovy
new file mode 100644
index 0000000..b983cf4
--- /dev/null
+++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/FileMappingLayerTest.groovy
@@ -0,0 +1,122 @@
+/*
+ * This file is part of fabric-loom, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 FabricMC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package net.fabricmc.loom.test.unit.layeredmappings
+
+import net.fabricmc.loom.api.mappings.layered.MappingsNamespace
+import net.fabricmc.loom.api.mappings.layered.spec.FileSpec
+import net.fabricmc.loom.configuration.providers.mappings.file.FileMappingsSpecBuilderImpl
+import net.fabricmc.loom.configuration.providers.mappings.intermediary.IntermediaryMappingsSpec
+import net.fabricmc.loom.util.DownloadUtil
+import net.fabricmc.loom.util.ZipUtils
+import spock.lang.Unroll
+
+import java.nio.file.Path
+import java.util.function.Consumer
+
+class FileMappingLayerTest extends LayeredMappingsSpecification {
+ @Unroll
+ def "read Yarn mappings from #setupType.displayName"() {
+ setup:
+ intermediaryUrl = INTERMEDIARY_1_17_URL
+ mockMinecraftProvider.getVersionInfo() >> VERSION_META_1_17
+ setupType.setup.delegate = this
+ def mappingFile = setupType.setup.call()
+ when:
+ def builder = FileMappingsSpecBuilderImpl.builder(FileSpec.create(mappingFile))
+ setupType.mappingsSpec.accept(builder)
+ def mappings = getLayeredMappings(
+ new IntermediaryMappingsSpec(),
+ builder.build()
+ )
+ then:
+ mappings.srcNamespace == "named"
+ mappings.dstNamespaces == ["intermediary", "official"]
+ mappings.classes.size() == 6111
+ mappings.classes[0].srcName == "net/minecraft/block/FenceBlock"
+ mappings.classes[0].getDstName(0) == "net/minecraft/class_2354"
+ mappings.classes[0].fields[0].srcName == "cullingShapes"
+ mappings.classes[0].fields[0].getDstName(0) == "field_11066"
+ mappings.classes[0].methods[0].srcName == "canConnectToFence"
+ mappings.classes[0].methods[0].getDstName(0) == "method_26375"
+ mappings.classes[0].methods[0].args[0].srcName == "state"
+ where:
+ setupType << YarnSetupType.values()
+ }
+
+ // Also tests the custom fallback namespace and source namespace functionality
+ def "read Mojang mappings from proguard"() {
+ setup:
+ intermediaryUrl = INTERMEDIARY_1_17_URL
+ mockMinecraftProvider.getVersionInfo() >> VERSION_META_1_17
+ def mappingsDownload = VERSION_META_1_17.download('client_mappings')
+ def mappingsFile = new File(tempDir, 'mappings.txt')
+ DownloadUtil.downloadIfChanged(new URL(mappingsDownload.url()), mappingsFile, mappingContext.logger)
+ when:
+ def mappings = getLayeredMappings(
+ new IntermediaryMappingsSpec(),
+ FileMappingsSpecBuilderImpl.builder(FileSpec.create(mappingsFile))
+ .fallbackNamespaces('named', 'official')
+ .mergeNamespace(MappingsNamespace.OFFICIAL)
+ .build()
+ )
+ def tiny = getTiny(mappings)
+ then:
+ mappings.srcNamespace == "named"
+ mappings.dstNamespaces == ["intermediary", "official"]
+ mappings.classes.size() == 6113
+ mappings.classes[0].srcName.hashCode() == 1869546970 // MojMap name, just check the hash
+ mappings.classes[0].getDstName(0) == "net/minecraft/class_2354"
+ mappings.classes[0].methods[0].args.size() == 0 // No Args
+ tiny.contains('this$0')
+ }
+
+ enum YarnSetupType {
+ TINY_JAR('tiny jar', { downloadFile(YARN_1_17_URL, "yarn.jar") }, { }),
+ BARE_TINY('bare tiny file', {
+ def yarnJar = downloadFile(YARN_1_17_URL, "yarn.jar")
+ def yarnTiny = new File(tempDir, "yarn.tiny")
+ yarnTiny.bytes = ZipUtils.unpack(yarnJar.toPath(), "mappings/mappings.tiny")
+ yarnTiny
+ }, { }),
+ ENIGMA_ZIP('enigma zip', {
+ // Recent Yarn data is not published as Enigma zips, so this zip is just a copy
+ // of Yarn's repo at a60a3189
+ Path.of("src/test/resources/mappings/yarn-1.17.zip")
+ }, {
+ it.mappingPath("mappings").enigmaMappings()
+ }),
+ ;
+
+ final String displayName
+ final Closure> setup
+ final Consumer mappingsSpec
+
+ YarnSetupType(String displayName, Closure> setup, Consumer mappingsSpec) {
+ this.displayName = displayName
+ this.setup = setup
+ this.mappingsSpec = mappingsSpec
+ }
+ }
+}
diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingSpecBuilderTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingSpecBuilderTest.groovy
index 7164f4f..abbb524 100644
--- a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingSpecBuilderTest.groovy
+++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingSpecBuilderTest.groovy
@@ -24,6 +24,7 @@
package net.fabricmc.loom.test.unit.layeredmappings
+import net.fabricmc.loom.configuration.providers.mappings.file.FileMappingsSpec
import net.fabricmc.loom.configuration.providers.mappings.utils.MavenFileSpec
import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec
import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuilderImpl
@@ -106,6 +107,20 @@ class LayeredMappingSpecBuilderTest extends Specification {
parchment.removePrefix() == false
}
+ def "yarn through file mappings"() {
+ when:
+ def spec = layered {
+ mappings("net.fabricmc:yarn:1.18.1+build.1:v2")
+ }
+ def layers = spec.layers()
+ then:
+ spec.version == "layered+hash.1284206205"
+ layers.size() == 2
+ layers[0].class == IntermediaryMappingsSpec
+ layers[1].class == FileMappingsSpec
+ ((layers[1] as FileMappingsSpec).fileSpec() as MavenFileSpec).dependencyNotation() == "net.fabricmc:yarn:1.18.1+build.1:v2"
+ }
+
LayeredMappingSpec layered(@DelegatesTo(LayeredMappingSpecBuilderImpl) Closure cl) {
LayeredMappingSpecBuilderImpl builder = new LayeredMappingSpecBuilderImpl()
new ClosureAction(cl).execute(builder)
diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsTestConstants.groovy
index 45a4a9c..9dc25f0 100644
--- a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsTestConstants.groovy
+++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsTestConstants.groovy
@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
- * Copyright (c) 2021 FabricMC
+ * Copyright (c) 2021-2022 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -44,4 +44,5 @@ interface LayeredMappingsTestConstants {
public static final String PARCHMENT_NOTATION = "org.parchmentmc.data:parchment-1.16.5:20210608-SNAPSHOT@zip"
public static final String PARCHMENT_URL = "https://maven.parchmentmc.net/org/parchmentmc/data/parchment-1.16.5/20210608-SNAPSHOT/parchment-1.16.5-20210608-SNAPSHOT.zip"
+ public static final String YARN_1_17_URL = "https://maven.fabricmc.net/net/fabricmc/yarn/1.17%2Bbuild.13/yarn-1.17%2Bbuild.13-v2.jar"
}
\ No newline at end of file
diff --git a/src/test/resources/mappings/yarn-1.17.zip b/src/test/resources/mappings/yarn-1.17.zip
new file mode 100644
index 0000000..b62cc49
Binary files /dev/null and b/src/test/resources/mappings/yarn-1.17.zip differ