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

Fix a bunch of issues in the containers APIs #1116

Merged
merged 15 commits into from
Sep 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,22 @@ fun <T : Any> NamedDomainObjectCollection<out Any>.getByName(name: String, type:
}


/**
* Locates an object by name and casts it to the expected [type] then configures it.
*
* If an object with the given [name] is not found, [UnknownDomainObjectException] is thrown.
* If the object is found but cannot be cast to the expected [type], [IllegalArgumentException] is thrown.
*
* @param name object name
* @param configure configuration action to apply to the object before returning it
* @return the object, never null
* @throws [UnknownDomainObjectException] When the given object is not found.
* @throws [IllegalArgumentException] When the given object cannot be cast to the expected type.
*/
fun <T : Any> NamedDomainObjectCollection<out Any>.getByName(name: String, type: KClass<T>, configure: T.() -> Unit): T =
getByName(name, type).also(configure)


/**
* Locates an object by name and casts it to the expected type [T] then configures it.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ inline val <T : Any, C : NamedDomainObjectContainer<T>> C.registering: Registeri
* @param C the concrete container type
* @param action the configuration action
*/
fun <T : Any, C : PolymorphicDomainObjectContainer<T>> C.registering(action: T.() -> Unit): RegisteringDomainObjectDelegateProviderWithAction<out C, T> =
fun <T : Any, C : NamedDomainObjectContainer<T>> C.registering(action: T.() -> Unit): RegisteringDomainObjectDelegateProviderWithAction<out C, T> =
RegisteringDomainObjectDelegateProviderWithAction(this, action)


Expand Down Expand Up @@ -116,7 +116,7 @@ operator fun <T : Any, C : NamedDomainObjectContainer<T>> RegisteringDomainObjec
/**
* Registers an element and provides a delegate with the resulting [NamedDomainObjectProvider].
*/
operator fun <T : Any, C : PolymorphicDomainObjectContainer<T>> RegisteringDomainObjectDelegateProviderWithAction<C, T>.provideDelegate(
operator fun <T : Any, C : NamedDomainObjectContainer<T>> RegisteringDomainObjectDelegateProviderWithAction<C, T>.provideDelegate(
receiver: Any?,
property: KProperty<*>
) = ExistingDomainObjectDelegate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.gradle.kotlin.dsl

import org.gradle.api.Task
import org.gradle.api.tasks.TaskCollection
import org.gradle.api.tasks.TaskContainer
import org.gradle.api.tasks.TaskProvider

Expand Down Expand Up @@ -87,6 +88,17 @@ operator fun <U : Task> ExistingDomainObjectDelegateProviderWithType<out TaskCon
)


/**
* Provides a [TaskProvider] delegate for the task of the given type named after the property after configuring it with the given action.
*/
operator fun <U : Task> ExistingDomainObjectDelegateProviderWithTypeAndAction<out TaskContainer, U>.provideDelegate(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀 That is quite a class name. 👀

receiver: Any?,
property: KProperty<*>
) = ExistingDomainObjectDelegate(
delegateProvider.named(property.name, type).apply { configure(action) }
)


/**
* Registers a task and provides a delegate with the resulting [TaskProvider].
*/
Expand Down Expand Up @@ -175,29 +187,29 @@ class TaskContainerScope(val container: TaskContainer) : TaskContainer by contai
/**
* Locates a task by name and type, without triggering its creation or configuration, failing if there is no such task.
*
* @see [TaskContainer.named]
* @see [TaskCollection.named]
*/
@Suppress("extension_shadowed_by_member")
inline fun <reified T : Task> TaskContainer.named(name: String): TaskProvider<T> =
inline fun <reified T : Task> TaskCollection<out Task>.named(name: String): TaskProvider<T> =
named(name, T::class)


/**
* Locates a task by name and type, without triggering its creation or configuration, failing if there is no such task.
*
* @see [TaskContainer.named]
* @see [TaskCollection.named]
*/
fun <T : Task> TaskContainer.named(name: String, type: KClass<T>): TaskProvider<T> =
fun <T : Task> TaskCollection<out Task>.named(name: String, type: KClass<T>): TaskProvider<T> =
named(name, type) {}


