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 "commons-io:commons-io:2.8.0" classpath 'de.undercouch:gradle-download-task:4.1.1' classpath 'net.fabricmc:tiny-remapper:0.4.2' classpath "net.fabricmc.unpick:unpick:${project.unpick_version}" classpath "net.fabricmc.unpick:unpick-format-utils:${project.unpick_version}" } } plugins { id 'java' // for constants, packages, javadoc id 'de.undercouch.download' version '4.1.1' id 'maven-publish' id 'org.cadixdev.licenser' version '0.6.1' id 'net.fabricmc.filament' version '0.3.0' } def minecraft_version = "1.17.1" 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 { enigmaRuntime { resolutionStrategy { cacheDynamicVersionsFor 0, "seconds" cacheChangingModulesFor 0, "seconds" } } javadocClasspath decompileClasspath mappingPoetJar { transitive = false } mappingPoet { extendsFrom mappingPoetJar transitive = true } unpick } def unpickMetaFile = file("unpick-definitions/unpick.json") dependencies { enigmaRuntime "cuchaz:enigma-swing:${project.enigma_version}" enigmaRuntime "net.fabricmc:stitch:${project.stitch_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}" unpick "net.fabricmc.unpick:unpick-cli:${project.unpick_version}" } def setupGroup = "jar setup" def yarnGroup = "yarn" def buildMappingGroup = "mapping build" def mapJarGroup = "jar mapping" def mappingsDir = file("mappings") def cacheFilesMinecraft = file(".gradle/minecraft") def tempDir = file(".gradle/temp") def mergedFile = file("${minecraft_version}-merged.jar") def intermediaryJar = file("${minecraft_version}-intermediary.jar") def unpickedJar = file("${minecraft_version}-intermediary-unpicked.jar") def namedJar = file("${minecraft_version}-named.jar") def versionFile = new File(cacheFilesMinecraft, "${minecraft_version}.json") def clientJar = new File(cacheFilesMinecraft, "${minecraft_version}-client.jar") def serverJar = new File(cacheFilesMinecraft, "${minecraft_version}-server.jar") def libraries = new File(cacheFilesMinecraft, "libraries") def libs = new File("build/libs/") import com.google.common.hash.Hashing import cuchaz.enigma.command.CheckMappingsCommand import cuchaz.enigma.command.ComposeMappingsCommand import cuchaz.enigma.command.ConvertMappingsCommand import cuchaz.enigma.command.MapSpecializedMethodsCommand import groovy.io.FileType import groovy.json.JsonSlurper import net.fabricmc.stitch.commands.CommandMergeTiny import net.fabricmc.stitch.commands.CommandProposeFieldNames import net.fabricmc.stitch.commands.CommandReorderTiny import net.fabricmc.stitch.commands.CommandRewriteIntermediary import net.fabricmc.stitch.commands.tinyv2.CommandMergeTinyV2 import net.fabricmc.stitch.commands.tinyv2.CommandProposeV2FieldNames import net.fabricmc.stitch.commands.tinyv2.CommandReorderTinyV2 import net.fabricmc.stitch.merge.JarMerger import net.fabricmc.tinyremapper.OutputConsumerPath import net.fabricmc.tinyremapper.TinyRemapper import net.fabricmc.tinyremapper.TinyUtils import org.apache.commons.io.FileUtils import java.nio.charset.StandardCharsets import java.util.zip.GZIPOutputStream boolean validateChecksum(File file, String checksum) { if (file != null) { def hash = com.google.common.io.Files.asByteSource(file).hash(Hashing.sha1()) def builder = new StringBuilder() hash.asBytes().each { builder.append(Integer.toString((it & 0xFF) + 0x100, 16).substring(1)) } return builder.toString().equals(checksum) } return false } task downloadVersionsManifest { group = setupGroup //inputs.property "mc_ver", minecraft_version inputs.property "currenttime", new Date() def manifestFile = new File(cacheFilesMinecraft, "version_manifest_v2.json") outputs.file(manifestFile) doLast { logger.lifecycle(":downloading minecraft versions manifest") FileUtils.copyURLToFile(new URL("https://launchermeta.mojang.com/mc/game/version_manifest_v2.json"), manifestFile) } } def getManifestVersion(File manifestFile, String minecraft_version) { def manifest = manifestFile.exists() ? new JsonSlurper().parseText(FileUtils.readFileToString(manifestFile)) : null return manifest != null ? manifest.versions.stream().filter({ it.id.equals(minecraft_version) }).findFirst() : java.util.Optional.empty() } task downloadWantedVersionManifest(dependsOn: downloadVersionsManifest) { group = setupGroup def manifestFile = downloadVersionsManifest.outputs.files.singleFile def manifestVersion = getManifestVersion(manifestFile, minecraft_version) //have to grab the release time as there's a current timestamp on each element?! inputs.property "releaseTime", manifestVersion.isPresent() ? manifestVersion.get().releaseTime : -1 outputs.file versionFile doLast { manifestVersion = getManifestVersion(manifestFile, minecraft_version) //nb need to re-read here in case it didn't exist before if (manifestVersion.isPresent() || versionFile.exists()) { if (manifestVersion.isPresent()) { FileUtils.copyURLToFile(new URL(manifestVersion.get().url), versionFile) } } else { throw new RuntimeException("No version data for Minecraft version ${minecraft_version}") } } } task downloadMcJars(dependsOn: downloadWantedVersionManifest) { group = setupGroup inputs.files versionFile outputs.files(clientJar, serverJar) outputs.upToDateWhen { def version = new JsonSlurper().parseText(FileUtils.readFileToString(versionFile)) return clientJar.exists() && serverJar.exists() && validateChecksum(clientJar, version.downloads.client.sha1) && validateChecksum(serverJar, version.downloads.server.sha1) } doLast { if (!versionFile.exists()) { throw new RuntimeException("Can't download the jars without the ${versionFile.name} file!") } //reload in case it changed def version = new JsonSlurper().parseText(FileUtils.readFileToString(versionFile)) logger.lifecycle(":downloading minecraft jars") download { src new URL(version.downloads.client.url) dest clientJar overwrite false } download { src new URL(version.downloads.server.url) dest serverJar overwrite false } } } task downloadIntermediary(type: Download) { group = buildMappingGroup def url = "https://github.com/FabricMC/intermediary/raw/master/mappings/${minecraft_version}.tiny" src com.google.common.net.UrlEscapers.urlFragmentEscaper().escape(url) dest new File(cacheFilesMinecraft, "${minecraft_version}-intermediary.tiny") } task downloadIntermediaryV2(type: Download) { group = buildMappingGroup def url = "https://maven.fabricmc.net/net/fabricmc/intermediary/${minecraft_version}/intermediary-${minecraft_version}-v2.jar" src com.google.common.net.UrlEscapers.urlFragmentEscaper().escape(url) dest new File(cacheFilesMinecraft, "${minecraft_version}-intermediary-v2.jar") def output = new File(cacheFilesMinecraft, "${minecraft_version}-intermediary-v2.tiny") outputs.file output doLast { copy { from({ zipTree(downloadIntermediaryV2.dest) }) { from 'mappings/mappings.tiny' rename 'mappings.tiny', "../${output.name}" } into output.parentFile } } } task mergeJars(dependsOn: downloadMcJars) { group = setupGroup inputs.files downloadMcJars.outputs.files.files outputs.file(mergedFile) doLast { logger.lifecycle(":merging jars") def client = inputs.files.files.find { it.name.endsWith("-client.jar") } def server = inputs.files.files.find { it.name.endsWith("-server.jar") } def merged = mergedFile if (merged.exists()) { return } def jarMerger = new JarMerger(client, server, merged) jarMerger.merge() jarMerger.close() } } task downloadMcLibs(dependsOn: downloadWantedVersionManifest) { group = setupGroup inputs.files versionFile outputs.dir(libraries) outputs.upToDateWhen { false } doLast { if (!versionFile.exists()) { throw new RuntimeException("Can't download the jars without the ${versionFile.name} file!") } def version = new JsonSlurper().parseText(FileUtils.readFileToString(versionFile, StandardCharsets.UTF_8)) logger.lifecycle(":downloading minecraft libraries") if (!libraries.exists()) { libraries.mkdirs() } version.libraries.each { def downloadUrl = it.downloads.artifact.url download { src downloadUrl dest new File(libraries, downloadUrl.substring(downloadUrl.lastIndexOf("/") + 1)) overwrite false } project.dependencies.add("decompileClasspath", it.name) } } } task invertIntermediary(dependsOn: downloadIntermediary, type: FileOutput) { group = buildMappingGroup def v1Input = downloadIntermediary.dest output = new File(cacheFilesMinecraft, "${minecraft_version}-intermediary-inverted.tiny") outputs.file(output) outputs.upToDateWhen { false } doLast { logger.lifecycle(":building inverted intermediary") String[] v1Args = [ v1Input.getAbsolutePath(), output.getAbsolutePath(), "intermediary", "official" ] new CommandReorderTiny().run(v1Args) } } task invertIntermediaryv2(dependsOn: downloadIntermediaryV2, type: FileOutput) { group = buildMappingGroup def v2Input = new File(cacheFilesMinecraft, "${minecraft_version}-intermediary-v2.tiny") output = new File(cacheFilesMinecraft, "${minecraft_version}-intermediary-inverted-v2.tiny") outputs.file(output) outputs.upToDateWhen { false } doLast { logger.lifecycle(":building inverted intermediary v2") String[] v2Args = [ v2Input.getAbsolutePath(), output.getAbsolutePath(), "intermediary", "official" ] new CommandReorderTinyV2().run(v2Args) } } task patchIntermediary(dependsOn: [mergeJars, downloadIntermediary]) { group = buildMappingGroup def intermediaryTinyInput = downloadIntermediary.outputs.files.singleFile def outputFile = new File(cacheFilesMinecraft, "${minecraft_version}-intermediary-full.tiny") outputs.file(outputFile) outputs.upToDateWhen { false } doLast { logger.lifecycle(":patching intermediary") String[] args = [ mergedFile.getAbsolutePath(), intermediaryTinyInput.getAbsolutePath(), outputFile.getAbsolutePath(), "--writeAll" ] new CommandRewriteIntermediary().run(args) } } task mapIntermediaryJar(dependsOn: [downloadMcLibs, downloadIntermediary, mergeJars]) { group = mapJarGroup inputs.files downloadMcLibs.outputs.files.files outputs.file(intermediaryJar) //Force the task to always run outputs.upToDateWhen { false } doLast { logger.lifecycle(":mapping minecraft to intermediary") def tinyInput = downloadIntermediary.dest mapJar(intermediaryJar, mergedFile, tinyInput, libraries, "official", "intermediary") } } task yarnUnpicked(dependsOn: "unpickIntermediaryJar") { group = yarnGroup doLast { ant.setLifecycleLogLevel "WARN" ant.java( classname: 'cuchaz.enigma.gui.Main', classpath: configurations.enigmaRuntime.asPath, fork: true, spawn: true ) { jvmarg(value: "-Xmx2048m") arg(value: '-jar') arg(value: unpickedJar.getAbsolutePath()) arg(value: '-mappings') arg(value: mappingsDir.getAbsolutePath()) arg(value: '-profile') arg(value: 'enigma_profile.json') } } } task yarn(dependsOn: mapIntermediaryJar) { group = yarnGroup doLast { ant.setLifecycleLogLevel "WARN" ant.java( classname: 'cuchaz.enigma.gui.Main', classpath: configurations.enigmaRuntime.asPath, fork: true, spawn: true ) { jvmarg(value: "-Xmx2048m") arg(value: '-jar') arg(value: intermediaryJar.getAbsolutePath()) arg(value: '-mappings') arg(value: mappingsDir.getAbsolutePath()) arg(value: '-profile') arg(value: 'enigma_profile.json') } } } task checkMappings(dependsOn: mapIntermediaryJar) { group = buildMappingGroup inputs.dir mappingsDir doLast { logger.lifecycle(":checking mappings") String[] args = [ intermediaryJar.getAbsolutePath(), mappingsDir.getAbsolutePath() ] try { new CheckMappingsCommand().run(args) } catch (IllegalStateException ignored) { // just print, don't fail the task } } } task buildYarnTiny(dependsOn: mapIntermediaryJar, type: WithV2FileOutput) { group = buildMappingGroup inputs.dir mappingsDir if (!libs.exists()) { libs.mkdirs() } v1Output = new File(tempDir, "yarn-mappings.tiny") v2Output = new File(tempDir, "yarn-mappings-v2.tiny") outputs.upToDateWhen { false } doLast { logger.lifecycle(":generating tiny mappings") new MapSpecializedMethodsCommand().run( intermediaryJar.getAbsolutePath(), "enigma", mappingsDir.getAbsolutePath(), "tinyv2:intermediary:named", v2Output.getAbsolutePath() ) new ConvertMappingsCommand().run( "tinyv2", v2Output.getAbsolutePath(), "tiny:intermediary:named", v1Output.getAbsolutePath()) } } task mergeTiny(dependsOn: ["buildYarnTiny", "invertIntermediary"], type: FileOutput) { group = buildMappingGroup def yarnTinyInput = buildYarnTiny.v1Output def intermediaryTinyInput = invertIntermediary.output def unorderedResultMappings = new File(tempDir, "mappings-unordered.tiny") output = new File(tempDir, "mappings.tiny") outputs.file(output) outputs.upToDateWhen { false } doLast { logger.lifecycle(":merging yarn and intermediary") String[] args = [ intermediaryTinyInput.getAbsolutePath(), yarnTinyInput.getAbsolutePath(), unorderedResultMappings.getAbsolutePath(), "intermediary", "official" ] new CommandMergeTiny().run(args) logger.lifecycle(":reordering merged intermediary") String[] args2 = [ unorderedResultMappings.getAbsolutePath(), output.getAbsolutePath(), "official", "intermediary", "named" ] new CommandReorderTiny().run(args2) } } task tinyJar(type: Jar, dependsOn: mergeTiny) { group = buildMappingGroup outputs.upToDateWhen { false } archiveFileName = "yarn-${yarnVersion}.jar" destinationDirectory.set(file("build/libs")) classifier = "" from(mergeTiny.output) { rename { "mappings/mappings.tiny" } } } task compressTiny(dependsOn: [tinyJar, mergeTiny], type: FileOutput) { group = buildMappingGroup def outputFile = new File(libs, "yarn-tiny-${yarnVersion}.gz") outputs.file(outputFile) output = outputFile def inputFile = mergeTiny.output outputs.upToDateWhen { false } doLast { logger.lifecycle(":compressing tiny mappings") 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 } tasks.build.dependsOn "compressTiny","tinyJar","v2UnmergedYarnJar", "v2MergedYarnJar" task exportMappingsOfficial(dependsOn: downloadIntermediary) { def composeInput = downloadIntermediary.dest doLast { logger.lifecycle(":exporting mappings") String[] args = [ "tiny", composeInput.getAbsolutePath(), "enigma", file("mappings/").getAbsolutePath(), "enigma", file("mappings_official/").getAbsolutePath(), "right" ] new ComposeMappingsCommand().run(args) } } task importMappingsOfficial(dependsOn: invertIntermediary) { def composeInput = invertIntermediary.output doLast { logger.lifecycle(":importing mappings") String[] args = [ "tiny", composeInput.getAbsolutePath(), "enigma", file("mappings_official/").getAbsolutePath(), "enigma", file("mappings/").getAbsolutePath(), "right" ] new ComposeMappingsCommand().run(args) } } task buildTinyWithEnum(dependsOn: "mergeTiny", type: FileOutput) { group = buildMappingGroup def noEnum = mergeTiny.output output = new File(tempDir, "named-with-enum.tiny") outputs.file(output) outputs.upToDateWhen { false } doLast { logger.lifecycle(":seeking auto-mappable fields") String[] argsPropose = [ mergedFile.getAbsolutePath(), // must use official jar noEnum.getAbsolutePath(), output.getAbsolutePath() ] new CommandProposeFieldNames().run(argsPropose) } } task mapNamedJar(dependsOn: ["mergeV2", "unpickIntermediaryJar"]) { group = mapJarGroup inputs.files downloadMcLibs.outputs.files.files outputs.file(namedJar) //Force the task to always run outputs.upToDateWhen { false } def jsrToJetbrains = [ "javax/annotation/Nullable": "org/jetbrains/annotations/Nullable", "javax/annotation/Nonnull": "org/jetbrains/annotations/NotNull", "javax/annotation/concurrent/Immutable": "org/jetbrains/annotations/Unmodifiable" ] doLast { logger.lifecycle(":mapping minecraft to named") mapJar(namedJar, unpickedJar, mergeV2.output, libraries, "intermediary", "named") { it.withMappings { out -> jsrToJetbrains.each { e -> out.acceptClass e.key, e.value } } } } } combineUnpickDefinitions { group = 'unpick' input = file('unpick-definitions') output = new File(tempDir, 'definitions.unpick') } remapUnpickDefinitionsIntermediary { dependsOn buildYarnTiny group = 'unpick' output = new File(tempDir, 'intermediary-definitions.unpick') mappings = buildYarnTiny.v2Output } task unpickIntermediaryJar(type: JavaExec, dependsOn: [mapIntermediaryJar, "constantsJar", remapUnpickDefinitionsIntermediary]) { outputs.upToDateWhen { false } group "unpick" mainClass = "daomephsta.unpick.cli.Main" systemProperty "java.util.logging.config.file", file('unpick-logging.properties') classpath configurations.unpick doFirst { args intermediaryJar.absolutePath, unpickedJar.absolutePath, remapUnpickDefinitionsIntermediary.output.get().asFile.absolutePath, constantsJar.archiveFile.get().asFile.absolutePath configurations.decompileClasspath.files.each { args it.absolutePath } } } // Setup the build for the unpicked constants sourceCompatibility = JavaVersion.VERSION_16 targetCompatibility = JavaVersion.VERSION_16 tasks.withType(JavaCompile).configureEach { it.options.encoding = "UTF-8" it.options.release = 16 } sourceSets { constants packageDocs // package info files } license { header file("HEADER") include '**/*.java' } task constantsJar(type: Jar) { from sourceSets.constants.output archiveClassifier = "constants" } 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 : [buildYarnTiny,mapIntermediaryJar], type : FileOutput){ group = buildMappingGroup def noEnumV2 = buildYarnTiny.v2Output output = new File(tempDir, "unmerged-named-v2-with-enum.tiny") outputs.upToDateWhen { false } doLast { logger.lifecycle(":seeking auto-mappable fields for unmerged mappings") String[] argsProposeV2 = [ intermediaryJar.getAbsolutePath(), // must use intermediary jar noEnumV2.getAbsolutePath(), output.getAbsolutePath(), "false" // don't replace existing names right now ] new CommandProposeV2FieldNames().run(argsProposeV2) } } task mergeV2(dependsOn: ["v2UnmergedYarnJar", "invertIntermediaryv2"], type: FileOutput) { def mergedV2 = new File(tempDir, "merged-v2.tiny"); output = new File(tempDir, "merged-reordered-v2.tiny") outputs.upToDateWhen { false } doLast { logger.lifecycle(":merging yarn and intermediary v2") String[] args = [ invertIntermediaryv2.output.getAbsolutePath(), insertAutoGeneratedEnumMappings.output.getAbsolutePath(), 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" outputs.upToDateWhen { false } archiveFileName = "yarn-${yarnVersion}-v2.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")) } task v2MergedYarnJar(dependsOn: ["mergeV2"], type: Jar) { def mappings = mergeV2.output group = "mapping build" outputs.upToDateWhen { false } 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")) } def fakeSourceDir = file(".gradle/temp/fakeSource") task genFakeSource(type: JavaExec, dependsOn: ["mergeV2", "mapNamedJar"]) { group = "javadoc generation" outputs.upToDateWhen { false } mainClass = "net.fabricmc.mappingpoet.Main" classpath configurations.mappingPoet // use merged v2 so we have all namespaces in jd args mergeV2.output.getAbsolutePath(), namedJar.getAbsolutePath(), fakeSourceDir.getAbsolutePath(), libraries.getAbsolutePath() doLast { logger.lifecycle ":Fake source generated" } } task decompileCFR(type: JavaExec, dependsOn: [mapNamedJar]) { mainClass = "org.benf.cfr.reader.Main" args namedJar.getAbsolutePath(), "--outputdir", file("namedSrc").absolutePath doFirst { file("namedSrc").deleteDir() classpath = configurations.decompileClasspath } } javadoc { dependsOn genFakeSource dependsOn downloadMcLibs group = "javadoc generation" outputs.upToDateWhen { false } if (ENV.CI) { // Java 17 is provided on CI to build the javadocs with, see https://github.com/FabricMC/yarn/issues/2429 javadocTool = javaToolchains.javadocToolFor { languageVersion = JavaLanguageVersion.of(17) } } def mappingPoetJar = project.provider { zipTree configurations.mappingPoetJar.singleFile } failOnError = false maxMemory = '2G' // verbose = true // enable to debug options { // verbose() // enable to debug source = "16" 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 links( 'https://guava.dev/releases/21.0/api/docs/', 'https://www.javadoc.io/doc/com.google.code.gson/gson/2.8.0/', 'https://logging.apache.org/log4j/2.x/log4j-api/apidocs/', "https://javadoc.io/doc/org.jetbrains/annotations/${project.jetbrains_annotations_version}/", 'https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.2/', 'https://javadoc.lwjgl.org/', 'https://fastutil.di.unimi.it/docs/', 'https://netty.io/4.1/api/', 'https://commons.apache.org/proper/commons-logging/javadocs/api-1.1.3/', 'https://commons.apache.org/proper/commons-lang/javadocs/api-3.5', 'https://commons.apache.org/proper/commons-io/javadocs/api-2.5', 'https://commons.apache.org/proper/commons-codec/archives/1.10/apidocs', 'https://commons.apache.org/proper/commons-compress/javadocs/api-1.8.1/', "https://maven.fabricmc.net/docs/fabric-loader-${project.fabric_loader_version}/", "https://docs.oracle.com/en/java/javase/16/docs/api/" ) // https://docs.oracle.com/en/java/javase/16/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 downloadMcLibs.outputs.files.asFileTree doLast { project.copy { from mappingPoetJar include "copy_on_click.js" into javadoc.outputDirectory } } doFirst { // lazy setting options { tagletPath configurations.mappingPoet.files.toList() 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 { dependsOn mapIntermediaryJar inputJar = intermediaryJar 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 void mapJar(File output, File input, File mappings, File libraries, String from, String to, Action action = { }) { if (output.exists()) { output.delete() } def remapperBuilder = TinyRemapper.newRemapper() .withMappings(TinyUtils.createTinyMappingProvider(mappings.toPath(), from, to)) .renameInvalidLocals(true) .rebuildSourceFilenames(true) action.execute(remapperBuilder) def remapper = remapperBuilder .build() try { def outputConsumer = new OutputConsumerPath(output.toPath()) outputConsumer.addNonClassFiles(input.toPath()) remapper.readInputs(input.toPath()) libraries.eachFileRecurse(FileType.FILES) { file -> remapper.readClassPath(file.toPath()) } remapper.apply(outputConsumer) outputConsumer.close() remapper.finish() } catch (Exception e) { remapper.finish() throw new RuntimeException("Failed to remap jar", e) } } class FileOutput extends DefaultTask { @OutputFile File output } class FileOutputInput extends DefaultTask { @InputFile File input @OutputFile File output } class WithV2FileOutput extends DefaultTask { @OutputFile File v1Output @OutputFile File v2Output }