From 9a3b82c8a314c7fa17febc34b9ad34d323b86bc8 Mon Sep 17 00:00:00 2001 From: modmuss Date: Mon, 15 Apr 2024 18:56:59 +0100 Subject: [PATCH] Initial config caching work (#1053) --- build.gradle | 15 --- .../fabricmc/loom/LoomGradleExtension.java | 2 + .../extension/LoomGradleExtensionImpl.java | 34 ++++- .../loom/task/DownloadAssetsTask.java | 41 +++--- .../net/fabricmc/loom/task/LoomTasks.java | 16 ++- .../task/launch/GenerateDLIConfigTask.java | 124 ++++++++++++++---- .../task/launch/GenerateLog4jConfigTask.java | 14 +- .../launch/GenerateRemapClasspathTask.java | 8 +- .../loom/util/download/DownloadFactory.java | 76 +++++++++++ .../loom/util/gradle/ProgressGroup.java | 5 + .../loom/util/gradle/SourceSetHelper.java | 10 ++ .../integration/ConfigurationCacheTest.groovy | 61 +++++++++ .../test/util/GradleProjectTestTrait.groovy | 15 ++- 13 files changed, 353 insertions(+), 68 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/util/download/DownloadFactory.java create mode 100644 src/test/groovy/net/fabricmc/loom/test/integration/ConfigurationCacheTest.groovy diff --git a/build.gradle b/build.gradle index 6654f461..f61b5ee0 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,6 @@ plugins { id 'eclipse' id 'groovy' id 'checkstyle' - id 'jacoco' id 'codenarc' alias(libs.plugins.kotlin) apply false // Delay this so we can perform magic 🪄 first. alias(libs.plugins.spotless) @@ -267,20 +266,6 @@ gradlePlugin { } } -jacoco { - toolVersion = libs.versions.jacoco.get() -} - -// Run to get test coverage. -jacocoTestReport { - dependsOn test - reports { - xml.required = false - csv.required = false - html.outputLocation = file("${layout.buildDirectory.get().asFile}/jacocoHtml") - } -} - test { maxHeapSize = "2560m" jvmArgs "-XX:+HeapDumpOnOutOfMemoryError" diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index e78939ac..7d3d29f7 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -125,4 +125,6 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI { Collection getLayeredMappingFactories(); LoomProblemReporter getProblemReporter(); + + boolean isConfigurationCacheActive(); } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index 851832df..139bd39f 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -32,7 +32,10 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import javax.inject.Inject; + import org.gradle.api.Project; +import org.gradle.api.configuration.BuildFeatures; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.FileCollection; import org.gradle.api.provider.ListProperty; @@ -57,7 +60,7 @@ import net.fabricmc.loom.util.download.Download; import net.fabricmc.loom.util.download.DownloadBuilder; import net.fabricmc.loom.util.gradle.GradleUtils; -public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implements LoomGradleExtension { +public abstract class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implements LoomGradleExtension { private final Project project; private final MixinExtension mixinApExtension; private final LoomFiles loomFiles; @@ -72,10 +75,16 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen private IntermediaryMinecraftProvider intermediaryMinecraftProvider; private InstallerData installerData; private boolean refreshDeps; - private Provider multiProjectOptimisation; + private final Provider multiProjectOptimisation; private final ListProperty libraryProcessorFactories; private final LoomProblemReporter problemReporter; + private final boolean configurationCacheActive; + private final boolean isolatedProjectsActive; + @Inject + protected abstract BuildFeatures getBuildFeatures(); + + @Inject public LoomGradleExtensionImpl(Project project, LoomFiles files) { super(project, files); this.project = project; @@ -99,6 +108,22 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen libraryProcessorFactories.addAll(LibraryProcessorManager.DEFAULT_LIBRARY_PROCESSORS); libraryProcessorFactories.finalizeValueOnRead(); + configurationCacheActive = getBuildFeatures().getConfigurationCache().getActive().get(); + isolatedProjectsActive = getBuildFeatures().getIsolatedProjects().getActive().get(); + + // Fundamentally impossible to support multi-project optimisation with the configuration cache and/or isolated projects. + if (multiProjectOptimisation.get() && configurationCacheActive) { + throw new UnsupportedOperationException("Multi-project optimisation is not supported with the configuration cache"); + } + + if (multiProjectOptimisation.get() && isolatedProjectsActive) { + throw new UnsupportedOperationException("Isolated projects are not supported with multi-project optimisation"); + } + + if (configurationCacheActive) { + project.getLogger().warn("Loom support for the Gradle configuration cache is highly experimental and may not work as expected. Please report any issues you encounter."); + } + if (refreshDeps) { project.getLogger().lifecycle("Refresh dependencies is in use, loom will be significantly slower."); } @@ -286,4 +311,9 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen public LoomProblemReporter getProblemReporter() { return problemReporter; } + + @Override + public boolean isConfigurationCacheActive() { + return configurationCacheActive; + } } diff --git a/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java b/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java index c31b683b..06c165bc 100644 --- a/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java +++ b/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java @@ -34,17 +34,18 @@ import javax.inject.Inject; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Nested; import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.TaskAction; +import org.gradle.internal.logging.progress.ProgressLoggerFactory; -import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.configuration.ide.RunConfigSettings; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; import net.fabricmc.loom.configuration.providers.minecraft.assets.AssetIndex; import net.fabricmc.loom.util.MirrorUtil; import net.fabricmc.loom.util.download.DownloadExecutor; +import net.fabricmc.loom.util.download.DownloadFactory; import net.fabricmc.loom.util.download.GradleDownloadProgressListener; import net.fabricmc.loom.util.gradle.ProgressGroup; @@ -58,12 +59,24 @@ public abstract class DownloadAssetsTask extends AbstractLoomTask { @Input public abstract Property getMinecraftVersion(); + @Input + public abstract Property getResourcesBaseUrl(); + + @Input + protected abstract Property getAssetsIndexJson(); + @OutputDirectory public abstract RegularFileProperty getAssetsDirectory(); @OutputDirectory public abstract RegularFileProperty getLegacyResourcesDirectory(); + @Inject + protected abstract ProgressLoggerFactory getProgressLoggerFactory(); + + @Nested + protected abstract DownloadFactory getDownloadFactory(); + @Inject public DownloadAssetsTask() { final MinecraftVersionMeta versionInfo = getExtension().getMinecraftProvider().getVersionInfo(); @@ -83,6 +96,11 @@ public abstract class DownloadAssetsTask extends AbstractLoomTask { getLegacyResourcesDirectory().set(new File(getProject().getProjectDir(), client.getRunDir() + "/resources")); } + getResourcesBaseUrl().set(MirrorUtil.getResourcesBase(getProject())); + getResourcesBaseUrl().finalizeValue(); + + getAssetsIndexJson().set(LoomGradlePlugin.GSON.toJson(getExtension().getMinecraftProvider().getVersionInfo().assetIndex())); + getAssetsHash().finalizeValue(); getAssetsDirectory().finalizeValueOnRead(); getLegacyResourcesDirectory().finalizeValueOnRead(); @@ -92,13 +110,13 @@ public abstract class DownloadAssetsTask extends AbstractLoomTask { public void downloadAssets() throws IOException { final AssetIndex assetIndex = getAssetIndex(); - try (ProgressGroup progressGroup = new ProgressGroup(getProject(), "Download Assets"); + try (ProgressGroup progressGroup = new ProgressGroup("Download Assets", getProgressLoggerFactory()); DownloadExecutor executor = new DownloadExecutor(getDownloadThreads().get())) { for (AssetIndex.Object object : assetIndex.getObjects()) { final String sha1 = object.hash(); - final String url = MirrorUtil.getResourcesBase(getProject()) + sha1.substring(0, 2) + "/" + sha1; + final String url = getResourcesBaseUrl().get() + sha1.substring(0, 2) + "/" + sha1; - getExtension() + getDownloadFactory() .download(url) .sha1(sha1) .progress(new GradleDownloadProgressListener(object.name(), progressGroup::createProgressLogger)) @@ -107,18 +125,11 @@ public abstract class DownloadAssetsTask extends AbstractLoomTask { } } - private MinecraftVersionMeta.AssetIndex getAssetIndexMeta() { - MinecraftVersionMeta versionInfo = getExtension().getMinecraftProvider().getVersionInfo(); - return versionInfo.assetIndex(); - } - private AssetIndex getAssetIndex() throws IOException { - final LoomGradleExtension extension = getExtension(); - final MinecraftProvider minecraftProvider = extension.getMinecraftProvider(); - final MinecraftVersionMeta.AssetIndex assetIndex = getAssetIndexMeta(); - final File indexFile = new File(getAssetsDirectory().get().getAsFile(), "indexes" + File.separator + assetIndex.fabricId(minecraftProvider.minecraftVersion()) + ".json"); + final MinecraftVersionMeta.AssetIndex assetIndex = LoomGradlePlugin.GSON.fromJson(getAssetsIndexJson().get(), MinecraftVersionMeta.AssetIndex.class); + final File indexFile = new File(getAssetsDirectory().get().getAsFile(), "indexes" + File.separator + assetIndex.fabricId(getMinecraftVersion().get()) + ".json"); - final String json = extension.download(assetIndex.url()) + final String json = getDownloadFactory().download(assetIndex.url()) .sha1(assetIndex.sha1()) .downloadString(indexFile.toPath()); diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index 794f26ac..42896a63 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.java @@ -56,18 +56,22 @@ public abstract class LoomTasks implements Runnable { t.setDescription("Migrates mappings to a new version."); t.getOutputs().upToDateWhen(o -> false); }); + + var generateLog4jConfig = getTasks().register("generateLog4jConfig", GenerateLog4jConfigTask.class, t -> { + t.setDescription("Generate the log4j config file"); + }); + var generateRemapClasspath = getTasks().register("generateRemapClasspath", GenerateRemapClasspathTask.class, t -> { + t.setDescription("Generate the remap classpath file"); + }); getTasks().register("generateDLIConfig", GenerateDLIConfigTask.class, t -> { t.setDescription("Generate the DevLaunchInjector config file"); // Must allow these IDE files to be generated first t.mustRunAfter(getTasks().named("eclipse")); t.mustRunAfter(getTasks().named("idea")); - }); - getTasks().register("generateLog4jConfig", GenerateLog4jConfigTask.class, t -> { - t.setDescription("Generate the log4j config file"); - }); - getTasks().register("generateRemapClasspath", GenerateRemapClasspathTask.class, t -> { - t.setDescription("Generate the remap classpath file"); + + t.dependsOn(generateLog4jConfig); + t.getRemapClasspathFile().set(generateRemapClasspath.get().getRemapClasspathFile()); }); getTasks().register("configureLaunch", task -> { diff --git a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java index c4774609..c37ded2c 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java @@ -36,19 +36,93 @@ import java.util.StringJoiner; import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; +import org.gradle.api.Project; +import org.gradle.api.file.RegularFileProperty; import org.gradle.api.logging.configuration.ConsoleOutput; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider; import net.fabricmc.loom.task.AbstractLoomTask; import net.fabricmc.loom.util.gradle.SourceSetHelper; public abstract class GenerateDLIConfigTask extends AbstractLoomTask { + @Input + protected abstract Property getVersionInfoJson(); + + @Input + protected abstract Property getMinecraftVersion(); + + @Input + protected abstract Property getSplitSourceSets(); + + @Input + protected abstract Property getPlainConsole(); + + @Input + protected abstract Property getANSISupportedIDE(); + + @Input + @Optional + protected abstract Property getClassPathGroups(); + + @Input + protected abstract Property getLog4jConfigPaths(); + + @Input + @Optional + protected abstract Property getClientGameJarPath(); + + @Input + @Optional + protected abstract Property getCommonGameJarPath(); + + @Input + protected abstract Property getAssetsDirectoryPath(); + + @Input + protected abstract Property getNativesDirectoryPath(); + + @InputFile + public abstract RegularFileProperty getRemapClasspathFile(); + + @OutputFile + protected abstract RegularFileProperty getDevLauncherConfig(); + + public GenerateDLIConfigTask() { + getVersionInfoJson().set(LoomGradlePlugin.GSON.toJson(getExtension().getMinecraftProvider().getVersionInfo())); + getMinecraftVersion().set(getExtension().getMinecraftProvider().minecraftVersion()); + getSplitSourceSets().set(getExtension().areEnvironmentSourceSetsSplit()); + getANSISupportedIDE().set(ansiSupportedIde(getProject())); + getPlainConsole().set(getProject().getGradle().getStartParameter().getConsoleOutput() == ConsoleOutput.Plain); + + if (!getExtension().getMods().isEmpty()) { + getClassPathGroups().set(buildClassPathGroups(getProject())); + } + + getLog4jConfigPaths().set(getAllLog4JConfigFiles(getProject())); + + if (getSplitSourceSets().get()) { + getClientGameJarPath().set(getGameJarPath("client")); + getCommonGameJarPath().set(getGameJarPath("common")); + } + + getAssetsDirectoryPath().set(new File(getExtension().getFiles().getUserCache(), "assets").getAbsolutePath()); + getNativesDirectoryPath().set(getExtension().getFiles().getNativesDirectory(getProject()).getAbsolutePath()); + getDevLauncherConfig().set(getExtension().getFiles().getDevLauncherConfig()); + } + @TaskAction public void run() throws IOException { - final MinecraftVersionMeta versionInfo = getExtension().getMinecraftProvider().getVersionInfo(); - File assetsDirectory = new File(getExtension().getFiles().getUserCache(), "assets"); + final MinecraftVersionMeta versionInfo = LoomGradlePlugin.GSON.fromJson(getVersionInfoJson().get(), MinecraftVersionMeta.class); + File assetsDirectory = new File(getAssetsDirectoryPath().get()); if (versionInfo.assets().equals("legacy")) { assetsDirectory = new File(assetsDirectory, "/legacy/" + versionInfo.id()); @@ -56,48 +130,42 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { final LaunchConfig launchConfig = new LaunchConfig() .property("fabric.development", "true") - .property("fabric.remapClasspathFile", getExtension().getFiles().getRemapClasspathFile().getAbsolutePath()) - .property("log4j.configurationFile", getAllLog4JConfigFiles()) + .property("fabric.remapClasspathFile", getRemapClasspathFile().get().getAsFile().getAbsolutePath()) + .property("log4j.configurationFile", getLog4jConfigPaths().get()) .property("log4j2.formatMsgNoLookups", "true") .argument("client", "--assetIndex") - .argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex().fabricId(getExtension().getMinecraftProvider().minecraftVersion())) + .argument("client", versionInfo.assetIndex().fabricId(getMinecraftVersion().get())) .argument("client", "--assetsDir") .argument("client", assetsDirectory.getAbsolutePath()); if (versionInfo.hasNativesToExtract()) { - String nativesPath = getExtension().getFiles().getNativesDirectory(getProject()).getAbsolutePath(); + String nativesPath = getNativesDirectoryPath().get(); launchConfig .property("client", "java.library.path", nativesPath) .property("client", "org.lwjgl.librarypath", nativesPath); } - if (getExtension().areEnvironmentSourceSetsSplit()) { - launchConfig.property("client", "fabric.gameJarPath.client", getGameJarPath("client")); - launchConfig.property("fabric.gameJarPath", getGameJarPath("common")); + if (getSplitSourceSets().get()) { + launchConfig.property("client", "fabric.gameJarPath.client", getClientGameJarPath().get()); + launchConfig.property("fabric.gameJarPath", getCommonGameJarPath().get()); } - if (!getExtension().getMods().isEmpty()) { - launchConfig.property("fabric.classPathGroups", getClassPathGroups()); + if (getClassPathGroups().isPresent()) { + launchConfig.property("fabric.classPathGroups", getClassPathGroups().get()); } - final boolean plainConsole = getProject().getGradle().getStartParameter().getConsoleOutput() == ConsoleOutput.Plain; - final boolean ansiSupportedIDE = new File(getProject().getRootDir(), ".vscode").exists() - || new File(getProject().getRootDir(), ".idea").exists() - || new File(getProject().getRootDir(), ".project").exists() - || (Arrays.stream(getProject().getRootDir().listFiles()).anyMatch(file -> file.getName().endsWith(".iws"))); - //Enable ansi by default for idea and vscode when gradle is not ran with plain console. - if (ansiSupportedIDE && !plainConsole) { + if (getANSISupportedIDE().get() && !getPlainConsole().get()) { launchConfig.property("fabric.log.disableAnsi", "false"); } - FileUtils.writeStringToFile(getExtension().getFiles().getDevLauncherConfig(), launchConfig.asString(), StandardCharsets.UTF_8); + FileUtils.writeStringToFile(getDevLauncherConfig().getAsFile().get(), launchConfig.asString(), StandardCharsets.UTF_8); } - private String getAllLog4JConfigFiles() { - return getExtension().getLog4jConfigs().getFiles().stream() + private static String getAllLog4JConfigFiles(Project project) { + return LoomGradleExtension.get(project).getLog4jConfigs().getFiles().stream() .map(File::getAbsolutePath) .collect(Collectors.joining(",")); } @@ -115,16 +183,24 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { /** * See: https://github.com/FabricMC/fabric-loader/pull/585. */ - private String getClassPathGroups() { - return getExtension().getMods().stream() + private static String buildClassPathGroups(Project project) { + return LoomGradleExtension.get(project).getMods().stream() .map(modSettings -> - SourceSetHelper.getClasspath(modSettings, getProject()).stream() + SourceSetHelper.getClasspath(modSettings, project).stream() .map(File::getAbsolutePath) .collect(Collectors.joining(File.pathSeparator)) ) .collect(Collectors.joining(File.pathSeparator+File.pathSeparator)); } + private static boolean ansiSupportedIde(Project project) { + File rootDir = project.getRootDir(); + return new File(rootDir, ".vscode").exists() + || new File(rootDir, ".idea").exists() + || new File(rootDir, ".project").exists() + || (Arrays.stream(rootDir.listFiles()).anyMatch(file -> file.getName().endsWith(".iws"))); + } + public static class LaunchConfig { private final Map> values = new HashMap<>(); diff --git a/src/main/java/net/fabricmc/loom/task/launch/GenerateLog4jConfigTask.java b/src/main/java/net/fabricmc/loom/task/launch/GenerateLog4jConfigTask.java index fc249bba..8f183f81 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateLog4jConfigTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateLog4jConfigTask.java @@ -29,14 +29,26 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import javax.inject.Inject; + +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; import net.fabricmc.loom.task.AbstractLoomTask; public abstract class GenerateLog4jConfigTask extends AbstractLoomTask { + @OutputFile + public abstract RegularFileProperty getOutputFile(); + + @Inject + public GenerateLog4jConfigTask() { + getOutputFile().set(getExtension().getFiles().getDefaultLog4jConfigFile()); + } + @TaskAction public void run() { - Path outputFile = getExtension().getFiles().getDefaultLog4jConfigFile().toPath(); + Path outputFile = getOutputFile().get().getAsFile().toPath(); try (InputStream is = GenerateLog4jConfigTask.class.getClassLoader().getResourceAsStream("log4j2.fabric.xml")) { Files.deleteIfExists(outputFile); diff --git a/src/main/java/net/fabricmc/loom/task/launch/GenerateRemapClasspathTask.java b/src/main/java/net/fabricmc/loom/task/launch/GenerateRemapClasspathTask.java index 388c8566..8a9263a1 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateRemapClasspathTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateRemapClasspathTask.java @@ -60,6 +60,10 @@ public abstract class GenerateRemapClasspathTask extends AbstractLoomTask { .map(configurations::named) .forEach(getRemapClasspath()::from); + for (Path minecraftJar : getExtension().getMinecraftJars(MappingsNamespace.INTERMEDIARY)) { + getRemapClasspath().from(minecraftJar.toFile()); + } + getRemapClasspathFile().set(getExtension().getFiles().getRemapClasspathFile()); } @@ -67,10 +71,6 @@ public abstract class GenerateRemapClasspathTask extends AbstractLoomTask { public void run() { final List remapClasspath = new ArrayList<>(getRemapClasspath().getFiles()); - for (Path minecraftJar : getExtension().getMinecraftJars(MappingsNamespace.INTERMEDIARY)) { - remapClasspath.add(minecraftJar.toFile()); - } - String str = remapClasspath.stream() .map(File::getAbsolutePath) .collect(Collectors.joining(File.pathSeparator)); diff --git a/src/main/java/net/fabricmc/loom/util/download/DownloadFactory.java b/src/main/java/net/fabricmc/loom/util/download/DownloadFactory.java new file mode 100644 index 00000000..47506f4c --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/download/DownloadFactory.java @@ -0,0 +1,76 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2024 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.download; + +import java.net.URISyntaxException; + +import javax.inject.Inject; + +import org.gradle.api.Project; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; + +import net.fabricmc.loom.LoomGradleExtension; + +/** + * Can be used to create a {@link DownloadBuilder} with the correct settings for the project within a task. + */ +public abstract class DownloadFactory { + @Input + protected abstract Property getIsOffline(); + + @Input + protected abstract Property getIsManualRefreshDependencies(); + + @Inject + public abstract Project getProject(); + + @Inject + public DownloadFactory() { + getIsOffline().set(getProject().getGradle().getStartParameter().isOffline()); + getIsManualRefreshDependencies().set(LoomGradleExtension.get(getProject()).refreshDeps()); + } + + // Matches the logic in LoomGradleExtensionImpl + public DownloadBuilder download(String url) { + DownloadBuilder builder; + + try { + builder = Download.create(url); + } catch (URISyntaxException e) { + throw new RuntimeException("Failed to create downloader for: " + e); + } + + if (getIsOffline().get()) { + builder.offline(); + } + + if (getIsManualRefreshDependencies().get()) { + builder.forceDownload(); + } + + return builder; + } +} diff --git a/src/main/java/net/fabricmc/loom/util/gradle/ProgressGroup.java b/src/main/java/net/fabricmc/loom/util/gradle/ProgressGroup.java index 02126565..7eef1d2a 100644 --- a/src/main/java/net/fabricmc/loom/util/gradle/ProgressGroup.java +++ b/src/main/java/net/fabricmc/loom/util/gradle/ProgressGroup.java @@ -43,6 +43,11 @@ public class ProgressGroup implements Closeable { this.progressLoggerFactory = ((ProjectInternal) project).getServices().get(ProgressLoggerFactory.class); } + public ProgressGroup(String name, ProgressLoggerFactory progressLoggerFactory) { + this.name = name; + this.progressLoggerFactory = progressLoggerFactory; + } + private void start() { this.progressGroup = this.progressLoggerFactory.newOperation(name).setDescription(name); this.progressGroup.started(); 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 2bdbf248..b693a8a0 100644 --- a/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java +++ b/src/main/java/net/fabricmc/loom/util/gradle/SourceSetHelper.java @@ -50,6 +50,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.VisibleForTesting; import org.xml.sax.InputSource; +import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.ModSettings; import net.fabricmc.loom.configuration.ide.idea.IdeaUtils; @@ -236,6 +237,15 @@ public final class SourceSetHelper { Objects.requireNonNull(sourceSet); Objects.requireNonNull(path); + final Project project = getSourceSetProject(sourceSet); + final LoomGradleExtension extension = LoomGradleExtension.get(project); + + if (extension.isConfigurationCacheActive()) { + // TODO config cache, figure this out + project.getLogger().warn("Unable to find resource ({}) in source set ({}) when configuration cache is active", path, sourceSet.getName()); + return null; + } + try { return sourceSet.getResources() .matching(patternFilterable -> patternFilterable.include(path)) diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/ConfigurationCacheTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/ConfigurationCacheTest.groovy new file mode 100644 index 00000000..8b900f30 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/ConfigurationCacheTest.groovy @@ -0,0 +1,61 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2024 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.test.integration + +import spock.lang.Specification +import spock.lang.Unroll + +import net.fabricmc.loom.test.util.GradleProjectTestTrait + +import static net.fabricmc.loom.test.LoomTestConstants.PRE_RELEASE_GRADLE +import static org.gradle.testkit.runner.TaskOutcome.FAILED + +class ConfigurationCacheTest extends Specification implements GradleProjectTestTrait { + @Unroll + def "Configuration cache (task #task)"() { + setup: + def gradle = gradleProject(project: "minimalBase", version: PRE_RELEASE_GRADLE) + gradle.buildGradle << """ + dependencies { + minecraft 'com.mojang:minecraft:1.20.4' + mappings 'net.fabricmc:yarn:1.20.4+build.3:v2' + modImplementation 'net.fabricmc:fabric-loader:0.15.6' + modImplementation 'net.fabricmc.fabric-api:fabric-api:0.95.4+1.20.4' + } + """.stripIndent() + when: + def result = gradle.run(task: task, configurationCache: true, isloatedProjects: false) + def result2 = gradle.run(task: task, configurationCache: true, isloatedProjects: false) + + then: + result.task(":${task}").outcome != FAILED + result2.task(":${task}").outcome != FAILED + + where: + task | _ + "help" | _ + "configureClientLaunch" | _ + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy b/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy index 4062cfeb..988035d6 100644 --- a/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy @@ -150,6 +150,7 @@ trait GradleProjectTestTrait { private String gradleHomeDir private String warningMode private boolean useBuildSrc + private boolean enableDebugging = true BuildResult run(Map options) { // Setup the system props to tell loom that its running in a test env @@ -165,6 +166,14 @@ trait GradleProjectTestTrait { args << options.task } + if (options.configurationCache || System.getenv("LOOM_TEST_CONFIGURATION_CACHE") != null) { + args << "--configuration-cache" + } + + if (options.isloatedProjects) { + args << "-Dorg.gradle.unsafe.isolated-projects=true" + } + args.addAll(options.tasks ?: []) args << "--stacktrace" @@ -179,6 +188,10 @@ trait GradleProjectTestTrait { writeBuildSrcDeps(runner) } + if (options.disableDebugging) { + enableDebugging = false + } + return options.expectFailure ? runner.buildAndFail() : runner.build() } @@ -188,7 +201,7 @@ trait GradleProjectTestTrait { .withPluginClasspath() .withGradleVersion(gradleVersion) .forwardOutput() - .withDebug(true) + .withDebug(enableDebugging) } File getProjectDir() {