https://kotlinlang.org logo
Title
l

Lukasz Kalnik

07/01/2022, 8:58 AM
I have a custom Gradle plugin which adds different common dependencies. However I cannot find the syntax for adding a dependency on another project. In Kotlin Gradle Script DSL the syntax would be:
testImplementation(project(":myProject"))
I need the syntax for writing a Kotlin plugin:
project.dependencies.add("testImplementation", /* How do I create a project dependency notation here? */)
j

jendrik

07/01/2022, 9:03 AM
It is the same in Kotlin DSL -
project(":myProject")
Which error are you getting if you do that?
l

Lukasz Kalnik

07/01/2022, 9:04 AM
I need the syntax for a custom Gradle plugin, not for the Kotlin Script DSL.
j

jendrik

07/01/2022, 9:05 AM
Got it. Is the Plugin written in Java or Kotlin?
l

Lukasz Kalnik

07/01/2022, 9:05 AM
In Kotlin
This is what I get now
j

jendrik

07/01/2022, 9:06 AM
The
project()
method is on the
Project
interface. So you can do
project.project(...)
l

Lukasz Kalnik

07/01/2022, 9:06 AM
😄
Works!
Thanks for your help 🙏
j

jendrik

07/01/2022, 9:12 AM
You can use Kotlin’s
with
to have the whole method in the
project
context, then it looks like in the DSL. E.g.:
override fun apply(project: Project): Unit = with(project) {
   ...
}
l

Lukasz Kalnik

07/01/2022, 9:12 AM
Yes, that's true
j

jendrik

07/01/2022, 9:12 AM
l

Lukasz Kalnik

07/01/2022, 9:13 AM
I was looking for some hint about it in the Gradle documentation, but it was not so easy to find.
I usually work only with the Kotlin DSL and there I think the
Project
is indeed always the implicit receiver for the whole syntax. I was not aware of this (because the DSL is so convenient that I never actually thought how it works under the hood and how it connects to the underlying Gradle Java API).
The samples are helpful, thanks!
v

Vampire

07/01/2022, 9:53 AM
Does it really work with
project.project(...)
? I thought it should be
project.dependencies.project(...)
.
project(...)
in a build script uses the latter (just navigate to it from a build script)
project.project
returns a
Project
while
project.dependencies.project
returns a
ProjectDependency
. Latest if you need to supply a configuration to depend on, you need to use
project.dependencies.project
even if it otherwise works with
project.project
.
l

Lukasz Kalnik

07/01/2022, 9:59 AM
Good point.
DependencyHandler.add()
just accepts
Object
...
Currently I do this in my plugin and it works (clean + build + tests pass):
override fun apply(project: Project) {
        with(project) {
            dependencies.add("testImplementation", project(":testFixtures"))
        }
    }
v

Vampire

07/01/2022, 10:02 AM
Yeah, because you can give it a variety of things like
ProjectDependency
,
String
and others. I'm not sure whether it accepts
Project
, could well be, I just was not aware
Well, then it seems to accept it unless you need a specific configuration
TIL
l

Lukasz Kalnik

07/01/2022, 10:02 AM
I mean it doesn't make it typesafe...
There could be an overload for special cases like
String
And the rest of dependency types could be handled just under a common interface like
Dependency
v

Vampire

07/01/2022, 10:03 AM
I have no idea why the Gradle guys designed it like that. Maybe because main usage target was Groovy DSL as sole DSL at that time and there you have duck-typing anyway.
l

Lukasz Kalnik

07/01/2022, 10:04 AM
True
That might have been the reason
v

Vampire

07/01/2022, 10:04 AM
But could also be a different reason. If you are curious, open a feature request to make the API type-safe with overloads and we will see what the Gradle guys respond :-)
l

Lukasz Kalnik

07/01/2022, 10:08 AM
project.dependencies.project()
doesn't allow String as argument:
v

Vampire

07/01/2022, 10:09 AM
Well, you can either use the Kotlin DSL classes that add that sugar, or you have to use the real arguments, which is
Map
project.dependencies.project(mapOf("path" to ":foo"))
But if it works with
project.project
you can also just stick with it, I just was not aware that is accepted.
l

