From 3ad44b992fbb6859b01a99eae1a317ea67754e4d Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Sat, 20 Jul 2024 15:10:49 +0200 Subject: Make builds reproducible allowing for verifying builds [no changelog] --- .github/workflows/build.yml | 4 +- build.gradle.kts | 8 +- src/main/resources/firmament.mixins.json | 10 --- src/main/resources/firmament.mixins.json.license | 3 - symbols/build.gradle.kts | 1 + .../kotlin/process/MixinAnnotationProcessor.kt | 99 ++++++++++++++++++++++ .../kotlin/process/SubscribeAnnotationProcessor.kt | 17 +++- 7 files changed, 122 insertions(+), 20 deletions(-) delete mode 100644 src/main/resources/firmament.mixins.json delete mode 100644 src/main/resources/firmament.mixins.json.license create mode 100644 symbols/src/main/kotlin/process/MixinAnnotationProcessor.kt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 37b5140..b048e5b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,7 @@ jobs: name: Checkout repository with: fetch-tags: true + fetch-depth: 0 - name: Set up JDK 21 uses: actions/setup-java@v4 with: @@ -29,9 +30,10 @@ jobs: uses: actions/upload-artifact@v3 with: path: build/libs/Firmament-*.jar - - name: Move build artifact around + - name: Move build artifact around and print check sum run: | rm -f build/libs/*sources*.jar + sha256sum build/libs/*.jar - name: Upload to discord run: | curl "$WEBHOOK_URL" -X POST -H "Content-type: multipart/form-data" --form "files[0]=@$(echo build/libs/*.jar)" diff --git a/build.gradle.kts b/build.gradle.kts index f4ec9ea..6e766fa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,7 +20,6 @@ plugins { id("com.github.johnrengelman.shadow") version "8.1.1" id("moe.nea.licenseextractificator") // id("io.github.juuxel.loom-vineflower") version "1.11.0" - id("io.shcm.shsupercm.fabric.fletchingtable") version "1.5" } java { @@ -264,6 +263,9 @@ tasks.create("printAllLicenses", LicenseDiscoveryTask::class.java, licensing).ap outputs.upToDateWhen { false } } -licensing.addExtraLicenseMatchers() +tasks.withType().configureEach { + isPreserveFileTimestamps = false + isReproducibleFileOrder = true +} -fletchingTable.defaultMixinEnvironment.set("client") +licensing.addExtraLicenseMatchers() diff --git a/src/main/resources/firmament.mixins.json b/src/main/resources/firmament.mixins.json deleted file mode 100644 index cdf66e0..0000000 --- a/src/main/resources/firmament.mixins.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "required": true, - "plugin": "moe.nea.firmament.init.MixinPlugin", - "package": "moe.nea.firmament.mixins", - "compatibilityLevel": "JAVA_17", - "injectors": { - "defaultRequire": 1 - }, - "refmap": "Firmament-refmap.json" -} diff --git a/src/main/resources/firmament.mixins.json.license b/src/main/resources/firmament.mixins.json.license deleted file mode 100644 index c01d463..0000000 --- a/src/main/resources/firmament.mixins.json.license +++ /dev/null @@ -1,3 +0,0 @@ -SPDX-FileCopyrightText: 2023 Linnea Gräf - -SPDX-License-Identifier: CC0-1.0 diff --git a/symbols/build.gradle.kts b/symbols/build.gradle.kts index fad3221..c02f1d5 100644 --- a/symbols/build.gradle.kts +++ b/symbols/build.gradle.kts @@ -16,4 +16,5 @@ dependencies { ksp("dev.zacsweers.autoservice:auto-service-ksp:1.1.0") implementation("com.google.auto.service:auto-service-annotations:1.1.1") implementation("com.google.devtools.ksp:symbol-processing-api:1.9.23-1.0.20") + implementation("com.google.code.gson:gson:2.11.0") } diff --git a/symbols/src/main/kotlin/process/MixinAnnotationProcessor.kt b/symbols/src/main/kotlin/process/MixinAnnotationProcessor.kt new file mode 100644 index 0000000..ccfbdd7 --- /dev/null +++ b/symbols/src/main/kotlin/process/MixinAnnotationProcessor.kt @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2024 Linnea Gräf + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.annotations.process + +import com.google.auto.service.AutoService +import com.google.devtools.ksp.processing.CodeGenerator +import com.google.devtools.ksp.processing.Dependencies +import com.google.devtools.ksp.processing.KSPLogger +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.processing.SymbolProcessorProvider +import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.Origin +import com.google.gson.Gson +import com.google.gson.JsonArray +import com.google.gson.JsonObject + + +class MixinAnnotationProcessor( + val codeGenerator: CodeGenerator, + val logger: KSPLogger +) : SymbolProcessor { + @AutoService(SymbolProcessorProvider::class) + class Provider : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { + return MixinAnnotationProcessor(environment.codeGenerator, environment.logger) + } + } + + val mixinPackage = "moe.nea.firmament.mixins" + val refmapName = "Firmament-refmap.json" + val mixinPlugin = "moe.nea.firmament.init.MixinPlugin" + val scaffold = """ +{ + "required": true, + "plugin": "moe.nea.firmament.init.MixinPlugin", + "package": "{mixinPackage}", + "compatibilityLevel": "JAVA_17", + "injectors": { + "defaultRequire": 1 + }, + "refmap": "{refmapName}", + "client": {mixins} +} +""" + var rounds = mutableListOf() + + override fun process(resolver: Resolver): List { + return resolver.getSymbolsWithAnnotation("org.spongepowered.asm.mixin.Mixin") + .filter { processElement(it, resolver) }.toList() + } + + override fun finish() { + val output = codeGenerator.createNewFile( + Dependencies( + aggregating = true, + *rounds.map { it.containingFile!! }.toTypedArray()), + "", "firmament.mixins", + extensionName = "json") + val writer = output.writer() + val gson = Gson() + val mixinJson = JsonObject() + mixinJson.addProperty("required", true) + mixinJson.addProperty("plugin", mixinPlugin) + mixinJson.addProperty("package", mixinPackage) + mixinJson.addProperty("compatibilityLevel", "JAVA_21") + mixinJson.addProperty("refmap", refmapName) + val mixinArray = JsonArray() + rounds.map { it.qualifiedName!!.asString().removePrefix("$mixinPackage.") } + .sorted() + .forEach(mixinArray::add) + mixinJson.add("client", mixinArray) + gson.toJson(mixinJson, writer) + writer.close() + rounds + } + + private fun processElement(decl: KSAnnotated, resolver: Resolver): Boolean { + if (decl !is KSClassDeclaration) { + logger.error("@Mixin only allowed on class declarations", decl) + return true + } + decl.qualifiedName ?: logger.error("@Mixin only allowed on classes with a proper name") + if (decl.origin != Origin.JAVA) logger.error("@Mixin only allowed in java code") + val packageName = decl.packageName.asString() + if (packageName != mixinPackage && !packageName.startsWith("$mixinPackage.")) + logger.error("@Mixin outside of mixin package", decl) + rounds.add(decl) + return true + } + + +} diff --git a/symbols/src/main/kotlin/process/SubscribeAnnotationProcessor.kt b/symbols/src/main/kotlin/process/SubscribeAnnotationProcessor.kt index 0999878..6d88b69 100644 --- a/symbols/src/main/kotlin/process/SubscribeAnnotationProcessor.kt +++ b/symbols/src/main/kotlin/process/SubscribeAnnotationProcessor.kt @@ -30,6 +30,7 @@ class SubscribeAnnotationProcessor( val codeGenerator: CodeGenerator, ) : SymbolProcessor { override fun finish() { + subscriptions.sort() val subscriptionSet = subscriptions.mapTo(mutableSetOf()) { it.parent.containingFile!! } val dependencies = Dependencies( aggregating = true, @@ -39,8 +40,7 @@ class SubscribeAnnotationProcessor( .createNewFile(dependencies, "moe.nea.firmament.annotations.generated", "AllSubscriptions") .bufferedWriter() subscriptionsFile.apply { - appendLine("// This file is @generated by SubscribeAnnotationProcessor at ${SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").format( - Date())}") + appendLine("// This file is @generated by SubscribeAnnotationProcessor") appendLine("// Do not edit") for (file in subscriptionSet) { appendLine("// Dependency: ${file.filePath}") @@ -70,7 +70,18 @@ class SubscribeAnnotationProcessor( val parent: KSClassDeclaration, val child: KSFunctionDeclaration, val type: KSType, - ) + ) : Comparable { + override fun compareTo(other: Subscription): Int { + var compare = parent.qualifiedName!!.asString().compareTo(other.parent.qualifiedName!!.asString()) + if (compare != 0) return compare + compare = other.child.simpleName.asString().compareTo(child.simpleName.asString()) + if (compare != 0) return compare + compare = other.type.declaration.qualifiedName!!.asString() + .compareTo(type.declaration.qualifiedName!!.asString()) + if (compare != 0) return compare + return 0 + } + } val subscriptions = mutableListOf() -- cgit