import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import java.util.concurrent.atomic.AtomicBoolean plugins { id "com.github.johnrengelman.shadow" } description = 'dd-java-agent' apply from: "$rootDir/gradle/java.gradle" apply from: "$rootDir/gradle/publish.gradle" configurations { shadowInclude sharedShadowInclude } /* * 7 shadow jars are created * - The main "dd-java-agent" jar that also has the bootstrap project * - 5 jars based on projects (instrumentation, jmxfetch, profiling, appsec, iast) * - 1 based on the shared dependencies * This general config is shared by all of them */ ext.generalShadowJarConfig = { mergeServiceFiles() exclude '**/module-info.class' // Prevents conflict with other SLF4J instances. Important for premain. relocate 'org.slf4j', 'datadog.slf4j' // rewrite dependencies calling Logger.getLogger relocate 'java.util.logging.Logger', 'datadog.trace.bootstrap.PatchLogger' if (!project.hasProperty("disableShadowRelocate") || !disableShadowRelocate) { // shadow OT impl to prevent casts to implementation relocate 'datadog.trace.common', 'datadog.trace.agent.common' relocate 'datadog.trace.core', 'datadog.trace.agent.core' relocate 'datadog.opentracing', 'datadog.trace.agent.ot' // shadow things in internal API that has slf4j in the API and is accessed from core relocate 'datadog.trace.relocate', 'datadog.trace.agent.relocate' } } def includeShadowJar(TaskProvider shadowJarTask, String jarname) { def opentracingFound = new AtomicBoolean() project.processResources { doFirst { eachFile { // We seem unlikely to use this name somewhere else. if (it.path.contains("opentracing") && it.name.contains("Format\$Builtin")) { opentracingFound.set(true) } } } doLast { if (opentracingFound.get()) { throw new GradleException("OpenTracing direct dependency found!") } } from(zipTree(shadowJarTask.get().archiveFile)) { into jarname 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' } } project.tasks.named("processResources").configure { dependsOn shadowJarTask } shadowJarTask.configure generalShadowJarConfig } def includeSubprojShadowJar(String projName, String jarname) { evaluationDependsOn projName def proj = project(projName) includeShadowJar proj.tasks.named("shadowJar"), jarname } includeSubprojShadowJar ':dd-java-agent:instrumentation', 'inst' includeSubprojShadowJar ':dd-java-agent:agent-jmxfetch', 'metrics' includeSubprojShadowJar ':dd-java-agent:agent-profiling', 'profiling' includeSubprojShadowJar ':dd-java-agent:appsec', 'appsec' includeSubprojShadowJar ':dd-java-agent:iast', 'iast' includeSubprojShadowJar ':dd-java-agent:agent-debugger', 'debugger' def sharedShadowJar = tasks.register('sharedShadowJar', ShadowJar) { configurations = [project.configurations.sharedShadowInclude] // Put the jar in a different directory so we don't overwrite the normal shadow jar and // break caching, and also to not interfere with CI scripts that copy everything in the // libs directory it.destinationDirectory.set(file("${project.buildDir}/shared-lib")) // Add a classifier so we don't confuse the jar file with the normal shadow jar archiveClassifier = 'shared' it.dependencies { exclude(project(':dd-java-agent:agent-bootstrap')) exclude(project(':dd-java-agent:agent-logging')) exclude(project(':dd-trace-api')) exclude(project(':internal-api')) exclude(project(':internal-api:internal-api-8')) exclude(project(':utils:time-utils')) exclude(dependency('org.slf4j::')) } } includeShadowJar(sharedShadowJar, 'shared') shadowJar generalShadowJarConfig >> { configurations = [project.configurations.shadowInclude] archiveClassifier = '' manifest { attributes( "Main-Class": "datadog.trace.bootstrap.AgentBootstrap", "Agent-Class": "datadog.trace.bootstrap.AgentBootstrap", "Premain-Class": "datadog.trace.bootstrap.AgentBootstrap", "Can-Redefine-Classes": true, "Can-Retransform-Classes": true, ) } } tasks.register('generateAgentJarIndex', JavaExec) { def contentDir = "${sourceSets.main.output.resourcesDir}" def indexFile = "${contentDir}/dd-java-agent.index" it.group = 'Build' it.description = "Generate dd-java-agent.index" it.inputs.files(contentDir) it.outputs.files(indexFile) it.mainClass = 'datadog.trace.bootstrap.AgentJarIndex$IndexGenerator' it.classpath = project.configurations.shadowInclude it.args = [contentDir] dependsOn 'processResources' dependsOn 'writeVersionNumberFile' } compileJava.dependsOn 'generateAgentJarIndex' subprojects { Project subProj -> // Don't need javadoc task run for internal projects. subProj.tasks.withType(Javadoc).configureEach { enabled = false } } // We don't want bundled dependencies to show up in the pom. tasks.withType(GenerateMavenPom).configureEach { task -> doFirst { task.pom.withXml { XmlProvider provider -> Node dependencies = provider.asNode().dependencies[0] dependencies.children().clear() } } } dependencies { testImplementation(project(':dd-java-agent:agent-bootstrap')) { exclude group: 'com.datadoghq', module: 'agent-logging' } testImplementation project(':dd-trace-api') testImplementation project(':dd-trace-core') testImplementation project(':utils:test-utils') testImplementation deps.testLogging testImplementation deps.guava testImplementation deps.okhttp testImplementation group: 'io.opentracing', name: 'opentracing-util', version: '0.31.0' // Includes for the top level shadow jar shadowInclude project(path: ':dd-java-agent:agent-bootstrap') shadowInclude project(path: ':dd-java-agent:agent-debugger:debugger-bootstrap') // Includes for the shared internal shadow jar sharedShadowInclude deps.shared // force a controlled version of ASM that is used by Debugger while pulled transitively by jnr sharedShadowInclude deps.asm sharedShadowInclude deps.asmcommons sharedShadowInclude project(':communication'), { transitive = false // do not bring along slf4j and dependent subprojects // (which are loaded on the bootstrap cl) } sharedShadowInclude project(':telemetry'), { transitive = false // do not bring along slf4j and dependent subprojects // (which are loaded on the bootstrap cl) } sharedShadowInclude project(':remote-config'), { transitive = false } sharedShadowInclude project(':utils:container-utils'), { transitive = false } sharedShadowInclude project(':utils:socket-utils'), { transitive = false } sharedShadowInclude project(':utils:version-utils'), { transitive = false } sharedShadowInclude project(':utils:process-utils'), { transitive = false } } tasks.withType(Test).configureEach { jvmArgs "-Ddd.service.name=java-agent-tests" jvmArgs "-Ddd.writer.type=LoggingWriter" // Multi-threaded logging seems to be causing deadlocks with Gradle's log capture. // jvmArgs "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=debug" // jvmArgs "-Dorg.slf4j.simpleLogger.defaultLogLevel=debug" doFirst { // Defining here to allow jacoco to be first on the command line. jvmArgs "-javaagent:${shadowJar.archivePath}" } testLogging { events "started" } if (project.hasProperty("disableShadowRelocate") && disableShadowRelocate) { exclude 'datadog/trace/agent/integration/classloading/ShadowPackageRenamingTest.class' } dependsOn "shadowJar" } tasks.register('checkAgentJarSize').configure { doLast { // Arbitrary limit to prevent unintentional increases to the agent jar size // Raise or lower as required assert shadowJar.archiveFile.get().getAsFile().length() < 21 * 1024 * 1024 } dependsOn "shadowJar" } tasks.named('check').configure { dependsOn 'checkAgentJarSize' }