From 5c92ebd20ba68b9f55d8cba7b3cded70d37664c0 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Tue, 13 Jun 2023 11:10:08 +0100 Subject: [PATCH 01/13] Loom 1.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a3c14eec..76c2c1a5 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { group = 'net.fabricmc' archivesBaseName = project.name -def baseVersion = '1.2' +def baseVersion = '1.3' def ENV = System.getenv() if (ENV.BUILD_NUMBER) { From af1f2497b5e37f3af0a803adb8993659031fe04c Mon Sep 17 00:00:00 2001 From: modmuss Date: Tue, 13 Jun 2023 11:16:14 +0100 Subject: [PATCH 02/13] Fix Kotlin 1.9.0 beta (#902) * Fix Kotlin 1.9.0 beta * Fix build --- .../loom/util/kotlin/KotlinPluginUtils.java | 20 +++++++-- .../unit/kotlin/KotlinPluginUtilsTest.groovy | 45 +++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 src/test/groovy/net/fabricmc/loom/test/unit/kotlin/KotlinPluginUtilsTest.groovy diff --git a/src/main/java/net/fabricmc/loom/util/kotlin/KotlinPluginUtils.java b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinPluginUtils.java index aab439a6..3129600c 100644 --- a/src/main/java/net/fabricmc/loom/util/kotlin/KotlinPluginUtils.java +++ b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinPluginUtils.java @@ -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("\\((?.*?)\\)|(?^[^(]*$)"); 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() { diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/kotlin/KotlinPluginUtilsTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/kotlin/KotlinPluginUtilsTest.groovy new file mode 100644 index 00000000..1ebd22e8 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/kotlin/KotlinPluginUtilsTest.groovy @@ -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" + } +} From 6e72125c0f02a72633a4b63ebfdfd597136cf0bd Mon Sep 17 00:00:00 2001 From: modmuss Date: Tue, 13 Jun 2023 11:16:56 +0100 Subject: [PATCH 03/13] Dont allow gradle to run game/genSources tasks in parallel (#901) * Dont allow gradle to run game/genSources tasks in parallel * Fix build --- .../fabricmc/loom/task/AbstractRunTask.java | 7 +++ .../loom/task/GenerateSourcesTask.java | 6 +++ .../loom/task/RemapTaskConfiguration.java | 3 ++ .../net/fabricmc/loom/task/RunGameTask.java | 2 +- .../util/gradle/SyncTaskBuildService.java | 52 +++++++++++++++++++ 5 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/fabricmc/loom/util/gradle/SyncTaskBuildService.java diff --git a/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java b/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java index d5ca8e2c..c7c4879e 100644 --- a/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java +++ b/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java @@ -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 getSyncTask(); + public AbstractRunTask(Function configProvider) { super(); setGroup(Constants.TaskGroup.FABRIC); diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index 0ae0a945..7f28716c 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -48,6 +48,7 @@ import javax.inject.Inject; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.Property; +import org.gradle.api.services.ServiceReference; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.OutputFile; @@ -72,6 +73,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; @@ -111,6 +113,10 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { @Inject public abstract WorkerDaemonClientsManager getWorkerDaemonClientsManager(); + // Prevent Gradle from running two gen sources tasks in parallel + @ServiceReference(SyncTaskBuildService.NAME) + abstract Property getSyncTask(); + @Inject public GenerateSourcesTask(DecompilerOptions decompilerOptions) { this.decompilerOptions = decompilerOptions; diff --git a/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java b/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java index 9f741c55..cbd3630a 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java +++ b/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java @@ -44,6 +44,7 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.util.Constants; 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"; @@ -64,6 +65,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; diff --git a/src/main/java/net/fabricmc/loom/task/RunGameTask.java b/src/main/java/net/fabricmc/loom/task/RunGameTask.java index faf7cebb..05d87116 100644 --- a/src/main/java/net/fabricmc/loom/task/RunGameTask.java +++ b/src/main/java/net/fabricmc/loom/task/RunGameTask.java @@ -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)); diff --git a/src/main/java/net/fabricmc/loom/util/gradle/SyncTaskBuildService.java b/src/main/java/net/fabricmc/loom/util/gradle/SyncTaskBuildService.java new file mode 100644 index 00000000..9a5be02b --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/gradle/SyncTaskBuildService.java @@ -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. + * + *
{@code
+ * @ServiceReference(SyncTaskBuildService.NAME)
+ * abstract Property getSyncTask();
+ * }
+ */ +public abstract class SyncTaskBuildService implements BuildService { + 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 { + } +} From 55c59ef97ed1eb443491fbc2c09e4614960b0af7 Mon Sep 17 00:00:00 2001 From: Luke Bemish Date: Tue, 13 Jun 2023 06:17:20 -0400 Subject: [PATCH 04/13] Change zip entry ordering to place manifest first (#887) * Change zip entry ordering to place manifest first * Add handling for signature related files * Update jar file hash * Hopefully update correct file hashes this time * Change jar hashes * Add correct source hashes --- .../loom/util/ZipReprocessorUtil.java | 47 ++++++++++++++++++- .../integration/ReproducibleBuildTest.groovy | 12 ++--- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java b/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java index 43b28db5..79f0c872 100644 --- a/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java +++ b/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java @@ -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); } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/ReproducibleBuildTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/ReproducibleBuildTest.groovy index 22700065..f40a65e0 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/ReproducibleBuildTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/ReproducibleBuildTest.groovy @@ -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" ] } From 3c487447fbda00565667e04dff01ee9f24826e52 Mon Sep 17 00:00:00 2001 From: zml Date: Tue, 13 Jun 2023 03:20:46 -0700 Subject: [PATCH 05/13] Fix interface injection of inner class interfaces (#900) Fixes #798 --- .../InterfaceInjectionProcessor.java | 71 +++++++++++++++++-- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java index ea0a7327..98a4a798 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java @@ -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; @@ -173,14 +175,20 @@ public abstract class InterfaceInjectionProcessor implements MinecraftJarProcess } private static String appendComment(String comment, List 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 = "

Interface {@link %s} injected by mod %s

".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); } } } @@ -221,7 +229,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 injectedInterfaces; + private final Set knownInnerClasses = new HashSet<>(); InjectingClassVisitor(int asmVersion, ClassWriter writer, List injectedInterfaces) { super(asmVersion, writer); @@ -254,5 +265,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(); + } } } From c62e96b0ae65d13a7434c6202a2f70449dd2cf6b Mon Sep 17 00:00:00 2001 From: Juuz <6596629+Juuxel@users.noreply.github.com> Date: Tue, 13 Jun 2023 13:25:43 +0300 Subject: [PATCH 06/13] Fix non-main source set remap configurations publishing by default (#891) Fixes #890. Note that there is *no simple way* to enable publishing for remap configurations created this way anymore as the publishing mode is read during the `createRemapConfigurations` call. This also changes the behaviour of `modClientX` configurations to no longer publish. If that is wanted, I can add a `createRemapConfigurations()` overload with some kind of configurability. --- .../fabricmc/loom/configuration/RemapConfigurations.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/RemapConfigurations.java b/src/main/java/net/fabricmc/loom/configuration/RemapConfigurations.java index 1428c8ee..03349991 100644 --- a/src/main/java/net/fabricmc/loom/configuration/RemapConfigurations.java +++ b/src/main/java/net/fabricmc/loom/configuration/RemapConfigurations.java @@ -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); + } }; } From c557647e06f71ae69ffdea2b1a067adeb72abe35 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 13 Jun 2023 12:32:35 +0200 Subject: [PATCH 07/13] Fix invalid run config containing module names with spaces (#889) --- .../net/fabricmc/loom/configuration/ide/idea/IdeaUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaUtils.java b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaUtils.java index 3c5f8263..e591ef18 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaUtils.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaUtils.java @@ -55,6 +55,6 @@ public class IdeaUtils { module = project.getName() + "." + module; } - return module; + return module.replace(' ', '_'); } } From 856f0a4f3398de89f6dc099910a20b6afb0337ad Mon Sep 17 00:00:00 2001 From: modmuss Date: Tue, 13 Jun 2023 18:31:02 +0100 Subject: [PATCH 08/13] Fix and validate kotlin version. (#903) --- build.gradle | 11 +++++++++-- gradle/libs.versions.toml | 8 ++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 gradle/libs.versions.toml diff --git a/build.gradle b/build.gradle index 76c2c1a5..0850aae9 100644 --- a/build.gradle +++ b/build.gradle @@ -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" } @@ -34,6 +34,12 @@ if (ENV.BUILD_NUMBER) { version = baseVersion + '.local' } +// 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 { maven { name = 'Fabric' @@ -100,7 +106,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 // Testing testImplementation(gradleTestKit()) @@ -216,6 +222,7 @@ test { } } +import org.gradle.launcher.cli.KotlinDslVersion import org.gradle.util.GradleVersion import org.w3c.dom.Document import org.w3c.dom.Element diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..a8f38ae5 --- /dev/null +++ b/gradle/libs.versions.toml @@ -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" } \ No newline at end of file From 68c7eb21ba81c31ec1939b9144f31ba9dd073026 Mon Sep 17 00:00:00 2001 From: modmuss Date: Tue, 13 Jun 2023 18:31:25 +0100 Subject: [PATCH 09/13] Enable reproducible builds by default (#899) * Enable reproducible builds by default * Review feedback --- src/main/java/net/fabricmc/loom/task/RemapJarTask.java | 4 ++++ src/test/resources/projects/reproducible/build.gradle | 6 ------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index 872ce42e..e680792c 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -105,6 +105,10 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { if (getLoomExtension().multiProjectOptimisation()) { setupPreparationTask(); } + + // Make outputs reproducible by default + setReproducibleFileOrder(true); + setPreserveFileTimestamps(false); } private void setupPreparationTask() { diff --git a/src/test/resources/projects/reproducible/build.gradle b/src/test/resources/projects/reproducible/build.gradle index bda1baa6..08c393f8 100644 --- a/src/test/resources/projects/reproducible/build.gradle +++ b/src/test/resources/projects/reproducible/build.gradle @@ -52,12 +52,6 @@ jar { } } -// Make Jars Reproducible -tasks.withType(AbstractArchiveTask) { - preserveFileTimestamps = false - reproducibleFileOrder = true -} - remapSourcesJar { preserveFileTimestamps = false reproducibleFileOrder = true From ed5e4ac8dd0025570c6854cbc3b037ca743f32be Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 13 Jun 2023 18:54:23 +0100 Subject: [PATCH 10/13] Add Eclipse to ANSI supported IDEs - fixes #726 (#803) --- .../net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java index 7176e985..c4774609 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java @@ -85,6 +85,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. From 590686fe1a97e7975d561c24600f8f1eda62cbeb Mon Sep 17 00:00:00 2001 From: modmuss Date: Tue, 13 Jun 2023 21:24:46 +0100 Subject: [PATCH 11/13] Fix + test minecraft metadata downloading (#905) --- .../providers/minecraft/ManifestVersion.java | 10 + .../minecraft/MinecraftMetadataProvider.java | 161 +++++++++++++ .../minecraft/MinecraftProvider.java | 97 ++------ .../unit/download/DownloadFileTest.groovy | 2 +- .../test/unit/download/DownloadTest.groovy | 7 +- .../MinecraftMetadataProviderTest.groovy | 220 ++++++++++++++++++ 6 files changed, 407 insertions(+), 90 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMetadataProvider.java create mode 100644 src/test/groovy/net/fabricmc/loom/test/unit/providers/MinecraftMetadataProviderTest.groovy diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/ManifestVersion.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/ManifestVersion.java index f60bf6e4..391683da 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/ManifestVersion.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/ManifestVersion.java @@ -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, Map 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); + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMetadataProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMetadataProvider.java new file mode 100644 index 00000000..396202ce --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMetadataProvider.java @@ -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 download; + + private ManifestVersion.Versions versionEntry; + private MinecraftVersionMeta versionMeta; + + public MinecraftMetadataProvider(Options options, Function 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 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; + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java index 7079db7b..f206a7a9 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java @@ -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 final Project project; @@ -86,11 +77,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(); @@ -98,16 +92,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"); @@ -119,73 +110,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)) @@ -193,7 +122,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)) @@ -256,7 +185,7 @@ public abstract class MinecraftProvider { } public MinecraftVersionMeta getVersionInfo() { - return versionInfo; + return Objects.requireNonNull(metadataProvider, "Metadata provider not setup").getVersionMeta(); } @Nullable diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy index 2865e99e..820647f9 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy @@ -252,7 +252,7 @@ class DownloadFileTest extends DownloadTest { def "Progress: String"() { setup: - server.get("/progressString") { + server.get("/progressFile") { it.result("Hello World") } diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadTest.groovy index b4e3981b..f19062bc 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadTest.groovy @@ -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() } } \ No newline at end of file diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/providers/MinecraftMetadataProviderTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/providers/MinecraftMetadataProviderTest.groovy new file mode 100644 index 00000000..815d34b1 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/providers/MinecraftMetadataProviderTest.groovy @@ -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" + } + ] +} +""" +} From fe823ddb30d7b2505ad20f142b9660540cbc5b3a Mon Sep 17 00:00:00 2001 From: modmuss Date: Thu, 15 Jun 2023 20:14:52 +0100 Subject: [PATCH 12/13] Add an internal API to allow 3rd party loom extensions to add library processors. (#906) Stability of this API will not be guaranteed. --- .../java/net/fabricmc/loom/LoomGradleExtension.java | 6 ++++++ .../minecraft/MinecraftLibraryProvider.java | 2 +- .../minecraft/library/LibraryProcessorManager.java | 13 ++++++++----- .../loom/extension/LoomGradleExtensionImpl.java | 11 +++++++++++ .../unit/library/LibraryProcessorManagerTest.groovy | 2 +- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index 10ac4db9..b985bd82 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -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; @@ -38,12 +40,14 @@ import net.fabricmc.loom.configuration.LoomDependencyManager; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile; 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.extension.LoomFiles; import net.fabricmc.loom.extension.MixinExtension; 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"); @@ -109,4 +113,6 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI { *

You can enable it by setting the Gradle property {@code fabric.loom.multiProjectOptimisation} to {@code true}. */ boolean multiProjectOptimisation(); + + ListProperty getLibraryProcessors(); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java index 8c1dc700..62fa2bdd 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java @@ -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 getEnabledProcessors() { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/library/LibraryProcessorManager.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/library/LibraryProcessorManager.java index a6782af3..51f59c0f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/library/LibraryProcessorManager.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/library/LibraryProcessorManager.java @@ -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> LIBRARY_PROCESSORS = List.of( + public static final List 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 libraryProcessorFactories; private final List enabledProcessors; - public LibraryProcessorManager(Platform platform, RepositoryHandler repositories, List enabledProcessors) { + public LibraryProcessorManager(Platform platform, RepositoryHandler repositories, List libraryProcessorFactories, List 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 getProcessors(LibraryContext context) { var processors = new ArrayList(); - 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 extends BiFunction { + public interface LibraryProcessorFactory extends BiFunction { } } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index 706090d7..81c5c793 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -33,6 +33,7 @@ import java.util.Objects; 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; @@ -44,6 +45,7 @@ import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile; 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.util.Constants; @@ -67,6 +69,7 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen private InstallerData installerData; private boolean refreshDeps; private Provider multiProjectOptimisation; + private final ListProperty libraryProcessorFactories; public LoomGradleExtensionImpl(Project project, LoomFiles files) { super(project, files); @@ -87,6 +90,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."); @@ -236,6 +242,11 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen return multiProjectOptimisation.getOrElse(false); } + @Override + public ListProperty getLibraryProcessors() { + return libraryProcessorFactories; + } + @Override protected void configureIntermediateMappingsProviderInternal(T provider) { provider.getMinecraftVersion().set(getProject().provider(() -> getMinecraftProvider().minecraftVersion())); diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/library/LibraryProcessorManagerTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/library/LibraryProcessorManagerTest.groovy index 62ac6ab1..9b5b4256 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/library/LibraryProcessorManagerTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/library/LibraryProcessorManagerTest.groovy @@ -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) From 4e593fc5ae02b4e6d7ff174041c52a2e04ce973d Mon Sep 17 00:00:00 2001 From: modmuss Date: Fri, 16 Jun 2023 21:55:04 +0100 Subject: [PATCH 13/13] Rework how unpick and linenumber maps are applied (#907) This should hopefully vastly improve debugging, and more imporantly not work in a consistant manner, making debugging issues a lot easier. This commit contains an intergration test that uses a real debugger to check that breakpoints are being fired as expected. --- build.gradle | 3 +- .../configuration/CompileConfiguration.java | 11 +- .../decompile/DecompileConfiguration.java | 19 +- .../SingleJarDecompileConfiguration.java | 25 +- .../SplitDecompileConfiguration.java | 41 +--- .../minecraft/MinecraftJarConfiguration.java | 18 +- .../AbstractMappedMinecraftProvider.java | 48 ++-- .../mapped/IntermediaryMinecraftProvider.java | 27 +- .../mapped/NamedMinecraftProvider.java | 27 +- .../ProcessedNamedMinecraftProvider.java | 30 ++- .../loom/task/GenerateSourcesTask.java | 141 +++++++++-- .../net/fabricmc/loom/task/UnpickJarTask.java | 118 --------- .../integration/DebugLineNumbersTest.groovy | 230 ++++++++++++++++++ .../test/util/GradleProjectTestTrait.groovy | 4 +- 14 files changed, 478 insertions(+), 264 deletions(-) delete mode 100644 src/main/java/net/fabricmc/loom/task/UnpickJarTask.java create mode 100644 src/test/groovy/net/fabricmc/loom/test/integration/DebugLineNumbersTest.groovy diff --git a/build.gradle b/build.gradle index 0850aae9..2e411eeb 100644 --- a/build.gradle +++ b/build.gradle @@ -95,7 +95,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 ('net.fabricmc:mercury:0.3.0') @@ -118,6 +118,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' diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index a37f3635..8930e596 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -57,6 +57,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.extension.MixinExtension; @@ -153,8 +154,8 @@ public abstract class CompileConfiguration implements Runnable { mappingConfiguration.applyToProject(getProject(), mappingsDep); // 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()); @@ -164,11 +165,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); } private void registerGameProcessors(ConfigContext configContext) { diff --git a/src/main/java/net/fabricmc/loom/configuration/decompile/DecompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/decompile/DecompileConfiguration.java index f535d093..0a6a5948 100644 --- a/src/main/java/net/fabricmc/loom/configuration/decompile/DecompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/decompile/DecompileConfiguration.java @@ -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 { protected final Project project; @@ -50,11 +51,13 @@ public abstract class DecompileConfiguration public abstract void afterEvaluation(); - protected final TaskProvider 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)); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/decompile/SingleJarDecompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/decompile/SingleJarDecompileConfiguration.java index 6fa25b5c..538c3018 100644 --- a/src/main/java/net/fabricmc/loom/configuration/decompile/SingleJarDecompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/decompile/SingleJarDecompileConfiguration.java @@ -25,11 +25,11 @@ 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.minecraft.MinecraftJar; import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider; import net.fabricmc.loom.task.GenerateSourcesTask; import net.fabricmc.loom.util.Constants; @@ -41,36 +41,25 @@ public class SingleJarDecompileConfiguration extends DecompileConfiguration minecraftJars = minecraftProvider.getMinecraftJarPaths(); + final List 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); } }); }); diff --git a/src/main/java/net/fabricmc/loom/configuration/decompile/SplitDecompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/decompile/SplitDecompileConfiguration.java index 0dd69ff2..0cf8fbe3 100644 --- a/src/main/java/net/fabricmc/loom/configuration/decompile/SplitDecompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/decompile/SplitDecompileConfiguration.java @@ -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 { @@ -44,41 +44,26 @@ public final class SplitDecompileConfiguration extends DecompileConfiguration unpickCommonJar = null; - TaskProvider 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 unpickCommonJarTask = unpickCommonJar; - final TaskProvider unpickClientOnlyJarTask = unpickClientOnlyJar; + final MinecraftJar commonJar = minecraftProvider.getCommonJar(); + final MinecraftJar clientOnlyJar = minecraftProvider.getClientOnlyJar(); final TaskProvider 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 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. diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJarConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJarConfiguration.java index 04a7468a..10aa592e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJarConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJarConfiguration.java @@ -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; @@ -73,8 +75,8 @@ public enum MinecraftJarConfiguration { ); private final Function minecraftProviderFunction; - private final BiFunction> intermediaryMinecraftProviderBiFunction; - private final BiFunction> namedMinecraftProviderBiFunction; + private final BiFunction> intermediaryMinecraftProviderBiFunction; + private final BiFunction> namedMinecraftProviderBiFunction; private final BiFunction, MinecraftJarProcessorManager, ProcessedNamedMinecraftProvider> processedNamedMinecraftProviderBiFunction; private final BiFunction> decompileConfigurationBiFunction; private final List supportedEnvironments; @@ -82,15 +84,15 @@ public enum MinecraftJarConfiguration { @SuppressWarnings("unchecked") // Just a bit of a generic mess :) , Q extends MappedMinecraftProvider> MinecraftJarConfiguration( Function minecraftProviderFunction, - BiFunction> intermediaryMinecraftProviderBiFunction, - BiFunction namedMinecraftProviderBiFunction, + BiFunction> intermediaryMinecraftProviderBiFunction, + BiFunction namedMinecraftProviderBiFunction, BiFunction> processedNamedMinecraftProviderBiFunction, BiFunction> decompileConfigurationBiFunction, List supportedEnvironments ) { this.minecraftProviderFunction = (Function) minecraftProviderFunction; - this.intermediaryMinecraftProviderBiFunction = (BiFunction>) (Object) intermediaryMinecraftProviderBiFunction; - this.namedMinecraftProviderBiFunction = (BiFunction>) namedMinecraftProviderBiFunction; + this.intermediaryMinecraftProviderBiFunction = (BiFunction>) (Object) intermediaryMinecraftProviderBiFunction; + this.namedMinecraftProviderBiFunction = (BiFunction>) namedMinecraftProviderBiFunction; this.processedNamedMinecraftProviderBiFunction = (BiFunction, MinecraftJarProcessorManager, ProcessedNamedMinecraftProvider>) (Object) processedNamedMinecraftProviderBiFunction; this.decompileConfigurationBiFunction = (BiFunction>) decompileConfigurationBiFunction; this.supportedEnvironments = supportedEnvironments; @@ -100,11 +102,11 @@ public enum MinecraftJarConfiguration { return minecraftProviderFunction; } - public BiFunction> getIntermediaryMinecraftProviderBiFunction() { + public BiFunction> getIntermediaryMinecraftProviderBiFunction() { return intermediaryMinecraftProviderBiFunction; } - public BiFunction> getNamedMinecraftProviderBiFunction() { + public BiFunction> getNamedMinecraftProviderBiFunction() { return namedMinecraftProviderBiFunction; } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java index 97c8acf1..61cba122 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java @@ -53,13 +53,13 @@ import net.fabricmc.tinyremapper.TinyRemapper; public abstract class AbstractMappedMinecraftProvider 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(); @@ -70,13 +70,13 @@ public abstract class AbstractMappedMinecraftProvider provide(ProvideContext context) throws Exception { final List 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); @@ -84,17 +84,25 @@ public abstract class AbstractMappedMinecraftProvider 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()); } } @@ -154,15 +162,15 @@ public abstract class AbstractMappedMinecraftProvider remappedJars) throws IOException { + private void remapInputs(List 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(); @@ -214,12 +222,8 @@ public abstract class AbstractMappedMinecraftProvider extends AbstractMappedMinecraftProvider 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 @@ -51,8 +52,8 @@ public abstract sealed class IntermediaryMinecraftProvider implements Merged { - public MergedImpl(ConfigContext configContext, MergedMinecraftProvider minecraftProvider) { - super(configContext, minecraftProvider); + public MergedImpl(Project project, MergedMinecraftProvider minecraftProvider) { + super(project, minecraftProvider); } @Override @@ -64,8 +65,8 @@ public abstract sealed class IntermediaryMinecraftProvider implements Split { - public SplitImpl(ConfigContext configContext, SplitMinecraftProvider minecraftProvider) { - super(configContext, minecraftProvider); + public SplitImpl(Project project, SplitMinecraftProvider minecraftProvider) { + super(project, minecraftProvider); } @Override @@ -85,17 +86,17 @@ public abstract sealed class IntermediaryMinecraftProvider 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 diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/NamedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/NamedMinecraftProvider.java index 8f07eda6..7534fdb6 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/NamedMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/NamedMinecraftProvider.java @@ -26,8 +26,9 @@ package net.fabricmc.loom.configuration.providers.minecraft.mapped; import java.util.List; +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; @@ -36,8 +37,8 @@ import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvide import net.fabricmc.tinyremapper.TinyRemapper; public abstract class NamedMinecraftProvider extends AbstractMappedMinecraftProvider { - public NamedMinecraftProvider(ConfigContext configContext, M minecraftProvider) { - super(configContext, minecraftProvider); + public NamedMinecraftProvider(Project project, M minecraftProvider) { + super(project, minecraftProvider); } @Override @@ -51,8 +52,8 @@ public abstract class NamedMinecraftProvider extend } public static final class MergedImpl extends NamedMinecraftProvider implements Merged { - public MergedImpl(ConfigContext configContext, MergedMinecraftProvider minecraftProvider) { - super(configContext, minecraftProvider); + public MergedImpl(Project project, MergedMinecraftProvider minecraftProvider) { + super(project, minecraftProvider); } @Override @@ -69,8 +70,8 @@ public abstract class NamedMinecraftProvider extend } public static final class SplitImpl extends NamedMinecraftProvider implements Split { - public SplitImpl(ConfigContext configContext, SplitMinecraftProvider minecraftProvider) { - super(configContext, minecraftProvider); + public SplitImpl(Project project, SplitMinecraftProvider minecraftProvider) { + super(project, minecraftProvider); } @Override @@ -95,17 +96,17 @@ public abstract class NamedMinecraftProvider extend public static final class SingleJarImpl extends NamedMinecraftProvider 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 diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java index 005fbdf1..d956079f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java @@ -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 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 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 minecraftJarMap, ConfigContext configContext) throws IOException { + for (Map.Entry 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)); } } diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index 7f28716c..a87176e2 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -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,13 @@ import org.gradle.api.file.ConfigurableFileCollection; 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 +72,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.decompilers.LineNumberRemapper; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.FileSystemUtil; @@ -90,16 +96,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 getInputJarName(); @InputFiles public abstract ConfigurableFileCollection getClasspath(); @@ -107,9 +107,26 @@ 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(); @@ -124,8 +141,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 @@ -136,10 +151,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; } @@ -149,7 +174,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 { @@ -157,18 +182,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 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 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 getUnpickArgs(Path inputJar, Path outputJar) { + var fileArgs = new ArrayList(); + + 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) { @@ -317,8 +418,8 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { } } - private File getMappedJarFileWithSuffix(String suffix) { - String path = getRuntimeJar().get().getAsFile().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); diff --git a/src/main/java/net/fabricmc/loom/task/UnpickJarTask.java b/src/main/java/net/fabricmc/loom/task/UnpickJarTask.java deleted file mode 100644 index d3c52e72..00000000 --- a/src/main/java/net/fabricmc/loom/task/UnpickJarTask.java +++ /dev/null @@ -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(); - } -} diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/DebugLineNumbersTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/DebugLineNumbersTest.groovy new file mode 100644 index 00000000..23a099af --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/DebugLineNumbersTest.groovy @@ -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 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> 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 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 Function, CompletionStage> toCompletionStage() { + return { Maybe m -> + CompletableFuture cf = new CompletableFuture<>() + m.subscribe(cf.&complete, cf.&completeExceptionally) { cf.complete(null) } + return cf + } + } + + Observable 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() + } + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy b/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy index 591893a0..a741f095 100644 --- a/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy @@ -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) {