CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-kotlin--kotlin-compiler-embeddable

Self-contained embeddable Kotlin compiler with shaded dependencies for integration into applications without classpath conflicts

Pending
Overview
Eval results
Files

incremental-compilation.mddocs/

Incremental Compilation Support

Advanced incremental compilation capabilities with change tracking, dependency analysis, and compilation optimization. The incremental compilation system minimizes compilation time by only recompiling changed sources and their dependencies.

Capabilities

IncrementalCompilationComponents

Main interface providing incremental compilation services and tracking components.

/**
 * Main interface for incremental compilation functionality
 * Provides tracking components for optimization and change detection
 */
interface IncrementalCompilationComponents {
    /** Symbol lookup tracker for dependency analysis */
    val lookupTracker: LookupTracker
    
    /** Multiplatform expect/actual tracker */
    val expectActualTracker: ExpectActualTracker
    
    /** Inline constant usage tracker */
    val inlineConstTracker: InlineConstTracker
    
    /** Enum when exhaustiveness tracker */
    val enumWhenTracker: EnumWhenTracker
    
    /** Import statement tracker */
    val importTracker: ImportTracker
    
    companion object {
        /** Create default incremental compilation components */
        fun create(): IncrementalCompilationComponents
        
        /** Create with custom configuration */
        fun create(configuration: IncrementalCompilationConfiguration): IncrementalCompilationComponents
    }
}

Usage Examples:

// Setup incremental compilation
val incrementalComponents = IncrementalCompilationComponents.create()

val configuration = CompilerConfiguration().apply {
    put(JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS, incrementalComponents)
}

val compiler = K2JVMCompiler()
val arguments = K2JVMCompilerArguments().apply {
    destination = "build/classes"
    // Other configuration...
}

// Compile with incremental support
val exitCode = compiler.exec(messageCollector, Services.EMPTY, arguments)

// Access tracking information
val lookupTracker = incrementalComponents.lookupTracker
val usedSymbols = lookupTracker.getUsedSymbols()
println("Used ${usedSymbols.size} symbols during compilation")

LookupTracker

Tracks symbol lookups during compilation for dependency analysis and change impact assessment.

/**
 * Tracks symbol lookups during compilation
 * Essential for determining compilation dependencies and change impact
 */
interface LookupTracker {
    /** Record a symbol lookup at specific location */
    fun record(
        filePath: String,
        position: Int,
        scopeFqName: String,
        name: String
    ): Unit
    
    /** Record lookup with additional context */
    fun record(
        filePath: String,
        position: Int,
        scopeFqName: String,
        scopeKind: ScopeKind,
        name: String
    ): Unit
    
    /** Get all recorded lookups */
    fun getUsedSymbols(): Set<LookupSymbol>
    
    /** Get lookups for specific file */
    fun getLookups(filePath: String): Collection<LookupSymbol>
    
    /** Clear all recorded lookups */
    fun clear(): Unit
    
    /** Check if lookup tracking is enabled */
    val requiresPosition: Boolean
}

/**
 * Represents a symbol lookup for incremental compilation
 */
data class LookupSymbol(
    /** Fully qualified name of the scope */
    val scope: String,
    
    /** Name of the looked up symbol */
    val name: String,
    
    /** Kind of scope (package, class, etc.) */
    val scopeKind: ScopeKind
)

/**
 * Types of scopes for symbol lookups
 */
enum class ScopeKind {
    PACKAGE, CLASSIFIER, OBJECT
}

LookupTracker Examples:

// Custom lookup tracker implementation
class DetailedLookupTracker : LookupTracker {
    private val lookups = mutableMapOf<String, MutableSet<LookupSymbol>>()
    
    override fun record(
        filePath: String,
        position: Int,
        scopeFqName: String,
        name: String
    ) {
        val symbol = LookupSymbol(scopeFqName, name, ScopeKind.CLASSIFIER)
        lookups.getOrPut(filePath) { mutableSetOf() }.add(symbol)
        
        // Log for debugging
        println("Lookup: $filePath:$position -> $scopeFqName.$name")
    }
    
    override fun record(
        filePath: String,
        position: Int,
        scopeFqName: String,
        scopeKind: ScopeKind,
        name: String
    ) {
        val symbol = LookupSymbol(scopeFqName, name, scopeKind)
        lookups.getOrPut(filePath) { mutableSetOf() }.add(symbol)
    }
    
    override fun getUsedSymbols(): Set<LookupSymbol> {
        return lookups.values.flatten().toSet()
    }
    
    override fun getLookups(filePath: String): Collection<LookupSymbol> {
        return lookups[filePath] ?: emptySet()
    }
    
    override fun clear() {
        lookups.clear()
    }
    
    override val requiresPosition: Boolean = true
    
    // Additional functionality
    fun getDependentFiles(changedSymbol: LookupSymbol): Set<String> {
        return lookups.filterValues { symbols ->
            changedSymbol in symbols
        }.keys
    }
}

ExpectActualTracker

Tracks expect/actual declarations in multiplatform projects for incremental compilation.

/**
 * Tracks expect/actual declarations in multiplatform projects
 * Ensures proper incremental compilation across platforms
 */
interface ExpectActualTracker {
    /** Report expect declaration */
    fun reportExpectDeclaration(
        filePath: String,
        fqName: String,
        position: Int
    ): Unit
    
    /** Report actual declaration */
    fun reportActualDeclaration(
        filePath: String,
        fqName: String,
        position: Int
    ): Unit
    
    /** Get all expect declarations */
    fun getExpectDeclarations(): Collection<ExpectActualDeclaration>
    
    /** Get all actual declarations */
    fun getActualDeclarations(): Collection<ExpectActualDeclaration>
    
    /** Find actual declarations for expect */
    fun findActuals(expectFqName: String): Collection<ExpectActualDeclaration>
    
    /** Find expect declaration for actual */
    fun findExpect(actualFqName: String): ExpectActualDeclaration?
}

/**
 * Represents an expect or actual declaration
 */
data class ExpectActualDeclaration(
    val filePath: String,
    val fqName: String,
    val position: Int,
    val isExpect: Boolean
)

InlineConstTracker

Tracks usage of inline constants for proper incremental compilation when constants change.

/**
 * Tracks usage of inline constants
 * Critical for incremental compilation when constant values change
 */
interface InlineConstTracker {
    /** Report usage of inline constant */
    fun report(
        filePath: String,
        position: Int,
        owner: String,
        name: String,
        constType: ConstType
    ): Unit
    
    /** Get all tracked inline constant usages */
    fun getUsedInlineConsts(): Collection<InlineConstUsage>
    
    /** Get inline constant usages for specific file */
    fun getUsages(filePath: String): Collection<InlineConstUsage>
    
    /** Check if constant change affects file */
    fun isAffectedBy(filePath: String, changedConst: InlineConstUsage): Boolean
}

/**
 * Represents usage of an inline constant
 */
data class InlineConstUsage(
    val filePath: String,
    val position: Int,
    val owner: String,
    val name: String,
    val constType: ConstType
)

/**
 * Types of inline constants
 */
enum class ConstType {
    PROPERTY, ENUM_ENTRY, COMPANION_OBJECT_PROPERTY
}

EnumWhenTracker

Tracks enum when expressions for incremental compilation when enum entries are added or removed.

/**
 * Tracks enum when expressions
 * Ensures recompilation when enum entries are added/removed
 */
interface EnumWhenTracker {
    /** Report enum when expression */
    fun report(
        filePath: String,
        position: Int,
        enumClassFqName: String,
        whenExpression: WhenExpressionInfo
    ): Unit
    
    /** Get all tracked when expressions */
    fun getWhenExpressions(): Collection<EnumWhenUsage>
    
    /** Check if enum change affects when expressions */
    fun isAffectedByEnumChange(
        enumFqName: String,
        addedEntries: Set<String>,
        removedEntries: Set<String>
    ): Collection<EnumWhenUsage>
}

/**
 * Information about when expression on enum
 */
data class WhenExpressionInfo(
    val isExhaustive: Boolean,
    val matchedEntries: Set<String>
)

/**
 * Usage of enum in when expression
 */
data class EnumWhenUsage(
    val filePath: String,
    val position: Int,
    val enumClassFqName: String,
    val whenInfo: WhenExpressionInfo
)

ImportTracker

Tracks import statements and their usage for optimizing incremental compilation.

/**
 * Tracks import statements and their usage
 * Helps optimize incremental compilation by tracking import dependencies
 */
interface ImportTracker {
    /** Report import statement */
    fun report(filePath: String, importedFqName: String): Unit
    
    /** Report star import */
    fun reportStarImport(filePath: String, packageFqName: String): Unit
    
    /** Get all imports for file */
    fun getImports(filePath: String): Collection<ImportInfo>
    
    /** Get files that import from package */
    fun getFilesImportingFrom(packageFqName: String): Collection<String>
    
    /** Check if symbol change affects imports */
    fun isAffectedBySymbolChange(symbolFqName: String): Collection<String>
}

/**
 * Information about an import
 */
data class ImportInfo(
    val importedFqName: String,
    val isStarImport: Boolean
)

Classpath Change Detection

Advanced classpath analysis for incremental compilation optimization.

/**
 * Analyzes classpath changes for incremental compilation
 */
interface ClasspathChangesComputer {
    /** Compute changes between classpaths */
    fun compute(
        previousClasspath: List<File>,
        currentClasspath: List<File>
    ): ClasspathChanges
    
    /** Create snapshot of current classpath */
    fun createSnapshot(classpath: List<File>): ClasspathSnapshot
    
    /** Compare snapshots for changes */
    fun compareSnapshots(
        previous: ClasspathSnapshot,
        current: ClasspathSnapshot
    ): ClasspathChanges
}

/**
 * Represents changes in classpath between compilations
 */
