用途
Pseudo-KMP 用于在 IntelliJ 社区单体仓库中创建、迁移或修改 pseudo-KMP 模块。该单体仓库由 JPS 编译,而 JPS 不支持 Kotlin Multiplatform。为模拟 KMP 的 expect/actual 声明,Fleet 团队创建了 expects-compiler-plugin 编译器插件。本技能说明如何设置和使用采用该机制的模块。
主要能力
- 通过 expects-compiler-plugin 在 JPS 环境下模拟 KMP 的 expect/actual 声明。
- 支持在公共源码集与平台特定源码集之间组织代码。
- 提供模块配置清单,确保模块通过结构测试校验。
- 提供 IML 文件配置方式,注册多平台支持依赖与编译器插件。
- 提供 expect/actual 的编写模式与平台后缀约定。
模块配置清单
创建或迁移 pseudo-KMP 模块时:
-
在结构测试中注册。将模块名加入
com.intellij.ideaProjectStructure.fast.MultiplatformModuleStructureTest中的multiplatformModules列表(路径:tests/ideaProjectStructure/testSrc/com/intellij/ideaProjectStructure/fast/MultiplatformModuleStructureTest.kt)。 -
仅使用允许的库依赖。测试强制使用封闭集合:
- kotlin-stdlib
- kotlinx-coroutines-core
- kotlinx-coroutines-debug
- jetbrains-annotations
- kotlinx-serialization-core
- kotlinx-serialization-json
- kotlin-reflect
- kotlinx-collections-immutable
如需新增库,将其加入测试中的
allowedMultiplatformLibraryDependencies。对其他多平台模块(即multiplatformModules中的模块)的依赖也是允许的。 -
设置源码文件夹:
src/— 公共代码srcJvm/或srcJvmMain/— JVM 特定代码(Java 代码也可放在此处)srcWasmJs/或srcWasmJsMain/— WasmJs 特定代码srcJsMain/— JS 特定代码srcNativeMain/— Native 特定代码
重要:不要在
.iml文件中将srcWasmJs/srcWasmJsMain添加为源码根。IDE 会尝试以 JVM 模式编译它。 -
在公共源码根添加
.pseudoCommonKotlinSourceSet标记文件(例如src/.pseudoCommonKotlinSourceSet)。该空文件告知 Kotlin 插件该源码集为公共代码,防止 Optimize Imports 移除在 JVM 上不必要但在公共代码中必需的导入(例如@JvmStatic、@JvmField)。
IML 文件设置
.iml 文件需要两项内容:multiplatformSupport 依赖和编译器插件注册。
以 community/platform/util/multiplatform/intellij.platform.util.multiplatform.iml 为参考。要查找当前插件版本,请查看该文件而非硬编码。
- 添加支持模块依赖(PROVIDED 作用域):
<orderEntry type="module" module-name="intellij.platform.multiplatformSupport" scope="PROVIDED" />
- 添加带 expects-compiler-plugin 的 Kotlin facet:
<facet type="kotlin-language" name="Kotlin">
<configuration version="5" platform="JVM 1.8" allPlatforms="JVM [1.8]" useProjectSettings="false">
<compilerSettings>
<option name="additionalArguments" value="-Xjvm-default=all ..." />
</compilerSettings>
<compilerArguments>
<stringArguments>
<stringArg name="jvmTarget" arg="1.8" />
<stringArg name="apiVersion" arg="2.3" />
<stringArg name="languageVersion" arg="2.3" />
</stringArguments>
<arrayArguments>
<arrayArg name="pluginClasspaths">
<args>$MAVEN_REPOSITORY$/jetbrains/fleet/expects-compiler-plugin/{VERSION}/expects-compiler-plugin-{VERSION}.jar</args>
</arrayArg>
</arrayArguments>
</compilerArguments>
</configuration>
</facet>
将 {VERSION} 替换为参考 IML 中的版本。
Expect/Actual 模式
仅支持顶层函数。expect 和 actual 声明必须在同一模块中。
公共侧(位于 src/):使用 linkToActual() 作为函数体:
import fleet.util.multiplatform.linkToActual
internal fun myFunction(param1: String, param2: Int): Boolean = linkToActual()
平台侧(位于 srcJvm/、srcWasmJs/ 等):使用 @Actual 注解。函数名必须是 expect 函数名加平台后缀:
import fleet.util.multiplatform.Actual
@Actual
internal fun myFunctionJvm(param1: String, param2: Int): Boolean {
// JVM implementation
}
平台后缀:
| 平台 | 后缀 | 源码文件夹 |
|---|---|---|
| JVM | Jvm | srcJvm/ 或 srcJvmMain/ |
| WasmJs | WasmJs | srcWasmJs/ 或 srcWasmJsMain/ |
| JS | Js | srcJsMain/ |
| Native | Native | srcNativeMain/ |
| 多平台构建 | Impl(罕见) |
真实示例:
公共侧(community/platform/util/base/multiplatform/src/com/intellij/util/text/CharArrayUtilKmp.kt):
internal fun getCharsPlatformSpecific(
sequence: CharSequence, srcOffset: Int, dst: CharArray, dstOffset: Int, len: Int
): Boolean = linkToActual()
JVM actual(community/platform/util/base/multiplatform/srcJvmMain/com/intellij/util/text/jvm.kt):
@Actual
internal fun getCharsPlatformSpecificJvm(
string: CharSequence, srcOffset: Int, dst: CharArray, dstOffset: Int, len: Int
): Boolean {
if (string is CharBuffer) { /* ... */ }
return false
}
代码转换注意事项
- 单体仓库使用 JVM 版 Kotlin 标准库,与公共版不同。需检查公共代码中哪些声明可用。
@JvmStatic、@JvmField等注解在公共代码中不会自动导入,需手动导入。.pseudoCommonKotlinSourceSet标记可防止 IDE 移除这些导入。- Java 代码可保留在平台特定源码集(
srcJvm/)中。
编辑之后
- 修改任何
.iml文件后运行./build/jpsModelToBazel.cmd。 - 运行 MultiplatformModuleStructureTest 验证模块结构:
./tests.cmd --module tests.ideaProjectStructure --test com.intellij.ideaProjectStructure.fast.MultiplatformModuleStructureTest
- 使用
mcp__ijproxy__lint_files对更改的文件进行 lint。
制品发布
单体仓库不直接向 Maven Central 发布多平台制品。具有 Gradle 基础设施的独立仓库会提取模块源码,用真正的 KMP 编译并发布。参见 JetBrains Space 中的 fleet-multiplatform-deps 和 fleet-parser-collection。
适用场景与限制
适用于在 IntelliJ 社区单体仓库中编写跨平台公共代码并保持 JVM 等平台特定实现的开发者。限制包括:仅支持顶层函数;expect 与 actual 必须位于同一模块;库依赖受封闭集合约束;srcWasmJs/srcWasmJsMain 不得作为 .iml 源码根;单体仓库不直接发布多平台制品。