Skip to content

Commit

Permalink
Jar-in-Jar: Throw when only incompatible version ranges were specified (
Browse files Browse the repository at this point in the history
#30)

For example, Gradle allows `[1.0,2.0[`, but Maven insists on
`[1.0,2.0)`.
  • Loading branch information
shartte authored Jun 20, 2024
1 parent e21af4d commit b66eb53
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import net.neoforged.jarjar.metadata.ContainedJarIdentifier;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.gradle.api.GradleException;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.component.ComponentSelector;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
Expand Down Expand Up @@ -130,10 +131,9 @@ private static String getEmbeddedFilename(ResolvedArtifactResult result, Contain

private static void collectFromComponent(ResolvedComponentResult rootComponent, Set<ContainedJarIdentifier> knownIdentifiers, Map<ContainedJarIdentifier, String> versions, Map<ContainedJarIdentifier, String> versionRanges) {
for (DependencyResult result : rootComponent.getDependencies()) {
if (!(result instanceof ResolvedDependencyResult)) {
if (!(result instanceof ResolvedDependencyResult resolvedResult)) {
continue;
}
ResolvedDependencyResult resolvedResult = (ResolvedDependencyResult) result;
ComponentSelector requested = resolvedResult.getRequested();
ResolvedVariantResult variant = resolvedResult.getResolvedVariant();

Expand All @@ -147,16 +147,19 @@ private static void collectFromComponent(ResolvedComponentResult rootComponent,

String versionRange = null;
if (requested instanceof ModuleComponentSelector requestedModule) {
if (isValidVersionRange(requestedModule.getVersionConstraint().getStrictVersion())) {
versionRange = requestedModule.getVersionConstraint().getStrictVersion();
} else if (isValidVersionRange(requestedModule.getVersionConstraint().getRequiredVersion())) {
versionRange = requestedModule.getVersionConstraint().getRequiredVersion();
} else if (isValidVersionRange(requestedModule.getVersionConstraint().getPreferredVersion())) {
versionRange = requestedModule.getVersionConstraint().getPreferredVersion();
} if (isValidVersionRange(requestedModule.getVersion())) {
versionRange = requestedModule.getVersion();
var constraint = requestedModule.getVersionConstraint();
if (!constraint.getStrictVersion().isEmpty()) {
versionRange = validateVersionRange(constraint.getStrictVersion(), requestedModule);
} else if (!constraint.getRequiredVersion().isEmpty()) {
versionRange = validateVersionRange(constraint.getRequiredVersion(), requestedModule);
} else if (!constraint.getPreferredVersion().isEmpty()) {
versionRange = validateVersionRange(constraint.getPreferredVersion(), requestedModule);
} else {
versionRange = validateVersionRange(requestedModule.getVersion(), requestedModule);
}
}

// If no range was specified, or this is a project-dependency, use a loose version range
if (versionRange == null) {
versionRange = makeOpenRange(variant);
}
Expand All @@ -174,8 +177,7 @@ private static void collectFromComponent(ResolvedComponentResult rootComponent,

private static @Nullable ArtifactIdentifier capabilityOrModule(final ResolvedVariantResult variant) {
ArtifactIdentifier moduleIdentifier = null;
if (variant.getOwner() instanceof ModuleComponentIdentifier) {
ModuleComponentIdentifier moduleComponentIdentifier = (ModuleComponentIdentifier) variant.getOwner();
if (variant.getOwner() instanceof ModuleComponentIdentifier moduleComponentIdentifier) {
moduleIdentifier = new ArtifactIdentifier(
moduleComponentIdentifier.getGroup(),
moduleComponentIdentifier.getModule(),
Expand All @@ -189,7 +191,7 @@ private static void collectFromComponent(ResolvedComponentResult rootComponent,
capability.getName(),
capability.getVersion()
))
.collect(Collectors.toList());
.toList();

if (moduleIdentifier != null && capabilityIdentifiers.contains(moduleIdentifier)) {
return moduleIdentifier;
Expand Down Expand Up @@ -221,16 +223,25 @@ private static void collectFromComponent(ResolvedComponentResult rootComponent,
return moduleOrCapabilityVersion(variant);
}

private static boolean isValidVersionRange(final @Nullable String range) {
if (range == null) {
return false;
}
private static String validateVersionRange(String range, ModuleComponentSelector module) {
var errorPrefix = "Unsupported version constraint '" + range + "' on Jar-in-Jar dependency " + module.getModuleIdentifier() + ": ";

VersionRange data;
try {
final VersionRange data = VersionRange.createFromVersionSpec(range);
return data.hasRestrictions() && data.getRecommendedVersion() == null && !range.contains("+");
data = VersionRange.createFromVersionSpec(range);
} catch (InvalidVersionSpecificationException e) {
return false;
throw new GradleException(errorPrefix + e.getMessage());
}

if (!data.hasRestrictions()) {
throw new GradleException(errorPrefix + "no restrictions");
} else if (data.getRecommendedVersion() != null) {
throw new GradleException(errorPrefix + "recommended versions are unsupported");
} else if (range.contains("+")) {
throw new GradleException(errorPrefix + "dynamic versions are unsupported");
}

return range;
}

/**
Expand Down
112 changes: 112 additions & 0 deletions src/test/java/net/neoforged/moddevgradle/tasks/JarJarTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package net.neoforged.moddevgradle.tasks;

import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.GradleRunner;
import org.gradle.testkit.runner.UnexpectedBuildFailure;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import static org.assertj.core.api.Assertions.assertThat;
import static org.gradle.testkit.runner.TaskOutcome.NO_SOURCE;
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

class JarJarTest {
@TempDir
Path tempDir;

@Test
public void testNoSourceWhenNoDependenciesAreDefined() throws IOException {
var result = runWithSource("");
assertEquals(NO_SOURCE, result.task(":jarJar").getOutcome());
}

@Test
public void testSuccessfulEmbed() throws IOException {
var result = runWithSource("""
dependencies {
jarJar(implementation("org.slf4j:slf4j-api")) {
version {
strictly '[0.1, 3.0)'
prefer '2.0.13'
}
}
}
""");
assertEquals(SUCCESS, result.task(":jarJar").getOutcome());
}

@Test
public void testUnsupportedStrictlyRange() {
var e = assertThrows(UnexpectedBuildFailure.class, () -> runWithSource("""
dependencies {
jarJar(implementation("org.slf4j:slf4j-api")) {
version {
strictly '[0.1, 3.0['
prefer '2.0.13'
}
}
}
"""));
assertThat(e).hasMessageContaining("Unsupported version constraint '[0.1, 3.0[' on Jar-in-Jar dependency org.slf4j:slf4j-api");
}

@Test
public void testUnsupportedRequiredRange() {
var e = assertThrows(UnexpectedBuildFailure.class, () -> runWithSource("""
dependencies {
jarJar(implementation("org.slf4j:slf4j-api:[0.1, 3.0["))
}
"""));
assertThat(e).hasMessageContaining("Unsupported version constraint '[0.1, 3.0[' on Jar-in-Jar dependency org.slf4j:slf4j-api");
}

@Test
public void testUnsupportedPreferredRange() {
var e = assertThrows(UnexpectedBuildFailure.class, () -> runWithSource("""
dependencies {
jarJar(implementation("org.slf4j:slf4j-api")) {
version {
prefer '[0.1, 3.0['
}
}
}
"""));
assertThat(e).hasMessageContaining("Unsupported version constraint '[0.1, 3.0[' on Jar-in-Jar dependency org.slf4j:slf4j-api");
}

@Test
public void testUnsupportedDynamicVersion() {
var e = assertThrows(UnexpectedBuildFailure.class, () -> runWithSource("""
dependencies {
jarJar(implementation("org.slf4j:slf4j-api:2.0.+"))
}
"""));
assertThat(e).hasMessageContaining("Unsupported version constraint '2.0.+' on Jar-in-Jar dependency org.slf4j:slf4j-api");
}

private BuildResult runWithSource(String source) throws IOException {
Files.writeString(tempDir.resolve("settings.gradle"), "");
Files.writeString(tempDir.resolve("build.gradle"), """
plugins {
id "net.neoforged.moddev"
}
repositories {
mavenCentral()
}
""" + source);

return GradleRunner.create()
.withPluginClasspath()
.withProjectDir(tempDir.toFile())
.withArguments("jarjar")
.withDebug(true)
.build();
}

}

0 comments on commit b66eb53

Please sign in to comment.