xref: /aosp_15_r20/external/opencensus-java/contrib/agent/build.gradle (revision a24ffb47c3166327784aa05b149974e82e8f71b8)
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