Skip to content

kpavlov/ksp-maven-plugin

Repository files navigation

KSP Maven Plugin

Maven Central Kotlin CI with Maven Api Docs GitHub License Kotlin JVM

A Maven plugin for running Kotlin Symbol Processing (KSP) on JVM projects.

Overview

This plugin integrates KSP (Kotlin Symbol Processing) into Maven builds, allowing you to process Kotlin source files with annotation processors that use the KSP API.

Check out the blog post.

References:

Features

  • Dual Scope Processing: Separate goals for main sources (process) and test sources (process-test)
  • Thread Safe: Fully supports Maven parallel builds (mvn -T)
  • Scope-Aware Logging: Distinct log prefixes for main ([ksp:main]) and test ([ksp:test]) processing
  • Incremental Compilation: Optional incremental processing support
  • Automatic Configuration: Automatically detects Kotlin version, JVM target, and other settings from the project environment
  • Flexible Configuration: Extensive configuration options for all KSP parameters

Goals

The plugin provides two goals:

  • process: Processes main sources in the generate-sources phase
  • process-test: Processes test sources in the generate-test-sources phase

Configuration

Basic Configuration

Minimal setup for processing main sources only:

<properties>
  <kotlin.version>2.3.10</kotlin.version>
</properties>

<build>
<plugins>
  <plugin>
    <groupId>me.kpavlov.ksp.maven</groupId>
    <artifactId>ksp-maven-plugin</artifactId>
    <version>[LATEST VERSION]</version>
    <executions>
      <execution>
        <goals>
          <goal>process</goal>
        </goals>
      </execution>
    </executions>
    <!-- KSP processors are plugin dependencies, not project dependencies -->
    <dependencies>
      <dependency>
        <groupId>com.example</groupId>
        <artifactId>my-ksp-processor</artifactId>
        <version>1.0.0</version>
      </dependency>
    </dependencies>
  </plugin>

  <plugin>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-maven-plugin</artifactId>
    <version>${kotlin.version}</version>
    <executions>
      <execution>
        <id>compile</id>
        <goals>
          <goal>compile</goal>
        </goals>
        <phase>compile</phase>
      </execution>
    </executions>
  </plugin>
</plugins>
<sourceDirectory>src/main/kotlin</sourceDirectory>
</build>

Processing Both Main and Test Sources (Recommended)

To process both main and test sources, use <extensions>true</extensions> to activate both goals automatically:

<properties>
  <kotlin.version>2.3.10</kotlin.version>
</properties>

<build>
  <plugins>
    <plugin>
      <groupId>me.kpavlov.ksp.maven</groupId>
      <artifactId>ksp-maven-plugin</artifactId>
      <version>[LATEST VERSION]</version>
      <extensions>true</extensions>
      <dependencies>
        <dependency>
          <groupId>com.example</groupId>
          <artifactId>my-ksp-processor</artifactId>
          <version>1.0.0</version>
        </dependency>
      </dependencies>
    </plugin>
  
    <plugin>
      <groupId>org.jetbrains.kotlin</groupId>
      <artifactId>kotlin-maven-plugin</artifactId>
      <version>${kotlin.version}</version>
      <executions>
        <execution>
          <id>compile</id>
          <goals>
            <goal>compile</goal>
          </goals>
          <phase>compile</phase>
        </execution>
        <execution>
          <id>test-compile</id>
          <goals>
            <goal>test-compile</goal>
          </goals>
          <phase>test-compile</phase>
        </execution>
      </executions>
    </plugin>
  </plugins>
  <sourceDirectory>src/main/kotlin</sourceDirectory>
  <testSourceDirectory>src/test/kotlin</testSourceDirectory>
</build>

Advanced Configuration

All available configuration options:

<plugin>
  <groupId>me.kpavlov.ksp.maven</groupId>
  <artifactId>ksp-maven-plugin</artifactId>
  <version>[LATEST VERSION]</version>
  <executions>
    <execution>
      <goals>
        <goal>process</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <!-- Main source directory to process (default: ${project.build.sourceDirectory}) -->
    <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>

    <!-- Additional source directories (default: none) -->
    <sourceDirs>
      <sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
      <sourceDir>${project.basedir}/src/generated/kotlin</sourceDir>
    </sourceDirs>

    <!-- Output directory for generated Kotlin sources (default: ${project.build.directory}/generated-sources/ksp) -->
    <kotlinOutputDir>${project.build.directory}/generated-sources/ksp</kotlinOutputDir>

    <!-- Output directory for generated Java sources (default: ${project.build.directory}/generated-sources/ksp) -->
    <javaOutputDir>${project.build.directory}/generated-sources/ksp</javaOutputDir>

    <!-- Output directory for compiled classes (default: ${project.build.directory}/ksp-classes) -->
    <classOutputDir>${project.build.directory}/ksp-classes</classOutputDir>

    <!-- Output directory for resources (default: ${project.build.directory}/generated-resources/ksp) -->
    <resourceOutputDir>${project.build.directory}/generated-resources/ksp</resourceOutputDir>

    <!-- KSP output directory (default: ${project.build.directory}/ksp) -->
    <kspOutputDir>${project.build.directory}/ksp</kspOutputDir>

    <!-- Cache directory for incremental processing (default: ${project.build.directory}/ksp-cache) -->
    <cachesDir>${project.build.directory}/ksp-cache</cachesDir>

    <!-- Enable incremental processing (default: false) -->
    <incremental>true</incremental>

    <!-- Enable incremental compilation logging (default: false) -->
    <incrementalLog>true</incrementalLog>

    <!-- Kotlin language version (default: detected from kotlin-maven-plugin or kotlin.version) -->
    <languageVersion>2.3</languageVersion>

    <!-- Kotlin API version (default: detected from languageVersion) -->
    <apiVersion>2.3</apiVersion>

    <!-- JVM default mode for interfaces (default: detected from kotlin-maven-plugin) -->
    <jvmDefaultMode></jvmDefaultMode>

    <!-- KSP processor options as key-value pairs (default: none) -->
    <processorOptions>
      <option1>value1</option1>
      <option2>value2</option2>
    </processorOptions>

    <!--
      Glob patterns to include specific SymbolProcessorProvider classes (default: all included).
      Use '*' for a single package segment, '**' for any depth.
      When non-empty, only providers whose fully-qualified class name matches at least
      one pattern are passed to KSP.
    -->
    <processorIncludes>
      <processorInclude>com.example.annotation.*</processorInclude>
    </processorIncludes>

    <!--
      Glob patterns to exclude specific SymbolProcessorProvider classes (default: none excluded).
      Providers matching any exclude pattern are removed even if they match an include pattern.
    -->
    <processorExcludes>
      <processorExclude>com.example.SlowProcessor</processorExclude>
    </processorExcludes>

    <!-- Continue build on processing errors (default: false) -->
    <ignoreProcessingErrors>false</ignoreProcessingErrors>

    <!-- Treat all warnings as errors (default: false) -->
    <allWarningsAsErrors>false</allWarningsAsErrors>

    <!-- Map annotation arguments in Java sources (default: true) -->
    <mapAnnotationArgumentsInJava>true</mapAnnotationArgumentsInJava>

    <!-- Module name (default: ${project.artifactId}) -->
    <moduleName>my-module</moduleName>

    <!-- JVM target version (default: detected from kotlin-maven-plugin or maven.compiler.release) -->
    <jvmTarget>17</jvmTarget>

    <!-- Skip KSP processing (default: false, can be set via -Dksp.skip=true) -->
    <skip>false</skip>

    <!-- Add generated sources to compilation (default: true) -->
    <addGeneratedSourcesToCompile>true</addGeneratedSourcesToCompile>

    <!-- Enable debug output (default: false) -->
    <debug>false</debug>
  </configuration>
  <!-- KSP processors are plugin dependencies, not project dependencies -->
  <dependencies>
    <dependency>
      <groupId>com.example</groupId>
      <artifactId>my-ksp-processor</artifactId>
      <version>1.0.0</version>
    </dependency>
  </dependencies>
</plugin>

Processor Filtering

KSP processors normally decide for themselves which classes to process. However, you can restrict which SymbolProcessorProvider implementations are activated using glob-style include and exclude patterns matched against the provider's fully-qualified class name.

Both processorIncludes and processorExcludes are List<String> parameters. In Maven XML configuration, each list element uses the singular form of the parameter name as the child tag (i.e. <processorInclude> inside <processorIncludes>, and <processorExclude> inside <processorExcludes>).

Pattern syntax:

Token Meaning
* Any sequence of characters within a single package segment (no dots)
** Any sequence of characters across package segments (including dots)
? Any single non-dot character
other Matched literally

Include/exclude semantics:

  • When processorIncludes is empty, all discovered providers pass the include check.
  • A provider is retained only when it satisfies the include check and does not match any processorExcludes pattern.
  • Excludes take priority: a provider that matches both an include and an exclude pattern is removed.

Example — run only one specific processor:

<configuration>
  <processorIncludes>
    <processorInclude>com.example.MyProcessor</processorInclude>
  </processorIncludes>
</configuration>

Example — exclude a slow or unwanted processor while keeping all others:

<configuration>
  <processorExcludes>
    <processorExclude>com.example.SlowProcessor</processorExclude>
  </processorExcludes>
</configuration>

Example — include a whole package but exclude one class within it:

<configuration>
  <processorIncludes>
    <processorInclude>com.example.annotation.*</processorInclude>
  </processorIncludes>
  <processorExcludes>
    <processorExclude>com.example.annotation.ExperimentalProcessor</processorExclude>
  </processorExcludes>
</configuration>

Automatic Parameter Detection

The plugin automatically detects many settings from the project configuration and properties:

Parameter Detection Source (in order of priority)
languageVersion <languageVersion> in kotlin-maven-plugin, kotlin.compiler.languageVersion property, or kotlin.version (major.minor)
apiVersion <apiVersion> in kotlin-maven-plugin, kotlin.compiler.apiVersion property, or languageVersion
jvmTarget <jvmTarget> in kotlin-maven-plugin, kotlin.compiler.jvmTarget, maven.compiler.release, or maven.compiler.target
jdkHome <jdkHome> in kotlin-maven-plugin, kotlin.compiler.jdkHome, or java.home system property
allWarningsAsErrors <allWarningsAsErrors> in kotlin-maven-plugin, or kotlin.compiler.allWarningsAsErrors property

Property Support

Many parameters can be set via command line or Maven properties:

  • ksp.skip: Skip KSP processing for main sources (-Dksp.skip=true)
  • ksp.skipTest: Skip KSP processing for test sources (-Dksp.skipTest=true)
  • ksp.debug: Enable debug output (-Dksp.debug=true)
  • kotlin.compiler.languageVersion: Set Kotlin language version
  • kotlin.compiler.jvmTarget: Set JVM target version

Output Directories

The plugin uses different output directories for main and test processing:

Main Sources (process goal)

  • Kotlin/Java sources: ${project.build.directory}/generated-sources/ksp
  • Classes: ${project.build.directory}/ksp-classes
  • Resources: ${project.build.directory}/generated-resources/ksp
  • KSP working directory: ${project.build.directory}/ksp
  • Cache: ${project.build.directory}/ksp-cache

Test Sources (process-test goal)

  • Kotlin/Java sources: ${project.build.directory}/generated-test-sources/ksp
  • Classes: ${project.build.directory}/ksp-test-classes
  • Resources: ${project.build.directory}/generated-test-resources/ksp
  • KSP working directory: ${project.build.directory}/ksp-test
  • Cache: ${project.build.directory}/ksp-test-cache

All directories can be customized via configuration parameters.

Build Phases

The plugin integrates with Maven's standard lifecycle phases:

Main Processing

  1. generate-sources phase: KSP processors run on main sources
  2. Generated sources automatically added to compilation source roots
  3. compile phase: Kotlin compiler compiles both original and generated sources

Test Processing

  1. generate-test-sources phase: KSP processors run on test sources
  2. Generated test sources automatically added to test compilation source roots
  3. test-compile phase: Kotlin compiler compiles both original and generated test sources

Parallel Execution Support

The plugin is fully thread-safe and supports Maven's parallel build execution:

# Build with 4 parallel threads
mvn clean install -T4

# Build using 1 thread per CPU core
mvn clean install -T1C

Each execution creates isolated instances of KotlinSymbolProcessing with no shared mutable state, ensuring safe concurrent builds in multi-module projects.

For detailed information about thread safety guarantees, see PARALLEL_EXECUTION.md.

Skipping KSP Processing

Skip Main Source Processing

Skip main source processing via command line:

mvn clean install -Dksp.skip=true

Or in your pom.xml:

<configuration>
  <skip>true</skip>
</configuration>

Skip Test Source Processing

Skip test source processing via command line:

mvn clean test -Dksp.skipTest=true

Or in your pom.xml for the process-test execution:

<execution>
  <id>process-test-sources</id>
  <goals>
    <goal>process-test</goal>
  </goals>
  <configuration>
    <skipTest>true</skipTest>
  </configuration>
</execution>

Troubleshooting

No processors found

If you see "No KSP processors found in dependencies":

  1. Verify your processor is added as a plugin dependency (inside <plugin><dependencies> section), not as a project dependency
  2. Check the processor JAR contains META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
  3. Ensure the dependency scope is not test

Compilation errors with generated code

If the Kotlin compiler can't find generated sources:

  1. Verify addGeneratedSourcesToCompile is true (default)
  2. Check that KSP plugin runs before Kotlin compilation
  3. Verify output directories are correct

Incremental compilation issues

If incremental compilation causes problems:

  1. Disable it: <incremental>false</incremental>
  2. Clean the cache: mvn clean
  3. Delete ${project.build.directory}/ksp-cache

Debug output

Enable debug output to see detailed processing information:

<configuration>
  <debug>true</debug>
</configuration>

This will log:

  • Found KSP processors
  • Processor classloader details
  • Processor providers
  • KSP configuration
  • Incremental changes (if enabled)

Log messages are prefixed with scope identifiers:

  • [ksp:main] for main source processing
  • [ksp:test] for test source processing

Contributing

Contributions are welcome. Follow the Kotlin coding conventions and ensure all tests pass before submitting a pull request.

Building the Plugin

Using Make (recommended)

Build, verify, install the plugin and test with sample project:

make build

Format code:

make format

Run linting:

make lint

Generate API documentation:

make apidocs

Run all checks (format, lint, build):

make all

Using Maven directly

Build and install the plugin:

mvn clean install

Test with the sample project:

cd sample-project
mvn clean compile

License

Apache License 2.0

Resources