Skip to content

Commit

Permalink
Add dynamic version support
Browse files Browse the repository at this point in the history
  • Loading branch information
shartte committed Jan 2, 2025
1 parent 42dbe83 commit 52d8308
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,28 @@ public Artifact get(MavenCoordinate mavenCoordinate) throws IOException {
return externalArtifact;
}

// Yet another special case: dynamic versions!
// Used in 1.12.1, for example. And yes, this will be very slow.
if (mavenCoordinate.isDynamicVersion()) {
var availableVersions = MavenMetadata.gatherVersions(
downloadManager,
repositoryBaseUrls,
mavenCoordinate.groupId(),
mavenCoordinate.artifactId()
);
for (var availableVersion : availableVersions) {
if (mavenCoordinate.matchesVersion(availableVersion.version())) {
var concreteMavenCoordinate = mavenCoordinate.withVersion(availableVersion.version());
return get(concreteMavenCoordinate, availableVersion.repositoryUrl());
}
}
}

var finalLocation = artifactsCache.resolve(mavenCoordinate.toRelativeRepositoryPath());

// Special case: NeoForge reference libraries that are only available via the Mojang download server
if (mavenCoordinate.groupId().equals("com.mojang") && mavenCoordinate.artifactId().equals("logging")) {
if (mavenCoordinate.groupId().equals("com.mojang") && mavenCoordinate.artifactId().equals("logging")
|| mavenCoordinate.groupId().equals("net.minecraft") && mavenCoordinate.artifactId().equals("launchwrapper")) {
return get(mavenCoordinate, MINECRAFT_LIBRARIES_URI);
}

Expand Down Expand Up @@ -219,8 +237,8 @@ public Artifact downloadFromManifest(MinecraftVersionManifest versionManifest, S
var downloadSpec = versionManifest.downloads().get(type);
if (downloadSpec == null) {
throw new IllegalArgumentException("Minecraft version manifest " + versionManifest.id()
+ " does not declare a download for " + type + ". Available: "
+ versionManifest.downloads().keySet());
+ " does not declare a download for " + type + ". Available: "
+ versionManifest.downloads().keySet());
}

var extension = FilenameUtil.getExtension(downloadSpec.uri().getPath());
Expand Down Expand Up @@ -261,11 +279,22 @@ public interface DownloadAction {
private Artifact getFromExternalManifest(MavenCoordinate artifactCoordinate) {
artifactCoordinate = normalizeExtension(artifactCoordinate);

// Try direct match first
var artifact = externallyProvided.get(artifactCoordinate);
if (artifact != null) {
return artifact;
}

// See if it's a dynamic version and match against all entries
if (artifactCoordinate.isDynamicVersion()) {
for (var entry : externallyProvided.entrySet()) {
if (artifactCoordinate.matchesVersion(entry.getKey().version())
&& entry.getKey().withVersion(artifactCoordinate.version()).equals(artifactCoordinate)) {
return entry.getValue();
}
}
}

// Fall back to looking up a wildcard version for dependency replacement in includeBuild scenarios
if (!"*".equals(artifactCoordinate.version())) {
artifact = externallyProvided.get(artifactCoordinate.withVersion("*"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package net.neoforged.neoform.runtime.artifacts;

import net.neoforged.neoform.runtime.downloads.DownloadManager;
import net.neoforged.neoform.runtime.utils.Logger;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;

/**
* Support class for querying maven metadata from a remote repository.
* The format is documented here: https://maven.apache.org/repositories/metadata.html
* We only deal with A-level metadata since we're interested in listing the versions of
* a specific artifact.
*/
final class MavenMetadata {
private static final Logger LOG = Logger.create();

private MavenMetadata() {
}

static List<AvailableVersion> gatherVersions(DownloadManager downloadManager,
List<URI> repositoryBaseUrls,
String groupId,
String artifactId) throws IOException {
var versions = new ArrayList<AvailableVersion>();
for (var repositoryBaseUrl : repositoryBaseUrls) {
versions.addAll(gatherVersions(downloadManager, repositoryBaseUrl, groupId, artifactId));
}
return versions;
}

static List<AvailableVersion> gatherVersions(DownloadManager downloadManager,
URI repositoryBaseUrl,
String groupId,
String artifactId) throws IOException {
var metadataUri = repositoryBaseUrl.toString();
if (!metadataUri.endsWith("/")) {
metadataUri += "/";
}
metadataUri += groupId.replace('.', '/') + "/" + artifactId + "/maven-metadata.xml";

byte[] metadataContent;

var tempFile = Files.createTempFile("maven-metadata", ".xml");
try {
Files.deleteIfExists(tempFile); // The downloader should assume it does not exist yet
downloadManager.download(URI.create(metadataUri), tempFile);
metadataContent = Files.readAllBytes(tempFile);
} catch (FileNotFoundException fnf) {
return List.of(); // Repository doesn't have artifact
} finally {
Files.deleteIfExists(tempFile);
}

try (var in = new ByteArrayInputStream(metadataContent)) {
var result = new ArrayList<AvailableVersion>();
var documentBuilder = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder();
var document = documentBuilder.parse(in).getDocumentElement();
var nodes = document.getChildNodes();
for (var i = 0; i < nodes.getLength(); i++) {
if (nodes.item(i) instanceof Element versioningEl && "versioning".equals(versioningEl.getTagName())) {
for (var versions = versioningEl.getFirstChild(); versions != null; versions = versions.getNextSibling()) {
if (versions instanceof Element versionsEl && "versions".equals(versionsEl.getTagName())) {
for (var child = versionsEl.getFirstChild(); child != null; child = child.getNextSibling()) {
if (child instanceof Element childEl && "version".equals(childEl.getTagName())) {
result.add(new AvailableVersion(
repositoryBaseUrl,
childEl.getTextContent().trim()
));
}
}
}
}
}
}
return result;
} catch (Exception e) {
LOG.println("Failed to parse Maven metadata from " + metadataUri + ": " + e);
throw new RuntimeException(e);
}
}

record AvailableVersion(URI repositoryUrl, String version) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,19 @@ public MavenCoordinate withVersion(String version) {
version
);
}

public boolean isDynamicVersion() {
// We only support extremely simple cases right now.
return version.endsWith("+");
}

public boolean matchesVersion(String version) {
// "+" acts as a prefix match according to Gradle
// https://docs.gradle.org/current/userguide/dependency_versions.html#sec:single-version-declarations
if (this.version.endsWith("+")) {
return version.startsWith(this.version.substring(0, this.version.length() - 1));
} else {
return this.version.equals(version);
}
}
}

0 comments on commit 52d8308

Please # to comment.