From 40309bc8acb7afb37bbc99d7a758c24ffd962b4c Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Fri, 19 Apr 2024 20:57:07 +0200 Subject: Backend: Add unimined build system (#1456) --- .gitignore | 2 + build.gradle.kts | 236 ++++++++++++++------- buildSrc/build.gradle.kts | 18 ++ buildSrc/src/AutoMixinDiscovery.kt | 50 +++++ buildSrc/src/EnvFile.kt | 12 ++ gradle.properties | 3 +- gradle/libs.versions.toml | 1 + log4j2.xml | 53 +++++ settings.gradle.kts | 9 +- .../skyhanni/mixins/init/SkyhanniMixinPlugin.java | 91 +------- .../skyhanni/utils/MinecraftConsoleFilter.kt | 13 +- 11 files changed, 298 insertions(+), 190 deletions(-) create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/AutoMixinDiscovery.kt create mode 100644 buildSrc/src/EnvFile.kt create mode 100644 log4j2.xml diff --git a/.gitignore b/.gitignore index d79aeba45..df98a38f7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ gradle.properties .devauth/* !.devauth/config.toml + +.env diff --git a/build.gradle.kts b/build.gradle.kts index 5503faaa7..f0553f106 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,12 +1,13 @@ -import org.apache.commons.lang3.SystemUtils import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import xyz.wagyourtail.unimined.api.minecraft.MinecraftConfig +import xyz.wagyourtail.unimined.api.minecraft.task.RemapJarTask +import xyz.wagyourtail.unimined.api.runs.RunConfig import java.io.ByteArrayOutputStream plugins { idea java - id("gg.essential.loom") version "0.10.0.+" - id("dev.architectury.architectury-pack200") version "0.1.3" + id("xyz.wagyourtail.unimined") version "1.2.0-SNAPSHOT" id("com.github.johnrengelman.shadow") version "7.1.2" kotlin("jvm") version "1.9.0" id("com.bnorm.power.kotlin-power-assert") version "0.13.0" @@ -38,6 +39,10 @@ sourceSets.main { kotlin.destinationDirectory.set(java.destinationDirectory) } +val patternSourceSet = sourceSets.create("pattern") { + this.runtimeClasspath += sourceSets.main.get().runtimeClasspath +} + repositories { mavenCentral() mavenLocal() @@ -51,15 +56,19 @@ repositories { maven("https://repo.nea.moe/releases") maven("https://maven.notenoughupdates.org/releases") maven("https://repo.hypixel.net/repository/Hypixel/") + ivy("https://repo1.maven.org/maven2/org/apache/logging/log4j") { + this.content { + this.includeGroup("log4jhack") + } + this.patternLayout { + this.artifact("log4j-[artifact]/[revision]/log4j-[artifact]-[revision].[ext]") + } + this.metadataSources { + this.artifact() + } + } } -val shadowImpl: Configuration by configurations.creating { - configurations.implementation.get().extendsFrom(this) -} - -val shadowModImpl: Configuration by configurations.creating { - configurations.modImplementation.get().extendsFrom(this) -} val devenvMod: Configuration by configurations.creating { isTransitive = false @@ -73,10 +82,125 @@ val headlessLwjgl by configurations.creating { val shot = shots.shot("minecraft", project.file("shots.txt")) +val modRuntimeOnly by configurations.creating { + configurations.runtimeOnly.get().extendsFrom(this) +} +val modCompileOnly by configurations.creating { + configurations.compileOnly.get().extendsFrom(this) +} + +fun RunConfig.setBaseConfig() { + val runDir = file("run").absoluteFile + this.javaVersion = JavaVersion.VERSION_1_8 + this.args.addAll( + listOf( + "--tweakClass", + "org.spongepowered.asm.launch.MixinTweaker", + "--tweakClass", + "io.github.notenoughupdates.moulconfig.tweaker.DevelopmentResourceTweaker", + "--mods", + devenvMod.resolve().joinToString(",") { it.relativeTo(runDir).path } + ) + ) + this.args.set( + this.args.indexOf("--gameDir") + 1, + runDir.absolutePath + ) + this.jvmArgs.removeIf { it.startsWith("-Xmx") || it.startsWith("-XX:") } + this.jvmArgs.addAll( + listOf( + "-Xmx4G", + "-Dmixin.debug=true", + "-Dlog4j.configurationFile=${project.file("log4j2.xml").absolutePath}" + ) + ) + this.workingDir = runDir + if (System.getenv("repo_action") != "true") { + this.jvmArgs.add("-Ddevauth.configDir=${rootProject.file(".devauth").absolutePath}") + } + this.env.putAll(parseEnvFile(file(".env"))) +} + +fun MinecraftConfig.defaultMinecraft() { + this.version("1.8.9") + this.mappings { + this.searge() + this.mcp("stable", "22-1.8.9") + } + this.minecraftForge { + this.loader("11.15.1.2318-1.8.9") + this.mixinConfig("mixins.skyhanni.json") + } +} + +unimined.minecraft { + this.defaultMinecraft() + this.runs { + this.config("client") { + this.setBaseConfig() + } + this.config("server") { + this.disabled = true + } + } + this.mods { + this.remap(modRuntimeOnly) + this.remap(devenvMod) + this.remap(modCompileOnly) + } +} + +unimined.minecraft(patternSourceSet) { + this.defaultMinecraft() + this.runs { + this.config("client") { + this.setBaseConfig() + this.jvmArgs.addAll( + listOf( + "-Dorg.lwjgl.opengl.Display.allowSoftwareOpenGL=true", + "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006", + "-javaagent:${headlessLwjgl.singleFile.absolutePath}" + ) + ) + val outputFile = project.file("build/regexes/constants.json") + this.env.put("SKYHANNI_DUMP_REGEXES", "${gitHash}:${outputFile.absolutePath}") + this.env.put("SKYHANNI_DUMP_REGEXES_EXIT", "true") + } + this.config("server") { + this.disabled = true + } + } +} +unimined.minecraft(sourceSets.test.get()) { + this.defaultMinecraft() + this.runs { + this.off = true + } +} +val modImplementation by configurations +val testModImplementation by configurations +val patternModImplementation by configurations + +testModImplementation.extendsFrom(modImplementation) +patternModImplementation.extendsFrom(testModImplementation) + +val shadowImpl: Configuration by configurations.creating { + configurations.implementation.get().extendsFrom(this) +} + +val shadowModImpl: Configuration by configurations.creating { + modImplementation.extendsFrom(this) + +} + +configurations.named("minecraftLibraries") { + this.resolutionStrategy { + this.force("org.apache.logging.log4j:log4j-core:2.8.1") + this.force("org.apache.logging.log4j:log4j-api:2.8.1") + } +} + dependencies { - minecraft("com.mojang:minecraft:1.8.9") - mappings("de.oceanlabs.mcp:mcp_stable:22-1.8.9") - forge("net.minecraftforge:forge:1.8.9-11.15.1.2318-1.8.9") // Discord RPC client shadowImpl("com.github.NetheriteMiner:DiscordIPC:3106be5") { @@ -92,14 +216,13 @@ dependencies { shadowImpl("org.spongepowered:mixin:0.7.11-SNAPSHOT") { isTransitive = false } - annotationProcessor("org.spongepowered:mixin:0.8.4-SNAPSHOT") implementation(kotlin("stdlib-jdk8")) shadowImpl("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") { exclude(group = "org.jetbrains.kotlin") } - modRuntimeOnly("me.djtheredstoner:DevAuth-forge-legacy:1.1.0") + modRuntimeOnly("me.djtheredstoner:DevAuth-forge-legacy:1.2.0") modCompileOnly("com.github.hannibal002:notenoughupdates:4957f0b:all") { exclude(module = "unspecified") @@ -124,9 +247,11 @@ dependencies { testImplementation("io.mockk:mockk:1.12.5") implementation("net.hypixel:mod-api:0.3.1") -} -configurations.getByName("minecraftNamed").dependencies.forEach { - shot.applyTo(it as HasConfigurableAttributes<*>) + + runtimeOnly(libs.terminalConsoleAppender) + // Manually load 2.0-beta.9 on the class path *after* loading 2.8.X, since forge uses some of the helper classes only available in this version. + runtimeOnly("log4jhack:api:2.0-beta9") + runtimeOnly("log4jhack:core:2.0-beta9") } tasks.withType(Test::class) { @@ -145,64 +270,19 @@ kotlin { } } -// Minecraft configuration: -loom { - launchConfigs { - "client" { - property("mixin.debug", "true") - if (System.getenv("repo_action") != "true") { - property("devauth.configDir", rootProject.file(".devauth").absolutePath) - } - arg("--tweakClass", "org.spongepowered.asm.launch.MixinTweaker") - arg("--tweakClass", "io.github.notenoughupdates.moulconfig.tweaker.DevelopmentResourceTweaker") - arg("--mods", devenvMod.resolve().joinToString(",") { it.relativeTo(file("run")).path }) - } - } - forge { - pack200Provider.set(dev.architectury.pack200.java.Pack200Adapter()) - mixinConfig("mixins.skyhanni.json") - } - @Suppress("UnstableApiUsage") - mixin { - defaultRefmapName.set("mixins.skyhanni.refmap.json") - } - runConfigs { - "client" { - if (SystemUtils.IS_OS_MAC_OSX) { - vmArgs.remove("-XstartOnFirstThread") - } - vmArgs.add("-Xmx4G") - } - "server" { - isIdeConfigGenerated = false - } - } -} - // Tasks: tasks.processResources { inputs.property("version", version) filesMatching("mcmod.info") { expand("version" to version) } + this.filesMatching("mixins.skyhanni.json") { + this.autoDiscoverMixins(sourceSets.main.get()) + } } -val generateRepoPatterns by tasks.creating(JavaExec::class) { - javaLauncher.set(javaToolchains.launcherFor(java.toolchain)) - mainClass.set("net.fabricmc.devlaunchinjector.Main") - workingDir(project.file("run")) - classpath(sourceSets.main.map { it.runtimeClasspath }, sourceSets.main.map { it.output }) - jvmArgs( - "-Dfabric.dli.config=${project.file(".gradle/loom-cache/launch.cfg").absolutePath}", - "-Dfabric.dli.env=client", - "-Dfabric.dli.main=net.minecraft.launchwrapper.Launch", - "-Dorg.lwjgl.opengl.Display.allowSoftwareOpenGL=true", - "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006", - "-javaagent:${headlessLwjgl.singleFile.absolutePath}" - ) - val outputFile = project.file("build/regexes/constants.json") - environment("SKYHANNI_DUMP_REGEXES", "${gitHash}:${outputFile.absolutePath}") - environment("SKYHANNI_DUMP_REGEXES_EXIT", "true") +val generateRepoPatterns by tasks.creating() { + afterEvaluate { dependsOn(tasks["patternRunClient"]) } } tasks.compileJava { @@ -215,6 +295,9 @@ tasks.withType(JavaCompile::class) { tasks.withType(Jar::class) { archiveBaseName.set("SkyHanni") + if (this.name != "remapJar") { + this.destinationDirectory.set(layout.buildDirectory.dir("badjars")) + } duplicatesStrategy = DuplicatesStrategy.EXCLUDE manifest.attributes.run { this["FMLCorePluginContainsFMLMod"] = "true" @@ -227,21 +310,14 @@ tasks.withType(Jar::class) { } -val remapJar by tasks.named("remapJar") { +val remapJar by tasks.named("remapJar") { archiveClassifier.set("") - from(tasks.shadowJar) - input.set(tasks.shadowJar.get().archiveFile) + inputFile.set(tasks.shadowJar.flatMap { it.archiveFile }) } tasks.shadowJar { - destinationDirectory.set(layout.buildDirectory.dir("badjars")) archiveClassifier.set("all-dev") configurations = listOf(shadowImpl, shadowModImpl) - doLast { - configurations.forEach { - println("Config: ${it.files}") - } - } exclude("META-INF/versions/**") mergeServiceFiles() relocate("io.github.notenoughupdates.moulconfig", "at.hannibal2.skyhanni.deps.moulconfig") @@ -249,9 +325,8 @@ tasks.shadowJar { } tasks.jar { archiveClassifier.set("nodeps") - destinationDirectory.set(layout.buildDirectory.dir("badjars")) } -tasks.assemble.get().dependsOn(tasks.remapJar) +tasks.assemble.get().dependsOn(remapJar) val compileKotlin: KotlinCompile by tasks compileKotlin.kotlinOptions { @@ -262,14 +337,13 @@ compileTestKotlin.kotlinOptions { jvmTarget = "1.8" } val sourcesJar by tasks.creating(Jar::class) { - destinationDirectory.set(layout.buildDirectory.dir("badjars")) archiveClassifier.set("src") from(sourceSets.main.get().allSource) } publishing.publications { create("maven") { - artifact(tasks.remapJar) + artifact(remapJar) artifact(sourcesJar) { classifier = "sources" } pom { name.set("SkyHanni") diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 000000000..15870b3ce --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,18 @@ + +plugins { + `kotlin-dsl` + kotlin("jvm")version "1.8.10" +} +repositories { + mavenCentral() +} + +dependencies { + implementation("com.google.code.gson:gson:2.10.1") +} + +sourceSets.main { + kotlin { + srcDir(file("src")) + } +} diff --git a/buildSrc/src/AutoMixinDiscovery.kt b/buildSrc/src/AutoMixinDiscovery.kt new file mode 100644 index 000000000..f66229030 --- /dev/null +++ b/buildSrc/src/AutoMixinDiscovery.kt @@ -0,0 +1,50 @@ +import com.google.gson.GsonBuilder +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import org.apache.tools.ant.filters.BaseParamFilterReader +import org.gradle.api.file.FileCopyDetails +import org.gradle.api.tasks.SourceSet +import java.io.File +import java.io.Reader +import java.io.StringReader + +class MixinFilterReader(reader: Reader) : BaseParamFilterReader() { + lateinit var sourceRoots: Collection + val gson = GsonBuilder().setPrettyPrinting().create() + val betterReader: StringReader by lazy { + StringReader(run { + val json = gson.fromJson(reader.readText(), JsonObject::class.java) + val mixinPackage = (json["package"] as JsonPrimitive).asString + val allMixins = (json["mixins"] as JsonArray?)?.map { it.asString }?.toMutableSet() ?: mutableSetOf() + sourceRoots + .forEach { base -> + base.walk() + .filter { it.isFile } + .forEach { + val relativeString = it.toRelativeString(base).replace("\\", "/") + if (relativeString.startsWith(mixinPackage.replace(".", "/") + "/") + && relativeString.endsWith(".java") + && it.readText().contains("@Mixin") + ) + allMixins.add( + relativeString.replace("/", ".").dropLast(5).drop(mixinPackage.length + 1) + ) + } + } + json.add("mixins", JsonArray().also { jsonAllMixins -> + allMixins.forEach { jsonAllMixins.add(it) } + }) + gson.toJson(json) + }) + + } + + override fun read(): Int { + return betterReader.read() + } +} + +fun FileCopyDetails.autoDiscoverMixins(sourceSet: SourceSet) { + filter(mapOf("sourceRoots" to sourceSet.allSource.srcDirs), MixinFilterReader::class.java) +} diff --git a/buildSrc/src/EnvFile.kt b/buildSrc/src/EnvFile.kt new file mode 100644 index 000000000..76d6fa959 --- /dev/null +++ b/buildSrc/src/EnvFile.kt @@ -0,0 +1,12 @@ +import java.io.File + +fun parseEnvFile(file: File): Map { + if (!file.exists()) return mapOf() + val map = mutableMapOf() + for (line in file.readText().lines()) { + if (line.isEmpty() || line.startsWith("#")) continue + val parts = line.split("=", limit = 2) + map[parts[0]] = parts.getOrNull(1) ?: "" + } + return map +} diff --git a/gradle.properties b/gradle.properties index 065574755..d7a34a502 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1 @@ -loom.platform=forge -org.gradle.jvmargs=-Xmx2g \ No newline at end of file +org.gradle.jvmargs=-Xmx2g diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 634e49064..6d6193970 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,3 +10,4 @@ libautoupdate = { module = "moe.nea:libautoupdate", version.ref = "libautoupdate headlessLwjgl = { module = "com.github.3arthqu4ke.HeadlessMc:headlessmc-lwjgl", version.ref = "headlessLwjgl" } jbAnnotations = { module = "org.jetbrains:annotations", version.ref = "jbAnnotations" } hotswapagentforge = { module = "moe.nea:hotswapagent-forge", version = "1.0.1" } +terminalConsoleAppender = { module = "net.minecrell:terminalconsoleappender", version = "1.3.0" } diff --git a/log4j2.xml b/log4j2.xml new file mode 100644 index 000000000..b37b4235d --- /dev/null +++ b/log4j2.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/settings.gradle.kts b/settings.gradle.kts index 12734a34a..5004d7675 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,13 +9,8 @@ pluginManagement { maven("https://repo.spongepowered.org/maven/") maven("https://repo.nea.moe/releases") maven("https://repo.sk1er.club/repository/maven-releases/") - } - resolutionStrategy { - eachPlugin { - when (requested.id.id) { - "gg.essential.loom" -> useModule("gg.essential:architectury-loom:${requested.version}") - } - } + maven("https://maven.wagyourtail.xyz/releases") + maven("https://maven.wagyourtail.xyz/snapshots") } } diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/init/SkyhanniMixinPlugin.java b/src/main/java/at/hannibal2/skyhanni/mixins/init/SkyhanniMixinPlugin.java index 1f1b0e1fe..eb4009ce5 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/init/SkyhanniMixinPlugin.java +++ b/src/main/java/at/hannibal2/skyhanni/mixins/init/SkyhanniMixinPlugin.java @@ -4,24 +4,13 @@ import org.spongepowered.asm.lib.tree.ClassNode; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Set; -import java.util.stream.Stream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; public class SkyhanniMixinPlugin implements IMixinConfigPlugin { @Override public void onLoad(String mixinPackage) { - } @Override @@ -31,7 +20,7 @@ public class SkyhanniMixinPlugin implements IMixinConfigPlugin { @Override public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { - return false; + return true; } @Override @@ -39,85 +28,9 @@ public class SkyhanniMixinPlugin implements IMixinConfigPlugin { } - public URL baseUrl(URL classUrl) { - String string = classUrl.toString(); - if (classUrl.getProtocol().equals("jar")) { - try { - return new URL(string.substring(4).split("!")[0]); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - } - if (string.endsWith(".class")) { - try { - return new URL(string.replace("\\", "/") - .replace(getClass().getCanonicalName().replace(".", "/") + ".class", "")); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - } - return classUrl; - } - - String mixinBasePackage = "at.hannibal2.skyhanni.mixins.transformers."; - String mixinBaseDir = mixinBasePackage.replace(".", "/"); - - List mixins = null; - - public void tryAddMixinClass(String className) { - String norm = (className.endsWith(".class") ? className.substring(0, className.length() - ".class".length()) : className) - .replace("\\", "/") - .replace("/", "."); - if (norm.startsWith(mixinBasePackage) && !norm.endsWith(".")) { - mixins.add(norm.substring(mixinBasePackage.length())); - } - } - - public void walkDir(Path file) { - System.out.println("Trying to find mixins from directory"); - try (Stream classes = Files.walk(file.resolve(mixinBaseDir))) { - classes.map(it -> file.relativize(it).toString()) - .forEach(this::tryAddMixinClass); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - @Override public List getMixins() { - if (mixins != null) return mixins; - System.out.println("Trying to discover mixins"); - mixins = new ArrayList<>(); - URL classUrl = getClass().getProtectionDomain().getCodeSource().getLocation(); - System.out.println("Found classes at " + classUrl); - Path file; - try { - file = Paths.get(baseUrl(classUrl).toURI()); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - System.out.println("Base directory found at " + file); - if (Files.isDirectory(file)) { - walkDir(file); - } else { - walkJar(file); - } - System.out.println("Found mixins: " + mixins); - - return mixins; - } - - private void walkJar(Path file) { - System.out.println("Trying to find mixins from jar file"); - try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(file))) { - ZipEntry next; - while ((next = zis.getNextEntry()) != null) { - tryAddMixinClass(next.getName()); - zis.closeEntry(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } + return new ArrayList<>(); } @Override diff --git a/src/main/java/at/hannibal2/skyhanni/utils/MinecraftConsoleFilter.kt b/src/main/java/at/hannibal2/skyhanni/utils/MinecraftConsoleFilter.kt index 4022b382c..5de464872 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/MinecraftConsoleFilter.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/MinecraftConsoleFilter.kt @@ -11,9 +11,10 @@ import org.apache.logging.log4j.core.Filter import org.apache.logging.log4j.core.LogEvent import org.apache.logging.log4j.core.Logger import org.apache.logging.log4j.core.LoggerContext +import org.apache.logging.log4j.core.filter.AbstractFilter import org.apache.logging.log4j.message.Message -class MinecraftConsoleFilter(private val loggerConfigName: String) : Filter { +class MinecraftConsoleFilter(private val loggerConfigName: String) : AbstractFilter(Filter.Result.ACCEPT, Filter.Result.DENY) { private val config get() = SkyHanniMod.feature.dev.minecraftConsoles private val filterConfig get() = config.consoleFilter @@ -35,8 +36,6 @@ class MinecraftConsoleFilter(private val loggerConfigName: String) : Filter { } } - // prevents error sending on every shutdown - fun stop() {} override fun filter(event: LogEvent?): Filter.Result { if (event == null) return Filter.Result.ACCEPT @@ -205,14 +204,6 @@ class MinecraftConsoleFilter(private val loggerConfigName: String) : Filter { } } - override fun getOnMismatch(): Filter.Result { - return Filter.Result.DENY - } - - override fun getOnMatch(): Filter.Result { - return Filter.Result.ACCEPT - } - override fun filter( logger: Logger?, level: Level?, -- cgit