Lukasz Kalnik

07/01/2022, 10:15 AM
I guess if I want to use the Kotlin DSL inside of my plugin code (which is in the
gradlePlugins
module) I need to somehow add dependency on the Kotlin DSL in this module, right?
Otherwise I cannot import the
DependencyHandler.project()
extension function from
org.gradle.kotlin.dsl.DependencyHandlerExtensions.kt
v

Vampire

07/01/2022, 10:23 AM
Exactly
l

Lukasz Kalnik

07/01/2022, 10:23 AM
How do I do this?
v

Vampire

07/01/2022, 10:23 AM
You can either apply the
kotlin-dsl
plugin if you also want the other bits it does, or there is a helper for it, I have to search for. Probably something like
implementation(kotlinDsl())
l

Lukasz Kalnik

07/01/2022, 10:25 AM
Looks like there is
implementation(gradleKotlinDsl())
v

Vampire

07/01/2022, 10:25 AM
Exactly
kotlin-dsl
applies
java-gradle-plugin
,
kotlin-dsl.base
, and
kotlin-dsl.precompiled-script-plugins
plugins
kotlin-dsl.base
plugin applies
embedded-kotlin
, adds
gradleKotlinDsl()
to the dependencies of
compileOnly
and
testImplementation
configurations, and configures the Kotlin DSL compiler plugins for example for proper SAM conversion for
Action
and similar.
So maybe
compileOnly(gradleKotlinDsl())
would be more appropriate as the classes are provided by the runtime environment already.
j

Javier

07/01/2022, 10:28 AM
I have it added as dependency and I can't access to
import org.gradle.api.artifacts.dsl.project
l

Lukasz Kalnik

07/01/2022, 10:28 AM
Thank you so much for all your helpful explanations. They really helped me understand what's going on behind the scenes (which is so much better than "just quickly fixing the problem")!
1
import org.gradle.kotlin.dsl.project
j

Javier

07/01/2022, 10:29 AM
as a workaround you can use
l

Lukasz Kalnik

07/01/2022, 10:29 AM
Javier, your import is not importing the extension function.
j

Javier

07/01/2022, 10:29 AM
exactly, is what I said
it is not accessible even with
gradleKotlinDsl
l

Lukasz Kalnik

07/01/2022, 10:30 AM
No, the import is wrong
That's what I mean
Compare to mine
j

Javier

07/01/2022, 10:30 AM
ah
j

Javier

07/01/2022, 10:30 AM
yeah I copy paste it wrong from an accessor
it works with it then
l

Lukasz Kalnik

07/01/2022, 10:31 AM
👍
v

Vampire

07/01/2022, 10:31 AM
👌
j

Javier

07/01/2022, 10:33 AM
I don't understand why
testImplementation
and so on are accessors whose need to be generated instead of being in an interface
I guess they generate even the base ones too and not only the custom ones?
v

Vampire

07/01/2022, 10:49 AM
They are not base ones, they are coming because of a built-in plugin, but still they come because of a plugin and only if it is applied.
Having the accessors there always would make the build script compile but then fail at runtime, and that's counter-productive.
j

Javier

07/01/2022, 11:17 AM
yeah but for example de kotlin dependency handler has implementation api and so on without being autogenerated
v

Vampire

07/01/2022, 12:12 PM
What do you mean with "the kotlin depedendency handler"?
If you mean doing
implementation(...)
in Kotlin DSL, then try navigating to it and looking at the file path and you will see it is generated.
j

Javier

07/01/2022, 12:26 PM
Kotlin sourcesets has its own dependency handler interface
they aren't generated
v

Vampire

07/01/2022, 12:28 PM
Well, but they are only useable when the Kotlin plugin is applied. And if it is applied,
implementation
is always usable. So it can also be hard-coded.
Besides that that is JetBrains code, not Gradle code
j

Javier

07/01/2022, 12:29 PM
yep