Skip to content

Commit

Permalink
Rewrite migrate mappings task to be configuration cache compatible.
Browse files Browse the repository at this point in the history
  • Loading branch information
modmuss50 committed Sep 6, 2024
1 parent d18c109 commit d9adba5
Show file tree
Hide file tree
Showing 6 changed files with 376 additions and 185 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,16 @@

import com.google.common.base.Suppliers;
import org.gradle.api.Project;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.FileCollection;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Optional;
import org.jetbrains.annotations.Nullable;

import net.fabricmc.loom.util.FileSystemUtil;
import net.fabricmc.loom.util.service.Service;
import net.fabricmc.loom.util.service.ServiceFactory;
import net.fabricmc.loom.util.service.ServiceType;
Expand All @@ -45,27 +51,58 @@ public final class TinyMappingsService extends Service<TinyMappingsService.Optio
public static final ServiceType<Options, TinyMappingsService> TYPE = new ServiceType<>(Options.class, TinyMappingsService.class);

public interface Options extends Service.Options {
@InputFile
RegularFileProperty getMappings();
@InputFiles
ConfigurableFileCollection getMappings(); // Only a single file

/**
* When present, the mappings will be read from the specified zip entry path.
*/
@Optional
@Input
Property<String> getZipEntryPath();
}

public static Provider<Options> createOptions(Project project, Path mappings) {
return TYPE.create(project, options -> options.getMappings().set(project.file(mappings)));
return TYPE.create(project, options -> {
options.getMappings().from(project.file(mappings));
options.getZipEntryPath().unset();
});
}

public static Provider<Options> createOptions(Project project, FileCollection mappings, @Nullable String zipEntryPath) {
return TYPE.create(project, options -> {
options.getMappings().from(mappings);
options.getZipEntryPath().set(zipEntryPath);
});
}

public TinyMappingsService(Options options, ServiceFactory serviceFactory) {
super(options, serviceFactory);
}

private final Supplier<MemoryMappingTree> mappingTree = Suppliers.memoize(() -> {
Path mappings = getOptions().getMappings().getSingleFile().toPath();

if (getOptions().getZipEntryPath().isPresent()) {
try (FileSystemUtil.Delegate delegate = FileSystemUtil.getJarFileSystem(mappings)) {
return readMappings(delegate.fs().getPath(getOptions().getZipEntryPath().get()));
} catch (IOException e) {
throw new UncheckedIOException("Failed to read mappings from zip", e);
}
}

return readMappings(mappings);
});

private MemoryMappingTree readMappings(Path mappings) {
try {
MemoryMappingTree mappingTree = new MemoryMappingTree();
MappingReader.read(getOptions().getMappings().get().getAsFile().toPath(), mappingTree);
MappingReader.read(mappings, mappingTree);
return mappingTree;
} catch (IOException e) {
throw new UncheckedIOException("Failed to read mappings", e);
}
});
}

