Implement support for non-kts scripts in gradle

#KT-26784 fixed
This commit is contained in:
Ilya Chernikov
2018-08-30 13:39:16 +03:00
parent 9ae478e4fc
commit a03bfb3561
18 changed files with 171 additions and 36 deletions
@@ -39,4 +39,6 @@ interface KotlinSourceSet : Named, HasKotlinDependencies {
const val COMMON_MAIN_SOURCE_SET_NAME = "commonMain"
const val COMMON_TEST_SOURCE_SET_NAME = "commonTest"
}
val customSourceFilesExtensions: Iterable<String> // lazy iterable expected
}
@@ -45,6 +45,7 @@ dependencies {
compileOnly project(':kotlin-compiler-runner')
compileOnly project(':kotlin-annotation-processing')
compileOnly project(':kotlin-annotation-processing-gradle')
compileOnly project(':kotlin-scripting-compiler')
compileOnly 'com.android.tools.build:gradle:2.0.0'
compileOnly 'org.codehaus.groovy:groovy-all:2.4.12'
@@ -62,6 +63,7 @@ dependencies {
runtime project(path: ':kotlin-annotation-processing-gradle', configuration: "runtimeJar")
runtime project(path: ':kotlin-android-extensions', configuration: 'runtimeJar')
runtime project(path: ':kotlin-compiler-runner', configuration: 'runtimeJar')
runtime project(path: ':kotlin-scripting-compiler-embeddable', configuration: 'runtimeJar')
runtime project(':kotlin-reflect')
// com.android.tools.build:gradle has ~50 unneeded transitive dependencies
@@ -162,6 +162,7 @@ internal class Kotlin2JvmSourceSetProcessor(
override fun doTargetSpecificProcessing() {
Kapt3KotlinGradleSubplugin.createAptConfigurationIfNeeded(project, kotlinCompilation.compilationName)
ScriptingGradleSubplugin.createDiscoveryConfigurationIfNeeded(project, kotlinCompilation.compilationName)
project.afterEvaluate { project ->
val javaTask = javaSourceSet?.let { project.tasks.findByName(it.compileJavaTaskName) as JavaCompile }
@@ -70,10 +70,12 @@ abstract class AbstractKotlinCompilation(
override val kotlinSourceSets: MutableSet<KotlinSourceSet> = mutableSetOf()
open fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
val compileTask = target.project.tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>
compileTask.source(sourceSet.kotlin)
if (addAsCommonSources) {
compileTask.commonSourceSet += sourceSet.kotlin
(target.project.tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).apply {
source(sourceSet.kotlin)
sourceFilesExtensions(sourceSet.customSourceFilesExtensions)
if (addAsCommonSources) {
commonSourceSet += sourceSet.kotlin
}
}
}
@@ -12,6 +12,7 @@ import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.internal.file.DefaultSourceDirectorySet
import org.gradle.api.internal.file.FileResolver
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder
@@ -53,7 +54,9 @@ class DefaultKotlinSourceSet(
get() = lowerCamelCaseName(runtimeOnlyConfigurationName, METADATA_CONFIGURATION_NAME_SUFFIX)
override val kotlin: SourceDirectorySet = createDefaultSourceDirectorySet(name + " Kotlin source", fileResolver).apply {
filter.include("**/*.java", "**/*.kt", "**/*.kts")
filter.include("**/*.java")
filter.include("**/*.kt")
filter.include("**/*.kts")
}
override val languageSettings: LanguageSettingsBuilder = DefaultLanguageSettingsBuilder()
@@ -89,6 +92,16 @@ class DefaultKotlinSourceSet(
get() = dependsOnSourceSetsImpl
override fun toString(): String = "source set $name"
override val customSourceFilesExtensions: Iterable<String>
get() = Iterable {
kotlin.filter.includes.mapNotNull { pattern ->
pattern.substringAfterLast('.').takeUnless { extension ->
DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS.any { extension.equals(it, ignoreCase = true) }
|| extension.any { it == '\\' || it == '/' }
}
}.iterator()
}
}
private fun KotlinSourceSet.checkForCircularDependencies(): Unit {
@@ -5,18 +5,104 @@
package org.jetbrains.kotlin.gradle.scripting.internal
import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.scripting.ScriptingExtension
import org.jetbrains.kotlin.gradle.tasks.GradleMessageCollector
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptDefinitionsFromClasspathDiscoverySource
import kotlin.properties.Delegates
class ScriptingGradleSubplugin : Plugin<Project> {
companion object {
fun isEnabled(project: Project) = project.plugins.findPlugin(ScriptingGradleSubplugin::class.java) != null
val MAIN_CONFIGURATION_NAME = "kotlinScriptDef"
fun getConfigurationName(sourceSetName: String): String = when (sourceSetName) {
"main" -> MAIN_CONFIGURATION_NAME
else -> "$sourceSetName${MAIN_CONFIGURATION_NAME.capitalize()}"
}
fun createDiscoveryConfigurationIfNeeded(project: Project, sourceSetName: String) {
project.configurations.maybeCreate(getConfigurationName(sourceSetName))
}
}
override fun apply(project: Project) {}
override fun apply(project: Project) {
project.afterEvaluate {
val javaPluginConvention = project.convention.findPlugin(JavaPluginConvention::class.java)
if (javaPluginConvention?.sourceSets?.isEmpty() == false) {
project.tasks.all { task ->
if (task is KotlinCompile) {
val configuration = project.configurations.findByName(getConfigurationName(task.sourceSetName))
if (configuration?.isEmpty == false) {
javaPluginConvention.sourceSets.findByName(task.sourceSetName)?.let { sourceSet ->
val extensionsTask =
project.tasks.create(
"discover${task.sourceSetName.capitalize()}ScriptsExtensions",
DiscoverScriptExtensionsTask::class.java
)
extensionsTask.sourceSet = sourceSet
extensionsTask.discoveryClasspathConfiguration = configuration
extensionsTask.kotlinCompile = task
task.dependsOn.add(extensionsTask)
}
}
}
}
} else {
project.logger.warn("kotlin scripting plugin: applied to a non-JVM project $project")
}
}
}
}
open class DiscoverScriptExtensionsTask : DefaultTask() {
@get:Internal
internal var sourceSet: SourceSet by Delegates.notNull()
@get:Internal
internal var discoveryClasspathConfiguration: Configuration by Delegates.notNull()
@get:Internal
internal var kotlinCompile: KotlinCompile by Delegates.notNull()
override fun getDependsOn(): MutableSet<Any> = kotlinCompile.dependsOn
@TaskAction
@Suppress("unused")
fun findKnownScriptExtensions() {
val scriptingClasspath = discoveryClasspathConfiguration.files.takeIf { it.isNotEmpty() } ?: return
val definitions =
ScriptDefinitionsFromClasspathDiscoverySource(
scriptingClasspath.toList(), emptyMap(),
GradleMessageCollector(project.logger)
).definitions
val extensions = definitions.mapTo(arrayListOf(), KotlinScriptDefinition::fileExtension)
val kotlinSourceSet = sourceSet.getConvention(KOTLIN_DSL_NAME) as? KotlinSourceSet
if (kotlinSourceSet == null) {
project.logger.warn("kotlin scripting plugin: kotlin source set not found: $project.$sourceSet")
} else if (extensions.isNotEmpty()) {
project.logger.info("kotlin scripting plugin: Add new extensions to the sourceset $project.$sourceSet: $extensions")
kotlinSourceSet.kotlin.filter.include(extensions.map { "**/*.$it" })
}
}
}
class ScriptingKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
@@ -40,7 +40,7 @@ internal open class KotlinCompileCommon : AbstractKotlinCompile<K2MetadataCompil
K2MetadataCompilerArguments()
override fun getSourceRoots(): SourceRoots =
SourceRoots.KotlinOnly.create(getSource())
SourceRoots.KotlinOnly.create(getSource(), sourceFilesExtensions)
override fun findKotlinCompilerClasspath(project: Project): List<File> =
findKotlinMetadataCompilerClasspath(project)
@@ -22,8 +22,8 @@ internal sealed class SourceRoots(val kotlinSourceFiles: List<File>) {
class ForJvm(kotlinSourceFiles: List<File>, val javaSourceRoots: Set<File>) : SourceRoots(kotlinSourceFiles) {
companion object {
fun create(taskSource: FileTree, sourceRoots: FilteringSourceRootsContainer): ForJvm {
val kotlinSourceFiles = (taskSource as Iterable<File>).filter(File::isKotlinFile)
fun create(taskSource: FileTree, sourceRoots: FilteringSourceRootsContainer, sourceFilesExtensions: List<String>): ForJvm {
val kotlinSourceFiles = (taskSource as Iterable<File>).filter { it.isKotlinFile(sourceFilesExtensions) }
val javaSourceRoots = findRootsForSources(
sourceRoots.sourceRoots, taskSource.filter(File::isJavaFile))
return ForJvm(kotlinSourceFiles, javaSourceRoots)
@@ -53,7 +53,8 @@ internal sealed class SourceRoots(val kotlinSourceFiles: List<File>) {
class KotlinOnly(kotlinSourceFiles: List<File>) : SourceRoots(kotlinSourceFiles) {
companion object {
fun create(taskSource: FileTree) = KotlinOnly((taskSource as Iterable<File>).filter(File::isKotlinFile))
fun create(taskSource: FileTree, sourceFilesExtensions: List<String>) =
KotlinOnly((taskSource as Iterable<File>).filter { it.isKotlinFile(sourceFilesExtensions) })
}
}
}
@@ -15,6 +15,7 @@ import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
@@ -153,6 +154,16 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
get() = (classpath + additionalClasspath)
.filterTo(LinkedHashSet(), File::exists)
private val sourceFilesExtensionsSources: MutableList<Iterable<String>> = mutableListOf()
@get:Input
val sourceFilesExtensions: List<String>
get() = DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS + sourceFilesExtensionsSources.flatten()
internal fun sourceFilesExtensions(extensions: Iterable<String>) {
sourceFilesExtensionsSources.add(extensions)
}
private val kotlinExt: KotlinProjectExtension
get() = project.extensions.findByType(KotlinProjectExtension::class.java)!!
@@ -360,7 +371,7 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
}
@Internal
override fun getSourceRoots() = SourceRoots.ForJvm.create(getSource(), sourceRootsContainer)
override fun getSourceRoots() = SourceRoots.ForJvm.create(getSource(), sourceRootsContainer, sourceFilesExtensions)
override fun callCompiler(args: K2JVMCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) {
sourceRoots as SourceRoots.ForJvm
@@ -487,7 +498,7 @@ open class Kotlin2JsCompile() : AbstractKotlinCompile<K2JSCompilerArguments>(),
kotlinOptionsImpl.updateArguments(args)
}
override fun getSourceRoots() = SourceRoots.KotlinOnly.create(getSource())
override fun getSourceRoots() = SourceRoots.KotlinOnly.create(getSource(), sourceFilesExtensions)
@get:InputFiles
@get:Optional
@@ -13,11 +13,8 @@ import java.util.*
internal fun File.isJavaFile() =
extension.equals("java", ignoreCase = true)
internal fun File.isKotlinFile(): Boolean =
extension.let {
"kt".equals(it, ignoreCase = true) ||
"kts".equals(it, ignoreCase = true)
}
internal fun File.isKotlinFile(sourceFilesExtensions: List<String>): Boolean =
!isJavaFile() && sourceFilesExtensions.any { it.equals(extension, ignoreCase = true) }
internal fun File.isClassFile(): Boolean =
extension.equals("class", ignoreCase = true)