From 84d6d87650fdb76ae28626d255410600670912bb Mon Sep 17 00:00:00 2001 From: Finn Rades <64548817+zOnlyKroks@users.noreply.github.com> Date: Sun, 9 Nov 2025 15:13:00 +0100 Subject: [PATCH] Optimize DeobfSpecContext by scanning configurations individually (#1439) * Optimize DeobfSpecContext by scanning configurations individually Previously, DeobfSpecContext used getFullClasspath().getFiles() which resolved the union of all configurations at once. This was extremely slow (~9.8s with FAPI). * Cleanup * Cleanup * Cleanup --- .../speccontext/DeobfSpecContext.java | 74 ++++++++++++------- .../processors/speccontext/ProjectView.java | 2 +- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/DeobfSpecContext.java b/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/DeobfSpecContext.java index c18d067c..3be4e57f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/DeobfSpecContext.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/DeobfSpecContext.java @@ -63,34 +63,57 @@ public record DeobfSpecContext(List modDependencies, public static DeobfSpecContext create(DeobfProjectView projectView) { AsyncCache> fmjCache = new AsyncCache<>(); - List dependentMods = getDependentMods(projectView, fmjCache); - Map mods = dependentMods.stream() - .collect(HashMap::new, (map, mod) -> map.put(mod.getId(), mod), Map::putAll); FileCollection mainRuntimeClasspath = projectView.getDependencies(DebofConfiguration.RUNTIME, DebofConfiguration.TargetSourceSet.MAIN); FileCollection mainCompileClasspath = projectView.getDependencies(DebofConfiguration.COMPILE, DebofConfiguration.TargetSourceSet.MAIN); // All mods in both [runtimeClasspath, compileClasspath] - Set mainTransformingModIds = common( - getModIds(mainRuntimeClasspath, fmjCache), - getModIds(mainCompileClasspath, fmjCache) - ); + List mainRuntimeMods = getModsFromConfiguration(mainRuntimeClasspath, fmjCache); + List mainCompileMods = getModsFromConfiguration(mainCompileClasspath, fmjCache); + + Set mainRuntimeModIds = toModIdSet(mainRuntimeMods); + Set mainCompileModIds = toModIdSet(mainCompileMods); + Set mainTransformingModIds = common(mainRuntimeModIds, mainCompileModIds); // All mods in both [runtimeClientClasspath, compileClientClasspath] + List clientRuntimeMods; + List clientCompileMods; Set clientTransformingModIds; if (projectView.areEnvironmentSourceSetsSplit()) { FileCollection clientRuntimeClasspath = projectView.getDependencies(DebofConfiguration.RUNTIME, DebofConfiguration.TargetSourceSet.CLIENT); FileCollection clientCompileClasspath = projectView.getDependencies(DebofConfiguration.COMPILE, DebofConfiguration.TargetSourceSet.CLIENT); - clientTransformingModIds = common( - getModIds(clientRuntimeClasspath, fmjCache), - getModIds(clientCompileClasspath, fmjCache) - ); + clientRuntimeMods = getModsFromConfiguration(clientRuntimeClasspath, fmjCache); + clientCompileMods = getModsFromConfiguration(clientCompileClasspath, fmjCache); + + Set clientRuntimeModIds = toModIdSet(clientRuntimeMods); + Set clientCompileModIds = toModIdSet(clientCompileMods); + clientTransformingModIds = common(clientRuntimeModIds, clientCompileModIds); } else { + clientRuntimeMods = List.of(); + clientCompileMods = List.of(); clientTransformingModIds = Set.of(); } + // Build the full list of dependent mods from all configurations + List allMods = new ArrayList<>(); + allMods.addAll(mainRuntimeMods); + allMods.addAll(mainCompileMods); + allMods.addAll(clientRuntimeMods); + allMods.addAll(clientCompileMods); + + // Add project dependencies + if (!projectView.disableProjectDependantMods()) { + for (Project dependentProject : SpecContext.getDependentProjects(projectView).toList()) { + allMods.addAll(fmjCache.getBlocking(dependentProject.getPath(), () -> FabricModJsonHelpers.getModsInProject(dependentProject))); + } + } + + List dependentMods = SpecContext.distinctSorted(allMods); + Map mods = dependentMods.stream() + .collect(HashMap::new, (map, mod) -> map.put(mod.getId(), mod), Map::putAll); + // All dependency mods that are on both the compile and runtime classpath List modDependenciesCompileRuntime = new ArrayList<>(getMods(mods, combine(mainTransformingModIds, clientTransformingModIds))); @@ -108,9 +131,8 @@ public record DeobfSpecContext(List modDependencies, // Returns a list of all the mods that the current project depends on private static List getDependentMods(DeobfProjectView projectView, AsyncCache> fmjCache) { var futures = new ArrayList>>(); - Set artifacts = projectView.getFullClasspath().getFiles(); - for (File artifact : artifacts) { + for (File artifact : projectView.getFullClasspath().getFiles()) { futures.add(fmjCache.get(artifact.toPath().toAbsolutePath().toString(), () -> { return getMod(artifact.toPath()) .map(List::of) @@ -128,21 +150,21 @@ public record DeobfSpecContext(List modDependencies, return SpecContext.distinctSorted(AsyncCache.joinList(futures)); } - // Returns a list of mod ids in a given configuration - private static Set getModIds(FileCollection configuration, AsyncCache> fmjCache) { + // Returns a list of mods from a given configuration + private static List getModsFromConfiguration(FileCollection configuration, AsyncCache> fmjCache) { var futures = new ArrayList>>(); - Set artifacts = configuration.getFiles(); - - for (File artifact : artifacts) { - futures.add(fmjCache.get(artifact.toPath().toAbsolutePath().toString(), () -> { - return getMod(artifact.toPath()) - .map(List::of) - .orElseGet(List::of); - })); + for (File artifact : configuration.getFiles()) { + futures.add(fmjCache.get(artifact.toPath().toAbsolutePath().toString(), () -> getMod(artifact.toPath()) + .map(List::of) + .orElseGet(List::of))); } - return SpecContext.distinctSorted(AsyncCache.joinList(futures)).stream() + return SpecContext.distinctSorted(AsyncCache.joinList(futures)); + } + + private static Set toModIdSet(List mods) { + return mods.stream() .map(FabricModJson::getId) .collect(HashSet::new, Set::add, Set::addAll); } @@ -170,9 +192,7 @@ public record DeobfSpecContext(List modDependencies, var mods = new ArrayList(); for (Project dependentProject : getCompileRuntimeProjectDependencies(projectView).toList()) { - List projectMods = fmjCache.getBlocking(dependentProject.getPath(), () -> { - return FabricModJsonHelpers.getModsInProject(dependentProject); - }); + List projectMods = fmjCache.getBlocking(dependentProject.getPath(), () -> FabricModJsonHelpers.getModsInProject(dependentProject)); mods.addAll(projectMods); } diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/ProjectView.java b/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/ProjectView.java index 5c779a94..43ed093b 100644 --- a/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/ProjectView.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/ProjectView.java @@ -40,7 +40,7 @@ import net.fabricmc.loom.util.gradle.GradleUtils; // Used to abstract out the Gradle API usage to ease unit testing. public interface ProjectView { - // Returns a list of Loom Projects found in the specified Configuration + //Returns a list of Loom Projects found in the specified Configuration Stream getLoomProjectDependencies(String name); // Returns the mods defined in the current project