Skip to content
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

Migrate to Kotlin DSL #172

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
Draft

Migrate to Kotlin DSL #172

wants to merge 11 commits into from

Conversation

eyalroth
Copy link
Contributor

Implements #171.

@eyalroth eyalroth mentioned this pull request Oct 9, 2021
@CristianGM
Copy link
Contributor

You have mixed too many changes (and reasons to change) in a single PR. I don't feel like reviewing so much lines looking for the important changes.

I would suggest splitting it, at least in the 3 things I've seen so far:
Gradle -> gradle.kts
Using kotlin property syntax instead of getters
Convert a java file to kt

Other than that I left a couple of comments.

Feel free to ping me if you want review in smaller PRs.

tags = listOf("coverage", "scala", "scoverage")
}

apply(plugin = "maven-publish")
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be moved to the plugins block

}
}

configure<PublishingExtension> {
Copy link
Contributor

Choose a reason for hiding this comment

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

And moving maven-publish to the plugins block you won't need to use configure, you'll have the accessor (and you will be able to write it as you did in Groovy)

Copy link

@netvl netvl left a comment

Choose a reason for hiding this comment

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

Feel free to ignore my comments here, but I have implemented quite a lot of Gradle plugins in Kotlin over the last several years, and hopefully my findings end up being useful :)

Comment on lines +45 to +46
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
Copy link

Choose a reason for hiding this comment

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

Note that these options have zero effect on compilation of non-Java code - that is, plugins like scala or kotlin ignore them completely. The extension documentation explicitly mentions that it is used for compiling Java sources.

That being said, explicitly setting Java compatibility is useful when publishing Gradle module metadata. This area is significantly underdocumented, but from what I see from Gradle sources, if the JVM compatibility attribute value is not explicitly configured for an outgoing configuration, it attempts to deduce its value from the state of the Java compilation tasks (defined in org.gradle.api.plugins.jvm.internal.DefaultJvmPluginServices.DefaultElementsConfigurationBuilder#build). In turn, configuration of the release option on the Java tasks take priority over the targetCompatibility setting, which (if not set on a task directly) is taken from this extension.

With modern Java versions, the recommended way to configure the target Java version is to specify the --release argument to javac, which translates to something like this in Gradle:

tasks.withType<JavaCompile>().configureEach {
    options.release.set(8)

You may also want to configure Kotlin compilation tasks to target Java 8 explicitly:

tasks.withType<KotlinCompile>().configureEach {
    kotlinOptions.jvmTarget = "1.8"
}

Comment on lines +50 to +51
compileOnly("org.scoverage:scalac-scoverage-plugin_2.13:1.4.2")
implementation(group = "commons-io", name = "commons-io", version = "2.6")
Copy link

Choose a reason for hiding this comment

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

For consistency, I'd recommend using the same syntax for all dependency declarations.


mustRunAfter(tasks["test"])
}
tasks["check"].dependsOn(crossScalaVersionTest)
Copy link

Choose a reason for hiding this comment

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

Suggested change
tasks["check"].dependsOn(crossScalaVersionTest)
tasks.check {
dependsOn(crossScalaVersionTest)
}

This is more idiomatic since it relies on lazy configuration of tasks.

showStandardStreams = System.getenv("CI") == "true"
}

mustRunAfter(tasks["test"])
Copy link

Choose a reason for hiding this comment

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

Suggested change
mustRunAfter(tasks["test"])
mustRunAfter(tasks.test)

Note that if a plugin is applied in the plugins block, its tasks created during the configuration time get typed accessors in the .gradle.kts files, which is the idiomatic way of accessing such tasks.


val kotlindocJar by tasks.registering(Jar::class) {
from(tasks.dokkaHtml.get().outputDirectory)
archiveClassifier.set("kotlindoc")
Copy link

Choose a reason for hiding this comment

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

Interesting, is kotlindoc classifier something which is understood by tooling? It's just I never heard about it; from my knowledge, Kotlin API docs are packaged into the regular javadoc classifier JARs.

@Nested
var runner: ScoverageRunner? = null

@InputFiles
Copy link

Choose a reason for hiding this comment

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

Note that you usually need to write @get:InputFiles for the annotation to be applied correctly. I think Gradle even fails if it is not like that.

import org.gradle.api.tasks.PathSensitivity.RELATIVE
import java.io.File

abstract class ScoverageAggregate: DefaultTask() {
Copy link

Choose a reason for hiding this comment

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

Note that all properties here, including the @Nested one, can be defined as abstract vals, removing the need to call objects methods explicitly.

This is called "managed properties", and is described here. It is considered idiomatic to declare as many properties as possible as managed.

* Defines a new Test Task which executes normal tests with the instrumented classes.
* Defines a new Check Task which enforces an overall line coverage requirement.
*/
abstract class ScoverageExtension(val project: Project) {
Copy link

Choose a reason for hiding this comment

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

Similarly to tasks, you can define all properties here as abstract, removing the need to invoke project directly. You can even make this class into an interface, and configure it in the plugin logic.

Comment on lines +58 to +59
project.plugins.apply(JavaPlugin::class.java)
project.plugins.apply(ScalaPlugin::class.java)
Copy link

Choose a reason for hiding this comment

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

Dependent plugins are conventionally applied inside the plugin, not inside the extension logic.

deleteReportsOnAggregation.set(false)
}

fun check(closure: Closure<*>) {
Copy link

Choose a reason for hiding this comment

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

Ideally this should be defined accepting an Action<CheckConfig>. Gradle will automatically generate Groovy closure-accepting method, as long as this class is instantiated through Gradle. Otherwise this API remains hard-to-use from Kotlin DSL.

Copy link

Choose a reason for hiding this comment

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

Also, consider using NamedDomainObjectContainer for checks, it seems like it is the idiomatic way to construct and configure nested configuration objects (even though in this case, names for checks seem redundant).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants