Symbol Export is a Kotlin compiler plugin and associated tooling that allows you to export Kotlin symbols (classes, functions, properties, etc.) from one module and use them in another module. This is particularly useful for:
- Creating type-safe references to Kotlin symbols across module boundaries
- Enabling code generation based on annotated symbols
- Providing a way to reference Kotlin symbols without direct dependencies
gradle/build-logic- The build logic for the projectannotations- Contains annotations that are shared between the compiler plugin and the runtimecompiler-plugin- The Kotlin compiler plugin that processes annotations and exports symbolsgradle-plugin- The Gradle plugin that applies the compiler plugin to Kotlin compilationsgenerator- The code generator used by the Gradle plugin to generate symbol classesnames-internal- The internal symbols objects used to pass serialized symbols between the compiler plugin and the code generatorsymbols- The public symbols API that defines the structure of exported symbolssymbols-*- Symbol conversion extensions for different platforms/librariestest- A build that tests symbol export and import functionality
The project defines several annotations in the annotations module.
The symbols module defines the structure of exported symbols.
- The Gradle plugin applies the compiler plugin to Kotlin compilations
- The compiler plugin identifies symbols marked with export annotations
- The compiler plugin validates the symbols and writes them to a JSON file
- The generator reads the JSON file and generates Kotlin code with objects representing the exported symbols
- The generated code can be used in other modules to reference the exported symbols
Use the JetBrains MCP (Machine Coding Protocol) whenever possible except for running tests, e.g., for things like searching for symbols or running builds.
Always use Gradle to do builds and run tests.
When running multiple tests, run the Gradle test task (or the particular test task needed) once, rather than running it multiple times for individual tests.
Always run tests using the Run-Tests interaction, not using Gradle via the command line or using the jetbrains MCP.
General compiler plugin instructions can be found in the links in the readme here.
Instructions on testing Kotlin compiler plugins can be found here.
The compiler plugin tests live in compiler-plugin/src/testData - these are effectively Kotlin templates that either include diagnostics or test FIR/IR dumps.
- The
.ktfiles are the templates - The
.fir.txtand.fir.ir.txtfiles are snapshot files generated by the tests - don't try to add them manually - The
.ktfiles don't always conform to proper Kotlin code - They are pre-processed using the pre-processors in
compiler-plugin/src/testFixtures/kotlin/dev/rnett/symbolexport/support
New tests with snapshot files will always fail the first time due to creating those files.
The setup for these tests lives in compiler-plugin/src/testFixtures.
They are run using the usual :compiler-plugin:test task. The :compiler-plugin:generateTests task generates the test classes.
Symbol-related imports and Kotlin test imports, when available, are automatically added (see TestConfigurations).
Each directory of test files has a corresponding test in /test-gen. These are created by GenerateTests.kt's main function.
To test a diagnostic, create a Kotlin file that would trigger it and include the diagnostic ID in the file. For example, for the diagnostic SYMBOL_EXPORT_PARENT_MUST_BE_EXPOSED:
Without diagnostic:
@Module
abstract class TestModule() : BaseModule() {
@Default
constructor(t: Int) : this()
}With diagnostic on secondary constructor:
@Module
abstract class TestModule() : BaseModule() {
<!SYMBOL_EXPORT_PARENT_MUST_BE_EXPOSED!>
@Default
constructor(t: Int) : this()<!>
}The <!> marks the end of the source range the diagnostic is on.
To export symbols from a module:
- Apply the symbol-export Gradle plugin to your module
- Add the annotations dependency to your module
- Mark symbols for export using the appropriate annotations
- Build your module to generate the exported symbols
To use exported symbols in another module:
- Add a dependency on the module with exported symbols
- Import the generated
Symbolsclass - Use the symbols to reference the original declarations
Common issues and solutions:
- Missing symbols: Ensure that all parent declarations are marked with either
@ExportSymbolor@ChildrenExported - Visibility errors: Exported symbols must be public or marked with
@PublishedApi - Local declaration errors: Local declarations cannot be exported
- Test failures: New tests with snapshot files will fail the first time due to creating those files
- Only run the JVM tests, ignore other platforms
- Run the common tests on the JVM target to test them. Do not run them on other targets.