Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kotlin migration part 7 #1026

Merged
merged 3 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package com.github.jengelman.gradle.plugins.shadow.transformers

import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp
import java.io.BufferedInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import org.apache.tools.zip.ZipEntry
import org.apache.tools.zip.ZipOutputStream
import org.codehaus.plexus.util.xml.XmlStreamReader
import org.codehaus.plexus.util.xml.XmlStreamWriter
import org.codehaus.plexus.util.xml.Xpp3Dom
import org.codehaus.plexus.util.xml.Xpp3DomBuilder
import org.codehaus.plexus.util.xml.Xpp3DomWriter
import org.gradle.api.file.FileTreeElement

/**
* A resource processor that aggregates plexus `components.xml` files.
*
* Modified from `org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer.java`
*
* @author John Engelman
*/
open class ComponentsXmlResourceTransformer : Transformer {
private val components = mutableMapOf<String, Xpp3Dom>()

override fun canTransformResource(element: FileTreeElement): Boolean {
return COMPONENTS_XML_PATH == element.relativePath.pathString
}

override fun transform(context: TransformerContext) {
val newDom = try {
val bis = object : BufferedInputStream(context.inputStream) {
@Throws(IOException::class)
override fun close() {
// leave ZIP open
}
}
Xpp3DomBuilder.build(XmlStreamReader(bis))
} catch (e: Exception) {
throw IOException("Error parsing components.xml in ${context.inputStream}", e)
}

// Only try to merge in components if there are some elements in the component-set
if (newDom.getChild("components") == null) return

val children = newDom.getChild("components").getChildren("component")
for (component in children) {
var role: String? = getValue(component, "role")
role = getRelocatedClass(role, context)
setValue(component, "role", role)

val roleHint = getValue(component, "role-hint")

var impl: String? = getValue(component, "implementation")
impl = getRelocatedClass(impl, context)
setValue(component, "implementation", impl)

val key = "$role:$roleHint"
// TODO: use the tools in Plexus to merge these properly. For now, I just need an all-or-nothing
// configuration carry over
components[key]?.getChild("configuration")?.let {
component.addChild(it)
}

val requirements = component.getChild("requirements")
if (requirements != null && requirements.childCount > 0) {
for (r in requirements.childCount - 1 downTo 0) {
val requirement = requirements.getChild(r)
var requiredRole: String? = getValue(requirement, "role")
requiredRole = getRelocatedClass(requiredRole, context)
setValue(requirement, "role", requiredRole)
}
}
components[key] = component
}
}

override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) {
val entry = ZipEntry(COMPONENTS_XML_PATH)
entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time)
os.putNextEntry(entry)

transformedResource.inputStream().use {
it.copyTo(os)
}
components.clear()
}

override fun hasTransformedResource(): Boolean = components.isNotEmpty()

@get:Throws(IOException::class)
private val transformedResource: ByteArray
get() {
val os = ByteArrayOutputStream(1024 * 4)
XmlStreamWriter(os).use { writer ->
val dom = Xpp3Dom("component-set")
val componentDom = Xpp3Dom("components")
dom.addChild(componentDom)
for (component in components.values) {
componentDom.addChild(component)
}
Xpp3DomWriter.write(writer, dom)
}
return os.toByteArray()
}

companion object {
const val COMPONENTS_XML_PATH: String = "META-INF/plexus/components.xml"

private fun getRelocatedClass(className: String?, context: TransformerContext): String? {
val stats = context.stats
if (!className.isNullOrEmpty()) {
for (relocator in context.relocators) {
if (relocator.canRelocateClass(className)) {
val relocateClassContext = RelocateClassContext(className, stats)
return relocator.relocateClass(relocateClassContext)
}
}
}
return className
}

private fun getValue(dom: Xpp3Dom, element: String): String {
return dom.getChild(element).value.orEmpty()
}

private fun setValue(dom: Xpp3Dom, element: String, value: String?) {
val child = dom.getChild(element)
if (child == null || value.isNullOrEmpty()) return
child.value = value
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.github.jengelman.gradle.plugins.shadow.transformers

import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.IOException
import java.io.InputStream
import org.apache.tools.zip.ZipEntry
import org.apache.tools.zip.ZipOutputStream
import org.gradle.api.file.FileTreeElement
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.util.PatternFilterable
import org.gradle.api.tasks.util.PatternSet

/**
* Modified from `org.apache.maven.plugins.shade.resource.ServiceResourceTransformer.java`
*
* Resources transformer that appends entries in `META-INF/services` resources into
* a single resource. For example, if there are several `META-INF/services/org.apache.maven.project.ProjectBuilder`
* resources spread across many JARs the individual entries will all be concatenated into a single
* `META-INF/services/org.apache.maven.project.ProjectBuilder` resource packaged into the resultant JAR produced
* by the shading process.
*
* @author jvanzyl
* @author Charlie Knudsen
* @author John Engelman
*/
@CacheableTransformer
open class ServiceFileTransformer(
private val patternSet: PatternSet = PatternSet()
.include(SERVICES_PATTERN)
.exclude(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN),
) : Transformer,
PatternFilterable by patternSet {
private val serviceEntries = mutableMapOf<String, ServiceStream>()

override fun canTransformResource(element: FileTreeElement): Boolean {
val target = if (element is ShadowCopyAction.ArchiveFileTreeElement) element.asFileTreeElement() else element
return patternSet.asSpec.isSatisfiedBy(target)
}

override fun transform(context: TransformerContext) {
val lines = context.inputStream.bufferedReader().readLines().toMutableList()
var targetPath = context.path
context.relocators.forEach { rel ->
if (rel.canRelocateClass(File(targetPath).name)) {
val classContext = RelocateClassContext.builder().className(targetPath).stats(context.stats).build()
targetPath = rel.relocateClass(classContext)
}
lines.forEachIndexed { i, line ->
if (rel.canRelocateClass(line)) {
val lineContext = RelocateClassContext.builder().className(line).stats(context.stats).build()
lines[i] = rel.relocateClass(lineContext)
}
}
}
lines.forEach { line ->
serviceEntries[targetPath] = serviceEntries.getOrDefault(targetPath, ServiceStream()).apply {
append(ByteArrayInputStream(line.toByteArray()))
}
}
}

override fun hasTransformedResource(): Boolean = serviceEntries.isNotEmpty()

override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) {
serviceEntries.forEach { (path, stream) ->
val entry = ZipEntry(path)
entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time)
os.putNextEntry(entry)
stream.toInputStream().use {
it.copyTo(os)
}
os.closeEntry()
}
}

/**
* {@inheritDoc}
*/
@Input
override fun getIncludes(): Set<String> = patternSet.includes

/**
* {@inheritDoc}
*/
@Input
override fun getExcludes(): Set<String> = patternSet.excludes

open fun setPath(path: String): PatternFilterable = apply {
patternSet.setIncludes(listOf("$path/**"))
}

open class ServiceStream : ByteArrayOutputStream(1024) {
@Throws(IOException::class)
open fun append(inputStream: InputStream) {
if (count > 0 && buf[count - 1] != '\n'.code.toByte() && buf[count - 1] != '\r'.code.toByte()) {
val newline = "\n".toByteArray()
write(newline, 0, newline.size)
}
inputStream.use {
it.copyTo(this)
}
}

open fun toInputStream(): InputStream = ByteArrayInputStream(buf, 0, count)
}

private companion object {
private const val SERVICES_PATTERN = "META-INF/services/**"
private const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN =
"META-INF/services/org.codehaus.groovy.runtime.ExtensionModule"
}
}
Loading