Use a hash for remapped dependency caching. (#1277)

* Use a hash from ModDependencyOptions for remapped dependency caching.

* Use a different group to allow exclusiveContent to work.

* Fix unit tests
This commit is contained in:
modmuss
2025-03-28 12:27:22 +00:00
committed by GitHub
parent 3de1339138
commit dbe1408a72
9 changed files with 202 additions and 36 deletions

View File

@@ -58,6 +58,8 @@ import org.gradle.api.tasks.SourceSet;
import org.gradle.jvm.JvmLibrary;
import org.gradle.language.base.artifact.SourcesArtifact;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
@@ -65,6 +67,7 @@ import net.fabricmc.loom.api.RemapConfigurationSettings;
import net.fabricmc.loom.configuration.RemapConfigurations;
import net.fabricmc.loom.configuration.mods.dependency.ModDependency;
import net.fabricmc.loom.configuration.mods.dependency.ModDependencyFactory;
import net.fabricmc.loom.configuration.mods.dependency.ModDependencyOptions;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets;
import net.fabricmc.loom.util.Checksum;
import net.fabricmc.loom.util.Constants;
@@ -79,6 +82,8 @@ public class ModConfigurationRemapper {
// This can happen when the dependency is a FileCollectionDependency or from a flatDir repository.
public static final String MISSING_GROUP = "unspecified";
private static final Logger LOGGER = LoggerFactory.getLogger(ModConfigurationRemapper.class);
public static void supplyModConfigurations(Project project, ServiceFactory serviceFactory, String mappingsSuffix, LoomGradleExtension extension, SourceRemapper sourceRemapper) {
final DependencyHandler dependencies = project.getDependencies();
// The configurations where the source and remapped artifacts go.
@@ -135,6 +140,14 @@ public class ModConfigurationRemapper {
}
}
final ModDependencyOptions modDependencyOptions = ModDependencyOptions.create(project, ModDependencyOptions.class, options -> {
options.getMappings().set(mappingsSuffix);
});
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Mod dependency options: {}", modDependencyOptions.getJson());
}
// Round 1: Discovery
// Go through all the configs to find artifacts to remap and
// the installer data. The installer data has to be added before
@@ -176,7 +189,7 @@ public class ModConfigurationRemapper {
continue;
}
final ModDependency modDependency = ModDependencyFactory.create(artifact, artifactMetadata, remappedConfig, clientRemappedConfig, mappingsSuffix, project);
final ModDependency modDependency = ModDependencyFactory.create(artifact, artifactMetadata, remappedConfig, clientRemappedConfig, modDependencyOptions, project);
scheduleSourcesRemapping(project, sourceRemapper, modDependency);
modDependencies.add(modDependency);
}
@@ -332,7 +345,7 @@ public class ModConfigurationRemapper {
}
if (dependency.isCacheInvalid(project, "sources")) {
final Path output = dependency.getWorkingFile("sources");
final Path output = dependency.getWorkingFile(project, "sources");
sourceRemapper.scheduleRemapSources(sourcesInput.toFile(), output.toFile(), false, true, () -> {
try {

View File

@@ -253,8 +253,8 @@ public class ModProcessor {
}
}
private static Path getRemappedOutput(ModDependency dependency) {
return dependency.getWorkingFile(null);
private Path getRemappedOutput(ModDependency dependency) {
return dependency.getWorkingFile(project, null);
}
private void remapJarManifestEntries(Path jar) throws IOException {

View File

@@ -37,23 +37,21 @@ import net.fabricmc.loom.configuration.mods.ArtifactRef;
public abstract sealed class ModDependency permits SplitModDependency, SimpleModDependency {
private final ArtifactRef artifact;
private final ArtifactMetadata metadata;
protected final String group;
protected final String name;
protected final String version;
private final String group;
private final String name;
private final String version;
@Nullable
protected final String classifier;
protected final String mappingsSuffix;
protected final Project project;
private final String classifier;
private final ModDependencyOptions options;
public ModDependency(ArtifactRef artifact, ArtifactMetadata metadata, String mappingsSuffix, Project project) {
public ModDependency(ArtifactRef artifact, ArtifactMetadata metadata, ModDependencyOptions options) {
this.artifact = artifact;
this.metadata = metadata;
this.group = artifact.group();
this.name = artifact.name();
this.version = artifact.version();
this.classifier = artifact.classifier();
this.mappingsSuffix = mappingsSuffix;
this.project = project;
this.options = options;
}
/**
@@ -71,10 +69,15 @@ public abstract sealed class ModDependency permits SplitModDependency, SimpleMod
*/
public abstract void applyToProject(Project project);
protected LocalMavenHelper createMaven(String name) {
/**
* Create a maven helper for the local cache.
* @param type The jar type, e.g "common" or "client" for split dependencies.
*/
protected LocalMavenHelper createMavenHelper(Project project, @Nullable String type) {
final LoomGradleExtension extension = LoomGradleExtension.get(project);
final Path root = extension.getFiles().getRemappedModCache().toPath();
return new LocalMavenHelper(getRemappedGroup(), name, this.version, this.classifier, root);
final String fullName = getName() + (type != null ? "-" + type : "");
return new LocalMavenHelper(getGroup(), fullName, this.version, this.classifier, root);
}
public ArtifactRef getInputArtifact() {
@@ -85,22 +88,26 @@ public abstract sealed class ModDependency permits SplitModDependency, SimpleMod
return metadata;
}
protected String getRemappedGroup() {
return getMappingsPrefix() + "." + group;
protected String getName() {
return "%s-%s".formatted(name, options.getCacheKey());
}
private String getMappingsPrefix() {
return mappingsSuffix.replace(".", "_").replace("-", "_").replace("+", "_");
protected String getGroup() {
return "remapped.%s".formatted(group);
}
protected String getVersion() {
return version;
}
public Path getInputFile() {
return artifact.path();
}
public Path getWorkingFile(@Nullable String classifier) {
public Path getWorkingFile(Project project, @Nullable String classifier) {
final LoomGradleExtension extension = LoomGradleExtension.get(project);
final String fileName = classifier == null ? String.format("%s-%s-%s.jar", getRemappedGroup(), name, version)
: String.format("%s-%s-%s-%s.jar", getRemappedGroup(), name, version, classifier);
final String fileName = classifier == null ? String.format("%s-%s-%s.jar", getGroup(), getName(), version)
: String.format("%s-%s-%s-%s.jar", getGroup(), getName(), version, classifier);
return extension.getFiles().getProjectBuildCache().toPath().resolve("remapped_working").resolve(fileName);
}

View File

@@ -41,7 +41,7 @@ import net.fabricmc.loom.util.AttributeHelper;
public class ModDependencyFactory {
private static final String TARGET_ATTRIBUTE_KEY = "loom-target";
public static ModDependency create(ArtifactRef artifact, ArtifactMetadata metadata, Configuration targetConfig, @Nullable Configuration targetClientConfig, String mappingsSuffix, Project project) {
public static ModDependency create(ArtifactRef artifact, ArtifactMetadata metadata, Configuration targetConfig, @Nullable Configuration targetClientConfig, ModDependencyOptions options, Project project) {
if (targetClientConfig != null && LoomGradleExtension.get(project).getSplitModDependencies().get()) {
final Optional<JarSplitter.Target> cachedTarget = readTarget(artifact);
JarSplitter.Target target;
@@ -54,11 +54,11 @@ public class ModDependencyFactory {
}
if (target != null) {
return new SplitModDependency(artifact, metadata, mappingsSuffix, targetConfig, targetClientConfig, target, project);
return new SplitModDependency(artifact, metadata, options, targetConfig, targetClientConfig, target, project);
}
}
return new SimpleModDependency(artifact, metadata, mappingsSuffix, targetConfig, project);
return new SimpleModDependency(artifact, metadata, options, targetConfig, project);
}
private static Optional<JarSplitter.Target> readTarget(ArtifactRef artifact) {

View File

@@ -0,0 +1,36 @@
/*
* 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.mods.dependency;
import org.gradle.api.provider.Property;
import net.fabricmc.loom.util.CacheKey;
/**
* Inputs used to process a mod dependency. The output jar is cached based on these properties.
*/
public abstract class ModDependencyOptions extends CacheKey {
public abstract Property<String> getMappings();
}

View File

@@ -40,10 +40,10 @@ public final class SimpleModDependency extends ModDependency {
private final Configuration targetConfig;
private final LocalMavenHelper maven;
public SimpleModDependency(ArtifactRef artifact, ArtifactMetadata metadata, String mappingsSuffix, Configuration targetConfig, Project project) {
super(artifact, metadata, mappingsSuffix, project);
public SimpleModDependency(ArtifactRef artifact, ArtifactMetadata metadata, ModDependencyOptions options, Configuration targetConfig, Project project) {
super(artifact, metadata, options);
this.targetConfig = Objects.requireNonNull(targetConfig);
this.maven = createMaven(name);
this.maven = createMavenHelper(project, null);
}
@Override

View File

@@ -48,13 +48,13 @@ public final class SplitModDependency extends ModDependency {
@Nullable
private final LocalMavenHelper clientMaven;
public SplitModDependency(ArtifactRef artifact, ArtifactMetadata metadata, String mappingsSuffix, Configuration targetCommonConfig, Configuration targetClientConfig, JarSplitter.Target target, Project project) {
super(artifact, metadata, mappingsSuffix, project);
public SplitModDependency(ArtifactRef artifact, ArtifactMetadata metadata, ModDependencyOptions options, Configuration targetCommonConfig, Configuration targetClientConfig, JarSplitter.Target target, Project project) {
super(artifact, metadata, options);
this.targetCommonConfig = Objects.requireNonNull(targetCommonConfig);
this.targetClientConfig = Objects.requireNonNull(targetClientConfig);
this.target = Objects.requireNonNull(target);
this.commonMaven = target.common() ? createMaven(name + "-common") : null;
this.clientMaven = target.client() ? createMaven(name + "-client") : null;
this.commonMaven = target.common() ? createMavenHelper(project, "common") : null;
this.clientMaven = target.client() ? createMavenHelper(project, "client") : null;
}
@Override
@@ -86,8 +86,8 @@ public final class SplitModDependency extends ModDependency {
// Split the jar into 2
case SPLIT -> {
final String suffix = variant == null ? "" : "-" + variant;
final Path commonTempJar = getWorkingFile("common" + suffix);
final Path clientTempJar = getWorkingFile("client" + suffix);
final Path commonTempJar = getWorkingFile(project, "common" + suffix);
final Path clientTempJar = getWorkingFile(project, "client" + suffix);
final JarSplitter splitter = new JarSplitter(path);
splitter.split(commonTempJar, clientTempJar);
@@ -114,15 +114,16 @@ public final class SplitModDependency extends ModDependency {
if (target == JarSplitter.Target.SPLIT) {
createModGroup(
project,
getCommonMaven().getOutputFile(null),
getClientMaven().getOutputFile(null)
);
}
}
private void createModGroup(Path commonJar, Path clientJar) {
private void createModGroup(Project project, Path commonJar, Path clientJar) {
LoomGradleExtension extension = LoomGradleExtension.get(project);
final ModSettings modSettings = extension.getMods().maybeCreate(String.format("%s-%s-%s", getRemappedGroup(), name, version));
final ModSettings modSettings = extension.getMods().maybeCreate(String.format("%s-%s-%s", getGroup(), getName(), getVersion()));
modSettings.getModFiles().from(
commonJar.toFile(),
clientJar.toFile()

View File

@@ -0,0 +1,60 @@
/*
* 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.util;
import java.nio.charset.StandardCharsets;
import java.util.function.Supplier;
import com.google.common.base.Suppliers;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.tasks.Internal;
import net.fabricmc.loom.util.gradle.GradleTypeAdapter;
/**
* A simple base class for creating cache keys. Extend this class and create abstract properties to be included in the cache key.
*/
public abstract class CacheKey {
private static final int CHECKSUM_LENGTH = 8;
private final transient Supplier<String> jsonSupplier = Suppliers.memoize(() -> GradleTypeAdapter.GSON.toJson(this));
private final transient Supplier<String> cacheKeySupplier = Suppliers.memoize(() -> Checksum.sha1Hex(jsonSupplier.get().getBytes(StandardCharsets.UTF_8)).substring(0, CHECKSUM_LENGTH));
public static <T> T create(Project project, Class<T> clazz, Action<T> action) {
T instance = project.getObjects().newInstance(clazz);
action.execute(instance);
return instance;
}
@Internal
public final String getJson() {
return jsonSupplier.get();
}
@Internal
public final String getCacheKey() {
return cacheKeySupplier.get();
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.unit
import spock.lang.Specification
import net.fabricmc.loom.configuration.mods.dependency.ModDependencyOptions
import net.fabricmc.loom.test.util.GradleTestUtil
import net.fabricmc.loom.util.CacheKey
class ModDependencyOptionsTest extends Specification {
def "test ModDependencyOptions cache key and json value"() {
given:
def project = GradleTestUtil.mockProject()
def modDependencyOptions = CacheKey.create(project, ModDependencyOptions) {
it.getMappings().set("testMappings")
}
when:
def json = modDependencyOptions.getJson()
def cacheKey = modDependencyOptions.getCacheKey()
then:
json == '{"__mappings__":"testMappings"}'
cacheKey == "c97692d3"
}
}