mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-30 05:05:20 -05:00
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
This commit is contained in:
@@ -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.
|
||||
*
|
||||
* <p>Important: The jars must already be valid mod jars (containing a fabric.mod.json file).
|
||||
* Non-mod jars will be rejected.
|
||||
*
|
||||
* <p>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<? extends Jar> jarTask, FileCollection jars);
|
||||
}
|
||||
|
||||
@@ -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<? extends Jar> jarTask, FileCollection jars) {
|
||||
throw new RuntimeException("Yeah... something is really wrong");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<? extends Jar> 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));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Task>, Serializable {
|
||||
private final Provider<Directory> nestedJarsDir;
|
||||
public class NestJarsAction implements Action<Task>, Serializable {
|
||||
private final FileCollection jars;
|
||||
|
||||
NestJarsAction(Provider<Directory> 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<File> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
30
src/test/resources/projects/nestJarsApi/build.gradle
Normal file
30
src/test/resources/projects/nestJarsApi/build.gradle
Normal file
@@ -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)
|
||||
@@ -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": "*"
|
||||
}
|
||||
1
src/test/resources/projects/nestJarsApi/settings.gradle
Normal file
1
src/test/resources/projects/nestJarsApi/settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
rootProject.name = 'nestJarsApi'
|
||||
@@ -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": "*"
|
||||
}
|
||||
Reference in New Issue
Block a user