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.grpc_context 24 compileOnly project(':opencensus-api') 25 compile libraries.byte_buddy 26 compile libraries.config 27 compile libraries.findbugs_annotations 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 checkstyle for integration tests if not java8. 160checkstyleIntegrationTest.enabled = JavaVersion.current().isJava8Compatible() 161 162// Disable findbugs for integration tests, too. 163findbugsIntegrationTest.enabled = false 164 165def javaExecutables = (System.getenv('JAVA_HOMES') ?: '') 166 .tokenize(File.pathSeparator) 167 .plus(System.getProperty('java.home')) 168 .collect { org.apache.tools.ant.taskdefs.condition.Os.isFamily( 169 org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS) 170 ? "${it}/bin/java.exe" 171 : "${it}/bin/java" } 172 .collect { new File(it).getCanonicalPath() } 173 .unique() 174 175assert javaExecutables.size > 0 : 176 'No Java executables found for running integration tests' 177 178task integrationTest 179 180javaExecutables.eachWithIndex { javaExecutable, index -> 181 def perVersionIntegrationTest = task("integrationTest_${index}", type: Test) { 182 testLogging { 183 // Let Gradle output the stdout and stderr from tests, too. This is useful for investigating 184 // test failures on Travis, where we can't view Gradle's test reports. 185 showStandardStreams = true 186 187 // Include the exception message and full stacktrace for failed tests. 188 exceptionFormat 'full' 189 } 190 191 dependsOn shadowJar 192 193 testClassesDirs = sourceSets.integrationTest.output.classesDirs 194 classpath = sourceSets.integrationTest.runtimeClasspath 195 196 executable = javaExecutable 197 198 // The JaCoCo agent must be specified first so that it can instrument our agent. 199 // This is a work around for the issue that the JaCoCo agent is added last, cf. 200 // https://discuss.gradle.org/t/jacoco-gradle-adds-the-agent-last-to-jvm-args/7124. 201 doFirst { 202 jvmArgs jacoco.asJvmArg // JaCoCo agent first. 203 jvmArgs "-javaagent:${shadowJar.archivePath}" // Our agent second. 204 jacoco.enabled = false // Don't add the JaCoCo agent again. 205 } 206 207 doFirst { logger.lifecycle("Running integration tests using ${javaExecutable}.") } 208 } 209 210 integrationTest.dependsOn perVersionIntegrationTest 211} 212 213check.dependsOn integrationTest 214integrationTest.mustRunAfter test 215 216// Merge JaCoCo's execution data from all tests into the main test's execution data file. 217task jacocoMerge(type: JacocoMerge) { 218 tasks.withType(Test).each { testTask -> 219 dependsOn testTask 220 executionData testTask.jacoco.destinationFile 221 } 222 doLast { 223 destinationFile.renameTo test.jacoco.destinationFile 224 } 225} 226 227jacocoTestReport.dependsOn jacocoMerge 228 229// JMH benchmarks 230 231dependencies { 232 jmh libraries.grpc_context 233} 234 235// Make the agent JAR available using a fixed file name so that we don't have to modify the JMH 236// benchmarks whenever the version changes. 237task agentJar(type: Copy) { 238 dependsOn shadowJar 239 240 from shadowJar.archivePath 241 into libsDir 242 rename { 'agent.jar' } 243} 244 245jmhJar.dependsOn agentJar 246jmhJar.dependsOn integrationTest 247