diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index b148d036..f776e736 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -74,6 +74,7 @@ import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraft import net.fabricmc.loom.configuration.providers.minecraft.mapped.SrgMinecraftProvider; import net.fabricmc.loom.configuration.sources.ForgeSourcesRemapper; import net.fabricmc.loom.extension.MixinExtension; +import net.fabricmc.loom.util.Checksum; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.ExceptionUtil; import net.fabricmc.loom.util.OperatingSystem; @@ -411,8 +412,9 @@ public final class CompileConfiguration { private static Path getLockFile(Project project) { final LoomGradleExtension extension = LoomGradleExtension.get(project); - final Path cacheDirectory = extension.getFiles().getProjectPersistentCache().toPath(); - return cacheDirectory.resolve("configuration.lock"); + final Path cacheDirectory = extension.getFiles().getUserCache().toPath(); + final String pathHash = Checksum.toHex(project.getProjectDir().getAbsolutePath().getBytes(StandardCharsets.UTF_8)).substring(0, 16); + return cacheDirectory.resolve("." + pathHash + ".lock"); } private static boolean getAndLock(Project project) { @@ -434,10 +436,23 @@ public final class CompileConfiguration { private static void releaseLock(Project project) { final Path lock = getLockFile(project); + if (!Files.exists(lock)) { + return; + } + try { - Files.deleteIfExists(lock); - } catch (IOException e) { - throw new UncheckedIOException("Failed to release project configuration lock", e); + Files.delete(lock); + } catch (IOException e1) { + try { + // If we failed to delete the lock file, moving it before trying to delete it may help. + final Path del = lock.resolveSibling(lock.getFileName() + ".del"); + Files.move(lock, del); + Files.delete(del); + } catch (IOException e2) { + var exception = new UncheckedIOException("Failed to release project configuration lock", e2); + exception.addSuppressed(e1); + throw exception; + } } } diff --git a/src/main/java/net/fabricmc/loom/configuration/MavenPublication.java b/src/main/java/net/fabricmc/loom/configuration/MavenPublication.java index 2245860b..7f629238 100644 --- a/src/main/java/net/fabricmc/loom/configuration/MavenPublication.java +++ b/src/main/java/net/fabricmc/loom/configuration/MavenPublication.java @@ -89,7 +89,6 @@ public final class MavenPublication { } } - // TODO: Remove this in Loom 0.12 private static void processEntry(Project project, String scope, Configuration config, PublishingExtension mavenPublish, AtomicBoolean reportedDeprecation) { mavenPublish.publications((publications) -> { for (Publication publication : publications) { @@ -101,7 +100,7 @@ public final class MavenPublication { continue; } else if (!reportedDeprecation.get() && !LoomGradleExtension.get(project).isForge()) { DeprecationHelper deprecationHelper = LoomGradleExtension.get(project).getDeprecationHelper(); - deprecationHelper.warn("Loom is applying dependency data manually to publications instead of using a software component (from(components[\"java\"])). This is deprecated and will be removed in Loom 0.13."); + deprecationHelper.warn("Loom is applying dependency data manually to publications instead of using a software component (from(components[\"java\"])). This is deprecated."); reportedDeprecation.set(true); } diff --git a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerTransformer.java b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerTransformer.java index a0f3ad50..69376e0a 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerTransformer.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerTransformer.java @@ -55,8 +55,6 @@ final class AccessWidenerTransformer { * Apply the rules from an access-widener to the given jar or zip file. */ void apply(File jarFile) { - logger.lifecycle("Processing file: " + jarFile.getName()); - try { ZipUtils.transform(jarFile.toPath(), getTransformers(accessWidener.getTargets())); } catch (IOException e) { diff --git a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java index 69b28cdf..c8a87540 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java @@ -136,8 +136,6 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource } } - project.getLogger().lifecycle("Processing file: " + jarFile.getName()); - try { ZipUtils.transform(jarFile.toPath(), getTransformers()); } catch (IOException e) { diff --git a/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java b/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java index da16ca3c..bb0ac719 100644 --- a/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java +++ b/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java @@ -103,7 +103,6 @@ public abstract class DownloadAssetsTask extends AbstractLoomTask { .download(url) .sha1(sha1) .progress(new GradleDownloadProgressListener(object.name(), progressGroup::createProgressLogger)) - .maxRetries(3) .downloadPathAsync(getAssetsPath(object, assetIndex), executor); } } diff --git a/src/main/java/net/fabricmc/loom/util/download/Download.java b/src/main/java/net/fabricmc/loom/util/download/Download.java index 12742ce6..b0a2dd6e 100644 --- a/src/main/java/net/fabricmc/loom/util/download/Download.java +++ b/src/main/java/net/fabricmc/loom/util/download/Download.java @@ -25,6 +25,7 @@ package net.fabricmc.loom.util.download; import java.io.IOException; +import java.io.InputStream; import java.io.UncheckedIOException; import java.net.HttpURLConnection; import java.net.ProxySelector; @@ -45,8 +46,8 @@ import java.time.Instant; import java.util.Locale; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.zip.GZIPInputStream; -import com.github.mizosoft.methanol.Methanol; import com.github.mizosoft.methanol.ProgressTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -85,10 +86,9 @@ public class Download { throw error("Unable to download %s in offline mode", this.url); } - return Methanol.newBuilder() + return HttpClient.newBuilder() .followRedirects(HttpClient.Redirect.ALWAYS) .proxy(ProxySelector.getDefault()) - .autoAcceptEncoding(true) .build(); } @@ -127,7 +127,7 @@ public class Download { } String downloadString() throws DownloadException { - final HttpResponse response = send(getRequest(), HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + final HttpResponse response = send(getRequest(), HttpResponse.BodyHandlers.ofInputStream()); final int statusCode = response.statusCode(); final boolean successful = statusCode >= 200 && statusCode < 300; @@ -135,7 +135,11 @@ public class Download { throw error("HTTP request to (%s) returned unsuccessful status (%d)", url, statusCode); } - return response.body(); + try (InputStream inputStream = decodeOutput(response)) { + return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + throw error(e, "Failed to decode download output"); + } } void downloadPath(Path output) throws DownloadException { @@ -174,7 +178,7 @@ public class Download { // Create a .lock file, this allows us to re-download if the download was forcefully aborted part way through. createLock(output); - HttpResponse response = send(httpRequest, HttpResponse.BodyHandlers.ofFile(output)); + HttpResponse response = send(httpRequest, HttpResponse.BodyHandlers.ofInputStream()); getAndResetLock(output); final int statusCode = response.statusCode(); @@ -185,13 +189,15 @@ public class Download { return; } - if (!success) { - try { - Files.deleteIfExists(output); - } catch (IOException ignored) { - // We tried. + if (success) { + try (InputStream inputStream = decodeOutput(response)) { + Files.write(output, inputStream.readAllBytes()); + } catch (IOException e) { + tryCleanup(output); + throw error(e, "Failed to decode and write download output"); } - + } else { + tryCleanup(output); throw error("HTTP request to (%s) returned unsuccessful status (%d)", url, statusCode); } @@ -224,6 +230,16 @@ public class Download { } } + private InputStream decodeOutput(HttpResponse response) throws IOException { + final String encoding = response.headers().firstValue("Content-Encoding").orElse(""); + + return switch (encoding) { + case "gzip" -> new GZIPInputStream(response.body()); + case "" -> response.body(); + default -> throw error("Unsupported encoding: %s", encoding); + }; + } + private boolean requiresDownload(Path output) throws DownloadException { if (getAndResetLock(output)) { LOGGER.warn("Forcing downloading {} as existing lock file was found. This may happen if the gradle build was forcefully canceled.", output); diff --git a/src/main/java/net/fabricmc/loom/util/download/DownloadBuilder.java b/src/main/java/net/fabricmc/loom/util/download/DownloadBuilder.java index 5a10f413..95d46623 100644 --- a/src/main/java/net/fabricmc/loom/util/download/DownloadBuilder.java +++ b/src/main/java/net/fabricmc/loom/util/download/DownloadBuilder.java @@ -44,7 +44,7 @@ public class DownloadBuilder { private boolean offline = false; private Duration maxAge = Duration.ZERO; private DownloadProgressListener progressListener = DownloadProgressListener.NONE; - private int maxRetries = 1; + private int maxRetries = 3; private DownloadBuilder(URI url) { this.url = url; diff --git a/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java b/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java index 1ff7b21e..aec6bc63 100644 --- a/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java +++ b/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java @@ -28,6 +28,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.UncheckedIOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -92,16 +94,7 @@ public final class SourceSetHelper { */ public static Project getSourceSetProject(SourceSet sourceSet) { final DefaultSourceSetOutput sourceSetOutput = (DefaultSourceSetOutput) sourceSet.getOutput(); - final DefaultTaskDependency taskDependency = (DefaultTaskDependency) sourceSetOutput.getClassesContributors(); - Project project = null; - - for (Object object : taskDependency.getMutableValues()) { - if (object instanceof Task task) { - project = task.getProject(); - } else if (object instanceof TaskProvider provider) { - project = provider.get().getProject(); - } - } + final Project project = getProjectFromSourceSetOutput(sourceSetOutput); if (project == null) { throw new NullPointerException("Unable to determine owning project for SourceSet: " + sourceSet.getName()); @@ -110,6 +103,55 @@ public final class SourceSetHelper { return project; } + private static Project getProjectFromSourceSetOutput(SourceSetOutput sourceSetOutput) { + final Class clazz = DefaultSourceSetOutput.class; + + try { + final Method getClassesContributorsMethod = clazz.getMethod("getClassesContributors"); + final Object classesContributors = getClassesContributorsMethod.invoke(sourceSetOutput); + + if (classesContributors instanceof List list) { + // Gradle 7.7 + return getProjectFromDirectoryContributions(list); + } else if (classesContributors instanceof DefaultTaskDependency taskDependency) { + // Pre Gradle 7.7 + return getProjectFromTaskDependency(taskDependency); + } else { + throw new UnsupportedOperationException(); + } + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + // Pre Gradle 7.7 + private static Project getProjectFromTaskDependency(DefaultTaskDependency taskDependency) { + for (Object object : taskDependency.getMutableValues()) { + if (object instanceof Task task) { + return task.getProject(); + } else if (object instanceof TaskProvider provider) { + return provider.get().getProject(); + } + } + + return null; + } + + // Gradle 7.7: https://github.com/gradle/gradle/commit/2797942dc71f0e0e186b7d0c5ba3e09eceea4507#diff-b19ce8fbc4aa4ebaeea74e39609636d65e385bce6990fd42d68581dd829f29b3L153 + private static Project getProjectFromDirectoryContributions(List classesContributions) { + for (Object classesContribution : classesContributions) { + try { + final Method getTask = classesContribution.getClass().getMethod("getTask"); + final TaskProvider taskProvider = (TaskProvider) getTask.invoke(classesContribution); + return taskProvider.get().getProject(); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + return null; + } + public static List getClasspath(ModSettings modSettings, Project project) { final List files = new ArrayList<>(); diff --git a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index 1b4c7183..d7fbc079 100644 --- a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy @@ -27,7 +27,7 @@ package net.fabricmc.loom.test import org.gradle.util.GradleVersion class LoomTestConstants { - private final static String NIGHTLY_VERSION = "7.6-20220729220941+0000" + private final static String NIGHTLY_VERSION = "7.7-20220827221558+0000" private final static boolean NIGHTLY_EXISTS = nightlyExists(NIGHTLY_VERSION) public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion() diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy index 038b87dc..d68fc213 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy @@ -44,7 +44,7 @@ class FabricAPITest extends Specification implements GradleProjectTestTrait { setup: def gradle = gradleProject( repo: "https://github.com/FabricMC/fabric.git", - commit: "417b986df14c7cdd5f16b0a340d28645910f8aa6", + commit: "5f243a8b7849eac4b30cd876a22a127797a1c406", version: version, patch: "fabric_api" ) @@ -54,7 +54,7 @@ class FabricAPITest extends Specification implements GradleProjectTestTrait { .replace('id "fabric-loom" version "0.9.50"', 'id "dev.architectury.loom"') .replace('"fabric-loom"', '"dev.architectury.loom"') - def server = ServerRunner.create(gradle.projectDir, "1.19.1") + def server = ServerRunner.create(gradle.projectDir, "1.19.2") .withMod(gradle.getOutputFile("fabric-api-${API_VERSION}.jar")) when: def result = gradle.run(tasks: ["build", "publishToMavenLocal"], args: ["--parallel", "-x", "check", "-x", "runDatagen", "-x", "runGametest"]) // Note: checkstyle does not appear to like being ran in a test runner @@ -64,11 +64,11 @@ class FabricAPITest extends Specification implements GradleProjectTestTrait { then: result.task(":build").outcome == SUCCESS - new File(gradle.mavenLocalDir, "net/fabricmc/fabric-api/fabric-biome-api-v1/9.0.15/fabric-biome-api-v1-9.0.15.jar").exists() - new File(gradle.mavenLocalDir, "net/fabricmc/fabric-api/fabric-biome-api-v1/9.0.15/fabric-biome-api-v1-9.0.15-sources.jar").exists() + new File(gradle.mavenLocalDir, "net/fabricmc/fabric-api/fabric-biome-api-v1/9.0.17/fabric-biome-api-v1-9.0.17.jar").exists() + new File(gradle.mavenLocalDir, "net/fabricmc/fabric-api/fabric-biome-api-v1/9.0.17/fabric-biome-api-v1-9.0.17-sources.jar").exists() serverResult.successful() - serverResult.output.contains("- fabric $API_VERSION") + serverResult.output.contains("- fabric-api $API_VERSION") where: version << STANDARD_TEST_VERSIONS } diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy index 451c3edc..8815a146 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy @@ -25,6 +25,7 @@ package net.fabricmc.loom.test.unit.download import io.javalin.http.HttpCode +import net.fabricmc.loom.util.Checksum import net.fabricmc.loom.util.download.Download import net.fabricmc.loom.util.download.DownloadException import net.fabricmc.loom.util.download.DownloadExecutor @@ -302,4 +303,35 @@ class DownloadFileTest extends DownloadTest { then: thrown DownloadException } + + def "File: Large"() { + setup: + byte[] data = new byte[1024 * 1024 * 10] // 10MB + new Random().nextBytes(data) + + server.get("/largeFile") { + it.result(data) + } + + def output = new File(File.createTempDir(), "file").toPath() + + when: + def result = Download.create("$PATH/largeFile").downloadPath(output) + + then: + Files.readAllBytes(output) == data + } + + // Known + def "Download Mojang Mappings"() { + setup: + def file = File.createTempDir().toPath().resolve("client.txt") + when: + Download.create("https://piston-data.mojang.com/v1/objects/8e8c9be5dc27802caba47053d4fdea328f7f89bd/client.txt") + .sha1("8e8c9be5dc27802caba47053d4fdea328f7f89bd") + .downloadPath(file) + + then: + Checksum.sha1Hex(file) == "8e8c9be5dc27802caba47053d4fdea328f7f89bd" + } } diff --git a/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy b/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy index 8d18497b..66420e55 100644 --- a/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy @@ -29,7 +29,7 @@ import groovy.transform.Immutable import java.util.concurrent.TimeUnit class ServerRunner { - static final String LOADER_VERSION = "0.14.8" + static final String LOADER_VERSION = "0.14.9" static final Map FABRIC_API_URLS = [ "1.16.5": "https://github.com/FabricMC/fabric/releases/download/0.37.1%2B1.16/fabric-api-0.37.1+1.16.jar", "1.17.1": "https://github.com/FabricMC/fabric/releases/download/0.37.1%2B1.17/fabric-api-0.37.1+1.17.jar" diff --git a/src/test/resources/patches/fabric_api.patch b/src/test/resources/patches/fabric_api.patch index a217affb..66042ce3 100644 --- a/src/test/resources/patches/fabric_api.patch +++ b/src/test/resources/patches/fabric_api.patch @@ -20,14 +20,3 @@ diff --git a/build.gradle b/build.gradle } def getBranch() { -@@ -207,10 +197,6 @@ - testmodImplementation sourceSets.client.output - } - -- loom { -- shareRemapCaches = true -- } -- - tasks.withType(ProcessResources).configureEach { - inputs.property "version", project.version -