Skip to content

Add Kover code coverage support#1527

Open
oliviernotteghem wants to merge 4 commits intobazel-contrib:masterfrom
oliviernotteghem:upstream-kover
Open

Add Kover code coverage support#1527
oliviernotteghem wants to merge 4 commits intobazel-contrib:masterfrom
oliviernotteghem:upstream-kover

Conversation

@oliviernotteghem
Copy link
Copy Markdown
Contributor

@oliviernotteghem oliviernotteghem commented Mar 18, 2026

Summary

This PR adds Kover (JetBrains' Kotlin code coverage tool) as an alternative to JaCoCo for code coverage in rules_kotlin.

Why Kover?

Feature JaCoCo Kover
Instrumentation Build-time bytecode modification Runtime JVM agent
Kotlin support Limited (misses inline functions) Native Kotlin awareness
Recompilation needed Yes No
Mixed Java/Kotlin Problematic Works well

New Toolchain Attributes

define_kt_toolchain(
    name = "kotlin_toolchain",
    experimental_kover_enabled = True,
    experimental_kover_agent = "@maven//:org_jetbrains_kotlinx_kover_jvm_agent",
    experimental_kover_exclude = ["com.example.generated.*"],
    experimental_kover_exclude_annotation = ["Generated"],
    experimental_kover_exclude_inherited_from = ["android.app.Activity"],
)

Usage

bazel coverage //your/kotlin/test_target

Output files are created alongside test outputs:

  • *-kover_report.ic - Binary coverage data
  • *-kover_metadata.txt - Metadata for Kover CLI report generation

Generate reports with:

java -jar kover-cli.jar report @path/to/kover_metadata.txt --html report/

Changes

  • kotlin/internal/jvm/kover.bzl (NEW) - Core Kover integration logic
  • kotlin/internal/jvm/compile.bzl - Disable JaCoCo instrumentation when Kover enabled
  • kotlin/internal/jvm/impl.bzl - Kover support in kt_jvm_test
  • kotlin/internal/jvm/kt_android_local_test_impl.bzl - Kover support for Android local tests
  • kotlin/internal/toolchains.bzl - New toolchain configuration attributes

Notes

  • Kover agent is added to both -Xbootclasspath/a: and -javaagent: to support code using the bootstrap classloader
  • Report generation is done outside Bazel (test actions are terminal)
  • For mixed Java/Kotlin sourcesets, JaCoCo instrumentation must be disabled

Test plan

  • Unit tests for get_kover_jvm_flags function
  • Toolchain builds successfully
  • Manual testing with actual Kotlin test targets

Usage

To enable Kover in your project:

  define_kt_toolchain(
      name = "kotlin_toolchain",
      experimental_kover_enabled = True,
      experimental_kover_agent = "@maven//:org_jetbrains_kotlinx_kover_jvm_agent",
      experimental_kover_exclude = ["com.example.generated.*"],
      experimental_kover_exclude_annotation = ["Generated"],
  )

  Then run: bazel coverage //your/kotlin/test_target

Android Code Duplication

kt_android_local_test_impl.bzl duplicates significant logic from rules_android. This needs to be kept in sync manually. There's ongoing work with Google to add better extensibility points to rules_android.

Summary

  This commit integrates Kover (JetBrains' Kotlin code coverage tool) into rules_kotlin as an alternative to JaCoCo. Kover uses a JVM agent for runtime
  instrumentation, eliminating the need for build-time bytecode modification.

  ---
  File-by-File Changes

  1. kotlin/internal/jvm/kover.bzl (NEW - 167 lines)

  Purpose: Core Kover integration logic.

  ┌───────────────────────────────────┬─────────┬───────────────────────────────────────────────────┐
  │             Function              │  Lines  │                    Description                    │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ is_kover_enabled(ctx)             │ 49-50   │ Checks if Kover is enabled via toolchain          │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ get_kover_agent_file(ctx)         │ 52-63   │ Retrieves Kover agent JAR from toolchain          │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ get_kover_jvm_flags(...)          │ 65-72   │ Generates -javaagent:kover.jar=file:args.txt flag │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ create_kover_agent_actions(...)   │ 74-99   │ Creates .ic output file and args file for agent   │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ create_kover_metadata_action(...) │ 102-167 │ Generates metadata file for Kover CLI reports     │
  └───────────────────────────────────┴─────────┴───────────────────────────────────────────────────┘

  Key implementation details:
  - Uses ctx.actions.run_shell hack to declare the .ic file (created by agent at runtime)
  - Filters source files and classfiles to include only same-package dependencies
  - Supports exclusions by class pattern, annotation, and inherited class

  ---
  2. kotlin/internal/jvm/compile.bzl (+5 lines)

  Changes:
  # Added import
  load("//kotlin/internal/jvm:kover.bzl", _is_kover_enabled = "is_kover_enabled")

  # Modified instrumentation flag
  args.add("--instrument_coverage", ctx.coverage_instrumented() and not _is_kover_enabled(ctx))

  Effect: Disables JaCoCo bytecode instrumentation when Kover is enabled, preventing conflicts.

  ---
  3. kotlin/internal/jvm/impl.bzl (+40 lines)

  Changes to kt_jvm_junit_test_impl:

  # Before (JaCoCo only)
  if ctx.configuration.coverage_enabled:
      jacocorunner = ctx.toolchains[_TOOLCHAIN_TYPE].jacocorunner
      coverage_runfiles = jacocorunner.files.to_list()

  # After (Kover + JaCoCo)
  if ctx.configuration.coverage_enabled:
      if _is_kover_enabled(ctx):
          # Setup Kover agent
          kover_agent_files = _get_kover_agent_files(ctx)
          kover_output_file, kover_args_file = _create_kover_agent_actions(...)
          kover_output_metadata_file = _create_kover_metadata_action(...)
          coverage_jvm_flags = [_get_kover_jvm_flags(...)]
          coverage_inputs = [depset(kover_agent_files)]
          coverage_runfiles = [kover_args_file, kover_output_metadata_file]
      else:
          # Existing JaCoCo path
          jacocorunner = ctx.toolchains[_TOOLCHAIN_TYPE].jacocorunner
          coverage_runfiles = jacocorunner.files.to_list()

  ---
  4. kotlin/internal/jvm/kt_android_local_test_impl.bzl (+199 lines)

  Major changes:

  1. _process_jvm function: Added Kover support mirroring the JVM test changes
  2. _process_stub function (NEW): Custom stub processor that:
    - Skips merged instrumentation JAR creation when using Kover
    - Properly handles coverage environment variables for both Kover and JaCoCo
  3. Helper functions (NEW):
    - _get_test_class(ctx) - Infers test class from sources
    - _create_stub(...) - Creates test launcher shell script
    - _get_classpath(s) - Formats classpath entries
    - _get_jvm_flags(...) - Assembles JVM flags for Robolectric

  ---
  5. kotlin/internal/toolchains.bzl (+32 lines)

  New toolchain attributes:

  ┌───────────────────────────────────────────┬─────────────┬───────────────────────────────┐
  │                 Attribute                 │    Type     │          Description          │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_enabled                │ bool        │ Enable Kover (default: False) │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_agent                  │ label       │ Kover agent JAR target        │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_exclude                │ string_list │ Class exclusion patterns      │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_exclude_annotation     │ string_list │ Annotation-based exclusions   │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_exclude_inherited_from │ string_list │ Inherited class exclusions    │
  └───────────────────────────────────────────┴─────────────┴───────────────────────────────┘

  Updated functions:
  - _kotlin_toolchain_impl: Adds Kover attributes to toolchain info
  - define_kt_toolchain: Adds Kover parameters to public API

  ---
  Architecture Diagram

  ┌─────────────────────────────────────────────────────────────────┐
  │                     bazel coverage //test                        │
  └─────────────────────────────────────────────────────────────────┘
                                  │
                  ┌───────────────┴───────────────┐
                  ▼                               ▼
          ┌──────────────┐               ┌──────────────┐
          │ Kover Enabled │               │ JaCoCo Path  │
          └──────────────┘               └──────────────┘
                  │                               │
                  ▼                               ▼
      ┌───────────────────┐         ┌─────────────────────┐
      │ No recompilation  │         │ Bytecode modified   │
      │ Agent instruments │         │ at compile time     │
      │ at runtime        │         └─────────────────────┘
      └───────────────────┘
                  │
                  ▼
      ┌───────────────────┐
      │ test-kover.args.txt│  ← Agent configuration
      │ test-kover_report.ic│  ← Binary coverage data
      │ test-kover_metadata.txt│ ← CLI report args
      └───────────────────┘
                  │
                  ▼
      ┌───────────────────────────────────────┐
      │ External: java -jar kover-cli.jar     │
      │           report @metadata.txt        │
      └───────────────────────────────────────┘

  Usage

  To enable Kover in your project:

  define_kt_toolchain(
      name = "kotlin_toolchain",
      experimental_kover_enabled = True,
      experimental_kover_agent = "@maven//:org_jetbrains_kotlinx_kover_jvm_agent",
      experimental_kover_exclude = ["com.example.generated.*"],
      experimental_kover_exclude_annotation = ["Generated"],
  )

  Then run: bazel coverage //your/kotlin/test_target

Android Code Duplication

  kt_android_local_test_impl.bzl duplicates significant logic from rules_android. This needs to be kept in sync manually. There's ongoing work with Google to add better
  extensibility points to rules_android.
Comment thread kotlin/internal/jvm/kover.bzl
@restingbull
Copy link
Copy Markdown
Collaborator

General, LGTM, modulo the windows check.

Comment on lines +41 to +42
# rules_java and Bazel core. For now, we disabled JaCoCo instrumentation accross the board,
# you will need to cherry-pick this PR https://github.com/uber-common/bazel/commit/cb9f6f042c64af96bbd77e21fe6fb75936c74f47
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that still true?

),
)

