diff --git a/bootstrap/test-project/build.gradle b/bootstrap/test-project/build.gradle index de039d52..6be442a1 100644 --- a/bootstrap/test-project/build.gradle +++ b/bootstrap/test-project/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' version '0.12.local' + id 'dev.architectury.loom' version '0.13.local' } dependencies { diff --git a/build.gradle b/build.gradle index 8e1d030f..65e365e3 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { group = "dev.architectury" archivesBaseName = project.name -def baseVersion = '0.12.0' +def baseVersion = '0.13.0' def runNumber = System.getenv("GITHUB_RUN_NUMBER") ?: "9999" def isSnapshot = System.getenv("PR_NUM") != null @@ -55,7 +55,6 @@ repositories { excludeGroupByRegex "org\\.eclipse\\.?.*" } } - mavenLocal() } configurations { @@ -82,7 +81,7 @@ dependencies { // libraries implementation ('commons-io:commons-io:2.11.0') implementation ('com.google.code.gson:gson:2.9.0') - implementation ('com.fasterxml.jackson.core:jackson-databind:2.13.2.2') + implementation ('com.fasterxml.jackson.core:jackson-databind:2.13.3') implementation ('com.google.guava:guava:31.1-jre') implementation ('org.ow2.asm:asm:9.3') implementation ('org.ow2.asm:asm-analysis:9.3') @@ -99,6 +98,7 @@ dependencies { // tinyfile management implementation ('dev.architectury:tiny-remapper:1.7.19') + // TODO: implementation ('net.fabricmc:tiny-remapper:0.8.5') implementation 'net.fabricmc:access-widener:2.1.0' implementation 'net.fabricmc:mapping-io:0.2.1' @@ -135,11 +135,11 @@ dependencies { exclude module: 'groovy-all' } testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.2' - testImplementation ('io.javalin:javalin:4.4.0') { + testImplementation ('io.javalin:javalin:4.6.1') { exclude group: 'org.jetbrains.kotlin' } testImplementation 'net.fabricmc:fabric-installer:0.9.0' - testImplementation 'org.mockito:mockito-core:4.4.0' + testImplementation 'org.mockito:mockito-core:4.6.1' compileOnly 'org.jetbrains:annotations:23.0.0' testCompileOnly 'org.jetbrains:annotations:23.0.0' @@ -212,6 +212,11 @@ test { maxHeapSize = "1536m" useJUnitPlatform() maxParallelForks = Runtime.runtime.availableProcessors() ?: 1 + + // Forward system prop onto tests. + if (System.getProperty("fabric.loom.test.homeDir")) { + systemProperty "fabric.loom.test.homeDir", System.getProperty("fabric.loom.test.homeDir") + } } import org.gradle.util.GradleVersion diff --git a/src/main/java/net/fabricmc/loom/build/MixinRefmapHelper.java b/src/main/java/net/fabricmc/loom/build/MixinRefmapHelper.java index 49cde0d7..69a24625 100644 --- a/src/main/java/net/fabricmc/loom/build/MixinRefmapHelper.java +++ b/src/main/java/net/fabricmc/loom/build/MixinRefmapHelper.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2018-2021 FabricMC + * Copyright (c) 2018-2022 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,46 +24,21 @@ package net.fabricmc.loom.build; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; import java.util.Collection; import java.util.Collections; import java.util.stream.Collectors; import java.util.stream.StreamSupport; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import net.fabricmc.loom.LoomGradlePlugin; public final class MixinRefmapHelper { private MixinRefmapHelper() { } private static final String FABRIC_MOD_JSON = "fabric.mod.json"; - @Nullable - public static JsonObject readFabricModJson(File output) { - try (ZipFile zip = new ZipFile(output)) { - ZipEntry entry = zip.getEntry(FABRIC_MOD_JSON); - - if (entry == null) { - return null; - } - - try (InputStreamReader reader = new InputStreamReader(zip.getInputStream(entry))) { - return LoomGradlePlugin.GSON.fromJson(reader, JsonObject.class); - } - } catch (IOException e) { - throw new RuntimeException("Cannot read file fabric.mod.json in the jar.", e); - } - } - @NotNull public static Collection getMixinConfigurationFiles(JsonObject fabricModJson) { JsonArray mixins = fabricModJson.getAsJsonArray("mixins"); diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java index 885b21bd..cef2f52b 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java @@ -24,7 +24,6 @@ package net.fabricmc.loom.configuration.ide; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -44,6 +43,9 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import org.apache.tools.ant.util.StringUtils; import org.gradle.api.Project; +import org.gradle.api.artifacts.ModuleVersionIdentifier; +import org.gradle.api.artifacts.ResolvedArtifact; +import org.gradle.api.artifacts.ResolvedModuleVersion; import org.gradle.api.tasks.SourceSet; import org.gradle.plugins.ide.eclipse.model.EclipseModel; import org.w3c.dom.Document; @@ -296,14 +298,6 @@ public class RunConfig { } public List getExcludedLibraryPaths(Project project) { - if (true) { - /* - This whole excluded libraries idea breaks down when the server library version does not match the client. - This is a quick change to disable it meanwhile a proper solution is sorted. - */ - return Collections.emptyList(); - } - if (!environment.equals("server")) { return Collections.emptyList(); } @@ -315,19 +309,32 @@ public class RunConfig { return Collections.emptyList(); } - final Set allLibraries = project.getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES).getFiles(); - final Set serverLibraries = project.getConfigurations().getByName(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES).getFiles(); + final Set allLibraries = getArtifacts(project, Constants.Configurations.MINECRAFT_DEPENDENCIES); + final Set serverLibraries = getArtifacts(project, Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES); final List clientOnlyLibraries = new LinkedList<>(); - for (File commonLibrary : allLibraries) { - if (!serverLibraries.contains(commonLibrary)) { - clientOnlyLibraries.add(commonLibrary.getAbsolutePath()); + for (ResolvedArtifact library : allLibraries) { + if (!containsLibrary(serverLibraries, library.getModuleVersion().getId())) { + clientOnlyLibraries.add(library.getFile().getAbsolutePath()); } } return clientOnlyLibraries; } + private static Set getArtifacts(Project project, String configuration) { + return project.getConfigurations().getByName(configuration) + .getResolvedConfiguration() + .getResolvedArtifacts(); + } + + private static boolean containsLibrary(Set artifacts, ModuleVersionIdentifier identifier) { + return artifacts.stream() + .map(ResolvedArtifact::getModuleVersion) + .map(ResolvedModuleVersion::getId) + .anyMatch(test -> test.getGroup().equals(identifier.getGroup()) && test.getName().equals(identifier.getName())); + } + private static String encodeEscaped(String s) { StringBuilder ret = new StringBuilder(); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java index 634eced1..3fa50eec 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2016-2021 FabricMC + * Copyright (c) 2016-2022 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -67,6 +67,7 @@ import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvid import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.DeletingFileVisitor; +import net.fabricmc.loom.util.FileSystemUtil; import net.fabricmc.loom.util.ZipUtils; import net.fabricmc.loom.util.service.SharedService; import net.fabricmc.loom.util.service.SharedServiceManager; @@ -299,9 +300,9 @@ public class MappingsProviderImpl implements MappingsProvider, SharedService { return; } - try (FileSystem fileSystem = FileSystems.newFileSystem(inputJar, (ClassLoader) null)) { - extractMappings(fileSystem, baseTinyMappings); - extractExtras(fileSystem); + try (FileSystemUtil.Delegate delegate = FileSystemUtil.getJarFileSystem(inputJar)) { + extractMappings(delegate.fs(), baseTinyMappings); + extractExtras(delegate.fs()); } if (areMappingsMergedV2(baseTinyMappings)) { @@ -383,8 +384,8 @@ public class MappingsProviderImpl implements MappingsProvider, SharedService { } public static void extractMappings(Path jar, Path extractTo) throws IOException { - try (FileSystem unmergedIntermediaryFs = FileSystems.newFileSystem(jar, (ClassLoader) null)) { - extractMappings(unmergedIntermediaryFs, extractTo); + try (FileSystemUtil.Delegate delegate = FileSystemUtil.getJarFileSystem(jar)) { + extractMappings(delegate.fs(), extractTo); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/signatures/SignatureFixesLayerImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/signatures/SignatureFixesLayerImpl.java index 5f906384..4328311b 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/signatures/SignatureFixesLayerImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/signatures/SignatureFixesLayerImpl.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 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 @@ -25,17 +25,13 @@ package net.fabricmc.loom.configuration.providers.mappings.extras.signatures; import java.io.IOException; -import java.io.InputStreamReader; import java.nio.file.Path; import java.util.Map; -import java.util.Objects; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; import org.jetbrains.annotations.ApiStatus; -import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.api.mappings.layered.MappingLayer; +import net.fabricmc.loom.util.ZipUtils; import net.fabricmc.mappingio.MappingVisitor; @ApiStatus.Experimental @@ -49,14 +45,9 @@ public record SignatureFixesLayerImpl(Path mappingsFile) implements MappingLayer @Override public Map getSignatureFixes() { - try (var zipFile = new ZipFile(mappingsFile().toFile())) { - ZipEntry zipFileEntry = zipFile.getEntry(SIGNATURE_FIXES_PATH); - Objects.requireNonNull(zipFileEntry, "Could not find %s in file".formatted(SIGNATURE_FIXES_PATH)); - - try (var reader = new InputStreamReader(zipFile.getInputStream(zipFileEntry))) { - //noinspection unchecked - return LoomGradlePlugin.OBJECT_MAPPER.readValue(reader, Map.class); - } + try { + //noinspection unchecked + return ZipUtils.unpackJackson(mappingsFile(), SIGNATURE_FIXES_PATH, Map.class); } catch (IOException e) { throw new RuntimeException("Failed to extract signature fixes", e); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentMappingLayer.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentMappingLayer.java index f6b602e8..0e2f68c6 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentMappingLayer.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentMappingLayer.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2016-2021 FabricMC + * Copyright (c) 2016-2022 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,15 +25,11 @@ package net.fabricmc.loom.configuration.providers.mappings.parchment; import java.io.IOException; -import java.io.InputStreamReader; import java.nio.file.Path; -import java.util.Objects; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.api.mappings.layered.MappingLayer; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.util.ZipUtils; import net.fabricmc.mappingio.MappingVisitor; public record ParchmentMappingLayer(Path parchmentFile, boolean removePrefix) implements MappingLayer { @@ -51,13 +47,6 @@ public record ParchmentMappingLayer(Path parchmentFile, boolean removePrefix) im } private ParchmentTreeV1 getParchmentData() throws IOException { - try (var zipFile = new ZipFile(parchmentFile().toFile())) { - ZipEntry zipFileEntry = zipFile.getEntry(PARCHMENT_DATA_FILE_NAME); - Objects.requireNonNull(zipFileEntry, "Could not find %s in parchment data file".formatted(PARCHMENT_DATA_FILE_NAME)); - - try (var reader = new InputStreamReader(zipFile.getInputStream(zipFileEntry))) { - return LoomGradlePlugin.OBJECT_MAPPER.readValue(reader, ParchmentTreeV1.class); - } - } + return ZipUtils.unpackJackson(parchmentFile, PARCHMENT_DATA_FILE_NAME, ParchmentTreeV1.class); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/TinyJarInfo.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/TinyJarInfo.java index e6622165..3238bf7a 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/TinyJarInfo.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/TinyJarInfo.java @@ -27,13 +27,12 @@ package net.fabricmc.loom.configuration.providers.mappings.tiny; import java.io.BufferedReader; import java.io.IOException; import java.io.UncheckedIOException; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Optional; +import net.fabricmc.loom.util.FileSystemUtil; import net.fabricmc.mappingio.MappingReader; import net.fabricmc.mappingio.format.MappingFormat; @@ -47,8 +46,8 @@ public record TinyJarInfo(boolean v2, Optional minecraftVersionId) { } private static boolean doesJarContainV2Mappings(Path path) throws IOException { - try (FileSystem fs = FileSystems.newFileSystem(path, (ClassLoader) null)) { - try (BufferedReader reader = Files.newBufferedReader(fs.getPath("mappings", "mappings.tiny"))) { + try (FileSystemUtil.Delegate delegate = FileSystemUtil.getJarFileSystem(path)) { + try (BufferedReader reader = Files.newBufferedReader(delegate.fs().getPath("mappings", "mappings.tiny"))) { return MappingReader.detectFormat(reader) == MappingFormat.TINY_2; } } catch (NoSuchFileException e) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java index ac616767..e88518b4 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java @@ -75,8 +75,6 @@ public class MinecraftLibraryProvider { if (runtimeOnlyLog4j && library.name().startsWith("org.apache.logging.log4j")) { // Make log4j a runtime only dep to force slf4j. project.getDependencies().add(Constants.Configurations.MINECRAFT_RUNTIME_DEPENDENCIES, library.name()); - } else if (serverBundleMetadata != null && isLibraryInBundle(serverBundleMetadata, library)) { - project.getDependencies().add(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES, library.name()); } else if (jarConfiguration.getSupportedEnvironments().contains("client")) { // Client only library, or legacy version project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, library.name()); @@ -117,6 +115,12 @@ public class MinecraftLibraryProvider { } } + if (serverBundleMetadata != null) { + for (BundleMetadata.Entry library : serverBundleMetadata.libraries()) { + project.getDependencies().add(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES, library.name()); + } + } + if (overrideLWJGL) { LWJGLVersionOverride.DEPENDENCIES.forEach(s -> project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, s)); LWJGLVersionOverride.NATIVES.forEach(s -> project.getDependencies().add(Constants.Configurations.MINECRAFT_NATIVES, s)); @@ -132,8 +136,4 @@ public class MinecraftLibraryProvider { project.getDependencies().add("modLocalRuntime", dependency); } } - - private static boolean isLibraryInBundle(BundleMetadata bundleMetadata, MinecraftVersionMeta.Library library) { - return bundleMetadata.libraries().stream().anyMatch(entry -> entry.name().equals(library.name())); - } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java index 1893703a..576a243e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java @@ -209,12 +209,14 @@ public abstract sealed class MinecraftSourceSets permits MinecraftSourceSets.Sin ); }); - if (project.getTasks().findByName(mainSourceSet.getSourcesJarTaskName()) == null) { - // No sources. - return; - } + // The sources task can be registered at a later time. + project.getTasks().configureEach(task -> { + if (!mainSourceSet.getSourcesJarTaskName().equals(task.getName()) || !(task instanceof Jar jar)) { + // Not the sources task we are looking for. + return; + } - project.getTasks().named(mainSourceSet.getSourcesJarTaskName(), Jar.class).configure(jar -> { + // The client only sources to the combined sources jar. jar.from(clientOnlySourceSet.getAllSource()); }); } @@ -224,7 +226,7 @@ public abstract sealed class MinecraftSourceSets permits MinecraftSourceSets.Sin } public static SourceSet getClientSourceSet(Project project) { - Preconditions.checkArgument(LoomGradleExtension.get(project).areEnvironmentSourceSetsSplit()); + Preconditions.checkArgument(LoomGradleExtension.get(project).areEnvironmentSourceSetsSplit(), "Cannot get client only sourceset as project is not split"); final JavaPluginExtension javaExtension = project.getExtensions().getByType(JavaPluginExtension.class); return javaExtension.getSourceSets().getByName(CLIENT_ONLY_SOURCE_SET_NAME); diff --git a/src/main/java/net/fabricmc/loom/decompilers/fernflower/FernFlowerUtils.java b/src/main/java/net/fabricmc/loom/decompilers/fernflower/FernFlowerUtils.java index ced6345c..ee07f045 100644 --- a/src/main/java/net/fabricmc/loom/decompilers/fernflower/FernFlowerUtils.java +++ b/src/main/java/net/fabricmc/loom/decompilers/fernflower/FernFlowerUtils.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2016-2020 FabricMC + * Copyright (c) 2016-2022 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,11 +26,11 @@ package net.fabricmc.loom.decompilers.fernflower; import java.io.File; import java.io.IOException; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; import org.jetbrains.java.decompiler.util.InterpreterUtil; +import net.fabricmc.loom.util.ZipUtils; + public class FernFlowerUtils { public static byte[] getBytecode(String externalPath, String internalPath) throws IOException { File file = new File(externalPath); @@ -38,15 +38,7 @@ public class FernFlowerUtils { if (internalPath == null) { return InterpreterUtil.getBytes(file); } else { - try (ZipFile archive = new ZipFile(file)) { - ZipEntry entry = archive.getEntry(internalPath); - - if (entry == null) { - throw new IOException("Entry not found: " + internalPath); - } - - return InterpreterUtil.getBytes(archive, entry); - } + return ZipUtils.unpack(file.toPath(), internalPath); } } } diff --git a/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java b/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java index c6f5ec05..f3d0d1aa 100644 --- a/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java @@ -24,18 +24,30 @@ package net.fabricmc.loom.task; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.jar.Manifest; import javax.inject.Inject; +import com.google.common.base.Preconditions; import org.gradle.api.Action; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Internal; import org.gradle.jvm.tasks.Jar; import org.gradle.workers.WorkAction; import org.gradle.workers.WorkParameters; @@ -44,9 +56,14 @@ import org.gradle.workers.WorkerExecutor; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.build.IntermediaryNamespaces; +import net.fabricmc.loom.task.service.JarManifestService; import net.fabricmc.loom.util.ZipReprocessorUtil; +import net.fabricmc.loom.util.ZipUtils; public abstract class AbstractRemapJarTask extends Jar { + public static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; + public static final String MANIFEST_NAMESPACE_KEY = "Fabric-Mapping-Namespace"; + @InputFile public abstract RegularFileProperty getInputFile(); @@ -68,11 +85,15 @@ public abstract class AbstractRemapJarTask extends Jar { @Inject protected abstract WorkerExecutor getWorkerExecutor(); + @Input + public abstract Property getIncludesClientOnlyClasses(); + @Inject public AbstractRemapJarTask() { getSourceNamespace().convention(MappingsNamespace.NAMED.toString()).finalizeValueOnRead(); getTargetNamespace().convention(IntermediaryNamespaces.intermediary(getProject())).finalizeValueOnRead(); getRemapperIsolation().convention(true).finalizeValueOnRead(); + getIncludesClientOnlyClasses().convention(false).finalizeValueOnRead(); } public final

void submitWork(Class> workAction, Action

action) { @@ -88,10 +109,21 @@ public abstract class AbstractRemapJarTask extends Jar { params.getArchivePreserveFileTimestamps().set(isPreserveFileTimestamps()); params.getArchiveReproducibleFileOrder().set(isReproducibleFileOrder()); + params.getJarManifestService().set(JarManifestService.get(getProject())); + + if (getIncludesClientOnlyClasses().get()) { + final List clientOnlyEntries = getClientOnlyEntries(); + applyClientOnlyManifestAttributes(params, clientOnlyEntries); + params.getClientOnlyEntries().set(clientOnlyEntries.stream().filter(s -> s.endsWith(".class")).toList()); + } + action.execute(params); }); } + @Internal + protected abstract List getClientOnlyEntries(); + public interface AbstractRemapParams extends WorkParameters { RegularFileProperty getInputFile(); RegularFileProperty getOutputFile(); @@ -101,6 +133,18 @@ public abstract class AbstractRemapJarTask extends Jar { Property getArchivePreserveFileTimestamps(); Property getArchiveReproducibleFileOrder(); + + Property getJarManifestService(); + MapProperty getManifestAttributes(); + + ListProperty getClientOnlyEntries(); + } + + protected void applyClientOnlyManifestAttributes(AbstractRemapParams params, List entries) { + params.getManifestAttributes().set(Map.of( + "Fabric-Loom-Split-Environment", "true", + "Fabric-Loom-Client-Only-Entries", String.join(";", entries) + )); } public abstract static class AbstractRemapAction implements WorkAction { @@ -113,6 +157,21 @@ public abstract class AbstractRemapJarTask extends Jar { outputFile = getParameters().getOutputFile().getAsFile().get().toPath(); } + protected void modifyJarManifest() throws IOException { + int count = ZipUtils.transform(outputFile, Map.of(MANIFEST_PATH, bytes -> { + var manifest = new Manifest(new ByteArrayInputStream(bytes)); + + getParameters().getJarManifestService().get().apply(manifest, getParameters().getManifestAttributes().get()); + manifest.getMainAttributes().putValue(MANIFEST_NAMESPACE_KEY, getParameters().getTargetNamespace().get()); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + manifest.write(out); + return out.toByteArray(); + })); + + Preconditions.checkState(count > 0, "Did not transform any jar manifest"); + } + protected void rewriteJar() throws IOException { final boolean isReproducibleFileOrder = getParameters().getArchiveReproducibleFileOrder().get(); final boolean isPreserveFileTimestamps = getParameters().getArchivePreserveFileTimestamps().get(); @@ -128,4 +187,31 @@ public abstract class AbstractRemapJarTask extends Jar { public RegularFileProperty getInput() { return getInputFile(); } + + protected static List getRootPaths(Set files) { + return files.stream() + .map(root -> { + String rootPath = root.getAbsolutePath().replace("\\", "/"); + + if (rootPath.charAt(rootPath.length() - 1) != '/') { + rootPath += '/'; + } + + return rootPath; + }).toList(); + } + + protected static Function relativePath(List rootPaths) { + return file -> { + String s = file.getAbsolutePath().replace("\\", "/"); + + for (String rootPath : rootPaths) { + if (s.startsWith(rootPath)) { + s = s.substring(rootPath.length()); + } + } + + return s; + }; + } } diff --git a/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java b/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java index 53cb545a..f09bfe2e 100644 --- a/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java +++ b/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2019-2021 FabricMC + * Copyright (c) 2019-2022 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,8 +26,6 @@ package net.fabricmc.loom.task; import java.io.File; import java.io.IOException; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.Set; @@ -51,6 +49,7 @@ import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.api.mappings.layered.spec.LayeredMappingSpecBuilder; import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsDependency; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; +import net.fabricmc.loom.util.FileSystemUtil; import net.fabricmc.loom.util.SourceRemapper; import net.fabricmc.lorenztiny.TinyMappingsJoiner; import net.fabricmc.mappingio.MappingReader; @@ -117,7 +116,7 @@ public class MigrateMappingsTask extends AbstractLoomTask { Set files; try { - if (mappings.startsWith("net.minecraft:mappings:") || mappings.startsWith("net.mojang.minecraft:mappings:")) { + if (mappings.startsWith("net.minecraft:mappings:")) { if (!mappings.endsWith(":" + LoomGradleExtension.get(project).getMinecraftProvider().minecraftVersion())) { throw new UnsupportedOperationException("Migrating Mojang mappings is currently only supported for the specified minecraft version"); } @@ -149,8 +148,8 @@ public class MigrateMappingsTask extends AbstractLoomTask { private static MemoryMappingTree getMappings(File mappings) throws IOException { MemoryMappingTree mappingTree = new MemoryMappingTree(); - try (FileSystem fileSystem = FileSystems.newFileSystem(mappings.toPath(), (ClassLoader) null)) { - MappingReader.read(fileSystem.getPath("mappings/mappings.tiny"), mappingTree); + try (FileSystemUtil.Delegate delegate = FileSystemUtil.getJarFileSystem(mappings.toPath())) { + MappingReader.read(delegate.fs().getPath("mappings/mappings.tiny"), mappingTree); } return mappingTree; diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index 98f21090..198ffa0a 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -26,7 +26,6 @@ package net.fabricmc.loom.task; import java.io.BufferedReader; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; @@ -47,7 +46,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.function.Function; import java.util.function.Supplier; import java.util.jar.Attributes; import java.util.jar.JarFile; @@ -58,7 +56,6 @@ import java.util.stream.StreamSupport; import javax.inject.Inject; -import com.google.common.base.Preconditions; import com.google.common.base.Suppliers; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -73,7 +70,6 @@ import org.gradle.api.file.FileCollection; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.provider.ListProperty; -import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.provider.SetProperty; @@ -100,7 +96,6 @@ import net.fabricmc.loom.build.nesting.JarNester; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.extension.MixinExtension; -import net.fabricmc.loom.task.service.JarManifestService; import net.fabricmc.loom.task.service.MappingsService; import net.fabricmc.loom.task.service.TinyRemapperService; import net.fabricmc.loom.util.Constants; @@ -108,6 +103,7 @@ import net.fabricmc.loom.util.ExceptionUtil; import net.fabricmc.loom.util.FileSystemUtil; import net.fabricmc.loom.util.LfWriter; import net.fabricmc.loom.util.ModPlatform; +import net.fabricmc.loom.util.ModUtils; import net.fabricmc.loom.util.Pair; import net.fabricmc.loom.util.SidedClassVisitor; import net.fabricmc.loom.util.ZipUtils; @@ -116,9 +112,6 @@ import net.fabricmc.loom.util.service.UnsafeWorkQueueHelper; import net.fabricmc.lorenztiny.TinyMappingsReader; public abstract class RemapJarTask extends AbstractRemapJarTask { - public static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; - public static final String MANIFEST_NAMESPACE_KEY = "Fabric-Mapping-Namespace"; - @InputFiles public abstract ConfigurableFileCollection getNestedJars(); @@ -128,9 +121,6 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { @Input public abstract Property getAddNestedDependencies(); - @Input - public abstract Property getIncludesClientOnlyClasses(); - /** * Gets the jar paths to the access wideners that will be converted to ATs for Forge runtime. * If you specify multiple files, they will be merged into one. @@ -170,7 +160,6 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { getClasspath().from(getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)); getAddNestedDependencies().convention(true).finalizeValueOnRead(); - getIncludesClientOnlyClasses().convention(false).finalizeValueOnRead(); getReadMixinConfigsFromManifest().convention(LoomGradleExtension.get(getProject()).isForge()).finalizeValueOnRead(); getInjectAccessWidener().convention(false); @@ -221,7 +210,6 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { } } - params.getJarManifestService().set(JarManifestService.get(getProject())); params.getTinyRemapperBuildServiceUuid().set(UnsafeWorkQueueHelper.create(getProject(), tinyRemapperService.get())); params.getRemapClasspath().from(getClasspath()); @@ -246,20 +234,6 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { if (!getAtAccessWideners().get().isEmpty()) { params.getMappingBuildServiceUuid().set(UnsafeWorkQueueHelper.create(getProject(), MappingsService.createDefault(getProject(), getSourceNamespace().get(), getTargetNamespace().get()))); } - - if (getIncludesClientOnlyClasses().get()) { - if (!extension.areEnvironmentSourceSetsSplit()) { - throw new UnsupportedOperationException("Jar cannot include client only classes as the sources are not split"); - } - - final List clientOnlyJarEntries = getClientOnlyJarEntries(); - params.getManifestAttributes().set(Map.of( - "Fabric-Loom-Split-Environment", "true", - "Fabric-Loom-Client-Only-Entries", String.join(";", clientOnlyJarEntries) - )); - - params.getClientOnlyClasses().set(clientOnlyJarEntries.stream().filter(s -> s.endsWith(".class")).toList()); - } }); } @@ -268,7 +242,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { final MixinExtension mixinExtension = extension.getMixin(); Collection allMixinConfigs = null; - final JsonObject fabricModJson = extension.getPlatform().get() == ModPlatform.FABRIC ? MixinRefmapHelper.readFabricModJson(getInputFile().getAsFile().get()) : null; + final JsonObject fabricModJson = extension.getPlatform().get() == ModPlatform.FABRIC ? ModUtils.getFabricModJson(getInputFile().getAsFile().get().toPath()) : null; if (fabricModJson == null) { if (extension.getPlatform().get() == ModPlatform.QUILT) { @@ -374,12 +348,8 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { record RefmapData(List mixinConfigs, String refmapName) implements Serializable { } ListProperty getMixinData(); - Property getJarManifestService(); Property getTinyRemapperBuildServiceUuid(); Property getMappingBuildServiceUuid(); - - MapProperty getManifestAttributes(); - ListProperty getClientOnlyClasses(); } public abstract static class RemapAction extends AbstractRemapAction { @@ -401,7 +371,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { remap(); - if (getParameters().getClientOnlyClasses().isPresent()) { + if (getParameters().getClientOnlyEntries().isPresent()) { markClientOnlyClasses(); } @@ -439,7 +409,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { } private void markClientOnlyClasses() throws IOException { - final Stream>> tranformers = getParameters().getClientOnlyClasses().get().stream() + final Stream>> tranformers = getParameters().getClientOnlyEntries().get().stream() .map(s -> new Pair<>(s, (ZipUtils.AsmClassOperator) classVisitor -> SidedClassVisitor.CLIENT.insertApplyVisitor(null, classVisitor) )); @@ -564,21 +534,6 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { JarNester.nestJars(jars, forgeNestedJars.getOrElse(List.of()), outputFile.toFile(), getParameters().getPlatform().get(), LOGGER); } - private void modifyJarManifest() throws IOException { - int count = ZipUtils.transform(outputFile, Map.of(MANIFEST_PATH, bytes -> { - var manifest = new Manifest(new ByteArrayInputStream(bytes)); - - getParameters().getJarManifestService().get().apply(manifest, getParameters().getManifestAttributes().get()); - manifest.getMainAttributes().putValue(MANIFEST_NAMESPACE_KEY, getParameters().getTargetNamespace().get()); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - manifest.write(out); - return out.toByteArray(); - })); - - Preconditions.checkState(count > 0, "Did not transform any jar manifest"); - } - private void addRefmaps() throws IOException { if (getParameters().getUseMixinExtension().get()) { return; @@ -596,7 +551,8 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { } } - private List getClientOnlyJarEntries() { + @Override + protected List getClientOnlyEntries() { final SourceSet clientSourceSet = MinecraftSourceSets.Split.getClientSourceSet(getProject()); final ConfigurableFileCollection output = getProject().getObjects().fileCollection(); @@ -613,33 +569,6 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { .toList(); } - private static List getRootPaths(Set files) { - return files.stream() - .map(root -> { - String rootPath = root.getAbsolutePath().replace("\\", "/"); - - if (rootPath.charAt(rootPath.length() - 1) != '/') { - rootPath += '/'; - } - - return rootPath; - }).toList(); - } - - private static Function relativePath(List rootPaths) { - return file -> { - String s = file.getAbsolutePath().replace("\\", "/"); - - for (String rootPath : rootPaths) { - if (s.startsWith(rootPath)) { - s = s.substring(rootPath.length()); - } - } - - return s; - }; - } - @Internal public TinyRemapperService getTinyRemapperService() { return tinyRemapperService.get(); diff --git a/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java index 099e4ae6..c65b1c54 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java @@ -26,15 +26,18 @@ package net.fabricmc.loom.task; import java.io.IOException; import java.nio.file.Files; +import java.util.List; import javax.inject.Inject; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.provider.Property; +import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskAction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.task.service.SourceRemapperService; import net.fabricmc.loom.util.service.UnsafeWorkQueueHelper; @@ -53,6 +56,15 @@ public abstract class RemapSourcesJarTask extends AbstractRemapJarTask { }); } + @Override + protected List getClientOnlyEntries() { + final SourceSet clientSourceSet = MinecraftSourceSets.Split.getClientSourceSet(getProject()); + + return clientSourceSet.getAllSource().getFiles().stream() + .map(relativePath(getRootPaths(clientSourceSet.getAllSource().getSrcDirs()))) + .toList(); + } + public interface RemapSourcesParams extends AbstractRemapParams { Property getSourcesRemapperServiceUuid(); } @@ -73,6 +85,7 @@ public abstract class RemapSourcesJarTask extends AbstractRemapJarTask { try { sourceRemapperService.remapSourcesJar(inputFile, outputFile); + modifyJarManifest(); rewriteJar(); } catch (Exception e) { try { diff --git a/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java b/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java index f62a80c9..07982cc7 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java +++ b/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java @@ -150,6 +150,7 @@ public class RemapTaskConfiguration { task.dependsOn(sourcesJarTask); task.getInputFile().convention(sourcesJarTask.getArchiveFile()); + task.getIncludesClientOnlyClasses().set(project.provider(extension::areEnvironmentSourceSetsSplit)); }); tasks.named(BasePlugin.ASSEMBLE_TASK_NAME).configure(task -> task.dependsOn(remapSourcesTask)); diff --git a/src/main/java/net/fabricmc/loom/util/FileSystemUtil.java b/src/main/java/net/fabricmc/loom/util/FileSystemUtil.java index 157decf5..a031ffa0 100644 --- a/src/main/java/net/fabricmc/loom/util/FileSystemUtil.java +++ b/src/main/java/net/fabricmc/loom/util/FileSystemUtil.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2016-2017 FabricMC + * Copyright (c) 2016-2022 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,20 +27,17 @@ package net.fabricmc.loom.util; import java.io.File; import java.io.IOException; import java.net.URI; -import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; -import java.nio.file.FileSystemAlreadyExistsException; -import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import java.util.Collections; -import java.util.Map; import java.util.function.Supplier; +import net.fabricmc.tinyremapper.FileSystemReference; + public final class FileSystemUtil { - public record Delegate(FileSystem fs, boolean owner) implements AutoCloseable, Supplier { + public record Delegate(FileSystemReference reference) implements AutoCloseable, Supplier { public byte[] readAllBytes(String path) throws IOException { Path fsPath = get().getPath(path); @@ -57,50 +54,36 @@ public final class FileSystemUtil { @Override public void close() throws IOException { - if (owner) { - fs.close(); - } + reference.close(); } @Override public FileSystem get() { - return fs; + return reference.getFs(); + } + + // TODO cleanup + public FileSystem fs() { + return get(); } } private FileSystemUtil() { } - private static final Map jfsArgsCreate = Map.of("create", "true"); - private static final Map jfsArgsEmpty = Collections.emptyMap(); - public static Delegate getJarFileSystem(File file, boolean create) throws IOException { - return getJarFileSystem(file.toURI(), create); + return new Delegate(FileSystemReference.openJar(file.toPath(), create)); } public static Delegate getJarFileSystem(Path path, boolean create) throws IOException { - return getJarFileSystem(path.toUri(), create); + return new Delegate(FileSystemReference.openJar(path, create)); } public static Delegate getJarFileSystem(Path path) throws IOException { - return getJarFileSystem(path, false); + return new Delegate(FileSystemReference.openJar(path)); } public static Delegate getJarFileSystem(URI uri, boolean create) throws IOException { - URI jarUri; - - try { - jarUri = new URI("jar:" + uri.getScheme(), uri.getHost(), uri.getPath(), uri.getFragment()); - } catch (URISyntaxException e) { - throw new IOException(e); - } - - try { - return new Delegate(FileSystems.newFileSystem(jarUri, create ? jfsArgsCreate : jfsArgsEmpty), true); - } catch (FileSystemAlreadyExistsException e) { - return new Delegate(FileSystems.getFileSystem(jarUri), false); - } catch (IOException e) { - throw new IOException("Could not create JAR file system for " + uri + " (create: " + create + ")", e); - } + return new Delegate(FileSystemReference.open(uri, create)); } } diff --git a/src/main/java/net/fabricmc/loom/util/ModUtils.java b/src/main/java/net/fabricmc/loom/util/ModUtils.java index f1076080..40c8ac81 100644 --- a/src/main/java/net/fabricmc/loom/util/ModUtils.java +++ b/src/main/java/net/fabricmc/loom/util/ModUtils.java @@ -27,15 +27,12 @@ package net.fabricmc.loom.util; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; import com.google.gson.JsonObject; import org.gradle.api.logging.Logger; import org.jetbrains.annotations.Nullable; -import net.fabricmc.loom.LoomGradlePlugin; - public final class ModUtils { private ModUtils() { } @@ -56,19 +53,11 @@ public final class ModUtils { @Nullable public static JsonObject getFabricModJson(Path path) { - final byte[] modJsonBytes; - try { - modJsonBytes = ZipUtils.unpackNullable(path, "fabric.mod.json"); + return ZipUtils.unpackGsonNullable(path, "fabric.mod.json", JsonObject.class); } catch (IOException e) { throw new UncheckedIOException("Failed to extract fabric.mod.json from " + path, e); } - - if (modJsonBytes == null) { - return null; - } - - return LoomGradlePlugin.GSON.fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class); } public static boolean shouldRemapMod(Logger logger, File input, Object id, ModPlatform platform, String config) { diff --git a/src/main/java/net/fabricmc/loom/util/ZipUtils.java b/src/main/java/net/fabricmc/loom/util/ZipUtils.java index da6b1003..2d1be1e6 100644 --- a/src/main/java/net/fabricmc/loom/util/ZipUtils.java +++ b/src/main/java/net/fabricmc/loom/util/ZipUtils.java @@ -107,6 +107,25 @@ public class ZipUtils { } } + public static T unpackGson(Path zip, String path, Class clazz) throws IOException { + final byte[] bytes = unpack(zip, path); + return LoomGradlePlugin.GSON.fromJson(new String(bytes, StandardCharsets.UTF_8), clazz); + } + + @Nullable + public static T unpackGsonNullable(Path zip, String path, Class clazz) throws IOException { + try { + return unpackGson(zip, path, clazz); + } catch (NoSuchFileException e) { + return null; + } + } + + public static T unpackJackson(Path zip, String path, Class clazz) throws IOException { + final byte[] bytes = unpack(zip, path); + return LoomGradlePlugin.OBJECT_MAPPER.readValue(new String(bytes, StandardCharsets.UTF_8), clazz); + } + public static void pack(Path from, Path zip) throws IOException { Files.deleteIfExists(zip); diff --git a/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy b/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy index 4dcecedd..c3dd4727 100644 --- a/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy @@ -46,6 +46,13 @@ trait GradleProjectTestTrait { File projectDir = options.projectDir as File ?: options.sharedFiles ? sharedProjectDir : File.createTempDir() File gradleHomeDir = options.gradleHomeDir as File ?: options.sharedFiles ? sharedGradleHomeDir : File.createTempDir() + // Useful for faster local testing. + def homeDirOverride = System.getProperty("fabric.loom.test.homeDir") + + if (homeDirOverride) { + gradleHomeDir = new File(homeDirOverride) + } + setupProject(options, projectDir) println([