diff --git a/build.gradle.kts b/build.gradle.kts index cedd7d8..d16f43b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,8 @@ plugins { java + id("shader") `maven-publish` signing - id("com.gradleup.shadow") id("pl.allegro.tech.build.axion-release") id("io.github.gradle-nexus.publish-plugin") } @@ -31,25 +31,33 @@ dependencies { embed(project(":utils")) embed(project(":class-inject")) embed(project(":class-match")) + embed(libs.asm) - implementation(libs.asm) - + // to satisfy javadoc + compileOnly(libs.asm) compileOnly(libs.spotbugs.annotations) } -// collect all subproject output into a single jar -fun allSources(): List { - return subprojects.filter { it.name != "testing" }.map { it.sourceSets.main.get().allSource } +// relocate embedded copy of asm to avoid clashes on classpath +shader { + relocate("org/objectweb/" to "datadog/instrument/") } + +// collect all subproject output into a single jar tasks.jar { dependsOn(embed) from(embed.map { zipTree(it) }) + // drop this file when embedding asm + exclude("module-info.class") +} +fun allSources(): List { + return subprojects.filter { it.name != "testing" }.map { it.sourceSets.main.get().allSource } } tasks.javadoc { dependsOn(embed) setSource(allSources()) exclude("datadog/instrument/glue", "datadog/instrument/utils/JVM.java") - var javadocOptions = (options as StandardJavadocDocletOptions) + val javadocOptions = (options as StandardJavadocDocletOptions) if (JavaVersion.current().isJava9Compatible) { javadocOptions.addBooleanOption("html5", true) } @@ -61,16 +69,6 @@ tasks.named("sourcesJar") { from(allSources()) } -// produce "-all" jar with a shaded copy of ASM -tasks.shadowJar { - dependsOn(embed) - from(embed.map { zipTree(it) }) - relocate("org.objectweb.asm", "datadog.instrument.asm") -} -tasks.assemble { - dependsOn(tasks.shadowJar) -} - publishing { publications { create("maven") { @@ -97,16 +95,6 @@ publishing { developerConnection = "scm:git@github.com:datadog/dd-instrument-java.git" url = "https://github.com/datadog/dd-instrument-java" } - withXml { - // mark ASM dependency as optional - var doc = asElement().ownerDocument - var deps = doc.getElementsByTagName("dependency") - for (i in 0 ..< deps.length) { - var optional = doc.createElement("optional") - optional.textContent = "true" - deps.item(i).appendChild(optional) - } - } } } } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 6f6599b..3de5399 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -9,7 +9,7 @@ repositories { dependencies { // needed to re-use the 'libs' version catalog between the main project and buildSrc implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) - implementation(libs.shadow) + implementation(libs.asm.commons) implementation(libs.spotless) implementation(libs.spotbugs) implementation(libs.axion.release) diff --git a/buildSrc/src/main/kotlin/shader.gradle.kts b/buildSrc/src/main/kotlin/shader.gradle.kts new file mode 100644 index 0000000..5dab640 --- /dev/null +++ b/buildSrc/src/main/kotlin/shader.gradle.kts @@ -0,0 +1,88 @@ +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.Opcodes +import org.objectweb.asm.commons.ClassRemapper +import org.objectweb.asm.commons.Remapper +import java.io.FilterReader +import java.io.Reader +import java.io.StringReader +import java.nio.charset.StandardCharsets + +// support configuration of relocations via extension +extensions.create("shader", Shader::class) + +// automatically apply shading to the main jar task +tasks.named("jar").configure { + // prune away unused directories after relocation + exclude { it.isDirectory } + // apply relocations as we add resources to the jar + eachFile(Shader()) + // this charset supports safe filtering of binary content, as chars map 1:1 to bytes + filteringCharset = "ISO-8859-1" +} + +/** Applies configured relocations as resources are copied into the jar. */ +open class Shader : Action { + companion object { + var relocations: Map = mapOf() + } + + // accepts relocations from build.gradle.kts + fun relocate(entry: Pair) { + relocations += entry + } + + override fun execute(t: FileCopyDetails) { + var resourcePath: String = t.path + // relocate any class-file references + if (resourcePath.endsWith(".class")) { + t.filter(ShadeClass::class) + } + // relocate file-name for consistency + relocations.forEach { (oldPath, newPath) -> + resourcePath = resourcePath.replace(oldPath, newPath) + } + t.path = resourcePath + } +} + +/** Filters class-files using remapper from asm-commons. */ +class ShadeClass(reader: Reader) : FilterReader(shade(reader)) { + companion object { + fun shade(reader: Reader): Reader { + val remapper = object : Remapper(Opcodes.ASM9) { + override fun map(internalName: String?): String? { + var result: String? = internalName + Shader.relocations.forEach { (oldPath, newPath) -> + result = result?.replace(oldPath, newPath) + } + return result + } + + override fun mapValue(value: Any?): Any? { + return if (value is String) { + map(value) + } else { + super.mapValue(value) + } + } + } + // rebuild constant-pool from scratch and make sure every method has a stack-map + val cw = ClassWriter(ClassWriter.COMPUTE_FRAMES) + // apply relocation to class-file and upgrade pre-Java 8 class-files to Java 8 + val cr = ClassReader(reader.readText().toByteArray(StandardCharsets.ISO_8859_1)) + cr.accept(ClassRemapper(ToJava8(cw), remapper), 0) + return StringReader(String(cw.toByteArray(), StandardCharsets.ISO_8859_1)) + } + } +} + +/** Upgrades pre-Java 8 class-files to Java 8, to allow faster verification at runtime. */ +class ToJava8(cv: ClassVisitor) : ClassVisitor(Opcodes.ASM9, cv) { + override fun visit( + version: Int, access: Int, name: String?, signature: String?, superName: String?, interfaces: Array? + ) { + super.visit(version.coerceAtLeast(Opcodes.V1_8), access, name, signature, superName, interfaces) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e3b68d3..d41178c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,5 @@ [versions] asm = "9.9" -shadow = "9.2.2" junit-jupiter = "5.13.4" junit-platform = "1.13.4" assertj = "3.27.5" @@ -13,7 +12,7 @@ jmh-plugin = "0.7.3" [libraries] asm = { module = "org.ow2.asm:asm", version.ref = "asm" } -shadow = { module = "com.gradleup.shadow:shadow-gradle-plugin", version.ref = "shadow" } +asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" } junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } junit-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junit-platform" } assertj-core = { module = "org.assertj:assertj-core", version.ref = "assertj" }