Add explicit api mode flag to top-level kotlin {} DSL block in Gradle

for both MPP and single-target projects.

For now, it is only applied to source sets that are connected to 'main'
compilation. It seems fine for all projects except android ones.
There's a way to determine android variant for source set,
but importing android projects seems to be made via other code path.

 #KT-36019 Fixed
 #KT-37652 Open
This commit is contained in:
Leonid Startsev
2020-02-13 13:34:19 +03:00
parent 59148cdce4
commit f0dc809255
3 changed files with 67 additions and 32 deletions
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.dsl
@@ -57,6 +46,12 @@ open class KotlinProjectExtension : KotlinSourceSetContainer {
val experimental: ExperimentalExtension
get() = DslObject(this).extensions.getByType(ExperimentalExtension::class.java)
var explicitApi: ExplicitApiMode? = null
fun explicitApi() {
explicitApi = ExplicitApiMode.Strict
}
override var sourceSets: NamedDomainObjectContainer<KotlinSourceSet>
@Suppress("UNCHECKED_CAST")
get() = DslObject(this).extensions.getByName("sourceSets") as NamedDomainObjectContainer<KotlinSourceSet>
@@ -228,4 +223,12 @@ enum class NativeCacheKind(val produce: String?, val outputKind: CompilerOutputK
fun byCompilerArgument(argument: String): NativeCacheKind? =
NativeCacheKind.values().firstOrNull { it.name.equals(argument, ignoreCase = true) }
}
}
enum class ExplicitApiMode(private val cliOption: String) {
Strict("strict"),
Warning("warning"),
Disabled("disabled");
fun toCompilerArg() = "-Xexplicit-api=$cliOption"
}
@@ -1,3 +1,8 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.plugin
import com.android.build.gradle.*
@@ -498,6 +503,7 @@ internal abstract class AbstractKotlinPlugin(
fun configureProjectGlobalSettings(project: Project, kotlinPluginVersion: String) {
configureDefaultVersionsResolutionStrategy(project, kotlinPluginVersion)
configureClassInspectionForIC(project)
project.setupGeneralKotlinExtensionParameters()
}
fun configureTarget(
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.gradle.dsl.configureOrCreate
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin.Companion.sourceSetFreeCompilerArgsPropertyName
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.checkSourceSetVisibilityRequirements
@@ -97,6 +98,7 @@ class KotlinMultiplatformPlugin(
// propagate compiler plugin options to the source set language settings
setupAdditionalCompilerArguments(project)
project.setupGeneralKotlinExtensionParameters()
project.pluginManager.apply(ScriptingGradleSubplugin::class.java)
}
@@ -134,22 +136,6 @@ class KotlinMultiplatformPlugin(
val associatedCompilation = primaryCompilationsBySourceSet[sourceSet] ?: metadataCompilation
project.tasks.getByName(associatedCompilation.compileKotlinTaskName) as AbstractCompile
}
// Also set ad-hoc free compiler args from the internal project property
freeCompilerArgsProvider = project.provider {
val propertyValue = with(project.extensions.extraProperties) {
val sourceSetFreeCompilerArgsPropertyName = sourceSetFreeCompilerArgsPropertyName(sourceSet.name)
if (has(sourceSetFreeCompilerArgsPropertyName)) {
get(sourceSetFreeCompilerArgsPropertyName)
} else null
}
mutableListOf<String>().apply {
when (propertyValue) {
is String -> add(propertyValue)
is Iterable<*> -> addAll(propertyValue.map { it.toString() })
}
}
}
}
}
}
@@ -314,7 +300,7 @@ class KotlinMultiplatformPlugin(
companion object {
const val METADATA_TARGET_NAME = "metadata"
private fun sourceSetFreeCompilerArgsPropertyName(sourceSetName: String) =
internal fun sourceSetFreeCompilerArgsPropertyName(sourceSetName: String) =
"kotlin.mpp.freeCompilerArgsForSourceSet.$sourceSetName"
internal const val GRADLE_NO_METADATA_WARNING = "This build consumes Gradle module metadata but does not produce " +
@@ -403,4 +389,44 @@ internal fun sourcesJarTask(
}
return result
}
}
internal fun Project.setupGeneralKotlinExtensionParameters() {
val sourceSetsInMainCompilation by lazy {
CompilationSourceSetUtil.compilationsBySourceSets(project).filterValues { compilations ->
compilations.any {
// kotlin main compilation
it.name == KotlinCompilation.MAIN_COMPILATION_NAME
// android compilation which is NOT in tested variant
|| (it as? KotlinJvmAndroidCompilation)?.let { getTestedVariantData(it.androidVariant) == null } == true
}
}.keys
}
kotlinExtension.sourceSets.all { sourceSet ->
(sourceSet.languageSettings as? DefaultLanguageSettingsBuilder)?.run {
// Set ad-hoc free compiler args from the internal project property
freeCompilerArgsProvider = project.provider {
val propertyValue = with(project.extensions.extraProperties) {
val sourceSetFreeCompilerArgsPropertyName = sourceSetFreeCompilerArgsPropertyName(sourceSet.name)
if (has(sourceSetFreeCompilerArgsPropertyName)) {
get(sourceSetFreeCompilerArgsPropertyName)
} else null
}
mutableListOf<String>().apply {
when (propertyValue) {
is String -> add(propertyValue)
is Iterable<*> -> addAll(propertyValue.map { it.toString() })
}
val explicitApiState = project.kotlinExtension.explicitApi?.toCompilerArg()
// do not look into lazy set if explicitApiMode was not enabled
if (explicitApiState != null && sourceSet in sourceSetsInMainCompilation)
add(explicitApiState)
}
}
}
}
}