mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-28 04:07:01 -05:00
Merge commit '91d2edef' into dev/1.14
# Conflicts: # src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java # src/main/java/net/fabricmc/loom/configuration/mods/ArtifactMetadata.java # src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java # src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java # src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java # src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -41,6 +41,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;
|
||||
@@ -334,4 +336,26 @@ public interface LoomGradleExtensionAPI {
|
||||
NeoForgeExtensionAPI getNeoForge();
|
||||
|
||||
void neoForge(Action<NeoForgeExtensionAPI> action);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor;
|
||||
import net.fabricmc.loom.configuration.mods.ModConfigurationRemapper;
|
||||
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;
|
||||
@@ -121,6 +122,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();
|
||||
@@ -181,8 +186,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
|
||||
|
||||
@@ -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> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -127,15 +127,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);
|
||||
|
||||
@@ -61,6 +61,6 @@ public record LoomDependencyManager(Project project, ServiceFactory serviceFacto
|
||||
}
|
||||
|
||||
private void handleNonRemapDependencies() {
|
||||
// TODO debof - do we need to do anything?
|
||||
DebofInstallerData.findAndApply(project);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<RemapConfigurationSettings> configurations = extension.getRemapConfigurations();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -125,8 +124,7 @@ public record ArtifactMetadata(boolean isFabricMod, RemapRequirements remapRequi
|
||||
final Path installerPath = fs.getPath(installerFile);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,12 @@ public record DebofConfiguration(String name, List<Function<SourceSet, String>>
|
||||
LoomConfigurations.Role.RESOLVABLE.apply(c);
|
||||
|
||||
for (Function<SourceSet, String> configProvider : configurationFunctions()) {
|
||||
Configuration sourceConfig = configurations.getByName(configProvider.apply(sourceSet));
|
||||
Configuration sourceConfig = configurations.findByName(configProvider.apply(sourceSet));
|
||||
|
||||
if (sourceConfig == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
c.extendsFrom(sourceConfig);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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<Project> 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<Project> getProjectDependencies(DebofConfiguration debofConfiguration) {
|
||||
return debofConfiguration.getConfigurations(project).stream()
|
||||
.flatMap(configuration -> getLoomProjectDependencies(configuration.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileCollection getFullClasspath() {
|
||||
ConfigurableFileCollection classpath = project.files();
|
||||
|
||||
@@ -25,14 +25,19 @@
|
||||
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.Collections;
|
||||
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;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
@@ -53,8 +58,6 @@ public record DeobfSpecContext(List<FabricModJson> modDependencies,
|
||||
List<FabricModJson> modDependenciesCompileRuntimeClient
|
||||
) implements SpecContext {
|
||||
public static DeobfSpecContext create(Project project) {
|
||||
DebofConfiguration.create(project);
|
||||
|
||||
return create(new DeobfProjectView.Impl(project));
|
||||
}
|
||||
|
||||
@@ -88,10 +91,16 @@ public record DeobfSpecContext(List<FabricModJson> modDependencies,
|
||||
clientTransformingModIds = Set.of();
|
||||
}
|
||||
|
||||
// All dependency mods that are on both the compile and runtime classpath
|
||||
List<FabricModJson> 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))
|
||||
);
|
||||
}
|
||||
@@ -103,7 +112,7 @@ public record DeobfSpecContext(List<FabricModJson> 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);
|
||||
}));
|
||||
@@ -127,7 +136,7 @@ public record DeobfSpecContext(List<FabricModJson> 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);
|
||||
}));
|
||||
@@ -138,6 +147,14 @@ public record DeobfSpecContext(List<FabricModJson> modDependencies,
|
||||
.collect(HashSet::new, Set::add, Set::addAll);
|
||||
}
|
||||
|
||||
private static Optional<FabricModJson> getMod(Path path) {
|
||||
if (Files.isRegularFile(path)) {
|
||||
return FabricModJsonFactory.createFromZipOptional(path);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static List<FabricModJson> getMods(Map<String, FabricModJson> mods, Set<String> ids) {
|
||||
List<FabricModJson> result = new ArrayList<>();
|
||||
|
||||
@@ -148,6 +165,34 @@ public record DeobfSpecContext(List<FabricModJson> modDependencies,
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns a list of mods that are on both to compile and runtime classpath
|
||||
private static List<FabricModJson> getCompileRuntimeProjectMods(DeobfProjectView projectView, AsyncCache<List<FabricModJson>> fmjCache) {
|
||||
var mods = new ArrayList<FabricModJson>();
|
||||
|
||||
for (Project dependentProject : getCompileRuntimeProjectDependencies(projectView).toList()) {
|
||||
List<FabricModJson> 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<Project> getCompileRuntimeProjectDependencies(DeobfProjectView projectView) {
|
||||
if (projectView.disableProjectDependantMods()) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
final Stream<Project> runtimeProjects = projectView.getProjectDependencies(DebofConfiguration.RUNTIME);
|
||||
final List<Project> compileProjects = projectView.getProjectDependencies(DebofConfiguration.COMPILE).toList();
|
||||
|
||||
return runtimeProjects
|
||||
.filter(compileProjects::contains); // Use the intersection of the two configurations.
|
||||
}
|
||||
|
||||
private static Set<String> common(Set<String> a, Set<String> b) {
|
||||
Set<String> copy = new HashSet<>(a);
|
||||
copy.retainAll(b);
|
||||
|
||||
@@ -50,6 +50,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.ForgeExtensionAPI;
|
||||
@@ -661,5 +663,10 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
public NeoForgeExtensionAPI getNeoForge() {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,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;
|
||||
@@ -64,6 +66,8 @@ import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMi
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.MojangMappedMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.SrgMinecraftProvider;
|
||||
import net.fabricmc.loom.task.NestJarsAction;
|
||||
import net.fabricmc.loom.task.RemapJarTask;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.Lazy;
|
||||
import net.fabricmc.loom.util.ModPlatform;
|
||||
@@ -191,6 +195,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");
|
||||
}
|
||||
|
||||
@@ -415,4 +420,17 @@ public abstract class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl
|
||||
ModPlatform.assertForgeLike(this);
|
||||
this.forgeRunsProvider = forgeRunsProvider;
|
||||
}
|
||||
|
||||
@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
|
||||
NestJarsAction.addToTask(task, jars, getPlatform().get());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,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;
|
||||
@@ -145,6 +146,7 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
// Internal inputs
|
||||
@ApiStatus.Internal
|
||||
@Nested
|
||||
@Optional
|
||||
protected abstract Property<SourceMappingsService.Options> getMappings();
|
||||
|
||||
// Internal outputs
|
||||
@@ -230,7 +232,10 @@ 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));
|
||||
@@ -422,11 +427,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);
|
||||
@@ -573,7 +580,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());
|
||||
@@ -682,11 +692,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()
|
||||
|
||||
@@ -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<GenerateRemapClasspathTask> 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> 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<ValidateAccessWidenerTask> 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<ValidateAccessWidenerTask> 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() {
|
||||
|
||||
@@ -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<Task>, Serializable {
|
||||
private final Provider<JarManifestService> manifestService;
|
||||
private final String targetNamespace;
|
||||
private final Provider<Boolean> areEnvironmentSourceSetsSplit;
|
||||
private final Provider<List<String>> clientOnlyEntries;
|
||||
|
||||
public ManifestModificationAction(
|
||||
Provider<JarManifestService> manifestService,
|
||||
String targetNamespace,
|
||||
Provider<Boolean> areEnvironmentSourceSetsSplit,
|
||||
Provider<List<String>> 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<String, String> 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.get()) {
|
||||
manifestAttributes.put(Constants.Manifest.SPLIT_ENV, "true");
|
||||
}
|
||||
|
||||
// Add client-only entries list if present
|
||||
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 -> {
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -26,51 +26,89 @@ package net.fabricmc.loom.task;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
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.ConfigurableFileCollection;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
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;
|
||||
import net.fabricmc.loom.util.ModPlatform;
|
||||
|
||||
/**
|
||||
* 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 abstract class NestJarsAction implements Action<Task>, Serializable {
|
||||
@InputFiles
|
||||
public abstract ConfigurableFileCollection getJars();
|
||||
|
||||
NestJarsAction(Provider<Directory> nestedJarsDir) {
|
||||
this.nestedJarsDir = nestedJarsDir;
|
||||
@Input
|
||||
public abstract Property<ModPlatform> getPlatform();
|
||||
|
||||
@Inject
|
||||
protected abstract WorkerExecutor getWorkerExecutor();
|
||||
|
||||
public static void addToTask(Jar task, FileCollection jars) {
|
||||
addToTask(task, jars, ModPlatform.FABRIC);
|
||||
}
|
||||
|
||||
public static void addToTask(Jar task, FileCollection jars, ModPlatform platform) {
|
||||
NestJarsAction nestJarsAction = task.getProject().getObjects().newInstance(NestJarsAction.class);
|
||||
nestJarsAction.getJars().from(jars);
|
||||
nestJarsAction.getPlatform().set(platform);
|
||||
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();
|
||||
|
||||
if (!nestedJarsDir.isPresent()) {
|
||||
return;
|
||||
}
|
||||
final WorkQueue workQueue = getWorkerExecutor().noIsolation();
|
||||
|
||||
final File outputDir = nestedJarsDir.get().getAsFile();
|
||||
workQueue.submit(NestAction.class, p -> {
|
||||
p.getArchiveFile().set(jarTask.getArchiveFile());
|
||||
p.getJars().setFrom(getJars());
|
||||
p.getPlatform().set(getPlatform());
|
||||
});
|
||||
}
|
||||
|
||||
if (outputDir.exists() && outputDir.isDirectory()) {
|
||||
final File[] jars = outputDir.listFiles((dir, name) -> name.endsWith(".jar"));
|
||||
public interface NestJarsParameters extends WorkParameters {
|
||||
RegularFileProperty getArchiveFile();
|
||||
ConfigurableFileCollection getJars();
|
||||
Property<ModPlatform> getPlatform();
|
||||
}
|
||||
|
||||
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());
|
||||
public abstract static class NestAction implements WorkAction<NestJarsParameters> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NestJarsAction.class);
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
final File jarFile = getParameters().getArchiveFile().get().getAsFile();
|
||||
final Set<File> jars = getParameters().getJars().getFiles();
|
||||
|
||||
// Nest all collected jars
|
||||
if (!jars.isEmpty()) {
|
||||
JarNester.nestJars(jars, jarFile, getParameters().getPlatform().get(), LOGGER);
|
||||
LOGGER.info("Nested {} jar(s) into {}", jars.size(), jarFile.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,14 +60,43 @@ public class NonRemappedJarTaskConfiguration {
|
||||
}
|
||||
|
||||
public void configure() {
|
||||
// No remapping needed - use simplified JIJ approach directly on jar task
|
||||
final Provider<JarManifestService> 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(processIncludeJarsTask.flatMap(NestableJarGenerationTask::getOutputDirectory)));
|
||||
|
||||
NestJarsAction.addToTask(task, project.fileTree(processIncludeJarsTask.flatMap(NestableJarGenerationTask::getOutputDirectory))
|
||||
.matching(pattern -> pattern.include("*.jar")));
|
||||
|
||||
task.doLast(new ManifestModificationAction(
|
||||
manifestServiceProvider,
|
||||
"official",
|
||||
project.provider(extension::areEnvironmentSourceSetsSplit),
|
||||
project.provider(this::getClientOnlyEntries)
|
||||
));
|
||||
|
||||
task.usesService(manifestServiceProvider);
|
||||
});
|
||||
|
||||
// Add jar task to unmapped collection
|
||||
extension.getUnmappedModCollection().from(project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getClientOnlyEntries() {
|
||||
if (!extension.areEnvironmentSourceSetsSplit()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final SourceSet clientSourceSet = SourceSetHelper.getSourceSetByName(
|
||||
MinecraftSourceSets.Split.CLIENT_ONLY_SOURCE_SET_NAME,
|
||||
project
|
||||
);
|
||||
final Provider<ClientEntriesService.Classes.Options> optionsProvider = ClientEntriesService.Classes.createOptions(project, clientSourceSet);
|
||||
|
||||
try (var serviceFactory = new ScopedServiceFactory()) {
|
||||
ClientEntriesService<ClientEntriesService.Classes.Options> service = serviceFactory.get(optionsProvider);
|
||||
return new ArrayList<>(service.getClientOnlyEntries());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to determine client-only entries", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask {
|
||||
protected abstract Property<String> getNativesDirectoryPath();
|
||||
|
||||
@InputFile
|
||||
@Optional
|
||||
public abstract RegularFileProperty getRemapClasspathFile();
|
||||
|
||||
@OutputFile
|
||||
@@ -180,10 +181,13 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask {
|
||||
boolean quilt = platform == ModPlatform.QUILT;
|
||||
final LaunchConfig launchConfig = new LaunchConfig()
|
||||
.property(!quilt ? "fabric.development" : "loader.development", "true")
|
||||
.property(!quilt ? "fabric.remapClasspathFile" : "loader.remapClasspathFile", getRemapClasspathFile().get().getAsFile().getAbsolutePath())
|
||||
.property("log4j.configurationFile", getLog4jConfigPaths().get())
|
||||
.property("log4j2.formatMsgNoLookups", "true");
|
||||
|
||||
if (getRemapClasspathFile().isPresent()) {
|
||||
launchConfig.property(!quilt ? "fabric.remapClasspathFile" : "loader.remapClasspathFile", getRemapClasspathFile().get().getAsFile().getAbsolutePath());
|
||||
}
|
||||
|
||||
if (versionInfo.hasNativesToExtract()) {
|
||||
String nativesPath = getNativesDirectoryPath().get();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,50 @@ 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");
|
||||
}
|
||||
}
|
||||
"""
|
||||
sourceFile.text = src
|
||||
|
||||
when:
|
||||
def result = gradle.run(tasks: [
|
||||
"build",
|
||||
"configureClientLaunch"
|
||||
])
|
||||
|
||||
then:
|
||||
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");
|
||||
@@ -64,4 +109,26 @@ class SimpleDebofTest extends Specification implements GradleProjectTestTrait {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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