feat: shade JaCoCo into codeflash-runtime as one fat JAR
Merge JaCoCo agent and CLI into the runtime JAR instead of shipping 3 separate JARs. JaCoCo already self-shades its internals with a version hash, so no relocation is needed. - Add AgentDispatcher premain that routes to profiler (config=) or JaCoCo (destfile=) based on agent args - Update shade plugin: Premain-Class → AgentDispatcher, add ServicesResourceTransformer and DontIncludeResourceTransformer - Rewrite build_jacoco_agent_arg() and generate_jacoco_report() to use the runtime JAR instead of separate JaCoCo JARs - Delete org.jacoco.agent-0.8.13-runtime.jar and org.jacoco.cli-0.8.13-nodeps.jar from resources/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b6acd6b7ce
commit
24ad61aa5c
8 changed files with 106 additions and 31 deletions
|
|
@ -40,6 +40,7 @@
|
|||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<jacoco.version>0.8.13</jacoco.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
|
@ -83,6 +84,22 @@
|
|||
<version>9.7.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JaCoCo agent (coverage instrumentation at runtime) -->
|
||||
<dependency>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>org.jacoco.agent</artifactId>
|
||||
<version>${jacoco.version}</version>
|
||||
<classifier>runtime</classifier>
|
||||
</dependency>
|
||||
|
||||
<!-- JaCoCo CLI (report generation from .exec files) -->
|
||||
<dependency>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>org.jacoco.cli</artifactId>
|
||||
<version>${jacoco.version}</version>
|
||||
<classifier>nodeps</classifier>
|
||||
</dependency>
|
||||
|
||||
<!-- JUnit 5 for testing -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
|
|
@ -145,10 +162,14 @@
|
|||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>com.codeflash.Comparator</mainClass>
|
||||
<manifestEntries>
|
||||
<Premain-Class>com.codeflash.profiler.ProfilerAgent</Premain-Class>
|
||||
<Premain-Class>com.codeflash.AgentDispatcher</Premain-Class>
|
||||
<Can-Retransform-Classes>true</Can-Retransform-Classes>
|
||||
</manifestEntries>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
|
||||
<resource>about.html</resource>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<filters>
|
||||
<filter>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package com.codeflash;
|
||||
|
||||
import java.lang.instrument.Instrumentation;
|
||||
|
||||
/**
|
||||
* Premain dispatcher that routes to either the CodeFlash line profiler or the
|
||||
* JaCoCo coverage agent based on the agent arguments.
|
||||
*
|
||||
* <p>Detection logic:
|
||||
* <ul>
|
||||
* <li>Args contain {@code config=} → line profiler mode → delegate to
|
||||
* {@link com.codeflash.profiler.ProfilerAgent}</li>
|
||||
* <li>Otherwise → JaCoCo mode → delegate to JaCoCo's PreMain</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>This is reliable because our profiler always receives
|
||||
* {@code config=/path/to/config.json} while JaCoCo always receives
|
||||
* {@code destfile=/path/to/jacoco.exec}.
|
||||
*/
|
||||
public class AgentDispatcher {
|
||||
|
||||
static boolean isProfilerMode(String agentArgs) {
|
||||
return agentArgs != null && agentArgs.contains("config=");
|
||||
}
|
||||
|
||||
public static void premain(String agentArgs, Instrumentation inst) throws Exception {
|
||||
if (isProfilerMode(agentArgs)) {
|
||||
com.codeflash.profiler.ProfilerAgent.premain(agentArgs, inst);
|
||||
} else {
|
||||
org.jacoco.agent.rt.internal_0e20598.PreMain.premain(agentArgs, inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.codeflash;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class AgentDispatcherTest {
|
||||
|
||||
@Test
|
||||
void profilerModeWhenConfigPresent() {
|
||||
assertTrue(AgentDispatcher.isProfilerMode("config=/tmp/config.json"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void profilerModeWithMultipleArgs() {
|
||||
assertTrue(AgentDispatcher.isProfilerMode("config=/tmp/config.json,output=results"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void jacocoModeWhenDestfilePresent() {
|
||||
assertFalse(AgentDispatcher.isProfilerMode("destfile=/tmp/jacoco.exec"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void jacocoModeWhenNullArgs() {
|
||||
assertFalse(AgentDispatcher.isProfilerMode(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void jacocoModeWhenEmptyArgs() {
|
||||
assertFalse(AgentDispatcher.isProfilerMode(""));
|
||||
}
|
||||
}
|
||||
|
|
@ -22,8 +22,6 @@ CODEFLASH_RUNTIME_VERSION = "1.0.0"
|
|||
CODEFLASH_RUNTIME_JAR_NAME = f"codeflash-runtime-{CODEFLASH_RUNTIME_VERSION}.jar"
|
||||
|
||||
JACOCO_PLUGIN_VERSION = "0.8.13"
|
||||
JACOCO_AGENT_JAR = f"org.jacoco.agent-{JACOCO_PLUGIN_VERSION}-runtime.jar"
|
||||
JACOCO_CLI_JAR = f"org.jacoco.cli-{JACOCO_PLUGIN_VERSION}-nodeps.jar"
|
||||
|
||||
_JAVA_RESOURCES_DIR = Path(__file__).parent / "resources"
|
||||
|
||||
|
|
@ -72,21 +70,6 @@ def restore_all_pom_backups() -> None:
|
|||
_pom_backups.clear()
|
||||
|
||||
|
||||
def find_jacoco_agent_jar() -> Path | None:
|
||||
"""Find the bundled JaCoCo agent JAR in package resources."""
|
||||
jar = _JAVA_RESOURCES_DIR / JACOCO_AGENT_JAR
|
||||
if jar.exists():
|
||||
return jar
|
||||
return None
|
||||
|
||||
|
||||
def find_jacoco_cli_jar() -> Path | None:
|
||||
"""Find the bundled JaCoCo CLI JAR in package resources."""
|
||||
jar = _JAVA_RESOURCES_DIR / JACOCO_CLI_JAR
|
||||
if jar.exists():
|
||||
return jar
|
||||
return None
|
||||
|
||||
|
||||
# MVN_CENTRAL_TODO: Uncomment once codeflash-runtime is published to Maven Central.
|
||||
# Steps:
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -28,8 +28,6 @@ from codeflash.languages.java.build_tools import (
|
|||
CODEFLASH_RUNTIME_VERSION,
|
||||
add_codeflash_dependency_to_pom,
|
||||
backup_pom,
|
||||
find_jacoco_agent_jar,
|
||||
find_jacoco_cli_jar,
|
||||
find_maven_executable,
|
||||
get_jacoco_xml_path,
|
||||
install_codeflash_runtime,
|
||||
|
|
@ -179,23 +177,29 @@ def _validate_java_class_name(class_name: str) -> bool:
|
|||
def build_jacoco_agent_arg(exec_dest: Path) -> str | None:
|
||||
"""Build the -javaagent arg for standalone JaCoCo coverage collection.
|
||||
|
||||
Returns None if the bundled JaCoCo agent JAR is not found.
|
||||
The codeflash-runtime JAR includes the shaded JaCoCo agent. The AgentDispatcher
|
||||
routes to JaCoCo when the args contain ``destfile=`` (no ``config=``).
|
||||
|
||||
Returns None if the runtime JAR is not found.
|
||||
"""
|
||||
agent_jar = find_jacoco_agent_jar()
|
||||
if agent_jar is None:
|
||||
logger.warning("Bundled JaCoCo agent JAR not found — coverage will not be collected")
|
||||
runtime_jar = _find_runtime_jar()
|
||||
if runtime_jar is None:
|
||||
logger.warning("codeflash-runtime JAR not found — coverage will not be collected")
|
||||
return None
|
||||
return f"-javaagent:{agent_jar}=destfile={exec_dest}"
|
||||
return f"-javaagent:{runtime_jar}=destfile={exec_dest}"
|
||||
|
||||
|
||||
def generate_jacoco_report(exec_file: Path, classfiles_dir: Path, sourcefiles_dir: Path, xml_output: Path) -> bool:
|
||||
"""Generate a JaCoCo XML report from a .exec file using the bundled CLI JAR.
|
||||
"""Generate a JaCoCo XML report from a .exec file.
|
||||
|
||||
Uses the JaCoCo CLI classes shaded into the codeflash-runtime JAR
|
||||
(invoked via ``java -cp <runtime_jar> org.jacoco.cli.internal.Main``).
|
||||
|
||||
Returns True if the report was generated successfully.
|
||||
"""
|
||||
cli_jar = find_jacoco_cli_jar()
|
||||
if cli_jar is None:
|
||||
logger.error("Bundled JaCoCo CLI JAR not found — cannot generate coverage report")
|
||||
runtime_jar = _find_runtime_jar()
|
||||
if runtime_jar is None:
|
||||
logger.error("codeflash-runtime JAR not found — cannot generate coverage report")
|
||||
return False
|
||||
|
||||
if not exec_file.exists():
|
||||
|
|
@ -206,8 +210,9 @@ def generate_jacoco_report(exec_file: Path, classfiles_dir: Path, sourcefiles_di
|
|||
|
||||
cmd = [
|
||||
shutil.which("java") or "java",
|
||||
"-jar",
|
||||
str(cli_jar),
|
||||
"-cp",
|
||||
str(runtime_jar),
|
||||
"org.jacoco.cli.internal.Main",
|
||||
"report",
|
||||
str(exec_file),
|
||||
"--classfiles",
|
||||
|
|
|
|||
Loading…
Reference in a new issue