diff options
Diffstat (limited to 'src/main/java/net/fabricmc/loom')
40 files changed, 969 insertions, 368 deletions
diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index 71ce957e..6c3541e9 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -50,12 +50,11 @@ import net.fabricmc.loom.configuration.providers.forge.SrgProvider; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; import net.fabricmc.loom.extension.LoomFiles; -import net.fabricmc.loom.extension.LoomGradleExtensionImpl; import net.fabricmc.loom.extension.MixinApExtension; public interface LoomGradleExtension extends LoomGradleExtensionAPI { static LoomGradleExtension get(Project project) { - return project.getExtensions().getByType(LoomGradleExtensionImpl.class); + return (LoomGradleExtension) project.getExtensions().getByName("loom"); } LoomFiles getFiles(); @@ -100,8 +99,6 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI { boolean isRootProject(); - boolean isShareCaches(); - default boolean ideSync() { return Boolean.parseBoolean(System.getProperty("idea.sync.active", "false")); } @@ -111,7 +108,8 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI { return String.format("https://maven.fabricmc.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar", minecraftVersion); } - MixinApExtension getMixinApExtension(); + @Override + MixinApExtension getMixin(); // =================== // Architectury Loom diff --git a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java index a79c5a8d..8cd9cb5a 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java +++ b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java @@ -38,6 +38,7 @@ import com.google.gson.GsonBuilder; import org.gradle.api.Project; import org.gradle.api.plugins.PluginAware; +import net.fabricmc.loom.api.LoomGradleExtensionAPI; import net.fabricmc.loom.bootstrap.BootstrappedPlugin; import net.fabricmc.loom.configuration.CompileConfiguration; import net.fabricmc.loom.configuration.FabricApiExtension; @@ -47,6 +48,7 @@ import net.fabricmc.loom.configuration.providers.mappings.MappingsCache; import net.fabricmc.loom.decompilers.DecompilerConfiguration; import net.fabricmc.loom.extension.LoomGradleExtensionImpl; import net.fabricmc.loom.extension.LoomFilesImpl; +import net.fabricmc.loom.extension.MinecraftGradleExtension; import net.fabricmc.loom.task.LoomTasks; public class LoomGradlePlugin implements BootstrappedPlugin { @@ -86,9 +88,9 @@ public class LoomGradlePlugin implements BootstrappedPlugin { project.apply(ImmutableMap.of("plugin", "eclipse")); project.apply(ImmutableMap.of("plugin", "idea")); - // Setup extensions, loom shadows minecraft - project.getExtensions().create(LoomGradleExtension.class, "minecraft", LoomGradleExtensionImpl.class, project, new LoomFilesImpl(project)); - project.getExtensions().add("loom", project.getExtensions().getByName("minecraft")); + // Setup extensions, minecraft wraps loom + var extension = project.getExtensions().create(LoomGradleExtensionAPI.class, "loom", LoomGradleExtensionImpl.class, project, new LoomFilesImpl(project)); + project.getExtensions().create(LoomGradleExtensionAPI.class, "minecraft", MinecraftGradleExtension.class, extension); project.getExtensions().create("fabricApi", FabricApiExtension.class, project); CompileConfiguration.setupConfigurations(project); diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index 146be30d..5230f9b9 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -34,6 +34,9 @@ import org.gradle.api.Action; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.artifacts.Dependency; import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; import org.gradle.api.provider.Property; import org.gradle.api.tasks.SourceSet; import org.jetbrains.annotations.ApiStatus; @@ -44,31 +47,77 @@ import net.fabricmc.loom.configuration.ide.RunConfigSettings; import net.fabricmc.loom.configuration.launch.LaunchProviderSettings; import net.fabricmc.loom.configuration.processors.JarProcessor; import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuilder; +import net.fabricmc.loom.util.DeprecationHelper; import net.fabricmc.loom.util.ModPlatform; /** * This is the public api available exposed to build scripts. */ public interface LoomGradleExtensionAPI { - File getAccessWidener(); + @ApiStatus.Internal + DeprecationHelper getDeprecationHelper(); + + RegularFileProperty getAccessWidenerPath(); + + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "0.11") + default File getAccessWidener() { + getDeprecationHelper().replaceWithInLoom0_11("accessWidener", "accessWidenerPath"); + return getAccessWidenerPath().getAsFile().getOrNull(); + } + + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "0.11") + default void setAccessWidener(File file) { + getDeprecationHelper().replaceWithInLoom0_11("accessWidener", "accessWidenerPath"); + getAccessWidenerPath().set(file); + } - void setAccessWidener(Object file); + Property<Boolean> getShareRemapCaches(); - void setShareCaches(boolean shareCaches); + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "0.11") + default void setShareCaches(boolean shareCaches) { + getDeprecationHelper().replaceWithInLoom0_11("shareCaches", "shareRemapCaches"); + getShareRemapCaches().set(shareCaches); + } - boolean isShareCaches(); + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "0.11") + default boolean isShareCaches() { + getDeprecationHelper().replaceWithInLoom0_11("shareCaches", "shareRemapCaches"); + return getShareRemapCaches().get(); + } default void shareCaches() { - setShareCaches(true); + getShareRemapCaches().set(true); + } + + ListProperty<LoomDecompiler> getGameDecompilers(); + + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "0.11") + default List<LoomDecompiler> getDecompilers() { + getDeprecationHelper().replaceWithInLoom0_11("decompilers", "gameDecompilers"); + return getGameDecompilers().get(); } - List<LoomDecompiler> getDecompilers(); + default void addDecompiler(LoomDecompiler decompiler) { + getGameDecompilers().add(decompiler); + } - void addDecompiler(LoomDecompiler decompiler); + ListProperty<JarProcessor> getGameJarProcessors(); - List<JarProcessor> getJarProcessors(); + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "0.11") + default List<JarProcessor> getJarProcessors() { + getDeprecationHelper().replaceWithInLoom0_11("jarProcessors", "gameJarProcessors"); + return getGameJarProcessors().get(); + } - void addJarProcessor(JarProcessor processor); + default void addJarProcessor(JarProcessor processor) { + getGameJarProcessors().add(processor); + } ConfigurableFileCollection getLog4jConfigs(); @@ -78,13 +127,35 @@ public interface LoomGradleExtensionAPI { Dependency layered(Action<LayeredMappingSpecBuilder> action); - String getRefmapName(); + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "0.11") + default String getRefmapName() { + getDeprecationHelper().replaceWithInLoom0_11("refmapName", "mixin.defaultRefmapName"); + return getMixin().getDefaultRefmapName().get(); + } + + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "0.11") + default void setRefmapName(String refmapName) { + getDeprecationHelper().replaceWithInLoom0_11("refmapName", "mixin.defaultRefmapName"); + getMixin().getDefaultRefmapName().set(refmapName); + } - void setRefmapName(String refmapName); + Property<Boolean> getRemapArchives(); - boolean isRemapMod(); + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "0.11") + default boolean isRemapMod() { + getDeprecationHelper().replaceWithInLoom0_11("remapMod", "remapArchives"); + return getRemapArchives().get(); + } - void setRemapMod(boolean remapMod); + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "0.11") + default void setRemapMod(boolean remapMod) { + getDeprecationHelper().replaceWithInLoom0_11("remapMod", "remapArchives"); + getRemapArchives().set(remapMod); + } void runs(Action<NamedDomainObjectContainer<RunConfigSettings>> action); @@ -93,9 +164,24 @@ public interface LoomGradleExtensionAPI { @ApiStatus.Experimental void mixin(Action<MixinApExtensionAPI> action); - void setCustomManifest(String customManifest); + @ApiStatus.Experimental + MixinApExtensionAPI getMixin(); + + Property<String> getCustomMinecraftManifest(); - String getCustomManifest(); + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "0.11") + default void setCustomManifest(String customManifest) { + getDeprecationHelper().replaceWithInLoom0_11("customManifest", "customMinecraftManifest"); + getCustomMinecraftManifest().set(customManifest); + } + + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "0.11") + default String getCustomManifest() { + getDeprecationHelper().replaceWithInLoom0_11("customManifest", "customMinecraftManifest"); + return getCustomMinecraftManifest().getOrNull(); + } // =================== // Architectury Loom diff --git a/src/main/java/net/fabricmc/loom/api/MixinApExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/MixinApExtensionAPI.java index 88b7fa79..e74eecbf 100644 --- a/src/main/java/net/fabricmc/loom/api/MixinApExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/MixinApExtensionAPI.java @@ -25,17 +25,19 @@ package net.fabricmc.loom.api; import org.gradle.api.Action; +import org.gradle.api.provider.Property; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.util.PatternSet; import org.jetbrains.annotations.ApiStatus; @ApiStatus.Experimental public interface MixinApExtensionAPI { + Property<String> getDefaultRefmapName(); + /** * Apply Mixin AP to sourceSet. * @param sourceSet the sourceSet that applies Mixin AP. - * @param refmapName the output ref-map name. By default this will - * be {@link net.fabricmc.loom.LoomGradleExtension#getRefmapName()} + * @param refmapName the output ref-map name. By default this will be {@link #getDefaultRefmapName()} * @param action used for filter the mixin json files. By default this will be all files * with name {@code *.mixins.json} that is inside the {@code resources} folder * of {@code sourceSet}. diff --git a/src/main/java/net/fabricmc/loom/build/JarRemapper.java b/src/main/java/net/fabricmc/loom/build/JarRemapper.java index d97bf925..5996401b 100644 --- a/src/main/java/net/fabricmc/loom/build/JarRemapper.java +++ b/src/main/java/net/fabricmc/loom/build/JarRemapper.java @@ -43,6 +43,7 @@ import org.gradle.api.Action; import org.gradle.api.Project; import org.objectweb.asm.commons.Remapper; +import net.fabricmc.loom.util.CloseableList; import net.fabricmc.loom.util.LoggerFilter; import net.fabricmc.stitch.util.Pair; @@ -101,31 +102,35 @@ public class JarRemapper { } } - List<OutputConsumerPath> outputConsumers = new ArrayList<>(); + //noinspection MismatchedQueryAndUpdateOfCollection + try (CloseableList<OutputConsumerPath> outputConsumers = new CloseableList<>()) { + for (RemapData data : remapData) { + OutputConsumerPath outputConsumer; + project.getLogger().info(":remapper output -> " + data.output.getFileName().toString()); - for (RemapData data : remapData) { - OutputConsumerPath outputConsumer; - project.getLogger().info(":remapper output -> " + data.output.getFileName().toString()); - - try { - Files.deleteIfExists(data.output); - outputConsumer = new OutputConsumerPath.Builder(data.output).build(); - } catch (Exception e) { - throw new RuntimeException("Failed to create remapper output " + data.output.getFileName().toString(), e); - } + try { + Files.deleteIfExists(data.output); + outputConsumer = new OutputConsumerPath.Builder(data.output).build(); + } catch (Exception e) { + throw new RuntimeException("Failed to create remapper output " + data.output.getFileName().toString(), e); + } - outputConsumers.add(outputConsumer); + outputConsumers.add(outputConsumer); - outputConsumer.addNonClassFiles(data.input); + outputConsumer.addNonClassFiles(data.input); - data.processAccessWidener(remapper.getRemapper()); - remapper.apply(outputConsumer, data.tag); - } + data.processAccessWidener(remapper.getRemapper()); + remapper.apply(outputConsumer, data.tag); + } - remapper.finish(); + remapper.finish(); + } catch (Exception e) { + for (RemapData data : remapData) { + // Cleanup bad outputs + Files.deleteIfExists(data.output); + } - for (OutputConsumerPath outputConsumer : outputConsumers) { - outputConsumer.close(); + throw new IOException("Failed to remap %s files".formatted(remapData.size()), e); } remapData.forEach(RemapData::complete); diff --git a/src/main/java/net/fabricmc/loom/build/MixinRefmapHelper.java b/src/main/java/net/fabricmc/loom/build/MixinRefmapHelper.java index 458a5ffb..d7452267 100644 --- a/src/main/java/net/fabricmc/loom/build/MixinRefmapHelper.java +++ b/src/main/java/net/fabricmc/loom/build/MixinRefmapHelper.java @@ -25,13 +25,23 @@ package net.fabricmc.loom.build; import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; import java.nio.file.Path; +import java.util.Collection; +import java.util.Collections; import java.util.Objects; +import java.util.stream.Collectors; import java.util.stream.Stream; +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.gradle.api.Project; +import org.jetbrains.annotations.NotNull; import org.zeroturnaround.zip.ZipUtil; import org.zeroturnaround.zip.transform.StringZipEntryTransformer; import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry; @@ -43,29 +53,78 @@ import net.fabricmc.loom.extension.MixinApExtension; public final class MixinRefmapHelper { private MixinRefmapHelper() { } + private static final String FABRIC_MOD_JSON = "fabric.mod.json"; + public static boolean addRefmapName(Project project, Path outputPath) { - MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension(); - File output = outputPath.toFile(); - - return mixin.getMixinSourceSetsStream().map(sourceSet -> { - MixinApExtension.MixinInformationContainer container = Objects.requireNonNull( - MixinApExtension.getMixinInformationContainer(sourceSet) - ); - Stream<String> mixinJsonNames = container.getMixinJsonNames(); - String refmapName = container.getRefmapName(); - - return ZipUtil.transformEntries(output, mixinJsonNames.map(f -> new ZipEntryTransformerEntry(f, new StringZipEntryTransformer("UTF-8") { - @Override - protected String transform(ZipEntry zipEntry, String input) { - JsonObject json = LoomGradlePlugin.GSON.fromJson(input, JsonObject.class); - - if (!json.has("refmap")) { - json.addProperty("refmap", refmapName); + try { + MixinApExtension mixin = LoomGradleExtension.get(project).getMixin(); + File output = outputPath.toFile(); + + Collection<String> allMixinConfigs = getMixinConfigurationFiles(readFabricModJson(output)); + + return mixin.getMixinSourceSetsStream().map(sourceSet -> { + MixinApExtension.MixinInformationContainer container = Objects.requireNonNull( + MixinApExtension.getMixinInformationContainer(sourceSet) + ); + + Stream<String> mixinConfigs = sourceSet.getResources() + .matching(container.mixinConfigPattern()) + .getFiles() + .stream() + .map(File::getName) + .filter(allMixinConfigs::contains); + + String refmapName = container.refmapNameProvider().get(); + + return ZipUtil.transformEntries(output, mixinConfigs.map(f -> new ZipEntryTransformerEntry(f, new StringZipEntryTransformer("UTF-8") { + @Override + protected String transform(ZipEntry zipEntry, String input) { + JsonObject json = LoomGradlePlugin.GSON.fromJson(input, JsonObject.class); + + if (!json.has("refmap")) { + json.addProperty("refmap", refmapName); + } + + return LoomGradlePlugin.GSON.toJson(json); } + })).toArray(ZipEntryTransformerEntry[]::new)); + }).reduce(false, Boolean::logicalOr); + } catch (Exception e) { + project.getLogger().error(e.getMessage()); + return false; + } + } + + @NotNull + private static JsonObject readFabricModJson(File output) { + try (ZipFile zip = new ZipFile(output)) { + ZipEntry entry = zip.getEntry(FABRIC_MOD_JSON); + + 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 output jar.", e); + } + } - return LoomGradlePlugin.GSON.toJson(json); - } - })).toArray(ZipEntryTransformerEntry[]::new)); - }).reduce(false, Boolean::logicalOr); + @NotNull + private static Collection<String> getMixinConfigurationFiles(JsonObject fabricModJson) { + JsonArray mixins = fabricModJson.getAsJsonArray("mixins"); + + if (mixins == null) { + return Collections.emptySet(); + } + + return StreamSupport.stream(mixins.spliterator(), false) + .map(e -> { + if (e instanceof JsonPrimitive str) { + return str.getAsString(); + } else if (e instanceof JsonObject obj) { + return obj.get("config").getAsString(); + } else { + throw new RuntimeException("Incorrect fabric.mod.json format"); + } + }).collect(Collectors.toSet()); } } diff --git a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java index a9dca7f0..210016ed 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java @@ -65,7 +65,7 @@ public abstract class AnnotationProcessorInvoker<T extends Task> { } protected static Collection<Configuration> getApConfigurations(Project project, Function<String, String> getApConfigNameFunc) { - MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension(); + MixinApExtension mixin = LoomGradleExtension.get(project).getMixin(); return mixin.getApConfigurationsStream(getApConfigNameFunc).collect(Collectors.toList()); } @@ -80,7 +80,7 @@ public abstract class AnnotationProcessorInvoker<T extends Task> { private void passMixinArguments(T task, SourceSet sourceSet) { try { LoomGradleExtension loom = LoomGradleExtension.get(project); - String refmapName = Objects.requireNonNull(MixinApExtension.getMixinInformationContainer(sourceSet)).getRefmapName(); + String refmapName = Objects.requireNonNull(MixinApExtension.getMixinInformationContainer(sourceSet)).refmapNameProvider().get(); Map<String, String> args = new HashMap<>() {{ put(Constants.MixinArguments.IN_MAP_FILE_NAMED_INTERMEDIARY, loom.getMappingsProvider().tinyMappings.getCanonicalPath()); put(Constants.MixinArguments.OUT_MAP_FILE_NAMED_INTERMEDIARY, loom.getNextMixinMappings().getCanonicalPath()); diff --git a/src/main/java/net/fabricmc/loom/build/mixin/JavaApInvoker.java b/src/main/java/net/fabricmc/loom/build/mixin/JavaApInvoker.java index a449600a..b1109f1c 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/JavaApInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/JavaApInvoker.java @@ -46,7 +46,7 @@ public class JavaApInvoker extends AnnotationProcessorInvoker<JavaCompile> { } private static Map<SourceSet, JavaCompile> getInvokerTasks(Project project) { - MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension(); + MixinApExtension mixin = LoomGradleExtension.get(project).getMixin(); return mixin.getInvokerTasksStream(AnnotationProcessorInvoker.JAVA) .collect(Collectors.toMap(Map.Entry::getKey, entry -> Objects.requireNonNull((JavaCompile) entry.getValue()))); } diff --git a/src/main/java/net/fabricmc/loom/build/mixin/KaptApInvoker.java b/src/main/java/net/fabricmc/loom/build/mixin/KaptApInvoker.java index 3fea3c6e..d49a473a 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/KaptApInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/KaptApInvoker.java @@ -66,7 +66,7 @@ public class KaptApInvoker extends AnnotationProcessorInvoker<JavaCompile> { } private static Map<SourceSet, JavaCompile> getInvokerTasks(Project project) { - MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension(); + MixinApExtension mixin = LoomGradleExtension.get(project).getMixin(); return mixin.getInvokerTasksStream(AnnotationProcessorInvoker.JAVA) .collect(Collectors.toMap(Map.Entry::getKey, entry -> Objects.requireNonNull((JavaCompile) entry.getValue()))); } @@ -82,7 +82,7 @@ public class KaptApInvoker extends AnnotationProcessorInvoker<JavaCompile> { SourceSet sourceSet = entry.getKey(); task.doLast(t -> { try { - String refmapName = Objects.requireNonNull(MixinApExtension.getMixinInformationContainer(sourceSet)).getRefmapName(); + String refmapName = Objects.requireNonNull(MixinApExtension.getMixinInformationContainer(sourceSet)).refmapNameProvider().get(); Path src = Paths.get(getRefmapDestination(task, refmapName)); Path dest = Paths.get(task.getDestinationDir().toString(), refmapName); diff --git a/src/main/java/net/fabricmc/loom/build/mixin/ScalaApInvoker.java b/src/main/java/net/fabricmc/loom/build/mixin/ScalaApInvoker.java index 7a3238b7..8597cdd9 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/ScalaApInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/ScalaApInvoker.java @@ -47,7 +47,7 @@ public class ScalaApInvoker extends AnnotationProcessorInvoker<ScalaCompile> { } private static Map<SourceSet, ScalaCompile> getInvokerTasks(Project project) { - MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension(); + MixinApExtension mixin = LoomGradleExtension.get(project).getMixin(); return mixin.getInvokerTasksStream(AnnotationProcessorInvoker.SCALA) .collect(Collectors.toMap(Map.Entry::getKey, entry -> Objects.requireNonNull((ScalaCompile) entry.getValue()))); } diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index 42d6e0fd..e6589da5 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -29,7 +29,6 @@ import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.SourceSet; -import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.javadoc.Javadoc; import org.gradle.jvm.tasks.Jar; @@ -147,11 +146,6 @@ public final class CompileConfiguration { Javadoc javadoc = (Javadoc) p.getTasks().getByName(JavaPlugin.JAVADOC_TASK_NAME); javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath())); - p.getTasks().withType(JavaCompile.class).configureEach(compile -> { - // Fork the java compiler to ensure that it does not keep any files open. - compile.getOptions().setFork(true); - }); - p.afterEvaluate(project -> { LoomGradleExtension extension = LoomGradleExtension.get(project); @@ -186,9 +180,10 @@ public final class CompileConfiguration { SetupIntelijRunConfigs.setup(project); GenVsCodeProjectTask.generate(project); + extension.getRemapArchives().finalizeValue(); // Enables the default mod remapper - if (extension.isRemapMod()) { + if (extension.getRemapArchives().get()) { RemapConfiguration.setupDefaultRemap(project); } else { Jar jarTask = (Jar) project.getTasks().getByName("jar"); @@ -201,7 +196,7 @@ public final class CompileConfiguration { System.setProperty("log4j.skipJansi", "true"); project.getLogger().info("Configuring compiler arguments for Java"); - MixinApExtension mixinApExtension = LoomGradleExtension.get(project).getMixinApExtension(); + MixinApExtension mixinApExtension = LoomGradleExtension.get(project).getMixin(); mixinApExtension.init(); new JavaApInvoker(project).configureMixin(); diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java index b0568731..b14d2678 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java @@ -25,6 +25,7 @@ package net.fabricmc.loom.configuration; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -170,7 +171,15 @@ public class LoomDependencyManager { ModCompileRemapper.remapDependencies(project, mappingsKey, extension, sourceRemapper); - sourceRemapper.remapAll(); + long start = System.currentTimeMillis(); + + try { + sourceRemapper.remapAll(); + } catch (IOException exception) { + throw new RuntimeException("Failed to remap mod sources", exception); + } + + project.getLogger().info("Source remapping took: %dms".formatted(System.currentTimeMillis() - start)); for (Runnable runnable : afterTasks) { runnable.run(); diff --git a/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java index f8d45018..6afcf2c1 100644 --- a/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java @@ -105,7 +105,7 @@ public class RemapConfiguration { // TODO what is this for? Task parentTask = project.getTasks().getByName("build"); - if (extension.isShareCaches()) { + if (extension.getShareRemapCaches().get()) { Project rootProject = project.getRootProject(); if (extension.isRootProject()) { @@ -116,7 +116,6 @@ public class RemapConfiguration { rootProject.getTasks().register(remapAllSourcesTaskName, RemapAllSourcesTask.class, task -> { task.sourceRemapper = sourceRemapper; - task.doLast(t -> sourceRemapper.remapAll()); }); parentTask = rootProject.getTasks().getByName(remapAllSourcesTaskName); @@ -165,7 +164,7 @@ public class RemapConfiguration { }); } - if (extension.isShareCaches()) { + if (extension.getShareRemapCaches().get()) { remapSourcesJarTask.setSourceRemapper(remapper); } diff --git a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerJarProcessor.java b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerJarProcessor.java index ef2f37e1..cecf4d34 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerJarProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerJarProcessor.java @@ -71,16 +71,22 @@ public class AccessWidenerJarProcessor implements JarProcessor { } @Override + public String getId() { + return "loom:access_widener"; + } + + @Override public void setup() { LoomGradleExtension loomGradleExtension = LoomGradleExtension.get(project); + File awPath = loomGradleExtension.getAccessWidenerPath().get().getAsFile(); - if (!loomGradleExtension.getAccessWidener().exists()) { - throw new RuntimeException("Could not find access widener file @ " + loomGradleExtension.getAccessWidener().getAbsolutePath()); + if (!awPath.exists()) { + throw new RuntimeException("Could not find access widener file @ " + awPath.getAbsolutePath()); } - inputHash = Checksum.sha256(loomGradleExtension.getAccessWidener()); + inputHash = Checksum.sha256(awPath); - try (BufferedReader reader = new BufferedReader(new FileReader(loomGradleExtension.getAccessWidener()))) { + try (BufferedReader reader = new BufferedReader(new FileReader(awPath))) { accessWidenerReader.read(reader); } catch (IOException e) { throw new RuntimeException("Failed to read project access widener file"); 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 a9335764..8c60619f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java @@ -52,6 +52,7 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.InstallerData; import net.fabricmc.loom.util.OperatingSystem; public class RunConfig { @@ -269,7 +270,13 @@ public class RunConfig { } private static String getMainClass(String side, LoomGradleExtension extension, String defaultMainClass) { - JsonObject installerJson = extension.getInstallerData() == null ? null : extension.getInstallerData().installerJson(); + InstallerData installerData = extension.getInstallerData() == null ? null : extension.getInstallerData(); + + if (installerData == null) { + return defaultMainClass; + } + + JsonObject installerJson = installerData.installerJson(); if (installerJson != null && installerJson.has("mainClass")) { JsonElement mainClassJson = installerJson.get("mainClass"); diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/JarProcessor.java b/src/main/java/net/fabricmc/loom/configuration/processors/JarProcessor.java index 33cde23b..71f9a6bf 100644 --- a/src/main/java/net/fabricmc/loom/configuration/processors/JarProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/JarProcessor.java @@ -27,6 +27,18 @@ package net.fabricmc.loom.configuration.processors; import java.io.File; public interface JarProcessor { + /** + * Returns a unique ID for this jar processor, containing all configuration details. + * + * <p>If the jar processor implementation class supports creating multiple jar processors with different effects, + * the needed configuration should also be included in this ID. Example: {@code path.to.MyJarProcessor#someOption}. + * + * @return the ID of this jar processor + */ + default String getId() { + return getClass().getName(); + } + void setup(); /** diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/JarProcessorManager.java b/src/main/java/net/fabricmc/loom/configuration/processors/JarProcessorManager.java index 9ee6864d..4c36c40d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/processors/JarProcessorManager.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/JarProcessorManager.java @@ -25,9 +25,27 @@ package net.fabricmc.loom.configuration.processors; import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; + +import com.google.common.hash.Hashing; +import com.google.common.io.CharSource; +import org.zeroturnaround.zip.ZipUtil; +import org.zeroturnaround.zip.transform.StreamZipEntryTransformer; +import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry; public class JarProcessorManager { + private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; + private static final String JAR_PROCESSOR_HASH_ATTRIBUTE = "Loom-Jar-Processor-Hash"; private final List<JarProcessor> jarProcessors; public JarProcessorManager(List<JarProcessor> jarProcessors) { @@ -47,13 +65,56 @@ public class JarProcessorManager { return true; } + String jarProcessorHash = getJarProcessorHash(); + + try (JarFile jar = new JarFile(file)) { + Attributes attributes = jar.getManifest().getMainAttributes(); + + if (!jarProcessorHash.equals(attributes.getValue(JAR_PROCESSOR_HASH_ATTRIBUTE))) { + return true; + } + } catch (IOException e) { + throw new UncheckedIOException("Could not check jar manifest of " + file, e); + } + return jarProcessors.stream().anyMatch(jarProcessor -> jarProcessor.isInvalid(file)); } + private String getJarProcessorHash() { + String jarProcessorIds = jarProcessors.stream() + .map(JarProcessor::getId) + .sorted() + .collect(Collectors.joining(";")); + + try { + return CharSource.wrap(jarProcessorIds) + .asByteSource(StandardCharsets.UTF_8) + .hash(Hashing.sha256()) + .toString(); + } catch (IOException e) { + throw new UncheckedIOException("Could not hash jar processor IDs", e); + } + } + public void process(File file) { for (JarProcessor jarProcessor : jarProcessors) { jarProcessor.process(file); } + + boolean manifestTransformed = ZipUtil.transformEntries(file, new ZipEntryTransformerEntry[] { + new ZipEntryTransformerEntry(MANIFEST_PATH, new StreamZipEntryTransformer() { + @Override + protected void transform(ZipEntry zipEntry, InputStream in, OutputStream out) throws IOException { + Manifest manifest = new Manifest(in); + manifest.getMainAttributes().putValue(JAR_PROCESSOR_HASH_ATTRIBUTE, getJarProcessorHash()); + manifest.write(out); + } + }) + }); + + if (!manifestTransformed) { + throw new RuntimeException("Could not add data to jar manifest in " + file); + } } public <T extends JarProcessor> T getByType(Class<T> tClass) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java index fa6e5703..2ac31e06 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java @@ -135,7 +135,7 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra } private void downloadMcJson(boolean offline) throws IOException { - if (getExtension().isShareCaches() && !getExtension().isRootProject() && versionManifestJson.exists() && !isRefreshDeps()) { + if (getExtension().getShareRemapCaches().get() && !getExtension().isRootProject() && versionManifestJson.exists() && !isRefreshDeps()) { return; } @@ -159,10 +159,10 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra Optional<ManifestVersion.Versions> optionalVersion = Optional.empty(); - if (getExtension().getCustomManifest() != null) { + if (getExtension().getCustomMinecraftManifest().isPresent()) { ManifestVersion.Versions customVersion = new ManifestVersion.Versions(); customVersion.id = minecraftVersion; - customVersion.url = getExtension().getCustomManifest(); + customVersion.url = getExtension().getCustomMinecraftManifest().get(); optionalVersion = Optional.of(customVersion); getProject().getLogger().lifecycle("Using custom minecraft manifest"); } @@ -226,7 +226,7 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra } private boolean hasRecentValidManifest() throws IOException { - if (getExtension().getCustomManifest() != null) { + if (getExtension().getCustomMinecraftManifest().isPresent()) { return false; } @@ -247,7 +247,7 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra } private void downloadJars(Logger logger) throws IOException { - if (getExtension().isShareCaches() && !getExtension().isRootProject() && minecraftClientJar.exists() && minecraftServerJar.exists() && !isRefreshDeps()) { + if (getExtension().getShareRemapCaches().get() && !getExtension().isRootProject() && minecraftClientJar.exists() && minecraftServerJar.exists() && !isRefreshDeps()) { return; } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GradleMappingContext.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GradleMappingContext.java index db478671..ced88424 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GradleMappingContext.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GradleMappingContext.java @@ -25,8 +25,9 @@ package net.fabricmc.loom.configuration.providers.mappings; import java.io.File; -import java.util.function.Supplier; +import java.io.IOException; +import org.apache.commons.io.FileUtils; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.logging.Logger; @@ -37,12 +38,11 @@ import net.fabricmc.loom.configuration.providers.MinecraftProvider; public class GradleMappingContext implements MappingContext { private final Project project; private final LoomGradleExtension extension; - private final Supplier<String> workingDirName; + private File workingDir; - public GradleMappingContext(Project project, Supplier<String> workingDirName) { + public GradleMappingContext(Project project) { this.project = project; this.extension = LoomGradleExtension.get(project); - this.workingDirName = workingDirName; } @Override @@ -62,10 +62,28 @@ public class GradleMappingContext implements MappingContext { } @Override + public File workingDirectory() { + if (workingDir == null) { + workingDir = new File(mappingsProvider().getMappingsDir().toFile(), "layered/" + minecraftProvider().minecraftVersion()); + + if (workingDir.exists()) { + try { + FileUtils.deleteDirectory(workingDir); + } catch (IOException e) { + getLogger().warn("Failed to cleanup layered mappings working directory: {}", e.getMessage()); + } + } + } + + return workingDir; + } + + @Override public File workingDirectory(String name) { - File tempDir = new File(mappingsProvider().getMappingsDir().toFile(), workingDirName.get()); - tempDir.mkdirs(); - return new File(tempDir, name); + File file = new File(workingDirectory(), name); + file.mkdirs(); + + return file; } @Override diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpec.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpec.java index 2e47af39..f42a3289 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpec.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpec.java @@ -27,8 +27,8 @@ package net.fabricmc.loom.configuration.providers.mappings; import java.util.List; public record LayeredMappingSpec(List<MappingsSpec<?>> layers) { - public String getVersion() { + public String getVersion(MappingContext context) { // TODO something better? - return "layered+hash.%d".formatted(Math.abs(hashCode())); + return "layered+hash.%d.minecraft.%s".formatted(Math.abs(hashCode()), context.minecraftVersion()); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsDependency.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsDependency.java index 1b5d673f..b253a114 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsDependency.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsDependency.java @@ -54,17 +54,16 @@ public class LayeredMappingsDependency implements SelfResolvingDependency { private final MappingContext mappingContext; private final LayeredMappingSpec layeredMappingSpec; - private final String version; + private String version = null; - public LayeredMappingsDependency(MappingContext mappingContext, LayeredMappingSpec layeredMappingSpec, String version) { + public LayeredMappingsDependency(MappingContext mappingContext, LayeredMappingSpec layeredMappingSpec) { this.mappingContext = mappingContext; this.layeredMappingSpec = layeredMappingSpec; - this.version = version; } @Override public Set<File> resolve() { - Path mappingsDir = mappingContext.mappingsProvider().getMappingsDir(); + Path mappingsDir = mappingContext.workingDirectory().toPath(); Path mappingsFile = mappingsDir.resolve(String.format("%s.%s-%s.tiny", GROUP, MODULE, getVersion())); if (!Files.exists(mappingsFile) || LoomGradlePlugin.refreshDeps) { @@ -115,6 +114,10 @@ public class LayeredMappingsDependency implements SelfResolvingDependency { @Override public String getVersion() { + if (version == null) { + version = layeredMappingSpec.getVersion(mappingContext); + } + return version; } @@ -129,7 +132,7 @@ public class LayeredMappingsDependency implements SelfResolvingDependency { @Override public Dependency copy() { - return new LayeredMappingsDependency(mappingContext, layeredMappingSpec, version); + return new LayeredMappingsDependency(mappingContext, layeredMappingSpec); } @Override diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingContext.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingContext.java index 3fbfb655..0ccd61ea 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingContext.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingContext.java @@ -41,6 +41,8 @@ public interface MappingContext { return minecraftProvider().minecraftVersion(); } + File workingDirectory(); + /** * Creates a temporary working dir to be used to store working files. */ 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 1de460e8..f15f6eca 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 @@ -277,11 +277,13 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings LoomGradleExtension extension = getExtension(); - if (extension.getAccessWidener() != null) { - extension.addJarProcessor(new AccessWidenerJarProcessor(getProject())); + if (extension.getAccessWidenerPath().isPresent()) { + extension.getGameJarProcessors().add(new AccessWidenerJarProcessor(getProject())); } - JarProcessorManager processorManager = new JarProcessorManager(extension.getJarProcessors()); + extension.getAccessWidenerPath().finalizeValue(); + extension.getGameJarProcessors().finalizeValue(); + JarProcessorManager processorManager = new JarProcessorManager(extension.getGameJarProcessors().get()); extension.setJarProcessorManager(processorManager); processorManager.setupProcessors(); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingLayer.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingLayer.java index 251ff986..4b4d407b 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingLayer.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingLayer.java @@ -44,15 +44,16 @@ import net.fabricmc.mappingio.MappingVisitor; import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; import net.fabricmc.mappingio.format.ProGuardReader; -public record MojangMappingLayer(MinecraftVersionMeta.Download clientDownload, +public record MojangMappingLayer(String minecraftVersion, + MinecraftVersionMeta.Download clientDownload, MinecraftVersionMeta.Download serverDownload, File workingDir, Logger logger, MojangMappingsSpec.SilenceLicenseOption silenceLicense) implements MappingLayer { @Override public void visit(MappingVisitor mappingVisitor) throws IOException { - var clientMappings = new File(workingDir(), "client.txt"); - var serverMappings = new File(workingDir(), "server.txt"); + var clientMappings = new File(workingDir(), "%s.client.txt".formatted(minecraftVersion)); + var serverMappings = new File(workingDir(), "%s.server.txt".formatted(minecraftVersion)); download(clientMappings, serverMappings); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingsSpec.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingsSpec.java index 67f22460..f304530f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingsSpec.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingsSpec.java @@ -78,6 +78,7 @@ public record MojangMappingsSpec(SilenceLicenseOption silenceLicense) implements } return new MojangMappingLayer( + context.minecraftVersion(), versionInfo.download(MANIFEST_CLIENT_MAPPINGS), versionInfo.download(MANIFEST_SERVER_MAPPINGS), context.workingDirectory("mojang"), diff --git a/src/main/java/net/fabricmc/loom/decompilers/DecompilerConfiguration.java b/src/main/java/net/fabricmc/loom/decompilers/DecompilerConfiguration.java index 40380a4d..bd51c4e8 100644 --- a/src/main/java/net/fabricmc/loom/decompilers/DecompilerConfiguration.java +++ b/src/main/java/net/fabricmc/loom/decompilers/DecompilerConfiguration.java @@ -36,7 +36,7 @@ public final class DecompilerConfiguration { public static void setup(Project project) { LoomGradleExtension extension = LoomGradleExtension.get(project); - extension.addDecompiler(new FabricFernFlowerDecompiler(project)); - extension.addDecompiler(new FabricCFRDecompiler(project)); + extension.getGameDecompilers().add(new FabricFernFlowerDecompiler(project)); + extension.getGameDecompilers().add(new FabricCFRDecompiler(project)); } } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index bf262615..e2a12f45 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -24,15 +24,6 @@ package net.fabricmc.loom.extension; -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.Set; import java.util.function.Consumer; import java.util.function.Supplier; @@ -42,7 +33,10 @@ import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.Project; import org.gradle.api.artifacts.Dependency; import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; import org.gradle.api.plugins.BasePluginConvention; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.provider.Property; import org.gradle.api.tasks.SourceSet; @@ -58,6 +52,7 @@ import net.fabricmc.loom.configuration.providers.mappings.GradleMappingContext; import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec; import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuilder; import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsDependency; +import net.fabricmc.loom.util.DeprecationHelper; import net.fabricmc.loom.util.ModPlatform; import net.fabricmc.loom.util.function.LazyBool; @@ -69,15 +64,14 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA private static final String PLATFORM_PROPERTY = "loom.platform"; private static final String INCLUDE_PROPERTY = "loom.forge.include"; - protected final List<LoomDecompiler> decompilers = new ArrayList<>(); - protected final List<JarProcessor> jarProcessors = new ArrayList<>(); + protected final DeprecationHelper deprecationHelper; + protected final ListProperty<LoomDecompiler> decompilers; + protected final ListProperty<JarProcessor> jarProcessors; protected final ConfigurableFileCollection log4jConfigs; - - protected File accessWidener = null; - protected boolean shareCaches = false; - protected String refmapName = null; - protected boolean remapMod = true; - protected String customManifest; + protected final RegularFileProperty accessWidener; + protected final Property<Boolean> shareCaches; + protected final Property<Boolean> remapArchives; + protected final Property<String> customManifest; private NamedDomainObjectContainer<RunConfigSettings> runConfigs; @@ -101,7 +95,19 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA protected LoomGradleExtensionApiImpl(Project project, LoomFiles directories) { this.runConfigs = project.container(RunConfigSettings.class, baseName -> new RunConfigSettings(project, baseName)); + this.decompilers = project.getObjects().listProperty(LoomDecompiler.class) + .empty(); + this.jarProcessors = project.getObjects().listProperty(JarProcessor.class) + .empty(); this.log4jConfigs = project.files(directories.getDefaultLog4jConfigFile()); + this.accessWidener = project.getObjects().fileProperty(); + this.shareCaches = project.getObjects().property(Boolean.class) + .convention(false); + this.remapArchives = project.getObjects().property(Boolean.class) + .convention(true); + this.customManifest = project.getObjects().property(String.class); + + this.deprecationHelper = new DeprecationHelper.ProjectBased(project); this.platform = project.getObjects().property(ModPlatform.class).convention(project.provider(Suppliers.memoize(() -> { Object platformProperty = project.findProperty(PLATFORM_PROPERTY); @@ -124,61 +130,46 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA } @Override - public File getAccessWidener() { - return accessWidener; + public DeprecationHelper getDeprecationHelper() { + return deprecationHelper; } @Override - public void setAccessWidener(Object file) { - Objects.requireNonNull(file, "Access widener file cannot be null"); - this.accessWidener = getProject().file(file); - } - - @Override - public void setShareCaches(boolean shareCaches) { - this.shareCaches = shareCaches; + public RegularFileProperty getAccessWidenerPath() { + return accessWidener; } @Override - public boolean isShareCaches() { + public Property<Boolean> getShareRemapCaches() { return shareCaches; } @Override - public List<LoomDecompiler> getDecompilers() { + public ListProperty<LoomDecompiler> getGameDecompilers() { return decompilers; } @Override - public void addDecompiler(LoomDecompiler decompiler) { - Objects.requireNonNull(decompiler, "Decompiler cannot be null"); - decompilers.add(decompiler); - } - - @Override - public List<JarProcessor> getJarProcessors() { + public ListProperty<JarProcessor> getGameJarProcessors() { return jarProcessors; } @Override - public void addJarProcessor(JarProcessor processor) { - Objects.requireNonNull(processor, "Jar processor cannot be null"); - jarProcessors.add(processor); - } - - @Override public Dependency layered(Action<LayeredMappingSpecBuilder> action) { LayeredMappingSpecBuilder builder = new LayeredMappingSpecBuilder(this); action.execute(builder); LayeredMappingSpec builtSpec = builder.build(); - return new LayeredMappingsDependency(new GradleMappingContext(getProject(), () -> { - return "layers/" + getMinecraftVersion() + "_" + builtSpec.getVersion().replace("+", "_").replace(".", "_"); - }), builtSpec, builtSpec.getVersion()); + return new LayeredMappingsDependency(new GradleMappingContext(getProject()), builtSpec); } protected abstract String getMinecraftVersion(); @Override + public Property<Boolean> getRemapArchives() { + return remapArchives; + } + + @Override public String getRefmapName() { if (refmapName == null || refmapName.isEmpty()) { String defaultRefmapName; @@ -197,16 +188,6 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA } @Override - public void setRefmapName(String refmapName) { - this.refmapName = refmapName; - } - - @Override - public void setRemapMod(boolean remapMod) { - this.remapMod = remapMod; - } - - @Override public void runs(Action<NamedDomainObjectContainer<RunConfigSettings>> action) { action.execute(runConfigs); } @@ -222,23 +203,12 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA } @Override - public boolean isRemapMod() { - return remapMod; - } - - @Override public void mixin(Action<MixinApExtensionAPI> action) { - action.execute(getMixinApExtension()); + action.execute(getMixin()); } @Override - public void setCustomManifest(String customManifest) { - Objects.requireNonNull(customManifest, "Custom manifest cannot be null"); - this.customManifest = customManifest; - } - - @Override - public String getCustomManifest() { + public Property<String> getCustomMinecraftManifest() { return customManifest; } @@ -246,8 +216,6 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA protected abstract LoomFiles getFiles(); - protected abstract MixinApExtension getMixinApExtension(); - @Override public void silentMojangMappingsLicense() { this.silentMojangMappingsLicense = true; @@ -390,6 +358,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA } @Override + public DeprecationHelper getDeprecationHelper() { + throw new RuntimeException("Yeah... something is really wrong"); + } + + @Override protected Project getProject() { throw new RuntimeException("Yeah... something is really wrong"); } @@ -400,7 +373,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA } @Override - protected MixinApExtension getMixinApExtension() { + public MixinApExtension getMixin() { throw new RuntimeException("Yeah... something is really wrong"); } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index e97a6e3c..feb6bc21 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -63,7 +63,8 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen public LoomGradleExtensionImpl(Project project, LoomFiles files) { super(project, files); this.project = project; - this.mixinApExtension = new MixinApExtensionImpl(project); + // Initiate with newInstance to allow gradle to decorate our extension + this.mixinApExtension = project.getObjects().newInstance(MixinApExtensionImpl.class, project); this.loomFiles = files; this.unmappedMods = project.files(); } @@ -166,7 +167,7 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen } @Override - public MixinApExtension getMixinApExtension() { + public MixinApExtension getMixin() { return this.mixinApExtension; } diff --git a/src/main/java/net/fabricmc/loom/extension/MinecraftGradleExtension.java b/src/main/java/net/fabricmc/loom/extension/MinecraftGradleExtension.java new file mode 100644 index 00000000..ae4804f7 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/extension/MinecraftGradleExtension.java @@ -0,0 +1,134 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.extension; + +import org.gradle.api.Action; +import org.gradle.api.NamedDomainObjectContainer; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; + +import net.fabricmc.loom.api.LoomGradleExtensionAPI; +import net.fabricmc.loom.api.MixinApExtensionAPI; +import net.fabricmc.loom.api.decompilers.LoomDecompiler; +import net.fabricmc.loom.configuration.ide.RunConfigSettings; +import net.fabricmc.loom.configuration.processors.JarProcessor; +import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuilder; +import net.fabricmc.loom.util.DeprecationHelper; + +public class MinecraftGradleExtension implements LoomGradleExtensionAPI { + private final LoomGradleExtensionAPI parent; + private boolean deprecationReported = false; + + public MinecraftGradleExtension(LoomGradleExtensionAPI parent) { + this.parent = parent; + } + + private void reportDeprecation() { + if (!deprecationReported) { + getDeprecationHelper().replaceWithInLoom0_11("minecraft", "loom"); + deprecationReported = true; + } + } + + @Override + public DeprecationHelper getDeprecationHelper() { + return parent.getDeprecationHelper(); + } + + @Override + public RegularFileProperty getAccessWidenerPath() { + reportDeprecation(); + return parent.getAccessWidenerPath(); + } + + @Override + public Property<Boolean> getShareRemapCaches() { + reportDeprecation(); + return parent.getShareRemapCaches(); + } + + @Override + public ListProperty<LoomDecompiler> getGameDecompilers() { + reportDeprecation(); + return parent.getGameDecompilers(); + } + + @Override + public ListProperty<JarProcessor> getGameJarProcessors() { + reportDeprecation(); + return parent.getGameJarProcessors(); + } + + @Override + public ConfigurableFileCollection getLog4jConfigs() { + reportDeprecation(); + return parent.getLog4jConfigs(); + } + + @Override + public Dependency layered(Action<LayeredMappingSpecBuilder> action) { + reportDeprecation(); + return parent.layered(action); + } + + @Override + public Property<Boolean> getRemapArchives() { + reportDeprecation(); + return parent.getRemapArchives(); + } + + @Override + public void runs(Action<NamedDomainObjectContainer<RunConfigSettings>> action) { + reportDeprecation(); + parent.runs(action); + } + + @Override + public NamedDomainObjectContainer<RunConfigSettings> getRunConfigs() { + reportDeprecation(); + return parent.getRunConfigs(); + } + + @Override + public void mixin(Action<MixinApExtensionAPI> action) { + reportDeprecation(); + parent.mixin(action); + } + + @Override + public MixinApExtensionAPI getMixin() { + reportDeprecation(); + return parent.getMixin(); + } + + @Override + public Property<String> getCustomMinecraftManifest() { + reportDeprecation(); + return parent.getCustomMinecraftManifest(); + } +} diff --git a/src/main/java/net/fabricmc/loom/extension/MixinApExtension.java b/src/main/java/net/fabricmc/loom/extension/MixinApExtension.java index e63700b1..73e78858 100644 --- a/src/main/java/net/fabricmc/loom/extension/MixinApExtension.java +++ b/src/main/java/net/fabricmc/loom/extension/MixinApExtension.java @@ -26,7 +26,6 @@ package net.fabricmc.loom.extension; import java.util.Collection; import java.util.Map; -import java.util.Objects; import java.util.function.Function; import java.util.stream.Stream; @@ -34,6 +33,7 @@ import org.gradle.api.InvalidUserDataException; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; import org.gradle.api.plugins.ExtraPropertiesExtension; +import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.util.PatternSet; @@ -55,42 +55,7 @@ public interface MixinApExtension extends MixinApExtensionAPI { * for configuring the mixin annotation processor. It's stored * in [SourceSet].ext.mixin. */ - final class MixinInformationContainer { - private final SourceSet sourceSet; - private final String refmapName; - private Stream<String> mixinJsonNames; - - final PatternSet mixinJsonPattern; - - public MixinInformationContainer(@NotNull SourceSet sourceSet, - @NotNull String refmapName, - @NotNull PatternSet mixinJsonPattern) { - this.sourceSet = sourceSet; - this.refmapName = refmapName; - this.mixinJsonPattern = mixinJsonPattern; - } - - void setMixinJsonNames(@NotNull Stream<String> mixinJsonNames) { - if (this.mixinJsonNames == null) { - this.mixinJsonNames = mixinJsonNames; - } - } - - @NotNull - public Stream<String> getMixinJsonNames() { - return Objects.requireNonNull(mixinJsonNames); - } - - @NotNull - public SourceSet getSourceSet() { - return sourceSet; - } - - @NotNull - public String getRefmapName() { - return refmapName; - } - } + record MixinInformationContainer(SourceSet sourceSet, Provider<String> refmapNameProvider, PatternSet mixinConfigPattern) { } @Nullable static MixinInformationContainer getMixinInformationContainer(SourceSet sourceSet) { diff --git a/src/main/java/net/fabricmc/loom/extension/MixinApExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/MixinApExtensionApiImpl.java index 76326380..db06c481 100644 --- a/src/main/java/net/fabricmc/loom/extension/MixinApExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/MixinApExtensionApiImpl.java @@ -28,15 +28,21 @@ import org.gradle.api.Action; import org.gradle.api.InvalidUserDataException; import org.gradle.api.Project; import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.util.PatternSet; -import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.MixinApExtensionAPI; public abstract class MixinApExtensionApiImpl implements MixinApExtensionAPI { protected abstract Project getProject(); - protected abstract PatternSet add0(SourceSet sourceSet, String refmapName); + + protected final PatternSet add0(SourceSet sourceSet, String refmapName) { + return add0(sourceSet, getProject().provider(() -> refmapName)); + } + + protected abstract PatternSet add0(SourceSet sourceSet, Provider<String> refmapName); @Override public void add(SourceSet sourceSet, String refmapName, Action<PatternSet> action) { @@ -51,14 +57,14 @@ public abstract class MixinApExtensionApiImpl implements MixinApExtensionAPI { @Override public void add(String sourceSetName, String refmapName, Action<PatternSet> action) { - // try to find sourceSet with name sourceSetName in this project - SourceSet sourceSet = getProject().getConvention().getPlugin(JavaPluginConvention.class) - .getSourceSets().findByName(sourceSetName); + add(sourceSetName, getProject().provider(() -> refmapName), action); + } - if (sourceSet == null) { - throw new InvalidUserDataException("No sourceSet " + sourceSetName + " was found"); - } + public void add(String sourceSetName, Provider<String> refmapName, Action<PatternSet> action) { + add(resolveSourceSet(sourceSetName), refmapName, action); + } + public void add(SourceSet sourceSet, Provider<String> refmapName, Action<PatternSet> action) { PatternSet pattern = add0(sourceSet, refmapName); action.execute(pattern); } @@ -70,8 +76,7 @@ public abstract class MixinApExtensionApiImpl implements MixinApExtensionAPI { @Override public void add(SourceSet sourceSet, Action<PatternSet> action) { - LoomGradleExtension extension = LoomGradleExtension.get(getProject()); - add(sourceSet, extension.getRefmapName(), action); + add(sourceSet, getDefaultRefmapName(), action); } @Override @@ -81,8 +86,7 @@ public abstract class MixinApExtensionApiImpl implements MixinApExtensionAPI { @Override public void add(String sourceSetName, Action<PatternSet> action) { - LoomGradleExtension extension = LoomGradleExtension.get(getProject()); - add(sourceSetName, extension.getRefmapName(), action); + add(sourceSetName, getDefaultRefmapName(), action); } @Override @@ -90,6 +94,18 @@ public abstract class MixinApExtensionApiImpl implements MixinApExtensionAPI { add(sourceSetName, x -> { }); } + private SourceSet resolveSourceSet(String sourceSetName) { + // try to find sourceSet with name sourceSetName in this project + SourceSet sourceSet = getProject().getConvention().getPlugin(JavaPluginConvention.class) + .getSourceSets().findByName(sourceSetName); + + if (sourceSet == null) { + throw new InvalidUserDataException("No sourceSet " + sourceSetName + " was found"); + } + + return sourceSet; + } + // This is here to ensure that LoomGradleExtensionApiImpl compiles without any unimplemented methods private final class EnsureCompile extends MixinApExtensionApiImpl { private EnsureCompile() { @@ -103,7 +119,12 @@ public abstract class MixinApExtensionApiImpl implements MixinApExtensionAPI { } @Override - protected PatternSet add0(SourceSet sourceSet, String refmapName) { + public Property<String> getDefaultRefmapName() { + throw new RuntimeException("Yeah... something is really wrong"); + } + + @Override + protected PatternSet add0(SourceSet sourceSet, Provider<String> refmapName) { throw new RuntimeException("Yeah... something is really wrong"); } } diff --git a/src/main/java/net/fabricmc/loom/extension/MixinApExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/MixinApExtensionImpl.java index fd541106..3602e75a 100644 --- a/src/main/java/net/fabricmc/loom/extension/MixinApExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/MixinApExtensionImpl.java @@ -24,7 +24,6 @@ package net.fabricmc.loom.extension; -import java.io.File; import java.util.AbstractMap; import java.util.Collection; import java.util.Collections; @@ -33,11 +32,16 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.inject.Inject; + import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.UnknownTaskException; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.plugins.BasePluginConvention; import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.util.PatternSet; @@ -46,10 +50,14 @@ import org.jetbrains.annotations.NotNull; public class MixinApExtensionImpl extends MixinApExtensionApiImpl implements MixinApExtension { private boolean isDefault; private final Project project; + private final Property<String> defaultRefmapName; + @Inject public MixinApExtensionImpl(Project project) { this.isDefault = true; this.project = project; + this.defaultRefmapName = project.getObjects().property(String.class) + .convention(project.provider(this::getDefaultMixinRefmapName)); } @Override @@ -58,8 +66,19 @@ public class MixinApExtensionImpl extends MixinApExtensionApiImpl implements Mix } @Override - protected PatternSet add0(SourceSet sourceSet, String refmapName) { - PatternSet pattern = new PatternSet().setIncludes(Collections.singletonList("*.mixins.json")); + public Property<String> getDefaultRefmapName() { + return defaultRefmapName; + } + + private String getDefaultMixinRefmapName() { + String defaultRefmapName = getProject().getConvention().getPlugin(BasePluginConvention.class).getArchivesBaseName() + "-refmap.json"; + getProject().getLogger().info("Could not find refmap definition, will be using default name: " + defaultRefmapName); + return defaultRefmapName; + } + + @Override + protected PatternSet add0(SourceSet sourceSet, Provider<String> refmapName) { + PatternSet pattern = new PatternSet().setIncludes(Collections.singletonList("*.json")); MixinApExtension.setMixinInformationContainer(sourceSet, new MixinApExtension.MixinInformationContainer(sourceSet, refmapName, pattern)); isDefault = false; @@ -71,19 +90,7 @@ public class MixinApExtensionImpl extends MixinApExtensionApiImpl implements Mix @NotNull public Stream<SourceSet> getMixinSourceSetsStream() { return project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().stream() - .filter(sourceSet -> { - MixinApExtension.MixinInformationContainer container = MixinApExtension.getMixinInformationContainer(sourceSet); - - if (container != null) { - PatternSet pattern = container.mixinJsonPattern; - Stream<String> mixinJsonNames = sourceSet.getResources() - .matching(pattern).getFiles().stream().map(File::getName); - container.setMixinJsonNames(mixinJsonNames); - return true; - } - - return false; - }); + .filter(sourceSet -> MixinApExtension.getMixinInformationContainer(sourceSet) != null); } @Override diff --git a/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java b/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java index cad89ae8..796ac49f 100644 --- a/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java +++ b/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java @@ -42,7 +42,7 @@ public abstract class AbstractRunTask extends JavaExec { public AbstractRunTask(Function<Project, RunConfig> configProvider) { super(); - setGroup(Constants.TASK_CATEGORY); + setGroup(Constants.TaskGroup.FABRIC); this.config = configProvider.apply(getProject()); setClasspath(config.sourceSet.getRuntimeClasspath()); diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index e4bf8a54..883169f8 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.java @@ -51,7 +51,7 @@ public final class LoomTasks { tasks.register("remapJar", RemapJarTask.class, t -> { t.setDescription("Remaps the built project jar to intermediary mappings."); - t.setGroup(Constants.TASK_CATEGORY); + t.setGroup(Constants.TaskGroup.FABRIC); }); tasks.register("downloadAssets", DownloadAssetsTask.class, t -> t.setDescription("Downloads required assets for Fabric.")); @@ -67,24 +67,24 @@ public final class LoomTasks { tasks.register("genIdeaWorkspace", GenIdeaProjectTask.class, t -> { t.setDescription("Generates an IntelliJ IDEA workspace from this project."); t.dependsOn("idea", "downloadAssets"); - t.setGroup("ide"); + t.setGroup(Constants.TaskGroup.IDE); }); tasks.register("genEclipseRuns", GenEclipseRunsTask.class, t -> { t.setDescription("Generates Eclipse run configurations for this project."); t.dependsOn("downloadAssets"); - t.setGroup("ide"); + t.setGroup(Constants.TaskGroup.IDE); }); tasks.register("cleanEclipseRuns", CleanEclipseRunsTask.class, t -> { t.setDescription("Removes Eclipse run configurations for this project."); - t.setGroup("ide"); + t.setGroup(Constants.TaskGroup.IDE); }); tasks.register("vscode", GenVsCodeProjectTask.class, t -> { t.setDescription("Generates VSCode launch configurations."); t.dependsOn("downloadAssets"); - t.setGroup("ide"); + t.setGroup(Constants.TaskGroup.IDE); }); } @@ -148,7 +148,9 @@ public final class LoomTasks { inputJar = outputJar; } - for (LoomDecompiler decompiler : extension.getDecompilers()) { + extension.getGameDecompilers().finalizeValue(); + + for (LoomDecompiler decompiler : extension.getGameDecompilers().get()) { String taskName = decompiler instanceof FabricFernFlowerDecompiler ? "genSources" : "genSourcesWith" + decompiler.name(); // decompiler will be passed to the constructor of GenerateSourcesTask GenerateSourcesTask generateSourcesTask = tasks.register(taskName, GenerateSourcesTask.class, decompiler).get(); diff --git a/src/main/java/net/fabricmc/loom/task/RemapAllSourcesTask.java b/src/main/java/net/fabricmc/loom/task/RemapAllSourcesTask.java index 62bff631..4a9a44cf 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapAllSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapAllSourcesTask.java @@ -24,7 +24,10 @@ package net.fabricmc.loom.task; +import java.io.IOException; + import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.TaskAction; import net.fabricmc.loom.util.SourceRemapper; @@ -35,4 +38,13 @@ public class RemapAllSourcesTask extends AbstractLoomTask { public SourceRemapper getSourceRemapper() { return sourceRemapper; } + + @TaskAction + public void remap() { + try { + sourceRemapper.remapAll(); + } catch (IOException exception) { + throw new RuntimeException("Failed to remap mod", exception); + } + } } diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index 1c0a993c..f202b7f7 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -248,7 +248,7 @@ public class RemapJarTask extends Jar { jarRemapper.scheduleRemap(input, output) .supplyAccessWidener((remapData, remapper) -> { - if (getRemapAccessWidener().getOrElse(false) && extension.getAccessWidener() != null) { + if (getRemapAccessWidener().getOrElse(false) && extension.getAccessWidenerPath().isPresent()) { AccessWidenerJarProcessor accessWidenerJarProcessor = extension.getJarProcessorManager().getByType(AccessWidenerJarProcessor.class); byte[] data; diff --git a/src/main/java/net/fabricmc/loom/util/CloseableList.java b/src/main/java/net/fabricmc/loom/util/CloseableList.java new file mode 100644 index 00000000..3d8ebf09 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/CloseableList.java @@ -0,0 +1,52 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.util; + +import java.io.Closeable; +import java.io.IOException; +import java.util.ArrayList; + +public class CloseableList<T extends Closeable> extends ArrayList<T> implements Closeable { + @Override + public void close() throws IOException { + IOException exception = null; + + for (T t : this) { + try { + t.close(); + } catch (IOException e) { + if (exception == null) { + exception = new IOException("Failed to close list"); + } + + exception.addSuppressed(e); + } + } + + if (exception != null) { + throw exception; + } + } +} diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 7d46245f..e7fdea26 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -40,7 +40,6 @@ public class Constants { public static final String EXPERIMENTAL_VERSIONS = "https://maven.fabricmc.net/net/minecraft/experimental_versions.json"; public static final String SYSTEM_ARCH = System.getProperty("os.arch").equals("64") ? "64" : "32"; - public static final String TASK_CATEGORY = "loom"; public static final int ASM_VERSION = Opcodes.ASM9; @@ -136,6 +135,14 @@ public class Constants { } } + public static final class TaskGroup { + public static final String FABRIC = "loom"; + public static final String IDE = "ide"; + + private TaskGroup() { + } + } + public static final class ForgeUserDev { public static final String LAUNCH_TESTING = "net.minecraftforge.userdev.LaunchTesting"; diff --git a/src/main/java/net/fabricmc/loom/util/DeprecationHelper.java b/src/main/java/net/fabricmc/loom/util/DeprecationHelper.java new file mode 100644 index 00000000..f38c9022 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/DeprecationHelper.java @@ -0,0 +1,86 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.util; + +import java.util.concurrent.atomic.AtomicBoolean; + +import org.gradle.api.Project; +import org.gradle.api.logging.LogLevel; +import org.gradle.api.logging.configuration.WarningMode; + +public interface DeprecationHelper { + default void replaceWithInLoom0_10(String currentName, String newName) { + toBeRemovedIn(currentName, newName, "Loom 0.10"); + } + + default void replaceWithInLoom0_11(String currentName, String newName) { + toBeRemovedIn(currentName, newName, "Loom 0.11"); + } + + default void replaceWithInLoom0_12(String currentName, String newName) { + toBeRemovedIn(currentName, newName, "Loom 0.12"); + } + + default void toBeRemovedIn(String currentName, String newName, String removalVersion) { + warn("The '%s' property has been deprecated, and has been replaced with '%s'. This is scheduled to be removed in %s.".formatted(currentName, newName, removalVersion)); + } + + Project getProject(); + + void warn(String warning); + + class ProjectBased implements DeprecationHelper { + private final Project project; + private final AtomicBoolean usingDeprecatedApi = new AtomicBoolean(false); + + public ProjectBased(Project project) { + this.project = project; + + project.getGradle().buildFinished(buildResult -> { + if (usingDeprecatedApi.get()) { + project.getLogger().lifecycle("Deprecated Loom APIs were used in this build, making it incompatible with future versions of Loom. " + + "Use Gradle warning modes to control the verbosity of the warnings."); + } + }); + } + + @Override + public Project getProject() { + return project; + } + + @Override + public void warn(String warning) { + WarningMode warningMode = getProject().getGradle().getStartParameter().getWarningMode(); + getProject().getLogger().log(warningMode == WarningMode.None ? LogLevel.INFO : LogLevel.WARN, warning); + + if (warningMode == WarningMode.Fail) { + throw new UnsupportedOperationException(warning); + } + + usingDeprecatedApi.set(true); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java index ef9e61b0..c7bc394c 100644 --- a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java +++ b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java @@ -29,15 +29,23 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; -import java.util.function.Consumer; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import com.google.common.base.Stopwatch; import org.cadixdev.lorenz.MappingSet; import org.cadixdev.mercury.Mercury; +import org.cadixdev.mercury.SourceProcessor; import org.cadixdev.mercury.remapper.MercuryRemapper; import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPlugin; import org.zeroturnaround.zip.ZipUtil; import net.fabricmc.loom.LoomGradleExtension; @@ -52,9 +60,8 @@ public class SourceRemapper { private final Project project; private String from; private String to; - private final List<Consumer<Mercury>> remapTasks = new ArrayList<>(); - - private Mercury mercury; + private final List<RemapTask> remapTasks = new ArrayList<>(); + private boolean hasStartedRemap = false; public SourceRemapper(Project project, boolean named) { this(project, named ? intermediary(project) : "named", !named ? intermediary(project) : "named"); @@ -71,106 +78,165 @@ public class SourceRemapper { this.to = to; } - public static void remapSources(Project project, File input, File output, String from, String to, boolean reproducibleFileOrder, boolean preserveFileTimestamps) { + public static void remapSources(Project project, File input, File output, String from, String to, boolean reproducibleFileOrder, boolean preserveFileTimestamps) throws IOException { SourceRemapper sourceRemapper = new SourceRemapper(project, from, to); sourceRemapper.scheduleRemapSources(input, output, reproducibleFileOrder, preserveFileTimestamps); sourceRemapper.remapAll(); } public void scheduleRemapSources(File source, File destination, boolean reproducibleFileOrder, boolean preserveFileTimestamps) { - remapTasks.add((mercury) -> { - try { - remapSourcesInner(mercury, source, destination); - ZipReprocessorUtil.reprocessZip(destination, reproducibleFileOrder, preserveFileTimestamps); + this.scheduleRemapSources(new RemapTask(source, destination, reproducibleFileOrder, preserveFileTimestamps)); + } - // Set the remapped sources creation date to match the sources if we're likely succeeded in making it - destination.setLastModified(source.lastModified()); - } catch (Exception e) { - // Failed to remap, lets clean up to ensure we try again next time - destination.delete(); - throw new RuntimeException("Failed to remap sources for " + source, e); - } - }); + public synchronized void scheduleRemapSources(RemapTask data) { + if (hasStartedRemap) { + throw new UnsupportedOperationException("Cannot add data after remapping has started"); + } + + this.remapTasks.add(data); } - public void remapAll() { + public void remapAll() throws IOException { + hasStartedRemap = true; + if (remapTasks.isEmpty()) { return; } Stopwatch stopwatch = Stopwatch.createStarted(); project.getLogger().lifecycle(":remapping " + remapTasks.size() + " sources"); + int threads = Math.min(remapTasks.size(), Math.max(2, Runtime.getRuntime().availableProcessors())); + ExecutorService ioExecutor = Executors.newFixedThreadPool(2); + ExecutorService remapExecutor = Executors.newFixedThreadPool(threads); - Mercury mercury = getMercuryInstance(); - ThreadingUtils.run(remapTasks, consumer -> consumer.accept(MercuryUtils.copyMercury(mercury))); + List<CompletableFuture<Void>> completableFutures = new ArrayList<>(); - project.getLogger().lifecycle(":remapped " + remapTasks.size() + " sources in " + stopwatch.stop()); + { + // We have to build the Mercury instances on the main thread as createMercuryRemapper resolves gradle stuff + // TODO refactor this a bit to not do that. + var mercuryQueue = new ConcurrentLinkedDeque<Mercury>(); - // TODO: FIXME - WORKAROUND https://github.com/FabricMC/fabric-loom/issues/45 - System.gc(); - } + final var remapper = createMercuryRemapper(); + final var inputClasspath = getInputClasspath(project); - private void remapSourcesInner(Mercury mercury, File source, File destination) throws Exception { - Stopwatch stopwatch = Stopwatch.createStarted(); - project.getLogger().info(":remapping source jar " + source.getName() + " from " + from + " to " + to); + for (int i = 0; i < threads; i++) { + Mercury mercury = createMercuryWithClassPath(project, toNamed); + mercury.getProcessors().add(remapper); + mercury.getClassPath().addAll(inputClasspath); - if (source.equals(destination)) { - if (source.isDirectory()) { - throw new RuntimeException("Directories must differ!"); + mercuryQueue.add(mercury); } - source = new File(destination.getAbsolutePath().substring(0, destination.getAbsolutePath().lastIndexOf('.')) + "-dev.jar"); + ThreadLocal<Mercury> mercuryThreadLocal = ThreadLocal.withInitial(() -> Objects.requireNonNull(mercuryQueue.pop(), "No Mercury instances left")); + + for (RemapTask remapTask : this.remapTasks) { + completableFutures.add( + CompletableFuture.supplyAsync(() -> + prepareForRemap(remapTask), ioExecutor) + .thenApplyAsync(remapData -> doRemap(mercuryThreadLocal.get(), remapData), remapExecutor) + .thenAcceptAsync(remapData -> completeRemap(remapData, remapTask), ioExecutor) + ); + } + } + for (CompletableFuture<Void> future : completableFutures) { try { - com.google.common.io.Files.move(destination, source); - } catch (IOException e) { - throw new RuntimeException("Could not rename " + destination.getName() + "!", e); + future.join(); + } catch (CompletionException e) { + try { + throw e.getCause(); + } catch (IOException ioe) { + throw ioe; + } catch (Throwable unknown) { + throw new RuntimeException("An unknown exception occured when remapping", unknown); + } } } - Path srcPath = source.toPath(); - boolean isSrcTmp = false; + ioExecutor.shutdown(); + remapExecutor.shutdown(); - if (!source.isDirectory()) { - // create tmp directory - isSrcTmp = true; - srcPath = Files.createTempDirectory("fabric-loom-src"); - ZipUtil.unpack(source, srcPath.toFile()); - } + // TODO: FIXME - WORKAROUND https://github.com/FabricMC/fabric-loom/issues/45 + System.gc(); + } - if (!destination.isDirectory() && destination.exists()) { - if (!destination.delete()) { - throw new RuntimeException("Could not delete " + destination.getName() + "!"); + private RemapData prepareForRemap(RemapTask remapTask) { + try { + File source = remapTask.source(); + final File destination = remapTask.destination(); + + if (source.equals(destination)) { + if (source.isDirectory()) { + throw new RuntimeException("Directories must differ!"); + } + + source = new File(destination.getAbsolutePath().substring(0, destination.getAbsolutePath().lastIndexOf('.')) + "-dev.jar"); + + try { + com.google.common.io.Files.move(destination, source); + } catch (IOException e) { + throw new RuntimeException("Could not rename " + destination.getName() + "!", e); + } } - } - StitchUtil.FileSystemDelegate dstFs = destination.isDirectory() ? null : StitchUtil.getJarFileSystem(destination, true); - Path dstPath = dstFs != null ? dstFs.get().getPath("/") : destination.toPath(); + Path srcPath = source.toPath(); + boolean isSrcTmp = false; - try { - mercury.rewrite(srcPath, dstPath); - } catch (Exception e) { - project.getLogger().warn("Could not remap " + source.getName() + " fully!", e); - } + if (!source.isDirectory()) { + // create tmp directory + isSrcTmp = true; + srcPath = Files.createTempDirectory("fabric-loom-src"); + ZipUtil.unpack(source, srcPath.toFile()); + } + + if (!destination.isDirectory() && destination.exists()) { + if (!destination.delete()) { + throw new RuntimeException("Could not delete " + destination.getName() + "!"); + } + } - copyNonJavaFiles(srcPath, dstPath, project, source); + StitchUtil.FileSystemDelegate dstFs = remapTask.destination().isDirectory() ? null : StitchUtil.getJarFileSystem(remapTask.destination(), true); + Path dstPath = dstFs != null ? dstFs.get().getPath("/") : remapTask.destination().toPath(); - if (dstFs != null) { - dstFs.close(); + return new RemapData(Objects.requireNonNull(srcPath, "srcPath"), Objects.requireNonNull(dstPath, "dstPath"), dstFs, isSrcTmp); + } catch (IOException e) { + throw new CompletionException("Failed to prepare for remap", e); } + } - if (isSrcTmp) { - Files.walkFileTree(srcPath, new DeletingFileVisitor()); + private RemapData doRemap(Mercury mercury, RemapData remapData) { + try { + mercury.rewrite(remapData.source(), remapData.destination()); + } catch (Exception e) { + project.getLogger().warn("Could not remap " + remapData.source().toString() + " fully!", e); + // TODO do something more with this error? delete the dst file in complete remap? } - project.getLogger().info(":remapped source jar " + source.getName() + " from " + from + " to " + to + " in " + stopwatch.stop()); + return remapData; } - private Mercury getMercuryInstance() { - if (this.mercury != null) { - return this.mercury; + private void completeRemap(RemapData remapData, RemapTask remapTask) { + try { + copyNonJavaFiles(remapData.source(), remapData.destination(), project, remapTask.source()); + + if (remapData.dstFs() != null) { + remapData.dstFs().close(); + } + + if (remapData.isSrcTmp()) { + Files.walkFileTree(remapData.source(), new DeletingFileVisitor()); + } + + ZipReprocessorUtil.reprocessZip(remapTask.destination(), remapTask.reproducibleFileOrder(), remapTask.preserveFileTimestamps()); + + // Set the remapped sources creation date to match the sources if we're likely succeeded in making it + remapTask.destination().setLastModified(remapTask.source().lastModified()); + } catch (IOException e) { + throw new CompletionException("Failed to complete remap", e); } + } + private SourceProcessor createMercuryRemapper() { LoomGradleExtension extension = LoomGradleExtension.get(project); MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); @@ -193,42 +259,7 @@ public class SourceRemapper { } }); - Mercury mercury = extension.getOrCreateSrcMercuryCache(id, () -> { - Mercury m = createMercuryWithClassPath(project, to.equals("named")); - - for (File file : extension.getUnmappedModCollection()) { - Path path = file.toPath(); - - if (Files.isRegularFile(path)) { - m.getClassPath().add(path); - } - } - - m.getClassPath().add(extension.getMinecraftMappedProvider().getMappedJar().toPath()); - m.getClassPath().add(extension.getMinecraftMappedProvider().getIntermediaryJar().toPath()); - - if (extension.isForge()) { - m.getClassPath().add(extension.getMinecraftMappedProvider().getSrgJar().toPath()); - m.getClassPath().add(extension.getMinecraftMappedProvider().getForgeMappedJar().toPath()); - m.getClassPath().add(extension.getMinecraftMappedProvider().getForgeIntermediaryJar().toPath()); - m.getClassPath().add(extension.getMinecraftMappedProvider().getForgeSrgJar().toPath()); - } - - Set<File> files = project.getConfigurations() - .detachedConfiguration(project.getDependencies().create(Constants.Dependencies.JETBRAINS_ANNOTATIONS + Constants.Dependencies.Versions.JETBRAINS_ANNOTATIONS)) - .resolve(); - - for (File file : files) { - m.getClassPath().add(file.toPath()); - } - - m.getProcessors().add(MercuryRemapper.create(mappings)); - - return m; - }); - - this.mercury = mercury; - return mercury; + return MercuryRemapper.create(mappings); } private static void copyNonJavaFiles(Path from, Path to, Project project, File source) throws IOException { @@ -246,26 +277,62 @@ public class SourceRemapper { } public static Mercury createMercuryWithClassPath(Project project, boolean toNamed) { - Mercury m = new Mercury(); - m.setGracefulClasspathChecks(true); + var mercury = new Mercury(); + mercury.setGracefulClasspathChecks(true); + + var classpath = mercury.getClassPath(); + classpath.addAll(getCompileClasspath(project, toNamed)); + + return mercury; + } + + private static Set<Path> getCompileClasspath(Project project, boolean toNamed) { + var classpath = new HashSet<Path>(); for (File file : project.getConfigurations().getByName(Constants.Configurations.LOADER_DEPENDENCIES).getFiles()) { - m.getClassPath().add(file.toPath()); + classpath.add(file.toPath()); } if (!toNamed) { - for (File file : project.getConfigurations().getByName("compileClasspath").getFiles()) { - m.getClassPath().add(file.toPath()); + for (File file : project.getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).getFiles()) { + classpath.add(file.toPath()); } } else { for (RemappedConfigurationEntry entry : Constants.MOD_COMPILE_ENTRIES) { for (File inputFile : project.getConfigurations().getByName(entry.sourceConfiguration()).getFiles()) { - m.getClassPath().add(inputFile.toPath()); + classpath.add(inputFile.toPath()); } } } - return m; + return classpath; + } + + private static Set<Path> getInputClasspath(Project project) { + LoomGradleExtension extension = LoomGradleExtension.get(project); + + var classpath = new HashSet<Path>(); + + for (File file : extension.getUnmappedModCollection()) { + Path path = file.toPath(); + + if (Files.isRegularFile(path)) { + classpath.add(path); + } + } + + classpath.add(extension.getMinecraftMappedProvider().getMappedJar().toPath()); + classpath.add(extension.getMinecraftMappedProvider().getIntermediaryJar().toPath()); + + Set<File> files = project.getConfigurations() + .detachedConfiguration(project.getDependencies().create(Constants.Dependencies.JETBRAINS_ANNOTATIONS + Constants.Dependencies.Versions.JETBRAINS_ANNOTATIONS)) + .resolve(); + + for (File file : files) { + classpath.add(file.toPath()); + } + + return classpath; } private static boolean isJavaFile(Path path) { @@ -273,4 +340,10 @@ public class SourceRemapper { // ".java" is not a valid java file return name.endsWith(".java") && name.length() != 5; } + + public static record RemapTask(File source, File destination, boolean reproducibleFileOrder, boolean preserveFileTimestamps) { + } + + public static record RemapData(Path source, Path destination, StitchUtil.FileSystemDelegate dstFs, boolean isSrcTmp) { + } } |