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
This commit is contained in:
Finn Rades
2025-11-09 15:13:00 +01:00
committed by GitHub
parent 739e124f26
commit 84d6d87650
2 changed files with 48 additions and 28 deletions

View File

@@ -63,34 +63,57 @@ public record DeobfSpecContext(List<FabricModJson> modDependencies,
public static DeobfSpecContext create(DeobfProjectView projectView) {
AsyncCache<List<FabricModJson>> fmjCache = new AsyncCache<>();
List<FabricModJson> dependentMods = getDependentMods(projectView, fmjCache);
Map<String, FabricModJson> 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<String> mainTransformingModIds = common(
getModIds(mainRuntimeClasspath, fmjCache),
getModIds(mainCompileClasspath, fmjCache)
);
List<FabricModJson> mainRuntimeMods = getModsFromConfiguration(mainRuntimeClasspath, fmjCache);
List<FabricModJson> mainCompileMods = getModsFromConfiguration(mainCompileClasspath, fmjCache);
Set<String> mainRuntimeModIds = toModIdSet(mainRuntimeMods);
Set<String> mainCompileModIds = toModIdSet(mainCompileMods);
Set<String> mainTransformingModIds = common(mainRuntimeModIds, mainCompileModIds);
// All mods in both [runtimeClientClasspath, compileClientClasspath]
List<FabricModJson> clientRuntimeMods;
List<FabricModJson> clientCompileMods;
Set<String> 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<String> clientRuntimeModIds = toModIdSet(clientRuntimeMods);
Set<String> 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<FabricModJson> 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<FabricModJson> dependentMods = SpecContext.distinctSorted(allMods);
Map<String, FabricModJson> 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<FabricModJson> modDependenciesCompileRuntime = new ArrayList<>(getMods(mods, combine(mainTransformingModIds, clientTransformingModIds)));
@@ -108,9 +131,8 @@ public record DeobfSpecContext(List<FabricModJson> modDependencies,
// Returns a list of all the mods that the current project depends on
private static List<FabricModJson> getDependentMods(DeobfProjectView projectView, AsyncCache<List<FabricModJson>> fmjCache) {
var futures = new ArrayList<CompletableFuture<List<FabricModJson>>>();
Set<File> 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<FabricModJson> modDependencies,
return SpecContext.distinctSorted(AsyncCache.joinList(futures));
}
// Returns a list of mod ids in a given configuration
private static Set<String> getModIds(FileCollection configuration, AsyncCache<List<FabricModJson>> fmjCache) {
// Returns a list of mods from a given configuration
private static List<FabricModJson> getModsFromConfiguration(FileCollection configuration, AsyncCache<List<FabricModJson>> fmjCache) {
var futures = new ArrayList<CompletableFuture<List<FabricModJson>>>();
Set<File> 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<String> toModIdSet(List<FabricModJson> mods) {
return mods.stream()
.map(FabricModJson::getId)
.collect(HashSet::new, Set::add, Set::addAll);
}
@@ -170,9 +192,7 @@ public record DeobfSpecContext(List<FabricModJson> modDependencies,
var mods = new ArrayList<FabricModJson>();
for (Project dependentProject : getCompileRuntimeProjectDependencies(projectView).toList()) {
List<FabricModJson> projectMods = fmjCache.getBlocking(dependentProject.getPath(), () -> {
return FabricModJsonHelpers.getModsInProject(dependentProject);
});
List<FabricModJson> projectMods = fmjCache.getBlocking(dependentProject.getPath(), () -> FabricModJsonHelpers.getModsInProject(dependentProject));
mods.addAll(projectMods);
}

View File

@@ -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<Project> getLoomProjectDependencies(String name);
// Returns the mods defined in the current project