diff options
Diffstat (limited to 'contrib/agent/build.gradle')
-rw-r--r-- | contrib/agent/build.gradle | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/contrib/agent/build.gradle b/contrib/agent/build.gradle new file mode 100644 index 00000000..11271a42 --- /dev/null +++ b/contrib/agent/build.gradle @@ -0,0 +1,246 @@ +plugins { + id 'com.github.johnrengelman.shadow' version '2.0.2' +} + +description = 'OpenCensus Agent' + +def agentPackage = 'io.opencensus.contrib.agent' +def agentMainClass = "${agentPackage}.AgentMain" + +// The package containing the classes that need to be loaded by the bootstrap classloader because +// they are used from classes loaded by the bootstrap classloader. +def agentBootstrapPackage = "${agentPackage}.bootstrap" +def agentBootstrapPackageDir = agentBootstrapPackage.replace('.', '/') + '/' +def agentBootstrapClasses = agentBootstrapPackageDir + '**' + +// The package to which we relocate all third party packages. This avoids any conflicts of the +// agent's classes with the app's classes, which are loaded by the same classloader (the system +// classloader). +def agentRepackaged = "${agentPackage}.deps" + +dependencies { + compileOnly libraries.auto_service + compileOnly libraries.grpc_context + compileOnly project(':opencensus-api') + compile libraries.byte_buddy + compile libraries.config + compile libraries.findbugs_annotations + compile libraries.guava + + signature 'org.codehaus.mojo.signature:java17:1.0@signature' +} + +jar { + manifest { + // Set the required manifest attributes for the Java agent, cf. + // https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html. + attributes 'Premain-Class': agentMainClass + attributes 'Can-Retransform-Classes': true + } +} + +// Create bootstrap.jar containing the classes that need to be loaded by the bootstrap +// classloader. +task bootstrapJar(type: Jar) { + // Output to 'bootstrap.jar'. + baseName = 'bootstrap' + version = null + + from sourceSets.main.output + include agentBootstrapClasses +} + +shadowJar.dependsOn bootstrapJar + +// Bundle the agent's classes and dependencies into a single, self-contained JAR file. +shadowJar { + // Output to opencensus-contrib-agent-VERSION.jar. + classifier = null + + // Include only the following dependencies (excluding transitive dependencies). + dependencies { + include(dependency(libraries.byte_buddy)) + include(dependency(libraries.config)) + include(dependency(libraries.guava)) + } + + // Exclude cruft which still snuck in. + exclude 'META-INF/maven/**' + exclude agentBootstrapClasses + + // Relocate third party packages to avoid any conflicts of the agent's classes with the app's + // classes, which are loaded by the same classloader (the system classloader). + // Byte Buddy: + relocate 'net.bytebuddy', agentRepackaged + '.bytebuddy' + // Config: + relocate 'com.typesafe.config', agentRepackaged + '.config' + // Guava: + relocate 'com.google.common', agentRepackaged + '.guava' + relocate 'com.google.thirdparty.publicsuffix', agentRepackaged + '.publicsuffix' + + doLast { + def agentPackageDir = agentPackage.replace('.', '/') + '/' + def agentBootstrapJar = agentPackageDir + 'bootstrap.jar' + + // Bundle bootstrap.jar. + ant.jar(update: 'true', destfile: shadowJar.archivePath) { + mappedresources { + fileset(file: bootstrapJar.archivePath) + globmapper(from: '*', to: agentBootstrapJar) + } + } + + // Assert that there's nothing obviously wrong with the JAR's contents. + new java.util.zip.ZipFile(shadowJar.archivePath).withCloseable { + // Must have bundled the bootstrap.jar. + assert it.entries().any { it.name == agentBootstrapJar } + + it.entries().each { entry -> + // Must not contain anything outside of ${agentPackage}, ... + assert entry.name.startsWith(agentPackageDir) || + // ... except for the expected entries. + [ agentPackageDir, + 'META-INF/MANIFEST.MF', + 'META-INF/services/io.opencensus.contrib.agent.instrumentation.Instrumenter', + 'reference.conf', + ].any { entry.isDirectory() ? it.startsWith(entry.name) : it == entry.name } + // Also, should not have the bootstrap classes. + assert !entry.name.startsWith(agentBootstrapPackageDir) + } + } + } +} + +jar.finalizedBy shadowJar + +// TODO(stschmidt): Proguard-shrink the agent JAR. + +// Integration tests. The setup was initially based on +// https://www.petrikainulainen.net/programming/gradle/getting-started-with-gradle-integration-testing/. +// We run the same suite of integration tests on different Java versions with the agent enabled. +// The JAVA_HOMES environment variable lists the home directories of the Java installations used +// for integration testing. + +// The default JAR has been replaced with a self-contained JAR by the shadowJar task. Therefore, +// remove all declared dependencies from the generated Maven POM for said JAR. +uploadArchives { + repositories { + mavenDeployer { + pom.whenConfigured { + dependencies = [] + } + } + } +} + +sourceSets { + integrationTest { + java { + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + srcDir file('src/integration-test/java') + } + resources.srcDir file('src/integration-test/resources') + } +} + +configurations { + integrationTestCompile.extendsFrom testCompile + integrationTestRuntime.extendsFrom testRuntime +} + +dependencies { + integrationTestCompile project(':opencensus-api') + integrationTestCompile project(':opencensus-testing') + integrationTestRuntime libraries.grpc_context + integrationTestRuntime project(':opencensus-impl-lite') +} + +// Disable checkstyle for integration tests if not java8. +checkstyleIntegrationTest.enabled = JavaVersion.current().isJava8Compatible() + +// Disable findbugs for integration tests, too. +findbugsIntegrationTest.enabled = false + +def javaExecutables = (System.getenv('JAVA_HOMES') ?: '') + .tokenize(File.pathSeparator) + .plus(System.getProperty('java.home')) + .collect { org.apache.tools.ant.taskdefs.condition.Os.isFamily( + org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS) + ? "${it}/bin/java.exe" + : "${it}/bin/java" } + .collect { new File(it).getCanonicalPath() } + .unique() + +assert javaExecutables.size > 0 : + 'No Java executables found for running integration tests' + +task integrationTest + +javaExecutables.eachWithIndex { javaExecutable, index -> + def perVersionIntegrationTest = task("integrationTest_${index}", type: Test) { + testLogging { + // Let Gradle output the stdout and stderr from tests, too. This is useful for investigating + // test failures on Travis, where we can't view Gradle's test reports. + showStandardStreams = true + + // Include the exception message and full stacktrace for failed tests. + exceptionFormat 'full' + } + + dependsOn shadowJar + + testClassesDirs = sourceSets.integrationTest.output.classesDirs + classpath = sourceSets.integrationTest.runtimeClasspath + + executable = javaExecutable + + // The JaCoCo agent must be specified first so that it can instrument our agent. + // This is a work around for the issue that the JaCoCo agent is added last, cf. + // https://discuss.gradle.org/t/jacoco-gradle-adds-the-agent-last-to-jvm-args/7124. + doFirst { + jvmArgs jacoco.asJvmArg // JaCoCo agent first. + jvmArgs "-javaagent:${shadowJar.archivePath}" // Our agent second. + jacoco.enabled = false // Don't add the JaCoCo agent again. + } + + doFirst { logger.lifecycle("Running integration tests using ${javaExecutable}.") } + } + + integrationTest.dependsOn perVersionIntegrationTest +} + +check.dependsOn integrationTest +integrationTest.mustRunAfter test + +// Merge JaCoCo's execution data from all tests into the main test's execution data file. +task jacocoMerge(type: JacocoMerge) { + tasks.withType(Test).each { testTask -> + dependsOn testTask + executionData testTask.jacoco.destinationFile + } + doLast { + destinationFile.renameTo test.jacoco.destinationFile + } +} + +jacocoTestReport.dependsOn jacocoMerge + +// JMH benchmarks + +dependencies { + jmh libraries.grpc_context +} + +// Make the agent JAR available using a fixed file name so that we don't have to modify the JMH +// benchmarks whenever the version changes. +task agentJar(type: Copy) { + dependsOn shadowJar + + from shadowJar.archivePath + into libsDir + rename { 'agent.jar' } +} + +jmhJar.dependsOn agentJar +jmhJar.dependsOn integrationTest |