mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-28 04:07:01 -05:00
Add task to migrate CT/AW mappings (#1412)
* Add task to migrate CT/AW mappings * Remove unused logger * Add option to map CT in place, add common base class * Add test for migrating AW/CT mappings * Add SkipWhenEmpty to MigrateClassTweakerMappingsTask.inputFile * Split CT migration service from base service
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2025 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.task;
|
||||
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.options.Option;
|
||||
|
||||
public abstract class AbstractMigrateMappingsTask extends AbstractLoomTask {
|
||||
@Input
|
||||
@Option(option = "mappings", description = "Target mappings")
|
||||
public abstract Property<String> getMappings();
|
||||
|
||||
@Input
|
||||
@Option(option = "overrideInputsIHaveABackup", description = "Override input files with the remapped files")
|
||||
public abstract Property<Boolean> getOverrideInputs();
|
||||
|
||||
public AbstractMigrateMappingsTask() {
|
||||
getOverrideInputs().convention(false);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2016-2022 FabricMC
|
||||
* Copyright (c) 2016-2025 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
|
||||
@@ -63,7 +63,7 @@ public abstract class LoomTasks implements Runnable {
|
||||
SourceSetHelper.getSourceSets(getProject()).all(sourceSet -> {
|
||||
if (SourceSetHelper.isMainSourceSet(sourceSet)) {
|
||||
getTasks().register("migrateMappings", MigrateMappingsTask.class, t -> {
|
||||
t.setDescription("Migrates mappings to a new version.");
|
||||
t.setDescription("Migrates source code mappings to a new version.");
|
||||
});
|
||||
|
||||
return;
|
||||
@@ -74,12 +74,16 @@ public abstract class LoomTasks implements Runnable {
|
||||
}
|
||||
|
||||
getTasks().register(sourceSet.getTaskName("migrate", "mappings"), MigrateMappingsTask.class, t -> {
|
||||
t.setDescription("Migrates mappings to a new version.");
|
||||
t.setDescription("Migrates source code mappings to a new version.");
|
||||
t.getInputDir().set(SourceSetHelper.getFirstSrcDir(sourceSet));
|
||||
t.getOutputDir().convention(getProject().getLayout().getProjectDirectory().dir(sourceSet.getTaskName("remapped", "src")));
|
||||
});
|
||||
});
|
||||
|
||||
getTasks().register("migrateClassTweakerMappings", MigrateClassTweakerMappingsTask.class, t -> {
|
||||
t.setDescription("Migrates access widener and class tweaker mappings to a new version.");
|
||||
});
|
||||
|
||||
var generateLog4jConfig = getTasks().register("generateLog4jConfig", GenerateLog4jConfigTask.class, t -> {
|
||||
t.setDescription("Generate the log4j config file");
|
||||
});
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2025 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.task;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.Nested;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.SkipWhenEmpty;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.tasks.UntrackedTask;
|
||||
import org.gradle.api.tasks.options.Option;
|
||||
|
||||
import net.fabricmc.classtweaker.api.ClassTweakerReader;
|
||||
import net.fabricmc.classtweaker.api.ClassTweakerWriter;
|
||||
import net.fabricmc.classtweaker.visitors.ClassTweakerRemapperVisitor;
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.task.service.MigrateClassTweakerMappingsService;
|
||||
import net.fabricmc.loom.util.service.ScopedServiceFactory;
|
||||
|
||||
@UntrackedTask(because = "Always rerun this task.")
|
||||
public abstract class MigrateClassTweakerMappingsTask extends AbstractMigrateMappingsTask {
|
||||
@InputFile
|
||||
@SkipWhenEmpty
|
||||
@Option(option = "input", description = "Access widener file")
|
||||
public abstract RegularFileProperty getInputFile();
|
||||
|
||||
@OutputFile
|
||||
@Option(option = "output", description = "Remapped access widener file")
|
||||
public abstract RegularFileProperty getOutputFile();
|
||||
|
||||
@Nested
|
||||
protected abstract Property<MigrateClassTweakerMappingsService.Options> getMigrationServiceOptions();
|
||||
|
||||
public MigrateClassTweakerMappingsTask() {
|
||||
getInputFile().convention(getExtension().getAccessWidenerPath());
|
||||
getOutputFile().convention(getProject().getLayout().getProjectDirectory().file("remapped.accesswidener"));
|
||||
getMigrationServiceOptions().set(MigrateClassTweakerMappingsService.createOptions(getProject(), getMappings()));
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void doTask() throws Throwable {
|
||||
try (var serviceFactory = new ScopedServiceFactory()) {
|
||||
final MigrateClassTweakerMappingsService service = serviceFactory.get(getMigrationServiceOptions().get());
|
||||
final Path inputFile = getInputFile().get().getAsFile().toPath();
|
||||
final byte[] inputBytes = Files.readAllBytes(inputFile);
|
||||
final int ctVersion = ClassTweakerReader.readVersion(inputBytes);
|
||||
|
||||
final ClassTweakerWriter writer = ClassTweakerWriter.create(ctVersion);
|
||||
final var remapper = new ClassTweakerRemapperVisitor(writer, service.getRemapper(), MappingsNamespace.NAMED.toString(), MappingsNamespace.NAMED.toString());
|
||||
ClassTweakerReader.create(remapper).read(inputBytes, "unused_id");
|
||||
|
||||
final boolean inPlace = getOverrideInputs().get();
|
||||
final Path targetFile = inPlace ? inputFile : getOutputFile().get().getAsFile().toPath();
|
||||
Files.write(targetFile, writer.getOutput());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2019-2024 FabricMC
|
||||
* Copyright (c) 2019-2025 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
|
||||
@@ -29,7 +29,6 @@ import java.nio.file.Path;
|
||||
|
||||
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;
|
||||
@@ -38,16 +37,12 @@ import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.tasks.UntrackedTask;
|
||||
import org.gradle.api.tasks.options.Option;
|
||||
|
||||
import net.fabricmc.loom.task.service.MigrateMappingsService;
|
||||
import net.fabricmc.loom.task.service.MigrateSourceCodeMappingsService;
|
||||
import net.fabricmc.loom.util.DeletingFileVisitor;
|
||||
import net.fabricmc.loom.util.service.ScopedServiceFactory;
|
||||
|
||||
@UntrackedTask(because = "Always rerun this task.")
|
||||
public abstract class MigrateMappingsTask extends AbstractLoomTask {
|
||||
@Input
|
||||
@Option(option = "mappings", description = "Target mappings")
|
||||
public abstract Property<String> getMappings();
|
||||
|
||||
public abstract class MigrateMappingsTask extends AbstractMigrateMappingsTask {
|
||||
@InputDirectory
|
||||
@SkipWhenEmpty
|
||||
@Option(option = "input", description = "Java source file directory")
|
||||
@@ -57,25 +52,20 @@ public abstract class MigrateMappingsTask extends AbstractLoomTask {
|
||||
@Option(option = "output", description = "Remapped source output directory")
|
||||
public abstract DirectoryProperty getOutputDir();
|
||||
|
||||
@Input
|
||||
@Option(option = "overrideInputsIHaveABackup", description = "Override input files with the remapped files")
|
||||
public abstract Property<Boolean> getOverrideInputs();
|
||||
|
||||
@Nested
|
||||
protected abstract Property<MigrateMappingsService.Options> getMigrationServiceOptions();
|
||||
protected abstract Property<MigrateSourceCodeMappingsService.Options> getMigrationServiceOptions();
|
||||
|
||||
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()));
|
||||
getOverrideInputs().convention(false);
|
||||
getMigrationServiceOptions().set(MigrateSourceCodeMappingsService.createOptions(getProject(), getMappings(), getInputDir(), getOutputDir()));
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void doTask() throws Throwable {
|
||||
try (var serviceFactory = new ScopedServiceFactory()) {
|
||||
MigrateMappingsService service = serviceFactory.get(getMigrationServiceOptions().get());
|
||||
service.migrateMapppings();
|
||||
MigrateSourceCodeMappingsService service = serviceFactory.get(getMigrationServiceOptions().get());
|
||||
service.migrateMappings();
|
||||
}
|
||||
|
||||
if (getOverrideInputs().get()) {
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2025 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.task.service;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.file.RegularFile;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.Nested;
|
||||
import org.objectweb.asm.commons.Remapper;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.TinyMappingsService;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
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;
|
||||
import net.fabricmc.mappingio.MappingReader;
|
||||
import net.fabricmc.mappingio.adapter.MappingNsRenamer;
|
||||
import net.fabricmc.mappingio.format.tiny.Tiny2FileWriter;
|
||||
import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
|
||||
public final class MigrateClassTweakerMappingsService extends Service<MigrateClassTweakerMappingsService.Options> implements Closeable {
|
||||
private static final ServiceType<Options, MigrateClassTweakerMappingsService> TYPE = new ServiceType<>(Options.class, MigrateClassTweakerMappingsService.class);
|
||||
private static final String MIGRATION_TARGET_NS = "migrationTarget";
|
||||
|
||||
public MigrateClassTweakerMappingsService(Options options, ServiceFactory serviceFactory) {
|
||||
super(options, serviceFactory);
|
||||
}
|
||||
|
||||
public interface Options extends Service.Options {
|
||||
@Nested
|
||||
Property<MigrateMappingsService.Options> getMappings();
|
||||
@Nested
|
||||
Property<TinyRemapperService.Options> getTinyRemapperOptions();
|
||||
@InputFile
|
||||
RegularFileProperty getMergedMappings();
|
||||
}
|
||||
|
||||
public static Provider<Options> createOptions(Project project, Provider<String> targetMappings) {
|
||||
ConfigurableFileCollection minecraftLibraryClasspath = project.getObjects().fileCollection();
|
||||
minecraftLibraryClasspath.from(project.getConfigurations().getByName(Constants.Configurations.MINECRAFT_COMPILE_LIBRARIES));
|
||||
minecraftLibraryClasspath.from(project.getConfigurations().getByName(Constants.Configurations.MINECRAFT_RUNTIME_LIBRARIES));
|
||||
|
||||
return TYPE.create(project, (o) -> {
|
||||
o.getMappings().set(MigrateMappingsService.createOptions(project, targetMappings));
|
||||
Provider<RegularFile> mergedMappings = o.getMappings().flatMap(m -> {
|
||||
return createMergedMappingFile(project, targetMappings, m.getSourceMappings(), m.getTargetMappings());
|
||||
});
|
||||
o.getMergedMappings().set(mergedMappings);
|
||||
|
||||
o.getTinyRemapperOptions().set(TinyRemapperService.TYPE.create(project, o2 -> {
|
||||
o2.getClasspath().from(o.getMappings().map(m -> m.getClasspath().minus(minecraftLibraryClasspath)));
|
||||
o2.getFrom().set(MappingsNamespace.NAMED.toString());
|
||||
o2.getTo().set(MIGRATION_TARGET_NS);
|
||||
o2.getMappings().add(MappingsService.TYPE.create(project, o3 -> {
|
||||
o3.getMappingsFile().set(mergedMappings);
|
||||
o3.getFrom().set(MappingsNamespace.NAMED.toString());
|
||||
o3.getTo().set(MIGRATION_TARGET_NS);
|
||||
o3.getRemapLocals().set(false);
|
||||
}));
|
||||
o2.getUselegacyMixinAP().set(false);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
public Remapper getRemapper() {
|
||||
final TinyRemapperService service = getServiceFactory().get(getOptions().getTinyRemapperOptions());
|
||||
return service.getTinyRemapperForRemapping().getEnvironment().getRemapper();
|
||||
}
|
||||
|
||||
private static Provider<RegularFile> createMergedMappingFile(Project project, Provider<String> targetMappingsId, Provider<MappingsService.Options> sourceOptions, Provider<TinyMappingsService.Options> targetOptions) {
|
||||
return sourceOptions.flatMap(sourceOpt -> targetOptions.flatMap(targetOpt -> {
|
||||
final Provider<RegularFile> fileProvider = project.getLayout()
|
||||
.getBuildDirectory()
|
||||
.file(targetMappingsId.map(id -> "migrate-class-tweaker-mappings-" + Checksum.of(id).sha256().hex(16) + ".tiny"));
|
||||
return fileProvider.map(file -> {
|
||||
final Path path = file.getAsFile().toPath();
|
||||
|
||||
if (!Files.exists(path) || LoomGradleExtension.get(project).refreshDeps()) {
|
||||
try {
|
||||
final MemoryMappingTree tree = mergeMappings(sourceOpt, targetOpt);
|
||||
Files.createDirectories(path.getParent());
|
||||
|
||||
try (var writer = new Tiny2FileWriter(Files.newBufferedWriter(path, StandardCharsets.UTF_8), false)) {
|
||||
tree.accept(writer);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return file;
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
private static MemoryMappingTree mergeMappings(MappingsService.Options sourceOptions, TinyMappingsService.Options targetOptions) throws IOException {
|
||||
final var tree = new MemoryMappingTree();
|
||||
MappingReader.read(sourceOptions.getMappingsFile().get().getAsFile().toPath(), tree);
|
||||
final var renamer = new MappingNsRenamer(tree, Map.of(MappingsNamespace.NAMED.toString(), MIGRATION_TARGET_NS));
|
||||
final Path mappingFile = targetOptions.getMappings().getSingleFile().toPath();
|
||||
|
||||
if (targetOptions.getZipEntryPath().isPresent()) {
|
||||
try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(mappingFile)) {
|
||||
MappingReader.read(fs.getPath(targetOptions.getZipEntryPath().get()), renamer);
|
||||
}
|
||||
} else {
|
||||
MappingReader.read(mappingFile, renamer);
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
Files.deleteIfExists(getOptions().getMergedMappings().get().getAsFile().toPath());
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2024 FabricMC
|
||||
* Copyright (c) 2024-2025 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
|
||||
@@ -24,32 +24,19 @@
|
||||
|
||||
package net.fabricmc.loom.task.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.cadixdev.lorenz.MappingSet;
|
||||
import org.cadixdev.mercury.Mercury;
|
||||
import org.cadixdev.mercury.mixin.MixinRemapper;
|
||||
import org.cadixdev.mercury.remapper.MercuryRemapper;
|
||||
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.file.DirectoryProperty;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputDirectory;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.Nested;
|
||||
import org.gradle.api.tasks.OutputDirectory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -58,14 +45,11 @@ 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.TinyMappingsService;
|
||||
import net.fabricmc.loom.util.DeletingFileVisitor;
|
||||
import net.fabricmc.loom.util.ExceptionUtil;
|
||||
import net.fabricmc.loom.util.service.Service;
|
||||
import net.fabricmc.loom.util.service.ServiceFactory;
|
||||
import net.fabricmc.loom.util.service.ServiceType;
|
||||
import net.fabricmc.lorenztiny.TinyMappingsJoiner;
|
||||
|
||||
public class MigrateMappingsService extends Service<MigrateMappingsService.Options> {
|
||||
public final class MigrateMappingsService extends Service<MigrateMappingsService.Options> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MigrateMappingsService.class);
|
||||
private static final ServiceType<Options, MigrateMappingsService> TYPE = new ServiceType<>(Options.class, MigrateMappingsService.class);
|
||||
|
||||
@@ -78,21 +62,14 @@ public class MigrateMappingsService extends Service<MigrateMappingsService.Optio
|
||||
Property<MappingsService.Options> getSourceMappings();
|
||||
@Nested
|
||||
Property<TinyMappingsService.Options> getTargetMappings();
|
||||
@InputDirectory
|
||||
DirectoryProperty getInputDir();
|
||||
@Input
|
||||
Property<String> getSourceCompatibility();
|
||||
@InputFiles
|
||||
ConfigurableFileCollection getClasspath();
|
||||
@OutputDirectory
|
||||
DirectoryProperty getOutputDir();
|
||||
}
|
||||
|
||||
public static Provider<Options> createOptions(Project project, Provider<String> targetMappings, DirectoryProperty inputDir, DirectoryProperty outputDir) {
|
||||
public static Provider<Options> createOptions(Project project, Provider<String> targetMappings) {
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
final Provider<String> from = project.provider(() -> "intermediary");
|
||||
final Provider<String> to = project.provider(() -> "named");
|
||||
final JavaVersion javaVersion = project.getExtensions().getByType(JavaPluginExtension.class).getSourceCompatibility();
|
||||
|
||||
ConfigurableFileCollection classpath = project.getObjects().fileCollection();
|
||||
classpath.from(project.getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME));
|
||||
@@ -104,64 +81,20 @@ public class MigrateMappingsService extends Service<MigrateMappingsService.Optio
|
||||
FileCollection targetMappingsFile = getTargetMappingsFile(project, targetMappings.get());
|
||||
o.getSourceMappings().set(MappingsService.createOptionsWithProjectMappings(project, from, to));
|
||||
o.getTargetMappings().set(TinyMappingsService.createOptions(project, targetMappingsFile, "mappings/mappings.tiny"));
|
||||
o.getSourceCompatibility().set(javaVersion.toString());
|
||||
o.getInputDir().set(inputDir);
|
||||
o.getClasspath().from(classpath);
|
||||
o.getOutputDir().set(outputDir);
|
||||
});
|
||||
}
|
||||
|
||||
public void migrateMapppings() throws IOException {
|
||||
final Path inputDir = getOptions().getInputDir().get().getAsFile().toPath();
|
||||
final Path outputDir = getOptions().getOutputDir().get().getAsFile().toPath();
|
||||
public MappingsService getSourceMappingsService() {
|
||||
return getServiceFactory().get(getOptions().getSourceMappings().get());
|
||||
}
|
||||
|
||||
if (!Files.exists(inputDir) || !Files.isDirectory(inputDir)) {
|
||||
throw new IllegalArgumentException("Could not find input directory: " + inputDir.toAbsolutePath());
|
||||
}
|
||||
public TinyMappingsService getTargetMappingsService() {
|
||||
return getServiceFactory().get(getOptions().getTargetMappings().get());
|
||||
}
|
||||
|
||||
if (Files.exists(outputDir)) {
|
||||
DeletingFileVisitor.deleteDirectory(outputDir);
|
||||
}
|
||||
|
||||
Files.createDirectories(outputDir);
|
||||
|
||||
Mercury mercury = new Mercury();
|
||||
mercury.setGracefulClasspathChecks(true);
|
||||
mercury.setSourceCompatibility(getOptions().getSourceCompatibility().get());
|
||||
|
||||
final MappingsService sourceMappingsService = getServiceFactory().get(getOptions().getSourceMappings().get());
|
||||
final TinyMappingsService targetMappingsService = getServiceFactory().get(getOptions().getTargetMappings().get());
|
||||
|
||||
final MappingSet mappingSet = new TinyMappingsJoiner(
|
||||
sourceMappingsService.getMemoryMappingTree(), MappingsNamespace.NAMED.toString(),
|
||||
targetMappingsService.getMappingTree(), MappingsNamespace.NAMED.toString(),
|
||||
MappingsNamespace.INTERMEDIARY.toString()
|
||||
).read();
|
||||
|
||||
mercury.getProcessors().add(MixinRemapper.create(mappingSet));
|
||||
mercury.getProcessors().add(MercuryRemapper.create(mappingSet));
|
||||
|
||||
for (File file : getOptions().getClasspath().getFiles()) {
|
||||
mercury.getClassPath().add(file.toPath());
|
||||
}
|
||||
|
||||
try {
|
||||
mercury.rewrite(
|
||||
inputDir,
|
||||
outputDir
|
||||
);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
DeletingFileVisitor.deleteDirectory(outputDir);
|
||||
} catch (IOException ignored) {
|
||||
// Nope
|
||||
}
|
||||
|
||||
throw ExceptionUtil.createDescriptiveWrapper(RuntimeException::new, "Failed to migrate mappings", e);
|
||||
}
|
||||
|
||||
// clean file descriptors
|
||||
System.gc();
|
||||
public FileCollection getClasspath() {
|
||||
return getOptions().getClasspath();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2024-2025 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.task.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.cadixdev.lorenz.MappingSet;
|
||||
import org.cadixdev.mercury.Mercury;
|
||||
import org.cadixdev.mercury.mixin.MixinRemapper;
|
||||
import org.cadixdev.mercury.remapper.MercuryRemapper;
|
||||
import org.gradle.api.JavaVersion;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.provider.Provider;
|
||||
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 net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.TinyMappingsService;
|
||||
import net.fabricmc.loom.util.DeletingFileVisitor;
|
||||
import net.fabricmc.loom.util.ExceptionUtil;
|
||||
import net.fabricmc.loom.util.service.Service;
|
||||
import net.fabricmc.loom.util.service.ServiceFactory;
|
||||
import net.fabricmc.loom.util.service.ServiceType;
|
||||
import net.fabricmc.lorenztiny.TinyMappingsJoiner;
|
||||
|
||||
public final class MigrateSourceCodeMappingsService extends Service<MigrateSourceCodeMappingsService.Options> {
|
||||
private static final ServiceType<Options, MigrateSourceCodeMappingsService> TYPE = new ServiceType<>(Options.class, MigrateSourceCodeMappingsService.class);
|
||||
|
||||
public MigrateSourceCodeMappingsService(Options options, ServiceFactory serviceFactory) {
|
||||
super(options, serviceFactory);
|
||||
}
|
||||
|
||||
public interface Options extends Service.Options {
|
||||
@Nested
|
||||
Property<MigrateMappingsService.Options> getMappings();
|
||||
@InputDirectory
|
||||
DirectoryProperty getInputDir();
|
||||
@Input
|
||||
Property<String> getSourceCompatibility();
|
||||
@OutputDirectory
|
||||
DirectoryProperty getOutputDir();
|
||||
}
|
||||
|
||||
public static Provider<Options> createOptions(Project project, Provider<String> targetMappings, DirectoryProperty inputDir, DirectoryProperty outputDir) {
|
||||
final JavaVersion javaVersion = project.getExtensions().getByType(JavaPluginExtension.class).getSourceCompatibility();
|
||||
|
||||
return TYPE.create(project, (o) -> {
|
||||
o.getMappings().set(MigrateMappingsService.createOptions(project, targetMappings));
|
||||
o.getSourceCompatibility().set(javaVersion.toString());
|
||||
o.getInputDir().set(inputDir);
|
||||
o.getOutputDir().set(outputDir);
|
||||
});
|
||||
}
|
||||
|
||||
public void migrateMappings() throws IOException {
|
||||
final Path inputDir = getOptions().getInputDir().get().getAsFile().toPath();
|
||||
final Path outputDir = getOptions().getOutputDir().get().getAsFile().toPath();
|
||||
|
||||
if (!Files.exists(inputDir) || !Files.isDirectory(inputDir)) {
|
||||
throw new IllegalArgumentException("Could not find input directory: " + inputDir.toAbsolutePath());
|
||||
}
|
||||
|
||||
if (Files.exists(outputDir)) {
|
||||
DeletingFileVisitor.deleteDirectory(outputDir);
|
||||
}
|
||||
|
||||
Files.createDirectories(outputDir);
|
||||
|
||||
Mercury mercury = new Mercury();
|
||||
mercury.setGracefulClasspathChecks(true);
|
||||
mercury.setSourceCompatibility(getOptions().getSourceCompatibility().get());
|
||||
|
||||
final MigrateMappingsService migrateMappingsService = getServiceFactory().get(getOptions().getMappings());
|
||||
final MappingsService sourceMappingsService = migrateMappingsService.getSourceMappingsService();
|
||||
final TinyMappingsService targetMappingsService = migrateMappingsService.getTargetMappingsService();
|
||||
|
||||
final MappingSet mappingSet = new TinyMappingsJoiner(
|
||||
sourceMappingsService.getMemoryMappingTree(), MappingsNamespace.NAMED.toString(),
|
||||
targetMappingsService.getMappingTree(), MappingsNamespace.NAMED.toString(),
|
||||
MappingsNamespace.INTERMEDIARY.toString()
|
||||
).read();
|
||||
|
||||
mercury.getProcessors().add(MixinRemapper.create(mappingSet));
|
||||
mercury.getProcessors().add(MercuryRemapper.create(mappingSet));
|
||||
|
||||
for (File file : migrateMappingsService.getClasspath().getFiles()) {
|
||||
mercury.getClassPath().add(file.toPath());
|
||||
}
|
||||
|
||||
try {
|
||||
mercury.rewrite(
|
||||
inputDir,
|
||||
outputDir
|
||||
);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
DeletingFileVisitor.deleteDirectory(outputDir);
|
||||
} catch (IOException ignored) {
|
||||
// Nope
|
||||
}
|
||||
|
||||
throw ExceptionUtil.createDescriptiveWrapper(RuntimeException::new, "Failed to migrate mappings", e);
|
||||
}
|
||||
|
||||
// clean file descriptors
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
@@ -244,4 +244,45 @@ class MigrateMappingsTest extends Specification implements GradleProjectTestTrai
|
||||
where:
|
||||
version << STANDARD_TEST_VERSIONS
|
||||
}
|
||||
|
||||
def "Migrate AW (in place: #inPlace, header: #header)"() {
|
||||
setup:
|
||||
def gradle = gradleProject(project: "minimalBase")
|
||||
gradle.buildGradle << """
|
||||
loom.accessWidenerPath = file('src/main/resources/test.accesswidener')
|
||||
dependencies {
|
||||
minecraft 'com.mojang:minecraft:24w36a'
|
||||
mappings 'net.fabricmc:yarn:24w36a+build.6:v2'
|
||||
}
|
||||
""".stripIndent()
|
||||
def awFile = new File(gradle.projectDir, 'src/main/resources/test.accesswidener')
|
||||
awFile.parentFile.mkdirs()
|
||||
awFile.text = header + '\n' + 'extendable method net/minecraft/block/PaneBlock connectsTo (Lnet/minecraft/block/BlockState;Z)Z'
|
||||
|
||||
when:
|
||||
def tasks = [
|
||||
"migrateClassTweakerMappings",
|
||||
"--mappings",
|
||||
"net.minecraft:mappings:24w36a"
|
||||
]
|
||||
|
||||
if (inPlace) {
|
||||
tasks.add("--overrideInputsIHaveABackup")
|
||||
}
|
||||
|
||||
def result = gradle.run(tasks: tasks)
|
||||
def remapped = (inPlace ? awFile : new File(gradle.projectDir, 'remapped.accesswidener')).text
|
||||
|
||||
then:
|
||||
result.task(":migrateClassTweakerMappings").outcome == SUCCESS
|
||||
remapped == header + '\n' + 'extendable\tmethod\tnet/minecraft/world/level/block/IronBarsBlock\tattachsTo\t(Lnet/minecraft/world/level/block/state/BlockState;Z)Z\n'
|
||||
|
||||
where:
|
||||
// Check that the header is kept intact and that the in place remapping works
|
||||
header | inPlace
|
||||
'accessWidener\tv1\tnamed' | false
|
||||
'accessWidener\tv1\tnamed' | true // the code is the same so we only need one case for in place remapping
|
||||
'accessWidener\tv2\tnamed' | false
|
||||
'classTweaker\tv1\tnamed' | false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user