Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promote native dependencies to compile-time dependencies if they're in all platforms #4

Merged
merged 2 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ repositories {

dependencies {
implementation 'de.undercouch.download:de.undercouch.download.gradle.plugin:5.5.0'
implementation 'org.apache.maven:maven-artifact:3.9.9'
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package net.neoforged.minecraftdependencies
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.transform.CompileStatic
import org.apache.maven.artifact.versioning.ComparableVersion
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
Expand Down Expand Up @@ -62,9 +63,10 @@ abstract class GenerateModuleMetadata extends DefaultTask implements HasMinecraf
metadata.variants = variants

List<String> clientDeps = []
List<String> clientCompileOnlyDeps = []
List<String> serverDeps = []
Map<String, List<String>> clientNatives = [:]
getMcDeps(serverDeps, clientDeps, clientNatives)
getMcDeps(serverDeps, clientDeps, clientCompileOnlyDeps, clientNatives)

Map metaJson = new JsonSlurper().parse(meta.get().asFile) as Map
int javaVersion = (metaJson.javaVersion as Map).majorVersion as int
Expand Down Expand Up @@ -93,6 +95,7 @@ abstract class GenerateModuleMetadata extends DefaultTask implements HasMinecraf
if (objcBridge) {
clientDepEntries.add(depOf(objcBridge))
}
clientDepEntries.addAll(depsOf(clientCompileOnlyDeps))

variants.add([
name : 'clientCompileDependencies',
Expand Down Expand Up @@ -219,7 +222,7 @@ abstract class GenerateModuleMetadata extends DefaultTask implements HasMinecraf
return hashes
}

private void getMcDeps(List<String> server, List<String> client, Map<String, List<String>> clientNatives) {
private void getMcDeps(List<String> server, List<String> client, List<String> clientCompileOnly, Map<String, List<String>> clientNatives) {
Map metaJson = new JsonSlurper().parse(meta.get().asFile) as Map
(metaJson.libraries as List<Map<String, Object>>).each { Map lib ->
Map downloads = lib.downloads as Map
Expand All @@ -243,6 +246,50 @@ abstract class GenerateModuleMetadata extends DefaultTask implements HasMinecraf
}
}
}

// Remove duplicates in natives
for (var libs in clientNatives.values()) {
var dedupedLibs = new LinkedHashSet(libs)
libs.clear()
libs.addAll(dedupedLibs)
}

// Promote natives to compile time dependencies if the same G+A+Classifier is present for all platforms
// Use the lowest version. This happens on some versions (i.e. 1.18.2) where lwjgl and all such dependencies
// are used in a lower version on OSX
for (String windowNativeArtifact in clientNatives.getOrDefault(platforms[0], [])) {
var coordinate = MavenCoordinate.parse(windowNativeArtifact)
var version = new ComparableVersion(coordinate.version())

var inAllPlatforms = true
for (var otherPlatform in platforms.drop(1)) {
var found = false
for (var otherPlatformArtifact in clientNatives.getOrDefault(otherPlatform, [])) {
var otherPlatformCoordinate = MavenCoordinate.parse(otherPlatformArtifact)
if (coordinate.equalsIgnoringVersion(otherPlatformCoordinate)) {
found = true
var otherVersion = new ComparableVersion(otherPlatformCoordinate.version())
if (otherVersion < version) {
version = otherVersion // use lowest
}
break
}
}
if (!found) {
inAllPlatforms = false
break
}
}

if (inAllPlatforms) {
println("Promoting " + coordinate + " (" + version + ") from natives to compile time dependency")
coordinate = coordinate.withVersion(version.toString())
if (client.stream().map(MavenCoordinate::parse).noneMatch { c -> c.equalsIgnoringVersion(coordinate) }) {
clientCompileOnly.add(coordinate.toString())
}
}
}

try (def zf = new ZipFile(serverJar.get().getAsFile())) {
def librariesListEntry = zf.getEntry('META-INF/libraries.list')
if (librariesListEntry != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package net.neoforged.minecraftdependencies;

import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;

/**
* Models the Maven coordinates for an artifact.
*/
public record MavenCoordinate(String groupId, String artifactId, String extension, String classifier, String version) {
public MavenCoordinate {
Objects.requireNonNull(groupId);
Objects.requireNonNull(artifactId);
Objects.requireNonNull(version);
if (extension == null) {
extension = "";
}
if (classifier == null) {
classifier = "";
}
}

/**
* Valid forms:
* <ul>
* <li>{@code groupId:artifactId:version}</li>
* <li>{@code groupId:artifactId:version:classifier}</li>
* <li>{@code groupId:artifactId:version:classifier@extension}</li>
* <li>{@code groupId:artifactId:version@extension}</li>
* </ul>
*/
public static MavenCoordinate parse(String coordinate) {
var coordinateAndExt = coordinate.split("@");
String extension = "";
if (coordinateAndExt.length > 2) {
throw new IllegalArgumentException("Malformed Maven coordinate: " + coordinate);
} else if (coordinateAndExt.length == 2) {
extension = coordinateAndExt[1];
coordinate = coordinateAndExt[0];
}

var parts = coordinate.split(":");
if (parts.length != 3 && parts.length != 4) {
throw new IllegalArgumentException("Malformed Maven coordinate: " + coordinate);
}

var groupId = parts[0];
var artifactId = parts[1];
var version = parts[2];
var classifier = parts.length == 4 ? parts[3] : "";
return new MavenCoordinate(groupId, artifactId, extension, classifier, version);
}

/**
* Constructs a path relative to the root of a Maven repository pointing to the artifact expressed through
* these coordinates.
*/
public Path toRelativeRepositoryPath() {
final String fileName = artifactId + "-" + version +
(!classifier.isEmpty() ? "-" + classifier : "") +
(!extension.isEmpty() ? "." + extension : ".jar");

String[] groups = groupId.split("\\.");
Path result = Paths.get(groups[0]);
for (int i = 1; i < groups.length; i++) {
result = result.resolve(groups[i]);
}

return result.resolve(artifactId).resolve(version).resolve(fileName);
}

@Override
public String toString() {
var result = new StringBuilder();
result.append(groupId).append(":").append(artifactId).append(":").append(version);
if (!classifier.isEmpty()) {
result.append(":").append(classifier);
}
if (!extension.isEmpty()) {
result.append("@").append(extension);
}
return result.toString();
}

public URI toRepositoryUri(URI baseUri) {
var originalBaseUri = baseUri.toString();
var relativePath = toRelativeRepositoryPath().toString().replace('\\', '/');
if (originalBaseUri.endsWith("/")) {
return URI.create(originalBaseUri + relativePath);
} else {
return URI.create(originalBaseUri + "/" + relativePath);
}
}

public boolean equalsIgnoringVersion(MavenCoordinate other) {
return groupId.equals(other.groupId)
&& artifactId.equals(other.artifactId)
&& extension.equals(other.extension)
&& classifier.equals(other.classifier);
}

public MavenCoordinate withClassifier(String classifier) {
return new MavenCoordinate(
groupId,
artifactId,
extension,
classifier,
version
);
}

public MavenCoordinate withVersion(String version) {
return new MavenCoordinate(
groupId,
artifactId,
extension,
classifier,
version
);
}
}
Loading