Merge remote-tracking branch 'upstream/exp/1.3' into exp/1.3

# Conflicts:
#	build.gradle
#	src/main/java/net/fabricmc/loom/LoomGradleExtension.java
#	src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java
#	src/main/java/net/fabricmc/loom/configuration/decompile/SingleJarDecompileConfiguration.java
#	src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJarConfiguration.java
#	src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java
#	src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/IntermediaryMinecraftProvider.java
#	src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/NamedMinecraftProvider.java
#	src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java
This commit is contained in:
Juuz
2023-06-21 21:40:25 +03:00
40 changed files with 1188 additions and 394 deletions

View File

@@ -8,7 +8,7 @@ plugins {
id 'checkstyle'
id 'jacoco'
id 'codenarc'
id "org.jetbrains.kotlin.jvm" version "1.8.0" // Must match the version included with gradle.
alias(libs.plugins.kotlin)
id "com.diffplug.spotless" version "6.18.0"
id "org.gradle.test-retry" version "1.5.2"
}
@@ -25,7 +25,7 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
group = "dev.architectury"
archivesBaseName = project.name
def baseVersion = '1.2'
def baseVersion = '1.3'
def ENV = System.getenv()
def runNumber = ENV.GITHUB_RUN_NUMBER ?: "9999"
@@ -40,6 +40,12 @@ if (!isSnapshot) {
logger.lifecycle(":building plugin v${version}")
// We must build against the version of Kotlin Gradle ships with.
def kotlinVersion = KotlinDslVersion.current().getKotlinVersion()
if (libs.versions.kotlin.get() != kotlinVersion) {
throw new IllegalStateException("Requires Kotlin version: ${kotlinVersion}")
}
repositories {
mavenCentral()
maven { url "https://maven.fabricmc.net/" }
@@ -103,7 +109,7 @@ dependencies {
// decompilers
implementation ('net.fabricmc:fabric-fernflower:2.0.0')
implementation ('net.fabricmc:cfr:0.2.0')
implementation ('net.fabricmc:cfr:0.2.1')
// source code remapping
implementation ('dev.architectury:mercury:0.1.2.15')
@@ -114,7 +120,7 @@ dependencies {
}
// Kapt integration
compileOnly('org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0') // Must match the version included with gradle.
compileOnly libs.kotlin.gradle.plugin
// Forge patches
implementation ('net.minecraftforge:installertools:1.2.0')
@@ -137,6 +143,7 @@ dependencies {
exclude group: 'org.jetbrains.kotlin'
}
testImplementation 'org.mockito:mockito-core:5.2.0'
testImplementation 'com.microsoft.java:com.microsoft.java.debug.core:0.46.0'
compileOnly 'org.jetbrains:annotations:24.0.1'
testCompileOnly 'org.jetbrains:annotations:24.0.1'
@@ -245,6 +252,7 @@ test {
}
}
import org.gradle.launcher.cli.KotlinDslVersion
import org.gradle.util.GradleVersion
import org.w3c.dom.Document
import org.w3c.dom.Element

View File

@@ -0,0 +1,8 @@
[versions]
kotlin = "1.8.10"
[libraries]
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
[plugins]
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }

View File

@@ -30,6 +30,8 @@ import java.util.List;
import org.gradle.api.Project;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.FileCollection;
import org.gradle.api.provider.ListProperty;
import org.jetbrains.annotations.ApiStatus;
import net.fabricmc.loom.api.LoomGradleExtensionAPI;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
@@ -46,6 +48,7 @@ import net.fabricmc.loom.configuration.providers.forge.SrgProvider;
import net.fabricmc.loom.configuration.providers.forge.mcpconfig.McpConfigProvider;
import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration;
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.configuration.providers.minecraft.mapped.SrgMinecraftProvider;
@@ -54,6 +57,7 @@ import net.fabricmc.loom.extension.MixinExtension;
import net.fabricmc.loom.util.ModPlatform;
import net.fabricmc.loom.util.download.DownloadBuilder;
@ApiStatus.Internal
public interface LoomGradleExtension extends LoomGradleExtensionAPI {
static LoomGradleExtension get(Project project) {
return (LoomGradleExtension) project.getExtensions().getByName("loom");
@@ -128,6 +132,8 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI {
*/
boolean multiProjectOptimisation();
ListProperty<LibraryProcessorManager.LibraryProcessorFactory> getLibraryProcessors();
// ===================
// Architectury Loom
// ===================

View File

@@ -70,6 +70,7 @@ import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets;
import net.fabricmc.loom.configuration.providers.minecraft.mapped.AbstractMappedMinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.mapped.SrgMinecraftProvider;
@@ -219,8 +220,8 @@ public abstract class CompileConfiguration implements Runnable {
}
// Provide the remapped mc jars
final IntermediaryMinecraftProvider<?> intermediaryMinecraftProvider = jarConfiguration.getIntermediaryMinecraftProviderBiFunction().apply(configContext, minecraftProvider);
NamedMinecraftProvider<?> namedMinecraftProvider = jarConfiguration.getNamedMinecraftProviderBiFunction().apply(configContext, minecraftProvider);
final IntermediaryMinecraftProvider<?> intermediaryMinecraftProvider = jarConfiguration.getIntermediaryMinecraftProviderBiFunction().apply(project, minecraftProvider);
NamedMinecraftProvider<?> namedMinecraftProvider = jarConfiguration.getNamedMinecraftProviderBiFunction().apply(project, minecraftProvider);
registerGameProcessors(configContext);
MinecraftJarProcessorManager minecraftJarProcessorManager = MinecraftJarProcessorManager.create(getProject());
@@ -230,11 +231,13 @@ public abstract class CompileConfiguration implements Runnable {
namedMinecraftProvider = jarConfiguration.getProcessedNamedMinecraftProviderBiFunction().apply(namedMinecraftProvider, minecraftJarProcessorManager);
}
final var provideContext = new AbstractMappedMinecraftProvider.ProvideContext(true, extension.refreshDeps(), configContext);
extension.setIntermediaryMinecraftProvider(intermediaryMinecraftProvider);
intermediaryMinecraftProvider.provide(true);
intermediaryMinecraftProvider.provide(provideContext);
extension.setNamedMinecraftProvider(namedMinecraftProvider);
namedMinecraftProvider.provide(true);
namedMinecraftProvider.provide(provideContext);
if (extension.isForge()) {
final SrgMinecraftProvider<?> srgMinecraftProvider = jarConfiguration.getSrgMinecraftProviderBiFunction().apply(configContext, minecraftProvider);

View File

@@ -183,7 +183,11 @@ public final class RemapConfigurations {
configuration.getTargetConfigurationName().convention(targetConfiguration);
configuration.getOnCompileClasspath().convention(compileClasspath);
configuration.getOnRuntimeClasspath().convention(runtimeClasspath);
configuration.getPublishingMode().convention(publishingMode);
// Publish only for the main source set.
if (SourceSet.MAIN_SOURCE_SET_NAME.equals(sourceSet.getName())) {
configuration.getPublishingMode().convention(publishingMode);
}
};
}

View File

@@ -27,13 +27,14 @@ package net.fabricmc.loom.configuration.decompile;
import java.io.File;
import org.gradle.api.Project;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.api.artifacts.ConfigurationContainer;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.ConfigContext;
import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration;
import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider;
import net.fabricmc.loom.task.UnpickJarTask;
import net.fabricmc.loom.task.GenerateSourcesTask;
import net.fabricmc.loom.util.Constants;
public abstract class DecompileConfiguration<T extends MappedMinecraftProvider> {
protected final Project project;
@@ -50,11 +51,13 @@ public abstract class DecompileConfiguration<T extends MappedMinecraftProvider>
public abstract void afterEvaluation();
protected final TaskProvider<UnpickJarTask> createUnpickJarTask(String name, File inputJar, File outputJar) {
return project.getTasks().register(name, UnpickJarTask.class, unpickJarTask -> {
unpickJarTask.getUnpickDefinitions().set(mappingConfiguration.getUnpickDefinitionsFile());
unpickJarTask.getInputJar().set(inputJar);
unpickJarTask.getOutputJar().set(outputJar);
});
protected final void configureUnpick(GenerateSourcesTask task, File unpickOutputJar) {
final ConfigurationContainer configurations = task.getProject().getConfigurations();
task.getUnpickDefinitions().set(mappingConfiguration.getUnpickDefinitionsFile());
task.getUnpickOutputJar().set(unpickOutputJar);
task.getUnpickConstantJar().setFrom(configurations.getByName(Constants.Configurations.MAPPING_CONSTANTS));
task.getUnpickClasspath().setFrom(configurations.getByName(Constants.Configurations.MINECRAFT_COMPILE_LIBRARIES));
task.getUnpickClasspath().from(configurations.getByName(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED));
}
}

View File

@@ -25,12 +25,12 @@
package net.fabricmc.loom.configuration.decompile;
import java.io.File;
import java.nio.file.Path;
import java.util.List;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.ConfigContext;
import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJar;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration;
import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider;
import net.fabricmc.loom.task.GenerateForgePatchedSourcesTask;
@@ -44,36 +44,25 @@ public class SingleJarDecompileConfiguration extends DecompileConfiguration<Mapp
@Override
public final void afterEvaluation() {
List<Path> minecraftJars = minecraftProvider.getMinecraftJarPaths();
final List<MinecraftJar> minecraftJars = minecraftProvider.getMinecraftJars();
assert minecraftJars.size() == 1;
final File namedJar = minecraftJars.get(0).toFile();
File mappedJar = namedJar;
if (mappingConfiguration.hasUnpickDefinitions()) {
File outputJar = new File(extension.getMappingConfiguration().mappingsWorkingDir().toFile(), "minecraft-unpicked.jar");
createUnpickJarTask("unpickJar", namedJar, outputJar);
mappedJar = outputJar;
}
final File inputJar = mappedJar;
final MinecraftJar minecraftJar = minecraftJars.get(0);
LoomGradleExtension.get(project).getDecompilerOptions().forEach(options -> {
final String decompilerName = options.getFormattedName();
String taskName = "genSourcesWith" + decompilerName;
// Decompiler will be passed to the constructor of GenerateSourcesTask
project.getTasks().register(taskName, GenerateSourcesTask.class, options).configure(task -> {
task.getInputJar().set(inputJar);
task.getRuntimeJar().set(namedJar);
task.getInputJarName().set(minecraftJar.getName());
task.getOutputJar().fileValue(GenerateSourcesTask.getMappedJarFileWithSuffix("-sources.jar", minecraftJar.getPath()));
task.dependsOn(project.getTasks().named("validateAccessWidener"));
task.setDescription("Decompile minecraft using %s.".formatted(decompilerName));
task.setGroup(Constants.TaskGroup.FABRIC);
if (mappingConfiguration.hasUnpickDefinitions()) {
task.dependsOn(project.getTasks().named("unpickJar"));
final File outputJar = new File(extension.getMappingConfiguration().mappingsWorkingDir().toFile(), "minecraft-unpicked.jar");
configureUnpick(task, outputJar);
}
});
});

View File

@@ -32,9 +32,9 @@ import org.gradle.api.tasks.TaskProvider;
import net.fabricmc.loom.api.decompilers.DecompilerOptions;
import net.fabricmc.loom.configuration.ConfigContext;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJar;
import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider;
import net.fabricmc.loom.task.GenerateSourcesTask;
import net.fabricmc.loom.task.UnpickJarTask;
import net.fabricmc.loom.util.Constants;
public final class SplitDecompileConfiguration extends DecompileConfiguration<MappedMinecraftProvider.Split> {
@@ -44,41 +44,26 @@ public final class SplitDecompileConfiguration extends DecompileConfiguration<Ma
@Override
public void afterEvaluation() {
File commonJarToDecompile = minecraftProvider.getCommonJar().toFile();
File clientOnlyJarToDecompile = minecraftProvider.getClientOnlyJar().toFile();
TaskProvider<UnpickJarTask> unpickCommonJar = null;
TaskProvider<UnpickJarTask> unpickClientOnlyJar = null;
if (mappingConfiguration.hasUnpickDefinitions()) {
commonJarToDecompile = new File(extension.getMappingConfiguration().mappingsWorkingDir().toFile(), "minecraft-common-unpicked.jar");
clientOnlyJarToDecompile = new File(extension.getMappingConfiguration().mappingsWorkingDir().toFile(), "minecraft-clientonly-unpicked.jar");
unpickCommonJar = createUnpickJarTask("unpickCommonJar", minecraftProvider.getCommonJar().toFile(), commonJarToDecompile);
unpickClientOnlyJar = createUnpickJarTask("unpickClientOnlyJar", minecraftProvider.getClientOnlyJar().toFile(), clientOnlyJarToDecompile);
}
// Need to re-declare them as final to access them from the lambada
final File commonJar = commonJarToDecompile;
final File clientOnlyJar = clientOnlyJarToDecompile;
final TaskProvider<UnpickJarTask> unpickCommonJarTask = unpickCommonJar;
final TaskProvider<UnpickJarTask> unpickClientOnlyJarTask = unpickClientOnlyJar;
final MinecraftJar commonJar = minecraftProvider.getCommonJar();
final MinecraftJar clientOnlyJar = minecraftProvider.getClientOnlyJar();
final TaskProvider<Task> commonDecompileTask = createDecompileTasks("Common", task -> {
task.getInputJar().set(commonJar);
task.getRuntimeJar().set(minecraftProvider.getCommonJar().toFile());
task.getInputJarName().set(commonJar.getName());
task.getOutputJar().fileValue(GenerateSourcesTask.getMappedJarFileWithSuffix("-sources.jar", commonJar.getPath()));
if (unpickCommonJarTask != null) {
task.dependsOn(unpickCommonJarTask);
if (mappingConfiguration.hasUnpickDefinitions()) {
File unpickJar = new File(extension.getMappingConfiguration().mappingsWorkingDir().toFile(), "minecraft-common-unpicked.jar");
configureUnpick(task, unpickJar);
}
});
final TaskProvider<Task> clientOnlyDecompileTask = createDecompileTasks("ClientOnly", task -> {
task.getInputJar().set(clientOnlyJar);
task.getRuntimeJar().set(minecraftProvider.getClientOnlyJar().toFile());
task.getInputJarName().set(clientOnlyJar.getName());
task.getOutputJar().fileValue(GenerateSourcesTask.getMappedJarFileWithSuffix("-sources.jar", clientOnlyJar.getPath()));
if (unpickCommonJarTask != null) {
task.dependsOn(unpickClientOnlyJarTask);
if (mappingConfiguration.hasUnpickDefinitions()) {
File unpickJar = new File(extension.getMappingConfiguration().mappingsWorkingDir().toFile(), "minecraft-clientonly-unpicked.jar");
configureUnpick(task, unpickJar);
}
// Don't allow them to run at the same time.

View File

@@ -55,6 +55,6 @@ public class IdeaUtils {
module = project.getName() + "." + module;
}
return module;
return module.replace(' ', '_');
}
}

View File

@@ -28,6 +28,7 @@ import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -44,6 +45,7 @@ import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -174,14 +176,20 @@ public abstract class InterfaceInjectionProcessor implements MinecraftJarProcess
}
private static String appendComment(String comment, List<InjectedInterface> injectedInterfaces) {
for (InjectedInterface injectedInterface : injectedInterfaces) {
String iiComment = "Interface {@link %s} injected by mod %s".formatted(injectedInterface.ifaceName.substring(injectedInterface.ifaceName.lastIndexOf("/") + 1), injectedInterface.modId);
if (injectedInterfaces.isEmpty()) {
return comment;
}
if (comment == null || !comment.contains(iiComment)) {
if (comment == null) {
comment = iiComment;
var commentBuilder = comment == null ? new StringBuilder() : new StringBuilder(comment);
for (InjectedInterface injectedInterface : injectedInterfaces) {
String iiComment = "<p>Interface {@link %s} injected by mod %s</p>".formatted(injectedInterface.ifaceName().replace('/', '.').replace('$', '.'), injectedInterface.modId());
if (commentBuilder.indexOf(iiComment) == -1) {
if (commentBuilder.isEmpty()) {
commentBuilder.append(iiComment);
} else {
comment += "\n" + iiComment;
commentBuilder.append('\n').append(iiComment);
}
}
}
@@ -226,7 +234,10 @@ public abstract class InterfaceInjectionProcessor implements MinecraftJarProcess
}
private static class InjectingClassVisitor extends ClassVisitor {
private static final int INTERFACE_ACCESS = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE;
private final List<InjectedInterface> injectedInterfaces;
private final Set<String> knownInnerClasses = new HashSet<>();
InjectingClassVisitor(int asmVersion, ClassWriter writer, List<InjectedInterface> injectedInterfaces) {
super(asmVersion, writer);
@@ -259,5 +270,53 @@ public abstract class InterfaceInjectionProcessor implements MinecraftJarProcess
super.visit(version, access, name, signature, superName, modifiedInterfaces.toArray(new String[0]));
}
@Override
public void visitInnerClass(final String name, final String outerName, final String innerName, final int access) {
this.knownInnerClasses.add(name);
super.visitInnerClass(name, outerName, innerName, access);
}
@Override
public void visitEnd() {
// inject any necessary inner class entries
// this may produce technically incorrect bytecode cuz we don't know the actual access flags for inner class entries
// but it's hopefully enough to quiet some IDE errors
for (final InjectedInterface itf : injectedInterfaces) {
if (this.knownInnerClasses.contains(itf.ifaceName())) {
continue;
}
int simpleNameIdx = itf.ifaceName().lastIndexOf('/');
final String simpleName = simpleNameIdx == -1 ? itf.ifaceName() : itf.ifaceName().substring(simpleNameIdx + 1);
int lastIdx = -1;
int dollarIdx = -1;
// Iterate through inner class entries starting from outermost to innermost
while ((dollarIdx = simpleName.indexOf('$', dollarIdx + 1)) != -1) {
if (dollarIdx - lastIdx == 1) {
continue;
}
// Emit the inner class entry from this to the last one
if (lastIdx != -1) {
final String outerName = itf.ifaceName().substring(0, simpleNameIdx + 1 + lastIdx);
final String innerName = simpleName.substring(lastIdx + 1, dollarIdx);
super.visitInnerClass(outerName + '$' + innerName, outerName, innerName, INTERFACE_ACCESS);
}
lastIdx = dollarIdx;
}
// If we have a trailer to append
if (lastIdx != -1 && lastIdx != simpleName.length()) {
final String outerName = itf.ifaceName().substring(0, simpleNameIdx + 1 + lastIdx);
final String innerName = simpleName.substring(lastIdx + 1);
super.visitInnerClass(outerName + '$' + innerName, outerName, innerName, INTERFACE_ACCESS);
}
}
super.visitEnd();
}
}
}

View File

@@ -27,8 +27,18 @@ package net.fabricmc.loom.configuration.providers.minecraft;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
public record ManifestVersion(List<Versions> versions, Map<String, String> latest) {
public static class Versions {
public String id, url, sha1;
}
@Nullable
public Versions getVersion(String id) {
return versions.stream()
.filter(versions -> versions.id.equalsIgnoreCase(id))
.findFirst()
.orElse(null);
}
}

View File

@@ -28,6 +28,8 @@ import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.gradle.api.Project;
import net.fabricmc.loom.configuration.ConfigContext;
import net.fabricmc.loom.configuration.decompile.DecompileConfiguration;
import net.fabricmc.loom.configuration.decompile.SingleJarDecompileConfiguration;
@@ -79,8 +81,8 @@ public enum MinecraftJarConfiguration {
);
private final Function<ConfigContext, MinecraftProvider> minecraftProviderFunction;
private final BiFunction<ConfigContext, MinecraftProvider, IntermediaryMinecraftProvider<?>> intermediaryMinecraftProviderBiFunction;
private final BiFunction<ConfigContext, MinecraftProvider, NamedMinecraftProvider<?>> namedMinecraftProviderBiFunction;
private final BiFunction<Project, MinecraftProvider, IntermediaryMinecraftProvider<?>> intermediaryMinecraftProviderBiFunction;
private final BiFunction<Project, MinecraftProvider, NamedMinecraftProvider<?>> namedMinecraftProviderBiFunction;
private final BiFunction<ConfigContext, MinecraftProvider, SrgMinecraftProvider<?>> srgMinecraftProviderBiFunction;
private final BiFunction<NamedMinecraftProvider<?>, MinecraftJarProcessorManager, ProcessedNamedMinecraftProvider<?, ?>> processedNamedMinecraftProviderBiFunction;
private final BiFunction<ConfigContext, MappedMinecraftProvider, DecompileConfiguration<?>> decompileConfigurationBiFunction;
@@ -89,16 +91,16 @@ public enum MinecraftJarConfiguration {
@SuppressWarnings("unchecked") // Just a bit of a generic mess :)
<M extends MinecraftProvider, P extends NamedMinecraftProvider<M>, Q extends MappedMinecraftProvider> MinecraftJarConfiguration(
Function<ConfigContext, M> minecraftProviderFunction,
BiFunction<ConfigContext, M, IntermediaryMinecraftProvider<M>> intermediaryMinecraftProviderBiFunction,
BiFunction<ConfigContext, M, P> namedMinecraftProviderBiFunction,
BiFunction<Project, M, IntermediaryMinecraftProvider<M>> intermediaryMinecraftProviderBiFunction,
BiFunction<Project, M, P> namedMinecraftProviderBiFunction,
BiFunction<ConfigContext, M, SrgMinecraftProvider<M>> srgMinecraftProviderBiFunction,
BiFunction<P, MinecraftJarProcessorManager, ProcessedNamedMinecraftProvider<M, P>> processedNamedMinecraftProviderBiFunction,
BiFunction<ConfigContext, Q, DecompileConfiguration<?>> decompileConfigurationBiFunction,
List<String> supportedEnvironments
) {
this.minecraftProviderFunction = (Function<ConfigContext, MinecraftProvider>) minecraftProviderFunction;
this.intermediaryMinecraftProviderBiFunction = (BiFunction<ConfigContext, MinecraftProvider, IntermediaryMinecraftProvider<?>>) (Object) intermediaryMinecraftProviderBiFunction;
this.namedMinecraftProviderBiFunction = (BiFunction<ConfigContext, MinecraftProvider, NamedMinecraftProvider<?>>) namedMinecraftProviderBiFunction;
this.intermediaryMinecraftProviderBiFunction = (BiFunction<Project, MinecraftProvider, IntermediaryMinecraftProvider<?>>) (Object) intermediaryMinecraftProviderBiFunction;
this.namedMinecraftProviderBiFunction = (BiFunction<Project, MinecraftProvider, NamedMinecraftProvider<?>>) namedMinecraftProviderBiFunction;
this.srgMinecraftProviderBiFunction = (BiFunction<ConfigContext, MinecraftProvider, SrgMinecraftProvider<?>>) (Object) srgMinecraftProviderBiFunction;
this.processedNamedMinecraftProviderBiFunction = (BiFunction<NamedMinecraftProvider<?>, MinecraftJarProcessorManager, ProcessedNamedMinecraftProvider<?, ?>>) (Object) processedNamedMinecraftProviderBiFunction;
this.decompileConfigurationBiFunction = (BiFunction<ConfigContext, MappedMinecraftProvider, DecompileConfiguration<?>>) decompileConfigurationBiFunction;
@@ -109,11 +111,11 @@ public enum MinecraftJarConfiguration {
return minecraftProviderFunction;
}
public BiFunction<ConfigContext, MinecraftProvider, IntermediaryMinecraftProvider<?>> getIntermediaryMinecraftProviderBiFunction() {
public BiFunction<Project, MinecraftProvider, IntermediaryMinecraftProvider<?>> getIntermediaryMinecraftProviderBiFunction() {
return intermediaryMinecraftProviderBiFunction;
}
public BiFunction<ConfigContext, MinecraftProvider, NamedMinecraftProvider<?>> getNamedMinecraftProviderBiFunction() {
public BiFunction<Project, MinecraftProvider, NamedMinecraftProvider<?>> getNamedMinecraftProviderBiFunction() {
return namedMinecraftProviderBiFunction;
}

View File

@@ -56,7 +56,7 @@ public class MinecraftLibraryProvider {
public MinecraftLibraryProvider(MinecraftProvider minecraftProvider, Project project) {
this.project = project;
this.minecraftProvider = minecraftProvider;
this.processorManager = new LibraryProcessorManager(platform, project.getRepositories(), getEnabledProcessors());
this.processorManager = new LibraryProcessorManager(platform, project.getRepositories(), LoomGradleExtension.get(project).getLibraryProcessors().get(), getEnabledProcessors());
}
private List<String> getEnabledProcessors() {

View File

@@ -0,0 +1,161 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2023 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.providers.minecraft;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.List;
import java.util.function.Function;
import org.gradle.api.Project;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.util.MirrorUtil;
import net.fabricmc.loom.util.download.DownloadBuilder;
public final class MinecraftMetadataProvider {
private final Options options;
private final Function<String, DownloadBuilder> download;
private ManifestVersion.Versions versionEntry;
private MinecraftVersionMeta versionMeta;
public MinecraftMetadataProvider(Options options, Function<String, DownloadBuilder> download) {
this.options = options;
this.download = download;
}
public MinecraftVersionMeta getVersionMeta() {
try {
if (versionEntry == null) {
versionEntry = getVersionEntry();
}
if (versionMeta == null) {
versionMeta = readVersionMeta();
}
} catch (IOException e) {
throw new UncheckedIOException(e.getMessage(), e);
}
return versionMeta;
}
private ManifestVersion.Versions getVersionEntry() throws IOException {
// Custom URL always takes priority
if (options.customManifestUrl() != null) {
ManifestVersion.Versions customVersion = new ManifestVersion.Versions();
customVersion.id = options.minecraftVersion();
customVersion.url = options.customManifestUrl();
return customVersion;
}
final List<ManifestVersionSupplier> suppliers = List.of(
// First try finding the version with caching
() -> getVersions(false),
// Then try finding the experimental version with caching
() -> getExperimentalVersions(false),
// Then force download Mojang's metadata to find the version
() -> getVersions(true),
// Finally try a force downloaded experimental metadata.
() -> getExperimentalVersions(true)
);
for (ManifestVersionSupplier supplier : suppliers) {
final ManifestVersion.Versions version = supplier.get().getVersion(options.minecraftVersion());
if (version != null) {
return version;
}
}
throw new RuntimeException("Failed to find minecraft version: " + options.minecraftVersion());
}
private ManifestVersion getVersions(boolean forceDownload) throws IOException {
return getVersions(options.versionManifestUrl(), options.versionManifestPath(), forceDownload);
}
private ManifestVersion getExperimentalVersions(boolean forceDownload) throws IOException {
return getVersions(options.experimentalVersionManifestUrl(), options.experimentalVersionManifestPath(), forceDownload);
}
private ManifestVersion getVersions(String url, Path cacheFile, boolean forceDownload) throws IOException {
DownloadBuilder builder = download.apply(url);
if (forceDownload) {
builder = builder.forceDownload();
} else {
builder = builder.defaultCache();
}
final String versionManifest = builder.downloadString(cacheFile);
return LoomGradlePlugin.OBJECT_MAPPER.readValue(versionManifest, ManifestVersion.class);
}
private MinecraftVersionMeta readVersionMeta() throws IOException {
final DownloadBuilder builder = download.apply(versionEntry.url);
if (versionEntry.sha1 != null) {
builder.sha1(versionEntry.sha1);
} else {
builder.defaultCache();
}
final String json = builder.downloadString(options.minecraftMetadataPath());
return LoomGradlePlugin.OBJECT_MAPPER.readValue(json, MinecraftVersionMeta.class);
}
public record Options(String minecraftVersion,
String versionManifestUrl,
String experimentalVersionManifestUrl,
@Nullable String customManifestUrl,
Path versionManifestPath,
Path experimentalVersionManifestPath,
Path minecraftMetadataPath) {
public static Options create(String minecraftVersion, Project project, Path minecraftMetadataPath) {
final LoomGradleExtension extension = LoomGradleExtension.get(project);
final Path userCache = extension.getFiles().getUserCache().toPath();
return new Options(
minecraftVersion,
MirrorUtil.getVersionManifests(project),
MirrorUtil.getExperimentalVersions(project),
extension.getCustomMinecraftManifest().getOrNull(),
userCache.resolve("version_manifest.json"),
userCache.resolve("experimental_version_manifest.json"),
minecraftMetadataPath
);
}
}
@FunctionalInterface
private interface ManifestVersionSupplier {
ManifestVersion get() throws IOException;
}
}

View File

@@ -25,7 +25,6 @@
package net.fabricmc.loom.configuration.providers.minecraft;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
@@ -37,25 +36,19 @@ import org.gradle.api.logging.Logger;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.configuration.ConfigContext;
import net.fabricmc.loom.configuration.DependencyInfo;
import net.fabricmc.loom.configuration.providers.BundleMetadata;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.MirrorUtil;
import net.fabricmc.loom.util.download.DownloadBuilder;
import net.fabricmc.loom.util.download.DownloadExecutor;
import net.fabricmc.loom.util.download.GradleDownloadProgressListener;
import net.fabricmc.loom.util.gradle.ProgressGroup;
public abstract class MinecraftProvider {
private String minecraftVersion;
private MinecraftVersionMeta versionInfo;
private MinecraftLibraryProvider libraryProvider;
private MinecraftMetadataProvider metadataProvider;
private File workingDir;
private File minecraftJson;
private File minecraftClientJar;
// Note this will be the boostrap jar starting with 21w39a
private File minecraftServerJar;
@@ -63,8 +56,6 @@ public abstract class MinecraftProvider {
private File minecraftExtractedServerJar;
@Nullable
private BundleMetadata serverBundleMetadata;
private File versionManifestJson;
private File experimentalVersionsJson;
private String jarPrefix = "";
private final Project project;
@@ -91,11 +82,14 @@ public abstract class MinecraftProvider {
initFiles();
downloadMcJson();
try (FileReader reader = new FileReader(minecraftJson)) {
versionInfo = LoomGradlePlugin.OBJECT_MAPPER.readValue(reader, MinecraftVersionMeta.class);
}
metadataProvider = new MinecraftMetadataProvider(
MinecraftMetadataProvider.Options.create(
minecraftVersion,
getProject(),
file("minecraft-info.json").toPath()
),
getExtension()::download
);
downloadJars();
@@ -103,16 +97,13 @@ public abstract class MinecraftProvider {
serverBundleMetadata = BundleMetadata.fromJar(minecraftServerJar.toPath());
}
libraryProvider = new MinecraftLibraryProvider(this, project);
final MinecraftLibraryProvider libraryProvider = new MinecraftLibraryProvider(this, project);
libraryProvider.provide();
}
protected void initFiles() {
workingDir = new File(getExtension().getFiles().getUserCache(), minecraftVersion);
workingDir.mkdirs();
minecraftJson = file("minecraft-info.json");
versionManifestJson = new File(getExtension().getFiles().getUserCache(), "version_manifest.json");
experimentalVersionsJson = new File(getExtension().getFiles().getUserCache(), "experimental_version_manifest.json");
if (provideClient()) {
minecraftClientJar = file("minecraft-client.jar");
@@ -124,73 +115,11 @@ public abstract class MinecraftProvider {
}
}
private void downloadMcJson() throws IOException {
final String versionManifestUrl = MirrorUtil.getVersionManifests(getProject());
final String versionManifest = getExtension().download(versionManifestUrl)
.defaultCache()
.downloadString(versionManifestJson.toPath());
final ManifestVersion mcManifest = LoomGradlePlugin.OBJECT_MAPPER.readValue(versionManifest, ManifestVersion.class);
ManifestVersion.Versions version = null;
if (getExtension().getCustomMinecraftManifest().isPresent()) {
ManifestVersion.Versions customVersion = new ManifestVersion.Versions();
customVersion.id = minecraftVersion;
customVersion.url = getExtension().getCustomMinecraftManifest().get();
version = customVersion;
getProject().getLogger().lifecycle("Using custom minecraft manifest");
}
if (version == null) {
version = mcManifest.versions().stream()
.filter(versions -> versions.id.equalsIgnoreCase(minecraftVersion))
.findFirst().orElse(null);
}
if (version == null) {
version = findExperimentalVersion();
}
if (version == null) {
throw new RuntimeException("Failed to find minecraft version: " + minecraftVersion);
}
getProject().getLogger().debug("Downloading Minecraft {} manifest", minecraftVersion);
final DownloadBuilder download = getExtension().download(version.url);
if (version.sha1 != null) {
download.sha1(version.sha1);
} else {
download.defaultCache();
}
download.downloadPath(minecraftJson.toPath());
}
// This attempts to find the version from fabric's own fallback version manifest json.
private ManifestVersion.Versions findExperimentalVersion() throws IOException {
final String expVersionManifest = getExtension().download(MirrorUtil.getExperimentalVersions(getProject()))
.defaultCache()
.downloadString(experimentalVersionsJson.toPath());
final ManifestVersion expManifest = LoomGradlePlugin.OBJECT_MAPPER.readValue(expVersionManifest, ManifestVersion.class);
final ManifestVersion.Versions result = expManifest.versions().stream()
.filter(versions -> versions.id.equalsIgnoreCase(minecraftVersion))
.findFirst()
.orElse(null);
if (result != null) {
getProject().getLogger().lifecycle("Using fallback experimental version {}", minecraftVersion);
}
return result;
}
private void downloadJars() throws IOException {
try (ProgressGroup progressGroup = new ProgressGroup(getProject(), "Download Minecraft jars");
DownloadExecutor executor = new DownloadExecutor(2)) {
if (provideClient()) {
final MinecraftVersionMeta.Download client = versionInfo.download("client");
final MinecraftVersionMeta.Download client = getVersionInfo().download("client");
getExtension().download(client.url())
.sha1(client.sha1())
.progress(new GradleDownloadProgressListener("Minecraft client", progressGroup::createProgressLogger))
@@ -198,7 +127,7 @@ public abstract class MinecraftProvider {
}
if (provideServer()) {
final MinecraftVersionMeta.Download server = versionInfo.download("server");
final MinecraftVersionMeta.Download server = getVersionInfo().download("server");
getExtension().download(server.url())
.sha1(server.sha1())
.progress(new GradleDownloadProgressListener("Minecraft server", progressGroup::createProgressLogger))
@@ -261,7 +190,7 @@ public abstract class MinecraftProvider {
}
public MinecraftVersionMeta getVersionInfo() {
return versionInfo;
return Objects.requireNonNull(metadataProvider, "Metadata provider not setup").getVersionMeta();
}
public String getJarPrefix() {

View File

@@ -43,7 +43,7 @@ import net.fabricmc.loom.configuration.providers.minecraft.library.processors.Ru
import net.fabricmc.loom.util.Platform;
public class LibraryProcessorManager {
private static final List<LibraryProcessorFactory<?>> LIBRARY_PROCESSORS = List.of(
public static final List<LibraryProcessorFactory> DEFAULT_LIBRARY_PROCESSORS = List.of(
ArmNativesLibraryProcessor::new,
LegacyASMLibraryProcessor::new,
LoomNativeSupportLibraryProcessor::new,
@@ -55,22 +55,25 @@ public class LibraryProcessorManager {
private final Platform platform;
private final RepositoryHandler repositories;
private final List<LibraryProcessorFactory> libraryProcessorFactories;
private final List<String> enabledProcessors;
public LibraryProcessorManager(Platform platform, RepositoryHandler repositories, List<String> enabledProcessors) {
public LibraryProcessorManager(Platform platform, RepositoryHandler repositories, List<LibraryProcessorFactory> libraryProcessorFactories, List<String> enabledProcessors) {
this.platform = platform;
this.repositories = repositories;
this.libraryProcessorFactories = libraryProcessorFactories;
this.enabledProcessors = enabledProcessors;
}
@VisibleForTesting
public LibraryProcessorManager(Platform platform, RepositoryHandler repositories) {
this(platform, repositories, Collections.emptyList());
this(platform, repositories, DEFAULT_LIBRARY_PROCESSORS, Collections.emptyList());
}
private List<LibraryProcessor> getProcessors(LibraryContext context) {
var processors = new ArrayList<LibraryProcessor>();
for (LibraryProcessorFactory<?> factory : LIBRARY_PROCESSORS) {
for (LibraryProcessorFactory factory : libraryProcessorFactories) {
final LibraryProcessor processor = factory.apply(platform, context);
final LibraryProcessor.ApplicationResult applicationResult = processor.getApplicationResult();
@@ -122,6 +125,6 @@ public class LibraryProcessorManager {
return Collections.unmodifiableList(libraries);
}
public interface LibraryProcessorFactory<T extends LibraryProcessor> extends BiFunction<Platform, LibraryContext, T> {
public interface LibraryProcessorFactory extends BiFunction<Platform, LibraryContext, LibraryProcessor> {
}
}

View File

@@ -59,13 +59,13 @@ import net.fabricmc.mappingio.tree.MemoryMappingTree;
public abstract class AbstractMappedMinecraftProvider<M extends MinecraftProvider> implements MappedMinecraftProvider.ProviderImpl {
protected final M minecraftProvider;
protected final ConfigContext configContext;
private final Project project;
protected final LoomGradleExtension extension;
public AbstractMappedMinecraftProvider(ConfigContext configContext, M minecraftProvider) {
this.configContext = configContext;
public AbstractMappedMinecraftProvider(Project project, M minecraftProvider) {
this.minecraftProvider = minecraftProvider;
this.extension = configContext.extension();
this.project = project;
this.extension = LoomGradleExtension.get(project);
}
public abstract MappingsNamespace getTargetNamespace();
@@ -76,13 +76,13 @@ public abstract class AbstractMappedMinecraftProvider<M extends MinecraftProvide
return Collections.emptyList();
}
public void provide(boolean applyDependencies) throws Exception {
public List<MinecraftJar> provide(ProvideContext context) throws Exception {
final List<RemappedJars> remappedJars = getRemappedJars();
assert !remappedJars.isEmpty();
if (!areOutputsValid(remappedJars) || extension.refreshDeps()) {
if (!areOutputsValid(remappedJars) || context.refreshOutputs()) {
try {
remapInputs(remappedJars);
remapInputs(remappedJars, context.configContext());
} catch (Throwable t) {
cleanOutputs(remappedJars);
@@ -90,17 +90,25 @@ public abstract class AbstractMappedMinecraftProvider<M extends MinecraftProvide
}
}
if (applyDependencies) {
if (context.applyDependencies()) {
final List<String> dependencyTargets = getDependencyTargets();
if (dependencyTargets.isEmpty()) {
return;
if (!dependencyTargets.isEmpty()) {
MinecraftSourceSets.get(getProject()).applyDependencies(
(configuration, name) -> getProject().getDependencies().add(configuration, getDependencyNotation(name)),
dependencyTargets
);
}
}
MinecraftSourceSets.get(getProject()).applyDependencies(
(configuration, name) -> getProject().getDependencies().add(configuration, getDependencyNotation(name)),
dependencyTargets
);
return remappedJars.stream()
.map(RemappedJars::outputJar)
.toList();
}
public record ProvideContext(boolean applyDependencies, boolean refreshOutputs, ConfigContext configContext) {
ProvideContext withApplyDependencies(boolean applyDependencies) {
return new ProvideContext(applyDependencies, refreshOutputs(), configContext());
}
}
@@ -160,15 +168,15 @@ public abstract class AbstractMappedMinecraftProvider<M extends MinecraftProvide
return true;
}
private void remapInputs(List<RemappedJars> remappedJars) throws IOException {
private void remapInputs(List<RemappedJars> remappedJars, ConfigContext configContext) throws IOException {
cleanOutputs(remappedJars);
for (RemappedJars remappedJar : remappedJars) {
remapJar(remappedJar);
remapJar(remappedJar, configContext);
}
}
private void remapJar(RemappedJars remappedJars) throws IOException {
private void remapJar(RemappedJars remappedJars, ConfigContext configContext) throws IOException {
final MappingConfiguration mappingConfiguration = extension.getMappingConfiguration();
final String fromM = remappedJars.sourceNamespace().toString();
final String toM = getTargetNamespace().toString();
@@ -229,12 +237,8 @@ public abstract class AbstractMappedMinecraftProvider<M extends MinecraftProvide
}
}
public ConfigContext getConfigContext() {
return configContext;
}
public Project getProject() {
return getConfigContext().project();
return project;
}
public M getMinecraftProvider() {

View File

@@ -27,9 +27,9 @@ package net.fabricmc.loom.configuration.providers.minecraft.mapped;
import java.util.List;
import dev.architectury.tinyremapper.TinyRemapper;
import org.gradle.api.Project;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.configuration.ConfigContext;
import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.SingleJarEnvType;
@@ -37,8 +37,8 @@ import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftPro
import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider;
public abstract sealed class IntermediaryMinecraftProvider<M extends MinecraftProvider> extends AbstractMappedMinecraftProvider<M> permits IntermediaryMinecraftProvider.MergedImpl, IntermediaryMinecraftProvider.SingleJarImpl, IntermediaryMinecraftProvider.SplitImpl {
public IntermediaryMinecraftProvider(ConfigContext configContext, M minecraftProvider) {
super(configContext, minecraftProvider);
public IntermediaryMinecraftProvider(Project project, M minecraftProvider) {
super(project, minecraftProvider);
}
@Override
@@ -52,8 +52,8 @@ public abstract sealed class IntermediaryMinecraftProvider<M extends MinecraftPr
}
public static final class MergedImpl extends IntermediaryMinecraftProvider<MergedMinecraftProvider> implements Merged {
public MergedImpl(ConfigContext configContext, MergedMinecraftProvider minecraftProvider) {
super(configContext, minecraftProvider);
public MergedImpl(Project project, MergedMinecraftProvider minecraftProvider) {
super(project, minecraftProvider);
}
@Override
@@ -65,8 +65,8 @@ public abstract sealed class IntermediaryMinecraftProvider<M extends MinecraftPr
}
public static final class SplitImpl extends IntermediaryMinecraftProvider<SplitMinecraftProvider> implements Split {
public SplitImpl(ConfigContext configContext, SplitMinecraftProvider minecraftProvider) {
super(configContext, minecraftProvider);
public SplitImpl(Project project, SplitMinecraftProvider minecraftProvider) {
super(project, minecraftProvider);
}
@Override
@@ -86,17 +86,17 @@ public abstract sealed class IntermediaryMinecraftProvider<M extends MinecraftPr
public static final class SingleJarImpl extends IntermediaryMinecraftProvider<SingleJarMinecraftProvider> implements SingleJar {
private final SingleJarEnvType env;
private SingleJarImpl(ConfigContext configContext, SingleJarMinecraftProvider minecraftProvider, SingleJarEnvType env) {
super(configContext, minecraftProvider);
private SingleJarImpl(Project project, SingleJarMinecraftProvider minecraftProvider, SingleJarEnvType env) {
super(project, minecraftProvider);
this.env = env;
}
public static SingleJarImpl server(ConfigContext configContext, SingleJarMinecraftProvider minecraftProvider) {
return new SingleJarImpl(configContext, minecraftProvider, SingleJarEnvType.SERVER);
public static SingleJarImpl server(Project project, SingleJarMinecraftProvider minecraftProvider) {
return new SingleJarImpl(project, minecraftProvider, SingleJarEnvType.SERVER);
}
public static SingleJarImpl client(ConfigContext configContext, SingleJarMinecraftProvider minecraftProvider) {
return new SingleJarImpl(configContext, minecraftProvider, SingleJarEnvType.CLIENT);
public static SingleJarImpl client(Project project, SingleJarMinecraftProvider minecraftProvider) {
return new SingleJarImpl(project, minecraftProvider, SingleJarEnvType.CLIENT);
}
@Override

View File

@@ -27,9 +27,9 @@ package net.fabricmc.loom.configuration.providers.minecraft.mapped;
import java.util.List;
import dev.architectury.tinyremapper.TinyRemapper;
import org.gradle.api.Project;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.configuration.ConfigContext;
import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.SingleJarEnvType;
@@ -37,8 +37,8 @@ import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftPro
import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider;
public abstract class NamedMinecraftProvider<M extends MinecraftProvider> extends AbstractMappedMinecraftProvider<M> {
public NamedMinecraftProvider(ConfigContext configContext, M minecraftProvider) {
super(configContext, minecraftProvider);
public NamedMinecraftProvider(Project project, M minecraftProvider) {
super(project, minecraftProvider);
}
@Override
@@ -52,8 +52,8 @@ public abstract class NamedMinecraftProvider<M extends MinecraftProvider> extend
}
public static final class MergedImpl extends NamedMinecraftProvider<MergedMinecraftProvider> implements Merged {
public MergedImpl(ConfigContext configContext, MergedMinecraftProvider minecraftProvider) {
super(configContext, minecraftProvider);
public MergedImpl(Project project, MergedMinecraftProvider minecraftProvider) {
super(project, minecraftProvider);
}
@Override
@@ -70,8 +70,8 @@ public abstract class NamedMinecraftProvider<M extends MinecraftProvider> extend
}
public static final class SplitImpl extends NamedMinecraftProvider<SplitMinecraftProvider> implements Split {
public SplitImpl(ConfigContext configContext, SplitMinecraftProvider minecraftProvider) {
super(configContext, minecraftProvider);
public SplitImpl(Project project, SplitMinecraftProvider minecraftProvider) {
super(project, minecraftProvider);
}
@Override
@@ -96,17 +96,17 @@ public abstract class NamedMinecraftProvider<M extends MinecraftProvider> extend
public static final class SingleJarImpl extends NamedMinecraftProvider<SingleJarMinecraftProvider> implements SingleJar {
private final SingleJarEnvType env;
private SingleJarImpl(ConfigContext configContext, SingleJarMinecraftProvider minecraftProvider, SingleJarEnvType env) {
super(configContext, minecraftProvider);
private SingleJarImpl(Project project, SingleJarMinecraftProvider minecraftProvider, SingleJarEnvType env) {
super(project, minecraftProvider);
this.env = env;
}
public static SingleJarImpl server(ConfigContext configContext, SingleJarMinecraftProvider minecraftProvider) {
return new SingleJarImpl(configContext, minecraftProvider, SingleJarEnvType.SERVER);
public static SingleJarImpl server(Project project, SingleJarMinecraftProvider minecraftProvider) {
return new SingleJarImpl(project, minecraftProvider, SingleJarEnvType.SERVER);
}
public static SingleJarImpl client(ConfigContext configContext, SingleJarMinecraftProvider minecraftProvider) {
return new SingleJarImpl(configContext, minecraftProvider, SingleJarEnvType.CLIENT);
public static SingleJarImpl client(Project project, SingleJarMinecraftProvider minecraftProvider) {
return new SingleJarImpl(project, minecraftProvider, SingleJarEnvType.CLIENT);
}
@Override

View File

@@ -29,10 +29,14 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.gradle.api.Project;
import net.fabricmc.loom.configuration.ConfigContext;
import net.fabricmc.loom.configuration.mods.dependency.LocalMavenHelper;
import net.fabricmc.loom.configuration.processors.MinecraftJarProcessorManager;
import net.fabricmc.loom.configuration.processors.ProcessorContextImpl;
@@ -49,26 +53,31 @@ public abstract class ProcessedNamedMinecraftProvider<M extends MinecraftProvide
private final MinecraftJarProcessorManager jarProcessorManager;
public ProcessedNamedMinecraftProvider(P parentMinecraftProvide, MinecraftJarProcessorManager jarProcessorManager) {
super(parentMinecraftProvide.getConfigContext(), parentMinecraftProvide.getMinecraftProvider());
super(parentMinecraftProvide.getProject(), parentMinecraftProvide.getMinecraftProvider());
this.parentMinecraftProvider = parentMinecraftProvide;
this.jarProcessorManager = Objects.requireNonNull(jarProcessorManager);
}
@Override
public void provide(boolean applyDependencies) throws Exception {
parentMinecraftProvider.provide(false);
public List<MinecraftJar> provide(ProvideContext context) throws Exception {
parentMinecraftProvider.provide(context.withApplyDependencies(false));
boolean requiresProcessing = parentMinecraftProvider.getMinecraftJars().stream()
boolean requiresProcessing = context.refreshOutputs() || parentMinecraftProvider.getMinecraftJars().stream()
.map(this::getProcessedPath)
.anyMatch(jarProcessorManager::requiresProcessingJar);
final Map<MinecraftJar, MinecraftJar> minecraftJarOutputMap = parentMinecraftProvider.getMinecraftJars().stream()
.collect(Collectors.toMap(Function.identity(), this::getProcessedJar));
if (requiresProcessing) {
processJars();
processJars(minecraftJarOutputMap, context.configContext());
}
if (applyDependencies) {
if (context.applyDependencies()) {
applyDependencies();
}
return List.copyOf(minecraftJarOutputMap.values());
}
@Override
@@ -76,14 +85,17 @@ public abstract class ProcessedNamedMinecraftProvider<M extends MinecraftProvide
return MavenScope.LOCAL;
}
private void processJars() throws IOException {
for (MinecraftJar minecraftJar : parentMinecraftProvider.getMinecraftJars()) {
final MinecraftJar outputJar = getProcessedJar(minecraftJar);
private void processJars(Map<MinecraftJar, MinecraftJar> minecraftJarMap, ConfigContext configContext) throws IOException {
for (Map.Entry<MinecraftJar, MinecraftJar> entry : minecraftJarMap.entrySet()) {
final MinecraftJar minecraftJar = entry.getKey();
final MinecraftJar outputJar = entry.getValue();
deleteSimilarJars(outputJar.getPath());
final LocalMavenHelper mavenHelper = getMavenHelper(minecraftJar.getName());
final Path outputPath = mavenHelper.copyToMaven(minecraftJar.getPath(), null);
assert outputJar.getPath().equals(outputPath);
jarProcessorManager.processJar(outputPath, new ProcessorContextImpl(configContext, minecraftJar));
}
}

View File

@@ -35,6 +35,7 @@ import com.google.common.base.Suppliers;
import org.gradle.api.Project;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.FileCollection;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Provider;
import net.fabricmc.loom.LoomGradleExtension;
@@ -49,6 +50,7 @@ import net.fabricmc.loom.configuration.providers.forge.ForgeRunsProvider;
import net.fabricmc.loom.configuration.providers.mappings.IntermediaryMappingsProvider;
import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration;
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.configuration.providers.minecraft.mapped.SrgMinecraftProvider;
@@ -76,6 +78,7 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
private InstallerData installerData;
private boolean refreshDeps;
private Provider<Boolean> multiProjectOptimisation;
private final ListProperty<LibraryProcessorManager.LibraryProcessorFactory> libraryProcessorFactories;
// +-------------------+
// | Architectury Loom |
@@ -103,6 +106,9 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
refreshDeps = manualRefreshDeps();
multiProjectOptimisation = GradleUtils.getBooleanPropertyProvider(project, Constants.Properties.MULTI_PROJECT_OPTIMISATION);
libraryProcessorFactories = project.getObjects().listProperty(LibraryProcessorManager.LibraryProcessorFactory.class);
libraryProcessorFactories.addAll(LibraryProcessorManager.DEFAULT_LIBRARY_PROCESSORS);
libraryProcessorFactories.finalizeValueOnRead();
if (refreshDeps) {
project.getLogger().lifecycle("Refresh dependencies is in use, loom will be significantly slower.");
@@ -262,6 +268,11 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
return multiProjectOptimisation.getOrElse(false);
}
@Override
public ListProperty<LibraryProcessorManager.LibraryProcessorFactory> getLibraryProcessors() {
return libraryProcessorFactories;
}
@Override
protected <T extends IntermediateMappingsProvider> void configureIntermediateMappingsProviderInternal(T provider) {
provider.getMinecraftVersion().set(getProject().provider(() -> getMinecraftProvider().minecraftVersion()));

View File

@@ -38,18 +38,25 @@ import java.util.stream.Collectors;
import org.gradle.api.Project;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.FileCollection;
import org.gradle.api.provider.Property;
import org.gradle.api.services.ServiceReference;
import org.gradle.api.specs.Spec;
import org.gradle.api.tasks.JavaExec;
import org.jetbrains.annotations.NotNull;
import net.fabricmc.loom.configuration.ide.RunConfig;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.gradle.SyncTaskBuildService;
public abstract class AbstractRunTask extends JavaExec {
private final RunConfig config;
// We control the classpath, as we use a ArgFile to pass it over the command line: https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html#commandlineargfile
private final ConfigurableFileCollection classpath = getProject().getObjects().fileCollection();
// Prevent Gradle from running two run tasks in parallel
@ServiceReference(SyncTaskBuildService.NAME)
abstract Property<SyncTaskBuildService> getSyncTask();
public AbstractRunTask(Function<Project, RunConfig> configProvider) {
super();
setGroup(Constants.TaskGroup.FABRIC);

View File

@@ -26,6 +26,7 @@ package net.fabricmc.loom.task;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.io.Writer;
@@ -49,10 +50,14 @@ 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.services.ServiceReference;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;
import org.gradle.process.ExecOperations;
import org.gradle.process.ExecResult;
import org.gradle.work.DisableCachingByDefault;
import org.gradle.workers.WorkAction;
import org.gradle.workers.WorkParameters;
@@ -68,6 +73,8 @@ import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.configuration.ConfigContextImpl;
import net.fabricmc.loom.configuration.processors.MappingProcessorContextImpl;
import net.fabricmc.loom.configuration.processors.MinecraftJarProcessorManager;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJar;
import net.fabricmc.loom.configuration.providers.minecraft.mapped.AbstractMappedMinecraftProvider;
import net.fabricmc.loom.configuration.sources.ForgeSourcesRemapper;
import net.fabricmc.loom.decompilers.LineNumberRemapper;
import net.fabricmc.loom.decompilers.linemap.LineMapClassFilter;
@@ -76,6 +83,7 @@ import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.FileSystemUtil;
import net.fabricmc.loom.util.IOStringConsumer;
import net.fabricmc.loom.util.Platform;
import net.fabricmc.loom.util.gradle.SyncTaskBuildService;
import net.fabricmc.loom.util.gradle.ThreadedProgressLoggerConsumer;
import net.fabricmc.loom.util.gradle.ThreadedSimpleProgressLogger;
import net.fabricmc.loom.util.gradle.WorkerDaemonClientsManagerHelper;
@@ -92,16 +100,10 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
private final DecompilerOptions decompilerOptions;
/**
* The jar to decompile, can be the unpick jar.
* The jar name to decompile, {@link MinecraftJar#getName()}.
*/
@InputFile
public abstract RegularFileProperty getInputJar();
/**
* The jar used at runtime.
*/
@InputFile
public abstract RegularFileProperty getRuntimeJar();
@Input
public abstract Property<String> getInputJarName();
@InputFiles
public abstract ConfigurableFileCollection getClasspath();
@@ -109,12 +111,33 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
@OutputFile
public abstract RegularFileProperty getOutputJar();
// Unpick
@InputFile
public abstract RegularFileProperty getUnpickDefinitions();
@InputFiles
public abstract ConfigurableFileCollection getUnpickConstantJar();
@InputFiles
public abstract ConfigurableFileCollection getUnpickClasspath();
@OutputFile
public abstract RegularFileProperty getUnpickOutputJar();
// Injects
@Inject
public abstract WorkerExecutor getWorkerExecutor();
@Inject
public abstract ExecOperations getExecOperations();
@Inject
public abstract WorkerDaemonClientsManager getWorkerDaemonClientsManager();
// Prevent Gradle from running two gen sources tasks in parallel
@ServiceReference(SyncTaskBuildService.NAME)
abstract Property<SyncTaskBuildService> getSyncTask();
@Inject
public GenerateSourcesTask(DecompilerOptions decompilerOptions) {
this.decompilerOptions = decompilerOptions;
@@ -122,8 +145,6 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
getOutputs().upToDateWhen((o) -> false);
getClasspath().from(decompilerOptions.getClasspath()).finalizeValueOnRead();
dependsOn(decompilerOptions.getClasspath().getBuiltBy());
getOutputJar().fileProvider(getProject().provider(() -> getMappedJarFileWithSuffix("-sources.jar")));
}
@TaskAction
@@ -134,10 +155,20 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
throw new UnsupportedOperationException("GenSources task requires a 64bit JVM to run due to the memory requirements.");
}
final MinecraftJar minecraftJar = rebuildInputJar();
// Input jar is the jar to decompile, this may be unpicked.
Path inputJar = minecraftJar.getPath();
// Runtime jar is the jar used to run the game
final Path runtimeJar = inputJar;
if (getUnpickDefinitions().isPresent()) {
inputJar = unpickJar(inputJar);
}
if (!platform.supportsUnixDomainSockets()) {
getProject().getLogger().warn("Decompile worker logging disabled as Unix Domain Sockets is not supported on your operating system.");
doWork(null);
doWork(null, inputJar, runtimeJar);
return;
}
@@ -147,7 +178,7 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
try (ThreadedProgressLoggerConsumer loggerConsumer = new ThreadedProgressLoggerConsumer(getProject(), decompilerOptions.getName(), "Decompiling minecraft sources");
IPCServer logReceiver = new IPCServer(ipcPath, loggerConsumer)) {
doWork(logReceiver);
doWork(logReceiver, inputJar, runtimeJar);
} catch (InterruptedException e) {
throw new RuntimeException("Failed to shutdown log receiver", e);
} finally {
@@ -162,18 +193,94 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
}
}
private void doWork(@Nullable IPCServer ipcServer) {
// Re-run the named minecraft provider to give us a fresh jar to decompile.
// This prevents re-applying line maps on an existing jar.
private MinecraftJar rebuildInputJar() {
final List<MinecraftJar> minecraftJars;
try (var serviceManager = new ScopedSharedServiceManager()) {
final var configContext = new ConfigContextImpl(getProject(), serviceManager, getExtension());
final var provideContext = new AbstractMappedMinecraftProvider.ProvideContext(false, true, configContext);
minecraftJars = getExtension().getNamedMinecraftProvider().provide(provideContext);
} catch (Exception e) {
throw new RuntimeException("Failed to rebuild input jars", e);
}
for (MinecraftJar minecraftJar : minecraftJars) {
if (minecraftJar.getName().equals(getInputJarName().get())) {
return minecraftJar;
}
}
throw new IllegalStateException("Could not find minecraft jar (%s) but got (%s)".formatted(
getInputJarName().get(),
minecraftJars.stream().map(MinecraftJar::getName).collect(Collectors.joining(", ")))
);
}
private Path unpickJar(Path inputJar) {
final Path outputJar = getUnpickOutputJar().get().getAsFile().toPath();
final List<String> args = getUnpickArgs(inputJar, outputJar);
ExecResult result = getExecOperations().javaexec(spec -> {
spec.getMainClass().set("daomephsta.unpick.cli.Main");
spec.classpath(getProject().getConfigurations().getByName(Constants.Configurations.UNPICK_CLASSPATH));
spec.args(args);
spec.systemProperty("java.util.logging.config.file", writeUnpickLogConfig().getAbsolutePath());
});
result.rethrowFailure();
return outputJar;
}
private List<String> getUnpickArgs(Path inputJar, Path outputJar) {
var fileArgs = new ArrayList<File>();
fileArgs.add(inputJar.toFile());
fileArgs.add(outputJar.toFile());
fileArgs.add(getUnpickDefinitions().get().getAsFile());
fileArgs.add(getUnpickConstantJar().getSingleFile());
// Classpath
for (Path minecraftJar : getExtension().getMinecraftJars(MappingsNamespace.NAMED)) {
fileArgs.add(minecraftJar.toFile());
}
for (File file : getUnpickClasspath()) {
fileArgs.add(file);
}
return fileArgs.stream()
.map(File::getAbsolutePath)
.toList();
}
private File writeUnpickLogConfig() {
final File unpickLoggingConfigFile = getExtension().getFiles().getUnpickLoggingConfigFile();
try (InputStream is = GenerateSourcesTask.class.getClassLoader().getResourceAsStream("unpick-logging.properties")) {
Files.deleteIfExists(unpickLoggingConfigFile.toPath());
Files.copy(Objects.requireNonNull(is), unpickLoggingConfigFile.toPath());
} catch (IOException e) {
throw new org.gradle.api.UncheckedIOException("Failed to copy unpick logging config", e);
}
return unpickLoggingConfigFile;
}
private void doWork(@Nullable IPCServer ipcServer, Path inputJar, Path runtimeJar) {
final String jvmMarkerValue = UUID.randomUUID().toString();
final WorkQueue workQueue = createWorkQueue(jvmMarkerValue);
workQueue.submit(DecompileAction.class, params -> {
params.getDecompilerOptions().set(decompilerOptions.toDto());
params.getInputJar().set(getInputJar());
params.getRuntimeJar().set(getRuntimeJar());
params.getInputJar().set(inputJar.toFile());
params.getRuntimeJar().set(runtimeJar.toFile());
params.getSourcesDestinationJar().set(getOutputJar());
params.getLinemap().set(getMappedJarFileWithSuffix("-sources.lmap"));
params.getLinemapJar().set(getMappedJarFileWithSuffix("-linemapped.jar"));
params.getLinemap().set(getMappedJarFileWithSuffix("-sources.lmap", runtimeJar));
params.getLinemapJar().set(getMappedJarFileWithSuffix("-linemapped.jar", runtimeJar));
params.getMappings().set(getMappings().toFile());
if (ipcServer != null) {
@@ -342,16 +449,12 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
}
}
private File getMappedJarFileWithSuffix(String suffix) {
return getMappedJarFileWithSuffix(getRuntimeJar(), suffix);
}
static File getMappedJarFileWithSuffix(RegularFileProperty runtimeJar, String suffix) {
return getMappedJarFileWithSuffix(runtimeJar.get().getAsFile(), suffix);
return getMappedJarFileWithSuffix(suffix, runtimeJar.get().getAsFile().toPath());
}
public static File getMappedJarFileWithSuffix(File runtimeJar, String suffix) {
String path = runtimeJar.getAbsolutePath();
public static File getMappedJarFileWithSuffix(String suffix, Path runtimeJar) {
final String path = runtimeJar.toFile().getAbsolutePath();
if (!path.toLowerCase(Locale.ROOT).endsWith(".jar")) {
throw new RuntimeException("Invalid mapped JAR path: " + path);

View File

@@ -165,6 +165,10 @@ public abstract class RemapJarTask extends AbstractRemapJarTask {
if (getLoomExtension().multiProjectOptimisation()) {
setupPreparationTask();
}
// Make outputs reproducible by default
setReproducibleFileOrder(true);
setPreserveFileTimestamps(false);
}
private void setupPreparationTask() {

View File

@@ -48,6 +48,7 @@ import net.fabricmc.loom.util.PropertyUtil;
import net.fabricmc.loom.util.aw2at.Aw2At;
import net.fabricmc.loom.util.gradle.GradleUtils;
import net.fabricmc.loom.util.gradle.SourceSetHelper;
import net.fabricmc.loom.util.gradle.SyncTaskBuildService;
public abstract class RemapTaskConfiguration implements Runnable {
public static final String REMAP_JAR_TASK_NAME = "remapJar";
@@ -68,6 +69,8 @@ public abstract class RemapTaskConfiguration implements Runnable {
public void run() {
final LoomGradleExtension extension = LoomGradleExtension.get(getProject());
SyncTaskBuildService.register(getProject());
if (GradleUtils.getBooleanProperty(getProject(), Constants.Properties.DONT_REMAP)) {
extension.getUnmappedModCollection().from(getTasks().getByName(JavaPlugin.JAR_TASK_NAME));
return;

View File

@@ -29,7 +29,7 @@ import javax.inject.Inject;
import net.fabricmc.loom.configuration.ide.RunConfig;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
public class RunGameTask extends AbstractRunTask {
public abstract class RunGameTask extends AbstractRunTask {
@Inject
public RunGameTask(RunConfigSettings settings) {
super(proj -> RunConfig.runConfig(proj, settings));

View File

@@ -1,118 +0,0 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 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.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.inject.Inject;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.OutputFile;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.extension.LoomFiles;
import net.fabricmc.loom.util.Constants;
public abstract class UnpickJarTask extends JavaExec {
@InputFile
public abstract RegularFileProperty getInputJar();
@InputFile
public abstract RegularFileProperty getUnpickDefinitions();
@InputFiles
// Only 1 file, but it comes from a configuration
public abstract ConfigurableFileCollection getConstantJar();
@InputFiles
public abstract ConfigurableFileCollection getUnpickClasspath();
@OutputFile
public abstract RegularFileProperty getOutputJar();
@Inject
public UnpickJarTask() {
classpath(getProject().getConfigurations().getByName(Constants.Configurations.UNPICK_CLASSPATH));
getMainClass().set("daomephsta.unpick.cli.Main");
getConstantJar().setFrom(getProject().getConfigurations().getByName(Constants.Configurations.MAPPING_CONSTANTS));
getUnpickClasspath().setFrom(getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_COMPILE_LIBRARIES));
getUnpickClasspath().from(getProject().getConfigurations().getByName(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED));
}
@Override
public void exec() {
fileArg(getInputJar().get().getAsFile(), getOutputJar().get().getAsFile(), getUnpickDefinitions().get().getAsFile());
fileArg(getConstantJar().getSingleFile());
// Classpath
for (Path minecraftJar : getExtension().getMinecraftJars(MappingsNamespace.NAMED)) {
fileArg(minecraftJar.toFile());
}
for (File file : getUnpickClasspath()) {
fileArg(file);
}
writeUnpickLogConfig();
systemProperty("java.util.logging.config.file", getDirectories().getUnpickLoggingConfigFile().getAbsolutePath());
super.exec();
}
private void writeUnpickLogConfig() {
try (InputStream is = UnpickJarTask.class.getClassLoader().getResourceAsStream("unpick-logging.properties")) {
Files.deleteIfExists(getDirectories().getUnpickLoggingConfigFile().toPath());
Files.copy(is, getDirectories().getUnpickLoggingConfigFile().toPath());
} catch (IOException e) {
throw new RuntimeException("Failed to copy unpick logging config", e);
}
}
private void fileArg(File... files) {
for (File file : files) {
args(file.getAbsolutePath());
}
}
@Internal
protected LoomGradleExtension getExtension() {
return LoomGradleExtension.get(getProject());
}
private LoomFiles getDirectories() {
return getExtension().getFiles();
}
}

View File

@@ -157,6 +157,7 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask {
final boolean plainConsole = getProject().getGradle().getStartParameter().getConsoleOutput() == ConsoleOutput.Plain;
final boolean ansiSupportedIDE = new File(getProject().getRootDir(), ".vscode").exists()
|| new File(getProject().getRootDir(), ".idea").exists()
|| new File(getProject().getRootDir(), ".project").exists()
|| (Arrays.stream(getProject().getRootDir().listFiles()).anyMatch(file -> file.getName().endsWith(".iws")));
//Enable ansi by default for idea and vscode when gradle is not ran with plain console.

View File

@@ -45,6 +45,51 @@ public class ZipReprocessorUtil {
private ZipReprocessorUtil() { }
private static final String MANIFEST_LOCATION = "META-INF/MANIFEST.MF";
private static final String META_INF = "META-INF/";
// See https://docs.oracle.com/en/java/javase/20/docs/specs/jar/jar.html#signed-jar-file
private static boolean isSpecialFile(String zipEntryName) {
if (!zipEntryName.startsWith(META_INF)) {
return false;
}
String[] parts = zipEntryName.split("/");
if (parts.length != 2) {
return false;
}
return parts[1].startsWith("SIG-")
|| parts[1].endsWith(".SF")
|| parts[1].endsWith(".DSA")
|| parts[1].endsWith(".RSA")
|| parts[1].endsWith(".EC");
}
private static int specialOrdering(String name1, String name2) {
if (name1.equals(name2)) {
return 0;
} else if (name1.equals(MANIFEST_LOCATION)) {
return -1;
} else if (name2.equals(MANIFEST_LOCATION)) {
return 1;
}
boolean isName1Special = isSpecialFile(name1);
boolean isName2Special = isSpecialFile(name2);
if (isName1Special && isName2Special) {
return name1.compareTo(name2);
} else if (isName1Special) {
return -1;
} else if (isName2Special) {
return 1;
}
return name1.compareTo(name2);
}
public static void reprocessZip(File file, boolean reproducibleFileOrder, boolean preserveFileTimestamps) throws IOException {
if (!reproducibleFileOrder && preserveFileTimestamps) {
return;
@@ -54,7 +99,7 @@ public class ZipReprocessorUtil {
ZipEntry[] entries;
if (reproducibleFileOrder) {
entries = zipFile.stream().sorted(Comparator.comparing(ZipEntry::getName)).toArray(ZipEntry[]::new);
entries = zipFile.stream().sorted(Comparator.comparing(ZipEntry::getName, ZipReprocessorUtil::specialOrdering)).toArray(ZipEntry[]::new);
} else {
entries = zipFile.stream().toArray(ZipEntry[]::new);
}

View File

@@ -0,0 +1,52 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2018-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.util.gradle;
import org.gradle.api.Project;
import org.gradle.api.services.BuildService;
import org.gradle.api.services.BuildServiceParameters;
/**
* Add the following snippet to task to prevent tasks running asynchronously with any other task with the same build service.
*
* <pre>{@code
* @ServiceReference(SyncTaskBuildService.NAME)
* abstract Property<SyncTaskBuildService> getSyncTask();
* }</pre>
*/
public abstract class SyncTaskBuildService implements BuildService<SyncTaskBuildService.Params> {
public static final String NAME = "loomSyncTask";
public static void register(Project project) {
project.getGradle().getSharedServices().registerIfAbsent(
NAME,
SyncTaskBuildService.class,
spec -> spec.getMaxParallelUsages().set(1)
);
}
public interface Params extends BuildServiceParameters {
}
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2022 FabricMC
* Copyright (c) 2022-2023 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
@@ -24,15 +24,17 @@
package net.fabricmc.loom.util.kotlin;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import kotlinx.metadata.jvm.KotlinClassMetadata;
import org.gradle.api.Project;
import org.jetbrains.annotations.VisibleForTesting;
public class KotlinPluginUtils {
private static final String KOTLIN_PLUGIN_ID = "org.jetbrains.kotlin.jvm";
private static final Pattern VERSION_PATTERN = Pattern.compile("\\((.*?)\\)");
private static final Pattern VERSION_PATTERN = Pattern.compile("\\((?<version>.*?)\\)|(?<newVersion>^[^(]*$)");
public static boolean hasKotlinPlugin(Project project) {
return project.getPluginManager().hasPlugin(KOTLIN_PLUGIN_ID);
@@ -43,15 +45,27 @@ public class KotlinPluginUtils {
/*
1.7.0-RC-release-217(1.7.0-RC)
1.6.21-release-334(1.6.21)
1.9.0-Beta
*/
final String implVersion = kotlinPluginClass.getPackage().getImplementationVersion();
return parseKotlinVersion(implVersion);
}
@VisibleForTesting
public static String parseKotlinVersion(String implVersion) {
final Matcher matcher = VERSION_PATTERN.matcher(implVersion);
if (!matcher.find()) {
throw new IllegalStateException("Unable to match Kotlin version from: " + implVersion);
}
return matcher.group(1);
String version = matcher.group("version");
if (version == null) {
version = matcher.group("newVersion");
}
return Objects.requireNonNull(version);
}
public static String getKotlinMetadataVersion() {

View File

@@ -0,0 +1,230 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2023 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 java.nio.charset.StandardCharsets
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletionStage
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import com.microsoft.java.debug.core.DebugUtility
import com.microsoft.java.debug.core.IDebugSession
import com.sun.jdi.Bootstrap
import com.sun.jdi.event.BreakpointEvent
import groovy.transform.CompileStatic
import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.functions.Function
import spock.lang.Specification
import spock.lang.Timeout
import net.fabricmc.loom.test.util.GradleProjectTestTrait
import net.fabricmc.loom.util.ZipUtils
import static net.fabricmc.loom.test.LoomTestConstants.PRE_RELEASE_GRADLE
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
@Timeout(value = 30, unit = TimeUnit.MINUTES)
class DebugLineNumbersTest extends Specification implements GradleProjectTestTrait {
static final String MAPPINGS = "1.20.1-net.fabricmc.yarn.1_20_1.1.20.1+build.1-v2"
static final Map<String, Integer> BREAKPOINTS = [
"net.minecraft.server.dedicated.ServerPropertiesLoader": 16,
"net.minecraft.server.dedicated.MinecraftDedicatedServer": 107,
"net.minecraft.registry.RegistryOps": 67
]
def "Debug test"() {
setup:
def gradle = gradleProject(project: "minimalBase", version: PRE_RELEASE_GRADLE)
gradle.buildGradle << '''
loom {
// Just test with the server, no need to also decompile the client
serverOnlyMinecraftJar()
}
dependencies {
minecraft "com.mojang:minecraft:1.20.1"
mappings "net.fabricmc:yarn:1.20.1+build.1:v2"
modImplementation 'net.fabricmc:fabric-loader:0.14.21'
}
runServer {
debugOptions {
enabled = true
port = 8050
host = "*"
server = true
suspend = true
}
}
'''
when:
// First generate sources
def genSources = gradle.run(task: "genSources")
genSources.task(":genSources").outcome == SUCCESS
// Print out the source of the file
def lines = getClassSource(gradle, "net/minecraft/server/dedicated/ServerPropertiesLoader.java").lines().toList()
int l = 1
for (final def line in lines) {
//println(l++ + ": " + line)
}
// I agree
def runDir = new File(gradle.projectDir, "run")
runDir.mkdirs()
new File(runDir, "eula.txt") << "eula=true"
new File(runDir, "server.properties") << ""
// Run the gradle task off thread
def executor = Executors.newSingleThreadExecutor()
def resultCF = CompletableFuture.supplyAsync({
gradle.run(task: "runServer")
}, executor)
Map<String, CompletableFuture<BreakpointEvent>> futures
def debugger = new Debugger(openDebugSession())
try {
futures = BREAKPOINTS.collectEntries { className, line ->
[(className): debugger.addBreakpoint(className, line)]
}
// Start running the game, the process has been suspended until this point.
debugger.start()
// Wait for all of the breakpoints
futures.values().forEach {
def result = it.get()
println("Breakpoint triggered: ${result.location()}")
}
} finally {
// Close the debugger and target process
debugger.close()
}
def result = resultCF.get()
executor.shutdown()
then:
result.task(":runServer").outcome == SUCCESS
BREAKPOINTS.forEach { className, line ->
futures[className].get().location().lineNumber() == line
}
}
private static String getClassSource(GradleProject gradle, String classname, String mappings = MAPPINGS) {
File sourcesJar = gradle.getGeneratedSources(mappings, "serveronly")
return new String(ZipUtils.unpack(sourcesJar.toPath(), classname), StandardCharsets.UTF_8)
}
private static IDebugSession openDebugSession() {
int timeout = 5
int maxTimeout = 120 / timeout
for (i in 0..maxTimeout) {
try {
return DebugUtility.attach(
Bootstrap.virtualMachineManager(),
"127.0.0.1",
8050,
timeout
)
} catch (ConnectException e) {
Thread.sleep(timeout * 1000)
if (i == maxTimeout) {
throw e
}
}
}
throw new IllegalStateException()
}
@CompileStatic // Makes RxJava somewhat usable in Groovy
class Debugger implements AutoCloseable {
final IDebugSession debugSession
Debugger(IDebugSession debugSession) {
this.debugSession = debugSession
debugSession.eventHub.events().subscribe({ }) {
// Manually bail out, as it seems this can be called after close()
it.printStackTrace()
System.exit(-1)
}
}
CompletableFuture<BreakpointEvent> addBreakpoint(String className, int lineNumber) {
def breakpoint = debugSession.createBreakpoint(
className,
lineNumber,
0,
null,
null
)
// Wait for the breakpoint to be installed
return breakpoint.install().thenCompose {
// Then compose with the first result
return breakpointEvents()
.filter { event ->
event.location().sourcePath().replaceAll("[\\\\/]", ".") == className + ".java" &&
event.location().lineNumber() == lineNumber
}
.firstElement()
.to(toCompletionStage())
}
}
private static <T> Function<Maybe<T>, CompletionStage<T>> toCompletionStage() {
return { Maybe<T> m ->
CompletableFuture<T> cf = new CompletableFuture<>()
m.subscribe(cf.&complete, cf.&completeExceptionally) { cf.complete(null) }
return cf
}
}
Observable<BreakpointEvent> breakpointEvents() {
return debugSession.getEventHub().breakpointEvents()
.map {
it.shouldResume = true
it.event as BreakpointEvent
}
}
void start() {
debugSession.start()
}
@Override
void close() throws Exception {
debugSession.terminate()
debugSession.eventHub.close()
}
}
}

View File

@@ -55,13 +55,13 @@ class ReproducibleBuildTest extends Specification implements GradleProjectTestTr
where:
version | modHash | sourceHash
DEFAULT_GRADLE | "ed3306ef60f434c55048cba8de5dab95" | [
"0d9eec9248d93eb6ec4a1cd4d927e609",
"436bf54ef015576b0a338d55d9a0bb82"
DEFAULT_GRADLE | "174c9b52f4bc6d489548d11b42e853cf" | [
"5e6e56df303b4fbaaef372d6f143dbfc",
"92b6fbffd0bd14bf3c626750eb86c264"
]
PRE_RELEASE_GRADLE | "ed3306ef60f434c55048cba8de5dab95" | [
"0d9eec9248d93eb6ec4a1cd4d927e609",
"436bf54ef015576b0a338d55d9a0bb82"
PRE_RELEASE_GRADLE | "174c9b52f4bc6d489548d11b42e853cf" | [
"5e6e56df303b4fbaaef372d6f143dbfc",
"92b6fbffd0bd14bf3c626750eb86c264"
]
}

View File

@@ -252,7 +252,7 @@ class DownloadFileTest extends DownloadTest {
def "Progress: String"() {
setup:
server.get("/progressString") {
server.get("/progressFile") {
it.result("Hello World")
}

View File

@@ -25,17 +25,14 @@
package net.fabricmc.loom.test.unit.download
import io.javalin.Javalin
import spock.lang.Shared
import spock.lang.Specification
abstract class DownloadTest extends Specification {
static final String PATH = "http://127.0.0.1:9081"
@Shared
Javalin server = Javalin.create { config ->
}.start(9081)
Javalin server = Javalin.create().start(9081)
def cleanupSpec() {
def cleanup() {
server.stop()
}
}

View File

@@ -0,0 +1,45 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2023 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.unit.kotlin
import spock.lang.Specification
import net.fabricmc.loom.util.kotlin.KotlinPluginUtils
class KotlinPluginUtilsTest extends Specification {
def "parseKotlinVersion"() {
when:
def parsedVersion = KotlinPluginUtils.parseKotlinVersion(version)
then:
parsedVersion == expected
where:
version | expected
"1.7.0-RC-release-217(1.7.0-RC)" | "1.7.0-RC"
"1.6.21-release-334(1.6.21)" | "1.6.21"
"1.9.0-Beta" | "1.9.0-Beta"
}
}

View File

@@ -82,7 +82,7 @@ class LibraryProcessorManagerTest extends LibraryProcessorTest {
when:
def platform = PlatformTestUtils.WINDOWS_X64
def (original, context) = getLibs("1.19.2", platform)
def processed = new LibraryProcessorManager(platform, GradleTestUtil.mockRepositoryHandler(), [
def processed = new LibraryProcessorManager(platform, GradleTestUtil.mockRepositoryHandler(), LibraryProcessorManager.DEFAULT_LIBRARY_PROCESSORS, [
RuntimeLog4jLibraryProcessor.class.simpleName
]).processLibraries(original, context)

View File

@@ -0,0 +1,220 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2023 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.unit.providers
import java.nio.file.Files
import java.nio.file.Path
import org.intellij.lang.annotations.Language
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMetadataProvider
import net.fabricmc.loom.test.LoomTestConstants
import net.fabricmc.loom.test.unit.download.DownloadTest
import net.fabricmc.loom.util.download.Download
class MinecraftMetadataProviderTest extends DownloadTest {
Path testDir
def setup() {
testDir = LoomTestConstants.TEST_DIR.toPath().resolve("MinecraftMetadataProviderTest")
testDir.toFile().deleteDir()
Files.createDirectories(testDir)
}
def "Cached result"() {
setup:
int calls = 0
server.get("/versionManifest") {
it.result(VERSION_MANIFEST_1)
calls++
}
when:
// Test the builtin caching of the metadata provider
def metaProvider = provider("1.20.1")
metaProvider.getVersionMeta()
def meta = metaProvider.getVersionMeta()
// Create a new provider to test download caching
def meta2 = provider("1.20.1").getVersionMeta()
then:
meta.id() == "1.20.1"
meta2.id() == "1.20.1"
calls == 1
}
// Tests a fix to https://github.com/FabricMC/fabric-loom/issues/886
def "Force download with new version"() {
setup:
int calls = 0
server.get("/versionManifest") {
it.result(calls == 0 ? VERSION_MANIFEST_1 : VERSION_MANIFEST_2)
calls++
}
server.get("/experimentalVersionManifest") {
it.result(EXP_VERSION_MANIFEST)
calls++
}
when:
def meta = provider("1.20.1").getVersionMeta()
def meta2 = provider("1.20.1-rc1").getVersionMeta()
then:
meta.id() == "1.20.1"
meta2.id() == "1.20.1-rc1"
calls == 3
}
def "Experimental"() {
setup:
int calls = 0
server.get("/versionManifest") {
it.result(VERSION_MANIFEST_1)
calls++
}
server.get("/experimentalVersionManifest") {
it.result(EXP_VERSION_MANIFEST)
calls++
}
when:
def meta = provider("1.19_deep_dark_experimental_snapshot-1").getVersionMeta()
then:
meta.id() == "1.19_deep_dark_experimental_snapshot-1"
calls == 2
}
def "Custom manifest"() {
setup:
server.get("/customManifest") {
it.result('{"id": "2.0.0"}')
}
when:
def meta = provider("2.0.0", "$PATH/customManifest").getVersionMeta()
then:
meta.id() == "2.0.0"
}
def "Get unknown"() {
setup:
int calls = 0
server.get("/versionManifest") {
it.result(VERSION_MANIFEST_1)
calls++
}
server.get("/experimentalVersionManifest") {
it.result(EXP_VERSION_MANIFEST)
calls++
}
when:
provider("2.0.0").getVersionMeta()
then:
def e = thrown(RuntimeException)
e.message == "Failed to find minecraft version: 2.0.0"
calls == 4
}
private MinecraftMetadataProvider provider(String version, String customUrl = null) {
return new MinecraftMetadataProvider(
options(version, customUrl),
Download.&create
)
}
private MinecraftMetadataProvider.Options options(String version, String customUrl) {
return new MinecraftMetadataProvider.Options(
version,
"$PATH/versionManifest",
"$PATH/experimentalVersionManifest",
customUrl,
testDir.resolve("version_manifest.json"),
testDir.resolve("experimental_version_manifest.json"),
testDir.resolve("${version}.json")
)
}
@Language("json")
private static final String VERSION_MANIFEST_1 = """
{
"latest": {
"release": "1.20.1",
"snapshot": "1.20.1"
},
"versions": [
{
"id": "1.20.1",
"url": "https://piston-meta.mojang.com/v1/packages/715ccf3330885e75b205124f09f8712542cbe7e0/1.20.1.json",
"sha1": "715ccf3330885e75b205124f09f8712542cbe7e0"
}
]
}
"""
@Language("json")
private static final String VERSION_MANIFEST_2 = """
{
"latest": {
"release": "1.20.1",
"snapshot": "1.20.1"
},
"versions": [
{
"id": "1.20.1",
"url": "https://piston-meta.mojang.com/v1/packages/715ccf3330885e75b205124f09f8712542cbe7e0/1.20.1.json",
"sha1": "715ccf3330885e75b205124f09f8712542cbe7e0"
},
{
"id": "1.20.1-rc1",
"url": "https://piston-meta.mojang.com/v1/packages/61c85d1e228b4ca6e48d2da903d2399c12b6a880/1.20.1-rc1.json",
"sha1": "61c85d1e228b4ca6e48d2da903d2399c12b6a880"
}
]
}
"""
@Language("json")
private static final String EXP_VERSION_MANIFEST = """
{
"latest": {
"release": "1.19_deep_dark_experimental_snapshot-1",
"snapshot": "1.19_deep_dark_experimental_snapshot-1"
},
"versions": [
{
"id": "1.19_deep_dark_experimental_snapshot-1",
"url": "https://maven.fabricmc.net/net/minecraft/1_19_deep_dark_experimental_snapshot-1.json",
"sha1": "c5b59acb75db612cf446b4ed4bd59b01e10092d1"
}
]
}
"""
}

View File

@@ -236,8 +236,8 @@ trait GradleProjectTestTrait {
return ZipUtils.unpackNullable(file.toPath(), entryName) != null
}
File getGeneratedSources(String mappings) {
return new File(getGradleHomeDir(), "caches/fabric-loom/minecraftMaven/net/minecraft/minecraft-merged/${mappings}/minecraft-merged-${mappings}-sources.jar")
File getGeneratedSources(String mappings, String jarType = "merged") {
return new File(getGradleHomeDir(), "caches/fabric-loom/minecraftMaven/net/minecraft/minecraft-${jarType}/${mappings}/minecraft-${jarType}-${mappings}-sources.jar")
}
File getGeneratedLocalSources(String mappings) {

View File

@@ -52,12 +52,6 @@ jar {
}
}
// Make Jars Reproducible
tasks.withType(AbstractArchiveTask) {
preserveFileTimestamps = false
reproducibleFileOrder = true
}
remapSourcesJar {
preserveFileTimestamps = false
reproducibleFileOrder = true