data class ClasspathChanges(
    /** Files added to classpath */
    val added: Set<File>,
    
    /** Files removed from classpath */
    val removed: Set<File>,
    
    /** Files modified in classpath */
    val modified: Set<File>,
    
    /** Unchanged files */
    val unchanged: Set<File>
) {
    /** Check if any changes occurred */
    val hasChanges: Boolean get() = added.isNotEmpty() || removed.isNotEmpty() || modified.isNotEmpty()
    
    /** Get all changed files */
    val changedFiles: Set<File> get() = added + removed + modified
}

/**
 * Snapshot of classpath state
 */
interface ClasspathSnapshot {
    /** Classpath entries in this snapshot */
    val entries: List<ClasspathEntrySnapshot>
    
    /** Timestamp when snapshot was created */
    val timestamp: Long
    
    /** Hash of the complete classpath */
    val hash: String
}

/**
 * Snapshot of individual classpath entry
 */
interface ClasspathEntrySnapshot {
    /** File path */
    val file: File
    
    /** Content hash */
    val hash: String
    
    /** Last modified timestamp */
    val lastModified: Long
    
    /** File size */
    val size: Long
    
    /** ABI hash for incremental compilation */
    val abiHash: String?
}

Advanced Incremental Compilation Example:

// Complete incremental compilation setup
class IncrementalCompilationManager {
    private val lookupTracker = DetailedLookupTracker()
    private val classpathComputer = ClasspathChangesComputer.create()
    private var previousSnapshot: ClasspathSnapshot? = null
    
    fun compile(
        sources: List<File>,
        classpath: List<File>,
        outputDir: File
    ): CompilationResult {
        
        // Check classpath changes
        val currentSnapshot = classpathComputer.createSnapshot(classpath)
        val classpathChanges = previousSnapshot?.let {
            classpathComputer.compareSnapshots(it, currentSnapshot)
        }
        
        // Determine which sources need recompilation
        val sourcesToCompile = if (classpathChanges?.hasChanges == true) {
            // Significant classpath changes - recompile all
            sources
        } else {
            // Check individual source changes
            determineChangedSources(sources)
        }
        
        // Setup incremental compilation components
        val incrementalComponents = IncrementalCompilationComponents.create().apply {
            // Use our custom lookup tracker
            (this as MutableIncrementalCompilationComponents).lookupTracker = this@IncrementalCompilationManager.lookupTracker
        }
        
        val configuration = CompilerConfiguration().apply {
            put(JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS, incrementalComponents)
            put(JVMConfigurationKeys.OUTPUT_DIRECTORY, outputDir)
        }
        
        // Perform compilation
        val compiler = K2JVMCompiler()
        val arguments = K2JVMCompilerArguments().apply {
            destination = outputDir.absolutePath
            classpath = classpath.joinToString(File.pathSeparator) { it.absolutePath }
            freeArgs = sourcesToCompile.map { it.absolutePath }
        }
        
        val messageCollector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN, false)
        val exitCode = compiler.exec(messageCollector, Services.EMPTY, arguments)
        
        // Update snapshot for next compilation
        previousSnapshot = currentSnapshot
        
        return CompilationResult(
            exitCode = exitCode,
            compiledFiles = sourcesToCompile.size,
            incrementalInfo = IncrementalInfo(
                classpathChanges = classpathChanges,
                recompiledSources = sourcesToCompile,
                symbolLookups = lookupTracker.getUsedSymbols()
            )
        )
    }
    
    private fun determineChangedSources(sources: List<File>): List<File> {
        // Implementation would check source file modifications
        // and use lookup tracker to determine dependent files
        return sources.filter { it.lastModified() > getLastCompilationTime() }
    }
    
    fun getImpactAnalysis(changedSymbol: String): Set<String> {
        return lookupTracker.getDependentFiles(
            LookupSymbol(changedSymbol, "", ScopeKind.CLASSIFIER)
        )
    }
}

// Result with incremental compilation information
data class IncrementalInfo(
    val classpathChanges: ClasspathChanges?,
    val recompiledSources: List<File>,
    val symbolLookups: Set<LookupSymbol>
)

Performance Optimization

// Incremental compilation configuration
data class IncrementalCompilationConfiguration(
    /** Enable lookup tracking */
    val enableLookupTracking: Boolean = true,
    
    /** Enable expect/actual tracking */
    val enableExpectActualTracking: Boolean = true,
    
    /** Enable inline constant tracking */
    val enableInlineConstTracking: Boolean = true,
    
    /** Enable enum when tracking */
    val enableEnumWhenTracking: Boolean = true,
    
    /** Enable import tracking */
    val enableImportTracking: Boolean = true,
    
    /** Maximum cache size for tracking data */
    val maxCacheSize: Int = 10000,
    
    /** Whether to persist tracking data between sessions */
    val persistTrackingData: Boolean = true,
    
    /** Directory for storing incremental data */
    val incrementalDataDir: File? = null
)

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-compiler-embeddable

docs

compiler-entry-points.md

configuration-system.md

high-level-api.md

incremental-compilation.md

index.md

message-collection.md

plugin-system.md

tile.json