From 39e3db44d72ec4fe1b9c619d6a71b2b7c6e998d6 Mon Sep 17 00:00:00 2001 From: oungsi2000 Date: Sun, 8 Mar 2026 19:58:48 +0900 Subject: [PATCH 1/3] =?UTF-8?q?build:=20Kotlin=20=EB=B2=84=EC=A0=84=20?= =?UTF-8?q?=EC=83=81=ED=96=A5=20=EB=B0=8F=20=EC=BB=B4=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EB=9F=AC=20=ED=94=8C=EB=9F=AC=EA=B7=B8=EC=9D=B8=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kotlin 버전을 `2.2.21`에서 `2.3.10`으로 상향 조정하고, `aspectk-core` 모듈의 빌드 설정 및 플러그인 등록 로직을 개선했습니다. 또한, 실험적으로 도입되었던 호환성 레이어 관련 설계 문서들을 정리했습니다. - **버전 업데이트 및 환경 설정**: - Kotlin 및 `jetbrainsKotlinJvm` 버전을 `2.3.10`으로 업데이트했습니다. - 프로젝트 버전을 `0.1.1-LOCAL-0`으로 변경했습니다. - `aspectk-core` 모듈에 `buildConfig` 생성 설정을 추가했습니다. - **컴파일러 플러그인 (`AspectKCompilerPluginRegistrar.kt`)**: - `pluginId`를 `BuildConfig.COMPILER_PLUGIN_ID` 상수를 사용하도록 수정하여 하드코딩을 제거했습니다. - **문서 및 구조 정리**: - `aspectk-core-compat` 모듈의 상세 설계 및 컨벤션 문서(`PLAN.md`, `CONVENTIONS.md`)를 삭제했습니다. - **샘플 프로젝트**: - `sample/composeApp`의 `aspectk` 플러그인 버전을 `0.1.1-LOCAL-0`으로 업데이트했습니다. --- aspectk-core-compat/CONVENTIONS.md | 333 ---------------- aspectk-core-compat/PLAN.md | 373 ------------------ aspectk-core/build.gradle.kts | 1 + .../core/AspectKCompilerPluginRegistrar.kt | 3 + gradle.properties | 2 +- gradle/libs.versions.toml | 4 +- sample/composeApp/build.gradle.kts | 2 +- 7 files changed, 8 insertions(+), 710 deletions(-) delete mode 100644 aspectk-core-compat/CONVENTIONS.md delete mode 100644 aspectk-core-compat/PLAN.md diff --git a/aspectk-core-compat/CONVENTIONS.md b/aspectk-core-compat/CONVENTIONS.md deleted file mode 100644 index 8b992ee..0000000 --- a/aspectk-core-compat/CONVENTIONS.md +++ /dev/null @@ -1,333 +0,0 @@ -# aspectk-core-compat 코딩 컨벤션 - -## 1. 패키지 구조 - -``` -io.github.molelabs.aspectk.compat # 베이스: 인터페이스, 어노테이션, 유틸 -io.github.molelabs.aspectk.compat.k2220 # Kotlin 2.2.20 구현 -io.github.molelabs.aspectk.compat.k2221 # Kotlin 2.2.21 구현 -io.github.molelabs.aspectk.compat.k230 # Kotlin 2.3.0 구현 -``` - -### 패키지명 생성 규칙 - -Kotlin 버전 → 패키지 접미사 변환: - -| Kotlin 버전 | 패키지명 | -|-------------|---------| -| `2.2.20` | `k2220` | -| `2.2.21` | `k2221` | -| `2.3.0` | `k230` | -| `2.3.20-Beta1` | `k2320_beta1` | -| `2.3.20-dev-5706` | `k2320_dev_5706` | - -규칙: -- 접두사 `k` + 메이저.마이너.패치에서 `.` 제거 -- Pre-release classifier는 `_` 구분자로 소문자 변환 -- `-` → `_` 변환 - ---- - -## 2. 파일 명명 - -### 베이스 모듈 -| 파일 | 역할 | -|------|------| -| `CompatContext.kt` | 핵심 인터페이스 + Factory + ServiceLoader Companion | -| `CompatApi.kt` | `@CompatApi` 어노테이션 정의 | -| `KotlinToolingVersion.kt` | 버전 파싱/비교 `Comparable` 구현체 | - -### 버전별 모듈 -| 파일 | 역할 | -|------|------| -| `version.txt` | 모듈 루트. 타겟 Kotlin 버전 plain text (예: `2.2.20`) | -| `CompatContextImpl.kt` | `CompatContext` 구현체. **파일명 고정.** | -| `META-INF/services/io.github.molelabs.aspectk.compat.CompatContext$Factory` | ServiceLoader 등록 | - ---- - -## 3. CompatContext 인터페이스 메서드 컨벤션 - -### 네이밍 - -```kotlin -// 패턴: 원본 함수명 + "Compat" 접미사 -fun registerIrExtensionCompat(...) -fun addBackingFieldCompat(...) -fun createClassReferenceCompat(...) -fun isConcreteFunctionImplCompat(...) -``` - -- 원본 Kotlin 컴파일러 API 이름을 기반으로 하되, 접미사 `Compat`를 붙여 직접 호출과 구분. -- 단, 원본 이름이 너무 길면 의미를 유지하는 선에서 축약 가능. - -### 어노테이션 - -모든 compat 메서드에는 `@CompatApi` 어노테이션 필수: - -```kotlin -@CompatApi( - since = "2.3.0", - reason = ChangeReason.DELETED, - message = "getContainingClassSymbol()이 fir.analysis.checkers에서 삭제됨", -) -fun getContainingClassSymbolCompat(symbol: FirBasedSymbol<*>): FirClassSymbol<*>? -``` - -### 파라미터 - -- 원본 API의 receiver를 **첫 번째 파라미터**로 변환: - ```kotlin - // 원본: IrClass.addFakeOverrides(typeSystem) - // Compat: fun addFakeOverridesCompat(irClass: IrClass, typeSystem: IrTypeSystemContext) - ``` -- 확장 함수가 아닌 일반 함수로 선언 (인터페이스 제약). - ---- - -## 4. 버전별 CompatContextImpl 작성 규칙 - -### 베이스 모듈 (가장 낮은 지원 버전) - -```kotlin -// k2220/CompatContextImpl.kt -package io.github.molelabs.aspectk.compat.k2220 - -// 네이티브 API는 `as ...Native` 별칭으로 import -import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension.Companion.registerExtension as registerExtensionNative - -class CompatContextImpl : CompatContext { - // 모든 메서드를 직접 구현 - override fun registerIrExtensionCompat( - configuration: CompilerConfiguration, - extension: IrGenerationExtension, - ) { - registerExtensionNative(configuration, extension) - } - - class Factory : CompatContext.Factory { - override val minVersion = "2.2.20" - override fun create(): CompatContext = CompatContextImpl() - } -} -``` - -### 후속 버전 모듈 (위임 + override) - -```kotlin -// k230/CompatContextImpl.kt -package io.github.molelabs.aspectk.compat.k230 - -import io.github.molelabs.aspectk.compat.CompatContext -import io.github.molelabs.aspectk.compat.k2221.CompatContextImpl as DelegateType - -class CompatContextImpl : CompatContext by DelegateType() { - // 2.3.0에서 변경된 메서드만 override - override fun registerIrExtensionCompat(...) { - // 2.3.0의 새로운 API 사용 - } - - class Factory : CompatContext.Factory { - override val minVersion = "2.3.0" - override fun create(): CompatContext = CompatContextImpl() - } -} -``` - -### 핵심 규칙 - -1. **`by DelegateType()`** 패턴 필수. 변경되지 않은 메서드는 이전 버전에 자동 위임. -2. **이전 버전의 `CompatContextImpl`을 `DelegateType`으로 import alias**. 모든 모듈에서 동일한 이름 사용. -3. **override는 변경된 메서드만**. 불필요한 override 금지. -4. **Factory.minVersion은 `version.txt`와 일치**해야 한다. - ---- - -## 5. build.gradle.kts 컨벤션 - -### 베이스 모듈 - -```kotlin -plugins { - id("java-library") - id("org.jetbrains.kotlin.jvm") - alias(libs.plugins.diffplug.spotless) -} - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -dependencies { - compileOnly(libs.kotlin.compiler) - compileOnly(libs.kotlin.stdlib) -} -``` - -### 버전별 모듈 - -```kotlin -plugins { - id("java-library") - id("org.jetbrains.kotlin.jvm") - alias(libs.plugins.diffplug.spotless) -} - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -// version.txt에서 타겟 Kotlin 컴파일러 버전 읽기 -val kotlinVersion = providers.fileContents( - layout.projectDirectory.file("version.txt") -).asText.map { it.trim() } - -dependencies { - compileOnly(kotlinVersion.map { "org.jetbrains.kotlin:kotlin-compiler:$it" }) - compileOnly(libs.kotlin.stdlib) - api(project(":aspectk-core-compat")) // 인터페이스 - implementation(project(":aspectk-core-compat:<이전 버전>")) // 위임 대상 -} -``` - -### 핵심 규칙 - -1. `kotlin-compiler`는 반드시 **`compileOnly`**. 런타임에는 호스트 컴파일러가 제공. -2. 각 모듈은 **정확히 하나의 Kotlin 컴파일러 버전**에 대해서만 컴파일. -3. 베이스 인터페이스 모듈은 `api`로, 위임 대상 모듈은 `implementation`으로 의존. - ---- - -## 6. ServiceLoader 등록 - -### 파일 경로 - -``` -src/main/resources/META-INF/services/io.github.molelabs.aspectk.compat.CompatContext$Factory -``` - -> **주의**: 파일명에 `$`가 포함됨 (Java inner class 표기). - -### 파일 내용 (한 줄) - -``` -io.github.molelabs.aspectk.compat.k2220.CompatContextImpl$Factory -``` - ---- - -## 7. Import 컨벤션 - -### 네이티브 API import alias - -컴파일러 API를 직접 호출할 때, 이름 충돌을 피하기 위해 `as ...Native` 별칭 사용: - -```kotlin -// Good -import org.jetbrains.kotlin.ir.util.addSimpleDelegatingConstructor as addSimpleDelegatingConstructorNative - -// Bad — 이름 충돌 위험 -import org.jetbrains.kotlin.ir.util.addSimpleDelegatingConstructor -``` - -### 이전 버전 import alias - -```kotlin -// Good — 모든 모듈에서 동일한 이름 -import io.github.molelabs.aspectk.compat.k2220.CompatContextImpl as DelegateType - -// Bad — 구체 이름 사용 -import io.github.molelabs.aspectk.compat.k2220.CompatContextImpl -``` - ---- - -## 8. 문서화 - -### CompatContext 메서드 - -```kotlin -/** - * [IrClass]에 fake override를 추가합니다. - * - * Kotlin 2.3.0에서 [IrTypeSystemContext] 파라미터 타입이 변경됨. - */ -@CompatApi(since = "2.3.0", reason = ChangeReason.ABI_CHANGE) -fun addFakeOverridesCompat(irClass: IrClass, typeSystem: IrTypeSystemContext) -``` - -### 버전별 override - -```kotlin -// k230/CompatContextImpl.kt -/** - * 2.3.0: IrTypeSystemContext가 새 인터페이스로 변경. - * 이전 버전의 구현과 다르게 새 API를 직접 호출. - */ -override fun addFakeOverridesCompat(irClass: IrClass, typeSystem: IrTypeSystemContext) { - irClass.addFakeOverrides(typeSystem) // 2.3.0 네이티브 API -} -``` - ---- - -## 9. 테스트 컨벤션 - -### KotlinToolingVersion 테스트 - -```kotlin -class KotlinToolingVersionTest { - @Test - fun `parse stable version`() { ... } - - @Test - fun `parse dev version`() { ... } - - @Test - fun `compare versions`() { ... } -} -``` - -### Factory 해석 테스트 - -```kotlin -class CompatContextFactoryTest { - @Test - fun `resolve factory for exact version`() { ... } - - @Test - fun `resolve factory picks highest compatible`() { ... } - - @Test - fun `resolve factory ignores higher versions`() { ... } -} -``` - ---- - -## 10. 코드 스타일 - -기존 AspectK 프로젝트 규칙을 따른다: - -- **ktlint 1.8.0** via Spotless -- **indent**: 4 spaces -- **wildcard import 금지** -- **Apache 2.0 라이센스 헤더** 필수 (모든 `.kt`, `.kts` 파일) -- **`./gradlew spotlessApply`** 커밋 전 실행 -- JVM target: 17 - ---- - -## 11. 새 Kotlin 버전 추가 체크리스트 - -1. [ ] `aspectk-core-compat/k/` 디렉토리 생성 -2. [ ] `version.txt` 작성 (예: `2.3.20`) -3. [ ] `build.gradle.kts` 작성 (이전 버전 모듈에 `implementation` 의존) -4. [ ] `CompatContextImpl.kt` 작성 (`by DelegateType()` + 변경된 API만 override) -5. [ ] `META-INF/services/` ServiceLoader 등록 파일 생성 -6. [ ] `settings.gradle.kts`는 자동 탐색으로 별도 수정 불필요 -7. [ ] `./gradlew spotlessApply` 실행 -8. [ ] `./gradlew check` 전체 테스트 통과 확인 -9. [ ] CI 매트릭스에 새 버전 추가 diff --git a/aspectk-core-compat/PLAN.md b/aspectk-core-compat/PLAN.md deleted file mode 100644 index bc3ef43..0000000 --- a/aspectk-core-compat/PLAN.md +++ /dev/null @@ -1,373 +0,0 @@ -# aspectk-core-compat 실행 계획 - -## 배경 - -Kotlin 컴파일러 플러그인 API는 버전 간 안정성이 보장되지 않는다. 클래스 이름 변경, 메서드 시그니처 변경, API 삭제 등이 빈번하게 발생한다. AspectK가 여러 Kotlin 버전을 동시에 지원하려면, **컴파일러 API 호환성 레이어**가 필요하다. - -Metro 프로젝트의 `compiler-compat` 모듈 설계를 참고하여, AspectK에 맞는 `aspectk-core-compat` 모듈을 구축한다. - -## 참고 프로젝트 - -- [ZacSweers/metro/compiler-compat](https://github.com/ZacSweers/metro/tree/main/compiler-compat) -- 핵심 패턴: **Interface + ServiceLoader + Kotlin class delegation (`by`)** - ---- - -## Phase 1: 모듈 구조 설계 - -### 1-1. 디렉토리 구조 - -``` -aspectk-core-compat/ # 베이스 모듈: 인터페이스 + 유틸리티 -├── PLAN.md -├── CONVENTIONS.md -├── build.gradle.kts -├── src/main/kotlin/io/github/molelabs/aspectk/compat/ -│ ├── CompatContext.kt # 핵심 인터페이스 + ServiceLoader Factory -│ ├── CompatApi.kt # 호환성 어노테이션 (@CompatApi) -│ └── KotlinToolingVersion.kt # 버전 파싱/비교 유틸리티 -│ -├── k2220/ # Kotlin 2.2.20 구현 (베이스) -│ ├── build.gradle.kts -│ ├── version.txt # "2.2.20" -│ └── src/ -│ ├── main/kotlin/.../k2220/ -│ │ └── CompatContextImpl.kt # 모든 메서드 직접 구현 -│ └── main/resources/META-INF/services/ -│ └── io.github.molelabs.aspectk.compat.CompatContext$Factory -│ -├── k2221/ # Kotlin 2.2.21 구현 -│ ├── build.gradle.kts -│ ├── version.txt # "2.2.21" -│ └── src/ -│ ├── main/kotlin/.../k2221/ -│ │ └── CompatContextImpl.kt # k2220에 위임, 변경점만 override -│ └── main/resources/META-INF/services/ -│ └── io.github.molelabs.aspectk.compat.CompatContext$Factory -│ -└── k230/ # Kotlin 2.3.0 구현 (향후) - ├── build.gradle.kts - ├── version.txt # "2.3.0" - └── src/ - ├── main/kotlin/.../k230/ - │ └── CompatContextImpl.kt # k2221에 위임, 변경점만 override - └── main/resources/META-INF/services/ - └── io.github.molelabs.aspectk.compat.CompatContext$Factory -``` - -### 1-2. settings.gradle.kts 연동 - -```kotlin -// settings.gradle.kts에 동적 모듈 탐색 추가 -include(":aspectk-core-compat") -file("aspectk-core-compat").listFiles() - ?.filter { it.isDirectory && it.name.startsWith("k") } - ?.forEach { include(":aspectk-core-compat:${it.name}") } -``` - ---- - -## Phase 2: CompatContext 인터페이스 설계 - -### 2-1. 래핑 대상 API 분석 (AspectK에서 사용 중인 컴파일러 API) - -현재 `aspectk-core`가 사용하는 Kotlin 컴파일러 API를 분석한 결과, 다음 카테고리로 분류된다: - -#### 카테고리 A — 플러그인 등록 (변경 가능성: 중) -| API | 사용 위치 | 비고 | -|-----|----------|------| -| `IrGenerationExtension.registerExtension()` | `AspectKCompilerPluginRegistrar` | 2.4.0에서 API 이동 (Metro 참고) | -| `CompilerPluginRegistrar` | `AspectKCompilerPluginRegistrar` | 비교적 안정 | - -#### 카테고리 B — IR 선언 빌더 (변경 가능성: 높음) -| API | 사용 위치 | 비고 | -|-----|----------|------| -| `irFactory.buildClass {}` | `MethodSignatureGenerator` | ClassKind, visibility 등 | -| `irFactory.buildField {}` | `MethodSignatureGenerator` | isStatic, isFinal | -| `irFactory.buildProperty {}` | `MethodSignatureGenerator` | backingField 연결 | -| `buildValueParameter {}` | `MethodSignatureGenerator` | IrValueParameterBuilder | -| `addSimpleDelegatingConstructor()` | `MethodSignatureGenerator` | 생성자 위임 | -| `createExpressionBody()` | `MethodSignatureGenerator` | 표현식 바디 | - -#### 카테고리 C — IR 빌더 DSL (변경 가능성: 중) -| API | 사용 위치 | 비고 | -|-----|----------|------| -| `irCall()` | 모든 Generator | 함수 호출 IR 생성 | -| `irBlock()` | `AdviceCallGenerator` | 블록 표현식 | -| `irGet()`, `irGetField()`, `irGetObject()` | `JoinPointGenerator` | 값/필드/객체 참조 | -| `irNull()`, `irString()`, `irBoolean()` | 여러 Generator | 리터럴 | -| `irVararg()` | `IrExtension` | 가변인자 | -| `DeclarationIrBuilder` | `IrExtension` | 빌더 컨텍스트 | - -#### 카테고리 D — IR 트리 순회/변환 (변경 가능성: 중) -| API | 사용 위치 | 비고 | -|-----|----------|------| -| `IrVisitorVoid` | `AspectVisitor`, `InheritableVisitor` | IR 트리 방문자 | -| `IrElementTransformerVoidWithContext` | `AspectTransformer` | IR 트리 변환자 | -| `acceptChildrenVoid()` | `AdviceGenerationExtension` | 자식 순회 | -| `deepCopyWithSymbols()` | `AdviceCallGenerator` | IR 깊은 복사 | - -#### 카테고리 E — 심볼 해석 (변경 가능성: 중) -| API | 사용 위치 | 비고 | -|-----|----------|------| -| `IrPluginContext.referenceClass()` | `AspectKIrCompilerContext` | ClassId → IrClassSymbol | -| `IrPluginContext.referenceFunctions()` | `IrExtension` | CallableId → IrFunctionSymbol | -| `IrPluginContext.irBuiltIns` | `MethodSignatureGenerator`, `IrExtension` | 기본 타입 | -| `IrPluginContext.irFactory` | `MethodSignatureGenerator` | IR 팩토리 | - -#### 카테고리 F — 타입 시스템 (변경 가능성: 낮음~중) -| API | 사용 위치 | 비고 | -|-----|----------|------| -| `IrType.classFqName` | `AspectVisitor`, `MethodSignatureGenerator` | 타입 FqName | -| `IrType.classOrNull` | `IrExtension` | nullable 클래스 심볼 | -| `IrType.typeWith()` | `MethodSignatureGenerator` | 파라미터화된 타입 | -| `IrType.isNullable()` | `MethodSignatureGenerator` | nullable 체크 | - -#### 카테고리 G — 구현체 의존 (변경 가능성: 높음) -| API | 사용 위치 | 비고 | -|-----|----------|------| -| `IrFunctionImpl` | `AspectTransformer`, `InheritableVisitor` | Fake Override 구분용 | -| `IrClassReferenceImpl` | `IrExtension` | KClass 표현식 직접 생성 | -| `allOverridden()` | `InheritableVisitor` | 오버라이드 체인 탐색 | - -### 2-2. CompatContext 인터페이스 초안 - -```kotlin -interface CompatContext { - // --- 카테고리 A: 플러그인 등록 --- - fun registerIrExtension( - configuration: CompilerConfiguration, - extension: IrGenerationExtension, - ) - - // --- 카테고리 B: IR 선언 빌더 --- - fun buildClassCompat( - irFactory: IrFactory, - builder: IrClassBuilder.() -> Unit, - ): IrClass - - fun addSimpleDelegatingConstructorCompat( - irClass: IrClass, - superConstructor: IrConstructor, - irBuiltIns: IrBuiltIns, - isPrimary: Boolean, - ): IrConstructor - - fun createExpressionBodyCompat( - irFactory: IrFactory, - expression: IrExpression, - ): IrExpressionBody - - // --- 카테고리 D: IR 구현체 체크 --- - fun isConcreteFunctionImpl(declaration: IrSimpleFunction): Boolean - - // --- 카테고리 G: KClass 표현식 --- - fun createClassReference( - startOffset: Int, - endOffset: Int, - classType: IrType, - pluginContext: IrPluginContext, - ): IrExpression - - // --- Factory --- - interface Factory { - val minVersion: String - fun create(): CompatContext - } - - companion object { - fun create(): CompatContext { /* ServiceLoader */ } - } -} -``` - -> **원칙**: 현재 API가 버전 간에 **실제로 변경될 때만** 메서드를 추가한다. 아직 변경되지 않은 API를 미리 래핑하지 않는다. - -### 2-3. @CompatApi 어노테이션 - -```kotlin -@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY) -annotation class CompatApi( - val since: String, // 변경이 발생한 Kotlin 버전 - val reason: ChangeReason, // DELETED, RENAMED, ABI_CHANGE, COMPAT - val message: String = "", // 설명 -) - -enum class ChangeReason { DELETED, RENAMED, ABI_CHANGE, COMPAT } -``` - ---- - -## Phase 3: 베이스 구현 (k2220) - -### 3-1. k2220/CompatContextImpl.kt - -- `CompatContext` 인터페이스의 **모든 메서드를 직접 구현** -- Kotlin 2.2.20의 네이티브 API를 직접 호출 -- 가장 낮은 지원 버전이므로 다른 모듈에 의존하지 않음 -- `import ... as ...Native` 패턴으로 이름 충돌 방지 - -### 3-2. ServiceLoader 등록 - -``` -META-INF/services/io.github.molelabs.aspectk.compat.CompatContext$Factory -→ io.github.molelabs.aspectk.compat.k2220.CompatContextImpl$Factory -``` - ---- - -## Phase 4: 위임 기반 버전별 구현 - -### 4-1. 위임 체인 - -``` -k230 --delegates-to--> k2221 -k2221 --delegates-to--> k2220 -k2220 (베이스, 모든 메서드 직접 구현) -``` - -### 4-2. 각 모듈의 build.gradle.kts 패턴 - -```kotlin -plugins { id("org.jetbrains.kotlin.jvm") } - -dependencies { - val kotlinVersion = providers.fileContents( - layout.projectDirectory.file("version.txt") - ).asText.map { it.trim() } - compileOnly(kotlinVersion.map { "org.jetbrains.kotlin:kotlin-compiler:$it" }) - compileOnly(libs.kotlin.stdlib) - api(project(":aspectk-core-compat")) // 베이스 인터페이스 - implementation(project(":aspectk-core-compat:k2220")) // 이전 버전 위임 대상 -} -``` - -### 4-3. 위임 구현 패턴 - -```kotlin -// k2221/CompatContextImpl.kt -package io.github.molelabs.aspectk.compat.k2221 - -import io.github.molelabs.aspectk.compat.CompatContext -import io.github.molelabs.aspectk.compat.k2220.CompatContextImpl as DelegateType - -class CompatContextImpl : CompatContext by DelegateType() { - // 2.2.21에서 변경된 API만 override - - class Factory : CompatContext.Factory { - override val minVersion = "2.2.21" - override fun create(): CompatContext = CompatContextImpl() - } -} -``` - ---- - -## Phase 5: aspectk-core 통합 - -### 5-1. aspectk-core의 의존성 변경 - -```kotlin -// aspectk-core/build.gradle.kts -dependencies { - implementation(project(":aspectk-core-compat")) - // 모든 버전별 구현체를 shade (런타임에 ServiceLoader가 탐색) - runtimeOnly(project(":aspectk-core-compat:k2220")) - runtimeOnly(project(":aspectk-core-compat:k2221")) - runtimeOnly(project(":aspectk-core-compat:k230")) -} -``` - -### 5-2. aspectk-core 코드 마이그레이션 - -```kotlin -// Before (직접 호출) -IrGenerationExtension.registerExtension(project, extension) - -// After (compat 레이어 경유) -val compat = CompatContext.create() -compat.registerIrExtension(configuration, extension) -``` - -### 5-3. 마이그레이션 우선순위 - -1. **즉시 래핑** — 이미 버전 간 변경이 확인된 API (예: `IrGenerationExtension.registerExtension`) -2. **예방적 래핑** — 구현체 클래스 직접 사용 (`IrFunctionImpl`, `IrClassReferenceImpl`) -3. **나중에 필요 시 추가** — 안정적인 API (`irCall`, `irGet` 등 빌더 DSL) - ---- - -## Phase 6: Shading 설정 - -### 6-1. Shadow Plugin 적용 - -`aspectk-core-compat` 모듈들은 **별도로 publish되지 않는다**. `aspectk-core` JAR에 shade(포함)된다. - -```kotlin -// aspectk-core/build.gradle.kts -plugins { - id("com.gradleup.shadow") // 또는 com.github.johnrengelman.shadow -} - -tasks.shadowJar { - configurations = listOf(project.configurations.runtimeOnly.get()) - relocate("io.github.molelabs.aspectk.compat", "io.github.molelabs.aspectk.core.compat.shaded") -} -``` - ---- - -## Phase 7: 테스트 - -### 7-1. 단위 테스트 -- `KotlinToolingVersion` 파싱/비교 테스트 -- `CompatContext.create()` ServiceLoader 해석 테스트 -- 각 버전별 Factory의 `minVersion` 검증 - -### 7-2. 통합 테스트 -- 기존 `aspectk-core-tests` 모듈의 테스트가 모든 지원 버전에서 통과하는지 확인 -- CI에서 각 Kotlin 버전별 매트릭스 테스트 - ---- - -## Phase 8: CI/CD - -### 8-1. Kotlin 버전 매트릭스 -```yaml -strategy: - matrix: - kotlin-version: ['2.2.20', '2.2.21', '2.3.0'] -``` - -### 8-2. 새 Kotlin 버전 지원 추가 시 워크플로우 -1. `aspectk-core-compat/k/` 디렉토리 생성 -2. `version.txt` 작성 -3. `CompatContextImpl.kt` 작성 (이전 버전에 위임, 변경점만 override) -4. `META-INF/services/` 등록 -5. CI 매트릭스에 버전 추가 -6. 기존 테스트 실행하여 검증 - ---- - -## 타임라인 - -| Phase | 설명 | 의존성 | -|-------|------|--------| -| 1 | 모듈 구조 생성 | — | -| 2 | CompatContext 인터페이스 설계 | Phase 1 | -| 3 | k2220 베이스 구현 | Phase 2 | -| 4 | k2221 위임 구현 | Phase 3 | -| 5 | aspectk-core 통합 | Phase 4 | -| 6 | Shading 설정 | Phase 5 | -| 7 | 테스트 | Phase 5 | -| 8 | CI 매트릭스 | Phase 7 | - ---- - -## 주의사항 - -1. **최소 래핑 원칙**: 변경이 확인된 API만 래핑한다. 불필요한 추상화는 유지보수 부담을 늘린다. -2. **`compileOnly` 의존성**: 각 버전 모듈은 `kotlin-compiler`를 `compileOnly`로 선언한다. 런타임에는 호스트 컴파일러가 제공. -3. **Shade 필수**: compat 모듈은 별도 publish하지 않는다. `aspectk-core` JAR에 포함. -4. **`version.txt` 컨벤션**: 각 버전 모듈의 루트에 타겟 Kotlin 버전을 plain text로 기록. -5. **ServiceLoader 파일명**: `io.github.molelabs.aspectk.compat.CompatContext$Factory` (`$` 주의). diff --git a/aspectk-core/build.gradle.kts b/aspectk-core/build.gradle.kts index 058ffed..a8c57ac 100644 --- a/aspectk-core/build.gradle.kts +++ b/aspectk-core/build.gradle.kts @@ -24,6 +24,7 @@ plugins { aspectKBuild { publish("AspectK Compiler Plugin") + generateBuildConfig("io.github.molelabs.aspectk.core") enableBackwardsCompatibility() } diff --git a/aspectk-core/src/main/kotlin/io/github/molelabs/aspectk/core/AspectKCompilerPluginRegistrar.kt b/aspectk-core/src/main/kotlin/io/github/molelabs/aspectk/core/AspectKCompilerPluginRegistrar.kt index 57bc4ad..1ef2657 100644 --- a/aspectk-core/src/main/kotlin/io/github/molelabs/aspectk/core/AspectKCompilerPluginRegistrar.kt +++ b/aspectk-core/src/main/kotlin/io/github/molelabs/aspectk/core/AspectKCompilerPluginRegistrar.kt @@ -28,6 +28,9 @@ import org.jetbrains.kotlin.config.CompilerConfiguration @OptIn(ExperimentalCompilerApi::class) @AutoService(CompilerPluginRegistrar::class) internal class AspectKCompilerPluginRegistrar : CompilerPluginRegistrar() { + override val pluginId: String + get() = BuildConfig.COMPILER_PLUGIN_ID + // Declares that this plugin targets the K2 (FIR + IR) compiler. override val supportsK2: Boolean get() = true diff --git a/gradle.properties b/gradle.properties index 01f8cf7..9bf4f80 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,4 +10,4 @@ org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8 org.jetbrains.dokka.experimental.gradle.pluginMode=V2EnabledWithHelpers PUBLISH_GROUP=io.github.mole-labs -PUBLISH_VERSION=0.1.0 +PUBLISH_VERSION=0.1.1-LOCAL-0 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8b9a174..a15bbe4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,13 +1,13 @@ [versions] dokka = "2.1.0" -kotlin = "2.2.21" +kotlin = "2.3.10" junitJupiter = "5.8.1" autoService = "1.1.1" binaryCompatibilityValidator = "0.18.1" spotless = "8.1.0" kotlinx-coroutines = "1.10.2" mockk = "1.13.8" -jetbrainsKotlinJvm = "2.2.21" +jetbrainsKotlinJvm = "2.3.10" mavenPublish = "0.34.0" [libraries] diff --git a/sample/composeApp/build.gradle.kts b/sample/composeApp/build.gradle.kts index 8a2f5e8..92a11a8 100644 --- a/sample/composeApp/build.gradle.kts +++ b/sample/composeApp/build.gradle.kts @@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask plugins { alias(libs.plugins.android.application) alias(libs.plugins.ksp) - id("io.github.mole-labs.aspectk") version "0.1.0" + id("io.github.mole-labs.aspectk") version "0.1.1-LOCAL-0" alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.kotlin.compose) alias(libs.plugins.compose.multiplatform) From 09769a2284d13ccba51a9dd71a2d2745b49fdde0 Mon Sep 17 00:00:00 2001 From: oungsi2000 Date: Sun, 8 Mar 2026 20:51:38 +0900 Subject: [PATCH 2/3] =?UTF-8?q?build:=20Kotlin=20=EB=B2=84=EC=A0=84=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=200.1.1=20=EB=A6=B4=EB=A6=AC=EC=A6=88=20=EC=A4=80?= =?UTF-8?q?=EB=B9=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 지원하는 Kotlin 버전 목록을 기반으로 프로젝트의 컴파일러 버전을 검증하는 로직을 추가하고, 정식 릴리즈를 위해 버전을 업데이트했습니다. - **Kotlin 버전 검증 로직 도입**: - `supported-versions.txt` 파일을 추가하여 지원되는 Kotlin 버전(2.2.20 ~ 2.3.10)을 명시했습니다. - `AspectKBuildPlugin`에서 위 파일을 읽어 `BuildConfig`에 지원 버전 목록(`SUPPORTED_KOTLIN_VERSIONS`)을 생성하도록 수정했습니다. - `AspectKGradleSubPlugin`에서 현재 사용 중인 Kotlin 컴파일러 버전이 지원 범위 내에 있는지 확인하고, 아닐 경우 `GradleException`을 발생시키도록 개선했습니다. - **프로젝트 및 샘플 업데이트**: - 프로젝트 전체 버전을 `0.1.1-LOCAL-0`에서 `0.1.1`로 업데이트했습니다. - 샘플 프로젝트의 Kotlin 버전을 `2.3.10`으로 상향 조정했습니다. - **기타**: - `AspectKGradleSubPlugin` 및 `AspectKBuildPlugin`의 코드 포맷팅을 정리했습니다. --- .../aspectk/plugin/AspectKGradleSubPlugin.kt | 49 +++++++++++++++++-- .../aspectk/build/AspectKBuildPlugin.kt | 23 ++++++++- gradle.properties | 2 +- sample/composeApp/build.gradle.kts | 2 +- sample/gradle/libs.versions.toml | 2 +- supported-versions.txt | 4 ++ 6 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 supported-versions.txt diff --git a/aspectk-plugin/src/main/kotlin/io/github/molelabs/aspectk/plugin/AspectKGradleSubPlugin.kt b/aspectk-plugin/src/main/kotlin/io/github/molelabs/aspectk/plugin/AspectKGradleSubPlugin.kt index 5888fa3..1e278dd 100644 --- a/aspectk-plugin/src/main/kotlin/io/github/molelabs/aspectk/plugin/AspectKGradleSubPlugin.kt +++ b/aspectk-plugin/src/main/kotlin/io/github/molelabs/aspectk/plugin/AspectKGradleSubPlugin.kt @@ -15,15 +15,21 @@ */ package io.github.molelabs.aspectk.plugin +import org.gradle.api.GradleException import org.gradle.api.Project import org.gradle.api.provider.Provider import org.gradle.api.tasks.SourceSetContainer +import org.jetbrains.kotlin.buildtools.api.ExperimentalBuildToolsApi +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerPluginSupportPlugin import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet.Companion.COMMON_MAIN_SOURCE_SET_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetContainer import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact import org.jetbrains.kotlin.gradle.plugin.SubpluginOption +import org.jetbrains.kotlin.gradle.plugin.kotlinToolingVersion +import org.jetbrains.kotlin.tooling.core.KotlinToolingVersion internal class AspectKGradleSubPlugin : KotlinCompilerPluginSupportPlugin { override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider> { @@ -41,19 +47,54 @@ internal class AspectKGradleSubPlugin : KotlinCompilerPluginSupportPlugin { override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean = true + @OptIn(ExperimentalBuildToolsApi::class, ExperimentalKotlinGradlePluginApi::class) override fun apply(target: Project) { + val compilerVersionProvider = + target.kotlinExtension.compilerVersion.map { KotlinToolingVersion(it) } + ?: target.provider { target.kotlinToolingVersion } + + val compilerVersion = compilerVersionProvider.get() + val supportedVersions = BuildConfig.SUPPORTED_KOTLIN_VERSIONS.map(::KotlinToolingVersion) + val minSupported = supportedVersions.min() + val maxSupported = supportedVersions.max() + val isSupported = compilerVersion in minSupported..maxSupported + + if (!isSupported) { + if (compilerVersion < minSupported) { + throw GradleException( + """ + "AspectK '${BuildConfig.VERSION} requires Kotlin ${BuildConfig.SUPPORTED_KOTLIN_VERSIONS.first()} or later, but this build uses $compilerVersion" + "Supported Kotlin versions: ${BuildConfig.SUPPORTED_KOTLIN_VERSIONS.first()} - ${BuildConfig.SUPPORTED_KOTLIN_VERSIONS.last()}" + """.trimIndent(), + ) + } else { + throw GradleException( + """ + This build uses unrecognized Kotlin version '$compilerVersion" + "Supported Kotlin versions: ${BuildConfig.SUPPORTED_KOTLIN_VERSIONS.first()} - ${BuildConfig.SUPPORTED_KOTLIN_VERSIONS.last()}" + """.trimIndent(), + ) + } + } + val aspectKRuntimeDependency = target.provider { - target.dependencyFactory.create(BuildConfig.GROUP, "aspectk-runtime", BuildConfig.VERSION) + target.dependencyFactory.create( + BuildConfig.GROUP, + "aspectk-runtime", + BuildConfig.VERSION, + ) } target.pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") { val kotlin = target.extensions.getByName("kotlin") as KotlinSourceSetContainer val commonMainSourceSet = kotlin.sourceSets.getByName(COMMON_MAIN_SOURCE_SET_NAME) - target.configurations.named(commonMainSourceSet.implementationConfigurationName).configure { - it.dependencies.addLater(aspectKRuntimeDependency) - } + target.configurations + .named(commonMainSourceSet.implementationConfigurationName) + .configure { + it.dependencies.addLater(aspectKRuntimeDependency) + } } target.pluginManager.withPlugin("org.jetbrains.kotlin.jvm") { diff --git a/buildSrc/src/main/kotlin/io/github/molelabs/aspectk/build/AspectKBuildPlugin.kt b/buildSrc/src/main/kotlin/io/github/molelabs/aspectk/build/AspectKBuildPlugin.kt index 5f52c04..6b2a1de 100644 --- a/buildSrc/src/main/kotlin/io/github/molelabs/aspectk/build/AspectKBuildPlugin.kt +++ b/buildSrc/src/main/kotlin/io/github/molelabs/aspectk/build/AspectKBuildPlugin.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask +import org.jetbrains.kotlin.tooling.core.KotlinToolingVersion private val Project.aspectKGroupId get() = property("PUBLISH_GROUP") as String private val Project.aspectKVersion get() = property("PUBLISH_VERSION") as String @@ -101,19 +102,39 @@ class AspectKBuildPlugin : Plugin { kotlin.explicitApi() } project.pluginManager.withPlugin("org.jetbrains.kotlin.jvm", kotlinPluginHandler) - project.pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform", kotlinPluginHandler) + project.pluginManager.withPlugin( + "org.jetbrains.kotlin.multiplatform", + kotlinPluginHandler, + ) } override fun generateBuildConfig(basePackage: String) { project.pluginManager.apply("com.github.gmazzo.buildconfig") val buildConfig = project.extensions.getByName("buildConfig") as BuildConfigExtension + + val versionAliasesFile = + project.isolated.rootProject.projectDirectory + .file("supported-versions.txt") + val supportedVersions = + versionAliasesFile.asFile + .readLines() + .filterNot { it.isBlank() || it.startsWith('#') } + .map { KotlinToolingVersion(it) } + .sorted() + buildConfig.apply { packageName(basePackage) buildConfigField("GROUP", project.aspectKGroupId) buildConfigField("VERSION", project.aspectKVersion) buildConfigField("COMPILER_PLUGIN_ID", "io.github.mole-labs.aspectk") buildConfigField("COMPILER_PLUGIN_ARTIFACT", "aspectk-core") + + buildConfigField( + "List", + "SUPPORTED_KOTLIN_VERSIONS", + "listOf(${supportedVersions.joinToString { "\"$it\"" }})", + ) } } diff --git a/gradle.properties b/gradle.properties index 9bf4f80..796c733 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,4 +10,4 @@ org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8 org.jetbrains.dokka.experimental.gradle.pluginMode=V2EnabledWithHelpers PUBLISH_GROUP=io.github.mole-labs -PUBLISH_VERSION=0.1.1-LOCAL-0 +PUBLISH_VERSION=0.1.1 diff --git a/sample/composeApp/build.gradle.kts b/sample/composeApp/build.gradle.kts index 92a11a8..170120c 100644 --- a/sample/composeApp/build.gradle.kts +++ b/sample/composeApp/build.gradle.kts @@ -4,8 +4,8 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask plugins { alias(libs.plugins.android.application) alias(libs.plugins.ksp) - id("io.github.mole-labs.aspectk") version "0.1.1-LOCAL-0" alias(libs.plugins.kotlin.multiplatform) + id("io.github.mole-labs.aspectk") version "0.1.1" alias(libs.plugins.kotlin.compose) alias(libs.plugins.compose.multiplatform) } diff --git a/sample/gradle/libs.versions.toml b/sample/gradle/libs.versions.toml index 095ee39..c9987cc 100644 --- a/sample/gradle/libs.versions.toml +++ b/sample/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "2.2.21" +kotlin = "2.3.10" compose-multiplatform = "1.8.0" agp = "8.7.3" androidx-activity-compose = "1.9.3" diff --git a/supported-versions.txt b/supported-versions.txt new file mode 100644 index 0000000..bc5f8fb --- /dev/null +++ b/supported-versions.txt @@ -0,0 +1,4 @@ +2.2.20 +2.2.21 +2.3.0 +2.3.10 \ No newline at end of file From 0e1fdbb149c729ed64834602150d92ba79e014f7 Mon Sep 17 00:00:00 2001 From: oungsi2000 Date: Sun, 8 Mar 2026 21:00:50 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=EC=B0=B8=EC=A1=B0=EB=90=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EC=82=AC=ED=95=AD=EC=9D=84=20=EB=B0=94=ED=83=95?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9E=91=EC=84=B1=ED=95=9C=20=EC=BB=A4?= =?UTF-8?q?=EB=B0=8B=20=EB=A9=94=EC=8B=9C=EC=A7=80=EC=9E=85=EB=8B=88?= =?UTF-8?q?=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ==== Commit Message ==== feat: Kotlin 버전 검증 로직 추가 및 지원 범위 확대 (0.1.1) 지원되지 않는 Kotlin 버전에서 빌드 시 명시적인 에러를 발생시키는 검증 로직을 추가하고, 지원 버전을 2.3.10까지 확대했습니다. - **Kotlin 버전 검증 및 지원 확대**: - 빌드 설정 시 프로젝트의 Kotlin 컴파일러 버전이 지원 범위를 벗어나면 `GradleException`을 발생시키도록 개선했습니다. - `supported-versions.txt`를 통해 지원 버전을 관리하며, 현재 지원 범위를 `2.2.20 ~ 2.3.10`으로 업데이트했습니다. - `BuildConfig`에 `SUPPORTED_KOTLIN_VERSIONS` 상수를 추가하여 관리 효율성을 높였습니다. - **기타 변경 사항**: - Kotlin 버전을 `2.3.10`으로 업그레이드했습니다. - `AspectKCompilerPluginRegistrar`에서 하드코딩된 `pluginId`를 `BuildConfig.COMPILER_PLUGIN_ID` 참조 방식으로 변경했습니다. - 문서(`compatibility.md`, `changelog.md`)에 0.1.1 버전의 변경 사항과 호환성 정보를 반영했습니다. --- docs/reference/changelog.md | 13 ++++++++++++- docs/reference/compatibility.md | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/reference/changelog.md b/docs/reference/changelog.md index 04a8d52..7602bc1 100644 --- a/docs/reference/changelog.md +++ b/docs/reference/changelog.md @@ -11,7 +11,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Dokka API documentation site - MkDocs-based documentation portal -## [0.x.x] — Initial Release +## [0.1.1] + +### Added +- Kotlin version validation: AspectK now throws a `GradleException` at configuration time if the project's Kotlin compiler version is not in the supported range +- `supported-versions.txt` as the single source of truth for supported Kotlin versions (2.2.20 ~ 2.3.10) +- `SUPPORTED_KOTLIN_VERSIONS` constant generated into `BuildConfig` from `supported-versions.txt` + +### Changed +- Kotlin version updated to 2.3.10 +- `pluginId` in `AspectKCompilerPluginRegistrar` now references `BuildConfig.COMPILER_PLUGIN_ID` instead of a hardcoded string + +## [0.1.0] — Initial Release ### Added - `@Aspect` annotation to mark aspect classes diff --git a/docs/reference/compatibility.md b/docs/reference/compatibility.md index e986a45..4dad170 100644 --- a/docs/reference/compatibility.md +++ b/docs/reference/compatibility.md @@ -8,6 +8,7 @@ in Kotlin 2.0 and requires K2 compiler mode. | AspectK Version | Supported Kotlin Range | |-----------------|----------------------| | 0.1.0 | 2.2.20 ~ 2.2.21 | +| 0.1.1 | 2.2.20 ~ 2.3.10 | !!! note Each AspectK release is tested against a specific Kotlin minor series. Using a Kotlin