0
# Incremental Compilation Support
1
2
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.
3
4
## Capabilities
5
6
### IncrementalCompilationComponents
7
8
Main interface providing incremental compilation services and tracking components.
9
10
```kotlin { .api }
11
/**
12
* Main interface for incremental compilation functionality
13
* Provides tracking components for optimization and change detection
14
*/
15
interface IncrementalCompilationComponents {
16
/** Symbol lookup tracker for dependency analysis */
17
val lookupTracker: LookupTracker
18
19
/** Multiplatform expect/actual tracker */
20
val expectActualTracker: ExpectActualTracker
21
22
/** Inline constant usage tracker */
23
val inlineConstTracker: InlineConstTracker
24
25
/** Enum when exhaustiveness tracker */
26
val enumWhenTracker: EnumWhenTracker
27
28
/** Import statement tracker */
29
val importTracker: ImportTracker
30
31
companion object {
32
/** Create default incremental compilation components */
33
fun create(): IncrementalCompilationComponents
34
35
/** Create with custom configuration */
36
fun create(configuration: IncrementalCompilationConfiguration): IncrementalCompilationComponents
37
}
38
}
39
```
40
41
**Usage Examples:**
42
43
```kotlin
44
// Setup incremental compilation
45
val incrementalComponents = IncrementalCompilationComponents.create()
46
47
val configuration = CompilerConfiguration().apply {
48
put(JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS, incrementalComponents)
49
}
50
51
val compiler = K2JVMCompiler()
52
val arguments = K2JVMCompilerArguments().apply {
53
destination = "build/classes"
54
// Other configuration...
55
}
56
57
// Compile with incremental support
58
val exitCode = compiler.exec(messageCollector, Services.EMPTY, arguments)
59
60
// Access tracking information
61
val lookupTracker = incrementalComponents.lookupTracker
62
val usedSymbols = lookupTracker.getUsedSymbols()
63
println("Used ${usedSymbols.size} symbols during compilation")
64
```
65
66
### LookupTracker
67
68
Tracks symbol lookups during compilation for dependency analysis and change impact assessment.
69
70
```kotlin { .api }
71
/**
72
* Tracks symbol lookups during compilation
73
* Essential for determining compilation dependencies and change impact
74
*/
75
interface LookupTracker {
76
/** Record a symbol lookup at specific location */
77
fun record(
78
filePath: String,
79
position: Int,
80
scopeFqName: String,
81
name: String
82
): Unit
83
84
/** Record lookup with additional context */
85
fun record(
86
filePath: String,
87
position: Int,
88
scopeFqName: String,
89
scopeKind: ScopeKind,
90
name: String
91
): Unit
92
93
/** Get all recorded lookups */
94
fun getUsedSymbols(): Set<LookupSymbol>
95
96
/** Get lookups for specific file */
97
fun getLookups(filePath: String): Collection<LookupSymbol>
98
99
/** Clear all recorded lookups */
100
fun clear(): Unit
101
102
/** Check if lookup tracking is enabled */
103
val requiresPosition: Boolean
104
}
105
106
/**
107
* Represents a symbol lookup for incremental compilation
108
*/
109
data class LookupSymbol(
110
/** Fully qualified name of the scope */
111
val scope: String,
112
113
/** Name of the looked up symbol */
114
val name: String,
115
116
/** Kind of scope (package, class, etc.) */
117
val scopeKind: ScopeKind
118
)
119
120
/**
121
* Types of scopes for symbol lookups
122
*/
123
enum class ScopeKind {
124
PACKAGE, CLASSIFIER, OBJECT
125
}
126
```
127
128
**LookupTracker Examples:**
129
130
```kotlin
131
// Custom lookup tracker implementation
132
class DetailedLookupTracker : LookupTracker {
133
private val lookups = mutableMapOf<String, MutableSet<LookupSymbol>>()
134
135
override fun record(
136
filePath: String,
137
position: Int,
138
scopeFqName: String,
139
name: String
140
) {
141
val symbol = LookupSymbol(scopeFqName, name, ScopeKind.CLASSIFIER)
142
lookups.getOrPut(filePath) { mutableSetOf() }.add(symbol)
143
144
// Log for debugging
145
println("Lookup: $filePath:$position -> $scopeFqName.$name")
146
}
147
148
override fun record(
149
filePath: String,
150
position: Int,
151
scopeFqName: String,
152
scopeKind: ScopeKind,
153
name: String
154
) {
155
val symbol = LookupSymbol(scopeFqName, name, scopeKind)
156
lookups.getOrPut(filePath) { mutableSetOf() }.add(symbol)
157
}
158
159
override fun getUsedSymbols(): Set<LookupSymbol> {
160
return lookups.values.flatten().toSet()
161
}
162
163
override fun getLookups(filePath: String): Collection<LookupSymbol> {
164
return lookups[filePath] ?: emptySet()
165
}
166
167
override fun clear() {
168
lookups.clear()
169
}
170
171
override val requiresPosition: Boolean = true
172
173
// Additional functionality
174
fun getDependentFiles(changedSymbol: LookupSymbol): Set<String> {
175
return lookups.filterValues { symbols ->
176
changedSymbol in symbols
177
}.keys
178
}
179
}
180
```
181
182
### ExpectActualTracker
183
184
Tracks expect/actual declarations in multiplatform projects for incremental compilation.
185
186
```kotlin { .api }
187
/**
188
* Tracks expect/actual declarations in multiplatform projects
189
* Ensures proper incremental compilation across platforms
190
*/
191
interface ExpectActualTracker {
192
/** Report expect declaration */
193
fun reportExpectDeclaration(
194
filePath: String,
195
fqName: String,
196
position: Int
197
): Unit
198
199
/** Report actual declaration */
200
fun reportActualDeclaration(
201
filePath: String,
202
fqName: String,
203
position: Int
204
): Unit
205
206
/** Get all expect declarations */
207
fun getExpectDeclarations(): Collection<ExpectActualDeclaration>
208
209
/** Get all actual declarations */
210
fun getActualDeclarations(): Collection<ExpectActualDeclaration>
211
212
/** Find actual declarations for expect */
213
fun findActuals(expectFqName: String): Collection<ExpectActualDeclaration>
214
215
/** Find expect declaration for actual */
216
fun findExpect(actualFqName: String): ExpectActualDeclaration?
217
}
218
219
/**
220
* Represents an expect or actual declaration
221
*/
222
data class ExpectActualDeclaration(
223
val filePath: String,
224
val fqName: String,
225
val position: Int,
226
val isExpect: Boolean
227
)
228
```
229
230
### InlineConstTracker
231
232
Tracks usage of inline constants for proper incremental compilation when constants change.
233
234
```kotlin { .api }
235
/**
236
* Tracks usage of inline constants
237
* Critical for incremental compilation when constant values change
238
*/
239
interface InlineConstTracker {
240
/** Report usage of inline constant */
241
fun report(
242
filePath: String,
243
position: Int,
244
owner: String,
245
name: String,
246
constType: ConstType
247
): Unit
248
249
/** Get all tracked inline constant usages */
250
fun getUsedInlineConsts(): Collection<InlineConstUsage>
251
252
/** Get inline constant usages for specific file */
253
fun getUsages(filePath: String): Collection<InlineConstUsage>
254
255
/** Check if constant change affects file */
256
fun isAffectedBy(filePath: String, changedConst: InlineConstUsage): Boolean
257
}
258
259
/**
260
* Represents usage of an inline constant
261
*/
262
data class InlineConstUsage(
263
val filePath: String,
264
val position: Int,
265
val owner: String,
266
val name: String,
267
val constType: ConstType
268
)
269
270
/**
271
* Types of inline constants
272
*/
273
enum class ConstType {
274
PROPERTY, ENUM_ENTRY, COMPANION_OBJECT_PROPERTY
275
}
276
```
277
278
### EnumWhenTracker
279
280
Tracks enum when expressions for incremental compilation when enum entries are added or removed.
281
282
```kotlin { .api }
283
/**
284
* Tracks enum when expressions
285
* Ensures recompilation when enum entries are added/removed
286
*/
287
interface EnumWhenTracker {
288
/** Report enum when expression */
289
fun report(
290
filePath: String,
291
position: Int,
292
enumClassFqName: String,
293
whenExpression: WhenExpressionInfo
294
): Unit
295
296
/** Get all tracked when expressions */
297
fun getWhenExpressions(): Collection<EnumWhenUsage>
298
299
/** Check if enum change affects when expressions */
300
fun isAffectedByEnumChange(
301
enumFqName: String,
302
addedEntries: Set<String>,
303
removedEntries: Set<String>
304
): Collection<EnumWhenUsage>
305
}
306
307
/**
308
* Information about when expression on enum
309
*/
310
data class WhenExpressionInfo(
311
val isExhaustive: Boolean,
312
val matchedEntries: Set<String>
313
)
314
315
/**
316
* Usage of enum in when expression
317
*/
318
data class EnumWhenUsage(
319
val filePath: String,
320
val position: Int,
321
val enumClassFqName: String,
322
val whenInfo: WhenExpressionInfo
323
)
324
```
325
326
### ImportTracker
327
328
Tracks import statements and their usage for optimizing incremental compilation.
329
330
```kotlin { .api }
331
/**
332
* Tracks import statements and their usage
333
* Helps optimize incremental compilation by tracking import dependencies
334
*/
335
interface ImportTracker {
336
/** Report import statement */
337
fun report(filePath: String, importedFqName: String): Unit
338
339
/** Report star import */
340
fun reportStarImport(filePath: String, packageFqName: String): Unit
341
342
/** Get all imports for file */
343
fun getImports(filePath: String): Collection<ImportInfo>
344
345
/** Get files that import from package */
346
fun getFilesImportingFrom(packageFqName: String): Collection<String>
347
348
/** Check if symbol change affects imports */
349
fun isAffectedBySymbolChange(symbolFqName: String): Collection<String>
350
}
351
352
/**
353
* Information about an import
354
*/
355
data class ImportInfo(
356
val importedFqName: String,
357
val isStarImport: Boolean
358
)
359
```
360
361
### Classpath Change Detection
362
363
Advanced classpath analysis for incremental compilation optimization.
364
365
```kotlin { .api }
366
/**
367
* Analyzes classpath changes for incremental compilation
368
*/
369
interface ClasspathChangesComputer {
370
/** Compute changes between classpaths */
371
fun compute(
372
previousClasspath: List<File>,
373
currentClasspath: List<File>
374
): ClasspathChanges
375
376
/** Create snapshot of current classpath */
377
fun createSnapshot(classpath: List<File>): ClasspathSnapshot
378
379
/** Compare snapshots for changes */
380
fun compareSnapshots(
381
previous: ClasspathSnapshot,
382
current: ClasspathSnapshot
383
): ClasspathChanges
384
}
385
386
/**
387
* Represents changes in classpath between compilations
388
*/
389
data class ClasspathChanges(
390
/** Files added to classpath */
391
val added: Set<File>,
392
393
/** Files removed from classpath */
394
val removed: Set<File>,
395
396
/** Files modified in classpath */
397
val modified: Set<File>,
398
399
/** Unchanged files */
400
val unchanged: Set<File>
401
) {
402
/** Check if any changes occurred */
403
val hasChanges: Boolean get() = added.isNotEmpty() || removed.isNotEmpty() || modified.isNotEmpty()
404
405
/** Get all changed files */
406
val changedFiles: Set<File> get() = added + removed + modified
407
}
408
409
/**
410
* Snapshot of classpath state
411
*/
412
interface ClasspathSnapshot {
413
/** Classpath entries in this snapshot */
414
val entries: List<ClasspathEntrySnapshot>
415
416
/** Timestamp when snapshot was created */
417
val timestamp: Long
418
419
/** Hash of the complete classpath */
420
val hash: String
421
}
422
423
/**
424
* Snapshot of individual classpath entry
425
*/
426
interface ClasspathEntrySnapshot {
427
/** File path */
428
val file: File
429
430
/** Content hash */
431
val hash: String
432
433
/** Last modified timestamp */
434
val lastModified: Long
435
436
/** File size */
437
val size: Long
438
439
/** ABI hash for incremental compilation */
440
val abiHash: String?
441
}
442
```
443
444
**Advanced Incremental Compilation Example:**
445
446
```kotlin
447
// Complete incremental compilation setup
448
class IncrementalCompilationManager {
449
private val lookupTracker = DetailedLookupTracker()
450
private val classpathComputer = ClasspathChangesComputer.create()
451
private var previousSnapshot: ClasspathSnapshot? = null
452
453
fun compile(
454
sources: List<File>,
455
classpath: List<File>,
456
outputDir: File
457
): CompilationResult {
458
459
// Check classpath changes
460
val currentSnapshot = classpathComputer.createSnapshot(classpath)
461
val classpathChanges = previousSnapshot?.let {
462
classpathComputer.compareSnapshots(it, currentSnapshot)
463
}
464
465
// Determine which sources need recompilation
466
val sourcesToCompile = if (classpathChanges?.hasChanges == true) {
467
// Significant classpath changes - recompile all
468
sources
469
} else {
470
// Check individual source changes
471
determineChangedSources(sources)
472
}
473
474
// Setup incremental compilation components
475
val incrementalComponents = IncrementalCompilationComponents.create().apply {
476
// Use our custom lookup tracker
477
(this as MutableIncrementalCompilationComponents).lookupTracker = this@IncrementalCompilationManager.lookupTracker
478
}
479
480
val configuration = CompilerConfiguration().apply {
481
put(JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS, incrementalComponents)
482
put(JVMConfigurationKeys.OUTPUT_DIRECTORY, outputDir)
483
}
484
485
// Perform compilation
486
val compiler = K2JVMCompiler()
487
val arguments = K2JVMCompilerArguments().apply {
488
destination = outputDir.absolutePath
489
classpath = classpath.joinToString(File.pathSeparator) { it.absolutePath }
490
freeArgs = sourcesToCompile.map { it.absolutePath }
491
}
492
493
val messageCollector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN, false)
494
val exitCode = compiler.exec(messageCollector, Services.EMPTY, arguments)
495
496
// Update snapshot for next compilation
497
previousSnapshot = currentSnapshot
498
499
return CompilationResult(
500
exitCode = exitCode,
501
compiledFiles = sourcesToCompile.size,
502
incrementalInfo = IncrementalInfo(
503
classpathChanges = classpathChanges,
504
recompiledSources = sourcesToCompile,
505
symbolLookups = lookupTracker.getUsedSymbols()
506
)
507
)
508
}
509
510
private fun determineChangedSources(sources: List<File>): List<File> {
511
// Implementation would check source file modifications
512
// and use lookup tracker to determine dependent files
513
return sources.filter { it.lastModified() > getLastCompilationTime() }
514
}
515
516
fun getImpactAnalysis(changedSymbol: String): Set<String> {
517
return lookupTracker.getDependentFiles(
518
LookupSymbol(changedSymbol, "", ScopeKind.CLASSIFIER)
519
)
520
}
521
}
522
523
// Result with incremental compilation information
524
data class IncrementalInfo(
525
val classpathChanges: ClasspathChanges?,
526
val recompiledSources: List<File>,
527
val symbolLookups: Set<LookupSymbol>
528
)
529
```
530
531
## Performance Optimization
532
533
```kotlin
534
// Incremental compilation configuration
535
data class IncrementalCompilationConfiguration(
536
/** Enable lookup tracking */
537
val enableLookupTracking: Boolean = true,
538
539
/** Enable expect/actual tracking */
540
val enableExpectActualTracking: Boolean = true,
541
542
/** Enable inline constant tracking */
543
val enableInlineConstTracking: Boolean = true,
544
545
/** Enable enum when tracking */
546
val enableEnumWhenTracking: Boolean = true,
547
548
/** Enable import tracking */
549
val enableImportTracking: Boolean = true,
550
551
/** Maximum cache size for tracking data */
552
val maxCacheSize: Int = 10000,
553
554
/** Whether to persist tracking data between sessions */
555
val persistTrackingData: Boolean = true,
556
557
/** Directory for storing incremental data */
558
val incrementalDataDir: File? = null
559
)
560
```