diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java index 28ae3941..f610c5ee 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java @@ -130,7 +130,7 @@ public class LoomDependencyManager { try { provider.provide(info, afterTasks::add); } catch (Exception e) { - throw new RuntimeException("Failed to provide " + dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion() + " : " + e.toString(), e); + throw new RuntimeException("Failed to provide " + dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion() + " : " + e.toString() + "\n\tEnsure minecraft is not open and try running with --refresh-dependencies. Use --stacktrace to see the full stacktrace.", e); } } } 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 2d0ac48b..b2cb1f49 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java @@ -110,8 +110,7 @@ public class MinecraftProvider extends DependencyProvider { try { mergeJars(getProject().getLogger()); } catch (ZipError e) { - DownloadUtil.delete(minecraftClientJar); - DownloadUtil.delete(minecraftServerJar); + deleteFiles(); getProject().getLogger().error("Could not merge JARs! Deleting source JARs - please re-run the command and move on.", e); throw new RuntimeException(); @@ -127,6 +126,12 @@ public class MinecraftProvider extends DependencyProvider { versionManifestJson = new File(getExtension().getUserCache(), "version_manifest.json"); } + public void deleteFiles() { + DownloadUtil.delete(minecraftClientJar); + DownloadUtil.delete(minecraftServerJar); + DownloadUtil.delete(minecraftMergedJar); + } + private void downloadMcJson(boolean offline) throws IOException { if (getExtension().isShareCaches() && !getExtension().isRootProject() && versionManifestJson.exists() && !isRefreshDeps()) { return; 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 1a9a5db0..3b2e9391 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 @@ -50,6 +50,7 @@ import net.fabricmc.loom.configuration.DependencyProvider; import net.fabricmc.loom.configuration.providers.MinecraftProvider; import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.DownloadUtil; import net.fabricmc.loom.util.TinyRemapperMappingsHelper; import net.fabricmc.loom.util.srg.AtRemapper; import net.fabricmc.loom.util.srg.CoreModClassRemapper; @@ -109,13 +110,13 @@ public class MinecraftMappedProvider extends DependencyProvider { mapMinecraftJar(); } catch (Throwable t) { // Cleanup some some things that may be in a bad state now - minecraftMappedJar.delete(); - minecraftIntermediaryJar.delete(); + DownloadUtil.delete(minecraftMappedJar); + DownloadUtil.delete(minecraftIntermediaryJar); + getExtension().getMinecraftProvider().deleteFiles(); if (getExtension().isForge()) { minecraftSrgJar.delete(); } - getExtension().getMappingsProvider().cleanFiles(); throw new RuntimeException("Failed to remap minecraft", t); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java index be5b5b73..44a9a3e8 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java @@ -110,18 +110,20 @@ public class MinecraftAssetsProvider { } else if (HashedDownloadUtil.requiresDownload(file, sha1, project.getLogger())) { toDownload++; - if (progressBar[0] == null) { - progressBar[0] = new ProgressBarBuilder() - .setConsumer(new DelegatingProgressBarConsumer(project.getLogger()::lifecycle)) - .setInitialMax(toDownload) - .setUpdateIntervalMillis(2000) - .setTaskName(":downloading assets") - .setStyle(ProgressBarStyle.ASCII) - .showSpeed() - .build(); - } + synchronized (progressBar) { + if (progressBar[0] == null) { + progressBar[0] = new ProgressBarBuilder() + .setConsumer(new DelegatingProgressBarConsumer(project.getLogger()::lifecycle)) + .setInitialMax(toDownload) + .setUpdateIntervalMillis(2000) + .setTaskName(":downloading assets") + .setStyle(ProgressBarStyle.ASCII) + .showSpeed() + .build(); + } - progressBar[0].maxHint(toDownload); + progressBar[0].maxHint(toDownload); + } executor.execute(() -> { String assetName = entry.getKey(); @@ -134,12 +136,12 @@ public class MinecraftAssetsProvider { project.getLogger().debug(":downloading asset " + assetName); try { - HashedDownloadUtil.downloadIfInvalid(new URL(Constants.RESOURCES_BASE + sha1.substring(0, 2) + "/" + sha1), file, sha1, project.getLogger(), true); + HashedDownloadUtil.downloadIfInvalid(new URL(Constants.RESOURCES_BASE + sha1.substring(0, 2) + "/" + sha1), file, sha1, project.getLogger(), true, false); } catch (IOException e) { throw new RuntimeException("Failed to download: " + assetName, e); } - synchronized (progressBar[0]) { + synchronized (progressBar) { progressBar[0].step(); } }); diff --git a/src/main/java/net/fabricmc/loom/util/DownloadUtil.java b/src/main/java/net/fabricmc/loom/util/DownloadUtil.java index 8a16ea91..8d4865a6 100644 --- a/src/main/java/net/fabricmc/loom/util/DownloadUtil.java +++ b/src/main/java/net/fabricmc/loom/util/DownloadUtil.java @@ -221,5 +221,7 @@ public class DownloadUtil { if (etagFile.exists()) { etagFile.delete(); } + + HashedDownloadUtil.delete(file); } } diff --git a/src/main/java/net/fabricmc/loom/util/GroovyXmlUtil.java b/src/main/java/net/fabricmc/loom/util/GroovyXmlUtil.java index c07fa64c..801cc118 100644 --- a/src/main/java/net/fabricmc/loom/util/GroovyXmlUtil.java +++ b/src/main/java/net/fabricmc/loom/util/GroovyXmlUtil.java @@ -29,13 +29,14 @@ import java.util.Optional; import java.util.stream.Stream; import groovy.util.Node; +import groovy.xml.QName; public final class GroovyXmlUtil { private GroovyXmlUtil() { } public static Node getOrCreateNode(Node parent, String name) { for (Object object : parent.children()) { - if (object instanceof Node && name.equals(((Node) object).name())) { + if (object instanceof Node && isSameName(((Node) object).name(), name)) { return (Node) object; } } @@ -45,7 +46,7 @@ public final class GroovyXmlUtil { public static Optional getNode(Node parent, String name) { for (Object object : parent.children()) { - if (object instanceof Node && name.equals(((Node) object).name())) { + if (object instanceof Node && isSameName(((Node) object).name(), name)) { return Optional.of((Node) object); } } @@ -53,6 +54,18 @@ public final class GroovyXmlUtil { return Optional.empty(); } + private static boolean isSameName(Object nodeName, String givenName) { + if (nodeName instanceof String) { + return nodeName.equals(givenName); + } + + if (nodeName instanceof QName) { + return ((QName) nodeName).matches(givenName); + } + + throw new UnsupportedOperationException("Cannot determine if " + nodeName.getClass() + " is the same as a String"); + } + public static Stream childrenNodesStream(Node node) { //noinspection unchecked return (Stream) (Stream) (((List) 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 97b5ea08..4ca156ac 100644 --- a/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java +++ b/src/main/java/net/fabricmc/loom/util/HashedDownloadUtil.java @@ -56,16 +56,27 @@ public class HashedDownloadUtil { } public static void downloadIfInvalid(URL from, File to, String expectedHash, Logger logger, boolean quiet) throws IOException { + downloadIfInvalid(from, to, expectedHash, logger, quiet, true); + } + + public static void downloadIfInvalid(URL from, File to, String expectedHash, Logger logger, boolean quiet, boolean strict) throws IOException { if (LoomGradlePlugin.refreshDeps) { delete(to); } if (to.exists()) { - String sha1 = getSha1(to, logger); + if (strict) { + if (Checksum.equals(to, expectedHash)) { + // The hash matches the target file + return; + } + } else { + String sha1 = getSha1(to, logger); - if (expectedHash.equals(sha1)) { - // The hash in the sha1 file matches - return; + if (expectedHash.equals(sha1)) { + // The hash in the sha1 file matches + return; + } } } @@ -77,6 +88,7 @@ public class HashedDownloadUtil { 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); } diff --git a/src/test/groovy/net/fabricmc/loom/GroovyXmlUtilTest.groovy b/src/test/groovy/net/fabricmc/loom/GroovyXmlUtilTest.groovy new file mode 100644 index 00000000..a379b727 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/GroovyXmlUtilTest.groovy @@ -0,0 +1,74 @@ +/* + * 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 + +import groovy.xml.QName +import net.fabricmc.loom.util.GroovyXmlUtil +import spock.lang.Specification + +class GroovyXmlUtilTest extends Specification { + def "getOrCreateNode finds existing node"() { + when: + def xmlTree = new XmlParser().parseText(text) + def existingNode = xmlTree[innerName] + def actualNode = GroovyXmlUtil.getOrCreateNode(xmlTree, innerName) + + then: + existingNode.text() == actualNode.text() + + where: + innerName | text + "bar" | "inner content to ensure correct" + "dependencies" | "inner content to ensure correct" + } + + def "getOrCreateNode creates a node if needed"() { + when: + def xmlTree = new XmlParser().parseText(text) + def actualNode = GroovyXmlUtil.getOrCreateNode(xmlTree, innerName) + + then: + xmlTree[QName.valueOf(actualNode.name().toString())] != null + + where: + innerName | text + "bar" | "" + "dependencies" | "" + } + + def "getNode finds existing node"() { + when: + def xmlTree = new XmlParser().parseText(text) + def actualNode = GroovyXmlUtil.getNode(xmlTree, innerName) + + then: + actualNode.isPresent() + + where: + innerName | text + "bar" | "inner content to ensure correct" + "dependencies" | "inner content to ensure correct" + } +}