diff --git a/.github/workflows/release-filament.yml b/.github/workflows/release-filament.yml new file mode 100644 index 0000000000..16a845a962 --- /dev/null +++ b/.github/workflows/release-filament.yml @@ -0,0 +1,16 @@ +name: Release Filament +on: [workflow_dispatch] # Manual trigger +jobs: + build: + runs-on: ubuntu-22.04 + container: + image: openjdk:18-jdk + options: --user root + steps: + - uses: actions/checkout@v2 + - uses: gradle/wrapper-validation-action@v1 + - run: ./gradlew :filament:build :filament:publish --stacktrace + env: + MAVEN_URL: ${{ secrets.MAVEN_URL }} + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 812ef1beab..37b72bd16a 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,6 @@ buildscript { dependencies { classpath "cuchaz:enigma-cli:${project.enigma_version}" classpath "net.fabricmc:stitch:${project.stitch_version}" - classpath "commons-io:commons-io:2.11.0" classpath "de.undercouch:gradle-download-task:4.1.1" classpath "net.fabricmc.unpick:unpick:${project.unpick_version}" classpath "net.fabricmc.unpick:unpick-format-utils:${project.unpick_version}" @@ -28,6 +27,10 @@ plugins { def minecraft_version = "22w42a" +filament { + minecraftVersion = minecraft_version +} + def ENV = System.getenv() // Fetch build number from Github Actions def build_number = ENV.BUILD_NUMBER ?: "local" @@ -99,37 +102,27 @@ 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") -// The vanilla server bootstrap jar (Contains the real server jar) -def serverBootstrapJar = new File(cacheFilesMinecraft, "${minecraft_version}-serverboostrap.jar") -// The real server jar, expected from the bootstrap -def serverJar = new File(cacheFilesMinecraft, "${minecraft_version}-server.jar") def serverIntermediaryJar = file("${minecraft_version}-server-intermediary.jar") -def libraries = new File(cacheFilesMinecraft, "libraries") def libs = new File("build/libs/") +def minecraftLibraries = tasks.minecraftLibraries.files + 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.json.JsonSlurper import net.fabricmc.filament.task.MapJarTask import net.fabricmc.stitch.commands.CommandMergeTiny 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.CommandReorderTinyV2 -import net.fabricmc.stitch.merge.JarMerger import net.fabricmc.nameproposal.MappingNameCompleter -import org.apache.commons.io.FileUtils -import java.nio.charset.StandardCharsets import java.util.zip.GZIPOutputStream boolean validateChecksum(File file, String checksum) { @@ -144,85 +137,6 @@ boolean validateChecksum(File file, String 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://piston-meta.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, serverBootstrapJar) - - outputs.upToDateWhen { - def version = new JsonSlurper().parseText(FileUtils.readFileToString(versionFile)) - return clientJar.exists() && serverBootstrapJar.exists() && validateChecksum(clientJar, version.downloads.client.sha1) && validateChecksum(serverBootstrapJar, 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 serverBootstrapJar - overwrite false - } - } -} - task downloadIntermediary(type: Download) { group = buildMappingGroup def url = "https://github.com/FabricMC/intermediary/raw/master/mappings/${minecraft_version}.tiny" @@ -251,99 +165,6 @@ task downloadIntermediaryV2(type: Download) { } } -import java.util.zip.ZipFile - -// Extracts the real server jar from the boostrap jar -task extractServerJar(dependsOn: downloadMcJars) { - group = setupGroup - inputs.file serverBootstrapJar - outputs.file serverJar - - doLast { - new ZipFile(serverBootstrapJar as File).withCloseable { zip -> - def serverVersionsEntry = zip.getEntry("META-INF/versions.list") - - if (!serverVersionsEntry) { - throw new RuntimeException("Could not find versions.list") - } - - def jarPath = null - - zip.getInputStream(serverVersionsEntry).withCloseable { is -> - def versions = is.text.split("\t") - assert versions.length == 3 - jarPath = versions[2] - } - - def serverJarEntry = zip.getEntry("META-INF/versions/${jarPath}") - - if (!serverJarEntry) { - throw new RuntimeException("Failed to find server jar entry ${jarPath}") - } - - zip.getInputStream(serverJarEntry).withCloseable { is -> - serverJar.bytes = is.readAllBytes() - } - } - } -} - -task mergeJars(dependsOn: extractServerJar) { - group = setupGroup - inputs.files (serverJar, clientJar) - outputs.file(mergedFile) - - doLast { - logger.lifecycle(":merging jars") - - if (mergedFile.exists()) { - return - } - - def jarMerger = new JarMerger(clientJar, serverJar, mergedFile) - 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 { - if (it.downloads.artifact == null) return // TODO: happens for downloads.classifiers - implement properly? - - 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 @@ -388,7 +209,7 @@ task invertIntermediaryv2(dependsOn: downloadIntermediaryV2, type: FileOutput) { } } -task patchIntermediary(dependsOn: [mergeJars, downloadIntermediary]) { +task patchIntermediary(dependsOn: [mergeMinecraftJars, downloadIntermediary]) { group = buildMappingGroup def intermediaryTinyInput = downloadIntermediary.outputs.files.singleFile @@ -400,7 +221,7 @@ task patchIntermediary(dependsOn: [mergeJars, downloadIntermediary]) { doLast { logger.lifecycle(":patching intermediary") String[] args = [ - mergedFile.getAbsolutePath(), + mergeMinecraftJars.output.getAbsolutePath(), intermediaryTinyInput.getAbsolutePath(), outputFile.getAbsolutePath(), "--writeAll" @@ -410,22 +231,22 @@ task patchIntermediary(dependsOn: [mergeJars, downloadIntermediary]) { } } -task mapIntermediaryJar(type: MapJarTask, dependsOn: [downloadMcLibs, downloadIntermediary, mergeJars]) { +task mapIntermediaryJar(type: MapJarTask, dependsOn: [downloadIntermediary, mergeMinecraftJars]) { group = mapJarGroup output = intermediaryJar - input = mergedFile + input = mergeMinecraftJars.outputFile mappings = downloadIntermediary.dest - classpath.from fileTree(libraries) + classpath.from minecraftLibraries from = 'official' to = 'intermediary' } -task mapServerIntermediaryJar(type: MapJarTask, dependsOn: [downloadMcLibs, downloadIntermediary, extractServerJar]) { +task mapServerIntermediaryJar(type: MapJarTask, dependsOn: [downloadIntermediary, extractBundledServer]) { group = mapJarGroup output = serverIntermediaryJar - input = serverJar + input = extractBundledServer.outputFile mappings = downloadIntermediary.dest - classpath.from fileTree(libraries) + classpath.from minecraftLibraries from = 'official' to = 'intermediary' } @@ -643,7 +464,7 @@ task unpickIntermediaryJar(type: JavaExec, dependsOn: [mapIntermediaryJar, "cons doFirst { args intermediaryJar.absolutePath, unpickedJar.absolutePath, remapUnpickDefinitionsIntermediary.output.get().asFile.absolutePath, constantsJar.archiveFile.get().asFile.absolutePath - configurations.decompileClasspath.files.each { + minecraftLibraries.files.forEach { args it.absolutePath } } @@ -782,7 +603,7 @@ task mapNamedJar(type: MapJarTask, dependsOn: ["mergeV2", "unpickIntermediaryJar output = namedJar input = unpickedJar mappings = mergeV2.output - classpath.from fileTree(libraries) + classpath.from minecraftLibraries from = 'intermediary' to = 'named' classMappings = [ @@ -792,15 +613,23 @@ task mapNamedJar(type: MapJarTask, dependsOn: ["mergeV2", "unpickIntermediaryJar ] } +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"]) { +task genFakeSource(type: JavaExec, dependsOn: ["mergeV2", "mapNamedJar", syncDependencies]) { 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() + args mergeV2.output.getAbsolutePath(), namedJar.getAbsolutePath(), fakeSourceDir.getAbsolutePath(), mcLibsDir.getAbsolutePath() doLast { logger.lifecycle ":Fake source generated" @@ -814,13 +643,13 @@ task decompileCFR(type: JavaExec, dependsOn: [mapNamedJar]) { doFirst { file("namedSrc").deleteDir() - classpath = configurations.decompileClasspath + classpath.from configurations.decompileClasspath + classpath.from minecraftLibraries } } javadoc { dependsOn genFakeSource - dependsOn downloadMcLibs group = "javadoc generation" outputs.upToDateWhen { false } @@ -878,7 +707,7 @@ javadoc { addBooleanOption 'Xdoclint:accessibility', true } source fileTree(fakeSourceDir) + sourceSets.constants.allJava + sourceSets.packageDocs.allJava - classpath = configurations.javadocClasspath.plus downloadMcLibs.outputs.files.asFileTree + classpath = configurations.javadocClasspath.plus minecraftLibraries doLast { project.copy { @@ -979,14 +808,6 @@ class FileOutput extends DefaultTask { File output } -class FileOutputInput extends DefaultTask { - @InputFile - File input - - @OutputFile - File output -} - class WithV2FileOutput extends DefaultTask { @OutputFile File v1Output diff --git a/filament/build.gradle b/filament/build.gradle index 54f6af479b..65ab4174c7 100644 --- a/filament/build.gradle +++ b/filament/build.gradle @@ -1,11 +1,18 @@ +import org.gradle.util.GradleVersion + plugins { id 'java-library' id 'java-gradle-plugin' id 'checkstyle' + id 'maven-publish' } group = 'net.fabricmc' +version = project.filament_version +def ENV = System.getenv() + +// Needed to read the main projects properties def properties = new Properties() file('../gradle.properties').newInputStream().withCloseable { properties.load(it) @@ -27,6 +34,16 @@ dependencies { implementation "net.fabricmc.unpick:unpick-format-utils:$properties.unpick_version" implementation "net.fabricmc:tiny-mappings-parser:$properties.tiny_mappings_parser_version" implementation "net.fabricmc:tiny-remapper:$properties.tiny_remapper_version" + implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.4.2' + + // Contains a number of useful utilities we can re-use. + implementation ("net.fabricmc:fabric-loom:1.0.7") { + transitive = true + } + + implementation ('net.fabricmc:stitch:0.6.2') { + exclude module: 'enigma' + } testImplementation platform("org.junit:junit-bom:$properties.junit_version") testImplementation 'org.junit.jupiter:junit-jupiter' @@ -54,4 +71,40 @@ gradlePlugin { implementationClass = 'net.fabricmc.filament.FilamentGradlePlugin' } } +} + +publishing { + repositories { + if (ENV.MAVEN_URL) { + repositories.maven { + name "fabric" + url ENV.MAVEN_URL + credentials { + username ENV.MAVEN_USERNAME + password ENV.MAVEN_PASSWORD + } + } + } + } +} + +/** + * Run this task to download the gradle sources next to the api jar, you may need to manually attach the sources jar + */ +task downloadGradleSources() { + doLast { + // Awful hack to find the gradle api location + def gradleApiFile = project.configurations.detachedConfiguration(dependencies.gradleApi()).files.stream() + .filter { + it.name.startsWith("gradle-api") + }.findFirst().orElseThrow() + + def gradleApiSources = new File(gradleApiFile.absolutePath.replace(".jar", "-sources.jar")) + def url = "https://services.gradle.org/distributions/gradle-${GradleVersion.current().getVersion()}-src.zip" + + gradleApiSources.delete() + + println("Downloading (${url}) to (${gradleApiSources})") + gradleApiSources << new URL(url).newInputStream() + } } \ No newline at end of file diff --git a/filament/gradle.properties b/filament/gradle.properties new file mode 100644 index 0000000000..bf1ac08054 --- /dev/null +++ b/filament/gradle.properties @@ -0,0 +1 @@ +filament_version=0.4.0 \ No newline at end of file diff --git a/filament/src/main/java/net/fabricmc/filament/FilamentExtension.java b/filament/src/main/java/net/fabricmc/filament/FilamentExtension.java new file mode 100644 index 0000000000..8a91855ce2 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/FilamentExtension.java @@ -0,0 +1,35 @@ +package net.fabricmc.filament; + +import java.io.File; + +import javax.inject.Inject; + +import org.gradle.api.Project; +import org.gradle.api.provider.Property; + +public abstract class FilamentExtension { + public static FilamentExtension get(Project project) { + return project.getExtensions().getByType(FilamentExtension.class); + } + + @Inject + protected abstract Project getProject(); + + public abstract Property getMinecraftVersion(); + + public abstract Property getMinecraftVersionManifestUrl(); + + @Inject + public FilamentExtension() { + getMinecraftVersion().finalizeValueOnRead(); + getMinecraftVersionManifestUrl().convention("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json").finalizeValueOnRead(); + } + + public File getCacheDirectory() { + return new File(getProject().getRootDir(), ".gradle/filament"); + } + + public File getMinecraftDirectory() { + return new File(getCacheDirectory(), getMinecraftVersion().get()); + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/FilamentGradlePlugin.java b/filament/src/main/java/net/fabricmc/filament/FilamentGradlePlugin.java index e7450ee45c..655c69e8e1 100644 --- a/filament/src/main/java/net/fabricmc/filament/FilamentGradlePlugin.java +++ b/filament/src/main/java/net/fabricmc/filament/FilamentGradlePlugin.java @@ -1,25 +1,86 @@ package net.fabricmc.filament; +import java.io.File; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import org.gradle.api.Plugin; import org.gradle.api.Project; +import org.gradle.api.file.RegularFile; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.TaskContainer; +import org.gradle.api.tasks.TaskProvider; import net.fabricmc.filament.task.CombineUnpickDefinitionsTask; +import net.fabricmc.filament.task.DownloadTask; import net.fabricmc.filament.task.GeneratePackageInfoMappingsTask; import net.fabricmc.filament.task.JavadocLintTask; import net.fabricmc.filament.task.RemapUnpickDefinitionsTask; +import net.fabricmc.filament.task.base.FileOutputTask; +import net.fabricmc.filament.task.minecraft.ExtractBundledServerTask; +import net.fabricmc.filament.task.minecraft.MergeMinecraftTask; +import net.fabricmc.filament.task.minecraft.MinecraftLibrariesTask; +import net.fabricmc.filament.task.minecraft.MinecraftVersionMetaTask; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; public final class FilamentGradlePlugin implements Plugin { + public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + @Override public void apply(Project project) { - project.getTasks().register("generatePackageInfoMappings", GeneratePackageInfoMappingsTask.class); - project.getTasks().register("javadocLint", JavadocLintTask.class); + final FilamentExtension extension = project.getExtensions().create("filament", FilamentExtension.class); + final TaskContainer tasks = project.getTasks(); - var combineUnpickDefinitions = project.getTasks().register("combineUnpickDefinitions", CombineUnpickDefinitionsTask.class); - project.getTasks().register("remapUnpickDefinitionsIntermediary", RemapUnpickDefinitionsTask.class, task -> { + var minecraftMetaTask = tasks.register("downloadMinecraftMeta", MinecraftVersionMetaTask.class); + var metaProvider = MinecraftVersionMetaTask.readMetadata(minecraftMetaTask); + + var minecraftClient = tasks.register("downloadMinecraftClientJar", DownloadTask.class, task -> { + Provider downloadProvider = metaProvider.map(meta -> meta.download("client")); + task.getUrl().set(downloadProvider.map(MinecraftVersionMeta.Download::url)); + task.getSha1().set(downloadProvider.map(MinecraftVersionMeta.Download::sha1)); + + task.getOutputFile().set(new File(extension.getMinecraftDirectory(), "client.jar")); + }); + var minecraftServer = tasks.register("downloadMinecraftServerJar", DownloadTask.class, task -> { + Provider downloadProvider = metaProvider.map(meta -> meta.download("server")); + task.getUrl().set(downloadProvider.map(MinecraftVersionMeta.Download::url)); + task.getSha1().set(downloadProvider.map(MinecraftVersionMeta.Download::sha1)); + + task.getOutputFile().set(new File(extension.getMinecraftDirectory(), "server_bundle.jar")); + }); + var extractBundledServer = tasks.register("extractBundledServer", ExtractBundledServerTask.class, task -> { + task.dependsOn(minecraftServer); + task.getServerJar().set(getOutput(minecraftServer)); + task.getOutputFile().set(new File(extension.getMinecraftDirectory(), "server.jar")); + }); + tasks.register("mergeMinecraftJars", MergeMinecraftTask.class, task -> { + task.getClientJar().set(getOutput(minecraftClient)); + task.getServerJar().set(getOutput(extractBundledServer)); + + task.getOutputFile().set(new File(extension.getMinecraftDirectory(), "merged.jar")); + }); + tasks.register("generatePackageInfoMappings", GeneratePackageInfoMappingsTask.class); + tasks.register("javadocLint", JavadocLintTask.class); + + var combineUnpickDefinitions = tasks.register("combineUnpickDefinitions", CombineUnpickDefinitionsTask.class); + tasks.register("remapUnpickDefinitionsIntermediary", RemapUnpickDefinitionsTask.class, task -> { task.dependsOn(combineUnpickDefinitions); task.getInput().set(combineUnpickDefinitions.flatMap(CombineUnpickDefinitionsTask::getOutput)); task.getSourceNamespace().set("named"); task.getTargetNamespace().set("intermediary"); }); + + tasks.register("minecraftLibraries", MinecraftLibrariesTask.class, task -> { + task.dependsOn(minecraftMetaTask); + + var files = MinecraftVersionMetaTask.readMetadata(minecraftMetaTask) + .map(meta -> MinecraftLibrariesTask.getDependencies(project, meta)) + .map(dependencies -> project.getConfigurations().detachedConfiguration(dependencies).resolve()); + task.getFiles().from(files); + }); + } + + private Provider getOutput(TaskProvider taskProvider) { + return taskProvider.flatMap(FileOutputTask::getOutputFile); } } diff --git a/filament/src/main/java/net/fabricmc/filament/task/DownloadTask.java b/filament/src/main/java/net/fabricmc/filament/task/DownloadTask.java new file mode 100644 index 0000000000..4380fa5219 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/task/DownloadTask.java @@ -0,0 +1,57 @@ +package net.fabricmc.filament.task; + +import java.net.URISyntaxException; + +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.TaskAction; +import org.gradle.workers.WorkAction; +import org.gradle.workers.WorkParameters; +import org.gradle.workers.WorkQueue; +import org.gradle.workers.WorkerExecutor; + +import net.fabricmc.filament.task.base.FileOutputTask; +import net.fabricmc.loom.util.download.Download; +import net.fabricmc.loom.util.download.DownloadException; + +public abstract class DownloadTask extends FileOutputTask { + @Input + public abstract Property getUrl(); + @Input + public abstract Property getSha1(); + + @Inject + protected abstract WorkerExecutor getWorkerExecutor(); + + @TaskAction + public void run() { + WorkQueue workQueue = getWorkerExecutor().noIsolation(); + workQueue.submit(DownloadAction.class, parameters -> { + parameters.getUrl().set(getUrl()); + parameters.getSha1().set(getSha1()); + parameters.getOutput().set(getOutputFile()); + }); + } + + public interface DownloadParameters extends WorkParameters { + Property getUrl(); + Property getSha1(); + RegularFileProperty getOutput(); + } + + public abstract static class DownloadAction implements WorkAction { + @Override + public void execute() { + try { + Download.create(getParameters().getUrl().get()) + .sha1(getParameters().getSha1().get()) + .downloadPath(getParameters().getOutput().get().getAsFile().toPath()); + } catch (DownloadException | URISyntaxException e) { + throw new RuntimeException("Failed to download", e); + } + } + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/task/JavadocLintTask.java b/filament/src/main/java/net/fabricmc/filament/task/JavadocLintTask.java index dcfec32d7d..04e4ae4828 100644 --- a/filament/src/main/java/net/fabricmc/filament/task/JavadocLintTask.java +++ b/filament/src/main/java/net/fabricmc/filament/task/JavadocLintTask.java @@ -104,7 +104,7 @@ public abstract class JavadocLintTask extends DefaultTask { @Override public void execute() { try { - var files = FileUtil.toPaths(getParameters().getMappingFiles().getFiles()).toArray(new Path[0]); + Path[] files = FileUtil.toPaths(getParameters().getMappingFiles().getFiles()).toArray(new Path[0]); EntryTree mappings = EnigmaMappingsReader.readFiles(ProgressListener.none(), files); List errors = new ArrayList<>(); diff --git a/filament/src/main/java/net/fabricmc/filament/task/MapJarTask.java b/filament/src/main/java/net/fabricmc/filament/task/MapJarTask.java index 531078e25c..14f9bab072 100644 --- a/filament/src/main/java/net/fabricmc/filament/task/MapJarTask.java +++ b/filament/src/main/java/net/fabricmc/filament/task/MapJarTask.java @@ -1,5 +1,6 @@ package net.fabricmc.filament.task; +import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -11,8 +12,6 @@ import javax.inject.Inject; import org.gradle.api.DefaultTask; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.FileSystemLocation; -import org.gradle.api.file.FileVisitDetails; -import org.gradle.api.file.FileVisitor; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; @@ -108,19 +107,11 @@ public abstract class MapJarTask extends DefaultTask { try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(output).build()) { Path input = getPath(params.getInput()); outputConsumer.addNonClassFiles(input); - remapper.readInputs(input); + remapper.readInputsAsync(input); - params.getClasspath().getAsFileTree().visit(new FileVisitor() { - @Override - public void visitDir(FileVisitDetails dirDetails) { - // ignore - } - - @Override - public void visitFile(FileVisitDetails fileDetails) { - remapper.readClassPath(fileDetails.getFile().toPath()); - } - }); + for (File file : params.getClasspath().getFiles()) { + remapper.readClassPathAsync(file.toPath()); + } remapper.apply(outputConsumer); } finally { diff --git a/filament/src/main/java/net/fabricmc/filament/task/base/FilamentTask.java b/filament/src/main/java/net/fabricmc/filament/task/base/FilamentTask.java new file mode 100644 index 0000000000..7333ed237e --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/task/base/FilamentTask.java @@ -0,0 +1,20 @@ +package net.fabricmc.filament.task.base; + +import javax.inject.Inject; + +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.Internal; + +import net.fabricmc.filament.FilamentExtension; + +public abstract class FilamentTask extends DefaultTask { + @Inject + public FilamentTask() { + setGroup("filament"); + } + + @Internal + protected FilamentExtension getExtension() { + return FilamentExtension.get(getProject()); + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/task/base/FileOutputTask.java b/filament/src/main/java/net/fabricmc/filament/task/base/FileOutputTask.java new file mode 100644 index 0000000000..1628fc80fa --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/task/base/FileOutputTask.java @@ -0,0 +1,9 @@ +package net.fabricmc.filament.task.base; + +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.OutputFile; + +public abstract class FileOutputTask extends FilamentTask { + @OutputFile + public abstract RegularFileProperty getOutputFile(); +} diff --git a/filament/src/main/java/net/fabricmc/filament/task/minecraft/ExtractBundledServerTask.java b/filament/src/main/java/net/fabricmc/filament/task/minecraft/ExtractBundledServerTask.java new file mode 100644 index 0000000000..1d32496d87 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/task/minecraft/ExtractBundledServerTask.java @@ -0,0 +1,27 @@ +package net.fabricmc.filament.task.minecraft; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; + +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.TaskAction; + +import net.fabricmc.filament.task.base.FileOutputTask; +import net.fabricmc.loom.util.FileSystemUtil; + +public abstract class ExtractBundledServerTask extends FileOutputTask { + @InputFile + public abstract RegularFileProperty getServerJar(); + + @TaskAction + public void run() throws IOException { + try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(getServerJar().get().getAsFile().toPath(), false)) { + String versionsList = new String(fs.readAllBytes("META-INF/versions.list"), StandardCharsets.UTF_8); + String jarPath = "META-INF/versions/" + versionsList.split("\t")[2]; + Files.copy(fs.getPath(jarPath), getOutputFile().get().getAsFile().toPath(), StandardCopyOption.REPLACE_EXISTING); + } + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/task/minecraft/MergeMinecraftTask.java b/filament/src/main/java/net/fabricmc/filament/task/minecraft/MergeMinecraftTask.java new file mode 100644 index 0000000000..6c1ee1eec6 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/task/minecraft/MergeMinecraftTask.java @@ -0,0 +1,28 @@ +package net.fabricmc.filament.task.minecraft; + +import java.io.IOException; + +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.TaskAction; + +import net.fabricmc.filament.task.base.FileOutputTask; +import net.fabricmc.stitch.merge.JarMerger; + +public abstract class MergeMinecraftTask extends FileOutputTask { + @InputFile + public abstract RegularFileProperty getClientJar(); + + @InputFile + public abstract RegularFileProperty getServerJar(); + + @TaskAction + public void run() throws IOException { + try (JarMerger jarMerger = new JarMerger( + getClientJar().getAsFile().get(), + getServerJar().getAsFile().get(), + getOutputFile().getAsFile().get())) { + jarMerger.merge(); + } + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/task/minecraft/MinecraftLibrariesTask.java b/filament/src/main/java/net/fabricmc/filament/task/minecraft/MinecraftLibrariesTask.java new file mode 100644 index 0000000000..77ba6bc272 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/task/minecraft/MinecraftLibrariesTask.java @@ -0,0 +1,21 @@ +package net.fabricmc.filament.task.minecraft; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.tasks.OutputFiles; + +import net.fabricmc.filament.task.base.FilamentTask; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; + +public abstract class MinecraftLibrariesTask extends FilamentTask { + @OutputFiles + public abstract ConfigurableFileCollection getFiles(); + + public static Dependency[] getDependencies(Project project, MinecraftVersionMeta meta) { + return meta.libraries().stream() + .filter(library -> library.artifact() != null) + .map(library -> project.getDependencies().create(library.name())) + .toArray(Dependency[]::new); + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/task/minecraft/MinecraftVersionMetaTask.java b/filament/src/main/java/net/fabricmc/filament/task/minecraft/MinecraftVersionMetaTask.java new file mode 100644 index 0000000000..85f47e7979 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/task/minecraft/MinecraftVersionMetaTask.java @@ -0,0 +1,84 @@ +package net.fabricmc.filament.task.minecraft; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; + +import net.fabricmc.filament.FilamentGradlePlugin; +import net.fabricmc.filament.task.base.FilamentTask; +import net.fabricmc.loom.configuration.providers.minecraft.ManifestVersion; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; +import net.fabricmc.loom.util.download.Download; + +public abstract class MinecraftVersionMetaTask extends FilamentTask { + @Input + public abstract Property getMinecraftVersion(); + + @Input + public abstract Property getMinecraftVersionManifestUrl(); + + @OutputFile + public abstract RegularFileProperty getVersionManifest(); + + @OutputFile + public abstract RegularFileProperty getVersionMetadata(); + + public MinecraftVersionMetaTask() { + // Use the Minecraft version as an input to ensure the task re-runs on upgrade + getMinecraftVersion().set(getExtension().getMinecraftVersion()); + getMinecraftVersionManifestUrl().set(getExtension().getMinecraftVersionManifestUrl()); + + getVersionManifest().set(new File(getExtension().getCacheDirectory(), "version_manifest.json")); + getVersionMetadata().set(new File(getExtension().getMinecraftDirectory(), "version.json")); + } + + @TaskAction + public void run() throws IOException, URISyntaxException { + final Path versionManifestPath = getVersionManifest().getAsFile().get().toPath(); + final Path versionMetadataPath = getVersionMetadata().getAsFile().get().toPath(); + + final String versionManifest = Download.create(getMinecraftVersionManifestUrl().get()) + .downloadString(versionManifestPath); + + final ManifestVersion mcManifest = FilamentGradlePlugin.OBJECT_MAPPER.readValue(versionManifest, ManifestVersion.class); + + ManifestVersion.Versions version = mcManifest.versions().stream() + .filter(versions -> versions.id.equalsIgnoreCase(getMinecraftVersion().get())) + .findFirst() + .orElse(null); + + if (version == null) { + throw new RuntimeException("Failed to find minecraft version: " + getMinecraftVersion().get()); + } + + Download.create(version.url) + .sha1(version.sha1) + .downloadPath(versionMetadataPath); + } + + public static Provider readMetadata(Provider task) { + return task.flatMap(t -> readMetadata(t.getVersionMetadata())); + } + + public static Provider readMetadata(RegularFileProperty file) { + return file.map(regularFile -> { + try { + String versionManifest = Files.readString(regularFile.getAsFile().toPath(), StandardCharsets.UTF_8); + return FilamentGradlePlugin.OBJECT_MAPPER.readValue(versionManifest, MinecraftVersionMeta.class); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } +} diff --git a/filament/src/test/resources/projects/javadocLint/build.gradle b/filament/src/test/resources/projects/javadocLint/build.gradle index 132c35b1bc..012ab3b747 100644 --- a/filament/src/test/resources/projects/javadocLint/build.gradle +++ b/filament/src/test/resources/projects/javadocLint/build.gradle @@ -2,6 +2,10 @@ plugins { id 'net.fabricmc.filament' } +filament { + minecraftVersion = "1.19.2" +} + javadocLint { mappingDirectory = file('mappings') } diff --git a/filament/src/test/resources/projects/unpickDef/build.gradle b/filament/src/test/resources/projects/unpickDef/build.gradle index 3985ba03d7..e9cf8e726b 100644 --- a/filament/src/test/resources/projects/unpickDef/build.gradle +++ b/filament/src/test/resources/projects/unpickDef/build.gradle @@ -2,6 +2,10 @@ plugins { id 'net.fabricmc.filament' } +filament { + minecraftVersion = "1.19.2" +} + combineUnpickDefinitions { input = file('unpick-definitions') output = file('combined_definitions.unpick') diff --git a/gradle.properties b/gradle.properties index 8859ea5c3b..310141b109 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ unpick_version=2.3.0 cfr_version=0.1.1 name_proposal_version=0.1.4 tiny_remapper_version=0.8.2 -asm_version=9.3 +asm_version=9.4 # Javadoc generation/linking fabric_loader_version=0.13.3