From 9286ef9163651541767ad7fb29eeb427f0602685 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Fri, 28 Jan 2022 20:25:14 +0000 Subject: [PATCH 01/27] Start 0.12 dev cycle --- bootstrap/test-project/build.gradle | 2 +- build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap/test-project/build.gradle b/bootstrap/test-project/build.gradle index f5408dac..36767715 100644 --- a/bootstrap/test-project/build.gradle +++ b/bootstrap/test-project/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '0.11.local' + id 'fabric-loom' version '0.12.local' } dependencies { diff --git a/build.gradle b/build.gradle index 0d4b5667..08aa6a87 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { group = 'net.fabricmc' archivesBaseName = project.name -def baseVersion = '0.11' +def baseVersion = '0.12' def ENV = System.getenv() if (ENV.BUILD_NUMBER) { From 4b5217649949f141e2c179e3bc89e82ab9ef7779 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Fri, 28 Jan 2022 20:26:16 +0000 Subject: [PATCH 02/27] Cleanup asset downloading, add support for legacy and pre-1.6 assets (#585) --- .../minecraft/assets/AssetIndex.java | 32 +++- .../minecraft/assets/AssetObject.java | 29 --- .../assets/MinecraftAssetsProvider.java | 162 ----------------- .../loom/task/DownloadAssetsTask.java | 169 +++++++++++++++++- .../task/launch/GenerateDLIConfigTask.java | 10 +- .../loom/util/HashedDownloadUtil.java | 8 - 6 files changed, 199 insertions(+), 211 deletions(-) delete mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/AssetObject.java delete mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/AssetIndex.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/AssetIndex.java index c87fc8c8..3dd6a3c5 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/AssetIndex.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/AssetIndex.java @@ -24,18 +24,38 @@ package net.fabricmc.loom.configuration.providers.minecraft.assets; -import java.util.HashSet; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonProperty; @SuppressWarnings("unused") -public record AssetIndex(Map objects, boolean virtual) { +public record AssetIndex(Map objects, boolean virtual, @JsonProperty("map_to_resources") boolean mapToResources) { public AssetIndex() { - this(new LinkedHashMap<>(), false); + this(new LinkedHashMap<>(), false, false); } - public Set getUniqueObjects() { - return new HashSet<>(this.objects.values()); + public Collection getObjects() { + return objects.entrySet().stream().map(Object::new).toList(); + } + + public record Entry(String hash, long size) { + } + + public record Object(String path, String hash, long size) { + private Object(Map.Entry entry) { + this(entry.getKey(), entry.getValue().hash(), entry.getValue().size()); + } + + public String name() { + int end = path().lastIndexOf("/") + 1; + + if (end > 0) { + return path().substring(end); + } + + return path(); + } } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/AssetObject.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/AssetObject.java deleted file mode 100644 index b7e63b12..00000000 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/AssetObject.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2016-2021 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.configuration.providers.minecraft.assets; - -@SuppressWarnings("unused") -public record AssetObject(String hash, long size) { -} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java deleted file mode 100644 index 175e85ed..00000000 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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.configuration.providers.minecraft.assets; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.net.URL; -import java.util.Deque; -import java.util.Map; -import java.util.concurrent.ConcurrentLinkedDeque; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import com.google.common.base.Stopwatch; -import org.gradle.api.GradleException; -import org.gradle.api.Project; - -import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; -import net.fabricmc.loom.util.MirrorUtil; -import net.fabricmc.loom.util.HashedDownloadUtil; -import net.fabricmc.loom.util.gradle.ProgressLoggerHelper; - -public class MinecraftAssetsProvider { - public static void provide(MinecraftProvider minecraftProvider, Project project) throws IOException { - LoomGradleExtension extension = LoomGradleExtension.get(project); - boolean offline = project.getGradle().getStartParameter().isOffline(); - - MinecraftVersionMeta versionInfo = minecraftProvider.getVersionInfo(); - MinecraftVersionMeta.AssetIndex assetIndex = versionInfo.assetIndex(); - - // get existing cache files - File assets = new File(extension.getFiles().getUserCache(), "assets"); - - if (!assets.exists()) { - assets.mkdirs(); - } - - File assetsInfo = new File(assets, "indexes" + File.separator + assetIndex.fabricId(minecraftProvider.minecraftVersion()) + ".json"); - - project.getLogger().info(":downloading asset index"); - - if (offline) { - if (assetsInfo.exists()) { - //We know it's outdated but can't do anything about it, oh well - project.getLogger().warn("Asset index outdated"); - } else { - //We don't know what assets we need, just that we don't have any - throw new GradleException("Asset index not found at " + assetsInfo.getAbsolutePath()); - } - } else { - HashedDownloadUtil.downloadIfInvalid(new URL(assetIndex.url()), assetsInfo, assetIndex.sha1(), project.getLogger(), false); - } - - Deque loggers = new ConcurrentLinkedDeque<>(); - ExecutorService executor = Executors.newFixedThreadPool(Math.min(10, Math.max(Runtime.getRuntime().availableProcessors() / 2, 1))); - - AssetIndex index; - - try (FileReader fileReader = new FileReader(assetsInfo)) { - index = LoomGradlePlugin.OBJECT_MAPPER.readValue(fileReader, AssetIndex.class); - } - - Stopwatch stopwatch = Stopwatch.createStarted(); - - Map parent = index.objects(); - - for (Map.Entry entry : parent.entrySet()) { - AssetObject object = entry.getValue(); - String sha1 = object.hash(); - String filename = "objects" + File.separator + sha1.substring(0, 2) + File.separator + sha1; - File file = new File(assets, filename); - - if (offline) { - if (file.exists()) { - project.getLogger().warn("Outdated asset " + entry.getKey()); - } else { - throw new GradleException("Asset " + entry.getKey() + " not found at " + file.getAbsolutePath()); - } - } else { - executor.execute(() -> { - final String[] assetName = {entry.getKey()}; - int end = assetName[0].lastIndexOf("/") + 1; - - if (end > 0) { - assetName[0] = assetName[0].substring(end); - } - - project.getLogger().debug("validating asset " + assetName[0]); - - final ProgressLoggerHelper[] progressLogger = new ProgressLoggerHelper[1]; - - try { - HashedDownloadUtil.downloadIfInvalid(new URL(MirrorUtil.getResourcesBase(project) + sha1.substring(0, 2) + "/" + sha1), file, sha1, project.getLogger(), true, () -> { - ProgressLoggerHelper logger = loggers.pollFirst(); - - if (logger == null) { - //Create a new logger if we need one - progressLogger[0] = ProgressLoggerHelper.getProgressFactory(project, MinecraftAssetsProvider.class.getName()); - progressLogger[0].start("Downloading assets...", "assets"); - } else { - // use a free logger if we can - progressLogger[0] = logger; - } - - project.getLogger().debug("downloading asset " + assetName[0]); - progressLogger[0].progress(String.format("%-30.30s", assetName[0]) + " - " + sha1); - }); - } catch (IOException e) { - throw new RuntimeException("Failed to download: " + assetName[0], e); - } - - if (progressLogger[0] != null) { - //Give this logger back if we used it - loggers.add(progressLogger[0]); - } - }); - } - } - - project.getLogger().info("Took " + stopwatch.stop() + " to iterate " + parent.size() + " asset index."); - - //Wait for the assets to all download - executor.shutdown(); - - try { - if (executor.awaitTermination(2, TimeUnit.HOURS)) { - executor.shutdownNow(); - } - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - loggers.forEach(ProgressLoggerHelper::completed); - } -} diff --git a/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java b/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java index f03a1079..c4c204b3 100644 --- a/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java +++ b/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java @@ -24,20 +24,179 @@ package net.fabricmc.loom.task; +import java.io.File; +import java.io.FileReader; import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URL; +import java.util.Deque; +import java.util.Objects; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import javax.inject.Inject; + +import org.gradle.api.GradleException; import org.gradle.api.Project; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.TaskAction; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.configuration.providers.minecraft.assets.MinecraftAssetsProvider; +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.configuration.ide.RunConfigSettings; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; +import net.fabricmc.loom.configuration.providers.minecraft.assets.AssetIndex; +import net.fabricmc.loom.util.HashedDownloadUtil; +import net.fabricmc.loom.util.MirrorUtil; +import net.fabricmc.loom.util.gradle.ProgressLoggerHelper; + +public abstract class DownloadAssetsTask extends AbstractLoomTask { + @Input + public abstract Property getAssetsHash(); + + @OutputDirectory + public abstract RegularFileProperty getAssetsDirectory(); + + @OutputDirectory + public abstract RegularFileProperty getLegacyResourcesDirectory(); + + @Inject + public DownloadAssetsTask() { + final MinecraftVersionMeta versionInfo = getExtension().getMinecraftProvider().getVersionInfo(); + final File assetsDir = new File(getExtension().getFiles().getUserCache(), "assets"); + + getAssetsDirectory().set(assetsDir); + getAssetsHash().set(versionInfo.assetIndex().sha1()); + + if (versionInfo.assets().equals("legacy")) { + getLegacyResourcesDirectory().set(new File(assetsDir, "/legacy/" + versionInfo.id())); + } else { + // pre-1.6 resources + RunConfigSettings client = Objects.requireNonNull(getExtension().getRunConfigs().findByName("client"), "Could not find client run config"); + getLegacyResourcesDirectory().set(new File(getProject().getProjectDir(), client.getRunDir() + "/resources")); + } + + getAssetsHash().finalizeValueOnRead(); + getAssetsDirectory().finalizeValueOnRead(); + getLegacyResourcesDirectory().finalizeValueOnRead(); + } -public class DownloadAssetsTask extends AbstractLoomTask { @TaskAction public void downloadAssets() throws IOException { - Project project = this.getProject(); - LoomGradleExtension extension = getExtension(); + final Project project = this.getProject(); + final File assetsDirectory = getAssetsDirectory().get().getAsFile(); + final Deque loggers = new ConcurrentLinkedDeque<>(); + final ExecutorService executor = Executors.newFixedThreadPool(Math.min(10, Math.max(Runtime.getRuntime().availableProcessors() / 2, 1))); + final AssetIndex assetIndex = getAssetIndex(); - MinecraftAssetsProvider.provide(extension.getMinecraftProvider(), project); + if (!assetsDirectory.exists()) { + assetsDirectory.mkdirs(); + } + + if (assetIndex.mapToResources()) { + getLegacyResourcesDirectory().get().getAsFile().mkdirs(); + } + + for (AssetIndex.Object object : assetIndex.getObjects()) { + final String path = object.path(); + final String sha1 = object.hash(); + final File file = getAssetsFile(object, assetIndex); + + if (getProject().getGradle().getStartParameter().isOffline()) { + if (!file.exists()) { + throw new GradleException("Asset " + path + " not found at " + file.getAbsolutePath()); + } + + continue; + } + + final Supplier getOrCreateLogger = () -> { + ProgressLoggerHelper logger = loggers.pollFirst(); + + if (logger == null) { + // No logger available, create a new one + logger = ProgressLoggerHelper.getProgressFactory(project, DownloadAssetsTask.class.getName()); + logger.start("Downloading assets...", "assets"); + } + + return logger; + }; + + executor.execute(() -> { + final ProgressLoggerHelper logger = getOrCreateLogger.get(); + + try { + HashedDownloadUtil.downloadIfInvalid(new URL(MirrorUtil.getResourcesBase(project) + sha1.substring(0, 2) + "/" + sha1), file, sha1, project.getLogger(), true, () -> { + project.getLogger().debug("downloading asset " + object.name()); + logger.progress(String.format("%-30.30s", object.name()) + " - " + sha1); + }); + } catch (IOException e) { + throw new UncheckedIOException("Failed to download: " + object.name(), e); + } + + // Give this logger back + loggers.add(logger); + }); + } + + // Wait for the assets to all download + try { + executor.shutdown(); + + if (executor.awaitTermination(2, TimeUnit.HOURS)) { + executor.shutdownNow(); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } finally { + loggers.forEach(ProgressLoggerHelper::completed); + } + } + + private MinecraftVersionMeta.AssetIndex getAssetIndexMeta() { + MinecraftVersionMeta versionInfo = getExtension().getMinecraftProvider().getVersionInfo(); + return versionInfo.assetIndex(); + } + + private AssetIndex getAssetIndex() throws IOException { + final LoomGradleExtension extension = getExtension(); + final MinecraftProvider minecraftProvider = extension.getMinecraftProvider(); + + MinecraftVersionMeta.AssetIndex assetIndex = getAssetIndexMeta(); + File assetsInfo = new File(getAssetsDirectory().get().getAsFile(), "indexes" + File.separator + assetIndex.fabricId(minecraftProvider.minecraftVersion()) + ".json"); + + getProject().getLogger().info(":downloading asset index"); + + if (getProject().getGradle().getStartParameter().isOffline()) { + if (assetsInfo.exists()) { + // We know it's outdated but can't do anything about it, oh well + getProject().getLogger().warn("Asset index outdated"); + } else { + // We don't know what assets we need, just that we don't have any + throw new GradleException("Asset index not found at " + assetsInfo.getAbsolutePath()); + } + } else { + HashedDownloadUtil.downloadIfInvalid(new URL(assetIndex.url()), assetsInfo, assetIndex.sha1(), getProject().getLogger(), false); + } + + try (FileReader fileReader = new FileReader(assetsInfo)) { + return LoomGradlePlugin.OBJECT_MAPPER.readValue(fileReader, AssetIndex.class); + } + } + + private File getAssetsFile(AssetIndex.Object object, AssetIndex index) { + if (index.mapToResources() || index.virtual()) { + return new File(getLegacyResourcesDirectory().get().getAsFile(), object.path()); + } + + final String filename = "objects" + File.separator + object.hash().substring(0, 2) + File.separator + object.hash(); + return new File(getAssetsDirectory().get().getAsFile(), filename); } } 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 f3259579..144f9fa9 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java @@ -39,6 +39,7 @@ import org.apache.commons.io.FileUtils; import org.gradle.api.logging.configuration.ConsoleOutput; import org.gradle.api.tasks.TaskAction; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; import net.fabricmc.loom.task.AbstractLoomTask; public abstract class GenerateDLIConfigTask extends AbstractLoomTask { @@ -46,6 +47,13 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { public void run() throws IOException { final String nativesPath = getExtension().getFiles().getNativesDirectory(getProject()).getAbsolutePath(); + final MinecraftVersionMeta versionInfo = getExtension().getMinecraftProvider().getVersionInfo(); + File assetsDirectory = new File(getExtension().getFiles().getUserCache(), "assets"); + + if (versionInfo.assets().equals("legacy")) { + assetsDirectory = new File(assetsDirectory, "/legacy/" + versionInfo.id()); + } + final LaunchConfig launchConfig = new LaunchConfig() .property("fabric.development", "true") .property("fabric.remapClasspathFile", getExtension().getFiles().getRemapClasspathFile().getAbsolutePath()) @@ -58,7 +66,7 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { .argument("client", "--assetIndex") .argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex().fabricId(getExtension().getMinecraftProvider().minecraftVersion())) .argument("client", "--assetsDir") - .argument("client", new File(getExtension().getFiles().getUserCache(), "assets").getAbsolutePath()); + .argument("client", assetsDirectory.getAbsolutePath()); final boolean plainConsole = getProject().getGradle().getStartParameter().getConsoleOutput() == ConsoleOutput.Plain; final boolean ansiSupportedIDE = new File(getProject().getRootDir(), ".vscode").exists() diff --git a/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java b/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java index 4f01e7d4..0d231d14 100644 --- a/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java +++ b/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java @@ -35,7 +35,6 @@ import java.util.zip.GZIPInputStream; import javax.annotation.Nullable; -import com.google.common.hash.Hashing; import com.google.common.io.Files; import org.apache.commons.io.FileUtils; import org.gradle.api.logging.Logger; @@ -92,13 +91,6 @@ public class HashedDownloadUtil { throw e; } - if (!Checksum.equals(to, expectedHash)) { - String actualHash = Files.asByteSource(to).hash(Hashing.sha1()).toString(); - delete(to); - - throw new IOException(String.format("Downloaded file from %s to %s and got unexpected hash of %s expected %s", from, to, actualHash, expectedHash)); - } - saveSha1(to, expectedHash, logger); } From 582b6826bd78c33d1bfdfccd94709b4c91979e7c Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Mon, 31 Jan 2022 16:30:12 +0000 Subject: [PATCH 03/27] Support client only jar, add tests for ancient minecraft versions. (#589) * Support client only jar, add tests for ancient minecraft versions. * Misc fixes. --- .../loom/api/LoomGradleExtensionAPI.java | 5 + .../minecraft/MergedMinecraftProvider.java | 4 + .../minecraft/MinecraftJarConfiguration.java | 16 +++- .../minecraft/MinecraftProvider.java | 61 ++++++++---- ...r.java => SingleJarMinecraftProvider.java} | 95 ++++++++++++++----- .../mapped/IntermediaryMinecraftProvider.java | 26 ++++- .../mapped/MappedMinecraftProvider.java | 14 ++- .../mapped/NamedMinecraftProvider.java | 26 ++++- .../ProcessedNamedMinecraftProvider.java | 26 ++++- .../net/fabricmc/loom/task/LoomTasks.java | 14 ++- .../test/integration/LegacyProjectTest.groovy | 33 +++++++ .../test/integration/MCJarConfigTest.groovy | 27 ++++++ 12 files changed, 281 insertions(+), 66 deletions(-) rename src/main/java/net/fabricmc/loom/configuration/providers/minecraft/{ServerOnlyMinecraftProvider.java => SingleJarMinecraftProvider.java} (50%) diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index f993e374..14c8f0d3 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -145,6 +145,11 @@ public interface LoomGradleExtensionAPI { getMinecraftJarConfiguration().set(MinecraftJarConfiguration.SERVER_ONLY); } + @ApiStatus.Experimental + default void clientOnlyMinecraftJar() { + getMinecraftJarConfiguration().set(MinecraftJarConfiguration.CLIENT_ONLY); + } + @ApiStatus.Experimental default void splitMinecraftJar() { getMinecraftJarConfiguration().set(MinecraftJarConfiguration.SPLIT); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MergedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MergedMinecraftProvider.java index 008cd5c0..54c3d89c 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MergedMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MergedMinecraftProvider.java @@ -58,6 +58,10 @@ public final class MergedMinecraftProvider extends MinecraftProvider { public void provide() throws Exception { super.provide(); + if (!getVersionInfo().isVersionOrNewer("2012-07-25T22:00:00+00:00" /* 1.3 release date */)) { + throw new UnsupportedOperationException("Minecraft versions 1.2.5 and older cannot be merged. Please use `loom { server/clientOnlyMinecraftJar() }`"); + } + if (!Files.exists(minecraftMergedJar) || isRefreshDeps()) { try { mergeJars(); 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 df949173..48a4ca9c 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 @@ -49,13 +49,21 @@ public enum MinecraftJarConfiguration { List.of("client", "server") ), SERVER_ONLY( - ServerOnlyMinecraftProvider::new, - IntermediaryMinecraftProvider.ServerOnlyImpl::new, - NamedMinecraftProvider.ServerOnlyImpl::new, - ProcessedNamedMinecraftProvider.ServerOnlyImpl::new, + SingleJarMinecraftProvider::server, + IntermediaryMinecraftProvider.SingleJarImpl::server, + NamedMinecraftProvider.SingleJarImpl::server, + ProcessedNamedMinecraftProvider.SingleJarImpl::server, SingleJarDecompileConfiguration::new, List.of("server") ), + CLIENT_ONLY( + SingleJarMinecraftProvider::client, + IntermediaryMinecraftProvider.SingleJarImpl::client, + NamedMinecraftProvider.SingleJarImpl::client, + ProcessedNamedMinecraftProvider.SingleJarImpl::client, + SingleJarDecompileConfiguration::new, + List.of("client") + ), SPLIT( SplitMinecraftProvider::new, IntermediaryMinecraftProvider.SplitImpl::new, 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 39df7e25..4c91dec5 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 @@ -34,6 +34,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import com.google.common.base.Preconditions; import com.google.common.io.Files; import org.gradle.api.GradleException; import org.gradle.api.Project; @@ -73,6 +74,14 @@ public abstract class MinecraftProvider { this.project = project; } + protected boolean provideClient() { + return true; + } + + protected boolean provideServer() { + return true; + } + public void provide() throws Exception { final DependencyInfo dependency = DependencyInfo.create(getProject(), Constants.Configurations.MINECRAFT); minecraftVersion = dependency.getDependency().getVersion(); @@ -88,7 +97,17 @@ public abstract class MinecraftProvider { } if (offline) { - if (minecraftClientJar.exists() && minecraftServerJar.exists()) { + boolean exists = true; + + if (provideServer() && !minecraftServerJar.exists()) { + exists = false; + } + + if (provideClient() && !minecraftClientJar.exists()) { + exists = false; + } + + if (exists) { getProject().getLogger().debug("Found client and server jars, presuming up-to-date"); } else { throw new GradleException("Missing jar(s); Client: " + minecraftClientJar.exists() + ", Server: " + minecraftServerJar.exists()); @@ -97,7 +116,9 @@ public abstract class MinecraftProvider { downloadJars(getProject().getLogger()); } - serverBundleMetadata = BundleMetadata.fromJar(minecraftServerJar.toPath()); + if (provideServer()) { + serverBundleMetadata = BundleMetadata.fromJar(minecraftServerJar.toPath()); + } libraryProvider = new MinecraftLibraryProvider(); libraryProvider.provide(this, getProject()); @@ -107,11 +128,17 @@ public abstract class MinecraftProvider { workingDir = new File(getExtension().getFiles().getUserCache(), minecraftVersion); workingDir.mkdirs(); minecraftJson = file("minecraft-info.json"); - minecraftClientJar = file("minecraft-client.jar"); - minecraftServerJar = file("minecraft-server.jar"); - minecraftExtractedServerJar = file("minecraft-extracted_server.jar"); 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"); + } + + if (provideServer()) { + minecraftServerJar = file("minecraft-server.jar"); + minecraftExtractedServerJar = file("minecraft-extracted_server.jar"); + } } private void downloadMcJson(boolean offline) throws IOException { @@ -231,14 +258,19 @@ public abstract class MinecraftProvider { return; } - MinecraftVersionMeta.Download client = versionInfo.download("client"); - MinecraftVersionMeta.Download server = versionInfo.download("server"); + if (provideClient()) { + MinecraftVersionMeta.Download client = versionInfo.download("client"); + HashedDownloadUtil.downloadIfInvalid(new URL(client.url()), minecraftClientJar, client.sha1(), logger, false); + } - HashedDownloadUtil.downloadIfInvalid(new URL(client.url()), minecraftClientJar, client.sha1(), logger, false); - HashedDownloadUtil.downloadIfInvalid(new URL(server.url()), minecraftServerJar, server.sha1(), logger, false); + if (provideServer()) { + MinecraftVersionMeta.Download server = versionInfo.download("server"); + HashedDownloadUtil.downloadIfInvalid(new URL(server.url()), minecraftServerJar, server.sha1(), logger, false); + } } protected final void extractBundledServerJar() throws IOException { + Preconditions.checkArgument(provideServer(), "Not configured to provide server jar"); Objects.requireNonNull(getServerBundleMetadata(), "Cannot bundled mc jar from none bundled server jar"); getLogger().info(":Extracting server jar from bootstrap"); @@ -269,17 +301,20 @@ public abstract class MinecraftProvider { } public File getMinecraftClientJar() { + Preconditions.checkArgument(provideClient(), "Not configured to provide client jar"); return minecraftClientJar; } // May be null on older versions @Nullable public File getMinecraftExtractedServerJar() { + Preconditions.checkArgument(provideServer(), "Not configured to provide server jar"); return minecraftExtractedServerJar; } // This may be the server bundler jar on newer versions prob not what you want. public File getMinecraftServerJar() { + Preconditions.checkArgument(provideServer(), "Not configured to provide server jar"); return minecraftServerJar; } @@ -291,14 +326,6 @@ public abstract class MinecraftProvider { return versionInfo; } - public MinecraftLibraryProvider getLibraryProvider() { - return libraryProvider; - } - - public String getTargetConfig() { - return Constants.Configurations.MINECRAFT; - } - @Nullable public BundleMetadata getServerBundleMetadata() { return serverBundleMetadata; diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/ServerOnlyMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SingleJarMinecraftProvider.java similarity index 50% rename from src/main/java/net/fabricmc/loom/configuration/providers/minecraft/ServerOnlyMinecraftProvider.java rename to src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SingleJarMinecraftProvider.java index a12343f1..62a8307e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/ServerOnlyMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SingleJarMinecraftProvider.java @@ -35,60 +35,64 @@ import net.fabricmc.tinyremapper.NonClassCopyMode; import net.fabricmc.tinyremapper.OutputConsumerPath; import net.fabricmc.tinyremapper.TinyRemapper; -public final class ServerOnlyMinecraftProvider extends MinecraftProvider { - private Path minecraftServerOnlyJar; +public final class SingleJarMinecraftProvider extends MinecraftProvider { + private final Environment environment; - public ServerOnlyMinecraftProvider(Project project) { + private Path minecraftEnvOnlyJar; + + private SingleJarMinecraftProvider(Project project, Environment environment) { super(project); + this.environment = environment; + } + + public static SingleJarMinecraftProvider server(Project project) { + return new SingleJarMinecraftProvider(project, new Server()); + } + + public static SingleJarMinecraftProvider client(Project project) { + return new SingleJarMinecraftProvider(project, new Client()); } @Override protected void initFiles() { super.initFiles(); - minecraftServerOnlyJar = path("minecraft-server-only.jar"); + minecraftEnvOnlyJar = path("minecraft-%s-only.jar".formatted(environment.name())); } @Override public List getMinecraftJars() { - return List.of(minecraftServerOnlyJar); + return List.of(minecraftEnvOnlyJar); } @Override public void provide() throws Exception { super.provide(); - boolean requiresRefresh = isRefreshDeps() || Files.notExists(minecraftServerOnlyJar); + boolean requiresRefresh = isRefreshDeps() || Files.notExists(minecraftEnvOnlyJar); if (!requiresRefresh) { return; } - BundleMetadata serverBundleMetadata = getServerBundleMetadata(); - - if (serverBundleMetadata == null) { - throw new UnsupportedOperationException("Only Minecraft versions using a bundled server jar support server only configuration, please use a merged jar setup for this version of minecraft"); - } - - extractBundledServerJar(); - final Path serverJar = getMinecraftExtractedServerJar().toPath(); + final Path inputJar = environment.getInputJar(this); TinyRemapper remapper = null; try { remapper = TinyRemapper.newRemapper().build(); - Files.deleteIfExists(minecraftServerOnlyJar); + Files.deleteIfExists(minecraftEnvOnlyJar); // Pass through tiny remapper to fix the meta-inf - try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(minecraftServerOnlyJar).build()) { - outputConsumer.addNonClassFiles(serverJar, NonClassCopyMode.FIX_META_INF, remapper); - remapper.readInputs(serverJar); + try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(minecraftEnvOnlyJar).build()) { + outputConsumer.addNonClassFiles(inputJar, NonClassCopyMode.FIX_META_INF, remapper); + remapper.readInputs(inputJar); remapper.apply(outputConsumer); } } catch (Exception e) { - Files.deleteIfExists(minecraftServerOnlyJar); - throw new RuntimeException("Failed to process server only jar", e); + Files.deleteIfExists(minecraftEnvOnlyJar); + throw new RuntimeException("Failed to process %s only jar".formatted(environment.name()), e); } finally { if (remapper != null) { remapper.finish(); @@ -96,7 +100,54 @@ public final class ServerOnlyMinecraftProvider extends MinecraftProvider { } } - public Path getMinecraftServerOnlyJar() { - return minecraftServerOnlyJar; + @Override + protected boolean provideClient() { + return environment instanceof Client; + } + + @Override + protected boolean provideServer() { + return environment instanceof Server; + } + + public Path getMinecraftEnvOnlyJar() { + return minecraftEnvOnlyJar; + } + + private interface Environment { + String name(); + + Path getInputJar(SingleJarMinecraftProvider provider) throws Exception; + } + + private static final class Server implements Environment { + @Override + public String name() { + return "server"; + } + + @Override + public Path getInputJar(SingleJarMinecraftProvider provider) throws Exception { + BundleMetadata serverBundleMetadata = provider.getServerBundleMetadata(); + + if (serverBundleMetadata == null) { + return provider.getMinecraftServerJar().toPath(); + } + + provider.extractBundledServerJar(); + return provider.getMinecraftExtractedServerJar().toPath(); + } + } + + private static final class Client implements Environment { + @Override + public String name() { + return "client"; + } + + @Override + public Path getInputJar(SingleJarMinecraftProvider provider) throws Exception { + return provider.getMinecraftClientJar().toPath(); + } } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/IntermediaryMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/IntermediaryMinecraftProvider.java index 11ce20bf..b6a56ee4 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/IntermediaryMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/IntermediaryMinecraftProvider.java @@ -32,12 +32,12 @@ import org.gradle.api.Project; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; -import net.fabricmc.loom.configuration.providers.minecraft.ServerOnlyMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider; import net.fabricmc.loom.util.SidedClassVisitor; import net.fabricmc.tinyremapper.TinyRemapper; -public abstract sealed class IntermediaryMinecraftProvider extends AbstractMappedMinecraftProvider permits IntermediaryMinecraftProvider.MergedImpl, IntermediaryMinecraftProvider.ServerOnlyImpl, IntermediaryMinecraftProvider.SplitImpl { +public abstract sealed class IntermediaryMinecraftProvider extends AbstractMappedMinecraftProvider permits IntermediaryMinecraftProvider.MergedImpl, IntermediaryMinecraftProvider.SingleJarImpl, IntermediaryMinecraftProvider.SplitImpl { public IntermediaryMinecraftProvider(Project project, M minecraftProvider) { super(project, minecraftProvider); } @@ -86,16 +86,32 @@ public abstract sealed class IntermediaryMinecraftProvider implements ServerOnly { - public ServerOnlyImpl(Project project, ServerOnlyMinecraftProvider minecraftProvider) { + public static final class SingleJarImpl extends IntermediaryMinecraftProvider implements SingleJar { + private final String env; + + private SingleJarImpl(Project project, SingleJarMinecraftProvider minecraftProvider, String env) { super(project, minecraftProvider); + this.env = env; + } + + public static SingleJarImpl server(Project project, SingleJarMinecraftProvider minecraftProvider) { + return new SingleJarImpl(project, minecraftProvider, "server"); + } + + public static SingleJarImpl client(Project project, SingleJarMinecraftProvider minecraftProvider) { + return new SingleJarImpl(project, minecraftProvider, "client"); } @Override public List getRemappedJars() { return List.of( - new RemappedJars(minecraftProvider.getMinecraftServerOnlyJar(), getServerOnlyJar(), MappingsNamespace.OFFICIAL) + new RemappedJars(minecraftProvider.getMinecraftEnvOnlyJar(), getEnvOnlyJar(), MappingsNamespace.OFFICIAL) ); } + + @Override + public String env() { + return env; + } } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MappedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MappedMinecraftProvider.java index 7bbd61be..6ec0f6c0 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MappedMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MappedMinecraftProvider.java @@ -65,16 +65,20 @@ public interface MappedMinecraftProvider { } } - interface ServerOnly extends ProviderImpl { - String SERVER_ONLY = "serverOnly"; + interface SingleJar extends ProviderImpl { + String env(); - default Path getServerOnlyJar() { - return getJar(SERVER_ONLY); + default String envName() { + return "%sOnly".formatted(env()); + } + + default Path getEnvOnlyJar() { + return getJar(envName()); } @Override default List getMinecraftJars() { - return List.of(getServerOnlyJar()); + return List.of(getEnvOnlyJar()); } } } 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 a143ae60..ec838049 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 @@ -33,7 +33,7 @@ import org.gradle.api.Project; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; -import net.fabricmc.loom.configuration.providers.minecraft.ServerOnlyMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.SidedClassVisitor; @@ -99,21 +99,37 @@ public abstract class NamedMinecraftProvider extend } } - public static final class ServerOnlyImpl extends NamedMinecraftProvider implements ServerOnly { - public ServerOnlyImpl(Project project, ServerOnlyMinecraftProvider minecraftProvider) { + public static final class SingleJarImpl extends NamedMinecraftProvider implements SingleJar { + private final String env; + + private SingleJarImpl(Project project, SingleJarMinecraftProvider minecraftProvider, String env) { super(project, minecraftProvider); + this.env = env; + } + + public static SingleJarImpl server(Project project, SingleJarMinecraftProvider minecraftProvider) { + return new SingleJarImpl(project, minecraftProvider, "server"); + } + + public static SingleJarImpl client(Project project, SingleJarMinecraftProvider minecraftProvider) { + return new SingleJarImpl(project, minecraftProvider, "client"); } @Override public List getRemappedJars() { return List.of( - new RemappedJars(minecraftProvider.getMinecraftServerOnlyJar(), getServerOnlyJar(), MappingsNamespace.OFFICIAL) + new RemappedJars(minecraftProvider.getMinecraftEnvOnlyJar(), getEnvOnlyJar(), MappingsNamespace.OFFICIAL) ); } @Override protected void applyDependencies(BiConsumer consumer) { - consumer.accept(Constants.Configurations.MINECRAFT_NAMED, SERVER_ONLY); + consumer.accept(Constants.Configurations.MINECRAFT_NAMED, envName()); + } + + @Override + public String env() { + return env; } } } 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 de22cf0b..90414803 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 @@ -36,7 +36,7 @@ import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.configuration.processors.JarProcessorManager; import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; -import net.fabricmc.loom.configuration.providers.minecraft.ServerOnlyMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider; public abstract class ProcessedNamedMinecraftProvider> extends NamedMinecraftProvider { @@ -156,14 +156,30 @@ public abstract class ProcessedNamedMinecraftProvider implements ServerOnly { - public ServerOnlyImpl(NamedMinecraftProvider.ServerOnlyImpl parentMinecraftProvide, JarProcessorManager jarProcessorManager) { + public static final class SingleJarImpl extends ProcessedNamedMinecraftProvider implements SingleJar { + private final String env; + + private SingleJarImpl(NamedMinecraftProvider.SingleJarImpl parentMinecraftProvide, JarProcessorManager jarProcessorManager, String env) { super(parentMinecraftProvide, jarProcessorManager); + this.env = env; + } + + public static ProcessedNamedMinecraftProvider.SingleJarImpl server(NamedMinecraftProvider.SingleJarImpl parentMinecraftProvide, JarProcessorManager jarProcessorManager) { + return new ProcessedNamedMinecraftProvider.SingleJarImpl(parentMinecraftProvide, jarProcessorManager, "server"); + } + + public static ProcessedNamedMinecraftProvider.SingleJarImpl client(NamedMinecraftProvider.SingleJarImpl parentMinecraftProvide, JarProcessorManager jarProcessorManager) { + return new ProcessedNamedMinecraftProvider.SingleJarImpl(parentMinecraftProvide, jarProcessorManager, "client"); } @Override - public Path getServerOnlyJar() { - return getProcessedPath(getParentMinecraftProvider().getServerOnlyJar()); + public Path getEnvOnlyJar() { + return getProcessedPath(getParentMinecraftProvider().getEnvOnlyJar()); + } + + @Override + public String env() { + return env; } } } diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index 09b36520..82a11568 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.java @@ -141,11 +141,19 @@ public final class LoomTasks { extension.getRunConfigs().create("client", RunConfigSettings::client); extension.getRunConfigs().create("server", RunConfigSettings::server); - // Remove the client run config when server only. Done by name to not remove any possible custom run configs + // Remove the client or server run config when not required. Done by name to not remove any possible custom run configs project.afterEvaluate(p -> { - if (extension.getMinecraftJarConfiguration().get() == MinecraftJarConfiguration.SERVER_ONLY) { - extension.getRunConfigs().removeIf(settings -> settings.getName().equals("client")); + String taskName = switch (extension.getMinecraftJarConfiguration().get()) { + case SERVER_ONLY -> "client"; + case CLIENT_ONLY -> "server"; + default -> null; + }; + + if (taskName == null) { + return; } + + extension.getRunConfigs().removeIf(settings -> settings.getName().equals(taskName)); }); } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy index 75259a17..7996292a 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy @@ -84,4 +84,37 @@ class LegacyProjectTest extends Specification implements GradleProjectTestTrait '1.4.7' | _ '1.3.2' | _ } + + @Unroll + def "Ancient minecraft (minecraft #version)"() { + setup: + def gradle = gradleProject(project: "minimalBase", version: PRE_RELEASE_GRADLE) + gradle.buildGradle << """ + loom { + intermediaryUrl = 'https://s.modm.us/intermediary-empty-v2.jar' + clientOnlyMinecraftJar() + } + + dependencies { + minecraft "com.mojang:minecraft:${version}" + mappings loom.layered() { + // No names + } + + modImplementation "net.fabricmc:fabric-loader:0.12.12" + } + """ + + when: + def result = gradle.run(task: "configureClientLaunch") + + then: + result.task(":configureClientLaunch").outcome == SUCCESS + + where: + version | _ + '1.2.5' | _ + 'b1.8.1' | _ + 'a1.2.5' | _ + } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/MCJarConfigTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/MCJarConfigTest.groovy index 5df77f3e..b349e25f 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/MCJarConfigTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/MCJarConfigTest.groovy @@ -59,6 +59,33 @@ class MCJarConfigTest extends Specification implements GradleProjectTestTrait { version << STANDARD_TEST_VERSIONS } + @Unroll + def "client only (gradle #version)"() { + setup: + def gradle = gradleProject(project: "minimalBase", version: version) + + gradle.buildGradle << ''' + loom { + clientOnlyMinecraftJar() + } + + dependencies { + minecraft "com.mojang:minecraft:1.18.1" + mappings "net.fabricmc:yarn:1.18.1+build.18:v2" + modImplementation "net.fabricmc:fabric-loader:0.12.12" + } + ''' + + when: + def result = gradle.run(task: "build") + + then: + result.task(":build").outcome == SUCCESS + + where: + version << STANDARD_TEST_VERSIONS + } + @Unroll def "split (gradle #version)"() { setup: From de31f1e0832d659fa69fc05cdaa5e3f5b73e80b3 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 9 Feb 2022 13:49:15 +0000 Subject: [PATCH 04/27] Intermediate mapping handling improvements. (#593) * Add API for intermediate mappings. Add hacky option to run with no intermediate mappings. * Add setter, and some getters that I need :) * Remove unused inject. * Don't add asm-all to the libraries. * Fix unit tests. --- build.gradle | 1 + .../fabricmc/loom/LoomGradleExtension.java | 4 -- .../loom/api/LoomGradleExtensionAPI.java | 19 +++++ .../IntermediateMappingsProvider.java | 47 ++++++++++++ .../mappings/GradleMappingContext.java | 10 ++- .../IntermediaryMappingsProvider.java | 71 +++++++++++++++++++ ....java => IntermediateMappingsService.java} | 45 +++++------- .../mappings/MappingsProviderImpl.java | 8 +-- .../NoOpIntermediateMappingsProvider.java | 51 +++++++++++++ .../mappings/tiny/MappingsMerger.java | 6 +- .../minecraft/MinecraftLibraryProvider.java | 3 + .../extension/LoomGradleExtensionApiImpl.java | 30 ++++++++ .../extension/LoomGradleExtensionImpl.java | 15 ++++ .../test/integration/LegacyProjectTest.groovy | 5 +- .../fabricmc/loom/test/unit/LoomMocks.groovy | 45 ++++++++++++ .../LayeredMappingsSpecification.groovy | 5 +- .../loom/test/util/GradleTestUtil.groovy | 38 ++++++++++ 17 files changed, 359 insertions(+), 44 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/api/mappings/intermediate/IntermediateMappingsProvider.java create mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediaryMappingsProvider.java rename src/main/java/net/fabricmc/loom/configuration/providers/mappings/{IntermediaryService.java => IntermediateMappingsService.java} (66%) create mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/mappings/NoOpIntermediateMappingsProvider.java create mode 100644 src/test/groovy/net/fabricmc/loom/test/unit/LoomMocks.groovy create mode 100644 src/test/groovy/net/fabricmc/loom/test/util/GradleTestUtil.groovy diff --git a/build.gradle b/build.gradle index 08aa6a87..3b98d34a 100644 --- a/build.gradle +++ b/build.gradle @@ -116,6 +116,7 @@ dependencies { exclude group: 'org.jetbrains.kotlin' } testImplementation 'net.fabricmc:fabric-installer:0.9.0' + testImplementation 'org.mockito:mockito-core:4.3.1' compileOnly 'org.jetbrains:annotations:23.0.0' } diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index 5514a79f..a654db74 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -112,10 +112,6 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI { boolean isRootProject(); - default String getIntermediaryUrl(String minecraftVersion) { - return String.format(this.getIntermediaryUrl().get(), minecraftVersion); - } - @Override MixinExtension getMixin(); diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index 14c8f0d3..af2479f5 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -35,9 +35,11 @@ import org.gradle.api.publish.maven.MavenPublication; import org.jetbrains.annotations.ApiStatus; import net.fabricmc.loom.api.decompilers.DecompilerOptions; +import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; import net.fabricmc.loom.api.mappings.layered.spec.LayeredMappingSpecBuilder; import net.fabricmc.loom.configuration.ide.RunConfigSettings; import net.fabricmc.loom.configuration.processors.JarProcessor; +import net.fabricmc.loom.configuration.providers.mappings.NoOpIntermediateMappingsProvider; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration; import net.fabricmc.loom.util.DeprecationHelper; @@ -130,6 +132,23 @@ public interface LoomGradleExtensionAPI { */ Property getEnableTransitiveAccessWideners(); + @ApiStatus.Experimental + IntermediateMappingsProvider getIntermediateMappingsProvider(); + + @ApiStatus.Experimental + void setIntermediateMappingsProvider(IntermediateMappingsProvider intermediateMappingsProvider); + + @ApiStatus.Experimental + void setIntermediateMappingsProvider(Class clazz, Action action); + + /** + * An Experimental option to provide empty intermediate mappings, to be used for game versions without any intermediate mappings. + */ + @ApiStatus.Experimental + default void noIntermediateMappings() { + setIntermediateMappingsProvider(NoOpIntermediateMappingsProvider.class, p -> { }); + } + /** * Use "%1$s" as a placeholder for the minecraft version. * diff --git a/src/main/java/net/fabricmc/loom/api/mappings/intermediate/IntermediateMappingsProvider.java b/src/main/java/net/fabricmc/loom/api/mappings/intermediate/IntermediateMappingsProvider.java new file mode 100644 index 00000000..f6b578a3 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/api/mappings/intermediate/IntermediateMappingsProvider.java @@ -0,0 +1,47 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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.api.mappings.intermediate; + +import java.io.IOException; +import java.nio.file.Path; + +import org.gradle.api.Named; +import org.gradle.api.provider.Property; +import org.jetbrains.annotations.ApiStatus; + +/** + * A simple API to allow 3rd party plugins. + * Implement by creating an abstract class overriding provide and getName + */ +@ApiStatus.Experimental +public abstract class IntermediateMappingsProvider implements Named { + public abstract Property getMinecraftVersion(); + + /** + * Generate or download a tinyv2 mapping file with intermediary and named namespaces. + * @throws IOException + */ + public abstract void provide(Path tinyMappings) throws IOException; +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GradleMappingContext.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GradleMappingContext.java index e2a83da6..39fe06db 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GradleMappingContext.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GradleMappingContext.java @@ -65,7 +65,7 @@ public class GradleMappingContext implements MappingContext { @Override public Supplier intermediaryTree() { - return () -> IntermediaryService.getInstance(project, minecraftProvider()).getMemoryMappingTree(); + return () -> IntermediateMappingsService.getInstance(project, minecraftProvider()).getMemoryMappingTree(); } @Override @@ -82,4 +82,12 @@ public class GradleMappingContext implements MappingContext { public Logger getLogger() { return project.getLogger(); } + + public Project getProject() { + return project; + } + + public LoomGradleExtension getExtension() { + return extension; + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediaryMappingsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediaryMappingsProvider.java new file mode 100644 index 00000000..b29c70f3 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediaryMappingsProvider.java @@ -0,0 +1,71 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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.mappings; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; + +import com.google.common.net.UrlEscapers; +import org.gradle.api.provider.Property; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; +import net.fabricmc.loom.util.DownloadUtil; + +public abstract class IntermediaryMappingsProvider extends IntermediateMappingsProvider { + private static final Logger LOGGER = LoggerFactory.getLogger(IntermediateMappingsProvider.class); + + public abstract Property getIntermediaryUrl(); + + @Override + public void provide(Path tinyMappings) throws IOException { + if (Files.exists(tinyMappings) && !LoomGradlePlugin.refreshDeps) { + return; + } + + // Download and extract intermediary + final Path intermediaryJarPath = Files.createTempFile(getName(), ".jar"); + final String encodedMcVersion = UrlEscapers.urlFragmentEscaper().escape(getMinecraftVersion().get()); + final URL url = new URL(getIntermediaryUrl().get().formatted(encodedMcVersion)); + + LOGGER.info("Downloading intermediary from {}", url); + + Files.deleteIfExists(tinyMappings); + Files.deleteIfExists(intermediaryJarPath); + + DownloadUtil.downloadIfChanged(url, intermediaryJarPath.toFile(), LOGGER); + MappingsProviderImpl.extractMappings(intermediaryJarPath, tinyMappings); + } + + @Override + public @NotNull String getName() { + return "intermediary-v2"; + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediaryService.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java similarity index 66% rename from src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediaryService.java rename to src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java index bd0c4e90..13b1427c 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediaryService.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java @@ -25,10 +25,8 @@ package net.fabricmc.loom.configuration.providers.mappings; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -37,59 +35,52 @@ import java.util.Objects; import java.util.function.Supplier; import com.google.common.base.Suppliers; -import com.google.common.net.UrlEscapers; import org.gradle.api.Project; import org.jetbrains.annotations.VisibleForTesting; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; -import net.fabricmc.loom.util.DownloadUtil; import net.fabricmc.loom.util.service.SharedService; import net.fabricmc.loom.util.service.SharedServiceManager; import net.fabricmc.mappingio.adapter.MappingNsCompleter; import net.fabricmc.mappingio.format.Tiny2Reader; import net.fabricmc.mappingio.tree.MemoryMappingTree; -public final class IntermediaryService implements SharedService { - private static final Logger LOGGER = LoggerFactory.getLogger(IntermediaryService.class); - +public final class IntermediateMappingsService implements SharedService { private final Path intermediaryTiny; private final Supplier memoryMappingTree = Suppliers.memoize(this::createMemoryMappingTree); - private IntermediaryService(Path intermediaryTiny) { + private IntermediateMappingsService(Path intermediaryTiny) { this.intermediaryTiny = intermediaryTiny; } - public static synchronized IntermediaryService getInstance(Project project, MinecraftProvider minecraftProvider) { + public static synchronized IntermediateMappingsService getInstance(Project project, MinecraftProvider minecraftProvider) { final LoomGradleExtension extension = LoomGradleExtension.get(project); - final String encodedMinecraftVersion = UrlEscapers.urlFragmentEscaper().escape(minecraftProvider.minecraftVersion()); - final String intermediaryArtifactUrl = extension.getIntermediaryUrl(encodedMinecraftVersion); + final IntermediateMappingsProvider intermediateProvider = extension.getIntermediateMappingsProvider(); + final String id = "IntermediateMappingsService:%s:%s".formatted(intermediateProvider.getName(), intermediateProvider.getMinecraftVersion().get()); - return SharedServiceManager.get(project).getOrCreateService("IntermediaryService:" + intermediaryArtifactUrl, - () -> create(intermediaryArtifactUrl, minecraftProvider)); + return SharedServiceManager.get(project).getOrCreateService(id, () -> create(intermediateProvider, minecraftProvider)); } @VisibleForTesting - public static IntermediaryService create(String intermediaryUrl, MinecraftProvider minecraftProvider) { - final Path intermediaryTiny = minecraftProvider.file("intermediary-v2.tiny").toPath(); - - if (!Files.exists(intermediaryTiny) || LoomGradlePlugin.refreshDeps) { - // Download and extract intermediary - File intermediaryJar = minecraftProvider.file("intermediary-v2.jar"); + public static IntermediateMappingsService create(IntermediateMappingsProvider intermediateMappingsProvider, MinecraftProvider minecraftProvider) { + final Path intermediaryTiny = minecraftProvider.file(intermediateMappingsProvider.getName() + ".tiny").toPath(); + try { + intermediateMappingsProvider.provide(intermediaryTiny); + } catch (IOException e) { try { - DownloadUtil.downloadIfChanged(new URL(intermediaryUrl), intermediaryJar, LOGGER); - MappingsProviderImpl.extractMappings(intermediaryJar.toPath(), intermediaryTiny); - } catch (IOException e) { - throw new UncheckedIOException("Failed to download and extract intermediary", e); + Files.deleteIfExists(intermediaryTiny); + } catch (IOException ex) { + ex.printStackTrace(); } + + throw new UncheckedIOException("Failed to provide intermediate mappings", e); } - return new IntermediaryService(intermediaryTiny); + return new IntermediateMappingsService(intermediaryTiny); } private MemoryMappingTree createMemoryMappingTree() { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java index 9b2b78b6..7c02ee4d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java @@ -83,9 +83,9 @@ public class MappingsProviderImpl implements MappingsProvider, SharedService { private UnpickMetadata unpickMetadata; private Map signatureFixes; - private final Supplier intermediaryService; + private final Supplier intermediaryService; - private MappingsProviderImpl(String mappingsIdentifier, Path mappingsWorkingDir, Supplier intermediaryService) { + private MappingsProviderImpl(String mappingsIdentifier, Path mappingsWorkingDir, Supplier intermediaryService) { this.mappingsIdentifier = mappingsIdentifier; this.mappingsWorkingDir = mappingsWorkingDir; @@ -99,7 +99,7 @@ public class MappingsProviderImpl implements MappingsProvider, SharedService { public static synchronized MappingsProviderImpl getInstance(Project project, DependencyInfo dependency, MinecraftProvider minecraftProvider) { return SharedServiceManager.get(project).getOrCreateService("MappingsProvider:%s:%s".formatted(dependency.getDepString(), minecraftProvider.minecraftVersion()), () -> { - Supplier intermediaryService = Suppliers.memoize(() -> IntermediaryService.getInstance(project, minecraftProvider)); + Supplier intermediaryService = Suppliers.memoize(() -> IntermediateMappingsService.getInstance(project, minecraftProvider)); return create(dependency, minecraftProvider, intermediaryService); }); } @@ -108,7 +108,7 @@ public class MappingsProviderImpl implements MappingsProvider, SharedService { return Objects.requireNonNull(mappingTree, "Cannot get mappings before they have been read").get(); } - private static MappingsProviderImpl create(DependencyInfo dependency, MinecraftProvider minecraftProvider, Supplier intermediaryService) { + private static MappingsProviderImpl create(DependencyInfo dependency, MinecraftProvider minecraftProvider, Supplier intermediaryService) { final String version = dependency.getResolvedVersion(); final Path inputJar = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve mappings: " + dependency)).toPath(); final String mappingsName = StringUtils.removeSuffix(dependency.getDependency().getGroup() + "." + dependency.getDependency().getName(), "-unmerged"); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/NoOpIntermediateMappingsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/NoOpIntermediateMappingsProvider.java new file mode 100644 index 00000000..28ddfc63 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/NoOpIntermediateMappingsProvider.java @@ -0,0 +1,51 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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.mappings; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.jetbrains.annotations.NotNull; + +import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; + +/** + * A bit of a hack, creates an empty intermediary mapping file to be used for mc versions without any intermediate mappings. + */ +public abstract class NoOpIntermediateMappingsProvider extends IntermediateMappingsProvider { + private static final String HEADER = "tiny\t2\t0\tofficial\tintermediary"; + + @Override + public void provide(Path tinyMappings) throws IOException { + Files.writeString(tinyMappings, HEADER, StandardCharsets.UTF_8); + } + + @Override + public @NotNull String getName() { + return "empty-intermediate"; + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/MappingsMerger.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/MappingsMerger.java index 86aa7afa..5988f143 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/MappingsMerger.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/MappingsMerger.java @@ -38,7 +38,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; -import net.fabricmc.loom.configuration.providers.mappings.IntermediaryService; +import net.fabricmc.loom.configuration.providers.mappings.IntermediateMappingsService; import net.fabricmc.mappingio.adapter.MappingNsCompleter; import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; import net.fabricmc.mappingio.format.Tiny2Reader; @@ -49,12 +49,12 @@ import net.fabricmc.mappingio.tree.MemoryMappingTree; public final class MappingsMerger { private static final Logger LOGGER = LoggerFactory.getLogger(MappingsMerger.class); - public static void mergeAndSaveMappings(Path from, Path out, IntermediaryService intermediaryService) throws IOException { + public static void mergeAndSaveMappings(Path from, Path out, IntermediateMappingsService intermediateMappingsService) throws IOException { Stopwatch stopwatch = Stopwatch.createStarted(); LOGGER.info(":merging mappings"); MemoryMappingTree intermediaryTree = new MemoryMappingTree(); - intermediaryService.getMemoryMappingTree().accept(new MappingSourceNsSwitch(intermediaryTree, MappingsNamespace.INTERMEDIARY.toString())); + intermediateMappingsService.getMemoryMappingTree().accept(new MappingSourceNsSwitch(intermediaryTree, MappingsNamespace.INTERMEDIARY.toString())); try (BufferedReader reader = Files.newBufferedReader(from, StandardCharsets.UTF_8)) { Tiny2Reader.read(reader, intermediaryTree); 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 bcc1d148..249a0a06 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 @@ -62,6 +62,9 @@ public class MinecraftLibraryProvider { // 1.4.7 contains an LWJGL version with an invalid maven pom, set the metadata sources to not use the pom for this version. if ("org.lwjgl.lwjgl:lwjgl:2.9.1-nightly-20130708-debug3".equals(library.name())) { LoomRepositoryPlugin.setupForLegacyVersions(project); + } else if (library.name().startsWith("org.ow2.asm:asm-all")) { + // Don't want asm-all, use the modern split version. + continue; } if (runtimeOnlyLog4j && library.name().startsWith("org.apache.logging.log4j")) { diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index 02b23e2c..d6594e01 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -40,6 +40,7 @@ import net.fabricmc.loom.api.InterfaceInjectionExtensionAPI; import net.fabricmc.loom.api.LoomGradleExtensionAPI; import net.fabricmc.loom.api.MixinExtensionAPI; import net.fabricmc.loom.api.decompilers.DecompilerOptions; +import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; import net.fabricmc.loom.api.mappings.layered.spec.LayeredMappingSpecBuilder; import net.fabricmc.loom.configuration.ide.RunConfigSettings; import net.fabricmc.loom.configuration.mods.ModVersionParser; @@ -65,6 +66,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA protected final Property setupRemappedVariants; protected final Property transitiveAccessWideners; protected final Property intermediary; + protected final Property intermediateMappingsProvider; private final Property runtimeOnlyLog4j; private final Property minecraftJarConfiguration; private final InterfaceInjectionExtensionAPI interfaceInjectionExtension; @@ -92,6 +94,9 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA this.intermediary = project.getObjects().property(String.class) .convention("https://maven.fabricmc.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar"); + this.intermediateMappingsProvider = project.getObjects().property(IntermediateMappingsProvider.class); + this.intermediateMappingsProvider.finalizeValueOnRead(); + this.versionParser = new ModVersionParser(project); this.deprecationHelper = new DeprecationHelper.ProjectBased(project); @@ -214,6 +219,26 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA return intermediary; } + @Override + public IntermediateMappingsProvider getIntermediateMappingsProvider() { + return intermediateMappingsProvider.get(); + } + + @Override + public void setIntermediateMappingsProvider(IntermediateMappingsProvider intermediateMappingsProvider) { + this.intermediateMappingsProvider.set(intermediateMappingsProvider); + } + + @Override + public void setIntermediateMappingsProvider(Class clazz, Action action) { + T provider = getProject().getObjects().newInstance(clazz); + configureIntermediateMappingsProviderInternal(provider); + action.execute(provider); + intermediateMappingsProvider.set(provider); + } + + protected abstract void configureIntermediateMappingsProviderInternal(T provider); + @Override public void disableDeprecatedPomGeneration(MavenPublication publication) { net.fabricmc.loom.configuration.MavenPublication.excludePublication(publication); @@ -256,6 +281,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA throw new RuntimeException("Yeah... something is really wrong"); } + @Override + protected void configureIntermediateMappingsProviderInternal(T provider) { + throw new RuntimeException("Yeah... something is really wrong"); + } + @Override public MixinExtension getMixin() { throw new RuntimeException("Yeah... something is really wrong"); diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index d805dcb8..b1871906 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -42,11 +42,13 @@ import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.FileCollection; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.InstallerData; import net.fabricmc.loom.configuration.LoomDependencyManager; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile; import net.fabricmc.loom.configuration.processors.JarProcessorManager; +import net.fabricmc.loom.configuration.providers.mappings.IntermediaryMappingsProvider; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider; @@ -78,6 +80,13 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen this.mixinApExtension = project.getObjects().newInstance(MixinExtensionImpl.class, project); this.loomFiles = files; this.unmappedMods = project.files(); + + // Setup the default intermediate mappings provider. + setIntermediateMappingsProvider(IntermediaryMappingsProvider.class, provider -> { + provider.getIntermediaryUrl() + .convention(getIntermediaryUrl()) + .finalizeValueOnRead(); + }); } @Override @@ -226,4 +235,10 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen public void addTransitiveAccessWideners(List accessWidenerFiles) { transitiveAccessWideners.addAll(accessWidenerFiles); } + + @Override + protected void configureIntermediateMappingsProviderInternal(T provider) { + provider.getMinecraftVersion().set(getProject().provider(() -> getMinecraftProvider().minecraftVersion())); + provider.getMinecraftVersion().disallowChanges(); + } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy index 7996292a..97bdb50e 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy @@ -32,7 +32,6 @@ import static net.fabricmc.loom.test.LoomTestConstants.PRE_RELEASE_GRADLE import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS import static org.gradle.testkit.runner.TaskOutcome.SUCCESS -// This test uses gradle 4.9 and 1.14.4 v1 mappings class LegacyProjectTest extends Specification implements GradleProjectTestTrait { @Unroll def "legacy build (gradle #version)"() { @@ -55,7 +54,7 @@ class LegacyProjectTest extends Specification implements GradleProjectTestTrait def gradle = gradleProject(project: "minimalBase", version: PRE_RELEASE_GRADLE) gradle.buildGradle << """ loom { - intermediaryUrl = 'https://s.modm.us/intermediary-empty-v2.jar' + noIntermediateMappings() } dependencies { @@ -91,7 +90,7 @@ class LegacyProjectTest extends Specification implements GradleProjectTestTrait def gradle = gradleProject(project: "minimalBase", version: PRE_RELEASE_GRADLE) gradle.buildGradle << """ loom { - intermediaryUrl = 'https://s.modm.us/intermediary-empty-v2.jar' + noIntermediateMappings() clientOnlyMinecraftJar() } diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/LoomMocks.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/LoomMocks.groovy new file mode 100644 index 00000000..1586abdb --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/LoomMocks.groovy @@ -0,0 +1,45 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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 + +import net.fabricmc.loom.configuration.providers.mappings.IntermediaryMappingsProvider +import net.fabricmc.loom.test.util.GradleTestUtil + +import static org.mockito.Mockito.spy +import static org.mockito.Mockito.when + +class LoomMocks { + static IntermediaryMappingsProvider intermediaryMappingsProviderMock(String minecraftVersion, String intermediaryUrl) { + def minecraftVersionProperty = GradleTestUtil.mockProperty(minecraftVersion) + def intermediaryUrlProperty = GradleTestUtil.mockProperty(intermediaryUrl) + + Objects.requireNonNull(minecraftVersionProperty.get()) + + def mock = spy(IntermediaryMappingsProvider.class) + when(mock.getMinecraftVersion()).thenReturn(minecraftVersionProperty) + when(mock.getIntermediaryUrl()).thenReturn(intermediaryUrlProperty) + return mock + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy index e0ce582b..3e226906 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy @@ -28,10 +28,11 @@ import net.fabricmc.loom.api.mappings.layered.MappingContext import net.fabricmc.loom.api.mappings.layered.MappingLayer import net.fabricmc.loom.api.mappings.layered.MappingsNamespace import net.fabricmc.loom.api.mappings.layered.spec.MappingsSpec -import net.fabricmc.loom.configuration.providers.mappings.IntermediaryService +import net.fabricmc.loom.configuration.providers.mappings.IntermediateMappingsService import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsProcessor import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider +import net.fabricmc.loom.test.unit.LoomMocks import net.fabricmc.mappingio.adapter.MappingDstNsReorder import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch import net.fabricmc.mappingio.format.Tiny2Writer @@ -122,7 +123,7 @@ abstract class LayeredMappingsSpecification extends Specification implements Lay @Override Supplier intermediaryTree() { return { - IntermediaryService.create(intermediaryUrl, minecraftProvider()).memoryMappingTree + IntermediateMappingsService.create(LoomMocks.intermediaryMappingsProviderMock("test", intermediaryUrl), minecraftProvider()).memoryMappingTree } } diff --git a/src/test/groovy/net/fabricmc/loom/test/util/GradleTestUtil.groovy b/src/test/groovy/net/fabricmc/loom/test/util/GradleTestUtil.groovy new file mode 100644 index 00000000..e19660c1 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/util/GradleTestUtil.groovy @@ -0,0 +1,38 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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.util + +import org.gradle.api.provider.Property + +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.when + +class GradleTestUtil { + static Property mockProperty(T value) { + def mock = mock(Property.class) + when(mock.get()).thenReturn(Objects.requireNonNull(value)) + return mock + } +} From 474d02f1b3b86c513d4893db5dda84d237c3ff0e Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Mon, 28 Feb 2022 18:41:03 +0000 Subject: [PATCH 05/27] Update gradle and deps. --- .github/workflows/test-push.yml | 6 +- build.gradle | 12 +- gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 269 +++++++++++------- ...ClassMetadataRemappingAnnotationVisitor.kt | 2 +- ...sMetadataRemappingAnnotationVisitorTest.kt | 8 +- 7 files changed, 174 insertions(+), 125 deletions(-) diff --git a/.github/workflows/test-push.yml b/.github/workflows/test-push.yml index 63458a4a..5b3b2393 100644 --- a/.github/workflows/test-push.yml +++ b/.github/workflows/test-push.yml @@ -5,7 +5,7 @@ jobs: strategy: fail-fast: false matrix: - version: [7.3.1-jdk17] + version: [7.4.0-jdk17] runs-on: ubuntu-20.04 container: image: gradle:${{ matrix.version }} @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-20.04 container: - image: gradle:7.3.1-jdk17 + image: gradle:7.4.0-jdk17 options: --user root steps: @@ -41,7 +41,7 @@ jobs: strategy: fail-fast: false matrix: - version: [7.3.1-jdk17] + version: [7.4.0-jdk17] test: ${{ fromJson(needs.prepare_test_matrix.outputs.matrix) }} runs-on: ubuntu-20.04 diff --git a/build.gradle b/build.gradle index 3b98d34a..f2341223 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ plugins { id 'jacoco' id 'codenarc' id "org.jetbrains.kotlin.jvm" version "1.5.31" // Must match the version included with gradle. - id "com.diffplug.spotless" version "5.14.1" + id "com.diffplug.spotless" version "6.3.0" } sourceCompatibility = 17 @@ -68,8 +68,8 @@ dependencies { // libraries implementation ('commons-io:commons-io:2.11.0') - implementation ('com.google.code.gson:gson:2.8.9') - implementation ('com.fasterxml.jackson.core:jackson-databind:2.13.0') + implementation ('com.google.code.gson:gson:2.9.0') + implementation ('com.fasterxml.jackson.core:jackson-databind:2.13.1') implementation ('com.google.guava:guava:31.0.1-jre') implementation ('org.ow2.asm:asm:9.2') implementation ('org.ow2.asm:asm-analysis:9.2') @@ -111,8 +111,8 @@ dependencies { testImplementation('org.spockframework:spock-core:2.0-groovy-3.0') { exclude module: 'groovy-all' } - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.1' - testImplementation ('io.javalin:javalin:3.13.11') { + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.2' + testImplementation ('io.javalin:javalin:4.3.0') { exclude group: 'org.jetbrains.kotlin' } testImplementation 'net.fabricmc:fabric-installer:0.9.0' @@ -153,7 +153,7 @@ spotless { checkstyle { configFile = file('checkstyle.xml') - toolVersion = '9.2' + toolVersion = '9.3' } codenarc { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644 GIT binary patch delta 18435 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 B8dv}T delta 17845 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 X49Luk)baa85H9$5YCsTPT`SVRWMtMW diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ac0b842f..b1159fc5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0c..c53aefaa 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt index f847fc19..dcbc6afa 100644 --- a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt +++ b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt @@ -63,7 +63,7 @@ class KotlinClassMetadataRemappingAnnotationVisitor(private val remapper: Remapp } // Can only be turned into KmPackage which is useless data is KotlinClassMetadata.FileFacade, is KotlinClassMetadata.MultiFileClassPart, - // Can't be turned into data + // Can't be turned into data is KotlinClassMetadata.MultiFileClassFacade, is KotlinClassMetadata.Unknown, null -> { // do nothing accept(next) diff --git a/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt b/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt index 369beaaa..e9a6f478 100644 --- a/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt +++ b/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt @@ -24,10 +24,6 @@ package net.fabricmc.loom.test.kotlin -import java.io.File -import java.io.PrintWriter -import java.io.StringWriter -import java.nio.file.Paths import net.fabricmc.loom.kotlin.remapping.KotlinMetadataRemappingClassVisitor import net.fabricmc.loom.util.TinyRemapperHelper import net.fabricmc.mappingio.MappingReader @@ -39,6 +35,10 @@ import org.objectweb.asm.ClassReader import org.objectweb.asm.Opcodes import org.objectweb.asm.util.Textifier import org.objectweb.asm.util.TraceClassVisitor +import java.io.File +import java.io.PrintWriter +import java.io.StringWriter +import java.nio.file.Paths // See: https://github.com/JetBrains/kotlin/blob/master/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/MetadataSmokeTest.kt#L67 class KotlinClassMetadataRemappingAnnotationVisitorTest { From fff4afe4051a905320cdef616c61b7094b66b369 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Fri, 4 Mar 2022 15:01:45 +0000 Subject: [PATCH 06/27] Initial support for split client/common sourcesets. (#606) --- bootstrap/build.gradle | 8 +- build.gradle | 8 +- .../loom/api/LoomGradleExtensionAPI.java | 6 + .../mixin/AnnotationProcessorInvoker.java | 8 +- .../configuration/CompileConfiguration.java | 19 +- .../minecraft/MinecraftSourceSets.java | 230 ++++++++++++++++++ .../minecraft/SplitMinecraftProvider.java | 3 +- .../AbstractMappedMinecraftProvider.java | 20 +- .../mapped/NamedMinecraftProvider.java | 17 +- .../ProcessedNamedMinecraftProvider.java | 14 +- .../extension/LoomGradleExtensionApiImpl.java | 23 ++ .../net/fabricmc/loom/task/LoomTasks.java | 14 +- .../net/fabricmc/loom/task/RemapJarTask.java | 108 ++++++-- .../task/launch/GenerateDLIConfigTask.java | 16 ++ .../loom/task/service/JarManifestService.java | 9 +- .../net/fabricmc/loom/util/Constants.java | 1 - .../fabricmc/loom/util/SidedClassVisitor.java | 24 +- .../java/net/fabricmc/loom/util/ZipUtils.java | 19 +- .../loom/test/LoomTestConstants.groovy | 2 +- .../test/integration/MCJarConfigTest.groovy | 29 ++- .../test/integration/SplitProjectTest.groovy | 49 ++++ .../projects/splitSources/build.gradle | 27 ++ .../example/client/ExampleModClient.java | 19 ++ .../example/client/TestClientClass.java | 8 + .../example/client/mixin/ExampleMixin.java | 16 ++ .../client/resources/modid.client.mixins.json | 14 ++ .../java/net/fabricmc/example/ExampleMod.java | 19 ++ .../src/main/resources/assets/modid/icon.png | Bin 0 -> 453 bytes .../src/main/resources/fabric.mod.json | 21 ++ 29 files changed, 679 insertions(+), 72 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java create mode 100644 src/test/groovy/net/fabricmc/loom/test/integration/SplitProjectTest.groovy create mode 100644 src/test/resources/projects/splitSources/build.gradle create mode 100644 src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/ExampleModClient.java create mode 100644 src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/TestClientClass.java create mode 100644 src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/mixin/ExampleMixin.java create mode 100644 src/test/resources/projects/splitSources/src/client/resources/modid.client.mixins.json create mode 100644 src/test/resources/projects/splitSources/src/main/java/net/fabricmc/example/ExampleMod.java create mode 100644 src/test/resources/projects/splitSources/src/main/resources/assets/modid/icon.png create mode 100644 src/test/resources/projects/splitSources/src/main/resources/fabric.mod.json diff --git a/bootstrap/build.gradle b/bootstrap/build.gradle index 54c094da..efd4f1e4 100644 --- a/bootstrap/build.gradle +++ b/bootstrap/build.gradle @@ -3,12 +3,14 @@ plugins { id 'groovy' } -sourceCompatibility = 8 -targetCompatibility = 8 +java { + toolchain { + languageVersion = JavaLanguageVersion.of(8) + } +} tasks.withType(JavaCompile).configureEach { it.options.encoding = "UTF-8" - it.options.release = 8 } repositories { diff --git a/build.gradle b/build.gradle index f2341223..9e35f2d6 100644 --- a/build.gradle +++ b/build.gradle @@ -12,12 +12,14 @@ plugins { id "com.diffplug.spotless" version "6.3.0" } -sourceCompatibility = 17 -targetCompatibility = 17 +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} tasks.withType(JavaCompile).configureEach { it.options.encoding = "UTF-8" - it.options.release = 17 } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index af2479f5..485c4e70 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -174,5 +174,11 @@ public interface LoomGradleExtensionAPI { getMinecraftJarConfiguration().set(MinecraftJarConfiguration.SPLIT); } + @ApiStatus.Experimental + void splitEnvironmentSourceSets(); + + @ApiStatus.Experimental + boolean areEnvironmentSourceSetsSplit(); + Property getRuntimeOnlyLog4j(); } diff --git a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java index 99ca5bac..f6315d37 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java @@ -41,6 +41,7 @@ import org.gradle.api.tasks.SourceSet; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.ide.idea.IdeaUtils; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.extension.MixinExtension; import net.fabricmc.loom.task.service.MixinMappingsService; import net.fabricmc.loom.util.Constants; @@ -101,15 +102,16 @@ public abstract class AnnotationProcessorInvoker { public void configureMixin() { ConfigurationContainer configs = project.getConfigurations(); + MinecraftSourceSets minecraftSourceSets = MinecraftSourceSets.get(project); if (!IdeaUtils.isIdeaSync()) { for (Configuration processorConfig : apConfigurations) { project.getLogger().info("Adding mixin to classpath of AP config: " + processorConfig.getName()); // Pass named MC classpath to mixin AP classpath processorConfig.extendsFrom( - configs.getByName(Constants.Configurations.MINECRAFT_NAMED), - configs.getByName(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED), - configs.getByName(Constants.Configurations.MAPPINGS_FINAL) + configs.getByName(minecraftSourceSets.getCombinedSourceSetName()), + configs.getByName(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED), + configs.getByName(Constants.Configurations.MAPPINGS_FINAL) ); // Add Mixin and mixin extensions (fabric-mixin-compile-extensions pulls mixin itself too) diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index 5f265430..673b50c7 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -25,6 +25,7 @@ package net.fabricmc.loom.configuration; import java.nio.charset.StandardCharsets; +import java.util.List; import org.gradle.api.NamedDomainObjectProvider; import org.gradle.api.Project; @@ -48,6 +49,7 @@ import net.fabricmc.loom.configuration.processors.JarProcessorManager; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; 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.IntermediaryMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider; import net.fabricmc.loom.extension.MixinExtension; @@ -62,7 +64,6 @@ public final class CompileConfiguration { extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH, configuration -> configuration.setTransitive(true)); extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED, configuration -> configuration.setTransitive(false)); - extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_NAMED, configuration -> configuration.setTransitive(false)); // The launchers do not recurse dependencies NamedDomainObjectProvider serverDeps = extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES, configuration -> configuration.setTransitive(false)); extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_RUNTIME_DEPENDENCIES, configuration -> configuration.setTransitive(false)); extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_DEPENDENCIES, configuration -> { @@ -114,13 +115,7 @@ public final class CompileConfiguration { } } - extendsFrom(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.MINECRAFT_NAMED, project); - extendsFrom(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.MINECRAFT_NAMED, project); - extendsFrom(JavaPlugin.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.MINECRAFT_NAMED, project); - extendsFrom(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.MINECRAFT_NAMED, project); - extendsFrom(Constants.Configurations.LOADER_DEPENDENCIES, Constants.Configurations.MINECRAFT_DEPENDENCIES, project); - extendsFrom(Constants.Configurations.MINECRAFT_NAMED, Constants.Configurations.LOADER_DEPENDENCIES, project); extendsFrom(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.MAPPINGS_FINAL, project); extendsFrom(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.MAPPINGS_FINAL, project); @@ -146,6 +141,8 @@ public final class CompileConfiguration { }); p.afterEvaluate(project -> { + MinecraftSourceSets.get(project).afterEvaluate(project); + try { setupMinecraft(project); } catch (Exception e) { @@ -284,7 +281,13 @@ public final class CompileConfiguration { .apply(project, extension.getNamedMinecraftProvider()).afterEvaluation(); } - private static void extendsFrom(String a, String b, Project project) { + public static void extendsFrom(List parents, String b, Project project) { + for (String parent : parents) { + extendsFrom(parent, b, project); + } + } + + public static void extendsFrom(String a, String b, Project project) { project.getConfigurations().getByName(a, configuration -> configuration.extendsFrom(project.getConfigurations().getByName(b))); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java new file mode 100644 index 00000000..ef9427ae --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java @@ -0,0 +1,230 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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 static net.fabricmc.loom.configuration.CompileConfiguration.extendsFrom; + +import java.util.List; +import java.util.function.BiConsumer; + +import com.google.common.base.Preconditions; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.tasks.SourceSet; +import org.gradle.jvm.tasks.Jar; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.util.Constants; + +public abstract sealed class MinecraftSourceSets permits MinecraftSourceSets.Single, MinecraftSourceSets.Split { + public static MinecraftSourceSets get(Project project) { + return LoomGradleExtension.get(project).areEnvironmentSourceSetsSplit() ? Split.INSTANCE : Single.INSTANCE; + } + + public abstract void applyDependencies(BiConsumer consumer, List targets); + + public abstract String getCombinedSourceSetName(); + + public abstract String getSourceSetForEnv(String env); + + protected abstract List getAllSourceSetNames(); + + public void evaluateSplit(Project project) { + final LoomGradleExtension extension = LoomGradleExtension.get(project); + Preconditions.checkArgument(extension.areEnvironmentSourceSetsSplit()); + + Split.INSTANCE.evaluate(project); + } + + public abstract void afterEvaluate(Project project); + + protected void createSourceSets(Project project) { + final LoomGradleExtension extension = LoomGradleExtension.get(project); + + for (String name : getAllSourceSetNames()) { + extension.createLazyConfiguration(name, configuration -> configuration.setTransitive(false)); + + // All the configurations extend the loader deps. + extendsFrom(name, Constants.Configurations.LOADER_DEPENDENCIES, project); + } + } + + /** + * Used when we have a single source set, either with split or merged jars. + */ + public static final class Single extends MinecraftSourceSets { + private static final String MINECRAFT_NAMED = "minecraftNamed"; + + private static final Single INSTANCE = new Single(); + + @Override + public void applyDependencies(BiConsumer consumer, List targets) { + for (String target : targets) { + consumer.accept(MINECRAFT_NAMED, target); + } + } + + @Override + public String getCombinedSourceSetName() { + return MINECRAFT_NAMED; + } + + @Override + public String getSourceSetForEnv(String env) { + return SourceSet.MAIN_SOURCE_SET_NAME; + } + + @Override + protected List getAllSourceSetNames() { + return List.of(MINECRAFT_NAMED); + } + + @Override + public void afterEvaluate(Project project) { + // This is done in afterEvaluate as we need to be sure that split source sets was not enabled. + createSourceSets(project); + + // Default compile and runtime sourcesets. + extendsFrom(List.of( + JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME, + JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME, + JavaPlugin.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME, + JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME), + MINECRAFT_NAMED, project + ); + } + } + + /** + * Used when we have a split client/common source set and split jars. + */ + public static final class Split extends MinecraftSourceSets { + private static final String MINECRAFT_COMMON_NAMED = "minecraftCommonNamed"; + private static final String MINECRAFT_CLIENT_ONLY_NAMED = "minecraftClientOnlyNamed"; + private static final String MINECRAFT_COMBINED_NAMED = "minecraftCombinedNamed"; + + private static final String CLIENT_ONLY_SOURCE_SET_NAME = "client"; + + private static final Split INSTANCE = new Split(); + + @Override + public void applyDependencies(BiConsumer consumer, List targets) { + Preconditions.checkArgument(targets.size() == 2); + Preconditions.checkArgument(targets.contains("common")); + Preconditions.checkArgument(targets.contains("clientOnly")); + + consumer.accept(MINECRAFT_COMMON_NAMED, "common"); + consumer.accept(MINECRAFT_CLIENT_ONLY_NAMED, "clientOnly"); + } + + @Override + public String getCombinedSourceSetName() { + return MINECRAFT_COMBINED_NAMED; + } + + @Override + public String getSourceSetForEnv(String env) { + return env.equals("client") ? CLIENT_ONLY_SOURCE_SET_NAME : SourceSet.MAIN_SOURCE_SET_NAME; + } + + @Override + protected List getAllSourceSetNames() { + return List.of(MINECRAFT_COMMON_NAMED, MINECRAFT_CLIENT_ONLY_NAMED, MINECRAFT_COMBINED_NAMED); + } + + // Called during evaluation, when the loom extension method is called. + private void evaluate(Project project) { + createSourceSets(project); + + // Combined extends from the 2 environments. + extendsFrom(MINECRAFT_COMBINED_NAMED, MINECRAFT_COMMON_NAMED, project); + extendsFrom(MINECRAFT_COMBINED_NAMED, MINECRAFT_CLIENT_ONLY_NAMED, project); + + final JavaPluginExtension javaExtension = project.getExtensions().getByType(JavaPluginExtension.class); + final LoomGradleExtension loomExtension = LoomGradleExtension.get(project); + + // Register our new client only source set, main becomes common only, with their respective jars. + SourceSet mainSourceSet = javaExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME); + SourceSet clientOnlySourceSet = javaExtension.getSourceSets().create(CLIENT_ONLY_SOURCE_SET_NAME); + + extendsFrom(List.of( + mainSourceSet.getCompileClasspathConfigurationName(), + mainSourceSet.getRuntimeClasspathConfigurationName() + ), MINECRAFT_COMMON_NAMED, project + ); + + extendsFrom(List.of( + clientOnlySourceSet.getCompileClasspathConfigurationName(), + clientOnlySourceSet.getRuntimeClasspathConfigurationName() + ), MINECRAFT_CLIENT_ONLY_NAMED, project + ); + + // Client depends on common. + extendsFrom(MINECRAFT_CLIENT_ONLY_NAMED, MINECRAFT_COMMON_NAMED, project); + clientOnlySourceSet.setCompileClasspath( + clientOnlySourceSet.getCompileClasspath() + .plus(mainSourceSet.getCompileClasspath()) + .plus(mainSourceSet.getOutput()) + ); + clientOnlySourceSet.setRuntimeClasspath( + clientOnlySourceSet.getRuntimeClasspath() + .plus(mainSourceSet.getRuntimeClasspath()) + .plus(mainSourceSet.getOutput()) + ); + + loomExtension.mixin(mixinExtension -> { + // Generate a refmap for mixins in the new source set. + mixinExtension.add(clientOnlySourceSet, "client-" + mixinExtension.getDefaultRefmapName().get(), (p) -> { }); + }); + + // Include the client only output in the jars + project.getTasks().named(mainSourceSet.getJarTaskName(), Jar.class).configure(jar -> { + jar.from(clientOnlySourceSet.getOutput().getClassesDirs()); + jar.from(clientOnlySourceSet.getOutput().getResourcesDir()); + }); + + if (project.getTasks().findByName(mainSourceSet.getSourcesJarTaskName()) == null) { + // No sources. + return; + } + + project.getTasks().named(mainSourceSet.getSourcesJarTaskName(), Jar.class).configure(jar -> { + jar.from(clientOnlySourceSet.getAllSource()); + }); + } + + @Override + public void afterEvaluate(Project project) { + } + + public static SourceSet getClientSourceSet(Project project) { + Preconditions.checkArgument(LoomGradleExtension.get(project).areEnvironmentSourceSetsSplit()); + + final JavaPluginExtension javaExtension = project.getExtensions().getByType(JavaPluginExtension.class); + return javaExtension.getSourceSets().getByName(CLIENT_ONLY_SOURCE_SET_NAME); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SplitMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SplitMinecraftProvider.java index 319b33e2..b63f4796 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SplitMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SplitMinecraftProvider.java @@ -77,7 +77,8 @@ public final class SplitMinecraftProvider extends MinecraftProvider { try (MinecraftJarSplitter jarSplitter = new MinecraftJarSplitter(clientJar, serverJar)) { // Required for loader to compute the version info also useful to have in both jars. jarSplitter.sharedEntry("version.json"); - jarSplitter.forcedClientEntry("assets/.mcassetsroot"); + jarSplitter.sharedEntry("assets/.mcassetsroot"); + jarSplitter.sharedEntry("assets/minecraft/lang/en_us.json"); jarSplitter.split(minecraftClientOnlyJar, minecraftCommonJar); } catch (Exception e) { 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 68239f4d..4a1ea140 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 @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -27,9 +27,9 @@ package net.fabricmc.loom.configuration.providers.minecraft.mapped; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.function.BiConsumer; import org.gradle.api.Project; @@ -38,6 +38,7 @@ import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.configuration.providers.minecraft.SignatureFixerApplyVisitor; import net.fabricmc.loom.util.TinyRemapperHelper; import net.fabricmc.tinyremapper.OutputConsumerPath; @@ -58,8 +59,8 @@ public abstract class AbstractMappedMinecraftProvider getRemappedJars(); - protected void applyDependencies(BiConsumer consumer) { - // Override if needed + public List getDependencyTargets() { + return Collections.emptyList(); } public void provide(boolean applyDependencies) throws Exception { @@ -77,7 +78,16 @@ public abstract class AbstractMappedMinecraftProvider getProject().getDependencies().add(configuration, getDependencyNotation(name))); + final List dependencyTargets = getDependencyTargets(); + + if (dependencyTargets.isEmpty()) { + return; + } + + MinecraftSourceSets.get(getProject()).applyDependencies( + (configuration, name) -> getProject().getDependencies().add(configuration, getDependencyNotation(name)), + dependencyTargets + ); } } 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 ec838049..9c9cdff7 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 @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -26,7 +26,6 @@ package net.fabricmc.loom.configuration.providers.minecraft.mapped; import java.nio.file.Path; import java.util.List; -import java.util.function.BiConsumer; import org.gradle.api.Project; @@ -35,7 +34,6 @@ import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvid import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider; -import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.SidedClassVisitor; import net.fabricmc.tinyremapper.TinyRemapper; @@ -67,8 +65,8 @@ public abstract class NamedMinecraftProvider extend } @Override - protected void applyDependencies(BiConsumer consumer) { - consumer.accept(Constants.Configurations.MINECRAFT_NAMED, MERGED); + public List getDependencyTargets() { + return List.of(MERGED); } } @@ -93,9 +91,8 @@ public abstract class NamedMinecraftProvider extend } @Override - protected void applyDependencies(BiConsumer consumer) { - consumer.accept(Constants.Configurations.MINECRAFT_NAMED, COMMON); - consumer.accept(Constants.Configurations.MINECRAFT_NAMED, CLIENT_ONLY); + public List getDependencyTargets() { + return List.of(CLIENT_ONLY, COMMON); } } @@ -123,8 +120,8 @@ public abstract class NamedMinecraftProvider extend } @Override - protected void applyDependencies(BiConsumer consumer) { - consumer.accept(Constants.Configurations.MINECRAFT_NAMED, envName()); + public List getDependencyTargets() { + return List.of(envName()); } @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 90414803..542fdfa6 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 @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -36,6 +36,7 @@ import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.configuration.processors.JarProcessorManager; import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider; @@ -85,7 +86,16 @@ public abstract class ProcessedNamedMinecraftProvider getProject().getDependencies().add(configuration, getDependencyNotation(name))); + final List dependencyTargets = parentMinecraftProvider.getDependencyTargets(); + + if (dependencyTargets.isEmpty()) { + return; + } + + MinecraftSourceSets.get(getProject()).applyDependencies( + (configuration, name) -> getProject().getDependencies().add(configuration, getDependencyNotation(name)), + dependencyTargets + ); } } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index d6594e01..aa6365ec 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -50,6 +50,7 @@ import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec; import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuilderImpl; import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsDependency; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.util.DeprecationHelper; /** @@ -69,6 +70,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA protected final Property intermediateMappingsProvider; private final Property runtimeOnlyLog4j; private final Property minecraftJarConfiguration; + private final Property splitEnvironmentalSourceSet; private final InterfaceInjectionExtensionAPI interfaceInjectionExtension; private final ModVersionParser versionParser; @@ -116,6 +118,9 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA this.interfaceInjectionExtension = project.getObjects().newInstance(InterfaceInjectionExtensionAPI.class); + this.splitEnvironmentalSourceSet = project.getObjects().property(Boolean.class).convention(false); + this.splitEnvironmentalSourceSet.finalizeValueOnRead(); + // Add main source set by default interfaceInjection(interfaceInjection -> { final JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class); @@ -254,6 +259,24 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA return runtimeOnlyLog4j; } + @Override + public void splitEnvironmentSourceSets() { + splitMinecraftJar(); + + splitEnvironmentalSourceSet.set(true); + + // We need to lock these values, as we setup the new source sets right away. + splitEnvironmentalSourceSet.finalizeValue(); + minecraftJarConfiguration.finalizeValue(); + + MinecraftSourceSets.get(getProject()).evaluateSplit(getProject()); + } + + @Override + public boolean areEnvironmentSourceSetsSplit() { + return splitEnvironmentalSourceSet.get(); + } + @Override public InterfaceInjectionExtensionAPI getInterfaceInjection() { return interfaceInjectionExtension; diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index 82a11568..78163b24 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2016-2021 FabricMC + * Copyright (c) 2016-2022 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 @@ -34,6 +34,7 @@ import org.gradle.api.tasks.TaskProvider; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.ide.RunConfigSettings; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.task.launch.GenerateDLIConfigTask; import net.fabricmc.loom.task.launch.GenerateLog4jConfigTask; import net.fabricmc.loom.task.launch.GenerateRemapClasspathTask; @@ -155,6 +156,17 @@ public final class LoomTasks { extension.getRunConfigs().removeIf(settings -> settings.getName().equals(taskName)); }); + + // Configure the run config source sets. + project.afterEvaluate(p -> { + if (!extension.areEnvironmentSourceSetsSplit()) { + return; + } + + extension.getRunConfigs().configureEach(settings -> + settings.source(MinecraftSourceSets.get(project).getSourceSetForEnv(settings.getEnvironment())) + ); + }); } public static Provider getIDELaunchConfigureTaskName(Project project) { diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index 56ec3d17..83af5533 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -26,16 +26,21 @@ package net.fabricmc.loom.task; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.Serializable; import java.nio.file.Files; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; +import java.util.function.Function; import java.util.function.Supplier; import java.util.jar.Manifest; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.inject.Inject; @@ -47,6 +52,7 @@ import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.FileCollection; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFiles; @@ -64,10 +70,13 @@ import net.fabricmc.loom.build.MixinRefmapHelper; import net.fabricmc.loom.build.nesting.IncludedJarFactory; import net.fabricmc.loom.build.nesting.JarNester; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.extension.MixinExtension; import net.fabricmc.loom.task.service.JarManifestService; import net.fabricmc.loom.task.service.TinyRemapperService; import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.Pair; +import net.fabricmc.loom.util.SidedClassVisitor; import net.fabricmc.loom.util.ZipUtils; import net.fabricmc.loom.util.service.UnsafeWorkQueueHelper; import net.fabricmc.tinyremapper.OutputConsumerPath; @@ -133,6 +142,16 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { if (legacyMixin) { setupLegacyMixinRefmapRemapping(params); } + + if (extension.areEnvironmentSourceSetsSplit()) { + final List clientOnlyJarEntries = getClientOnlyJarEntries(); + params.getManifestAttributes().set(Map.of( + "Fabric-Loom-Split-Environment", "true", + "Fabric-Loom-Client-Only-Entries", String.join(";", clientOnlyJarEntries) + )); + + params.getClientOnlyClasses().set(clientOnlyJarEntries.stream().filter(s -> s.endsWith(".class")).toList()); + } }); } @@ -154,34 +173,14 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { MixinExtension.getMixinInformationContainer(sourceSet) ); - String[] rootPaths = sourceSet.getResources().getSrcDirs().stream() - .map(root -> { - String rootPath = root.getAbsolutePath().replace("\\", "/"); - - if (rootPath.charAt(rootPath.length() - 1) != '/') { - rootPath += '/'; - } - - return rootPath; - }) - .toArray(String[]::new); + final List rootPaths = getRootPaths(sourceSet.getResources().getSrcDirs()); final String refmapName = container.refmapNameProvider().get(); final List mixinConfigs = container.sourceSet().getResources() .matching(container.mixinConfigPattern()) .getFiles() .stream() - .map(file -> { - String s = file.getAbsolutePath().replace("\\", "/"); - - for (String rootPath : rootPaths) { - if (s.startsWith(rootPath)) { - s = s.substring(rootPath.length()); - } - } - - return s; - }) + .map(relativePath(rootPaths)) .filter(allMixinConfigs::contains) .toList(); @@ -200,6 +199,9 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { Property getJarManifestService(); Property getTinyRemapperBuildServiceUuid(); + + MapProperty getManifestAttributes(); + ListProperty getClientOnlyClasses(); } public abstract static class RemapAction extends AbstractRemapAction { @@ -220,6 +222,11 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { tinyRemapper = tinyRemapperService.getTinyRemapperForRemapping(); remap(); + + if (getParameters().getClientOnlyClasses().isPresent()) { + markClientOnlyClasses(); + } + remapAccessWidener(); addRefmaps(); addNestedJars(); @@ -245,6 +252,15 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { } } + private void markClientOnlyClasses() throws IOException { + final Stream>> tranformers = getParameters().getClientOnlyClasses().get().stream() + .map(s -> new Pair<>(s, + (ZipUtils.AsmClassOperator) classVisitor -> SidedClassVisitor.CLIENT.insertApplyVisitor(null, classVisitor) + )); + + ZipUtils.transform(outputFile, tranformers); + } + private void remapAccessWidener() throws IOException { final AccessWidenerFile accessWidenerFile = AccessWidenerFile.fromModJar(inputFile); @@ -289,7 +305,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { int count = ZipUtils.transform(outputFile, Map.of(MANIFEST_PATH, bytes -> { var manifest = new Manifest(new ByteArrayInputStream(bytes)); - getParameters().getJarManifestService().get().apply(manifest); + getParameters().getJarManifestService().get().apply(manifest, getParameters().getManifestAttributes().get()); manifest.getMainAttributes().putValue("Fabric-Mapping-Namespace", getParameters().getTargetNamespace().get()); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -317,6 +333,50 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { } } + private List getClientOnlyJarEntries() { + final SourceSet clientSourceSet = MinecraftSourceSets.Split.getClientSourceSet(getProject()); + + final ConfigurableFileCollection output = getProject().getObjects().fileCollection(); + output.from(clientSourceSet.getOutput().getClassesDirs()); + output.from(clientSourceSet.getOutput().getResourcesDir()); + + final List rootPaths = new ArrayList<>(); + + rootPaths.addAll(getRootPaths(clientSourceSet.getOutput().getClassesDirs().getFiles())); + rootPaths.addAll(getRootPaths(Set.of(Objects.requireNonNull(clientSourceSet.getOutput().getResourcesDir())))); + + return output.getAsFileTree().getFiles().stream() + .map(relativePath(rootPaths)) + .toList(); + } + + private static List getRootPaths(Set files) { + return files.stream() + .map(root -> { + String rootPath = root.getAbsolutePath().replace("\\", "/"); + + if (rootPath.charAt(rootPath.length() - 1) != '/') { + rootPath += '/'; + } + + return rootPath; + }).toList(); + } + + private static Function relativePath(List rootPaths) { + return file -> { + String s = file.getAbsolutePath().replace("\\", "/"); + + for (String rootPath : rootPaths) { + if (s.startsWith(rootPath)) { + s = s.substring(rootPath.length()); + } + } + + return s; + }; + } + @Internal public TinyRemapperService getTinyRemapperService() { return tinyRemapperService.get(); 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 144f9fa9..38d2637f 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java @@ -40,6 +40,7 @@ import org.gradle.api.logging.configuration.ConsoleOutput; import org.gradle.api.tasks.TaskAction; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider; import net.fabricmc.loom.task.AbstractLoomTask; public abstract class GenerateDLIConfigTask extends AbstractLoomTask { @@ -68,6 +69,11 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { .argument("client", "--assetsDir") .argument("client", assetsDirectory.getAbsolutePath()); + if (getExtension().areEnvironmentSourceSetsSplit()) { + launchConfig.property("client", "fabric.gameJarPath.client", getGameJarPath("client")); + launchConfig.property("fabric.gameJarPath", getGameJarPath("common")); + } + final boolean plainConsole = getProject().getGradle().getStartParameter().getConsoleOutput() == ConsoleOutput.Plain; final boolean ansiSupportedIDE = new File(getProject().getRootDir(), ".vscode").exists() || new File(getProject().getRootDir(), ".idea").exists() @@ -87,6 +93,16 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { .collect(Collectors.joining(",")); } + private String getGameJarPath(String env) { + MappedMinecraftProvider.Split split = (MappedMinecraftProvider.Split) getExtension().getNamedMinecraftProvider(); + + return switch (env) { + case "client" -> split.getClientOnlyJar().toAbsolutePath().toString(); + case "common" -> split.getCommonJar().toAbsolutePath().toString(); + default -> throw new UnsupportedOperationException(); + }; + } + public static class LaunchConfig { private final Map> values = new HashMap<>(); diff --git a/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java b/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java index 4de6cba1..117207fa 100644 --- a/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java +++ b/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -25,6 +25,7 @@ package net.fabricmc.loom.task.service; import java.io.Serializable; +import java.util.Map; import java.util.Optional; import java.util.jar.Attributes; import java.util.jar.Manifest; @@ -70,7 +71,7 @@ public abstract class JarManifestService implements BuildService extraValues) { // Don't set when running the reproducible build tests as it will break them when anything updates if (Boolean.getBoolean("loom.test.reproducible")) { return; @@ -91,6 +92,10 @@ public abstract class JarManifestService implements BuildService entry : extraValues.entrySet()) { + attributes.putValue(entry.getKey(), entry.getValue()); + } } private static Optional getLoaderVersion(Project project) { diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 8cb8bc78..cd90b25d 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -70,7 +70,6 @@ public class Constants { public static final String MINECRAFT_DEPENDENCIES = "minecraftLibraries"; public static final String MINECRAFT_RUNTIME_DEPENDENCIES = "minecraftRuntimeOnlyLibraries"; public static final String MINECRAFT_NATIVES = "minecraftNatives"; - public static final String MINECRAFT_NAMED = "minecraftNamed"; public static final String MAPPINGS = "mappings"; public static final String MAPPINGS_FINAL = "mappingsFinal"; public static final String LOADER_DEPENDENCIES = "loaderLibraries"; diff --git a/src/main/java/net/fabricmc/loom/util/SidedClassVisitor.java b/src/main/java/net/fabricmc/loom/util/SidedClassVisitor.java index 51fe9c51..5d2f7c6a 100644 --- a/src/main/java/net/fabricmc/loom/util/SidedClassVisitor.java +++ b/src/main/java/net/fabricmc/loom/util/SidedClassVisitor.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -42,6 +42,7 @@ public final class SidedClassVisitor extends ClassVisitor { private static final String SIDE_DESCRIPTOR = "Lnet/fabricmc/api/EnvType;"; private final String side; + private boolean hasExisting = false; private SidedClassVisitor(String side, ClassVisitor next) { super(Constants.ASM_VERSION, next); @@ -49,11 +50,22 @@ public final class SidedClassVisitor extends ClassVisitor { } @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - super.visit(version, access, name, signature, superName, interfaces); + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + if (ENVIRONMENT_DESCRIPTOR.equals(descriptor)) { + hasExisting = true; + } - final AnnotationVisitor annotationVisitor = visitAnnotation(ENVIRONMENT_DESCRIPTOR, true); - annotationVisitor.visitEnum("value", SIDE_DESCRIPTOR, side.toUpperCase(Locale.ROOT)); - annotationVisitor.visitEnd(); + return super.visitAnnotation(descriptor, visible); + } + + @Override + public void visitEnd() { + if (!hasExisting) { + final AnnotationVisitor annotationVisitor = visitAnnotation(ENVIRONMENT_DESCRIPTOR, true); + annotationVisitor.visitEnum("value", SIDE_DESCRIPTOR, side.toUpperCase(Locale.ROOT)); + annotationVisitor.visitEnd(); + } + + super.visitEnd(); } } diff --git a/src/main/java/net/fabricmc/loom/util/ZipUtils.java b/src/main/java/net/fabricmc/loom/util/ZipUtils.java index c4a35a3a..6c4518fe 100644 --- a/src/main/java/net/fabricmc/loom/util/ZipUtils.java +++ b/src/main/java/net/fabricmc/loom/util/ZipUtils.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -44,6 +44,9 @@ import java.util.function.Function; import java.util.stream.Stream; import org.jetbrains.annotations.Nullable; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; import net.fabricmc.loom.LoomGradlePlugin; @@ -231,6 +234,20 @@ public class ZipUtils { T apply(T arg) throws IOException; } + public interface AsmClassOperator extends UnsafeUnaryOperator { + ClassVisitor visit(ClassVisitor classVisitor); + + @Override + default byte[] apply(byte[] arg) throws IOException { + final ClassReader reader = new ClassReader(arg); + final ClassWriter writer = new ClassWriter(0); + + reader.accept(visit(writer), 0); + + return writer.toByteArray(); + } + } + private static Map> collectTransformersStream(Stream>> transforms) { Map> map = new HashMap<>(); Iterator>> iterator = transforms.iterator(); diff --git a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index 6f852317..91bc751c 100644 --- a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy @@ -28,7 +28,7 @@ import org.gradle.util.GradleVersion class LoomTestConstants { public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion() - public final static String PRE_RELEASE_GRADLE = "7.5-20220129032445+0000" + public final static String PRE_RELEASE_GRADLE = "7.5-20220228014109+0000" public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/MCJarConfigTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/MCJarConfigTest.groovy index b349e25f..d58d0741 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/MCJarConfigTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/MCJarConfigTest.groovy @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -112,4 +112,31 @@ class MCJarConfigTest extends Specification implements GradleProjectTestTrait { where: version << STANDARD_TEST_VERSIONS } + + @Unroll + def "split env (gradle #version)"() { + setup: + def gradle = gradleProject(project: "minimalBase", version: version) + + gradle.buildGradle << ''' + loom { + splitEnvironmentSourceSets() + } + + dependencies { + minecraft "com.mojang:minecraft:1.18.1" + mappings "net.fabricmc:yarn:1.18.1+build.18:v2" + modImplementation "net.fabricmc:fabric-loader:0.12.12" + } + ''' + + when: + def result = gradle.run(task: "build") + + then: + result.task(":build").outcome == SUCCESS + + where: + version << STANDARD_TEST_VERSIONS + } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/SplitProjectTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/SplitProjectTest.groovy new file mode 100644 index 00000000..b7440833 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/SplitProjectTest.groovy @@ -0,0 +1,49 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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 net.fabricmc.loom.test.util.GradleProjectTestTrait +import spock.lang.Specification +import spock.lang.Unroll + +import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class SplitProjectTest extends Specification implements GradleProjectTestTrait { + @Unroll + def "build (gradle #version)"() { + setup: + def gradle = gradleProject(project: "splitSources", version: version) + + when: + def result = gradle.run(task: "build") + + then: + result.task(":build").outcome == SUCCESS + + where: + version << STANDARD_TEST_VERSIONS + } +} diff --git a/src/test/resources/projects/splitSources/build.gradle b/src/test/resources/projects/splitSources/build.gradle new file mode 100644 index 00000000..ea40b502 --- /dev/null +++ b/src/test/resources/projects/splitSources/build.gradle @@ -0,0 +1,27 @@ +plugins { + id 'fabric-loom' version '0.12.local' + id 'maven-publish' +} + +sourceCompatibility = JavaVersion.VERSION_17 +targetCompatibility = JavaVersion.VERSION_17 + +loom { + splitEnvironmentSourceSets() +} + +dependencies { + minecraft "com.mojang:minecraft:1.18.2" + mappings "net.fabricmc:yarn:1.18.2+build.1:v2" + modImplementation "net.fabricmc:fabric-loader:0.13.3" + + modImplementation "net.fabricmc.fabric-api:fabric-api:0.47.8+1.18.2" +} + +tasks.withType(JavaCompile).configureEach { + it.options.release = 17 +} + +java { + withSourcesJar() +} \ No newline at end of file diff --git a/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/ExampleModClient.java b/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/ExampleModClient.java new file mode 100644 index 00000000..9047ed62 --- /dev/null +++ b/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/ExampleModClient.java @@ -0,0 +1,19 @@ +package net.fabricmc.example.client; + +import net.fabricmc.api.ClientModInitializer; + +import net.minecraft.client.MinecraftClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ExampleModClient implements ClientModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger(ExampleModClient.class); + + @Override + public void onInitializeClient() { + LOGGER.info("Hello Client"); + + // Check we can compile against the client. + MinecraftClient client; + } +} diff --git a/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/TestClientClass.java b/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/TestClientClass.java new file mode 100644 index 00000000..46dbdecc --- /dev/null +++ b/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/TestClientClass.java @@ -0,0 +1,8 @@ +package net.fabricmc.example.client; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +@Environment(EnvType.CLIENT) +public class TestClientClass { +} diff --git a/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/mixin/ExampleMixin.java b/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/mixin/ExampleMixin.java new file mode 100644 index 00000000..afd4a3bc --- /dev/null +++ b/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/mixin/ExampleMixin.java @@ -0,0 +1,16 @@ +package net.fabricmc.example.client.mixin; + +import net.fabricmc.example.ExampleMod; +import net.minecraft.client.gui.screen.TitleScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TitleScreen.class) +public class ExampleMixin { + @Inject(at = @At("HEAD"), method = "init()V") + private void init(CallbackInfo info) { + ExampleMod.LOGGER.info("This line is printed by an example mod mixin!"); + } +} diff --git a/src/test/resources/projects/splitSources/src/client/resources/modid.client.mixins.json b/src/test/resources/projects/splitSources/src/client/resources/modid.client.mixins.json new file mode 100644 index 00000000..7c42cb48 --- /dev/null +++ b/src/test/resources/projects/splitSources/src/client/resources/modid.client.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "net.fabricmc.example.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + ], + "client": [ + "ExampleMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/src/test/resources/projects/splitSources/src/main/java/net/fabricmc/example/ExampleMod.java b/src/test/resources/projects/splitSources/src/main/java/net/fabricmc/example/ExampleMod.java new file mode 100644 index 00000000..72df3bf7 --- /dev/null +++ b/src/test/resources/projects/splitSources/src/main/java/net/fabricmc/example/ExampleMod.java @@ -0,0 +1,19 @@ +package net.fabricmc.example; + +import net.fabricmc.api.ModInitializer; + +import net.minecraft.block.Block; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ExampleMod implements ModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger("modid"); + + @Override + public void onInitialize() { + LOGGER.info("Hello Fabric world!"); + + // Check we can compile against common code. + Block block; + } +} diff --git a/src/test/resources/projects/splitSources/src/main/resources/assets/modid/icon.png b/src/test/resources/projects/splitSources/src/main/resources/assets/modid/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..047b91f2347de5cf95f23284476fddbe21ba23fe GIT binary patch literal 453 zcmV;$0XqJPP)QAFYGys`80vegN0XDFh0OXKz&i8?Le#x7{1X)R+00000NkvXXu0mjf73i~T literal 0 HcmV?d00001 diff --git a/src/test/resources/projects/splitSources/src/main/resources/fabric.mod.json b/src/test/resources/projects/splitSources/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..f68569fa --- /dev/null +++ b/src/test/resources/projects/splitSources/src/main/resources/fabric.mod.json @@ -0,0 +1,21 @@ +{ + "schemaVersion": 1, + "id": "modid", + "version": "${version}", + + "name": "Example Mod", + "description": "This is an example description! Tell everyone what your mod is about!", + "icon": "assets/modid/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "net.fabricmc.example.ExampleMod" + ], + "client": [ + "net.fabricmc.example.client.ExampleModClient" + ] + }, + "mixins": [ + "modid.client.mixins.json" + ] +} From 0a8b7925648edd56f3aa5136254e7771e27b5de4 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Mon, 14 Mar 2022 01:07:53 +0000 Subject: [PATCH 07/27] Add DSL to configure mod class path groups. (#608) * Add basic mod settings. * Cleanup and review feedback. * Add idea output dir support. * Eclipse and vscode + better tests. --- .../loom/api/LoomGradleExtensionAPI.java | 10 ++ .../net/fabricmc/loom/api/ModSettings.java | 55 ++++++ .../loom/configuration/ide/RunConfig.java | 13 +- .../configuration/ide/idea/IdeaUtils.java | 13 ++ .../extension/LoomGradleExtensionApiImpl.java | 13 ++ .../task/launch/GenerateDLIConfigTask.java | 18 ++ .../loom/util/gradle/SourceSetHelper.java | 167 ++++++++++++++++++ .../loom/test/unit/SourceSetHelperTest.groovy | 115 ++++++++++++ 8 files changed, 393 insertions(+), 11 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/api/ModSettings.java create mode 100644 src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java create mode 100644 src/test/groovy/net/fabricmc/loom/test/unit/SourceSetHelperTest.groovy diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index 485c4e70..69c44a1a 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -84,6 +84,16 @@ public interface LoomGradleExtensionAPI { void mixin(Action action); + /** + * Optionally register and configure a {@link ModSettings} object. The name should match the modid. + * This is generally only required when the mod spans across multiple classpath directories, such as when using split sourcesets. + */ + @ApiStatus.Experimental + void mods(Action> action); + + @ApiStatus.Experimental + NamedDomainObjectContainer getMods(); + @ApiStatus.Experimental // TODO: move this from LoomGradleExtensionAPI to LoomGradleExtension once getRefmapName & setRefmapName is removed. MixinExtensionAPI getMixin(); diff --git a/src/main/java/net/fabricmc/loom/api/ModSettings.java b/src/main/java/net/fabricmc/loom/api/ModSettings.java new file mode 100644 index 00000000..471f7d65 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/api/ModSettings.java @@ -0,0 +1,55 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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.api; + +import javax.inject.Inject; + +import org.gradle.api.Named; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.tasks.SourceSet; +import org.jetbrains.annotations.ApiStatus; + +/** + * A {@link Named} object for setting mod-related values. The {@linkplain Named#getName() name} should match the mod id. + */ +@ApiStatus.Experimental +public abstract class ModSettings implements Named { + /** + * List of classpath directories, used to populate the `fabric.classPathGroups` Fabric Loader system property. + */ + public abstract ListProperty getModSourceSets(); + + @Inject + public ModSettings() { + getModSourceSets().finalizeValueOnRead(); + } + + /** + * Mark a {@link SourceSet} output directories part of the named mod. + */ + public void sourceSet(SourceSet sourceSet) { + getModSourceSets().add(sourceSet); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java index 7979b1b2..cd834b1e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java @@ -50,6 +50,7 @@ import org.w3c.dom.Node; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.InstallerData; import net.fabricmc.loom.configuration.ide.idea.IdeaSyncTask; +import net.fabricmc.loom.configuration.ide.idea.IdeaUtils; import net.fabricmc.loom.configuration.providers.BundleMetadata; import net.fabricmc.loom.util.Constants; @@ -101,16 +102,6 @@ public class RunConfig { return e; } - private static String getIdeaModuleName(Project project, SourceSet srcs) { - String module = project.getName() + "." + srcs.getName(); - - while ((project = project.getParent()) != null) { - module = project.getName() + "." + module; - } - - return module; - } - private static void populate(Project project, LoomGradleExtension extension, RunConfig runConfig, String environment) { runConfig.configName += extension.isRootProject() ? "" : " (" + project.getPath() + ")"; runConfig.eclipseProjectName = project.getExtensions().getByType(EclipseModel.class).getProject().getName(); @@ -166,7 +157,7 @@ public class RunConfig { RunConfig runConfig = new RunConfig(); runConfig.configName = configName; populate(project, extension, runConfig, environment); - runConfig.ideaModuleName = getIdeaModuleName(project, sourceSet); + runConfig.ideaModuleName = IdeaUtils.getIdeaModuleName(project, sourceSet); runConfig.runDirIdeaUrl = "file://$PROJECT_DIR$/" + runDir; runConfig.runDir = runDir; runConfig.sourceSet = sourceSet; 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 e1aa3ea4..fd1e26ab 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 @@ -26,6 +26,9 @@ package net.fabricmc.loom.configuration.ide.idea; import java.util.Objects; +import org.gradle.api.Project; +import org.gradle.api.tasks.SourceSet; + public class IdeaUtils { public static boolean isIdeaSync() { return Boolean.parseBoolean(System.getProperty("idea.sync.active", "false")); @@ -42,4 +45,14 @@ public class IdeaUtils { final int minor = Integer.parseInt(split[1]); return major > 2021 || (major == 2021 && minor >= 3); } + + public static String getIdeaModuleName(Project project, SourceSet srcs) { + String module = project.getName() + "." + srcs.getName(); + + while ((project = project.getParent()) != null) { + module = project.getName() + "." + module; + } + + return module; + } } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index aa6365ec..490fa4d1 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -39,6 +39,7 @@ import org.gradle.api.tasks.SourceSet; import net.fabricmc.loom.api.InterfaceInjectionExtensionAPI; import net.fabricmc.loom.api.LoomGradleExtensionAPI; import net.fabricmc.loom.api.MixinExtensionAPI; +import net.fabricmc.loom.api.ModSettings; import net.fabricmc.loom.api.decompilers.DecompilerOptions; import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; import net.fabricmc.loom.api.mappings.layered.spec.LayeredMappingSpecBuilder; @@ -77,6 +78,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA private final NamedDomainObjectContainer runConfigs; private final NamedDomainObjectContainer decompilers; + private final NamedDomainObjectContainer mods; protected LoomGradleExtensionApiImpl(Project project, LoomFiles directories) { this.jarProcessors = project.getObjects().listProperty(JarProcessor.class) @@ -106,6 +108,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA this.runConfigs = project.container(RunConfigSettings.class, baseName -> new RunConfigSettings(project, baseName)); this.decompilers = project.getObjects().domainObjectContainer(DecompilerOptions.class); + this.mods = project.getObjects().domainObjectContainer(ModSettings.class); this.minecraftJarConfiguration = project.getObjects().property(MinecraftJarConfiguration.class).convention(MinecraftJarConfiguration.MERGED); this.minecraftJarConfiguration.finalizeValueOnRead(); @@ -282,6 +285,16 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA return interfaceInjectionExtension; } + @Override + public void mods(Action> action) { + action.execute(getMods()); + } + + @Override + public NamedDomainObjectContainer getMods() { + return mods; + } + // This is here to ensure that LoomGradleExtensionApiImpl compiles without any unimplemented methods private final class EnsureCompile extends LoomGradleExtensionApiImpl { private EnsureCompile() { 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 38d2637f..3e078131 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java @@ -42,6 +42,7 @@ import org.gradle.api.tasks.TaskAction; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider; import net.fabricmc.loom.task.AbstractLoomTask; +import net.fabricmc.loom.util.gradle.SourceSetHelper; public abstract class GenerateDLIConfigTask extends AbstractLoomTask { @TaskAction @@ -74,6 +75,10 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { launchConfig.property("fabric.gameJarPath", getGameJarPath("common")); } + if (!getExtension().getMods().isEmpty()) { + launchConfig.property("fabric.classPathGroups", getClassPathGroups()); + } + final boolean plainConsole = getProject().getGradle().getStartParameter().getConsoleOutput() == ConsoleOutput.Plain; final boolean ansiSupportedIDE = new File(getProject().getRootDir(), ".vscode").exists() || new File(getProject().getRootDir(), ".idea").exists() @@ -103,6 +108,19 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { }; } + /** + * See: https://github.com/FabricMC/fabric-loader/pull/585. + */ + private String getClassPathGroups() { + return getExtension().getMods().stream() + .map(modSettings -> + SourceSetHelper.getClasspath(modSettings, getProject()).stream() + .map(File::getAbsolutePath) + .collect(Collectors.joining(File.pathSeparator)) + ) + .collect(Collectors.joining(File.pathSeparator+File.pathSeparator)); + } + public static class LaunchConfig { private final Map> values = new HashMap<>(); diff --git a/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java b/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java new file mode 100644 index 00000000..4630bde6 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java @@ -0,0 +1,167 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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 java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.gradle.api.Project; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetOutput; +import org.intellij.lang.annotations.Language; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.VisibleForTesting; +import org.xml.sax.InputSource; + +import net.fabricmc.loom.api.ModSettings; +import net.fabricmc.loom.configuration.ide.idea.IdeaUtils; + +public final class SourceSetHelper { + @VisibleForTesting + @Language("xpath") + public static final String IDEA_OUTPUT_XPATH = "/project/component[@name='ProjectRootManager']/output/@url"; + + private SourceSetHelper() { + } + + public static List getClasspath(ModSettings modSettings, Project project) { + return modSettings.getModSourceSets().get().stream() + .flatMap(sourceSet -> getClasspath(sourceSet, project).stream()) + .toList(); + } + + public static List getClasspath(SourceSet sourceSet, Project project) { + final List classpath = getGradleClasspath(sourceSet); + + classpath.addAll(getIdeaClasspath(sourceSet, project)); + classpath.addAll(getEclipseClasspath(sourceSet, project)); + classpath.addAll(getVscodeClasspath(sourceSet, project)); + + return classpath; + } + + private static List getGradleClasspath(SourceSet sourceSet) { + final SourceSetOutput output = sourceSet.getOutput(); + final File resources = output.getResourcesDir(); + + final List classpath = new ArrayList<>(); + + classpath.addAll(output.getClassesDirs().getFiles()); + + if (resources != null) { + classpath.add(resources); + } + + return classpath; + } + + @VisibleForTesting + public static List getIdeaClasspath(SourceSet sourceSet, Project project) { + final File projectDir = project.getRootDir(); + final File dotIdea = new File(projectDir, ".idea"); + + if (!dotIdea.exists()) { + return Collections.emptyList(); + } + + final File miscXml = new File(dotIdea, "misc.xml"); + + if (!miscXml.exists()) { + return Collections.emptyList(); + } + + String outputDirUrl = evaluateXpath(miscXml, IDEA_OUTPUT_XPATH); + + if (outputDirUrl == null) { + return Collections.emptyList(); + } + + outputDirUrl = outputDirUrl.replace("$PROJECT_DIR$", projectDir.getAbsolutePath()); + outputDirUrl = outputDirUrl.replaceAll("^file:", ""); + + final File productionDir = new File(outputDirUrl, "production"); + final File outputDir = new File(productionDir, IdeaUtils.getIdeaModuleName(project, sourceSet)); + + return Collections.singletonList(outputDir); + } + + @Nullable + private static String evaluateXpath(File file, @Language("xpath") String expression) { + final XPath xpath = XPathFactory.newInstance().newXPath(); + + try (FileInputStream fis = new FileInputStream(file)) { + return xpath.evaluate(expression, new InputSource(fis)); + } catch (XPathExpressionException e) { + return null; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @VisibleForTesting + public static List getEclipseClasspath(SourceSet sourceSet, Project project) { + // Somewhat of a guess, I'm unsure if this is correct for multi-project builds + final File projectDir = project.getProjectDir(); + final File classpath = new File(projectDir, ".classpath"); + + if (!classpath.exists()) { + return Collections.emptyList(); + } + + return getBinDirClasspath(projectDir, sourceSet); + } + + @VisibleForTesting + public static List getVscodeClasspath(SourceSet sourceSet, Project project) { + // Somewhat of a guess, I'm unsure if this is correct for multi-project builds + final File projectDir = project.getProjectDir(); + final File dotVscode = new File(projectDir, ".vscode"); + + if (!dotVscode.exists()) { + return Collections.emptyList(); + } + + return getBinDirClasspath(projectDir, sourceSet); + } + + private static List getBinDirClasspath(File projectDir, SourceSet sourceSet) { + final File binDir = new File(projectDir, "bin"); + + if (!binDir.exists()) { + return Collections.emptyList(); + } + + return Collections.singletonList(new File(binDir, sourceSet.getName())); + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/SourceSetHelperTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/SourceSetHelperTest.groovy new file mode 100644 index 00000000..22499db5 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/SourceSetHelperTest.groovy @@ -0,0 +1,115 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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 + +import net.fabricmc.loom.util.gradle.SourceSetHelper +import org.gradle.api.Project +import org.gradle.api.tasks.SourceSet +import org.intellij.lang.annotations.Language +import spock.lang.Shared +import spock.lang.Specification + +class SourceSetHelperTest extends Specification { + @Shared + private static File projectDir = File.createTempDir() + + def "idea classpath"() { + given: + def miscXml = new File(projectDir, ".idea/misc.xml") + miscXml.parentFile.mkdirs() + miscXml.text = MISC_XML + + def mockProject = Mock(Project) + def mockSourceSet = Mock(SourceSet) + + mockProject.getName() >> "UnitTest" + mockProject.getRootDir() >> projectDir + mockSourceSet.getName() >> "main" + + when: + def result = SourceSetHelper.getIdeaClasspath(mockSourceSet, mockProject) + + then: + result.size() == 1 + !result[0].toString().startsWith("file:") + + println(result[0].toString()) + } + + def "eclipse classpath"() { + given: + def classpath = new File(projectDir, ".classpath") + classpath.createNewFile() + + def binDir = new File(projectDir, "bin") + binDir.mkdirs() + + def mockProject = Mock(Project) + def mockSourceSet = Mock(SourceSet) + + mockProject.getName() >> "UnitTest" + mockProject.getProjectDir() >> projectDir + mockSourceSet.getName() >> "main" + + when: + def result = SourceSetHelper.getEclipseClasspath(mockSourceSet, mockProject) + + then: + result.size() == 1 + println(result[0].toString()) + } + + def "vscode classpath"() { + given: + def dotVscode = new File(projectDir, ".vscode") + dotVscode.mkdirs() + + def binDir = new File(projectDir, "bin") + binDir.mkdirs() + + def mockProject = Mock(Project) + def mockSourceSet = Mock(SourceSet) + + mockProject.getName() >> "UnitTest" + mockProject.getProjectDir() >> projectDir + mockSourceSet.getName() >> "main" + + when: + def result = SourceSetHelper.getVscodeClasspath(mockSourceSet, mockProject) + + then: + result.size() == 1 + println(result[0].toString()) + } + + @Language("xml") + private static String MISC_XML = """ + + + + + +""" +} From f59e7b4a3eeb7628622e36fc12effa008f6c4727 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Mon, 14 Mar 2022 01:21:03 +0000 Subject: [PATCH 08/27] Handle remapping of KotlinClassMetadata.FileFacade & KotlinClassMetadata.MultiFileClassPart (#603) --- ...ClassMetadataRemappingAnnotationVisitor.kt | 15 ++++-- .../kotlin/remapping/RemappingKmVisitors.kt | 46 ++++++++++++++++++ ...sMetadataRemappingAnnotationVisitorTest.kt | 19 ++++++++ .../resources/classes/TestExtensionKt.class | Bin 0 -> 843 bytes .../mappings/TestExtensionKt.mappings | 2 + .../fabricmc/language/kotlin/TestExtension.kt | 14 ++++++ 6 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 src/test/resources/classes/TestExtensionKt.class create mode 100644 src/test/resources/mappings/TestExtensionKt.mappings create mode 100644 src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestExtension.kt diff --git a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt index dcbc6afa..3adc3522 100644 --- a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt +++ b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt @@ -61,9 +61,18 @@ class KotlinClassMetadataRemappingAnnotationVisitor(private val remapper: Remapp accept(next) } } - // Can only be turned into KmPackage which is useless data - is KotlinClassMetadata.FileFacade, is KotlinClassMetadata.MultiFileClassPart, - // Can't be turned into data + is KotlinClassMetadata.FileFacade -> { + val kpackage = metadata.toKmPackage() + val writer = KotlinClassMetadata.FileFacade.Writer() + kpackage.accept(RemappingKmVisitors(remapper).RemappingKmPackageVisitor(writer)) + writeClassHeader(writer.write().header) + } + is KotlinClassMetadata.MultiFileClassPart -> { + val kpackage = metadata.toKmPackage() + val writer = KotlinClassMetadata.MultiFileClassPart.Writer() + kpackage.accept(RemappingKmVisitors(remapper).RemappingKmPackageVisitor(writer)) + writeClassHeader(writer.write(metadata.facadeClassName, metadata.header.metadataVersion, metadata.header.extraInt).header) + } is KotlinClassMetadata.MultiFileClassFacade, is KotlinClassMetadata.Unknown, null -> { // do nothing accept(next) diff --git a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/RemappingKmVisitors.kt b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/RemappingKmVisitors.kt index 3acbd24a..7cb9ed04 100644 --- a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/RemappingKmVisitors.kt +++ b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/RemappingKmVisitors.kt @@ -40,6 +40,8 @@ import kotlinx.metadata.KmExtensionType import kotlinx.metadata.KmFunctionExtensionVisitor import kotlinx.metadata.KmFunctionVisitor import kotlinx.metadata.KmLambdaVisitor +import kotlinx.metadata.KmPackageExtensionVisitor +import kotlinx.metadata.KmPackageVisitor import kotlinx.metadata.KmPropertyExtensionVisitor import kotlinx.metadata.KmPropertyVisitor import kotlinx.metadata.KmTypeAliasVisitor @@ -54,6 +56,7 @@ import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor import kotlinx.metadata.jvm.JvmFieldSignature import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor import kotlinx.metadata.jvm.JvmMethodSignature +import kotlinx.metadata.jvm.JvmPackageExtensionVisitor import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor import kotlinx.metadata.jvm.JvmTypeExtensionVisitor import kotlinx.metadata.jvm.JvmTypeParameterExtensionVisitor @@ -189,6 +192,15 @@ class RemappingKmVisitors(private val remapper: Remapper) { override fun visitContract(): KmContractVisitor { return RemappingKmContractVisitor(super.visitContract()) } + + override fun visitTypeParameter( + flags: Flags, + name: String, + id: Int, + variance: KmVariance + ): KmTypeParameterVisitor { + return RemappingKmTypeParameterVisitor(super.visitTypeParameter(flags, name, id, variance)) + } } inner class RemappingKmContractVisitor(delegate: KmContractVisitor?) : KmContractVisitor(delegate) { @@ -356,4 +368,38 @@ class RemappingKmVisitors(private val remapper: Remapper) { super.visit(remapJvmMethodSignature(signature)) } } + + inner class RemappingKmPackageVisitor(delegate: KmPackageVisitor?) : KmPackageVisitor(delegate) { + override fun visitExtensions(type: KmExtensionType): KmPackageExtensionVisitor { + return RemappingJvmPackageExtensionVisitor(super.visitExtensions(type) as JvmPackageExtensionVisitor?) + } + + override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor { + return RemappingKmFunctionVisitor(super.visitFunction(flags, name)) + } + + override fun visitProperty( + flags: Flags, + name: String, + getterFlags: Flags, + setterFlags: Flags + ): KmPropertyVisitor { + return RemappingKmPropertyVisitor(super.visitProperty(flags, name, getterFlags, setterFlags)) + } + + override fun visitTypeAlias(flags: Flags, name: String): KmTypeAliasVisitor { + return RemappingKmTypeAliasVisitor(super.visitTypeAlias(flags, name)) + } + } + + inner class RemappingJvmPackageExtensionVisitor(delegate: JvmPackageExtensionVisitor?) : JvmPackageExtensionVisitor(delegate) { + override fun visitLocalDelegatedProperty( + flags: Flags, + name: String, + getterFlags: Flags, + setterFlags: Flags + ): KmPropertyVisitor { + return RemappingKmPropertyVisitor(super.visitLocalDelegatedProperty(flags, name, getterFlags, setterFlags)) + } + } } diff --git a/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt b/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt index e9a6f478..5ea1caa0 100644 --- a/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt +++ b/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt @@ -68,6 +68,25 @@ class KotlinClassMetadataRemappingAnnotationVisitorTest { println(d2Regex.find(asm)?.value) } + @Test + fun extensionTest() { + val input = getClassBytes("TestExtensionKt") + val classReader = ClassReader(input) + + val tinyRemapper = TinyRemapper.newRemapper() + .withMappings(readMappings("TestExtensionKt")) + .build() + + val stringWriter = StringWriter() + val traceClassVisitor = TraceClassVisitor(null, TextifierImpl(), PrintWriter(stringWriter)) + + classReader.accept(KotlinMetadataRemappingClassVisitor(tinyRemapper.environment.remapper, traceClassVisitor), 0) + + val d2Regex = Regex("(d2=)(.*)") + val asm = stringWriter.toString() + println(d2Regex.find(asm)?.value) + } + private fun getClassBytes(name: String): ByteArray { return File("src/test/resources/classes/$name.class").readBytes() } diff --git a/src/test/resources/classes/TestExtensionKt.class b/src/test/resources/classes/TestExtensionKt.class new file mode 100644 index 0000000000000000000000000000000000000000..8ed6cd7e59c55a774e24873560203d872f6a1eb7 GIT binary patch literal 843 zcmaJ;O-~a+7=C72T3CyeDndaJp-QRXf-x}>V`3DOjkOvf9ON|J4rSWiS+g^3;KtAK z>cPZ#FvO!j%J@#(AbQ|1@7Fx<=kx3L_a6WrW0PS+OY0wrW~#zC^dq6$J<*naH?fh@ z{=PJJ_rywVRHEP5JQzc1^xri*GNfpRA#aCJhUIGA)r^&vVJeQSFSS+n)ZZPb zo%I8T)q0Y){f@NFR48qHp><+~r5?s_B(~9uq8*0ZGkdJe^KpzbER6>0^kZLXD^o2Z zKhQR%bQPLBJcgO@ScctE{(F&%SQ3z7ty=$ojc@UvpL0YJZ#`nJNNGs- zraTexr$}zbNsCzhhw6u2%aDDUv?Rk+otSL&;-*aZMKhvEsh)%)IuNOH{wP@a&?ACl z8R)(;isBaxy!gv5gSVIT(onuuu26ZDb1=-f1jIHtvTkV_xQsF6$X_GxDQVgQg<5Ii z8>VX6ubBBlK1Y?LM>>Z|$061oYKX-x%#v55m!~5lPX@<>fxM3m(oZNlM{UZ7$OM=V hun^!j76UBdPJm_HJp?mY!RjHh24e;lgIR-ne*gsc)kpvU literal 0 HcmV?d00001 diff --git a/src/test/resources/mappings/TestExtensionKt.mappings b/src/test/resources/mappings/TestExtensionKt.mappings new file mode 100644 index 00000000..ca345906 --- /dev/null +++ b/src/test/resources/mappings/TestExtensionKt.mappings @@ -0,0 +1,2 @@ +tiny 2 0 intermediary named +c net/minecraft/class_1297 net/minecraft/entity/Entity \ No newline at end of file diff --git a/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestExtension.kt b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestExtension.kt new file mode 100644 index 00000000..2d37d24d --- /dev/null +++ b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestExtension.kt @@ -0,0 +1,14 @@ +package net.fabricmc.language.kotlin + +import net.minecraft.entity.Entity + +class TestExtension { + fun testExtCompile() { + val entity: Entity? = null + entity!!.testExt() + } +} + +fun Entity.testExt() { + velocityDirty = true +} \ No newline at end of file From 0ba3e142ba1ad6e03f1d3398dd5d5a3b62db1589 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Mon, 14 Mar 2022 01:44:11 +0000 Subject: [PATCH 09/27] Update gradle + deps --- build.gradle | 9 +++++---- gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 59821 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 10 +++++----- .../loom/test/LoomTestConstants.groovy | 2 +- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index 9e35f2d6..1cf84729 100644 --- a/build.gradle +++ b/build.gradle @@ -71,8 +71,8 @@ dependencies { // libraries implementation ('commons-io:commons-io:2.11.0') implementation ('com.google.code.gson:gson:2.9.0') - implementation ('com.fasterxml.jackson.core:jackson-databind:2.13.1') - implementation ('com.google.guava:guava:31.0.1-jre') + implementation ('com.fasterxml.jackson.core:jackson-databind:2.13.2') + implementation ('com.google.guava:guava:31.1-jre') implementation ('org.ow2.asm:asm:9.2') implementation ('org.ow2.asm:asm-analysis:9.2') implementation ('org.ow2.asm:asm-commons:9.2') @@ -110,7 +110,7 @@ dependencies { // Testing testImplementation(gradleTestKit()) - testImplementation('org.spockframework:spock-core:2.0-groovy-3.0') { + testImplementation('org.spockframework:spock-core:2.1-groovy-3.0') { exclude module: 'groovy-all' } testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.2' @@ -118,9 +118,10 @@ dependencies { exclude group: 'org.jetbrains.kotlin' } testImplementation 'net.fabricmc:fabric-installer:0.9.0' - testImplementation 'org.mockito:mockito-core:4.3.1' + testImplementation 'org.mockito:mockito-core:4.4.0' compileOnly 'org.jetbrains:annotations:23.0.0' + testCompileOnly 'org.jetbrains:annotations:23.0.0' } jar { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..41d9927a4d4fb3f96a785543079b8df6723c946b 100644 GIT binary patch delta 8958 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b1159fc5..d7e66b5c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index c53aefaa..1b6c7873 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,10 +32,10 @@ # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # diff --git a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index 91bc751c..74ed875d 100644 --- a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy @@ -28,7 +28,7 @@ import org.gradle.util.GradleVersion class LoomTestConstants { public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion() - public final static String PRE_RELEASE_GRADLE = "7.5-20220228014109+0000" + public final static String PRE_RELEASE_GRADLE = "7.5-20220312232108+0000" public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] } From 38675b4994f77024be531c5a4dfc50a1deb2965d Mon Sep 17 00:00:00 2001 From: Cat Core <34719527+arthurbambou@users.noreply.github.com> Date: Sat, 19 Mar 2022 01:43:20 +0100 Subject: [PATCH 10/27] Fix another broken library pom (#616) * Update MinecraftLibraryProvider.java * Update LegacyProjectTest.groovy --- .../providers/minecraft/MinecraftLibraryProvider.java | 2 +- .../net/fabricmc/loom/test/integration/LegacyProjectTest.groovy | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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 249a0a06..260ec81f 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 @@ -60,7 +60,7 @@ public class MinecraftLibraryProvider { if (library.isValidForOS() && !library.hasNatives() && library.artifact() != null) { // 1.4.7 contains an LWJGL version with an invalid maven pom, set the metadata sources to not use the pom for this version. - if ("org.lwjgl.lwjgl:lwjgl:2.9.1-nightly-20130708-debug3".equals(library.name())) { + if ("org.lwjgl.lwjgl:lwjgl:2.9.1-nightly-20130708-debug3".equals(library.name()) || "org.lwjgl.lwjgl:lwjgl:2.9.1-nightly-20131017".equals(library.name())) { LoomRepositoryPlugin.setupForLegacyVersions(project); } else if (library.name().startsWith("org.ow2.asm:asm-all")) { // Don't want asm-all, use the modern split version. diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy index 97bdb50e..7461566c 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy @@ -79,6 +79,7 @@ class LegacyProjectTest extends Specification implements GradleProjectTestTrait '1.12.2' | _ '1.8.9' | _ '1.7.10' | _ + '1.7' | _ '1.6.4' | _ '1.4.7' | _ '1.3.2' | _ From 007bc97c1bb2682cd2fc9a8e5df4b4798df75886 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 30 Mar 2022 18:11:47 +0100 Subject: [PATCH 11/27] Update decompilers and mercury (JDT). --- build.gradle | 12 ++++++++---- .../decompilers/fernflower/FernflowerLogger.java | 1 + .../net/fabricmc/loom/test/LoomTestConstants.groovy | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 1cf84729..f106514b 100644 --- a/build.gradle +++ b/build.gradle @@ -71,7 +71,7 @@ dependencies { // libraries implementation ('commons-io:commons-io:2.11.0') implementation ('com.google.code.gson:gson:2.9.0') - implementation ('com.fasterxml.jackson.core:jackson-databind:2.13.2') + implementation ('com.fasterxml.jackson.core:jackson-databind:2.13.2.1') implementation ('com.google.guava:guava:31.1-jre') implementation ('org.ow2.asm:asm:9.2') implementation ('org.ow2.asm:asm-analysis:9.2') @@ -94,11 +94,11 @@ dependencies { } // decompilers - implementation ('net.fabricmc:fabric-fernflower:1.4.1') - implementation ('net.fabricmc:cfr:0.0.9') + implementation ('net.fabricmc:fabric-fernflower:1.5.0') + implementation ('net.fabricmc:cfr:0.1.0') // source code remapping - implementation ('net.fabricmc:mercury:0.2.4') + implementation ('net.fabricmc:mercury:0.2.5') // Kotlin implementation("org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.4.1") { @@ -277,6 +277,10 @@ task writeActionsTestMatrix() { } def className = it.name.replace(".groovy", "") + + // Disabled for CI, as it fails too much. + if (className.endsWith("DecompileTest")) return + testMatrix.add("net.fabricmc.loom.test.integration.${className}") } } diff --git a/src/main/java/net/fabricmc/loom/decompilers/fernflower/FernflowerLogger.java b/src/main/java/net/fabricmc/loom/decompilers/fernflower/FernflowerLogger.java index a98060e9..3699e3a6 100644 --- a/src/main/java/net/fabricmc/loom/decompilers/fernflower/FernflowerLogger.java +++ b/src/main/java/net/fabricmc/loom/decompilers/fernflower/FernflowerLogger.java @@ -40,6 +40,7 @@ public class FernflowerLogger extends IFernflowerLogger { @Override public void writeMessage(String message, Severity severity) { if (message.contains("Inconsistent inner class entries for")) return; + if (message.contains("Inconsistent generic signature in method")) return; System.err.println(message); } diff --git a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index 74ed875d..a0e5112f 100644 --- a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy @@ -28,7 +28,7 @@ import org.gradle.util.GradleVersion class LoomTestConstants { public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion() - public final static String PRE_RELEASE_GRADLE = "7.5-20220312232108+0000" + public final static String PRE_RELEASE_GRADLE = "7.5-20220329233654+0000" public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] } From 981081c2c95b9fa1712529324579422e60246282 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 30 Mar 2022 19:26:07 +0100 Subject: [PATCH 12/27] Add protection around using the Kotlin class metadata remapping on mismatching Kotlin versions. This is going to need further work to run the kotlin remapper in a forked JVM using the version of Koltin that the project was built against. --- build.gradle | 4 +- ...ClassMetadataRemappingAnnotationVisitor.kt | 45 +++++++++++++++---- .../KotlinMetadataRemappingClassVisitor.kt | 16 ++++++- ...sMetadataRemappingAnnotationVisitorTest.kt | 45 ++++++++++++++----- 4 files changed, 86 insertions(+), 24 deletions(-) diff --git a/build.gradle b/build.gradle index f106514b..5fab02e2 100644 --- a/build.gradle +++ b/build.gradle @@ -101,12 +101,12 @@ dependencies { implementation ('net.fabricmc:mercury:0.2.5') // Kotlin - implementation("org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.4.1") { + implementation('org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.4.2') { transitive = false } // Kapt integration - compileOnly('org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0') + compileOnly('org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31') // Must match the version included with gradle. // Testing testImplementation(gradleTestKit()) diff --git a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt index 3adc3522..353a8b11 100644 --- a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt +++ b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt @@ -30,10 +30,13 @@ import org.objectweb.asm.AnnotationVisitor import org.objectweb.asm.Opcodes import org.objectweb.asm.commons.Remapper import org.objectweb.asm.tree.AnnotationNode +import org.slf4j.LoggerFactory -class KotlinClassMetadataRemappingAnnotationVisitor(private val remapper: Remapper, val next: AnnotationVisitor) : +class KotlinClassMetadataRemappingAnnotationVisitor(private val remapper: Remapper, val next: AnnotationVisitor, val className: String?) : AnnotationNode(Opcodes.ASM9, KotlinMetadataRemappingClassVisitor.ANNOTATION_DESCRIPTOR) { + private val logger = LoggerFactory.getLogger(javaClass) + private var _name: String? = null override fun visit(name: String?, value: Any?) { @@ -43,12 +46,24 @@ class KotlinClassMetadataRemappingAnnotationVisitor(private val remapper: Remapp override fun visitEnd() { super.visitEnd() - when (val metadata = readMetadata()) { + + val header = readHeader() ?: return + + val headerVersion = header.metadataVersion.joinToString(".") + if (headerVersion != KotlinVersion.CURRENT.toString()) { + logger.warn("Skipping remap of kotlin metadata for class ($className) as it was built with Kotlin ($headerVersion) while Gradle is running with the included Kotlin version (${KotlinVersion.CURRENT}).") + accept(next) + return + } + + when (val metadata = KotlinClassMetadata.read(header)) { is KotlinClassMetadata.Class -> { val klass = metadata.toKmClass() val writer = KotlinClassMetadata.Class.Writer() klass.accept(RemappingKmVisitors(remapper).RemappingKmClassVisitor(writer)) - writeClassHeader(writer.write().header) + val remapped = writer.write(header.metadataVersion).header + writeClassHeader(remapped) + validateKotlinClassHeader(remapped, header) } is KotlinClassMetadata.SyntheticClass -> { val klambda = metadata.toKmLambda() @@ -56,7 +71,9 @@ class KotlinClassMetadataRemappingAnnotationVisitor(private val remapper: Remapp if (klambda != null) { val writer = KotlinClassMetadata.SyntheticClass.Writer() klambda.accept(RemappingKmVisitors(remapper).RemappingKmLambdaVisitor(writer)) - writeClassHeader(writer.write().header) + val remapped = writer.write(header.metadataVersion).header + writeClassHeader(remapped) + validateKotlinClassHeader(remapped, header) } else { accept(next) } @@ -65,13 +82,17 @@ class KotlinClassMetadataRemappingAnnotationVisitor(private val remapper: Remapp val kpackage = metadata.toKmPackage() val writer = KotlinClassMetadata.FileFacade.Writer() kpackage.accept(RemappingKmVisitors(remapper).RemappingKmPackageVisitor(writer)) - writeClassHeader(writer.write().header) + val remapped = writer.write(header.metadataVersion).header + writeClassHeader(remapped) + validateKotlinClassHeader(remapped, header) } is KotlinClassMetadata.MultiFileClassPart -> { val kpackage = metadata.toKmPackage() val writer = KotlinClassMetadata.MultiFileClassPart.Writer() kpackage.accept(RemappingKmVisitors(remapper).RemappingKmPackageVisitor(writer)) - writeClassHeader(writer.write(metadata.facadeClassName, metadata.header.metadataVersion, metadata.header.extraInt).header) + val remapped = writer.write(metadata.facadeClassName, metadata.header.metadataVersion, metadata.header.extraInt).header + writeClassHeader(remapped) + validateKotlinClassHeader(remapped, header) } is KotlinClassMetadata.MultiFileClassFacade, is KotlinClassMetadata.Unknown, null -> { // do nothing @@ -81,7 +102,7 @@ class KotlinClassMetadataRemappingAnnotationVisitor(private val remapper: Remapp } @Suppress("UNCHECKED_CAST") - private fun readMetadata(): KotlinClassMetadata? { + private fun readHeader(): KotlinClassHeader? { var kind: Int? = null var metadataVersion: IntArray? = null var data1: Array? = null @@ -106,8 +127,7 @@ class KotlinClassMetadataRemappingAnnotationVisitor(private val remapper: Remapp } } - val header = KotlinClassHeader(kind, metadataVersion, data1, data2, extraString, packageName, extraInt) - return KotlinClassMetadata.read(header) + return KotlinClassHeader(kind, metadataVersion, data1, data2, extraString, packageName, extraInt) } private fun writeClassHeader(header: KotlinClassHeader) { @@ -130,4 +150,11 @@ class KotlinClassMetadataRemappingAnnotationVisitor(private val remapper: Remapp newNode.accept(next) } + + private fun validateKotlinClassHeader(remapped: KotlinClassHeader, original: KotlinClassHeader) { + // This can happen when the remapper is ran on a kotlin version that does not match the version the class was compiled with. + if (remapped.data2.size != original.data2.size) { + throw RuntimeException("Kotlin class metadata size mismatch: data2 size does not match original. New: ${remapped.data2.size} Old: ${original.data2.size}") + } + } } diff --git a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataRemappingClassVisitor.kt b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataRemappingClassVisitor.kt index f6716a89..9a014555 100644 --- a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataRemappingClassVisitor.kt +++ b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataRemappingClassVisitor.kt @@ -35,11 +35,25 @@ class KotlinMetadataRemappingClassVisitor(private val remapper: Remapper, next: val ANNOTATION_DESCRIPTOR: String = Type.getDescriptor(Metadata::class.java) } + var className: String? = null + + override fun visit( + version: Int, + access: Int, + name: String?, + signature: String?, + superName: String?, + interfaces: Array? + ) { + this.className = name + super.visit(version, access, name, signature, superName, interfaces) + } + override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor? { var result: AnnotationVisitor? = super.visitAnnotation(descriptor, visible) if (descriptor == ANNOTATION_DESCRIPTOR && result != null) { - result = KotlinClassMetadataRemappingAnnotationVisitor(remapper, result) + result = KotlinClassMetadataRemappingAnnotationVisitor(remapper, result, className) } return result diff --git a/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt b/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt index 5ea1caa0..5d7cbc62 100644 --- a/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt +++ b/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt @@ -30,8 +30,10 @@ import net.fabricmc.mappingio.MappingReader import net.fabricmc.mappingio.tree.MemoryMappingTree import net.fabricmc.tinyremapper.IMappingProvider import net.fabricmc.tinyremapper.TinyRemapper +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassVisitor import org.objectweb.asm.Opcodes import org.objectweb.asm.util.Textifier import org.objectweb.asm.util.TraceClassVisitor @@ -58,14 +60,19 @@ class KotlinClassMetadataRemappingAnnotationVisitorTest { .withMappings(readMappings("PosInChunk")) .build() - val stringWriter = StringWriter() - val traceClassVisitor = TraceClassVisitor(null, TextifierImpl(), PrintWriter(stringWriter)) + val inputWriter = StringWriter() + classReader.accept(stringWriterVisitor(inputWriter), 0) - classReader.accept(KotlinMetadataRemappingClassVisitor(tinyRemapper.environment.remapper, traceClassVisitor), 0) + val remappedWriter = StringWriter() + classReader.accept(KotlinMetadataRemappingClassVisitor(tinyRemapper.environment.remapper, stringWriterVisitor(remappedWriter)), 0) - val d2Regex = Regex("(d2=)(.*)") - val asm = stringWriter.toString() - println(d2Regex.find(asm)?.value) + val d2In = d2(inputWriter.toString()) + val d2Out = d2(remappedWriter.toString()) + + println(d2In) + println(d2Out) + + assertEquals(d2In.size, d2Out.size) } @Test @@ -77,14 +84,19 @@ class KotlinClassMetadataRemappingAnnotationVisitorTest { .withMappings(readMappings("TestExtensionKt")) .build() - val stringWriter = StringWriter() - val traceClassVisitor = TraceClassVisitor(null, TextifierImpl(), PrintWriter(stringWriter)) + val inputWriter = StringWriter() + classReader.accept(stringWriterVisitor(inputWriter), 0) - classReader.accept(KotlinMetadataRemappingClassVisitor(tinyRemapper.environment.remapper, traceClassVisitor), 0) + val remappedWriter = StringWriter() + classReader.accept(KotlinMetadataRemappingClassVisitor(tinyRemapper.environment.remapper, stringWriterVisitor(remappedWriter)), 0) - val d2Regex = Regex("(d2=)(.*)") - val asm = stringWriter.toString() - println(d2Regex.find(asm)?.value) + val d2In = d2(inputWriter.toString()) + val d2Out = d2(remappedWriter.toString()) + + println(d2In) + println(d2Out) + + assertEquals(d2In.size, d2Out.size) } private fun getClassBytes(name: String): ByteArray { @@ -97,5 +109,14 @@ class KotlinClassMetadataRemappingAnnotationVisitorTest { return TinyRemapperHelper.create(mappingTree, "named", "intermediary", false) } + private fun stringWriterVisitor(writer: StringWriter): ClassVisitor { + return TraceClassVisitor(null, TextifierImpl(), PrintWriter(writer)) + } + + private fun d2(bytecode: String): List { + val d2Regex = Regex("d2=\\{(.*)}") + return d2Regex.find(bytecode)!!.groupValues[1].split(",") + } + private class TextifierImpl : Textifier(Opcodes.ASM9) } From 2462e12e1630e4a72dd00ec521638cca2c8df512 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 13 Apr 2022 20:09:47 +0100 Subject: [PATCH 13/27] Update deps --- bootstrap/build.gradle | 2 +- build.gradle | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bootstrap/build.gradle b/bootstrap/build.gradle index efd4f1e4..4083cf1f 100644 --- a/bootstrap/build.gradle +++ b/bootstrap/build.gradle @@ -21,7 +21,7 @@ dependencies { implementation gradleApi() testImplementation(gradleTestKit()) - testImplementation('org.spockframework:spock-core:2.0-groovy-3.0') { + testImplementation('org.spockframework:spock-core:2.1-groovy-3.0') { exclude module: 'groovy-all' } } diff --git a/build.gradle b/build.gradle index 5fab02e2..51cdccc9 100644 --- a/build.gradle +++ b/build.gradle @@ -71,13 +71,13 @@ dependencies { // libraries implementation ('commons-io:commons-io:2.11.0') implementation ('com.google.code.gson:gson:2.9.0') - implementation ('com.fasterxml.jackson.core:jackson-databind:2.13.2.1') + implementation ('com.fasterxml.jackson.core:jackson-databind:2.13.2.2') implementation ('com.google.guava:guava:31.1-jre') - implementation ('org.ow2.asm:asm:9.2') - implementation ('org.ow2.asm:asm-analysis:9.2') - implementation ('org.ow2.asm:asm-commons:9.2') - implementation ('org.ow2.asm:asm-tree:9.2') - implementation ('org.ow2.asm:asm-util:9.2') + implementation ('org.ow2.asm:asm:9.3') + implementation ('org.ow2.asm:asm-analysis:9.3') + implementation ('org.ow2.asm:asm-commons:9.3') + implementation ('org.ow2.asm:asm-tree:9.3') + implementation ('org.ow2.asm:asm-util:9.3') // game handling utils implementation ('net.fabricmc:stitch:0.6.1') { @@ -95,7 +95,7 @@ dependencies { // decompilers implementation ('net.fabricmc:fabric-fernflower:1.5.0') - implementation ('net.fabricmc:cfr:0.1.0') + implementation ('net.fabricmc:cfr:0.1.1') // source code remapping implementation ('net.fabricmc:mercury:0.2.5') @@ -114,7 +114,7 @@ dependencies { exclude module: 'groovy-all' } testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.2' - testImplementation ('io.javalin:javalin:4.3.0') { + testImplementation ('io.javalin:javalin:4.4.0') { exclude group: 'org.jetbrains.kotlin' } testImplementation 'net.fabricmc:fabric-installer:0.9.0' From ccc70f134d7886a8d3538f7e7141620458902849 Mon Sep 17 00:00:00 2001 From: Jared Date: Thu, 14 Apr 2022 00:15:18 +0200 Subject: [PATCH 14/27] Make run tasks use the correct path in multiproject environments. (#624) --- src/main/java/net/fabricmc/loom/task/AbstractRunTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java b/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java index e52f3a31..d4ee306f 100644 --- a/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java +++ b/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java @@ -52,7 +52,7 @@ public abstract class AbstractRunTask extends JavaExec { @Override public void exec() { - setWorkingDir(new File(getProject().getRootDir(), config.runDir)); + setWorkingDir(new File(getProject().getProjectDir(), config.runDir)); super.exec(); } From ff6e7a23b94f2e014032e0e16aa10bd5308ffa97 Mon Sep 17 00:00:00 2001 From: Jared Date: Thu, 14 Apr 2022 00:35:27 +0200 Subject: [PATCH 15/27] Ensure the Mojang maven repo is added before maven central. Close #621 (#625) --- .../net/fabricmc/loom/LoomRepositoryPlugin.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java b/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java index 8b89642c..2301f575 100644 --- a/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java +++ b/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java @@ -26,7 +26,9 @@ package net.fabricmc.loom; import org.gradle.api.Plugin; import org.gradle.api.Project; +import org.gradle.api.artifacts.ArtifactRepositoryContainer; import org.gradle.api.artifacts.dsl.RepositoryHandler; +import org.gradle.api.artifacts.repositories.ArtifactRepository; import org.gradle.api.artifacts.repositories.IvyArtifactRepository; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; import org.gradle.api.initialization.Settings; @@ -68,7 +70,8 @@ public class LoomRepositoryPlugin implements Plugin { repo.setName("Fabric"); repo.setUrl(MirrorUtil.getFabricRepository(target)); }); - repositories.maven(repo -> { + + MavenArtifactRepository mojangRepo = repositories.maven(repo -> { repo.setName("Mojang"); repo.setUrl(MirrorUtil.getLibrariesBase(target)); @@ -79,6 +82,16 @@ public class LoomRepositoryPlugin implements Plugin { sources.ignoreGradleMetadataRedirection(); }); }); + + // If a mavenCentral repo is already defined, remove the mojang repo and add it back before the mavenCentral repo so that it will be checked first. + // See: https://github.com/FabricMC/fabric-loom/issues/621 + ArtifactRepository mavenCentral = repositories.findByName(ArtifactRepositoryContainer.DEFAULT_MAVEN_CENTRAL_REPO_NAME); + + if (mavenCentral != null) { + repositories.remove(mojangRepo); + repositories.add(repositories.indexOf(mavenCentral), mojangRepo); + } + repositories.mavenCentral(); repositories.ivy(repo -> { From 66ef9659a2d0dc633c2224ae6b0c96f178dc75fe Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Sat, 16 Apr 2022 00:06:56 +0100 Subject: [PATCH 16/27] Fail hard with an error message when calling the wrong officialMojangMappings method. Before this would have been a weird silent failure and resulted in bad mappings. --- .../loom/api/LoomGradleExtensionAPI.java | 4 +-- .../extension/LoomGradleExtensionApiImpl.java | 16 +++++++++++ .../MojangMappingsProjectTest.groovy | 27 ++++++++++++++++++- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index 485c4e70..1e7a880f 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -70,9 +70,7 @@ public interface LoomGradleExtensionAPI { ConfigurableFileCollection getLog4jConfigs(); - default Dependency officialMojangMappings() { - return layered(LayeredMappingSpecBuilder::officialMojangMappings); - } + Dependency officialMojangMappings(); Dependency layered(Action action); diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index aa6365ec..3d27b4ba 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -78,6 +78,9 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA private final NamedDomainObjectContainer runConfigs; private final NamedDomainObjectContainer decompilers; + // A common mistake with layered mappings is to call the wrong `officialMojangMappings` method, use this to keep track of when we are building a layered mapping spec. + protected final ThreadLocal layeredSpecBuilderScope = ThreadLocal.withInitial(() -> false); + protected LoomGradleExtensionApiImpl(Project project, LoomFiles directories) { this.jarProcessors = project.getObjects().listProperty(JarProcessor.class) .empty(); @@ -162,10 +165,23 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA return jarProcessors; } + @Override + public Dependency officialMojangMappings() { + if (layeredSpecBuilderScope.get()) { + throw new IllegalStateException("Use `officialMojangMappings()` when configuring layered mappings, not the extension method `loom.officialMojangMappings()`"); + } + + return layered(LayeredMappingSpecBuilder::officialMojangMappings); + } + @Override public Dependency layered(Action action) { LayeredMappingSpecBuilderImpl builder = new LayeredMappingSpecBuilderImpl(); + + layeredSpecBuilderScope.set(true); action.execute(builder); + layeredSpecBuilderScope.set(false); + LayeredMappingSpec builtSpec = builder.build(); return new LayeredMappingsDependency(getProject(), new GradleMappingContext(getProject(), builtSpec.getVersion().replace("+", "_").replace(".", "_")), builtSpec, builtSpec.getVersion()); } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/MojangMappingsProjectTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/MojangMappingsProjectTest.groovy index 6afba527..2e1db92c 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/MojangMappingsProjectTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/MojangMappingsProjectTest.groovy @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2018-2021 FabricMC + * Copyright (c) 2018-2022 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 @@ -74,4 +74,29 @@ class MojangMappingsProjectTest extends Specification implements GradleProjectTe where: version << STANDARD_TEST_VERSIONS } + + @Unroll + def "fail with wrong officialMojangMappings usage (gradle #version)"() { + setup: + def gradle = gradleProject(project: "minimalBase", version: version) + + gradle.buildGradle << ''' + dependencies { + minecraft "com.mojang:minecraft:1.18.2" + mappings loom.layered { + // This is the wrong method to call! + loom.officialMojangMappings() + } + } + ''' + + when: + def result = gradle.run(task: "build", expectFailure: true) + + then: + result.output.contains("Use `officialMojangMappings()` when configuring layered mappings, not the extension method `loom.officialMojangMappings()`") + + where: + version << STANDARD_TEST_VERSIONS + } } From 463847184ef1133efe0beaf81ffcdabe347516e4 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Sat, 16 Apr 2022 00:14:42 +0100 Subject: [PATCH 17/27] Update mapping namespace manifest entry when remapping mod dependencies. --- .../loom/configuration/mods/ModProcessor.java | 17 +++++++++++++++++ .../net/fabricmc/loom/task/RemapJarTask.java | 5 +++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java index 4775599b..872fb6fa 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java @@ -24,6 +24,8 @@ package net.fabricmc.loom.configuration.mods; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; @@ -33,6 +35,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.jar.Manifest; import com.google.gson.JsonObject; import org.gradle.api.Project; @@ -47,6 +50,7 @@ import net.fabricmc.loom.configuration.RemappedConfigurationEntry; import net.fabricmc.loom.configuration.processors.dependency.ModDependencyInfo; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.kotlin.remapping.KotlinMetadataTinyRemapperExtension; +import net.fabricmc.loom.task.RemapJarTask; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.TinyRemapperHelper; import net.fabricmc.loom.util.ZipUtils; @@ -217,8 +221,21 @@ public class ModProcessor { } stripNestedJars(info.getRemappedOutput()); + remapJarManifestEntries(info.getRemappedOutput().toPath()); info.finaliseRemapping(); } } + + private void remapJarManifestEntries(Path jar) throws IOException { + ZipUtils.transform(jar, Map.of(RemapJarTask.MANIFEST_PATH, bytes -> { + var manifest = new Manifest(new ByteArrayInputStream(bytes)); + + manifest.getMainAttributes().putValue(RemapJarTask.MANIFEST_NAMESPACE_KEY, toM); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + manifest.write(out); + return out.toByteArray(); + })); + } } diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index 83af5533..86f43489 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -83,7 +83,8 @@ import net.fabricmc.tinyremapper.OutputConsumerPath; import net.fabricmc.tinyremapper.TinyRemapper; public abstract class RemapJarTask extends AbstractRemapJarTask { - private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; + public static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; + public static final String MANIFEST_NAMESPACE_KEY = "Fabric-Mapping-Namespace"; @InputFiles public abstract ConfigurableFileCollection getNestedJars(); @@ -306,7 +307,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { var manifest = new Manifest(new ByteArrayInputStream(bytes)); getParameters().getJarManifestService().get().apply(manifest, getParameters().getManifestAttributes().get()); - manifest.getMainAttributes().putValue("Fabric-Mapping-Namespace", getParameters().getTargetNamespace().get()); + manifest.getMainAttributes().putValue(MANIFEST_NAMESPACE_KEY, getParameters().getTargetNamespace().get()); ByteArrayOutputStream out = new ByteArrayOutputStream(); manifest.write(out); From ba2c98f7fdd5886fbc79a352f323692813fb39f5 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Sat, 16 Apr 2022 18:39:11 +0100 Subject: [PATCH 18/27] Run the kotlin metadata remapper in its own classloader with the compiler kotlin version. (#626) --- .../loom/configuration/mods/ModProcessor.java | 17 +++- .../task/service/TinyRemapperService.java | 26 +++-- .../loom/util/kotlin/KotlinClasspath.java | 34 +++++++ .../util/kotlin/KotlinClasspathService.java | 73 ++++++++++++++ .../KotlinMetadataTinyRemapperExtension.java | 30 ++++++ .../loom/util/kotlin/KotlinPluginUtils.java | 52 ++++++++++ .../kotlin/KotlinRemapperClassloader.java | 93 ++++++++++++++++++ ...ClassMetadataRemappingAnnotationVisitor.kt | 12 +-- .../KotlinMetadataRemappingClassVisitor.kt | 6 ++ ...otlinMetadataTinyRemapperExtensionImpl.kt} | 5 +- .../loom/test/LoomTestConstants.groovy | 2 +- .../KotlinRemapperClassloaderTest.groovy | 96 +++++++++++++++++++ .../projects/kotlin/build.gradle.kts | 6 +- .../fabricmc/language/kotlin/TestExtension.kt | 5 + .../fabricmc/language/kotlin/TestModClass.kt | 4 +- 15 files changed, 435 insertions(+), 26 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/util/kotlin/KotlinClasspath.java create mode 100644 src/main/java/net/fabricmc/loom/util/kotlin/KotlinClasspathService.java create mode 100644 src/main/java/net/fabricmc/loom/util/kotlin/KotlinMetadataTinyRemapperExtension.java create mode 100644 src/main/java/net/fabricmc/loom/util/kotlin/KotlinPluginUtils.java create mode 100644 src/main/java/net/fabricmc/loom/util/kotlin/KotlinRemapperClassloader.java rename src/main/kotlin/net/fabricmc/loom/kotlin/remapping/{KotlinMetadataTinyRemapperExtension.kt => KotlinMetadataTinyRemapperExtensionImpl.kt} (89%) create mode 100644 src/test/groovy/net/fabricmc/loom/test/unit/kotlin/KotlinRemapperClassloaderTest.groovy diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java index 872fb6fa..0f982155 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java @@ -49,11 +49,12 @@ import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.RemappedConfigurationEntry; import net.fabricmc.loom.configuration.processors.dependency.ModDependencyInfo; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; -import net.fabricmc.loom.kotlin.remapping.KotlinMetadataTinyRemapperExtension; import net.fabricmc.loom.task.RemapJarTask; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.TinyRemapperHelper; import net.fabricmc.loom.util.ZipUtils; +import net.fabricmc.loom.util.kotlin.KotlinClasspathService; +import net.fabricmc.loom.util.kotlin.KotlinRemapperClassloader; import net.fabricmc.tinyremapper.InputTag; import net.fabricmc.tinyremapper.NonClassCopyMode; import net.fabricmc.tinyremapper.OutputConsumerPath; @@ -139,8 +140,6 @@ public class ModProcessor { private void remapJars(List remapList) throws IOException { final LoomGradleExtension extension = LoomGradleExtension.get(project); final MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); - final boolean useKotlinExtension = project.getPluginManager().hasPlugin("org.jetbrains.kotlin.jvm"); - Path[] mcDeps = project.getConfigurations().getByName(Constants.Configurations.LOADER_DEPENDENCIES).getFiles() .stream().map(File::toPath).toArray(Path[]::new); @@ -150,8 +149,12 @@ public class ModProcessor { .withMappings(TinyRemapperHelper.create(mappingsProvider.getMappings(), fromM, toM, false)) .renameInvalidLocals(false); - if (useKotlinExtension) { - builder.extension(KotlinMetadataTinyRemapperExtension.INSTANCE); + final KotlinClasspathService kotlinClasspathService = KotlinClasspathService.getOrCreateIfRequired(project); + KotlinRemapperClassloader kotlinRemapperClassloader = null; + + if (kotlinClasspathService != null) { + kotlinRemapperClassloader = KotlinRemapperClassloader.create(kotlinClasspathService); + builder.extension(kotlinRemapperClassloader.getTinyRemapperExtension()); } final TinyRemapper remapper = builder.build(); @@ -209,6 +212,10 @@ public class ModProcessor { } } finally { remapper.finish(); + + if (kotlinRemapperClassloader != null) { + kotlinRemapperClassloader.close(); + } } for (ModDependencyInfo info : remapList) { diff --git a/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java b/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java index 54d974a6..78f146bb 100644 --- a/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java +++ b/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java @@ -36,10 +36,13 @@ import java.util.Objects; import java.util.StringJoiner; import org.gradle.api.Project; +import org.jetbrains.annotations.Nullable; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.kotlin.remapping.KotlinMetadataTinyRemapperExtension; import net.fabricmc.loom.task.AbstractRemapJarTask; +import net.fabricmc.loom.util.kotlin.KotlinClasspath; +import net.fabricmc.loom.util.kotlin.KotlinClasspathService; +import net.fabricmc.loom.util.kotlin.KotlinRemapperClassloader; import net.fabricmc.loom.util.service.SharedService; import net.fabricmc.loom.util.service.SharedServiceManager; import net.fabricmc.tinyremapper.IMappingProvider; @@ -54,15 +57,15 @@ public class TinyRemapperService implements SharedService { final LoomGradleExtension extension = LoomGradleExtension.get(project); final SharedServiceManager sharedServiceManager = SharedServiceManager.get(project); final boolean legacyMixin = extension.getMixin().getUseLegacyMixinAp().get(); - final boolean useKotlinExtension = project.getPluginManager().hasPlugin("org.jetbrains.kotlin.jvm"); + final @Nullable KotlinClasspathService kotlinClasspathService = KotlinClasspathService.getOrCreateIfRequired(project); // Generates an id that is used to share the remapper across projects. This tasks in the remap jar task name to handle custom remap jar tasks separately. final var joiner = new StringJoiner(":"); joiner.add(extension.getMappingsProvider().getBuildServiceName("remapJarService", from, to)); joiner.add(remapJarTask.getName()); - if (useKotlinExtension) { - joiner.add("kotlin"); + if (kotlinClasspathService != null) { + joiner.add("kotlin-" + kotlinClasspathService.version()); } if (remapJarTask.getRemapperIsolation().get()) { @@ -79,7 +82,7 @@ public class TinyRemapperService implements SharedService { mappings.add(MixinMappingsService.getService(SharedServiceManager.get(project)).getMappingProvider(from, to)); } - return new TinyRemapperService(mappings, !legacyMixin, useKotlinExtension); + return new TinyRemapperService(mappings, !legacyMixin, kotlinClasspathService); }); service.readClasspath(remapJarTask.getClasspath().getFiles().stream().map(File::toPath).toList()); @@ -88,12 +91,14 @@ public class TinyRemapperService implements SharedService { } private TinyRemapper tinyRemapper; + @Nullable + private KotlinRemapperClassloader kotlinRemapperClassloader; private final Map inputTagMap = new HashMap<>(); private final HashSet classpath = new HashSet<>(); // Set to true once remapping has started, once set no inputs can be read. private boolean isRemapping = false; - public TinyRemapperService(List mappings, boolean useMixinExtension, boolean useKotlinExtension) { + public TinyRemapperService(List mappings, boolean useMixinExtension, @Nullable KotlinClasspath kotlinClasspath) { TinyRemapper.Builder builder = TinyRemapper.newRemapper(); for (IMappingProvider provider : mappings) { @@ -104,8 +109,9 @@ public class TinyRemapperService implements SharedService { builder.extension(new net.fabricmc.tinyremapper.extension.mixin.MixinExtension()); } - if (useKotlinExtension) { - builder.extension(KotlinMetadataTinyRemapperExtension.INSTANCE); + if (kotlinClasspath != null) { + kotlinRemapperClassloader = KotlinRemapperClassloader.create(kotlinClasspath); + builder.extension(kotlinRemapperClassloader.getTinyRemapperExtension()); } tinyRemapper = builder.build(); @@ -156,5 +162,9 @@ public class TinyRemapperService implements SharedService { tinyRemapper.finish(); tinyRemapper = null; } + + if (kotlinRemapperClassloader != null) { + kotlinRemapperClassloader.close(); + } } } diff --git a/src/main/java/net/fabricmc/loom/util/kotlin/KotlinClasspath.java b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinClasspath.java new file mode 100644 index 00000000..f7461f08 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinClasspath.java @@ -0,0 +1,34 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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.kotlin; + +import java.net.URL; +import java.util.Set; + +public interface KotlinClasspath { + String version(); + + Set classpath(); +} diff --git a/src/main/java/net/fabricmc/loom/util/kotlin/KotlinClasspathService.java b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinClasspathService.java new file mode 100644 index 00000000..0084fd47 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinClasspathService.java @@ -0,0 +1,73 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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.kotlin; + +import java.io.UncheckedIOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Set; +import java.util.stream.Collectors; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.util.service.SharedService; +import net.fabricmc.loom.util.service.SharedServiceManager; + +public record KotlinClasspathService(Set classpath, String version) implements KotlinClasspath, SharedService { + @Nullable + public static KotlinClasspathService getOrCreateIfRequired(Project project) { + if (!KotlinPluginUtils.hasKotlinPlugin(project)) { + return null; + } + + return getOrCreate(project, KotlinPluginUtils.getKotlinPluginVersion(project)); + } + + public static synchronized KotlinClasspathService getOrCreate(Project project, String kotlinVersion) { + final String id = "kotlinclasspath:" + kotlinVersion; + final SharedServiceManager sharedServiceManager = SharedServiceManager.get(project); + return sharedServiceManager.getOrCreateService(id, () -> create(project, kotlinVersion)); + } + + private static KotlinClasspathService create(Project project, String kotlinVersion) { + // Create a detached config to resolve the koltin std lib for the provided version. + Configuration detachedConfiguration = project.getConfigurations().detachedConfiguration( + project.getDependencies().create("org.jetbrains.kotlin:kotlin-stdlib:" + kotlinVersion) + ); + + Set classpath = detachedConfiguration.getFiles().stream() + .map(file -> { + try { + return file.toURI().toURL(); + } catch (MalformedURLException e) { + throw new UncheckedIOException(e); + } + }).collect(Collectors.toSet());; + + return new KotlinClasspathService(classpath, kotlinVersion); + } +} diff --git a/src/main/java/net/fabricmc/loom/util/kotlin/KotlinMetadataTinyRemapperExtension.java b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinMetadataTinyRemapperExtension.java new file mode 100644 index 00000000..1e13a350 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinMetadataTinyRemapperExtension.java @@ -0,0 +1,30 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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.kotlin; + +import net.fabricmc.tinyremapper.TinyRemapper; + +public interface KotlinMetadataTinyRemapperExtension extends TinyRemapper.ApplyVisitorProvider, TinyRemapper.Extension { +} diff --git a/src/main/java/net/fabricmc/loom/util/kotlin/KotlinPluginUtils.java b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinPluginUtils.java new file mode 100644 index 00000000..0eb3a464 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinPluginUtils.java @@ -0,0 +1,52 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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.kotlin; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.DependencySet; + +public class KotlinPluginUtils { + private static final String KOTLIN_PLUGIN_ID = "org.jetbrains.kotlin.jvm"; + private static final String KOTLIN_PLUGIN_GROUP = "org.jetbrains.kotlin.jvm"; + private static final String KOTLIN_PLUGIN_NAME = "org.jetbrains.kotlin.jvm.gradle.plugin"; + + public static boolean hasKotlinPlugin(Project project) { + return project.getPluginManager().hasPlugin(KOTLIN_PLUGIN_ID); + } + + public static String getKotlinPluginVersion(Project project) { + DependencySet buildDependencies = project.getBuildscript().getConfigurations() + .getByName("classpath").getDependencies(); + + for (Dependency dependency : buildDependencies) { + if (KOTLIN_PLUGIN_GROUP.equals(dependency.getGroup()) && KOTLIN_PLUGIN_NAME.equals(dependency.getName())) { + return dependency.getVersion(); + } + } + + throw new IllegalStateException("Unable to get the kotlin plugin version"); + } +} diff --git a/src/main/java/net/fabricmc/loom/util/kotlin/KotlinRemapperClassloader.java b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinRemapperClassloader.java new file mode 100644 index 00000000..1ad422c4 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinRemapperClassloader.java @@ -0,0 +1,93 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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.kotlin; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import kotlinx.metadata.jvm.KotlinClassHeader; + +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.kotlin.remapping.KotlinMetadataTinyRemapperExtensionImpl; + +/** + * Used to run the Kotlin remapper with a specific version of Koltin that may not match the kotlin version included with gradle. + */ +public class KotlinRemapperClassloader extends URLClassLoader { + // Packages that should be loaded from the gradle plugin classloader. + private static final List PARENT_PACKAGES = List.of( + "net.fabricmc.tinyremapper", + "net.fabricmc.loom.util.kotlin", + "org.objectweb.asm", + "org.slf4j" + ); + + private KotlinRemapperClassloader(URL[] urls) { + super(urls, null); + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (PARENT_PACKAGES.stream().anyMatch(name::startsWith)) { + return LoomGradlePlugin.class.getClassLoader().loadClass(name); + } + + return super.loadClass(name, resolve); + } + + public static KotlinRemapperClassloader create(KotlinClasspath classpathProvider) { + // Include the libraries that are not on the kotlin classpath. + final Stream loomUrls = getClassUrls( + KotlinMetadataTinyRemapperExtensionImpl.class, // Loom + KotlinClassHeader.class // Kotlin metadata api + ); + + final URL[] urls = Stream.concat( + loomUrls, + classpathProvider.classpath().stream() + ).toArray(URL[]::new); + + return new KotlinRemapperClassloader(urls); + } + + private static Stream getClassUrls(Class... classes) { + return Arrays.stream(classes).map(klass -> klass.getProtectionDomain().getCodeSource().getLocation()); + } + + /** + * Load the {@link KotlinMetadataTinyRemapperExtensionImpl} class on the new classloader. + */ + public KotlinMetadataTinyRemapperExtension getTinyRemapperExtension() { + try { + Class klass = this.loadClass(KotlinMetadataTinyRemapperExtensionImpl.class.getCanonicalName()); + return (KotlinMetadataTinyRemapperExtension) klass.getField("INSTANCE").get(null); + } catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException("Failed to create instance", e); + } + } +} diff --git a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt index 353a8b11..57fca33b 100644 --- a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt +++ b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt @@ -49,11 +49,11 @@ class KotlinClassMetadataRemappingAnnotationVisitor(private val remapper: Remapp val header = readHeader() ?: return - val headerVersion = header.metadataVersion.joinToString(".") - if (headerVersion != KotlinVersion.CURRENT.toString()) { - logger.warn("Skipping remap of kotlin metadata for class ($className) as it was built with Kotlin ($headerVersion) while Gradle is running with the included Kotlin version (${KotlinVersion.CURRENT}).") - accept(next) - return + val headerVersion = KotlinVersion(header.metadataVersion[0], header.metadataVersion[1], 0) + val currentMinorVersion = KotlinVersion(KotlinVersion.CURRENT.major, KotlinVersion.CURRENT.minor, 0) + + if (headerVersion != currentMinorVersion) { + logger.info("Kotlin metadata for class ($className) as it was built using a different major Kotlin version (${header.metadataVersion[0]}.${header.metadataVersion[1]}.x) while the remapper is using (${KotlinVersion.CURRENT}).") } when (val metadata = KotlinClassMetadata.read(header)) { @@ -154,7 +154,7 @@ class KotlinClassMetadataRemappingAnnotationVisitor(private val remapper: Remapp private fun validateKotlinClassHeader(remapped: KotlinClassHeader, original: KotlinClassHeader) { // This can happen when the remapper is ran on a kotlin version that does not match the version the class was compiled with. if (remapped.data2.size != original.data2.size) { - throw RuntimeException("Kotlin class metadata size mismatch: data2 size does not match original. New: ${remapped.data2.size} Old: ${original.data2.size}") + logger.info("Kotlin class metadata size mismatch: data2 size does not match original in class $className. New: ${remapped.data2.size} Old: ${original.data2.size}") } } } diff --git a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataRemappingClassVisitor.kt b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataRemappingClassVisitor.kt index 9a014555..e22b05fb 100644 --- a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataRemappingClassVisitor.kt +++ b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataRemappingClassVisitor.kt @@ -24,6 +24,7 @@ package net.fabricmc.loom.kotlin.remapping +import org.jetbrains.annotations.VisibleForTesting import org.objectweb.asm.AnnotationVisitor import org.objectweb.asm.ClassVisitor import org.objectweb.asm.Opcodes @@ -58,4 +59,9 @@ class KotlinMetadataRemappingClassVisitor(private val remapper: Remapper, next: return result } + + @VisibleForTesting + fun getRuntimeKotlinVersion(): String { + return KotlinVersion.CURRENT.toString() + } } diff --git a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataTinyRemapperExtension.kt b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataTinyRemapperExtensionImpl.kt similarity index 89% rename from src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataTinyRemapperExtension.kt rename to src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataTinyRemapperExtensionImpl.kt index 1ba80919..5ba16756 100644 --- a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataTinyRemapperExtension.kt +++ b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataTinyRemapperExtensionImpl.kt @@ -24,12 +24,13 @@ package net.fabricmc.loom.kotlin.remapping +import net.fabricmc.loom.util.kotlin.KotlinMetadataTinyRemapperExtension import net.fabricmc.tinyremapper.TinyRemapper import net.fabricmc.tinyremapper.api.TrClass import org.objectweb.asm.ClassVisitor -object KotlinMetadataTinyRemapperExtension : TinyRemapper.ApplyVisitorProvider, TinyRemapper.Extension { - override fun insertApplyVisitor(cls: TrClass, next: ClassVisitor): ClassVisitor { +object KotlinMetadataTinyRemapperExtensionImpl : KotlinMetadataTinyRemapperExtension { + override fun insertApplyVisitor(cls: TrClass, next: ClassVisitor?): ClassVisitor { return KotlinMetadataRemappingClassVisitor(cls.environment.remapper, next) } diff --git a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index a0e5112f..45642ec9 100644 --- a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy @@ -28,7 +28,7 @@ import org.gradle.util.GradleVersion class LoomTestConstants { public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion() - public final static String PRE_RELEASE_GRADLE = "7.5-20220329233654+0000" + public final static String PRE_RELEASE_GRADLE = "7.5-20220415181004+0000" public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] } diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/kotlin/KotlinRemapperClassloaderTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/kotlin/KotlinRemapperClassloaderTest.groovy new file mode 100644 index 00000000..bf0bcecf --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/kotlin/KotlinRemapperClassloaderTest.groovy @@ -0,0 +1,96 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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 net.fabricmc.loom.util.kotlin.KotlinClasspath +import net.fabricmc.loom.util.kotlin.KotlinRemapperClassloader +import net.fabricmc.tinyremapper.api.TrClass +import net.fabricmc.tinyremapper.api.TrEnvironment +import net.fabricmc.tinyremapper.api.TrRemapper +import org.objectweb.asm.ClassReader +import org.objectweb.asm.tree.ClassNode +import spock.lang.Specification + +class KotlinRemapperClassloaderTest extends Specification { + private static String KOTLIN_VERSION = "1.6.10" + private static String KOTLIN_URL = "https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/${KOTLIN_VERSION}/kotlin-stdlib-${KOTLIN_VERSION}.jar" + + def "Test Koltin Remapper Classloader"() { + given: + def classLoader = KotlinRemapperClassloader.create(new TestKotlinClasspath()) + def mockTrClass = Mock(TrClass) + def mockEnv = Mock(TrEnvironment) + def mockRemapper = Mock(TrRemapper) + + mockRemapper.map(_) >> { args -> args[0] } + mockRemapper.mapMethodDesc(_) >> { args -> args[0] } + + mockEnv.remapper >> mockRemapper + mockTrClass.environment >> mockEnv + + def classReader = new ClassReader(getClassBytes("TestExtensionKt")) + + when: + def extension = classLoader.tinyRemapperExtension + def visitor = extension.insertApplyVisitor(mockTrClass, new ClassNode()) + + classReader.accept(visitor, 0) + + then: + extension != null + visitor != null + + // Ensure that the visitor is using the kotlin version specified on the classpath. + visitor.runtimeKotlinVersion == KOTLIN_VERSION + } + + private class TestKotlinClasspath implements KotlinClasspath { + @Override + String version() { + return KOTLIN_VERSION + } + + @Override + Set classpath() { + def file = downloadFile(KOTLIN_URL, "kotlin-stdlib.jar") + + return Set.of( + file.toURI().toURL() + ) + } + } + + File tempDir = File.createTempDir() + File downloadFile(String url, String name) { + File dst = new File(tempDir, name) + dst.parentFile.mkdirs() + dst << new URL(url).newInputStream() + return dst + } + + def getClassBytes(String name) { + return new File("src/test/resources/classes/${name}.class").bytes + } +} diff --git a/src/test/resources/projects/kotlin/build.gradle.kts b/src/test/resources/projects/kotlin/build.gradle.kts index 6e9bcc2b..904c20a4 100644 --- a/src/test/resources/projects/kotlin/build.gradle.kts +++ b/src/test/resources/projects/kotlin/build.gradle.kts @@ -1,8 +1,8 @@ import java.util.Properties plugins { - kotlin("jvm") version "1.6.10" - kotlin("plugin.serialization") version "1.6.10" + kotlin("jvm") version "1.6.20" + kotlin("plugin.serialization") version "1.6.20" id("fabric-loom") } @@ -17,5 +17,5 @@ dependencies { minecraft(group = "com.mojang", name = "minecraft", version = "1.16.5") mappings(group = "net.fabricmc", name = "yarn", version = "1.16.5+build.5", classifier = "v2") modImplementation("net.fabricmc:fabric-loader:0.12.12") - modImplementation(group = "net.fabricmc", name = "fabric-language-kotlin", version = "1.7.1+kotlin.1.6.10") + modImplementation(group = "net.fabricmc", name = "fabric-language-kotlin", version = "1.7.3+kotlin.1.6.20") } \ No newline at end of file diff --git a/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestExtension.kt b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestExtension.kt index 2d37d24d..281850f5 100644 --- a/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestExtension.kt +++ b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestExtension.kt @@ -1,6 +1,7 @@ package net.fabricmc.language.kotlin import net.minecraft.entity.Entity +import net.minecraft.util.Identifier class TestExtension { fun testExtCompile() { @@ -11,4 +12,8 @@ class TestExtension { fun Entity.testExt() { velocityDirty = true +} + +fun Identifier.testExt(): String { + return "Hello ext" } \ No newline at end of file diff --git a/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestModClass.kt b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestModClass.kt index 456ce7a9..a82609a1 100644 --- a/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestModClass.kt +++ b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestModClass.kt @@ -11,13 +11,15 @@ class TestModClass : ModInitializer { val logger = LogManager.getFormatterLogger("KotlinLanguageTest") override fun onInitialize() { - val json = Json.encodeToString(ExampleSerializable(Identifier("kotlin:hello"), 12.0)) + val ident = Identifier("kotlin:hello") + val json = Json.encodeToString(ExampleSerializable(ident, 12.0)) val obj = Json.decodeFromString(json) logger.info("**************************") logger.info("Hello from Kotlin TestModClass") logger.info(json) logger.info(obj) + logger.info(ident.testExt()) logger.info("**************************") } } \ No newline at end of file From 0fda2a07bdfc05a5b77960d6493b940d58fdc199 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Sun, 17 Apr 2022 20:20:50 +0100 Subject: [PATCH 19/27] Improve kotlin plugin version retrieval. Update FLK in test. --- .../loom/util/kotlin/KotlinPluginUtils.java | 16 ++-------------- .../loom/test/integration/KotlinTest.groovy | 2 +- .../fabricmc/loom/test/util/ServerRunner.groovy | 2 +- 3 files changed, 4 insertions(+), 16 deletions(-) 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 0eb3a464..6c6c570b 100644 --- a/src/main/java/net/fabricmc/loom/util/kotlin/KotlinPluginUtils.java +++ b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinPluginUtils.java @@ -25,28 +25,16 @@ package net.fabricmc.loom.util.kotlin; import org.gradle.api.Project; -import org.gradle.api.artifacts.Dependency; -import org.gradle.api.artifacts.DependencySet; public class KotlinPluginUtils { private static final String KOTLIN_PLUGIN_ID = "org.jetbrains.kotlin.jvm"; - private static final String KOTLIN_PLUGIN_GROUP = "org.jetbrains.kotlin.jvm"; - private static final String KOTLIN_PLUGIN_NAME = "org.jetbrains.kotlin.jvm.gradle.plugin"; public static boolean hasKotlinPlugin(Project project) { return project.getPluginManager().hasPlugin(KOTLIN_PLUGIN_ID); } public static String getKotlinPluginVersion(Project project) { - DependencySet buildDependencies = project.getBuildscript().getConfigurations() - .getByName("classpath").getDependencies(); - - for (Dependency dependency : buildDependencies) { - if (KOTLIN_PLUGIN_GROUP.equals(dependency.getGroup()) && KOTLIN_PLUGIN_NAME.equals(dependency.getName())) { - return dependency.getVersion(); - } - } - - throw new IllegalStateException("Unable to get the kotlin plugin version"); + Class koltinPluginClass = project.getPlugins().getPlugin(KOTLIN_PLUGIN_ID).getClass(); + return koltinPluginClass.getPackage().getImplementationVersion().split("-")[0]; } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/KotlinTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/KotlinTest.groovy index afe06b03..abf67bb3 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/KotlinTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/KotlinTest.groovy @@ -39,7 +39,7 @@ class KotlinTest extends Specification implements GradleProjectTestTrait { def gradle = gradleProject(project: "kotlin", version: version) def server = ServerRunner.create(gradle.projectDir, "1.16.5") .withMod(gradle.getOutputFile("fabric-example-mod-0.0.1.jar")) - .downloadMod(ServerRunner.FABRIC_LANG_KOTLIN, "fabric-language-kotlin-1.7.1+kotlin.1.6.10.jar") + .downloadMod(ServerRunner.FABRIC_LANG_KOTLIN, "fabric-language-kotlin-1.7.3+kotlin.1.6.20.jar") when: def result = gradle.run(task: "build") diff --git a/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy b/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy index 0a95fd7d..351a6869 100644 --- a/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy @@ -34,7 +34,7 @@ class ServerRunner { "1.16.5": "https://github.com/FabricMC/fabric/releases/download/0.37.1%2B1.16/fabric-api-0.37.1+1.16.jar", "1.17.1": "https://github.com/FabricMC/fabric/releases/download/0.37.1%2B1.17/fabric-api-0.37.1+1.17.jar" ] - static final String FABRIC_LANG_KOTLIN = "https://maven.fabricmc.net/net/fabricmc/fabric-language-kotlin/1.7.1%2Bkotlin.1.6.10/fabric-language-kotlin-1.7.1%2Bkotlin.1.6.10.jar" + static final String FABRIC_LANG_KOTLIN = "https://maven.fabricmc.net/net/fabricmc/fabric-language-kotlin/1.7.3%2Bkotlin.1.6.20/fabric-language-kotlin-1.7.3%2Bkotlin.1.6.20.jar" final File serverDir final String minecraftVersion From 5f5dfcb3a7395910e0fda53e09503a43a7eb8de1 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Sun, 17 Apr 2022 21:15:58 +0100 Subject: [PATCH 20/27] Add refmap target namespace property. (#628) * Add refmap target namespace Property. * Fix build. --- .../net/fabricmc/loom/api/MixinExtensionAPI.java | 2 ++ .../build/mixin/AnnotationProcessorInvoker.java | 2 +- .../loom/extension/MixinExtensionApiImpl.java | 15 ++++++++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/api/MixinExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/MixinExtensionAPI.java index 78440ec2..2de8e8da 100644 --- a/src/main/java/net/fabricmc/loom/api/MixinExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/MixinExtensionAPI.java @@ -36,6 +36,8 @@ public interface MixinExtensionAPI { Property getDefaultRefmapName(); + Property getRefmapTargetNamespace(); + /** * Apply Mixin AP to sourceSet. * @param sourceSet the sourceSet that applies Mixin AP. diff --git a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java index f6315d37..9785f1f6 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java @@ -89,7 +89,7 @@ public abstract class AnnotationProcessorInvoker { put(Constants.MixinArguments.IN_MAP_FILE_NAMED_INTERMEDIARY, loom.getMappingsProvider().tinyMappings.toFile().getCanonicalPath()); put(Constants.MixinArguments.OUT_MAP_FILE_NAMED_INTERMEDIARY, MixinMappingsService.getMixinMappingFile(project, sourceSet).getCanonicalPath()); put(Constants.MixinArguments.OUT_REFMAP_FILE, getRefmapDestination(task, refmapName)); - put(Constants.MixinArguments.DEFAULT_OBFUSCATION_ENV, "named:intermediary"); + put(Constants.MixinArguments.DEFAULT_OBFUSCATION_ENV, "named:" + loom.getMixin().getRefmapTargetNamespace().get()); put(Constants.MixinArguments.QUIET, "true"); }}; diff --git a/src/main/java/net/fabricmc/loom/extension/MixinExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/MixinExtensionApiImpl.java index 58838e0e..74b70423 100644 --- a/src/main/java/net/fabricmc/loom/extension/MixinExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/MixinExtensionApiImpl.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -36,15 +36,21 @@ import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.util.PatternSet; import net.fabricmc.loom.api.MixinExtensionAPI; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; public abstract class MixinExtensionApiImpl implements MixinExtensionAPI { protected final Project project; protected final Property useMixinAp; + private final Property refmapTargetNamespace; public MixinExtensionApiImpl(Project project) { this.project = Objects.requireNonNull(project); this.useMixinAp = project.getObjects().property(Boolean.class) .convention(true); + + this.refmapTargetNamespace = project.getObjects().property(String.class) + .convention(MappingsNamespace.INTERMEDIARY.toString()); + this.refmapTargetNamespace.finalizeValueOnRead(); } protected final PatternSet add0(SourceSet sourceSet, String refmapName) { @@ -58,6 +64,13 @@ public abstract class MixinExtensionApiImpl implements MixinExtensionAPI { return useMixinAp; } + @Override + public Property getRefmapTargetNamespace() { + if (!getUseLegacyMixinAp().get()) throw new IllegalStateException("You need to set useLegacyMixinAp = true to configure Mixin annotation processor."); + + return refmapTargetNamespace; + } + @Override public void add(SourceSet sourceSet, String refmapName, Action action) { PatternSet pattern = add0(sourceSet, refmapName); From 29499fd0bd88f75618c0cfa1947824b739a5d0f5 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Tue, 19 Apr 2022 18:14:50 +0100 Subject: [PATCH 21/27] Workaround: https://github.com/gradle/gradle/issues/14727 --- .../loom/util/kotlin/KotlinClasspathService.java | 16 +++++++++------- .../loom/util/kotlin/KotlinPluginUtils.java | 5 +++++ .../util/kotlin/KotlinRemapperClassloader.java | 5 +---- .../kotlin/KotlinRemapperClassloaderTest.groovy | 9 +++++++-- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/util/kotlin/KotlinClasspathService.java b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinClasspathService.java index 0084fd47..05b9cada 100644 --- a/src/main/java/net/fabricmc/loom/util/kotlin/KotlinClasspathService.java +++ b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinClasspathService.java @@ -44,19 +44,21 @@ public record KotlinClasspathService(Set classpath, String version) impleme return null; } - return getOrCreate(project, KotlinPluginUtils.getKotlinPluginVersion(project)); + return getOrCreate(project, KotlinPluginUtils.getKotlinPluginVersion(project), KotlinPluginUtils.getKotlinMetadataVersion()); } - public static synchronized KotlinClasspathService getOrCreate(Project project, String kotlinVersion) { - final String id = "kotlinclasspath:" + kotlinVersion; + public static synchronized KotlinClasspathService getOrCreate(Project project, String kotlinVersion, String kotlinMetadataVersion) { + final String id = "kotlinclasspath:%s:%s".formatted(kotlinVersion, kotlinMetadataVersion); final SharedServiceManager sharedServiceManager = SharedServiceManager.get(project); - return sharedServiceManager.getOrCreateService(id, () -> create(project, kotlinVersion)); + return sharedServiceManager.getOrCreateService(id, () -> create(project, kotlinVersion, kotlinMetadataVersion)); } - private static KotlinClasspathService create(Project project, String kotlinVersion) { - // Create a detached config to resolve the koltin std lib for the provided version. + private static KotlinClasspathService create(Project project, String kotlinVersion, String kotlinMetadataVersion) { + // Create a detached config to resolve the kotlin std lib for the provided version. Configuration detachedConfiguration = project.getConfigurations().detachedConfiguration( - project.getDependencies().create("org.jetbrains.kotlin:kotlin-stdlib:" + kotlinVersion) + project.getDependencies().create("org.jetbrains.kotlin:kotlin-stdlib:" + kotlinVersion), + // Load kotlinx-metadata-jvm like this to work around: https://github.com/gradle/gradle/issues/14727 + project.getDependencies().create("org.jetbrains.kotlinx:kotlinx-metadata-jvm:" + kotlinMetadataVersion) ); Set classpath = detachedConfiguration.getFiles().stream() 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 6c6c570b..b0e7aaf8 100644 --- a/src/main/java/net/fabricmc/loom/util/kotlin/KotlinPluginUtils.java +++ b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinPluginUtils.java @@ -24,6 +24,7 @@ package net.fabricmc.loom.util.kotlin; +import kotlinx.metadata.jvm.KotlinClassMetadata; import org.gradle.api.Project; public class KotlinPluginUtils { @@ -37,4 +38,8 @@ public class KotlinPluginUtils { Class koltinPluginClass = project.getPlugins().getPlugin(KOTLIN_PLUGIN_ID).getClass(); return koltinPluginClass.getPackage().getImplementationVersion().split("-")[0]; } + + public static String getKotlinMetadataVersion() { + return KotlinClassMetadata.class.getPackage().getImplementationVersion(); + } } diff --git a/src/main/java/net/fabricmc/loom/util/kotlin/KotlinRemapperClassloader.java b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinRemapperClassloader.java index 1ad422c4..fb3ee13b 100644 --- a/src/main/java/net/fabricmc/loom/util/kotlin/KotlinRemapperClassloader.java +++ b/src/main/java/net/fabricmc/loom/util/kotlin/KotlinRemapperClassloader.java @@ -30,8 +30,6 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Stream; -import kotlinx.metadata.jvm.KotlinClassHeader; - import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.kotlin.remapping.KotlinMetadataTinyRemapperExtensionImpl; @@ -63,8 +61,7 @@ public class KotlinRemapperClassloader extends URLClassLoader { public static KotlinRemapperClassloader create(KotlinClasspath classpathProvider) { // Include the libraries that are not on the kotlin classpath. final Stream loomUrls = getClassUrls( - KotlinMetadataTinyRemapperExtensionImpl.class, // Loom - KotlinClassHeader.class // Kotlin metadata api + KotlinMetadataTinyRemapperExtensionImpl.class // Loom ); final URL[] urls = Stream.concat( diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/kotlin/KotlinRemapperClassloaderTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/kotlin/KotlinRemapperClassloaderTest.groovy index bf0bcecf..fc7df3fc 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/kotlin/KotlinRemapperClassloaderTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/kotlin/KotlinRemapperClassloaderTest.groovy @@ -25,6 +25,7 @@ package net.fabricmc.loom.test.unit.kotlin import net.fabricmc.loom.util.kotlin.KotlinClasspath +import net.fabricmc.loom.util.kotlin.KotlinPluginUtils import net.fabricmc.loom.util.kotlin.KotlinRemapperClassloader import net.fabricmc.tinyremapper.api.TrClass import net.fabricmc.tinyremapper.api.TrEnvironment @@ -35,7 +36,9 @@ import spock.lang.Specification class KotlinRemapperClassloaderTest extends Specification { private static String KOTLIN_VERSION = "1.6.10" + private static String KOTLIN_METADATA_VERSION = KotlinPluginUtils.kotlinMetadataVersion private static String KOTLIN_URL = "https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/${KOTLIN_VERSION}/kotlin-stdlib-${KOTLIN_VERSION}.jar" + private static String KOTLIN_METADATA_URL = "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-metadata-jvm/${KOTLIN_METADATA_VERSION}/kotlinx-metadata-jvm-${KOTLIN_METADATA_VERSION}.jar" def "Test Koltin Remapper Classloader"() { given: @@ -74,10 +77,12 @@ class KotlinRemapperClassloaderTest extends Specification { @Override Set classpath() { - def file = downloadFile(KOTLIN_URL, "kotlin-stdlib.jar") + def kotlin = downloadFile(KOTLIN_URL, "kotlin-stdlib.jar") + def metadata = downloadFile(KOTLIN_METADATA_URL, "kotlin-metadata.jar") return Set.of( - file.toURI().toURL() + kotlin.toURI().toURL(), + metadata.toURI().toURL() ) } } From f632dee2df924659116acd1183d6ffe4356d6067 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Tue, 19 Apr 2022 23:30:28 +0100 Subject: [PATCH 22/27] Mod provided javadoc (#627) --- .../loom/api/LoomGradleExtensionAPI.java | 7 + .../configuration/CompileConfiguration.java | 10 + .../InterfaceInjectionProcessor.java | 19 +- .../mods/ModJavadocProcessor.java | 218 ++++++++++++++++++ .../extension/LoomGradleExtensionApiImpl.java | 9 + .../loom/task/GenerateSourcesTask.java | 7 + .../net/fabricmc/loom/util/Constants.java | 5 + .../java/net/fabricmc/loom/util/ModUtils.java | 36 ++- .../test/integration/ModJavadocTest.groovy | 62 +++++ .../test/util/GradleProjectTestTrait.groovy | 6 +- .../projects/modJavadoc/build.gradle | 18 ++ .../dummyDependency/fabric.mod.json | 9 + .../modJavadoc/dummyDependency/javadoc.tiny | 7 + 13 files changed, 396 insertions(+), 17 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/configuration/mods/ModJavadocProcessor.java create mode 100644 src/test/groovy/net/fabricmc/loom/test/integration/ModJavadocTest.groovy create mode 100644 src/test/resources/projects/modJavadoc/build.gradle create mode 100644 src/test/resources/projects/modJavadoc/dummyDependency/fabric.mod.json create mode 100644 src/test/resources/projects/modJavadoc/dummyDependency/javadoc.tiny diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index 8fa3170d..68bb181d 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -140,6 +140,13 @@ public interface LoomGradleExtensionAPI { */ Property getEnableTransitiveAccessWideners(); + /** + * When true loom will apply mod provided javadoc from dependencies. + * + * @return the property controlling the mod provided javadoc + */ + Property getEnableModProvidedJavadoc(); + @ApiStatus.Experimental IntermediateMappingsProvider getIntermediateMappingsProvider(); diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index 673b50c7..68c05d1f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -45,6 +45,7 @@ import net.fabricmc.loom.build.mixin.ScalaApInvoker; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor; import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerJarProcessor; import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor; +import net.fabricmc.loom.configuration.mods.ModJavadocProcessor; import net.fabricmc.loom.configuration.processors.JarProcessorManager; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration; @@ -239,6 +240,15 @@ public final class CompileConfiguration { } } + if (extension.getEnableModProvidedJavadoc().get()) { + // This doesn't do any processing on the compiled jar, but it does have an effect on the generated sources. + final ModJavadocProcessor javadocProcessor = ModJavadocProcessor.create(project); + + if (javadocProcessor != null) { + extension.getGameJarProcessors().add(javadocProcessor); + } + } + JarProcessorManager processorManager = new JarProcessorManager(extension.getGameJarProcessors().get()); extension.setJarProcessorManager(processorManager); processorManager.setupProcessors(); 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 bad920f4..b29002fe 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java @@ -61,6 +61,7 @@ import net.fabricmc.loom.configuration.processors.JarProcessor; import net.fabricmc.loom.task.GenerateSourcesTask; import net.fabricmc.loom.util.Checksum; import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.ModUtils; import net.fabricmc.loom.util.Pair; import net.fabricmc.loom.util.TinyRemapperHelper; import net.fabricmc.loom.util.ZipUtils; @@ -270,23 +271,15 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource private record InjectedInterface(String modId, String className, String ifaceName) { /** - * Reads the injected interfaces contained in a mod jar, or returns null if there is none. + * Reads the injected interfaces contained in a mod jar, or returns empty if there is none. */ public static List fromModJar(Path modJarPath) { - final byte[] modJsonBytes; + final JsonObject jsonObject = ModUtils.getFabricModJson(modJarPath); - try { - modJsonBytes = ZipUtils.unpackNullable(modJarPath, "fabric.mod.json"); - } catch (IOException e) { - throw new RuntimeException("Failed to extract fabric.mod.json from " + modJarPath); - } - - if (modJsonBytes == null) { + if (jsonObject == null) { return Collections.emptyList(); } - final JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class); - return fromJson(jsonObject); } @@ -299,11 +292,11 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource final JsonObject custom = jsonObject.getAsJsonObject("custom"); - if (!custom.has("loom:injected_interfaces")) { + if (!custom.has(Constants.CustomModJsonKeys.INJECTED_INTERFACE)) { return Collections.emptyList(); } - final JsonObject addedIfaces = custom.getAsJsonObject("loom:injected_interfaces"); + final JsonObject addedIfaces = custom.getAsJsonObject(Constants.CustomModJsonKeys.INJECTED_INTERFACE); final List result = new ArrayList<>(); diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModJavadocProcessor.java b/src/main/java/net/fabricmc/loom/configuration/mods/ModJavadocProcessor.java new file mode 100644 index 00000000..e6f1d4c3 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModJavadocProcessor.java @@ -0,0 +1,218 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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.mods; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UncheckedIOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import com.google.gson.JsonObject; +import org.gradle.api.Project; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.configuration.RemappedConfigurationEntry; +import net.fabricmc.loom.configuration.processors.JarProcessor; +import net.fabricmc.loom.task.GenerateSourcesTask; +import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.ModUtils; +import net.fabricmc.loom.util.ZipUtils; +import net.fabricmc.mappingio.MappingReader; +import net.fabricmc.mappingio.tree.MappingTree; +import net.fabricmc.mappingio.tree.MemoryMappingTree; + +public final class ModJavadocProcessor implements JarProcessor, GenerateSourcesTask.MappingsProcessor { + private static final Logger LOGGER = LoggerFactory.getLogger(ModJavadocProcessor.class); + + private final List javadocs; + + private ModJavadocProcessor(List javadocs) { + this.javadocs = javadocs; + } + + @Nullable + public static ModJavadocProcessor create(Project project) { + final LoomGradleExtension extension = LoomGradleExtension.get(project); + final List javadocs = new ArrayList<>(); + + for (RemappedConfigurationEntry entry : Constants.MOD_COMPILE_ENTRIES) { + Set artifacts = extension.getLazyConfigurationProvider(entry.sourceConfiguration()) + .get() + .resolve(); + + for (File artifact : artifacts) { + if (!ModUtils.isMod(artifact.toPath())) { + continue; + } + + final ModJavadoc modJavadoc; + + try { + modJavadoc = ModJavadoc.fromModJar(artifact.toPath()); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read mod jar (%s)".formatted(artifact), e); + } + + if (modJavadoc != null) { + javadocs.add(modJavadoc); + } + } + } + + if (javadocs.isEmpty()) { + return null; + } + + return new ModJavadocProcessor(javadocs); + } + + @Override + public boolean transform(MemoryMappingTree mappings) { + for (ModJavadoc javadoc : javadocs) { + javadoc.apply(mappings); + } + + return true; + } + + @Override + public String getId() { + return "loom:interface_injection:" + javadocs.hashCode(); + } + + @Override + public void setup() { + } + + @Override + public void process(File file) { + // No need to actually process anything, we need to be a JarProcessor to ensure that the jar is cached correctly. + } + + public record ModJavadoc(String modId, MemoryMappingTree mappingTree) { + @Nullable + public static ModJavadoc fromModJar(Path path) throws IOException { + JsonObject jsonObject = ModUtils.getFabricModJson(path); + + if (jsonObject == null || !jsonObject.has("custom")) { + return null; + } + + final String modId = jsonObject.get("id").getAsString(); + final JsonObject custom = jsonObject.getAsJsonObject("custom"); + + if (!custom.has(Constants.CustomModJsonKeys.PROVIDED_JAVADOC)) { + return null; + } + + final String javaDocPath = custom.getAsJsonPrimitive(Constants.CustomModJsonKeys.PROVIDED_JAVADOC).getAsString(); + final byte[] data = ZipUtils.unpack(path, javaDocPath); + final MemoryMappingTree mappings = new MemoryMappingTree(); + + try (Reader reader = new InputStreamReader(new ByteArrayInputStream(data))) { + MappingReader.read(reader, mappings); + } + + if (!mappings.getSrcNamespace().equals(MappingsNamespace.INTERMEDIARY.toString())) { + throw new IllegalStateException("Javadoc provided by mod (%s) must be have an intermediary source namespace".formatted(modId)); + } + + if (!mappings.getDstNamespaces().isEmpty()) { + throw new IllegalStateException("Javadoc provided by mod (%s) must not contain any dst names".formatted(modId)); + } + + return new ModJavadoc(modId, mappings); + } + + public void apply(MemoryMappingTree target) { + if (!mappingTree.getSrcNamespace().equals(target.getSrcNamespace())) { + throw new IllegalStateException("Cannot apply mappings to differing namespaces. source: %s target: %s".formatted(mappingTree.getSrcNamespace(), target.getSrcNamespace())); + } + + for (MappingTree.ClassMapping sourceClass : mappingTree.getClasses()) { + final MappingTree.ClassMapping targetClass = target.getClass(sourceClass.getSrcName()); + + if (targetClass == null) { + LOGGER.warn("Could not find provided javadoc target class {} from mod {}", sourceClass.getSrcName(), modId); + continue; + } + + applyComment(sourceClass, targetClass); + + for (MappingTree.FieldMapping sourceField : sourceClass.getFields()) { + final MappingTree.FieldMapping targetField = targetClass.getField(sourceField.getSrcName(), sourceField.getSrcDesc()); + + if (targetField == null) { + LOGGER.warn("Could not find provided javadoc target field {}{} from mod {}", sourceField.getSrcName(), sourceField.getSrcDesc(), modId); + continue; + } + + applyComment(sourceField, targetField); + } + + for (MappingTree.MethodMapping sourceMethod : sourceClass.getMethods()) { + final MappingTree.MethodMapping targetMethod = targetClass.getMethod(sourceMethod.getSrcName(), sourceMethod.getSrcDesc()); + + if (targetMethod == null) { + LOGGER.warn("Could not find provided javadoc target method {}{} from mod {}", sourceMethod.getSrcName(), sourceMethod.getSrcDesc(), modId); + continue; + } + + applyComment(sourceMethod, targetMethod); + } + } + } + + private void applyComment(T source, T target) { + String sourceComment = source.getComment(); + + if (sourceComment == null) { + LOGGER.warn("Mod {} provided javadoc has mapping for {}, without comment", modId, source); + return; + } + + String targetComment = target.getComment(); + + if (targetComment == null) { + targetComment = ""; + } else { + targetComment += "\n"; + } + + targetComment += sourceComment; + target.setComment(targetComment); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index d5e8edba..ebf0f246 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -67,6 +67,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA protected final Property customManifest; protected final Property setupRemappedVariants; protected final Property transitiveAccessWideners; + protected final Property modProvidedJavadoc; protected final Property intermediary; protected final Property intermediateMappingsProvider; private final Property runtimeOnlyLog4j; @@ -98,6 +99,9 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA this.transitiveAccessWideners = project.getObjects().property(Boolean.class) .convention(true); this.transitiveAccessWideners.finalizeValueOnRead(); + this.modProvidedJavadoc = project.getObjects().property(Boolean.class) + .convention(true); + this.modProvidedJavadoc.finalizeValueOnRead(); this.intermediary = project.getObjects().property(String.class) .convention("https://maven.fabricmc.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar"); @@ -234,6 +238,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA return transitiveAccessWideners; } + @Override + public Property getEnableModProvidedJavadoc() { + return modProvidedJavadoc; + } + protected abstract Project getProject(); protected abstract LoomFiles getFiles(); diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index cb03b257..dbb81768 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -64,6 +64,7 @@ import net.fabricmc.loom.api.decompilers.LoomDecompiler; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerMappingsProcessor; import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor; +import net.fabricmc.loom.configuration.mods.ModJavadocProcessor; import net.fabricmc.loom.decompilers.LineNumberRemapper; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.FileSystemUtil; @@ -329,6 +330,12 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { mappingsProcessors.add(new InterfaceInjectionProcessor(getProject())); } + final ModJavadocProcessor javadocProcessor = ModJavadocProcessor.create(getProject()); + + if (javadocProcessor != null) { + mappingsProcessors.add(javadocProcessor); + } + if (mappingsProcessors.isEmpty()) { return inputMappings; } diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index cd90b25d..c6417c90 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -142,4 +142,9 @@ public class Constants { private TaskGroup() { } } + + public static final class CustomModJsonKeys { + public static final String INJECTED_INTERFACE = "loom:injected_interfaces"; + public static final String PROVIDED_JAVADOC = "loom:provided_javadoc"; + } } diff --git a/src/main/java/net/fabricmc/loom/util/ModUtils.java b/src/main/java/net/fabricmc/loom/util/ModUtils.java index d49b5188..aeb0c963 100644 --- a/src/main/java/net/fabricmc/loom/util/ModUtils.java +++ b/src/main/java/net/fabricmc/loom/util/ModUtils.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2016-2021 FabricMC + * Copyright (c) 2016-2022 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 @@ -25,12 +25,42 @@ package net.fabricmc.loom.util; import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; + +import com.google.gson.JsonObject; +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.LoomGradlePlugin; public final class ModUtils { private ModUtils() { } - public static boolean isMod(File input) { - return ZipUtils.contains(input.toPath(), "fabric.mod.json"); + public static boolean isMod(File file) { + return isMod(file.toPath()); + } + + public static boolean isMod(Path input) { + return ZipUtils.contains(input, "fabric.mod.json"); + } + + @Nullable + public static JsonObject getFabricModJson(Path path) { + final byte[] modJsonBytes; + + try { + modJsonBytes = ZipUtils.unpackNullable(path, "fabric.mod.json"); + } catch (IOException e) { + throw new UncheckedIOException("Failed to extract fabric.mod.json from " + path, e); + } + + if (modJsonBytes == null) { + return null; + } + + return LoomGradlePlugin.GSON.fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class); } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/ModJavadocTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/ModJavadocTest.groovy new file mode 100644 index 00000000..51397634 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/ModJavadocTest.groovy @@ -0,0 +1,62 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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 net.fabricmc.loom.test.util.GradleProjectTestTrait +import net.fabricmc.loom.util.ZipUtils +import spock.lang.Specification +import spock.lang.Unroll + +import java.nio.charset.StandardCharsets + +import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class ModJavadocTest extends Specification implements GradleProjectTestTrait { + @Unroll + def "mod javadoc (gradle #version)"() { + setup: + def gradle = gradleProject(project: "modJavadoc", version: version) + ZipUtils.pack(new File(gradle.projectDir, "dummyDependency").toPath(), new File(gradle.projectDir, "dummy.jar").toPath()) + + when: + def result = gradle.run(task: "genSources") + def blocks = getClassSource(gradle, "net/minecraft/block/Blocks.java") + + then: + result.task(":genSources").outcome == SUCCESS + blocks.contains("An example of a mod added class javadoc") + blocks.contains("An example of a mod added field javadoc") + blocks.contains("An example of a mod added method javadoc") + + where: + version << STANDARD_TEST_VERSIONS + } + + private static String getClassSource(GradleProject gradle, String classname) { + File sourcesJar = gradle.getGeneratedLocalSources("1.17.1/net.fabricmc.yarn.1_17_1.1.17.1+build.59-v2") + return new String(ZipUtils.unpack(sourcesJar.toPath(), classname), StandardCharsets.UTF_8) + } +} 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 d47ffda7..c5893a12 100644 --- a/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -234,6 +234,10 @@ trait GradleProjectTestTrait { return new File(getGradleHomeDir(), "caches/fabric-loom/${mappings}/minecraft-merged-named-sources.jar") } + File getGeneratedLocalSources(String mappings) { + return new File(getProjectDir(), ".gradle/loom-cache/${mappings}/minecraft-project-@-merged-named-sources.jar") + } + void buildSrc(String name) { useBuildSrc = true diff --git a/src/test/resources/projects/modJavadoc/build.gradle b/src/test/resources/projects/modJavadoc/build.gradle new file mode 100644 index 00000000..52f1fc39 --- /dev/null +++ b/src/test/resources/projects/modJavadoc/build.gradle @@ -0,0 +1,18 @@ +// This is used by a range of tests that append to this file before running the gradle tasks. +// Can be used for tests that require minimal custom setup +plugins { + id 'fabric-loom' + id 'maven-publish' +} + +archivesBaseName = "fabric-example-mod" +version = "1.0.0" +group = "com.example" + +dependencies { + minecraft "com.mojang:minecraft:1.17.1" + mappings "net.fabricmc:yarn:1.17.1+build.59:v2" + modImplementation "net.fabricmc:fabric-loader:0.11.6" + + modImplementation files("dummy.jar") +} \ No newline at end of file diff --git a/src/test/resources/projects/modJavadoc/dummyDependency/fabric.mod.json b/src/test/resources/projects/modJavadoc/dummyDependency/fabric.mod.json new file mode 100644 index 00000000..aeefb996 --- /dev/null +++ b/src/test/resources/projects/modJavadoc/dummyDependency/fabric.mod.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": 1, + "id": "dummy", + "version": "1", + "name": "Dummy Mod", + "custom": { + "loom:provided_javadoc": "javadoc.tiny" + } +} diff --git a/src/test/resources/projects/modJavadoc/dummyDependency/javadoc.tiny b/src/test/resources/projects/modJavadoc/dummyDependency/javadoc.tiny new file mode 100644 index 00000000..4a9212e5 --- /dev/null +++ b/src/test/resources/projects/modJavadoc/dummyDependency/javadoc.tiny @@ -0,0 +1,7 @@ +tiny 2 0 intermediary +c net/minecraft/class_2246 + c An example of a mod added class javadoc + f Lnet/minecraft/class_2248; field_10382 + c An example of a mod added field javadoc + m (I)Ljava/util/function/ToIntFunction; method_26107 + c An example of a mod added method javadoc \ No newline at end of file From b7cdb718d7bb32978e4ab9bd797f5989d9a1ce95 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 20 Apr 2022 18:37:11 +0100 Subject: [PATCH 23/27] Update to support 22w16a's arm64 macos natives, and tiny remapper. --- build.gradle | 2 +- .../providers/minecraft/LWJGLVersionOverride.java | 14 ++++++++++---- .../providers/minecraft/MinecraftVersionMeta.java | 11 +++++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 51cdccc9..d67a8ca1 100644 --- a/build.gradle +++ b/build.gradle @@ -85,7 +85,7 @@ dependencies { } // tinyfile management - implementation ('net.fabricmc:tiny-remapper:0.8.1') + implementation ('net.fabricmc:tiny-remapper:0.8.2') implementation 'net.fabricmc:access-widener:2.1.0' implementation 'net.fabricmc:mapping-io:0.2.1' diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/LWJGLVersionOverride.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/LWJGLVersionOverride.java index b4109f20..7cdcde94 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/LWJGLVersionOverride.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/LWJGLVersionOverride.java @@ -37,7 +37,7 @@ import net.fabricmc.loom.util.Architecture; import net.fabricmc.loom.util.OperatingSystem; public class LWJGLVersionOverride { - public static final String LWJGL_VERSION = "3.3.0"; + public static final String LWJGL_VERSION = "3.3.1"; @Nullable public static final String NATIVE_CLASSIFIER = getNativesClassifier(); @@ -66,9 +66,15 @@ public class LWJGLVersionOverride { return false; } - return versionMeta.libraries().stream() - .map(MinecraftVersionMeta.Library::name) - .anyMatch(s -> s.startsWith("org.lwjgl:lwjgl:3")); + boolean supportedLwjglVersion = versionMeta.libraries().stream() + .anyMatch(library -> library.name().startsWith("org.lwjgl:lwjgl:3")); + + boolean hasExistingNatives = versionMeta.libraries().stream() + .filter(library -> library.name().startsWith("org.lwjgl:lwjgl")) + .anyMatch(MinecraftVersionMeta.Library::hasNativesForOS); + + // Is LWJGL 3, and doesn't have any existing compatible LWGL natives. + return supportedLwjglVersion && !hasExistingNatives; } public static boolean forceOverride(Project project) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java index 81009f46..26612d49 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import net.fabricmc.loom.util.Architecture; import net.fabricmc.loom.util.OperatingSystem; @SuppressWarnings("unused") @@ -85,7 +86,7 @@ public record MinecraftVersionMeta( return false; } - if (natives.get(OperatingSystem.CURRENT_OS) == null) { + if (classifierForOS() == null) { return false; } @@ -93,7 +94,13 @@ public record MinecraftVersionMeta( } public Download classifierForOS() { - return downloads().classifier(natives.get(OperatingSystem.CURRENT_OS)); + String classifier = natives.get(OperatingSystem.CURRENT_OS); + + if (Architecture.CURRENT.isArm()) { + classifier += "-arm64"; + } + + return downloads().classifier(classifier); } public Download artifact() { From 1dfea9ef7e3df94a3c2714aec5dfb484fdb6623d Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Thu, 21 Apr 2022 20:52:11 +0100 Subject: [PATCH 24/27] Add a FileCollection to ModSettings --- src/main/java/net/fabricmc/loom/api/ModSettings.java | 6 ++++++ .../net/fabricmc/loom/util/gradle/SourceSetHelper.java | 9 +++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/api/ModSettings.java b/src/main/java/net/fabricmc/loom/api/ModSettings.java index 471f7d65..0e0eff0e 100644 --- a/src/main/java/net/fabricmc/loom/api/ModSettings.java +++ b/src/main/java/net/fabricmc/loom/api/ModSettings.java @@ -27,6 +27,7 @@ package net.fabricmc.loom.api; import javax.inject.Inject; import org.gradle.api.Named; +import org.gradle.api.file.FileCollection; import org.gradle.api.provider.ListProperty; import org.gradle.api.tasks.SourceSet; import org.jetbrains.annotations.ApiStatus; @@ -41,6 +42,11 @@ public abstract class ModSettings implements Named { */ public abstract ListProperty getModSourceSets(); + /** + * List of classpath directories, or jar files used to populate the `fabric.classPathGroups` Fabric Loader system property. + */ + public abstract FileCollection getModFiles(); + @Inject public ModSettings() { getModSourceSets().finalizeValueOnRead(); diff --git a/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java b/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java index 4630bde6..4950be4f 100644 --- a/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java +++ b/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java @@ -56,9 +56,14 @@ public final class SourceSetHelper { } public static List getClasspath(ModSettings modSettings, Project project) { - return modSettings.getModSourceSets().get().stream() + final List files = new ArrayList<>(); + + files.addAll(modSettings.getModSourceSets().get().stream() .flatMap(sourceSet -> getClasspath(sourceSet, project).stream()) - .toList(); + .toList()); + files.addAll(modSettings.getModFiles().getFiles()); + + return Collections.unmodifiableList(files); } public static List getClasspath(SourceSet sourceSet, Project project) { From 6ecaa609e8d7571f6a86e6ea0e24b11ef935307d Mon Sep 17 00:00:00 2001 From: KosmX Date: Thu, 21 Apr 2022 21:54:29 +0200 Subject: [PATCH 25/27] Create MixinMappingsService for every target MC version. (#631) * Create MixinMappingsService for every target MC version. * Use mappings identifier to identify MixinMappingsService. --- .../fabricmc/loom/task/service/MixinMappingsService.java | 7 ++++--- .../fabricmc/loom/task/service/TinyRemapperService.java | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/task/service/MixinMappingsService.java b/src/main/java/net/fabricmc/loom/task/service/MixinMappingsService.java index 37466a19..a1a7336f 100644 --- a/src/main/java/net/fabricmc/loom/task/service/MixinMappingsService.java +++ b/src/main/java/net/fabricmc/loom/task/service/MixinMappingsService.java @@ -31,6 +31,7 @@ import org.gradle.api.Project; import org.gradle.api.tasks.SourceSet; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.util.service.SharedService; import net.fabricmc.loom.util.service.SharedServiceManager; import net.fabricmc.tinyremapper.IMappingProvider; @@ -47,13 +48,13 @@ public final class MixinMappingsService implements SharedService { final LoomGradleExtension extension = LoomGradleExtension.get(project); File mixinMapping = new File(extension.getFiles().getProjectBuildCache(), "mixin-map-" + extension.getMappingsProvider().mappingsIdentifier() + "." + sourceSet.getName() + ".tiny"); - getService(SharedServiceManager.get(project)).mixinMappings.add(mixinMapping); + getService(SharedServiceManager.get(project), extension.getMappingsProvider()).mixinMappings.add(mixinMapping); return mixinMapping; } - static synchronized MixinMappingsService getService(SharedServiceManager sharedServiceManager) { - return sharedServiceManager.getOrCreateService("MixinMappings", () -> new MixinMappingsService(sharedServiceManager)); + static synchronized MixinMappingsService getService(SharedServiceManager sharedServiceManager, MappingsProviderImpl mappingsProvider) { + return sharedServiceManager.getOrCreateService("MixinMappings-" + mappingsProvider.mappingsIdentifier(), () -> new MixinMappingsService(sharedServiceManager)); } IMappingProvider getMappingProvider(String from, String to) { diff --git a/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java b/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java index 78f146bb..4e4c33ff 100644 --- a/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java +++ b/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java @@ -79,7 +79,7 @@ public class TinyRemapperService implements SharedService { mappings.add(MappingsService.createDefault(project, from, to).getMappingsProvider()); if (legacyMixin) { - mappings.add(MixinMappingsService.getService(SharedServiceManager.get(project)).getMappingProvider(from, to)); + mappings.add(MixinMappingsService.getService(SharedServiceManager.get(project), extension.getMappingsProvider()).getMappingProvider(from, to)); } return new TinyRemapperService(mappings, !legacyMixin, kotlinClasspathService); From 752c829eb06ba95bf58dfba65258723ab30306d0 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Sat, 30 Apr 2022 21:35:17 +0100 Subject: [PATCH 26/27] Fix ModSettings not using a ConfigurableFileCollection :) Need to add some tests for this ;) --- src/main/java/net/fabricmc/loom/api/ModSettings.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/api/ModSettings.java b/src/main/java/net/fabricmc/loom/api/ModSettings.java index 0e0eff0e..af31a2cc 100644 --- a/src/main/java/net/fabricmc/loom/api/ModSettings.java +++ b/src/main/java/net/fabricmc/loom/api/ModSettings.java @@ -27,7 +27,7 @@ package net.fabricmc.loom.api; import javax.inject.Inject; import org.gradle.api.Named; -import org.gradle.api.file.FileCollection; +import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.provider.ListProperty; import org.gradle.api.tasks.SourceSet; import org.jetbrains.annotations.ApiStatus; @@ -45,11 +45,12 @@ public abstract class ModSettings implements Named { /** * List of classpath directories, or jar files used to populate the `fabric.classPathGroups` Fabric Loader system property. */ - public abstract FileCollection getModFiles(); + public abstract ConfigurableFileCollection getModFiles(); @Inject public ModSettings() { getModSourceSets().finalizeValueOnRead(); + getModFiles().finalizeValueOnRead(); } /** From 8b3bfde825cb27df9c66dd78c7826bf5a785e676 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Sun, 1 May 2022 13:26:03 +0100 Subject: [PATCH 27/27] Add LibraryLocationLogger to help debug loom library version issues. --- .../net/fabricmc/loom/LoomGradlePlugin.java | 2 + .../loom/util/LibraryLocationLogger.java | 76 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 src/main/java/net/fabricmc/loom/util/LibraryLocationLogger.java diff --git a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java index 096c1b81..3544ffb1 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java +++ b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java @@ -45,6 +45,7 @@ import net.fabricmc.loom.decompilers.DecompilerConfiguration; import net.fabricmc.loom.extension.LoomFiles; import net.fabricmc.loom.extension.LoomGradleExtensionImpl; import net.fabricmc.loom.task.LoomTasks; +import net.fabricmc.loom.util.LibraryLocationLogger; public class LoomGradlePlugin implements BootstrappedPlugin { public static boolean refreshDeps; @@ -63,6 +64,7 @@ public class LoomGradlePlugin implements BootstrappedPlugin { public void apply(Project project) { project.getLogger().lifecycle("Fabric Loom: " + LOOM_VERSION); + LibraryLocationLogger.logLibraryVersions(); refreshDeps = project.getGradle().getStartParameter().isRefreshDependencies() || Boolean.getBoolean("loom.refresh"); diff --git a/src/main/java/net/fabricmc/loom/util/LibraryLocationLogger.java b/src/main/java/net/fabricmc/loom/util/LibraryLocationLogger.java new file mode 100644 index 00000000..2d27427f --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/LibraryLocationLogger.java @@ -0,0 +1,76 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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; + +import java.util.List; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Preconditions; +import com.google.gson.Gson; +import kotlinx.metadata.jvm.KotlinClassMetadata; +import org.apache.commons.io.FileUtils; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.commons.ClassRemapper; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.analysis.Analyzer; +import org.objectweb.asm.util.ASMifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * It's quite common for other plugins to shade these libraries, thus the wrong version is used. + * This file logs out the version + location of each library as a debugging aid. + * + *

gradlew buildEnvironment is a useful command to run alongside this. + */ +public final class LibraryLocationLogger { + private static final List> libraryClasses = List.of( + KotlinClassMetadata.class, + ClassVisitor.class, + Analyzer.class, + ClassRemapper.class, + ClassNode.class, + ASMifier.class, + ObjectMapper.class, + Gson.class, + Preconditions.class, + FileUtils.class + ); + + private static final Logger LOGGER = LoggerFactory.getLogger(LibraryLocationLogger.class); + + public static void logLibraryVersions() { + for (Class clazz : libraryClasses) { + LOGGER.info("({}) with version ({}) was loaded from ({})", + clazz.getName(), + clazz.getPackage().getImplementationVersion(), + clazz.getProtectionDomain().getCodeSource().getLocation().getPath() + ); + } + } + + private LibraryLocationLogger() { + } +}