# TODO: follow up with Google to have rules_android provide better extensibility points
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a specific ticket regarding this?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked them directly about this, they're open if we submit a PR that's reasonable.

"-Xbootclasspath/a:%s" % (kover_agent_files[0].short_path),
"-javaagent:%s=file:%s" % (kover_agent_files[0].short_path, kover_args_file.short_path),
]
return " ".join(jvm_args)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JVM argument parser might split this incorrectly -- return list?

kover_output_file = ctx.actions.declare_file(binary_output_name)

# Hack: there is curently no way to indicate this file will be created Kover agent
ctx.actions.run_shell(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can cause some issues with non *nix OSes. Use:

ctx.actions.write(kover_output_file, "")

)
ctx.actions.write(
kover_args_file,
"report.file=../../%s" % binary_output_name, # Kotlin compiler runs in runfiles folder, make sure file is created is correct location
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relationship between the runfiles root and bazel-bin is not guaranteed and can vary by OS, --sandbox_base setting, and execution strategy.

Try kover_output_file.short_path (workspace-relative path resolved at runtime) or construct the path via ctx.bin_dir.path. Might want to use ${RUNFILES_DIR} to anchor it.

excludes = []

for dep in deps:
if dep.label.package != ctx.label.package:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. dep.label.package == ctx.label.packagewhich will drop all cross-package deps. Example: test is in //tests/foo:bar_test and the library under test is in //core/baz:baz_lib, coverage will be empty.

What's the goal on this?

srcs.extend(["--src", path])

if JavaInfo in dep:
for classfile in dep[JavaInfo].transitive_runtime_jars.to_list():
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment above about reducing memory usage.

@@ -1,6 +1,9 @@
load(":jvm_deps_tests.bzl", "jvm_deps_test_suite")
load(":kover_test.bzl", "kover_test_suite")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing kover_toolchain_test_suite, so the toolchain tests are never run.


# Expected format includes both -Xbootclasspath/a and -javaagent flags
expected = "-Xbootclasspath/a:external/kover/kover-jvm-agent.jar -javaagent:external/kover/kover-jvm-agent.jar=file:bazel-out/k8-fastbuild/bin/test-kover.args.txt"
asserts.equals(env, expected, result)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. Is this right? feels like it should be two strings for the jvm parse.

load(
"//kotlin/internal/jvm:kover.bzl",
_create_kover_agent_actions = "create_kover_agent_actions",
_create_kover_metadata_action = "create_kover_metadata_action",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: _get_kover_agent_files (plural) does not match the exported symbol get_kover_agent_file.

Also, single file, rather than list? Would clean up the ambiguous [0] bits below.

metadata_output_name = "%s-kover_metadata.txt" % name
kover_output_metadata_file = ctx.actions.declare_file(metadata_output_name)

srcs = []
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use Args for these collections.

continue

if InstrumentedFilesInfo in dep:
for src in dep[InstrumentedFilesInfo].instrumented_files.to_list():
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to_list is almost always a bad idea.

This can be accomplish this in a way that doesn't blow out starlark memory by using map and closure -- e.g.:

def filter(src):
   if src.short_path.startswith(ctx.label.package + "/"):
        path = _paths.dirname(src.short_path)
        return "--src\n" + path

src.add_joined(
  dep[InstrumentedFilesInfo].instrumented_files,
  join_with="\n",
  map_each=lambda src: 
  allow_closure = True, 
  uniquify = True
)

ryanulep pushed a commit to uber-common/rules_kotlin that referenced this pull request Apr 9, 2026
Summary

  This commit integrates Kover (JetBrains' Kotlin code coverage tool) into rules_kotlin as an alternative to JaCoCo. Kover uses a JVM agent for runtime
  instrumentation, eliminating the need for build-time bytecode modification.

  ---
  File-by-File Changes

  1. kotlin/internal/jvm/kover.bzl (NEW - 167 lines)

  Purpose: Core Kover integration logic.

  ┌───────────────────────────────────┬─────────┬───────────────────────────────────────────────────┐
  │             Function              │  Lines  │                    Description                    │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ is_kover_enabled(ctx)             │ 49-50   │ Checks if Kover is enabled via toolchain          │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ get_kover_agent_file(ctx)         │ 52-63   │ Retrieves Kover agent JAR from toolchain          │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ get_kover_jvm_flags(...)          │ 65-72   │ Generates -javaagent:kover.jar=file:args.txt flag │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ create_kover_agent_actions(...)   │ 74-99   │ Creates .ic output file and args file for agent   │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ create_kover_metadata_action(...) │ 102-167 │ Generates metadata file for Kover CLI reports     │
  └───────────────────────────────────┴─────────┴───────────────────────────────────────────────────┘

  Key implementation details:
  - Uses ctx.actions.run_shell hack to declare the .ic file (created by agent at runtime)
  - Filters source files and classfiles to include only same-package dependencies
  - Supports exclusions by class pattern, annotation, and inherited class

  ---
  2. kotlin/internal/jvm/compile.bzl (+5 lines)

  Changes:
  # Added import
  load("//kotlin/internal/jvm:kover.bzl", _is_kover_enabled = "is_kover_enabled")

  # Modified instrumentation flag
  args.add("--instrument_coverage", ctx.coverage_instrumented() and not _is_kover_enabled(ctx))

  Effect: Disables JaCoCo bytecode instrumentation when Kover is enabled, preventing conflicts.

  ---
  3. kotlin/internal/jvm/impl.bzl (+40 lines)

  Changes to kt_jvm_junit_test_impl:

  # Before (JaCoCo only)
  if ctx.configuration.coverage_enabled:
      jacocorunner = ctx.toolchains[_TOOLCHAIN_TYPE].jacocorunner
      coverage_runfiles = jacocorunner.files.to_list()

  # After (Kover + JaCoCo)
  if ctx.configuration.coverage_enabled:
      if _is_kover_enabled(ctx):
          # Setup Kover agent
          kover_agent_files = _get_kover_agent_files(ctx)
          kover_output_file, kover_args_file = _create_kover_agent_actions(...)
          kover_output_metadata_file = _create_kover_metadata_action(...)
          coverage_jvm_flags = [_get_kover_jvm_flags(...)]
          coverage_inputs = [depset(kover_agent_files)]
          coverage_runfiles = [kover_args_file, kover_output_metadata_file]
      else:
          # Existing JaCoCo path
          jacocorunner = ctx.toolchains[_TOOLCHAIN_TYPE].jacocorunner
          coverage_runfiles = jacocorunner.files.to_list()

  ---
  4. kotlin/internal/jvm/kt_android_local_test_impl.bzl (+199 lines)

  Major changes:

  1. _process_jvm function: Added Kover support mirroring the JVM test changes
  2. _process_stub function (NEW): Custom stub processor that:
    - Skips merged instrumentation JAR creation when using Kover
    - Properly handles coverage environment variables for both Kover and JaCoCo
  3. Helper functions (NEW):
    - _get_test_class(ctx) - Infers test class from sources
    - _create_stub(...) - Creates test launcher shell script
    - _get_classpath(s) - Formats classpath entries
    - _get_jvm_flags(...) - Assembles JVM flags for Robolectric

  ---
  5. kotlin/internal/toolchains.bzl (+32 lines)

  New toolchain attributes:

  ┌───────────────────────────────────────────┬─────────────┬───────────────────────────────┐
  │                 Attribute                 │    Type     │          Description          │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_enabled                │ bool        │ Enable Kover (default: False) │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_agent                  │ label       │ Kover agent JAR target        │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_exclude                │ string_list │ Class exclusion patterns      │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_exclude_annotation     │ string_list │ Annotation-based exclusions   │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_exclude_inherited_from │ string_list │ Inherited class exclusions    │
  └───────────────────────────────────────────┴─────────────┴───────────────────────────────┘

  Updated functions:
  - _kotlin_toolchain_impl: Adds Kover attributes to toolchain info
  - define_kt_toolchain: Adds Kover parameters to public API

  ---
  Architecture Diagram

  ┌─────────────────────────────────────────────────────────────────┐
  │                     bazel coverage //test                        │
  └─────────────────────────────────────────────────────────────────┘
                                  │
                  ┌───────────────┴───────────────┐
                  ▼                               ▼
          ┌──────────────┐               ┌──────────────┐
          │ Kover Enabled │               │ JaCoCo Path  │
          └──────────────┘               └──────────────┘
                  │                               │
                  ▼                               ▼
      ┌───────────────────┐         ┌─────────────────────┐
      │ No recompilation  │         │ Bytecode modified   │
      │ Agent instruments │         │ at compile time     │
      │ at runtime        │         └─────────────────────┘
      └───────────────────┘
                  │
                  ▼
      ┌───────────────────┐
      │ test-kover.args.txt│  ← Agent configuration
      │ test-kover_report.ic│  ← Binary coverage data
      │ test-kover_metadata.txt│ ← CLI report args
      └───────────────────┘
                  │
                  ▼
      ┌───────────────────────────────────────┐
      │ External: java -jar kover-cli.jar     │
      │           report @metadata.txt        │
      └───────────────────────────────────────┘

  Usage

  To enable Kover in your project:

  define_kt_toolchain(
      name = "kotlin_toolchain",
      experimental_kover_enabled = True,
      experimental_kover_agent = "@maven//:org_jetbrains_kotlinx_kover_jvm_agent",
      experimental_kover_exclude = ["com.example.generated.*"],
      experimental_kover_exclude_annotation = ["Generated"],
  )

  Then run: bazel coverage //your/kotlin/test_target

Android Code Duplication

  kt_android_local_test_impl.bzl duplicates significant logic from rules_android. This needs to be kept in sync manually. There's ongoing work with Google to add better
  extensibility points to rules_android.
ryanulep pushed a commit to uber-common/rules_kotlin that referenced this pull request Apr 16, 2026
Summary

  This commit integrates Kover (JetBrains' Kotlin code coverage tool) into rules_kotlin as an alternative to JaCoCo. Kover uses a JVM agent for runtime
  instrumentation, eliminating the need for build-time bytecode modification.

  ---
  File-by-File Changes

  1. kotlin/internal/jvm/kover.bzl (NEW - 167 lines)

  Purpose: Core Kover integration logic.

  ┌───────────────────────────────────┬─────────┬───────────────────────────────────────────────────┐
  │             Function              │  Lines  │                    Description                    │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ is_kover_enabled(ctx)             │ 49-50   │ Checks if Kover is enabled via toolchain          │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ get_kover_agent_file(ctx)         │ 52-63   │ Retrieves Kover agent JAR from toolchain          │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ get_kover_jvm_flags(...)          │ 65-72   │ Generates -javaagent:kover.jar=file:args.txt flag │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ create_kover_agent_actions(...)   │ 74-99   │ Creates .ic output file and args file for agent   │
  ├───────────────────────────────────┼─────────┼───────────────────────────────────────────────────┤
  │ create_kover_metadata_action(...) │ 102-167 │ Generates metadata file for Kover CLI reports     │
  └───────────────────────────────────┴─────────┴───────────────────────────────────────────────────┘

  Key implementation details:
  - Uses ctx.actions.run_shell hack to declare the .ic file (created by agent at runtime)
  - Filters source files and classfiles to include only same-package dependencies
  - Supports exclusions by class pattern, annotation, and inherited class

  ---
  2. kotlin/internal/jvm/compile.bzl (+5 lines)

  Changes:
  # Added import
  load("//kotlin/internal/jvm:kover.bzl", _is_kover_enabled = "is_kover_enabled")

  # Modified instrumentation flag
  args.add("--instrument_coverage", ctx.coverage_instrumented() and not _is_kover_enabled(ctx))

  Effect: Disables JaCoCo bytecode instrumentation when Kover is enabled, preventing conflicts.

  ---
  3. kotlin/internal/jvm/impl.bzl (+40 lines)

  Changes to kt_jvm_junit_test_impl:

  # Before (JaCoCo only)
  if ctx.configuration.coverage_enabled:
      jacocorunner = ctx.toolchains[_TOOLCHAIN_TYPE].jacocorunner
      coverage_runfiles = jacocorunner.files.to_list()

  # After (Kover + JaCoCo)
  if ctx.configuration.coverage_enabled:
      if _is_kover_enabled(ctx):
          # Setup Kover agent
          kover_agent_files = _get_kover_agent_files(ctx)
          kover_output_file, kover_args_file = _create_kover_agent_actions(...)
          kover_output_metadata_file = _create_kover_metadata_action(...)
          coverage_jvm_flags = [_get_kover_jvm_flags(...)]
          coverage_inputs = [depset(kover_agent_files)]
          coverage_runfiles = [kover_args_file, kover_output_metadata_file]
      else:
          # Existing JaCoCo path
          jacocorunner = ctx.toolchains[_TOOLCHAIN_TYPE].jacocorunner
          coverage_runfiles = jacocorunner.files.to_list()

  ---
  4. kotlin/internal/jvm/kt_android_local_test_impl.bzl (+199 lines)

  Major changes:

  1. _process_jvm function: Added Kover support mirroring the JVM test changes
  2. _process_stub function (NEW): Custom stub processor that:
    - Skips merged instrumentation JAR creation when using Kover
    - Properly handles coverage environment variables for both Kover and JaCoCo
  3. Helper functions (NEW):
    - _get_test_class(ctx) - Infers test class from sources
    - _create_stub(...) - Creates test launcher shell script
    - _get_classpath(s) - Formats classpath entries
    - _get_jvm_flags(...) - Assembles JVM flags for Robolectric

  ---
  5. kotlin/internal/toolchains.bzl (+32 lines)

  New toolchain attributes:

  ┌───────────────────────────────────────────┬─────────────┬───────────────────────────────┐
  │                 Attribute                 │    Type     │          Description          │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_enabled                │ bool        │ Enable Kover (default: False) │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_agent                  │ label       │ Kover agent JAR target        │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_exclude                │ string_list │ Class exclusion patterns      │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_exclude_annotation     │ string_list │ Annotation-based exclusions   │
  ├───────────────────────────────────────────┼─────────────┼───────────────────────────────┤
  │ experimental_kover_exclude_inherited_from │ string_list │ Inherited class exclusions    │
  └───────────────────────────────────────────┴─────────────┴───────────────────────────────┘

  Updated functions:
  - _kotlin_toolchain_impl: Adds Kover attributes to toolchain info
  - define_kt_toolchain: Adds Kover parameters to public API

  ---
  Architecture Diagram

  ┌─────────────────────────────────────────────────────────────────┐
  │                     bazel coverage //test                        │
  └─────────────────────────────────────────────────────────────────┘
                                  │
                  ┌───────────────┴───────────────┐
                  ▼                               ▼
          ┌──────────────┐               ┌──────────────┐
          │ Kover Enabled │               │ JaCoCo Path  │
          └──────────────┘               └──────────────┘
                  │                               │
                  ▼                               ▼
      ┌───────────────────┐         ┌─────────────────────┐
      │ No recompilation  │         │ Bytecode modified   │
      │ Agent instruments │         │ at compile time     │
      │ at runtime        │         └─────────────────────┘
      └───────────────────┘
                  │
                  ▼
      ┌───────────────────┐
      │ test-kover.args.txt│  ← Agent configuration
      │ test-kover_report.ic│  ← Binary coverage data
      │ test-kover_metadata.txt│ ← CLI report args
      └───────────────────┘
                  │
                  ▼
      ┌───────────────────────────────────────┐
      │ External: java -jar kover-cli.jar     │
      │           report @metadata.txt        │
      └───────────────────────────────────────┘

  Usage

  To enable Kover in your project:

  define_kt_toolchain(
      name = "kotlin_toolchain",
      experimental_kover_enabled = True,
      experimental_kover_agent = "@maven//:org_jetbrains_kotlinx_kover_jvm_agent",
      experimental_kover_exclude = ["com.example.generated.*"],
      experimental_kover_exclude_annotation = ["Generated"],
  )

  Then run: bazel coverage //your/kotlin/test_target

Android Code Duplication

  kt_android_local_test_impl.bzl duplicates significant logic from rules_android. This needs to be kept in sync manually. There's ongoing work with Google to add better
  extensibility points to rules_android.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants