New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support `ObjectConfigurationAction.to` for arbitrary targets #659

Open
bamboo opened this Issue Jan 16, 2018 · 7 comments

Comments

Projects
None yet
4 participants
@bamboo
Copy link
Member

bamboo commented Jan 16, 2018

Example:

// build.gradle.kts
val myShape = MutableRectangle()
apply {
  from("golden-ratio.gradle.kts")
  to(myShape)
}

// golden-ratio.gradle.kts
when (target) { // recover target type information 
  is MutableRectangle -> target.run {
    width = 1.618 * height    
  }
}
@eskatos

This comment has been minimized.

Copy link
Member

eskatos commented Jan 31, 2018

Or consider deprecating the to() methods upstream.

@eskatos eskatos modified the milestones: 1.0.1, 1.1.0 Jan 31, 2018

@henrik242

This comment has been minimized.

Copy link

henrik242 commented Mar 6, 2018

Any progress here..?

@JLLeitschuh

This comment has been minimized.

Copy link
Contributor

JLLeitschuh commented May 1, 2018

What is the IDE support plan for this? Currently, for *.gradle.kts this is always Project.

Also, are you considering adding a target instead of making this the target that to is passed?

Also, what are you plans for adding support for this while not breaking existing users who assume that *.gradle.kts will behave as above.

Proposal
Something equivalent to a bash shebang that tells the script what context to compile it with.
Something like this could work at the top of files:

@file:ApplyTargets(MutableRectangle::class, Project::class, Settings::class)

If only one is passed, you can make this that type, but if multiple are passed to the annotation make this Any.

@JLLeitschuh

This comment has been minimized.

Copy link
Contributor

JLLeitschuh commented Sep 8, 2018

Anyone looking for a stopgap solution to this problem, this is what I've come up with:

/**
 * Makes it so that this script can be called with `this` as both a
 * [KotlinBuildScript] & [KotlinSettingsScript] by providing an adapter
 * to the two things that `this` could be.
 *
 *  - [Issue #821](https://github.com/gradle/kotlin-dsl/issues/821)
 *  - [Issue #659](https://github.com/gradle/kotlin-dsl/issues/659)
 */
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: KClass<*> = KotlinBuildScript::class
    val kotlinSettingsClass: KClass<*> = KotlinSettingsScript::class

    /*
     * The following is what I'd like to try to be doing, but it would not compile:
     * ```
     * when(this) {
     *     is KotlinBuildScript -> ...
     *     is KotlinSettingsScript -> ..
     *     else -> ...
     * }
     * ```
     * This is because, according to the Kotlin compiler, when applied to a project script,
     * `this` will only ever be a `KotlinBuildScript`, and when applied to a settings script,
     * it will only ever be `KotlinSettingsScript`.
     * The compiler doesn't know that it will be compiled a second time with `this` as a different
     * type. Thus, we are left with this hacky solution.
     */
    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] }
    )
}
@JLLeitschuh

This comment has been minimized.

Copy link
Contributor

JLLeitschuh commented Oct 16, 2018

@bamboo Would this just work with the custom multi-stage custom bytecode that is generated for these scripts now or would that logic need to be customized to support a new script target?

Also, is there a proposed extension for these sorts of files?
I was thinking something like *.other.gradle.kts or *.extension.gradle.kts where Extra infers that the script is being applied to something that is ExtensionAware.
Perhaps *.custom.gradle.kts?

I've considered trying to tackle this entire issue myself at some point and opening a PR. If there are any pitfalls I might immediately hit "like the custom compiled bytecode generation that @bamboo implemented) that would be helpful.

@bamboo

This comment has been minimized.

Copy link
Member

bamboo commented Oct 17, 2018

Hey @JLLeitschuh,

It still requires some work, mainly:

  • a new Kotlin script template base class (like KotlinBuildScript), as for the filename we were thinking in *.any.gradle.kts
  • proper support for the new template in the bytecode emitter
  • a PR to the IntelliJ Kotlin Plugin making it recognise the new template and file pattern
@JLLeitschuh

This comment has been minimized.

Copy link
Contributor

JLLeitschuh commented Oct 17, 2018

Since the *.any.gradle.kts scripts won't require/support a plugins block, is the bytecode emitter just a no-op for these things?

Also, can we assume any implied interfaces of the target? For example, can we always assume that the target will be ExtensionAware? Is that an unsafe assumption?

@eskatos eskatos modified the milestones: 1.1.0, 2.0.0 Oct 30, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment