Assign closure to variable in Kotlin DSL

Dear guru,

I have the following code in Groovy DSL. The idea is to save repositories configuration in variable and than use it later in several places. Could you please tell me how to do that in Kotlin?

def repos = {
	mavenLocal()

	maven {
		url "..."
	}
}

pluginManagement {
    repositories(repos)
    repositories {
        gradlePluginPortal()
    }
}

repositories(repos)

Thanks in advance,
Vadim

Technically, this is a Groovy Closure:

def repos = {
	mavenLocal()

	maven {
		url "..."
	}
}

which can be written in Kotlin as

val repos = closureOf<RepositoryHandler> {
    mavenLocal()
    maven(url = "...")
}

However, this won’t give you exactly what you want because the repositories method in Project takes a Closure<RepositoryHandler>, but the repositories method in Settings.pluginManagement takes an Action<RepositoryHandler>. In Groovy, a Closure works for both, but in Kotlin, the strong typing means you can’t pass in the exact same value as is. You’d have to do some conversion.

Is it possible to convert Closure to Action object manually in Kotlin script?

Don’t use a Closure in Kotlin code unless you are forced to, for example by some plugin that depends on getting a Closure as parameter.
But for Gradle built-in things this should not be the case.

Settings.pluginManagement.repositories takes an Action<? super RepositoryHandler>.
Project.repositories takes a Closure, but
ProjectExtensions.kt of the kotlin-dsl classes defines an exstension function Project.repositories that takes a RepositoryHandler.() -> Unit.

And RepositoryHandler.() -> Unit does work as Action<? super RepositoryHandler>.

So what you want is

val repos: RepositoryHandler.() -> Unit = {
    mavenLocal()
    maven("...")
}

But be aware that pluginManagement block is special and executed before the rest of the script, so that settings plugins for the same settings script are resolved from the repositories and with the rules defined in the pluginManagement block, so you can for example not do something like this as then repo is not found:

val repos: RepositoryHandler.() -> Unit = {
}

pluginManagement {
    repositories(repos)
}

What you can do is for example

pluginManagement {
    val repos: RepositoryHandler.() -> Unit = {
    }

    repositories(repos)
    dependencyResolutionManagement.repositories(repos)
}

as the pluginManagement block is evaluated first separately, but can still configure the whole settings object, it is more like a beforeEvaluate with a different name and special methods additional that are only available inside.

Or you could do it like

pluginManagement {
    var repos: RepositoryHandler.() -> Unit by extra
    repos = {
    }

    repositories(repos)
}

dependencyResolutionManagement {
    val repos: RepositoryHandler.() -> Unit by settings
    repositories(repos)
}
1 Like

@Vampire, thank you a lot for detailed explanation! It’s pretty clear now.