mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-28 04:07:01 -05:00
Enable the usage of JSR annotations (#1420)
* Add option to disable remapping JSR annotations * Move JSR annotation remapping to a JAR processor * Organize imports * Remap JetBrains annotations back to JSR when configured * Fix indentation * Rename useJsrAnnotations Rename it to remapJsrAnnotationsToJetBrains to make clear what Loom does * Update JSR annotation remapper exception message * Add integration test * Document remapJsrAnnotationsToJetBrains * Fix javadoc format * Checkstyle fix --------- Co-authored-by: modmuss50 <modmuss50@gmail.com>
This commit is contained in:
@@ -266,6 +266,19 @@ public interface LoomGradleExtensionAPI {
|
||||
|
||||
boolean areEnvironmentSourceSetsSplit();
|
||||
|
||||
/**
|
||||
* When enabled, Loom remaps JSR {@code Nullable}, {@code Nonnull}, and {@code Immutable} annotations to their JetBrains counterparts in the Minecraft JAR.
|
||||
*
|
||||
* <p>When disabled, Loom keeps JSR annotations as-is, and remaps any JetBrains {@code Nullable}, {@code NotNull}, and {@code Unmodifiable} annotations to their JSR counterparts in the Minecraft JAR.
|
||||
*
|
||||
* <p>This has no effect on Minecraft versions that solely use JSpecify annotations.
|
||||
*
|
||||
* <p>Default: true
|
||||
*
|
||||
* @return the property controlling the remapping of JSR annotations
|
||||
*/
|
||||
Property<Boolean> getRemapJsrAnnotationsToJetBrains();
|
||||
|
||||
Property<Boolean> getRuntimeOnlyLog4j();
|
||||
|
||||
Property<Boolean> getSplitModDependencies();
|
||||
|
||||
@@ -61,6 +61,7 @@ import net.fabricmc.loom.build.mixin.KaptApInvoker;
|
||||
import net.fabricmc.loom.build.mixin.ScalaApInvoker;
|
||||
import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor;
|
||||
import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor;
|
||||
import net.fabricmc.loom.configuration.processors.JsrAnnotationRemapperProcessor;
|
||||
import net.fabricmc.loom.configuration.processors.MinecraftJarProcessorManager;
|
||||
import net.fabricmc.loom.configuration.processors.ModJavadocProcessor;
|
||||
import net.fabricmc.loom.configuration.processors.speccontext.DebofConfiguration;
|
||||
@@ -241,6 +242,10 @@ public abstract class CompileConfiguration implements Runnable {
|
||||
if (interfaceInjection.isEnabled()) {
|
||||
extension.addMinecraftJarProcessor(InterfaceInjectionProcessor.class, "fabric-loom:interface-inject", interfaceInjection.getEnableDependencyInterfaceInjection().get());
|
||||
}
|
||||
|
||||
if (!extension.getRemapJsrAnnotationsToJetBrains().get()) {
|
||||
extension.addMinecraftJarProcessor(JsrAnnotationRemapperProcessor.class, "fabric-loom:jsr-annotations");
|
||||
}
|
||||
}
|
||||
|
||||
private void setupMixinAp(MixinExtension mixin) {
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.configuration.processors;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.api.processor.MinecraftJarProcessor;
|
||||
import net.fabricmc.loom.api.processor.ProcessorContext;
|
||||
import net.fabricmc.loom.api.processor.SpecContext;
|
||||
import net.fabricmc.loom.util.TinyRemapperLoggerAdapter;
|
||||
import net.fabricmc.tinyremapper.IMappingProvider;
|
||||
import net.fabricmc.tinyremapper.OutputConsumerPath;
|
||||
import net.fabricmc.tinyremapper.TinyRemapper;
|
||||
|
||||
public class JsrAnnotationRemapperProcessor implements MinecraftJarProcessor<JsrAnnotationRemapperProcessor.Spec> {
|
||||
private static final Map<String, String> JETBRAINS_TO_JSR = Map.of(
|
||||
"org/jetbrains/annotations/Nullable", "javax/annotation/Nullable",
|
||||
"org/jetbrains/annotations/NotNull", "javax/annotation/Nonnull",
|
||||
"org/jetbrains/annotations/Unmodifiable", "javax/annotation/concurrent/Immutable"
|
||||
);
|
||||
|
||||
private final String name;
|
||||
|
||||
@Inject
|
||||
public JsrAnnotationRemapperProcessor(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Spec buildSpec(SpecContext context) {
|
||||
return new Spec(JETBRAINS_TO_JSR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processJar(Path jar, Spec spec, ProcessorContext context) throws IOException {
|
||||
TinyRemapper tinyRemapper = TinyRemapper.newRemapper(TinyRemapperLoggerAdapter.INSTANCE)
|
||||
.withMappings(spec.getMappings())
|
||||
.build();
|
||||
|
||||
try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(jar).build()) {
|
||||
tinyRemapper.readInputs(jar);
|
||||
tinyRemapper.apply(outputConsumer);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to remap JAR " + jar + " with mapping " + spec.annotationMapping(), e);
|
||||
} finally {
|
||||
tinyRemapper.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public record Spec(Map<String, String> annotationMapping) implements MinecraftJarProcessor.Spec {
|
||||
public IMappingProvider getMappings() {
|
||||
return out -> annotationMapping.forEach(out::acceptClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,6 +96,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
protected final Property<String> intermediary;
|
||||
protected final Property<IntermediateMappingsProvider> intermediateMappingsProvider;
|
||||
private final Property<String> productionNamespace;
|
||||
private final Property<Boolean> remapJsrAnnotationsToJetBrains;
|
||||
private final Property<Boolean> runtimeOnlyLog4j;
|
||||
private final Property<Boolean> splitModDependencies;
|
||||
private final Property<MinecraftJarConfiguration<?, ?, ?>> minecraftJarConfiguration;
|
||||
@@ -180,6 +181,9 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
this.accessWidener.finalizeValueOnRead();
|
||||
this.getGameJarProcessors().finalizeValueOnRead();
|
||||
|
||||
this.remapJsrAnnotationsToJetBrains = project.getObjects().property(Boolean.class).convention(true);
|
||||
this.remapJsrAnnotationsToJetBrains.finalizeValueOnRead();
|
||||
|
||||
this.runtimeOnlyLog4j = project.getObjects().property(Boolean.class).convention(false);
|
||||
this.runtimeOnlyLog4j.finalizeValueOnRead();
|
||||
|
||||
@@ -406,6 +410,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
return minecraftJarConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property<Boolean> getRemapJsrAnnotationsToJetBrains() {
|
||||
return remapJsrAnnotationsToJetBrains;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property<Boolean> getRuntimeOnlyLog4j() {
|
||||
return runtimeOnlyLog4j;
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.test.integration
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
import net.fabricmc.loom.test.util.GradleProjectTestTrait
|
||||
import net.fabricmc.loom.util.ZipUtils
|
||||
|
||||
import static net.fabricmc.loom.test.LoomTestConstants.PRE_RELEASE_GRADLE
|
||||
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
|
||||
|
||||
class JsrAnnotationsTest extends Specification implements GradleProjectTestTrait {
|
||||
static final String MAPPINGS = "21w13a-net.fabricmc.yarn.21w13a.21w13a+build.30-v2"
|
||||
static final boolean REMAP_JSR_DEFAULT = true
|
||||
|
||||
def "Remap JSR annotations to JetBrains #remapJsrAnnotations"() {
|
||||
setup:
|
||||
def gradle = gradleProject(project: "unpick", version: PRE_RELEASE_GRADLE)
|
||||
gradle.buildGradle << """
|
||||
loom {
|
||||
remapJsrAnnotationsToJetBrains = ${remapJsrAnnotations}
|
||||
}
|
||||
""".stripIndent()
|
||||
when:
|
||||
def result = gradle.run(tasks: [
|
||||
"genSourcesWithVineflower",
|
||||
"--info"
|
||||
])
|
||||
def source = getClassSource(gradle, "net/minecraft/util/annotation/MethodsReturnNonnullByDefault.java", remapJsrAnnotations != REMAP_JSR_DEFAULT)
|
||||
|
||||
then:
|
||||
result.task(":genSourcesWithVineflower").outcome == SUCCESS
|
||||
source.contains(remapJsrAnnotations ? "@NotNull" : "@Nonnull")
|
||||
source.contains(remapJsrAnnotations ? "import org.jetbrains.annotations.NotNull;" : "import javax.annotation.Nonnull;")
|
||||
!source.contains(remapJsrAnnotations ? "@Nonnull" : "@NotNull")
|
||||
!source.contains(remapJsrAnnotations ? "import javax.annotation.Nonnull;" : "import org.jetbrains.annotations.NotNull;")
|
||||
|
||||
where:
|
||||
[remapJsrAnnotations] << [[true, false]].combinations()
|
||||
}
|
||||
|
||||
private static String getClassSource(GradleProject gradle, String classname, boolean local, String mappings = MAPPINGS) {
|
||||
File sourcesJar = local ? gradle.getGeneratedLocalSources(mappings) : gradle.getGeneratedSources(mappings)
|
||||
return new String(ZipUtils.unpack(sourcesJar.toPath(), classname), StandardCharsets.UTF_8)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user