1plugins { 2 id 'com.github.johnrengelman.shadow' version '2.0.2' 3} 4 5description = 'OpenCensus Agent' 6 7def agentPackage = 'io.opencensus.contrib.agent' 8def agentMainClass = "${agentPackage}.AgentMain" 9 10// The package containing the classes that need to be loaded by the bootstrap classloader because 11// they are used from classes loaded by the bootstrap classloader. 12def agentBootstrapPackage = "${agentPackage}.bootstrap" 13def agentBootstrapPackageDir = agentBootstrapPackage.replace('.', '/') + '/' 14def agentBootstrapClasses = agentBootstrapPackageDir + '**' 15 16// The package to which we relocate all third party packages. This avoids any conflicts of the 17// agent's classes with the app's classes, which are loaded by the same classloader (the system 18// classloader). 19def agentRepackaged = "${agentPackage}.deps" 20 21dependencies { 22 compileOnly libraries.auto_service 23 compileOnly libraries.findbugs_annotations 24 compileOnly libraries.grpc_context 25 compileOnly project(':opencensus-api') 26 compile libraries.byte_buddy 27 compile libraries.config 28 compile libraries.guava 29 30 signature 'org.codehaus.mojo.signature:java17:1.0@signature' 31} 32 33jar { 34 manifest { 35 // Set the required manifest attributes for the Java agent, cf. 36 // https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html. 37 attributes 'Premain-Class': agentMainClass 38 attributes 'Can-Retransform-Classes': true 39 } 40} 41 42// Create bootstrap.jar containing the classes that need to be loaded by the bootstrap 43// classloader. 44task bootstrapJar(type: Jar) { 45 // Output to 'bootstrap.jar'. 46 baseName = 'bootstrap' 47 version = null 48 49 from sourceSets.main.output 50 include agentBootstrapClasses 51} 52 53shadowJar.dependsOn bootstrapJar 54 55// Bundle the agent's classes and dependencies into a single, self-contained JAR file. 56shadowJar { 57 // Output to opencensus-contrib-agent-VERSION.jar. 58 classifier = null 59 60 // Include only the following dependencies (excluding transitive dependencies). 61 dependencies { 62 include(dependency(libraries.byte_buddy)) 63 include(dependency(libraries.config)) 64 include(dependency(libraries.guava)) 65 } 66 67 // Exclude cruft which still snuck in. 68 exclude 'META-INF/maven/**' 69 exclude agentBootstrapClasses 70 71 // Relocate third party packages to avoid any conflicts of the agent's classes with the app's 72 // classes, which are loaded by the same classloader (the system classloader). 73 // Byte Buddy: 74 relocate 'net.bytebuddy', agentRepackaged + '.bytebuddy' 75 // Config: 76 relocate 'com.typesafe.config', agentRepackaged + '.config' 77 // Guava: 78 relocate 'com.google.common', agentRepackaged + '.guava' 79 relocate 'com.google.thirdparty.publicsuffix', agentRepackaged + '.publicsuffix' 80 81 doLast { 82 def agentPackageDir = agentPackage.replace('.', '/') + '/' 83 def agentBootstrapJar = agentPackageDir + 'bootstrap.jar' 84 85 // Bundle bootstrap.jar. 86 ant.jar(update: 'true', destfile: shadowJar.archivePath) { 87 mappedresources { 88 fileset(file: bootstrapJar.archivePath) 89 globmapper(from: '*', to: agentBootstrapJar) 90 } 91 } 92 93 // Assert that there's nothing obviously wrong with the JAR's contents. 94 new java.util.zip.ZipFile(shadowJar.archivePath).withCloseable { 95 // Must have bundled the bootstrap.jar. 96 assert it.entries().any { it.name == agentBootstrapJar } 97 98 it.entries().each { entry -> 99 // Must not contain anything outside of ${agentPackage}, ... 100 assert entry.name.startsWith(agentPackageDir) || 101 // ... except for the expected entries. 102 [ agentPackageDir, 103 'META-INF/MANIFEST.MF', 104 'META-INF/services/io.opencensus.contrib.agent.instrumentation.Instrumenter', 105 'reference.conf', 106 ].any { entry.isDirectory() ? it.startsWith(entry.name) : it == entry.name } 107 // Also, should not have the bootstrap classes. 108 assert !entry.name.startsWith(agentBootstrapPackageDir) 109 } 110 } 111 } 112} 113 114jar.finalizedBy shadowJar 115 116// TODO(stschmidt): Proguard-shrink the agent JAR. 117 118// Integration tests. The setup was initially based on 119// https://www.petrikainulainen.net/programming/gradle/getting-started-with-gradle-integration-testing/. 120// We run the same suite of integration tests on different Java versions with the agent enabled. 121// The JAVA_HOMES environment variable lists the home directories of the Java installations used 122// for integration testing. 123 124// The default JAR has been replaced with a self-contained JAR by the shadowJar task. Therefore, 125// remove all declared dependencies from the generated Maven POM for said JAR. 126uploadArchives { 127 repositories { 128 mavenDeployer { 129 pom.whenConfigured { 130 dependencies = [] 131 } 132 } 133 } 134} 135 136sourceSets { 137 integrationTest { 138 java { 139 compileClasspath += main.output + test.output 140 runtimeClasspath += main.output + test.output 141 srcDir file('src/integration-test/java') 142 } 143 resources.srcDir file('src/integration-test/resources') 144 } 145} 146 147configurations { 148 integrationTestCompile.extendsFrom testCompile 149 integrationTestRuntime.extendsFrom testRuntime 150} 151 152dependencies { 153 integrationTestCompile project(':opencensus-api') 154 integrationTestCompile project(':opencensus-testing') 155 integrationTestRuntime libraries.grpc_context 156 integrationTestRuntime project(':opencensus-impl-lite') 157} 158 159// Disable findbugs for integration tests, too. 160findbugsIntegrationTest.enabled = false 161 162def javaExecutables = (System.getenv('JAVA_HOMES') ?: '') 163 .tokenize(File.pathSeparator) 164 .plus(System.getProperty('java.home')) 165 .collect { org.apache.tools.ant.taskdefs.condition.Os.isFamily( 166 org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS) 167 ? "${it}/bin/java.exe" 168 : "${it}/bin/java" } 169 .collect { new File(it).getCanonicalPath() } 170 .unique() 171 172assert javaExecutables.size > 0 : 173 'No Java executables found for running integration tests' 174 175task integrationTest 176 177javaExecutables.eachWithIndex { javaExecutable, index -> 178 def perVersionIntegrationTest = task("integrationTest_${index}", type: Test) { 179 testLogging { 180 // Let Gradle output the stdout and stderr from tests, too. This is useful for investigating 181 // test failures on Travis, where we can't view Gradle's test reports. 182 showStandardStreams = true 183 184 // Include the exception message and full stacktrace for failed tests. 185 exceptionFormat 'full' 186 } 187 188 dependsOn shadowJar 189 190 testClassesDirs = sourceSets.integrationTest.output.classesDirs 191 classpath = sourceSets.integrationTest.runtimeClasspath 192 193 executable = javaExecutable 194 195 // The JaCoCo agent must be specified first so that it can instrument our agent. 196 // This is a work around for the issue that the JaCoCo agent is added last, cf. 197 // https://discuss.gradle.org/t/jacoco-gradle-adds-the-agent-last-to-jvm-args/7124. 198 doFirst { 199 jvmArgs jacoco.asJvmArg // JaCoCo agent first. 200 jvmArgs "-javaagent:${shadowJar.archivePath}" // Our agent second. 201 jacoco.enabled = false // Don't add the JaCoCo agent again. 202 } 203 204 doFirst { logger.lifecycle("Running integration tests using ${javaExecutable}.") } 205 } 206 207 integrationTest.dependsOn perVersionIntegrationTest 208} 209 210check.dependsOn integrationTest 211integrationTest.mustRunAfter test 212 213// Merge JaCoCo's execution data from all tests into the main test's execution data file. 214task jacocoMerge(type: JacocoMerge) { 215 tasks.withType(Test).each { testTask -> 216 dependsOn testTask 217 executionData testTask.jacoco.destinationFile 218 } 219 doLast { 220 destinationFile.renameTo test.jacoco.destinationFile 221 } 222} 223 224jacocoTestReport.dependsOn jacocoMerge 225 226// JMH benchmarks 227 228dependencies { 229 jmh libraries.grpc_context 230} 231 232// Make the agent JAR available using a fixed file name so that we don't have to modify the JMH 233// benchmarks whenever the version changes. 234task agentJar(type: Copy) { 235 dependsOn shadowJar 236 237 from shadowJar.archivePath 238 into libsDir 239 rename { 'agent.jar' } 240} 241 242jmhJar.dependsOn agentJar 243jmhJar.dependsOn integrationTest 244