Skip to content
This repository has been archived by the owner on Aug 19, 2020. It is now read-only.

Introduce embeddedKotlinVersion property and kotlinModule extension. #94

Closed
bamboo opened this issue Jul 13, 2016 · 9 comments
Closed

Comments

@bamboo
Copy link
Member

bamboo commented Jul 13, 2016

To allow plugins and buildSrc code to be compiled against the version of Kotlin shipped with Gradle.

@big-guy
Copy link
Member

big-guy commented Jul 13, 2016

Shouldn't this be more like the localGroovy() dependency?

@bamboo
Copy link
Member Author

bamboo commented Jul 13, 2016

Possibly.

The thing with Kotlin is that there are many libraries and at least one plugin for which you might want to pin down the version to the one shipped with Gradle. So instead of providing a different function for each - localKotlinStdlib(), localKotlinReflect(), localKotlinGradlePlugin(), etc - a simpler solution seemed to be to expose a single property that could then be used to construct the correct dependency on any of those - compile("org.jetbrains.kotlin:kotlin-stdlib:$localKotlinVersion").

A localKotlin(module: String) helper function could (should?) be provided as well - compile(localKotlin("stdlib")). In fact that's how we do it in gradle-script-kotlin own build file.

What do you think?

@big-guy
Copy link
Member

big-guy commented Jul 13, 2016

I think it could be that localKotlin() == whatever Kotlin libs we ship (basically, some sort of equivalent to the groovy-all jar).

And then if you want to use additional libraries, you could add those explicitly with a version you pick or figure out programmatically. Is there an equivalent to GroovySystem.version?

I'm not saying this is better; it's just similar to what we're using with Groovy. It's not good that Groovy isn't a proper dependency when you use localGroovy().

I know Groovy doesn't make strong guarantees about compiling with one version of Groovy and running with another. Does Kotlin have the same sort of guarantee?

@bamboo
Copy link
Member Author

bamboo commented Jul 13, 2016

About Kotlin compatibility guarantees, we have this statement from the Kotlin 1.0 release announcement:

As of 1.0, we are committed to long-term backward compatibility of the language and its standard library (kotlin-stdlib):

  • a newer compiler will work with older binaries (but older compilers may not understand newer binaries, like javac 1.6 can’t read classes compiled by javac 1.8);
  • older binaries will keep working with newer binaries at runtime (newer code may require newer dependencies, though).

There is something equivalent to GroovySystem.version but it is part of the compiler package (org.jetbrains.kotlin.cli.common.KotlinVersion.VERSION) which I don't think is subject to the same guarantees as the standard libraries. localKotlinVersion would basically be an alias for that property under our own control and that would free us from exposing the compiler API to the build script.

About localKotlin(), I'd rather expose a more explicit and finer grained dependency mechanism such as the combination of localKotlin(module: String) and localKotlinVersion. Do you see any downsides to that?

@big-guy
Copy link
Member

big-guy commented Jul 15, 2016

Thanks, I think that mostly answers my compatibility question. If I were building a Kotlin project using the newest Kotlin-Gradle plugin but wanted to compile against the oldest Kotlin 1.0 stdlib, would that work? I guess you would need to be able to specify the compiler's version (separate from the plugin), which doesn't seem to be possible with a quick look at the docs. If I want to use an older Kotlin version, I shouldn't be forced to downgrade the version of the Kotlin-Gradle plugin too. And that shouldn't be a reason to expose a localKotlinVersion so that you keep all the versions (Kotlin-Gradle plugin, Kotlin compiler, Kotlin stdlib) in lockstep.

My inclination would be to be conservative, localKotlin() gives you all the Kotlin jars Gradle ships with (but not the Kotlin-Gradle plugin) and everything else has to be added manually (no separate localKotlinVersion). plugins { id "kotlin" } could be supported in some hacky way to resolve to what Gradle ships with.

I say that because there are a few different scenarios with different "Kotlin versions" in play.

  1. I'm building a Kotlin project and I'm using a build.gradle file (Groovy).
  2. I'm building a Gradle plugin using Kotlin (or using Kotlin in buildSrc) and I'm using a build.gradle file (Groovy).
  3. I'm building a Kotlin project and I'm using a build.gradle.kts file (Kotlin).
  4. I'm building a Gradle plugin using Kotlin (or using Kotlin in buildSrc) and I'm using a build.gradle.kts file (Kotlin).

For 1) and 3), I think we want to encourage people to keep doing what they're doing. Add the Kotlin-Gradle plugin themselves and dependencies however they want. They shouldn't use localKotlin() because it basically means their project is directly tied to Gradle's version for no good reason, but they can.
For 2) and 4), I think we want to encourage people to use what Gradle ships with or an older version for compatibility's sake. localKotlin() would be the simplest thing since it's equivalent to localGroovy().

I think a localKotlin(module: String) exposes too many implementation details. e.g., we decide to not ship kotlin-foobar anymore, do we have to continue to support that lookup? If a module changed names, do we map the old name to the new name? If localKotlin() always gave you "everything", but that everything changed meanings, we could automatically add the external dependency or just put it in the release notes.

The most compelling reason for not having a localKotlinVersion to me is that it may be confusing to users which version it refers to and encourage people to use it the wrong way. Is it the version of Kotlin used by the build.gradle.kts script (Yes?), is it the version of the Kotlin compiler for the project (could be no if Gradle ships with Kotlin 1.1, but the user has upgraded the plugin to Kotlin 1.5), or is it the version of the Kotlin libraries on the compile classpath (what the user ultimately wants, but we don't know)?

Potentially, the more "helper" pieces are just a Kotlin-Gradle extension and not a part of gradle-script-kotlin at all. Gradle could provide the localKotlin() (as I described above) and the Kotlin-Gradle plugin could provide helpers to construct dependency coordinates that work with that compiler. People writing regular Kotlin projects wouldn't use localKotlin() and the extension would make it easy to add libraries. People writing Gradle plugins with Kotlin would use localKotlin() (or an older version if they are trying to maintain backwards compatibility) and need to manually add more dependencies based on their knowledge of what Gradle is providing.

I think it's a good thing to encourage the separation between what Gradle ships with and what version of the Kotlin-Gradle plugin is being used. IOW, if Gradle shipped with an ancient version of the Kotlin-Gradle plugin, build.gradle.kts would have to be written with the old Kotlin version in mind, but people should be able to compile Kotlin code with the very newest plugin.

@bamboo
Copy link
Member Author

bamboo commented Jul 15, 2016

First of all, thanks for the deep thinking you are putting into this, @big-guy, it's been really helpful so far.

if Gradle shipped with an ancient version of the Kotlin-Gradle plugin, build.gradle.kts would have to be written with the old Kotlin version in mind, but people should be able to compile Kotlin code with the very newest plugin.

Absolutely. Although we currently don't ship and don't plan to ship the Kotlin-Gradle plugin that statement still makes sense when we replace Kotlin-Gradle plugin by Kotlin compiler which we do ship. Being able to compile with the very newest plugin will be desirable even when the version of the compiler shipped with Gradle is recent but not necessarily the very newest one.

With regards to being able to compile your project against an older version of Kotlin or, more specifically, being able to use a version of kotlin-gradle-plugin that ships with an older version of Kotlin from a gradle-script-kotlin based build, the guarantee is basically that of newer Kotlin binaries (the result of compiling build.gradle.kts) running against older versions of the runtime (the one in the buildscript classpath): it might work but ymmv. At least for the near future that will be the case.

I mostly agree with you on the different scenarios with different "Kotlin versions" in play. I partially disagree on scenario 3. I think it might be counterproductive to keep two Kotlin versions in mind - the one I use for automation and the one I use for the application. I think one language to rule them all is a better value proposition so I want to make it easier for people to follow that route and that also includes the ability to upgrade gradle-script-kotlin independenly of Gradle.

About localKotlin() you made me realise that what I'd really like to have at this point is something more akin to helpers to construct dependency coordinates. One very compelling reason for me is that once there's a function the IDE can help you discover it and type it but GAV strings are opaque (until we have something like type providers anyway). Something like fun DependencyHandler.kotlinModule(name: String, version: String = "1.1-M01") would go a long way and the semantics are really simple, it builds that org.jetbrains.kotlin:kotlin-$name:$version thing for you.

The most compelling reason for not having a localKotlinVersion to me is that it may be confusing to users which version it refers to and encourage people to use it the wrong way. Is it the version of Kotlin used by the build.gradle.kts script (Yes?), is it the version of the Kotlin compiler for the project (could be no if Gradle ships with Kotlin 1.1, but the user has upgraded the plugin to Kotlin 1.5), or is it the version of the Kotlin libraries on the compile classpath (what the user ultimately wants, but we don't know)?

It is the version of Kotlin embedded in gradle-script-kotlin so I think we might mitigate the confusion by choosing a better name (embeddedKotlinVersion maybe). We do expect people to be using an IDE together with GSK so documentation should be a keystroke away in any case. All that being said, while I still think it is important to make that information available via a property, I expect people to be using the kotlinModule helper far more often.

@big-guy
Copy link
Member

big-guy commented Jul 15, 2016

No worries.

With regards to being able to compile your project against an older version of Kotlin or, more specifically, being able to use a version of kotlin-gradle-plugin that ships with an older version of Kotlin from a gradle-script-kotlin based build, the guarantee is basically that of newer Kotlin binaries (the result of compiling build.gradle.kts) running against older versions of the runtime (the one in the buildscript classpath): it might work but ymmv. At least for the near future that will be the case.

I'm not really talking about the gradle-script-kotlin case. The version of Kotlin that we use internally has to be the version that people use for the build script (so upgrading the Kotlin runtime separately doesn't make sense). I think absolutely there are at least two potentially different versions of Kotlin that need to be kept in mind. Maybe I should have split the use cases a bit differently. All the scenarios could be using either a Kotlin or Groovy based build script.

  1. I'm building a Kotlin project.
  2. I'm building a Gradle plugin that uses Kotlin.
  3. I'm using Kotlin in buildSrc.

For 1), I very strongly think we should separate the version of Kotlin that comes with Gradle with the version that someone uses in their project. Let's say we didn't have gradle-script-kotlin and only had a core kotlin-gradle-plugin that was built-in. For someone to build their Kotlin project, they would apply plugin: 'kotlin' and add org.jetbrains.kotlin:kotlin:$version dependencies to the compile configuration. The kotlin plugin would need to be written so that it can build with a range of Kotlin versions (that may be the same or different from what ships with Gradle) effectively using different Kotlin compiler versions. The build author could use localKotlin() to add the libraries that ship with Gradle, but that's less safe than using external coordinates.

For 2), it's the same as above only someone may decide that they only support the version of Gradle that their plugin is built with (and use localKotlin()). Or, since there's no guarantee that a Kotlin-based Gradle plugin built with a newer version of the compiler will work with older builds, they may decide to target the oldest version Kotlin that is used with Gradle. So in this scenario, I'm building with Gradle 3.5 (that bundles Kotlin 1.5), but I want to be able to run my plugin against Gradle 3.0 (that bundles Kotlin 1.0), so I'll want to use the 1.0 version of the Kotlin compiler and runtime to build my plugin. That's separate from using the 1.0 version of the kotlin-gradle-plugin and separate from the version of Kotlin that I'm using for my build script.

1 and 2) also get messy because there could be three Kotlin versions that are in play. Your build.gradle.kts is using one version, you're adding a particular version of kotlin-gradle-plugin that is using another version, and you need to build against an older version of Kotlin.

For 3), it makes less sense to use external coordinates since the buildSrc project is tightly coupled to a particular version of Gradle (you're not going to reuse buildSrc for other projects). You want to use the version of Kotlin that is being used by Gradle for the build scripts. localKotlin() makes sense for this case.

I'm glad I got you to think of something different :) I really think the helpers to construct dependency coordinates are not a feature of gradle-script-kotlin, but of kotlin-gradle-plugin. gradle-script-kotlin is for adding Kotlin scripting support to build scripts and kotlin-gradle-plugin is for adding Kotlin support to Gradle projects.

What's the use case for upgrading gradle-script-kotlin separately? I understand that right now there's a need for feedback, but it seems like in the majority case, someone would want to use the version documented with a particular release of Gradle and we'd want to be able to limit the variety of test combinations. Someone shouldn't need to upgrade gradle-script-kotlin to use a newer version of the Kotlin compiler/runtime for their project.

@bamboo
Copy link
Member Author

bamboo commented Jul 15, 2016

What's the use case for upgrading gradle-script-kotlin separately?

In general the reasons are the same for why you would like to upgrade to a new version of any plugin separately from Gradle. You might want to take advantage of some new feature and don't want to wait. Your build might depend on some specific quirk of a Gradle component and upgrading just this single module is less risky.

In the particular case of gradle-script-kotlin the most important that come to mind are:

  1. You want to take advantage of a new gradle-script-kotlin feature (say, better IDE integration for buildSrc).
  2. You want to take advantage of a new Kotlin version for your build (say, a faster compiler).
  3. You want to take advantage of a new Kotlin version for your project and you are sold on the simplicity of the one language to rule them all approach so instead of upgrading just kotlin-gradle-plugin you'd rather upgrade your whole Kotlin stack so to speak.

Someone shouldn't need to upgrade gradle-script-kotlin to use a newer version of the Kotlin compiler/runtime for their project.

Fully agreed.

I really think the ​_helpers to construct dependency coordinates_​ are not a feature of gradle-script-kotlin, but of kotlin-gradle-plugin.

I don't disagree. It's just far easier to add it to gradle-script-kotlin and start taking advantage of it sooner rather than later. The 0.* cycle is perfect for that sort of usability experiment, gather feedback and then consolidate things as we approach 1.0. There's also the fact that currently there's no way to use external extensions inside a buildscript block.

I very strongly think we should separate the version of Kotlin that comes with Gradle with the version that someone uses in their project

And I'm not planning to make that any harder than it is Today. This is only about making it easier to go with the one language to rule them all flow, particularly in the months leading up to 1.0 GA as both Kotlin 1.1 and GSK mature.

@bamboo
Copy link
Member Author

bamboo commented Jul 15, 2016

The kotlin plugin would need to be written so that it can build with a range of Kotlin versions (that may be the same or different from what ships with Gradle) effectively using different Kotlin compiler versions.

Yes. And this level of sophistication will probably require more isolation than we currently provide. Something to keep in mind.

@bamboo bamboo changed the title Introduce localKotlinVersion property Introduce embeddedKotlinVersion property Jul 18, 2016
@bamboo bamboo closed this as completed in ee30b3a Jul 19, 2016
@bamboo bamboo changed the title Introduce embeddedKotlinVersion property Introduce embeddedKotlinVersion property and kotlinModule extension. Jul 20, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants