Gradle中基于已有Kotlin代码生成代码并打包入Jar的构建问题
问题
在Gradle Kotlin库项目中,我需要先编译原有Kotlin代码,通过自省原有代码(由代码中的main()函数触发)生成额外Kotlin代码,再编译生成的代码,最后将原有代码和生成代码的编译产物打包到输出Jar中。我编写的构建脚本如下:
sourceSets { generated { kotlin { srcDir "src/generated/kotlin" } compileClasspath += main.output + configurations.runtimeClasspath runtimeClasspath += main.output } } task generateCode(type: JavaExec) { classpath = sourceSets.main.runtimeClasspath mainClass = "com.something.CodeGenMainKt" doFirst { file("src/generated/kotlin").mkdirs() } dependsOn compileKotlin } task compileGeneratedKotlin(type: KotlinCompile) { source = sourceSets.generated.kotlin dependsOn generateCode } jar { dependsOn compileGeneratedKotlin from sourceSets.main.output from sourceSets.generated.output }
运行时出现错误:
A problem occurred evaluating root project 'biz-kotlin'. > Could not create task ':compileGeneratedKotlin'. > Could not create task of type 'KotlinCompile'. > Unable to determine constructor argument #1: missing parameter of type KotlinJvmCompilerOptions, or no service of type KotlinJvmCompilerOptions.
请问为何无法添加该额外编译步骤?其余脚本是否存在问题?
解决方法
错误原因
你手动创建compileGeneratedKotlin任务时出错,核心原因是新版Gradle Kotlin插件(Kotlin 1.8+)中,KotlinCompile任务的构造必须依赖KotlinJvmCompilerOptions服务,手动实例化该任务时无法自动获取这个必要的构造参数,导致报错。
修复步骤
Gradle会为自定义的sourceSet自动生成对应的编译任务,完全不需要手动创建compileGeneratedKotlin。你只需要调整任务依赖关系即可:
- 删除手动定义的
compileGeneratedKotlin任务 - 让自动生成的
compileGeneratedKotlin任务依赖generateCode任务 - 确保
jar任务依赖自动生成的编译任务
修改后的完整脚本:
sourceSets { generated { kotlin { srcDir "src/generated/kotlin" } compileClasspath += main.output + configurations.runtimeClasspath runtimeClasspath += main.output } } task generateCode(type: JavaExec) { classpath = sourceSets.main.runtimeClasspath mainClass = "com.something.CodeGenMainKt" doFirst { file("src/generated/kotlin").mkdirs() } dependsOn compileKotlin } // 关联自动生成的编译任务与代码生成任务 tasks.named('compileGeneratedKotlin') { dependsOn generateCode } jar { dependsOn tasks.named('compileGeneratedKotlin') from sourceSets.main.output from sourceSets.generated.output }
其余脚本的优化建议
- 避免污染源码目录:生成的代码属于构建产物,建议将生成目录放在
build目录下(比如build/generated/sources/codegen/kotlin),而不是src目录,防止构建产物混入源码仓库。修改方式:sourceSets { generated { kotlin { srcDir "${buildDir}/generated/sources/codegen/kotlin" } // 其余配置不变 } } task generateCode(type: JavaExec) { // 其余配置不变 doFirst { file("${buildDir}/generated/sources/codegen/kotlin").mkdirs() } } - 优化代码生成任务的classpath:
sourceSets.main.runtimeClasspath可能包含不必要的依赖,改用sourceSets.main.compileClasspath + sourceSets.main.output更精准,确保只包含编译和自省原有代码所需的类:task generateCode(type: JavaExec) { classpath = sourceSets.main.compileClasspath + sourceSets.main.output // 其余配置不变 } - 清理旧生成代码:可以在代码生成前清理目标目录,避免旧代码干扰新生成的内容:
task generateCode(type: JavaExec) { // 其余配置不变 doFirst { def genDir = file("${buildDir}/generated/sources/codegen/kotlin") genDir.deleteDir() genDir.mkdirs() } }
内容的提问来源于stack exchange,提问作者xs0
相关产品推荐
相关产品推荐

