Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support running KSP via KotlinCompilerRunner #1119

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

ting-yuan
Copy link
Collaborator

which is based on KGP's GradleCompilerRunnerWithWorker.

Also refactored the code to allow selection between the old and new
ways to create KSP tasks and call the compiler.

@ting-yuan
Copy link
Collaborator Author

@gavra0 here is a bit more explanation.

KotlinCompilerRunner.kt is what I hope to contribute to kotlin-gradle-plugin-api and KotlinCompilerRunnerImpls.kt to kotlin-gradle-plugin.

Copy link
Contributor

@gavra0 gavra0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, it seems ok. The API surface is not too big:

  • given a task you'll get a compiler runner
  • compiler runner should allow you to specify compiler args and input/output/common sources

Comment on lines 29 to 32
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2NativeCompilerArguments
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires moving arguments to -api module, we need to check if that's possible and if they match the API guarantees.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I'll create a simplified / public version of those.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed with a simplified version of the arguments in 8782e89.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is the right way to solve this. Forking of args has some downsides, as they are generated directly from compiler flags.

@Tapchicoma what do you think? Is compiler args something you'd like to expose in the API?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these particular flags are the part of compiler and will never be in KGP-api artifact. On the other side - we are exposing currently kotlinOptions and from 1.8.0 - compilerOptions generated DSL. And we have plans to add more missing options to compilerOptions in followup releases.

Probably we should think about another approach that will not require to use Kotlin compiler arguments from KSP side. Also this arguments are not an official compiler api and could have breaking changes between minor releases.

@get:Internal val workerExecutor: WorkerExecutor
) : KotlinCompilerRunner {
@get:Internal
internal val taskProvider: Provider<GradleCompileTaskProvider> = objectFactory.property(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm positively surprised that GradleCompileTaskProvider does not require Kotlin-specific task type, only Gradle Task is enough :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it is declared on AbstractKotlinCompile and is used to provide generic compilation configuration that is same for all targets 🙂

which is based on KGP's GradleCompilerRunnerWithWorker.

Also refactored the code to allow selection between the old and new
ways to create KSP tasks and call the compiler.
and target specific subclasses. This is a simplified version of the
CommonCompilerArguments family.
@ting-yuan
Copy link
Collaborator Author

ting-yuan commented Oct 7, 2022

The proposed KotlinCompilerArguments mostly comes from kotlinOptions, compilerOptions (compiler args annotated with GradleOptions) and fields in BaseKotlinCompile and KotlinCompileTool:

    // From compilerOptions / kotlinOptions:
    var verbose: Boolean = false
    var allWarningsAsErrors: Boolean = false
    var languageVersion: String? = null
    var apiVersion: String? = null
    var useK2: Boolean = false
    var multiPlatform: Boolean = false
    var moduleName: String? = null
    var jvmTarget: String? = null // JVM
    var noJdk: Boolean = false // JVM
    var noStdlib: Boolean = false // JS
    var target: String = "v5"  // JS
    var target: String? = null // K/N

    // From BaseKotlinCompile / KotlinCompileTool / KotlinTaskConfigs.kt:
    var friendPaths: List<File> = emptyList()
    var libraries: List<File> = emptyList()
    var destination: File? = null
    var pluginOptions: List<String> = emptyList()
    var pluginClasspaths: List<File> = emptyList()

With a few exceptions. They are unlikely to change and may be stable enough to get into the KGP-api artifact.

    // JVM
    var noStdlib: Boolean = false
    var noReflect: Boolean = false
    var allowNoSourceFiles: Boolean = false

Proposal

  1. Add the above JVM options (noStdlib, noReflect, allowNoSourceFiles) into CompilerJvmOptions. And replace KotlinCompilerArguments (defined in this patch) with compilerOptions. Because CompilerCommonOptions / CompilerJvmOptions are configurable in Kotlin DSL, it should make sense to use it to configure / setup the custom compilation.
  2. Require the entry point in the proposed (e.g., createKotlinJvmCompilerRunner) API to accept a subtype of BaseKotlinCompile or KotlinJvmCompile, so that runJvmCompilerAsync can read the options defined in KotlinJvmCompile / BaseKotlinCompile.
  3. Add freeArgs to runJvmCompilerAsync. KSP needs this to pass modified / removed files in its plugin options at execution time. Also it can cover some of the unstable compiler arguments (at users' discretion). Please see comments below.

Admittedly, there are still a few options that may be subject to change in the future. It might be better to handle those in freeArgs, at users' risk / discretion, until they become stable.

    // Used to select between the new and old expect actual linker
    var expectActualLinker: Boolean = false

    // These are used to select among JS backends.
    // In fact, in KGP, they are never set up; The free format args, e.g. -Xir-only are used instead.
    var irOnly: Boolean = false
    var irProduceJs: Boolean = false
    var irProduceKlibDir: Boolean = false
    var irProduceKlibFile: Boolean = false
    var irBuildCache: Boolean = false
    var wasm: Boolean = false

To summarize, with the newly proposed KotlinCompilerRunner:

  1. It accepts subtypes of BaseKotlinCompile and returns an instance of KotlinCompilerRunner, which has a member function run<target>CompilerAsync.
  2. Users (e.g., KSP) fill CompilerCommonOptions / CompilerJvmOptions / etc and configure BaseKotlinCompile, and call run<target>CompilerAsync with the options, freeArgs and sources / destination.

@Tapchicoma what do you think? The only new interface introduced is KotlinCompilerRunner. Nothing new around compiler args will need to be maintained.

edit: A factory / builder of the Compiler<target>Options will also be needed. As well as a way to provide default values of fields in BaseKotlinCompile.

@ting-yuan ting-yuan force-pushed the gradle branch 2 times, most recently from 0bf44d1 to e959ad9 Compare October 11, 2022 06:03
@ting-yuan
Copy link
Collaborator Author

ting-yuan commented Oct 11, 2022

@Tapchicoma I've uploaded an implementation that makes use of compilerOptions instead of CommonCompilerArguments. The use of freeArgs is limited and most of them can be added to the API.

"-Xallow-no-source-files", "-no-stdlib", "-Xmulti-platform", "-Xexpect-actual-linker",
"-Xplugin=...", "-P plugin:..."

Currently I have to copy some free args from JS compilation task to select the backend and filter the libraries. Because IR_LEGACY is being deprecated, these will go away soon:

"-Xir-only", "-Xir-produce-js", "-Xir-produce-klib-file", "-Xir-produce-klib-dir", "-Xir-build-cache", "-Xwasm"

Paths are moved into the parameters of run*CompilerAsync. The API now becomes, for example,

interface KotlinJvmCompilerRunner : KotlinCompilerRunner {
    fun runJvmCompilerAsync(
        options: CompilerJvmOptions,
        freeArgs: List<String>,
        sources: List<File>,
        commonSources: List<File>,
        friendPaths: List<File>,
        libraries: List<File>,
        destination: File
    )
}

// Can be moved to somewhere more appropriate, with objectFactory removed.
fun createKotlinJvmCompilerRunner(
    task: Task,
    objectFactory: ObjectFactory,
): KotlinJvmCompilerRunner {
    return objectFactory.newInstance<KotlinJvmCompilerRunnerImpl>(task)
}

@@ -24,6 +24,7 @@ plugins {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin-api:$kotlinBaseVersion")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinBaseVersion")
implementation("org.jetbrains.kotlin:kotlin-compiler-runner:$kotlinBaseVersion")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KGP has compileOnly dependency on this library as all classes should come from kotlin-compiler-embeddable on runtime. Probably you should do the same

// TODO: to be replaced by KotlinJvmFactory, etc.
class CompilerOptionsFactory {
companion object {
fun createCompilerJvmOptions(objectFactory: ObjectFactory): CompilerJvmOptions =

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are targeting Kotlin 1.8.0 - you could already use KotlinJvmFactory to create CompilerJvmOptions

Comment on lines +37 to +41
fun createCompilerJsOptions(objectFactory: ObjectFactory): CompilerJsOptions =
objectFactory.newInstance<CompilerJsOptionsDefault>()

fun createCompilerCommonOptions(objectFactory: ObjectFactory): CompilerCommonOptions =
objectFactory.newInstance<CompilerCommonOptionsDefault>()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For KotlinJsFactory and KotlinCommonFactory please open new issues, possibly it is still ok to add them into 1.8.0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you need same way to create for soon-to-added CompilerNativeOptionsinterface - pleases create an issue as well

@Tapchicoma
Copy link

Add the above JVM options (noStdlib, noReflect, allowNoSourceFiles) into CompilerJvmOptions. And replace KotlinCompilerArguments (defined in this patch) with compilerOptions. Because CompilerCommonOptions / CompilerJvmOptions are configurable in Kotlin DSL, it should make sense to use it to configure / setup the custom compilation.

FYI: KGP adds these noStdlib and noReflect options when final Kotlin compiler arguments are calculated.


// TODO: Maybe move those functions into proper KGP class when getting into KGP.

// TODO: Remove objectFactory when getting into KGP.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think KGP or some Kotlin library should provide method to create compiler runners for specific platforms. It could be useful for other build tools as well. Would be nice to open an issue.

@get:Internal val workerExecutor: WorkerExecutor
) : KotlinCompilerRunner {
@get:Internal
internal val taskProvider: Provider<GradleCompileTaskProvider> = objectFactory.property(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it is declared on AbstractKotlinCompile and is used to provide generic compilation configuration that is same for all targets 🙂

@Tapchicoma
Copy link

Add freeArgs to runJvmCompilerAsync. KSP needs this to pass modified / removed files in its plugin options at execution time. Also it can cover some of the unstable compiler arguments (at users' discretion). Please see comments below.

Generally KGP will try to move away from freeCompilerArguments by adding all required options into Kotlin*CompilerOptions. If you need some specific options open a new issues, so we can prioritize them.

@Tapchicoma
Copy link

Add freeArgs to runJvmCompilerAsync. KSP needs this to pass modified / removed files in its plugin options at execution time. Also it can cover some of the unstable compiler arguments (at users' discretion). Please see comments below.

FYI: KGP will go away from freeCompilerArgs and try to provide all user-required via compilerOptions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants