Single relocation declaration

- Add gradle/runtime-relocations.gradle as the one runtime-download relocation map
- Read the slimJar relocations and shrinkRelocations from that map
- Pass the derived relocation prefixes into BukkitArtifactVerifier
- Drop the duplicate jar size check the packaging audit already enforces
This commit is contained in:
Brian Neumann-Fopiano
2026-09-08 03:40:29 -04:00
parent c83d6964f1
commit 6e3e1f3c64
5 changed files with 64 additions and 74 deletions
@@ -3,6 +3,7 @@ import org.gradle.api.GradleException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
@@ -23,39 +24,24 @@ public final class BukkitArtifactVerifier {
"art/arcane/iris/",
"art/arcane/volmlib/"
);
// Relocation targets for the libraries slimjar downloads and relocates at runtime. Compiled
// references to them are correct and the classes are correctly absent from the jar.
private static final List<String> RUNTIME_DOWNLOADED_PREFIXES = List.of(
"art/arcane/iris/util/paper/",
"art/arcane/iris/util/kyori/",
"art/arcane/iris/util/metrics/",
"art/arcane/iris/util/maven/",
"art/arcane/iris/util/plexus/",
"art/arcane/iris/util/sisu/",
"art/arcane/iris/util/aether/",
"art/arcane/iris/util/guice/",
"art/arcane/iris/util/dom4j/",
"art/arcane/iris/util/jaxen/",
"art/arcane/iris/util/gson/",
"art/arcane/iris/util/lru/",
"art/arcane/iris/util/caffeine/",
"art/arcane/iris/util/paralithic/"
);
private static final String MATTER_SLICE_PACKAGE = "art/arcane/volmlib/util/matter/slices/";
private static final String LANGUAGE_DIRECTORY = "languages/";
private BukkitArtifactVerifier() {
}
/**
* @param runtimeDownloadedPrefixes relocation targets for the libraries slimjar downloads and
* relocates at runtime, derived from the single relocation
* declaration in gradle/runtime-relocations.gradle. Compiled
* references to them are correct and the classes are correctly
* absent from the jar.
*/
public static void verify(File artifact, List<String> requiredEntries,
int minimumMatterSlices, long maximumArtifactBytes) {
int minimumMatterSlices, Collection<String> runtimeDownloadedPrefixes) {
if (!artifact.isFile()) {
throw new GradleException("Missing Bukkit Iris artifact: " + artifact.getAbsolutePath());
}
if (artifact.length() > maximumArtifactBytes) {
throw new GradleException(artifact.getName() + " is " + artifact.length()
+ " bytes; Bukkit artifacts must not exceed " + maximumArtifactBytes + " bytes");
}
try (JarFile jar = new JarFile(artifact)) {
for (String requiredEntry : requiredEntries) {
@@ -113,7 +99,7 @@ public final class BukkitArtifactVerifier {
for (String reference : ClassReferences.read(readEntryBytes(jar, entry))) {
if (shippedClasses.contains(reference)
|| !startsWithAny(reference, SHIPPED_PREFIXES)
|| startsWithAny(reference, RUNTIME_DOWNLOADED_PREFIXES)) {
|| startsWithAny(reference, runtimeDownloadedPrefixes)) {
continue;
}
dangling.putIfAbsent(reference, entry.getName());
@@ -145,7 +131,7 @@ public final class BukkitArtifactVerifier {
}
}
private static boolean startsWithAny(String value, List<String> prefixes) {
private static boolean startsWithAny(String value, Collection<String> prefixes) {
for (String prefix : prefixes) {
if (value.startsWith(prefix)) {
return true;
@@ -30,6 +30,13 @@ public class BukkitArtifactVerifierTest {
NMS_BINDING + ".class"
);
private static final int MATTER_SLICES = 1;
private static final List<String> RUNTIME_DOWNLOADED_PREFIXES = List.of(
"art/arcane/iris/util/kyori/",
"art/arcane/iris/util/gson/",
"art/arcane/iris/util/lru/",
"art/arcane/iris/util/caffeine/",
"art/arcane/iris/util/paralithic/"
);
@Rule
public final TemporaryFolder temporaryFolder = new TemporaryFolder();
@@ -38,7 +45,7 @@ public class BukkitArtifactVerifierTest {
public void acceptsCompleteArtifact() throws Exception {
File artifact = createArtifact(validEntries());
BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, Long.MAX_VALUE);
BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, RUNTIME_DOWNLOADED_PREFIXES);
}
@Test
@@ -48,7 +55,7 @@ public class BukkitArtifactVerifierTest {
File artifact = createArtifact(entries);
GradleException failure = assertThrows(GradleException.class,
() -> BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, Long.MAX_VALUE));
() -> BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, RUNTIME_DOWNLOADED_PREFIXES));
assertTrue(failure.getMessage().contains(PLUGIN_DESCRIPTOR));
}
@@ -59,7 +66,7 @@ public class BukkitArtifactVerifierTest {
File artifact = createArtifact(entries);
GradleException failure = assertThrows(GradleException.class,
() -> BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, Long.MAX_VALUE));
() -> BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, RUNTIME_DOWNLOADED_PREFIXES));
assertTrue(failure.getMessage().contains(SLIMJAR_DEPENDENCIES));
}
@@ -70,7 +77,7 @@ public class BukkitArtifactVerifierTest {
File artifact = createArtifact(entries);
GradleException failure = assertThrows(GradleException.class,
() -> BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, Long.MAX_VALUE));
() -> BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, RUNTIME_DOWNLOADED_PREFIXES));
assertTrue(failure.getMessage().contains(SLIMJAR_RESOLUTIONS));
}
@@ -81,7 +88,7 @@ public class BukkitArtifactVerifierTest {
File artifact = createArtifact(entries);
GradleException failure = assertThrows(GradleException.class,
() -> BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, Long.MAX_VALUE));
() -> BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, RUNTIME_DOWNLOADED_PREFIXES));
assertTrue(failure.getMessage().contains("art/arcane/volmlib/util/noise/CNG"));
}
@@ -100,20 +107,20 @@ public class BukkitArtifactVerifierTest {
classReferencing("art/arcane/iris/ParalithicConsumer", "art/arcane/iris/util/paralithic/functions/Function"));
File artifact = createArtifact(entries);
BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, Long.MAX_VALUE);
BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, RUNTIME_DOWNLOADED_PREFIXES);
}
@Test
public void rejectsArtifactAboveConfiguredSize() throws Exception {
File artifact = createArtifact(validEntries());
public void rejectsReferenceOutsideRuntimeDownloadedPrefixes() throws Exception {
Map<String, byte[]> entries = validEntries();
entries.put("art/arcane/iris/PaperConsumer.class",
classReferencing("art/arcane/iris/PaperConsumer", "art/arcane/iris/util/paper/PaperLib"));
File artifact = createArtifact(entries);
GradleException failure = assertThrows(GradleException.class,
() -> BukkitArtifactVerifier.verify(
artifact,
REQUIRED_ENTRIES,
MATTER_SLICES,
artifact.length() - 1L));
assertTrue(failure.getMessage().contains("must not exceed"));
() -> BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES,
RUNTIME_DOWNLOADED_PREFIXES));
assertTrue(failure.getMessage().contains("art/arcane/iris/util/paper/PaperLib"));
}
@Test
@@ -123,7 +130,7 @@ public class BukkitArtifactVerifierTest {
File artifact = createArtifact(entries);
GradleException failure = assertThrows(GradleException.class,
() -> BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, Long.MAX_VALUE));
() -> BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, RUNTIME_DOWNLOADED_PREFIXES));
assertTrue(failure.getMessage().contains("locale files"));
}
@@ -134,7 +141,7 @@ public class BukkitArtifactVerifierTest {
File artifact = createArtifact(entries);
GradleException failure = assertThrows(GradleException.class,
() -> BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, Long.MAX_VALUE));
() -> BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, RUNTIME_DOWNLOADED_PREFIXES));
assertTrue(failure.getMessage().contains("Matter slice types"));
}
@@ -145,7 +152,7 @@ public class BukkitArtifactVerifierTest {
classAnnotatedWith("art/arcane/iris/Annotated", "com/google/errorprone/annotations/CanIgnoreReturnValue"));
File artifact = createArtifact(entries);
BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, Long.MAX_VALUE);
BukkitArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, MATTER_SLICES, RUNTIME_DOWNLOADED_PREFIXES);
}
private Map<String, byte[]> validEntries() {