Merge remote-tracking branch 'FabricMC/exp/1.5' into exp/1.5

# Conflicts:
#	build.gradle
#	gradle/libs.versions.toml
#	src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java
#	src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java
#	src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java
#	src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy
This commit is contained in:
shedaniel
2023-11-18 00:32:59 +08:00
30 changed files with 396 additions and 69 deletions

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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()) {

View File

@@ -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");
}

View File

@@ -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<String> 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<String> 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();
}
}

View File

@@ -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<InputTag> 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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -48,7 +48,7 @@ public record TinyJarInfo(boolean v2, Optional<String> 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;

View File

@@ -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

View File

@@ -156,13 +156,13 @@ public abstract sealed class MinecraftSourceSets permits MinecraftSourceSets.Sin
@Override
public void applyDependencies(BiConsumer<String, String> consumer, List<String> 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

View File

@@ -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<Path, MinecraftJar> jarFunction;
private final String name;
SingleJarEnvType(Function<Path, MinecraftJar> jarFunction) {
SingleJarEnvType(Function<Path, MinecraftJar> jarFunction, String name) {
this.jarFunction = jarFunction;
this.name = name;
}
public Function<Path, MinecraftJar> getJar() {
return jarFunction;
}
public String getName() {
return name;
}
}

View File

@@ -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() {

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -56,4 +56,6 @@ public interface Platform {
Architecture getArchitecture();
boolean supportsUnixDomainSockets();
boolean isRaspberryPi();
}

View File

@@ -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:

View File

@@ -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)
}

View File

@@ -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()
}
}

View File

@@ -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

View File

@@ -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<String, String> 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
}
}

View File

@@ -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()
}

View File

@@ -58,6 +58,11 @@ class PlatformTestUtils implements Platform {
supportsUnixDomainSockets
}
@Override
boolean isRaspberryPi() {
return false
}
@Immutable
static class TestArchitecture implements Architecture {
boolean is64Bit

View File

@@ -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 {