diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index f1edd74..b8929f3 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -25,6 +25,7 @@ package net.fabricmc.loom; import java.io.File; +import java.nio.file.Path; import java.util.List; import java.util.function.Supplier; @@ -39,13 +40,15 @@ import org.gradle.api.file.FileCollection; import org.gradle.api.tasks.SourceSet; import net.fabricmc.loom.api.LoomGradleExtensionAPI; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.InstallerData; import net.fabricmc.loom.configuration.LoomDependencyManager; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile; import net.fabricmc.loom.configuration.processors.JarProcessorManager; -import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider; import net.fabricmc.loom.extension.LoomFiles; import net.fabricmc.loom.extension.MixinExtension; @@ -83,17 +86,31 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI { JarProcessorManager getJarProcessorManager(); - default MinecraftProviderImpl getMinecraftProvider() { - return getDependencyManager().getProvider(MinecraftProviderImpl.class); + MinecraftProvider getMinecraftProvider(); + + void setMinecraftProvider(MinecraftProvider minecraftProvider); + + MappingsProviderImpl getMappingsProvider(); + + void setMappingsProvider(MappingsProviderImpl mappingsProvider); + + NamedMinecraftProvider getNamedMinecraftProvider(); + + IntermediaryMinecraftProvider getIntermediaryMinecraftProvider(); + + void setNamedMinecraftProvider(NamedMinecraftProvider namedMinecraftProvider); + + void setIntermediaryMinecraftProvider(IntermediaryMinecraftProvider intermediaryMinecraftProvider); + + default List getMinecraftJars(MappingsNamespace mappingsNamespace) { + return switch (mappingsNamespace) { + case NAMED -> getNamedMinecraftProvider().getMinecraftJars(); + case INTERMEDIARY -> getIntermediaryMinecraftProvider().getMinecraftJars(); + case OFFICIAL -> getMinecraftProvider().getMinecraftJars(); + }; } - default MappingsProviderImpl getMappingsProvider() { - return getDependencyManager().getProvider(MappingsProviderImpl.class); - } - - default MinecraftMappedProvider getMinecraftMappedProvider() { - return getMappingsProvider().mappedProvider; - } + FileCollection getMinecraftJarsCollection(MappingsNamespace mappingsNamespace); File getMixinMappings(SourceSet sourceSet); diff --git a/src/main/java/net/fabricmc/loom/api/mappings/layered/MappingContext.java b/src/main/java/net/fabricmc/loom/api/mappings/layered/MappingContext.java index cbc7d3a..aaa7e50 100644 --- a/src/main/java/net/fabricmc/loom/api/mappings/layered/MappingContext.java +++ b/src/main/java/net/fabricmc/loom/api/mappings/layered/MappingContext.java @@ -30,8 +30,8 @@ import org.gradle.api.artifacts.Dependency; import org.gradle.api.logging.Logger; import org.jetbrains.annotations.ApiStatus; -import net.fabricmc.loom.configuration.providers.MinecraftProvider; import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; @ApiStatus.Experimental /* Very Experimental and not cleanly separated from the impl atm */ public interface MappingContext { diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index 1826d75..35f7050 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -24,7 +24,6 @@ package net.fabricmc.loom.configuration; -import java.io.File; import java.nio.charset.StandardCharsets; import org.gradle.api.NamedDomainObjectProvider; @@ -34,7 +33,6 @@ import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.tasks.AbstractCopyTask; import org.gradle.api.tasks.SourceSet; -import org.gradle.api.tasks.TaskContainer; import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.javadoc.Javadoc; @@ -42,12 +40,21 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.build.mixin.JavaApInvoker; import net.fabricmc.loom.build.mixin.KaptApInvoker; import net.fabricmc.loom.build.mixin.ScalaApInvoker; -import net.fabricmc.loom.configuration.providers.LaunchProvider; -import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; +import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor; +import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerJarProcessor; +import net.fabricmc.loom.configuration.decompile.MergedDecompileConfiguration; +import net.fabricmc.loom.configuration.decompile.SplitDecompileConfiguration; +import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor; +import net.fabricmc.loom.configuration.processors.JarProcessorManager; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; +import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.ProcessedNamedMinecraftProvider; import net.fabricmc.loom.extension.MixinExtension; -import net.fabricmc.loom.task.GenerateSourcesTask; -import net.fabricmc.loom.task.UnpickJarTask; import net.fabricmc.loom.util.Constants; public final class CompileConfiguration { @@ -123,6 +130,11 @@ public final class CompileConfiguration { extendsFrom(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES, project); extendsFrom(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES, project); + + // Add the dev time dependencies + project.getDependencies().add(Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES, Constants.Dependencies.DEV_LAUNCH_INJECTOR + Constants.Dependencies.Versions.DEV_LAUNCH_INJECTOR); + project.getDependencies().add(Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES, Constants.Dependencies.TERMINAL_CONSOLE_APPENDER + Constants.Dependencies.Versions.TERMINAL_CONSOLE_APPENDER); + project.getDependencies().add(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME, Constants.Dependencies.JETBRAINS_ANNOTATIONS + Constants.Dependencies.Versions.JETBRAINS_ANNOTATIONS); } public static void configureCompile(Project p) { @@ -135,13 +147,14 @@ public final class CompileConfiguration { }); p.afterEvaluate(project -> { + try { + setupMinecraft(project); + } catch (Exception e) { + throw new RuntimeException("Failed to setup minecraft", e); + } + LoomDependencyManager dependencyManager = new LoomDependencyManager(); extension.setDependencyManager(dependencyManager); - - dependencyManager.addProvider(new MinecraftProviderImpl(project)); - dependencyManager.addProvider(new MappingsProviderImpl(project)); - dependencyManager.addProvider(new LaunchProvider(project)); - dependencyManager.handleDependencies(project); extension.getRemapArchives().finalizeValue(); @@ -174,6 +187,82 @@ public final class CompileConfiguration { } } + // This is not thread safe across projects synchronize it here just to be sure, might be possible to move this further down, but for now this will do. + private static synchronized void setupMinecraft(Project project) throws Exception { + final LoomGradleExtension extension = LoomGradleExtension.get(project); + + boolean split = project.getProperties().get("fabric.loom.experimental.splitMcJars") != null; + + // Provide the vanilla mc jars + final MinecraftProvider minecraftProvider = split ? new SplitMinecraftProvider(project) : new MergedMinecraftProvider(project); + extension.setMinecraftProvider(minecraftProvider); + minecraftProvider.provide(); + + // Provide the mappings + final MappingsProviderImpl mappingsProvider = new MappingsProviderImpl(project, minecraftProvider); + extension.setMappingsProvider(mappingsProvider); + mappingsProvider.provide(); + + // Provide the remapped mc jars + final IntermediaryMinecraftProvider intermediaryMinecraftProvider; + NamedMinecraftProvider namedMinecraftProvider; + + if (split) { + intermediaryMinecraftProvider = new IntermediaryMinecraftProvider.SplitImpl(project, (SplitMinecraftProvider) minecraftProvider); + namedMinecraftProvider = new NamedMinecraftProvider.SplitImpl(project, (SplitMinecraftProvider) minecraftProvider); + } else { + intermediaryMinecraftProvider = new IntermediaryMinecraftProvider.MergedImpl(project, (MergedMinecraftProvider) minecraftProvider); + namedMinecraftProvider = new NamedMinecraftProvider.MergedImpl(project, (MergedMinecraftProvider) minecraftProvider); + } + + final JarProcessorManager jarProcessorManager = createJarProcessorManager(project); + + if (jarProcessorManager.active()) { + // Wrap the named MC provider for one that will provide the processed jars + if (split) { + namedMinecraftProvider = new ProcessedNamedMinecraftProvider.SplitImpl((NamedMinecraftProvider.SplitImpl) namedMinecraftProvider, jarProcessorManager); + } else { + namedMinecraftProvider = new ProcessedNamedMinecraftProvider.MergedImpl((NamedMinecraftProvider.MergedImpl) namedMinecraftProvider, jarProcessorManager); + } + } + + extension.setIntermediaryMinecraftProvider(intermediaryMinecraftProvider); + intermediaryMinecraftProvider.provide(true); + + extension.setNamedMinecraftProvider(namedMinecraftProvider); + namedMinecraftProvider.provide(true); + } + + private static JarProcessorManager createJarProcessorManager(Project project) { + final LoomGradleExtension extension = LoomGradleExtension.get(project); + + if (extension.getAccessWidenerPath().isPresent()) { + extension.getGameJarProcessors().add(new AccessWidenerJarProcessor(project)); + } + + if (extension.getEnableTransitiveAccessWideners().get()) { + TransitiveAccessWidenerJarProcessor transitiveAccessWidenerJarProcessor = new TransitiveAccessWidenerJarProcessor(project); + + if (!transitiveAccessWidenerJarProcessor.isEmpty()) { + extension.getGameJarProcessors().add(transitiveAccessWidenerJarProcessor); + } + } + + if (extension.getEnableInterfaceInjection().get()) { + InterfaceInjectionProcessor jarProcessor = new InterfaceInjectionProcessor(project); + + if (!jarProcessor.isEmpty()) { + extension.getGameJarProcessors().add(jarProcessor); + } + } + + JarProcessorManager processorManager = new JarProcessorManager(extension.getGameJarProcessors().get()); + extension.setJarProcessorManager(processorManager); + processorManager.setupProcessors(); + + return processorManager; + } + private static void setupMixinAp(Project project, MixinExtension mixin) { mixin.init(); @@ -198,39 +287,15 @@ public final class CompileConfiguration { } private static void configureDecompileTasks(Project project) { - final TaskContainer tasks = project.getTasks(); final LoomGradleExtension extension = LoomGradleExtension.get(project); - MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); - - File mappedJar = mappingsProvider.mappedProvider.getMappedJar(); - - if (mappingsProvider.hasUnpickDefinitions()) { - File outputJar = mappingsProvider.mappedProvider.getUnpickedJar(); - - tasks.register("unpickJar", UnpickJarTask.class, unpickJarTask -> { - unpickJarTask.getUnpickDefinitions().set(mappingsProvider.getUnpickDefinitionsFile()); - unpickJarTask.getInputJar().set(mappingsProvider.mappedProvider.getMappedJar()); - unpickJarTask.getOutputJar().set(outputJar); - }); - - mappedJar = outputJar; + if (extension.getNamedMinecraftProvider() instanceof MappedMinecraftProvider.Merged mergedMappedMinecraftProvider) { + new MergedDecompileConfiguration(project, mergedMappedMinecraftProvider).afterEvaluation(); + } else if (extension.getNamedMinecraftProvider() instanceof MappedMinecraftProvider.Split splitMinecraftProvider) { + new SplitDecompileConfiguration(project, splitMinecraftProvider).afterEvaluation(); + } else { + throw new UnsupportedOperationException(); } - - final File inputJar = mappedJar; - - extension.getGameDecompilers().configureEach(decompiler -> { - String taskName = "genSourcesWith" + decompiler.name(); - - // Set the input jar for the task after evaluation has occurred. - tasks.named(taskName, GenerateSourcesTask.class).configure(task -> { - task.getInputJar().set(inputJar); - - if (mappingsProvider.hasUnpickDefinitions()) { - task.dependsOn(tasks.named("unpickJar")); - } - }); - }); } private static void extendsFrom(String a, String b, Project project) { diff --git a/src/main/java/net/fabricmc/loom/configuration/DependencyInfo.java b/src/main/java/net/fabricmc/loom/configuration/DependencyInfo.java new file mode 100644 index 0000000..3c09e2b --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/DependencyInfo.java @@ -0,0 +1,140 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016-2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration; + +import java.io.File; +import java.util.Optional; +import java.util.Set; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.DependencySet; +import org.gradle.api.artifacts.ResolvedDependency; +import org.gradle.api.artifacts.SelfResolvingDependency; + +public class DependencyInfo { + final Project project; + final Dependency dependency; + final Configuration sourceConfiguration; + + private String resolvedVersion = null; + + public static DependencyInfo create(Project project, String configuration) { + return create(project, project.getConfigurations().getByName(configuration)); + } + + public static DependencyInfo create(Project project, Configuration configuration) { + DependencySet dependencies = configuration.getDependencies(); + + if (dependencies.isEmpty()) { + throw new IllegalArgumentException(String.format("Configuration '%s' has no dependencies", configuration.getName())); + } + + if (dependencies.size() != 1) { + throw new IllegalArgumentException(String.format("Configuration '%s' must only have 1 dependency", configuration.getName())); + } + + return create(project, dependencies.iterator().next(), configuration); + } + + public static DependencyInfo create(Project project, Dependency dependency, Configuration sourceConfiguration) { + if (dependency instanceof SelfResolvingDependency selfResolvingDependency) { + return new FileDependencyInfo(project, selfResolvingDependency, sourceConfiguration); + } else { + return new DependencyInfo(project, dependency, sourceConfiguration); + } + } + + DependencyInfo(Project project, Dependency dependency, Configuration sourceConfiguration) { + this.project = project; + this.dependency = dependency; + this.sourceConfiguration = sourceConfiguration; + } + + public Dependency getDependency() { + return dependency; + } + + public String getResolvedVersion() { + if (resolvedVersion != null) { + return resolvedVersion; + } + + for (ResolvedDependency rd : sourceConfiguration.getResolvedConfiguration().getFirstLevelModuleDependencies()) { + if (rd.getModuleGroup().equals(dependency.getGroup()) && rd.getModuleName().equals(dependency.getName())) { + resolvedVersion = rd.getModuleVersion(); + return resolvedVersion; + } + } + + resolvedVersion = dependency.getVersion(); + return resolvedVersion; + } + + public Configuration getSourceConfiguration() { + return sourceConfiguration; + } + + public Set resolve() { + if (dependency instanceof SelfResolvingDependency selfResolvingDependency) { + return selfResolvingDependency.resolve(); + } + + return sourceConfiguration.files(dependency); + } + + public Optional resolveFile() { + Set files = resolve(); + + if (files.isEmpty()) { + return Optional.empty(); + } else if (files.size() > 1) { + StringBuilder builder = new StringBuilder(this.toString()); + builder.append(" resolves to more than one file:"); + + for (File f : files) { + builder.append("\n\t-").append(f.getAbsolutePath()); + } + + throw new RuntimeException(builder.toString()); + } else { + return files.stream().findFirst(); + } + } + + @Override + public String toString() { + return getDepString(); + } + + public String getDepString() { + return dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion(); + } + + public String getResolvedDepString() { + return dependency.getGroup() + ":" + dependency.getName() + ":" + getResolvedVersion(); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/DependencyProvider.java b/src/main/java/net/fabricmc/loom/configuration/DependencyProvider.java deleted file mode 100644 index 74642f7..0000000 --- a/src/main/java/net/fabricmc/loom/configuration/DependencyProvider.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2016-2021 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.configuration; - -import java.io.File; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import com.google.common.collect.Iterables; -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import org.apache.commons.io.FilenameUtils; -import org.gradle.api.InvalidUserDataException; -import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.Dependency; -import org.gradle.api.artifacts.ResolvedDependency; -import org.gradle.api.artifacts.SelfResolvingDependency; - -import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.configuration.providers.MinecraftProvider; -import net.fabricmc.loom.extension.LoomFiles; -import net.fabricmc.loom.util.ZipUtils; - -public abstract class DependencyProvider { - private LoomDependencyManager dependencyManager; - private final Project project; - private final LoomGradleExtension extension; - - public DependencyProvider(Project project) { - this.project = project; - this.extension = LoomGradleExtension.get(project); - } - - public abstract void provide(DependencyInfo dependency, Consumer postPopulationScheduler) throws Exception; - - public abstract String getTargetConfig(); - - public Dependency addDependency(Object object, String target) { - if (object instanceof File) { - object = project.files(object); - } - - return project.getDependencies().add(target, object); - } - - public void register(LoomDependencyManager dependencyManager) { - this.dependencyManager = dependencyManager; - } - - public LoomDependencyManager getDependencyManager() { - return dependencyManager; - } - - public Project getProject() { - return project; - } - - public LoomGradleExtension getExtension() { - return extension; - } - - public LoomFiles getDirectories() { - return getExtension().getFiles(); - } - - public MinecraftProvider getMinecraftProvider() { - return getExtension().getMinecraftProvider(); - } - - public boolean isRefreshDeps() { - return LoomGradlePlugin.refreshDeps; - } - - public static class DependencyInfo { - final Project project; - final Dependency dependency; - final Configuration sourceConfiguration; - - private String resolvedVersion = null; - - public static DependencyInfo create(Project project, Dependency dependency, Configuration sourceConfiguration) { - if (dependency instanceof SelfResolvingDependency selfResolvingDependency) { - return new FileDependencyInfo(project, selfResolvingDependency, sourceConfiguration); - } else { - return new DependencyInfo(project, dependency, sourceConfiguration); - } - } - - private DependencyInfo(Project project, Dependency dependency, Configuration sourceConfiguration) { - this.project = project; - this.dependency = dependency; - this.sourceConfiguration = sourceConfiguration; - } - - public Dependency getDependency() { - return dependency; - } - - public String getResolvedVersion() { - if (resolvedVersion != null) { - return resolvedVersion; - } - - for (ResolvedDependency rd : sourceConfiguration.getResolvedConfiguration().getFirstLevelModuleDependencies()) { - if (rd.getModuleGroup().equals(dependency.getGroup()) && rd.getModuleName().equals(dependency.getName())) { - resolvedVersion = rd.getModuleVersion(); - return resolvedVersion; - } - } - - resolvedVersion = dependency.getVersion(); - return resolvedVersion; - } - - public Configuration getSourceConfiguration() { - return sourceConfiguration; - } - - public Set resolve() { - if (dependency instanceof SelfResolvingDependency selfResolvingDependency) { - return selfResolvingDependency.resolve(); - } - - return sourceConfiguration.files(dependency); - } - - public Optional resolveFile() { - Set files = resolve(); - - if (files.isEmpty()) { - return Optional.empty(); - } else if (files.size() > 1) { - StringBuilder builder = new StringBuilder(this.toString()); - builder.append(" resolves to more than one file:"); - - for (File f : files) { - builder.append("\n\t-").append(f.getAbsolutePath()); - } - - throw new RuntimeException(builder.toString()); - } else { - return files.stream().findFirst(); - } - } - - @Override - public String toString() { - return getDepString(); - } - - public String getDepString() { - return dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion(); - } - - public String getResolvedDepString() { - return dependency.getGroup() + ":" + dependency.getName() + ":" + getResolvedVersion(); - } - } - - public static class FileDependencyInfo extends DependencyInfo { - protected final Map classifierToFile = new HashMap<>(); - protected final Set resolvedFiles; - protected final String group, name, version; - - FileDependencyInfo(Project project, SelfResolvingDependency dependency, Configuration configuration) { - super(project, dependency, configuration); - - Set files = dependency.resolve(); - this.resolvedFiles = files; - switch (files.size()) { - case 0 -> //Don't think Gradle would ever let you do this - throw new IllegalStateException("Empty dependency?"); - case 1 -> //Single file dependency - classifierToFile.put("", Iterables.getOnlyElement(files)); - default -> { //File collection, try work out the classifiers - List sortedFiles = files.stream().sorted(Comparator.comparing(File::getName, Comparator.comparingInt(String::length))).collect(Collectors.toList()); - //First element in sortedFiles is the one with the shortest name, we presume all the others are different classifier types of this - File shortest = sortedFiles.remove(0); - String shortestName = FilenameUtils.removeExtension(shortest.getName()); //name.jar -> name - - for (File file : sortedFiles) { - if (!file.getName().startsWith(shortestName)) { - //If there is another file which doesn't start with the same name as the presumed classifier-less one we're out of our depth - throw new IllegalArgumentException("Unable to resolve classifiers for " + this + " (failed to sort " + files + ')'); - } - } - - //We appear to be right, therefore this is the normal dependency file we want - classifierToFile.put("", shortest); - int start = shortestName.length(); - - for (File file : sortedFiles) { - //Now we just have to work out what classifier type the other files are, this shouldn't even return an empty string - String classifier = FilenameUtils.removeExtension(file.getName()).substring(start); - - //The classifier could well be separated with a dash (thing name.jar and name-sources.jar), we don't want that leading dash - if (classifierToFile.put(classifier.charAt(0) == '-' ? classifier.substring(1) : classifier, file) != null) { - throw new InvalidUserDataException("Duplicate classifiers for " + this + " (\"" + file.getName().substring(start) + "\" in " + files + ')'); - } - } - } - } - - if (dependency.getGroup() != null && dependency.getVersion() != null) { - group = dependency.getGroup(); - name = dependency.getName(); - version = dependency.getVersion(); - } else { - group = "net.fabricmc.synthetic"; - File root = classifierToFile.get(""); //We've built the classifierToFile map, now to try find a name and version for our dependency - byte[] modJson; - - try { - if ("jar".equals(FilenameUtils.getExtension(root.getName())) && (modJson = ZipUtils.unpackNullable(root.toPath(), "fabric.mod.json")) != null) { - //It's a Fabric mod, see how much we can extract out - JsonObject json = new Gson().fromJson(new String(modJson, StandardCharsets.UTF_8), JsonObject.class); - - if (json == null || !json.has("id") || !json.has("version")) { - throw new IllegalArgumentException("Invalid Fabric mod jar: " + root + " (malformed json: " + json + ')'); - } - - if (json.has("name")) { //Go for the name field if it's got one - name = json.get("name").getAsString(); - } else { - name = json.get("id").getAsString(); - } - - version = json.get("version").getAsString(); - } else { - //Not a Fabric mod, just have to make something up - name = FilenameUtils.removeExtension(root.getName()); - version = "1.0"; - } - } catch (IOException e) { - throw new UncheckedIOException("Failed to read input file: " + root, e); - } - } - } - - @Override - public String getResolvedVersion() { - return version; - } - - @Override - public String getDepString() { - //Use our custom name and version with the dummy group rather than the null:unspecified:null it would otherwise return - return group + ':' + name + ':' + version; - } - - @Override - public String getResolvedDepString() { - return getDepString(); - } - - @Override - public Set resolve() { - return this.resolvedFiles; - } - } -} diff --git a/src/main/java/net/fabricmc/loom/configuration/FileDependencyInfo.java b/src/main/java/net/fabricmc/loom/configuration/FileDependencyInfo.java new file mode 100644 index 0000000..2f55bbe --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/FileDependencyInfo.java @@ -0,0 +1,149 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016-2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import com.google.common.collect.Iterables; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import org.apache.commons.io.FilenameUtils; +import org.gradle.api.InvalidUserDataException; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.SelfResolvingDependency; + +import net.fabricmc.loom.util.ZipUtils; + +public class FileDependencyInfo extends DependencyInfo { + protected final Map classifierToFile = new HashMap<>(); + protected final Set resolvedFiles; + protected final String group, name, version; + + FileDependencyInfo(Project project, SelfResolvingDependency dependency, Configuration configuration) { + super(project, dependency, configuration); + + Set files = dependency.resolve(); + this.resolvedFiles = files; + switch (files.size()) { + case 0 -> //Don't think Gradle would ever let you do this + throw new IllegalStateException("Empty dependency?"); + case 1 -> //Single file dependency + classifierToFile.put("", Iterables.getOnlyElement(files)); + default -> { //File collection, try work out the classifiers + List sortedFiles = files.stream().sorted(Comparator.comparing(File::getName, Comparator.comparingInt(String::length))).collect(Collectors.toList()); + //First element in sortedFiles is the one with the shortest name, we presume all the others are different classifier types of this + File shortest = sortedFiles.remove(0); + String shortestName = FilenameUtils.removeExtension(shortest.getName()); //name.jar -> name + + for (File file : sortedFiles) { + if (!file.getName().startsWith(shortestName)) { + //If there is another file which doesn't start with the same name as the presumed classifier-less one we're out of our depth + throw new IllegalArgumentException("Unable to resolve classifiers for " + this + " (failed to sort " + files + ')'); + } + } + + //We appear to be right, therefore this is the normal dependency file we want + classifierToFile.put("", shortest); + int start = shortestName.length(); + + for (File file : sortedFiles) { + //Now we just have to work out what classifier type the other files are, this shouldn't even return an empty string + String classifier = FilenameUtils.removeExtension(file.getName()).substring(start); + + //The classifier could well be separated with a dash (thing name.jar and name-sources.jar), we don't want that leading dash + if (classifierToFile.put(classifier.charAt(0) == '-' ? classifier.substring(1) : classifier, file) != null) { + throw new InvalidUserDataException("Duplicate classifiers for " + this + " (\"" + file.getName().substring(start) + "\" in " + files + ')'); + } + } + } + } + + if (dependency.getGroup() != null && dependency.getVersion() != null) { + group = dependency.getGroup(); + name = dependency.getName(); + version = dependency.getVersion(); + } else { + group = "net.fabricmc.synthetic"; + File root = classifierToFile.get(""); //We've built the classifierToFile map, now to try find a name and version for our dependency + byte[] modJson; + + try { + if ("jar".equals(FilenameUtils.getExtension(root.getName())) && (modJson = ZipUtils.unpackNullable(root.toPath(), "fabric.mod.json")) != null) { + //It's a Fabric mod, see how much we can extract out + JsonObject json = new Gson().fromJson(new String(modJson, StandardCharsets.UTF_8), JsonObject.class); + + if (json == null || !json.has("id") || !json.has("version")) { + throw new IllegalArgumentException("Invalid Fabric mod jar: " + root + " (malformed json: " + json + ')'); + } + + if (json.has("name")) { //Go for the name field if it's got one + name = json.get("name").getAsString(); + } else { + name = json.get("id").getAsString(); + } + + version = json.get("version").getAsString(); + } else { + //Not a Fabric mod, just have to make something up + name = FilenameUtils.removeExtension(root.getName()); + version = "1.0"; + } + } catch (IOException e) { + throw new UncheckedIOException("Failed to read input file: " + root, e); + } + } + } + + @Override + public String getResolvedVersion() { + return version; + } + + @Override + public String getDepString() { + //Use our custom name and version with the dummy group rather than the null:unspecified:null it would otherwise return + return group + ':' + name + ':' + version; + } + + @Override + public String getResolvedDepString() { + return getDepString(); + } + + @Override + public Set resolve() { + return this.resolvedFiles; + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java index 7f92402..a5dcb81 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java @@ -29,15 +29,12 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import com.google.gson.JsonObject; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Dependency; -import org.gradle.api.artifacts.DependencySet; import org.gradle.api.artifacts.ExternalModuleDependency; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; @@ -45,105 +42,17 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.LoomRepositoryPlugin; import net.fabricmc.loom.build.ModCompileRemapper; -import net.fabricmc.loom.configuration.DependencyProvider.DependencyInfo; import net.fabricmc.loom.configuration.ide.idea.IdeaUtils; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.SourceRemapper; import net.fabricmc.loom.util.ZipUtils; public class LoomDependencyManager { - private static class ProviderList { - private final String key; - private final List providers = new ArrayList<>(); - - ProviderList(String key) { - this.key = key; - } - } - - private final List dependencyProviderList = new ArrayList<>(); - - public T addProvider(T provider) { - if (dependencyProviderList.contains(provider)) { - throw new RuntimeException("Provider is already registered"); - } - - if (getProvider(provider.getClass()) != null) { - throw new RuntimeException("Provider of this type is already registered"); - } - - provider.register(this); - dependencyProviderList.add(provider); - return provider; - } - - public T getProvider(Class clazz) { - for (DependencyProvider provider : dependencyProviderList) { - if (provider.getClass() == clazz) { - return (T) provider; - } - } - - return null; - } - public void handleDependencies(Project project) { List afterTasks = new ArrayList<>(); - MappingsProviderImpl mappingsProvider = null; - project.getLogger().info(":setting up loom dependencies"); LoomGradleExtension extension = LoomGradleExtension.get(project); - Map providerListMap = new HashMap<>(); - List targetProviders = new ArrayList<>(); - - for (DependencyProvider provider : dependencyProviderList) { - providerListMap.computeIfAbsent(provider.getTargetConfig(), (k) -> { - ProviderList list = new ProviderList(k); - targetProviders.add(list); - return list; - }).providers.add(provider); - - if (provider instanceof MappingsProviderImpl) { - mappingsProvider = (MappingsProviderImpl) provider; - } - } - - if (mappingsProvider == null) { - throw new RuntimeException("Could not find MappingsProvider instance!"); - } - - for (ProviderList list : targetProviders) { - Configuration configuration = project.getConfigurations().getByName(list.key); - DependencySet dependencies = configuration.getDependencies(); - - if (dependencies.isEmpty()) { - throw new IllegalArgumentException(String.format("No '%s' dependency was specified!", list.key)); - } - - if (dependencies.size() > 1) { - throw new IllegalArgumentException(String.format("Only one '%s' dependency should be specified, but %d were!", - list.key, - dependencies.size()) - ); - } - - for (Dependency dependency : dependencies) { - for (DependencyProvider provider : list.providers) { - DependencyProvider.DependencyInfo info = DependencyInfo.create(project, dependency, configuration); - - try { - provider.provide(info, afterTasks::add); - } catch (Exception e) { - throw new RuntimeException("Failed to provide " + dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion() + " : " + e.toString(), e); - } - } - } - } - - SourceRemapper sourceRemapper = new SourceRemapper(project, true); - String mappingsIdentifier = mappingsProvider.mappingsIdentifier(); if (extension.getInstallerData() == null) { //If we've not found the installer JSON we've probably skipped remapping Fabric loader, let's go looking @@ -168,14 +77,17 @@ public class LoomDependencyManager { } } - if (extension.getInstallerData() == null) { - project.getLogger().warn("fabric-installer.json not found in classpath!"); - } + SourceRemapper sourceRemapper = new SourceRemapper(project, true); + String mappingsIdentifier = extension.getMappingsProvider().mappingsIdentifier(); ModCompileRemapper.remapDependencies(project, mappingsIdentifier, extension, sourceRemapper); sourceRemapper.remapAll(); + if (extension.getInstallerData() == null) { + project.getLogger().warn("fabric-installer.json not found in classpath!"); + } + for (Runnable runnable : afterTasks) { runnable.run(); } diff --git a/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerJarProcessor.java b/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerJarProcessor.java index 1305de7..00a1d34 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerJarProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerJarProcessor.java @@ -174,7 +174,10 @@ public class TransitiveAccessWidenerJarProcessor implements JarProcessor { TinyRemapper tinyRemapper = TinyRemapperHelper.getTinyRemapper(project, "intermediary", "named"); tinyRemapper.readClassPath(TinyRemapperHelper.getMinecraftDependencies(project)); - tinyRemapper.readClassPath(extension.getMinecraftMappedProvider().getIntermediaryJar().toPath()); + + for (Path minecraftJar : extension.getMinecraftJars(MappingsNamespace.INTERMEDIARY)) { + tinyRemapper.readClassPath(minecraftJar); + } return tinyRemapper; } catch (IOException e) { diff --git a/src/main/java/net/fabricmc/loom/configuration/decompile/MergedDecompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/decompile/MergedDecompileConfiguration.java new file mode 100644 index 0000000..c0ae75c --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/decompile/MergedDecompileConfiguration.java @@ -0,0 +1,92 @@ +/* + * 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.configuration.decompile; + +import java.io.File; + +import org.gradle.api.Project; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider; +import net.fabricmc.loom.task.GenerateSourcesTask; +import net.fabricmc.loom.task.UnpickJarTask; +import net.fabricmc.loom.util.Constants; + +public final class MergedDecompileConfiguration { + private final Project project; + private final MappedMinecraftProvider.Merged minecraftProvider; + private final LoomGradleExtension extension; + private final MappingsProviderImpl mappingsProvider; + + public MergedDecompileConfiguration(Project project, MappedMinecraftProvider.Merged minecraftProvider) { + this.project = project; + this.minecraftProvider = minecraftProvider; + this.extension = LoomGradleExtension.get(project); + this.mappingsProvider = extension.getMappingsProvider(); + } + + public void afterEvaluation() { + File mappedJar = minecraftProvider.getMergedJar().toFile(); + + if (mappingsProvider.hasUnpickDefinitions()) { + File outputJar = new File(extension.getMappingsProvider().mappingsWorkingDir().toFile(), "minecraft-unpicked.jar"); + + project.getTasks().register("unpickJar", UnpickJarTask.class, unpickJarTask -> { + unpickJarTask.getUnpickDefinitions().set(mappingsProvider.getUnpickDefinitionsFile()); + unpickJarTask.getInputJar().set(minecraftProvider.getMergedJar().toFile()); + unpickJarTask.getOutputJar().set(outputJar); + }); + + mappedJar = outputJar; + } + + final File inputJar = mappedJar; + + LoomGradleExtension.get(project).getGameDecompilers().forEach(decompiler -> { + String taskName = "genSourcesWith" + decompiler.name(); + // Decompiler will be passed to the constructor of GenerateSourcesTask + project.getTasks().register(taskName, GenerateSourcesTask.class, decompiler).configure(task -> { + task.getInputJar().set(inputJar); + task.getRuntimeJar().set(minecraftProvider.getMergedJar().toFile()); + + task.dependsOn(project.getTasks().named("validateAccessWidener")); + task.setDescription("Decompile minecraft using %s.".formatted(decompiler.name())); + task.setGroup(Constants.TaskGroup.FABRIC); + + if (mappingsProvider.hasUnpickDefinitions()) { + task.dependsOn(project.getTasks().named("unpickJar")); + } + }); + }); + + project.getTasks().register("genSources", task -> { + task.setDescription("Decompile minecraft using the default decompiler."); + task.setGroup(Constants.TaskGroup.FABRIC); + + task.dependsOn(project.getTasks().named("genSourcesWithCfr")); + }); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/decompile/SplitDecompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/decompile/SplitDecompileConfiguration.java new file mode 100644 index 0000000..d02574e --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/decompile/SplitDecompileConfiguration.java @@ -0,0 +1,132 @@ +/* + * 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.configuration.decompile; + +import java.io.File; + +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.tasks.TaskProvider; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider; +import net.fabricmc.loom.task.GenerateSourcesTask; +import net.fabricmc.loom.task.UnpickJarTask; +import net.fabricmc.loom.util.Constants; + +public final class SplitDecompileConfiguration { + private final Project project; + private final MappedMinecraftProvider.Split minecraftProvider; + private final LoomGradleExtension extension; + private final MappingsProviderImpl mappingsProvider; + + public SplitDecompileConfiguration(Project project, MappedMinecraftProvider.Split minecraftProvider) { + this.project = project; + this.minecraftProvider = minecraftProvider; + this.extension = LoomGradleExtension.get(project); + this.mappingsProvider = extension.getMappingsProvider(); + } + + public void afterEvaluation() { + File commonJarToDecompile = minecraftProvider.getCommonJar().toFile(); + File clientOnlyJarToDecompile = minecraftProvider.getClientOnlyJar().toFile(); + + TaskProvider unpickCommonJar = null; + TaskProvider unpickClientOnlyJar = null; + + if (mappingsProvider.hasUnpickDefinitions()) { + commonJarToDecompile = new File(extension.getMappingsProvider().mappingsWorkingDir().toFile(), "minecraft-common-unpicked.jar"); + clientOnlyJarToDecompile = new File(extension.getMappingsProvider().mappingsWorkingDir().toFile(), "minecraft-clientonly-unpicked.jar"); + + unpickCommonJar = createUnpickJarTask("Common", minecraftProvider.getCommonJar().toFile(), commonJarToDecompile); + unpickClientOnlyJar = createUnpickJarTask("ClientOnly", minecraftProvider.getClientOnlyJar().toFile(), clientOnlyJarToDecompile); + } + + // Need to re-declare them as final to access them from the lambada + final File commonJar = commonJarToDecompile; + final File clientOnlyJar = clientOnlyJarToDecompile; + final TaskProvider unpickCommonJarTask = unpickCommonJar; + final TaskProvider unpickClientOnlyJarTask = unpickClientOnlyJar; + + final TaskProvider commonDecompileTask = createDecompileTasks("Common", task -> { + task.getInputJar().set(commonJar); + task.getRuntimeJar().set(minecraftProvider.getCommonJar().toFile()); + + if (unpickCommonJarTask != null) { + task.dependsOn(unpickCommonJarTask); + } + }); + + final TaskProvider clientOnlyDecompileTask = createDecompileTasks("ClientOnly", task -> { + task.getInputJar().set(clientOnlyJar); + task.getRuntimeJar().set(minecraftProvider.getClientOnlyJar().toFile()); + + if (unpickCommonJarTask != null) { + task.dependsOn(unpickClientOnlyJarTask); + } + + // Don't allow them to run at the same time. + task.mustRunAfter(commonDecompileTask); + }); + + project.getTasks().register("genSources", task -> { + task.setDescription("Decompile minecraft using the default decompiler."); + task.setGroup(Constants.TaskGroup.FABRIC); + + task.dependsOn(commonDecompileTask); + task.dependsOn(clientOnlyDecompileTask); + }); + } + + private TaskProvider createUnpickJarTask(String name, File inputJar, File outputJar) { + return project.getTasks().register("unpick%sJar".formatted(name), UnpickJarTask.class, unpickJarTask -> { + unpickJarTask.getUnpickDefinitions().set(mappingsProvider.getUnpickDefinitionsFile()); + unpickJarTask.getInputJar().set(inputJar); + unpickJarTask.getOutputJar().set(outputJar); + }); + } + + private TaskProvider createDecompileTasks(String name, Action configureAction) { + extension.getGameDecompilers().forEach(decompiler -> { + final String taskName = "gen%sSourcesWith%s".formatted(name, decompiler.name()); + + project.getTasks().register(taskName, GenerateSourcesTask.class, decompiler).configure(task -> { + configureAction.execute(task); + task.dependsOn(project.getTasks().named("validateAccessWidener")); + task.setDescription("Decompile minecraft using %s.".formatted(decompiler.name())); + task.setGroup(Constants.TaskGroup.FABRIC); + }); + }); + + return project.getTasks().register("gen%sSources".formatted(name), task -> { + task.setDescription("Decompile minecraft (%s) using the default decompiler.".formatted(name)); + task.setGroup(Constants.TaskGroup.FABRIC); + + task.dependsOn(project.getTasks().named("gen%sSourcesWithCfr".formatted(name))); + }); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java index a8055d6..ce9c0dc 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java @@ -33,7 +33,7 @@ import org.gradle.execution.taskgraph.TaskExecutionGraphInternal; public class IdeaConfiguration { public static void setup(Project project) { TaskProvider ideaSyncTask = project.getTasks().register("ideaSyncTask", IdeaSyncTask.class, ideaSyncTask1 -> { - ideaSyncTask1.dependsOn(project.getTasks().named("downloadAssets")); + ideaSyncTask1.dependsOn(project.getTasks().named("configureLaunch")); }); if (!IdeaUtils.isIdeaSync()) { diff --git a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java index 1666658..956de6e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java @@ -51,6 +51,7 @@ import org.objectweb.asm.ClassWriter; import org.objectweb.asm.commons.Remapper; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.RemappedConfigurationEntry; import net.fabricmc.loom.configuration.processors.JarProcessor; import net.fabricmc.loom.util.Constants; @@ -272,7 +273,10 @@ public class InterfaceInjectionProcessor implements JarProcessor { try { TinyRemapper tinyRemapper = TinyRemapperHelper.getTinyRemapper(project, "intermediary", "named"); tinyRemapper.readClassPath(TinyRemapperHelper.getMinecraftDependencies(project)); - tinyRemapper.readClassPath(extension.getMinecraftMappedProvider().getIntermediaryJar().toPath()); + + for (Path minecraftJar : extension.getMinecraftJars(MappingsNamespace.INTERMEDIARY)) { + tinyRemapper.readClassPath(minecraftJar); + } return tinyRemapper; } catch (IOException e) { diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java index bc888d1..9ee6e54 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java @@ -46,7 +46,6 @@ import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.RemappedConfigurationEntry; import net.fabricmc.loom.configuration.processors.dependency.ModDependencyInfo; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.TinyRemapperHelper; import net.fabricmc.loom.util.ZipUtils; @@ -134,10 +133,8 @@ public class ModProcessor { private void remapJars(List remapList) throws IOException { final LoomGradleExtension extension = LoomGradleExtension.get(project); - final MinecraftMappedProvider mappedProvider = extension.getMinecraftMappedProvider(); final MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); - Path intermediaryJar = mappedProvider.getIntermediaryJar().toPath(); Path[] mcDeps = project.getConfigurations().getByName(Constants.Configurations.LOADER_DEPENDENCIES).getFiles() .stream().map(File::toPath).toArray(Path[]::new); @@ -148,7 +145,10 @@ public class ModProcessor { .renameInvalidLocals(false) .build(); - remapper.readClassPathAsync(intermediaryJar); + for (Path minecraftJar : extension.getMinecraftJars(MappingsNamespace.INTERMEDIARY)) { + remapper.readClassPathAsync(minecraftJar); + } + remapper.readClassPathAsync(mcDeps); final Map tagMap = new HashMap<>(); diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/MinecraftProcessedProvider.java b/src/main/java/net/fabricmc/loom/configuration/processors/MinecraftProcessedProvider.java deleted file mode 100644 index fc8aa86..0000000 --- a/src/main/java/net/fabricmc/loom/configuration/processors/MinecraftProcessedProvider.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2020-2021 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.configuration.processors; - -import java.io.File; -import java.io.IOException; -import java.util.function.Consumer; - -import org.apache.commons.io.FileUtils; -import org.gradle.api.Project; - -import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; -import net.fabricmc.loom.util.Constants; - -public class MinecraftProcessedProvider extends MinecraftMappedProvider { - public final String projectMappedClassifier; - - private File projectMappedJar; - - private final JarProcessorManager jarProcessorManager; - - public MinecraftProcessedProvider(Project project, JarProcessorManager jarProcessorManager) { - super(project); - this.jarProcessorManager = jarProcessorManager; - this.projectMappedClassifier = "project-" + project.getPath().replace(':', '@') - + "-mapped"; - } - - @Override - protected void addDependencies(DependencyInfo dependency, Consumer postPopulationScheduler) { - if (jarProcessorManager.isInvalid(projectMappedJar) || isRefreshDeps()) { - getProject().getLogger().info(":processing mapped jar"); - invalidateJar(); - - try { - FileUtils.copyFile(super.getMappedJar(), projectMappedJar); - } catch (IOException e) { - throw new RuntimeException("Failed to copy source jar", e); - } - - jarProcessorManager.process(projectMappedJar); - } - - getProject().getDependencies().add(Constants.Configurations.MINECRAFT_NAMED, - getProject().getDependencies().module("net.minecraft:minecraft-" + projectMappedClassifier + ":" + getMinecraftProvider().minecraftVersion() + "/" + getExtension().getMappingsProvider().mappingsIdentifier())); - } - - private void invalidateJar() { - if (projectMappedJar.exists()) { - getProject().getLogger().warn("Invalidating project jar"); - - try { - FileUtils.forceDelete(projectMappedJar); - } catch (IOException e) { - throw new RuntimeException("Failed to invalidate jar, try stopping gradle daemon or closing the game", e); - } - } - } - - @Override - public void initFiles(MinecraftProviderImpl minecraftProvider, MappingsProviderImpl mappingsProvider) { - super.initFiles(minecraftProvider, mappingsProvider); - - projectMappedJar = new File(getDirectories().getRootProjectPersistentCache(), getMinecraftProvider().minecraftVersion() + "/" - + getExtension().getMappingsProvider().mappingsIdentifier() + "/minecraft-" + projectMappedClassifier + ".jar"); - } - - @Override - public File getMappedJar() { - return projectMappedJar; - } -} 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 0ee8917..1cf907d 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 @@ -35,7 +35,7 @@ import org.gradle.api.logging.Logger; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.mappings.layered.MappingContext; -import net.fabricmc.loom.configuration.providers.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; public class GradleMappingContext implements MappingContext { private final Project project; 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 3bd3b12..e56cbfd 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 @@ -39,7 +39,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.Objects; -import java.util.function.Consumer; import java.util.regex.Pattern; import com.google.common.base.Stopwatch; @@ -53,14 +52,9 @@ import org.objectweb.asm.Opcodes; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; -import net.fabricmc.loom.configuration.DependencyProvider; -import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor; -import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerJarProcessor; -import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor; -import net.fabricmc.loom.configuration.processors.JarProcessorManager; -import net.fabricmc.loom.configuration.processors.MinecraftProcessedProvider; -import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; +import net.fabricmc.loom.configuration.DependencyInfo; +import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.DeletingFileVisitor; import net.fabricmc.loom.util.DownloadUtil; @@ -76,9 +70,7 @@ import net.fabricmc.mappingio.tree.MemoryMappingTree; import net.fabricmc.stitch.Command; import net.fabricmc.stitch.commands.CommandProposeFieldNames; -public class MappingsProviderImpl extends DependencyProvider implements MappingsProvider { - public MinecraftMappedProvider mappedProvider; - +public class MappingsProviderImpl implements MappingsProvider { public String mappingsIdentifier; private Path mappingsWorkingDir; @@ -95,19 +87,23 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings private MemoryMappingTree mappingTree; private Map signatureFixes; - public MappingsProviderImpl(Project project) { - super(project); + private final Project project; + private final MinecraftProvider minecraftProvider; + private final LoomGradleExtension extension; + public MappingsProviderImpl(Project project, MinecraftProvider minecraftProvider) { + this.project = project; + this.minecraftProvider = minecraftProvider; + this.extension = LoomGradleExtension.get(project); } public MemoryMappingTree getMappings() throws IOException { return Objects.requireNonNull(mappingTree, "Cannot get mappings before they have been read"); } - @Override - public void provide(DependencyInfo dependency, Consumer postPopulationScheduler) throws Exception { - MinecraftProviderImpl minecraftProvider = getDependencyManager().getProvider(MinecraftProviderImpl.class); + public void provide() throws Exception { + final DependencyInfo dependency = DependencyInfo.create(project, Constants.Configurations.MAPPINGS); - getProject().getLogger().info(":setting up mappings (" + dependency.getDependency().getName() + " " + dependency.getResolvedVersion() + ")"); + project.getLogger().info(":setting up mappings (" + dependency.getDependency().getName() + " " + dependency.getResolvedVersion() + ")"); String version = dependency.getResolvedVersion(); File mappingsJar = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not find yarn mappings: " + dependency)); @@ -119,7 +115,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings initFiles(); if (Files.notExists(tinyMappings) || isRefreshDeps()) { - storeMappings(getProject(), minecraftProvider, mappingsJar.toPath()); + storeMappings(project, minecraftProvider, mappingsJar.toPath()); } else { try (FileSystem fileSystem = FileSystems.newFileSystem(mappingsJar.toPath(), (ClassLoader) null)) { extractExtras(fileSystem); @@ -140,49 +136,11 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings dependency.getDependency().getVersion() ); - getProject().getDependencies().add(Constants.Configurations.MAPPING_CONSTANTS, notation); + project.getDependencies().add(Constants.Configurations.MAPPING_CONSTANTS, notation); populateUnpickClasspath(); } - addDependency(tinyMappingsJar.toFile(), Constants.Configurations.MAPPINGS_FINAL); - - LoomGradleExtension extension = getExtension(); - - if (extension.getAccessWidenerPath().isPresent()) { - extension.getGameJarProcessors().add(new AccessWidenerJarProcessor(getProject())); - } - - if (extension.getEnableTransitiveAccessWideners().get()) { - TransitiveAccessWidenerJarProcessor transitiveAccessWidenerJarProcessor = new TransitiveAccessWidenerJarProcessor(getProject()); - - if (!transitiveAccessWidenerJarProcessor.isEmpty()) { - extension.getGameJarProcessors().add(transitiveAccessWidenerJarProcessor); - } - } - - if (extension.getEnableInterfaceInjection().get()) { - InterfaceInjectionProcessor jarProcessor = new InterfaceInjectionProcessor(getProject()); - - if (!jarProcessor.isEmpty()) { - extension.getGameJarProcessors().add(jarProcessor); - } - } - - extension.getAccessWidenerPath().finalizeValue(); - extension.getGameJarProcessors().finalizeValue(); - JarProcessorManager processorManager = new JarProcessorManager(extension.getGameJarProcessors().get()); - extension.setJarProcessorManager(processorManager); - processorManager.setupProcessors(); - - if (processorManager.active()) { - mappedProvider = new MinecraftProcessedProvider(getProject(), processorManager); - getProject().getLogger().info("Using project based jar storage"); - } else { - mappedProvider = new MinecraftMappedProvider(getProject()); - } - - mappedProvider.initFiles(minecraftProvider, this); - mappedProvider.provide(dependency, postPopulationScheduler); + project.getDependencies().add(Constants.Configurations.MAPPINGS_FINAL, project.files(tinyMappingsJar.toFile())); } private String getMappingsClassifier(DependencyInfo dependency, boolean isV2) { @@ -196,7 +154,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings } private boolean isV2(DependencyInfo dependency, File mappingsJar) throws IOException { - String minecraftVersion = getMinecraftProvider().minecraftVersion(); + String minecraftVersion = minecraftProvider.minecraftVersion(); // Only do this for official yarn, there isn't really a way we can get the mc version for all mappings if (dependency.getDependency().getGroup() != null && dependency.getDependency().getGroup().equals("net.fabricmc") && dependency.getDependency().getName().equals("yarn") && dependency.getDependency().getVersion() != null) { @@ -205,7 +163,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings String yarnMinecraftVersion = yarnVersion.substring(0, yarnVersion.lastIndexOf(separator)); if (!yarnMinecraftVersion.equalsIgnoreCase(minecraftVersion)) { - getProject().getLogger().warn("Minecraft Version ({}) does not match yarn's minecraft version ({})", minecraftVersion, yarnMinecraftVersion); + project.getLogger().warn("Minecraft Version ({}) does not match yarn's minecraft version ({})", minecraftVersion, yarnMinecraftVersion); } // We can save reading the zip file + header by checking the file name @@ -215,7 +173,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings } } - private void storeMappings(Project project, MinecraftProviderImpl minecraftProvider, Path yarnJar) throws IOException { + private void storeMappings(Project project, MinecraftProvider minecraftProvider, Path yarnJar) throws IOException { project.getLogger().info(":extracting " + yarnJar.getFileName()); try (FileSystem fileSystem = FileSystems.newFileSystem(yarnJar, (ClassLoader) null)) { @@ -227,10 +185,14 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings // These are unmerged v2 mappings mergeAndSaveMappings(project, baseTinyMappings, tinyMappings); } else { - // These are merged v1 mappings - Files.deleteIfExists(tinyMappings); - project.getLogger().lifecycle(":populating field names"); - suggestFieldNames(minecraftProvider, baseTinyMappings, tinyMappings); + if (minecraftProvider instanceof MergedMinecraftProvider mergedMinecraftProvider) { + // These are merged v1 mappings + Files.deleteIfExists(tinyMappings); + project.getLogger().lifecycle(":populating field names"); + suggestFieldNames(mergedMinecraftProvider, baseTinyMappings, tinyMappings); + } else { + throw new UnsupportedOperationException("V1 mappings only support merged minecraft"); + } } } @@ -311,7 +273,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings private void populateUnpickClasspath() { String unpickCliName = "unpick-cli"; - getProject().getDependencies().add(Constants.Configurations.UNPICK_CLASSPATH, + project.getDependencies().add(Constants.Configurations.UNPICK_CLASSPATH, String.format("%s:%s:%s", unpickMetadata.unpickGroup, unpickCliName, unpickMetadata.unpickVersion) ); @@ -324,7 +286,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings }; for (String asm : asmDeps) { - getProject().getDependencies().add(Constants.Configurations.UNPICK_CLASSPATH, + project.getDependencies().add(Constants.Configurations.UNPICK_CLASSPATH, asm.formatted(Opcodes.class.getPackage().getImplementationVersion()) ); } @@ -400,7 +362,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings return tree; } - private void suggestFieldNames(MinecraftProviderImpl minecraftProvider, Path oldMappings, Path newMappings) { + private void suggestFieldNames(MergedMinecraftProvider minecraftProvider, Path oldMappings, Path newMappings) { Command command = new CommandProposeFieldNames(); runCommand(command, minecraftProvider.getMergedJar().getAbsolutePath(), oldMappings.toAbsolutePath().toString(), @@ -416,7 +378,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings } private void initFiles() { - mappingsWorkingDir = getMinecraftProvider().dir(mappingsIdentifier).toPath(); + mappingsWorkingDir = minecraftProvider.dir(mappingsIdentifier).toPath(); baseTinyMappings = mappingsWorkingDir.resolve("mappings-base.tiny"); tinyMappings = mappingsWorkingDir.resolve("mappings.tiny"); tinyMappingsJar = mappingsWorkingDir.resolve("mappings.jar"); @@ -439,23 +401,18 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings } } - @Override - public String getTargetConfig() { - return Constants.Configurations.MAPPINGS; - } - public Path getIntermediaryTiny() throws IOException { if (intermediaryTiny == null) { - intermediaryTiny = getMinecraftProvider().file("intermediary-v2.tiny").toPath(); + intermediaryTiny = minecraftProvider.file("intermediary-v2.tiny").toPath(); if (!Files.exists(intermediaryTiny) || (isRefreshDeps() && !hasRefreshed)) { hasRefreshed = true; // Download and extract intermediary - String encodedMinecraftVersion = UrlEscapers.urlFragmentEscaper().escape(getMinecraftProvider().minecraftVersion()); - String intermediaryArtifactUrl = getExtension().getIntermediaryUrl(encodedMinecraftVersion); - File intermediaryJar = getMinecraftProvider().file("intermediary-v2.jar"); - DownloadUtil.downloadIfChanged(new URL(intermediaryArtifactUrl), intermediaryJar, getProject().getLogger()); + String encodedMinecraftVersion = UrlEscapers.urlFragmentEscaper().escape(minecraftProvider.minecraftVersion()); + String intermediaryArtifactUrl = extension.getIntermediaryUrl(encodedMinecraftVersion); + File intermediaryJar = minecraftProvider.file("intermediary-v2.jar"); + DownloadUtil.downloadIfChanged(new URL(intermediaryArtifactUrl), intermediaryJar, project.getLogger()); extractMappings(intermediaryJar.toPath(), intermediaryTiny); } } @@ -471,7 +428,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings private String createMappingsIdentifier(String mappingsName, String version, String classifier) { // mappingsName . mcVersion . version classifier // Example: net.fabricmc.yarn . 1_16_5 . 1.16.5+build.5 -v2 - return mappingsName + "." + getMinecraftProvider().minecraftVersion().replace(' ', '_').replace('.', '_').replace('-', '_') + "." + version + classifier; + return mappingsName + "." + minecraftProvider.minecraftVersion().replace(' ', '_').replace('.', '_').replace('-', '_') + "." + version + classifier; } public String mappingsIdentifier() { @@ -506,4 +463,8 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings public record UnpickMetadata(String unpickGroup, String unpickVersion) { } + + protected boolean isRefreshDeps() { + return LoomGradlePlugin.refreshDeps; + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MergedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MergedMinecraftProvider.java new file mode 100644 index 0000000..672e958 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MergedMinecraftProvider.java @@ -0,0 +1,95 @@ +/* + * 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.configuration.providers.minecraft; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; + +import org.gradle.api.Project; + +import net.fabricmc.loom.util.HashedDownloadUtil; +import net.fabricmc.stitch.merge.JarMerger; + +public final class MergedMinecraftProvider extends MinecraftProvider { + private File minecraftMergedJar; + + public MergedMinecraftProvider(Project project) { + super(project); + } + + @Override + protected void initFiles() { + super.initFiles(); + minecraftMergedJar = file("minecraft-merged.jar"); + } + + @Override + public List getMinecraftJars() { + return List.of(minecraftMergedJar.toPath()); + } + + @Override + public void provide() throws Exception { + super.provide(); + + if (!minecraftMergedJar.exists() || isRefreshDeps()) { + try { + mergeJars(); + } catch (Throwable e) { + HashedDownloadUtil.delete(getMinecraftClientJar()); + HashedDownloadUtil.delete(getMinecraftServerJar()); + minecraftMergedJar.delete(); + + getProject().getLogger().error("Could not merge JARs! Deleting source JARs - please re-run the command and move on.", e); + throw e; + } + } + } + + private void mergeJars() throws IOException { + getLogger().info(":merging jars"); + + File jarToMerge = getMinecraftServerJar(); + + if (getServerBundleMetadata() != null) { + extractBundledServerJar(); + jarToMerge = getMinecraftExtractedServerJar(); + } + + Objects.requireNonNull(jarToMerge, "Cannot merge null input jar?"); + + try (JarMerger jarMerger = new JarMerger(getMinecraftClientJar(), jarToMerge, minecraftMergedJar)) { + jarMerger.enableSyntheticParamsOffset(); + jarMerger.merge(); + } + } + + public File getMergedJar() { + return minecraftMergedJar; + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJarSplitter.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJarSplitter.java new file mode 100644 index 0000000..87d7a80 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftJarSplitter.java @@ -0,0 +1,169 @@ +/* + * 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.configuration.providers.minecraft; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Objects; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import java.util.stream.Stream; + +import com.google.common.collect.Sets; + +import net.fabricmc.loom.util.FileSystemUtil; + +public class MinecraftJarSplitter implements AutoCloseable { + private final Path clientInputJar; + private final Path serverInputJar; + + private EntryData entryData; + private Set sharedEntries = new HashSet<>(); + private Set forcedClientEntries = new HashSet<>(); + + public MinecraftJarSplitter(Path clientInputJar, Path serverInputJar) { + this.clientInputJar = Objects.requireNonNull(clientInputJar); + this.serverInputJar = Objects.requireNonNull(serverInputJar); + } + + public void split(Path clientOnlyOutputJar, Path commonOutputJar) throws IOException { + Objects.requireNonNull(clientOnlyOutputJar); + Objects.requireNonNull(commonOutputJar); + + if (entryData == null) { + entryData = new EntryData(getJarEntries(clientInputJar), getJarEntries(serverInputJar)); + } + + // Not something we expect, will require 3 jars, server, client and common. + assert entryData.serverOnlyEntries.isEmpty(); + + copyEntriesToJar(entryData.commonEntries, serverInputJar, commonOutputJar); + copyEntriesToJar(entryData.clientOnlyEntries, clientInputJar, clientOnlyOutputJar); + } + + public void sharedEntry(String path) { + this.sharedEntries.add(path); + } + + public void forcedClientEntry(String path) { + this.forcedClientEntries.add(path); + } + + private Set getJarEntries(Path input) throws IOException { + Set entries = Sets.newHashSet(); + + try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(input); + Stream walk = Files.walk(fs.get().getPath("/"))) { + Iterator iterator = walk.iterator(); + + while (iterator.hasNext()) { + Path fsPath = iterator.next(); + + if (!Files.isRegularFile(fsPath)) { + continue; + } + + String entryPath = fs.get().getPath("/").relativize(fsPath).toString(); + + if (entryPath.startsWith("META-INF/")) { + continue; + } + + entries.add(entryPath); + } + } + + return entries; + } + + private void copyEntriesToJar(Set entries, Path inputJar, Path outputJar) throws IOException { + Files.deleteIfExists(outputJar); + + try (FileSystemUtil.Delegate inputFs = FileSystemUtil.getJarFileSystem(inputJar); + FileSystemUtil.Delegate outputFs = FileSystemUtil.getJarFileSystem(outputJar, true)) { + for (String entry : entries) { + Path inputPath = inputFs.get().getPath(entry); + Path outputPath = outputFs.get().getPath(entry); + + assert Files.isRegularFile(inputPath); + + Path outputPathParent = outputPath.getParent(); + + if (outputPathParent != null) { + Files.createDirectories(outputPathParent); + } + + Files.copy(inputPath, outputPath, StandardCopyOption.COPY_ATTRIBUTES); + } + + writeManifest(outputFs); + } + } + + private void writeManifest(FileSystemUtil.Delegate outputFs) throws IOException { + final Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + manifest.write(out); + Files.createDirectories(outputFs.get().getPath("META-INF")); + Files.write(outputFs.get().getPath("META-INF/MANIFEST.MF"), out.toByteArray()); + } + + @Override + public void close() throws Exception { + } + + private final class EntryData { + private final Set clientEntries; + private final Set serverEntries; + private final Set commonEntries; + private final Set clientOnlyEntries; + private final Set serverOnlyEntries; + + private EntryData(Set clientEntries, Set serverEntries) { + this.clientEntries = clientEntries; + this.serverEntries = serverEntries; + + this.commonEntries = Sets.newHashSet(clientEntries); + this.commonEntries.retainAll(serverEntries); + this.commonEntries.addAll(sharedEntries); + this.commonEntries.removeAll(forcedClientEntries); + + this.clientOnlyEntries = Sets.newHashSet(clientEntries); + this.clientOnlyEntries.removeAll(serverEntries); + this.clientOnlyEntries.addAll(sharedEntries); + this.clientOnlyEntries.addAll(forcedClientEntries); + + this.serverOnlyEntries = Sets.newHashSet(serverEntries); + this.serverOnlyEntries.removeAll(clientEntries); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java index 3e4a3c4..0169ba8 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java @@ -31,13 +31,12 @@ import org.gradle.api.Project; import org.gradle.api.artifacts.ExternalModuleDependency; import net.fabricmc.loom.configuration.providers.BundleMetadata; -import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; import net.fabricmc.loom.util.Constants; public class MinecraftLibraryProvider { private static final Pattern NATIVES_PATTERN = Pattern.compile("^(?.*)/(.*?)/(?.*)/((?.*?)-([0-9].*?)-)(?.*).jar$"); - public void provide(MinecraftProviderImpl minecraftProvider, Project project) { + public void provide(MinecraftProvider minecraftProvider, Project project) { MinecraftVersionMeta versionInfo = minecraftProvider.getVersionInfo(); BundleMetadata serverBundleMetadata = minecraftProvider.getServerBundleMetadata(); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java deleted file mode 100644 index d1ecf29..0000000 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2016-2021 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.configuration.providers.minecraft; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Map; -import java.util.function.Consumer; - -import org.gradle.api.Project; - -import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; -import net.fabricmc.loom.configuration.DependencyProvider; -import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; -import net.fabricmc.loom.util.Constants; -import net.fabricmc.loom.util.TinyRemapperHelper; -import net.fabricmc.tinyremapper.OutputConsumerPath; -import net.fabricmc.tinyremapper.TinyRemapper; - -public class MinecraftMappedProvider extends DependencyProvider { - private File minecraftMappedJar; - private File minecraftIntermediaryJar; - - private MinecraftProviderImpl minecraftProvider; - - public MinecraftMappedProvider(Project project) { - super(project); - } - - @Override - public void provide(DependencyInfo dependency, Consumer postPopulationScheduler) throws Exception { - if (Files.notExists(getExtension().getMappingsProvider().tinyMappings)) { - throw new RuntimeException("mappings file not found"); - } - - if (!getExtension().getMinecraftProvider().getMergedJar().exists()) { - throw new RuntimeException("input merged jar not found"); - } - - if (!minecraftMappedJar.exists() || !getIntermediaryJar().exists() || isRefreshDeps()) { - if (minecraftMappedJar.exists()) { - minecraftMappedJar.delete(); - } - - minecraftMappedJar.getParentFile().mkdirs(); - - if (minecraftIntermediaryJar.exists()) { - minecraftIntermediaryJar.delete(); - } - - try { - mapMinecraftJar(); - } catch (Throwable t) { - // Cleanup some some things that may be in a bad state now - minecraftMappedJar.delete(); - minecraftIntermediaryJar.delete(); - getExtension().getMappingsProvider().cleanFiles(); - throw new RuntimeException("Failed to remap minecraft", t); - } - } - - if (!minecraftMappedJar.exists()) { - throw new RuntimeException("mapped jar not found"); - } - - addDependencies(dependency, postPopulationScheduler); - } - - private void mapMinecraftJar() throws IOException { - String fromM = MappingsNamespace.OFFICIAL.toString(); - - MappingsProviderImpl mappingsProvider = getExtension().getMappingsProvider(); - - Path input = minecraftProvider.getMergedJar().toPath(); - Path outputMapped = minecraftMappedJar.toPath(); - Path outputIntermediary = minecraftIntermediaryJar.toPath(); - - for (String toM : Arrays.asList(MappingsNamespace.NAMED.toString(), MappingsNamespace.INTERMEDIARY.toString())) { - final boolean toNamed = MappingsNamespace.NAMED.toString().equals(toM); - final boolean toIntermediary = MappingsNamespace.INTERMEDIARY.toString().equals(toM); - final Path output = toNamed ? outputMapped : outputIntermediary; - - getProject().getLogger().info(":remapping minecraft (TinyRemapper, " + fromM + " -> " + toM + ")"); - - Files.deleteIfExists(output); - - final Map remappedSignatures = SignatureFixerApplyVisitor.getRemappedSignatures(toIntermediary, mappingsProvider, getProject(), toM); - TinyRemapper remapper = TinyRemapperHelper.getTinyRemapper(getProject(), fromM, toM, true, (builder) -> { - builder.extraPostApplyVisitor(new SignatureFixerApplyVisitor(remappedSignatures)); - }); - - try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(output).build()) { - outputConsumer.addNonClassFiles(input); - remapper.readClassPath(TinyRemapperHelper.getMinecraftDependencies(getProject())); - remapper.readInputs(input); - remapper.apply(outputConsumer); - } catch (Exception e) { - throw new RuntimeException("Failed to remap JAR " + input + " with mappings from " + mappingsProvider.tinyMappings, e); - } finally { - remapper.finish(); - } - } - } - - protected void addDependencies(DependencyInfo dependency, Consumer postPopulationScheduler) { - getProject().getDependencies().add(Constants.Configurations.MINECRAFT_NAMED, - getProject().getDependencies().module("net.minecraft:minecraft-mapped:" + getMinecraftProvider().minecraftVersion() + "/" + getExtension().getMappingsProvider().mappingsIdentifier())); - } - - public void initFiles(MinecraftProviderImpl minecraftProvider, MappingsProviderImpl mappingsProvider) { - this.minecraftProvider = minecraftProvider; - minecraftIntermediaryJar = new File(getExtension().getMappingsProvider().mappingsWorkingDir().toFile(), "minecraft-intermediary.jar"); - minecraftMappedJar = new File(getExtension().getMappingsProvider().mappingsWorkingDir().toFile(), "minecraft-mapped.jar"); - } - - protected File getJarDirectory(File parentDirectory, String type) { - return new File(parentDirectory, getJarVersionString(type)); - } - - protected String getJarVersionString(String type) { - return String.format("%s-%s", type, getExtension().getMappingsProvider().mappingsIdentifier()); - } - - public File getIntermediaryJar() { - return minecraftIntermediaryJar; - } - - public File getMappedJar() { - return minecraftMappedJar; - } - - public final File getBaseMappedJar() { - return minecraftMappedJar; - } - - public File getUnpickedJar() { - return new File(getExtension().getMappingsProvider().mappingsWorkingDir().toFile(), "minecraft-unpicked.jar"); - } - - @Override - public String getTargetConfig() { - return Constants.Configurations.MINECRAFT_NAMED; - } -} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java similarity index 78% rename from src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java rename to src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java index de895de..47024c3 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java @@ -22,15 +22,17 @@ * SOFTWARE. */ -package net.fabricmc.loom.configuration.providers; +package net.fabricmc.loom.configuration.providers.minecraft; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; import java.util.Optional; -import java.util.function.Consumer; import com.google.common.io.Files; import org.gradle.api.GradleException; @@ -38,18 +40,16 @@ import org.gradle.api.Project; import org.gradle.api.logging.Logger; import org.jetbrains.annotations.Nullable; +import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.configuration.DependencyProvider; -import net.fabricmc.loom.configuration.providers.minecraft.ManifestVersion; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftLibraryProvider; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; +import net.fabricmc.loom.configuration.DependencyInfo; +import net.fabricmc.loom.configuration.providers.BundleMetadata; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.DownloadUtil; import net.fabricmc.loom.util.HashedDownloadUtil; import net.fabricmc.loom.util.MirrorUtil; -import net.fabricmc.stitch.merge.JarMerger; -public class MinecraftProviderImpl extends DependencyProvider implements MinecraftProvider { +public abstract class MinecraftProvider { private String minecraftVersion; private MinecraftVersionMeta versionInfo; @@ -64,16 +64,17 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra private File minecraftExtractedServerJar; @Nullable private BundleMetadata serverBundleMetadata; - private File minecraftMergedJar; private File versionManifestJson; private File experimentalVersionsJson; - public MinecraftProviderImpl(Project project) { - super(project); + private final Project project; + + public MinecraftProvider(Project project) { + this.project = project; } - @Override - public void provide(DependencyInfo dependency, Consumer postPopulationScheduler) throws Exception { + public void provide() throws Exception { + final DependencyInfo dependency = DependencyInfo.create(getProject(), Constants.Configurations.MINECRAFT); minecraftVersion = dependency.getDependency().getVersion(); boolean offline = getProject().getGradle().getStartParameter().isOffline(); @@ -89,9 +90,6 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra if (offline) { if (minecraftClientJar.exists() && minecraftServerJar.exists()) { getProject().getLogger().debug("Found client and server jars, presuming up-to-date"); - } else if (minecraftMergedJar.exists()) { - //Strictly we don't need the split jars if the merged one exists, let's try go on - getProject().getLogger().warn("Missing game jar but merged jar present, things might end badly"); } else { throw new GradleException("Missing jar(s); Client: " + minecraftClientJar.exists() + ", Server: " + minecraftServerJar.exists()); } @@ -103,31 +101,17 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra libraryProvider = new MinecraftLibraryProvider(); libraryProvider.provide(this, getProject()); - - if (!minecraftMergedJar.exists() || isRefreshDeps()) { - try { - mergeJars(getProject().getLogger()); - } catch (Throwable e) { - HashedDownloadUtil.delete(minecraftClientJar); - HashedDownloadUtil.delete(minecraftServerJar); - minecraftMergedJar.delete(); - - getProject().getLogger().error("Could not merge JARs! Deleting source JARs - please re-run the command and move on.", e); - throw e; - } - } } - private void initFiles() { - workingDir = new File(getDirectories().getUserCache(), minecraftVersion); + protected void initFiles() { + workingDir = new File(getExtension().getFiles().getUserCache(), minecraftVersion); workingDir.mkdirs(); minecraftJson = file("minecraft-info.json"); minecraftClientJar = file("minecraft-client.jar"); minecraftServerJar = file("minecraft-server.jar"); minecraftExtractedServerJar = file("minecraft-extracted_server.jar"); - minecraftMergedJar = file("minecraft-merged.jar"); - versionManifestJson = new File(getDirectories().getUserCache(), "version_manifest.json"); - experimentalVersionsJson = new File(getDirectories().getUserCache(), "experimental_version_manifest.json"); + versionManifestJson = new File(getExtension().getFiles().getUserCache(), "version_manifest.json"); + experimentalVersionsJson = new File(getExtension().getFiles().getUserCache(), "experimental_version_manifest.json"); } private void downloadMcJson(boolean offline) throws IOException { @@ -254,55 +238,52 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra HashedDownloadUtil.downloadIfInvalid(new URL(server.url()), minecraftServerJar, server.sha1(), logger, false); } - private void mergeJars(Logger logger) throws IOException { - logger.info(":merging jars"); + protected final void extractBundledServerJar() throws IOException { + Objects.requireNonNull(getServerBundleMetadata(), "Cannot bundled mc jar from none bundled server jar"); - File jarToMerge = minecraftServerJar; + getLogger().info(":Extracting server jar from bootstrap"); - if (serverBundleMetadata != null) { - logger.info(":Extracting server jar from bootstrap"); - - if (serverBundleMetadata.versions().size() != 1) { - throw new UnsupportedOperationException("Expected only 1 version in META-INF/versions.list, but got %d".formatted(serverBundleMetadata.versions().size())); - } - - serverBundleMetadata.versions().get(0).unpackEntry(minecraftServerJar.toPath(), minecraftExtractedServerJar.toPath()); - jarToMerge = minecraftExtractedServerJar; + if (getServerBundleMetadata().versions().size() != 1) { + throw new UnsupportedOperationException("Expected only 1 version in META-INF/versions.list, but got %d".formatted(getServerBundleMetadata().versions().size())); } - try (JarMerger jarMerger = new JarMerger(minecraftClientJar, jarToMerge, minecraftMergedJar)) { - jarMerger.enableSyntheticParamsOffset(); - jarMerger.merge(); - } + getServerBundleMetadata().versions().get(0).unpackEntry(minecraftServerJar.toPath(), getMinecraftExtractedServerJar().toPath()); } - public File getMergedJar() { - return minecraftMergedJar; - } - - @Override public File workingDir() { return workingDir; } - @Override public File dir(String path) { File dir = file(path); dir.mkdirs(); return dir; } - @Override public File file(String path) { return new File(workingDir(), path); } - @Override + public File getMinecraftClientJar() { + return minecraftClientJar; + } + + // May be null on older versions + @Nullable + public File getMinecraftExtractedServerJar() { + return minecraftExtractedServerJar; + } + + // This may be the server bundler jar on newer versions prob not what you want. + @Deprecated + public File getMinecraftServerJar() { + return minecraftServerJar; + } + public String minecraftVersion() { return minecraftVersion; } - @Override public MinecraftVersionMeta getVersionInfo() { return versionInfo; } @@ -311,7 +292,6 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra return libraryProvider; } - @Override public String getTargetConfig() { return Constants.Configurations.MINECRAFT; } @@ -320,4 +300,22 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra public BundleMetadata getServerBundleMetadata() { return serverBundleMetadata; } + + protected Logger getLogger() { + return getProject().getLogger(); + } + + public abstract List getMinecraftJars(); + + protected Project getProject() { + return project; + } + + protected LoomGradleExtension getExtension() { + return LoomGradleExtension.get(getProject()); + } + + protected boolean isRefreshDeps() { + return LoomGradlePlugin.refreshDeps; + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SplitMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SplitMinecraftProvider.java new file mode 100644 index 0000000..cca2d01 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SplitMinecraftProvider.java @@ -0,0 +1,99 @@ +/* + * 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.configuration.providers.minecraft; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.gradle.api.Project; + +import net.fabricmc.loom.configuration.providers.BundleMetadata; + +public final class SplitMinecraftProvider extends MinecraftProvider { + private File minecraftClientOnlyJar; + private File minecraftCommonJar; + + public SplitMinecraftProvider(Project project) { + super(project); + } + + @Override + protected void initFiles() { + super.initFiles(); + + minecraftClientOnlyJar = file("minecraft-client-only.jar"); + minecraftCommonJar = file("minecraft-common.jar"); + } + + @Override + public List getMinecraftJars() { + return List.of(minecraftClientOnlyJar.toPath(), minecraftCommonJar.toPath()); + } + + @Override + public void provide() throws Exception { + super.provide(); + + boolean requiresRefresh = isRefreshDeps() || !minecraftClientOnlyJar.exists() || !minecraftCommonJar.exists(); + + if (!requiresRefresh) { + return; + } + + BundleMetadata serverBundleMetadata = getServerBundleMetadata(); + + if (serverBundleMetadata == null) { + throw new UnsupportedOperationException("Only Minecraft versions using a bundled server jar can be split, please use a merged jar setup for this version of minecraft"); + } + + extractBundledServerJar(); + + final Path clientJar = getMinecraftClientJar().toPath(); + final Path serverJar = getMinecraftExtractedServerJar().toPath(); + + try (MinecraftJarSplitter jarSplitter = new MinecraftJarSplitter(clientJar, serverJar)) { + // Required for loader to compute the version info also useful to have in both jars. + jarSplitter.sharedEntry("version.json"); + jarSplitter.forcedClientEntry("assets/.mcassetsroot"); + + jarSplitter.split(minecraftClientOnlyJar.toPath(), minecraftCommonJar.toPath()); + } catch (Exception e) { + Files.deleteIfExists(minecraftClientOnlyJar.toPath()); + Files.deleteIfExists(minecraftCommonJar.toPath()); + + throw new RuntimeException("Failed to split minecraft", e); + } + } + + public File getMinecraftClientOnlyJar() { + return minecraftClientOnlyJar; + } + + public File getMinecraftCommonJar() { + return minecraftCommonJar; + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java index 87f41e0..175e85e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java @@ -41,14 +41,14 @@ import org.gradle.api.Project; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; import net.fabricmc.loom.util.MirrorUtil; import net.fabricmc.loom.util.HashedDownloadUtil; import net.fabricmc.loom.util.gradle.ProgressLoggerHelper; public class MinecraftAssetsProvider { - public static void provide(MinecraftProviderImpl minecraftProvider, Project project) throws IOException { + public static void provide(MinecraftProvider minecraftProvider, Project project) throws IOException { LoomGradleExtension extension = LoomGradleExtension.get(project); boolean offline = project.getGradle().getStartParameter().isOffline(); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java new file mode 100644 index 0000000..68239f4 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java @@ -0,0 +1,166 @@ +/* + * 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.configuration.providers.minecraft.mapped; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +import org.gradle.api.Project; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.SignatureFixerApplyVisitor; +import net.fabricmc.loom.util.TinyRemapperHelper; +import net.fabricmc.tinyremapper.OutputConsumerPath; +import net.fabricmc.tinyremapper.TinyRemapper; + +public abstract class AbstractMappedMinecraftProvider implements MappedMinecraftProvider.ProviderImpl { + protected final M minecraftProvider; + private final Project project; + protected final LoomGradleExtension extension; + + public AbstractMappedMinecraftProvider(Project project, M minecraftProvider) { + this.project = project; + this.minecraftProvider = minecraftProvider; + this.extension = LoomGradleExtension.get(project); + } + + public abstract MappingsNamespace getTargetNamespace(); + + public abstract List getRemappedJars(); + + protected void applyDependencies(BiConsumer consumer) { + // Override if needed + } + + public void provide(boolean applyDependencies) throws Exception { + final List remappedJars = getRemappedJars(); + assert !remappedJars.isEmpty(); + + if (!areOutputsValid(remappedJars) || LoomGradlePlugin.refreshDeps) { + try { + remapInputs(remappedJars); + } catch (Throwable t) { + cleanOutputs(remappedJars); + + throw new RuntimeException("Failed to remap minecraft", t); + } + } + + if (applyDependencies) { + applyDependencies((configuration, name) -> getProject().getDependencies().add(configuration, getDependencyNotation(name))); + } + } + + protected abstract Path getDirectory(); + + @Override + public Path getJar(String name) { + return getDirectory().resolve(getName(name) + ".jar"); + } + + protected String getName(String name) { + return "minecraft-%s-%s".formatted(name, getTargetNamespace().toString()); + } + + protected String getDependencyNotation(String name) { + return "net.minecraft:%s:%s/%s".formatted(getName(name), extension.getMinecraftProvider().minecraftVersion(), extension.getMappingsProvider().mappingsIdentifier()); + } + + private boolean areOutputsValid(List remappedJars) { + for (RemappedJars remappedJar : remappedJars) { + if (!Files.exists(remappedJar.outputJar())) { + return false; + } + } + + return true; + } + + private void remapInputs(List remappedJars) throws IOException { + cleanOutputs(remappedJars); + + for (RemappedJars remappedJar : remappedJars) { + remapJar(remappedJar); + } + } + + private void remapJar(RemappedJars remappedJars) throws IOException { + final MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); + final String fromM = remappedJars.sourceNamespace().toString(); + final String toM = getTargetNamespace().toString(); + + Files.deleteIfExists(remappedJars.outputJar()); + + final Map remappedSignatures = SignatureFixerApplyVisitor.getRemappedSignatures(getTargetNamespace() == MappingsNamespace.INTERMEDIARY, mappingsProvider, project, toM); + TinyRemapper remapper = TinyRemapperHelper.getTinyRemapper(project, fromM, toM, true, (builder) -> { + builder.extraPostApplyVisitor(new SignatureFixerApplyVisitor(remappedSignatures)); + configureRemapper(remappedJars, builder); + }); + + try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(remappedJars.outputJar()).build()) { + outputConsumer.addNonClassFiles(remappedJars.inputJar()); + remapper.readClassPath(TinyRemapperHelper.getMinecraftDependencies(project)); + + for (Path path : remappedJars.remapClasspath()) { + remapper.readClassPath(path); + } + + remapper.readInputs(remappedJars.inputJar()); + remapper.apply(outputConsumer); + } catch (Exception e) { + throw new RuntimeException("Failed to remap JAR " + remappedJars.inputJar() + " with mappings from " + mappingsProvider.tinyMappings, e); + } finally { + remapper.finish(); + } + } + + protected void configureRemapper(RemappedJars remappedJars, TinyRemapper.Builder tinyRemapperBuilder) { + } + + private void cleanOutputs(List remappedJars) throws IOException { + for (RemappedJars remappedJar : remappedJars) { + Files.deleteIfExists(remappedJar.outputJar()); + } + } + + public Project getProject() { + return project; + } + + public M getMinecraftProvider() { + return minecraftProvider; + } + + public record RemappedJars(Path inputJar, Path outputJar, MappingsNamespace sourceNamespace, Path... remapClasspath) { + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/IntermediaryMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/IntermediaryMinecraftProvider.java new file mode 100644 index 0000000..773c127 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/IntermediaryMinecraftProvider.java @@ -0,0 +1,87 @@ +/* + * 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.configuration.providers.minecraft.mapped; + +import java.nio.file.Path; +import java.util.List; + +import org.gradle.api.Project; + +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider; +import net.fabricmc.loom.util.SidedClassVisitor; +import net.fabricmc.tinyremapper.TinyRemapper; + +public abstract sealed class IntermediaryMinecraftProvider extends AbstractMappedMinecraftProvider permits IntermediaryMinecraftProvider.SplitImpl, IntermediaryMinecraftProvider.MergedImpl { + public IntermediaryMinecraftProvider(Project project, M minecraftProvider) { + super(project, minecraftProvider); + } + + @Override + protected Path getDirectory() { + return extension.getMinecraftProvider().workingDir().toPath(); + } + + @Override + public final MappingsNamespace getTargetNamespace() { + return MappingsNamespace.INTERMEDIARY; + } + + public static final class MergedImpl extends IntermediaryMinecraftProvider implements Merged { + public MergedImpl(Project project, MergedMinecraftProvider minecraftProvider) { + super(project, minecraftProvider); + } + + @Override + public List getRemappedJars() { + return List.of( + new RemappedJars(minecraftProvider.getMergedJar().toPath(), getMergedJar(), MappingsNamespace.OFFICIAL) + ); + } + } + + public static final class SplitImpl extends IntermediaryMinecraftProvider implements Split { + public SplitImpl(Project project, SplitMinecraftProvider minecraftProvider) { + super(project, minecraftProvider); + } + + @Override + public List getRemappedJars() { + return List.of( + new RemappedJars(minecraftProvider.getMinecraftCommonJar().toPath(), getCommonJar(), MappingsNamespace.OFFICIAL), + new RemappedJars(minecraftProvider.getMinecraftClientOnlyJar().toPath(), getClientOnlyJar(), MappingsNamespace.OFFICIAL, minecraftProvider.getMinecraftCommonJar().toPath()) + ); + } + + @Override + protected void configureRemapper(RemappedJars remappedJars, TinyRemapper.Builder tinyRemapperBuilder) { + if (remappedJars.outputJar().equals(getClientOnlyJar())) { + tinyRemapperBuilder.extraPostApplyVisitor(SidedClassVisitor.CLIENT); + } + } + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MappedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MappedMinecraftProvider.java new file mode 100644 index 0000000..62464d1 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/MappedMinecraftProvider.java @@ -0,0 +1,67 @@ +/* + * 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.configuration.providers.minecraft.mapped; + +import java.nio.file.Path; +import java.util.List; + +public interface MappedMinecraftProvider { + List getMinecraftJars(); + + interface ProviderImpl extends MappedMinecraftProvider { + Path getJar(String name); + } + + interface Merged extends ProviderImpl { + String MERGED = "merged"; + + default Path getMergedJar() { + return getJar(MERGED); + } + + @Override + default List getMinecraftJars() { + return List.of(getMergedJar()); + } + } + + interface Split extends ProviderImpl { + String COMMON = "common"; + String CLIENT_ONLY = "clientOnly"; + + default Path getCommonJar() { + return getJar(COMMON); + } + + default Path getClientOnlyJar() { + return getJar(CLIENT_ONLY); + } + + @Override + default List getMinecraftJars() { + return List.of(getCommonJar(), getClientOnlyJar()); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/NamedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/NamedMinecraftProvider.java new file mode 100644 index 0000000..c3a127e --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/NamedMinecraftProvider.java @@ -0,0 +1,100 @@ +/* + * 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.configuration.providers.minecraft.mapped; + +import java.nio.file.Path; +import java.util.List; +import java.util.function.BiConsumer; + +import org.gradle.api.Project; + +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider; +import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.SidedClassVisitor; +import net.fabricmc.tinyremapper.TinyRemapper; + +public abstract class NamedMinecraftProvider extends AbstractMappedMinecraftProvider { + public NamedMinecraftProvider(Project project, M minecraftProvider) { + super(project, minecraftProvider); + } + + @Override + protected Path getDirectory() { + return extension.getMappingsProvider().mappingsWorkingDir(); + } + + @Override + public final MappingsNamespace getTargetNamespace() { + return MappingsNamespace.NAMED; + } + + public static final class MergedImpl extends NamedMinecraftProvider implements Merged { + public MergedImpl(Project project, MergedMinecraftProvider minecraftProvider) { + super(project, minecraftProvider); + } + + @Override + public List getRemappedJars() { + return List.of( + new RemappedJars(minecraftProvider.getMergedJar().toPath(), getMergedJar(), MappingsNamespace.OFFICIAL) + ); + } + + @Override + protected void applyDependencies(BiConsumer consumer) { + consumer.accept(Constants.Configurations.MINECRAFT_NAMED, MERGED); + } + } + + public static final class SplitImpl extends NamedMinecraftProvider implements Split { + public SplitImpl(Project project, SplitMinecraftProvider minecraftProvider) { + super(project, minecraftProvider); + } + + @Override + public List getRemappedJars() { + return List.of( + new RemappedJars(minecraftProvider.getMinecraftCommonJar().toPath(), getCommonJar(), MappingsNamespace.OFFICIAL), + new RemappedJars(minecraftProvider.getMinecraftClientOnlyJar().toPath(), getClientOnlyJar(), MappingsNamespace.OFFICIAL, minecraftProvider.getMinecraftCommonJar().toPath()) + ); + } + + @Override + protected void configureRemapper(RemappedJars remappedJars, TinyRemapper.Builder tinyRemapperBuilder) { + if (remappedJars.outputJar().equals(getClientOnlyJar())) { + tinyRemapperBuilder.extraPostApplyVisitor(SidedClassVisitor.CLIENT); + } + } + + @Override + protected void applyDependencies(BiConsumer consumer) { + consumer.accept(Constants.Configurations.MINECRAFT_NAMED, COMMON); + consumer.accept(Constants.Configurations.MINECRAFT_NAMED, CLIENT_ONLY); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java new file mode 100644 index 0000000..5201a16 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java @@ -0,0 +1,147 @@ +/* + * 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.configuration.providers.minecraft.mapped; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.List; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.configuration.processors.JarProcessorManager; +import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider; + +public abstract class ProcessedNamedMinecraftProvider> extends NamedMinecraftProvider { + private final P parentMinecraftProvider; + private final JarProcessorManager jarProcessorManager; + private final String projectMappedName; + private final Path projectMappedDir; + + public ProcessedNamedMinecraftProvider(P parentMinecraftProvide, JarProcessorManager jarProcessorManager) { + super(parentMinecraftProvide.getProject(), parentMinecraftProvide.getMinecraftProvider()); + this.parentMinecraftProvider = parentMinecraftProvide; + this.jarProcessorManager = jarProcessorManager; + + this.projectMappedName = "minecraft-project-%s-".formatted(getProject().getPath().replace(':', '@')); + + final LoomGradleExtension extension = LoomGradleExtension.get(getProject()); + this.projectMappedDir = extension.getFiles().getRootProjectPersistentCache().toPath() + .resolve(getMinecraftProvider().minecraftVersion()) + .resolve(extension.getMappingsProvider().mappingsIdentifier()); + } + + @Override + public void provide(boolean applyDependencies) throws Exception { + parentMinecraftProvider.provide(false); + + final List inputJars = parentMinecraftProvider.getMinecraftJars(); + boolean requiresProcessing = LoomGradlePlugin.refreshDeps || inputJars.stream() + .map(this::getProcessedPath) + .map(Path::toFile) + .anyMatch(jarProcessorManager::isInvalid); + + if (requiresProcessing) { + try { + Files.createDirectories(projectMappedDir); + } catch (IOException e) { + throw new UncheckedIOException("Failed to create project mapped dir", e); + } + + for (Path inputJar : inputJars) { + final Path outputJar = getProcessedPath(inputJar); + + Files.copy(inputJar, outputJar, StandardCopyOption.REPLACE_EXISTING); + jarProcessorManager.process(outputJar.toFile()); + } + } + + if (applyDependencies) { + parentMinecraftProvider.applyDependencies((configuration, name) -> getProject().getDependencies().add(configuration, getDependencyNotation(name))); + } + } + + @Override + protected String getName(String name) { + return "%s%s-%s".formatted(projectMappedName, name, getTargetNamespace().toString()); + } + + @Override + public Path getJar(String name) { + // Something has gone wrong if this gets called. + throw new UnsupportedOperationException(); + } + + @Override + public List getRemappedJars() { + throw new UnsupportedOperationException(); + } + + @Override + public List getMinecraftJars() { + return getParentMinecraftProvider().getMinecraftJars().stream() + .map(this::getProcessedPath) + .toList(); + } + + public P getParentMinecraftProvider() { + return parentMinecraftProvider; + } + + public Path getProcessedPath(Path input) { + return projectMappedDir.resolve(input.getFileName().toString().replace("minecraft-", projectMappedName)); + } + + public static final class MergedImpl extends ProcessedNamedMinecraftProvider implements Merged { + public MergedImpl(NamedMinecraftProvider.MergedImpl parentMinecraftProvide, JarProcessorManager jarProcessorManager) { + super(parentMinecraftProvide, jarProcessorManager); + } + + @Override + public Path getMergedJar() { + return getProcessedPath(getParentMinecraftProvider().getMergedJar()); + } + } + + public static final class SplitImpl extends ProcessedNamedMinecraftProvider implements Split { + public SplitImpl(NamedMinecraftProvider.SplitImpl parentMinecraftProvide, JarProcessorManager jarProcessorManager) { + super(parentMinecraftProvide, jarProcessorManager); + } + + @Override + public Path getCommonJar() { + return getProcessedPath(getParentMinecraftProvider().getCommonJar()); + } + + @Override + public Path getClientOnlyJar() { + return getProcessedPath(getParentMinecraftProvider().getClientOnlyJar()); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/extension/LoomFiles.java b/src/main/java/net/fabricmc/loom/extension/LoomFiles.java index 4fc07be..6e5e0f1 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomFiles.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomFiles.java @@ -47,4 +47,5 @@ public interface LoomFiles { File getDefaultLog4jConfigFile(); File getDevLauncherConfig(); File getUnpickLoggingConfigFile(); + File getRemapClasspathFile(); } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java index a79bb56..16752f5 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java @@ -92,4 +92,9 @@ public abstract class LoomFilesBaseImpl implements LoomFiles { public File getUnpickLoggingConfigFile() { return new File(getProjectPersistentCache(), "unpick-logging.properties"); } + + @Override + public File getRemapClasspathFile() { + return new File(getProjectPersistentCache(), "remapClasspath.txt"); + } } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index 58dbd7e..f581567 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -96,6 +96,9 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA this.versionParser = new ModVersionParser(project); this.deprecationHelper = new DeprecationHelper.ProjectBased(project); + + this.accessWidener.finalizeValueOnRead(); + this.getGameJarProcessors().finalizeValueOnRead(); } @Override diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index 37077dc..55541ec 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -25,6 +25,7 @@ package net.fabricmc.loom.extension; import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -43,10 +44,15 @@ import org.gradle.api.file.FileCollection; import org.gradle.api.tasks.SourceSet; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.InstallerData; import net.fabricmc.loom.configuration.LoomDependencyManager; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile; import net.fabricmc.loom.configuration.processors.JarProcessorManager; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider; public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implements LoomGradleExtension { private final Project project; @@ -62,6 +68,10 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen private LoomDependencyManager dependencyManager; private JarProcessorManager jarProcessorManager; + private MinecraftProvider minecraftProvider; + private MappingsProviderImpl mappingsProvider; + private NamedMinecraftProvider namedMinecraftProvider; + private IntermediaryMinecraftProvider intermediaryMinecraftProvider; private InstallerData installerData; public LoomGradleExtensionImpl(Project project, LoomFiles files) { @@ -116,6 +126,55 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen return Objects.requireNonNull(jarProcessorManager, "Cannot get JarProcessorManager before it has been setup"); } + @Override + public MinecraftProvider getMinecraftProvider() { + return Objects.requireNonNull(minecraftProvider, "Cannot get MinecraftProvider before it has been setup"); + } + + @Override + public void setMinecraftProvider(MinecraftProvider minecraftProvider) { + this.minecraftProvider = minecraftProvider; + } + + @Override + public MappingsProviderImpl getMappingsProvider() { + return Objects.requireNonNull(mappingsProvider, "Cannot get MappingsProvider before it has been setup"); + } + + @Override + public void setMappingsProvider(MappingsProviderImpl mappingsProvider) { + this.mappingsProvider = mappingsProvider; + } + + @Override + public NamedMinecraftProvider getNamedMinecraftProvider() { + return Objects.requireNonNull(namedMinecraftProvider, "Cannot get NamedMinecraftProvider before it has been setup"); + } + + @Override + public IntermediaryMinecraftProvider getIntermediaryMinecraftProvider() { + return Objects.requireNonNull(intermediaryMinecraftProvider, "Cannot get IntermediaryMinecraftProvider before it has been setup"); + } + + @Override + public void setNamedMinecraftProvider(NamedMinecraftProvider namedMinecraftProvider) { + this.namedMinecraftProvider = namedMinecraftProvider; + } + + @Override + public void setIntermediaryMinecraftProvider(IntermediaryMinecraftProvider intermediaryMinecraftProvider) { + this.intermediaryMinecraftProvider = intermediaryMinecraftProvider; + } + + @Override + public FileCollection getMinecraftJarsCollection(MappingsNamespace mappingsNamespace) { + return getProject().files( + getProject().provider(() -> + getProject().files(getMinecraftJars(mappingsNamespace).stream().map(Path::toFile).toList()) + ) + ); + } + @Override public MappingSet getOrCreateSrcMappingCache(int id, Supplier factory) { return srcMappingCache[id] != null ? srcMappingCache[id] : (srcMappingCache[id] = factory.get()); diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index 3faf600..c932418 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -57,12 +57,10 @@ import org.gradle.workers.WorkerExecutor; import org.gradle.workers.internal.WorkerDaemonClientsManager; import org.jetbrains.annotations.Nullable; -import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.decompilers.DecompilationMetadata; import net.fabricmc.loom.api.decompilers.LoomDecompiler; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile; import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerMappingsProcessor; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.decompilers.LineNumberRemapper; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.FileSystemUtil; @@ -77,9 +75,18 @@ import net.fabricmc.loom.util.ipc.IPCServer; public abstract class GenerateSourcesTask extends AbstractLoomTask { public final LoomDecompiler decompiler; + /** + * The jar to decompile, can be the unpick jar. + */ @InputFile public abstract RegularFileProperty getInputJar(); + /** + * The jar used at runtime. + */ + @InputFile + public abstract RegularFileProperty getRuntimeJar(); + /** * Max memory for forked JVM in megabytes. */ @@ -153,7 +160,7 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { params.getOptions().set(getOptions()); params.getInputJar().set(getInputJar()); - params.getRuntimeJar().set(getExtension().getMappingsProvider().mappedProvider.getMappedJar()); + params.getRuntimeJar().set(getRuntimeJar()); params.getSourcesDestinationJar().set(getMappedJarFileWithSuffix("-sources.jar")); params.getLinemap().set(getMappedJarFileWithSuffix("-sources.lmap")); params.getLinemapJar().set(getMappedJarFileWithSuffix("-linemapped.jar")); @@ -299,10 +306,7 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { } private File getMappedJarFileWithSuffix(String suffix) { - LoomGradleExtension extension = LoomGradleExtension.get(getProject()); - MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); - File mappedJar = mappingsProvider.mappedProvider.getMappedJar(); - String path = mappedJar.getAbsolutePath(); + String path = getRuntimeJar().get().getAsFile().getAbsolutePath(); if (!path.toLowerCase(Locale.ROOT).endsWith(".jar")) { throw new RuntimeException("Invalid mapped JAR path: " + path); diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index 971664f..30a70d0 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.java @@ -31,6 +31,9 @@ import org.gradle.api.tasks.TaskProvider; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.ide.RunConfigSettings; +import net.fabricmc.loom.task.launch.GenerateDLIConfigTask; +import net.fabricmc.loom.task.launch.GenerateLog4jConfigTask; +import net.fabricmc.loom.task.launch.GenerateRemapClasspathTask; import net.fabricmc.loom.util.Constants; public final class LoomTasks { @@ -47,11 +50,32 @@ public final class LoomTasks { RemapTaskConfiguration.setupRemap(project); - TaskProvider extractNatives = tasks.register("extractNatives", ExtractNativesTask.class); + tasks.register("extractNatives", ExtractNativesTask.class, t -> { + t.setDescription("Extracts the minecraft platform specific natives."); + }); tasks.register("downloadAssets", DownloadAssetsTask.class, t -> { - t.dependsOn(extractNatives); t.setDescription("Downloads required assets for Fabric."); }); + tasks.register("generateDLIConfig", GenerateDLIConfigTask.class, t -> { + t.setDescription("Generate the DevLaunchInjector config file"); + }); + tasks.register("generateLog4jConfig", GenerateLog4jConfigTask.class, t -> { + t.setDescription("Generate the log4j config file"); + }); + tasks.register("generateRemapClasspath", GenerateRemapClasspathTask.class, t -> { + t.setDescription("Generate the remap classpath file"); + }); + + tasks.register("configureLaunch", task -> { + task.dependsOn(tasks.named("extractNatives")); + task.dependsOn(tasks.named("downloadAssets")); + task.dependsOn(tasks.named("generateDLIConfig")); + task.dependsOn(tasks.named("generateLog4jConfig")); + task.dependsOn(tasks.named("generateRemapClasspath")); + + task.setDescription("Setup the required files to launch Minecraft"); + task.setGroup(Constants.TaskGroup.FABRIC); + }); TaskProvider validateAccessWidener = tasks.register("validateAccessWidener", ValidateAccessWidenerTask.class, t -> { t.setDescription("Validate all the rules in the access widener against the Minecraft jar"); @@ -62,19 +86,18 @@ public final class LoomTasks { registerIDETasks(tasks); registerRunTasks(tasks, project); - registerDecompileTasks(tasks, project); } private static void registerIDETasks(TaskContainer tasks) { tasks.register("genIdeaWorkspace", GenIdeaProjectTask.class, t -> { t.setDescription("Generates an IntelliJ IDEA workspace from this project."); - t.dependsOn("idea", "downloadAssets"); + t.dependsOn("idea", "configureLaunch"); t.setGroup(Constants.TaskGroup.IDE); }); tasks.register("genEclipseRuns", GenEclipseRunsTask.class, t -> { t.setDescription("Generates Eclipse run configurations for this project."); - t.dependsOn("downloadAssets"); + t.dependsOn("configureLaunch"); t.setGroup(Constants.TaskGroup.IDE); }); @@ -85,7 +108,7 @@ public final class LoomTasks { tasks.register("vscode", GenVsCodeProjectTask.class, t -> { t.setDescription("Generates VSCode launch configurations."); - t.dependsOn("downloadAssets"); + t.dependsOn("configureLaunch"); t.setGroup(Constants.TaskGroup.IDE); }); } @@ -102,32 +125,11 @@ public final class LoomTasks { tasks.register(taskName, RunGameTask.class, config).configure(t -> { t.setDescription("Starts the '" + config.getConfigName() + "' run configuration"); - if (config.getEnvironment().equals("client")) { - t.dependsOn("downloadAssets"); - } + t.dependsOn("configureLaunch"); }); }); extension.getRunConfigs().create("client", RunConfigSettings::client); extension.getRunConfigs().create("server", RunConfigSettings::server); } - - private static void registerDecompileTasks(TaskContainer tasks, Project project) { - LoomGradleExtension.get(project).getGameDecompilers().configureEach(decompiler -> { - String taskName = "genSourcesWith" + decompiler.name(); - // Decompiler will be passed to the constructor of GenerateSourcesTask - tasks.register(taskName, GenerateSourcesTask.class, decompiler).configure(task -> { - task.setDescription("Decompile minecraft using %s.".formatted(decompiler.name())); - task.setGroup(Constants.TaskGroup.FABRIC); - task.dependsOn(tasks.named("validateAccessWidener")); - }); - }); - - tasks.register("genSources", task -> { - task.setDescription("Decompile minecraft using the default decompiler."); - task.setGroup(Constants.TaskGroup.FABRIC); - - task.dependsOn(project.getTasks().named("genSourcesWithCfr")); - }); - } } diff --git a/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java b/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java index 053c93b..fb9a8fb 100644 --- a/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java +++ b/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java @@ -51,7 +51,6 @@ import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.api.mappings.layered.spec.LayeredMappingSpecBuilder; import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsDependency; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; import net.fabricmc.loom.util.SourceRemapper; import net.fabricmc.lorenztiny.TinyMappingsJoiner; import net.fabricmc.mappingio.MappingReader; @@ -101,7 +100,7 @@ public class MigrateMappingsTask extends AbstractLoomTask { try { MemoryMappingTree currentMappings = mappingsProvider.getMappings(); MemoryMappingTree targetMappings = getMappings(mappings); - migrateMappings(project, extension.getMinecraftMappedProvider(), inputDir, outputDir, currentMappings, targetMappings); + migrateMappings(project, extension, inputDir, outputDir, currentMappings, targetMappings); project.getLogger().lifecycle(":remapped project written to " + outputDir.toAbsolutePath()); } catch (IOException e) { throw new IllegalArgumentException("Error while loading mappings", e); @@ -157,7 +156,7 @@ public class MigrateMappingsTask extends AbstractLoomTask { return mappingTree; } - private static void migrateMappings(Project project, MinecraftMappedProvider minecraftMappedProvider, + private static void migrateMappings(Project project, LoomGradleExtension extension, Path inputDir, Path outputDir, MemoryMappingTree currentMappings, MemoryMappingTree targetMappings ) throws IOException { project.getLogger().info(":joining mappings"); @@ -174,8 +173,13 @@ public class MigrateMappingsTask extends AbstractLoomTask { final JavaVersion javaVersion = project.getExtensions().getByType(JavaPluginExtension.class).getSourceCompatibility(); mercury.setSourceCompatibility(javaVersion.toString()); - mercury.getClassPath().add(minecraftMappedProvider.getMappedJar().toPath()); - mercury.getClassPath().add(minecraftMappedProvider.getIntermediaryJar().toPath()); + for (Path intermediaryJar : extension.getMinecraftJars(MappingsNamespace.INTERMEDIARY)) { + mercury.getClassPath().add(intermediaryJar); + } + + for (Path intermediaryJar : extension.getMinecraftJars(MappingsNamespace.NAMED)) { + mercury.getClassPath().add(intermediaryJar); + } mercury.getProcessors().add(MercuryRemapper.create(mappingSet)); diff --git a/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java b/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java index 35fa28d..34d2cc8 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java +++ b/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java @@ -84,8 +84,8 @@ public class RemapTaskConfiguration { // Remove -dev jars from the default jar task for (String configurationName : new String[] { JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME, JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME }) { Configuration configuration = project.getConfigurations().getByName(configurationName); + final Task jarTask = project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME); configuration.getArtifacts().removeIf(artifact -> { - Task jarTask = project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME); // if the artifact is a -dev jar and "builtBy jar" return "dev".equals(artifact.getClassifier()) && artifact.getBuildDependencies().getDependencies(null).contains(jarTask); }); diff --git a/src/main/java/net/fabricmc/loom/task/UnpickJarTask.java b/src/main/java/net/fabricmc/loom/task/UnpickJarTask.java index e750cb1..3ac72fe 100644 --- a/src/main/java/net/fabricmc/loom/task/UnpickJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/UnpickJarTask.java @@ -28,6 +28,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; +import java.nio.file.Path; import javax.inject.Inject; @@ -40,7 +41,7 @@ import org.gradle.api.tasks.JavaExec; import org.gradle.api.tasks.OutputFile; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.configuration.providers.LaunchProvider; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.extension.LoomFiles; import net.fabricmc.loom.util.Constants; @@ -76,7 +77,9 @@ public abstract class UnpickJarTask extends JavaExec { fileArg(getConstantJar().getSingleFile()); // Classpath - fileArg(getExtension().getMinecraftMappedProvider().getMappedJar()); + for (Path minecraftJar : getExtension().getMinecraftJars(MappingsNamespace.NAMED)) { + fileArg(minecraftJar.toFile()); + } for (File file : getUnpickClasspath()) { fileArg(file); @@ -89,7 +92,7 @@ public abstract class UnpickJarTask extends JavaExec { } private void writeUnpickLogConfig() { - try (InputStream is = LaunchProvider.class.getClassLoader().getResourceAsStream("unpick-logging.properties")) { + try (InputStream is = UnpickJarTask.class.getClassLoader().getResourceAsStream("unpick-logging.properties")) { Files.deleteIfExists(getDirectories().getUnpickLoggingConfigFile().toPath()); Files.copy(is, getDirectories().getUnpickLoggingConfigFile().toPath()); } catch (IOException e) { diff --git a/src/main/java/net/fabricmc/loom/task/ValidateAccessWidenerTask.java b/src/main/java/net/fabricmc/loom/task/ValidateAccessWidenerTask.java index 4ebbb33..5f2e79b 100644 --- a/src/main/java/net/fabricmc/loom/task/ValidateAccessWidenerTask.java +++ b/src/main/java/net/fabricmc/loom/task/ValidateAccessWidenerTask.java @@ -25,6 +25,7 @@ package net.fabricmc.loom.task; import java.io.BufferedReader; +import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; @@ -33,8 +34,10 @@ import java.nio.file.Files; import javax.inject.Inject; import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.api.tasks.TaskAction; @@ -42,6 +45,7 @@ import net.fabricmc.accesswidener.AccessWidenerFormatException; import net.fabricmc.accesswidener.AccessWidenerReader; import net.fabricmc.accesswidener.AccessWidenerVisitor; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.tinyremapper.TinyRemapper; import net.fabricmc.tinyremapper.api.TrEnvironment; @@ -50,15 +54,15 @@ public abstract class ValidateAccessWidenerTask extends DefaultTask { @InputFile public abstract RegularFileProperty getAccessWidener(); - @InputFile - public abstract RegularFileProperty getTargetJar(); + @InputFiles + public abstract ConfigurableFileCollection getTargetJars(); @Inject public ValidateAccessWidenerTask() { final LoomGradleExtension extension = LoomGradleExtension.get(getProject()); getAccessWidener().convention(extension.getAccessWidenerPath()).finalizeValueOnRead(); - getTargetJar().convention(getProject().getObjects().fileProperty().fileValue(extension.getMinecraftMappedProvider().getMappedJar())).finalizeValueOnRead(); + getTargetJars().from(extension.getMinecraftJarsCollection(MappingsNamespace.NAMED)); // Ignore outputs for up-to-date checks as there aren't any (so only inputs are checked) getOutputs().upToDateWhen(task -> true); @@ -67,7 +71,10 @@ public abstract class ValidateAccessWidenerTask extends DefaultTask { @TaskAction public void run() { final TinyRemapper tinyRemapper = TinyRemapper.newRemapper().build(); - tinyRemapper.readClassPath(getTargetJar().get().getAsFile().toPath()); + + for (File file : getTargetJars().getFiles()) { + tinyRemapper.readClassPath(file.toPath()); + } final AccessWidenerValidator validator = new AccessWidenerValidator(tinyRemapper.getEnvironment()); final AccessWidenerReader accessWidenerReader = new AccessWidenerReader(validator); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java similarity index 55% rename from src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java rename to src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java index aeb387d..f325957 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2019-2021 FabricMC + * Copyright (c) 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 @@ -22,43 +22,33 @@ * SOFTWARE. */ -package net.fabricmc.loom.configuration.providers; +package net.fabricmc.loom.task.launch; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringJoiner; -import java.util.function.Consumer; import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; -import org.gradle.api.Project; import org.gradle.api.logging.configuration.ConsoleOutput; -import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.tasks.TaskAction; -import net.fabricmc.loom.configuration.DependencyProvider; -import net.fabricmc.loom.configuration.RemappedConfigurationEntry; -import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.task.AbstractLoomTask; -public class LaunchProvider extends DependencyProvider { - public LaunchProvider(Project project) { - super(project); - } - - @Override - public void provide(DependencyInfo dependency, Consumer postPopulationScheduler) throws IOException { +public abstract class GenerateDLIConfigTask extends AbstractLoomTask { + @TaskAction + public void run() throws IOException { final String nativesPath = getExtension().getFiles().getNativesDirectory(getProject()).getAbsolutePath(); final LaunchConfig launchConfig = new LaunchConfig() .property("fabric.development", "true") - .property("fabric.remapClasspathFile", getRemapClasspathFile().getAbsolutePath()) + .property("fabric.remapClasspathFile", getExtension().getFiles().getRemapClasspathFile().getAbsolutePath()) .property("log4j.configurationFile", getAllLog4JConfigFiles()) .property("log4j2.formatMsgNoLookups", "true") @@ -68,7 +58,7 @@ public class LaunchProvider extends DependencyProvider { .argument("client", "--assetIndex") .argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex().fabricId(getExtension().getMinecraftProvider().minecraftVersion())) .argument("client", "--assetsDir") - .argument("client", new File(getDirectories().getUserCache(), "assets").getAbsolutePath()); + .argument("client", new File(getExtension().getFiles().getUserCache(), "assets").getAbsolutePath()); final boolean plainConsole = getProject().getGradle().getStartParameter().getConsoleOutput() == ConsoleOutput.Plain; final boolean ansiSupportedIDE = new File(getProject().getRootDir(), ".vscode").exists() @@ -80,18 +70,7 @@ public class LaunchProvider extends DependencyProvider { launchConfig.property("fabric.log.disableAnsi", "false"); } - writeLog4jConfig(); - FileUtils.writeStringToFile(getDirectories().getDevLauncherConfig(), launchConfig.asString(), StandardCharsets.UTF_8); - - addDependency(Constants.Dependencies.DEV_LAUNCH_INJECTOR + Constants.Dependencies.Versions.DEV_LAUNCH_INJECTOR, Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES); - addDependency(Constants.Dependencies.TERMINAL_CONSOLE_APPENDER + Constants.Dependencies.Versions.TERMINAL_CONSOLE_APPENDER, Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES); - addDependency(Constants.Dependencies.JETBRAINS_ANNOTATIONS + Constants.Dependencies.Versions.JETBRAINS_ANNOTATIONS, JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME); - - postPopulationScheduler.accept(this::writeRemapClassPath); - } - - private File getLog4jConfigFile() { - return getDirectories().getDefaultLog4jConfigFile(); + FileUtils.writeStringToFile(getExtension().getFiles().getDevLauncherConfig(), launchConfig.asString(), StandardCharsets.UTF_8); } private String getAllLog4JConfigFiles() { @@ -100,48 +79,6 @@ public class LaunchProvider extends DependencyProvider { .collect(Collectors.joining(",")); } - private File getRemapClasspathFile() { - return new File(getDirectories().getDevLauncherConfig().getParentFile(), "remapClasspath.txt"); - } - - private void writeLog4jConfig() { - try (InputStream is = LaunchProvider.class.getClassLoader().getResourceAsStream("log4j2.fabric.xml")) { - Files.deleteIfExists(getLog4jConfigFile().toPath()); - Files.copy(is, getLog4jConfigFile().toPath()); - } catch (IOException e) { - throw new RuntimeException("Failed to generate log4j config", e); - } - } - - private void writeRemapClassPath() { - List inputConfigurations = new ArrayList<>(); - inputConfigurations.add(Constants.Configurations.LOADER_DEPENDENCIES); - inputConfigurations.addAll(Constants.MOD_COMPILE_ENTRIES.stream().map(RemappedConfigurationEntry::sourceConfiguration).collect(Collectors.toList())); - - List remapClasspath = new ArrayList<>(); - - for (String inputConfiguration : inputConfigurations) { - remapClasspath.addAll(getProject().getConfigurations().getByName(inputConfiguration).getFiles()); - } - - remapClasspath.add(getExtension().getMinecraftMappedProvider().getIntermediaryJar()); - - String str = remapClasspath.stream() - .map(File::getAbsolutePath) - .collect(Collectors.joining(File.pathSeparator)); - - try { - Files.writeString(getRemapClasspathFile().toPath(), str); - } catch (IOException e) { - throw new RuntimeException("Failed to generate remap classpath", e); - } - } - - @Override - public String getTargetConfig() { - return Constants.Configurations.MINECRAFT_NAMED; - } - public static class LaunchConfig { private final Map> values = new HashMap<>(); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java b/src/main/java/net/fabricmc/loom/task/launch/GenerateLog4jConfigTask.java similarity index 60% rename from src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java rename to src/main/java/net/fabricmc/loom/task/launch/GenerateLog4jConfigTask.java index d8e4a77..fc249bb 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateLog4jConfigTask.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2018-2021 FabricMC + * Copyright (c) 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 @@ -22,20 +22,27 @@ * SOFTWARE. */ -package net.fabricmc.loom.configuration.providers; +package net.fabricmc.loom.task.launch; -import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; +import org.gradle.api.tasks.TaskAction; -public interface MinecraftProvider { - File workingDir(); +import net.fabricmc.loom.task.AbstractLoomTask; - File dir(String path); +public abstract class GenerateLog4jConfigTask extends AbstractLoomTask { + @TaskAction + public void run() { + Path outputFile = getExtension().getFiles().getDefaultLog4jConfigFile().toPath(); - File file(String path); - - String minecraftVersion(); - - MinecraftVersionMeta getVersionInfo(); + try (InputStream is = GenerateLog4jConfigTask.class.getClassLoader().getResourceAsStream("log4j2.fabric.xml")) { + Files.deleteIfExists(outputFile); + Files.copy(is, outputFile); + } catch (IOException e) { + throw new RuntimeException("Failed to generate log4j config", e); + } + } } diff --git a/src/main/java/net/fabricmc/loom/task/launch/GenerateRemapClasspathTask.java b/src/main/java/net/fabricmc/loom/task/launch/GenerateRemapClasspathTask.java new file mode 100644 index 0000000..d97b5ab --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateRemapClasspathTask.java @@ -0,0 +1,69 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.task.launch; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.gradle.api.tasks.TaskAction; + +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.configuration.RemappedConfigurationEntry; +import net.fabricmc.loom.task.AbstractLoomTask; +import net.fabricmc.loom.util.Constants; + +public abstract class GenerateRemapClasspathTask extends AbstractLoomTask { + @TaskAction + public void run() { + List inputConfigurations = new ArrayList<>(); + inputConfigurations.add(Constants.Configurations.LOADER_DEPENDENCIES); + inputConfigurations.addAll(Constants.MOD_COMPILE_ENTRIES.stream().map(RemappedConfigurationEntry::sourceConfiguration).toList()); + + List remapClasspath = new ArrayList<>(); + + for (String inputConfiguration : inputConfigurations) { + remapClasspath.addAll(getProject().getConfigurations().getByName(inputConfiguration).getFiles()); + } + + for (Path minecraftJar : getExtension().getMinecraftJars(MappingsNamespace.INTERMEDIARY)) { + remapClasspath.add(minecraftJar.toFile()); + } + + String str = remapClasspath.stream() + .map(File::getAbsolutePath) + .collect(Collectors.joining(File.pathSeparator)); + + try { + Files.writeString(getExtension().getFiles().getRemapClasspathFile().toPath(), str); + } catch (IOException e) { + throw new RuntimeException("Failed to generate remap classpath", e); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java b/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java index 3ca404e..4f01e7d 100644 --- a/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java +++ b/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java @@ -48,7 +48,7 @@ public class HashedDownloadUtil { } public static void downloadIfInvalid(URL from, File to, String expectedHash, Logger logger, boolean quiet, Runnable startDownload) throws IOException { - if (LoomGradlePlugin.refreshDeps) { + if (LoomGradlePlugin.refreshDeps && !Boolean.getBoolean("loom.refresh")) { delete(to); } diff --git a/src/main/java/net/fabricmc/loom/util/SidedClassVisitor.java b/src/main/java/net/fabricmc/loom/util/SidedClassVisitor.java new file mode 100644 index 0000000..51fe9c5 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/SidedClassVisitor.java @@ -0,0 +1,59 @@ +/* + * 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.Locale; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassVisitor; + +import net.fabricmc.tinyremapper.TinyRemapper; + +/** + * Applies the @Environment annotation to all classes. + */ +public final class SidedClassVisitor extends ClassVisitor { + public static final TinyRemapper.ApplyVisitorProvider CLIENT = (cls, next) -> new SidedClassVisitor("client", next); + public static final TinyRemapper.ApplyVisitorProvider SERVER = (cls, next) -> new SidedClassVisitor("server", next); + + private static final String ENVIRONMENT_DESCRIPTOR = "Lnet/fabricmc/api/Environment;"; + private static final String SIDE_DESCRIPTOR = "Lnet/fabricmc/api/EnvType;"; + + private final String side; + + private SidedClassVisitor(String side, ClassVisitor next) { + super(Constants.ASM_VERSION, next); + this.side = side; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + super.visit(version, access, name, signature, superName, interfaces); + + final AnnotationVisitor annotationVisitor = visitAnnotation(ENVIRONMENT_DESCRIPTOR, true); + annotationVisitor.visitEnum("value", SIDE_DESCRIPTOR, side.toUpperCase(Locale.ROOT)); + annotationVisitor.visitEnd(); + } +} diff --git a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java index 08b241d..64261ea 100644 --- a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java +++ b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java @@ -183,8 +183,13 @@ public class SourceRemapper { } } - m.getClassPath().add(extension.getMinecraftMappedProvider().getMappedJar().toPath()); - m.getClassPath().add(extension.getMinecraftMappedProvider().getIntermediaryJar().toPath()); + for (Path intermediaryJar : extension.getMinecraftJars(MappingsNamespace.INTERMEDIARY)) { + m.getClassPath().add(intermediaryJar); + } + + for (Path intermediaryJar : extension.getMinecraftJars(MappingsNamespace.NAMED)) { + m.getClassPath().add(intermediaryJar); + } Set files = project.getConfigurations() .detachedConfiguration(project.getDependencies().create(Constants.Dependencies.JETBRAINS_ANNOTATIONS + Constants.Dependencies.Versions.JETBRAINS_ANNOTATIONS)) diff --git a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index 01cc67b..3aab493 100644 --- a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy @@ -28,7 +28,7 @@ import org.gradle.util.GradleVersion class LoomTestConstants { public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion() - public final static String PRE_RELEASE_GRADLE = "7.5-20211228231407+0000" + public final static String PRE_RELEASE_GRADLE = "7.5-20220101231120+0000" public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] } diff --git a/src/test/groovy/net/fabricmc/loom/test/benchmark/SimpleBenchmark.groovy b/src/test/groovy/net/fabricmc/loom/test/benchmark/SimpleBenchmark.groovy new file mode 100644 index 0000000..6fd7976 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/benchmark/SimpleBenchmark.groovy @@ -0,0 +1,75 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.test.benchmark + +import groovy.time.TimeCategory +import groovy.time.TimeDuration +import net.fabricmc.loom.test.LoomTestConstants +import net.fabricmc.loom.test.util.GradleProjectTestTrait + +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +/** + * Run this class, passing a working dir as the first argument. + * Allow for one warm up run before profiling, follow up runs should not be using the network. + */ +@Singleton +class SimpleBenchmark implements GradleProjectTestTrait { + def run(File dir) { + // Forces loom to refresh files + System.setProperty("loom.refresh", "true") + + def gradle = gradleProject( + project: "minimalBase", + version: LoomTestConstants.PRE_RELEASE_GRADLE, + projectDir: new File(dir, "project"), + gradleHomeDir: new File(dir, "gradlehome") + ) + + gradle.buildGradle << ''' + dependencies { + minecraft "com.mojang:minecraft:1.18.1" + mappings "net.fabricmc:yarn:1.18.1+build.17:v2" + modImplementation "net.fabricmc.fabric-api:fabric-api:0.45.1+1.18" + } + ''' + + def timeStart = new Date() + + def result = gradle.run(tasks: ["clean", "build"]) + + def timeStop = new Date() + TimeDuration duration = TimeCategory.minus(timeStop, timeStart) + println(duration) + + assert result.task(":build").outcome == SUCCESS + + System.exit(0) + } + + static void main(String[] args) { + getInstance().run(new File(args[0])) + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/MinecraftJarSplitterTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/MinecraftJarSplitterTest.groovy new file mode 100644 index 0000000..0b6d979 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/MinecraftJarSplitterTest.groovy @@ -0,0 +1,69 @@ +/* + * 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.test.unit + +import net.fabricmc.loom.configuration.providers.BundleMetadata +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarSplitter +import spock.lang.Specification + +class MinecraftJarSplitterTest extends Specification { + public static final String CLIENT_JAR_URL = "https://launcher.mojang.com/v1/objects/7e46fb47609401970e2818989fa584fd467cd036/client.jar" + public static final String SERVER_BUNDLE_JAR_URL = "https://launcher.mojang.com/v1/objects/125e5adf40c659fd3bce3e66e67a16bb49ecc1b9/server.jar" + + public static final File mcJarDir = File.createTempDir() + + def "split jars"() { + given: + def clientJar = downloadJarIfNotExists(CLIENT_JAR_URL, "client.jar") + def serverBundleJar = downloadJarIfNotExists(SERVER_BUNDLE_JAR_URL, "server_bundle.jar") + def serverJar = new File(mcJarDir, "server.jar") + + def clientOnlyJar = new File(mcJarDir, "client_only.jar") + def commonJar = new File(mcJarDir, "common.jar") + when: + def serverBundleMetadata = BundleMetadata.fromJar(serverBundleJar.toPath()) + serverBundleMetadata.versions().find().unpackEntry(serverBundleJar.toPath(), serverJar.toPath()) + + clientOnlyJar.delete() + commonJar.delete() + + new MinecraftJarSplitter(clientJar.toPath(), serverJar.toPath()).withCloseable { + it.split(clientOnlyJar.toPath(), commonJar.toPath()) + } + then: + serverBundleMetadata.versions().size() == 1 + } + + File downloadJarIfNotExists(String url, String name) { + File dst = new File(mcJarDir, name) + + if (!dst.exists()) { + dst.parentFile.mkdirs() + dst << new URL(url).newInputStream() + } + + return dst + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy index f751f3a..fb68243 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy @@ -25,7 +25,7 @@ package net.fabricmc.loom.test.unit.layeredmappings import groovy.transform.CompileStatic -import net.fabricmc.loom.configuration.providers.MinecraftProvider +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsProcessor import net.fabricmc.loom.api.mappings.layered.MappingContext diff --git a/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy b/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy index fa08793..bd68fd9 100644 --- a/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy @@ -211,7 +211,7 @@ trait GradleProjectTestTrait { } File getGeneratedSources(String mappings) { - return new File(getGradleHomeDir(), "caches/fabric-loom/${mappings}/minecraft-mapped-sources.jar") + return new File(getGradleHomeDir(), "caches/fabric-loom/${mappings}/minecraft-merged-named-sources.jar") } } } \ No newline at end of file