diff options
author | shedaniel <daniel@shedaniel.me> | 2021-04-04 19:30:51 +0800 |
---|---|---|
committer | shedaniel <daniel@shedaniel.me> | 2021-04-04 19:30:51 +0800 |
commit | ad1754a932ef0c6b2134f5d717f6e55f692d85ee (patch) | |
tree | e46d6f443b4b68698d494dc5de4aec71a0f74bd2 /src/main/java/net/fabricmc/loom | |
parent | 8bf5870c7f2d2cae6efe79ea46230e633db1e9c3 (diff) | |
parent | 98731532d57d31e3084ef932cce2435d872772c2 (diff) | |
download | architectury-loom-ad1754a932ef0c6b2134f5d717f6e55f692d85ee.tar.gz architectury-loom-ad1754a932ef0c6b2134f5d717f6e55f692d85ee.tar.bz2 architectury-loom-ad1754a932ef0c6b2134f5d717f6e55f692d85ee.zip |
Merge remote-tracking branch 'FabricMC/dev/0.7' into dev/0.7-forge
# Conflicts:
# .github/workflows/test-push.yml
# build.gradle
# src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java
# src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java
# src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java
# src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProvider.java
# src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java
# src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java
# src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractFernFlowerDecompiler.java
# src/main/java/net/fabricmc/loom/decompilers/fernflower/ForkingJavaExec.java
# src/main/java/net/fabricmc/loom/task/RemapJarTask.java
# src/main/java/net/fabricmc/loom/util/GroovyXmlUtil.java
# src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java
# src/test/groovy/net/fabricmc/loom/BuildUtils.groovy
Diffstat (limited to 'src/main/java/net/fabricmc/loom')
30 files changed, 1109 insertions, 557 deletions
diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index 654f96c0..3fcefcea 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -501,6 +501,10 @@ public class LoomGradleExtension { return new File(getProjectPersistentCache(), "log4j.xml"); } + public File getUnpickLoggingConfigFile() { + return new File(getProjectPersistentCache(), "unpick-logging.properties"); + } + public ConfigurableFileCollection getLog4jConfigs() { return log4jConfigs; } diff --git a/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java b/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java new file mode 100644 index 00000000..6330b612 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java @@ -0,0 +1,95 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 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.build.nesting; + +import java.io.File; +import java.util.Collection; +import java.util.zip.ZipEntry; + +import com.google.common.base.Preconditions; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.gradle.api.logging.Logger; +import org.zeroturnaround.zip.FileSource; +import org.zeroturnaround.zip.ZipEntrySource; +import org.zeroturnaround.zip.ZipUtil; +import org.zeroturnaround.zip.transform.StringZipEntryTransformer; +import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry; + +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.util.ModUtils; + +public class JarNester { + public static void nestJars(Collection<File> jars, File modJar, Logger logger) { + if (jars.isEmpty()) { + logger.debug("Nothing to nest into " + modJar.getName()); + return; + } + + Preconditions.checkArgument(ModUtils.isMod(modJar), "Cannot nest jars into none mod jar " + modJar.getName()); + + ZipUtil.addEntries(modJar, jars.stream().map(file -> new FileSource("META-INF/jars/" + file.getName(), file)).toArray(ZipEntrySource[]::new)); + + boolean didNest = ZipUtil.transformEntries(modJar, single(new ZipEntryTransformerEntry("fabric.mod.json", new StringZipEntryTransformer() { + @Override + protected String transform(ZipEntry zipEntry, String input) { + JsonObject json = LoomGradlePlugin.GSON.fromJson(input, JsonObject.class); + JsonArray nestedJars = json.getAsJsonArray("jars"); + + if (nestedJars == null || !json.has("jars")) { + nestedJars = new JsonArray(); + } + + for (File file : jars) { + String nestedJarPath = "META-INF/jars/" + file.getName(); + + for (JsonElement nestedJar : nestedJars) { + JsonObject jsonObject = nestedJar.getAsJsonObject(); + + if (jsonObject.has("file") && jsonObject.get("file").getAsString().equals(nestedJarPath)) { + throw new IllegalStateException("Cannot nest 2 jars at the same path: " + nestedJarPath); + } + } + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("file", nestedJarPath); + nestedJars.add(jsonObject); + + logger.debug("Nested " + nestedJarPath + " into " + modJar.getName()); + } + + json.add("jars", nestedJars); + + return LoomGradlePlugin.GSON.toJson(json); + } + }))); + Preconditions.checkArgument(didNest, "Failed to nest jars into " + modJar.getName()); + } + + private static ZipEntryTransformerEntry[] single(ZipEntryTransformerEntry element) { + return new ZipEntryTransformerEntry[]{element}; + } +} diff --git a/src/main/java/net/fabricmc/loom/build/nesting/MergedNestedJarProvider.java b/src/main/java/net/fabricmc/loom/build/nesting/MergedNestedJarProvider.java new file mode 100644 index 00000000..566002d4 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/build/nesting/MergedNestedJarProvider.java @@ -0,0 +1,46 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 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.build.nesting; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Collectors; + +public class MergedNestedJarProvider implements NestedJarProvider { + private final NestedJarProvider[] parents; + + public MergedNestedJarProvider(NestedJarProvider... parents) { + this.parents = parents; + } + + @Override + public Collection<File> provide() { + return Arrays.stream(parents) + .map(NestedJarProvider::provide) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/net/fabricmc/loom/build/NestedJars.java b/src/main/java/net/fabricmc/loom/build/nesting/NestedDependencyProvider.java index c88c4487..dbca51ed 100644 --- a/src/main/java/net/fabricmc/loom/build/NestedJars.java +++ b/src/main/java/net/fabricmc/loom/build/nesting/NestedDependencyProvider.java @@ -22,22 +22,18 @@ * SOFTWARE. */ -package net.fabricmc.loom.build; +package net.fabricmc.loom.build.nesting; import java.io.File; import java.io.IOException; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.stream.Collectors; -import java.util.zip.ZipEntry; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; import org.apache.commons.io.FileUtils; import org.gradle.api.Project; @@ -50,68 +46,64 @@ import org.gradle.api.artifacts.ResolvedArtifact; import org.gradle.api.artifacts.ResolvedConfiguration; import org.gradle.api.artifacts.ResolvedDependency; import org.gradle.api.tasks.bundling.AbstractArchiveTask; -import org.zeroturnaround.zip.FileSource; -import org.zeroturnaround.zip.ZipEntrySource; import org.zeroturnaround.zip.ZipUtil; -import org.zeroturnaround.zip.transform.StringZipEntryTransformer; -import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.task.RemapJarTask; import net.fabricmc.loom.util.Constants; -public class NestedJars { - public static boolean addNestedJars(Project project, Path modJarPath) { - List<File> containedJars = getContainedJars(project); +public final class NestedDependencyProvider implements NestedJarProvider { + final Project project; + final List<DependencyInfo<?>> files; - if (containedJars.isEmpty()) { - return false; - } + private NestedDependencyProvider(Project project, List<DependencyInfo<?>> files) { + this.project = project; + this.files = files; + } - File modJar = modJarPath.toFile(); + public static NestedDependencyProvider createNestedDependencyProviderFromConfiguration(Project project, Configuration configuration) { + List<DependencyInfo<?>> fileList = new ArrayList<>(); + Set<String> visited = new HashSet<>(); - ZipUtil.addOrReplaceEntries(modJar, containedJars.stream().map(file -> new FileSource("META-INF/jars/" + file.getName(), file)).toArray(ZipEntrySource[]::new)); + fileList.addAll(populateProjectDependencies(configuration, visited)); + fileList.addAll(populateResolvedDependencies(configuration, visited)); - return ZipUtil.transformEntries(modJar, single(new ZipEntryTransformerEntry("fabric.mod.json", new StringZipEntryTransformer() { - @Override - protected String transform(ZipEntry zipEntry, String input) { - JsonObject json = LoomGradlePlugin.GSON.fromJson(input, JsonObject.class); - JsonArray nestedJars = json.getAsJsonArray("jars"); + return new NestedDependencyProvider(project, fileList); + } - if (nestedJars == null || !json.has("jars")) { - nestedJars = new JsonArray(); - } + // Looks for any deps that require a sub project to be built first + public static List<RemapJarTask> getRequiredTasks(Project project) { + List<RemapJarTask> remapTasks = new ArrayList<>(); - for (File file : containedJars) { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("file", "META-INF/jars/" + file.getName()); - nestedJars.add(jsonObject); - } + Configuration configuration = project.getConfigurations().getByName(Constants.Configurations.INCLUDE); + DependencySet dependencies = configuration.getDependencies(); - json.add("jars", nestedJars); + for (Dependency dependency : dependencies) { + if (dependency instanceof ProjectDependency) { + ProjectDependency projectDependency = (ProjectDependency) dependency; + Project dependencyProject = projectDependency.getDependencyProject(); - return LoomGradlePlugin.GSON.toJson(json); + for (Task task : dependencyProject.getTasksByName("remapJar", false)) { + if (task instanceof RemapJarTask) { + remapTasks.add((RemapJarTask) task); + } + } } - }))); - } - - private static List<File> getContainedJars(Project project) { - List<File> fileList = new ArrayList<>(); + } - Configuration configuration = project.getConfigurations().getByName(Constants.Configurations.INCLUDE); - ResolvedConfiguration resolvedConfiguration = configuration.getResolvedConfiguration(); - Set<ResolvedDependency> dependencies = resolvedConfiguration.getFirstLevelModuleDependencies(); + return remapTasks; + } - // Bit ugly doing this, id guess there is a better way but this works. - Set<String> projectDeps = new HashSet<>(); + private static List<DependencyInfo<ProjectDependency>> populateProjectDependencies(Configuration configuration, Set<String> visited) { + List<DependencyInfo<ProjectDependency>> fileList = new ArrayList<>(); for (Dependency dependency : configuration.getDependencies()) { if (dependency instanceof ProjectDependency) { ProjectDependency projectDependency = (ProjectDependency) dependency; Project dependencyProject = projectDependency.getDependencyProject(); - projectDeps.add(dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion()); + visited.add(dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion()); // TODO change this to allow just normal jar tasks, so a project can have a none loom sub project Collection<Task> remapJarTasks = dependencyProject.getTasksByName("remapJar", false); @@ -119,82 +111,53 @@ public class NestedJars { for (Task task : remapJarTasks.isEmpty() ? jarTasks : remapJarTasks) { if (task instanceof RemapJarTask) { - fileList.addAll(prepareForNesting( - Collections.singleton(((RemapJarTask) task).getArchivePath()), - projectDependency, - new ProjectDependencyMetaExtractor(), - project - )); + File file = ((RemapJarTask) task).getArchivePath(); + fileList.add(new DependencyInfo<>(projectDependency, new ProjectDependencyMetaExtractor(), file)); } else if (task instanceof AbstractArchiveTask) { - fileList.addAll(prepareForNesting( - Collections.singleton(((AbstractArchiveTask) task).getArchivePath()), - projectDependency, - new ProjectDependencyMetaExtractor(), - project - )); + File file = ((AbstractArchiveTask) task).getArchivePath(); + fileList.add(new DependencyInfo<>(projectDependency, new ProjectDependencyMetaExtractor(), file)); } } } } + return fileList; + } + + private static List<DependencyInfo<ResolvedDependency>> populateResolvedDependencies(Configuration configuration, Set<String> visited) { + ResolvedConfiguration resolvedConfiguration = configuration.getResolvedConfiguration(); + Set<ResolvedDependency> dependencies = resolvedConfiguration.getFirstLevelModuleDependencies(); + + List<DependencyInfo<ResolvedDependency>> fileList = new ArrayList<>(); + for (ResolvedDependency dependency : dependencies) { - if (projectDeps.contains(dependency.getModuleGroup() + ":" + dependency.getModuleName() + ":" + dependency.getModuleVersion())) { + if (visited.contains(dependency.getModuleGroup() + ":" + dependency.getModuleName() + ":" + dependency.getModuleVersion())) { continue; - } else { - fileList.addAll(prepareForNesting( - dependency - .getModuleArtifacts() - .stream() - .map(ResolvedArtifact::getFile) - .collect(Collectors.toSet()), - dependency, - new ResolvedDependencyMetaExtractor(), - project - )); } - } - for (File file : fileList) { - if (!file.exists()) { - throw new RuntimeException("Failed to include nested jars, as it could not be found @ " + file.getAbsolutePath()); - } + List<File> files = dependency + .getModuleArtifacts() + .stream() + .map(ResolvedArtifact::getFile) + .collect(Collectors.toList()); - if (file.isDirectory() || !file.getName().endsWith(".jar")) { - throw new RuntimeException("Failed to include nested jars, as file was not a jar: " + file.getAbsolutePath()); + for (File file : files) { + fileList.add(new DependencyInfo<>(dependency, new ResolvedDependencyMetaExtractor(), file)); } } return fileList; } - // Looks for any deps that require a sub project to be built first - public static List<RemapJarTask> getRequiredTasks(Project project) { - List<RemapJarTask> remapTasks = new ArrayList<>(); - - Configuration configuration = project.getConfigurations().getByName(Constants.Configurations.INCLUDE); - DependencySet dependencies = configuration.getDependencies(); - - for (Dependency dependency : dependencies) { - if (dependency instanceof ProjectDependency) { - ProjectDependency projectDependency = (ProjectDependency) dependency; - Project dependencyProject = projectDependency.getDependencyProject(); - - for (Task task : dependencyProject.getTasksByName("remapJar", false)) { - if (task instanceof RemapJarTask) { - remapTasks.add((RemapJarTask) task); - } - } - } - } + @Override + public List<File> provide() { + List<File> fileList = new ArrayList<>(); - return remapTasks; - } + for (DependencyInfo<?> metaFile : files) { + metaFile.validateInputs(); - //This is a good place to do pre-nesting operations, such as adding a fabric.mod.json to a library - private static <D> List<File> prepareForNesting(Set<File> files, D dependency, DependencyMetaExtractor<D> metaExtractor, Project project) { - List<File> fileList = new ArrayList<>(); + File file = metaFile.file; - for (File file : files) { //A lib that doesnt have a mod.json, we turn it into a fake mod if (!ZipUtil.containsEntry(file, "fabric.mod.json")) { LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); @@ -216,7 +179,7 @@ public class NestedJars { throw new RuntimeException("Failed to copy file", e); } - ZipUtil.addEntry(tempFile, "fabric.mod.json", getMod(dependency, metaExtractor).getBytes()); + ZipUtil.addEntry(tempFile, "fabric.mod.json", generateModForDependency(metaFile).getBytes()); fileList.add(tempFile); } else { // Default copy the jar right in @@ -228,7 +191,10 @@ public class NestedJars { } // Generates a barebones mod for a dependency - private static <D> String getMod(D dependency, DependencyMetaExtractor<D> metaExtractor) { + private static <D> String generateModForDependency(DependencyInfo<D> info) { + DependencyMetaExtractor<D> metaExtractor = info.metaExtractor; + D dependency = info.dependency; + JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("schemaVersion", 1); jsonObject.addProperty("id", (metaExtractor.group(dependency) + "_" + metaExtractor.name(dependency)).replaceAll("\\.", "_").toLowerCase(Locale.ENGLISH)); @@ -242,8 +208,26 @@ public class NestedJars { return LoomGradlePlugin.GSON.toJson(jsonObject); } - private static ZipEntryTransformerEntry[] single(ZipEntryTransformerEntry element) { - return new ZipEntryTransformerEntry[]{element}; + private static class DependencyInfo<D> { + final D dependency; + final DependencyMetaExtractor<D> metaExtractor; + final File file; + + DependencyInfo(D dependency, DependencyMetaExtractor<D> metaExtractor, File file) { + this.dependency = dependency; + this.metaExtractor = metaExtractor; + this.file = file; + } + + public void validateInputs() { + if (!file.exists()) { + throw new RuntimeException("Failed to include nested jars, as it could not be found @ " + file.getAbsolutePath()); + } + + if (file.isDirectory() || !file.getName().endsWith(".jar")) { + throw new RuntimeException("Failed to include nested jars, as file was not a jar: " + file.getAbsolutePath()); + } + } } private interface DependencyMetaExtractor<D> { diff --git a/src/main/java/net/fabricmc/loom/build/nesting/NestedJarPathProvider.java b/src/main/java/net/fabricmc/loom/build/nesting/NestedJarPathProvider.java new file mode 100644 index 00000000..badb2656 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/build/nesting/NestedJarPathProvider.java @@ -0,0 +1,67 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 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.build.nesting; + +import java.io.File; +import java.util.Collection; +import java.util.Set; + +import com.google.common.base.Preconditions; +import org.gradle.api.Project; + +import net.fabricmc.loom.util.ModUtils; + +public final class NestedJarPathProvider implements NestedJarProvider { + private final Set<Object> nestedPaths; + private Set<File> files = null; + public NestedJarPathProvider(Set<Object> nestedPaths) { + this.nestedPaths = nestedPaths; + } + + private Set<File> resolve(Project project) { + return project.files(nestedPaths).getFiles(); + } + + @Override + public void prepare(Project project) { + if (files == null) { + files = resolve(project); + } + } + + @Override + public Collection<File> provide() { + validateFiles(); + return files; + } + + private void validateFiles() { + for (File file : files) { + Preconditions.checkArgument(file.getName().endsWith(".jar"), String.format("Tried to nest %s but it is not a jar", file.getAbsolutePath())); + Preconditions.checkArgument(file.exists(), String.format("Tried to nest jar %s but it does not exist", file.getAbsolutePath())); + Preconditions.checkArgument(ModUtils.isMod(file), String.format("Cannot use nest none mod jar %s", file.getAbsolutePath())); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/build/nesting/NestedJarProvider.java b/src/main/java/net/fabricmc/loom/build/nesting/NestedJarProvider.java new file mode 100644 index 00000000..3ddbae31 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/build/nesting/NestedJarProvider.java @@ -0,0 +1,40 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 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.build.nesting; + +import java.io.File; +import java.util.Collection; + +import org.gradle.api.Project; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public interface NestedJarProvider { + // provide all the files to be included, they should already be resolved but can be transformed here + Collection<File> provide(); + + // Setup the files ready to be provided + default void prepare(Project project) { } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index 01310e6d..23de41b2 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -24,17 +24,8 @@ package net.fabricmc.loom.configuration; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import com.google.common.collect.ImmutableMap; -import org.gradle.api.Action; import org.gradle.api.Project; -import org.gradle.api.Task; -import org.gradle.api.UnknownTaskException; import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.repositories.MavenArtifactRepository; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.SourceSet; @@ -43,8 +34,6 @@ import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.javadoc.Javadoc; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.build.JarRemapper; -import net.fabricmc.loom.build.NestedJars; import net.fabricmc.loom.build.mixin.JavaApInvoker; import net.fabricmc.loom.build.mixin.KaptApInvoker; import net.fabricmc.loom.build.mixin.ScalaApInvoker; @@ -58,17 +47,8 @@ import net.fabricmc.loom.configuration.providers.forge.McpConfigProvider; import net.fabricmc.loom.configuration.providers.forge.PatchProvider; import net.fabricmc.loom.configuration.providers.forge.SrgProvider; import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; -import net.fabricmc.loom.task.AbstractLoomTask; -import net.fabricmc.loom.task.GenVsCodeProjectTask; -import net.fabricmc.loom.task.RemapAllSourcesTask; -import net.fabricmc.loom.task.RemapJarTask; -import net.fabricmc.loom.task.RemapSourcesJarTask; import net.fabricmc.loom.util.Constants; -import net.fabricmc.loom.util.SourceRemapper; -/** - * Add Minecraft dependencies to compile time. - */ public final class CompileConfiguration { private CompileConfiguration() { } @@ -121,9 +101,13 @@ public final class CompileConfiguration { Configuration includeConfig = project.getConfigurations().maybeCreate(Constants.Configurations.INCLUDE); includeConfig.setTransitive(false); // Dont get transitive deps + project.getConfigurations().maybeCreate(Constants.Configurations.MAPPING_CONSTANTS); + extendsFrom(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME, Constants.Configurations.MAPPING_CONSTANTS, project); + project.getConfigurations().maybeCreate(Constants.Configurations.MAPPINGS); project.getConfigurations().maybeCreate(Constants.Configurations.MAPPINGS_FINAL); project.getConfigurations().maybeCreate(Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES); + project.getConfigurations().maybeCreate(Constants.Configurations.UNPICK_CLASSPATH); for (RemappedConfigurationEntry entry : Constants.MOD_COMPILE_ENTRIES) { Configuration compileModsConfig = project.getConfigurations().maybeCreate(entry.getSourceConfiguration()); @@ -152,75 +136,18 @@ public final class CompileConfiguration { extendsFrom(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES, project); } - /** - * Permit to add a Maven repository to a target project. - * - * @param target The target project - * @param name The name of the repository - * @param url The URL of the repository - * @return An object containing the name and the URL of the repository that can be modified later - */ - public static MavenArtifactRepository addMavenRepo(Project target, final String name, final String url) { - return addMavenRepo(target, name, url, repo -> { - }); - } - - public static MavenArtifactRepository addMavenRepo(Project target, final String name, final String url, final Action<MavenArtifactRepository> action) { - return target.getRepositories().maven(repo -> { - repo.setName(name); - repo.setUrl(url); - action.execute(repo); - }); - } - - public static void configureCompile(Project project) { - JavaPluginConvention javaModule = (JavaPluginConvention) project.getConvention().getPlugins().get("java"); + public static void configureCompile(Project p) { + JavaPluginConvention javaModule = (JavaPluginConvention) p.getConvention().getPlugins().get("java"); SourceSet main = javaModule.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME); - Javadoc javadoc = (Javadoc) project.getTasks().getByName(JavaPlugin.JAVADOC_TASK_NAME); + Javadoc javadoc = (Javadoc) p.getTasks().getByName(JavaPlugin.JAVADOC_TASK_NAME); javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath())); - project.afterEvaluate(project1 -> { - LoomGradleExtension extension = project1.getExtensions().getByType(LoomGradleExtension.class); - - project1.getRepositories().flatDir(flatDirectoryArtifactRepository -> { - flatDirectoryArtifactRepository.dir(extension.getRootProjectBuildCache()); - flatDirectoryArtifactRepository.setName("UserLocalCacheFiles"); - }); - - project1.getRepositories().maven(mavenArtifactRepository -> { - mavenArtifactRepository.setUrl(extension.getRemappedModCache()); - mavenArtifactRepository.setName("UserLocalRemappedMods"); - }); - - project1.getRepositories().maven(mavenArtifactRepository -> { - mavenArtifactRepository.setName("Fabric"); - mavenArtifactRepository.setUrl("https://maven.fabricmc.net/"); - }); - - project1.getRepositories().maven(mavenArtifactRepository -> { - mavenArtifactRepository.setName("Mojang"); - mavenArtifactRepository.setUrl("https://libraries.minecraft.net/"); - }); - - project1.getRepositories().maven(mavenArtifactRepository -> { - mavenArtifactRepository.setName("Forge"); - mavenArtifactRepository.setUrl("https://files.minecraftforge.net/maven/"); - - mavenArtifactRepository.metadataSources(sources -> { - sources.mavenPom(); - - try { - MavenArtifactRepository.MetadataSources.class.getDeclaredMethod("ignoreGradleMetadataRedirection") - .invoke(sources); - } catch (Throwable ignored) { - // Method not available - } - }); - }); + p.afterEvaluate(project -> { + LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); - project1.getRepositories().mavenCentral(); + MavenConfiguration.setup(project); LoomDependencyManager dependencyManager = new LoomDependencyManager(); extension.setDependencyManager(dependencyManager); @@ -245,118 +172,20 @@ public final class CompileConfiguration { dependencyManager.addProvider(new MappingsProvider(project)); dependencyManager.addProvider(new LaunchProvider(project)); - dependencyManager.handleDependencies(project1); + dependencyManager.handleDependencies(project); - project1.getTasks().getByName("idea").finalizedBy(project1.getTasks().getByName("genIdeaWorkspace")); - project1.getTasks().getByName("eclipse").finalizedBy(project1.getTasks().getByName("genEclipseRuns")); - project1.getTasks().getByName("cleanEclipse").finalizedBy(project1.getTasks().getByName("cleanEclipseRuns")); + project.getTasks().getByName("idea").finalizedBy(project.getTasks().getByName("genIdeaWorkspace")); + project.getTasks().getByName("eclipse").finalizedBy(project.getTasks().getByName("genEclipseRuns")); + project.getTasks().getByName("cleanEclipse").finalizedBy(project.getTasks().getByName("cleanEclipseRuns")); - SetupIntelijRunConfigs.setup(project1); + SetupIntelijRunConfigs.setup(project); GenVsCodeProjectTask.generate(project1); // Enables the default mod remapper if (extension.remapMod) { - AbstractArchiveTask jarTask = (AbstractArchiveTask) project1.getTasks().getByName("jar"); - RemapJarTask remapJarTask = (RemapJarTask) project1.getTasks().findByName("remapJar"); - - assert remapJarTask != null; - - if (!remapJarTask.getInput().isPresent()) { - jarTask.setClassifier("dev"); - remapJarTask.setClassifier(""); - remapJarTask.getInput().set(jarTask.getArchivePath()); - } - - if (extension.isForge()) { - remapJarTask.getToM().set("srg"); - ((Jar) jarTask).manifest(manifest -> { - List<String> configs = new ArrayList<>(); - - if (extension.mixinConfig != null) { - configs.add(extension.mixinConfig); - } - - if (extension.mixinConfigs != null) { - configs.addAll(extension.mixinConfigs); - } - - manifest.attributes(ImmutableMap.of("MixinConfigs", String.join(",", configs))); - }); - } - - extension.getUnmappedModCollection().from(jarTask); - remapJarTask.getAddNestedDependencies().set(true); - remapJarTask.getRemapAccessWidener().set(true); - - project1.getArtifacts().add("archives", remapJarTask); - remapJarTask.dependsOn(jarTask); - project1.getTasks().getByName("build").dependsOn(remapJarTask); - - project.getTasks().withType(RemapJarTask.class).forEach(task -> { - if (task.getAddNestedDependencies().getOrElse(false)) { - NestedJars.getRequiredTasks(project1).forEach(task::dependsOn); - } - }); - - SourceRemapper remapper = null; - Task parentTask = project1.getTasks().getByName("build"); - - if (extension.isShareCaches()) { - Project rootProject = project.getRootProject(); - - if (extension.isRootProject()) { - SourceRemapper sourceRemapper = new SourceRemapper(rootProject, false); - JarRemapper jarRemapper = new JarRemapper(); - - remapJarTask.jarRemapper = jarRemapper; - - rootProject.getTasks().register("remapAllSources", RemapAllSourcesTask.class, task -> { - task.sourceRemapper = sourceRemapper; - task.doLast(t -> sourceRemapper.remapAll()); - }); - - parentTask = rootProject.getTasks().getByName("remapAllSources"); - - rootProject.getTasks().register("remapAllJars", AbstractLoomTask.class, task -> { - task.doLast(t -> { - try { - jarRemapper.remap(rootProject); - } catch (IOException e) { - throw new RuntimeException("Failed to remap jars", e); - } - }); - }); - } else { - parentTask = rootProject.getTasks().getByName("remapAllSources"); - remapper = ((RemapAllSourcesTask) parentTask).sourceRemapper; - - remapJarTask.jarRemapper = ((RemapJarTask) rootProject.getTasks().getByName("remapJar")).jarRemapper; - - project1.getTasks().getByName("build").dependsOn(parentTask); - project1.getTasks().getByName("build").dependsOn(rootProject.getTasks().getByName("remapAllJars")); - rootProject.getTasks().getByName("remapAllJars").dependsOn(project1.getTasks().getByName("remapJar")); - } - } - - try { - AbstractArchiveTask sourcesTask = (AbstractArchiveTask) project1.getTasks().getByName("sourcesJar"); - - RemapSourcesJarTask remapSourcesJarTask = (RemapSourcesJarTask) project1.getTasks().findByName("remapSourcesJar"); - remapSourcesJarTask.setInput(sourcesTask.getArchivePath()); - remapSourcesJarTask.setOutput(sourcesTask.getArchivePath()); - remapSourcesJarTask.doLast(task -> project1.getArtifacts().add("archives", remapSourcesJarTask.getOutput())); - remapSourcesJarTask.dependsOn(project1.getTasks().getByName("sourcesJar")); - - if (extension.isShareCaches()) { - remapSourcesJarTask.setSourceRemapper(remapper); - } - - parentTask.dependsOn(remapSourcesJarTask); - } catch (UnknownTaskException ignored) { - // pass - } + RemapConfiguration.setupDefaultRemap(project); } else { - AbstractArchiveTask jarTask = (AbstractArchiveTask) project1.getTasks().getByName("jar"); + AbstractArchiveTask jarTask = (AbstractArchiveTask) project.getTasks().getByName("jar"); extension.getUnmappedModCollection().from(jarTask); } @@ -379,7 +208,7 @@ public final class CompileConfiguration { } }); - if (project.getPluginManager().hasPlugin("org.jetbrains.kotlin.kapt")) { + if (p.getPluginManager().hasPlugin("org.jetbrains.kotlin.kapt")) { // If loom is applied after kapt, then kapt will use the AP arguments too early for loom to pass the arguments we need for mixin. throw new IllegalArgumentException("fabric-loom must be applied BEFORE kapt in the plugins { } block."); } diff --git a/src/main/java/net/fabricmc/loom/configuration/MavenConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/MavenConfiguration.java new file mode 100644 index 00000000..2fb2ae7c --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/MavenConfiguration.java @@ -0,0 +1,57 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 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 org.gradle.api.Project; + +import net.fabricmc.loom.LoomGradleExtension; + +public class MavenConfiguration { + public static void setup(Project project) { + LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); + + project.getRepositories().flatDir(repo -> { + repo.setName("UserLocalCacheFiles"); + repo.dir(extension.getRootProjectBuildCache()); + }); + + project.getRepositories().maven(repo -> { + repo.setName("UserLocalRemappedMods"); + repo.setUrl(extension.getRemappedModCache()); + }); + + project.getRepositories().maven(repo -> { + repo.setName("Fabric"); + repo.setUrl("https://maven.fabricmc.net/"); + }); + + project.getRepositories().maven(repo -> { + repo.setName("Mojang"); + repo.setUrl("https://libraries.minecraft.net/"); + }); + + project.getRepositories().mavenCentral(); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java new file mode 100644 index 00000000..7534c27b --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java @@ -0,0 +1,159 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 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.IOException; + +import com.google.common.base.Preconditions; +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.UnknownTaskException; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; +import org.jetbrains.annotations.ApiStatus; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.build.JarRemapper; +import net.fabricmc.loom.build.nesting.NestedDependencyProvider; +import net.fabricmc.loom.task.AbstractLoomTask; +import net.fabricmc.loom.task.RemapAllSourcesTask; +import net.fabricmc.loom.task.RemapJarTask; +import net.fabricmc.loom.task.RemapSourcesJarTask; +import net.fabricmc.loom.util.SourceRemapper; + +public class RemapConfiguration { + private static final String DEFAULT_JAR_TASK_NAME = JavaPlugin.JAR_TASK_NAME; + private static final String DEFAULT_SOURCES_JAR_TASK_NAME = "sourcesJar"; + private static final String DEFAULT_REMAP_JAR_TASK_NAME = "remapJar"; + private static final String DEFAULT_REMAP_SOURCES_JAR_TASK_NAME = "remapSourcesJar"; + private static final String DEFAULT_REMAP_ALL_JARS_TASK_NAME = "remapAllJars"; + private static final String DEFAULT_REMAP_ALL_SOURCES_TASK_NAME = "remapAllSources"; + + public static void setupDefaultRemap(Project project) { + setupRemap(project, true, DEFAULT_JAR_TASK_NAME, DEFAULT_SOURCES_JAR_TASK_NAME, DEFAULT_REMAP_JAR_TASK_NAME, DEFAULT_REMAP_SOURCES_JAR_TASK_NAME, DEFAULT_REMAP_ALL_JARS_TASK_NAME, DEFAULT_REMAP_ALL_SOURCES_TASK_NAME); + } + + @ApiStatus.Experimental // This is only an api if you squint really hard, expect it to explode every 5 mins. If you must call in afterEvaluate on all projects + public static void setupRemap(Project project, String jarTaskName, String sourcesJarTaskName, String remapJarTaskName, String remapSourcesJarTaskName, String remapAllJarsTaskName, String remapAllSourcesTaskName) { + setupRemap(project, false, jarTaskName, sourcesJarTaskName, remapJarTaskName, remapSourcesJarTaskName, remapAllJarsTaskName, remapAllSourcesTaskName); + } + + // isDefaultRemap is set to true for the standard remap task, some defaults are left out when this is false. + private static void setupRemap(Project project, boolean isDefaultRemap, String jarTaskName, String sourcesJarTaskName, String remapJarTaskName, String remapSourcesJarTaskName, String remapAllJarsTaskName, String remapAllSourcesTaskName) { + LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); + AbstractArchiveTask jarTask = (AbstractArchiveTask) project.getTasks().getByName(jarTaskName); + RemapJarTask remapJarTask = (RemapJarTask) project.getTasks().findByName(remapJarTaskName); + + assert remapJarTask != null; + + if (!remapJarTask.getInput().isPresent() && isDefaultRemap) { + jarTask.setClassifier("dev"); + remapJarTask.setClassifier(""); + remapJarTask.getInput().set(jarTask.getArchivePath()); + } + + if (isDefaultRemap) { + extension.getUnmappedModCollection().from(jarTask); + remapJarTask.getAddNestedDependencies().set(true); + remapJarTask.getRemapAccessWidener().set(true); + + project.getArtifacts().add("archives", remapJarTask); + } + + remapJarTask.dependsOn(jarTask); + project.getTasks().getByName("build").dependsOn(remapJarTask); + + // TODO this might be wrong? + project.getTasks().withType(RemapJarTask.class).forEach(task -> { + if (task.getAddNestedDependencies().getOrElse(false)) { + NestedDependencyProvider.getRequiredTasks(project).forEach(task::dependsOn); + } + }); + + SourceRemapper remapper = null; + // TODO what is this for? + Task parentTask = project.getTasks().getByName("build"); + + if (extension.isShareCaches()) { + Project rootProject = project.getRootProject(); + + if (extension.isRootProject()) { + SourceRemapper sourceRemapper = new SourceRemapper(rootProject, false); + JarRemapper jarRemapper = new JarRemapper(); + + remapJarTask.jarRemapper = jarRemapper; + + rootProject.getTasks().register(remapAllSourcesTaskName, RemapAllSourcesTask.class, task -> { + task.sourceRemapper = sourceRemapper; + task.doLast(t -> sourceRemapper.remapAll()); + }); + + parentTask = rootProject.getTasks().getByName(remapAllSourcesTaskName); + + rootProject.getTasks().register(remapAllJarsTaskName, AbstractLoomTask.class, task -> { + task.doLast(t -> { + try { + jarRemapper.remap(); + } catch (IOException e) { + throw new RuntimeException("Failed to remap jars", e); + } + }); + }); + } else { + parentTask = rootProject.getTasks().getByName(remapAllSourcesTaskName); + remapper = ((RemapAllSourcesTask) parentTask).sourceRemapper; + Preconditions.checkNotNull(remapper); + + remapJarTask.jarRemapper = ((RemapJarTask) rootProject.getTasks().getByName(remapJarTaskName)).jarRemapper; + + project.getTasks().getByName("build").dependsOn(parentTask); + project.getTasks().getByName("build").dependsOn(rootProject.getTasks().getByName(remapAllJarsTaskName)); + rootProject.getTasks().getByName(remapAllJarsTaskName).dependsOn(project.getTasks().getByName(remapJarTaskName)); + } + } + + try { + AbstractArchiveTask sourcesTask = (AbstractArchiveTask) project.getTasks().getByName(sourcesJarTaskName); + + RemapSourcesJarTask remapSourcesJarTask = (RemapSourcesJarTask) project.getTasks().findByName(remapSourcesJarTaskName); + Preconditions.checkNotNull(remapSourcesJarTask, "Could not find " + remapSourcesJarTaskName + " in " + project.getName()); + remapSourcesJarTask.setInput(sourcesTask.getArchivePath()); + remapSourcesJarTask.setOutput(sourcesTask.getArchivePath()); + remapSourcesJarTask.dependsOn(project.getTasks().getByName(sourcesJarTaskName)); + + if (isDefaultRemap) { + remapSourcesJarTask.doLast(task -> project.getArtifacts().add("archives", remapSourcesJarTask.getOutput())); + } + + if (extension.isShareCaches()) { + remapSourcesJarTask.setSourceRemapper(remapper); + } + + parentTask.dependsOn(remapSourcesJarTask); + } catch (UnknownTaskException ignored) { + // pass + } + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java index 8bbca4fa..603de2db 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java @@ -69,6 +69,7 @@ public class RunConfig { public String programArgs; public List<String> vscodeBeforeRun = new ArrayList<>(); public final Map<String, String> envVariables = new HashMap<>(); + public SourceSet sourceSet; public Element genRuns(Element doc) { Element root = this.addXml(doc, "component", ImmutableMap.of("name", "ProjectRunConfigurationManager")); @@ -235,6 +236,7 @@ public class RunConfig { runConfig.ideaModuleName = getIdeaModuleName(project, sourceSet); runConfig.runDirIdeaUrl = "file://$PROJECT_DIR$/" + runDir; runConfig.runDir = runDir; + runConfig.sourceSet = sourceSet; // Custom parameters for (String progArg : settings.getProgramArgs()) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java index b2cb1f49..226f3f17 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java @@ -110,7 +110,8 @@ public class MinecraftProvider extends DependencyProvider { try { mergeJars(getProject().getLogger()); } catch (ZipError e) { - deleteFiles(); + HashedDownloadUtil.delete(minecraftClientJar); + HashedDownloadUtil.delete(minecraftServerJar); getProject().getLogger().error("Could not merge JARs! Deleting source JARs - please re-run the command and move on.", e); throw new RuntimeException(); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProvider.java index df5aa108..40ae05d0 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProvider.java @@ -28,6 +28,7 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; @@ -41,6 +42,7 @@ import java.util.function.Consumer; import com.google.common.base.Preconditions; import com.google.common.net.UrlEscapers; +import com.google.gson.JsonObject; import org.apache.commons.io.FileUtils; import org.apache.tools.ant.util.StringUtils; import org.gradle.api.Project; @@ -50,6 +52,7 @@ import org.zeroturnaround.zip.ZipEntrySource; import org.zeroturnaround.zip.ZipUtil; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.configuration.DependencyProvider; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor; import net.fabricmc.loom.configuration.processors.JarProcessorManager; @@ -95,6 +98,10 @@ public class MappingsProvider extends DependencyProvider { public File mixinTinyMappingsWithSrg; // FORGE: The mixin mappings have srg names in intermediary. public File srgToNamedSrg; // FORGE: srg to named in srg file format + private File unpickDefinitionsFile; + private boolean hasUnpickDefinitions; + private UnpickMetadata unpickMetadata; + public MappingsProvider(Project project) { super(project); mappingsDir = getExtension().getUserCache().toPath().resolve("mappings"); @@ -173,6 +180,7 @@ public class MappingsProvider extends DependencyProvider { } tinyMappings = mappingsDir.resolve(StringUtils.removeSuffix(mappingsJar.getName(), ".jar") + ".tiny").toFile(); + unpickDefinitionsFile = mappingsDir.resolve(StringUtils.removeSuffix(mappingsJar.getName(), ".jar") + ".unpick").toFile(); tinyMappingsJar = new File(getExtension().getUserCache(), mappingsJar.getName().replace(".jar", "-" + jarClassifier + ".jar")); tinyMappingsWithSrg = mappingsDir.resolve(StringUtils.removeSuffix(mappingsJar.getName(), ".jar") + "-srg.tiny"); mixinTinyMappingsWithSrg = mappingsDir.resolve(StringUtils.removeSuffix(mappingsJar.getName(), ".jar") + "-mixin-srg.tiny").toFile(); @@ -180,12 +188,27 @@ public class MappingsProvider extends DependencyProvider { if (!tinyMappings.exists() || isRefreshDeps()) { storeMappings(getProject(), minecraftProvider, mappingsJar.toPath(), postPopulationScheduler); + } else { + try (FileSystem fileSystem = FileSystems.newFileSystem(mappingsJar.toPath(), (ClassLoader) null)) { + extractUnpickDefinitions(fileSystem, unpickDefinitionsFile.toPath()); + } } if (!tinyMappingsJar.exists() || isRefreshDeps()) { ZipUtil.pack(new ZipEntrySource[] {new FileSource("mappings/mappings.tiny", tinyMappings)}, tinyMappingsJar); } + if (hasUnpickDefinitions()) { + String notation = String.format("%s:%s:%s:constants", + dependency.getDependency().getGroup(), + dependency.getDependency().getName(), + dependency.getDependency().getVersion() + ); + + getProject().getDependencies().add(Constants.Configurations.MAPPING_CONSTANTS, notation); + populateUnpickClasspath(); + } + if (getExtension().shouldGenerateSrgTiny()) { if (Files.notExists(tinyMappingsWithSrg) || isRefreshDeps()) { SrgMerger.mergeSrg(getExtension().getSrgProvider().getSrg().toPath(), tinyMappings.toPath(), tinyMappingsWithSrg, true); @@ -248,6 +271,7 @@ public class MappingsProvider extends DependencyProvider { try (FileSystem fileSystem = FileSystems.newFileSystem(yarnJar, (ClassLoader) null)) { extractMappings(fileSystem, baseTinyMappings); + extractUnpickDefinitions(fileSystem, unpickDefinitionsFile.toPath()); } if (baseMappingsAreV2()) { @@ -317,6 +341,40 @@ public class MappingsProvider extends DependencyProvider { Files.copy(jar.getPath("mappings/mappings.tiny"), extractTo, StandardCopyOption.REPLACE_EXISTING); } + private void extractUnpickDefinitions(FileSystem jar, Path extractTo) throws IOException { + Path unpickPath = jar.getPath("extras/definitions.unpick"); + Path unpickMetadataPath = jar.getPath("extras/unpick.json"); + + if (!Files.exists(unpickPath) || !Files.exists(unpickMetadataPath)) { + return; + } + + Files.copy(unpickPath, extractTo, StandardCopyOption.REPLACE_EXISTING); + + unpickMetadata = parseUnpickMetadata(unpickMetadataPath); + hasUnpickDefinitions = true; + } + + private UnpickMetadata parseUnpickMetadata(Path input) throws IOException { + JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(new String(Files.readAllBytes(input), StandardCharsets.UTF_8), JsonObject.class); + + if (!jsonObject.has("version") || jsonObject.get("version").getAsInt() != 1) { + throw new UnsupportedOperationException("Unsupported unpick version"); + } + + return new UnpickMetadata( + jsonObject.get("unpickGroup").getAsString(), + jsonObject.get("unpickVersion").getAsString() + ); + } + + private void populateUnpickClasspath() { + String unpickCliName = "unpick-cli"; + getProject().getDependencies().add(Constants.Configurations.UNPICK_CLASSPATH, + String.format("%s:%s:%s", unpickMetadata.unpickGroup, unpickCliName, unpickMetadata.unpickVersion) + ); + } + private void extractIntermediary(Path intermediaryJar, Path intermediaryTiny) throws IOException { getProject().getLogger().info(":extracting " + intermediaryJar.getFileName()); @@ -439,4 +497,22 @@ public class MappingsProvider extends DependencyProvider { public String getMappingsKey() { return mappingsName + "." + minecraftVersion.replace(' ', '_').replace('.', '_').replace('-', '_') + "." + mappingsVersion; } + + public File getUnpickDefinitionsFile() { + return unpickDefinitionsFile; + } + + public boolean hasUnpickDefinitions() { + return hasUnpickDefinitions; + } + + public static class UnpickMetadata { + public final String unpickGroup; + public final String unpickVersion; + + public UnpickMetadata(String unpickGroup, String unpickVersion) { + this.unpickGroup = unpickGroup; + this.unpickVersion = unpickVersion; + } + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MojangMappingsDependency.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MojangMappingsDependency.java index af9dcbdb..09010c87 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MojangMappingsDependency.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MojangMappingsDependency.java @@ -66,7 +66,7 @@ import org.zeroturnaround.zip.ZipUtil; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; -import net.fabricmc.loom.util.DownloadUtil; +import net.fabricmc.loom.util.HashedDownloadUtil; import net.fabricmc.lorenztiny.TinyMappingsReader; import net.fabricmc.mapping.tree.TinyMappingFactory; @@ -178,11 +178,11 @@ public class MojangMappingsDependency extends AbstractModuleDependency implement throw new RuntimeException("Failed to find official mojang mappings for " + getVersion()); } - String clientMappingsUrl = versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS).getUrl(); - String serverMappingsUrl = versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS).getUrl(); + MinecraftVersionMeta.Download clientMappingsDownload = versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS); + MinecraftVersionMeta.Download serverMappingsDownload = versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS); - DownloadUtil.downloadIfChanged(new URL(clientMappingsUrl), clientMappings.toFile(), project.getLogger()); - DownloadUtil.downloadIfChanged(new URL(serverMappingsUrl), serverMappings.toFile(), project.getLogger()); + HashedDownloadUtil.downloadIfInvalid(new URL(clientMappingsDownload.getUrl()), clientMappings.toFile(), clientMappingsDownload.getSha1(), project.getLogger(), false); + HashedDownloadUtil.downloadIfInvalid(new URL(serverMappingsDownload.getUrl()), serverMappings.toFile(), clientMappingsDownload.getSha1(), project.getLogger(), false); MappingSet mappings = MappingSet.create(); 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 index 3b2e9391..891054b6 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java @@ -149,6 +149,8 @@ public class MinecraftMappedProvider extends DependencyProvider { getProject().getLogger().lifecycle(":remapping minecraft (TinyRemapper, " + fromM + " -> " + toM + ")"); + Files.deleteIfExists(output); + try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(output).build()) { if (getExtension().isForge()) { outputConsumer.addNonClassFiles(input, NonClassCopyMode.FIX_META_INF, remapper); @@ -279,6 +281,10 @@ public class MinecraftMappedProvider extends DependencyProvider { return minecraftMappedJar; } + public File getUnpickedJar() { + return new File(getJarDirectory(getExtension().getUserCache(), "mapped"), "minecraft-" + getJarVersionString("unpicked") + ".jar"); + } + @Override public String getTargetConfig() { return Constants.Configurations.MINECRAFT_NAMED; diff --git a/src/main/java/net/fabricmc/loom/decompilers/fernflower/ForkingJavaExec.java b/src/main/java/net/fabricmc/loom/decompilers/fernflower/ForkingJavaExec.java index e4b61b02..90080d69 100644 --- a/src/main/java/net/fabricmc/loom/decompilers/fernflower/ForkingJavaExec.java +++ b/src/main/java/net/fabricmc/loom/decompilers/fernflower/ForkingJavaExec.java @@ -24,6 +24,9 @@ package net.fabricmc.loom.decompilers.fernflower; +import java.net.URL; +import java.net.URLClassLoader; + import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.artifacts.ConfigurationContainer; @@ -40,15 +43,29 @@ import org.gradle.process.JavaExecSpec; */ public class ForkingJavaExec { public static ExecResult javaexec(Project project, Action<? super JavaExecSpec> action) { - ConfigurationContainer configurations = project.getBuildscript().getConfigurations(); - DependencyHandler handler = project.getDependencies(); - FileCollection classpath = project.getBuildscript().getConfigurations().getByName("classpath") - .plus(project.getRootProject().getBuildscript().getConfigurations().getByName("classpath")) - .plus(configurations.detachedConfiguration(handler.localGroovy())); - return project.javaexec(spec -> { - spec.classpath(classpath); + spec.classpath(getClasspath(project)); action.execute(spec); }); } + + private static Object getClasspath(Project project) { + if (System.getProperty("fabric.loom.test") != null) { + return getTestClasspath(); + } + + return getRuntimeClasspath(project.getRootProject().getPlugins().hasPlugin("fabric-loom") ? project.getRootProject() : project); + } + + private static FileCollection getRuntimeClasspath(Project project) { + ConfigurationContainer configurations = project.getBuildscript().getConfigurations(); + DependencyHandler handler = project.getDependencies(); + return configurations.getByName("classpath") + .plus(project.getRootProject().getBuildscript().getConfigurations().getByName("classpath")) + .plus(configurations.detachedConfiguration(handler.localGroovy())); + } + + private static URL[] getTestClasspath() { + return ((URLClassLoader) ForkingJavaExec.class.getClassLoader()).getURLs(); + } } diff --git a/src/main/java/net/fabricmc/loom/decompilers/fernflower/TinyJavadocProvider.java b/src/main/java/net/fabricmc/loom/decompilers/fernflower/TinyJavadocProvider.java index 15cf3bf8..07db43f1 100644 --- a/src/main/java/net/fabricmc/loom/decompilers/fernflower/TinyJavadocProvider.java +++ b/src/main/java/net/fabricmc/loom/decompilers/fernflower/TinyJavadocProvider.java @@ -36,6 +36,8 @@ import java.util.Map; import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructField; import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.StructRecordComponent; +import org.objectweb.asm.Opcodes; import net.fabricmc.fernflower.api.IFabricJavadocProvider; import net.fabricmc.mapping.tree.ClassDef; @@ -73,11 +75,63 @@ public class TinyJavadocProvider implements IFabricJavadocProvider { @Override public String getClassDoc(StructClass structClass) { ClassDef classDef = classes.get(structClass.qualifiedName); - return classDef != null ? classDef.getComment() : null; + + if (classDef == null) { + return null; + } + + if (!isRecord(structClass)) { + return classDef.getComment(); + } + + /** + * Handle the record component docs here. + * + * Record components are mapped via the field name, thus take the docs from the fields and display them on then class. + */ + List<String> parts = new ArrayList<>(); + + if (classDef.getComment() != null) { + parts.add(classDef.getComment()); + } + + boolean addedParam = false; + + for (StructRecordComponent component : structClass.getRecordComponents()) { + // The component will always match the field name and descriptor + FieldDef fieldDef = fields.get(new EntryTriple(structClass.qualifiedName, component.getName(), component.getDescriptor())); + + if (fieldDef == null) { + continue; + } + + String comment = fieldDef.getComment(); + + if (comment != null) { + if (!addedParam && classDef.getComment() != null) { + //Add a blank line before components when the class has a comment + parts.add(""); + addedParam = true; + } + + parts.add(String.format("@param %s %s", fieldDef.getName(namespace), comment)); + } + } + + if (parts.isEmpty()) { + return null; + } + + return String.join("\n", parts); } @Override public String getFieldDoc(StructClass structClass, StructField structField) { + // None static fields in records are handled in the class javadoc. + if (isRecord(structClass) && !isStatic(structField)) { + return null; + } + FieldDef fieldDef = fields.get(new EntryTriple(structClass.qualifiedName, structField.getName(), structField.getDescriptor())); return fieldDef != null ? fieldDef.getComment() : null; } @@ -126,4 +180,12 @@ public class TinyJavadocProvider implements IFabricJavadocProvider { throw new RuntimeException("Failed to read mappings", e); } } + + public static boolean isRecord(StructClass structClass) { + return (structClass.getAccessFlags() & Opcodes.ACC_RECORD) != 0; + } + + public static boolean isStatic(StructField structField) { + return (structField.getAccessFlags() & Opcodes.ACC_STATIC) != 0; + } } diff --git a/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java b/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java index 61c27abb..34f2dac8 100644 --- a/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java +++ b/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java @@ -32,31 +32,23 @@ import java.util.List; import java.util.function.Function; import org.gradle.api.Project; -import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.tasks.JavaExec; -import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.ide.RunConfig; public abstract class AbstractRunTask extends JavaExec { - private final Function<Project, RunConfig> configProvider; - private RunConfig config; + private final RunConfig config; - public AbstractRunTask(Function<Project, RunConfig> config) { + public AbstractRunTask(Function<Project, RunConfig> configProvider) { super(); setGroup("fabric"); - this.configProvider = config; + this.config = configProvider.apply(getProject()); - setClasspath(getProject().getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME)); - classpath(this.getProject().getExtensions().getByType(LoomGradleExtension.class).getUnmappedModCollection()); + setClasspath(config.sourceSet.getRuntimeClasspath()); } @Override public void exec() { - if (config == null) { - config = configProvider.apply(getProject()); - } - List<String> argsSplit = new ArrayList<>(); String[] args = config.programArgs.split(" "); int partPos = -1; @@ -94,10 +86,6 @@ public abstract class AbstractRunTask extends JavaExec { @Override public void setWorkingDir(File dir) { - if (config == null) { - config = configProvider.apply(getProject()); - } - if (!dir.exists()) { dir.mkdirs(); } @@ -107,19 +95,11 @@ public abstract class AbstractRunTask extends JavaExec { @Override public String getMain() { - if (config == null) { - config = configProvider.apply(getProject()); - } - return config.mainClass; } @Override public List<String> getJvmArgs() { - if (config == null) { - config = configProvider.apply(getProject()); - } - List<String> superArgs = super.getJvmArgs(); List<String> args = new ArrayList<>(superArgs != null ? superArgs : Collections.emptyList()); args.addAll(Arrays.asList(config.vmArgs.split(" "))); diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index 7c5862e4..25d6fe47 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -35,6 +35,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; +import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.TaskAction; import net.fabricmc.loom.LoomGradleExtension; @@ -49,6 +50,8 @@ import net.fabricmc.stitch.util.StitchUtil; public class GenerateSourcesTask extends AbstractLoomTask { public final LoomDecompiler decompiler; + private File inputJar; + @Inject public GenerateSourcesTask(LoomDecompiler decompiler) { this.decompiler = decompiler; @@ -65,26 +68,18 @@ public class GenerateSourcesTask extends AbstractLoomTask { .stream().map(File::toPath).collect(Collectors.toSet()); DecompilationMetadata metadata = new DecompilationMetadata(threads, javaDocs, libraries); - Path compiledJar = getExtension().getMappingsProvider().mappedProvider.getMappedJar().toPath(); + Path runtimeJar = getExtension().getMappingsProvider().mappedProvider.getMappedJar().toPath(); Path sourcesDestination = getMappedJarFileWithSuffix("-sources.jar").toPath(); Path linemap = getMappedJarFileWithSuffix("-sources.lmap").toPath(); - decompiler.decompile(compiledJar, sourcesDestination, linemap, metadata); + decompiler.decompile(inputJar.toPath(), sourcesDestination, linemap, metadata); if (Files.exists(linemap)) { Path linemappedJarDestination = getMappedJarFileWithSuffix("-linemapped.jar").toPath(); - remapLineNumbers(compiledJar, linemap, linemappedJarDestination); - - // In order for IDEs to recognize the new line mappings, we need to overwrite the existing compiled jar - // with the linemapped one. In the name of not destroying the existing jar, we will copy it to somewhere else. - Path unlinemappedJar = getMappedJarFileWithSuffix("-unlinemapped.jar").toPath(); + // Line map the actually jar used to run the game, not the one used to decompile + remapLineNumbers(runtimeJar, linemap, linemappedJarDestination); - // The second time genSources is ran, we want to keep the existing unlinemapped jar. - if (!Files.exists(unlinemappedJar)) { - Files.copy(compiledJar, unlinemappedJar); - } - - Files.copy(linemappedJarDestination, compiledJar, StandardCopyOption.REPLACE_EXISTING); + Files.copy(linemappedJarDestination, runtimeJar, StandardCopyOption.REPLACE_EXISTING); Files.delete(linemappedJarDestination); } } @@ -117,4 +112,14 @@ public class GenerateSourcesTask extends AbstractLoomTask { return new File(path.substring(0, path.length() - 4) + suffix); } + + @InputFile + public File getInputJar() { + return inputJar; + } + + public GenerateSourcesTask setInputJar(File inputJar) { + this.inputJar = inputJar; + return this; + } } diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index 198f92e6..1b1de6a7 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.java @@ -24,6 +24,8 @@ package net.fabricmc.loom.task; +import java.io.File; + import com.google.common.base.Preconditions; import org.gradle.api.Project; import org.gradle.api.tasks.TaskContainer; @@ -31,6 +33,7 @@ import org.gradle.api.tasks.TaskContainer; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.decompilers.LoomDecompiler; import net.fabricmc.loom.configuration.ide.RunConfigSettings; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; import net.fabricmc.loom.decompilers.fernflower.FabricFernFlowerDecompiler; public final class LoomTasks { @@ -110,10 +113,30 @@ public final class LoomTasks { LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); project.afterEvaluate(p -> { + MappingsProvider mappingsProvider = extension.getMappingsProvider(); + File inputJar = mappingsProvider.mappedProvider.getMappedJar(); + + if (mappingsProvider.hasUnpickDefinitions()) { + File outputJar = mappingsProvider.mappedProvider.getUnpickedJar(); + + tasks.register("unpickJar", UnpickJarTask.class, unpickJarTask -> { + unpickJarTask.setUnpickDefinition(mappingsProvider.getUnpickDefinitionsFile()); + unpickJarTask.setInputJar(mappingsProvider.mappedProvider.getMappedJar()); + unpickJarTask.setOutputJar(outputJar); + }); + + inputJar = outputJar; + } + for (LoomDecompiler decompiler : extension.getDecompilers()) { String taskName = decompiler instanceof FabricFernFlowerDecompiler ? "genSources" : "genSourcesWith" + decompiler.name(); // decompiler will be passed to the constructor of GenerateSourcesTask - tasks.register(taskName, GenerateSourcesTask.class, decompiler); + GenerateSourcesTask generateSourcesTask = tasks.register(taskName, GenerateSourcesTask.class, decompiler).get(); + generateSourcesTask.setInputJar(inputJar); + + if (mappingsProvider.hasUnpickDefinitions()) { + generateSourcesTask.dependsOn(tasks.getByName("unpickJar")); + } } }); } diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index 25165098..7b3c923e 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -36,7 +36,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.AbstractMap; import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -53,6 +56,7 @@ import me.shedaniel.architectury.refmapremapper.remapper.Remapper; import me.shedaniel.architectury.refmapremapper.remapper.SimpleReferenceRemapper; import org.gradle.api.Action; import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.FileCollection; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.Property; @@ -60,19 +64,25 @@ import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.TaskAction; import org.gradle.jvm.tasks.Jar; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import org.zeroturnaround.zip.ZipUtil; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.build.JarRemapper; import net.fabricmc.loom.build.MixinRefmapHelper; -import net.fabricmc.loom.build.NestedJars; +import net.fabricmc.loom.build.nesting.NestedJarPathProvider; +import net.fabricmc.loom.build.nesting.JarNester; +import net.fabricmc.loom.build.nesting.MergedNestedJarProvider; +import net.fabricmc.loom.build.nesting.NestedDependencyProvider; +import net.fabricmc.loom.build.nesting.NestedJarProvider; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor; import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; +import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.LoggerFilter; import net.fabricmc.loom.util.TinyRemapperMappingsHelper; -import net.fabricmc.loom.util.ZipReprocessorUtil; import net.fabricmc.loom.util.gradle.GradleSupport; +import net.fabricmc.loom.util.ZipReprocessorUtil; import net.fabricmc.mapping.tree.ClassDef; import net.fabricmc.mapping.tree.FieldDef; import net.fabricmc.mapping.tree.MethodDef; @@ -86,17 +96,20 @@ import net.fabricmc.tinyremapper.TinyUtils; public class RemapJarTask extends Jar { private final RegularFileProperty input; private final Property<Boolean> addNestedDependencies; + private final Property<Boolean> addDefaultNestedDependencies; private final Property<Boolean> remapAccessWidener; private final List<Action<TinyRemapper.Builder>> remapOptions = new ArrayList<>(); private final Property<String> fromM; private final Property<String> toM; public JarRemapper jarRemapper; private FileCollection classpath; + private final Set<Object> nestedPaths = new LinkedHashSet<>(); public RemapJarTask() { super(); input = GradleSupport.getfileProperty(getProject()); addNestedDependencies = getProject().getObjects().property(Boolean.class); + addDefaultNestedDependencies = getProject().getObjects().property(Boolean.class); remapAccessWidener = getProject().getObjects().property(Boolean.class); fromM = getProject().getObjects().property(String.class); toM = getProject().getObjects().property(String.class); @@ -104,199 +117,26 @@ public class RemapJarTask extends Jar { toM.set("intermediary"); // false by default, I have no idea why I have to do it for this property and not the other one remapAccessWidener.set(false); + addDefaultNestedDependencies.set(true); } @TaskAction public void doTask() throws Throwable { - if (jarRemapper == null) { - doSingleRemap(); - } else { - scheduleRemap(); - } - } - - public void doSingleRemap() throws Throwable { - Project project = getProject(); - LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); - Path input = this.getInput().getAsFile().get().toPath(); - Path output = this.getArchivePath().toPath(); - - if (!Files.exists(input)) { - throw new FileNotFoundException(input.toString()); - } - - MappingsProvider mappingsProvider = extension.getMappingsProvider(); - - String fromM = this.fromM.get(); - String toM = this.toM.get(); - - Path[] classpath = getRemapClasspath(); - - LoggerFilter.replaceSystemOut(); - TinyRemapper.Builder remapperBuilder = TinyRemapper.newRemapper(); - remapperBuilder.logger(getProject().getLogger()::lifecycle); - remapperBuilder = remapperBuilder.withMappings(TinyRemapperMappingsHelper.create(extension.isForge() ? mappingsProvider.getMappingsWithSrg() : mappingsProvider.getMappings(), fromM, toM, false)); - - for (File mixinMapFile : extension.getAllMixinMappings()) { - if (mixinMapFile.exists()) { - IMappingProvider provider = TinyUtils.createTinyMappingProvider(mixinMapFile.toPath(), fromM, "intermediary"); - remapperBuilder = remapperBuilder.withMappings(extension.isForge() ? remapToSrg(extension, provider) : provider); - } - } - - // Apply any requested options to tiny remapper - for (Action<TinyRemapper.Builder> remapOption : this.remapOptions) { - remapOption.execute(remapperBuilder); - } - - project.getLogger().info(":remapping " + input.getFileName()); - - StringBuilder rc = new StringBuilder("Remap classpath: "); - - for (Path p : classpath) { - rc.append("\n - ").append(p.toString()); - } - - project.getLogger().debug(rc.toString()); - - TinyRemapper remapper = remapperBuilder.build(); - - try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(output).build()) { - outputConsumer.addNonClassFiles(input); - remapper.readClassPath(classpath); - remapper.readInputs(input); - remapper.apply(outputConsumer); - } catch (Exception e) { - remapper.finish(); - throw new RuntimeException("Failed to remap " + input + " to " + output, e); - } - - if (getRemapAccessWidener().getOrElse(false) && extension.accessWidener != null) { - extension.getJarProcessorManager().getByType(AccessWidenerJarProcessor.class).remapAccessWidener(output, remapper.getRemapper()); - } - - remapper.finish(); - - if (!Files.exists(output)) { - throw new RuntimeException("Failed to remap " + input + " to " + output + " - file missing!"); - } - - if (MixinRefmapHelper.addRefmapName(extension.getRefmapName(), output)) { - project.getLogger().debug("Transformed mixin reference maps in output JAR!"); - } + boolean singleRemap = false; - if (extension.isForge()) { - try (FileSystem fs = FileSystems.newFileSystem(URI.create("jar:" + output.toUri()), ImmutableMap.of("create", false))) { - Path refmapPath = fs.getPath(extension.getRefmapName()); - - if (Files.exists(refmapPath)) { - try (Reader refmapReader = Files.newBufferedReader(refmapPath, StandardCharsets.UTF_8)) { - Gson gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create(); - JsonObject refmapElement = gson.fromJson(refmapReader, JsonObject.class); - refmapElement = RefmapRemapper.remap(new Remapper() { - ReferenceRemapper remapper = createReferenceRemapper(extension); - - @Override - @Nullable - public MappingsRemapper remapMappings() { - return className -> remapper; - } - - @Override - @Nullable - public Map.Entry<String, @Nullable MappingsRemapper> remapMappingsData(String data) { - if (Objects.equals(data, "named:intermediary")) { - return new AbstractMap.SimpleEntry<>("searge", remapMappings()); - } - - return null; - } - }, refmapElement); - Files.delete(refmapPath); - Files.write(refmapPath, gson.toJson(refmapElement).getBytes(StandardCharsets.UTF_8)); - } - } - } + if (jarRemapper == null) { + singleRemap = true; + jarRemapper = new JarRemapper(); } - if (getAddNestedDependencies().getOrElse(false)) { - if (NestedJars.addNestedJars(project, output)) { - project.getLogger().debug("Added nested jar paths to mod json"); - } - } + scheduleRemap(singleRemap || getProject().getExtensions().getByType(LoomGradleExtension.class).isRootProject()); - if (isReproducibleFileOrder() || isPreserveFileTimestamps()) { - ZipReprocessorUtil.reprocessZip(output.toFile(), isReproducibleFileOrder(), isPreserveFileTimestamps()); + if (singleRemap) { + jarRemapper.remap(); } } - private ReferenceRemapper createReferenceRemapper(LoomGradleExtension extension) throws IOException { - TinyTree srg = extension.getMappingsProvider().getMappingsWithSrg(); - - return new SimpleReferenceRemapper(new SimpleReferenceRemapper.Remapper() { - @Override - @Nullable - public String mapClass(String value) { - return srg.getClasses().stream() - .filter(classDef -> Objects.equals(classDef.getName("intermediary"), value)) - .findFirst() - .map(classDef -> classDef.getName("srg")) - .orElse(null); - } - - @Override - @Nullable - public String mapMethod(@Nullable String className, String methodName, String methodDescriptor) { - if (className != null) { - Optional<ClassDef> classDef = srg.getClasses().stream() - .filter(c -> Objects.equals(c.getName("intermediary"), className)) - .findFirst(); - - if (classDef.isPresent()) { - for (MethodDef methodDef : classDef.get().getMethods()) { - if (Objects.equals(methodDef.getName("intermediary"), methodName) && Objects.equals(methodDef.getDescriptor("intermediary"), methodDescriptor)) { - return methodDef.getName("srg"); - } - } - } - } - - return srg.getClasses().stream() - .flatMap(classDef -> classDef.getMethods().stream()) - .filter(methodDef -> Objects.equals(methodDef.getName("intermediary"), methodName) && Objects.equals(methodDef.getDescriptor("intermediary"), methodDescriptor)) - .findFirst() - .map(methodDef -> methodDef.getName("srg")) - .orElse(null); - } - - @Override - @Nullable - public String mapField(@Nullable String className, String fieldName, String fieldDescriptor) { - if (className != null) { - Optional<ClassDef> classDef = srg.getClasses().stream() - .filter(c -> Objects.equals(c.getName("intermediary"), className)) - .findFirst(); - - if (classDef.isPresent()) { - for (FieldDef fieldDef : classDef.get().getFields()) { - if (Objects.equals(fieldDef.getName("intermediary"), fieldName) && Objects.equals(fieldDef.getDescriptor("intermediary"), fieldDescriptor)) { - return fieldDef.getName("srg"); - } - } - } - } - - return srg.getClasses().stream() - .flatMap(classDef -> classDef.getFields().stream()) - .filter(fieldDef -> Objects.equals(fieldDef.getName("intermediary"), fieldName) && Objects.equals(fieldDef.getDescriptor("intermediary"), fieldDescriptor)) - .findFirst() - .map(fieldDef -> fieldDef.getName("srg")) - .orElse(null); - } - }); - } - - public void scheduleRemap() throws Throwable { + public void scheduleRemap(boolean isMainRemapTask) throws Throwable { Project project = getProject(); LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); Path input = this.getInput().getAsFile().get().toPath(); @@ -311,7 +151,7 @@ public class RemapJarTask extends Jar { String fromM = this.fromM.get(); String toM = this.toM.get(); - if (extension.isRootProject()) { + if (isMainRemapTask) { jarRemapper.addToClasspath(getRemapClasspath()); jarRemapper.addMappings(TinyRemapperMappingsHelper.create(extension.isForge() ? mappingsProvider.getMappingsWithSrg() : mappingsProvider.getMappings(), fromM, toM, false)); @@ -327,6 +167,9 @@ public class RemapJarTask extends Jar { // Add remap options to the jar remapper jarRemapper.addOptions(this.remapOptions); + NestedJarProvider nestedJarProvider = getNestedJarProvider(); + nestedJarProvider.prepare(getProject()); + jarRemapper.scheduleRemap(input, output) .supplyAccessWidener((remapData, remapper) -> { if (getRemapAccessWidener().getOrElse(false) && extension.accessWidener != null) { @@ -357,18 +200,43 @@ public class RemapJarTask extends Jar { } if (getAddNestedDependencies().getOrElse(false)) { - if (NestedJars.addNestedJars(project, output)) { - project.getLogger().debug("Added nested jar paths to mod json"); - } + JarNester.nestJars(nestedJarProvider.provide(), output.toFile(), project.getLogger()); } if (accessWidener != null) { boolean replaced = ZipUtil.replaceEntry(data.output.toFile(), accessWidener.getLeft(), accessWidener.getRight()); Preconditions.checkArgument(replaced, "Failed to remap access widener"); } + + if (isReproducibleFileOrder() || !isPreserveFileTimestamps()) { + try { + ZipReprocessorUtil.reprocessZip(output.toFile(), isReproducibleFileOrder(), isPreserveFileTimestamps()); + } catch (IOException e) { + throw new RuntimeException("Failed to re-process jar", e); + } + } }); } + private NestedJarProvider getNestedJarProvider() { + Configuration includeConfiguration = getProject().getConfigurations().getByName(Constants.Configurations.INCLUDE); + + if (!addDefaultNestedDependencies.getOrElse(true)) { + return new NestedJarPathProvider(nestedPaths); + } + + NestedJarProvider baseProvider = NestedDependencyProvider.createNestedDependencyProviderFromConfiguration(getProject(), includeConfiguration); + + if (nestedPaths.isEmpty()) { + return baseProvider; + } + + return new MergedNestedJarProvider( + baseProvider, + new NestedJarPathProvider(nestedPaths) + ); + } + private IMappingProvider remapToSrg(LoomGradleExtension extension, IMappingProvider parent) throws IOException { TinyTree srg = extension.getMappingsProvider().getMappingsWithSrg(); @@ -444,6 +312,11 @@ public class RemapJarTask extends Jar { } @Input + public Property<Boolean> getAddDefaultNestedDependencies() { + return addDefaultNestedDependencies; + } + + @Input public Property<Boolean> getRemapAccessWidener() { return remapAccessWidener; } @@ -462,6 +335,14 @@ public class RemapJarTask extends Jar { return this; } + @ApiStatus.Experimental // This only allows mod jars, proceed with care when trying to pass in configurations with projects, or something that depends on a task. + public RemapJarTask include(Object... paths) { + Collections.addAll(nestedPaths, paths); + this.addNestedDependencies.set(true); + + return this; + } + @Input public Property<String> getFromM() { return fromM; diff --git a/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java index 2d388fba..9548ad7f 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java @@ -26,8 +26,6 @@ package net.fabricmc.loom.task; import java.io.File; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.Internal; @@ -35,29 +33,24 @@ import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; import net.fabricmc.loom.util.SourceRemapper; -import net.fabricmc.loom.util.ZipReprocessorUtil; public class RemapSourcesJarTask extends AbstractLoomTask { private Object input; private Object output; private String direction = "intermediary"; private SourceRemapper sourceRemapper = null; - private final Property<Boolean> archivePreserveFileTimestamps; - private final Property<Boolean> archiveReproducibleFileOrder; + private boolean preserveFileTimestamps = true; + private boolean reproducibleFileOrder = false; public RemapSourcesJarTask() { - ObjectFactory objectFactory = getProject().getObjects(); - archivePreserveFileTimestamps = objectFactory.property(Boolean.class); - archiveReproducibleFileOrder = objectFactory.property(Boolean.class); } @TaskAction public void remap() throws Exception { if (sourceRemapper == null) { - SourceRemapper.remapSources(getProject(), getInput(), getOutput(), direction.equals("named")); - ZipReprocessorUtil.reprocessZip(getOutput(), archivePreserveFileTimestamps.getOrElse(true), archiveReproducibleFileOrder.getOrElse(false)); + SourceRemapper.remapSources(getProject(), getInput(), getOutput(), direction.equals("named"), reproducibleFileOrder, preserveFileTimestamps); } else { - sourceRemapper.scheduleRemapSources(getInput(), getOutput(), archivePreserveFileTimestamps.getOrElse(true), archiveReproducibleFileOrder.getOrElse(false)); + sourceRemapper.scheduleRemapSources(getInput(), getOutput(), reproducibleFileOrder, preserveFileTimestamps); } } @@ -97,4 +90,22 @@ public class RemapSourcesJarTask extends AbstractLoomTask { public void setTargetNamespace(String value) { this.direction = value; } + + @Input + public boolean isPreserveFileTimestamps() { + return preserveFileTimestamps; + } + + public void setPreserveFileTimestamps(boolean preserveFileTimestamps) { + this.preserveFileTimestamps = preserveFileTimestamps; + } + + @Input + public boolean isReproducibleFileOrder() { + return reproducibleFileOrder; + } + + public void setReproducibleFileOrder(boolean reproducibleFileOrder) { + this.reproducibleFileOrder = reproducibleFileOrder; + } } diff --git a/src/main/java/net/fabricmc/loom/task/UnpickJarTask.java b/src/main/java/net/fabricmc/loom/task/UnpickJarTask.java new file mode 100644 index 00000000..e27d2ff2 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/UnpickJarTask.java @@ -0,0 +1,126 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 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; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; + +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.Internal; +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.util.Constants; + +public class UnpickJarTask extends JavaExec { + File inputJar; + File unpickDefinition; + + File outputJar; + + public UnpickJarTask() { + getOutputs().upToDateWhen(e -> false); + classpath(getProject().getConfigurations().getByName(Constants.Configurations.UNPICK_CLASSPATH)); + setMain("daomephsta.unpick.cli.Main"); + } + + @Override + public void exec() { + fileArg(getInputJar(), getOutputJar(), getUnpickDefinition()); + fileArg(getConstantJar()); + + // Classpath + fileArg(getExtension().getMinecraftMappedProvider().getMappedJar()); + fileArg(getMinecraftDependencies()); + + writeUnpickLogConfig(); + systemProperty("java.util.logging.config.file", getExtension().getUnpickLoggingConfigFile().getAbsolutePath()); + + super.exec(); + } + + private void writeUnpickLogConfig() { + try (InputStream is = LaunchProvider.class.getClassLoader().getResourceAsStream("unpick-logging.properties")) { + Files.deleteIfExists(getExtension().getUnpickLoggingConfigFile().toPath()); + Files.copy(is, getExtension().getUnpickLoggingConfigFile().toPath()); + } catch (IOException e) { + throw new RuntimeException("Failed to copy unpick logging config", e); + } + } + + private File[] getMinecraftDependencies() { + return getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES) + .resolve().toArray(new File[0]); + } + + private File getConstantJar() { + return getProject().getConfigurations().getByName(Constants.Configurations.MAPPING_CONSTANTS).getSingleFile(); + } + + @InputFile + public File getInputJar() { + return inputJar; + } + + public UnpickJarTask setInputJar(File inputJar) { + this.inputJar = inputJar; + return this; + } + + @InputFile + public File getUnpickDefinition() { + return unpickDefinition; + } + + public UnpickJarTask setUnpickDefinition(File unpickDefinition) { + this.unpickDefinition = unpickDefinition; + return this; + } + + @OutputFile + public File getOutputJar() { + return outputJar; + } + + public UnpickJarTask setOutputJar(File outputJar) { + this.outputJar = outputJar; + return this; + } + + private void fileArg(File... files) { + for (File file : files) { + args(file.getAbsolutePath()); + } + } + + @Internal + protected LoomGradleExtension getExtension() { + return getProject().getExtensions().getByType(LoomGradleExtension.class); + } +} diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 2e124766..b9059217 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -87,6 +87,8 @@ public class Constants { public static final String FORGE_DEPENDENCIES = "forgeDependencies"; @Deprecated // Not to be used in gradle 7+ public static final String COMPILE = "compile"; + public static final String MAPPING_CONSTANTS = "mappingsConstants"; + public static final String UNPICK_CLASSPATH = "unpick"; private Configurations() { } diff --git a/src/main/java/net/fabricmc/loom/util/DownloadUtil.java b/src/main/java/net/fabricmc/loom/util/DownloadUtil.java index 8d4865a6..1da65ae5 100644 --- a/src/main/java/net/fabricmc/loom/util/DownloadUtil.java +++ b/src/main/java/net/fabricmc/loom/util/DownloadUtil.java @@ -89,6 +89,7 @@ public class DownloadUtil { if ((code < 200 || code > 299) && code != HttpURLConnection.HTTP_NOT_MODIFIED) { //Didn't get what we expected + delete(to); throw new IOException(connection.getResponseMessage() + " for " + from); } @@ -111,7 +112,7 @@ public class DownloadUtil { try { // Try download to the output FileUtils.copyInputStreamToFile(connection.getInputStream(), to); } catch (IOException e) { - to.delete(); // Probably isn't good if it fails to copy/save + delete(to); // Probably isn't good if it fails to copy/save throw e; } diff --git a/src/main/java/net/fabricmc/loom/util/GroovyXmlUtil.java b/src/main/java/net/fabricmc/loom/util/GroovyXmlUtil.java index 801cc118..4e587e10 100644 --- a/src/main/java/net/fabricmc/loom/util/GroovyXmlUtil.java +++ b/src/main/java/net/fabricmc/loom/util/GroovyXmlUtil.java @@ -31,6 +31,8 @@ import java.util.stream.Stream; import groovy.util.Node; import groovy.xml.QName; +import net.fabricmc.loom.util.gradle.GradleSupport; + public final class GroovyXmlUtil { private GroovyXmlUtil() { } @@ -63,9 +65,19 @@ public final class GroovyXmlUtil { return ((QName) nodeName).matches(givenName); } + // New groovy 3 (gradle 7) class + if (GradleSupport.IS_GRADLE_7_OR_NEWER && nodeName.getClass().getName().equals("groovy.namespace.QName")) { + return isSameNameGroovy3(nodeName, givenName); + } + throw new UnsupportedOperationException("Cannot determine if " + nodeName.getClass() + " is the same as a String"); } + // TODO Move out of its own method when requiring gradle 7 + private static boolean isSameNameGroovy3(Object nodeName, String givenName) { + return ((groovy.namespace.QName) nodeName).matches(givenName); + } + public static Stream<Node> childrenNodesStream(Node node) { //noinspection unchecked return (Stream<Node>) (Stream) (((List<Object>) node.children()).stream().filter((i) -> i instanceof Node)); diff --git a/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java b/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java index 4ca156ac..a03126d1 100644 --- a/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java +++ b/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java @@ -25,6 +25,7 @@ package net.fabricmc.loom.util; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; @@ -60,6 +61,10 @@ public class HashedDownloadUtil { } public static void downloadIfInvalid(URL from, File to, String expectedHash, Logger logger, boolean quiet, boolean strict) throws IOException { + downloadIfInvalid(from, to, expectedHash, logger, quiet, strict, () -> { }); + } + + public static void downloadIfInvalid(URL from, File to, String expectedHash, Logger logger, boolean quiet, boolean strict, Runnable startDownload) throws IOException { if (LoomGradlePlugin.refreshDeps) { delete(to); } @@ -80,6 +85,8 @@ public class HashedDownloadUtil { } } + startDownload.run(); + HttpURLConnection connection = (HttpURLConnection) from.openConnection(); connection.setRequestProperty("Accept-Encoding", "gzip"); connection.connect(); @@ -121,14 +128,18 @@ public class HashedDownloadUtil { @Nullable private static String getSha1(File to, Logger logger) { - File sha1File = getSha1File(to); - - if (!sha1File.exists()) { + if (!to.exists()) { + delete(to); return null; } + File sha1File = getSha1File(to); + try { return Files.asCharSource(sha1File, StandardCharsets.UTF_8).read(); + } catch (FileNotFoundException ignored) { + // Quicker to catch this than do an exists check before. + return null; } catch (IOException e) { logger.warn("Error reading sha1 file '{}'.", sha1File); return null; diff --git a/src/main/java/net/fabricmc/loom/util/ModUtils.java b/src/main/java/net/fabricmc/loom/util/ModUtils.java new file mode 100644 index 00000000..1dfc43b2 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/ModUtils.java @@ -0,0 +1,42 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.util; + +import java.io.File; +import java.io.IOException; +import java.util.zip.ZipFile; + +public final class ModUtils { + private ModUtils() { + } + + public static boolean isMod(File input) { + try (ZipFile zipFile = new ZipFile(input)) { + return zipFile.getEntry("fabric.mod.json") != null; + } catch (IOException e) { + return false; + } + } +} diff --git a/src/main/java/net/fabricmc/loom/util/OperatingSystem.java b/src/main/java/net/fabricmc/loom/util/OperatingSystem.java index 2f69bcb8..db622790 100644 --- a/src/main/java/net/fabricmc/loom/util/OperatingSystem.java +++ b/src/main/java/net/fabricmc/loom/util/OperatingSystem.java @@ -49,6 +49,10 @@ public class OperatingSystem { return System.getProperty("sun.arch.data.model").contains("64"); } + public static boolean isWindows() { + return getOS().equals("windows"); + } + public static boolean isCIBuild() { String loomProperty = System.getProperty("fabric.loom.ci"); diff --git a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java index 0acc4693..dc6c7f58 100644 --- a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java +++ b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java @@ -62,17 +62,12 @@ public class SourceRemapper { this.toNamed = toNamed; } - public static void remapSources(Project project, File input, File output, boolean named) throws Exception { + public static void remapSources(Project project, File input, File output, boolean named, boolean reproducibleFileOrder, boolean preserveFileTimestamps) { SourceRemapper sourceRemapper = new SourceRemapper(project, named); - sourceRemapper.scheduleRemapSources(input, output, false, true); + sourceRemapper.scheduleRemapSources(input, output, reproducibleFileOrder, preserveFileTimestamps); sourceRemapper.remapAll(); } - @Deprecated - public void scheduleRemapSources(File source, File destination) throws Exception { - scheduleRemapSources(source, destination, false, true); // Not reproducable by default, old behavior - } - public void scheduleRemapSources(File source, File destination, boolean reproducibleFileOrder, boolean preserveFileTimestamps) { remapTasks.add((logger) -> { try { diff --git a/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java b/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java index 662acf82..735b3346 100644 --- a/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java +++ b/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java @@ -29,11 +29,20 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.attribute.FileTime; +import java.util.Calendar; +import java.util.Comparator; +import java.util.GregorianCalendar; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; public class ZipReprocessorUtil { + /** + * See {@link org.gradle.api.internal.file.archive.ZipCopyAction} about this. + */ + private static final long CONSTANT_TIME_FOR_ZIP_ENTRIES = new GregorianCalendar(1980, Calendar.FEBRUARY, 1, 0, 0, 0).getTimeInMillis(); + private ZipReprocessorUtil() { } public static void reprocessZip(File file, boolean reproducibleFileOrder, boolean preserveFileTimestamps) throws IOException { @@ -45,7 +54,7 @@ public class ZipReprocessorUtil { ZipEntry[] entries; if (reproducibleFileOrder) { - entries = zipFile.stream().sorted((a, b) -> a.getName().compareTo(b.getName())).toArray(ZipEntry[]::new); + entries = zipFile.stream().sorted(Comparator.comparing(ZipEntry::getName)).toArray(ZipEntry[]::new); } else { entries = zipFile.stream().toArray(ZipEntry[]::new); } @@ -54,11 +63,16 @@ public class ZipReprocessorUtil { try (ZipOutputStream zipOutputStream = new ZipOutputStream(outZip)) { for (ZipEntry entry : entries) { + ZipEntry newEntry = entry; + if (!preserveFileTimestamps) { - entry.setTime(0); + newEntry = new ZipEntry(entry.getName()); + newEntry.setTime(CONSTANT_TIME_FOR_ZIP_ENTRIES); + newEntry.setLastModifiedTime(FileTime.fromMillis(CONSTANT_TIME_FOR_ZIP_ENTRIES)); + newEntry.setLastAccessTime(FileTime.fromMillis(CONSTANT_TIME_FOR_ZIP_ENTRIES)); } - zipOutputStream.putNextEntry(entry); + zipOutputStream.putNextEntry(newEntry); InputStream inputStream = zipFile.getInputStream(entry); byte[] buf = new byte[1024]; int length; |