Kotlin / binary-compatibility-validator

Public API management tool
Apache License 2.0
760 stars 55 forks source link

BCV Gradle Plugin should not depend on kotlin-compiler-embeddable #208

Open aSemy opened 3 months ago

aSemy commented 3 months ago

Currently, BCV has a compileOnly dependency on kotlin-compiler-embeddable, and (I presume) expects that KGP will provide it at runtime.

https://github.com/Kotlin/binary-compatibility-validator/blob/29a83d78596bfdbc37f885da881edc40021eb073/build.gradle.kts#L69

kotlin-compiler-embeddable is not supposed to be exposed in the Gradle plugin classpath. It will create problems for .gradle.kts buildscripts compilation once it starts to contain classes compiled with language version 2.1 (limiting the supported Gradle versions range).

In the scope of KT-61706, kotlin-compiler-embeddable will be removed from the runtime dependencies of KGP.

Proposed fix

  1. Split the BCV generator and validator code into a separate subproject, which (just for quick reference) I'll call bcv-core.
  2. bcv-core has an implementation dependency on kotlin-compiler-embeddable.
  3. bcv-gradle (the current project has a whole) has a compileOnly dependency on bcv-core.
  4. bcv-gradle uses the Worker API to invoke bcv-core using an isolated classpath.
  5. BCV Gradle Plugin creates a Configuration for fetching bcv-core at runtime, and passing it into the Worker.
└── binary-compatibility-validator/
    ├── buildSrc/
    │
    ├── binary-compatibility-validator-core/
    │   └── build.gradle.kts
    │
    ├── binary-compatibility-validator-gradle/
    │   └── build.gradle.kts
    │
    ├── build.gradle.kts
    └── settings.gradle.kts

The end result is BCV Gradle Plugin becomes a 0-dependency wrapper over the bcv-core. This is how BCV-MU has been architected.

As well as making BCV better, what is also interesting is that this set up opens the door to a Maven Plugin, or a CLI, or whatever else.

See also

JakeWharton commented 3 months ago
  1. Split the BCV generator and validator code into a separate subproject, which (just for quick reference) I'll call bcv-core.

  2. bcv-core has an implementation dependency on kotlin-compiler-embeddable.

  3. bcv-gradle (the current project has a whole) has a compileOnly dependency on bcv-core.

  4. bcv-gradle uses the Worker API to invoke bcv-core using an isolated classpath.

  5. BCV Gradle Plugin creates a Configuration for fetching bcv-core at runtime, and passing it into the Worker.

I also do 1 & 2 for all my tooling, but I differ on the rest. I tend to include a main() in what you call 'bcv-core', and then use the worker API + a dedicated configuration to javaexec that artifact. This avoids the compileOnly dependency and accidental references in the non-worker types, while also forcing you to provide the entrypoint for other build systems (but absolving you from actually having to do any additional work to integrate them).

(You could just use a JavaExec task as the configuration gives you isolation, but the worker API is the only way to enable project-local parallelism)