'gradle kotlindsl extract logic to outer scripts

What is the recommended way to extract gradle build code blocks to external scripts? Notice these scripts should support references to the gradle project, extra etc. So - compiling kt files in buildSrc isn't what I'm looking for.

I've tried to create files like logger.gradle.kts and these seem to "know" the project ref / compile, but any fun I write in them is not referenceable in the main build file although I apply like this:

`apply (from = "logger.gradle.kts")`

The error I get as part of the build is:

Unresolved reference: logInfo - where logInfo is a fun in logger.gradle.kts.

This is the logger file I am using:

import org.apache.tools.ant.taskdefs.condition.Os
import org.gradle.internal.logging.text.StyledTextOutput
import org.gradle.internal.logging.text.StyledTextOutputFactory
import org.gradle.internal.logging.services.DefaultStyledTextOutputFactory
import org.gradle.internal.logging.text.StyledTextOutput.Style


fun <R> callBasedOnContext(
    ifBuildScript: KotlinBuildScript.() -> R,
    ifSettingsScript: KotlinSettingsScript.() -> R
): R {
    /*
     * A bit of a hack to get around a compiler error when trying to do
     * `is KotlinBuildScript` and `is KotlinSettingsScript`.
     */
    val kotlinProjectClass = KotlinBuildScript::class
    val kotlinSettingsClass = KotlinSettingsScript::class

    return when {
        kotlinProjectClass.isInstance(this) -> (this as KotlinBuildScript).ifBuildScript()
        kotlinSettingsClass.isInstance(this) -> (this as KotlinSettingsScript).ifSettingsScript()
        else -> throw AssertionError("$this is not being applied to a supported type.")
    }
}

val extra: ExtraPropertiesExtension by lazy {
    callBasedOnContext(
        ifBuildScript = { extra },
        ifSettingsScript = { (settings as ExtensionAware).extra }
    )
}

fun hasPropertyHelper(propertyName: String): Boolean {
    return callBasedOnContext(
        ifBuildScript = { hasProperty(propertyName) },
        ifSettingsScript = { (settings as ExtensionAware).extra.properties.containsKey(propertyName) }
    )
}

fun propertyHelper(propertyName: String): Any? {
    return callBasedOnContext(
        ifBuildScript = { property(propertyName) },
        ifSettingsScript = { (settings as ExtensionAware).extra.properties[propertyName] }
    )
}

extra["logDebug"] = this::logDebug
extra["logInfo"] = this::logInfo
extra["logWarn"] = this::logWarn
extra["logError"] = this::logError
extra["logTitle"] = this::logTitle
extra["logStyles"] = this::logStyles

val loggerOut = DefaultStyledTextOutputFactory(null, null).create("styled_output")
val loggerOutError = loggerOut.withStyle(Style.Failure)
val loggerOutWarn = loggerOut.withStyle(Style.Description)
val loggerOutInfo = loggerOut.withStyle(Style.Success)
val loggerOutDebug = loggerOut.withStyle(Style.Normal)
val loggerOutTitle = loggerOut.withStyle(Style.Header)

fun log(message: String, out: StyledTextOutput) {
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        project.logger.quiet(message)
    } else {
        out.println(message)
    }
}

fun logTitle(message: String) {
    log("\n---------------------------------------------", loggerOutTitle)
    log("  ${message.toUpperCase()}", loggerOutTitle)
    log("---------------------------------------------", loggerOutTitle)
}

fun logDebug(message: String) {
    log("[DEBUG] " + message, loggerOutDebug)
}

fun logInfo(message: String) {
    log("[INFO] " + message, loggerOutInfo)
}

fun logWarn(message: String) {
    log("[WARN] " + message, loggerOutWarn)
}

fun logError(message: String) {
    log("[ERROR] " + message, loggerOutError)
}

fun logStyles() {
    val out = DefaultStyledTextOutputFactory(null, null).create("styled_test")

    log("Style: Normal", out.withStyle(Style.Normal))
    log("Style: Header", out.withStyle(Style.Header))
    log("Style: UserInput", out.withStyle(Style.UserInput))
    log("Style: Identifier", out.withStyle(Style.Identifier))
    log("Style: Description", out.withStyle(Style.Description))
    log("Style: ProgressStatus", out.withStyle(Style.ProgressStatus))
    log("Style: Success", out.withStyle(Style.Success))
    log("Style: SuccessHeader", out.withStyle(Style.SuccessHeader))
    log("Style: Failure", out.withStyle(Style.Failure))
    log("Style: FailureHeader", out.withStyle(Style.FailureHeader))
    log("Style: Info", out.withStyle(Style.Info))
    log("Style: Error", out.withStyle(Style.Error))
}

And usages in build.gradle.kts:

plugins {
    base
    idea
    `java-library`
    scala
    apply(from = "gradle_scripts/logger.gradle.kts")
}

And invoking like this:

logInfo("Application Version: ${version}")

the method fails on:

Unresolved reference: logInfo

Note: If I add the functions to extra (which is recommendation I saw somewhere on hos to expose methods between scripts files):

extra["logDebug"] = this::logDebug
extra["logInfo"] = this::logInfo
extra["logWarn"] = this::logWarn
extra["logError"] = this::logError
extra["logTitle"] = this::logTitle
extra["logStyles"] = this::logStyles

It then fails on:

Logger_gradle@a1f3cf9 is not being applied to a supported type.


Solution 1:[1]

Gradle Kotlin DSL does not support this operation if I'm not wrong.

If you try to add a "redundant" public modifier to logInfo(...):

public fun logInfo(s: String) {
    println(s)
}

You will get a compilation error:

Modifier 'public' is not applicable to 'local function'

The recommended way is leveraging Extension Functions?, for example:

val Project.isSnapshotBuild
    get() = (version as String).contains("snapshot", true)

check a complete sample here.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 2BAB