From e561cca19ac9057df3fc25bcb60f808c93a301ae Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Thu, 4 Aug 2022 08:56:37 +0100 Subject: [PATCH] Split mod dependencies into client/common as required. --- build.gradle | 2 +- .../loom/api/LoomGradleExtensionAPI.java | 12 +- .../net/fabricmc/loom/api/ModSettings.java | 1 - .../loom/api/RemapConfigurationSettings.java | 24 +- .../configuration/CompileConfiguration.java | 27 +-- .../configuration/LoomDependencyManager.java | 8 +- .../loom/configuration/MavenPublication.java | 7 +- .../configuration/RemapConfigurations.java | 25 +- .../mods/AccessWidenerUtils.java | 77 ++++++ .../loom/configuration/mods/JarSplitter.java | 112 ++++++++- .../mods/ModConfigurationRemapper.java | 119 ++++++---- .../loom/configuration/mods/ModProcessor.java | 123 ++++------ .../mods/dependency/LocalMavenHelper.java | 119 ++++++++++ .../mods/dependency/ModDependency.java | 103 ++++++++ .../mods/dependency/ModDependencyFactory.java | 86 +++++++ .../mods/dependency/SimpleModDependency.java | 62 +++++ .../mods/dependency/SplitModDependency.java | 138 +++++++++++ .../ModJavadocProcessor.java | 3 +- .../dependency/ModDependencyInfo.java | 221 ------------------ .../processors/dependency/RemapData.java | 30 --- .../extension/LoomGradleExtensionApiImpl.java | 10 +- .../extension/LoomGradleExtensionImpl.java | 2 +- .../mods => extension}/ModVersionParser.java | 2 +- .../loom/task/GenerateSourcesTask.java | 2 +- .../net/fabricmc/loom/task/LoomTasks.java | 5 +- .../loom/task/RemapTaskConfiguration.java | 5 +- .../fabricmc/loom/util/AttributeHelper.java | 61 +++++ .../fabricmc/loom/util/SourceRemapper.java | 9 +- .../fabricmc/loom/util/download/Download.java | 54 ++--- .../{GradleSupport.java => GradleUtils.java} | 23 +- .../loom/test/LoomTestConstants.groovy | 4 +- .../test/integration/FabricAPITest.groovy | 10 +- .../IdeaClasspathModificationsTest.groovy | 4 +- .../loom/test/unit/JarSplitterTest.groovy | 47 +++- .../loom/test/util/ServerRunner.groovy | 2 +- src/test/resources/patches/fabric_api.patch | 17 +- .../projects/multiproject/build.gradle | 4 - 37 files changed, 1045 insertions(+), 515 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/configuration/mods/AccessWidenerUtils.java create mode 100644 src/main/java/net/fabricmc/loom/configuration/mods/dependency/LocalMavenHelper.java create mode 100644 src/main/java/net/fabricmc/loom/configuration/mods/dependency/ModDependency.java create mode 100644 src/main/java/net/fabricmc/loom/configuration/mods/dependency/ModDependencyFactory.java create mode 100644 src/main/java/net/fabricmc/loom/configuration/mods/dependency/SimpleModDependency.java create mode 100644 src/main/java/net/fabricmc/loom/configuration/mods/dependency/SplitModDependency.java rename src/main/java/net/fabricmc/loom/configuration/{mods => processors}/ModJavadocProcessor.java (98%) delete mode 100644 src/main/java/net/fabricmc/loom/configuration/processors/dependency/ModDependencyInfo.java delete mode 100644 src/main/java/net/fabricmc/loom/configuration/processors/dependency/RemapData.java rename src/main/java/net/fabricmc/loom/{configuration/mods => extension}/ModVersionParser.java (98%) create mode 100644 src/main/java/net/fabricmc/loom/util/AttributeHelper.java rename src/main/java/net/fabricmc/loom/util/gradle/{GradleSupport.java => GradleUtils.java} (72%) diff --git a/build.gradle b/build.gradle index 11a683b5..ff96a6f3 100644 --- a/build.gradle +++ b/build.gradle @@ -188,7 +188,7 @@ jacocoTestReport { } test { - maxHeapSize = "1536m" + maxHeapSize = "2560m" useJUnitPlatform() // Forward system prop onto tests. diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index e0f1005f..82bb2341 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -82,16 +82,12 @@ public interface LoomGradleExtensionAPI { * 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 NamedDomainObjectList getRemapConfigurations(); - @ApiStatus.Experimental RemapConfigurationSettings addRemapConfiguration(String name, Action action); void createRemapConfigurations(SourceSet sourceSet); @@ -173,29 +169,25 @@ public interface LoomGradleExtensionAPI { */ Property getIntermediaryUrl(); - @ApiStatus.Experimental Property getMinecraftJarConfiguration(); - @ApiStatus.Experimental default void serverOnlyMinecraftJar() { getMinecraftJarConfiguration().set(MinecraftJarConfiguration.SERVER_ONLY); } - @ApiStatus.Experimental default void clientOnlyMinecraftJar() { getMinecraftJarConfiguration().set(MinecraftJarConfiguration.CLIENT_ONLY); } - @ApiStatus.Experimental default void splitMinecraftJar() { getMinecraftJarConfiguration().set(MinecraftJarConfiguration.SPLIT); } - @ApiStatus.Experimental void splitEnvironmentSourceSets(); - @ApiStatus.Experimental boolean areEnvironmentSourceSetsSplit(); Property getRuntimeOnlyLog4j(); + + Property getSplitModDependencies(); } diff --git a/src/main/java/net/fabricmc/loom/api/ModSettings.java b/src/main/java/net/fabricmc/loom/api/ModSettings.java index f52546ca..45d246be 100644 --- a/src/main/java/net/fabricmc/loom/api/ModSettings.java +++ b/src/main/java/net/fabricmc/loom/api/ModSettings.java @@ -41,7 +41,6 @@ import net.fabricmc.loom.util.gradle.SourceSetReference; /** * 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, or jar files used to populate the `fabric.classPathGroups` Fabric Loader system property. diff --git a/src/main/java/net/fabricmc/loom/api/RemapConfigurationSettings.java b/src/main/java/net/fabricmc/loom/api/RemapConfigurationSettings.java index b23e589d..bdc7ecd1 100644 --- a/src/main/java/net/fabricmc/loom/api/RemapConfigurationSettings.java +++ b/src/main/java/net/fabricmc/loom/api/RemapConfigurationSettings.java @@ -54,7 +54,7 @@ public abstract class RemapConfigurationSettings implements Named { this.name = name; getTargetConfigurationName().finalizeValueOnRead(); - getClientTargetConfigurationName().finalizeValueOnRead(); + getClientSourceConfigurationName().finalizeValueOnRead(); getOnCompileClasspath().finalizeValueOnRead(); getOnRuntimeClasspath().finalizeValueOnRead(); getPublishingMode().convention(PublishingMode.NONE).finalizeValueOnRead(); @@ -74,9 +74,9 @@ public abstract class RemapConfigurationSettings implements Named { * Optional, only used when split sourcesets are enabled. * When not present client only entries should go onto the target configuration. * - * @return The client target configuration name + * @return The client source configuration name */ - public abstract Property getClientTargetConfigurationName(); + public abstract Property getClientSourceConfigurationName(); /** * @return True if this configuration's artifacts should be exposed for compile operations. @@ -119,6 +119,12 @@ public abstract class RemapConfigurationSettings implements Named { return getName() + "Mapped"; } + @ApiStatus.Internal + @Internal + public final Provider getClientRemappedConfigurationName() { + return getClientSourceConfigurationName().map(s -> s + "Mapped"); + } + @ApiStatus.Internal @Internal public final NamedDomainObjectProvider getSourceConfiguration() { @@ -143,7 +149,17 @@ public abstract class RemapConfigurationSettings implements Named { return getProject().provider(() -> { boolean split = LoomGradleExtension.get(getProject()).getMinecraftJarConfiguration().get() == MinecraftJarConfiguration.SPLIT; Preconditions.checkArgument(split, "Cannot get client target configuration when project is not split"); - return getConfigurationByName(getClientTargetConfigurationName().get()).get(); + return getConfigurationByName(getClientSourceConfigurationName().get()).get(); + }); + } + + @ApiStatus.Internal + @Internal + public final Provider getClientRemappedConfiguration() { + return getProject().provider(() -> { + boolean split = LoomGradleExtension.get(getProject()).getMinecraftJarConfiguration().get() == MinecraftJarConfiguration.SPLIT; + Preconditions.checkArgument(split, "Cannot get client remapped configuration when project is not split"); + return getConfigurationByName(getClientRemappedConfigurationName().get()).get(); }); } diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index dccb5477..2e6386d2 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -49,8 +49,8 @@ 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.processors.ModJavadocProcessor; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; @@ -60,6 +60,7 @@ import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraft import net.fabricmc.loom.extension.MixinExtension; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.ExceptionUtil; +import net.fabricmc.loom.util.gradle.GradleUtils; import net.fabricmc.loom.util.gradle.SourceSetHelper; public final class CompileConfiguration { @@ -116,15 +117,15 @@ public final class CompileConfiguration { project.getDependencies().add(JavaPlugin.TEST_COMPILE_ONLY_CONFIGURATION_NAME, Constants.Dependencies.JETBRAINS_ANNOTATIONS + Constants.Dependencies.Versions.JETBRAINS_ANNOTATIONS); } - public static void configureCompile(Project p) { - LoomGradleExtension extension = LoomGradleExtension.get(p); + public static void configureCompile(Project project) { + LoomGradleExtension extension = LoomGradleExtension.get(project); - p.getTasks().named(JavaPlugin.JAVADOC_TASK_NAME, Javadoc.class).configure(javadoc -> { - final SourceSet main = SourceSetHelper.getMainSourceSet(p); + project.getTasks().named(JavaPlugin.JAVADOC_TASK_NAME, Javadoc.class).configure(javadoc -> { + final SourceSet main = SourceSetHelper.getMainSourceSet(project); javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath())); }); - p.afterEvaluate(project -> { + GradleUtils.afterSuccessfulEvaluation(project, () -> { MinecraftSourceSets.get(project).afterEvaluate(project); final boolean previousRefreshDeps = extension.refreshDeps(); @@ -156,20 +157,20 @@ public final class CompileConfiguration { configureDecompileTasks(project); }); - finalizedBy(p, "idea", "genIdeaWorkspace"); - finalizedBy(p, "eclipse", "genEclipseRuns"); - finalizedBy(p, "cleanEclipse", "cleanEclipseRuns"); + finalizedBy(project, "idea", "genIdeaWorkspace"); + finalizedBy(project, "eclipse", "genEclipseRuns"); + finalizedBy(project, "cleanEclipse", "cleanEclipseRuns"); // Add the "dev" jar to the "namedElements" configuration - p.artifacts(artifactHandler -> artifactHandler.add(Constants.Configurations.NAMED_ELEMENTS, p.getTasks().named("jar"))); + project.artifacts(artifactHandler -> artifactHandler.add(Constants.Configurations.NAMED_ELEMENTS, project.getTasks().named("jar"))); // Ensure that the encoding is set to UTF-8, no matter what the system default is // this fixes some edge cases with special characters not displaying correctly // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html - p.getTasks().withType(AbstractCopyTask.class).configureEach(abstractCopyTask -> abstractCopyTask.setFilteringCharset(StandardCharsets.UTF_8.name())); - p.getTasks().withType(JavaCompile.class).configureEach(javaCompile -> javaCompile.getOptions().setEncoding(StandardCharsets.UTF_8.name())); + project.getTasks().withType(AbstractCopyTask.class).configureEach(abstractCopyTask -> abstractCopyTask.setFilteringCharset(StandardCharsets.UTF_8.name())); + project.getTasks().withType(JavaCompile.class).configureEach(javaCompile -> javaCompile.getOptions().setEncoding(StandardCharsets.UTF_8.name())); - if (p.getPluginManager().hasPlugin("org.jetbrains.kotlin.kapt")) { + if (project.getPluginManager().hasPlugin("org.jetbrains.kotlin.kapt")) { // If loom is applied after kapt, then kapt will use the AP arguments too early for loom to pass the arguments we need for mixin. throw new IllegalArgumentException("fabric-loom must be applied BEFORE kapt in the plugins { } block."); } diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java index 804c32f8..22ce5a3c 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java @@ -25,8 +25,6 @@ package net.fabricmc.loom.configuration; import java.io.File; -import java.io.IOException; -import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -41,8 +39,8 @@ import org.gradle.api.artifacts.repositories.MavenArtifactRepository; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.LoomRepositoryPlugin; -import net.fabricmc.loom.configuration.mods.ModConfigurationRemapper; import net.fabricmc.loom.configuration.ide.idea.IdeaUtils; +import net.fabricmc.loom.configuration.mods.ModConfigurationRemapper; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.SourceRemapper; import net.fabricmc.loom.util.ZipUtils; @@ -102,8 +100,8 @@ public class LoomDependencyManager { } return LoomGradlePlugin.GSON.fromJson(new String(bytes, StandardCharsets.UTF_8), JsonObject.class); - } catch (IOException e) { - throw new UncheckedIOException("Failed to try and read installer json from", e); + } catch (Exception e) { + throw new RuntimeException("Failed to try and read installer json from " + file, e); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/MavenPublication.java b/src/main/java/net/fabricmc/loom/configuration/MavenPublication.java index 8604a73a..fb0baf54 100644 --- a/src/main/java/net/fabricmc/loom/configuration/MavenPublication.java +++ b/src/main/java/net/fabricmc/loom/configuration/MavenPublication.java @@ -47,6 +47,7 @@ import org.gradle.api.publish.PublishingExtension; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.util.DeprecationHelper; import net.fabricmc.loom.util.GroovyXmlUtil; +import net.fabricmc.loom.util.gradle.GradleUtils; public final class MavenPublication { // ImmutableMap is needed since it guarantees ordering @@ -61,14 +62,14 @@ public final class MavenPublication { } public static void configure(Project project) { - project.afterEvaluate((p) -> { + GradleUtils.afterSuccessfulEvaluation(project, () -> { AtomicBoolean reportedDeprecation = new AtomicBoolean(false); CONFIGURATION_TO_SCOPE.forEach((configurationName, scope) -> { - Configuration config = p.getConfigurations().getByName(configurationName); + Configuration config = project.getConfigurations().getByName(configurationName); // add modsCompile to maven-publish - PublishingExtension mavenPublish = p.getExtensions().findByType(PublishingExtension.class); + PublishingExtension mavenPublish = project.getExtensions().findByType(PublishingExtension.class); if (mavenPublish != null) { processEntry(project, scope, config, mavenPublish, reportedDeprecation); diff --git a/src/main/java/net/fabricmc/loom/configuration/RemapConfigurations.java b/src/main/java/net/fabricmc/loom/configuration/RemapConfigurations.java index 149eb345..7a556937 100644 --- a/src/main/java/net/fabricmc/loom/configuration/RemapConfigurations.java +++ b/src/main/java/net/fabricmc/loom/configuration/RemapConfigurations.java @@ -76,11 +76,34 @@ public final class RemapConfigurations { // Apply the client target names to the main configurations for (ConfigurationOption option : getValidOptions(mainSourceSet)) { configurations.getByName(option.name(mainSourceSet), settings -> { - settings.getClientTargetConfigurationName().convention(option.targetName(clientSourceSet)); + String name = option.targetName(clientSourceSet); + + if (name == null) { + return; + } + + settings.getClientSourceConfigurationName().set(name); + createClientMappedConfiguration(project, settings, clientSourceSet); }); } } + private static void createClientMappedConfiguration(Project project, RemapConfigurationSettings settings, SourceSet clientSourceSet) { + final Configuration remappedConfiguration = project.getConfigurations().create(settings.getClientRemappedConfigurationName().get()); + // Don't get transitive deps of already remapped mods + remappedConfiguration.setTransitive(false); + + if (settings.getOnCompileClasspath().get()) { + extendsFrom(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED, remappedConfiguration, project); + + extendsFrom(clientSourceSet.getCompileClasspathConfigurationName(), remappedConfiguration, project); + } + + if (settings.getOnRuntimeClasspath().get()) { + extendsFrom(clientSourceSet.getRuntimeClasspathConfigurationName(), remappedConfiguration, project); + } + } + public static void applyToProject(Project project, RemapConfigurationSettings settings) { // No point bothering to make it lazily, gradle realises configurations right away. // diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/AccessWidenerUtils.java b/src/main/java/net/fabricmc/loom/configuration/mods/AccessWidenerUtils.java new file mode 100644 index 00000000..3f463919 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/mods/AccessWidenerUtils.java @@ -0,0 +1,77 @@ +/* + * 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.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; + +import com.google.gson.JsonObject; +import org.objectweb.asm.commons.Remapper; + +import net.fabricmc.accesswidener.AccessWidenerReader; +import net.fabricmc.accesswidener.AccessWidenerRemapper; +import net.fabricmc.accesswidener.AccessWidenerWriter; +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.util.ZipUtils; + +public class AccessWidenerUtils { + /** + * Remap a mods access widener from intermediary to named, so that loader can apply it in our dev-env. + */ + public static byte[] remapAccessWidener(byte[] input, Remapper remapper) { + int version = AccessWidenerReader.readVersion(input); + + AccessWidenerWriter writer = new AccessWidenerWriter(version); + AccessWidenerRemapper awRemapper = new AccessWidenerRemapper( + writer, + remapper, + MappingsNamespace.INTERMEDIARY.toString(), + MappingsNamespace.NAMED.toString() + ); + AccessWidenerReader reader = new AccessWidenerReader(awRemapper); + reader.read(input); + return writer.write(); + } + + public static AccessWidenerData readAccessWidenerData(Path inputJar) throws IOException { + byte[] modJsonBytes = ZipUtils.unpack(inputJar, "fabric.mod.json"); + JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class); + + if (!jsonObject.has("accessWidener")) { + return null; + } + + String accessWidenerPath = jsonObject.get("accessWidener").getAsString(); + byte[] accessWidener = ZipUtils.unpack(inputJar, accessWidenerPath); + AccessWidenerReader.Header header = AccessWidenerReader.readHeader(accessWidener); + + return new AccessWidenerData(accessWidenerPath, header, accessWidener); + } + + public record AccessWidenerData(String path, AccessWidenerReader.Header header, byte[] content) { + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/JarSplitter.java b/src/main/java/net/fabricmc/loom/configuration/mods/JarSplitter.java index 91bc25bd..57348e0a 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/JarSplitter.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/JarSplitter.java @@ -26,17 +26,23 @@ package net.fabricmc.loom.configuration.mods; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.stream.Stream; +import org.jetbrains.annotations.Nullable; + import net.fabricmc.loom.task.AbstractRemapJarTask; import net.fabricmc.loom.util.FileSystemUtil; @@ -47,15 +53,83 @@ public class JarSplitter { this.inputJar = inputJar; } - public boolean split(Path commonOutputJar, Path clientOutputJar) throws IOException { + @Nullable + public Target analyseTarget() { try (FileSystemUtil.Delegate input = FileSystemUtil.getJarFileSystem(inputJar)) { final Manifest manifest = input.fromInputStream(Manifest::new, AbstractRemapJarTask.MANIFEST_PATH); + + if (!Boolean.parseBoolean(manifest.getMainAttributes().getValue(AbstractRemapJarTask.MANIFEST_SPLIT_ENV_KEY))) { + // Jar was not built with splitting enabled. + return null; + } + + final HashSet clientEntries = new HashSet<>(readClientEntries(manifest)); + + if (clientEntries.isEmpty()) { + // No client entries. + return Target.COMMON_ONLY; + } + + final List entries = new LinkedList<>(); + + // Must collect all the input entries to see if this might be a client only jar. + try (Stream walk = Files.walk(input.get().getPath("/"))) { + final Iterator iterator = walk.iterator(); + + while (iterator.hasNext()) { + final Path entry = iterator.next(); + + if (!Files.isRegularFile(entry)) { + continue; + } + + final Path relativePath = input.get().getPath("/").relativize(entry); + + if (relativePath.startsWith("META-INF")) { + if (isSignatureData(relativePath)) { + // Ignore any signature data + continue; + } + + if (relativePath.endsWith("MANIFEST.MF")) { + // Ignore the manifest + continue; + } + } + + entries.add(relativePath.toString()); + } + } + + for (String entry : entries) { + if (!clientEntries.contains(entry)) { + // Found a common entry, we need to split,. + return Target.SPLIT; + } + } + + // All input entries are client only entries. + return Target.CLIENT_ONLY; + } catch (IOException e) { + throw new UncheckedIOException("Failed to read jar", e); + } + } + + public boolean split(Path commonOutputJar, Path clientOutputJar) throws IOException { + Files.deleteIfExists(commonOutputJar); + Files.deleteIfExists(clientOutputJar); + + try (FileSystemUtil.Delegate input = FileSystemUtil.getJarFileSystem(inputJar)) { + final Manifest manifest = input.fromInputStream(Manifest::new, AbstractRemapJarTask.MANIFEST_PATH); + + if (!Boolean.parseBoolean(manifest.getMainAttributes().getValue(AbstractRemapJarTask.MANIFEST_SPLIT_ENV_KEY))) { + throw new UnsupportedOperationException("Cannot split jar that has not been built with a split env"); + } + final List clientEntries = readClientEntries(manifest); if (clientEntries.isEmpty()) { - // No client entries, just copy the input jar - Files.copy(inputJar, commonOutputJar); - return false; + throw new IllegalStateException("Expected to split jar with no client entries"); } try (FileSystemUtil.Delegate commonOutput = FileSystemUtil.getJarFileSystem(commonOutputJar, true); @@ -126,15 +200,10 @@ public class JarSplitter { private List readClientEntries(Manifest manifest) { final Attributes attributes = manifest.getMainAttributes(); - final String splitEnvValue = attributes.getValue(AbstractRemapJarTask.MANIFEST_SPLIT_ENV_KEY); final String clientEntriesValue = attributes.getValue(AbstractRemapJarTask.MANIFEST_CLIENT_ENTRIES_KEY); - if (splitEnvValue == null || !splitEnvValue.equals("true")) { - throw new UnsupportedOperationException("Cannot split jar that has not been built with a split env"); - } - - if (clientEntriesValue == null) { - throw new IllegalStateException("Split jar does not contain any client only classes"); + if (clientEntriesValue == null || clientEntriesValue.isBlank()) { + return Collections.emptyList(); } return Arrays.stream(clientEntriesValue.split(";")).toList(); @@ -175,4 +244,25 @@ public class JarSplitter { Files.write(path, bytes); } + + public enum Target { + COMMON_ONLY(true, false), + CLIENT_ONLY(false, true), + SPLIT(true, true); + + final boolean common, client; + + Target(boolean common, boolean client) { + this.common = common; + this.client = client; + } + + public boolean common() { + return common; + } + + public boolean client() { + return client; + } + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModConfigurationRemapper.java b/src/main/java/net/fabricmc/loom/configuration/mods/ModConfigurationRemapper.java index a33e41fe..3f9f8bce 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModConfigurationRemapper.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModConfigurationRemapper.java @@ -26,12 +26,13 @@ package net.fabricmc.loom.configuration.mods; import java.io.File; import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; -import com.google.common.io.Files; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.FileCollectionDependency; @@ -50,8 +51,8 @@ import org.jetbrains.annotations.Nullable; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.RemapConfigurationSettings; -import net.fabricmc.loom.configuration.processors.dependency.ModDependencyInfo; -import net.fabricmc.loom.configuration.processors.dependency.RemapData; +import net.fabricmc.loom.configuration.mods.dependency.ModDependency; +import net.fabricmc.loom.configuration.mods.dependency.ModDependencyFactory; import net.fabricmc.loom.util.Checksum; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.ModUtils; @@ -67,9 +68,6 @@ public class ModConfigurationRemapper { public static void supplyModConfigurations(Project project, String mappingsSuffix, LoomGradleExtension extension, SourceRemapper sourceRemapper) { final DependencyHandler dependencies = project.getDependencies(); - final File modStore = extension.getFiles().getRemappedModCache(); - final RemapData remapData = new RemapData(mappingsSuffix, modStore); - for (RemapConfigurationSettings entry : extension.getRemapConfigurations()) { entry.getRemappedConfiguration().configure(remappedConfig -> { /* @@ -79,15 +77,15 @@ public class ModConfigurationRemapper { */ final Configuration sourceConfig = entry.getSourceConfiguration().get(); final Configuration targetConfig = entry.getTargetConfiguration().get(); - final boolean hasClientTarget = entry.getClientTargetConfigurationName().isPresent(); + final boolean hasClientTarget = entry.getClientSourceConfigurationName().isPresent(); Configuration clientRemappedConfig = null; if (hasClientTarget) { - clientRemappedConfig = entry.getClientTargetConfiguration().get(); + clientRemappedConfig = entry.getClientRemappedConfiguration().get(); } - final List modDependencies = new ArrayList<>(); + final List modDependencies = new ArrayList<>(); for (ArtifactRef artifact : resolveArtifacts(project, sourceConfig)) { if (!ModUtils.isMod(artifact.path())) { @@ -95,47 +93,36 @@ public class ModConfigurationRemapper { continue; } - final ModDependencyInfo info = new ModDependencyInfo(artifact, remappedConfig, clientRemappedConfig, remapData); - - if (extension.refreshDeps()) { - info.forceRemap(); - } - - if (artifact.sources() != null) { - scheduleSourcesRemapping(project, sourceRemapper, artifact.sources().toFile(), info.getRemappedOutput("sources")); - } - - modDependencies.add(info); + final ModDependency modDependency = ModDependencyFactory.create(artifact, remappedConfig, clientRemappedConfig, mappingsSuffix, project); + scheduleSourcesRemapping(project, sourceRemapper, modDependency); + modDependencies.add(modDependency); } if (modDependencies.isEmpty()) { + // Nothing else to do return; } - try { - new ModProcessor(project, sourceConfig).processMods(modDependencies); - } catch (IOException e) { - // Failed to remap, lets clean up to ensure we try again next time - modDependencies.forEach(info -> info.getRemappedOutput().delete()); - throw new RuntimeException("Failed to remap mods", e); + final boolean refreshDeps = LoomGradleExtension.get(project).refreshDeps(); + final List toRemap = modDependencies.stream() + .filter(dependency -> refreshDeps || dependency.isCacheInvalid(project, null)) + .toList(); + + if (!toRemap.isEmpty()) { + try { + new ModProcessor(project, sourceConfig).processMods(toRemap); + } catch (IOException e) { + throw new UncheckedIOException("Failed to remap mods", e); + } } // Add all of the remapped mods onto the config - for (ModDependencyInfo info : modDependencies) { - project.getDependencies().add(info.targetConfig.getName(), info.getRemappedNotation()); + for (ModDependency info : modDependencies) { + info.applyToProject(project); + createConstraints(info.getInputArtifact(), targetConfig, sourceConfig, dependencies); - if (info.getArtifact() instanceof ArtifactRef.ResolvedArtifactRef mavenArtifact) { - final String dependencyCoordinate = "%s:%s".formatted(mavenArtifact.group(), mavenArtifact.name()); - - // Prevent adding the same un-remapped dependency to the target configuration. - targetConfig.getDependencyConstraints().add(dependencies.getConstraints().create(dependencyCoordinate, constraint -> { - constraint.because("configuration (%s) already contains the remapped module from configuration (%s)".formatted( - targetConfig.getName(), - sourceConfig.getName() - )); - - constraint.version(MutableVersionConstraint::rejectAll); - })); + if (clientRemappedConfig != null) { + createConstraints(info.getInputArtifact(), entry.getClientTargetConfiguration().get(), sourceConfig, dependencies); } } @@ -147,6 +134,27 @@ public class ModConfigurationRemapper { } } + private static void createConstraints(ArtifactRef artifact, Configuration targetConfig, Configuration sourceConfig, DependencyHandler dependencies) { + if (true) { + // Disabled due to the gradle module metadata causing issues. Try the MavenProject test to reproduce issue. + return; + } + + if (artifact instanceof ArtifactRef.ResolvedArtifactRef mavenArtifact) { + final String dependencyCoordinate = "%s:%s".formatted(mavenArtifact.group(), mavenArtifact.name()); + + // Prevent adding the same un-remapped dependency to the target configuration. + targetConfig.getDependencyConstraints().add(dependencies.getConstraints().create(dependencyCoordinate, constraint -> { + constraint.because("configuration (%s) already contains the remapped module from configuration (%s)".formatted( + targetConfig.getName(), + sourceConfig.getName() + )); + + constraint.version(MutableVersionConstraint::rejectAll); + })); + } + } + private static List resolveArtifacts(Project project, Configuration configuration) { final List artifacts = new ArrayList<>(); @@ -162,9 +170,8 @@ public class ModConfigurationRemapper { final FileCollection files = dependency.getFiles(); for (File artifact : files) { - final String name = Files.getNameWithoutExtension(artifact.getAbsolutePath()); + final String name = getNameWithoutExtension(artifact.toPath()); final String version = replaceIfNullOrEmpty(dependency.getVersion(), () -> Checksum.truncatedSha256(artifact)); - artifacts.add(new ArtifactRef.FileArtifactRef(artifact.toPath(), group, name, version)); } } @@ -172,6 +179,12 @@ public class ModConfigurationRemapper { return artifacts; } + private static String getNameWithoutExtension(Path file) { + final String fileName = file.getFileName().toString(); + final int dotIndex = fileName.lastIndexOf('.'); + return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex); + } + @Nullable public static Path findSources(Project project, ResolvedArtifact artifact) { final DependencyHandler dependencies = project.getDependencies(); @@ -191,15 +204,27 @@ public class ModConfigurationRemapper { return null; } - private static void scheduleSourcesRemapping(Project project, SourceRemapper sourceRemapper, File input, File output) { + private static void scheduleSourcesRemapping(Project project, SourceRemapper sourceRemapper, ModDependency dependency) { if (OperatingSystem.isCIBuild()) { return; } - if (!output.exists() || input.lastModified() <= 0 || input.lastModified() > output.lastModified() || LoomGradleExtension.get(project).refreshDeps()) { - sourceRemapper.scheduleRemapSources(input, output, false, true); // Depenedency sources are used in ide only so don't need to be reproducable - } else { - project.getLogger().info(output.getName() + " is up to date with " + input.getName()); + final Path sourcesInput = dependency.getInputArtifact().sources(); + + if (sourcesInput == null || Files.notExists(sourcesInput)) { + return; + } + + if (dependency.isCacheInvalid(project, "sources")) { + final Path output = dependency.getWorkingFile("sources"); + + sourceRemapper.scheduleRemapSources(sourcesInput.toFile(), output.toFile(), false, true, () -> { + try { + dependency.copyToCache(project, output, "sources"); + } catch (IOException e) { + throw new UncheckedIOException("Failed to apply sources to local cache for: " + dependency, e); + } + }); } } 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 3bccaa94..a5998ff8 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java @@ -31,7 +31,6 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -41,18 +40,15 @@ import java.util.jar.Manifest; import com.google.gson.JsonObject; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; -import org.objectweb.asm.commons.Remapper; -import net.fabricmc.accesswidener.AccessWidenerReader; -import net.fabricmc.accesswidener.AccessWidenerRemapper; -import net.fabricmc.accesswidener.AccessWidenerWriter; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.RemapConfigurationSettings; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; -import net.fabricmc.loom.configuration.processors.dependency.ModDependencyInfo; +import net.fabricmc.loom.configuration.mods.dependency.ModDependency; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.task.RemapJarTask; import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.Pair; import net.fabricmc.loom.util.TinyRemapperHelper; import net.fabricmc.loom.util.ZipUtils; import net.fabricmc.loom.util.kotlin.KotlinClasspathService; @@ -74,75 +70,28 @@ public class ModProcessor { this.sourceConfiguration = sourceConfiguration; } - public void processMods(List processList) throws IOException { - ArrayList remapList = new ArrayList<>(); - - for (ModDependencyInfo info : processList) { - if (info.requiresRemapping()) { - project.getLogger().debug("{} requires remapping", info.getInputFile()); - Files.deleteIfExists(info.getRemappedOutput().toPath()); - - remapList.add(info); - } - } - - if (remapList.isEmpty()) { - project.getLogger().debug("No mods to remap, skipping"); - return; - } - + public void processMods(List remapList) throws IOException { try { project.getLogger().lifecycle(":remapping {} mods from {}", remapList.size(), sourceConfiguration.getName()); remapJars(remapList); } catch (Exception e) { - project.getLogger().error(String.format(Locale.ENGLISH, "Failed to remap %d mods", remapList.size()), e); - - for (ModDependencyInfo info : remapList) { - Files.deleteIfExists(info.getRemappedOutput().toPath()); - } - - throw e; - } - - // Check all the mods we expect exist - for (ModDependencyInfo info : processList) { - if (!info.getRemappedOutput().exists()) { - throw new RuntimeException("Failed to find remapped mod: " + info); - } + throw new RuntimeException(String.format(Locale.ENGLISH, "Failed to remap %d mods", remapList.size()), e); } } - private void stripNestedJars(File file) { + private void stripNestedJars(Path path) { // Strip out all contained jar info as we dont want loader to try and load the jars contained in dev. try { - ZipUtils.transformJson(JsonObject.class, file.toPath(), Map.of("fabric.mod.json", json -> { + ZipUtils.transformJson(JsonObject.class, path, Map.of("fabric.mod.json", json -> { json.remove("jars"); return json; })); } catch (IOException e) { - throw new UncheckedIOException("Failed to strip nested jars from %s".formatted(file), e); + throw new UncheckedIOException("Failed to strip nested jars from %s".formatted(path), e); } } - /** - * Remap another mod's access widener from intermediary to named, so that loader can apply it in our dev-env. - */ - private byte[] remapAccessWidener(byte[] input, Remapper remapper) { - int version = AccessWidenerReader.readVersion(input); - - AccessWidenerWriter writer = new AccessWidenerWriter(version); - AccessWidenerRemapper awRemapper = new AccessWidenerRemapper( - writer, - remapper, - MappingsNamespace.INTERMEDIARY.toString(), - MappingsNamespace.NAMED.toString() - ); - AccessWidenerReader reader = new AccessWidenerReader(awRemapper); - reader.read(input); - return writer.write(); - } - - private void remapJars(List remapList) throws IOException { + private void remapJars(List remapList) throws IOException { final LoomGradleExtension extension = LoomGradleExtension.get(project); final MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); Path[] mcDeps = project.getConfigurations().getByName(Constants.Configurations.LOADER_DEPENDENCIES).getFiles() @@ -168,13 +117,13 @@ public class ModProcessor { remapper.readClassPathAsync(mcDeps); - final Map tagMap = new HashMap<>(); - final Map outputConsumerMap = new HashMap<>(); - final Map accessWidenerMap = new HashMap<>(); + final Map tagMap = new HashMap<>(); + final Map outputConsumerMap = new HashMap<>(); + final Map> accessWidenerMap = new HashMap<>(); for (RemapConfigurationSettings entry : extension.getRemapConfigurations()) { for (File inputFile : entry.getSourceConfiguration().get().getFiles()) { - if (remapList.stream().noneMatch(info -> info.getInputFile().equals(inputFile))) { + if (remapList.stream().noneMatch(info -> info.getInputFile().toFile().equals(inputFile))) { project.getLogger().debug("Adding " + inputFile + " onto the remap classpath"); remapper.readClassPathAsync(inputFile.toPath()); @@ -182,35 +131,37 @@ public class ModProcessor { } } - for (ModDependencyInfo info : remapList) { + for (ModDependency info : remapList) { InputTag tag = remapper.createInputTag(); project.getLogger().debug("Adding " + info.getInputFile() + " as a remap input"); - remapper.readInputsAsync(tag, info.getInputFile().toPath()); + remapper.readInputsAsync(tag, info.getInputFile()); tagMap.put(info, tag); + + Files.deleteIfExists(getRemappedOutput(info)); } try { // Apply this in a second loop as we need to ensure all the inputs are on the classpath before remapping. - for (ModDependencyInfo info : remapList) { + for (ModDependency dependency : remapList) { try { - OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(info.getRemappedOutput().toPath()).build(); + OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(getRemappedOutput(dependency)).build(); - outputConsumer.addNonClassFiles(info.getInputFile().toPath(), NonClassCopyMode.FIX_META_INF, remapper); - outputConsumerMap.put(info, outputConsumer); + outputConsumer.addNonClassFiles(dependency.getInputFile(), NonClassCopyMode.FIX_META_INF, remapper); + outputConsumerMap.put(dependency, outputConsumer); - final ModDependencyInfo.AccessWidenerData accessWidenerData = info.getAccessWidenerData(); + final AccessWidenerUtils.AccessWidenerData accessWidenerData = AccessWidenerUtils.readAccessWidenerData(dependency.getInputFile()); if (accessWidenerData != null) { - project.getLogger().debug("Remapping access widener in {}", info.getInputFile()); - byte[] remappedAw = remapAccessWidener(accessWidenerData.content(), remapper.getEnvironment().getRemapper()); - accessWidenerMap.put(info, remappedAw); + project.getLogger().debug("Remapping access widener in {}", dependency.getInputFile()); + byte[] remappedAw = AccessWidenerUtils.remapAccessWidener(accessWidenerData.content(), remapper.getEnvironment().getRemapper()); + accessWidenerMap.put(dependency, new Pair<>(remappedAw, accessWidenerData.path())); } - remapper.apply(outputConsumer, tagMap.get(info)); + remapper.apply(outputConsumer, tagMap.get(dependency)); } catch (Exception e) { - throw new RuntimeException("Failed to remap: " + info.getRemappedNotation(), e); + throw new RuntimeException("Failed to remap: " + dependency, e); } } } finally { @@ -221,22 +172,26 @@ public class ModProcessor { } } - for (ModDependencyInfo info : remapList) { - outputConsumerMap.get(info).close(); - byte[] accessWidener = accessWidenerMap.get(info); + for (ModDependency dependency : remapList) { + outputConsumerMap.get(dependency).close(); + + final Path output = getRemappedOutput(dependency); + final Pair accessWidener = accessWidenerMap.get(dependency); if (accessWidener != null) { - assert info.getAccessWidenerData() != null; - ZipUtils.replace(info.getRemappedOutput().toPath(), info.getAccessWidenerData().path(), accessWidener); + ZipUtils.replace(output, accessWidener.right(), accessWidener.left()); } - stripNestedJars(info.getRemappedOutput()); - remapJarManifestEntries(info.getRemappedOutput().toPath()); - - info.finaliseRemapping(); + stripNestedJars(output); + remapJarManifestEntries(output); + dependency.copyToCache(project, output, null); } } + private static Path getRemappedOutput(ModDependency dependency) { + return dependency.getWorkingFile(null); + } + private void remapJarManifestEntries(Path jar) throws IOException { ZipUtils.transform(jar, Map.of(RemapJarTask.MANIFEST_PATH, bytes -> { var manifest = new Manifest(new ByteArrayInputStream(bytes)); diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/dependency/LocalMavenHelper.java b/src/main/java/net/fabricmc/loom/configuration/mods/dependency/LocalMavenHelper.java new file mode 100644 index 00000000..4d7e31e7 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/mods/dependency/LocalMavenHelper.java @@ -0,0 +1,119 @@ +/* + * 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.dependency; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; + +import org.gradle.api.Project; +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.LoomGradleExtension; + +public final class LocalMavenHelper { + private final String group; + private final String name; + private final String version; + @Nullable + private final String baseClassifier; + private final Project project; + + LocalMavenHelper(String group, String name, String version, @Nullable String classifier, Project project) { + this.group = group; + this.name = name; + this.version = version; + this.baseClassifier = classifier; + this.project = project; + } + + public Path copyToMaven(Path artifact, @Nullable String classifier) throws IOException { + if (!artifact.getFileName().toString().endsWith(".jar")) { + throw new UnsupportedOperationException(); + } + + Files.createDirectories(getDirectory()); + savePom(); + return Files.copy(artifact, getOutputFile(classifier), StandardCopyOption.REPLACE_EXISTING); + } + + public boolean exists(String classifier) { + return Files.exists(getOutputFile(classifier)) && Files.exists(getPomPath()); + } + + public String getNotation() { + if (baseClassifier != null) { + return String.format("%s:%s:%s:%s", group, name, version, baseClassifier); + } + + return String.format("%s:%s:%s", group, name, version); + } + + private void savePom() { + try { + String pomTemplate; + + try (InputStream input = ModDependency.class.getClassLoader().getResourceAsStream("mod_compile_template.pom")) { + pomTemplate = new String(input.readAllBytes(), StandardCharsets.UTF_8); + } + + pomTemplate = pomTemplate + .replace("%GROUP%", group) + .replace("%NAME%", name) + .replace("%VERSION%", version); + + Files.writeString(getPomPath(), pomTemplate, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new UncheckedIOException("Failed to write mod pom", e); + } + } + + private Path getRoot() { + final LoomGradleExtension extension = LoomGradleExtension.get(project); + return extension.getFiles().getRemappedModCache().toPath(); + } + + private Path getDirectory() { + return getRoot().resolve("%s/%s/%s".formatted(group.replace(".", "/"), name, version)); + } + + private Path getPomPath() { + return getDirectory().resolve("%s-%s.pom".formatted(name, version)); + } + + public Path getOutputFile(@Nullable String classifier) { + if (classifier == null) { + classifier = baseClassifier; + } + + final String fileName = classifier == null ? String.format("%s-%s.jar", name, version) + : String.format("%s-%s-%s.jar", name, version, classifier); + return getDirectory().resolve(fileName); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/dependency/ModDependency.java b/src/main/java/net/fabricmc/loom/configuration/mods/dependency/ModDependency.java new file mode 100644 index 00000000..2cde009c --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/mods/dependency/ModDependency.java @@ -0,0 +1,103 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2020-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.mods.dependency; + +import java.io.IOException; +import java.nio.file.Path; + +import org.gradle.api.Project; +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.mods.ArtifactRef; + +public abstract sealed class ModDependency permits SplitModDependency, SimpleModDependency { + private final ArtifactRef artifact; + protected final String group; + protected final String name; + protected final String version; + @Nullable + protected final String classifier; + protected final String mappingsSuffix; + protected final Project project; + + public ModDependency(ArtifactRef artifact, String mappingsSuffix, Project project) { + this.artifact = artifact; + this.group = artifact.group(); + this.name = artifact.name(); + this.version = artifact.version(); + this.classifier = artifact.classifier(); + this.mappingsSuffix = mappingsSuffix; + this.project = project; + } + + /** + * Returns true when the cache is invalid. + */ + public abstract boolean isCacheInvalid(Project project, @Nullable String variant); + + /** + * Write an artifact to the local cache. + */ + public abstract void copyToCache(Project project, Path path, @Nullable String variant) throws IOException; + + /** + * Apply the dependency to the project. + */ + public abstract void applyToProject(Project project); + + protected LocalMavenHelper createMaven(String name) { + return new LocalMavenHelper(getRemappedGroup(), name, this.version, this.classifier, this.project); + } + + public ArtifactRef getInputArtifact() { + return artifact; + } + + protected String getRemappedGroup() { + return getMappingsPrefix() + "." + group; + } + + private String getMappingsPrefix() { + return mappingsSuffix.replace(".", "_").replace("-", "_").replace("+", "_"); + } + + public Path getInputFile() { + return artifact.path(); + } + + public Path getWorkingFile(@Nullable String classifier) { + final LoomGradleExtension extension = LoomGradleExtension.get(project); + final String fileName = classifier == null ? String.format("%s-%s-%s.jar", getRemappedGroup(), name, version) + : String.format("%s-%s-%s-%s.jar", getRemappedGroup(), name, version, classifier); + + return extension.getFiles().getProjectBuildCache().toPath().resolve("remapped_working").resolve(fileName); + } + + @Override + public String toString() { + return "ModDependency{" + "group='" + group + '\'' + ", name='" + name + '\'' + ", version='" + version + '\'' + ", classifier='" + classifier + '\'' + '}'; + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/dependency/ModDependencyFactory.java b/src/main/java/net/fabricmc/loom/configuration/mods/dependency/ModDependencyFactory.java new file mode 100644 index 00000000..f16ddfee --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/mods/dependency/ModDependencyFactory.java @@ -0,0 +1,86 @@ +/* + * 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.dependency; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Optional; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.mods.ArtifactRef; +import net.fabricmc.loom.configuration.mods.JarSplitter; +import net.fabricmc.loom.util.AttributeHelper; + +public class ModDependencyFactory { + private static final String TARGET_ATTRIBUTE_KEY = "loom-target"; + + public static ModDependency create(ArtifactRef artifact, Configuration targetConfig, @Nullable Configuration targetClientConfig, String mappingsSuffix, Project project) { + if (targetClientConfig != null && LoomGradleExtension.get(project).getSplitModDependencies().get()) { + final Optional cachedTarget = readTarget(artifact); + JarSplitter.Target target; + + if (cachedTarget.isPresent()) { + target = cachedTarget.get(); + } else { + target = new JarSplitter(artifact.path()).analyseTarget(); + writeTarget(artifact, target); + } + + if (target != null) { + return new SplitModDependency(artifact, mappingsSuffix, targetConfig, targetClientConfig, target, project); + } + } + + return new SimpleModDependency(artifact, mappingsSuffix, targetConfig, project); + } + + private static Optional readTarget(ArtifactRef artifact) { + try { + return AttributeHelper.readAttribute(artifact.path(), TARGET_ATTRIBUTE_KEY).map(s -> { + if ("null".equals(s)) { + return null; + } + + return JarSplitter.Target.valueOf(s); + }); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read artifact target attribute", e); + } + } + + private static void writeTarget(ArtifactRef artifact, JarSplitter.Target target) { + final String value = target != null ? target.name() : "null"; + + try { + AttributeHelper.writeAttribute(artifact.path(), TARGET_ATTRIBUTE_KEY, value); + } catch (IOException e) { + throw new UncheckedIOException("Failed to write artifact target attribute", e); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/dependency/SimpleModDependency.java b/src/main/java/net/fabricmc/loom/configuration/mods/dependency/SimpleModDependency.java new file mode 100644 index 00000000..8a1bd82e --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/mods/dependency/SimpleModDependency.java @@ -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.configuration.mods.dependency; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Objects; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.configuration.mods.ArtifactRef; + +// Single jar in and out +public final class SimpleModDependency extends ModDependency { + private final Configuration targetConfig; + private final LocalMavenHelper maven; + + public SimpleModDependency(ArtifactRef artifact, String mappingsSuffix, Configuration targetConfig, Project project) { + super(artifact, mappingsSuffix, project); + this.targetConfig = Objects.requireNonNull(targetConfig); + this.maven = createMaven(name); + } + + @Override + public boolean isCacheInvalid(Project project, @Nullable String variant) { + return !maven.exists(variant); + } + + @Override + public void copyToCache(Project project, Path path, @Nullable String variant) throws IOException { + maven.copyToMaven(path, variant); + } + + @Override + public void applyToProject(Project project) { + project.getDependencies().add(targetConfig.getName(), maven.getNotation()); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/dependency/SplitModDependency.java b/src/main/java/net/fabricmc/loom/configuration/mods/dependency/SplitModDependency.java new file mode 100644 index 00000000..3eabeb3a --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/mods/dependency/SplitModDependency.java @@ -0,0 +1,138 @@ +/* + * 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.dependency; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Objects; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.mods.ArtifactRef; +import net.fabricmc.loom.configuration.mods.JarSplitter; + +// Single jar in, 2 out. +public final class SplitModDependency extends ModDependency { + private final Configuration targetCommonConfig; + private final Configuration targetClientConfig; + private final JarSplitter.Target target; + @Nullable + private final LocalMavenHelper commonMaven; + @Nullable + private final LocalMavenHelper clientMaven; + + public SplitModDependency(ArtifactRef artifact, String mappingsSuffix, Configuration targetCommonConfig, Configuration targetClientConfig, JarSplitter.Target target, Project project) { + super(artifact, mappingsSuffix, project); + this.targetCommonConfig = Objects.requireNonNull(targetCommonConfig); + this.targetClientConfig = Objects.requireNonNull(targetClientConfig); + this.target = Objects.requireNonNull(target); + this.commonMaven = target.common() ? createMaven(name + "-common") : null; + this.clientMaven = target.client() ? createMaven(name + "-client") : null; + } + + @Override + public boolean isCacheInvalid(Project project, @Nullable String variant) { + boolean exists = switch (target) { + case COMMON_ONLY -> getCommonMaven().exists(variant); + case CLIENT_ONLY -> getClientMaven().exists(variant); + case SPLIT -> getCommonMaven().exists(variant) && getClientMaven().exists(variant); + }; + + return !exists; + } + + @Override + public void copyToCache(Project project, Path path, @Nullable String variant) throws IOException { + // Split dependencies build with loom 0.12 do not contain the required data to split the sources + if (target == JarSplitter.Target.SPLIT && variant != null) { + final JarSplitter.Target artifactTarget = new JarSplitter(path).analyseTarget(); + + if (artifactTarget != target) { + // Found a broken artifact, copy it to both locations without splitting. + getCommonMaven().copyToMaven(path, variant); + getClientMaven().copyToMaven(path, variant); + return; + } + } + + switch (target) { + // Split the jar into 2 + case SPLIT -> { + final String suffix = variant == null ? "" : "-" + variant; + final Path commonTempJar = getWorkingFile("common" + suffix); + final Path clientTempJar = getWorkingFile("client" + suffix); + + final JarSplitter splitter = new JarSplitter(path); + splitter.split(commonTempJar, clientTempJar); + + getCommonMaven().copyToMaven(commonTempJar, variant); + getClientMaven().copyToMaven(clientTempJar, variant); + } + + // No splitting to be done, just copy the input jar to the respective location. + case CLIENT_ONLY -> getClientMaven().copyToMaven(path, variant); + case COMMON_ONLY -> getCommonMaven().copyToMaven(path, variant); + } + } + + @Override + public void applyToProject(Project project) { + if (target.common()) { + project.getDependencies().add(targetCommonConfig.getName(), getCommonMaven().getNotation()); + } + + if (target.client()) { + project.getDependencies().add(targetClientConfig.getName(), getClientMaven().getNotation()); + } + + if (target == JarSplitter.Target.SPLIT) { + createModGroup( + getCommonMaven().getOutputFile(null), + getClientMaven().getOutputFile(null) + ); + } + } + + private void createModGroup(Path commonJar, Path clientJar) { + LoomGradleExtension extension = LoomGradleExtension.get(project); + extension.getMods().register(String.format("%s-%s-%s", getRemappedGroup(), name, version), modSettings -> + modSettings.getModFiles().from( + commonJar.toFile(), + clientJar.toFile() + ) + ); + } + + public LocalMavenHelper getCommonMaven() { + return Objects.requireNonNull(commonMaven, "Cannot get null common maven helper"); + } + + public LocalMavenHelper getClientMaven() { + return Objects.requireNonNull(clientMaven, "Cannot get null client maven helper"); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModJavadocProcessor.java b/src/main/java/net/fabricmc/loom/configuration/processors/ModJavadocProcessor.java similarity index 98% rename from src/main/java/net/fabricmc/loom/configuration/mods/ModJavadocProcessor.java rename to src/main/java/net/fabricmc/loom/configuration/processors/ModJavadocProcessor.java index 17579051..b06447fe 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModJavadocProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/ModJavadocProcessor.java @@ -22,7 +22,7 @@ * SOFTWARE. */ -package net.fabricmc.loom.configuration.mods; +package net.fabricmc.loom.configuration.processors; import java.io.ByteArrayInputStream; import java.io.File; @@ -44,7 +44,6 @@ import org.slf4j.LoggerFactory; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.RemapConfigurationSettings; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; -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; diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/dependency/ModDependencyInfo.java b/src/main/java/net/fabricmc/loom/configuration/processors/dependency/ModDependencyInfo.java deleted file mode 100644 index 3c56f7e8..00000000 --- a/src/main/java/net/fabricmc/loom/configuration/processors/dependency/ModDependencyInfo.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2020-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.processors.dependency; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; - -import com.google.gson.JsonObject; -import org.apache.commons.io.FileUtils; -import org.gradle.api.artifacts.Configuration; -import org.jetbrains.annotations.Nullable; - -import net.fabricmc.accesswidener.AccessWidenerReader; -import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; -import net.fabricmc.loom.configuration.mods.ArtifactRef; -import net.fabricmc.loom.util.ZipUtils; - -public class ModDependencyInfo { - private final ArtifactRef artifact; - private final String group; - public final String name; - public final String version; - @Nullable - public final String classifier; - public final Configuration targetConfig; - @Nullable - public final Configuration targetClientConfig; - public final RemapData remapData; - - @Nullable - private final AccessWidenerData accessWidenerData; - - private boolean forceRemap = false; - - public ModDependencyInfo(ArtifactRef artifact, Configuration targetConfig, @Nullable Configuration targetClientConfig, RemapData remapData) { - this.artifact = artifact; - this.group = artifact.group(); - this.name = artifact.name(); - this.version = artifact.version(); - this.classifier = artifact.classifier(); - this.targetConfig = targetConfig; - this.targetClientConfig = targetClientConfig; - this.remapData = remapData; - - try { - this.accessWidenerData = readAccessWidenerData(artifact.path()); - } catch (IOException e) { - throw new UncheckedIOException("Failed to read access widener data from" + artifact.path(), e); - } - } - - public ArtifactRef getArtifact() { - return artifact; - } - - public String getRemappedNotation() { - if (!hasClassifier()) { - return String.format("%s:%s:%s", getGroup(), name, version); - } - - return String.format("%s:%s:%s:%s", getGroup(), name, version, classifier); - } - - public String getRemappedFilename(boolean withClassifier) { - if (!hasClassifier() || !withClassifier) { - return String.format("%s-%s", name, version); - } - - return String.format("%s-%s-%s", name, version, classifier); - } - - public File getRemappedDir() { - return new File(remapData.modStore(), String.format("%s/%s/%s", getGroup().replace(".", "/"), name, version)); - } - - public File getRemappedOutput() { - return new File(getRemappedDir(), getRemappedFilename(true) + ".jar"); - } - - public File getRemappedOutput(String classifier) { - return new File(getRemappedDir(), getRemappedFilename(false) + "-" + classifier + ".jar"); - } - - private File getRemappedPom() { - return new File(getRemappedDir(), String.format("%s-%s", name, version) + ".pom"); - } - - private String getGroup() { - return getMappingsPrefix(remapData.mappingsSuffix()) + "." + group; - } - - public static String getMappingsPrefix(String mappings) { - return mappings.replace(".", "_").replace("-", "_").replace("+", "_"); - } - - public File getInputFile() { - return artifact.path().toFile(); - } - - private boolean outputHasInvalidAccessWidener() { - if (accessWidenerData == null) { - // This mod doesn't use an AW - return false; - } - - assert getRemappedOutput().exists(); - final AccessWidenerData outputAWData; - - try { - outputAWData = readAccessWidenerData(getRemappedOutput().toPath()); - } catch (IOException e) { - throw new UncheckedIOException("Failed to read output access widener data from " + getRemappedOutput(), e); - } - - if (outputAWData == null) { - // We know for sure the input has an AW, something is wrong if the output hasn't got one. - return true; - } - - // The output jar must have an AW in the "named" namespace. - return !MappingsNamespace.NAMED.toString().equals(outputAWData.header().getNamespace()); - } - - public boolean requiresRemapping() { - final File inputFile = artifact.path().toFile(); - final long lastModified = inputFile.lastModified(); - return !getRemappedOutput().exists() || lastModified <= 0 || lastModified > getRemappedOutput().lastModified() || forceRemap || !getRemappedPom().exists() || outputHasInvalidAccessWidener(); - } - - public void finaliseRemapping() { - getRemappedOutput().setLastModified(artifact.path().toFile().lastModified()); - savePom(); - - // Validate that the remapped AW is what we want. - if (outputHasInvalidAccessWidener()) { - throw new RuntimeException("Failed to validate remapped access widener in " + getRemappedOutput()); - } - } - - private void savePom() { - try { - String pomTemplate; - - try (InputStream input = ModDependencyInfo.class.getClassLoader().getResourceAsStream("mod_compile_template.pom")) { - pomTemplate = new String(input.readAllBytes(), StandardCharsets.UTF_8); - } - - pomTemplate = pomTemplate - .replace("%GROUP%", getGroup()) - .replace("%NAME%", name) - .replace("%VERSION%", version); - - FileUtils.writeStringToFile(getRemappedPom(), pomTemplate, StandardCharsets.UTF_8); - } catch (IOException e) { - throw new RuntimeException("Failed to write mod pom", e); - } - } - - public void forceRemap() { - forceRemap = true; - } - - @Override - public String toString() { - return getRemappedNotation(); - } - - public boolean hasClassifier() { - return classifier != null && !classifier.isEmpty(); - } - - @Nullable - public AccessWidenerData getAccessWidenerData() { - return accessWidenerData; - } - - private static AccessWidenerData readAccessWidenerData(Path inputJar) throws IOException { - byte[] modJsonBytes = ZipUtils.unpack(inputJar, "fabric.mod.json"); - JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class); - - if (!jsonObject.has("accessWidener")) { - return null; - } - - String accessWidenerPath = jsonObject.get("accessWidener").getAsString(); - byte[] accessWidener = ZipUtils.unpack(inputJar, accessWidenerPath); - AccessWidenerReader.Header header = AccessWidenerReader.readHeader(accessWidener); - - return new AccessWidenerData(accessWidenerPath, header, accessWidener); - } - - public record AccessWidenerData(String path, AccessWidenerReader.Header header, byte[] content) { - } -} diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/dependency/RemapData.java b/src/main/java/net/fabricmc/loom/configuration/processors/dependency/RemapData.java deleted file mode 100644 index c3483029..00000000 --- a/src/main/java/net/fabricmc/loom/configuration/processors/dependency/RemapData.java +++ /dev/null @@ -1,30 +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.processors.dependency; - -import java.io.File; - -public record RemapData(String mappingsSuffix, File modStore) { -} diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index 26ec80df..a083272d 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -46,7 +46,6 @@ import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; import net.fabricmc.loom.api.mappings.layered.spec.LayeredMappingSpecBuilder; import net.fabricmc.loom.configuration.RemapConfigurations; import net.fabricmc.loom.configuration.ide.RunConfigSettings; -import net.fabricmc.loom.configuration.mods.ModVersionParser; import net.fabricmc.loom.configuration.processors.JarProcessor; import net.fabricmc.loom.configuration.providers.mappings.GradleMappingContext; import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec; @@ -71,6 +70,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA protected final Property intermediary; protected final Property intermediateMappingsProvider; private final Property runtimeOnlyLog4j; + private final Property splitModDependencies; private final Property minecraftJarConfiguration; private final Property splitEnvironmentalSourceSet; private final InterfaceInjectionExtensionAPI interfaceInjectionExtension; @@ -122,6 +122,9 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA this.runtimeOnlyLog4j = project.getObjects().property(Boolean.class).convention(false); this.runtimeOnlyLog4j.finalizeValueOnRead(); + this.splitModDependencies = project.getObjects().property(Boolean.class).convention(true); + this.splitModDependencies.finalizeValueOnRead(); + this.interfaceInjectionExtension = project.getObjects().newInstance(InterfaceInjectionExtensionAPI.class); this.splitEnvironmentalSourceSet = project.getObjects().property(Boolean.class).convention(false); @@ -267,6 +270,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA return runtimeOnlyLog4j; } + @Override + public Property getSplitModDependencies() { + return splitModDependencies; + } + @Override public void splitEnvironmentSourceSets() { splitMinecraftJar(); diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index a0f1e7ff..a81a6d29 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -232,7 +232,7 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen builder.offline(); } - if (project.getGradle().getStartParameter().isRefreshDependencies() || Boolean.getBoolean("loom.refresh")) { + if (refreshDeps()) { builder.forceDownload(); } diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModVersionParser.java b/src/main/java/net/fabricmc/loom/extension/ModVersionParser.java similarity index 98% rename from src/main/java/net/fabricmc/loom/configuration/mods/ModVersionParser.java rename to src/main/java/net/fabricmc/loom/extension/ModVersionParser.java index 983bc1e3..0ded4629 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModVersionParser.java +++ b/src/main/java/net/fabricmc/loom/extension/ModVersionParser.java @@ -22,7 +22,7 @@ * SOFTWARE. */ -package net.fabricmc.loom.configuration.mods; +package net.fabricmc.loom.extension; import java.io.File; import java.io.FileReader; diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index 441cee44..41e755e1 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -64,7 +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.configuration.processors.ModJavadocProcessor; import net.fabricmc.loom.decompilers.LineNumberRemapper; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.FileSystemUtil; diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index 3df0dd22..c0bb9651 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.java @@ -39,6 +39,7 @@ import net.fabricmc.loom.task.launch.GenerateDLIConfigTask; import net.fabricmc.loom.task.launch.GenerateLog4jConfigTask; import net.fabricmc.loom.task.launch.GenerateRemapClasspathTask; import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.gradle.GradleUtils; public final class LoomTasks { private LoomTasks() { @@ -87,7 +88,7 @@ public final class LoomTasks { registerRunTasks(tasks, project); // Must be done in afterEvaluate to allow time for the build script to configure the jar config. - project.afterEvaluate(p -> { + GradleUtils.afterSuccessfulEvaluation(project, () -> { LoomGradleExtension extension = LoomGradleExtension.get(project); if (extension.getMinecraftJarConfiguration().get() == MinecraftJarConfiguration.SERVER_ONLY) { @@ -150,7 +151,7 @@ public final class LoomTasks { extension.getRunConfigs().create("server", RunConfigSettings::server); // Remove the client or server run config when not required. Done by name to not remove any possible custom run configs - project.afterEvaluate(p -> { + GradleUtils.afterSuccessfulEvaluation(project, () -> { String taskName = switch (extension.getMinecraftJarConfiguration().get()) { case SERVER_ONLY -> "client"; case CLIENT_ONLY -> "server"; diff --git a/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java b/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java index 4af39206..815b201b 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java +++ b/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java @@ -38,6 +38,7 @@ import org.gradle.api.tasks.bundling.Jar; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.gradle.GradleUtils; import net.fabricmc.loom.util.gradle.SourceSetHelper; public class RemapTaskConfiguration { @@ -84,7 +85,7 @@ public class RemapTaskConfiguration { return; } - project.afterEvaluate(p -> { + GradleUtils.afterSuccessfulEvaluation(project, () -> { // Remove -dev jars from the default jar task for (String configurationName : new String[] { JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME, JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME }) { Configuration configuration = project.getConfigurations().getByName(configurationName); @@ -135,7 +136,7 @@ public class RemapTaskConfiguration { return; } - project.afterEvaluate(p -> { + GradleUtils.afterSuccessfulEvaluation(project, () -> { final Task sourcesTask = project.getTasks().findByName(sourcesJarTaskName); if (!(sourcesTask instanceof Jar sourcesJarTask)) { diff --git a/src/main/java/net/fabricmc/loom/util/AttributeHelper.java b/src/main/java/net/fabricmc/loom/util/AttributeHelper.java new file mode 100644 index 00000000..587927bb --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/AttributeHelper.java @@ -0,0 +1,61 @@ +/* + * 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.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.UserDefinedFileAttributeView; +import java.util.Optional; + +public final class AttributeHelper { + private AttributeHelper() { + } + + public static Optional readAttribute(Path path, String key) throws IOException { + final UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class); + + if (!attributeView.list().contains(key)) { + return Optional.empty(); + } + + final ByteBuffer buffer = ByteBuffer.allocate(attributeView.size(key)); + attributeView.read(key, buffer); + buffer.flip(); + final String value = StandardCharsets.UTF_8.decode(buffer).toString(); + return Optional.of(value); + } + + public static void writeAttribute(Path path, String key, String value) throws IOException { + // TODO may need to fallback to creating a separate file if this isnt supported. + final UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class); + final byte[] bytes = value.getBytes(StandardCharsets.UTF_8); + final ByteBuffer buffer = ByteBuffer.wrap(bytes); + final int written = attributeView.write(key, buffer); + assert written == bytes.length; + } +} diff --git a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java index c40cf72e..4c069aea 100644 --- a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java +++ b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java @@ -61,13 +61,7 @@ public class SourceRemapper { this.toNamed = toNamed; } - public static void remapSources(Project project, File input, File output, boolean named, boolean reproducibleFileOrder, boolean preserveFileTimestamps) { - SourceRemapper sourceRemapper = new SourceRemapper(project, named); - sourceRemapper.scheduleRemapSources(input, output, reproducibleFileOrder, preserveFileTimestamps); - sourceRemapper.remapAll(); - } - - public void scheduleRemapSources(File source, File destination, boolean reproducibleFileOrder, boolean preserveFileTimestamps) { + public void scheduleRemapSources(File source, File destination, boolean reproducibleFileOrder, boolean preserveFileTimestamps, Runnable completionCallback) { remapTasks.add((logger) -> { try { logger.progress("remapping sources - " + source.getName()); @@ -76,6 +70,7 @@ public class SourceRemapper { // Set the remapped sources creation date to match the sources if we're likely succeeded in making it destination.setLastModified(source.lastModified()); + completionCallback.run(); } catch (Exception e) { // Failed to remap, lets clean up to ensure we try again next time destination.delete(); diff --git a/src/main/java/net/fabricmc/loom/util/download/Download.java b/src/main/java/net/fabricmc/loom/util/download/Download.java index 21fb99ea..cd756118 100644 --- a/src/main/java/net/fabricmc/loom/util/download/Download.java +++ b/src/main/java/net/fabricmc/loom/util/download/Download.java @@ -34,14 +34,12 @@ import java.net.http.HttpClient; import java.net.http.HttpHeaders; import java.net.http.HttpRequest; import java.net.http.HttpResponse; -import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.FileTime; -import java.nio.file.attribute.UserDefinedFileAttributeView; import java.time.Duration; import java.time.Instant; import java.util.Locale; @@ -53,6 +51,7 @@ import com.github.mizosoft.methanol.ProgressTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.fabricmc.loom.util.AttributeHelper; import net.fabricmc.loom.util.Checksum; public class Download { @@ -226,6 +225,11 @@ public class Download { } private boolean requiresDownload(Path output) throws DownloadException { + if (getAndResetLock(output)) { + LOGGER.warn("Forcing downloading {} as existing lock file was found. This may happen if the gradle build was forcefully canceled.", output); + return true; + } + if (forceDownload || !exists(output)) { // File does not exist, or we are forced to download again. return true; @@ -236,11 +240,6 @@ public class Download { return false; } - if (getAndResetLock(output)) { - LOGGER.warn("Forcing downloading {} as existing lock file was found. This may happen if the gradle build was forcefully canceled.", output); - return true; - } - if (expectedHash != null) { final String hashAttribute = readHash(output).orElse(""); @@ -301,7 +300,7 @@ public class Download { private Optional readEtag(Path output) { try { - return readAttribute(output, E_TAG); + return AttributeHelper.readAttribute(output, E_TAG); } catch (IOException e) { return Optional.empty(); } @@ -309,7 +308,7 @@ public class Download { private void writeEtag(Path output, String eTag) throws DownloadException { try { - writeAttribute(output, E_TAG, eTag); + AttributeHelper.writeAttribute(output, E_TAG, eTag); } catch (IOException e) { throw error(e, "Failed to write etag to (%s)", output); } @@ -317,7 +316,7 @@ public class Download { private Optional readHash(Path output) { try { - return readAttribute(output, "LoomHash"); + return AttributeHelper.readAttribute(output, "LoomHash"); } catch (IOException e) { return Optional.empty(); } @@ -325,7 +324,7 @@ public class Download { private void writeHash(Path output, String eTag) throws DownloadException { try { - writeAttribute(output, "LoomHash", eTag); + AttributeHelper.writeAttribute(output, "LoomHash", eTag); } catch (IOException e) { throw error(e, "Failed to write hash to (%s)", output); } @@ -344,29 +343,6 @@ public class Download { return path.getFileSystem() == FileSystems.getDefault() ? path.toFile().exists() : Files.exists(path); } - private static Optional readAttribute(Path path, String key) throws IOException { - final UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class); - - if (!attributeView.list().contains(key)) { - return Optional.empty(); - } - - final ByteBuffer buffer = ByteBuffer.allocate(attributeView.size(key)); - attributeView.read(key, buffer); - buffer.flip(); - final String value = StandardCharsets.UTF_8.decode(buffer).toString(); - return Optional.of(value); - } - - private static void writeAttribute(Path path, String key, String value) throws IOException { - // TODO may need to fallback to creating a separate file if this isnt supported. - final UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class); - final byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - final ByteBuffer buffer = ByteBuffer.wrap(bytes); - final int written = attributeView.write(key, buffer); - assert written == bytes.length; - } - private FileTime getLastModified(Path path) throws IOException { final BasicFileAttributeView basicView = Files.getFileAttributeView(path, BasicFileAttributeView.class); return basicView.readAttributes().lastModifiedTime(); @@ -380,10 +356,12 @@ public class Download { final Path lock = getLockFile(output); final boolean exists = Files.exists(lock); - try { - Files.deleteIfExists(lock); - } catch (IOException e) { - throw error(e, "Failed to release lock on %s", lock); + if (exists) { + try { + Files.delete(lock); + } catch (IOException e) { + throw error(e, "Failed to release lock on %s", lock); + } } return exists; diff --git a/src/main/java/net/fabricmc/loom/util/gradle/GradleSupport.java b/src/main/java/net/fabricmc/loom/util/gradle/GradleUtils.java similarity index 72% rename from src/main/java/net/fabricmc/loom/util/gradle/GradleSupport.java rename to src/main/java/net/fabricmc/loom/util/gradle/GradleUtils.java index 0ac6bab0..56162a2e 100644 --- a/src/main/java/net/fabricmc/loom/util/gradle/GradleSupport.java +++ b/src/main/java/net/fabricmc/loom/util/gradle/GradleUtils.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) 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 @@ -24,14 +24,21 @@ package net.fabricmc.loom.util.gradle; -import org.gradle.util.GradleVersion; +import org.gradle.api.Project; -// This is used to bridge the gap over large gradle api changes. -public class GradleSupport { - public static final boolean IS_GRADLE_7_OR_NEWER = isIsGradle7OrNewer(); +public final class GradleUtils { + private GradleUtils() { + } - public static boolean isIsGradle7OrNewer() { - String version = GradleVersion.current().getVersion(); - return Integer.parseInt(version.substring(0, version.indexOf("."))) >= 7; + // For some crazy reason afterEvaluate is still invoked when the configuration fails + public static void afterSuccessfulEvaluation(Project project, Runnable afterEvaluate) { + project.afterEvaluate(p -> { + if (p.getState().getFailure() != null) { + // Let gradle handle the failure + return; + } + + afterEvaluate.run(); + }); } } diff --git a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index 78e1cf28..1b4c7183 100644 --- a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy @@ -27,12 +27,12 @@ package net.fabricmc.loom.test import org.gradle.util.GradleVersion class LoomTestConstants { - private final static String NIGHTLY_VERSION = "7.6-20220722221130+0000" + private final static String NIGHTLY_VERSION = "7.6-20220729220941+0000" private final static boolean NIGHTLY_EXISTS = nightlyExists(NIGHTLY_VERSION) public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion() // Tests that depend specifically on the nightly will run on the current version when the nightly is not available. - public final static String PRE_RELEASE_GRADLE = NIGHTLY_EXISTS ? "7.6-20220516224938+0000" : DEFAULT_GRADLE + public final static String PRE_RELEASE_GRADLE = NIGHTLY_EXISTS ? NIGHTLY_VERSION : DEFAULT_GRADLE public final static String[] STANDARD_TEST_VERSIONS = NIGHTLY_EXISTS ? [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] : [DEFAULT_GRADLE] /** diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy index 0bfbf172..0c9f9a0a 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy @@ -44,7 +44,7 @@ class FabricAPITest extends Specification implements GradleProjectTestTrait { setup: def gradle = gradleProject( repo: "https://github.com/FabricMC/fabric.git", - commit: "71b634e5b7845296b11be3fa6545f4fbfacc017f", + commit: "417b986df14c7cdd5f16b0a340d28645910f8aa6", version: version, patch: "fabric_api" ) @@ -52,18 +52,18 @@ class FabricAPITest extends Specification implements GradleProjectTestTrait { // Set the version to something constant gradle.buildGradle.text = gradle.buildGradle.text.replace('project.version + "+" + (ENV.GITHUB_RUN_NUMBER ? "" : "local-") + getBranch()', "\"$API_VERSION\"") - def server = ServerRunner.create(gradle.projectDir, "1.17.1") + def server = ServerRunner.create(gradle.projectDir, "1.19.1") .withMod(gradle.getOutputFile("fabric-api-${API_VERSION}.jar")) when: - def result = gradle.run(tasks: ["build", "publishToMavenLocal"], args: ["--parallel", "-x", "check"]) // Note: checkstyle does not appear to like being ran in a test runner + def result = gradle.run(tasks: ["build", "publishToMavenLocal"], args: ["--parallel", "-x", "check", "-x", "runDatagen", "-x", "runGametest"]) // Note: checkstyle does not appear to like being ran in a test runner gradle.printOutputFiles() def serverResult = server.run() then: result.task(":build").outcome == SUCCESS - new File(gradle.mavenLocalDir, "net/fabricmc/fabric-api/fabric-biome-api-v1/3.2.2/fabric-biome-api-v1-3.2.2.jar").exists() - new File(gradle.mavenLocalDir, "net/fabricmc/fabric-api/fabric-biome-api-v1/3.2.2/fabric-biome-api-v1-3.2.2-sources.jar").exists() + new File(gradle.mavenLocalDir, "net/fabricmc/fabric-api/fabric-biome-api-v1/9.0.15/fabric-biome-api-v1-9.0.15.jar").exists() + new File(gradle.mavenLocalDir, "net/fabricmc/fabric-api/fabric-biome-api-v1/9.0.15/fabric-biome-api-v1-9.0.15-sources.jar").exists() serverResult.successful() serverResult.output.contains("- fabric $API_VERSION") diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/IdeaClasspathModificationsTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/IdeaClasspathModificationsTest.groovy index 42d00c38..faa78686 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/IdeaClasspathModificationsTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/IdeaClasspathModificationsTest.groovy @@ -84,7 +84,7 @@ class IdeaClasspathModificationsTest extends Specification { %IDEA_ENV_VARS% - + '''.trim() @@ -104,7 +104,7 @@ class IdeaClasspathModificationsTest extends Specification { %IDEA_ENV_VARS% - + '''.trim() diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/JarSplitterTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/JarSplitterTest.groovy index a0a2a78a..be337401 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/JarSplitterTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/JarSplitterTest.groovy @@ -28,15 +28,54 @@ import net.fabricmc.loom.configuration.mods.JarSplitter import spock.lang.Specification class JarSplitterTest extends Specification { - public static final String INPUT_JAR_URL = "https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-lifecycle-events-v1/2.1.0%2B33fbc738a9/fabric-lifecycle-events-v1-2.1.0%2B33fbc738a9.jar" + public static final String SPLIT_INPUT_JAR_URL = "https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-lifecycle-events-v1/2.1.0%2B33fbc738a9/fabric-lifecycle-events-v1-2.1.0%2B33fbc738a9.jar" + public static final String CLIENT_INPUT_JAR_URL = "https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-renderer-indigo/0.6.10%2B5187d39f95/fabric-renderer-indigo-0.6.10%2B5187d39f95.jar" + public static final String COMMON_INPUT_JAR_URL = "https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api-lookup-api-v1/1.6.7%2B9ff28f4095/fabric-api-lookup-api-v1-1.6.7%2B9ff28f4095.jar" + public static final String NORMAL_INPUT_JAR_URL = "https://maven.fabricmc.net/net/fabricmc/fabric-loader/0.14.8/fabric-loader-0.14.8.jar" public static final File workingDir = new File("build/test/split") + def "analyse: split"() { + given: + def inputJar = downloadJarIfNotExists(SPLIT_INPUT_JAR_URL, "split.jar") + when: + def target = new JarSplitter(inputJar.toPath()).analyseTarget() + then: + target == JarSplitter.Target.SPLIT + } + + def "analyse: client"() { + given: + def inputJar = downloadJarIfNotExists(CLIENT_INPUT_JAR_URL, "client.jar") + when: + def target = new JarSplitter(inputJar.toPath()).analyseTarget() + then: + target == JarSplitter.Target.CLIENT_ONLY + } + + def "analyse: common"() { + given: + def inputJar = downloadJarIfNotExists(COMMON_INPUT_JAR_URL, "common.jar") + when: + def target = new JarSplitter(inputJar.toPath()).analyseTarget() + then: + target == JarSplitter.Target.COMMON_ONLY + } + + def "analyse: normal"() { + given: + def inputJar = downloadJarIfNotExists(NORMAL_INPUT_JAR_URL, "normal.jar") + when: + def target = new JarSplitter(inputJar.toPath()).analyseTarget() + then: + target == null + } + def "split jar"() { given: - def inputJar = downloadJarIfNotExists(INPUT_JAR_URL, "input.jar") - def commonOutputJar = getFile("common.jar") - def clientOutputJar = getFile("client.jar") + def inputJar = downloadJarIfNotExists(SPLIT_INPUT_JAR_URL, "split.jar") + def commonOutputJar = getFile("common-out.jar") + def clientOutputJar = getFile("client-out.jar") def jarSplitter = new JarSplitter(inputJar.toPath()) when: 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 351a6869..8d18497b 100644 --- a/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy @@ -29,7 +29,7 @@ import groovy.transform.Immutable import java.util.concurrent.TimeUnit class ServerRunner { - static final String LOADER_VERSION = "0.12.5" + static final String LOADER_VERSION = "0.14.8" static final Map FABRIC_API_URLS = [ "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" diff --git a/src/test/resources/patches/fabric_api.patch b/src/test/resources/patches/fabric_api.patch index c1d922e9..a217affb 100644 --- a/src/test/resources/patches/fabric_api.patch +++ b/src/test/resources/patches/fabric_api.patch @@ -1,7 +1,7 @@ diff --git a/build.gradle b/build.gradle ---- a/build.gradle (revision 71b634e5b7845296b11be3fa6545f4fbfacc017f) -+++ b/build.gradle (date 1638654919842) -@@ -31,17 +31,7 @@ +--- a/build.gradle (revision 5cda40cfe1ddb507dd509c89117b0801a6ab79d9) ++++ b/build.gradle (date 1659386544411) +@@ -33,17 +33,7 @@ throw new NullPointerException("Could not find version for " + project.name) } @@ -20,3 +20,14 @@ diff --git a/build.gradle b/build.gradle } def getBranch() { +@@ -207,10 +197,6 @@ + testmodImplementation sourceSets.client.output + } + +- loom { +- shareRemapCaches = true +- } +- + tasks.withType(ProcessResources).configureEach { + inputs.property "version", project.version + diff --git a/src/test/resources/projects/multiproject/build.gradle b/src/test/resources/projects/multiproject/build.gradle index 0a90269a..62858e2f 100644 --- a/src/test/resources/projects/multiproject/build.gradle +++ b/src/test/resources/projects/multiproject/build.gradle @@ -35,10 +35,6 @@ allprojects { archiveClassifier.set "dev" } - loom { - shareCaches() - } - java { withSourcesJar() }