import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jk1.license.filter.LicenseBundleNormalizer import com.github.jk1.license.render.InventoryMarkdownReportRenderer plugins { id("com.github.jk1.dependency-license-report") id("otel.java-conventions") id("otel.publish-conventions") id("io.opentelemetry.instrumentation.javaagent-shadowing") } description = "OpenTelemetry Javaagent" group = "io.opentelemetry.javaagent" // this configuration collects libs that will be placed in the bootstrap classloader val bootstrapLibs by configurations.creating { isCanBeResolved = true isCanBeConsumed = false } // this configuration collects only required instrumentations and agent machinery val baseJavaagentLibs by configurations.creating { isCanBeResolved = true isCanBeConsumed = false } // this configuration collects libs that will be placed in the agent classloader, isolated from the instrumented application code val javaagentLibs by configurations.creating { isCanBeResolved = true isCanBeConsumed = false extendsFrom(baseJavaagentLibs) } // exclude javaagent dependencies from the bootstrap classpath bootstrapLibs.run { exclude("net.bytebuddy") exclude("org.ow2.asm") exclude("io.opentelemetry", "opentelemetry-sdk") exclude("io.opentelemetry", "opentelemetry-sdk-extension-autoconfigure") } // exclude dependencies that are to be placed in bootstrap from agent libs - they won't be added to inst/ listOf(baseJavaagentLibs, javaagentLibs).forEach { it.run { exclude("org.slf4j") exclude("io.opentelemetry", "opentelemetry-api") exclude("io.opentelemetry", "opentelemetry-semconv") } } val licenseReportDependencies by configurations.creating { extendsFrom(bootstrapLibs) } dependencies { bootstrapLibs(project(":instrumentation-api")) bootstrapLibs(project(":instrumentation-api-semconv")) bootstrapLibs(project(":instrumentation-annotations-support")) bootstrapLibs(project(":instrumentation-appender-api-internal")) bootstrapLibs(project(":javaagent-bootstrap")) // extension-api contains both bootstrap packages and agent packages bootstrapLibs(project(":javaagent-extension-api")) baseJavaagentLibs(project(":javaagent-extension-api")) baseJavaagentLibs(project(":javaagent-tooling")) baseJavaagentLibs(project(":muzzle")) // TODO (trask) replace with opentelemetry-instrumentation-annotations baseJavaagentLibs(project(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent")) baseJavaagentLibs(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:javaagent")) baseJavaagentLibs(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.4:javaagent")) baseJavaagentLibs(project(":instrumentation:opentelemetry-instrumentation-api:javaagent")) baseJavaagentLibs(project(":instrumentation:executors:javaagent")) baseJavaagentLibs(project(":instrumentation:internal:internal-class-loader:javaagent")) baseJavaagentLibs(project(":instrumentation:internal:internal-eclipse-osgi-3.6:javaagent")) baseJavaagentLibs(project(":instrumentation:internal:internal-lambda:javaagent")) baseJavaagentLibs(project(":instrumentation:internal:internal-reflection:javaagent")) baseJavaagentLibs(project(":instrumentation:internal:internal-url-class-loader:javaagent")) // concurrentlinkedhashmap-lru and weak-lock-free are copied in to the instrumentation-api module licenseReportDependencies("com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2") licenseReportDependencies("com.blogspot.mydailyjava:weak-lock-free:0.18") // TODO ideally this would be :instrumentation instead of :javaagent-tooling // in case there are dependencies (accidentally) pulled in by instrumentation modules // but I couldn't get that to work licenseReportDependencies(project(":javaagent-tooling")) licenseReportDependencies(project(":javaagent-extension-api")) testCompileOnly(project(":javaagent-bootstrap")) testCompileOnly(project(":javaagent-extension-api")) testImplementation("com.google.guava:guava") testImplementation("io.opentelemetry:opentelemetry-sdk") testImplementation("io.opentracing.contrib.dropwizard:dropwizard-opentracing:0.2.2") } val javaagentDependencies = dependencies // collect all bootstrap and javaagent instrumentation dependencies project(":instrumentation").subprojects { val subProj = this plugins.withId("otel.javaagent-bootstrap") { javaagentDependencies.run { add(bootstrapLibs.name, project(subProj.path)) } } plugins.withId("otel.javaagent-instrumentation") { javaagentDependencies.run { add(javaagentLibs.name, project(subProj.path)) } } } tasks { processResources { from(rootProject.file("licenses")) { into("META-INF/licenses") } } val buildBootstrapLibs by registering(ShadowJar::class) { configurations = listOf(bootstrapLibs) // exclude the agent part of the javaagent-extension-api; these classes will be added in relocate tasks exclude("io/opentelemetry/javaagent/extension/**") duplicatesStrategy = DuplicatesStrategy.EXCLUDE archiveFileName.set("bootstrapLibs.jar") } val relocateBaseJavaagentLibs by registering(ShadowJar::class) { configurations = listOf(baseJavaagentLibs) excludeBootstrapClasses() duplicatesStrategy = DuplicatesStrategy.FAIL archiveFileName.set("baseJavaagentLibs-relocated.jar") } val relocateJavaagentLibs by registering(ShadowJar::class) { configurations = listOf(javaagentLibs) excludeBootstrapClasses() duplicatesStrategy = DuplicatesStrategy.FAIL archiveFileName.set("javaagentLibs-relocated.jar") } // Includes everything needed for OOTB experience val shadowJar by existing(ShadowJar::class) { dependsOn(buildBootstrapLibs) from(zipTree(buildBootstrapLibs.get().archiveFile)) dependsOn(relocateJavaagentLibs) isolateClasses(relocateJavaagentLibs.get().archiveFile) duplicatesStrategy = DuplicatesStrategy.FAIL archiveClassifier.set("") manifest { attributes(jar.get().manifest.attributes) attributes( "Main-Class" to "io.opentelemetry.javaagent.OpenTelemetryAgent", "Agent-Class" to "io.opentelemetry.javaagent.OpenTelemetryAgent", "Premain-Class" to "io.opentelemetry.javaagent.OpenTelemetryAgent", "Can-Redefine-Classes" to true, "Can-Retransform-Classes" to true ) } } // Includes only the agent machinery and required instrumentations val baseJavaagentJar by registering(ShadowJar::class) { dependsOn(buildBootstrapLibs) from(zipTree(buildBootstrapLibs.get().archiveFile)) dependsOn(relocateBaseJavaagentLibs) isolateClasses(relocateBaseJavaagentLibs.get().archiveFile) duplicatesStrategy = DuplicatesStrategy.FAIL archiveClassifier.set("base") manifest { attributes(shadowJar.get().manifest.attributes) } } jar { // Empty jar that cannot be used for anything and isn't published. archiveClassifier.set("dontuse") } val baseJar by configurations.creating { isCanBeConsumed = true isCanBeResolved = false } artifacts { add("baseJar", baseJavaagentJar) } assemble { dependsOn(shadowJar, baseJavaagentJar) } withType().configureEach { dependsOn(shadowJar) jvmArgs("-Dotel.javaagent.debug=true") jvmArgumentProviders.add(JavaagentProvider(shadowJar.flatMap { it.archiveFile })) testLogging { events("started") } } val cleanLicenses by registering(Delete::class) { delete(rootProject.file("licenses")) } named("generateLicenseReport").configure { dependsOn(cleanLicenses) } // Because we reconfigure publishing to only include the shadow jar, the Gradle metadata is not correct. // Since we are fully bundled and have no dependencies, Gradle metadata wouldn't provide any advantage over // the POM anyways so in practice we shouldn't be losing anything. withType().configureEach { enabled = false } } // Don't publish non-shadowed jar (shadowJar is in shadowRuntimeElements) with(components["java"] as AdhocComponentWithVariants) { configurations.forEach { withVariantsFromConfiguration(configurations["apiElements"]) { skip() } withVariantsFromConfiguration(configurations["runtimeElements"]) { skip() } } } licenseReport { outputDir = rootProject.file("licenses").absolutePath renderers = arrayOf(InventoryMarkdownReportRenderer()) configurations = arrayOf(licenseReportDependencies.name) excludeGroups = arrayOf( "io.opentelemetry.instrumentation", "io.opentelemetry.javaagent" ) filters = arrayOf(LicenseBundleNormalizer("$projectDir/license-normalizer-bundle.json", true)) } fun CopySpec.isolateClasses(jar: Provider) { from(zipTree(jar)) { // important to keep prefix "inst" short, as it is prefixed to lots of strings in runtime mem into("inst") rename("(^.*)\\.class\$", "\$1.classdata") // Rename LICENSE file since it clashes with license dir on non-case sensitive FSs (i.e. Mac) rename("""^LICENSE$""", "LICENSE.renamed") exclude("META-INF/INDEX.LIST") exclude("META-INF/*.DSA") exclude("META-INF/*.SF") } } // exclude bootstrap projects from javaagent libs - they won't be added to inst/ fun ShadowJar.excludeBootstrapClasses() { dependencies { exclude(project(":instrumentation-api")) exclude(project(":instrumentation-api-semconv")) exclude(project(":instrumentation-annotations-support")) exclude(project(":instrumentation-appender-api-internal")) exclude(project(":javaagent-bootstrap")) } // exclude the bootstrap part of the javaagent-extension-api exclude("io/opentelemetry/javaagent/bootstrap/**") } class JavaagentProvider( @InputFile @PathSensitive(PathSensitivity.RELATIVE) val agentJar: Provider ) : CommandLineArgumentProvider { override fun asArguments(): Iterable = listOf( "-javaagent:${file(agentJar).absolutePath}" ) }