Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Fabric-Loom-Mixin-Remap-Type manifest entry #980

Merged
merged 12 commits into from
Nov 20, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
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;
Expand All @@ -38,31 +39,45 @@

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

Expand All @@ -74,7 +89,32 @@ public static ArtifactMetadata create(ArtifactRef artifact) throws IOException {
}
}

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() {
Expand All @@ -100,4 +140,15 @@ private Predicate<ArtifactMetadata> getShouldRemap() {
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);
}
}
}
27 changes: 14 additions & 13 deletions src/main/java/net/fabricmc/loom/configuration/mods/JarSplitter.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@

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;

Expand All @@ -58,9 +59,9 @@ public JarSplitter(Path inputJar) {
@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;
}
Expand Down Expand Up @@ -122,9 +123,9 @@ public boolean split(Path commonOutputJar, Path clientOutputJar) throws IOExcept
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");
}

Expand Down Expand Up @@ -157,7 +158,7 @@ public boolean split(Path commonOutputJar, Path clientOutputJar) throws IOExcept

final String entryPath = relativePath.toString();

if (entryPath.equals(AbstractRemapJarTask.MANIFEST_PATH)) {
if (entryPath.equals(Constants.Manifest.PATH)) {
continue;
}

Expand All @@ -183,11 +184,11 @@ public boolean split(Path commonOutputJar, Path clientOutputJar) throws IOExcept
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));
}
}

Expand All @@ -197,7 +198,7 @@ public boolean split(Path commonOutputJar, Path clientOutputJar) throws IOExcept
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);
Expand All @@ -206,7 +207,7 @@ private byte[] writeWithEnvironment(Manifest in, String value) throws IOExceptio

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();
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,15 @@
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;
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;
Expand Down Expand Up @@ -137,9 +139,9 @@ public static void supplyModConfigurations(Project project, SharedServiceManager
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) {
Expand All @@ -158,7 +160,7 @@ public static void supplyModConfigurations(Project project, SharedServiceManager
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
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;
Expand Down Expand Up @@ -149,9 +148,14 @@ private void remapJars(List<ModDependency> remapList) throws IOException {
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();

Expand Down Expand Up @@ -182,8 +186,14 @@ private void remapJars(List<ModDependency> remapList) throws IOException {

// 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());
Expand Down Expand Up @@ -243,10 +253,10 @@ private static Path getRemappedOutput(ModDependency dependency) {
}

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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@
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;
Expand All @@ -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();
Expand Down Expand Up @@ -78,6 +81,10 @@ public ArtifactRef getInputArtifact() {
return artifact;
}

public ArtifactMetadata getMetadata() {
return metadata;
}

protected String getRemappedGroup() {
return getMappingsPrefix() + "." + group;
}
Expand Down
Loading
Loading