Kotlin 跨平台项目使用编译任务来生成 artifact. 每个编译目标可以有一个或多个编译任务, 比如, 用于产品和测试的编译任务.
对每个编译目标, 默认的编译任务包括:
如果需要编译除产品代码与单元测试之外的其他代码, 比如, 集成测试, 或性能测试, 你可以 创建自定义编译任务.
你可以在以下范围配置如何产生 artifact:
请参见对所有编译目标或特定编译目标可用的 编译任务参数列表 和 编译器选项.
kotlin {
targets.all {
compilations.all {
compilerOptions.configure {
allWarningsAsErrors.set(true)
}
}
}
}
kotlin {
jvm().compilations.all {
compilerOptions.configure {
jvmTarget.set(JvmTarget.JVM_1_8)
}
}
}
kotlin {
jvm().compilations.all {
compilerOptions.configure {
jvmTarget.set(JvmTarget.JVM_1_8)
}
}
}
kotlin {
jvm {
val main by compilations.getting {
compilerOptions.configure {
jvmTarget.set(JvmTarget.JVM_1_8)
}
}
}
}
kotlin {
jvm {
compilations.main {
compilerOptions.configure {
jvmTarget.set(JvmTarget.JVM_1_8)
}
}
}
}
创建自定义编译任务
如果需要编译除产品代码与单元测试之外的其他代码, 比如, 集成测试, 或性能测试, 请创建自定义编译任务.
比如, 要对 jvm()
编译目标的集成测试创建自定义编译任务, 请向 compilations
集合内添加新的元素.
kotlin {
jvm() {
compilations {
val main by getting
val integrationTest by compilations.creating {
defaultSourceSet {
dependencies {
// 使用 main 编译任务的编译期类路径和输出进行编译:
implementation(main.compileDependencyFiles + main.output.classesDirs)
implementation(kotlin("test-junit"))
/* ... */
}
}
// 创建 test 任务来运行这个编译任务产生的测试:
tasks.create<Test>("integrationTest") {
// 运行测试使用的类路径包含:
// 编译期依赖项(包括 'main'), 运行时依赖项, 以及这个编译任务的输出:
classpath = compileDependencyFiles + runtimeDependencyFiles + output.allOutputs
// 只运行这个编译任务的输出中包含的测试:
testClassesDirs = output.classesDirs
}
}
}
}
}
kotlin {
jvm() {
compilations.create('integrationTest') {
defaultSourceSet {
dependencies {
def main = compilations.main
// 使用 main 编译任务的编译期类路径和输出进行编译:
implementation(main.compileDependencyFiles + main.output.classesDirs)
implementation kotlin('test-junit')
/* ... */
}
}
// 创建 test 任务来运行这个编译任务产生的测试:
tasks.create('jvmIntegrationTest', Test) {
// 运行测试使用的类路径包含:
// 编译期依赖项(包括 'main'), 运行时依赖项, 以及这个编译任务的输出:
classpath = compileDependencyFiles + runtimeDependencyFiles + output.allOutputs
// 只运行这个编译任务的输出中包含的测试:
testClassesDirs = output.classesDirs
}
}
}
}
对于其他情况也需要创建自定义编译任务, 比如, 如果希望在你的最终 artifact 中对不同的 JVM 版本组合编译任务, 或者已经在 Gradle 中设置过源代码集, 希望迁移到跨平台项目.
在 JVM 编译任务中使用 Java 源代码
使用 项目向导 创建项目时, Java 源代码会包含在 JVM 编译任务内.
在构建脚本中, 以下代码会应用 Gradle java
plugin, 并配置编译目标, 使其与 java
plugin 协作:
kotlin {
jvm {
withJava()
}
}
Java 源代码文件 放在 Kotlin 源代码根路径的子目录之内. 比如, 路径是:
共通源代码集不可以包含 Java 源代码.
由于目前的限制, Kotlin plugin 会替换 Java plugin 配置的某些任务:
使用编译目标的 JAR 任务, 而不是 jar
任务 (比如, jvmJar
).
使用编译目标的 test 任务, 而不是 test
任务 (比如, jvmTest
).
资源由编译任务中的同等任务处理, 而不是 *ProcessResources
任务.
这个编译目标的发布由 Kotlin plugin 处理, 而且不需要 Java plugin 的具体步骤.
Kotlin 提供 与原生语言的交互能力, 以及对编译任务进行相关配置的 DSL.
原生语言 | 支持的平台 | 备注 |
---|
C | 所有平台, WebAssembly 除外 | |
Objective-C | Apple 平台 (macOS, iOS, watchOS, tvOS) | |
经由 Objective-C 的 Swift | Apple 平台 (macOS, iOS, watchOS, tvOS) | Kotlin 只能使用 @objc 属性标注过的 Swift 声明. |
编译任务可以与几个原生库交互. 在编译任务的 cinterops
代码段中, 可以使用 可用的参数 来配置交互能力.
kotlin {
linuxX64 { // 请替换为你需要的编译目标.
compilations.getByName("main") {
val myInterop by cinterops.creating {
// 描述原生 API 的 def 文件.
// 默认路径是 src/nativeInterop/cinterop/<interop-name>.def
defFile(project.file("def-file.def"))
// 用来放置生成的 Kotlin API 的包.
packageName("org.sample")
// 通过 cinterop 工具传递给编译器的参数.
compilerOpts("-Ipath/to/headers")
// 用来查找头文件的目录.
includeDirs.apply {
// 用来查找头文件的目录 (等价于编译器的 -I<path> 参数).
allHeaders("path1", "path2")
// 用来查找 def 文件的 'headerFilter' 参数中指定的头文件时使用的额外的目录.
// 等价于 -headerFilterAdditionalSearchPrefix 命令行参数.
headerFilterOnly("path1", "path2")
}
// includeDirs.allHeaders 的缩写方式.
includeDirs("include/directory", "another/directory")
}
val anotherInterop by cinterops.creating { /* ... */ }
}
}
}
kotlin {
linuxX64 { // 请替换为你需要的编译目标.
compilations.main {
cinterops {
myInterop {
// 描述原生 API 的 def 文件.
// 默认路径是 src/nativeInterop/cinterop/<interop-name>.def
defFile project.file("def-file.def")
// 用来放置生成的 Kotlin API 的包.
packageName 'org.sample'
// 通过 cinterop 工具传递给编译器的参数.
compilerOpts '-Ipath/to/headers'
// 用来查找头文件的目录 (等价于编译器的 -I<path> 参数).
includeDirs.allHeaders("path1", "path2")
// 用来查找 def 文件的 'headerFilter' 参数中指定的头文件时使用的额外的目录.
// 等价于 -headerFilterAdditionalSearchPrefix 命令行参数.
includeDirs.headerFilterOnly("path1", "path2")
// includeDirs.allHeaders 的缩写方式.
includeDirs("include/directory", "another/directory")
}
anotherInterop { /* ... */ }
}
}
}
}
Android 编译任务
对 Android 编译目标默认创建的编译任务会与 Android 构建变体(build variant) 绑定: 对每个构建变体, 会创建一个相同名称的 Kotlin 编译任务.
然后, 对每个构建变体编译的每个 Android 源代码集, 会创建 Kotlin 源代码集, 名称是 Android 源代码集名前面加上编译目标名, 比如, 对于 Kotlin 编译目标 android
, 以及 Android 源代码集 debug
, 对应的 Kotlin 源代码集名为 androidDebug
. 这些 Kotlin 源代码集会被添加到对应的构建变体的编译任务中.
默认的源代码集 commonMain
会被添加到所有的产品构建变体(无论是应用程序还是库) 的编译任务中. 类似的, 源代码集 commonTest
, 会被添加到单元测试(Unit Test)和设备测试(Instrumented Test)的构建变体中.
也支持使用 kapt 的注解处理, 但是由于目前的限制, 要求在配置 kapt
依赖项之前创建 Android 编译目标, 因此需要使用顶级的 dependencies { ... }
代码段, 而不是使用 Kotlin 源代码集的依赖项配置语法.
kotlin {
android { /* ... */ }
}
dependencies {
kapt("com.my.annotation:processor:1.0.0")
}
源代码集层级结构的编译任务
Kotlin 可以使用 dependsOn
关系来创建 源代码集层级结构.
如果源代码集 jvmMain
依赖源代码集 commonMain
, 那么:
对某个编译目标, 当 jvmMain
被编译时, commonMain
也会参与这个编译任务, 而且也会被编译称为同一个编译目标的二进制形式, 比如 JVM class 文件.
jvmMain
的源代码可以 '看见' commonMain
的声明, 包括内部声明, 也能看见 commonMain
的 依赖项, 即使是标记为 implementation
的依赖项.
jvmMain
可以包含 commonMain
的 预期声明(expected declaration) 的平台相关的实现.
commonMain
的资源会与 jvmMain
的资源一起处理, 复制.
jvmMain
和 commonMain
的 语言设置 应该保持一致.
语言设置的一致性检查规则是:
jvmMain
的 languageVersion
设置应该高于或等于 commonMain
的设置.
commonMain
启用的所有非稳定的语言特性, jvmMain
都应该启用 (对于 bugfix 特性没有这个要求).
commonMain
使用的所有实验性注解, jvmMain
都应该使用.
apiVersion
, bugfix 语言特性, 以及 progressiveMode
可以任意设定.
最终更新: 2024/10/17