mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-28 04:07:01 -05:00
Add Fabric-Loom-Mixin-Remap-Type manifest entry (#980)
This commit is contained in:
@@ -29,6 +29,7 @@ import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
@@ -38,31 +39,45 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.configuration.InstallerData;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
import net.fabricmc.loom.util.fmj.FabricModJsonFactory;
|
||||
|
||||
public record ArtifactMetadata(boolean isFabricMod, RemapRequirements remapRequirements, @Nullable InstallerData installerData) {
|
||||
public record ArtifactMetadata(boolean isFabricMod, RemapRequirements remapRequirements, @Nullable InstallerData installerData, MixinRemapType mixinRemapType) {
|
||||
private static final String INSTALLER_PATH = "fabric-installer.json";
|
||||
private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF";
|
||||
private static final String MANIFEST_REMAP_KEY = "Fabric-Loom-Remap";
|
||||
|
||||
public static ArtifactMetadata create(ArtifactRef artifact) throws IOException {
|
||||
public static ArtifactMetadata create(ArtifactRef artifact, String currentLoomVersion) throws IOException {
|
||||
boolean isFabricMod;
|
||||
RemapRequirements remapRequirements = RemapRequirements.DEFAULT;
|
||||
InstallerData installerData = null;
|
||||
MixinRemapType refmapRemapType = MixinRemapType.MIXIN;
|
||||
|
||||
try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(artifact.path())) {
|
||||
isFabricMod = FabricModJsonFactory.containsMod(fs);
|
||||
final Path manifestPath = fs.getPath(MANIFEST_PATH);
|
||||
final Path manifestPath = fs.getPath(Constants.Manifest.PATH);
|
||||
|
||||
if (Files.exists(manifestPath)) {
|
||||
final var manifest = new Manifest(new ByteArrayInputStream(Files.readAllBytes(manifestPath)));
|
||||
final Attributes mainAttributes = manifest.getMainAttributes();
|
||||
final String value = mainAttributes.getValue(MANIFEST_REMAP_KEY);
|
||||
final String remapValue = mainAttributes.getValue(Constants.Manifest.REMAP_KEY);
|
||||
final String loomVersion = mainAttributes.getValue(Constants.Manifest.LOOM_VERSION);
|
||||
final String mixinRemapType = mainAttributes.getValue(Constants.Manifest.MIXIN_REMAP_TYPE);
|
||||
|
||||
if (value != null) {
|
||||
if (remapValue != null) {
|
||||
// Support opting into and out of remapping with "Fabric-Loom-Remap" manifest entry
|
||||
remapRequirements = Boolean.parseBoolean(value) ? RemapRequirements.OPT_IN : RemapRequirements.OPT_OUT;
|
||||
remapRequirements = Boolean.parseBoolean(remapValue) ? RemapRequirements.OPT_IN : RemapRequirements.OPT_OUT;
|
||||
}
|
||||
|
||||
if (mixinRemapType != null) {
|
||||
try {
|
||||
refmapRemapType = MixinRemapType.valueOf(mixinRemapType.toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalStateException("Unknown mixin remap type: " + mixinRemapType);
|
||||
}
|
||||
}
|
||||
|
||||
if (loomVersion != null && refmapRemapType != MixinRemapType.STATIC) {
|
||||
validateLoomVersion(loomVersion, currentLoomVersion);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +89,32 @@ public record ArtifactMetadata(boolean isFabricMod, RemapRequirements remapRequi
|
||||
}
|
||||
}
|
||||
|
||||
return new ArtifactMetadata(isFabricMod, remapRequirements, installerData);
|
||||
return new ArtifactMetadata(isFabricMod, remapRequirements, installerData, refmapRemapType);
|
||||
}
|
||||
|
||||
// Validates that the version matches or is less than the current loom version
|
||||
// This is only done for jars with tiny-remapper remapped mixins.
|
||||
private static void validateLoomVersion(String version, String currentLoomVersion) {
|
||||
if ("0.0.0+unknown".equals(currentLoomVersion)) {
|
||||
// Unknown version, skip validation. This is the case when running from source (tests)
|
||||
return;
|
||||
}
|
||||
|
||||
final String[] versionParts = version.split("\\.");
|
||||
final String[] currentVersionParts = currentLoomVersion.split("\\.");
|
||||
|
||||
// Check major and minor version
|
||||
for (int i = 0; i < 2; i++) {
|
||||
final int versionPart = Integer.parseInt(versionParts[i]);
|
||||
final int currentVersionPart = Integer.parseInt(currentVersionParts[i]);
|
||||
|
||||
if (versionPart > currentVersionPart) {
|
||||
throw new IllegalStateException("Mod was built with a newer version of Loom (%s), you are using Loom (%s)".formatted(version, currentLoomVersion));
|
||||
} else if (versionPart < currentVersionPart) {
|
||||
// Older version, no need to check further
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean shouldRemap() {
|
||||
@@ -100,4 +140,15 @@ public record ArtifactMetadata(boolean isFabricMod, RemapRequirements remapRequi
|
||||
return shouldRemap;
|
||||
}
|
||||
}
|
||||
|
||||
public enum MixinRemapType {
|
||||
// Jar uses refmaps, so will be remapped by mixin
|
||||
MIXIN,
|
||||
// Jar does not use refmaps, so will be remapped by tiny-remapper
|
||||
STATIC;
|
||||
|
||||
public String manifestValue() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,11 +43,12 @@ import java.util.stream.Stream;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.task.AbstractRemapJarTask;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
|
||||
public class JarSplitter {
|
||||
public static final String MANIFEST_SPLIT_ENV_NAME_KEY = "Fabric-Loom-Split-Environment-Name";
|
||||
private static final Attributes.Name MANIFEST_SPLIT_ENV_NAME = new Attributes.Name(Constants.Manifest.SPLIT_ENV);
|
||||
private static final Attributes.Name MANIFEST_CLIENT_ENTRIES_NAME = new Attributes.Name(Constants.Manifest.CLIENT_ENTRIES);
|
||||
|
||||
final Path inputJar;
|
||||
|
||||
@@ -58,9 +59,9 @@ public class JarSplitter {
|
||||
@Nullable
|
||||
public Target analyseTarget() {
|
||||
try (FileSystemUtil.Delegate input = FileSystemUtil.getJarFileSystem(inputJar)) {
|
||||
final Manifest manifest = input.fromInputStream(Manifest::new, AbstractRemapJarTask.MANIFEST_PATH);
|
||||
final Manifest manifest = input.fromInputStream(Manifest::new, Constants.Manifest.PATH);
|
||||
|
||||
if (!Boolean.parseBoolean(manifest.getMainAttributes().getValue(AbstractRemapJarTask.MANIFEST_SPLIT_ENV_KEY))) {
|
||||
if (!Boolean.parseBoolean(manifest.getMainAttributes().getValue(Constants.Manifest.SPLIT_ENV))) {
|
||||
// Jar was not built with splitting enabled.
|
||||
return null;
|
||||
}
|
||||
@@ -122,9 +123,9 @@ public class JarSplitter {
|
||||
Files.deleteIfExists(clientOutputJar);
|
||||
|
||||
try (FileSystemUtil.Delegate input = FileSystemUtil.getJarFileSystem(inputJar)) {
|
||||
final Manifest manifest = input.fromInputStream(Manifest::new, AbstractRemapJarTask.MANIFEST_PATH);
|
||||
final Manifest manifest = input.fromInputStream(Manifest::new, Constants.Manifest.PATH);
|
||||
|
||||
if (!Boolean.parseBoolean(manifest.getMainAttributes().getValue(AbstractRemapJarTask.MANIFEST_SPLIT_ENV_KEY))) {
|
||||
if (!Boolean.parseBoolean(manifest.getMainAttributes().getValue(Constants.Manifest.SPLIT_ENV))) {
|
||||
throw new UnsupportedOperationException("Cannot split jar that has not been built with a split env");
|
||||
}
|
||||
|
||||
@@ -157,7 +158,7 @@ public class JarSplitter {
|
||||
|
||||
final String entryPath = relativePath.toString();
|
||||
|
||||
if (entryPath.equals(AbstractRemapJarTask.MANIFEST_PATH)) {
|
||||
if (entryPath.equals(Constants.Manifest.PATH)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -183,11 +184,11 @@ public class JarSplitter {
|
||||
stripSignatureData(outManifest);
|
||||
|
||||
attributes.remove(Attributes.Name.SIGNATURE_VERSION);
|
||||
Objects.requireNonNull(attributes.remove(AbstractRemapJarTask.MANIFEST_SPLIT_ENV_NAME));
|
||||
Objects.requireNonNull(attributes.remove(AbstractRemapJarTask.MANIFEST_CLIENT_ENTRIES_NAME));
|
||||
Objects.requireNonNull(attributes.remove(MANIFEST_SPLIT_ENV_NAME));
|
||||
Objects.requireNonNull(attributes.remove(MANIFEST_CLIENT_ENTRIES_NAME));
|
||||
|
||||
writeBytes(writeWithEnvironment(outManifest, "common"), commonOutput.getPath(AbstractRemapJarTask.MANIFEST_PATH));
|
||||
writeBytes(writeWithEnvironment(outManifest, "client"), clientOutput.getPath(AbstractRemapJarTask.MANIFEST_PATH));
|
||||
writeBytes(writeWithEnvironment(outManifest, "common"), commonOutput.getPath(Constants.Manifest.PATH));
|
||||
writeBytes(writeWithEnvironment(outManifest, "client"), clientOutput.getPath(Constants.Manifest.PATH));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +198,7 @@ public class JarSplitter {
|
||||
private byte[] writeWithEnvironment(Manifest in, String value) throws IOException {
|
||||
final Manifest manifest = new Manifest(in);
|
||||
final Attributes attributes = manifest.getMainAttributes();
|
||||
attributes.putValue(MANIFEST_SPLIT_ENV_NAME_KEY, value);
|
||||
attributes.putValue(Constants.Manifest.SPLIT_ENV_NAME, value);
|
||||
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
manifest.write(out);
|
||||
@@ -206,7 +207,7 @@ public class JarSplitter {
|
||||
|
||||
private List<String> readClientEntries(Manifest manifest) {
|
||||
final Attributes attributes = manifest.getMainAttributes();
|
||||
final String clientEntriesValue = attributes.getValue(AbstractRemapJarTask.MANIFEST_CLIENT_ENTRIES_KEY);
|
||||
final String clientEntriesValue = attributes.getValue(Constants.Manifest.CLIENT_ENTRIES);
|
||||
|
||||
if (clientEntriesValue == null || clientEntriesValue.isBlank()) {
|
||||
return Collections.emptyList();
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,7 @@ import org.gradle.language.base.artifact.SourcesArtifact;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.api.RemapConfigurationSettings;
|
||||
import net.fabricmc.loom.configuration.RemapConfigurations;
|
||||
import net.fabricmc.loom.configuration.mods.dependency.ModDependency;
|
||||
@@ -63,6 +64,7 @@ import net.fabricmc.loom.configuration.mods.dependency.ModDependencyFactory;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.ExceptionUtil;
|
||||
import net.fabricmc.loom.util.SourceRemapper;
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper;
|
||||
import net.fabricmc.loom.util.service.SharedServiceManager;
|
||||
@@ -137,9 +139,9 @@ public class ModConfigurationRemapper {
|
||||
final ArtifactMetadata artifactMetadata;
|
||||
|
||||
try {
|
||||
artifactMetadata = ArtifactMetadata.create(artifact);
|
||||
artifactMetadata = ArtifactMetadata.create(artifact, LoomGradlePlugin.LOOM_VERSION);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to read metadata from" + artifact.path(), e);
|
||||
throw ExceptionUtil.createDescriptiveWrapper(UncheckedIOException::new, "Failed to read metadata from " + artifact.path(), e);
|
||||
}
|
||||
|
||||
if (artifactMetadata.installerData() != null) {
|
||||
@@ -158,7 +160,7 @@ public class ModConfigurationRemapper {
|
||||
continue;
|
||||
}
|
||||
|
||||
final ModDependency modDependency = ModDependencyFactory.create(artifact, remappedConfig, clientRemappedConfig, mappingsSuffix, project);
|
||||
final ModDependency modDependency = ModDependencyFactory.create(artifact, artifactMetadata, remappedConfig, clientRemappedConfig, mappingsSuffix, project);
|
||||
scheduleSourcesRemapping(project, sourceRemapper, modDependency);
|
||||
modDependencies.add(modDependency);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,6 @@ import net.fabricmc.loom.api.RemapConfigurationSettings;
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.mods.dependency.ModDependency;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration;
|
||||
import net.fabricmc.loom.task.RemapJarTask;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.Pair;
|
||||
import net.fabricmc.loom.util.TinyRemapperHelper;
|
||||
@@ -149,9 +148,14 @@ public class ModProcessor {
|
||||
builder.extension(kotlinRemapperClassloader.getTinyRemapperExtension());
|
||||
}
|
||||
|
||||
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(hasMixinsWithoutRefmaps::contains));
|
||||
final Set<InputTag> remapMixins = new HashSet<>();
|
||||
final boolean requiresStaticMixinRemap = remapList.stream()
|
||||
.anyMatch(modDependency -> modDependency.getMetadata().mixinRemapType() == ArtifactMetadata.MixinRemapType.STATIC);
|
||||
|
||||
if (requiresStaticMixinRemap) {
|
||||
// Configure the mixin extension to remap mixins from mod jars that were remapped with the mixin extension.
|
||||
builder.extension(new MixinExtension(remapMixins::contains));
|
||||
}
|
||||
|
||||
final TinyRemapper remapper = builder.build();
|
||||
|
||||
@@ -182,8 +186,14 @@ public class ModProcessor {
|
||||
|
||||
// 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);
|
||||
if (info.getMetadata().mixinRemapType() == ArtifactMetadata.MixinRemapType.STATIC) {
|
||||
if (!requiresStaticMixinRemap) {
|
||||
// Should be impossible but stranger things have happened.
|
||||
throw new IllegalStateException("Was not configured for static remap, but a mod required it?!");
|
||||
}
|
||||
|
||||
project.getLogger().info("Remapping mixins in {} statically", info.getInputFile());
|
||||
remapMixins.add(tag);
|
||||
}
|
||||
|
||||
remapper.readInputsAsync(tag, info.getInputFile());
|
||||
@@ -243,10 +253,10 @@ public class ModProcessor {
|
||||
}
|
||||
|
||||
private void remapJarManifestEntries(Path jar) throws IOException {
|
||||
ZipUtils.transform(jar, Map.of(RemapJarTask.MANIFEST_PATH, bytes -> {
|
||||
ZipUtils.transform(jar, Map.of(Constants.Manifest.PATH, bytes -> {
|
||||
var manifest = new Manifest(new ByteArrayInputStream(bytes));
|
||||
|
||||
manifest.getMainAttributes().putValue(RemapJarTask.MANIFEST_NAMESPACE_KEY, toM);
|
||||
manifest.getMainAttributes().putValue(Constants.Manifest.MAPPING_NAMESPACE, toM);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
manifest.write(out);
|
||||
|
||||
@@ -31,10 +31,12 @@ import org.gradle.api.Project;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactMetadata;
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactRef;
|
||||
|
||||
public abstract sealed class ModDependency permits SplitModDependency, SimpleModDependency {
|
||||
private final ArtifactRef artifact;
|
||||
private final ArtifactMetadata metadata;
|
||||
protected final String group;
|
||||
protected final String name;
|
||||
protected final String version;
|
||||
@@ -43,8 +45,9 @@ public abstract sealed class ModDependency permits SplitModDependency, SimpleMod
|
||||
protected final String mappingsSuffix;
|
||||
protected final Project project;
|
||||
|
||||
public ModDependency(ArtifactRef artifact, String mappingsSuffix, Project project) {
|
||||
public ModDependency(ArtifactRef artifact, ArtifactMetadata metadata, String mappingsSuffix, Project project) {
|
||||
this.artifact = artifact;
|
||||
this.metadata = metadata;
|
||||
this.group = artifact.group();
|
||||
this.name = artifact.name();
|
||||
this.version = artifact.version();
|
||||
@@ -78,6 +81,10 @@ public abstract sealed class ModDependency permits SplitModDependency, SimpleMod
|
||||
return artifact;
|
||||
}
|
||||
|
||||
public ArtifactMetadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
protected String getRemappedGroup() {
|
||||
return getMappingsPrefix() + "." + group;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.gradle.api.artifacts.Configuration;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactMetadata;
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactRef;
|
||||
import net.fabricmc.loom.configuration.mods.JarSplitter;
|
||||
import net.fabricmc.loom.util.AttributeHelper;
|
||||
@@ -40,7 +41,7 @@ import net.fabricmc.loom.util.AttributeHelper;
|
||||
public class ModDependencyFactory {
|
||||
private static final String TARGET_ATTRIBUTE_KEY = "loom-target";
|
||||
|
||||
public static ModDependency create(ArtifactRef artifact, Configuration targetConfig, @Nullable Configuration targetClientConfig, String mappingsSuffix, Project project) {
|
||||
public static ModDependency create(ArtifactRef artifact, ArtifactMetadata metadata, Configuration targetConfig, @Nullable Configuration targetClientConfig, String mappingsSuffix, Project project) {
|
||||
if (targetClientConfig != null && LoomGradleExtension.get(project).getSplitModDependencies().get()) {
|
||||
final Optional<JarSplitter.Target> cachedTarget = readTarget(artifact);
|
||||
JarSplitter.Target target;
|
||||
@@ -53,11 +54,11 @@ public class ModDependencyFactory {
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
return new SplitModDependency(artifact, mappingsSuffix, targetConfig, targetClientConfig, target, project);
|
||||
return new SplitModDependency(artifact, metadata, mappingsSuffix, targetConfig, targetClientConfig, target, project);
|
||||
}
|
||||
}
|
||||
|
||||
return new SimpleModDependency(artifact, mappingsSuffix, targetConfig, project);
|
||||
return new SimpleModDependency(artifact, metadata, mappingsSuffix, targetConfig, project);
|
||||
}
|
||||
|
||||
private static Optional<JarSplitter.Target> readTarget(ArtifactRef artifact) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactMetadata;
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactRef;
|
||||
|
||||
// Single jar in and out
|
||||
@@ -39,8 +40,8 @@ public final class SimpleModDependency extends ModDependency {
|
||||
private final Configuration targetConfig;
|
||||
private final LocalMavenHelper maven;
|
||||
|
||||
public SimpleModDependency(ArtifactRef artifact, String mappingsSuffix, Configuration targetConfig, Project project) {
|
||||
super(artifact, mappingsSuffix, project);
|
||||
public SimpleModDependency(ArtifactRef artifact, ArtifactMetadata metadata, String mappingsSuffix, Configuration targetConfig, Project project) {
|
||||
super(artifact, metadata, mappingsSuffix, project);
|
||||
this.targetConfig = Objects.requireNonNull(targetConfig);
|
||||
this.maven = createMaven(name);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.ModSettings;
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactMetadata;
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactRef;
|
||||
import net.fabricmc.loom.configuration.mods.JarSplitter;
|
||||
|
||||
@@ -47,8 +48,8 @@ public final class SplitModDependency extends ModDependency {
|
||||
@Nullable
|
||||
private final LocalMavenHelper clientMaven;
|
||||
|
||||
public SplitModDependency(ArtifactRef artifact, String mappingsSuffix, Configuration targetCommonConfig, Configuration targetClientConfig, JarSplitter.Target target, Project project) {
|
||||
super(artifact, mappingsSuffix, project);
|
||||
public SplitModDependency(ArtifactRef artifact, ArtifactMetadata metadata, String mappingsSuffix, Configuration targetCommonConfig, Configuration targetClientConfig, JarSplitter.Target target, Project project) {
|
||||
super(artifact, metadata, mappingsSuffix, project);
|
||||
this.targetCommonConfig = Objects.requireNonNull(targetCommonConfig);
|
||||
this.targetClientConfig = Objects.requireNonNull(targetClientConfig);
|
||||
this.target = Objects.requireNonNull(target);
|
||||
|
||||
@@ -39,7 +39,7 @@ import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import net.fabricmc.loom.configuration.mods.JarSplitter;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
|
||||
public class MinecraftJarSplitter implements AutoCloseable {
|
||||
@@ -132,11 +132,11 @@ public class MinecraftJarSplitter implements AutoCloseable {
|
||||
private void writeManifest(FileSystemUtil.Delegate outputFs, String env) throws IOException {
|
||||
final Manifest manifest = new Manifest();
|
||||
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||
manifest.getMainAttributes().putValue(JarSplitter.MANIFEST_SPLIT_ENV_NAME_KEY, env);
|
||||
manifest.getMainAttributes().putValue(Constants.Manifest.SPLIT_ENV_NAME, env);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
manifest.write(out);
|
||||
Files.createDirectories(outputFs.get().getPath("META-INF"));
|
||||
Files.write(outputFs.get().getPath("META-INF/MANIFEST.MF"), out.toByteArray());
|
||||
Files.write(outputFs.get().getPath(Constants.Manifest.PATH), out.toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,7 +35,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -66,19 +65,12 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.task.service.JarManifestService;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.ZipReprocessorUtil;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper;
|
||||
|
||||
public abstract class AbstractRemapJarTask extends Jar {
|
||||
public static final String MANIFEST_PATH = "META-INF/MANIFEST.MF";
|
||||
public static final String MANIFEST_NAMESPACE_KEY = "Fabric-Mapping-Namespace";
|
||||
public static final String MANIFEST_SPLIT_ENV_KEY = "Fabric-Loom-Split-Environment";
|
||||
public static final String MANIFEST_CLIENT_ENTRIES_KEY = "Fabric-Loom-Client-Only-Entries";
|
||||
public static final String MANIFEST_JAR_TYPE_KEY = "Fabric-Jar-Type";
|
||||
public static final Attributes.Name MANIFEST_SPLIT_ENV_NAME = new Attributes.Name(MANIFEST_SPLIT_ENV_KEY);
|
||||
public static final Attributes.Name MANIFEST_CLIENT_ENTRIES_NAME = new Attributes.Name(MANIFEST_CLIENT_ENTRIES_KEY);
|
||||
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getInputFile();
|
||||
|
||||
@@ -157,7 +149,7 @@ public abstract class AbstractRemapJarTask extends Jar {
|
||||
}
|
||||
|
||||
if (getJarType().isPresent()) {
|
||||
params.getManifestAttributes().put(MANIFEST_JAR_TYPE_KEY, getJarType().get());
|
||||
params.getManifestAttributes().put(Constants.Manifest.JAR_TYPE, getJarType().get());
|
||||
}
|
||||
|
||||
action.execute(params);
|
||||
@@ -197,8 +189,8 @@ public abstract class AbstractRemapJarTask extends Jar {
|
||||
|
||||
protected void applyClientOnlyManifestAttributes(AbstractRemapParams params, List<String> entries) {
|
||||
params.getManifestAttributes().set(Map.of(
|
||||
MANIFEST_SPLIT_ENV_KEY, "true",
|
||||
MANIFEST_CLIENT_ENTRIES_KEY, String.join(";", entries)
|
||||
Constants.Manifest.SPLIT_ENV, "true",
|
||||
Constants.Manifest.CLIENT_ENTRIES, String.join(";", entries)
|
||||
));
|
||||
}
|
||||
|
||||
@@ -213,11 +205,11 @@ public abstract class AbstractRemapJarTask extends Jar {
|
||||
}
|
||||
|
||||
protected void modifyJarManifest() throws IOException {
|
||||
int count = ZipUtils.transform(outputFile, Map.of(MANIFEST_PATH, bytes -> {
|
||||
int count = ZipUtils.transform(outputFile, Map.of(Constants.Manifest.PATH, bytes -> {
|
||||
var manifest = new Manifest(new ByteArrayInputStream(bytes));
|
||||
|
||||
getParameters().getJarManifestService().get().apply(manifest, getParameters().getManifestAttributes().get());
|
||||
manifest.getMainAttributes().putValue(MANIFEST_NAMESPACE_KEY, getParameters().getTargetNamespace().get());
|
||||
manifest.getMainAttributes().putValue(Constants.Manifest.MAPPING_NAMESPACE, getParameters().getTargetNamespace().get());
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
manifest.write(out);
|
||||
|
||||
@@ -64,6 +64,7 @@ import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.build.nesting.IncludedJarFactory;
|
||||
import net.fabricmc.loom.build.nesting.JarNester;
|
||||
import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile;
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactMetadata;
|
||||
import net.fabricmc.loom.extension.MixinExtension;
|
||||
import net.fabricmc.loom.task.service.TinyRemapperService;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
@@ -150,6 +151,12 @@ public abstract class RemapJarTask extends AbstractRemapJarTask {
|
||||
if (mixinAp) {
|
||||
setupLegacyMixinRefmapRemapping(params);
|
||||
}
|
||||
|
||||
// Add the mixin refmap remap type to the manifest
|
||||
// This is used by the mod dependency remapper to determine if it should remap the refmap
|
||||
// or if the refmap should be remapped by mixin at runtime.
|
||||
final var refmapRemapType = mixinAp ? ArtifactMetadata.MixinRemapType.MIXIN : ArtifactMetadata.MixinRemapType.STATIC;
|
||||
params.getManifestAttributes().put(Constants.Manifest.MIXIN_REMAP_TYPE, refmapRemapType.manifestValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
package net.fabricmc.loom.task.service;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.jar.Attributes;
|
||||
@@ -78,7 +77,7 @@ public abstract class JarManifestService implements BuildService<JarManifestServ
|
||||
Attributes attributes = manifest.getMainAttributes();
|
||||
|
||||
extraValues.entrySet().stream()
|
||||
.sorted(Comparator.comparing(Map.Entry::getKey))
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.forEach(entry -> {
|
||||
attributes.putValue(entry.getKey(), entry.getValue());
|
||||
});
|
||||
@@ -90,17 +89,17 @@ public abstract class JarManifestService implements BuildService<JarManifestServ
|
||||
|
||||
Params p = getParameters();
|
||||
|
||||
attributes.putValue("Fabric-Gradle-Version", p.getGradleVersion().get());
|
||||
attributes.putValue("Fabric-Loom-Version", p.getLoomVersion().get());
|
||||
attributes.putValue("Fabric-Mixin-Compile-Extensions-Version", p.getMCEVersion().get());
|
||||
attributes.putValue("Fabric-Minecraft-Version", p.getMinecraftVersion().get());
|
||||
attributes.putValue("Fabric-Tiny-Remapper-Version", p.getTinyRemapperVersion().get());
|
||||
attributes.putValue("Fabric-Loader-Version", p.getFabricLoaderVersion().get());
|
||||
attributes.putValue(Constants.Manifest.GRADLE_VERSION, p.getGradleVersion().get());
|
||||
attributes.putValue(Constants.Manifest.LOOM_VERSION, p.getLoomVersion().get());
|
||||
attributes.putValue(Constants.Manifest.MIXIN_COMPILE_EXTENSIONS_VERSION, p.getMCEVersion().get());
|
||||
attributes.putValue(Constants.Manifest.MINECRAFT_VERSION, p.getMinecraftVersion().get());
|
||||
attributes.putValue(Constants.Manifest.TINY_REMAPPER_VERSION, p.getTinyRemapperVersion().get());
|
||||
attributes.putValue(Constants.Manifest.FABRIC_LOADER_VERSION, p.getFabricLoaderVersion().get());
|
||||
|
||||
// This can be overridden by mods if required
|
||||
if (!attributes.containsKey("Fabric-Mixin-Version")) {
|
||||
attributes.putValue("Fabric-Mixin-Version", p.getMixinVersion().get().version());
|
||||
attributes.putValue("Fabric-Mixin-Group", p.getMixinVersion().get().group());
|
||||
if (!attributes.containsKey(Constants.Manifest.MIXIN_VERSION)) {
|
||||
attributes.putValue(Constants.Manifest.MIXIN_VERSION, p.getMixinVersion().get().version());
|
||||
attributes.putValue(Constants.Manifest.MIXIN_GROUP, p.getMixinVersion().get().group());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,4 +124,24 @@ public class Constants {
|
||||
public static final String DISABLE_PROJECT_DEPENDENT_MODS = "fabric.loom.disableProjectDependentMods";
|
||||
public static final String LIBRARY_PROCESSORS = "fabric.loom.libraryProcessors";
|
||||
}
|
||||
|
||||
public static final class Manifest {
|
||||
public static final String PATH = "META-INF/MANIFEST.MF";
|
||||
|
||||
public static final String REMAP_KEY = "Fabric-Loom-Remap";
|
||||
public static final String MIXIN_REMAP_TYPE = "Fabric-Loom-Mixin-Remap-Type";
|
||||
public static final String MAPPING_NAMESPACE = "Fabric-Mapping-Namespace";
|
||||
public static final String SPLIT_ENV = "Fabric-Loom-Split-Environment";
|
||||
public static final String SPLIT_ENV_NAME = "Fabric-Loom-Split-Environment-Name";
|
||||
public static final String CLIENT_ENTRIES = "Fabric-Loom-Client-Only-Entries";
|
||||
public static final String JAR_TYPE = "Fabric-Jar-Type";
|
||||
public static final String GRADLE_VERSION = "Fabric-Gradle-Version";
|
||||
public static final String LOOM_VERSION = "Fabric-Loom-Version";
|
||||
public static final String MIXIN_COMPILE_EXTENSIONS_VERSION = "Fabric-Mixin-Compile-Extensions-Version";
|
||||
public static final String MINECRAFT_VERSION = "Fabric-Minecraft-Version";
|
||||
public static final String TINY_REMAPPER_VERSION = "Fabric-Tiny-Remapper-Version";
|
||||
public static final String FABRIC_LOADER_VERSION = "Fabric-Loader-Version";
|
||||
public static final String MIXIN_VERSION = "Fabric-Mixin-Version";
|
||||
public static final String MIXIN_GROUP = "Fabric-Mixin-Group";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ import org.intellij.lang.annotations.MagicConstant;
|
||||
public class ZipReprocessorUtil {
|
||||
private ZipReprocessorUtil() { }
|
||||
|
||||
private static final String MANIFEST_LOCATION = "META-INF/MANIFEST.MF";
|
||||
private static final String META_INF = "META-INF/";
|
||||
|
||||
// See https://docs.oracle.com/en/java/javase/20/docs/specs/jar/jar.html#signed-jar-file
|
||||
@@ -68,9 +67,9 @@ public class ZipReprocessorUtil {
|
||||
private static int specialOrdering(String name1, String name2) {
|
||||
if (name1.equals(name2)) {
|
||||
return 0;
|
||||
} else if (name1.equals(MANIFEST_LOCATION)) {
|
||||
} else if (name1.equals(Constants.Manifest.PATH)) {
|
||||
return -1;
|
||||
} else if (name2.equals(MANIFEST_LOCATION)) {
|
||||
} else if (name2.equals(Constants.Manifest.PATH)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,20 +32,19 @@ import spock.lang.Unroll
|
||||
|
||||
import net.fabricmc.loom.test.util.GradleProjectTestTrait
|
||||
import net.fabricmc.loom.test.util.ServerRunner
|
||||
import net.fabricmc.loom.util.ZipUtils
|
||||
|
||||
import static net.fabricmc.loom.test.LoomTestConstants.*
|
||||
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
|
||||
|
||||
@Timeout(value = 30, unit = TimeUnit.MINUTES)
|
||||
class FabricAPITest extends Specification implements GradleProjectTestTrait {
|
||||
private static final String API_VERSION = "0.0.0+loom"
|
||||
|
||||
@Unroll
|
||||
def "build and run (gradle #version, mixin ap disabled: #disableMixinAp)"() {
|
||||
setup:
|
||||
def gradle = gradleProject(
|
||||
repo: "https://github.com/FabricMC/fabric.git",
|
||||
commit: "f091af96c53963fadf9dbc391c67bb40e5678a96",
|
||||
commit: "23e8616e7457d7d4a65119b93952d134607ffc5c",
|
||||
version: version,
|
||||
patch: "fabric_api"
|
||||
)
|
||||
@@ -53,24 +52,39 @@ 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 = """
|
||||
|
||||
gradle.buildGradle << """
|
||||
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\"") + mixinApPatch
|
||||
def minecraftVersion = "23w45a"
|
||||
def server = ServerRunner.create(gradle.projectDir, minecraftVersion)
|
||||
.withMod(gradle.getOutputFile("fabric-api-999.0.0.jar"))
|
||||
|
||||
def server = ServerRunner.create(gradle.projectDir, "23w33a")
|
||||
.withMod(gradle.getOutputFile("fabric-api-${API_VERSION}.jar"))
|
||||
// Test that the dependent mod can be built against the previously built fabric-api
|
||||
def dependentMod = gradleProject(project: "minimalBase", version: version)
|
||||
dependentMod.buildGradle << """
|
||||
repositories {
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
loom {
|
||||
loom.mixin.useLegacyMixinAp = ${!disableMixinAp}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:${minecraftVersion}"
|
||||
mappings "net.fabricmc:yarn:${minecraftVersion}+build.1:v2"
|
||||
|
||||
modImplementation "net.fabricmc.fabric-api:fabric-api:999.0.0"
|
||||
}
|
||||
"""
|
||||
when:
|
||||
def result = gradle.run(tasks: [
|
||||
"clean",
|
||||
"build",
|
||||
"publishToMavenLocal"
|
||||
], args: [
|
||||
@@ -85,19 +99,31 @@ class FabricAPITest extends Specification implements GradleProjectTestTrait {
|
||||
gradle.printOutputFiles()
|
||||
|
||||
def serverResult = server.run()
|
||||
def dependentModResult = dependentMod.run(task: "build")
|
||||
|
||||
then:
|
||||
result.task(":build").outcome == SUCCESS
|
||||
result.task(":prepareRemapJar").outcome == SUCCESS
|
||||
|
||||
new File(gradle.mavenLocalDir, "net/fabricmc/fabric-api/fabric-biome-api-v1/13.0.11/fabric-biome-api-v1-13.0.11.jar").exists()
|
||||
new File(gradle.mavenLocalDir, "net/fabricmc/fabric-api/fabric-biome-api-v1/13.0.11/fabric-biome-api-v1-13.0.11-sources.jar").exists()
|
||||
def biomeApiJar = new File(gradle.mavenLocalDir, "net/fabricmc/fabric-api/fabric-biome-api-v1/999.0.0/fabric-biome-api-v1-999.0.0.jar")
|
||||
def manifest = ZipUtils.unpack(biomeApiJar.toPath(), "META-INF/MANIFEST.MF").toString()
|
||||
new File(gradle.mavenLocalDir, "net/fabricmc/fabric-api/fabric-biome-api-v1/999.0.0/fabric-biome-api-v1-999.0.0-sources.jar").exists()
|
||||
|
||||
if (disableMixinAp) {
|
||||
manifest.contains("Fabric-Loom-Mixin-Remap-Type=static")
|
||||
} else {
|
||||
manifest.contains("Fabric-Loom-Mixin-Remap-Type=mixin")
|
||||
}
|
||||
|
||||
serverResult.successful()
|
||||
serverResult.output.contains("- fabric-api $API_VERSION")
|
||||
serverResult.output.contains("- fabric-api 999.0.0")
|
||||
|
||||
dependentModResult.task(":build").outcome == SUCCESS
|
||||
|
||||
where:
|
||||
[version, disableMixinAp] << [
|
||||
[DEFAULT_GRADLE],
|
||||
[false, true]
|
||||
[PRE_RELEASE_GRADLE],
|
||||
[false, true].shuffled()
|
||||
].combinations()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,11 +55,11 @@ class ReproducibleBuildTest extends Specification implements GradleProjectTestTr
|
||||
|
||||
where:
|
||||
version | modHash | sourceHash
|
||||
DEFAULT_GRADLE | "4bb8acb5e575a4080a8fe1282f8e1994" | [
|
||||
DEFAULT_GRADLE | "207bd75aa34fc996a97e962dd98b61d5" | [
|
||||
"8e8fac2a5e32fc872e6cf0f9ccc55cfd",
|
||||
"ed331b6fae5677797a0104eba014e255"
|
||||
]
|
||||
PRE_RELEASE_GRADLE | "4bb8acb5e575a4080a8fe1282f8e1994" | [
|
||||
PRE_RELEASE_GRADLE | "207bd75aa34fc996a97e962dd98b61d5" | [
|
||||
"8e8fac2a5e32fc872e6cf0f9ccc55cfd",
|
||||
"ed331b6fae5677797a0104eba014e255"
|
||||
]
|
||||
|
||||
@@ -31,8 +31,11 @@ import spock.lang.Specification
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactMetadata
|
||||
import net.fabricmc.loom.configuration.mods.ArtifactRef
|
||||
|
||||
import static net.fabricmc.loom.configuration.mods.ArtifactMetadata.MixinRemapType.MIXIN
|
||||
import static net.fabricmc.loom.configuration.mods.ArtifactMetadata.MixinRemapType.STATIC
|
||||
import static net.fabricmc.loom.configuration.mods.ArtifactMetadata.RemapRequirements.*
|
||||
import static net.fabricmc.loom.test.util.ZipTestUtils.*
|
||||
import static net.fabricmc.loom.test.util.ZipTestUtils.createZip
|
||||
import static net.fabricmc.loom.test.util.ZipTestUtils.manifest
|
||||
|
||||
class ArtifactMetadataTest extends Specification {
|
||||
def "is fabric mod"() {
|
||||
@@ -101,8 +104,92 @@ class ArtifactMetadataTest extends Specification {
|
||||
false | ["fabric.mod.json": "{}"] // Fabric mod, no installer data
|
||||
}
|
||||
|
||||
private static ArtifactMetadata createMetadata(Path zip) {
|
||||
return ArtifactMetadata.create(createArtifact(zip))
|
||||
def "Refmap remap type" () {
|
||||
given:
|
||||
def zip = createZip(entries)
|
||||
when:
|
||||
def metadata = createMetadata(zip)
|
||||
def result = metadata.mixinRemapType()
|
||||
then:
|
||||
result == type
|
||||
where:
|
||||
type | entries
|
||||
MIXIN | ["hello.json": "{}"] // None Mod jar
|
||||
MIXIN | ["fabric.mod.json": "{}"] // Fabric mod without manfiest file
|
||||
MIXIN | ["fabric.mod.json": "{}", "META-INF/MANIFEST.MF": manifest("Fabric-Loom-Mixin-Remap-Type", "mixin")] // Fabric mod without remap type entry
|
||||
STATIC | ["fabric.mod.json": "{}", "META-INF/MANIFEST.MF": manifest("Fabric-Loom-Mixin-Remap-Type", "static")] // Fabric mod opt-in
|
||||
}
|
||||
|
||||
// Test that a mod with the same or older version of loom can be read
|
||||
def "Valid loom version"() {
|
||||
given:
|
||||
def zip = createMod(modLoomVersion, "mixin")
|
||||
when:
|
||||
def metadata = createMetadata(zip, loomVersion)
|
||||
then:
|
||||
metadata != null
|
||||
where:
|
||||
loomVersion | modLoomVersion
|
||||
"1.4" | "1.0.1"
|
||||
"1.4" | "1.0.99"
|
||||
"1.4" | "1.4"
|
||||
"1.4" | "1.4.0"
|
||||
"1.4" | "1.4.1"
|
||||
"1.4" | "1.4.99"
|
||||
"1.4" | "1.4.local"
|
||||
"1.5" | "1.4.99"
|
||||
"2.0" | "1.4.99"
|
||||
}
|
||||
|
||||
// Test that a mod with the same or older version of loom can be read
|
||||
def "Invalid loom version"() {
|
||||
given:
|
||||
def zip = createMod(modLoomVersion, "mixin")
|
||||
when:
|
||||
def metadata = createMetadata(zip, loomVersion)
|
||||
then:
|
||||
def e = thrown(IllegalStateException)
|
||||
e.message == "Mod was built with a newer version of Loom ($modLoomVersion), you are using Loom ($loomVersion)"
|
||||
where:
|
||||
loomVersion | modLoomVersion
|
||||
"1.4" | "1.5"
|
||||
"1.4" | "1.5.00"
|
||||
"1.4" | "2.0"
|
||||
"1.4" | "2.4"
|
||||
}
|
||||
|
||||
def "Accepts all Loom versions"() {
|
||||
given:
|
||||
def zip = createMod(modLoomVersion, "static")
|
||||
when:
|
||||
def metadata = createMetadata(zip, loomVersion)
|
||||
then:
|
||||
metadata != null
|
||||
where:
|
||||
loomVersion | modLoomVersion
|
||||
// Valid
|
||||
"1.4" | "1.0.1"
|
||||
"1.4" | "1.0.99"
|
||||
"1.4" | "1.4"
|
||||
"1.4" | "1.4.0"
|
||||
"1.4" | "1.4.1"
|
||||
"1.4" | "1.4.99"
|
||||
"1.4" | "1.4.local"
|
||||
"1.5" | "1.4.99"
|
||||
"2.0" | "1.4.99"
|
||||
// Usually invalid
|
||||
"1.4" | "1.5"
|
||||
"1.4" | "1.5.00"
|
||||
"1.4" | "2.0"
|
||||
"1.4" | "2.4"
|
||||
}
|
||||
|
||||
private static Path createMod(String loomVersion, String remapType) {
|
||||
return createZip(["fabric.mod.json": "{}", "META-INF/MANIFEST.MF": manifest(["Fabric-Loom-Version": loomVersion, "Fabric-Loom-Mixin-Remap-Type": remapType])])
|
||||
}
|
||||
|
||||
private static ArtifactMetadata createMetadata(Path zip, String loomVersion = "1.4") {
|
||||
return ArtifactMetadata.create(createArtifact(zip), loomVersion)
|
||||
}
|
||||
|
||||
private static ArtifactRef createArtifact(Path zip) {
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
||||
@@ -50,9 +50,15 @@ class ZipTestUtils {
|
||||
}
|
||||
|
||||
static String manifest(String key, String value) {
|
||||
return manifest(Map.of(key, value))
|
||||
}
|
||||
|
||||
static String manifest(Map<String, String> entries) {
|
||||
def manifest = new Manifest()
|
||||
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0")
|
||||
manifest.getMainAttributes().putValue(key, value)
|
||||
entries.forEach { key, value ->
|
||||
manifest.getMainAttributes().putValue(key, value)
|
||||
}
|
||||
|
||||
def out = new ByteArrayOutputStream()
|
||||
manifest.write(out)
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
diff --git a/build.gradle b/build.gradle
|
||||
--- a/build.gradle (revision 14d319c0729baf781e171e3c9f845fda55670f1b)
|
||||
+++ b/build.gradle (date 1688330748664)
|
||||
@@ -37,17 +37,7 @@
|
||||
throw new NullPointerException("Could not find version for " + project.name)
|
||||
}
|
||||
|
||||
--- a/build.gradle (revision 23e8616e7457d7d4a65119b93952d134607ffc5c)
|
||||
+++ b/build.gradle (date 1699535194191)
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
def ENV = System.getenv()
|
||||
|
||||
-version = project.version + "+" + (ENV.GITHUB_RUN_NUMBER ? "" : "local-") + getBranch()
|
||||
+version = "999.0.0"
|
||||
logger.lifecycle("Building Fabric: " + version)
|
||||
|
||||
|
||||
@@ -22,24 +22,7 @@
|
||||
import org.apache.commons.codec.digest.DigestUtils
|
||||
|
||||
def getSubprojectVersion(project) {
|
||||
- // Get the version from the gradle.properties file
|
||||
- def version = project.properties["${project.name}-version"]
|
||||
-
|
||||
- if (!version) {
|
||||
- throw new NullPointerException("Could not find version for " + project.name)
|
||||
- }
|
||||
-
|
||||
- if (grgit == null) {
|
||||
- return version + "+nogit"
|
||||
- }
|
||||
@@ -16,7 +32,7 @@ diff --git a/build.gradle b/build.gradle
|
||||
- }
|
||||
-
|
||||
- return version + "+" + latestCommits.get(0).id.substring(0, 8) + DigestUtils.sha256Hex(project.rootProject.minecraft_version).substring(0, 2)
|
||||
+ return version
|
||||
+ return "999.0.0"
|
||||
}
|
||||
|
||||
def getBranch() {
|
||||
|
||||
Reference in New Issue
Block a user