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
@@ -0,0 +1,9 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.build
val DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS = listOf("kt", "kts")
@@ -21,11 +21,8 @@ import java.io.File
fun File.isJavaFile() =
extension.equals("java", ignoreCase = true)
fun File.isKotlinFile(): Boolean =
extension.let {
"kt".equals(it, ignoreCase = true) ||
"kts".equals(it, ignoreCase = true)
}
fun File.isKotlinFile(sourceFilesExtensions: List<String>): Boolean =
!isJavaFile() && sourceFilesExtensions.any { it.equals(extension, ignoreCase = true) }
fun File.isClassFile(): Boolean =
extension.equals("class", ignoreCase = true)
@@ -10,7 +10,8 @@ import java.io.File
class DirtyFilesContainer(
private val caches: IncrementalCachesManager<*>,
private val reporter: ICReporter
private val reporter: ICReporter,
private val sourceFilesExtensions: List<String>
) {
private val myDirtyFiles = HashSet<File>()
@@ -18,7 +19,7 @@ class DirtyFilesContainer(
ArrayList(myDirtyFiles)
fun add(files: Iterable<File>) {
val existingKotlinFiles = files.filter { it.isKotlinFile() }
val existingKotlinFiles = files.filter { it.isKotlinFile(sourceFilesExtensions) }
if (existingKotlinFiles.isNotEmpty()) {
myDirtyFiles.addAll(existingKotlinFiles)
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.incremental
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
import org.jetbrains.kotlin.build.GeneratedFile
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
@@ -50,6 +51,7 @@ abstract class IncrementalCompilerRunner<
protected val cacheDirectory = File(workingDir, cacheDirName)
protected val dirtySourcesSinceLastTimeFile = File(workingDir, DIRTY_SOURCES_FILE_NAME)
protected val lastBuildInfoFile = File(workingDir, LAST_BUILD_INFO_FILE_NAME)
protected open val kotlinSourceFilesExtensions: List<String> = DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
protected abstract fun isICEnabled(): Boolean
protected abstract fun createCacheManager(args: Args): CacheManager
@@ -85,7 +87,7 @@ abstract class IncrementalCompilerRunner<
if (providedChangedFiles == null) {
caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles)
}
val allKotlinFiles = allSourceFiles.filter { it.isKotlinFile() }
val allKotlinFiles = allSourceFiles.filter { it.isKotlinFile(kotlinSourceFilesExtensions) }
return compileIncrementally(args, caches, allKotlinFiles, CompilationMode.Rebuild(), messageCollector)
}
@@ -281,8 +283,8 @@ abstract class IncrementalCompilerRunner<
changedFiles: ChangedFiles.Known
): DirtyData {
val removedClasses = HashSet<String>()
val dirtyFiles = changedFiles.modified.filterTo(HashSet()) { it.isKotlinFile() }
val removedFiles = changedFiles.removed.filterTo(HashSet()) { it.isKotlinFile() }
val dirtyFiles = changedFiles.modified.filterTo(HashSet()) { it.isKotlinFile(kotlinSourceFilesExtensions) }
val removedFiles = changedFiles.removed.filterTo(HashSet()) { it.isKotlinFile(kotlinSourceFilesExtensions) }
val existingClasses = classesFqNames(dirtyFiles)
val previousClasses = caches.platformCache
@@ -90,7 +90,7 @@ class IncrementalJsCompilerRunner(
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile)
?: return CompilationMode.Rebuild { "No information on previous build" }
val dirtyFiles = DirtyFilesContainer(caches, reporter)
val dirtyFiles = DirtyFilesContainer(caches, reporter, kotlinSourceFilesExtensions)
initDirtyFiles(dirtyFiles, changedFiles)
val libs = (args.libraries ?: "").split(File.pathSeparator).map { File(it) }
@@ -22,6 +22,7 @@ import com.intellij.psi.PsiClass
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.PsiJavaFile
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
import org.jetbrains.kotlin.build.GeneratedFile
import org.jetbrains.kotlin.build.GeneratedJvmClass
import org.jetbrains.kotlin.build.JvmSourceRoot
@@ -40,7 +41,6 @@ import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.multiproject.EmptyModulesApiHistory
import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistory
import org.jetbrains.kotlin.incremental.storage.version.CacheVersionManager
import org.jetbrains.kotlin.incremental.util.Either
import org.jetbrains.kotlin.load.java.JavaClassesTracker
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
@@ -57,8 +57,8 @@ fun makeIncrementally(
messageCollector: MessageCollector = MessageCollector.NONE,
reporter: ICReporter = EmptyICReporter
) {
val kotlinExtensions = listOf("kt", "kts")
val allExtensions = kotlinExtensions + listOf("java")
val kotlinExtensions = DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
val allExtensions = kotlinExtensions + "java"
val rootsWalk = sourceRoots.asSequence().flatMap { it.walk() }
val files = rootsWalk.filter(File::isFile)
val sourceFiles = files.filter { it.extension.toLowerCase() in allExtensions }.toList()
@@ -75,7 +75,8 @@ fun makeIncrementally(
usePreciseJavaTracking = true,
localStateDirs = emptyList(),
buildHistoryFile = buildHistoryFile,
modulesApiHistory = EmptyModulesApiHistory
modulesApiHistory = EmptyModulesApiHistory,
kotlinSourceFilesExtensions = kotlinExtensions
)
compiler.compile(sourceFiles, args, messageCollector, providedChangedFiles = null)
}
@@ -104,9 +105,10 @@ class IncrementalJvmCompilerRunner(
cachesVersionManagers: List<CacheVersionManager>,
reporter: ICReporter,
private val usePreciseJavaTracking: Boolean,
buildHistoryFile: File,
buildHistoryFile: File,
localStateDirs: Collection<File>,
private val modulesApiHistory: ModulesApiHistory
private val modulesApiHistory: ModulesApiHistory,
override val kotlinSourceFilesExtensions: List<String> = DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
) : IncrementalCompilerRunner<K2JVMCompilerArguments, IncrementalJvmCachesManager>(
workingDir,
"caches-jvm",
@@ -145,7 +147,7 @@ class IncrementalJvmCompilerRunner(
changedFiles: ChangedFiles.Known,
args: K2JVMCompilerArguments
): CompilationMode {
val dirtyFiles = DirtyFilesContainer(caches, reporter)
val dirtyFiles = DirtyFilesContainer(caches, reporter, kotlinSourceFilesExtensions)
initDirtyFiles(dirtyFiles, changedFiles)
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile) ?: return CompilationMode.Rebuild { "No information on previous build" }
@@ -204,7 +204,7 @@ abstract class AbstractLookupTrackerTest : TestWithWorkingDir() {
val testDir = File(path)
val workToOriginalFileMap = HashMap(copyTestSources(testDir, srcDir, filePrefix = ""))
var dirtyFiles = srcDir.walk().filterTo(HashSet()) { it.isKotlinFile() }
var dirtyFiles = srcDir.walk().filterTo(HashSet()) { it.isKotlinFile(listOf("kt", "kts")) }
val steps = getModificationsToPerform(testDir, moduleNames = null, allowNoFilesWithSuffixInTestData = true, touchPolicy = TouchPolicy.CHECKSUM)
.filter { it.isNotEmpty() }
@@ -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)
@@ -14,6 +14,7 @@ dependencies {
compileOnly(project(":compiler:cli"))
compile(project(":kotlin-scripting-common"))
compile(project(":kotlin-scripting-jvm"))
compile(project(":kotlin-stdlib"))
compileOnly(project(":kotlin-reflect-api"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(intellijDep()) { includeJars("asm-all") }
@@ -34,6 +35,14 @@ sourceSets {
"test" { projectDefault() }
}
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
kotlinOptions {
languageVersion = "1.2"
apiVersion = "1.2"
freeCompilerArgs += "-Xskip-metadata-version-check"
}
}
val jar = runtimeJar {
from(fileTree("$projectDir/src")) { include("META-INF/**") }
}