From 014a6fce2b788fca59dc43c7757dcd81dadebcbd Mon Sep 17 00:00:00 2001 From: modmuss Date: Fri, 15 Dec 2023 09:47:26 +0000 Subject: [PATCH] Remapper extensions (#984) * Remapper extension API * Fix build * More work * Fixes, thanks Gradle. * Build fix * Cleanup --- .../fabricmc/loom/LoomGradleExtension.java | 3 + .../loom/api/LoomGradleExtensionAPI.java | 4 + .../loom/api/remapping/RemapperContext.java | 47 +++++++ .../loom/api/remapping/RemapperExtension.java | 53 ++++++++ .../api/remapping/RemapperParameters.java | 42 ++++++ .../api/remapping/TinyRemapperExtension.java | 68 ++++++++++ .../loom/configuration/mods/ModProcessor.java | 5 + .../extension/LoomGradleExtensionApiImpl.java | 24 ++++ .../extension/LoomGradleExtensionImpl.java | 5 + .../extension/RemapperExtensionHolder.java | 128 ++++++++++++++++++ .../task/service/TinyRemapperService.java | 10 +- .../test/integration/SimpleProjectTest.groovy | 2 + .../StringReplacementClassVisitor.groovy | 61 +++++++++ .../buildSrc/remapext/TestPlugin.groovy | 43 ++++++ .../remapext/TestRemapperExtension.groovy | 54 ++++++++ .../remapext/TestTinyRemapperExtension.groovy | 44 ++++++ .../java/net/fabricmc/example/ExampleMod.java | 6 +- 17 files changed, 595 insertions(+), 4 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/api/remapping/RemapperContext.java create mode 100644 src/main/java/net/fabricmc/loom/api/remapping/RemapperExtension.java create mode 100644 src/main/java/net/fabricmc/loom/api/remapping/RemapperParameters.java create mode 100644 src/main/java/net/fabricmc/loom/api/remapping/TinyRemapperExtension.java create mode 100644 src/main/java/net/fabricmc/loom/extension/RemapperExtensionHolder.java create mode 100644 src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/StringReplacementClassVisitor.groovy create mode 100644 src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/TestPlugin.groovy create mode 100644 src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/TestRemapperExtension.groovy create mode 100644 src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/TestTinyRemapperExtension.groovy diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index b985bd82d..836138bac 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -45,6 +45,7 @@ import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider; import net.fabricmc.loom.extension.LoomFiles; import net.fabricmc.loom.extension.MixinExtension; +import net.fabricmc.loom.extension.RemapperExtensionHolder; import net.fabricmc.loom.util.download.DownloadBuilder; @ApiStatus.Internal @@ -115,4 +116,6 @@ default List getMinecraftJars(MappingsNamespace mappingsNamespace) { boolean multiProjectOptimisation(); ListProperty getLibraryProcessors(); + + ListProperty getRemapperExtensions(); } diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index 78584e110..4fee840a5 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -44,6 +44,8 @@ import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; import net.fabricmc.loom.api.mappings.layered.spec.LayeredMappingSpecBuilder; import net.fabricmc.loom.api.processor.MinecraftJarProcessor; +import net.fabricmc.loom.api.remapping.RemapperExtension; +import net.fabricmc.loom.api.remapping.RemapperParameters; import net.fabricmc.loom.configuration.ide.RunConfigSettings; import net.fabricmc.loom.configuration.processors.JarProcessor; import net.fabricmc.loom.configuration.providers.mappings.NoOpIntermediateMappingsProvider; @@ -221,4 +223,6 @@ default void splitMinecraftJar() { Property getRuntimeOnlyLog4j(); Property getSplitModDependencies(); + + void addRemapperExtension(Class> remapperExtensionClass, Class parametersClass, Action parameterAction); } diff --git a/src/main/java/net/fabricmc/loom/api/remapping/RemapperContext.java b/src/main/java/net/fabricmc/loom/api/remapping/RemapperContext.java new file mode 100644 index 000000000..6f050263e --- /dev/null +++ b/src/main/java/net/fabricmc/loom/api/remapping/RemapperContext.java @@ -0,0 +1,47 @@ +/* + * 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.api.remapping; + +import org.objectweb.asm.commons.Remapper; + +/** + * Context for a {@link RemapperExtension}. + */ +public interface RemapperContext { + /** + * @return The {@link Remapper} instance + */ + Remapper remapper(); + + /** + * @return the source namespace + */ + String sourceNamespace(); + + /** + * @return the target namespace + */ + String targetNamespace(); +} diff --git a/src/main/java/net/fabricmc/loom/api/remapping/RemapperExtension.java b/src/main/java/net/fabricmc/loom/api/remapping/RemapperExtension.java new file mode 100644 index 000000000..43a006e09 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/api/remapping/RemapperExtension.java @@ -0,0 +1,53 @@ +/* + * 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.api.remapping; + +import javax.inject.Inject; + +import org.gradle.api.Action; +import org.objectweb.asm.ClassVisitor; + +/** + * A remapper extension can be used to add extra processing to the remapping process. + * + *

Implementations of RemapperExtension's must have the following: + * A single constructor annotated with {@link Inject}, and taking a single argument of the parameters. + * Or a single constructor annotated with {@link Inject} taking no arguments, when the extension does not have any parameters. + * + *

Use {@link net.fabricmc.loom.api.LoomGradleExtensionAPI#addRemapperExtension(Class, Class, Action)} to register a remapper extension. + * + * @param Parameter type for the extension. Should be {@link RemapperParameters.None} if the action does not have parameters. + */ +public interface RemapperExtension { + /** + * Return a {@link ClassVisitor} that will be used when remapping the given class. + * + * @param className The name of the class being remapped + * @param remapperContext The remapper context + * @param classVisitor The parent class visitor + * @return A {@link ClassVisitor} that will be used when remapping the given class, or the given {@code classVisitor} if no extra processing is required for this class. + */ + ClassVisitor insertVisitor(String className, RemapperContext remapperContext, ClassVisitor classVisitor); +} diff --git a/src/main/java/net/fabricmc/loom/api/remapping/RemapperParameters.java b/src/main/java/net/fabricmc/loom/api/remapping/RemapperParameters.java new file mode 100644 index 000000000..32f806a7f --- /dev/null +++ b/src/main/java/net/fabricmc/loom/api/remapping/RemapperParameters.java @@ -0,0 +1,42 @@ +/* + * 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.api.remapping; + +import org.jetbrains.annotations.ApiStatus; + +/** + * Marker interface for parameter objects to {@link RemapperExtension}s. + * + *

Design based off of Gradle's {@link org.gradle.workers.WorkParameters}. + */ +public interface RemapperParameters { + final class None implements RemapperParameters { + @ApiStatus.Internal + public static None INSTANCE = new None(); + + private None() { + } + } +} diff --git a/src/main/java/net/fabricmc/loom/api/remapping/TinyRemapperExtension.java b/src/main/java/net/fabricmc/loom/api/remapping/TinyRemapperExtension.java new file mode 100644 index 000000000..417b77f6f --- /dev/null +++ b/src/main/java/net/fabricmc/loom/api/remapping/TinyRemapperExtension.java @@ -0,0 +1,68 @@ +/* + * 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.api.remapping; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.tinyremapper.TinyRemapper; + +/** + * A remapper extension, that has direct access to the TinyRemapper APIs. + * + *

This API is not stable and may change without notice. + */ +@ApiStatus.Experimental +public interface TinyRemapperExtension { + /** + * See: {@link TinyRemapper.Builder#extraAnalyzeVisitor(TinyRemapper.AnalyzeVisitorProvider)}. + * + * @return A {@link TinyRemapper.AnalyzeVisitorProvider} or {@code null}. + */ + @Nullable + default TinyRemapper.AnalyzeVisitorProvider getAnalyzeVisitorProvider() { + return null; + } + + /** + * See: {@link TinyRemapper.Builder#extraPreApplyVisitor(TinyRemapper.ApplyVisitorProvider)}. + * + * @return A {@link TinyRemapper.ApplyVisitorProvider} or {@code null}. + */ + @Nullable + default TinyRemapper.ApplyVisitorProvider getPreApplyVisitor() { + return null; + } + + /** + * See: {@link TinyRemapper.Builder#extraPostApplyVisitor(TinyRemapper.ApplyVisitorProvider)}. + * + * @return A {@link TinyRemapper.ApplyVisitorProvider} or {@code null}. + */ + @Nullable + default TinyRemapper.ApplyVisitorProvider getPostApplyVisitor() { + return null; + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java index a4cf4503e..b961df489 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java @@ -51,6 +51,7 @@ 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.extension.RemapperExtensionHolder; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.Pair; import net.fabricmc.loom.util.TinyRemapperHelper; @@ -157,6 +158,10 @@ private void remapJars(List remapList) throws IOException { builder.extension(new MixinExtension(remapMixins::contains)); } + for (RemapperExtensionHolder holder : extension.getRemapperExtensions().get()) { + holder.apply(builder, fromM, toM, project.getObjects()); + } + final TinyRemapper remapper = builder.build(); for (Path minecraftJar : extension.getMinecraftJars(MappingsNamespace.INTERMEDIARY)) { diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index f8d492ed1..0bf6e98e5 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -34,6 +34,7 @@ import org.gradle.api.artifacts.Dependency; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import org.gradle.api.provider.SetProperty; @@ -50,6 +51,8 @@ import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; import net.fabricmc.loom.api.mappings.layered.spec.LayeredMappingSpecBuilder; import net.fabricmc.loom.api.processor.MinecraftJarProcessor; +import net.fabricmc.loom.api.remapping.RemapperExtension; +import net.fabricmc.loom.api.remapping.RemapperParameters; import net.fabricmc.loom.configuration.RemapConfigurations; import net.fabricmc.loom.configuration.ide.RunConfigSettings; import net.fabricmc.loom.configuration.processors.JarProcessor; @@ -91,6 +94,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA private final NamedDomainObjectContainer mods; private final NamedDomainObjectList remapConfigurations; private final ListProperty> minecraftJarProcessors; + protected final ListProperty remapperExtensions; // A common mistake with layered mappings is to call the wrong `officialMojangMappings` method, use this to keep track of when we are building a layered mapping spec. protected final ThreadLocal layeredSpecBuilderScope = ThreadLocal.withInitial(() -> false); @@ -150,6 +154,9 @@ protected LoomGradleExtensionApiImpl(Project project, LoomFiles directories) { this.splitEnvironmentalSourceSet = project.getObjects().property(Boolean.class).convention(false); this.splitEnvironmentalSourceSet.finalizeValueOnRead(); + remapperExtensions = project.getObjects().listProperty(RemapperExtensionHolder.class); + remapperExtensions.finalizeValueOnRead(); + // Enable dep iface injection by default interfaceInjection(interfaceInjection -> { interfaceInjection.getEnableDependencyInterfaceInjection().convention(true).finalizeValueOnRead(); @@ -382,6 +389,23 @@ public void createRemapConfigurations(SourceSet sourceSet) { RemapConfigurations.setupForSourceSet(getProject(), sourceSet); } + @Override + public void addRemapperExtension(Class> remapperExtensionClass, Class parametersClass, Action parameterAction) { + final ObjectFactory objectFactory = getProject().getObjects(); + final RemapperExtensionHolder holder; + + if (parametersClass != RemapperParameters.None.class) { + T parameters = objectFactory.newInstance(parametersClass); + parameterAction.execute(parameters); + holder = objectFactory.newInstance(RemapperExtensionHolder.class, parameters); + } else { + holder = objectFactory.newInstance(RemapperExtensionHolder.class, RemapperParameters.None.INSTANCE); + } + + holder.getRemapperExtensionClassName().set(remapperExtensionClass.getName()); + remapperExtensions.add(holder); + } + // This is here to ensure that LoomGradleExtensionApiImpl compiles without any unimplemented methods private final class EnsureCompile extends LoomGradleExtensionApiImpl { private EnsureCompile() { diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index 81c5c793a..7fe809fce 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -247,6 +247,11 @@ public ListProperty getLibraryP return libraryProcessorFactories; } + @Override + public ListProperty getRemapperExtensions() { + return remapperExtensions; + } + @Override protected void configureIntermediateMappingsProviderInternal(T provider) { provider.getMinecraftVersion().set(getProject().provider(() -> getMinecraftProvider().minecraftVersion())); diff --git a/src/main/java/net/fabricmc/loom/extension/RemapperExtensionHolder.java b/src/main/java/net/fabricmc/loom/extension/RemapperExtensionHolder.java new file mode 100644 index 000000000..454e75ec9 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/extension/RemapperExtensionHolder.java @@ -0,0 +1,128 @@ +/* + * 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.extension; + +import javax.inject.Inject; + +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Nested; +import org.gradle.api.tasks.Optional; +import org.jetbrains.annotations.Nullable; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.commons.Remapper; + +import net.fabricmc.loom.api.remapping.RemapperContext; +import net.fabricmc.loom.api.remapping.RemapperExtension; +import net.fabricmc.loom.api.remapping.RemapperParameters; +import net.fabricmc.loom.api.remapping.TinyRemapperExtension; +import net.fabricmc.tinyremapper.TinyRemapper; +import net.fabricmc.tinyremapper.api.TrClass; + +public abstract class RemapperExtensionHolder { + // Null when RemapperParameters.None.class + private final RemapperParameters remapperParameters; + + @Inject + public RemapperExtensionHolder(RemapperParameters remapperParameters) { + this.remapperParameters = remapperParameters; + } + + @Input + public abstract Property getRemapperExtensionClassName(); + + @Nested + @Optional + public RemapperParameters getRemapperParameters() { + return remapperParameters; + } + + public void apply(TinyRemapper.Builder tinyRemapperBuilder, String sourceNamespace, String targetNamespace, ObjectFactory objectFactory) { + final RemapperExtension remapperExtension = newInstance(objectFactory); + + tinyRemapperBuilder.extraPostApplyVisitor(new RemapperExtensionImpl(remapperExtension, sourceNamespace, targetNamespace)); + + if (remapperExtension instanceof TinyRemapperExtension tinyRemapperExtension) { + final TinyRemapper.AnalyzeVisitorProvider analyzeVisitorProvider = tinyRemapperExtension.getAnalyzeVisitorProvider(); + final TinyRemapper.ApplyVisitorProvider preApplyVisitorProvider = tinyRemapperExtension.getPreApplyVisitor(); + final TinyRemapper.ApplyVisitorProvider postApplyVisitorProvider = tinyRemapperExtension.getPostApplyVisitor(); + + if (analyzeVisitorProvider != null) { + tinyRemapperBuilder.extraAnalyzeVisitor(analyzeVisitorProvider); + } + + if (preApplyVisitorProvider != null) { + tinyRemapperBuilder.extraPreApplyVisitor(preApplyVisitorProvider); + } + + if (postApplyVisitorProvider != null) { + tinyRemapperBuilder.extraPostApplyVisitor(postApplyVisitorProvider); + } + } + } + + private RemapperExtension newInstance(ObjectFactory objectFactory) { + try { + Class remapperExtensionClass = Class.forName(getRemapperExtensionClassName().get()) + .asSubclass(RemapperExtension.class); + + if (remapperParameters == RemapperParameters.None.INSTANCE) { + return objectFactory.newInstance(remapperExtensionClass); + } + + return objectFactory.newInstance(remapperExtensionClass, remapperParameters); + } catch (Exception e) { + throw new RuntimeException("Failed to create remapper extension", e); + } + } + + private static final class RemapperExtensionImpl implements TinyRemapper.ApplyVisitorProvider { + private final RemapperExtension remapperExtension; + private final String sourceNamespace; + private final String targetNamespace; + + @Nullable + private RemapperContext context; + + private RemapperExtensionImpl(RemapperExtension remapperExtension, String sourceNamespace, String targetNamespace) { + this.remapperExtension = remapperExtension; + this.sourceNamespace = sourceNamespace; + this.targetNamespace = targetNamespace; + } + + @Override + public ClassVisitor insertApplyVisitor(TrClass cls, ClassVisitor next) { + if (context == null) { + context = new RemapperContextImpl(cls.getEnvironment().getRemapper(), sourceNamespace, targetNamespace); + } + + return remapperExtension.insertVisitor(cls.getName(), context, next); + } + } + + private record RemapperContextImpl(Remapper remapper, String sourceNamespace, String targetNamespace) implements RemapperContext { + } +} diff --git a/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java b/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java index 3e1eee877..d576e07ba 100644 --- a/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java +++ b/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java @@ -39,11 +39,13 @@ import org.gradle.api.Project; import org.gradle.api.invocation.Gradle; +import org.gradle.api.model.ObjectFactory; import org.gradle.api.tasks.SourceSet; import org.jetbrains.annotations.Nullable; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.build.mixin.AnnotationProcessorInvoker; +import net.fabricmc.loom.extension.RemapperExtensionHolder; import net.fabricmc.loom.task.AbstractRemapJarTask; import net.fabricmc.loom.util.gradle.GradleUtils; import net.fabricmc.loom.util.gradle.SourceSetHelper; @@ -90,7 +92,7 @@ public static synchronized TinyRemapperService getOrCreate(SharedServiceManager mappings.add(gradleMixinMappingProvider(serviceManager, project.getGradle(), extension.getMappingConfiguration().mappingsIdentifier, from, to)); } - return new TinyRemapperService(mappings, !legacyMixin, kotlinClasspathService, extension.getKnownIndyBsms().get()); + return new TinyRemapperService(mappings, !legacyMixin, kotlinClasspathService, extension.getKnownIndyBsms().get(), extension.getRemapperExtensions().get(), from, to, project.getObjects()); }); service.readClasspath(remapJarTask.getClasspath().getFiles().stream().map(File::toPath).filter(Files::exists).toList()); @@ -129,7 +131,7 @@ private static IMappingProvider gradleMixinMappingProvider(SharedServiceManager // Set to true once remapping has started, once set no inputs can be read. private boolean isRemapping = false; - public TinyRemapperService(List mappings, boolean useMixinExtension, @Nullable KotlinClasspath kotlinClasspath, Set knownIndyBsms) { + private TinyRemapperService(List mappings, boolean useMixinExtension, @Nullable KotlinClasspath kotlinClasspath, Set knownIndyBsms, List remapperExtensions, String sourceNamespace, String targetNamespace, ObjectFactory objectFactory) { TinyRemapper.Builder builder = TinyRemapper.newRemapper().withKnownIndyBsm(knownIndyBsms); for (IMappingProvider provider : mappings) { @@ -145,6 +147,10 @@ public TinyRemapperService(List mappings, boolean useMixinExte builder.extension(kotlinRemapperClassloader.getTinyRemapperExtension()); } + for (RemapperExtensionHolder holder : remapperExtensions) { + holder.apply(builder, sourceNamespace, targetNamespace, objectFactory); + } + tinyRemapper = builder.build(); } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/SimpleProjectTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/SimpleProjectTest.groovy index 3fc12eddf..ff7bda775 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/SimpleProjectTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/SimpleProjectTest.groovy @@ -42,6 +42,7 @@ class SimpleProjectTest extends Specification implements GradleProjectTestTrait def "build and run (gradle #version)"() { setup: def gradle = gradleProject(project: "simple", version: version) + gradle.buildSrc("remapext") // apply the remap extension plugin def server = ServerRunner.create(gradle.projectDir, "1.16.5") .withMod(gradle.getOutputFile("fabric-example-mod-1.0.0.jar")) @@ -60,6 +61,7 @@ class SimpleProjectTest extends Specification implements GradleProjectTestTrait serverResult.successful() serverResult.output.contains("Hello simple Fabric mod") // A check to ensure our mod init was actually called + serverResult.output.contains("Hello Loom!") // Check that the remapper extension worked where: version << STANDARD_TEST_VERSIONS } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/StringReplacementClassVisitor.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/StringReplacementClassVisitor.groovy new file mode 100644 index 000000000..b84501e9f --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/StringReplacementClassVisitor.groovy @@ -0,0 +1,61 @@ +/* + * 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.integration.buildSrc.remapext + +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.MethodVisitor + +class StringReplacementClassVisitor extends ClassVisitor { + final Map replacements + + StringReplacementClassVisitor(int api, ClassVisitor classVisitor, Map replacements) { + super(api, classVisitor) + this.replacements = replacements + } + + @Override + MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + def methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions) + return new StringReplacementMethodVisitor(api, methodVisitor) + } + + class StringReplacementMethodVisitor extends MethodVisitor { + StringReplacementMethodVisitor(int api, MethodVisitor methodVisitor) { + super(api, methodVisitor) + } + + @Override + void visitLdcInsn(Object value) { + if (value instanceof String) { + String replacement = replacements.get(value) + if (replacement != null) { + value = replacement + } + } + + super.visitLdcInsn(value) + } + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/TestPlugin.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/TestPlugin.groovy new file mode 100644 index 000000000..c3084c912 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/TestPlugin.groovy @@ -0,0 +1,43 @@ +/* + * 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.integration.buildSrc.remapext + +import org.gradle.api.Plugin +import org.gradle.api.Project + +import net.fabricmc.loom.LoomGradleExtension +import net.fabricmc.loom.api.remapping.RemapperParameters + +class TestPlugin implements Plugin { + @Override + void apply(Project project) { + def extension = LoomGradleExtension.get(project) + extension.addRemapperExtension(TestRemapperExtension.class, TestRemapperExtension.Params.class) { TestRemapperExtension.Params p -> + p.replacements.put("Hello World!", "Hello Loom!") + } + extension.addRemapperExtension(TestTinyRemapperExtension.class, RemapperParameters.None.class) { RemapperParameters.None p -> + } + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/TestRemapperExtension.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/TestRemapperExtension.groovy new file mode 100644 index 000000000..7f71841dc --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/TestRemapperExtension.groovy @@ -0,0 +1,54 @@ +/* + * 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.integration.buildSrc.remapext + +import javax.inject.Inject + +import org.gradle.api.provider.MapProperty +import org.objectweb.asm.ClassVisitor + +import net.fabricmc.loom.api.remapping.RemapperContext +import net.fabricmc.loom.api.remapping.RemapperExtension +import net.fabricmc.loom.api.remapping.RemapperParameters +import net.fabricmc.loom.util.Constants + +abstract class TestRemapperExtension implements RemapperExtension { + final Params parameters + + @Inject + TestRemapperExtension(Params parameters) { + this.parameters = parameters + } + + @Override + ClassVisitor insertVisitor(String className, RemapperContext remapperContext, ClassVisitor classVisitor) { + def replacements = parameters.replacements.get() + return new StringReplacementClassVisitor(Constants.ASM_VERSION, classVisitor, replacements) + } + + interface Params extends RemapperParameters { + MapProperty getReplacements() + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/TestTinyRemapperExtension.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/TestTinyRemapperExtension.groovy new file mode 100644 index 000000000..412c4d1ae --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/buildSrc/remapext/TestTinyRemapperExtension.groovy @@ -0,0 +1,44 @@ +/* + * 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.integration.buildSrc.remapext + +import org.objectweb.asm.ClassVisitor + +import net.fabricmc.loom.api.remapping.RemapperContext +import net.fabricmc.loom.api.remapping.RemapperExtension +import net.fabricmc.loom.api.remapping.RemapperParameters +import net.fabricmc.loom.api.remapping.TinyRemapperExtension +import net.fabricmc.tinyremapper.TinyRemapper + +class TestTinyRemapperExtension implements RemapperExtension, TinyRemapperExtension { + @Override + ClassVisitor insertVisitor(String className, RemapperContext remapperContext, ClassVisitor classVisitor) { + return classVisitor + } + + TinyRemapper.AnalyzeVisitorProvider analyzeVisitorProvider = null + TinyRemapper.ApplyVisitorProvider preApplyVisitor = null + TinyRemapper.ApplyVisitorProvider PostApplyVisitor = null +} diff --git a/src/test/resources/projects/simple/src/main/java/net/fabricmc/example/ExampleMod.java b/src/test/resources/projects/simple/src/main/java/net/fabricmc/example/ExampleMod.java index 5330d1193..ab456d385 100644 --- a/src/test/resources/projects/simple/src/main/java/net/fabricmc/example/ExampleMod.java +++ b/src/test/resources/projects/simple/src/main/java/net/fabricmc/example/ExampleMod.java @@ -1,9 +1,10 @@ package net.fabricmc.example; -import net.fabricmc.api.ModInitializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import net.fabricmc.api.ModInitializer; + public class ExampleMod implements ModInitializer { public static final Logger LOGGER = LogManager.getLogger("modid"); @@ -13,5 +14,6 @@ public class ExampleMod implements ModInitializer { @Override public void onInitialize() { LOGGER.info("Hello simple Fabric mod!"); + LOGGER.info("Hello World!"); } -} \ No newline at end of file +}