Skip to content

Commit

Permalink
Adjust script compilation to use jsr305 strict mode
Browse files Browse the repository at this point in the history
This is what we already do for Kotlin plugins. This change makes
the Gradle DSL more consistent.
  • Loading branch information
donat committed Nov 2, 2021
1 parent ab66688 commit 97a5545
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 7 deletions.
2 changes: 1 addition & 1 deletion gradle/shared-with-buildSrc/mirrors.settings.gradle.kts
Expand Up @@ -53,7 +53,7 @@ fun withMirrors(handler: RepositoryHandler) {
if (this is MavenArtifactRepository) {
originalUrls.forEach { name, originalUrl ->
if (normalizeUrl(originalUrl) == normalizeUrl(this.url.toString()) && mirrorUrls.containsKey(name)) {
this.setUrl(mirrorUrls.get(name))
mirrorUrls.get(name)?.let { this.setUrl(it) }
}
}
}
Expand Down
@@ -0,0 +1,38 @@
/*
* Copyright 2021 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.api.internal;

import org.gradle.api.Transformer;

import javax.annotation.Nullable;

/**
* <p>{@code Transformer} extension that explicitly allows {@code null} return values</p>
*
* @param <OUT> The type the value is transformed to.
* @param <IN> The type of the value to be transformed.
*/
public abstract class NullableTransformer<OUT, IN> implements Transformer<OUT, IN> {

@Nullable
@Override
public abstract OUT transform(IN in);

public Transformer<OUT, IN> asTransformer() {
return this;
}
}
Expand Up @@ -76,7 +76,12 @@ public interface ContentFilterable {
* Adds a content filter based on the provided transformer. The Closure will be called with each line (stripped of line
* endings) and should return a String to replace the line or {@code null} to remove the line. If every line is
* removed, the result will be an empty file, not an absent one.
*
* <p>
* Note, that due to the nullability constraints clients written in Kotlin cannot return null values as it results in a
* compile-time error. To fix that, the {@code nullableTransformer} utility method should be used:
* <pre>
* filter(nullableTransformer { line -&gt; ... })
* </pre>
* @param transformer to implement line based filtering
* @return this
*/
Expand Down
Expand Up @@ -38,6 +38,10 @@ Some plugins will break with this new version of Gradle, for example because the

=== Potential breaking changes

=== Kotlin build scripts compilation enforces non-null APIs

If a Kotlin build script uses a Java API that has nullability annotations (JSR-305) then the compilation will enforce those constraints. Previous Gradle versions, however, forgot to add the same enforcement to the Kotlin build script compilation. Starting from Gradle 7.4 the nullability checks are enforced in the script compilation.

==== Updates to default tool integration versions

- PMD has been updated to https://github.com/pmd/pmd/releases/tag/pmd_releases%2F6.39.0[PMD 6.39.0].
Expand Down
Expand Up @@ -218,9 +218,9 @@ tasks.register<Copy>("filter") {
"[$line]"
}
// Use a closure to remove lines
filter { line: String ->
filter(nullableTransformer { line: String ->
if (line.startsWith('-')) null else line
}
})
filteringCharset = "UTF-8"
}
// end::filter-files[]
Expand Down
Expand Up @@ -21,7 +21,7 @@ java {
// end::java-cross-compilation[]

tasks.withType<Test>().configureEach {
systemProperty("targetJavaVersion", project.findProperty("targetJavaVersion"))
project.findProperty("targetJavaVersion")?.let { systemProperty("targetJavaVersion", it) }
}

tasks.register("checkJavadocOutput") {
Expand Down
Expand Up @@ -147,7 +147,7 @@ import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
task("dumpKotlinBuildScriptModelClassPath") {
doLast {
val modelName = KotlinBuildScriptModel::class.qualifiedName
val modelName = KotlinBuildScriptModel::class.qualifiedName!!
val builderRegistry = (project as ProjectInternal).services[ToolingModelBuilderRegistry::class.java]
val builder = builderRegistry.getBuilder(modelName)
val model = builder.buildAll(modelName, project) as KotlinBuildScriptModel
Expand Down
Expand Up @@ -123,7 +123,7 @@ public void setDestinationFile(Provider<File> destinationFile) {
this.destinationFile.set(destinationFile);
}

public void setDestinationFile(File destinationFile) {
public void setDestinationFile(@Nullable File destinationFile) { // nullability must match on getter and setter argument to end up with a writable Kotlin property
this.destinationFile.set(destinationFile);
}

Expand Down
@@ -0,0 +1,34 @@
package org.gradle.kotlin.dsl.support

import org.gradle.kotlin.dsl.fixtures.AbstractKotlinIntegrationTest
import org.junit.Test
import spock.lang.Issue


class KotlinScriptCompilerIntegrationTest : AbstractKotlinIntegrationTest() {

@Test
@Issue("https://github.com/gradle/gradle/issues/18714")
fun `Build scripts are compiled with jsr305 strict mode`() {

// given:
withFile(
"buildSrc/src/main/java/StringTransformer.java",
"""
@org.gradle.api.NonNullApi
public interface StringTransformer {
String transform(String input);
}
"""
)

withBuildScript("StringTransformer { input -> null }")

// when:
val result = buildAndFail("help")

// then:
result.assertHasDescription("Script compilation error")
result.hasErrorOutput("Null can not be a value of a non-null type String")
}
}
Expand Up @@ -15,7 +15,10 @@
*/
package org.gradle.kotlin.dsl

import org.gradle.api.Incubating
import org.gradle.api.Transformer
import org.gradle.api.file.ContentFilterable
import org.gradle.api.internal.NullableTransformer

import java.io.FilterReader
import kotlin.reflect.KClass
Expand Down Expand Up @@ -111,3 +114,19 @@ fun <T : FilterReader> ContentFilterable.filter(filterType: KClass<T>, vararg pr
fun <T : FilterReader> ContentFilterable.filter(filterType: KClass<T>, properties: Map<String, Any?>) =
if (properties.isEmpty()) filter(filterType.java)
else filter(properties, filterType.java)


/**
* Creates a new transformer that can return null and can be used in the context of [ContentFilterable.filter].
*
* @param transformer the spec of the transformer to be returned.
* @since 7.4
*/
@Incubating
fun ContentFilterable.nullableTransformer(transformer: (String) -> String?): Transformer<String, String> {
return object : NullableTransformer<String, String>() {
override fun transform(input: String): String? {
return transformer.invoke(input)
}
}.asTransformer()
}
Expand Up @@ -57,6 +57,9 @@ import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl

import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor.Companion.registerExtension
import org.jetbrains.kotlin.load.java.JavaTypeEnhancementState
import org.jetbrains.kotlin.load.java.Jsr305Settings
import org.jetbrains.kotlin.load.java.ReportLevel

import org.jetbrains.kotlin.name.NameUtils

Expand Down Expand Up @@ -352,6 +355,7 @@ val gradleKotlinDslLanguageVersionSettings = LanguageVersionSettingsImpl(
apiVersion = ApiVersion.KOTLIN_1_4,
analysisFlags = mapOf(
AnalysisFlags.skipMetadataVersionCheck to true,
JvmAnalysisFlags.javaTypeEnhancementState to JavaTypeEnhancementState(Jsr305Settings(ReportLevel.STRICT, ReportLevel.STRICT)) { ReportLevel.STRICT },
JvmAnalysisFlags.jvmDefaultMode to JvmDefaultMode.ENABLE,
),
specificFeatures = mapOf(
Expand Down

0 comments on commit 97a5545

Please sign in to comment.