Prepare a single test bootstrap jar at top level. (#1635)
* Prepare a single test bootstrap jar at top level. * Apply suggestions from code review Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com> * Add comment * Use buildDir instead of build * Testing SDK also in bootstrap * Remove sdk from testing bootstrap loader Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
parent
8e48aa2025
commit
9ac34bce67
|
@ -14,7 +14,7 @@ plugins {
|
|||
|
||||
id 'com.dorongold.task-tree' version '1.5'
|
||||
|
||||
id "com.github.johnrengelman.shadow" version "6.0.0"
|
||||
id "com.github.johnrengelman.shadow" version "6.1.0"
|
||||
|
||||
id "com.diffplug.spotless" version "5.6.1"
|
||||
id "com.github.spotbugs" version "4.5.1"
|
||||
|
|
|
@ -5,21 +5,13 @@
|
|||
|
||||
package io.opentelemetry.instrumentation.gradle;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Callable;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.file.DuplicatesStrategy;
|
||||
import org.gradle.api.plugins.JavaLibraryPlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.tasks.Internal;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.api.tasks.testing.Test;
|
||||
import org.gradle.jvm.tasks.Jar;
|
||||
import org.gradle.process.CommandLineArgumentProvider;
|
||||
|
||||
/**
|
||||
|
@ -29,106 +21,22 @@ import org.gradle.process.CommandLineArgumentProvider;
|
|||
// TODO(anuraaga): Migrate more build logic into this plugin to avoid having two places for it.
|
||||
public class AutoInstrumentationPlugin implements Plugin<Project> {
|
||||
|
||||
/**
|
||||
* An exact copy of {@code
|
||||
* io.opentelemetry.javaagent.tooling.Constants#BOOTSTRAP_PACKAGE_PREFIXES}. We can't reference it
|
||||
* directly since this file needs to be compiled before the other packages.
|
||||
*/
|
||||
public static final String[] BOOTSTRAP_PACKAGE_PREFIXES_COPY = {
|
||||
"io.opentelemetry.javaagent.common.exec",
|
||||
"io.opentelemetry.javaagent.slf4j",
|
||||
"io.opentelemetry.javaagent.bootstrap",
|
||||
"io.opentelemetry.javaagent.shaded",
|
||||
"io.opentelemetry.javaagent.instrumentation.api",
|
||||
};
|
||||
|
||||
// Aditional classes we need only for tests and aren't shared with the agent business logic.
|
||||
private static final String[] TEST_BOOTSTRAP_PREFIXES;
|
||||
|
||||
static {
|
||||
String[] testBS = {
|
||||
"io.opentelemetry.instrumentation.api",
|
||||
"io.opentelemetry.api", // OpenTelemetry API
|
||||
"io.opentelemetry.context", // OpenTelemetry API
|
||||
"org.slf4j",
|
||||
"ch.qos.logback",
|
||||
// Tomcat's servlet classes must be on boostrap
|
||||
// when running tomcat test
|
||||
"javax.servlet.ServletContainerInitializer",
|
||||
"javax.servlet.ServletContext"
|
||||
};
|
||||
TEST_BOOTSTRAP_PREFIXES =
|
||||
Arrays.copyOf(
|
||||
BOOTSTRAP_PACKAGE_PREFIXES_COPY,
|
||||
BOOTSTRAP_PACKAGE_PREFIXES_COPY.length + testBS.length);
|
||||
System.arraycopy(
|
||||
testBS, 0, TEST_BOOTSTRAP_PREFIXES, BOOTSTRAP_PACKAGE_PREFIXES_COPY.length, testBS.length);
|
||||
for (int i = 0; i < TEST_BOOTSTRAP_PREFIXES.length; i++) {
|
||||
TEST_BOOTSTRAP_PREFIXES[i] = TEST_BOOTSTRAP_PREFIXES[i].replace('.', '/');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().apply(JavaLibraryPlugin.class);
|
||||
|
||||
project
|
||||
.getTasks()
|
||||
.withType(
|
||||
Test.class,
|
||||
task -> {
|
||||
TaskProvider<Jar> bootstrapJar =
|
||||
project.getTasks().register(task.getName() + "BootstrapJar", Jar.class);
|
||||
|
||||
Configuration testClasspath =
|
||||
project.getConfigurations().findByName(task.getName() + "RuntimeClasspath");
|
||||
if (testClasspath == null) {
|
||||
// Same classpath as default test task
|
||||
testClasspath =
|
||||
project
|
||||
.getConfigurations()
|
||||
.findByName(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
}
|
||||
|
||||
String bootstrapJarName = task.getName() + "-bootstrap.jar";
|
||||
|
||||
Configuration testClasspath0 = testClasspath;
|
||||
bootstrapJar.configure(
|
||||
jar -> {
|
||||
jar.dependsOn(testClasspath0.getBuildDependencies());
|
||||
jar.getArchiveFileName().set(bootstrapJarName);
|
||||
jar.setIncludeEmptyDirs(false);
|
||||
// Classpath is ordered in priority, but later writes into the JAR would take
|
||||
// priority, so we exclude the later ones (we need this to make sure logback is
|
||||
// picked up).
|
||||
jar.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE);
|
||||
jar.from(
|
||||
project.files(
|
||||
// Needs to be a Callable so it's executed lazily at runtime, instead of
|
||||
// configuration time where the classpath may still be getting built up.
|
||||
(Callable<?>)
|
||||
() ->
|
||||
testClasspath0.resolve().stream()
|
||||
.filter(
|
||||
file ->
|
||||
!file.isDirectory()
|
||||
&& file.getName().endsWith(".jar"))
|
||||
.map(project::zipTree)
|
||||
.collect(toList())));
|
||||
|
||||
jar.eachFile(
|
||||
file -> {
|
||||
if (!isBootstrapClass(file.getPath())) {
|
||||
file.exclude();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
task.dependsOn(bootstrapJar);
|
||||
task.getJvmArgumentProviders()
|
||||
.add(
|
||||
new InstrumentationTestArgs(
|
||||
new File(project.getBuildDir(), "libs/" + bootstrapJarName)));
|
||||
task.dependsOn(":testing-bootstrap:shadowJar");
|
||||
File testingBootstrapJar =
|
||||
new File(
|
||||
project.project(":testing-bootstrap").getBuildDir(),
|
||||
"libs/testing-bootstrap.jar");
|
||||
// Make sure tests get rerun if the contents of the testing-bootstrap.jar change
|
||||
task.getInputs().property("testing-bootstrap-jar", testingBootstrapJar);
|
||||
task.getJvmArgumentProviders().add(new InstrumentationTestArgs(testingBootstrapJar));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -150,13 +58,4 @@ public class AutoInstrumentationPlugin implements Plugin<Project> {
|
|||
"-Xbootclasspath/a:" + bootstrapJar.getAbsolutePath(), "-Dnet.bytebuddy.raw=true");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isBootstrapClass(String filePath) {
|
||||
for (String testBootstrapPrefix : TEST_BOOTSTRAP_PREFIXES) {
|
||||
if (filePath.startsWith(testBootstrapPrefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,10 @@ shadowJar {
|
|||
|
||||
mergeServiceFiles()
|
||||
|
||||
relocate "com.google", "io.opentelemetry.javaagent.shaded.com.google"
|
||||
relocate "javax.annotation", "io.opentelemetry.javaagent.shaded.javax.annotation"
|
||||
relocate "org.checkerframework", "io.opentelemetry.javaagent.shaded.org.checkerframework"
|
||||
relocate "org.codehaus", "io.opentelemetry.javaagent.shaded.org.codehaus"
|
||||
relocate "com.google", "shaded.for.testing.com.google"
|
||||
relocate "javax.annotation", "shaded.for.testing.javax.annotation"
|
||||
relocate "org.checkerframework", "shaded.for.testing.org.checkerframework"
|
||||
relocate "org.codehaus", "shaded.for.testing.org.codehaus"
|
||||
|
||||
exclude 'META-INF/maven/**'
|
||||
exclude 'org/codehaus/mojo/animal_sniffer/**' // this is Java 8 bytecode
|
||||
|
|
|
@ -47,6 +47,7 @@ include ':instrumentation-api'
|
|||
include ':javaagent-api'
|
||||
|
||||
// misc
|
||||
include ':testing-bootstrap'
|
||||
include ':testing-common'
|
||||
include ':utils:test-utils'
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
plugins {
|
||||
id "com.github.johnrengelman.shadow"
|
||||
}
|
||||
|
||||
apply from: "$rootDir/gradle/java.gradle"
|
||||
|
||||
// Depend on all libraries that are in the bootstrap classloader when running the agent. When
|
||||
// running tests, we simulate this by adding the jar produced by this project to the bootstrap
|
||||
// classpath.
|
||||
|
||||
dependencies {
|
||||
implementation project(":instrumentation-api")
|
||||
implementation project(":javaagent-bootstrap")
|
||||
|
||||
implementation deps.opentelemetryApi
|
||||
implementation deps.opentelemetryContext
|
||||
implementation deps.slf4j
|
||||
implementation "ch.qos.logback:logback-classic:${versions.logback}"
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveFileName = "testing-bootstrap.jar"
|
||||
|
||||
// need to exclude these logback classes from the bootstrap jar, otherwise tomcat will find them
|
||||
// and try to load them from the bootstrap class loader, which will fail with NoClassDefFoundError
|
||||
// since their super classes are servlet classes which are not in the bootstrap class loader
|
||||
exclude "ch/qos/logback/classic/servlet/LogbackServletContainerInitializer.class"
|
||||
exclude "ch/qos/logback/classic/servlet/LogbackServletContextListener.class"
|
||||
exclude "META-INF/services/javax.servlet.ServletContainerInitializer"
|
||||
}
|
Loading…
Reference in New Issue