buildscript { repositories { maven { name "Fabric Repository" url 'https://maven.fabricmc.net' } mavenCentral() mavenLocal() } dependencies { classpath "cuchaz:enigma-cli:${project.enigma_version}" classpath "net.fabricmc:stitch:${project.stitch_version}" classpath "net.fabricmc:name-proposal:${project.name_proposal_version}" } } plugins { id 'java' // for constants, packages, javadoc id 'maven-publish' id "com.diffplug.spotless" version "6.17.0" id 'net.fabricmc.filament' } def minecraft_version = "23w14a" filament { minecraftVersion = minecraft_version } def ENV = System.getenv() // Fetch build number from Github Actions def build_number = ENV.BUILD_NUMBER ?: "local" def yarnVersion = "${minecraft_version}+build.$build_number" version = yarnVersion if (ENV.BRANCH_NAME) { def branch = ENV.BRANCH_NAME.substring(ENV.BRANCH_NAME.lastIndexOf('/') + 1) if (minecraft_version != branch) { throw new IllegalStateException("Branch name (${branch}) does not match the mc version (${minecraft_version})") } } repositories { mavenCentral() maven { name "Fabric Repository" url 'https://maven.fabricmc.net' } maven { name "Mojang" url 'https://libraries.minecraft.net/' } mavenLocal() } configurations { asm enigmaRuntime { extendsFrom asm } javadocClasspath decompileClasspath mappingPoetJar { transitive = false } mappingPoet { extendsFrom mappingPoetJar extendsFrom asm transitive = true } } def unpickMetaFile = file("unpick-definitions/unpick.json") dependencies { enigmaRuntime "cuchaz:enigma-swing:${project.enigma_version}" enigmaRuntime "net.fabricmc:name-proposal:${project.name_proposal_version}" javadocClasspath "net.fabricmc:fabric-loader:${project.fabric_loader_version}" javadocClasspath "org.jetbrains:annotations:${project.jetbrains_annotations_version}" javadocClasspath "com.google.code.findbugs:jsr305:3.0.2" // for some other jsr annotations decompileClasspath "net.fabricmc:cfr:${project.cfr_version}" mappingPoetJar "net.fabricmc:mappingpoet:${project.mappingpoet_version}" asm "org.ow2.asm:asm:${project.asm_version}" asm "org.ow2.asm:asm-tree:${project.asm_version}" asm "org.ow2.asm:asm-commons:${project.asm_version}" asm "org.ow2.asm:asm-util:${project.asm_version}" } def yarnGroup = "yarn" def buildMappingGroup = "mapping build" def mapJarGroup = "jar mapping" def mappingsDir = file("mappings") def tempDir = file("build/temp/yarn") def cacheFilesMinecraft = new File(tempDir, "minecraft") def libs = new File("build/libs/") def minecraftLibraries = configurations.minecraftLibraries import cuchaz.enigma.command.CheckMappingsCommand import net.fabricmc.filament.task.MapJarTask import net.fabricmc.filament.task.DownloadTask import net.fabricmc.filament.task.ExtractZipEntryTask import net.fabricmc.filament.task.UnpickJarTask import net.fabricmc.filament.task.base.WithFileInput import net.fabricmc.filament.task.base.WithFileOutput import net.fabricmc.filament.task.enigma.ConvertMappingsTask import net.fabricmc.filament.task.enigma.MapSpecializedMethodsTask import net.fabricmc.stitch.commands.CommandMergeTiny import net.fabricmc.stitch.commands.CommandReorderTiny import net.fabricmc.stitch.commands.tinyv2.CommandMergeTinyV2 import net.fabricmc.stitch.commands.tinyv2.CommandReorderTinyV2 import net.fabricmc.nameproposal.MappingNameCompleter import org.gradle.work.DisableCachingByDefault import java.util.zip.GZIPOutputStream task downloadIntermediary(type: DownloadTask) { group = buildMappingGroup url = "https://github.com/FabricMC/intermediary/raw/master/mappings/${minecraft_version}.tiny" output = new File(cacheFilesMinecraft, "${minecraft_version}-intermediary.tiny") } task downloadIntermediaryV2(type: DownloadTask) { group = buildMappingGroup url = "https://maven.fabricmc.net/net/fabricmc/intermediary/${minecraft_version}/intermediary-${minecraft_version}-v2.jar" output = new File(cacheFilesMinecraft, "${minecraft_version}-intermediary-v2.jar") } task extractIntermediaryV2(type: ExtractZipEntryTask) { input = downloadIntermediaryV2.output output = new File(cacheFilesMinecraft, "${minecraft_version}-intermediary-v2.tiny") entry = "mappings/mappings.tiny" } task invertIntermediary(dependsOn: downloadIntermediary, type: FileInputOutput) { group = buildMappingGroup input = downloadIntermediary.output output = new File(cacheFilesMinecraft, "${minecraft_version}-intermediary-inverted.tiny") doLast { String[] v1Args = [ inputFile.getAbsolutePath(), outputFile.getAbsolutePath(), "intermediary", "official" ] new CommandReorderTiny().run(v1Args) } } task invertIntermediaryv2(dependsOn: [downloadIntermediaryV2, extractIntermediaryV2], type: FileInputOutput) { group = buildMappingGroup output = new File(cacheFilesMinecraft, "${minecraft_version}-intermediary-inverted-v2.tiny") input = extractIntermediaryV2.output doLast { String[] v2Args = [ inputFile.getAbsolutePath(), outputFile.getAbsolutePath(), "intermediary", "official" ] new CommandReorderTinyV2().run(v2Args) } } task mapIntermediaryJar(type: MapJarTask, dependsOn: [downloadIntermediary, mergeMinecraftJars]) { group = mapJarGroup output = file("${minecraft_version}-intermediary.jar") input = mergeMinecraftJars.output mappings = downloadIntermediary.output classpath.from minecraftLibraries from = 'official' to = 'intermediary' } task mapServerIntermediaryJar(type: MapJarTask, dependsOn: [downloadIntermediary, extractBundledServer]) { group = mapJarGroup output = file("${minecraft_version}-server-intermediary.jar") input = extractBundledServer.output mappings = downloadIntermediary.output classpath.from minecraftLibraries from = 'official' to = 'intermediary' } task yarn(dependsOn: mapIntermediaryJar, type: EnigmaTask) { group = yarnGroup jar = mapIntermediaryJar.output mappings = mappingsDir } task yarnCommon(type: EnigmaTask) { group = yarnGroup jar = mapServerIntermediaryJar.output mappings = mappingsDir } task checkMappings() { group = buildMappingGroup inputs.dir mappingsDir inputs.file mapIntermediaryJar.output def intermediaryJarPath = mapIntermediaryJar.outputFile.absolutePath doLast { String[] args = [ intermediaryJarPath, mappingsDir.getAbsolutePath() ] try { new CheckMappingsCommand().run(args) } catch (IllegalStateException ignored) { // just print, don't fail the task } } } task mapSpecializedMethods(type: MapSpecializedMethodsTask) { intermediaryJarFile = mapIntermediaryJar.output mappings = mappingsDir output = new File(tempDir, "yarn-mappings-v2.tiny") inputMappingsFormat = "enigma" outputMappingsFormat = "tinyv2:intermediary:named" } task convertToV1(type: ConvertMappingsTask) { input = mapSpecializedMethods.output output = new File(tempDir, "yarn-mappings.tiny") inputMappingsFormat = "tinyv2" outputMappingsFormat = "tiny:intermediary:named" } task mergeTiny(dependsOn: ["convertToV1", "invertIntermediary"], type: FileOutput) { group = buildMappingGroup def unorderedResultMappings = new File(tempDir, "mappings-unordered.tiny") outputs.file unorderedResultMappings output = new File(tempDir, "mappings.tiny") inputs.file convertToV1.output inputs.file invertIntermediary.output def intermediaryTinyPath = invertIntermediary.outputFile.absolutePath def yarnTinyPath = convertToV1.outputFile.absolutePath def outputPath = unorderedResultMappings.absolutePath doLast { String[] args = [ intermediaryTinyPath, yarnTinyPath, outputPath, "intermediary", "official" ] new CommandMergeTiny().run(args) String[] args2 = [ unorderedResultMappings.getAbsolutePath(), output.getAbsolutePath(), "official", "intermediary", "named" ] new CommandReorderTiny().run(args2) } } // Disable the default jar task jar { enabled = false } task tinyJar(type: Jar, dependsOn: mergeTiny) { group = buildMappingGroup archiveFileName = "yarn-${yarnVersion}.jar" destinationDirectory.set(file("build/libs")) archiveClassifier = "" from(mergeTiny.output) { rename { "mappings/mappings.tiny" } } manifest { attributes("Minecraft-Version-Id": minecraft_version) } } task compressTiny(dependsOn: [tinyJar, mergeTiny], type: FileInputOutput) { group = buildMappingGroup input = mergeTiny.output output = new File(libs, "yarn-tiny-${yarnVersion}.gz") doLast { def buffer = new byte[1024] def fileOutputStream = new FileOutputStream(outputFile) def outputStream = new GZIPOutputStream(fileOutputStream) def fileInputStream = new FileInputStream(inputFile) def length while ((length = fileInputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length) } fileInputStream.close() outputStream.finish() outputStream.close() } } clean.doFirst { delete tempDir, cacheFilesMinecraft } sourceSets { constants packageDocs // package info files } task constantsJar(type: Jar) { from sourceSets.constants.output archiveClassifier = "constants" } tasks.build.dependsOn "compressTiny","tinyJar","v2UnmergedYarnJar", "v2MergedYarnJar", "javadocJar" combineUnpickDefinitions { group = 'unpick' input = file('unpick-definitions') output = new File(tempDir, 'definitions.unpick') } // Setup the build for the unpicked constants java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } tasks.withType(JavaCompile).configureEach { it.options.encoding = "UTF-8" it.options.release = 17 } spotless { lineEndings = com.diffplug.spotless.LineEnding.UNIX java { licenseHeaderFile(rootProject.file("HEADER")) } } task sourcesJar(type: Jar, dependsOn: classes) { archiveClassifier = "sources" from sourceSets.constants.allSource } // Only build jars for package infos if we need to actually expose stuff like annotation in the future. build.dependsOn constantsJar task insertAutoGeneratedEnumMappings(dependsOn : [mapIntermediaryJar, downloadIntermediary], type : FileInputOutput){ group = buildMappingGroup input = mapSpecializedMethods.output output = new File(tempDir, "unmerged-named-v2-with-enum.tiny") inputs.file mapIntermediaryJar.output inputs.file downloadIntermediary.output def intermediaryJarPath = mapIntermediaryJar.outputPath def intermediaryMappingsPath = downloadIntermediary.outputPath doLast { MappingNameCompleter.completeNames( intermediaryJarPath, inputPath, intermediaryMappingsPath, outputPath ) } } remapUnpickDefinitionsIntermediary { group = 'unpick' output = new File(tempDir, 'intermediary-definitions.unpick') mappings = insertAutoGeneratedEnumMappings.output } task unpickIntermediaryJar(type: UnpickJarTask) { group = 'unpick' input = mapIntermediaryJar.output output = file("${minecraft_version}-intermediary-unpicked.jar") unpickDefinition = remapUnpickDefinitionsIntermediary.output constantsJarFile = constantsJar.archiveFile classpath.from minecraftLibraries } task yarnUnpicked(dependsOn: "unpickIntermediaryJar", type: EnigmaTask) { group = yarnGroup jar = unpickIntermediaryJar.output mappings = mappingsDir } task mergeV2(dependsOn: ["v2UnmergedYarnJar"], type: FileOutput) { def mergedV2 = new File(tempDir, "merged-v2.tiny"); outputs.file mergedV2 output = new File(tempDir, "merged-reordered-v2.tiny") inputs.file insertAutoGeneratedEnumMappings.output inputs.file invertIntermediaryv2.output def intermediaryPath = invertIntermediaryv2.outputFile.absolutePath def mappingsPath = insertAutoGeneratedEnumMappings.outputFile.absolutePath doLast { String[] args = [ intermediaryPath, mappingsPath, mergedV2.getAbsolutePath(), "intermediary", "official" ] new CommandMergeTinyV2().run(args) //Reorder the mappings to match the output of loom args = [ mergedV2.getAbsolutePath(), output.getAbsolutePath(), "official", "intermediary", "named" ] new CommandReorderTinyV2().run(args) } } task v2UnmergedYarnJar(dependsOn: [insertAutoGeneratedEnumMappings, combineUnpickDefinitions], type: Jar) { def mappings = insertAutoGeneratedEnumMappings.output group = "mapping build" archiveFileName = "yarn-${yarnVersion}-v2.jar" from(file(mappings)) { rename mappings.get().asFile.name, "mappings/mappings.tiny" } from(combineUnpickDefinitions.output) { rename combineUnpickDefinitions.output.get().asFile.name, "extras/definitions.unpick" } from (file(unpickMetaFile)) { expand version: project.unpick_version rename unpickMetaFile.name, "extras/unpick.json" } destinationDirectory.set(file("build/libs")) manifest { attributes("Minecraft-Version-Id": minecraft_version) } } task v2MergedYarnJar(dependsOn: ["mergeV2"], type: Jar) { def mappings = mergeV2.output group = "mapping build" archiveFileName = "yarn-${yarnVersion}-mergedv2.jar" from(file(mappings)) { rename mappings.name, "mappings/mappings.tiny" } from(combineUnpickDefinitions.output) { rename combineUnpickDefinitions.output.get().asFile.name, "extras/definitions.unpick" } from (file(unpickMetaFile)) { expand version: project.unpick_version rename unpickMetaFile.name, "extras/unpick.json" } destinationDirectory.set(file("build/libs")) manifest { attributes("Minecraft-Version-Id": minecraft_version) } } task mapNamedJar(type: MapJarTask, dependsOn: ["mergeV2", "unpickIntermediaryJar"]) { group = mapJarGroup output = file("${minecraft_version}-named.jar") input = unpickIntermediaryJar.output mappings = mergeV2.output classpath.from minecraftLibraries from = 'intermediary' to = 'named' classMappings = [ "javax/annotation/Nullable": "org/jetbrains/annotations/Nullable", "javax/annotation/Nonnull": "org/jetbrains/annotations/NotNull", "javax/annotation/concurrent/Immutable": "org/jetbrains/annotations/Unmodifiable" ] } def mcLibsDir = file('build/tmp/mclibs') // Task to copy all the mc libs into a single directory. task syncDependencies(type: Sync) { from minecraftLibraries into mcLibsDir } def fakeSourceDir = file(".gradle/temp/fakeSource") task genFakeSource(type: JavaExec, dependsOn: ["mergeV2", "mapNamedJar", syncDependencies]) { group = "javadoc generation" inputs.file mergeV2.output inputs.file mapNamedJar.output inputs.dir mcLibsDir outputs.dir fakeSourceDir mainClass = "net.fabricmc.mappingpoet.Main" classpath configurations.mappingPoet // use merged v2 so we have all namespaces in jd args mergeV2.output.getAbsolutePath(), mapNamedJar.outputFile.getAbsolutePath(), fakeSourceDir.getAbsolutePath(), mcLibsDir.getAbsolutePath() } task decompileCFR(type: JavaExec, dependsOn: [mapNamedJar]) { mainClass = "org.benf.cfr.reader.Main" args mapNamedJar.outputFile.getAbsolutePath(), "--outputdir", file("namedSrc").absolutePath doFirst { file("namedSrc").deleteDir() classpath.from configurations.decompileClasspath classpath.from minecraftLibraries } } javadoc { dependsOn genFakeSource group = "javadoc generation" def mappingPoetJar = project.provider { zipTree configurations.mappingPoetJar.singleFile } failOnError = false maxMemory = '2G' // verbose = true // enable to debug options { // verbose() // enable to debug source = "17" encoding = 'UTF-8' charSet = 'UTF-8' memberLevel = JavadocMemberLevel.PRIVATE splitIndex true tags( 'apiNote:a:API Note:', 'implSpec:a:Implementation Requirements:', 'implNote:a:Implementation Note:' ) taglets "net.fabricmc.mappingpoet.jd.MappingTaglet" // taglet path, header, extra stylesheet settings deferred it.use() addBooleanOption "-allow-script-in-comments", true addBooleanOption "-ignore-source-errors", true links( 'https://guava.dev/releases/31.1-jre/api/docs/', 'https://www.javadoc.io/doc/com.google.code.gson/gson/2.10/', 'https://logging.apache.org/log4j/2.x/log4j-api/apidocs/', 'https://www.slf4j.org/apidocs/', "https://javadoc.io/doc/org.jetbrains/annotations/${project.jetbrains_annotations_version}/", 'https://javadoc.lwjgl.org/', 'https://fastutil.di.unimi.it/docs/', 'https://javadoc.scijava.org/JOML/', 'https://netty.io/4.1/api/', 'https://www.oshi.ooo/oshi-core-java11/apidocs/', 'https://java-native-access.github.io/jna/5.12.1/javadoc/', 'https://unicode-org.github.io/icu-docs/apidoc/released/icu4j/', 'https://jopt-simple.github.io/jopt-simple/apidocs/', 'https://solutions.weblite.ca/java-objective-c-bridge/docs/', 'https://commons.apache.org/proper/commons-logging/apidocs/', 'https://commons.apache.org/proper/commons-lang/javadocs/api-release/', 'https://commons.apache.org/proper/commons-io/apidocs/', 'https://commons.apache.org/proper/commons-codec/archives/1.15/apidocs/', 'https://commons.apache.org/proper/commons-compress/apidocs/', 'https://hc.apache.org/httpcomponents-client-4.5.x/current/httpclient/apidocs/', "https://maven.fabricmc.net/docs/fabric-loader-${project.fabric_loader_version}/", "https://docs.oracle.com/en/java/javase/17/docs/api/" ) // https://docs.oracle.com/en/java/javase/17/docs/specs/man/javadoc.html#additional-options-provided-by-the-standard-doclet addBooleanOption 'Xdoclint:html', true addBooleanOption 'Xdoclint:syntax', true addBooleanOption 'Xdoclint:reference', true addBooleanOption 'Xdoclint:accessibility', true } source fileTree(fakeSourceDir) + sourceSets.constants.allJava + sourceSets.packageDocs.allJava classpath = configurations.javadocClasspath.plus minecraftLibraries def fs = services.get(FileSystemOperations.class) def outputDir = javadoc.outputDirectory doLast { fs.copy { from mappingPoetJar include "copy_on_click.js" into outputDir } } def tagletClasspath = configurations.mappingPoet.files.toList() doFirst { // lazy setting options { tagletPath tagletClasspath header mappingPoetJar.get().filter { it.name == 'javadoc_header.txt' }.singleFile.text.trim() // cannot include line breaks addFileOption "-add-stylesheet", mappingPoetJar.get().filter { it.name == 'forms.css' }.singleFile } } } task javadocJar(type: Jar, dependsOn: ["javadoc"]) { group = "javadoc generation" from javadoc.destinationDir archiveVersion.set yarnVersion archiveClassifier = 'javadoc' } generatePackageInfoMappings { inputJar = mapIntermediaryJar.output packageName = "net/minecraft/unused/packageinfo/" outputDir = file("mappings/net/minecraft/unused/packageinfo") } javadocLint { group = "javadoc generation" mappingDirectory = file("mappings") } check.dependsOn javadocLint publishing { publications { maven(MavenPublication) { groupId 'net.fabricmc' artifactId "yarn" version yarnVersion artifact(compressTiny.output) { classifier "tiny" builtBy compressTiny } artifact(tinyJar) artifact(v2UnmergedYarnJar) { classifier "v2" } artifact(v2MergedYarnJar) { classifier "mergedv2" } artifact(constantsJar) { classifier "constants" } artifact sourcesJar artifact javadocJar } } repositories { if (ENV.MAVEN_URL) { maven { url ENV.MAVEN_URL credentials { username ENV.MAVEN_USERNAME password ENV.MAVEN_PASSWORD } } } } } // A task to ensure that the version being released has not already been released. task checkVersion { doFirst { def xml = new URL("https://maven.fabricmc.net/net/fabricmc/yarn/maven-metadata.xml").text def metadata = new XmlSlurper().parseText(xml) def versions = metadata.versioning.versions.version*.text(); if (versions.contains(version)) { throw new RuntimeException("${version} has already been released!") } } } publish.mustRunAfter checkVersion abstract class FileOutput extends DefaultTask { @OutputFile File output } abstract class FileInputOutput extends DefaultTask implements WithFileInput, WithFileOutput { } @DisableCachingByDefault abstract class EnigmaTask extends JavaExec { @InputFile abstract RegularFileProperty getJar() @Input abstract Property getMappings() EnigmaTask() { classpath = project.configurations.enigmaRuntime mainClass.set('cuchaz.enigma.gui.Main') } @TaskAction void exec() { args '-jar' args jar.get().asFile.absolutePath args '-mappings' args mappings.get().absolutePath args '-profile' args 'enigma_profile.json' jvmArgs "-Xmx2048m" super.exec() } }