From eca987a2d3f762d421efa9a695898ce3da2ff66b Mon Sep 17 00:00:00 2001 From: Finn Rades <64548817+zOnlyKroks@users.noreply.github.com> Date: Fri, 7 Nov 2025 22:06:07 +0100 Subject: [PATCH 1/8] Add nestJars API for nesting locally built mod jars (#1427) * Add nestJars API for FileCollection support * Add missin new-line * Fix imports * Rework constructor to avoid ugliness * Update java docs * Update java docs * Implement asked improvements * Fix checkstyle --- .../loom/api/LoomGradleExtensionAPI.java | 24 +++++++++ .../extension/LoomGradleExtensionApiImpl.java | 7 +++ .../extension/LoomGradleExtensionImpl.java | 17 ++++++ .../fabricmc/loom/task/NestJarsAction.java | 40 +++++--------- .../task/NonRemappedJarTaskConfiguration.java | 5 +- .../test/integration/NestJarsApiTest.groovy | 54 +++++++++++++++++++ .../projects/nestJarsApi/build.gradle | 30 +++++++++++ .../nested-mod-content/fabric.mod.json | 8 +++ .../projects/nestJarsApi/settings.gradle | 1 + .../src/main/resources/fabric.mod.json | 8 +++ 10 files changed, 166 insertions(+), 28 deletions(-) create mode 100644 src/test/groovy/net/fabricmc/loom/test/integration/NestJarsApiTest.groovy create mode 100644 src/test/resources/projects/nestJarsApi/build.gradle create mode 100644 src/test/resources/projects/nestJarsApi/nested-mod-content/fabric.mod.json create mode 100644 src/test/resources/projects/nestJarsApi/settings.gradle create mode 100644 src/test/resources/projects/nestJarsApi/src/main/resources/fabric.mod.json diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index 4da936ba..f1284d9d 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -40,6 +40,8 @@ import org.gradle.api.provider.Provider; import org.gradle.api.provider.SetProperty; import org.gradle.api.publish.maven.MavenPublication; import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.jvm.tasks.Jar; import org.jetbrains.annotations.ApiStatus; import net.fabricmc.loom.api.decompilers.DecompilerOptions; @@ -272,4 +274,26 @@ public interface LoomGradleExtensionAPI { * @return A lazily evaluated {@link FileCollection} containing the named minecraft jars. */ FileCollection getNamedMinecraftJars(); + + /** + * Nest mod jars from a {@link FileCollection} into the specified jar task. + * This is useful for including locally built mod jars or jars that don't come from Maven. + * + *

Important: The jars must already be valid mod jars (containing a fabric.mod.json file). + * Non-mod jars will be rejected. + * + *

Example usage: + * {@snippet lang=groovy : + * loom { + * nestJars(tasks.jar, files('local-mod.jar')) + * nestJars(tasks.remapJar, tasks.named('buildOtherMod')) + * } + * } + * + * @param jarTask the jar task to nest jars into (can be jar or remapJar) + * @param jars the file collection containing mod jars to nest + * @since 1.14 + */ + @ApiStatus.Experimental + void nestJars(TaskProvider jarTask, FileCollection jars); } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index 4ab91c17..f3cff2a8 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -45,6 +45,8 @@ import org.gradle.api.provider.Provider; import org.gradle.api.provider.SetProperty; import org.gradle.api.publish.maven.MavenPublication; import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.jvm.tasks.Jar; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.InterfaceInjectionExtensionAPI; @@ -543,5 +545,10 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA public MixinExtension getMixin() { throw new RuntimeException("Yeah... something is really wrong"); } + + @Override + public void nestJars(TaskProvider jarTask, FileCollection jars) { + throw new RuntimeException("Yeah... something is really wrong"); + } } } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index 4c332e44..ec07da21 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -40,6 +40,8 @@ import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.FileCollection; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.jvm.tasks.Jar; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomNoRemapGradlePlugin; @@ -57,6 +59,8 @@ import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.library.LibraryProcessorManager; import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider; +import net.fabricmc.loom.task.NestJarsAction; +import net.fabricmc.loom.task.RemapJarTask; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.download.Download; import net.fabricmc.loom.util.download.DownloadBuilder; @@ -339,4 +343,17 @@ public abstract class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl public boolean disableObfuscation() { return disableObfuscation.get(); } + + @Override + public void nestJars(TaskProvider jarTask, FileCollection jars) { + jarTask.configure(task -> { + if (task instanceof RemapJarTask remapJarTask) { + // For RemapJarTask, add to the nestedJars property + remapJarTask.getNestedJars().from(jars); + } else { + // For regular Jar tasks (non-remap mode), add a NestJarsAction with the FileCollection + task.doLast(new NestJarsAction(jars)); + } + }); + } } diff --git a/src/main/java/net/fabricmc/loom/task/NestJarsAction.java b/src/main/java/net/fabricmc/loom/task/NestJarsAction.java index 200ef520..54f52059 100644 --- a/src/main/java/net/fabricmc/loom/task/NestJarsAction.java +++ b/src/main/java/net/fabricmc/loom/task/NestJarsAction.java @@ -26,12 +26,12 @@ package net.fabricmc.loom.task; import java.io.File; import java.io.Serializable; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; import org.gradle.api.Action; import org.gradle.api.Task; -import org.gradle.api.file.Directory; -import org.gradle.api.provider.Provider; +import org.gradle.api.file.FileCollection; import org.gradle.jvm.tasks.Jar; import org.jetbrains.annotations.NotNull; @@ -39,38 +39,26 @@ import net.fabricmc.loom.build.nesting.JarNester; /** * Configuration-cache-compatible action for nesting jars. - * Uses a provider to avoid capturing task references at configuration time. + * Uses a FileCollection to avoid capturing task references at configuration time. * Do NOT turn me into a record! */ -class NestJarsAction implements Action, Serializable { - private final Provider nestedJarsDir; +public class NestJarsAction implements Action, Serializable { + private final FileCollection jars; - NestJarsAction(Provider nestedJarsDir) { - this.nestedJarsDir = nestedJarsDir; + public NestJarsAction(FileCollection jars) { + this.jars = jars; } @Override public void execute(@NotNull Task t) { final Jar jarTask = (Jar) t; final File jarFile = jarTask.getArchiveFile().get().getAsFile(); + final List allJars = new ArrayList<>(jars.getFiles()); - if (!nestedJarsDir.isPresent()) { - return; - } - - final File outputDir = nestedJarsDir.get().getAsFile(); - - if (outputDir.exists() && outputDir.isDirectory()) { - final File[] jars = outputDir.listFiles((dir, name) -> name.endsWith(".jar")); - - if (jars != null && jars.length > 0) { - JarNester.nestJars( - Arrays.asList(jars), - jarFile, - jarTask.getLogger() - ); - jarTask.getLogger().lifecycle("Nested {} jar(s) into {}", jars.length, jarFile.getName()); - } + // Nest all collected jars + if (!allJars.isEmpty()) { + JarNester.nestJars(allJars, jarFile, jarTask.getLogger()); + jarTask.getLogger().lifecycle("Nested {} jar(s) into {}", allJars.size(), jarFile.getName()); } } -} \ No newline at end of file +} diff --git a/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java b/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java index 56a9e00e..57b5f186 100644 --- a/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java +++ b/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java @@ -52,10 +52,11 @@ public class NonRemappedJarTaskConfiguration { project.getTasks().named(JavaPlugin.JAR_TASK_NAME, Jar.class).configure(task -> { task.dependsOn(processIncludeJarsTask); // Use JarNester to properly add jars and update fabric.mod.json - task.doLast(new NestJarsAction(processIncludeJarsTask.flatMap(NestableJarGenerationTask::getOutputDirectory))); + task.doLast(new NestJarsAction(project.fileTree(processIncludeJarsTask.flatMap(NestableJarGenerationTask::getOutputDirectory)) + .matching(pattern -> pattern.include("*.jar")))); }); // Add jar task to unmapped collection extension.getUnmappedModCollection().from(project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME)); } -} \ No newline at end of file +} diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/NestJarsApiTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/NestJarsApiTest.groovy new file mode 100644 index 00000000..2a880690 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/NestJarsApiTest.groovy @@ -0,0 +1,54 @@ +/* + * 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.integration + +import spock.lang.Specification +import spock.lang.Unroll + +import net.fabricmc.loom.test.util.GradleProjectTestTrait + +import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class NestJarsApiTest extends Specification implements GradleProjectTestTrait { + @Unroll + def "nest jars using loom.nestJars() API (gradle #version)"() { + setup: + def gradle = gradleProject(project: "nestJarsApi", version: version) + + when: + def result = gradle.run(tasks: ["remapJar"]) + + then: + result.task(":remapJar").outcome == SUCCESS + result.task(":createNestedMod").outcome == SUCCESS + + // Assert the locally built mod jar is nested using the new API + gradle.hasOutputZipEntry("nestJarsApi.jar", "META-INF/jars/nested-mod.jar") + + where: + version << STANDARD_TEST_VERSIONS + } +} \ No newline at end of file diff --git a/src/test/resources/projects/nestJarsApi/build.gradle b/src/test/resources/projects/nestJarsApi/build.gradle new file mode 100644 index 00000000..c230b03a --- /dev/null +++ b/src/test/resources/projects/nestJarsApi/build.gradle @@ -0,0 +1,30 @@ +plugins { + id 'fabric-loom' +} + +repositories { + mavenCentral() +} + +dependencies { + minecraft 'com.mojang:minecraft:1.20.1' + mappings loom.officialMojangMappings() +} + +// Create a simple mod jar file to nest +task createNestedMod(type: Jar) { + archiveBaseName = 'nested-mod' + destinationDirectory = layout.buildDirectory.dir('nested-mods') + + from('nested-mod-content') { + include 'fabric.mod.json' + } +} + +// Use the new nestJars API to nest the locally built jar +loom { + nestJars(tasks.named('remapJar'), files(createNestedMod.outputs.files)) +} + +// Make sure remapJar depends on the nested mod being created +tasks.remapJar.dependsOn(createNestedMod) \ No newline at end of file diff --git a/src/test/resources/projects/nestJarsApi/nested-mod-content/fabric.mod.json b/src/test/resources/projects/nestJarsApi/nested-mod-content/fabric.mod.json new file mode 100644 index 00000000..0c638f6c --- /dev/null +++ b/src/test/resources/projects/nestJarsApi/nested-mod-content/fabric.mod.json @@ -0,0 +1,8 @@ +{ + "schemaVersion": 1, + "id": "nestedmod", + "version": "1.0.0", + "name": "Nested Mod", + "description": "This mod will be nested using the nestJars API", + "environment": "*" +} \ No newline at end of file diff --git a/src/test/resources/projects/nestJarsApi/settings.gradle b/src/test/resources/projects/nestJarsApi/settings.gradle new file mode 100644 index 00000000..b242e076 --- /dev/null +++ b/src/test/resources/projects/nestJarsApi/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'nestJarsApi' \ No newline at end of file diff --git a/src/test/resources/projects/nestJarsApi/src/main/resources/fabric.mod.json b/src/test/resources/projects/nestJarsApi/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..92d3280f --- /dev/null +++ b/src/test/resources/projects/nestJarsApi/src/main/resources/fabric.mod.json @@ -0,0 +1,8 @@ +{ + "schemaVersion": 1, + "id": "mainmod", + "version": "1.0.0", + "name": "Main Mod", + "description": "Main mod that nests another mod using nestJars API", + "environment": "*" +} \ No newline at end of file From ec0026113618583ee2c58858d76f56fb71e0f5c1 Mon Sep 17 00:00:00 2001 From: modmuss Date: Fri, 7 Nov 2025 21:54:51 +0000 Subject: [PATCH 2/8] Find and apply installer data (#1428) --- .../configuration/DebofInstallerData.java | 82 +++++++++++++++++++ .../loom/configuration/InstallerData.java | 8 ++ .../configuration/LoomConfigurations.java | 5 ++ .../configuration/LoomDependencyManager.java | 2 +- .../configuration/mods/ArtifactMetadata.java | 10 +-- .../speccontext/DeobfSpecContext.java | 2 - .../noRemap/SimpleDebofTest.groovy | 3 + 7 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/configuration/DebofInstallerData.java diff --git a/src/main/java/net/fabricmc/loom/configuration/DebofInstallerData.java b/src/main/java/net/fabricmc/loom/configuration/DebofInstallerData.java new file mode 100644 index 00000000..84ee0f31 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/DebofInstallerData.java @@ -0,0 +1,82 @@ +/* + * 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; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Objects; +import java.util.Optional; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.fabricmc.loom.configuration.processors.speccontext.DebofConfiguration; +import net.fabricmc.loom.util.ZipUtils; +import net.fabricmc.loom.util.fmj.FabricModJson; +import net.fabricmc.loom.util.fmj.FabricModJsonFactory; + +public class DebofInstallerData { + private static final Logger LOGGER = LoggerFactory.getLogger(DebofInstallerData.class); + + public static void findAndApply(Project project) { + for (DebofConfiguration debofConfiguration : DebofConfiguration.ALL) { + for (Configuration configuration : debofConfiguration.getConfigurations(project)) { + Optional installerData = configuration.getFiles().parallelStream() + .map(DebofInstallerData::getInstaller) + .filter(Objects::nonNull) + .findFirst(); + + if (installerData.isPresent()) { + LOGGER.info("Applying installer data from configuration '{}'", configuration.getName()); + installerData.get().applyToProject(project); + return; + } + } + } + + LOGGER.info("No installer data found in any configuration."); + } + + @Nullable + private static InstallerData getInstaller(File file) { + try { + byte[] installerData = ZipUtils.unpackNullable(file.toPath(), InstallerData.INSTALLER_PATH); + + if (installerData == null) { + return null; + } + + FabricModJson fabricModJson = FabricModJsonFactory.createFromZip(file.toPath()); + LOGGER.info("Found installer in mod {} version {}", fabricModJson.getId(), fabricModJson.getModVersion()); + return InstallerData.fromBytes(installerData, fabricModJson.getModVersion()); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read " + file, e); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/InstallerData.java b/src/main/java/net/fabricmc/loom/configuration/InstallerData.java index ce8da78d..639e7b30 100644 --- a/src/main/java/net/fabricmc/loom/configuration/InstallerData.java +++ b/src/main/java/net/fabricmc/loom/configuration/InstallerData.java @@ -24,6 +24,8 @@ package net.fabricmc.loom.configuration; +import java.nio.charset.StandardCharsets; + import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -36,13 +38,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.LoomRepositoryPlugin; import net.fabricmc.loom.configuration.ide.idea.IdeaUtils; import net.fabricmc.loom.util.Constants; public record InstallerData(String version, JsonObject installerJson) { + public static final String INSTALLER_PATH = "fabric-installer.json"; private static final Logger LOGGER = LoggerFactory.getLogger(InstallerData.class); + public static InstallerData fromBytes(byte[] bytes, String version) { + return new InstallerData(version, LoomGradlePlugin.GSON.fromJson(new String(bytes, StandardCharsets.UTF_8), JsonObject.class)); + } + public void applyToProject(Project project) { LoomGradleExtension extension = LoomGradleExtension.get(project); diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java b/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java index fad2cb1a..c3c098ab 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java @@ -45,6 +45,7 @@ import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.provider.Provider; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.processors.speccontext.DebofConfiguration; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.LoomVersions; import net.fabricmc.loom.util.gradle.SourceSetHelper; @@ -161,6 +162,10 @@ public abstract class LoomConfigurations implements Runnable { extendsFrom(Constants.Configurations.MINECRAFT_TEST_CLIENT_RUNTIME_LIBRARIES, Constants.Configurations.LOADER_DEPENDENCIES); register(Constants.Configurations.PRODUCTION_RUNTIME_MODS, Role.RESOLVABLE); + + if (extension.disableObfuscation()) { + DebofConfiguration.create(getProject()); + } } private NamedDomainObjectProvider register(String name, Role role) { diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java index 17610851..2237933d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java @@ -56,6 +56,6 @@ public record LoomDependencyManager(Project project, ServiceFactory serviceFacto } private void handleNonRemapDependencies() { - // TODO debof - do we need to do anything? + DebofInstallerData.findAndApply(project); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ArtifactMetadata.java b/src/main/java/net/fabricmc/loom/configuration/mods/ArtifactMetadata.java index 4ee3c72d..de6e81fd 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ArtifactMetadata.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ArtifactMetadata.java @@ -26,7 +26,6 @@ package net.fabricmc.loom.configuration.mods; import java.io.ByteArrayInputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -37,18 +36,14 @@ import java.util.function.Predicate; import java.util.jar.Attributes; import java.util.jar.Manifest; -import com.google.gson.JsonObject; import org.jetbrains.annotations.Nullable; -import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.configuration.InstallerData; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.FileSystemUtil; import net.fabricmc.loom.util.fmj.FabricModJsonFactory; public record ArtifactMetadata(boolean isFabricMod, RemapRequirements remapRequirements, @Nullable InstallerData installerData, MixinRemapType mixinRemapType, List knownIdyBsms) { - private static final String INSTALLER_PATH = "fabric-installer.json"; - public static ArtifactMetadata create(ArtifactRef artifact, String currentLoomVersion) throws IOException { boolean isFabricMod; RemapRequirements remapRequirements = RemapRequirements.DEFAULT; @@ -90,11 +85,10 @@ public record ArtifactMetadata(boolean isFabricMod, RemapRequirements remapRequi } } - final Path installerPath = fs.getPath(INSTALLER_PATH); + final Path installerPath = fs.getPath(InstallerData.INSTALLER_PATH); if (isFabricMod && Files.exists(installerPath)) { - final JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(Files.readString(installerPath, StandardCharsets.UTF_8), JsonObject.class); - installerData = new InstallerData(artifact.version(), jsonObject); + installerData = InstallerData.fromBytes(Files.readAllBytes(installerPath), artifact.version()); } } 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 78b9b307..2982046f 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 @@ -53,8 +53,6 @@ public record DeobfSpecContext(List modDependencies, List modDependenciesCompileRuntimeClient ) implements SpecContext { public static DeobfSpecContext create(Project project) { - DebofConfiguration.create(project); - return create(new DeobfProjectView.Impl(project)); } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/SimpleDebofTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/SimpleDebofTest.groovy index 0f7fb59c..0574e411 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/SimpleDebofTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/SimpleDebofTest.groovy @@ -41,6 +41,7 @@ class SimpleDebofTest extends Specification implements GradleProjectTestTrait { gradle.buildGradle << ''' dependencies { minecraft 'com.mojang:minecraft:25w45a_unobfuscated' + implementation "net.fabricmc:fabric-loader:0.17.3" } ''' def sourceFile = new File(gradle.projectDir, "src/main/java/example/Test.java") @@ -50,6 +51,8 @@ class SimpleDebofTest extends Specification implements GradleProjectTestTrait { import net.minecraft.resources.Identifier; + import org.spongepowered.asm.mixin.Mixin; // Make sure we applied loaders deps via the installer data + public class Test { public static void main(String[] args) { Identifier id = Identifier.fromNamespaceAndPath("loom", "test"); From 43e1ad7b3169a17ee666b30e140077adcb42ee35 Mon Sep 17 00:00:00 2001 From: modmuss Date: Fri, 7 Nov 2025 22:07:13 +0000 Subject: [PATCH 3/8] Disable remap classpath system prop (#1426) * Disable remap classpath system prop * Fix build --- .../net/fabricmc/loom/task/LoomTasks.java | 132 +++++++++++------- .../task/launch/GenerateDLIConfigTask.java | 6 +- .../noRemap/SimpleDebofTest.groovy | 6 +- 3 files changed, 88 insertions(+), 56 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index 312d558d..c39023f2 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.java @@ -25,6 +25,7 @@ package net.fabricmc.loom.task; import java.io.File; +import java.util.Objects; import javax.inject.Inject; @@ -36,6 +37,7 @@ import org.gradle.api.tasks.Sync; import org.gradle.api.tasks.TaskContainer; import org.gradle.api.tasks.TaskOutputs; import org.gradle.api.tasks.TaskProvider; +import org.jetbrains.annotations.Nullable; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.ide.RunConfigSettings; @@ -60,6 +62,82 @@ public abstract class LoomTasks implements Runnable { @Override public void run() { + LoomGradleExtension extension = LoomGradleExtension.get(getProject()); + + if (!extension.disableObfuscation()) { + registerMigrateMappingsTasks(); + } + + var generateLog4jConfig = getTasks().register("generateLog4jConfig", GenerateLog4jConfigTask.class, t -> { + t.setDescription("Generate the log4j config file"); + }); + + @Nullable TaskProvider generateRemapClasspath = null; + + if (!extension.disableObfuscation()) { + generateRemapClasspath = getTasks().register("generateRemapClasspath", GenerateRemapClasspathTask.class, t -> { + t.setDescription("Generate the remap classpath file"); + }); + } + + // Make the lambda happy + final @Nullable TaskProvider generateRemapClasspathTask = generateRemapClasspath; + + getTasks().register("generateDLIConfig", GenerateDLIConfigTask.class, t -> { + t.setDescription("Generate the DevLaunchInjector config file"); + + // Must allow these IDE files to be generated first + t.mustRunAfter("eclipse"); + + t.dependsOn(generateLog4jConfig); + + if (!extension.disableObfuscation()) { + GenerateRemapClasspathTask remapClasspath = Objects.requireNonNull(generateRemapClasspathTask.get()); + t.getRemapClasspathFile().set(remapClasspath.getRemapClasspathFile()); + } + }); + + getTasks().register("configureLaunch", task -> { + task.dependsOn(getTasks().named("generateDLIConfig")); + task.dependsOn(getTasks().named("generateLog4jConfig")); + + if (!extension.disableObfuscation()) { + task.dependsOn(getTasks().named("generateRemapClasspath")); + } + + task.setDescription("Setup the required files to launch Minecraft"); + task.setGroup(Constants.TaskGroup.FABRIC); + }); + + TaskProvider validateAccessWidener = getTasks().register("validateAccessWidener", ValidateAccessWidenerTask.class, t -> { + t.setDescription("Validate all the rules in the access widener against the Minecraft jar"); + t.setGroup("verification"); + }); + + getTasks().named("check").configure(task -> task.dependsOn(validateAccessWidener)); + + registerIDETasks(); + registerRunTasks(); + + // Must be done in afterEvaluate to allow time for the build script to configure the jar config. + GradleUtils.afterSuccessfulEvaluation(getProject(), () -> { + if (extension.getMinecraftJarConfiguration().get() == MinecraftJarConfiguration.SERVER_ONLY) { + // Server only, nothing more to do. + return; + } + + final MinecraftVersionMeta versionInfo = extension.getMinecraftProvider().getVersionInfo(); + + if (versionInfo == null) { + // Something has gone wrong, don't register the task. + return; + } + + registerClientSetupTasks(getTasks(), versionInfo.hasNativesToExtract()); + }); + } + + private void registerMigrateMappingsTasks() { SourceSetHelper.getSourceSets(getProject()).all(sourceSet -> { if (SourceSetHelper.isMainSourceSet(sourceSet)) { getTasks().register("migrateMappings", MigrateMappingsTask.class, t -> { @@ -83,60 +161,6 @@ public abstract class LoomTasks implements Runnable { getTasks().register("migrateClassTweakerMappings", MigrateClassTweakerMappingsTask.class, t -> { t.setDescription("Migrates access widener and class tweaker mappings to a new version."); }); - - var generateLog4jConfig = getTasks().register("generateLog4jConfig", GenerateLog4jConfigTask.class, t -> { - t.setDescription("Generate the log4j config file"); - }); - var generateRemapClasspath = getTasks().register("generateRemapClasspath", GenerateRemapClasspathTask.class, t -> { - t.setDescription("Generate the remap classpath file"); - }); - getTasks().register("generateDLIConfig", GenerateDLIConfigTask.class, t -> { - t.setDescription("Generate the DevLaunchInjector config file"); - - // Must allow these IDE files to be generated first - t.mustRunAfter("eclipse"); - - t.dependsOn(generateLog4jConfig); - t.getRemapClasspathFile().set(generateRemapClasspath.get().getRemapClasspathFile()); - }); - - getTasks().register("configureLaunch", task -> { - task.dependsOn(getTasks().named("generateDLIConfig")); - task.dependsOn(getTasks().named("generateLog4jConfig")); - task.dependsOn(getTasks().named("generateRemapClasspath")); - - task.setDescription("Setup the required files to launch Minecraft"); - task.setGroup(Constants.TaskGroup.FABRIC); - }); - - TaskProvider validateAccessWidener = getTasks().register("validateAccessWidener", ValidateAccessWidenerTask.class, t -> { - t.setDescription("Validate all the rules in the access widener against the Minecraft jar"); - t.setGroup("verification"); - }); - - getTasks().named("check").configure(task -> task.dependsOn(validateAccessWidener)); - - registerIDETasks(); - registerRunTasks(); - - // Must be done in afterEvaluate to allow time for the build script to configure the jar config. - GradleUtils.afterSuccessfulEvaluation(getProject(), () -> { - LoomGradleExtension extension = LoomGradleExtension.get(getProject()); - - if (extension.getMinecraftJarConfiguration().get() == MinecraftJarConfiguration.SERVER_ONLY) { - // Server only, nothing more to do. - return; - } - - final MinecraftVersionMeta versionInfo = extension.getMinecraftProvider().getVersionInfo(); - - if (versionInfo == null) { - // Something has gone wrong, don't register the task. - return; - } - - registerClientSetupTasks(getTasks(), versionInfo.hasNativesToExtract()); - }); } private void registerIDETasks() { diff --git a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java index a6ab0b26..8e04873a 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java @@ -89,6 +89,7 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { protected abstract Property getNativesDirectoryPath(); @InputFile + @Optional public abstract RegularFileProperty getRemapClasspathFile(); @OutputFile @@ -128,7 +129,6 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { final LaunchConfig launchConfig = new LaunchConfig() .property("fabric.development", "true") - .property("fabric.remapClasspathFile", getRemapClasspathFile().get().getAsFile().getAbsolutePath()) .property("log4j.configurationFile", getLog4jConfigPaths().get()) .property("log4j2.formatMsgNoLookups", "true") @@ -137,6 +137,10 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { .argument("client", "--assetsDir") .argument("client", assetsDirectory.getAbsolutePath()); + if (getRemapClasspathFile().isPresent()) { + launchConfig.property("fabric.remapClasspathFile", getRemapClasspathFile().get().getAsFile().getAbsolutePath()); + } + if (versionInfo.hasNativesToExtract()) { String nativesPath = getNativesDirectoryPath().get(); diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/SimpleDebofTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/SimpleDebofTest.groovy index 0574e411..55e21070 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/SimpleDebofTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/SimpleDebofTest.groovy @@ -62,9 +62,13 @@ class SimpleDebofTest extends Specification implements GradleProjectTestTrait { sourceFile.text = src when: - def result = gradle.run(task: "build") + def result = gradle.run(tasks: [ + "build", + "configureClientLaunch" + ]) then: result.task(":build").outcome == SUCCESS + result.task(":configureClientLaunch").outcome == SUCCESS } } From f9dbaae926ca0eefdf36fceb41b3e1dad7486503 Mon Sep 17 00:00:00 2001 From: Finn Rades <64548817+zOnlyKroks@users.noreply.github.com> Date: Fri, 7 Nov 2025 23:40:08 +0100 Subject: [PATCH 4/8] Add manifest attributes and jar filtering to non-remapped jar task (#1429) * Add manifest attributes and jar filtering to non-remapped jar task * Fix checkstyle * Implement feedback --- .../loom/task/ManifestModificationAction.java | 111 ++++++++++++++++++ .../task/NonRemappedJarTaskConfiguration.java | 46 +++++++- .../noRemap/IncludedJarsNoRemapTest.groovy | 16 +++ 3 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/task/ManifestModificationAction.java diff --git a/src/main/java/net/fabricmc/loom/task/ManifestModificationAction.java b/src/main/java/net/fabricmc/loom/task/ManifestModificationAction.java new file mode 100644 index 00000000..328e1c7c --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/ManifestModificationAction.java @@ -0,0 +1,111 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016-2021 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.task; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.io.UncheckedIOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.jar.Manifest; + +import org.gradle.api.Action; +import org.gradle.api.Task; +import org.gradle.api.provider.Provider; +import org.gradle.jvm.tasks.Jar; +import org.jetbrains.annotations.NotNull; + +import net.fabricmc.loom.task.service.JarManifestService; +import net.fabricmc.loom.util.Check; +import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.ZipUtils; + +/** + * Action that modifies the manifest of a jar file to add Loom metadata. + * Configuration-cache-compatible implementation using providers. + */ +public class ManifestModificationAction implements Action, Serializable { + private final Provider manifestService; + private final String targetNamespace; + private final boolean areEnvironmentSourceSetsSplit; + private final List clientOnlyEntries; + + public ManifestModificationAction( + Provider manifestService, + String targetNamespace, + boolean areEnvironmentSourceSetsSplit, + List clientOnlyEntries) { + this.manifestService = manifestService; + this.targetNamespace = targetNamespace; + this.areEnvironmentSourceSetsSplit = areEnvironmentSourceSetsSplit; + this.clientOnlyEntries = clientOnlyEntries; + } + + @Override + public void execute(@NotNull Task t) { + final Jar jarTask = (Jar) t; + final File jarFile = jarTask.getArchiveFile().get().getAsFile(); + + try { + modifyManifest(jarFile); + } catch (IOException e) { + throw new UncheckedIOException("Failed to modify jar manifest for " + jarFile.getName(), e); + } + } + + private void modifyManifest(File jarFile) throws IOException { + Map manifestAttributes = new HashMap<>(); + + // Set the mapping namespace to "official" for non-remapped jars + manifestAttributes.put(Constants.Manifest.MAPPING_NAMESPACE, targetNamespace); + + // Set split environment flag if source sets are split (even for common-only jars) + if (areEnvironmentSourceSetsSplit) { + manifestAttributes.put(Constants.Manifest.SPLIT_ENV, "true"); + } + + // Add client-only entries list if present + if (clientOnlyEntries != null && !clientOnlyEntries.isEmpty()) { + manifestAttributes.put(Constants.Manifest.CLIENT_ENTRIES, String.join(";", clientOnlyEntries)); + } + + int count = ZipUtils.transform(jarFile.toPath(), Map.of(Constants.Manifest.PATH, bytes -> { + var manifest = new Manifest(new ByteArrayInputStream(bytes)); + + // Apply standard Loom manifest attributes (Gradle version, Loom version, etc.) + manifestService.get().apply(manifest, manifestAttributes); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + manifest.write(out); + return out.toByteArray(); + })); + + Check.require(count > 0, "Did not transform any jar manifest"); + } +} diff --git a/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java b/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java index 57b5f186..06f98843 100644 --- a/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java +++ b/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java @@ -24,13 +24,25 @@ package net.fabricmc.loom.task; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import org.gradle.api.Project; import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskProvider; import org.gradle.jvm.tasks.Jar; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.build.nesting.NestableJarGenerationTask; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; +import net.fabricmc.loom.task.service.ClientEntriesService; +import net.fabricmc.loom.task.service.JarManifestService; +import net.fabricmc.loom.util.gradle.SourceSetHelper; +import net.fabricmc.loom.util.service.ScopedServiceFactory; /** * Configures the jar task for non-remapped (non-obfuscated) output. @@ -48,15 +60,43 @@ public class NonRemappedJarTaskConfiguration { } public void configure() { - // No remapping needed - use simplified JIJ approach directly on jar task + final Provider manifestServiceProvider = JarManifestService.get(project); + project.getTasks().named(JavaPlugin.JAR_TASK_NAME, Jar.class).configure(task -> { task.dependsOn(processIncludeJarsTask); - // Use JarNester to properly add jars and update fabric.mod.json + task.doLast(new NestJarsAction(project.fileTree(processIncludeJarsTask.flatMap(NestableJarGenerationTask::getOutputDirectory)) .matching(pattern -> pattern.include("*.jar")))); + + task.doLast(new ManifestModificationAction( + manifestServiceProvider, + "official", + extension.areEnvironmentSourceSetsSplit(), + getClientOnlyEntries() + )); + + task.usesService(manifestServiceProvider); }); - // Add jar task to unmapped collection extension.getUnmappedModCollection().from(project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME)); } + + private List getClientOnlyEntries() { + if (!extension.areEnvironmentSourceSetsSplit()) { + return Collections.emptyList(); + } + + final SourceSet clientSourceSet = SourceSetHelper.getSourceSetByName( + MinecraftSourceSets.Split.CLIENT_ONLY_SOURCE_SET_NAME, + project + ); + final Provider optionsProvider = ClientEntriesService.Classes.createOptions(project, clientSourceSet); + + try (var serviceFactory = new ScopedServiceFactory()) { + ClientEntriesService service = serviceFactory.get(optionsProvider); + return new ArrayList<>(service.getClientOnlyEntries()); + } catch (IOException e) { + throw new RuntimeException("Failed to determine client-only entries", e); + } + } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/IncludedJarsNoRemapTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/IncludedJarsNoRemapTest.groovy index 8a1c78ae..63492178 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/IncludedJarsNoRemapTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/IncludedJarsNoRemapTest.groovy @@ -24,6 +24,8 @@ package net.fabricmc.loom.test.integration.noRemap +import java.util.jar.Manifest + import spock.lang.Specification import spock.lang.Unroll @@ -52,6 +54,20 @@ class IncludedJarsNoRemapTest extends Specification implements GradleProjectTest !gradle.hasOutputZipEntry("includedJars.jar", "META-INF/jars/log4j-api-2.22.0.jar") !gradle.hasOutputZipEntry("includedJars.jar", "META-INF/jars/adventure-api-4.14.0.jar") + // Verify manifest attributes + def manifestContent = gradle.getOutputZipEntry("includedJars.jar", "META-INF/MANIFEST.MF") + def manifest = new Manifest(new ByteArrayInputStream(manifestContent.bytes)) + def attributes = manifest.getMainAttributes() + + // Check that the namespace is set to "official" for non-remapped jars + attributes.getValue("Fabric-Mapping-Namespace") == "official" + + // Check that Loom metadata is present + attributes.getValue("Fabric-Gradle-Version") != null + attributes.getValue("Fabric-Loom-Version") != null + attributes.getValue("Fabric-Minecraft-Version") != null + attributes.getValue("Fabric-Tiny-Remapper-Version") != null + where: version << STANDARD_TEST_VERSIONS } From 03d4fd077b74f2a44ac44527467dbddf8c799908 Mon Sep 17 00:00:00 2001 From: modmuss Date: Sat, 8 Nov 2025 09:04:40 +0000 Subject: [PATCH 5/8] Very many debof fixes (#1430) --- .../vineflower/VineflowerDecompiler.java | 7 ++- .../configuration/CompileConfiguration.java | 5 ++ .../configuration/LoomConfigurations.java | 5 -- .../configuration/RemapConfigurations.java | 5 ++ .../fabricapi/FabricApiAbstractSourceSet.java | 4 +- .../speccontext/DebofConfiguration.java | 7 ++- .../speccontext/DeobfSpecContext.java | 15 ++++- .../extension/LoomGradleExtensionImpl.java | 1 + .../loom/task/GenerateSourcesTask.java | 33 ++++++---- .../loom/task/ManifestModificationAction.java | 14 ++--- .../task/NonRemappedJarTaskConfiguration.java | 4 +- .../noRemap/SimpleDebofTest.groovy | 60 +++++++++++++++++++ 12 files changed, 130 insertions(+), 30 deletions(-) diff --git a/src/decompilers/vineflower/net/fabricmc/loom/decompilers/vineflower/VineflowerDecompiler.java b/src/decompilers/vineflower/net/fabricmc/loom/decompilers/vineflower/VineflowerDecompiler.java index ad865d73..40f14d41 100644 --- a/src/decompilers/vineflower/net/fabricmc/loom/decompilers/vineflower/VineflowerDecompiler.java +++ b/src/decompilers/vineflower/net/fabricmc/loom/decompilers/vineflower/VineflowerDecompiler.java @@ -48,11 +48,14 @@ public final class VineflowerDecompiler implements LoomInternalDecompiler { IFernflowerPreferences.REMOVE_SYNTHETIC, "1", IFernflowerPreferences.LOG_LEVEL, "trace", IFernflowerPreferences.THREADS, String.valueOf(context.numberOfThreads()), - IFernflowerPreferences.INDENT_STRING, "\t", - IFabricJavadocProvider.PROPERTY_NAME, new TinyJavadocProvider(context.javaDocs().toFile()) + IFernflowerPreferences.INDENT_STRING, "\t" ) ); + if (context.javaDocs() != null) { + options.put(IFabricJavadocProvider.PROPERTY_NAME, new TinyJavadocProvider(context.javaDocs().toFile())); + } + options.putAll(context.options()); IResultSaver saver = new ThreadSafeResultSaver(sourcesDestination::toFile, linemapDestination::toFile); diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index fe8a640a..f34be070 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -63,6 +63,7 @@ import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor; import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor; import net.fabricmc.loom.configuration.processors.MinecraftJarProcessorManager; import net.fabricmc.loom.configuration.processors.ModJavadocProcessor; +import net.fabricmc.loom.configuration.processors.speccontext.DebofConfiguration; import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsFactory; import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMetadataProvider; @@ -103,6 +104,10 @@ public abstract class CompileConfiguration implements Runnable { afterEvaluationWithService((serviceFactory) -> { final ConfigContext configContext = new ConfigContextImpl(getProject(), serviceFactory, extension); + if (extension.disableObfuscation()) { + DebofConfiguration.create(getProject()); + } + MinecraftSourceSets.get(getProject()).afterEvaluate(getProject()); final boolean previousRefreshDeps = extension.refreshDeps(); diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java b/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java index c3c098ab..fad2cb1a 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java @@ -45,7 +45,6 @@ import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.provider.Provider; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.configuration.processors.speccontext.DebofConfiguration; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.LoomVersions; import net.fabricmc.loom.util.gradle.SourceSetHelper; @@ -162,10 +161,6 @@ public abstract class LoomConfigurations implements Runnable { extendsFrom(Constants.Configurations.MINECRAFT_TEST_CLIENT_RUNTIME_LIBRARIES, Constants.Configurations.LOADER_DEPENDENCIES); register(Constants.Configurations.PRODUCTION_RUNTIME_MODS, Role.RESOLVABLE); - - if (extension.disableObfuscation()) { - DebofConfiguration.create(getProject()); - } } private NamedDomainObjectProvider register(String name, Role role) { diff --git a/src/main/java/net/fabricmc/loom/configuration/RemapConfigurations.java b/src/main/java/net/fabricmc/loom/configuration/RemapConfigurations.java index 03349991..e0607b33 100644 --- a/src/main/java/net/fabricmc/loom/configuration/RemapConfigurations.java +++ b/src/main/java/net/fabricmc/loom/configuration/RemapConfigurations.java @@ -73,6 +73,11 @@ public final class RemapConfigurations { public static void configureClientConfigurations(Project project, SourceSet clientSourceSet) { final LoomGradleExtension extension = LoomGradleExtension.get(project); + + if (extension.disableObfuscation()) { + return; + } + extension.createRemapConfigurations(clientSourceSet); final NamedDomainObjectList configurations = extension.getRemapConfigurations(); diff --git a/src/main/java/net/fabricmc/loom/configuration/fabricapi/FabricApiAbstractSourceSet.java b/src/main/java/net/fabricmc/loom/configuration/fabricapi/FabricApiAbstractSourceSet.java index 9be2291d..3f74f279 100644 --- a/src/main/java/net/fabricmc/loom/configuration/fabricapi/FabricApiAbstractSourceSet.java +++ b/src/main/java/net/fabricmc/loom/configuration/fabricapi/FabricApiAbstractSourceSet.java @@ -76,7 +76,9 @@ abstract class FabricApiAbstractSourceSet { mod.sourceSet(getSourceSetName()); }); - extension.createRemapConfigurations(sourceSets.getByName(getSourceSetName())); + if (!extension.disableObfuscation()) { + extension.createRemapConfigurations(sourceSets.getByName(getSourceSetName())); + } return sourceSet; } diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/DebofConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/DebofConfiguration.java index cca90f81..16fcb11b 100644 --- a/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/DebofConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/DebofConfiguration.java @@ -87,7 +87,12 @@ public record DebofConfiguration(String name, List> LoomConfigurations.Role.RESOLVABLE.apply(c); for (Function configProvider : configurationFunctions()) { - Configuration sourceConfig = configurations.getByName(configProvider.apply(sourceSet)); + Configuration sourceConfig = configurations.findByName(configProvider.apply(sourceSet)); + + if (sourceConfig == null) { + continue; + } + c.extendsFrom(sourceConfig); } }); 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 2982046f..750394a7 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 @@ -25,12 +25,15 @@ package net.fabricmc.loom.configuration.processors.speccontext; import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -101,7 +104,7 @@ public record DeobfSpecContext(List modDependencies, for (File artifact : artifacts) { futures.add(fmjCache.get(artifact.toPath().toAbsolutePath().toString(), () -> { - return FabricModJsonFactory.createFromZipOptional(artifact.toPath()) + return getMod(artifact.toPath()) .map(List::of) .orElseGet(List::of); })); @@ -125,7 +128,7 @@ public record DeobfSpecContext(List modDependencies, for (File artifact : artifacts) { futures.add(fmjCache.get(artifact.toPath().toAbsolutePath().toString(), () -> { - return FabricModJsonFactory.createFromZipOptional(artifact.toPath()) + return getMod(artifact.toPath()) .map(List::of) .orElseGet(List::of); })); @@ -136,6 +139,14 @@ public record DeobfSpecContext(List modDependencies, .collect(HashSet::new, Set::add, Set::addAll); } + private static Optional getMod(Path path) { + if (Files.isRegularFile(path)) { + return FabricModJsonFactory.createFromZipOptional(path); + } + + return Optional.empty(); + } + private static List getMods(Map mods, Set ids) { List result = new ArrayList<>(); diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index ec07da21..08cbb2d6 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -174,6 +174,7 @@ public abstract class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl @Override public MappingConfiguration getMappingConfiguration() { if (disableObfuscation()) { + project.getLogger().lifecycle("help", new RuntimeException()); throw new UnsupportedOperationException("Cannot get mappings configuration in a non-obfuscated environment"); } diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index d1565202..1d7ada8f 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -70,6 +70,7 @@ import org.gradle.workers.internal.WorkerDaemonClientsManager; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; +import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.decompilers.DecompilationMetadata; import net.fabricmc.loom.api.decompilers.DecompilerOptions; import net.fabricmc.loom.api.decompilers.LoomDecompiler; @@ -140,6 +141,7 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { // Internal inputs @ApiStatus.Internal @Nested + @Optional protected abstract Property getMappings(); // Internal outputs @@ -221,15 +223,16 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { getUseCache().convention(true); getResetCache().convention(getExtension().refreshDeps()); - getMappings().set(SourceMappingsService.create(getProject())); + if (!LoomGradleExtension.get(getProject()).disableObfuscation()) { + getMappings().set(SourceMappingsService.create(getProject())); + getUnpickOptions().set(UnpickService.createOptions(this)); + } getMaxCachedFiles().set(GradleUtils.getIntegerPropertyProvider(getProject(), Constants.Properties.DECOMPILE_CACHE_MAX_FILES).orElse(50_000)); getMaxCacheFileAge().set(GradleUtils.getIntegerPropertyProvider(getProject(), Constants.Properties.DECOMPILE_CACHE_MAX_AGE).orElse(90)); getDaemonUtilsContext().set(getProject().getObjects().newInstance(DaemonUtils.Context.class, getProject())); - getUnpickOptions().set(UnpickService.createOptions(this)); - mustRunAfter(getProject().getTasks().withType(AbstractRemapJarTask.class)); } @@ -407,11 +410,13 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { sj.add(unpick.getUnpickCacheKey()); } - SourceMappingsService mappingsService = serviceFactory.get(getMappings()); - String mappingsHash = mappingsService.getProcessorHash(); + if (getMappings().isPresent()) { + SourceMappingsService mappingsService = serviceFactory.get(getMappings()); + String mappingsHash = mappingsService.getProcessorHash(); - if (mappingsHash != null) { - sj.add(mappingsHash); + if (mappingsHash != null) { + sj.add(mappingsHash); + } } getLogger().info("Decompile cache data: {}", sj); @@ -484,7 +489,10 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { params.getInputJar().set(inputJar.toFile()); params.getOutputJar().set(outputJar.toFile()); params.getLinemapFile().set(linemapFile.toFile()); - params.getMappings().set(getMappings()); + + if (getMappings().isPresent()) { + params.getMappings().set(getMappings()); + } if (ipcServer != null) { params.getIPCPath().set(ipcServer.getPath().toFile()); @@ -587,11 +595,16 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { } try (var serviceFactory = new ScopedServiceFactory()) { - final SourceMappingsService mappingsService = serviceFactory.get(getParameters().getMappings()); + Path javaDocs = null; + + if (getParameters().getMappings().isPresent()) { + final SourceMappingsService mappingsService = serviceFactory.get(getParameters().getMappings()); + javaDocs = mappingsService.getMappingsFile(); + } final var metadata = new DecompilationMetadata( decompilerOptions.maxThreads(), - mappingsService.getMappingsFile(), + javaDocs, getLibraries(), logger, decompilerOptions.options() diff --git a/src/main/java/net/fabricmc/loom/task/ManifestModificationAction.java b/src/main/java/net/fabricmc/loom/task/ManifestModificationAction.java index 328e1c7c..841773bf 100644 --- a/src/main/java/net/fabricmc/loom/task/ManifestModificationAction.java +++ b/src/main/java/net/fabricmc/loom/task/ManifestModificationAction.java @@ -53,14 +53,14 @@ import net.fabricmc.loom.util.ZipUtils; public class ManifestModificationAction implements Action, Serializable { private final Provider manifestService; private final String targetNamespace; - private final boolean areEnvironmentSourceSetsSplit; - private final List clientOnlyEntries; + private final Provider areEnvironmentSourceSetsSplit; + private final Provider> clientOnlyEntries; public ManifestModificationAction( Provider manifestService, String targetNamespace, - boolean areEnvironmentSourceSetsSplit, - List clientOnlyEntries) { + Provider areEnvironmentSourceSetsSplit, + Provider> clientOnlyEntries) { this.manifestService = manifestService; this.targetNamespace = targetNamespace; this.areEnvironmentSourceSetsSplit = areEnvironmentSourceSetsSplit; @@ -86,13 +86,13 @@ public class ManifestModificationAction implements Action, Serializable { manifestAttributes.put(Constants.Manifest.MAPPING_NAMESPACE, targetNamespace); // Set split environment flag if source sets are split (even for common-only jars) - if (areEnvironmentSourceSetsSplit) { + if (areEnvironmentSourceSetsSplit.get()) { manifestAttributes.put(Constants.Manifest.SPLIT_ENV, "true"); } // Add client-only entries list if present - if (clientOnlyEntries != null && !clientOnlyEntries.isEmpty()) { - manifestAttributes.put(Constants.Manifest.CLIENT_ENTRIES, String.join(";", clientOnlyEntries)); + if (clientOnlyEntries != null && !clientOnlyEntries.get().isEmpty()) { + manifestAttributes.put(Constants.Manifest.CLIENT_ENTRIES, String.join(";", clientOnlyEntries.get())); } int count = ZipUtils.transform(jarFile.toPath(), Map.of(Constants.Manifest.PATH, bytes -> { diff --git a/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java b/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java index 06f98843..fb2218f1 100644 --- a/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java +++ b/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java @@ -71,8 +71,8 @@ public class NonRemappedJarTaskConfiguration { task.doLast(new ManifestModificationAction( manifestServiceProvider, "official", - extension.areEnvironmentSourceSetsSplit(), - getClientOnlyEntries() + project.provider(extension::areEnvironmentSourceSetsSplit), + project.provider(this::getClientOnlyEntries) )); task.usesService(manifestServiceProvider); diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/SimpleDebofTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/SimpleDebofTest.groovy index 55e21070..2f5ee992 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/SimpleDebofTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/noRemap/SimpleDebofTest.groovy @@ -71,4 +71,64 @@ class SimpleDebofTest extends Specification implements GradleProjectTestTrait { result.task(":build").outcome == SUCCESS result.task(":configureClientLaunch").outcome == SUCCESS } + + @Unroll + def "split build"() { + setup: + def gradle = gradleProject(project: "minimalBaseNoRemap", version: PRE_RELEASE_GRADLE) + gradle.buildGradle << ''' + loom { + splitEnvironmentSourceSets() + } + + dependencies { + minecraft 'com.mojang:minecraft:25w45a_unobfuscated' + implementation "net.fabricmc:fabric-loader:0.17.3" + } + ''' + def sourceFile = new File(gradle.projectDir, "src/main/java/example/Test.java") + sourceFile.parentFile.mkdirs() + @Language("JAVA") String src = """ + package example; + + import net.minecraft.resources.Identifier; + + import org.spongepowered.asm.mixin.Mixin; // Make sure we applied loaders deps via the installer data + + public class Test { + public static void main(String[] args) { + Identifier id = Identifier.fromNamespaceAndPath("loom", "test"); + } + } + """ + sourceFile.text = src + + when: + def result = gradle.run(task: "build") + + then: + result.task(":build").outcome == SUCCESS + } + + @Unroll + def "genSources split build"() { + setup: + def gradle = gradleProject(project: "minimalBaseNoRemap", version: PRE_RELEASE_GRADLE) + gradle.buildGradle << ''' + loom { + splitEnvironmentSourceSets() + } + + dependencies { + minecraft 'com.mojang:minecraft:25w45a_unobfuscated' + implementation "net.fabricmc:fabric-loader:0.17.3" + } + ''' + + when: + def result = gradle.run(task: "genSources") + + then: + result.task(":genSources").outcome == SUCCESS + } } From 549edb7ad98a993a2364cb224f290d5f28cc8bd8 Mon Sep 17 00:00:00 2001 From: modmuss Date: Sat, 8 Nov 2025 23:20:06 +0000 Subject: [PATCH 6/8] Remove "namedElements" when debof (#1435) --- .../loom/configuration/CompileConfiguration.java | 6 ++++-- .../loom/configuration/LoomConfigurations.java | 16 ++++++++-------- .../loom/util/gradle/SourceSetHelper.java | 4 +++- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index f34be070..2ba1575b 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -149,8 +149,10 @@ public abstract class CompileConfiguration implements Runnable { finalizedBy("eclipse", "genEclipseRuns"); - // Add the "dev" jar to the "namedElements" configuration - getProject().artifacts(artifactHandler -> artifactHandler.add(Configurations.NAMED_ELEMENTS, getTasks().named("jar"))); + if (!extension.disableObfuscation()) { + // Add the "dev" jar to the "namedElements" configuration + getProject().artifacts(artifactHandler -> artifactHandler.add(Configurations.NAMED_ELEMENTS, getTasks().named("jar"))); + } // Ensure that the encoding is set to UTF-8, no matter what the system default is // this fixes some edge cases with special characters not displaying correctly diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java b/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java index fad2cb1a..92b166e0 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java @@ -126,15 +126,15 @@ public abstract class LoomConfigurations implements Runnable { }); }); - registerNonTransitive(Constants.Configurations.MAPPING_CONSTANTS, Role.RESOLVABLE); - - register(Constants.Configurations.NAMED_ELEMENTS, Role.CONSUMABLE).configure(configuration -> { - configuration.extendsFrom(getConfigurations().getByName(JavaPlugin.API_CONFIGURATION_NAME)); - }); - - extendsFrom(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME, Constants.Configurations.MAPPING_CONSTANTS); - if (!extension.disableObfuscation()) { + registerNonTransitive(Constants.Configurations.MAPPING_CONSTANTS, Role.RESOLVABLE); + + register(Constants.Configurations.NAMED_ELEMENTS, Role.CONSUMABLE).configure(configuration -> { + configuration.extendsFrom(getConfigurations().getByName(JavaPlugin.API_CONFIGURATION_NAME)); + }); + + extendsFrom(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME, Constants.Configurations.MAPPING_CONSTANTS); + register(Constants.Configurations.MAPPINGS, Role.RESOLVABLE); register(Constants.Configurations.MAPPINGS_FINAL, Role.RESOLVABLE); extendsFrom(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.MAPPINGS_FINAL); diff --git a/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java b/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java index fa5a66aa..8560a4e9 100644 --- a/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java +++ b/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java @@ -146,7 +146,9 @@ public final class SourceSetHelper { // Add dev jars from dependency projects if the source set is "main". if (forExport && SourceSet.MAIN_SOURCE_SET_NAME.equals(reference.sourceSet().getName()) && GradleUtils.isLoomCompanionProject(reference.project())) { - String configurationName = GradleUtils.isLoomProject(reference.project()) + boolean isLoom = GradleUtils.isLoomProject(reference.project()); + boolean isDebof = isLoom && LoomGradleExtension.get(reference.project()).disableObfuscation(); + String configurationName = isLoom && !isDebof ? Constants.Configurations.NAMED_ELEMENTS : JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME; final Configuration namedElements = reference.project().getConfigurations().getByName(configurationName); From 9ad97b74b9158399ef3cbaff29e779bfcdd277fa Mon Sep 17 00:00:00 2001 From: modmuss Date: Sun, 9 Nov 2025 09:45:21 +0000 Subject: [PATCH 7/8] Fix project deps (#1437) --- .../speccontext/DeobfProjectView.java | 10 +++++ .../speccontext/DeobfSpecContext.java | 38 ++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/DeobfProjectView.java b/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/DeobfProjectView.java index a2788cdf..e3214348 100644 --- a/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/DeobfProjectView.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/speccontext/DeobfProjectView.java @@ -24,6 +24,8 @@ package net.fabricmc.loom.configuration.processors.speccontext; +import java.util.stream.Stream; + import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.ConfigurableFileCollection; @@ -32,6 +34,8 @@ import org.gradle.api.file.FileCollection; public interface DeobfProjectView extends ProjectView { FileCollection getDependencies(DebofConfiguration debofConfiguration, DebofConfiguration.TargetSourceSet targetSourceSet); + Stream getProjectDependencies(DebofConfiguration debofConfiguration); + FileCollection getFullClasspath(); class Impl extends AbstractProjectView implements DeobfProjectView { @@ -44,6 +48,12 @@ public interface DeobfProjectView extends ProjectView { return debofConfiguration.getConfiguration(project, targetSourceSet); } + @Override + public Stream getProjectDependencies(DebofConfiguration debofConfiguration) { + return debofConfiguration.getConfigurations(project).stream() + .flatMap(configuration -> getLoomProjectDependencies(configuration.getName())); + } + @Override public FileCollection getFullClasspath() { ConfigurableFileCollection classpath = project.files(); 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 750394a7..c18d067c 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 @@ -28,6 +28,7 @@ import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -36,6 +37,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; import org.gradle.api.Project; import org.gradle.api.file.FileCollection; @@ -89,10 +91,16 @@ public record DeobfSpecContext(List modDependencies, clientTransformingModIds = Set.of(); } + // All dependency mods that are on both the compile and runtime classpath + List modDependenciesCompileRuntime = new ArrayList<>(getMods(mods, combine(mainTransformingModIds, clientTransformingModIds))); + + // Add all of the project depedencies that are on both the compile and runtime classpath + modDependenciesCompileRuntime.addAll(getCompileRuntimeProjectMods(projectView, fmjCache)); + return new DeobfSpecContext( dependentMods, projectView.getMods(), - getMods(mods, combine(mainTransformingModIds, clientTransformingModIds)), + modDependenciesCompileRuntime, getMods(mods, onlyInLeft(clientTransformingModIds, mainTransformingModIds)) ); } @@ -157,6 +165,34 @@ public record DeobfSpecContext(List modDependencies, return result; } + // Returns a list of mods that are on both to compile and runtime classpath + private static List getCompileRuntimeProjectMods(DeobfProjectView projectView, AsyncCache> fmjCache) { + var mods = new ArrayList(); + + for (Project dependentProject : getCompileRuntimeProjectDependencies(projectView).toList()) { + List projectMods = fmjCache.getBlocking(dependentProject.getPath(), () -> { + return FabricModJsonHelpers.getModsInProject(dependentProject); + }); + + mods.addAll(projectMods); + } + + return Collections.unmodifiableList(mods); + } + + // Returns a list of Loom Projects found in both the runtime and compile classpath + private static Stream getCompileRuntimeProjectDependencies(DeobfProjectView projectView) { + if (projectView.disableProjectDependantMods()) { + return Stream.empty(); + } + + final Stream runtimeProjects = projectView.getProjectDependencies(DebofConfiguration.RUNTIME); + final List compileProjects = projectView.getProjectDependencies(DebofConfiguration.COMPILE).toList(); + + return runtimeProjects + .filter(compileProjects::contains); // Use the intersection of the two configurations. + } + private static Set common(Set a, Set b) { Set copy = new HashSet<>(a); copy.retainAll(b); From 91d2edefbfd93d74cd50511efc7ec14ecdcdf89e Mon Sep 17 00:00:00 2001 From: modmuss Date: Sun, 9 Nov 2025 10:40:18 +0000 Subject: [PATCH 8/8] NestJarsAction improvements (#1438) --- .../extension/LoomGradleExtensionImpl.java | 2 +- .../fabricmc/loom/task/NestJarsAction.java | 61 +++++++++++++++---- .../task/NonRemappedJarTaskConfiguration.java | 4 +- 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index 08cbb2d6..23542e77 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -353,7 +353,7 @@ public abstract class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl remapJarTask.getNestedJars().from(jars); } else { // For regular Jar tasks (non-remap mode), add a NestJarsAction with the FileCollection - task.doLast(new NestJarsAction(jars)); + NestJarsAction.addToTask(task, jars); } }); } diff --git a/src/main/java/net/fabricmc/loom/task/NestJarsAction.java b/src/main/java/net/fabricmc/loom/task/NestJarsAction.java index 54f52059..4715408a 100644 --- a/src/main/java/net/fabricmc/loom/task/NestJarsAction.java +++ b/src/main/java/net/fabricmc/loom/task/NestJarsAction.java @@ -26,14 +26,24 @@ package net.fabricmc.loom.task; import java.io.File; import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; +import java.util.Set; + +import javax.inject.Inject; import org.gradle.api.Action; import org.gradle.api.Task; +import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.FileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.InputFiles; import org.gradle.jvm.tasks.Jar; +import org.gradle.workers.WorkAction; +import org.gradle.workers.WorkParameters; +import org.gradle.workers.WorkQueue; +import org.gradle.workers.WorkerExecutor; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import net.fabricmc.loom.build.nesting.JarNester; @@ -42,23 +52,50 @@ import net.fabricmc.loom.build.nesting.JarNester; * Uses a FileCollection to avoid capturing task references at configuration time. * Do NOT turn me into a record! */ -public class NestJarsAction implements Action, Serializable { - private final FileCollection jars; +public abstract class NestJarsAction implements Action, Serializable { + @InputFiles + public abstract ConfigurableFileCollection getJars(); - public NestJarsAction(FileCollection jars) { - this.jars = jars; + @Inject + protected abstract WorkerExecutor getWorkerExecutor(); + + public static void addToTask(Jar task, FileCollection jars) { + NestJarsAction nestJarsAction = task.getProject().getObjects().newInstance(NestJarsAction.class); + nestJarsAction.getJars().from(jars); + task.getInputs().files(nestJarsAction.getJars()); // I don't think @InputFiles works, so to be sure add the jars to the task input anyway. + task.doLast(nestJarsAction); } @Override public void execute(@NotNull Task t) { final Jar jarTask = (Jar) t; - final File jarFile = jarTask.getArchiveFile().get().getAsFile(); - final List allJars = new ArrayList<>(jars.getFiles()); - // Nest all collected jars - if (!allJars.isEmpty()) { - JarNester.nestJars(allJars, jarFile, jarTask.getLogger()); - jarTask.getLogger().lifecycle("Nested {} jar(s) into {}", allJars.size(), jarFile.getName()); + final WorkQueue workQueue = getWorkerExecutor().noIsolation(); + + workQueue.submit(NestAction.class, p -> { + p.getArchiveFile().set(jarTask.getArchiveFile()); + p.getJars().setFrom(getJars()); + }); + } + + public interface NestJarsParameters extends WorkParameters { + RegularFileProperty getArchiveFile(); + ConfigurableFileCollection getJars(); + } + + public abstract static class NestAction implements WorkAction { + private static final Logger LOGGER = LoggerFactory.getLogger(NestJarsAction.class); + + @Override + public void execute() { + final File jarFile = getParameters().getArchiveFile().get().getAsFile(); + final Set jars = getParameters().getJars().getFiles(); + + // Nest all collected jars + if (!jars.isEmpty()) { + JarNester.nestJars(jars, jarFile, LOGGER); + LOGGER.info("Nested {} jar(s) into {}", jars.size(), jarFile.getName()); + } } } } diff --git a/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java b/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java index fb2218f1..4fc6fad6 100644 --- a/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java +++ b/src/main/java/net/fabricmc/loom/task/NonRemappedJarTaskConfiguration.java @@ -65,8 +65,8 @@ public class NonRemappedJarTaskConfiguration { project.getTasks().named(JavaPlugin.JAR_TASK_NAME, Jar.class).configure(task -> { task.dependsOn(processIncludeJarsTask); - task.doLast(new NestJarsAction(project.fileTree(processIncludeJarsTask.flatMap(NestableJarGenerationTask::getOutputDirectory)) - .matching(pattern -> pattern.include("*.jar")))); + NestJarsAction.addToTask(task, project.fileTree(processIncludeJarsTask.flatMap(NestableJarGenerationTask::getOutputDirectory)) + .matching(pattern -> pattern.include("*.jar"))); task.doLast(new ManifestModificationAction( manifestServiceProvider,