/**
* Configures a task by name and type, without triggering its creation or configuration, failing if there is no such task.
*
* @see [TaskContainer.named]
* @see [TaskCollection.named]
* @see [TaskProvider.configure]
*/
fun <T : Task> TaskContainer.named(name: String, type: KClass<T>, configuration: T.() -> Unit): TaskProvider<T> =
fun <T : Task> TaskCollection<out Task>.named(name: String, type: KClass<T>, configuration: T.() -> Unit): TaskProvider<T> =
uncheckedCast(named(name).also { provider ->
provider.configure { obj ->
configuration(
Expand All @@ -208,6 +220,37 @@ fun <T : Task> TaskContainer.named(name: String, type: KClass<T>, configuration:
})


/**
* Configures a task by name and type, without triggering its creation or configuration, failing if there is no such task.
*
* @see [TaskCollection.named]
* @see [TaskProvider.configure]
*/
inline fun <reified T : Task> TaskCollection<out Task>.named(name: String, noinline configuration: T.() -> Unit): TaskProvider<T> =
named<T>(name).apply {
configure(configuration)
}


/**
* Defines a new object, which will be created when it is required.
*
* @see [TaskContainer.register]
*/
@Suppress("extension_shadowed_by_member")
inline fun <reified T : Task> TaskContainer.register(name: String): TaskProvider<T> =
register(name, T::class.java)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seeing a @Suppress("extension_shadowed_by_member") usually flags something isn't going to work correctly in my experience.
Do you have a test that ensures this can be called without an issue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, look at other files changed in this PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍



/**
* Defines and configure a new object, which will be created when it is required.
*
* @see [TaskContainer.register]
*/
inline fun <reified T : Task> TaskContainer.register(name: String, noinline configuration: T.() -> Unit): TaskProvider<T> =
register(name, T::class.java, configuration)


/**
* Creates a [Task] with the given [name] and type, passing the given arguments to the [javax.inject.Inject]-annotated constructor,
* and adds it to this project tasks container.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.gradle.kotlin.dsl

import org.gradle.api.Project

import org.gradle.kotlin.dsl.fixtures.AbstractDslTest
import org.gradle.kotlin.dsl.fixtures.newProjectBuilderProjectWith

import org.hamcrest.CoreMatchers.hasItem
import org.hamcrest.CoreMatchers.hasItems
import org.hamcrest.MatcherAssert.assertThat

import org.junit.Test


class NamedContainersDslTest : AbstractDslTest() {

@Test
fun `monomorphic named domain object container api`() {

testConfigurationContainerVia("api", """

val foo: Configuration = configurations.getByName("foo")
val bar: Configuration = configurations.getByName("bar") {
extendsFrom(foo)
}

val bazar: Configuration = configurations.create("bazar")
val cathedral: Configuration = configurations.create("cathedral") {
extendsFrom(bazar)
}

val cabin: NamedDomainObjectProvider<Configuration> = configurations.named("cabin")
val castle: NamedDomainObjectProvider<Configuration> = configurations.named("castle") {
extendsFrom(cabin.get())
}

val valley: NamedDomainObjectProvider<Configuration> = configurations.register("valley")
val hill: NamedDomainObjectProvider<Configuration> = configurations.register("hill") {
extendsFrom(valley.get())
}
""")
}

@Test
fun `monomorphic named domain object container scope api`() {

testConfigurationContainerVia("scope-api", """
configurations {

val foo: Configuration = getByName("foo")
val bar: Configuration = getByName("bar") {
extendsFrom(foo)
}

val bazar: Configuration = create("bazar")
val cathedral: Configuration = create("cathedral") {
extendsFrom(bazar)
}

val cabin: NamedDomainObjectProvider<Configuration> = named("cabin")
val castle: NamedDomainObjectProvider<Configuration> = named("castle") {
extendsFrom(cabin.get())
}

val valley: NamedDomainObjectProvider<Configuration> = register("valley")
val hill: NamedDomainObjectProvider<Configuration> = register("hill") {
extendsFrom(valley.get())
}
}
""")
}

@Test
fun `monomorphic named domain object container delegated properties`() {

testConfigurationContainerVia("delegated-properties", """

val foo: Configuration by configurations.getting
val bar: Configuration by configurations.getting {
extendsFrom(foo)
}

val bazar: Configuration by configurations.creating
val cathedral: Configuration by configurations.creating {
extendsFrom(bazar)
}

val cabin: NamedDomainObjectProvider<Configuration> by configurations.existing
val castle: NamedDomainObjectProvider<Configuration> by configurations.existing {
extendsFrom(cabin.get())
}

val valley: NamedDomainObjectProvider<Configuration> by configurations.registering
val hill: NamedDomainObjectProvider<Configuration> by configurations.registering {
extendsFrom(valley.get())
}
""")
}

@Test
fun `monomorphic named domain object container scope delegated properties`() {

testConfigurationContainerVia("scope-delegated-properties", """
configurations {

val foo: Configuration by getting
val bar: Configuration by getting {
extendsFrom(foo)
}

val bazar: Configuration by creating
val cathedral: Configuration by creating {
extendsFrom(bazar)
}

val cabin: NamedDomainObjectProvider<Configuration> by existing
val castle: NamedDomainObjectProvider<Configuration> by existing {
extendsFrom(cabin.get())
}

val valley: NamedDomainObjectProvider<Configuration> by registering
val hill: NamedDomainObjectProvider<Configuration> by registering {
extendsFrom(valley.get())
}
}
""")
}

@Test
fun `monomorphic named domain object container scope string invoke`() {

testConfigurationContainerVia("scope-string-invoke", """
configurations {

val foo: NamedDomainObjectProvider<Configuration> = "foo"()
val bar: NamedDomainObjectProvider<Configuration> = "bar" {
extendsFrom(foo.get())
}

val cabin: NamedDomainObjectProvider<Configuration> = "cabin"()
val castle: NamedDomainObjectProvider<Configuration> = "castle" {
extendsFrom(cabin.get())
}
}
""") {
configurations {
val bazar by creating
create("cathedral") {
it.extendsFrom(bazar)
}
val valley by registering
register("hill") {
it.extendsFrom(valley.get())
}
}
apply(plugin = "java")
}
}

private
fun testConfigurationContainerVia(name: String, script: String, configuration: Project.() -> Unit = {}) {
newProjectBuilderProjectWith(newFolder(name)).run {

preExistingConfigurations.forEach { name ->
configurations.register(name)
}

configuration()

eval(script)

assertThat(
configurations.names.sorted(),
hasItems(
*expectedConfigurationsExtendsFrom.flatMap {
listOf(it.first, it.second)
}.sorted().toTypedArray()
)
)
expectedConfigurationsExtendsFrom.forEach { (first, second) ->
assertThat(configurations[first].extendsFrom, hasItem(configurations[second]))
}
}
}

companion object {

val preExistingConfigurations = listOf(
"foo", "bar", "cabin", "castle"
)

val expectedConfigurationsExtendsFrom = listOf(
"bar" to "foo",
"cathedral" to "bazar",
"castle" to "cabin",
"hill" to "valley"
)
}
}
Loading