diff --git a/build.gradle b/build.gradle index 287b0ce3..87880b71 100644 --- a/build.gradle +++ b/build.gradle @@ -50,7 +50,7 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { } group = "dev.architectury" -def baseVersion = '1.4' +def baseVersion = '1.5' def ENV = System.getenv() def runNumber = ENV.GITHUB_RUN_NUMBER ?: "9999" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a3584572..56902334 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ guava = "32.1.2-jre" stitch = "0.6.2" tiny-remapper = "1.10.23" access-widener = "2.1.0" -mapping-io = "0.4.2" +mapping-io = "0.5.0-beta.3" lorenz-tiny = "4.0.2" mercury = "0.1.2.15" kotlinx-metadata = "0.7.0" diff --git a/gradle/test.libs.versions.toml b/gradle/test.libs.versions.toml index 1c637f77..8d77fb39 100644 --- a/gradle/test.libs.versions.toml +++ b/gradle/test.libs.versions.toml @@ -6,7 +6,7 @@ mockito = "5.4.0" java-debug = "0.48.0" mixin = "0.11.4+mixin.0.8.5" -gradle-nightly = "8.5-20230908221250+0000" +gradle-nightly = "8.6-20231107135843+0000" fabric-loader = "0.14.22" fabric-installer = "0.11.1" diff --git a/src/main/java/net/fabricmc/loom/configuration/FabricApiExtension.java b/src/main/java/net/fabricmc/loom/configuration/FabricApiExtension.java index fb202727..702a535b 100644 --- a/src/main/java/net/fabricmc/loom/configuration/FabricApiExtension.java +++ b/src/main/java/net/fabricmc/loom/configuration/FabricApiExtension.java @@ -159,6 +159,8 @@ public abstract class FabricApiExtension { // Create a classpath group for this mod. Assume that the main sourceset is already in a group. mod.sourceSet(DATAGEN_SOURCESET_NAME); }); + + extension.createRemapConfigurations(sourceSets.getByName(DATAGEN_SOURCESET_NAME)); } if (settings.getCreateRunConfiguration().get()) { diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java index 241cba67..0a3ec0ae 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java @@ -345,6 +345,11 @@ public class RunConfigSettings implements Named { environment("client"); defaultMainClass(Constants.Knot.KNOT_CLIENT); + if (Platform.CURRENT.isRaspberryPi()) { + getProject().getLogger().info("Raspberry Pi detected, setting MESA_GL_VERSION_OVERRIDE=4.3"); + environmentVariable("MESA_GL_VERSION_OVERRIDE", "4.3"); + } + if (getExtension().isForgeLike()) { forgeTemplate("client"); } diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/MixinDetector.java b/src/main/java/net/fabricmc/loom/configuration/mods/MixinDetector.java new file mode 100644 index 00000000..6b79c5fe --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/mods/MixinDetector.java @@ -0,0 +1,74 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2023 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.mods; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.util.FileSystemUtil; +import net.fabricmc.loom.util.fmj.FabricModJson; +import net.fabricmc.loom.util.fmj.FabricModJsonFactory; + +public final class MixinDetector { + public static boolean hasMixinsWithoutRefmap(Path modJar) throws IOException { + try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(modJar)) { + final List mixinConfigs = getMixinConfigs(modJar); + + if (!mixinConfigs.isEmpty()) { + for (String mixinConfig : mixinConfigs) { + final Path configPath = fs.getPath(mixinConfig); + if (Files.notExists(configPath)) continue; + + try (BufferedReader reader = Files.newBufferedReader(configPath)) { + final JsonObject json = LoomGradlePlugin.GSON.fromJson(reader, JsonObject.class); + + if (!json.has("refmap")) { + // We found a mixin config with no refmap, exit the loop. + return true; + } + } catch (JsonParseException e) { + throw new RuntimeException("Could not parse mixin config %s from jar %s".formatted(mixinConfig, modJar.toAbsolutePath()), e); + } + } + } + + return false; + } + } + + private static List getMixinConfigs(Path modJar) { + // Nullable because we don't care here if we can't read it. + // We can just assume there are no mixins. + final FabricModJson fabricModJson = FabricModJsonFactory.createFromZipNullable(modJar); + return fabricModJson != null ? fabricModJson.getMixinConfigurations() : List.of(); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java index 6ea28635..100c535e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java @@ -32,9 +32,11 @@ import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.jar.Manifest; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -187,9 +189,9 @@ public class ModProcessor { builder.extension(kotlinRemapperClassloader.getTinyRemapperExtension()); } - if (extension.isNeoForge()) { - builder.extension(new MixinExtension()); - } + final Set hasMixinsWithoutRefmaps = new HashSet<>(); + // Configure the mixin extension to remap mixins from mod jars detected not to contain refmaps. + builder.extension(new MixinExtension(tag -> extension.isNeoForge() || hasMixinsWithoutRefmaps.contains(tag))); final TinyRemapper remapper = builder.build(); @@ -218,6 +220,12 @@ public class ModProcessor { project.getLogger().debug("Adding " + info.getInputFile() + " as a remap input"); + // Note: this is done at a jar level, not at the level of an individual mixin config. + // If a mod has multiple mixin configs, it's assumed that either all or none of them have refmaps. + if (MixinDetector.hasMixinsWithoutRefmap(info.getInputFile())) { + hasMixinsWithoutRefmaps.add(tag); + } + remapper.readInputsAsync(tag, info.getInputFile()); tagMap.put(info, tag); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java index d605ea27..80171fa1 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java @@ -45,7 +45,7 @@ import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; import net.fabricmc.loom.util.service.SharedService; import net.fabricmc.loom.util.service.SharedServiceManager; import net.fabricmc.mappingio.adapter.MappingNsCompleter; -import net.fabricmc.mappingio.format.Tiny2Reader; +import net.fabricmc.mappingio.format.tiny.Tiny2FileReader; import net.fabricmc.mappingio.tree.MemoryMappingTree; public final class IntermediateMappingsService implements SharedService { @@ -90,7 +90,7 @@ public final class IntermediateMappingsService implements SharedService { MappingNsCompleter nsCompleter = new MappingNsCompleter(tree, Collections.singletonMap(MappingsNamespace.NAMED.toString(), MappingsNamespace.INTERMEDIARY.toString()), true); try (BufferedReader reader = Files.newBufferedReader(getIntermediaryTiny(), StandardCharsets.UTF_8)) { - Tiny2Reader.read(reader, nsCompleter); + Tiny2FileReader.read(reader, nsCompleter); } } catch (IOException e) { throw new UncheckedIOException("Failed to read intermediary mappings", e); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsDependency.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsDependency.java index 4ce75ef7..c912bbe8 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsDependency.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsDependency.java @@ -53,7 +53,7 @@ import net.fabricmc.loom.configuration.providers.mappings.utils.AddConstructorMa import net.fabricmc.loom.util.ZipUtils; import net.fabricmc.mappingio.adapter.MappingDstNsReorder; import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; -import net.fabricmc.mappingio.format.Tiny2Writer; +import net.fabricmc.mappingio.format.tiny.Tiny2FileWriter; import net.fabricmc.mappingio.tree.MemoryMappingTree; public class LayeredMappingsDependency implements SelfResolvingDependency, FileCollectionDependency { @@ -99,7 +99,7 @@ public class LayeredMappingsDependency implements SelfResolvingDependency, FileC MemoryMappingTree mappings = processor.getMappings(layers); try (Writer writer = new StringWriter()) { - Tiny2Writer tiny2Writer = new Tiny2Writer(writer, false); + var tiny2Writer = new Tiny2FileWriter(writer, false); MappingDstNsReorder nsReorder = new MappingDstNsReorder(tiny2Writer, Collections.singletonList(MappingsNamespace.NAMED.toString())); MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(nsReorder, MappingsNamespace.INTERMEDIARY.toString(), true); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java index 2e58c839..3e02849d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java @@ -384,7 +384,7 @@ public class MappingConfiguration { private static boolean areMappingsV2(Path path) throws IOException { try (BufferedReader reader = Files.newBufferedReader(path)) { - return MappingReader.detectFormat(reader) == MappingFormat.TINY_2; + return MappingReader.detectFormat(reader) == MappingFormat.TINY_2_FILE; } catch (NoSuchFileException e) { // TODO: just check the mappings version when Parser supports V1 in readMetadata() return false; 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 index 06b79461..2d6dd61a 100644 --- 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 @@ -78,7 +78,7 @@ public record FileMappingsLayer( ); MappingNsRenamer renamer = new MappingNsRenamer(nsSwitch, fallbackNamespaceReplacements); - MappingReader.read(path, enigma ? MappingFormat.ENIGMA : null, renamer); + MappingReader.read(path, enigma ? MappingFormat.ENIGMA_DIR : null, renamer); } @Override diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingLayer.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingLayer.java index a6adf4e4..d6ce4893 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingLayer.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingLayer.java @@ -40,7 +40,7 @@ import net.fabricmc.loom.configuration.providers.mappings.intermediary.Intermedi import net.fabricmc.loom.configuration.providers.mappings.utils.DstNameFilterMappingVisitor; import net.fabricmc.mappingio.MappingVisitor; import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; -import net.fabricmc.mappingio.format.ProGuardReader; +import net.fabricmc.mappingio.format.proguard.ProGuardFileReader; public record MojangMappingLayer(String minecraftVersion, Path clientMappings, @@ -63,8 +63,8 @@ public record MojangMappingLayer(String minecraftVersion, try (BufferedReader clientBufferedReader = Files.newBufferedReader(clientMappings, StandardCharsets.UTF_8); BufferedReader serverBufferedReader = Files.newBufferedReader(serverMappings, StandardCharsets.UTF_8)) { - ProGuardReader.read(clientBufferedReader, MappingsNamespace.NAMED.toString(), MappingsNamespace.OFFICIAL.toString(), nsSwitch); - ProGuardReader.read(serverBufferedReader, MappingsNamespace.NAMED.toString(), MappingsNamespace.OFFICIAL.toString(), nsSwitch); + ProGuardFileReader.read(clientBufferedReader, MappingsNamespace.NAMED.toString(), MappingsNamespace.OFFICIAL.toString(), nsSwitch); + ProGuardFileReader.read(serverBufferedReader, MappingsNamespace.NAMED.toString(), MappingsNamespace.OFFICIAL.toString(), nsSwitch); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/MappingsMerger.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/MappingsMerger.java index 5988f143..db845663 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/MappingsMerger.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/MappingsMerger.java @@ -41,8 +41,8 @@ import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.providers.mappings.IntermediateMappingsService; import net.fabricmc.mappingio.adapter.MappingNsCompleter; import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; -import net.fabricmc.mappingio.format.Tiny2Reader; -import net.fabricmc.mappingio.format.Tiny2Writer; +import net.fabricmc.mappingio.format.tiny.Tiny2FileReader; +import net.fabricmc.mappingio.format.tiny.Tiny2FileWriter; import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.mappingio.tree.MemoryMappingTree; @@ -57,7 +57,7 @@ public final class MappingsMerger { intermediateMappingsService.getMemoryMappingTree().accept(new MappingSourceNsSwitch(intermediaryTree, MappingsNamespace.INTERMEDIARY.toString())); try (BufferedReader reader = Files.newBufferedReader(from, StandardCharsets.UTF_8)) { - Tiny2Reader.read(reader, intermediaryTree); + Tiny2FileReader.read(reader, intermediaryTree); } MemoryMappingTree officialTree = new MemoryMappingTree(); @@ -67,7 +67,7 @@ public final class MappingsMerger { inheritMappedNamesOfEnclosingClasses(officialTree); - try (Tiny2Writer writer = new Tiny2Writer(Files.newBufferedWriter(out, StandardCharsets.UTF_8), false)) { + try (var writer = new Tiny2FileWriter(Files.newBufferedWriter(out, StandardCharsets.UTF_8), false)) { officialTree.accept(writer); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/TinyJarInfo.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/TinyJarInfo.java index 3238bf7a..2ea2837e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/TinyJarInfo.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/TinyJarInfo.java @@ -48,7 +48,7 @@ public record TinyJarInfo(boolean v2, Optional minecraftVersionId) { private static boolean doesJarContainV2Mappings(Path path) throws IOException { try (FileSystemUtil.Delegate delegate = FileSystemUtil.getJarFileSystem(path)) { try (BufferedReader reader = Files.newBufferedReader(delegate.fs().getPath("mappings", "mappings.tiny"))) { - return MappingReader.detectFormat(reader) == MappingFormat.TINY_2; + return MappingReader.detectFormat(reader) == MappingFormat.TINY_2_FILE; } } catch (NoSuchFileException e) { return false; diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJar.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJar.java index 6686a131..311d2021 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJar.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJar.java @@ -28,7 +28,7 @@ import java.io.File; import java.nio.file.Path; import java.util.Objects; -public abstract sealed class MinecraftJar permits MinecraftJar.Merged, MinecraftJar.Common, MinecraftJar.ServerOnly, MinecraftJar.ClientOnly { +public abstract sealed class MinecraftJar permits MinecraftJar.Client, MinecraftJar.ClientOnly, MinecraftJar.Common, MinecraftJar.Merged, MinecraftJar.Server { private final Path path; private final boolean merged, client, server; private final String name; @@ -68,8 +68,10 @@ public abstract sealed class MinecraftJar permits MinecraftJar.Merged, Minecraft public abstract MinecraftJar forPath(Path path); public static final class Merged extends MinecraftJar { + public static final String NAME = "merged"; + public Merged(Path path) { - super(path, true, true, true, "merged"); + super(path, true, true, true, NAME); } @Override @@ -79,8 +81,10 @@ public abstract sealed class MinecraftJar permits MinecraftJar.Merged, Minecraft } public static final class Common extends MinecraftJar { + public static final String NAME = "common"; + public Common(Path path) { - super(path, false, false, true, "common"); + super(path, false, false, true, NAME); } @Override @@ -89,20 +93,39 @@ public abstract sealed class MinecraftJar permits MinecraftJar.Merged, Minecraft } } - public static final class ServerOnly extends MinecraftJar { - public ServerOnly(Path path) { - super(path, false, false, true, "serverOnly"); + public static final class Server extends MinecraftJar { + public static final String NAME = "server"; + + public Server(Path path) { + super(path, false, false, true, NAME); } @Override public MinecraftJar forPath(Path path) { - return new ServerOnly(path); + return new Server(path); } } + // Un-split client jar + public static final class Client extends MinecraftJar { + public static final String NAME = "client"; + + public Client(Path path) { + super(path, false, true, false, NAME); + } + + @Override + public MinecraftJar forPath(Path path) { + return new Client(path); + } + } + + // Split client jar public static final class ClientOnly extends MinecraftJar { + public static final String NAME = "clientOnly"; + public ClientOnly(Path path) { - super(path, false, true, false, "clientOnly"); + super(path, false, true, false, NAME); } @Override diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java index a975f30e..f34ecb0c 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java @@ -156,13 +156,13 @@ public abstract sealed class MinecraftSourceSets permits MinecraftSourceSets.Sin @Override public void applyDependencies(BiConsumer consumer, List targets) { Preconditions.checkArgument(targets.size() == 2); - Preconditions.checkArgument(targets.contains("common")); - Preconditions.checkArgument(targets.contains("clientOnly")); + Preconditions.checkArgument(targets.contains(MinecraftJar.Common.NAME)); + Preconditions.checkArgument(targets.contains(MinecraftJar.ClientOnly.NAME)); - consumer.accept(MINECRAFT_COMMON_NAMED.runtime(), "common"); - consumer.accept(MINECRAFT_CLIENT_ONLY_NAMED.runtime(), "clientOnly"); - consumer.accept(MINECRAFT_COMMON_NAMED.compile(), "common"); - consumer.accept(MINECRAFT_CLIENT_ONLY_NAMED.compile(), "clientOnly"); + consumer.accept(MINECRAFT_COMMON_NAMED.runtime(), MinecraftJar.Common.NAME); + consumer.accept(MINECRAFT_CLIENT_ONLY_NAMED.runtime(), MinecraftJar.ClientOnly.NAME); + consumer.accept(MINECRAFT_COMMON_NAMED.compile(), MinecraftJar.Common.NAME); + consumer.accept(MINECRAFT_CLIENT_ONLY_NAMED.compile(), MinecraftJar.ClientOnly.NAME); } @Override diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SingleJarEnvType.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SingleJarEnvType.java index 7063c166..1101de5f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SingleJarEnvType.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SingleJarEnvType.java @@ -28,16 +28,22 @@ import java.nio.file.Path; import java.util.function.Function; public enum SingleJarEnvType { - CLIENT(MinecraftJar.ClientOnly::new), - SERVER(MinecraftJar.ServerOnly::new); + CLIENT(MinecraftJar.Client::new, MinecraftJar.Client.NAME), + SERVER(MinecraftJar.Server::new, MinecraftJar.Server.NAME); private final Function jarFunction; + private final String name; - SingleJarEnvType(Function jarFunction) { + SingleJarEnvType(Function jarFunction, String name) { this.jarFunction = jarFunction; + this.name = name; } public Function getJar() { return jarFunction; } + + public String getName() { + return name; + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MappedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MappedMinecraftProvider.java index 4ea32349..68a5cdc8 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MappedMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MappedMinecraftProvider.java @@ -42,7 +42,7 @@ public interface MappedMinecraftProvider { } interface Merged extends ProviderImpl { - String MERGED = "merged"; + String MERGED = MinecraftJar.Merged.NAME; default MinecraftJar getMergedJar() { return new MinecraftJar.Merged(getJar(MERGED)); @@ -55,8 +55,8 @@ public interface MappedMinecraftProvider { } interface Split extends ProviderImpl { - String COMMON = "common"; - String CLIENT_ONLY = "clientOnly"; + String COMMON = MinecraftJar.Common.NAME; + String CLIENT_ONLY = MinecraftJar.ClientOnly.NAME; default MinecraftJar getCommonJar() { return new MinecraftJar.Common(getJar(COMMON)); @@ -76,7 +76,7 @@ public interface MappedMinecraftProvider { SingleJarEnvType env(); default String envName() { - return "%sOnly".formatted(env()); + return env().getName(); } default MinecraftJar getEnvOnlyJar() { diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index c4cb22e5..4cef8192 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -94,7 +94,7 @@ import net.fabricmc.loom.util.ipc.IPCServer; import net.fabricmc.loom.util.service.ScopedSharedServiceManager; import net.fabricmc.mappingio.MappingReader; import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; -import net.fabricmc.mappingio.format.Tiny2Writer; +import net.fabricmc.mappingio.format.tiny.Tiny2FileWriter; import net.fabricmc.mappingio.tree.MemoryMappingTree; @DisableCachingByDefault @@ -521,7 +521,7 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { } try (Writer writer = Files.newBufferedWriter(outputMappings, StandardCharsets.UTF_8)) { - Tiny2Writer tiny2Writer = new Tiny2Writer(writer, false); + var tiny2Writer = new Tiny2FileWriter(writer, false); mappingTree.accept(new MappingSourceNsSwitch(tiny2Writer, MappingsNamespace.NAMED.toString())); } catch (IOException e) { throw new RuntimeException("Failed to write mappings", e); diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index c73a3aca..e32cfa5d 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.java @@ -134,21 +134,31 @@ public abstract class LoomTasks implements Runnable { }); } + private static String getRunConfigTaskName(RunConfigSettings config) { + String configName = config.getName(); + return "run" + configName.substring(0, 1).toUpperCase() + configName.substring(1); + } + private void registerRunTasks() { LoomGradleExtension extension = LoomGradleExtension.get(getProject()); Preconditions.checkArgument(extension.getRunConfigs().size() == 0, "Run configurations must not be registered before loom"); extension.getRunConfigs().whenObjectAdded(config -> { - String configName = config.getName(); - String taskName = "run" + configName.substring(0, 1).toUpperCase() + configName.substring(1); - - getTasks().register(taskName, RunGameTask.class, config).configure(t -> { + getTasks().register(getRunConfigTaskName(config), RunGameTask.class, config).configure(t -> { t.setDescription("Starts the '" + config.getConfigName() + "' run configuration"); t.dependsOn(config.getEnvironment().equals("client") ? "configureClientLaunch" : "configureLaunch"); }); }); + + extension.getRunConfigs().whenObjectRemoved(runConfigSettings -> { + getTasks().named(getRunConfigTaskName(runConfigSettings), task -> { + // Disable the task so it can't be run + task.setEnabled(false); + }); + }); + extension.getRunConfigs().create("client", RunConfigSettings::client); extension.getRunConfigs().create("server", RunConfigSettings::server); diff --git a/src/main/java/net/fabricmc/loom/util/CurrentPlatform.java b/src/main/java/net/fabricmc/loom/util/CurrentPlatform.java index fdd7e3cb..4cf82459 100644 --- a/src/main/java/net/fabricmc/loom/util/CurrentPlatform.java +++ b/src/main/java/net/fabricmc/loom/util/CurrentPlatform.java @@ -28,6 +28,10 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.net.StandardProtocolFamily; import java.nio.channels.ServerSocketChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; final class CurrentPlatform implements Platform { static final Platform INSTANCE = new CurrentPlatform(); @@ -35,11 +39,13 @@ final class CurrentPlatform implements Platform { private final OperatingSystem operatingSystem; private final Architecture architecture; private final boolean supportsUnixDomainSockets; + private final boolean isRaspberryPi; private CurrentPlatform() { this.operatingSystem = getCurrentOperatingSystem(); this.architecture = getCurrentArchitecture(); this.supportsUnixDomainSockets = isUnixDomainSocketsSupported(); + this.isRaspberryPi = getIsRaspberryPi(operatingSystem, architecture); } private static OperatingSystem getCurrentOperatingSystem() { @@ -84,6 +90,29 @@ final class CurrentPlatform implements Platform { } } + /** + * Returns true if the current system is a Raspberry Pi running Debian 12 (Bookworm). + */ + private static boolean getIsRaspberryPi(OperatingSystem operatingSystem, Architecture architecture) { + if (operatingSystem != OperatingSystem.LINUX || !architecture.isArm()) { + return false; + } + + try { + final Path releasePath = Paths.get("/etc/os-release"); + final String release = Files.readString(releasePath, StandardCharsets.UTF_8); + final boolean isDebianBookworm = release.contains("VERSION_CODENAME=bookworm"); + + final Path modelPath = Paths.get("/sys/firmware/devicetree/base/model"); + final String model = Files.readString(modelPath, StandardCharsets.UTF_8); + final boolean isRaspberryPi = model.startsWith("Raspberry Pi"); + + return isDebianBookworm && isRaspberryPi; + } catch (IOException e) { + return false; + } + } + @Override public OperatingSystem getOperatingSystem() { return operatingSystem; @@ -98,4 +127,9 @@ final class CurrentPlatform implements Platform { public boolean supportsUnixDomainSockets() { return supportsUnixDomainSockets; } + + @Override + public boolean isRaspberryPi() { + return isRaspberryPi; + } } diff --git a/src/main/java/net/fabricmc/loom/util/Platform.java b/src/main/java/net/fabricmc/loom/util/Platform.java index c21c5544..19a06a80 100644 --- a/src/main/java/net/fabricmc/loom/util/Platform.java +++ b/src/main/java/net/fabricmc/loom/util/Platform.java @@ -56,4 +56,6 @@ public interface Platform { Architecture getArchitecture(); boolean supportsUnixDomainSockets(); + + boolean isRaspberryPi(); } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/DataGenerationTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/DataGenerationTest.groovy index a5fb4a33..778b6611 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/DataGenerationTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/DataGenerationTest.groovy @@ -85,6 +85,8 @@ class DataGenerationTest extends Specification implements GradleProjectTestTrait mappings "net.fabricmc:yarn:1.20.2+build.4:v2" modImplementation "net.fabricmc:fabric-loader:0.14.23" modImplementation "net.fabricmc.fabric-api:fabric-api:0.90.0+1.20.2" + + modDatagenImplementation fabricApi.module("fabric-data-generation-api-v1", "0.90.0+1.20.2") } ''' when: diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/DebugLineNumbersTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/DebugLineNumbersTest.groovy index 23a099af..b239301b 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/DebugLineNumbersTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/DebugLineNumbersTest.groovy @@ -41,6 +41,7 @@ import io.reactivex.functions.Function import spock.lang.Specification import spock.lang.Timeout +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJar import net.fabricmc.loom.test.util.GradleProjectTestTrait import net.fabricmc.loom.util.ZipUtils @@ -138,7 +139,7 @@ class DebugLineNumbersTest extends Specification implements GradleProjectTestTra } private static String getClassSource(GradleProject gradle, String classname, String mappings = MAPPINGS) { - File sourcesJar = gradle.getGeneratedSources(mappings, "serveronly") + File sourcesJar = gradle.getGeneratedSources(mappings, MinecraftJar.Server.NAME) return new String(ZipUtils.unpack(sourcesJar.toPath(), classname), StandardCharsets.UTF_8) } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy index 7a4d8298..d5433e49 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.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-2023 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 @@ -41,7 +41,7 @@ class FabricAPITest extends Specification implements GradleProjectTestTrait { private static final String API_VERSION = "0.0.0+loom" @Unroll - def "build and run (gradle #version)"() { + def "build and run (gradle #version, mixin ap disabled: #disableMixinAp)"() { setup: def gradle = gradleProject( repo: "https://github.com/FabricMC/fabric.git", @@ -52,10 +52,22 @@ class FabricAPITest extends Specification implements GradleProjectTestTrait { gradle.enableMultiProjectOptimisation() + // Disable the mixin ap if needed. Fabric API is a large enough test project to see if something breaks. + def mixinApPatch = "" + + if (disableMixinAp) { + mixinApPatch = """ + + allprojects { + loom.mixin.useLegacyMixinAp = false + } + """.stripIndent() + } + // Set the version to something constant gradle.buildGradle.text = gradle.buildGradle.text.replace('project.version + "+" + (ENV.GITHUB_RUN_NUMBER ? "" : "local-") + getBranch()', "\"$API_VERSION\"") .replace('id "fabric-loom" version "0.9.50"', 'id "dev.architectury.loom"') - .replace('"fabric-loom"', '"dev.architectury.loom"') + .replace('"fabric-loom"', '"dev.architectury.loom"') + mixinApPatch def server = ServerRunner.create(gradle.projectDir, "23w33a") .withMod(gradle.getOutputFile("fabric-api-${API_VERSION}.jar")) @@ -85,7 +97,9 @@ class FabricAPITest extends Specification implements GradleProjectTestTrait { serverResult.successful() serverResult.output.contains("- fabric-api $API_VERSION") where: - //version << STANDARD_TEST_VERSIONS - version << [DEFAULT_GRADLE] + [version, disableMixinAp] << [ + [DEFAULT_GRADLE], + [false, true] + ].combinations() } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/MultiMcVersionTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/MultiMcVersionTest.groovy index 2e677541..8b15a8fd 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/MultiMcVersionTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/MultiMcVersionTest.groovy @@ -33,29 +33,34 @@ import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS import static org.gradle.testkit.runner.TaskOutcome.SUCCESS class MultiMcVersionTest extends Specification implements GradleProjectTestTrait { + static def versions = [ + 'fabric-1.14.4', + 'fabric-1.15', + 'fabric-1.15.2', + 'fabric-1.16', + 'fabric-1.16.5', + 'fabric-1.17', + 'fabric-1.17.1', + 'fabric-1.18', + 'fabric-1.18.2', + 'fabric-1.19', + 'fabric-1.19.3' + ] + @Unroll def "build (gradle #version)"() { setup: def gradle = gradleProject(project: "multi-mc-versions", version: version) + versions.forEach { + // Make dir as its now required by Gradle + new File(gradle.projectDir, it).mkdir() + } + when: def result = gradle.run(tasks: "build") then: - def versions = [ - 'fabric-1.14.4', - 'fabric-1.15', - 'fabric-1.15.2', - 'fabric-1.16', - 'fabric-1.16.5', - 'fabric-1.17', - 'fabric-1.17.1', - 'fabric-1.18', - 'fabric-1.18.2', - 'fabric-1.19', - 'fabric-1.19.3' - ] - result.task(":build").outcome == SUCCESS versions.forEach { result.task(":$it:build").outcome == SUCCESS diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/MixinDetectorTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/MixinDetectorTest.groovy new file mode 100644 index 00000000..58606360 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/MixinDetectorTest.groovy @@ -0,0 +1,129 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2023 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 + +import java.nio.file.Path + +import groovy.json.JsonOutput +import spock.lang.Specification +import spock.lang.TempDir + +import net.fabricmc.loom.configuration.mods.MixinDetector +import net.fabricmc.loom.util.FileSystemUtil + +class MixinDetectorTest extends Specification { + @TempDir + Path tempDir + + private Path makeJar(Map mixinConfigs) { + def path = tempDir.resolve("test.jar") + def fs = FileSystemUtil.getJarFileSystem(path, true) + + try { + // Create fabric.mod.json + def fabricModJson = JsonOutput.toJson([ + schemaVersion: 1, + id: 'test', + version: '1', + mixins: mixinConfigs.keySet() + ]) + fs.getPath('fabric.mod.json').text = fabricModJson + + // Write all mixin configs + mixinConfigs.forEach { name, content -> + fs.getPath(name).text = content + } + } finally { + fs.close() + } + + return path + } + + def "jar without mixins has no mixins without refmaps"() { + setup: + def jarPath = makeJar([:]) + + when: + def hasMixinsWithoutRefmaps = MixinDetector.hasMixinsWithoutRefmap(jarPath) + + then: + !hasMixinsWithoutRefmaps // no mixins + } + + def "jar with one mixin config with refmap has no mixins without refmaps"() { + setup: + def jarPath = makeJar([ + 'test.mixins.json': JsonOutput.toJson([ + 'package': 'com.example.test', + 'mixins': ['TestMixin'], + 'refmap': 'test-refmap.json' + ]) + ]) + + when: + def hasMixinsWithoutRefmaps = MixinDetector.hasMixinsWithoutRefmap(jarPath) + + then: + !hasMixinsWithoutRefmaps // no mixins with refmaps + } + + def "jar with one mixin config without refmap has mixins without refmaps"() { + setup: + def jarPath = makeJar([ + 'test.mixins.json': JsonOutput.toJson([ + 'package': 'com.example.test', + 'mixins': ['TestMixin'] + ]) + ]) + + when: + def hasMixinsWithoutRefmaps = MixinDetector.hasMixinsWithoutRefmap(jarPath) + + then: + hasMixinsWithoutRefmaps // mixins with refmaps + } + + def "jar with mixed mixin configs has mixins without refmaps"() { + setup: + def jarPath = makeJar([ + 'test.mixins.json': JsonOutput.toJson([ + 'package': 'com.example.test', + 'mixins': ['TestMixin'] + ]), + 'test2.mixins.json': JsonOutput.toJson([ + 'package': 'com.example.test2', + 'mixins': ['TestMixin2'], + 'refmap': 'test2-refmap.json' + ]) + ]) + + when: + def hasMixinsWithoutRefmaps = MixinDetector.hasMixinsWithoutRefmap(jarPath) + + then: + hasMixinsWithoutRefmaps // mixins with refmaps + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy index 17273f22..629167e9 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy @@ -48,7 +48,7 @@ import net.fabricmc.loom.util.download.Download import net.fabricmc.loom.util.download.DownloadBuilder import net.fabricmc.mappingio.adapter.MappingDstNsReorder import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch -import net.fabricmc.mappingio.format.Tiny2Writer +import net.fabricmc.mappingio.format.tiny.Tiny2FileWriter import net.fabricmc.mappingio.tree.MemoryMappingTree abstract class LayeredMappingsSpecification extends Specification implements LayeredMappingsTestConstants { @@ -102,7 +102,7 @@ abstract class LayeredMappingsSpecification extends Specification implements Lay String getTiny(MemoryMappingTree mappingTree) { def sw = new StringWriter() - mappingTree.accept(new Tiny2Writer(sw, false)) + mappingTree.accept(new Tiny2FileWriter(sw, false)) return sw.toString() } diff --git a/src/test/groovy/net/fabricmc/loom/test/util/PlatformTestUtils.groovy b/src/test/groovy/net/fabricmc/loom/test/util/PlatformTestUtils.groovy index 4bc83eb2..f927fb8c 100644 --- a/src/test/groovy/net/fabricmc/loom/test/util/PlatformTestUtils.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/util/PlatformTestUtils.groovy @@ -58,6 +58,11 @@ class PlatformTestUtils implements Platform { supportsUnixDomainSockets } + @Override + boolean isRaspberryPi() { + return false + } + @Immutable static class TestArchitecture implements Architecture { boolean is64Bit diff --git a/src/test/resources/projects/runconfigs/build.gradle b/src/test/resources/projects/runconfigs/build.gradle index 2b146cee..679a3b23 100644 --- a/src/test/resources/projects/runconfigs/build.gradle +++ b/src/test/resources/projects/runconfigs/build.gradle @@ -25,6 +25,13 @@ loom { name = 'Custom Main Class' mainClass.set 'net.fabricmc.example.Main' } + + // Test that removing a run config works + removeMe { + inherit server + } + + remove removeMe } runConfigs.configureEach {