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 ae32f7f9..4e3a628a 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 00000000..1dc71f69 --- /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 5af12f7d..2f9bd593 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: *

* * @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 e3faef1c..faaaba2f 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 action); } diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java index 0933c8e9..38ab079b 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java @@ -24,11 +24,14 @@ package net.fabricmc.loom.configuration.ide.idea; +import java.util.ArrayList; import java.util.List; +import org.gradle.StartParameter; +import org.gradle.TaskExecutionRequest; import org.gradle.api.Project; import org.gradle.api.tasks.TaskProvider; -import org.gradle.execution.taskgraph.TaskExecutionGraphInternal; +import org.gradle.internal.DefaultTaskExecutionRequest; import net.fabricmc.loom.task.LoomTasks; @@ -42,8 +45,10 @@ public class IdeaConfiguration { return; } - // Run the idea sync task, is this exposed via the api? - final TaskExecutionGraphInternal taskGraph = (TaskExecutionGraphInternal) project.getGradle().getTaskGraph(); - taskGraph.whenReady(taskExecutionGraph -> taskGraph.addEntryTasks(List.of(ideaSyncTask.get()))); + final StartParameter startParameter = project.getGradle().getStartParameter(); + final List taskRequests = new ArrayList<>(startParameter.getTaskRequests()); + + taskRequests.add(new DefaultTaskExecutionRequest(List.of("ideaSyncTask"))); + startParameter.setTaskRequests(taskRequests); } } 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 b3746258..26dd21b9 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 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 00000000..9cc0a5cc --- /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 00000000..5d8a1669 --- /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 00000000..53681ad6 --- /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 2843a50f..d1e2f1bd 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/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index 64ce9495..6f852317 100644 --- a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy @@ -28,7 +28,7 @@ import org.gradle.util.GradleVersion class LoomTestConstants { public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion() - public final static String PRE_RELEASE_GRADLE = "7.5-20220124231322+0000" + public final static String PRE_RELEASE_GRADLE = "7.5-20220129032445+0000" public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/RunConfigTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/RunConfigTest.groovy index 46d8b3a3..18f8e684 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/RunConfigTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/RunConfigTest.groovy @@ -27,7 +27,9 @@ package net.fabricmc.loom.test.integration import net.fabricmc.loom.test.util.GradleProjectTestTrait import spock.lang.Specification import spock.lang.Unroll +import spock.util.environment.RestoreSystemProperties +import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS import static org.gradle.testkit.runner.TaskOutcome.SUCCESS // This test runs a mod that exits on mod init @@ -52,4 +54,32 @@ class RunConfigTest extends Specification implements GradleProjectTestTrait { 'runTestmodServer' | _ 'runAutoTestServer' | _ } + + @RestoreSystemProperties + @Unroll + def "idea auto configuration (gradle #version)"() { + setup: + System.setProperty("idea.sync.active", "true") + def gradle = gradleProject(project: "minimalBase", version: version) + + new File(gradle.projectDir, ".idea").mkdirs() + + gradle.buildGradle << ''' + dependencies { + minecraft "com.mojang:minecraft:1.18.1" + mappings "net.fabricmc:yarn:1.18.1+build.18:v2" + modImplementation "net.fabricmc:fabric-loader:0.12.12" + } + ''' + + when: + // Dont run with any tasks, the idea sync task should be invoked automatically due to the system prop + def result = gradle.run(tasks: []) + + then: + result.task(":ideaSyncTask").outcome == SUCCESS + + where: + version << STANDARD_TEST_VERSIONS + } } 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 921edff8..80709e19 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 00000000..b983cf45 --- /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 7164f4fe..abbb5242 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 45a4a9cb..9dc25f0c 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 00000000..b62cc492 Binary files /dev/null and b/src/test/resources/mappings/yarn-1.17.zip differ