Update MinecraftExec to work with config cache

- Switch withFileContents/withFileSource to be normal inputs.
 - Download complementary shaders from Maven instead.
This commit is contained in:
Jonathan Coates
2026-09-17 14:52:57 +01:00
parent cc54413718
commit fbbee65c84
6 changed files with 71 additions and 93 deletions
@@ -19,9 +19,7 @@ import org.gradle.process.JavaForkOptions
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
import org.gradle.testing.jacoco.tasks.JacocoReport
import java.io.File
import java.io.IOException
import java.net.URI
import java.util.regex.Pattern
abstract class CCTweakedExtension(private val project: Project) {
@@ -55,10 +53,12 @@ abstract class CCTweakedExtension(private val project: Project) {
ExternalProjectArtifacts.configure(this)
}
val embeddedProjectClasses: Provider<FileCollection> = embeddedProjectsResolved.map(ExternalProjectArtifacts::classes)
val embeddedProjectClasses: Provider<FileCollection> =
embeddedProjectsResolved.map(ExternalProjectArtifacts::classes)
val embeddedProjectClassesAndResources: Provider<FileCollection> =
embeddedProjectsResolved.map(ExternalProjectArtifacts::classesAndResources)
val embeddedProjectSources: Provider<FileCollection> = embeddedProjectsResolved.map(ExternalProjectArtifacts::sources)
val embeddedProjectSources: Provider<FileCollection> =
embeddedProjectsResolved.map(ExternalProjectArtifacts::sources)
/**
* Enable our custom linters on this project.
@@ -105,41 +105,6 @@ abstract class CCTweakedExtension(private val project: Project) {
}
}
/**
* Download a file by creating a dummy Ivy repository.
*
* This should only be used for one-off downloads. Using a more conventional Ivy or Maven repository is preferred
* where possible.
*/
fun downloadFile(label: String, url: String): File {
val uri = URI(url)
val path = File(uri.path)
project.repositories.ivy {
name = label
setUrl(URI(uri.scheme, uri.userInfo, uri.host, uri.port, path.parent, null, null))
patternLayout {
artifact("[artifact].[ext]")
}
metadataSources {
artifact()
}
content {
includeModule("cc.tweaked.internal", path.nameWithoutExtension)
}
}
return project.configurations.detachedConfiguration(
project.dependencies.create(
mapOf(
"group" to "cc.tweaked.internal",
"name" to path.nameWithoutExtension,
"ext" to path.extension,
),
),
).resolve().single()
}
private fun <T : Any> gitProvider(default: T, command: List<String>, process: (String) -> T): Provider<T> {
val baseResult = project.providers.exec {
commandLine = listOf("git", "-C", project.rootDir.absolutePath) + command
@@ -32,10 +32,12 @@ fun DependencyHandler.annotationProcessorEverywhere(dep: Any) {
fun JavaExec.copyToFull(spec: JavaExec) {
copyTo(spec)
// Additional Java options
spec.jvmArgs = jvmArgs // Fabric overrides getJvmArgs so copyTo doesn't do the right thing.
// Copy arguments. We have to evaluate argument providers immediately to avoid holding a reference to the other
// task.
spec.args = args
spec.argumentProviders.addAll(argumentProviders)
for (argProvider in argumentProviders) spec.args(argProvider.asArguments())
// Additional Java options
spec.mainClass.set(mainClass)
spec.classpath = classpath
spec.javaLauncher.set(javaLauncher)
@@ -6,18 +6,22 @@ package cc.tweaked.gradle
import net.neoforged.moddevgradle.internal.RunGameTask
import org.gradle.api.GradleException
import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.invocation.Gradle
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Provider
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.gradle.api.tasks.*
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.JavaExec
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.getByName
import org.gradle.language.base.plugins.LifecycleBasePlugin
import java.io.File
import java.nio.file.Files
import java.util.concurrent.TimeUnit
import java.util.function.Supplier
import javax.inject.Inject
import kotlin.random.Random
@@ -34,19 +38,28 @@ abstract class ClientJavaExec : JavaExec() {
}
@get:Input
val renderdoc get() = project.hasProperty("renderdoc")
val renderdoc: Provider<Boolean> = project.provider { project.hasProperty("renderdoc") }
/**
* When [false], tests will not be run automatically, allowing the user to debug rendering.
* When `false`, tests will not be run automatically, allowing the user to debug rendering.
*/
@get:Input
val clientDebug get() = renderdoc || project.hasProperty("clientDebug")
val clientDebug: Provider<Boolean> = project.provider { renderdoc.get() || project.hasProperty("clientDebug") }
/**
* When [false], tests will not run under a framebuffer.
* When `false`, tests will not run under a framebuffer.
*/
@get:Input
val useFramebuffer get() = !clientDebug && !project.hasProperty("clientNoFramebuffer")
val useFramebuffer: Provider<Boolean> =
project.provider { !clientDebug.get() && !project.hasProperty("clientNoFramebuffer") }
/** Additional files to write to the destination directory. */
@get:Input
abstract val fileContents: MapProperty<String, String>
/** Additional files to copy to the destination directory. */
@get:Input
abstract val fileSources: MapProperty<String, File>
/**
* The path test results are written to.
@@ -55,8 +68,8 @@ abstract class ClientJavaExec : JavaExec() {
val testResults = project.layout.buildDirectory.file("test-results/$name.xml")
private fun setTestProperties() {
if (!clientDebug) systemProperty("cctest.client", "")
if (renderdoc) environment("LD_PRELOAD", "/usr/lib/librenderdoc.so")
if (!clientDebug.get()) systemProperty("cctest.client", "")
if (renderdoc.get()) environment("LD_PRELOAD", "/usr/lib/librenderdoc.so")
systemProperty("cctest.gametest-report", testResults.get().asFile.absoluteFile)
workingDir(project.layout.buildDirectory.dir("gametest/$name"))
}
@@ -102,27 +115,19 @@ abstract class ClientJavaExec : JavaExec() {
systemProperty("cctest.tags", tags.joinToString(","))
}
/**
* Write a file with the given contents before starting Minecraft. This may be useful for writing config files.
*/
fun withFileContents(path: Any, contents: Supplier<String>) {
val file = project.file(path).toPath()
doFirst {
Files.createDirectories(file.parent)
Files.writeString(file, contents.get())
}
}
/** Configure Iris to use the given shader pack. */
fun withShaderPack(shaders: FileCollection) = withShaderPack(shaders.elements.map { it.single().asFile })
/**
* Copy a file to the provided path before starting Minecraft. This copy only occurs if the file does not already
* exist.
*/
fun withFileFrom(path: Any, source: Supplier<File>) {
val file = project.file(path).toPath()
doFirst {
Files.createDirectories(file.parent)
if (!Files.exists(file)) Files.copy(source.get().toPath(), file)
}
/** Configure Iris to use the given shader pack. */
fun withShaderPack(shaders: Provider<File>) {
fileSources.put("shaderpacks/shaders.zip", shaders)
fileContents.put(
"config/iris.properties",
"""
enableShaders=true
shaderPack=shaders.zip
""".trimIndent(),
)
}
@TaskAction
@@ -130,7 +135,23 @@ abstract class ClientJavaExec : JavaExec() {
Files.createDirectories(workingDir.toPath())
fsOperations.delete { delete(workingDir.resolve("screenshots")) }
if (useFramebuffer) {
for ((dest, contents) in fileContents.get()) {
val destFile = workingDir.resolve(dest).toPath()
Files.createDirectories(destFile.parent)
Files.writeString(destFile, contents)
}
for ((dest, source) in fileSources.get()) {
val destFile = workingDir.resolve(dest)
Files.createDirectories(destFile.parentFile.toPath())
fsOperations.copy {
from(source);
into(destFile.parent)
rename { destFile.name }
}
}
if (useFramebuffer.get()) {
clientRunner.get().wrapClient(this) { super.exec() }
} else {
super.exec()
+2
View File
@@ -36,6 +36,7 @@ kotlin-coroutines = "1.11.0"
nightConfig = "3.9.0"
# Minecraft mods
complementary = "r5.9.3"
emi = "1.0.8+1.20.1"
fabricPermissions = "0.3.20230723"
iris = "1.6.4+1.20"
@@ -102,6 +103,7 @@ nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
# Minecraft mods
complementary = { module = "maven.modrinth:complementary-reimagined", version.ref = "complementary" }
create-fabric = { module = "com.simibubi.create:create-fabric-1.20.1", version.ref = "create-fabric" }
create-forge = { module = "com.simibubi.create:create-1.20.1", version.ref = "create-forge" }
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
-12
View File
@@ -81,18 +81,6 @@ tasks.javadoc {
// Include the core-api in our javadoc export. This is wrong, but it means we can export a single javadoc dump.
source("../core-api/src/main/java")
options {
this as StandardJavadocDocletOptions
addBooleanOption("-allow-script-in-comments", true)
bottom(
"""
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/components/prism-core.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
<link href=" https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css " rel="stylesheet">
""".trimIndent(),
)
}
}
configurations.named("javadocElements") {
+9 -9
View File
@@ -67,6 +67,11 @@ configurations {
runtimeClasspath { extendsFrom(localImplementation.get()) }
}
val testWithIrisShaders = configurations.dependencyScope("testWithIrisShaders")
val testWithIrisShadersResolve = configurations.resolvable("testWithIrisShadersResolve") {
extendsFrom(testWithIrisShaders)
}
dependencies {
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
modCompileOnly(libs.bundles.externalMods.fabric.compile) {
@@ -83,6 +88,7 @@ dependencies {
"modTestWithSodium"(libs.sodium)
"modTestWithIris"(libs.iris)
"modTestWithIris"(libs.sodium)
"testWithIrisShaders"(variantOf(libs.complementary) { artifactType("zip") })
"includeRuntimeOnly"(libs.cobalt)
"includeRuntimeOnly"(libs.jzlib)
@@ -289,15 +295,9 @@ val runGametestClientWithIris = tasks.register<ClientJavaExec>("runGametestClien
tags("iris")
classpath += configurations["testWithIris"]
withFileFrom(workingDir.resolve("shaderpacks/ComplementaryShaders_v4.6.zip")) {
cct.downloadFile("Complementary Shaders", "https://edge.forgecdn.net/files/3951/170/ComplementaryShaders_v4.6.zip")
}
withFileContents(workingDir.resolve("config/iris.properties")) {
"""
enableShaders=true
shaderPack=ComplementaryShaders_v4.6.zip
""".trimIndent()
}
// Ideally we'd use a detached configuration here, but that seems to ignore the artifactType (so it tries to fetch
// a jar), hence we use a full one.
withShaderPack(testWithIrisShadersResolve.get())
}
cct.jacoco(runGametestClientWithIris)