Skip to content

Commit 26d34af

Browse files
committed
KTL-724 Added compiler arguments support
We are using `kotlin-compiler-arguments-description` maven dependency for extracting all known compiler arguments for the current kotlin version. We've added an endpoint for getting all known compiler dependencies by the given platform. We've extended `run` and `translate` endpoints with the user's compiler args.
1 parent 2a68884 commit 26d34af

37 files changed

+9746
-365
lines changed

build.gradle.kts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ version = "${libs.versions.kotlin.get()}-SNAPSHOT"
1212
val propertyFile = "application.properties"
1313

1414
plugins {
15-
alias(libs.plugins.spring.dependency.management)
1615
alias(libs.plugins.spring.boot)
16+
alias(libs.plugins.spring.dependency.management)
1717
alias(libs.plugins.kotlin.plugin.spring)
1818
id("base-kotlin-jvm-conventions")
1919
}
@@ -39,11 +39,14 @@ val resourceDependency: Configuration by configurations.creating {
3939
}
4040

4141
dependencies {
42-
annotationProcessor("org.springframework:spring-context-indexer")
43-
implementation("com.google.code.gson:gson")
42+
annotationProcessor(libs.spring.context.indexer)
4443
implementation("org.springframework.boot:spring-boot-starter-web")
45-
implementation(libs.springdoc)
44+
implementation("org.springframework.boot:spring-boot-starter-validation")
4645
implementation(libs.aws.springboot.container)
46+
implementation(libs.springdoc)
47+
implementation(libs.gson)
48+
implementation(libs.kotlinx.serialization.json)
49+
implementation(libs.kotlin.compiler.arguments.description)
4750
implementation(libs.junit)
4851
implementation(libs.logback.logstash.encoder)
4952
implementation(libs.intellij.trove4j)
@@ -77,6 +80,8 @@ fun buildPropertyFile() {
7780

7881
fun generateProperties(prefix: String = "") = """
7982
# this file is autogenerated by build.gradle.kts
83+
server.error.include-message=always
84+
server.error.include-binding-errors=always
8085
kotlin.version=${kotlinVersion}
8186
policy.file=${prefix + policy}
8287
indexes.file=${prefix + indexes}
@@ -131,7 +136,7 @@ val buildLambda by tasks.creating(Zip::class) {
131136
from(libWasmFolder) { into(libWasm) }
132137
from(libComposeWasmFolder) { into(libComposeWasm) }
133138
from(libJVMFolder) { into(libJVM) }
134-
from(compilerPluginsForJVMFolder) {into(compilerPluginsForJVM)}
139+
from(compilerPluginsForJVMFolder) { into(compilerPluginsForJVM) }
135140
from(libComposeWasmCompilerPluginsFolder) { into(libComposeWasmCompilerPlugins) }
136141
into("lib") {
137142
from(configurations.compileClasspath) { exclude("tomcat-embed-*") }
@@ -160,4 +165,13 @@ tasks.withType<Test> {
160165
doFirst {
161166
this@withType.environment("kotlin.wasm.node.path", executablePath)
162167
}
168+
169+
// We disable this on TeamCity, because we don't want to fail this test,
170+
// when compiler server's test run as a K2 user project.
171+
// But for our pull requests we still need to run this test, so we add it to our GitHub action.
172+
if (providers.gradleProperty("teamcity").isPresent) {
173+
filter {
174+
excludeTestsMatching("com.compiler.server.CompilerArguments*")
175+
}
176+
}
163177
}

gradle/libs.versions.toml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ kotlin-stdlib-wasm-js = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-
3131
kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlin" }
3232
kotlin-test-junit = { group = "org.jetbrains.kotlin", name = "kotlin-test-junit", version.ref = "kotlin" }
3333
kotlin-compiler = { group = "org.jetbrains.kotlin", name = "kotlin-compiler", version.ref = "kotlin" }
34+
kotlin-compiler-arguments-description = { group = "org.jetbrains.kotlin", name = "kotlin-compiler-arguments-description", version.ref = "kotlin" }
3435
kotlin-script-runtime = { group = "org.jetbrains.kotlin", name = "kotlin-script-runtime", version.ref = "kotlin" }
3536
kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
3637
kotlin-gradle-plugin-idea = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin-idea", version.ref = "kotlin" }
@@ -45,10 +46,9 @@ kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-cor
4546
kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinx-datetime" }
4647
kotlinx-io-bytestring = { group = "org.jetbrains.kotlinx", name = "kotlinx-io-bytestring", version.ref = "kotlinx-io" }
4748
kotlinx-io-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-io-core", version.ref = "kotlinx-io" }
49+
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
4850
kotlinx-serialization-json-jvm = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json-jvm", version.ref = "kotlinx-serialization" }
4951
kotlinx-serialization-core-jvm = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core-jvm", version.ref = "kotlinx-serialization" }
50-
springdoc = { group = "org.springdoc", name = "springdoc-openapi-starter-webmvc-ui", version.ref = "springdoc" }
51-
aws-springboot-container = { group = "com.amazonaws.serverless", name = "aws-serverless-java-container-springboot3", version.ref = "aws-serverless-java-container-springboot3" }
5252
junit = { group = "junit", name = "junit", version.ref = "junit" }
5353
logback-logstash-encoder = { group = "net.logstash.logback", name = "logstash-logback-encoder", version.ref = "logstash-logback-encoder" }
5454
intellij-trove4j = { group = "org.jetbrains.intellij.deps", name = "trove4j", version.ref = "trove4j" }
@@ -68,6 +68,12 @@ compose-material3 = { group = "org.jetbrains.compose.material3", name = "materia
6868
compose-components-resources = { group = "org.jetbrains.compose.components", name = "components-resources", version.ref = "compose" }
6969
kotlin-serialization-plugin = {group= "org.jetbrains.kotlin", name="kotlin-serialization-compiler-plugin", version.ref = "kotlin"}
7070
gradle-develocity = {group = "com.gradle", name= "develocity-gradle-plugin", version.ref = "gradle-develocity"}
71+
gson = { group = "com.google.code.gson", name = "gson"}
72+
73+
#spring stack dependencies
74+
aws-springboot-container = { group = "com.amazonaws.serverless", name = "aws-serverless-java-container-springboot3", version.ref = "aws-serverless-java-container-springboot3" }
75+
spring-context-indexer = { group = "org.springframework", name = "spring-context-indexer"}
76+
springdoc = { group = "org.springdoc", name = "springdoc-openapi-starter-webmvc-ui", version.ref = "springdoc" }
7177

7278
[bundles]
7379
kotlin-stdlib = ["kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8"]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.compiler.server.api
2+
3+
import com.compiler.server.model.ExtendedCompilerArgumentValue
4+
import com.fasterxml.jackson.annotation.JsonTypeInfo
5+
6+
data class CompilerArgumentResponse(val compilerArguments: Set<CompilerArgument>) {
7+
8+
data class CompilerArgument(
9+
val name: String,
10+
val shortName: String?,
11+
val description: String?,
12+
@field:JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "type")
13+
val type: ExtendedCompilerArgumentValue<*>,
14+
val disabled: Boolean,
15+
var predefinedValues: Any?
16+
)
17+
}
18+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.compiler.server.api
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
4+
5+
@JsonIgnoreProperties(ignoreUnknown = true)
6+
data class ProjectFileRequestDto(val text: String = "", val name: String = "")
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.compiler.server.api
2+
3+
import com.compiler.server.model.ProjectType
4+
import com.compiler.server.validation.CompilerArgumentsConstraint
5+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
6+
7+
@JsonIgnoreProperties(ignoreUnknown = true)
8+
data class RunRequest(
9+
val args: String = "",
10+
val files: List<ProjectFileRequestDto> = listOf(),
11+
@CompilerArgumentsConstraint(ProjectType.JAVA)
12+
val compilerArguments: Map<String, Any> = emptyMap()
13+
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.compiler.server.api
2+
3+
import com.compiler.server.model.ProjectType
4+
import com.compiler.server.validation.CompilerArgumentsConstraint
5+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
6+
7+
@JsonIgnoreProperties(ignoreUnknown = true)
8+
data class TestRequest(
9+
val args: String = "",
10+
val files: List<ProjectFileRequestDto> = listOf(),
11+
@CompilerArgumentsConstraint(ProjectType.JAVA)
12+
val compilerArguments: Map<String, Any> = emptyMap()
13+
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.compiler.server.api
2+
3+
import com.compiler.server.model.ProjectType
4+
import com.compiler.server.validation.CompilerArgumentsConstraint
5+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
6+
7+
@JsonIgnoreProperties(ignoreUnknown = true)
8+
class TranslateComposeWasmRequest(
9+
val args: String = "",
10+
val files: List<ProjectFileRequestDto> = listOf(),
11+
@CompilerArgumentsConstraint(ProjectType.COMPOSE_WASM)
12+
val firstPhaseCompilerArguments: Map<String, Any> = emptyMap(),
13+
@CompilerArgumentsConstraint(ProjectType.COMPOSE_WASM)
14+
val secondPhaseCompilerArguments: Map<String, Any> = emptyMap()
15+
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.compiler.server.api
2+
3+
import com.compiler.server.model.ProjectType
4+
import com.compiler.server.validation.CompilerArgumentsConstraint
5+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
6+
7+
@JsonIgnoreProperties(ignoreUnknown = true)
8+
data class TranslateJsRequest(
9+
val args: String = "",
10+
val files: List<ProjectFileRequestDto> = listOf(),
11+
@CompilerArgumentsConstraint(ProjectType.JS)
12+
val firstPhaseCompilerArguments: Map<String, Any> = emptyMap(),
13+
@CompilerArgumentsConstraint(ProjectType.JS)
14+
val secondPhaseCompilerArguments: Map<String, Any> = emptyMap()
15+
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.compiler.server.api
2+
3+
import com.compiler.server.model.ProjectType
4+
import com.compiler.server.validation.CompilerArgumentsConstraint
5+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
6+
7+
@JsonIgnoreProperties(ignoreUnknown = true)
8+
class TranslateWasmRequest(
9+
val args: String = "",
10+
val files: List<ProjectFileRequestDto> = listOf(),
11+
@CompilerArgumentsConstraint(ProjectType.WASM)
12+
val firstPhaseCompilerArguments: Map<String, Any> = emptyMap(),
13+
@CompilerArgumentsConstraint(ProjectType.WASM)
14+
val secondPhaseCompilerArguments: Map<String, Any> = emptyMap()
15+
)

src/main/kotlin/com/compiler/server/compiler/components/KotlinCompiler.kt

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package com.compiler.server.compiler.components
22

33
import com.compiler.server.executor.CommandLineArgument
44
import com.compiler.server.executor.JavaExecutor
5+
import com.compiler.server.model.ExtendedCompilerArgument
56
import com.compiler.server.model.JvmExecutionResult
67
import com.compiler.server.model.OutputDirectory
78
import com.compiler.server.model.bean.LibrariesFile
89
import com.compiler.server.model.toExceptionDescriptor
10+
import com.compiler.server.utils.CompilerArgumentsUtil
911
import component.KotlinEnvironment
1012
import executors.JUnitExecutors
1113
import executors.JavaRunnerExecutor
@@ -32,7 +34,9 @@ class KotlinCompiler(
3234
private val kotlinEnvironment: KotlinEnvironment,
3335
private val javaExecutor: JavaExecutor,
3436
private val librariesFile: LibrariesFile,
35-
@Value("\${policy.file}") private val policyFileName: String
37+
@Value("\${policy.file}") private val policyFileName: String,
38+
private val compilerArgumentsUtil: CompilerArgumentsUtil,
39+
private val jvmCompilerArguments: Set<ExtendedCompilerArgument>,
3640
) {
3741
private val policyFile = File(policyFileName)
3842

@@ -51,16 +55,16 @@ class KotlinCompiler(
5155

5256
return stringWriter.toString()
5357
}
54-
58+
5559
private fun JvmExecutionResult.addByteCode(compiled: JvmClasses) {
5660
jvmByteCode = compiled.files
5761
.mapNotNull { (_, bytes) -> runCatching { bytes.asHumanReadable() }.getOrNull() }
5862
.takeUnless { it.isEmpty() }
5963
?.joinToString("\n\n")
6064
}
6165

62-
fun run(files: List<KtFile>, addByteCode: Boolean, args: String): JvmExecutionResult {
63-
return execute(files, addByteCode) { output, compiled ->
66+
fun run(files: List<KtFile>, addByteCode: Boolean, args: String, userCompilerArguments: Map<String, Any>): JvmExecutionResult {
67+
return execute(files, addByteCode, userCompilerArguments) { output, compiled ->
6468
val mainClass = JavaRunnerExecutor::class.java.name
6569
val compiledMainClass = when (compiled.mainClasses.size) {
6670
0 -> return@execute JvmExecutionResult(
@@ -80,25 +84,22 @@ class KotlinCompiler(
8084
}
8185
}
8286

83-
fun test(files: List<KtFile>, addByteCode: Boolean): JvmExecutionResult {
84-
return execute(files, addByteCode) { output, _ ->
87+
fun test(files: List<KtFile>, addByteCode: Boolean, userCompilerArguments: Map<String, Any>): JvmExecutionResult {
88+
return execute(files, addByteCode, userCompilerArguments) { output, _ ->
8589
val mainClass = JUnitExecutors::class.java.name
8690
javaExecutor.execute(argsFrom(mainClass, output, listOf(output.path.toString())))
8791
.asJUnitExecutionResult()
8892
}
8993
}
9094

9195
@OptIn(ExperimentalPathApi::class)
92-
fun compile(files: List<KtFile>): CompilationResult<JvmClasses> = usingTempDirectory { inputDir ->
96+
fun compile(files: List<KtFile>, userCompilerArguments: Map<String, Any>): CompilationResult<JvmClasses> = usingTempDirectory { inputDir ->
9397
val ioFiles = files.writeToIoFiles(inputDir)
9498
usingTempDirectory { outputDir ->
95-
val arguments = ioFiles.map { it.absolutePathString() } + KotlinEnvironment.additionalCompilerArguments + listOf(
96-
"-cp", kotlinEnvironment.classpath.joinToString(PATH_SEPARATOR) { it.absolutePath },
97-
"-module-name", "web-module",
98-
"-no-stdlib", "-no-reflect",
99-
"-progressive",
100-
"-d", outputDir.absolutePathString(),
101-
) + kotlinEnvironment.compilerPlugins.map { plugin -> "-Xplugin=${plugin.absolutePath}" }
99+
val arguments = ioFiles.map { it.absolutePathString() } +
100+
compilerArgumentsUtil.convertCompilerArgumentsToCompilationString(jvmCompilerArguments, compilerArgumentsUtil.PREDEFINED_JVM_ARGUMENTS, userCompilerArguments) +
101+
listOf("-d", outputDir.absolutePathString())
102+
102103
K2JVMCompiler().tryCompilation(inputDir, ioFiles, arguments) {
103104
val outputFiles = buildMap {
104105
outputDir.visitFileTree {
@@ -137,11 +138,12 @@ class KotlinCompiler(
137138
}.toSet()
138139

139140
private fun execute(
140-
files: List<KtFile>,
141-
addByteCode: Boolean,
142-
block: (output: OutputDirectory, compilation: JvmClasses) -> JvmExecutionResult
141+
files: List<KtFile>,
142+
addByteCode: Boolean,
143+
userCompilerArguments: Map<String, Any>,
144+
block: (output: OutputDirectory, compilation: JvmClasses) -> JvmExecutionResult
143145
): JvmExecutionResult = try {
144-
when (val compilationResult = compile(files)) {
146+
when (val compilationResult = compile(files, userCompilerArguments)) {
145147
is Compiled<JvmClasses> -> {
146148
usingTempDirectory { outputDir ->
147149
val output = write(compilationResult.result, outputDir)

0 commit comments

Comments
 (0)