public MemoryMappingTree getMappingTree() {
return mappingTree.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.Nested;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.SourceSet;
Expand Down Expand Up @@ -246,12 +245,6 @@ public RegularFileProperty getInput() {
return getInputFile();
}

@ApiStatus.Internal
@Internal
protected LoomGradleExtension getLoomExtension() {
return LoomGradleExtension.get(getProject());
}

private SourceSet getClientSourceSet() {
Preconditions.checkArgument(LoomGradleExtension.get(getProject()).areEnvironmentSourceSetsSplit(), "Cannot get client sourceset as project is not split");
return SourceSetHelper.getSourceSetByName(getClientOnlySourceSetName().get(), getProject());
Expand Down
190 changes: 24 additions & 166 deletions src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2019-2022 FabricMC
* Copyright (c) 2019-2024 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
Expand All @@ -24,189 +24,47 @@

package net.fabricmc.loom.task;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Set;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import org.cadixdev.lorenz.MappingSet;
import org.cadixdev.mercury.Mercury;
import org.cadixdev.mercury.remapper.MercuryRemapper;
import org.gradle.api.GradleException;
import org.gradle.api.IllegalDependencyNotation;
import org.gradle.api.JavaVersion;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputDirectory;
import org.gradle.api.tasks.Nested;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.options.Option;
import org.gradle.work.DisableCachingByDefault;

import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuilderImpl;
import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsFactory;
import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration;
import net.fabricmc.loom.util.FileSystemUtil;
import net.fabricmc.loom.util.SourceRemapper;
import net.fabricmc.loom.task.service.MigrateMappingsService;
import net.fabricmc.loom.util.service.ScopedServiceFactory;
import net.fabricmc.lorenztiny.TinyMappingsJoiner;
import net.fabricmc.mappingio.MappingReader;
import net.fabricmc.mappingio.tree.MemoryMappingTree;

@DisableCachingByDefault(because = "Always rerun this task.")
public abstract class MigrateMappingsTask extends AbstractLoomTask {
private Path inputDir;
private Path outputDir;
private String mappings;

public MigrateMappingsTask() {
inputDir = getProject().file("src/main/java").toPath();
outputDir = getProject().file("remappedSrc").toPath();

// Ensure we resolve the classpath inputs before running the task.
getCompileClasspath().from(getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME));

notCompatibleWithConfigurationCache("Not yet supported.");
}
@Input
@Option(option = "mappings", description = "Target mappings")
public abstract Property<String> getMappings();

@InputDirectory
@Option(option = "input", description = "Java source file directory")
public void setInputDir(String inputDir) {
this.inputDir = getProject().file(inputDir).toPath();
}
public abstract DirectoryProperty getInputDir();

@OutputDirectory
@Option(option = "output", description = "Remapped source output directory")
public void setOutputDir(String outputDir) {
this.outputDir = getProject().file(outputDir).toPath();
}
public abstract DirectoryProperty getOutputDir();

@Option(option = "mappings", description = "Target mappings")
public void setMappings(String mappings) {
this.mappings = mappings;
}
@Nested
protected abstract Property<MigrateMappingsService.Options> getMigrationServiceOptions();

@InputFiles
public abstract ConfigurableFileCollection getCompileClasspath();
public MigrateMappingsTask() {
getInputDir().convention(getProject().getLayout().getProjectDirectory().dir("src/main/java"));
getOutputDir().convention(getProject().getLayout().getProjectDirectory().dir("remappedSrc"));
getMigrationServiceOptions().set(MigrateMappingsService.createOptions(getProject(), getMappings(), getInputDir(), getOutputDir()));
}

@TaskAction
public void doTask() throws Throwable {
Project project = getProject();
LoomGradleExtension extension = getExtension();

project.getLogger().info(":loading mappings");

if (!Files.exists(inputDir) || !Files.isDirectory(inputDir)) {
throw new IllegalArgumentException("Could not find input directory: " + inputDir.toAbsolutePath());
}

Files.createDirectories(outputDir);

File mappings = loadMappings();
MappingConfiguration mappingConfiguration = extension.getMappingConfiguration();

try (var serviceFactory = new ScopedServiceFactory()) {
MemoryMappingTree currentMappings = mappingConfiguration.getMappingsService(project, serviceFactory).getMappingTree();
MemoryMappingTree targetMappings = getMappings(mappings);
migrateMappings(project, extension, inputDir, outputDir, currentMappings, targetMappings);
project.getLogger().lifecycle(":remapped project written to " + outputDir.toAbsolutePath());
} catch (IOException e) {
throw new IllegalArgumentException("Error while loading mappings", e);
MigrateMappingsService service = serviceFactory.get(getMigrationServiceOptions().get());
service.migrateMapppings();
}
}

private File loadMappings() {
Project project = getProject();

if (mappings == null || mappings.isEmpty()) {
throw new IllegalArgumentException("No mappings were specified. Use --mappings=\"\" to specify target mappings");
}

Set<File> files;

try {
if (mappings.startsWith("net.minecraft:mappings:")) {
if (!mappings.endsWith(":" + LoomGradleExtension.get(project).getMinecraftProvider().minecraftVersion())) {
throw new UnsupportedOperationException("Migrating Mojang mappings is currently only supported for the specified minecraft version");
}

LayeredMappingsFactory dep = new LayeredMappingsFactory(LayeredMappingSpecBuilderImpl.buildOfficialMojangMappings());
files = Collections.singleton(dep.resolve(getProject()).toFile());
} else {
Dependency dependency = project.getDependencies().create(mappings);
files = project.getConfigurations().detachedConfiguration(dependency).resolve();
}
} catch (IllegalDependencyNotation ignored) {
project.getLogger().info("Could not locate mappings, presuming V2 Yarn");

try {
files = project.getConfigurations().detachedConfiguration(project.getDependencies().create(ImmutableMap.of("group", "net.fabricmc", "name", "yarn", "version", mappings, "classifier", "v2"))).resolve();
} catch (GradleException ignored2) {
project.getLogger().info("Could not locate mappings, presuming V1 Yarn");
files = project.getConfigurations().detachedConfiguration(project.getDependencies().create(ImmutableMap.of("group", "net.fabricmc", "name", "yarn", "version", mappings))).resolve();
}
} catch (IOException e) {
throw new UncheckedIOException("Failed to resolve mappings", e);
}

if (files.isEmpty()) {
throw new IllegalArgumentException("Mappings could not be found");
}

return Iterables.getOnlyElement(files);
}

private static MemoryMappingTree getMappings(File mappings) throws IOException {
MemoryMappingTree mappingTree = new MemoryMappingTree();

try (FileSystemUtil.Delegate delegate = FileSystemUtil.getJarFileSystem(mappings.toPath())) {
MappingReader.read(delegate.fs().getPath("mappings/mappings.tiny"), mappingTree);
}

return mappingTree;
}

private static void migrateMappings(Project project, LoomGradleExtension extension,
Path inputDir, Path outputDir, MemoryMappingTree currentMappings, MemoryMappingTree targetMappings
) throws IOException {
project.getLogger().info(":joining mappings");

MappingSet mappingSet = new TinyMappingsJoiner(
currentMappings, MappingsNamespace.NAMED.toString(),
targetMappings, MappingsNamespace.NAMED.toString(),
MappingsNamespace.INTERMEDIARY.toString()
).read();

project.getLogger().lifecycle(":remapping");
Mercury mercury = SourceRemapper.createMercuryWithClassPath(project, false);

final JavaVersion javaVersion = project.getExtensions().getByType(JavaPluginExtension.class).getSourceCompatibility();
mercury.setSourceCompatibility(javaVersion.toString());

for (Path intermediaryJar : extension.getMinecraftJars(MappingsNamespace.INTERMEDIARY)) {
mercury.getClassPath().add(intermediaryJar);
}

for (Path intermediaryJar : extension.getMinecraftJars(MappingsNamespace.NAMED)) {
mercury.getClassPath().add(intermediaryJar);
}

mercury.getProcessors().add(MercuryRemapper.create(mappingSet));

try {
mercury.rewrite(inputDir, outputDir);
} catch (Exception e) {
project.getLogger().warn("Could not remap fully!", e);
}

project.getLogger().info(":cleaning file descriptors");
System.gc();
}
}
Loading

0 comments on commit d9adba5

Please sign in to comment.