-
-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement compile-time access-wideners
- Loading branch information
1 parent
2cd372a
commit 036f928
Showing
9 changed files
with
265 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
134 changes: 134 additions & 0 deletions
134
buildSrc/src/main/kotlin/accesswidener/AccessWidenerPlugin.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package accesswidener | ||
|
||
import net.fabricmc.accesswidener.AccessWidener | ||
import net.fabricmc.accesswidener.AccessWidenerClassVisitor | ||
import net.fabricmc.accesswidener.AccessWidenerReader | ||
import org.gradle.api.Plugin | ||
import org.gradle.api.Project | ||
import org.gradle.kotlin.dsl.* | ||
import org.gradle.kotlin.dsl.exclude | ||
import org.gradle.kotlin.dsl.register | ||
import org.objectweb.asm.ClassReader | ||
import org.objectweb.asm.ClassWriter | ||
import org.objectweb.asm.Opcodes | ||
import java.io.BufferedReader | ||
import java.io.ByteArrayOutputStream | ||
import java.io.InputStreamReader | ||
import java.nio.file.Path | ||
import java.security.MessageDigest | ||
import java.util.HexFormat | ||
import java.util.zip.ZipInputStream | ||
import java.util.zip.ZipOutputStream | ||
import kotlin.io.path.bufferedReader | ||
import kotlin.io.path.exists | ||
import kotlin.io.path.inputStream | ||
import kotlin.io.path.nameWithoutExtension | ||
import kotlin.io.path.notExists | ||
import kotlin.io.path.readBytes | ||
|
||
private const val GROUP_ID = "xyz.xenondevs.nova" | ||
private const val ARTIFACT_ID = "paper-server" | ||
|
||
class AccessWidenerPlugin : Plugin<Project> { | ||
|
||
override fun apply(target: Project) { | ||
val repoDir = target.layout.projectDirectory.dir(".gradle/caches/nova-access-widener/").asFile.toPath() | ||
|
||
target.repositories { | ||
maven(repoDir) { | ||
content { includeModule(GROUP_ID, ARTIFACT_ID) } | ||
} | ||
} | ||
|
||
val accessWidenedServer = target.configurations.create("accessWidenedServer") { | ||
defaultDependencies { | ||
val coordinates = widenAndInstall(target, repoDir) | ||
add(target.dependencies.create(coordinates)) | ||
} | ||
} | ||
|
||
val compileOnly = target.configurations.getByName("compileOnly") | ||
compileOnly.extendsFrom(accessWidenedServer) | ||
compileOnly.exclude("io.papermc.paper", "paper-server") | ||
} | ||
|
||
private fun widenAndInstall(project: Project, repo: Path): String { | ||
val devBundle = readDevBundle(project) | ||
val (_, artifactId, version) = devBundle.mappedServerCoordinates.split(':') | ||
|
||
val mojangMappedServer = project.configurations.getByName("mojangMappedServer") | ||
val serverArtifacts = mojangMappedServer.incoming.artifacts.artifactFiles | ||
val binJar = serverArtifacts.first { it.name == "$artifactId-$version.jar" }.toPath() | ||
val sourcesJar = binJar.parent.resolve(binJar.nameWithoutExtension + "-sources.jar") | ||
|
||
val accessWidener = project.layout.projectDirectory.asFile.toPath().resolve("src/main/resources/nova.accesswidener") | ||
|
||
val hash = hashServer(binJar, accessWidener) | ||
|
||
val coordinates = "$GROUP_ID:$ARTIFACT_ID:$version-$hash" | ||
if (resolveCoordinates(repo, coordinates).notExists()) { | ||
install(repo, coordinates, devBundle.dependencies, applyAccessWideners(accessWidener, binJar), sourcesJar) | ||
println("access-widened server installed") | ||
} else { | ||
println("access-widened server was cached") | ||
} | ||
|
||
return coordinates | ||
} | ||
|
||
private fun readDevBundle(project: Project): DevBundleInfo { | ||
val paperDevBundle = project.configurations.getByName("paperweightDevelopmentBundle") | ||
.incoming.artifacts.artifactFiles.first() | ||
ZipInputStream(paperDevBundle.inputStream()).use { zin -> | ||
generateSequence { zin.nextEntry } | ||
.forEach { entry -> | ||
if (entry.name == "config.json") { | ||
val reader = BufferedReader(InputStreamReader(zin)) | ||
return DevBundleInfo.fromJson(reader) | ||
} | ||
} | ||
} | ||
|
||
throw IllegalStateException("config.json not found in paperweight development bundle") | ||
} | ||
|
||
private fun hashServer(jar: Path, accessWideners: Path): String { | ||
val md = MessageDigest.getInstance("MD5") | ||
md.update(jar.readBytes()) | ||
md.update(accessWideners.readBytes()) | ||
return HexFormat.of().formatHex(md.digest()) | ||
} | ||
|
||
private fun applyAccessWideners(accessWidener: Path, jar: Path): ByteArray { | ||
val widener = readAccessWidenerFile(accessWidener) | ||
|
||
val out = ByteArrayOutputStream() | ||
ZipInputStream(jar.inputStream()).use { zin -> | ||
val zout = ZipOutputStream(out) | ||
generateSequence { zin.nextEntry } | ||
.forEach { entry -> | ||
zout.putNextEntry(entry) | ||
if (entry.name.endsWith(".class")) { | ||
val classReader = ClassReader(zin) | ||
val classWriter = ClassWriter(0) | ||
val widenerVisitor = AccessWidenerClassVisitor.createClassVisitor(Opcodes.ASM9, classWriter, widener) | ||
classReader.accept(widenerVisitor, 0) | ||
zout.write(classWriter.toByteArray()) | ||
} else { | ||
zin.copyTo(zout) | ||
} | ||
zout.closeEntry() | ||
} | ||
zout.close() | ||
} | ||
|
||
return out.toByteArray() | ||
} | ||
|
||
private fun readAccessWidenerFile(file: Path): AccessWidener { | ||
val widener = AccessWidener() | ||
file.bufferedReader().use { reader -> AccessWidenerReader(widener).read(reader) } | ||
return widener | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package accesswidener | ||
|
||
import com.google.gson.JsonObject | ||
import com.google.gson.JsonParser | ||
import xyz.xenondevs.commons.gson.getAllStrings | ||
import xyz.xenondevs.commons.gson.getArray | ||
import xyz.xenondevs.commons.gson.getObject | ||
import xyz.xenondevs.commons.gson.getString | ||
import java.io.Reader | ||
|
||
data class DevBundleInfo( | ||
val mappedServerCoordinates: String, | ||
val dependencies: List<String>, | ||
) { | ||
|
||
companion object { | ||
|
||
fun fromJson(reader: Reader): DevBundleInfo { | ||
val json = JsonParser.parseReader(reader) as JsonObject | ||
val buildData = json.getObject("buildData") | ||
|
||
val dependencies = ArrayList<String>() | ||
dependencies.addAll(buildData.getArray("compileDependencies").getAllStrings()) | ||
dependencies.add(json.getString("apiCoordinates")) | ||
|
||
return DevBundleInfo(json.getString("mappedServerCoordinates"), dependencies) | ||
} | ||
|
||
} | ||
|
||
} |
79 changes: 79 additions & 0 deletions
79
buildSrc/src/main/kotlin/accesswidener/TinyMavenRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package accesswidener | ||
|
||
import java.nio.file.Path | ||
import javax.xml.XMLConstants | ||
import javax.xml.stream.XMLOutputFactory | ||
import javax.xml.stream.XMLStreamWriter | ||
import kotlin.io.path.copyTo | ||
import kotlin.io.path.createDirectories | ||
import kotlin.io.path.deleteIfExists | ||
import kotlin.io.path.exists | ||
import kotlin.io.path.outputStream | ||
import kotlin.io.path.writeBytes | ||
import kotlin.io.use | ||
import kotlin.text.replace | ||
import kotlin.text.split | ||
|
||
fun install( | ||
repo: Path, | ||
coordinates: String, | ||
dependencyCoordinates: List<String>, | ||
jar: ByteArray, | ||
sources: Path | ||
) { | ||
val repoJar = resolveCoordinates(repo, coordinates) | ||
repoJar.parent.createDirectories() | ||
repoJar.deleteIfExists() | ||
repoJar.writeBytes(jar) | ||
|
||
if (sources.exists()) { | ||
val repoSources = resolveCoordinates(repo, coordinates, classifier = "sources") | ||
repoSources.deleteIfExists() | ||
sources.copyTo(repoSources) | ||
} | ||
|
||
val pom = resolveCoordinates(repo, coordinates, packaging = "pom") | ||
pom.outputStream().use { out -> | ||
val writer = XMLOutputFactory.newInstance().createXMLStreamWriter(out) | ||
|
||
writer.writeStartDocument("UTF-8", "1.0"); | ||
writer.writeStartElement("project") | ||
writer.writeNamespace("xsi", XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI) | ||
writer.writeNamespace("", "http://maven.apache.org/POM/4.0.0") | ||
writer.writeAttribute(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "schemaLocation", "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd") | ||
|
||
writer.writeElement("modelVersion", "4.0.0") | ||
writer.writeCoordinates(coordinates) | ||
|
||
writer.writeStartElement("dependencies") | ||
for (dep in dependencyCoordinates) { | ||
writer.writeStartElement("dependency") | ||
writer.writeCoordinates(dep) | ||
writer.writeEndElement() | ||
} | ||
writer.writeEndElement() | ||
|
||
writer.writeEndElement() | ||
writer.writeEndDocument() | ||
} | ||
} | ||
|
||
fun resolveCoordinates(repo: Path, coordinates: String, classifier: String = "", packaging: String = "jar"): Path { | ||
val (group, artifact, version) = coordinates.split(':') | ||
val dir = repo.resolve("${group.replace('.', '/')}/$artifact/$version") | ||
val fileName = if (classifier.isBlank()) "$artifact-$version.$packaging" else "$artifact-$version-$classifier.$packaging" | ||
return dir.resolve(fileName) | ||
} | ||
|
||
private fun XMLStreamWriter.writeElement(name: String, value: String) { | ||
writeStartElement(name) | ||
writeCharacters(value) | ||
writeEndElement() | ||
} | ||
|
||
private fun XMLStreamWriter.writeCoordinates(coordinates: String) { | ||
val (group, artifact, version) = coordinates.split(':') | ||
writeElement("groupId", group) | ||
writeElement("artifactId", artifact) | ||
writeElement("version", version) | ||
} |
1 change: 1 addition & 0 deletions
1
...rc/src/main/kotlin/BuildBundlerJarTask.kt → ...ain/kotlin/bundler/BuildBundlerJarTask.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
package bundler | ||
|
||
import org.gradle.api.DefaultTask | ||
import org.gradle.api.Project | ||
|
1 change: 1 addition & 0 deletions
1
buildSrc/src/main/kotlin/BundlerJarPlugin.kt → ...c/main/kotlin/bundler/BundlerJarPlugin.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
package bundler | ||
|
||
import org.gradle.api.Plugin | ||
import org.gradle.api.Project | ||
|
2 changes: 2 additions & 0 deletions
2
buildSrc/src/main/kotlin/BundlerPlugin.kt → .../src/main/kotlin/bundler/BundlerPlugin.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
package bundler | ||
|
||
import org.gradle.api.Plugin | ||
import org.gradle.api.Project | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
accessWidener v2 nova |