KT-33908 Make Kotlin Gradle plugin compatible with configuration cache

#KT-33908 Fixed
This commit is contained in:
Nataliya Valtman
2019-10-30 13:22:59 +03:00
committed by nataliya.valtman
parent ea57b4cccb
commit a766369e72
62 changed files with 1629 additions and 824 deletions
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-20200603220033+0000-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
@@ -726,7 +726,7 @@ fun getSomething() = 10
project.build("assemble", options = options) { project.build("assemble", options = options) {
assertFailed() assertFailed()
assertContains("Class 'User' is not abstract and does not implement abstract member public abstract fun writeToParcel") assertContainsRegex("Class 'User' is not abstract and does not implement abstract member public abstract fun (writeToParcel|describeContents)".toRegex())
} }
File(project.projectDir, "app/build.gradle").modify { it.replace("[\"views\"]", "[\"parcelize\", \"views\"]") } File(project.projectDir, "app/build.gradle").modify { it.replace("[\"views\"]", "[\"parcelize\", \"views\"]") }
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.gradle.model.ModelContainer
import org.jetbrains.kotlin.gradle.model.ModelFetcherBuildAction import org.jetbrains.kotlin.gradle.model.ModelFetcherBuildAction
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType
import org.jetbrains.kotlin.gradle.util.* import org.jetbrains.kotlin.gradle.util.*
import org.jetbrains.kotlin.test.util.trimTrailingWhitespaces
import org.junit.After import org.junit.After
import org.junit.AfterClass import org.junit.AfterClass
import org.junit.Assert import org.junit.Assert
@@ -22,6 +21,8 @@ import java.io.File
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlin.test.* import kotlin.test.*
import org.jetbrains.kotlin.test.util.trimTrailingWhitespaces
val SYSTEM_LINE_SEPARATOR: String = System.getProperty("line.separator") val SYSTEM_LINE_SEPARATOR: String = System.getProperty("line.separator")
abstract class BaseGradleIT { abstract class BaseGradleIT {
@@ -205,9 +206,15 @@ abstract class BaseGradleIT {
val withBuildCache: Boolean = false, val withBuildCache: Boolean = false,
val kaptOptions: KaptOptions? = null, val kaptOptions: KaptOptions? = null,
val parallelTasksInProject: Boolean? = null, val parallelTasksInProject: Boolean? = null,
val jsCompilerType: KotlinJsCompilerType? = null val jsCompilerType: KotlinJsCompilerType? = null,
val configurationCache: Boolean = false,
val configurationCacheProblems: ConfigurationCacheProblems = ConfigurationCacheProblems.FAIL
) )
enum class ConfigurationCacheProblems {
FAIL, WARN
}
data class KaptOptions( data class KaptOptions(
val verbose: Boolean, val verbose: Boolean,
val useWorkers: Boolean, val useWorkers: Boolean,
@@ -707,9 +714,9 @@ Finished executing task ':$taskName'|
val xmlString = buildString { val xmlString = buildString {
appendln("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") appendln("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
appendln("<results>") appendln("<results>")
files.forEach { files.forEach { file ->
appendln( appendln(
it.readText() file.readText()
.trimTrailingWhitespaces() .trimTrailingWhitespaces()
.replace(projectDir.absolutePath, "/\$PROJECT_DIR$") .replace(projectDir.absolutePath, "/\$PROJECT_DIR$")
.replace(projectDir.name, "\$PROJECT_NAME$") .replace(projectDir.name, "\$PROJECT_NAME$")
@@ -801,6 +808,9 @@ Finished executing task ':$taskName'|
add("-Pkotlin.js.compiler=$it") add("-Pkotlin.js.compiler=$it")
} }
add("-Dorg.gradle.unsafe.configuration-cache=${options.configurationCache}")
add("-Dorg.gradle.unsafe.configuration-cache-problems=${options.configurationCacheProblems.name.toLowerCase()}")
// Workaround: override a console type set in the user machine gradle.properties (since Gradle 4.3): // Workaround: override a console type set in the user machine gradle.properties (since Gradle 4.3):
add("--console=plain") add("--console=plain")
addAll(options.freeCommandLineArgs) addAll(options.freeCommandLineArgs)
@@ -0,0 +1,73 @@
/*
* 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
import org.jetbrains.kotlin.gradle.util.AGPVersion
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.junit.Test
class ConfigurationCacheForAndroidIT : AbstractConfigurationCacheIT() {
private val androidGradlePluginVersion: AGPVersion
get() = AGPVersion.v4_2_0
override fun defaultBuildOptions() =
super.defaultBuildOptions().copy(
androidHome = KotlinTestUtils.findAndroidSdk(),
androidGradlePluginVersion = androidGradlePluginVersion,
configurationCache = true,
/* AGP causes a configuration cache problem:
- plugin 'com.android.internal.application': registration of listener on 'TaskExecutionGraph.addTaskExecutionListener' is unsupported
which causes tests to fail when configuration-cache-problems=fail is used.
However, everything works fine with WARN level reporting.
TODO: switch to FAIL when AGP no longer causes the cache problem
*/
configurationCacheProblems = ConfigurationCacheProblems.WARN
)
@Test
fun testAndroidKaptProject() = with(Project("android-dagger", directoryPrefix = "kapt2")) {
applyAndroid40Alpha4KotlinVersionWorkaround()
projectDir.resolve("gradle.properties").appendText("\nkapt.incremental.apt=false")
testConfigurationCacheOf(":app:compileDebugKotlin", ":app:kaptDebugKotlin", ":app:kaptGenerateStubsDebugKotlin")
}
@Test
fun testKotlinAndroidProject() = with(Project("AndroidProject")) {
applyAndroid40Alpha4KotlinVersionWorkaround()
testConfigurationCacheOf(":Lib:compileFlavor1DebugKotlin", ":Android:compileFlavor1DebugKotlin")
}
/**
* Android Gradle plugin 4.0-alpha4 depends on the EAP versions of some o.j.k modules.
* Force the current Kotlin version, so the EAP versions are not queried from the
* test project's repositories, where there's no 'kotlin-eap' repo.
* TODO remove this workaround once an Android Gradle plugin version is used that depends on the stable Kotlin version
*/
private fun Project.applyAndroid40Alpha4KotlinVersionWorkaround() {
setupWorkingDir()
val resolutionStrategyHack = """
configurations.all {
resolutionStrategy.dependencySubstitution.all { dependency ->
def requested = dependency.requested
if (requested instanceof ModuleComponentSelector && requested.group == 'org.jetbrains.kotlin') {
dependency.useTarget requested.group + ':' + requested.module + ':' + '${defaultBuildOptions().kotlinVersion}'
}
}
}
""".trimIndent()
gradleBuildScript().appendText(
"\n" + """
buildscript {
$resolutionStrategyHack
}
$resolutionStrategyHack
""".trimIndent()
)
}
}
@@ -0,0 +1,118 @@
/*
* 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
import org.jetbrains.kotlin.gradle.util.findFileByName
import org.junit.Test
import java.io.File
import java.net.URI
import java.util.Arrays.asList
import kotlin.test.fail
class ConfigurationCacheIT : AbstractConfigurationCacheIT() {
@Test
fun testSimpleKotlinJvmProject() = with(Project("kotlinProject")) {
testConfigurationCacheOf(":compileKotlin")
}
@Test
fun testIncrementalKaptProject() = with(Project("kaptIncrementalCompilationProject")) {
setupIncrementalAptProject("AGGREGATING")
testConfigurationCacheOf(
":compileKotlin",
":kaptKotlin",
buildOptions = defaultBuildOptions().copy(
incremental = true,
kaptOptions = KaptOptions(
verbose = true,
useWorkers = true,
incrementalKapt = true,
includeCompileClasspath = false
)
)
)
}
@Test
fun testInstantExecution() = with(Project("instantExecution")) {
testConfigurationCacheOf("assemble", executedTaskNames = asList(":lib-project:compileKotlin"))
}
@Test
fun testInstantExecutionForJs() = with(Project("instantExecutionToJs")) {
testConfigurationCacheOf("assemble", executedTaskNames = asList(":compileKotlin2Js"))
}
}
abstract class AbstractConfigurationCacheIT : BaseGradleIT() {
override fun defaultBuildOptions() =
super.defaultBuildOptions().copy(configurationCache = true)
override val defaultGradleVersion: GradleVersionRequired = GradleVersionRequired.AtLeast("6.6-rc-3")
protected fun Project.testConfigurationCacheOf(
vararg taskNames: String,
executedTaskNames: List<String>? = null,
buildOptions: BuildOptions = defaultBuildOptions()
) {
// First, run a build that serializes the tasks state for instant execution in further builds
val executedTask: List<String> = executedTaskNames ?: taskNames.toList()
build(*taskNames, options = buildOptions) {
assertSuccessful()
assertTasksExecuted(executedTask)
assertContains("Calculating task graph as no configuration cache is available for tasks: ${taskNames.joinToString(separator = " ")}")
checkInstantExecutionSucceeded()
}
build("clean") {
assertSuccessful()
}
// Then run a build where tasks states are deserialized to check that they work correctly in this mode
build(*taskNames, options = buildOptions) {
assertSuccessful()
assertTasksExecuted(executedTask)
assertContains("Reusing configuration cache.")
}
build(*taskNames, options = buildOptions) {
assertSuccessful()
assertTasksUpToDate(executedTask)
}
}
private fun Project.checkInstantExecutionSucceeded() {
instantExecutionReportFile()?.let { htmlReportFile ->
fail("Instant execution problems were found, check ${htmlReportFile.asClickableFileUrl()} for details.")
}
}
/**
* Copies all files from the directory containing the given [htmlReportFile] to a
* fresh temp dir and returns a reference to the copied [htmlReportFile] in the new
* directory.
*/
private fun copyReportToTempDir(htmlReportFile: File): File =
createTempDir().let { tempDir ->
htmlReportFile.parentFile.copyRecursively(tempDir)
tempDir.resolve(htmlReportFile.name)
}
/**
* The instant execution report file, if exists, indicates problems were
* found while caching the task graph.
*/
private fun Project.instantExecutionReportFile() = projectDir
.resolve("configuration-cache")
.findFileByName("configuration-cache-report.html")
?.let { copyReportToTempDir(it) }
private fun File.asClickableFileUrl(): String =
URI("file", "", toURI().path, null, null).toString()
}
@@ -1,149 +0,0 @@
/*
* Copyright 2010-2019 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
import org.jetbrains.kotlin.gradle.util.AGPVersion
import org.jetbrains.kotlin.gradle.util.findFileByName
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.junit.Test
import java.io.File
import java.net.URI
import kotlin.test.fail
class InstantExecutionIT : BaseGradleIT() {
private val androidGradlePluginVersion: AGPVersion
get() = AGPVersion.v4_0_ALPHA_8
override fun defaultBuildOptions() =
super.defaultBuildOptions().copy(
androidHome = KotlinTestUtils.findAndroidSdk(),
androidGradlePluginVersion = androidGradlePluginVersion
)
private val minimumGradleVersion = GradleVersionRequired.AtLeast("6.1-rc-3")
@Test
fun testSimpleKotlinJvmProject() = with(Project("kotlinProject", minimumGradleVersion)) {
testInstantExecutionOf(":compileKotlin")
}
@Test
fun testSimpleKotlinAndroidProject() = with(Project("android-dagger", minimumGradleVersion, "kapt2")) {
applyAndroid40Alpha4KotlinVersionWorkaround()
projectDir.resolve("gradle.properties").appendText("\nkapt.incremental.apt=false")
testInstantExecutionOf(":app:compileDebugKotlin", ":app:kaptDebugKotlin", ":app:kaptGenerateStubsDebugKotlin")
}
@Test
fun testIncrementalKaptProject() = with(getIncrementalKaptProject()) {
testInstantExecutionOf(
":compileKotlin",
":kaptKotlin",
buildOptions = defaultBuildOptions().copy(
incremental = true,
kaptOptions = KaptOptions(
verbose = true,
useWorkers = true,
incrementalKapt = true,
includeCompileClasspath = false
)
)
)
}
private fun getIncrementalKaptProject() =
Project("kaptIncrementalCompilationProject", minimumGradleVersion).apply {
setupIncrementalAptProject("AGGREGATING")
}
private fun Project.testInstantExecutionOf(vararg taskNames: String, buildOptions: BuildOptions = defaultBuildOptions()) {
// First, run a build that serializes the tasks state for instant execution in further builds
instantExecutionOf(*taskNames, buildOptions = buildOptions) {
assertSuccessful()
assertTasksExecuted(*taskNames)
checkInstantExecutionSucceeded()
}
build("clean") {
assertSuccessful()
}
// Then run a build where tasks states are deserialized to check that they work correctly in this mode
instantExecutionOf(*taskNames, buildOptions = buildOptions) {
assertSuccessful()
assertTasksExecuted(*taskNames)
}
instantExecutionOf(*taskNames, buildOptions = buildOptions) {
assertSuccessful()
assertTasksUpToDate(*taskNames)
}
}
private fun Project.checkInstantExecutionSucceeded() {
instantExecutionReportFile()?.let { htmlReportFile ->
fail("Instant execution problems were found, check ${htmlReportFile.asClickableFileUrl()} for details.")
}
}
private fun Project.instantExecutionOf(
vararg tasks: String,
buildOptions: BuildOptions = defaultBuildOptions(),
check: CompiledProject.() -> Unit
) =
build("-Dorg.gradle.unsafe.instant-execution=true", *tasks, options = buildOptions, check = check)
/**
* Copies all files from the directory containing the given [htmlReportFile] to a
* fresh temp dir and returns a reference to the copied [htmlReportFile] in the new
* directory.
*/
private fun copyReportToTempDir(htmlReportFile: File): File =
createTempDir().let { tempDir ->
htmlReportFile.parentFile.copyRecursively(tempDir)
tempDir.resolve(htmlReportFile.name)
}
/**
* The instant execution report file, if exists, indicates problems were
* found while caching the task graph.
*/
private fun Project.instantExecutionReportFile() = projectDir
.resolve(".instant-execution-state")
.findFileByName("instant-execution-report.html")
?.let { copyReportToTempDir(it) }
private fun File.asClickableFileUrl(): String =
URI("file", "", toURI().path, null, null).toString()
/**
* Android Gradle plugin 4.0-alpha4 depends on the EAP versions of some o.j.k modules.
* Force the current Kotlin version, so the EAP versions are not queried from the
* test project's repositories, where there's no 'kotlin-eap' repo.
* TODO remove this workaround once an Android Gradle plugin version is used that depends on the stable Kotlin version
*/
private fun Project.applyAndroid40Alpha4KotlinVersionWorkaround() {
setupWorkingDir()
val resolutionStrategyHack = """
configurations.all {
resolutionStrategy.dependencySubstitution.all { dependency ->
def requested = dependency.requested
if (requested instanceof ModuleComponentSelector && requested.group == 'org.jetbrains.kotlin') {
dependency.useTarget requested.group + ':' + requested.module + ':' + '${defaultBuildOptions().kotlinVersion}'
}
}
}
""".trimIndent()
gradleBuildScript().appendText("\n" + """
buildscript {
$resolutionStrategyHack
}
$resolutionStrategyHack
""".trimIndent())
}
}
@@ -365,7 +365,7 @@ class KotlinGradleIT : BaseGradleIT() {
} }
@Test @Test
fun testDowngradeTo106() { fun testDowngradePluginVersion() {
val project = Project("kotlinProject") val project = Project("kotlinProject")
val options = defaultBuildOptions().copy(incremental = true, withDaemon = false) val options = defaultBuildOptions().copy(incremental = true, withDaemon = false)
@@ -373,7 +373,7 @@ class KotlinGradleIT : BaseGradleIT() {
assertSuccessful() assertSuccessful()
} }
project.build("clean", "assemble", options = options.copy(kotlinVersion = "1.0.6")) { project.build("clean", "assemble", options = options.copy(kotlinVersion = "1.3.41")) {
assertSuccessful() assertSuccessful()
} }
} }
@@ -78,7 +78,9 @@ class UpToDateIT : BaseGradleIT() {
val originalPaths get() = originalCompilerCp.map { it.replace("\\", "/") }.joinToString(", ") { "'$it'" } val originalPaths get() = originalCompilerCp.map { it.replace("\\", "/") }.joinToString(", ") { "'$it'" }
override fun initProject(project: Project) = with(project) { override fun initProject(project: Project) = with(project) {
buildGradle.appendText("\nprintln 'compiler_cp=' + compileKotlin.getComputedCompilerClasspath\$kotlin_gradle_plugin()") buildGradle.appendText(
"\nafterEvaluate { println 'compiler_cp=' + compileKotlin.getComputedCompilerClasspath\$kotlin_gradle_plugin() }"
)
build("clean") { originalCompilerCp = "compiler_cp=\\[(.*)]".toRegex().find(output)!!.groupValues[1].split(", ") } build("clean") { originalCompilerCp = "compiler_cp=\\[(.*)]".toRegex().find(output)!!.groupValues[1].split(", ") }
buildGradle.appendText("""${'\n'} buildGradle.appendText("""${'\n'}
// Add Kapt to the project to test its input checks as well: // Add Kapt to the project to test its input checks as well:
@@ -24,6 +24,7 @@ class AGPVersion private constructor(private val versionNumber: VersionNumber) {
val v3_3_2 = fromString("3.3.2") val v3_3_2 = fromString("3.3.2")
val v3_4_1 = fromString("3.4.1") val v3_4_1 = fromString("3.4.1")
val v3_6_0 = fromString("3.6.0") val v3_6_0 = fromString("3.6.0")
val v4_0_ALPHA_8 = fromString("4.0.0-alpha08") val v4_1_0 = fromString("4.1.0-beta02")
val v4_2_0 = fromString("4.2.0-alpha07")
} }
} }
@@ -0,0 +1,16 @@
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
subprojects {
repositories {
mavenLocal()
mavenCentral()
}
}
@@ -0,0 +1,5 @@
apply plugin: 'kotlin'
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
@@ -0,0 +1,10 @@
/*
* 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 libproject
class Util {
val projectName: String = "lib-project"
}
@@ -0,0 +1,6 @@
apply plugin: 'kotlin'
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile project(':lib-project')
}
@@ -0,0 +1,10 @@
import libProject.Util
/*
* 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.
*/
fun main(args: Array<String>) {
println(Util.projectName)
}
@@ -0,0 +1 @@
include ':main-project', ':lib-project'
@@ -0,0 +1,37 @@
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin2js'
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-test-js:$kotlin_version"
}
task jarSources(type: Jar) {
from sourceSets.main.allSource
classifier = 'source'
}
artifacts {
compile jarSources
}
compileKotlin2Js.kotlinOptions.outputFile = "${buildDir}/kotlin2js/main/module.js"
compileTestKotlin2Js.kotlinOptions.outputFile = "${buildDir}/kotlin2js/test/module-tests.js"
if (project.findProperty("kotlin.js.useIrBackend")?.toBoolean() == true) {
compileKotlin2Js.kotlinOptions.freeCompilerArgs += ["-Xir-produce-klib-dir", "-Xir-only"]
compileTestKotlin2Js.kotlinOptions.freeCompilerArgs += ["-Xir-produce-js"]
}
@@ -0,0 +1,10 @@
/*
* 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 project
class Main {
fun value(): String = "instant"
}
@@ -0,0 +1,13 @@
/*
* 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.
*/
import project.Main
import kotlin.test.*
class MainTest {
@Test
fun mySimpleTest() {
assertEquals("instant", Main().value())
}
}
@@ -2,12 +2,12 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
android { android {
compileSdkVersion 29 compileSdkVersion 26
buildToolsVersion "29.0.2" buildToolsVersion "28.0.3"
defaultConfig { defaultConfig {
minSdkVersion 21 minSdkVersion 26
targetSdkVersion 29 targetSdkVersion 26
} }
} }
@@ -2,12 +2,12 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
android { android {
compileSdkVersion 29 compileSdkVersion 26
buildToolsVersion "29.0.2" buildToolsVersion "28.0.3"
defaultConfig { defaultConfig {
minSdkVersion 21 minSdkVersion 26
targetSdkVersion 29 targetSdkVersion 26
} }
} }
@@ -5,17 +5,18 @@
package org.jetbrains.kotlin.compilerRunner package org.jetbrains.kotlin.compilerRunner
import org.gradle.api.Task
import org.gradle.workers.IsolationMode import org.gradle.workers.IsolationMode
import org.gradle.workers.WorkerExecutor import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.gradle.logging.kotlinDebug import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.tasks.GradleCompileTaskProvider
internal class GradleCompilerRunnerWithWorkers( internal class GradleCompilerRunnerWithWorkers(
task: Task, taskProvider: GradleCompileTaskProvider,
private val workersExecutor: WorkerExecutor private val workersExecutor: WorkerExecutor
) : GradleCompilerRunner(task) { ) : GradleCompilerRunner(taskProvider) {
override fun runCompilerAsync(workArgs: GradleKotlinCompilerWorkArguments) { override fun runCompilerAsync(workArgs: GradleKotlinCompilerWorkArguments) {
task.logger.kotlinDebug { "Starting Kotlin compiler work from task '${task.path}'" } loggerProvider.kotlinDebug { "Starting Kotlin compiler work from task '${pathProvider}'" }
// todo: write tests with Workers enabled; // todo: write tests with Workers enabled;
workersExecutor.submit(GradleKotlinCompilerWork::class.java) { config -> workersExecutor.submit(GradleKotlinCompilerWork::class.java) { config ->
config.isolationMode = IsolationMode.NONE config.isolationMode = IsolationMode.NONE
@@ -6,8 +6,8 @@
package org.jetbrains.kotlin.compilerRunner package org.jetbrains.kotlin.compilerRunner
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.invocation.Gradle import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logger
import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.bundling.AbstractArchiveTask import org.gradle.api.tasks.bundling.AbstractArchiveTask
import org.gradle.jvm.tasks.Jar import org.gradle.jvm.tasks.Jar
@@ -24,11 +24,12 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
import org.jetbrains.kotlin.gradle.plugin.mpp.ownModuleName import org.jetbrains.kotlin.gradle.plugin.mpp.ownModuleName
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.tasks.GradleCompileTaskProvider
import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTaskData import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTaskData
import org.jetbrains.kotlin.gradle.tasks.locateTask import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.utils.archivePathCompatible import org.jetbrains.kotlin.gradle.utils.archivePathCompatible
import org.jetbrains.kotlin.gradle.utils.newTmpFile import org.jetbrains.kotlin.gradle.utils.newTmpFile
import org.jetbrains.kotlin.gradle.utils.relativeToRoot import org.jetbrains.kotlin.gradle.utils.relativeOrCanonical
import org.jetbrains.kotlin.incremental.IncrementalModuleEntry import org.jetbrains.kotlin.incremental.IncrementalModuleEntry
import org.jetbrains.kotlin.incremental.IncrementalModuleInfo import org.jetbrains.kotlin.incremental.IncrementalModuleInfo
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
@@ -50,9 +51,21 @@ const val COULD_NOT_CONNECT_TO_DAEMON_MESSAGE = "Could not connect to Kotlin com
internal fun kotlinCompilerExecutionStrategy(): String = internal fun kotlinCompilerExecutionStrategy(): String =
System.getProperty(KOTLIN_COMPILER_EXECUTION_STRATEGY_PROPERTY) ?: DAEMON_EXECUTION_STRATEGY System.getProperty(KOTLIN_COMPILER_EXECUTION_STRATEGY_PROPERTY) ?: DAEMON_EXECUTION_STRATEGY
internal open class GradleCompilerRunner(protected val task: Task) { /*
protected val project: Project Using real taskProvider cause "field 'taskProvider' from type 'org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner':
get() = task.project value 'fixed(class org.jetbrains.kotlin.gradle.tasks.KotlinCompile_Decorated, task ':compileKotlin')'
is not assignable to 'org.gradle.api.tasks.TaskProvider'" exception
*/
internal open class GradleCompilerRunner(protected val taskProvider: GradleCompileTaskProvider) {
internal val pathProvider = taskProvider.path
internal val loggerProvider = taskProvider.logger
internal val buildDirProvider = taskProvider.buildDir
internal val projectDirProvider = taskProvider.projectDir
internal val projectRootDirProvider = taskProvider.rootDir
internal val sessionDirProvider = taskProvider.sessionsDir
internal val projectNameProvider = taskProvider.projectName
internal val incrementalModuleInfoProvider = taskProvider.buildModulesInfo
/** /**
* Compiler might be executed asynchronously. Do not do anything requiring end of compilation after this function is called. * Compiler might be executed asynchronously. Do not do anything requiring end of compilation after this function is called.
@@ -107,7 +120,7 @@ internal open class GradleCompilerRunner(protected val task: Task) {
environment: GradleCompilerEnvironment environment: GradleCompilerEnvironment
) { ) {
if (compilerArgs.version) { if (compilerArgs.version) {
task.logger.lifecycle( loggerProvider.lifecycle(
"Kotlin version " + loadCompilerVersion(environment.compilerClasspath) + "Kotlin version " + loadCompilerVersion(environment.compilerClasspath) +
" (JRE " + System.getProperty("java.runtime.version") + ")" " (JRE " + System.getProperty("java.runtime.version") + ")"
) )
@@ -128,9 +141,16 @@ internal open class GradleCompilerRunner(protected val task: Task) {
} }
val incrementalCompilationEnvironment = environment.incrementalCompilationEnvironment val incrementalCompilationEnvironment = environment.incrementalCompilationEnvironment
val modulesInfo = incrementalCompilationEnvironment?.let { buildModulesInfo(project.gradle) } val modulesInfo = incrementalCompilationEnvironment?.let { incrementalModuleInfoProvider }
val workArgs = GradleKotlinCompilerWorkArguments( val workArgs = GradleKotlinCompilerWorkArguments(
projectFiles = ProjectFilesForCompilation(project), projectFiles = ProjectFilesForCompilation(
loggerProvider,
projectDirProvider,
buildDirProvider,
projectNameProvider,
projectRootDirProvider,
sessionDirProvider
),
compilerFullClasspath = environment.compilerFullClasspath, compilerFullClasspath = environment.compilerFullClasspath,
compilerClassName = compilerClassName, compilerClassName = compilerClassName,
compilerArgs = argsArray, compilerArgs = argsArray,
@@ -138,12 +158,12 @@ internal open class GradleCompilerRunner(protected val task: Task) {
incrementalCompilationEnvironment = incrementalCompilationEnvironment, incrementalCompilationEnvironment = incrementalCompilationEnvironment,
incrementalModuleInfo = modulesInfo, incrementalModuleInfo = modulesInfo,
outputFiles = environment.outputFiles.toList(), outputFiles = environment.outputFiles.toList(),
taskPath = task.path, taskPath = pathProvider,
buildReportMode = environment.buildReportMode, buildReportMode = environment.buildReportMode,
kotlinScriptExtensions = environment.kotlinScriptExtensions, kotlinScriptExtensions = environment.kotlinScriptExtensions,
allWarningsAsErrors = compilerArgs.allWarningsAsErrors allWarningsAsErrors = compilerArgs.allWarningsAsErrors
) )
TaskLoggers.put(task.path, task.logger) TaskLoggers.put(pathProvider, loggerProvider)
runCompilerAsync(workArgs) runCompilerAsync(workArgs)
} }
@@ -221,7 +241,7 @@ internal open class GradleCompilerRunner(protected val task: Task) {
} else { } else {
if (target is KotlinWithJavaTarget<*>) { if (target is KotlinWithJavaTarget<*>) {
val jar = project.tasks.getByName(target.artifactsTaskName) as Jar val jar = project.tasks.getByName(target.artifactsTaskName) as Jar
jarToClassListFile[jar.archivePathCompatible.canonicalFile] = target.defaultArtifactClassesListFile jarToClassListFile[jar.archivePathCompatible.canonicalFile] = target.defaultArtifactClassesListFile.get()
} }
} }
} }
@@ -261,11 +281,9 @@ internal open class GradleCompilerRunner(protected val task: Task) {
private var clientIsAliveFlagFile: File? = null private var clientIsAliveFlagFile: File? = null
@Synchronized @Synchronized
internal fun getOrCreateClientFlagFile(project: Project): File { internal fun getOrCreateClientFlagFile(log: Logger, projectName: String): File {
val log = project.logger
if (clientIsAliveFlagFile == null || !clientIsAliveFlagFile!!.exists()) { if (clientIsAliveFlagFile == null || !clientIsAliveFlagFile!!.exists()) {
val projectName = project.rootProject.name.normalizeForFlagFile() clientIsAliveFlagFile = newTmpFile(prefix = "kotlin-compiler-in-${projectName}-", suffix = ".alive")
clientIsAliveFlagFile = newTmpFile(prefix = "kotlin-compiler-in-$projectName-", suffix = ".alive")
log.kotlinDebug { CREATED_CLIENT_FILE_PREFIX + clientIsAliveFlagFile!!.canonicalPath } log.kotlinDebug { CREATED_CLIENT_FILE_PREFIX + clientIsAliveFlagFile!!.canonicalPath }
} else { } else {
log.kotlinDebug { EXISTING_CLIENT_FILE_PREFIX + clientIsAliveFlagFile!!.canonicalPath } log.kotlinDebug { EXISTING_CLIENT_FILE_PREFIX + clientIsAliveFlagFile!!.canonicalPath }
@@ -274,7 +292,7 @@ internal open class GradleCompilerRunner(protected val task: Task) {
return clientIsAliveFlagFile!! return clientIsAliveFlagFile!!
} }
private fun String.normalizeForFlagFile(): String { internal fun String.normalizeForFlagFile(): String {
val validChars = ('a'..'z') + ('0'..'9') + "-_" val validChars = ('a'..'z') + ('0'..'9') + "-_"
return filter { it in validChars } return filter { it in validChars }
} }
@@ -285,14 +303,13 @@ internal open class GradleCompilerRunner(protected val task: Task) {
// session files are deleted at org.jetbrains.kotlin.gradle.plugin.KotlinGradleBuildServices.buildFinished // session files are deleted at org.jetbrains.kotlin.gradle.plugin.KotlinGradleBuildServices.buildFinished
@Synchronized @Synchronized
internal fun getOrCreateSessionFlagFile(project: Project): File { internal fun getOrCreateSessionFlagFile(log: Logger, sessionsDir: File, projectRootDir: File): File {
val log = project.logger
if (sessionFlagFile == null || !sessionFlagFile!!.exists()) { if (sessionFlagFile == null || !sessionFlagFile!!.exists()) {
val sessionFilesDir = sessionsDir(project).apply { mkdirs() } val sessionFilesDir = sessionsDir.apply { mkdirs() }
sessionFlagFile = newTmpFile(prefix = "kotlin-compiler-", suffix = ".salive", directory = sessionFilesDir) sessionFlagFile = newTmpFile(prefix = "kotlin-compiler-", suffix = ".salive", directory = sessionFilesDir)
log.kotlinDebug { CREATED_SESSION_FILE_PREFIX + sessionFlagFile!!.relativeToRoot(project) } log.kotlinDebug { CREATED_SESSION_FILE_PREFIX + sessionFlagFile!!.relativeOrCanonical(projectRootDir) }
} else { } else {
log.kotlinDebug { EXISTING_SESSION_FILE_PREFIX + sessionFlagFile!!.relativeToRoot(project) } log.kotlinDebug { EXISTING_SESSION_FILE_PREFIX + sessionFlagFile!!.relativeOrCanonical(projectRootDir) }
} }
return sessionFlagFile!! return sessionFlagFile!!
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.compilerRunner package org.jetbrains.kotlin.compilerRunner
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.logging.Logger
import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.config.Services
@@ -35,11 +36,12 @@ internal class ProjectFilesForCompilation(
val sessionFlagFile: File, val sessionFlagFile: File,
val buildDir: File val buildDir: File
) : Serializable { ) : Serializable {
constructor(project: Project) : this( //TODO
projectRootFile = project.rootProject.projectDir, constructor(logger: Logger, projectDir:File, buildDir: File, prjectName: String, projectRootDir: File, sessionDir: File) : this(
clientIsAliveFlagFile = GradleCompilerRunner.getOrCreateClientFlagFile(project), projectRootFile = projectDir,
sessionFlagFile = GradleCompilerRunner.getOrCreateSessionFlagFile(project), clientIsAliveFlagFile = GradleCompilerRunner.getOrCreateClientFlagFile(logger, prjectName),
buildDir = project.buildDir sessionFlagFile = GradleCompilerRunner.getOrCreateSessionFlagFile(logger, sessionDir, projectRootDir),
buildDir = buildDir
) )
companion object { companion object {
@@ -5,13 +5,15 @@
package org.jetbrains.kotlin.gradle.internal package org.jetbrains.kotlin.gradle.internal
import org.gradle.api.tasks.TaskProvider import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.* import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.gradle.dsl.Coroutines
import org.jetbrains.kotlin.gradle.dsl.fillDefaultValues
import org.jetbrains.kotlin.gradle.logging.kotlinDebug import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompileArgumentsProvider
import org.jetbrains.kotlin.gradle.utils.getValue import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompilerArgumentsProvider
import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray
import org.jetbrains.kotlin.incremental.classpathAsList import org.jetbrains.kotlin.incremental.classpathAsList
import org.jetbrains.kotlin.incremental.destinationAsFile import org.jetbrains.kotlin.incremental.destinationAsFile
@@ -38,22 +40,20 @@ internal fun compilerArgumentsConfigurationFlags(defaultsOnly: Boolean, ignoreCl
* but outside the tasks, so that this state & logic can be reused without referencing the task directly. */ * but outside the tasks, so that this state & logic can be reused without referencing the task directly. */
internal open class AbstractKotlinCompileArgumentsContributor<T : CommonCompilerArguments>( internal open class AbstractKotlinCompileArgumentsContributor<T : CommonCompilerArguments>(
// Don't save this reference into a property! That would be hostile to Gradle instant execution // Don't save this reference into a property! That would be hostile to Gradle instant execution
taskProvider: TaskProvider<out AbstractKotlinCompile<T>> taskProvider: KotlinCompileArgumentsProvider<out AbstractKotlinCompile<T>>
) : CompilerArgumentsContributor<T> { ) : CompilerArgumentsContributor<T> {
private val coroutines by taskProvider.map { it.coroutines }
protected val logger by taskProvider.map { it.logger } private val coroutines = taskProvider.coroutines
protected val logger = taskProvider.logger
private val isMultiplatform by taskProvider.map { it.isMultiplatform } private val isMultiplatform = taskProvider.isMultiplatform
private val pluginClasspath = taskProvider.pluginClasspath
private val pluginClasspath by taskProvider.map { it.pluginClasspath } private val pluginOptions = taskProvider.pluginOptions
private val pluginOptions by taskProvider.map { it.pluginOptions }
override fun contributeArguments( override fun contributeArguments(
args: T, args: T,
flags: Collection<CompilerArgumentsConfigurationFlag> flags: Collection<CompilerArgumentsConfigurationFlag>
) { ) {
args.coroutinesState = when (coroutines) { args.coroutinesState = when (coroutines.get()) {
Coroutines.ENABLE -> CommonCompilerArguments.ENABLE Coroutines.ENABLE -> CommonCompilerArguments.ENABLE
Coroutines.WARN -> CommonCompilerArguments.WARN Coroutines.WARN -> CommonCompilerArguments.WARN
Coroutines.ERROR -> CommonCompilerArguments.ERROR Coroutines.ERROR -> CommonCompilerArguments.ERROR
@@ -79,23 +79,14 @@ internal open class AbstractKotlinCompileArgumentsContributor<T : CommonCompiler
internal open class KotlinJvmCompilerArgumentsContributor( internal open class KotlinJvmCompilerArgumentsContributor(
// Don't save this reference into a property! That would be hostile to Gradle instant execution. Only map it to the task properties. // Don't save this reference into a property! That would be hostile to Gradle instant execution. Only map it to the task properties.
taskProvider: TaskProvider<out KotlinCompile> taskProvider: KotlinJvmCompilerArgumentsProvider
) : AbstractKotlinCompileArgumentsContributor<K2JVMCompilerArguments>(taskProvider) { ) : AbstractKotlinCompileArgumentsContributor<K2JVMCompilerArguments>(taskProvider) {
private val moduleName by taskProvider.map { it.moduleName } private val moduleName = taskProvider.moduleName
private val friendPaths = taskProvider.friendPaths
private val friendPaths by taskProvider.map { it.friendPaths } private val compileClasspath = taskProvider.compileClasspath
private val destinationDir = taskProvider.destinationDir
private val compileClasspath by taskProvider.map { it.compileClasspath } private val kotlinOptions = taskProvider.kotlinOptions
private val destinationDir by taskProvider.map { it.destinationDir }
private val kotlinOptions by taskProvider.map {
listOfNotNull(
it.parentKotlinOptionsImpl as KotlinJvmOptionsImpl?,
it.kotlinOptions as KotlinJvmOptionsImpl
)
}
override fun contributeArguments( override fun contributeArguments(
args: K2JVMCompilerArguments, args: K2JVMCompilerArguments,
@@ -121,6 +112,6 @@ internal open class KotlinJvmCompilerArgumentsContributor(
} }
args.destinationAsFile = destinationDir args.destinationAsFile = destinationDir
kotlinOptions.forEach { it.updateArguments(args) } kotlinOptions.forEach { it?.updateArguments(args) }
} }
} }
@@ -10,6 +10,7 @@ import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.api.BaseVariant import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.SourceKind import com.android.build.gradle.api.SourceKind
import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.SystemInfo
import com.intellij.util.lang.JavaVersion
import org.gradle.api.GradleException import org.gradle.api.GradleException
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
@@ -22,7 +23,6 @@ import org.gradle.api.tasks.*
import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.process.CommandLineArgumentProvider import org.gradle.process.CommandLineArgumentProvider
import org.gradle.tooling.provider.model.ToolingModelBuilder
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.CLASS_STRUCTURE_ARTIFACT_TYPE import org.jetbrains.kotlin.gradle.internal.kapt.incremental.CLASS_STRUCTURE_ARTIFACT_TYPE
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTaskData import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTaskData
import org.jetbrains.kotlin.gradle.tasks.locateTask import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.io.ObjectOutputStream import java.io.ObjectOutputStream
@@ -474,7 +475,14 @@ class Kapt3GradleSubplugin @Inject internal constructor(private val registry: To
val dslJavacOptions: Provider<Map<String, String>> = project.provider { val dslJavacOptions: Provider<Map<String, String>> = project.provider {
kaptExtension.getJavacOptions().toMutableMap().also { result -> kaptExtension.getJavacOptions().toMutableMap().also { result ->
if (javaCompile != null && "-source" !in result && "--source" !in result && "--release" !in result) { if (javaCompile != null && "-source" !in result && "--source" !in result && "--release" !in result) {
val sourceOptionKey = if (SystemInfo.isJavaVersionAtLeast(12, 0, 0)) { val atLeast12Java =
if (isConfigurationCacheAvailable(project.gradle)) {
val currentJavaVersion = JavaVersion.parse(project.providers.systemProperty("java.version").forUseAtConfigurationTime().get())
currentJavaVersion.feature >= 12
} else {
SystemInfo.isJavaVersionAtLeast(12, 0, 0)
}
val sourceOptionKey = if (atLeast12Java) {
"--source" "--source"
} else { } else {
"-source" "-source"
@@ -20,7 +20,7 @@ import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
import org.jetbrains.kotlin.gradle.tasks.FilteringSourceRootsContainer import org.jetbrains.kotlin.gradle.tasks.FilteringSourceRootsContainer
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -50,7 +50,7 @@ open class KaptGenerateStubsTask : KotlinCompile() {
@get:Classpath @get:Classpath
@get:InputFiles @get:InputFiles
val kaptClasspath: FileCollection val kaptClasspath: FileCollection
get() = project.files(kaptClasspathConfigurations) get() = objects.fileCollection().from(kaptClasspathConfigurations)
@get:Internal @get:Internal
internal lateinit var kaptClasspathConfigurations: List<Configuration> internal lateinit var kaptClasspathConfigurations: List<Configuration>
@@ -69,6 +69,9 @@ open class KaptGenerateStubsTask : KotlinCompile() {
error("KaptGenerateStubsTask.useModuleDetection setter should not be called!") error("KaptGenerateStubsTask.useModuleDetection setter should not be called!")
} }
@get:Input
val verbose = (project.hasProperty("kapt.verbose") && project.property("kapt.verbose").toString().toBoolean() == true)
override fun source(vararg sources: Any): SourceTask { override fun source(vararg sources: Any): SourceTask {
return super.source(sourceRootsContainer.add(sources)) return super.source(sourceRootsContainer.add(sources))
} }
@@ -95,7 +98,7 @@ open class KaptGenerateStubsTask : KotlinCompile() {
val pluginOptionsWithKapt = pluginOptions.withWrappedKaptOptions(withApClasspath = kaptClasspath) val pluginOptionsWithKapt = pluginOptions.withWrappedKaptOptions(withApClasspath = kaptClasspath)
args.pluginOptions = (pluginOptionsWithKapt.arguments + args.pluginOptions!!).toTypedArray() args.pluginOptions = (pluginOptionsWithKapt.arguments + args.pluginOptions!!).toTypedArray()
args.verbose = project.hasProperty("kapt.verbose") && project.property("kapt.verbose").toString().toBoolean() == true args.verbose = verbose
args.classpathAsList = this.compileClasspath.filter { it.exists() }.toList() args.classpathAsList = this.compileClasspath.filter { it.exists() }.toList()
args.destinationAsFile = this.destinationDir args.destinationAsFile = this.destinationDir
} }
@@ -1,6 +1,5 @@
package org.jetbrains.kotlin.gradle.internal package org.jetbrains.kotlin.gradle.internal
import com.intellij.openapi.util.io.FileUtil
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.internal.ConventionTask import org.gradle.api.internal.ConventionTask
@@ -28,7 +27,7 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
outputs.cacheIf(reason) { useBuildCache } outputs.cacheIf(reason) { useBuildCache }
} }
override fun localStateDirectories(): FileCollection = project.files() override fun localStateDirectories(): FileCollection = objects.fileCollection()
@get:Internal @get:Internal
@field:Transient @field:Transient
@@ -37,10 +36,13 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
@get:Internal @get:Internal
internal lateinit var stubsDir: File internal lateinit var stubsDir: File
@get:Internal
internal val objects = project.objects
@get:Classpath @get:Classpath
@get:InputFiles @get:InputFiles
val kaptClasspath: FileCollection val kaptClasspath: FileCollection
get() = project.files(kaptClasspathConfigurations) get() = objects.fileCollection().from(kaptClasspathConfigurations)
@get:Classpath @get:Classpath
@get:InputFiles @get:InputFiles
@@ -137,8 +139,36 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
private fun isRootAllowed(file: File): Boolean = private fun isRootAllowed(file: File): Boolean =
file.exists() && file.exists() &&
!FileUtil.isAncestor(destinationDir, file, /* strict = */ false) && !isAncestor(destinationDir, file) &&
!FileUtil.isAncestor(classesDir, file, /* strict = */ false) !isAncestor(classesDir, file)
//Have to avoid using FileUtil because it is required system property reading that is not allowed for configuration cache
private fun isAncestor(dir: File, file: File): Boolean {
val path = file.canonicalPath
val prefix = dir.canonicalPath
val pathLength = path.length
val prefixLength = prefix.length
//TODO
val caseSensitive = true
return if (prefixLength == 0) {
true
} else if (prefixLength > pathLength) {
false
} else if (!path.regionMatches(0, prefix, 0, prefixLength, ignoreCase = !caseSensitive)) {
return false
} else if (pathLength == prefixLength) {
return true
} else {
val lastPrefixChar: Char = prefix.get(prefixLength - 1)
var slashOrSeparatorIdx = prefixLength
if (lastPrefixChar == '/' || lastPrefixChar == File.separatorChar) {
slashOrSeparatorIdx = prefixLength - 1
}
val next1 = path[slashOrSeparatorIdx]
return !(next1 != '/' && next1 != File.separatorChar)
}
}
private fun FileCollection?.orEmpty(): FileCollection = private fun FileCollection?.orEmpty(): FileCollection =
this ?: project.files() this ?: project.files()
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
import org.jetbrains.kotlin.gradle.tasks.GradleCompileTaskProvider
import org.jetbrains.kotlin.gradle.utils.getValue import org.jetbrains.kotlin.gradle.utils.getValue
import org.jetbrains.kotlin.gradle.utils.optionalProvider import org.jetbrains.kotlin.gradle.utils.optionalProvider
import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray
@@ -43,6 +44,9 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
val pluginClasspath: FileCollection val pluginClasspath: FileCollection
get() = project.configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME) get() = project.configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME)
@get:Internal
val taskProvider = GradleCompileTaskProvider(this)
override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments() override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments()
private val compileKotlinArgumentsContributor by project.provider { private val compileKotlinArgumentsContributor by project.provider {
@@ -50,8 +54,9 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
} }
override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) { override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
compileKotlinArgumentsContributor.contributeArguments(args, compilerArgumentsConfigurationFlags( compileKotlinArgumentsContributor.contributeArguments(
defaultsOnly, args, compilerArgumentsConfigurationFlags(
defaultsOnly,
ignoreClasspathResolutionErrors ignoreClasspathResolutionErrors
)) ))
@@ -106,7 +111,7 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
throw GradleException("Could not find tools.jar in system classpath, which is required for kapt to work") throw GradleException("Could not find tools.jar in system classpath, which is required for kapt to work")
} }
val compilerRunner = GradleCompilerRunner(this) val compilerRunner = GradleCompilerRunner(taskProvider)
compilerRunner.runJvmCompilerAsync( compilerRunner.runJvmCompilerAsync(
sourcesToCompile = emptyList(), sourcesToCompile = emptyList(),
commonSources = emptyList(), commonSources = emptyList(),
@@ -11,13 +11,12 @@ import org.gradle.workers.IsolationMode
import org.gradle.workers.WorkerExecutor import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptIncrementalChanges import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptIncrementalChanges
import org.jetbrains.kotlin.gradle.plugin.CompositeSubpluginOption
import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPluginWrapper import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPluginWrapper
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
import org.jetbrains.kotlin.gradle.tasks.findKotlinStdlibClasspath import org.jetbrains.kotlin.gradle.tasks.findKotlinStdlibClasspath
import org.jetbrains.kotlin.gradle.tasks.findToolsJar import org.jetbrains.kotlin.gradle.tasks.findToolsJar
import org.jetbrains.kotlin.gradle.utils.getValue import org.jetbrains.kotlin.gradle.utils.getValue
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.utils.PathUtil import org.jetbrains.kotlin.utils.PathUtil
import java.io.File import java.io.File
import java.io.Serializable import java.io.Serializable
@@ -29,7 +28,7 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor
@get:InputFiles @get:InputFiles
@get:Classpath @get:Classpath
@Suppress("unused") @Suppress("unused")
val kaptJars: Collection<File> by project.provider { val kaptJars: Collection<File> by lazy {
project.configurations.getByName(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).resolve() project.configurations.getByName(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).resolve()
} }
@@ -48,6 +47,18 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor
@get:Input @get:Input
lateinit var javacOptions: Map<String, String> lateinit var javacOptions: Map<String, String>
@get:Input
internal val kotlinAndroidPluginWrapperPluginDoesNotExist = project.plugins.none { it is KotlinAndroidPluginWrapper }
@get:Input
internal val kotlinStdlibClasspath = findKotlinStdlibClasspath(project)
@get:Internal
internal val projectDir = project.projectDir
@get:Internal
internal val providers = project.providers
private fun getAnnotationProcessorOptions(): Map<String, String> { private fun getAnnotationProcessorOptions(): Map<String, String> {
val options = processorOptions.subpluginOptionsByPluginId[Kapt3GradleSubplugin.KAPT_SUBPLUGIN_ID] ?: return emptyMap() val options = processorOptions.subpluginOptionsByPluginId[Kapt3GradleSubplugin.KAPT_SUBPLUGIN_ID] ?: return emptyMap()
@@ -70,7 +81,7 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor
} }
val compileClasspath = classpath.files.toMutableList() val compileClasspath = classpath.files.toMutableList()
if (project.plugins.none { it is KotlinAndroidPluginWrapper }) { if (kotlinAndroidPluginWrapperPluginDoesNotExist) {
compileClasspath.addAll(0, PathUtil.getJdkClassesRootsFromCurrentJre()) compileClasspath.addAll(0, PathUtil.getJdkClassesRootsFromCurrentJre())
} }
@@ -82,7 +93,7 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor
} }
val optionsForWorker = KaptOptionsForWorker( val optionsForWorker = KaptOptionsForWorker(
project.projectDir, projectDir,
compileClasspath, compileClasspath,
javaSourceRoots.toList(), javaSourceRoots.toList(),
@@ -108,25 +119,35 @@ open class KaptWithoutKotlincTask @Inject constructor(private val workerExecutor
if (annotationProcessorFqNames.isEmpty() && kaptClasspath.isEmpty()) if (annotationProcessorFqNames.isEmpty() && kaptClasspath.isEmpty())
return return
val kaptClasspath = kaptJars + findKotlinStdlibClasspath(project) val kaptClasspath = kaptJars + kotlinStdlibClasspath
workerExecutor.submit(KaptExecution::class.java) { config -> workerExecutor.submit(KaptExecution::class.java) { config ->
val isolationModeStr = project.findProperty("kapt.workers.isolation") as String? ?: "none" //TODO for gradle < 6.5
val isolationModeStr = getValue("kapt.workers.isolation") ?: "none"
config.isolationMode = when (isolationModeStr.toLowerCase()) { config.isolationMode = when (isolationModeStr.toLowerCase()) {
"process" -> IsolationMode.PROCESS "process" -> IsolationMode.PROCESS
"none" -> IsolationMode.NONE "none" -> IsolationMode.NONE
else -> IsolationMode.NONE else -> IsolationMode.NONE
} }
config.params(optionsForWorker, findToolsJar()?.toURI()?.toURL()?.toString().orEmpty(), kaptClasspath) config.params(optionsForWorker, findToolsJar()?.toURI()?.toURL()?.toString().orEmpty(), kaptClasspath)
if (project.findProperty("kapt.workers.log.classloading") == "true") { if (getValue("kapt.workers.log.classloading") == "true") {
// for tests // for tests
config.forkOptions.jvmArgs("-verbose:class") config.forkOptions.jvmArgs("-verbose:class")
} }
logger.info("Kapt worker classpath: ${config.classpath}") logger.info("Kapt worker classpath: ${config.classpath}")
} }
} }
internal fun getValue(propertyName: String): String? =
if (isGradleVersionAtLeast(6, 5)) {
providers.systemProperty(propertyName).forUseAtConfigurationTime().orNull
} else {
project.findProperty(propertyName) as String?
}
} }
private class KaptExecution @Inject constructor( private class KaptExecution @Inject constructor(
val optionsForWorker: KaptOptionsForWorker, val optionsForWorker: KaptOptionsForWorker,
val toolsJarURLSpec: String, val toolsJarURLSpec: String,
@@ -144,7 +144,7 @@ class KotlinModelBuilder(private val kotlinPluginVersion: String, private val an
} }
private fun AbstractKotlinCompile<*>.createExperimentalFeatures(): ExperimentalFeatures { private fun AbstractKotlinCompile<*>.createExperimentalFeatures(): ExperimentalFeatures {
return ExperimentalFeaturesImpl(coroutinesStr) return ExperimentalFeaturesImpl(coroutinesStr.get())
} }
} }
} }
@@ -0,0 +1,23 @@
/*
* 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 org.gradle.api.Project
import org.gradle.build.event.BuildEventsListenerRegistry
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import javax.inject.Inject
open class BuildEventsListenerRegistryHolder @Inject constructor(val listenerRegistry: BuildEventsListenerRegistry?) {
companion object {
fun getInstance(project: Project) = run {
if (isConfigurationCacheAvailable(project.gradle)) {
project.objects.newInstance(BuildEventsListenerRegistryHolder::class.java)
} else {
BuildEventsListenerRegistryHolder(null)
}
}
}
}
@@ -0,0 +1,21 @@
/*
* 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 org.gradle.tooling.events.FinishEvent
import org.gradle.tooling.events.OperationCompletionListener
//Available since Gradle 6.1
class KotlinGradleBuildListener(
// val gradle: Gradle,
val services: KotlinGradleFinishBuildHandler
) : OperationCompletionListener {
override fun onFinish(event: FinishEvent) {
// services.buildFinished(gradle)
}
}
@@ -10,20 +10,18 @@ import org.gradle.BuildResult
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.invocation.Gradle import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logging import org.gradle.api.logging.Logging
import org.jetbrains.kotlin.compilerRunner.DELETED_SESSION_FILE_PREFIX
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
import org.jetbrains.kotlin.gradle.logging.kotlinDebug import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
import org.jetbrains.kotlin.gradle.report.configureBuildReporter import org.jetbrains.kotlin.gradle.report.configureBuildReporter
import org.jetbrains.kotlin.gradle.utils.relativeToRoot import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import org.jetbrains.kotlin.utils.addToStdlib.sumByLong
import java.lang.management.ManagementFactory
import kotlin.math.max
//Support Gradle 6 and less. Move to
internal class KotlinGradleBuildServices private constructor( internal class KotlinGradleBuildServices private constructor(
private val gradle: Gradle private val gradle: Gradle
) : BuildAdapter() { ) : BuildAdapter() {
companion object { companion object {
private val CLASS_NAME = KotlinGradleBuildServices::class.java.simpleName private val CLASS_NAME = KotlinGradleBuildServices::class.java.simpleName
const val FORCE_SYSTEM_GC_MESSAGE = "Forcing System.gc()" const val FORCE_SYSTEM_GC_MESSAGE = "Forcing System.gc()"
@@ -34,36 +32,69 @@ internal class KotlinGradleBuildServices private constructor(
val ALREADY_INITIALIZED_MESSAGE = "$CLASS_NAME is already initialized" val ALREADY_INITIALIZED_MESSAGE = "$CLASS_NAME is already initialized"
@field:Volatile @field:Volatile
private var instance: KotlinGradleBuildServices? = null internal var instance: KotlinGradleBuildServices? = null
// @JvmStatic
// @Synchronized
// fun getInstance(gradle: Gradle): KotlinGradleBuildServices {
// val log = Logging.getLogger(KotlinGradleBuildServices::class.java)
//
// if (instance != null) {
// log.kotlinDebug(ALREADY_INITIALIZED_MESSAGE)
// return instance!!
// }
//
// val services = KotlinGradleBuildServices(gradle)
// instance = services
// if (!isGradleVersionAtLeast(6,1)) {
// gradle.addBuildListener(services)
// log.kotlinDebug(INIT_MESSAGE)
// } else {
// BuildEventsListenerRegistry.
// }
//
// services.buildStarted()
// return services
// }
@JvmStatic @JvmStatic
@Synchronized @Synchronized
fun getInstance(gradle: Gradle): KotlinGradleBuildServices { fun getInstance(project: Project, listenerRegistryHolder: BuildEventsListenerRegistryHolder): KotlinGradleBuildServices {
val log = Logging.getLogger(KotlinGradleBuildServices::class.java) val log = Logging.getLogger(KotlinGradleBuildServices::class.java)
val kotlinGradleListenerProvider: org.gradle.api.provider.Provider<KotlinGradleBuildListener> = project.provider {
KotlinGradleBuildListener(KotlinGradleFinishBuildHandler())
}
if (instance != null) { if (instance != null) {
log.kotlinDebug(ALREADY_INITIALIZED_MESSAGE) log.kotlinDebug(ALREADY_INITIALIZED_MESSAGE)
return instance!! return instance!!
} }
val gradle = project.gradle
val services = KotlinGradleBuildServices(gradle) val services = KotlinGradleBuildServices(gradle)
gradle.addBuildListener(services) if (isConfigurationCacheAvailable(gradle)) {
instance = services listenerRegistryHolder.listenerRegistry!!.onTaskCompletion(kotlinGradleListenerProvider)
log.kotlinDebug(INIT_MESSAGE) } else {
gradle.addBuildListener(services)
instance = services
log.kotlinDebug(INIT_MESSAGE)
}
services.buildStarted() services.buildStarted()
return services return services
} }
} }
private val log = Logging.getLogger(this.javaClass) private val log = Logging.getLogger(this.javaClass)
private var startMemory: Long? = null private var buildHandler: KotlinGradleFinishBuildHandler? = null
private val shouldReportMemoryUsage = System.getProperty(SHOULD_REPORT_MEMORY_USAGE_PROPERTY) != null
// There is function with the same name in BuildAdapter, // There is function with the same name in BuildAdapter,
// but it is called before any plugin can attach build listener // but it is called before any plugin can attach build listener
fun buildStarted() { fun buildStarted() {
startMemory = getUsedMemoryKb() buildHandler = KotlinGradleFinishBuildHandler()
buildHandler!!.buildStart()
TaskLoggers.clear() TaskLoggers.clear()
TaskExecutionResults.clear() TaskExecutionResults.clear()
@@ -72,57 +103,11 @@ internal class KotlinGradleBuildServices private constructor(
} }
override fun buildFinished(result: BuildResult) { override fun buildFinished(result: BuildResult) {
TaskLoggers.clear() buildHandler!!.buildFinished(result.gradle!!)
TaskExecutionResults.clear()
val gradle = result.gradle!!
GradleCompilerRunner.clearBuildModulesInfo()
val rootProject = gradle.rootProject
val sessionsDir = GradleCompilerRunner.sessionsDir(rootProject)
if (sessionsDir.exists()) {
val sessionFiles = sessionsDir.listFiles()
// it is expected that only one session file per build exists
// afaik is is not possible to run multiple gradle builds in one project since gradle locks some dirs
if (sessionFiles.size > 1) {
log.warn("w: Detected multiple Kotlin daemon sessions at ${sessionsDir.relativeToRoot(rootProject)}")
}
for (file in sessionFiles) {
file.delete()
log.kotlinDebug { DELETED_SESSION_FILE_PREFIX + file.relativeToRoot(rootProject) }
}
}
if (shouldReportMemoryUsage) {
val startMem = startMemory!!
val endMem = getUsedMemoryKb()!!
// the value reported here is not necessarily a leak, since it is calculated before collecting the plugin classes
// but on subsequent runs in the daemon it should be rather small, then the classes are actually reused by the daemon (see above)
log.lifecycle("[KOTLIN][PERF] Used memory after build: $endMem kb (difference since build start: ${"%+d".format(endMem - startMem)} kb)")
}
gradle.removeListener(this)
instance = null instance = null
log.kotlinDebug(DISPOSE_MESSAGE) log.kotlinDebug(DISPOSE_MESSAGE)
} }
private fun getUsedMemoryKb(): Long? {
if (!shouldReportMemoryUsage) return null
log.lifecycle(FORCE_SYSTEM_GC_MESSAGE)
val gcCountBefore = getGcCount()
System.gc()
while (getGcCount() == gcCountBefore) {
}
val rt = Runtime.getRuntime()
return (rt.totalMemory() - rt.freeMemory()) / 1024
}
private fun getGcCount(): Long =
ManagementFactory.getGarbageCollectorMXBeans().sumByLong { max(0, it.collectionCount) }
private val multipleProjectsHolder = KotlinPluginInMultipleProjectsHolder( private val multipleProjectsHolder = KotlinPluginInMultipleProjectsHolder(
trackPluginVersionsSeparately = true trackPluginVersionsSeparately = true
@@ -0,0 +1,81 @@
/*
* 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 org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logging
import org.jetbrains.kotlin.compilerRunner.DELETED_SESSION_FILE_PREFIX
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
import org.jetbrains.kotlin.gradle.utils.relativeToRoot
import org.jetbrains.kotlin.utils.addToStdlib.sumByLong
import java.lang.management.ManagementFactory
import kotlin.math.max
// move to KotlinGradleBuildListener when min supported version is 6.1
class KotlinGradleFinishBuildHandler {
private val log = Logging.getLogger(this.javaClass)
private var startMemory: Long? = null
private val shouldReportMemoryUsage = System.getProperty(KotlinGradleBuildServices.SHOULD_REPORT_MEMORY_USAGE_PROPERTY) != null
fun buildStart() {
startMemory = getUsedMemoryKb()
}
fun buildFinished(gradle: Gradle) {
TaskLoggers.clear()
TaskExecutionResults.clear()
GradleCompilerRunner.clearBuildModulesInfo()
val rootProject = gradle.rootProject
val sessionsDir = GradleCompilerRunner.sessionsDir(rootProject)
if (sessionsDir.exists()) {
val sessionFiles = sessionsDir.listFiles()
// it is expected that only one session file per build exists
// afaik is is not possible to run multiple gradle builds in one project since gradle locks some dirs
if (sessionFiles.size > 1) {
log.warn("w: Detected multiple Kotlin daemon sessions at ${sessionsDir.relativeToRoot(rootProject)}")
}
for (file in sessionFiles) {
file.delete()
log.kotlinDebug { DELETED_SESSION_FILE_PREFIX + file.relativeToRoot(rootProject) }
}
}
if (shouldReportMemoryUsage) {
val startMem = startMemory!!
val endMem = getUsedMemoryKb()!!
// the value reported here is not necessarily a leak, since it is calculated before collecting the plugin classes
// but on subsequent runs in the daemon it should be rather small, then the classes are actually reused by the daemon (see above)
log.lifecycle("[KOTLIN][PERF] Used memory after build: $endMem kb (difference since build start: ${"%+d".format(endMem - startMem)} kb)")
}
gradle.removeListener(this)
}
internal fun getUsedMemoryKb(): Long? {
if (!shouldReportMemoryUsage) return null
log.lifecycle(KotlinGradleBuildServices.FORCE_SYSTEM_GC_MESSAGE)
val gcCountBefore = getGcCount()
System.gc()
while (getGcCount() == gcCountBefore) {
}
val rt = Runtime.getRuntime()
return (rt.totalMemory() - rt.freeMemory()) / 1024
}
private fun getGcCount(): Long =
ManagementFactory.getGarbageCollectorMXBeans().sumByLong { max(0, it.collectionCount) }
}
@@ -1,3 +1,4 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2016 JetBrains s.r.o.
* *
@@ -103,7 +103,7 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
private fun prepareKotlinCompileTask(): TaskProvider<out T> = private fun prepareKotlinCompileTask(): TaskProvider<out T> =
registerKotlinCompileTask(register = ::doRegisterTask).also { task -> registerKotlinCompileTask(register = ::doRegisterTask).also { task ->
kotlinCompilation.output.addClassesDir { project.files(task.map { it.destinationDir }) } kotlinCompilation.output.addClassesDir { project.files(task.map { it.destinationDir }).builtBy(task) }
} }
protected fun registerKotlinCompileTask( protected fun registerKotlinCompileTask(
@@ -24,6 +24,7 @@ import org.gradle.api.internal.FeaturePreviews
import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.file.FileResolver
import org.gradle.api.logging.Logger import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging import org.gradle.api.logging.Logging
import org.gradle.build.event.BuildEventsListenerRegistry
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.logging.kotlinDebug import org.jetbrains.kotlin.gradle.logging.kotlinDebug
@@ -49,6 +50,7 @@ import kotlin.reflect.KClass
abstract class KotlinBasePluginWrapper( abstract class KotlinBasePluginWrapper(
protected val fileResolver: FileResolver protected val fileResolver: FileResolver
) : Plugin<Project> { ) : Plugin<Project> {
private val log = Logging.getLogger(this.javaClass) private val log = Logging.getLogger(this.javaClass)
val kotlinPluginVersion = loadKotlinVersionFromResource(log) val kotlinPluginVersion = loadKotlinVersionFromResource(log)
@@ -58,7 +60,8 @@ abstract class KotlinBasePluginWrapper(
DefaultKotlinSourceSetFactory(project, fileResolver) DefaultKotlinSourceSetFactory(project, fileResolver)
override fun apply(project: Project) { override fun apply(project: Project) {
val statisticsReporter = KotlinBuildStatsService.getOrCreateInstance(project.gradle) val listenerRegistryHolder = BuildEventsListenerRegistryHolder.getInstance(project)
val statisticsReporter = KotlinBuildStatsService.getOrCreateInstance(project, listenerRegistryHolder)
statisticsReporter?.report(StringMetrics.KOTLIN_COMPILER_VERSION, kotlinPluginVersion) statisticsReporter?.report(StringMetrics.KOTLIN_COMPILER_VERSION, kotlinPluginVersion)
checkGradleCompatibility() checkGradleCompatibility()
@@ -80,7 +83,8 @@ abstract class KotlinBasePluginWrapper(
// TODO: consider only set if if daemon or parallel compilation are enabled, though this way it should be safe too // TODO: consider only set if if daemon or parallel compilation are enabled, though this way it should be safe too
System.setProperty(org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY, "true") System.setProperty(org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY, "true")
val kotlinGradleBuildServices = KotlinGradleBuildServices.getInstance(project.gradle)
val kotlinGradleBuildServices = KotlinGradleBuildServices.getInstance(project, listenerRegistryHolder)
kotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion) kotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion)
@@ -27,16 +27,16 @@ import java.io.File
internal fun AbstractCompile.appendClasspathDynamically(file: File) { internal fun AbstractCompile.appendClasspathDynamically(file: File) {
var added = false var added = false
val objects = project.objects
doFirst { doFirst {
if (file !in classpath) { if (file !in classpath) {
classpath += project.files(file) classpath += objects.fileCollection().from(file)
added = true added = true
} }
} }
doLast { doLast {
if (added) { if (added) {
classpath -= project.files(file) classpath -= objects.fileCollection().from(file)
} }
} }
} }
@@ -24,9 +24,14 @@ import java.util.zip.ZipOutputStream
import javax.xml.parsers.DocumentBuilderFactory import javax.xml.parsers.DocumentBuilderFactory
internal sealed class MetadataDependencyResolution( internal sealed class MetadataDependencyResolution(
@field:Transient // can't be used with Gradle Instant Execution, but fortunately not needed when deserialized
val dependency: ResolvedComponentResult, val dependency: ResolvedComponentResult,
@field:Transient
val projectDependency: Project? val projectDependency: Project?
) { ) {
/** Evaluate and store the value, as the [dependency] will be lost during Gradle instant execution */
// val originalArtifactFiles: List<File> = dependency.dependents.flatMap { it.allModuleArtifacts } .map { it.file }
override fun toString(): String { override fun toString(): String {
val verb = when (this) { val verb = when (this) {
is KeepOriginalDependency -> "keep" is KeepOriginalDependency -> "keep"
@@ -59,7 +64,10 @@ internal sealed class MetadataDependencyResolution(
* If [doProcessFiles] is true, these temporary files are actually re-created during the call, * If [doProcessFiles] is true, these temporary files are actually re-created during the call,
* otherwise only their paths are returned, while the files might be missing. * otherwise only their paths are returned, while the files might be missing.
*/ */
abstract fun getMetadataFilesBySourceSet(baseDir: File, doProcessFiles: Boolean): Map<String, FileCollection> fun getMetadataFilesBySourceSet(baseDir: File, doProcessFiles: Boolean): Map<String, FileCollection> =
getExtractableMetadataFiles(baseDir).getMetadataFilesPerSourceSet(doProcessFiles)
abstract fun getExtractableMetadataFiles(baseDir: File): ExtractableMetadataFiles
override fun toString(): String = override fun toString(): String =
super.toString() + ", sourceSets = " + allVisibleSourceSetNames.joinToString(", ", "[", "]") { super.toString() + ", sourceSets = " + allVisibleSourceSetNames.joinToString(", ", "[", "]") {
@@ -183,6 +191,7 @@ internal class GranularMetadataTransformation(
allModuleDependencies.forEach { resolvedDependency -> allModuleDependencies.forEach { resolvedDependency ->
if (resolvedDependency.selected !in visitedDependencies) { if (resolvedDependency.selected !in visitedDependencies) {
// val files = resolvedDependency.moduleArtifacts.map { it.file }
result.add( result.add(
MetadataDependencyResolution.ExcludeAsUnrequested( MetadataDependencyResolution.ExcludeAsUnrequested(
resolvedDependency.selected, resolvedDependency.selected,
@@ -273,6 +282,20 @@ internal class GranularMetadataTransformation(
module, resolvedToProject, projectStructureMetadata, allVisibleSourceSets, visibleSourceSetsExcludingDependsOn, module, resolvedToProject, projectStructureMetadata, allVisibleSourceSets, visibleSourceSetsExcludingDependsOn,
transitiveDependenciesToVisit, mppDependencyMetadataExtractor transitiveDependenciesToVisit, mppDependencyMetadataExtractor
) )
/*
return object : MetadataDependencyResolution.ChooseVisibleSourceSets(
module,
projectDependency,
projectStructureMetadata,
allVisibleSourceSets,
visibleSourceSetsExcludingDependsOn,
transitiveDependenciesToVisit
) {
override fun getExtractableMetadataFiles(baseDir: File): ExtractableMetadataFiles =
mppDependencyMetadataExtractor.getExtractableMetadataFiles(visibleSourceSetsExcludingDependsOn, baseDir)
}
*/
} }
private class ChooseVisibleSourceSetsImpl( private class ChooseVisibleSourceSetsImpl(
@@ -291,18 +314,24 @@ internal class GranularMetadataTransformation(
visibleSourceSetNamesExcludingDependsOn, visibleSourceSetNamesExcludingDependsOn,
visibleTransitiveDependencies visibleTransitiveDependencies
) { ) {
override fun getMetadataFilesBySourceSet(baseDir: File, doProcessFiles: Boolean): Map<String, FileCollection> = override fun getExtractableMetadataFiles(baseDir: File): ExtractableMetadataFiles =
metadataExtractor.getVisibleSourceSetsMetadata(visibleSourceSetNamesExcludingDependsOn, baseDir, doProcessFiles) object : ExtractableMetadataFiles() {
override fun getMetadataFilesPerSourceSet(doProcessFiles: Boolean) : Map<String, FileCollection>
= metadataExtractor.getExtractableMetadataFiles(visibleSourceSetNamesExcludingDependsOn, baseDir, doProcessFiles)
.getMetadataFilesPerSourceSet(doProcessFiles)
}
} }
} }
private abstract class MppDependencyMetadataExtractor(val project: Project, val dependency: ResolvedComponentResult) { private abstract class MppDependencyMetadataExtractor(val project: Project, val dependency: ResolvedComponentResult) {
abstract fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? abstract fun getProjectStructureMetadata(): KotlinProjectStructureMetadata?
abstract fun getVisibleSourceSetsMetadata(
abstract fun getExtractableMetadataFiles(
visibleSourceSetNames: Set<String>, visibleSourceSetNames: Set<String>,
baseDir: File, baseDir: File,
doProcessFiles: Boolean doProcessFiles: Boolean
): Map<String, FileCollection> ): ExtractableMetadataFiles
} }
private class ProjectMppDependencyMetadataExtractor( private class ProjectMppDependencyMetadataExtractor(
@@ -313,14 +342,19 @@ private class ProjectMppDependencyMetadataExtractor(
override fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? = override fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? =
buildKotlinProjectStructureMetadata(dependencyProject) buildKotlinProjectStructureMetadata(dependencyProject)
override fun getVisibleSourceSetsMetadata( override fun getExtractableMetadataFiles(
visibleSourceSetNames: Set<String>, visibleSourceSetNames: Set<String>,
baseDir: File, baseDir: File,
doProcessFiles: Boolean doProcessFiles: Boolean
): Map<String, FileCollection> = ): ExtractableMetadataFiles {
dependencyProject.multiplatformExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME).compilations val result = dependencyProject.multiplatformExtension.targets.getByName(KotlinMultiplatformPlugin.METADATA_TARGET_NAME).compilations
.filter { it.name in visibleSourceSetNames } .filter { it.name in visibleSourceSetNames }
.associate { it.defaultSourceSet.name to it.output.classesDirs } .associate { it.defaultSourceSet.name to it.output.classesDirs }
return object : ExtractableMetadataFiles() {
override fun getMetadataFilesPerSourceSet(doProcessFiles: Boolean): Map<String, FileCollection> = result
}
}
} }
private class JarArtifactMppDependencyMetadataExtractor( private class JarArtifactMppDependencyMetadataExtractor(
@@ -344,72 +378,106 @@ private class JarArtifactMppDependencyMetadataExtractor(
} }
} }
override fun getVisibleSourceSetsMetadata( override fun getExtractableMetadataFiles(
visibleSourceSetNames: Set<String>, visibleSourceSetNames: Set<String>,
baseDir: File, baseDir: File,
doProcessFiles: Boolean doProcessFiles: Boolean
): Map<String, FileCollection> { ): ExtractableMetadataFiles {
// val artifactFile = artifact.file
val primaryArtifact = primaryArtifact val primaryArtifact = primaryArtifact
val moduleId = ModuleIds.fromComponent(project, dependency) val moduleId = ModuleIds.fromComponent(project, dependency)
return extractSourceSetMetadataFromArtifacts( // val jarArtifact = artifact
// ?: return object : ExtractableMetadataFiles() {
// override fun getMetadataFilesPerSourceSet(doProcessFiles: Boolean): Map<String, FileCollection> = emptyMap()
// }
return JarExtractableMetadataFiles(
moduleId, moduleId,
project,
primaryArtifact,
// artifactJar,
// visibleSourceSetNames,
baseDir, baseDir,
doProcessFiles, doProcessFiles,
visibleSourceSetNames.associate { it to (metadataArtifactBySourceSet[it] ?: primaryArtifact) } visibleSourceSetNames.associate { it to (metadataArtifactBySourceSet[it] ?: primaryArtifact) }
) )
} }
private fun extractSourceSetMetadataFromArtifacts( private class JarExtractableMetadataFiles(
module: ModuleDependencyIdentifier, private val module: ModuleDependencyIdentifier,
baseDir: File, private val project: Project,
private val primaryArtifact: File,
private val baseDir: File,
doProcessFiles: Boolean, doProcessFiles: Boolean,
artifactBySourceSet: Map<String, File> private val artifactBySourceSet: Map<String, File>
): Map<String, FileCollection> { ) : ExtractableMetadataFiles() {
val moduleString = "${module.groupId}-${module.moduleId}"
val transformedModuleRoot = run { baseDir.resolve(moduleString).also { it.mkdirs() } }
val resultFiles = mutableMapOf<String, FileCollection>() //TODO its part of extractor. Move it
fun getProjectStructureMetadata(): KotlinProjectStructureMetadata? {
return ZipFile(primaryArtifact).use { zip ->
val metadata = zip.getEntry("META-INF/$MULTIPLATFORM_PROJECT_METADATA_FILE_NAME")
?: return null
val projectStructureMetadata = checkNotNull(getProjectStructureMetadata()) { val metadataXmlDocument = zip.getInputStream(metadata).use { inputStream ->
"can't extract metadata from a module without project structure metadata" DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream)
}
parseKotlinSourceSetMetadataFromXml(metadataXmlDocument)
}
} }
artifactBySourceSet.forEach { (sourceSetName, artifact) -> override fun getMetadataFilesPerSourceSet(doProcessFiles: Boolean): Map<String, FileCollection> {
ZipFile(artifact).use { zip -> val moduleString = "${module.groupId}-${module.moduleId}"
val entries = zip.entries().asSequence().filter { it.name.startsWith("$sourceSetName/") }.toList() val transformedModuleRoot = run { baseDir.resolve(moduleString).also { it.mkdirs() } }
// TODO: once IJ supports non-JAR metadata dependencies, extract to a directory, not a JAR val resultFiles = mutableMapOf<String, FileCollection>()
// Also, if both IJ and the CLI compiler can read metadata from a path inside a JAR, then no operations will be needed val projectStructureMetadata = checkNotNull(getProjectStructureMetadata()) {
"can't extract metadata from a module without project structure metadata"
}
// val artifactJar :File= artifactBySourceSet.values().first()
artifactBySourceSet.forEach { (sourceSetName, artifact) ->
ZipFile(artifact).use { zip ->
val entries = zip.entries().asSequence().filter { it.name.startsWith("$sourceSetName/") }.toList()
if (entries.any()) { // TODO: once IJ supports non-JAR metadata dependencies, extract to a directory, not a JAR
val extension = projectStructureMetadata.sourceSetBinaryLayout[sourceSetName]?.archiveExtension // Also, if both IJ and the CLI compiler can read metadata from a path inside a JAR, then no operations will be needed
?: SourceSetMetadataLayout.METADATA.archiveExtension
val extractToJarFile = transformedModuleRoot.resolve("$moduleString-$sourceSetName.$extension") if (entries.any()) {
resultFiles[sourceSetName] = project.files(extractToJarFile) val extension = projectStructureMetadata.sourceSetBinaryLayout[sourceSetName]?.archiveExtension
?: SourceSetMetadataLayout.METADATA.archiveExtension
if (doProcessFiles) { val extractToJarFile = transformedModuleRoot.resolve("$moduleString-$sourceSetName.$extension")
ZipOutputStream(extractToJarFile.outputStream()).use { resultZipOutput -> resultFiles[sourceSetName] = project.files(extractToJarFile)
for (entry in entries) {
if (entry.isDirectory)
continue
// Drop the source set name from the entry path if (doProcessFiles) {
val resultEntry = ZipEntry(entry.name.substringAfter("/")) ZipOutputStream(extractToJarFile.outputStream()).use { resultZipOutput ->
for (entry in entries) {
if (entry.isDirectory)
continue
zip.getInputStream(entry).use { inputStream -> // Drop the source set name from the entry path
resultZipOutput.putNextEntry(resultEntry) val resultEntry = ZipEntry(entry.name.substringAfter("/"))
inputStream.copyTo(resultZipOutput)
resultZipOutput.closeEntry() zip.getInputStream(entry).use { inputStream ->
resultZipOutput.putNextEntry(resultEntry)
inputStream.copyTo(resultZipOutput)
resultZipOutput.closeEntry()
}
} }
} }
} }
} }
} }
} }
}
return resultFiles return resultFiles
}
} }
} }
// This class is needed to encapsulate how we extract the files and point to them in a way that doesn't capture the Gradle project state
internal abstract class ExtractableMetadataFiles {
abstract fun getMetadataFilesPerSourceSet(doProcessFiles: Boolean): Map<String, FileCollection>
}
@@ -15,22 +15,28 @@ import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope.*
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME import org.jetbrains.kotlin.gradle.targets.metadata.ALL_COMPILE_METADATA_CONFIGURATION_NAME
import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator
import org.jetbrains.kotlin.gradle.utils.getValue
import java.io.File import java.io.File
import javax.inject.Inject import javax.inject.Inject
open class TransformKotlinGranularMetadata open class TransformKotlinGranularMetadata
@Inject constructor( @Inject constructor(
@get:Internal @get:Internal
@field:Transient
val kotlinSourceSet: KotlinSourceSet val kotlinSourceSet: KotlinSourceSet
) : DefaultTask() { ) : DefaultTask() {
@get:OutputDirectory @get:OutputDirectory
val outputsDir: File = project.buildDir.resolve("kotlinSourceSetMetadata/${kotlinSourceSet.name}") val outputsDir: File by project.provider {
project.buildDir.resolve("kotlinSourceSetMetadata/${kotlinSourceSet.name}")
}
@Suppress("unused") // Gradle input @Suppress("unused") // Gradle input
@get:InputFiles @get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE) @get:PathSensitive(PathSensitivity.RELATIVE)
internal val allSourceSetsMetadataConfiguration = project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME) internal val allSourceSetsMetadataConfiguration: FileCollection by lazy {
project.files(project.configurations.getByName(ALL_COMPILE_METADATA_CONFIGURATION_NAME))
}
private val participatingSourceSets: Set<KotlinSourceSet> private val participatingSourceSets: Set<KotlinSourceSet>
get() = transformation.kotlinSourceSet.getSourceSetHierarchy().toMutableSet().apply { get() = transformation.kotlinSourceSet.getSourceSetHierarchy().toMutableSet().apply {
@@ -40,15 +46,14 @@ open class TransformKotlinGranularMetadata
@Suppress("unused") // Gradle input @Suppress("unused") // Gradle input
@get:Input @get:Input
internal val inputSourceSetsAndCompilations: Map<String, Iterable<String>> internal val inputSourceSetsAndCompilations: Map<String, Iterable<String>> by project.provider {
get() { val sourceSets = participatingSourceSets
val sourceSets = participatingSourceSets CompilationSourceSetUtil.compilationsBySourceSets(project)
return CompilationSourceSetUtil.compilationsBySourceSets(project) .filterKeys { it in sourceSets }
.filterKeys { it in sourceSets } .entries.associate { (sourceSet, compilations) ->
.entries.associate { (sourceSet, compilations) -> sourceSet.name to compilations.map { it.name }.sorted()
sourceSet.name to compilations.map { it.name }.sorted()
}
} }
}
private val participatingCompilations: Iterable<KotlinCompilation<*>> private val participatingCompilations: Iterable<KotlinCompilation<*>>
get() { get() {
@@ -58,13 +63,15 @@ open class TransformKotlinGranularMetadata
@Suppress("unused") // Gradle input @Suppress("unused") // Gradle input
@get:Input @get:Input
internal val inputCompilationDependencies: Map<String, Set<List<String?>>> internal val inputCompilationDependencies: Map<String, Set<List<String?>>> by project.provider {
get() = participatingCompilations.associate { participatingCompilations.associate {
it.name to project.configurations.getByName(it.compileDependencyConfigurationName) it.name to project.configurations.getByName(it.compileDependencyConfigurationName)
.allDependencies.map { listOf(it.group, it.name, it.version) }.toSet() .allDependencies.map { listOf(it.group, it.name, it.version) }.toSet()
} }
}
@get:Internal @get:Internal
@delegate:Transient
internal val transformation: GranularMetadataTransformation by lazy { internal val transformation: GranularMetadataTransformation by lazy {
GranularMetadataTransformation( GranularMetadataTransformation(
project, project,
@@ -82,14 +89,39 @@ open class TransformKotlinGranularMetadata
} }
@get:Internal @get:Internal
internal val metadataDependencyResolutions: Iterable<MetadataDependencyResolution> @delegate:Transient // exclude from Gradle instant execution state
get() = transformation.metadataDependencyResolutions internal val metadataDependencyResolutions: Iterable<MetadataDependencyResolution> by project.provider {
transformation.metadataDependencyResolutions
}
//TODO avoid using it
@get:Internal @get:Internal
internal val filesByResolution: Map<out MetadataDependencyResolution, FileCollection> internal val filesByResolution: Map<out MetadataDependencyResolution, FileCollection>
get() = metadataDependencyResolutions.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>() get() = metadataDependencyResolutions.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
.associate { it to project.files(it.getMetadataFilesBySourceSet(outputsDir, doProcessFiles = false).values) } .associate { it to project.files(it.getMetadataFilesBySourceSet(outputsDir, doProcessFiles = false).values) }
// @get:Internal
// internal val filesByOriginalFiles: Map<out Iterable<File>, FileCollection> by project.provider {
// metadataDependencyResolutions.associate {
// it.originalArtifactFiles to project.files(
// when (it) {
// is MetadataDependencyResolution.ChooseVisibleSourceSets ->
// it.getMetadataFilesBySourceSet(outputsDir, doProcessFiles = false).values
// is MetadataDependencyResolution.ExcludeAsUnrequested ->
// emptyList()
// is MetadataDependencyResolution.KeepOriginalDependency ->
// it.originalArtifactFiles
// }
// )
// }
// }
private val extractableFiles by project.provider {
transformation.metadataDependencyResolutions
.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
.map { it.getExtractableMetadataFiles(outputsDir) }
}
@TaskAction @TaskAction
fun transformMetadata() { fun transformMetadata() {
if (outputsDir.isDirectory) { if (outputsDir.isDirectory) {
@@ -97,8 +129,6 @@ open class TransformKotlinGranularMetadata
} }
outputsDir.mkdirs() outputsDir.mkdirs()
metadataDependencyResolutions extractableFiles.forEach { it.getMetadataFilesPerSourceSet(doProcessFiles = true) }
.filterIsInstance<MetadataDependencyResolution.ChooseVisibleSourceSets>()
.forEach { it.getMetadataFilesBySourceSet(outputsDir, doProcessFiles = true) }
} }
} }
@@ -69,6 +69,7 @@ internal class DefaultLanguageSettingsBuilder : LanguageSettingsBuilder {
} }
/* A Kotlin task that is responsible for code analysis of the owner of this language settings builder. */ /* A Kotlin task that is responsible for code analysis of the owner of this language settings builder. */
@Transient // not needed during Gradle Instant Execution
var compilerPluginOptionsTask: Lazy<AbstractCompile?> = lazyOf(null) var compilerPluginOptionsTask: Lazy<AbstractCompile?> = lazyOf(null)
val compilerPluginArguments: List<String>? val compilerPluginArguments: List<String>?
@@ -76,7 +77,7 @@ internal class DefaultLanguageSettingsBuilder : LanguageSettingsBuilder {
val pluginOptionsTask = compilerPluginOptionsTask.value ?: return null val pluginOptionsTask = compilerPluginOptionsTask.value ?: return null
return when (pluginOptionsTask) { return when (pluginOptionsTask) {
is AbstractKotlinCompile<*> -> pluginOptionsTask.pluginOptions is AbstractKotlinCompile<*> -> pluginOptionsTask.pluginOptions
is AbstractKotlinNativeCompile<*> -> pluginOptionsTask.compilerPluginOptions is AbstractKotlinNativeCompile<*, *> -> pluginOptionsTask.compilerPluginOptions
else -> error("Unexpected task: $pluginOptionsTask") else -> error("Unexpected task: $pluginOptionsTask")
}.arguments }.arguments
} }
@@ -86,7 +87,7 @@ internal class DefaultLanguageSettingsBuilder : LanguageSettingsBuilder {
val pluginClasspathTask = compilerPluginOptionsTask.value ?: return null val pluginClasspathTask = compilerPluginOptionsTask.value ?: return null
return when (pluginClasspathTask) { return when (pluginClasspathTask) {
is AbstractKotlinCompile<*> -> pluginClasspathTask.pluginClasspath is AbstractKotlinCompile<*> -> pluginClasspathTask.pluginClasspath
is AbstractKotlinNativeCompile<*> -> pluginClasspathTask.compilerPluginClasspath ?: pluginClasspathTask.project.files() is AbstractKotlinNativeCompile<*, *> -> pluginClasspathTask.compilerPluginClasspath ?: pluginClasspathTask.project.files()
else -> error("Unexpected task: $pluginClasspathTask") else -> error("Unexpected task: $pluginClasspathTask")
} }
} }
@@ -0,0 +1,215 @@
/*
* 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.statistics
import org.gradle.api.Project
import org.gradle.api.artifacts.DependencySet
import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logging
import org.jetbrains.kotlin.gradle.utils.API
import org.jetbrains.kotlin.gradle.utils.COMPILE
import org.jetbrains.kotlin.gradle.utils.IMPLEMENTATION
import org.jetbrains.kotlin.gradle.utils.RUNTIME
import org.jetbrains.kotlin.statistics.BuildSessionLogger
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
import java.io.File
import java.lang.management.ManagementFactory
import javax.management.MBeanServer
import javax.management.ObjectName
import kotlin.system.measureTimeMillis
class KotlinBuildStatHandler {
companion object {
@JvmStatic
internal fun getLogger() = Logging.getLogger(KotlinBuildStatHandler::class.java)
internal fun <T> runSafe(methodName: String, action: () -> T?): T? {
return try {
getLogger().debug("Executing [$methodName]")
action.invoke()
} catch (e: Throwable) {
logException("Could not execute [$methodName]", e)
null
}
}
internal fun logException(description: String, e: Throwable) {
getLogger().info(description)
getLogger().debug(e.message, e)
}
}
fun buildFinished(
gradle: Gradle?,
beanName: ObjectName,
sessionLogger: BuildSessionLogger,
action: String?,
failure: Throwable?
) {
runSafe("${KotlinBuildStatHandler::class.java}.buildFinished") {
try {
try {
if (gradle != null) reportGlobalMetrics(gradle, sessionLogger)
} finally {
sessionLogger.finishBuildSession(action, failure)
}
} finally {
val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer()
if (mbs.isRegistered(beanName)) {
mbs.unregisterMBean(beanName)
}
}
}
}
internal fun reportGlobalMetrics(gradle: Gradle, sessionLogger: BuildSessionLogger) {
System.getProperty("os.name")?.also {
sessionLogger.report(StringMetrics.OS_TYPE, System.getProperty("os.name"))
}
sessionLogger.report(NumericalMetrics.CPU_NUMBER_OF_CORES, Runtime.getRuntime().availableProcessors().toLong())
sessionLogger.report(StringMetrics.GRADLE_VERSION, gradle.gradleVersion)
sessionLogger.report(BooleanMetrics.EXECUTED_FROM_IDEA, System.getProperty("idea.active") != null)
sessionLogger.report(NumericalMetrics.GRADLE_DAEMON_HEAP_SIZE, Runtime.getRuntime().maxMemory())
sessionLogger.report(
BooleanMetrics.KOTLIN_OFFICIAL_CODESTYLE,
gradle.rootProject.properties["kotlin.code.style"] == "official"
) // constants are saved in IDEA plugin and could not be accessed directly
gradle.taskGraph.whenReady() { taskExecutionGraph ->
val executedTaskNames = taskExecutionGraph.allTasks.map { it.name }.distinct()
report(sessionLogger, BooleanMetrics.COMPILATION_STARTED, executedTaskNames.contains("compileKotlin"), null)
report(sessionLogger, BooleanMetrics.TESTS_EXECUTED, executedTaskNames.contains("compileTestKotlin"), null)
report(sessionLogger, BooleanMetrics.MAVEN_PUBLISH_EXECUTED, executedTaskNames.contains("install"), null)
}
fun buildSrcExists(project: Project) = File(project.projectDir, "buildSrc").exists()
if (buildSrcExists(gradle.rootProject)) {
sessionLogger.report(BooleanMetrics.BUILD_SRC_EXISTS, true)
}
val statisticOverhead = measureTimeMillis {
gradle.allprojects { project ->
for (configuration in project.configurations) {
val configurationName = configuration.name
val dependencies = configuration.dependencies
when (configurationName) {
"kapt" -> {
sessionLogger.report(BooleanMetrics.ENABLED_KAPT, true)
dependencies?.forEach { dependency ->
when (dependency.group) {
"com.google.dagger" -> sessionLogger.report(BooleanMetrics.ENABLED_DAGGER, true)
"com.android.databinding" -> sessionLogger.report(BooleanMetrics.ENABLED_DATABINDING, true)
}
}
}
API -> {
sessionLogger.report(NumericalMetrics.CONFIGURATION_API_COUNT, 1)
reportLibrariesVersions(sessionLogger, dependencies)
}
IMPLEMENTATION -> {
sessionLogger.report(NumericalMetrics.CONFIGURATION_IMPLEMENTATION_COUNT, 1)
reportLibrariesVersions(sessionLogger, dependencies)
}
COMPILE -> {
sessionLogger.report(NumericalMetrics.CONFIGURATION_COMPILE_COUNT, 1)
reportLibrariesVersions(sessionLogger, dependencies)
}
RUNTIME -> {
sessionLogger.report(NumericalMetrics.CONFIGURATION_RUNTIME_COUNT, 1)
reportLibrariesVersions(sessionLogger, dependencies)
}
}
}
sessionLogger.report(NumericalMetrics.NUMBER_OF_SUBPROJECTS, 1)
sessionLogger.report(BooleanMetrics.KOTLIN_KTS_USED, project.buildscript.sourceFile?.name?.endsWith(".kts") ?: false)
sessionLogger.report(NumericalMetrics.GRADLE_NUMBER_OF_TASKS, project.tasks.names.size.toLong())
sessionLogger.report(
NumericalMetrics.GRADLE_NUMBER_OF_UNCONFIGURED_TASKS,
project.tasks.names.count { name ->
try {
project.tasks.named(name).javaClass.name.contains("TaskCreatingProvider")
} catch (_: Exception) {
true
}
}.toLong()
)
if (buildSrcExists(project)) {
sessionLogger.report(NumericalMetrics.BUILD_SRC_COUNT, 1)
sessionLogger.report(BooleanMetrics.BUILD_SRC_EXISTS, true)
}
}
}
sessionLogger.report(NumericalMetrics.STATISTICS_VISIT_ALL_PROJECTS_OVERHEAD, statisticOverhead)
}
private fun reportLibrariesVersions(sessionLogger: BuildSessionLogger, dependencies: DependencySet?) {
dependencies?.forEach { dependency ->
when {
dependency.group?.startsWith("org.springframework") ?: false -> sessionLogger.report(
StringMetrics.LIBRARY_SPRING_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group?.startsWith("com.vaadin") ?: false -> sessionLogger.report(
StringMetrics.LIBRARY_VAADIN_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group?.startsWith("com.google.gwt") ?: false -> sessionLogger.report(
StringMetrics.LIBRARY_GWT_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group?.startsWith("org.hibernate") ?: false -> sessionLogger.report(
StringMetrics.LIBRARY_HIBERNATE_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group == "org.jetbrains.kotlin" && dependency.name.startsWith("kotlin-stdlib") -> sessionLogger.report(
StringMetrics.KOTLIN_STDLIB_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group == "org.jetbrains.kotlinx" && dependency.name == "kotlinx-coroutines" -> sessionLogger.report(
StringMetrics.KOTLIN_COROUTINES_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group == "org.jetbrains.kotlin" && dependency.name == "kotlin-reflect" -> sessionLogger.report(
StringMetrics.KOTLIN_REFLECT_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group == "org.jetbrains.kotlinx" && dependency.name
.startsWith("kotlinx-serialization-runtime") -> sessionLogger.report(
StringMetrics.KOTLIN_SERIALIZATION_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group == "com.android.tools.build" && dependency.name.startsWith("gradle") -> sessionLogger.report(
StringMetrics.ANDROID_GRADLE_PLUGIN_VERSION,
dependency.version ?: "0.0.0"
)
}
}
}
internal fun report(sessionLogger: BuildSessionLogger, metric: BooleanMetrics, value: Boolean, subprojectName: String?) {
runSafe("report metric ${metric.name}") {
sessionLogger.report(metric, value, subprojectName)
}
}
internal fun report(sessionLogger: BuildSessionLogger, metric: NumericalMetrics, value: Long, subprojectName: String?) {
runSafe("report metric ${metric.name}") {
sessionLogger.report(metric, value, subprojectName)
}
}
internal fun report(sessionLogger: BuildSessionLogger, metric: StringMetrics, value: String, subprojectName: String?) {
runSafe("report metric ${metric.name}") {
sessionLogger.report(metric, value, subprojectName)
}
}
}
@@ -0,0 +1,66 @@
/*
* 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.statistics
import org.gradle.api.invocation.Gradle
import org.gradle.tooling.events.FinishEvent
import org.gradle.tooling.events.OperationCompletionListener
import org.gradle.tooling.events.configuration.ProjectConfigurationFinishEvent
import org.jetbrains.kotlin.statistics.BuildSessionLogger
import java.lang.management.ManagementFactory
import javax.management.MBeanServer
import javax.management.ObjectName
open class KotlinBuildStatListener(val beanName: ObjectName/*, val gradle: Gradle*/) : OperationCompletionListener, AutoCloseable {
private var projectEvaluatedTime: Long? = null
override fun onFinish(event: FinishEvent?) {
if (event is ProjectConfigurationFinishEvent) {
projectEvaluatedTime = event.eventTime
}
//todo is it any chance to get failure exception?
//todo nothing to do?
// KotlinBuildStatHandler.runSafe("${KotlinBuildStatListener::class.java}.onFinish") {
//
//
// try {
// val finishTime = event?.result?.endTime
// val startTime = event?.result?.startTime
// report(NumericalMetrics.GRADLE_BUILD_DURATION, finishTime - it.buildStartedTime)
// report(NumericalMetrics.GRADLE_EXECUTION_DURATION, finishTime - it.projectEvaluatedTime)
// report(NumericalMetrics.BUILD_FINISH_TIME, finishTime)
// } finally {
// val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer()
// if (mbs.isRegistered(beanName)) {
// mbs.unregisterMBean(beanName)
// }
// }
// }
}
override fun close() {
// val sessionLogger = BuildSessionLogger(gradle.gradleUserHomeDir)
// KotlinBuildStatHandler.runSafe("${KotlinBuildStatListener::class.java}.close()") {
// try {
// try {
// KotlinBuildStatHandler().reportGlobalMetrics(gradle, sessionLogger)
// } finally {
//// report(NumericalMetrics.GRADLE_BUILD_DURATION, finishTime - it.buildStartedTime)
//// report(NumericalMetrics.GRADLE_EXECUTION_DURATION, finishTime - it.projectEvaluatedTime)
//// report(NumericalMetrics.BUILD_FINISH_TIME, finishTime)
// }
//
// } finally {
// val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer()
// if (mbs.isRegistered(beanName)) {
// mbs.unregisterMBean(beanName)
// }
// }
// }
}
}
@@ -8,15 +8,13 @@ package org.jetbrains.kotlin.gradle.plugin.statistics
import org.gradle.BuildAdapter import org.gradle.BuildAdapter
import org.gradle.BuildResult import org.gradle.BuildResult
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.DependencySet
import org.gradle.api.invocation.Gradle import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logging import org.gradle.api.logging.Logging
import org.gradle.initialization.BuildRequestMetaData import org.gradle.initialization.BuildRequestMetaData
import org.gradle.invocation.DefaultGradle import org.gradle.invocation.DefaultGradle
import org.jetbrains.kotlin.gradle.utils.API import org.jetbrains.kotlin.gradle.plugin.BuildEventsListenerRegistryHolder
import org.jetbrains.kotlin.gradle.utils.COMPILE import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatHandler.Companion.runSafe
import org.jetbrains.kotlin.gradle.utils.IMPLEMENTATION import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import org.jetbrains.kotlin.gradle.utils.RUNTIME
import org.jetbrains.kotlin.statistics.BuildSessionLogger import org.jetbrains.kotlin.statistics.BuildSessionLogger
import org.jetbrains.kotlin.statistics.BuildSessionLogger.Companion.STATISTICS_FOLDER_NAME import org.jetbrains.kotlin.statistics.BuildSessionLogger.Companion.STATISTICS_FOLDER_NAME
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
@@ -87,8 +85,10 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd
*/ */
@JvmStatic @JvmStatic
@Synchronized @Synchronized
internal fun getOrCreateInstance(gradle: Gradle): IStatisticsValuesConsumer? { internal fun getOrCreateInstance(project: Project, listenerRegistryHolder: BuildEventsListenerRegistryHolder): IStatisticsValuesConsumer? {
return runSafe("${KotlinBuildStatsService::class.java}.getOrCreateInstance") { return runSafe("${KotlinBuildStatsService::class.java}.getOrCreateInstance") {
val gradle = project.gradle
statisticsIsEnabled = statisticsIsEnabled ?: checkStatisticsEnabled(gradle) statisticsIsEnabled = statisticsIsEnabled ?: checkStatisticsEnabled(gradle)
if (statisticsIsEnabled != true) { if (statisticsIsEnabled != true) {
null null
@@ -106,13 +106,17 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd
) )
instance = JMXKotlinBuildStatsService(mbs, beanName) instance = JMXKotlinBuildStatsService(mbs, beanName)
} else { } else {
val kotlinBuildStatProvider = project.provider{ KotlinBuildStatListener(beanName) }
val newInstance = DefaultKotlinBuildStatsService(gradle, beanName) val newInstance = DefaultKotlinBuildStatsService(gradle, beanName)
instance = newInstance instance = newInstance
log.debug("Instantiated ${KotlinBuildStatsService::class.java}: new instance $instance") log.debug("Instantiated ${KotlinBuildStatsService::class.java}: new instance $instance")
mbs.registerMBean(StandardMBean(newInstance, KotlinBuildStatsMXBean::class.java), beanName) mbs.registerMBean(StandardMBean(newInstance, KotlinBuildStatsMXBean::class.java), beanName)
} }
} }
gradle.addBuildListener(instance) if (!isConfigurationCacheAvailable(gradle)) {
gradle.addBuildListener(instance)
}
instance instance
} }
} }
@@ -130,7 +134,7 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd
} }
this.report(NumericalMetrics.STATISTICS_COLLECT_METRICS_OVERHEAD, duration) this.report(NumericalMetrics.STATISTICS_COLLECT_METRICS_OVERHEAD, duration)
} catch (e: Throwable) { } catch (e: Throwable) {
logException("Could collect statistics metrics", e) KotlinBuildStatHandler.logException("Could collect statistics metrics", e)
} }
} }
} }
@@ -149,21 +153,6 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd
gradle.rootProject.properties[ENABLE_STATISTICS_PROPERTY_NAME]?.toString()?.toBoolean() ?: DEFAULT_STATISTICS_STATE gradle.rootProject.properties[ENABLE_STATISTICS_PROPERTY_NAME]?.toString()?.toBoolean() ?: DEFAULT_STATISTICS_STATE
} }
} }
private fun logException(description: String, e: Throwable) {
getLogger().info(description)
getLogger().debug(e.message, e)
}
internal fun <T> runSafe(methodName: String, action: () -> T?): T? {
return try {
getLogger().debug("Executing [$methodName]")
action.invoke()
} catch (e: Throwable) {
logException("Could not execute [$methodName]", e)
null
}
}
} }
} }
@@ -212,132 +201,6 @@ internal class DefaultKotlinBuildStatsService internal constructor(
return (gradle as? DefaultGradle)?.services?.get(BuildRequestMetaData::class.java)?.startTime return (gradle as? DefaultGradle)?.services?.get(BuildRequestMetaData::class.java)?.startTime
} }
private fun reportLibrariesVersions(dependencies: DependencySet?) {
dependencies?.forEach { dependency ->
when {
dependency.group?.startsWith("org.springframework") ?: false -> sessionLogger.report(
StringMetrics.LIBRARY_SPRING_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group?.startsWith("com.vaadin") ?: false -> sessionLogger.report(
StringMetrics.LIBRARY_VAADIN_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group?.startsWith("com.google.gwt") ?: false -> sessionLogger.report(
StringMetrics.LIBRARY_GWT_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group?.startsWith("org.hibernate") ?: false -> sessionLogger.report(
StringMetrics.LIBRARY_HIBERNATE_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group == "org.jetbrains.kotlin" && dependency.name.startsWith("kotlin-stdlib") -> sessionLogger.report(
StringMetrics.KOTLIN_STDLIB_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group == "org.jetbrains.kotlinx" && dependency.name == "kotlinx-coroutines" -> sessionLogger.report(
StringMetrics.KOTLIN_COROUTINES_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group == "org.jetbrains.kotlin" && dependency.name == "kotlin-reflect" -> sessionLogger.report(
StringMetrics.KOTLIN_REFLECT_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group == "org.jetbrains.kotlinx" && dependency.name
.startsWith("kotlinx-serialization-runtime") -> sessionLogger.report(
StringMetrics.KOTLIN_SERIALIZATION_VERSION,
dependency.version ?: "0.0.0"
)
dependency.group == "com.android.tools.build" && dependency.name.startsWith("gradle") -> sessionLogger.report(
StringMetrics.ANDROID_GRADLE_PLUGIN_VERSION,
dependency.version ?: "0.0.0"
)
}
}
}
private fun reportGlobalMetrics(gradle: Gradle) {
System.getProperty("os.name")?.also {
sessionLogger.report(StringMetrics.OS_TYPE, System.getProperty("os.name"))
}
sessionLogger.report(NumericalMetrics.CPU_NUMBER_OF_CORES, Runtime.getRuntime().availableProcessors().toLong())
sessionLogger.report(StringMetrics.GRADLE_VERSION, gradle.gradleVersion)
sessionLogger.report(BooleanMetrics.EXECUTED_FROM_IDEA, System.getProperty("idea.active") != null)
sessionLogger.report(NumericalMetrics.GRADLE_DAEMON_HEAP_SIZE, Runtime.getRuntime().maxMemory())
sessionLogger.report(
BooleanMetrics.KOTLIN_OFFICIAL_CODESTYLE,
gradle.rootProject.properties["kotlin.code.style"] == "official"
) // constants are saved in IDEA plugin and could not be accessed directly
gradle.taskGraph.whenReady() { taskExecutionGraph ->
val executedTaskNames = taskExecutionGraph.allTasks.map { it.name }.distinct()
report(BooleanMetrics.COMPILATION_STARTED, executedTaskNames.contains("compileKotlin"))
report(BooleanMetrics.TESTS_EXECUTED, executedTaskNames.contains("compileTestKotlin"))
report(BooleanMetrics.MAVEN_PUBLISH_EXECUTED, executedTaskNames.contains("install"))
}
fun buildSrcExists(project: Project) = File(project.projectDir, "buildSrc").exists()
if (buildSrcExists(gradle.rootProject)) {
sessionLogger.report(BooleanMetrics.BUILD_SRC_EXISTS, true)
}
val statisticOverhead = measureTimeMillis {
gradle.allprojects { project ->
for (configuration in project.configurations) {
val configurationName = configuration.name
val dependencies = configuration.dependencies
when (configurationName) {
"kapt" -> {
sessionLogger.report(BooleanMetrics.ENABLED_KAPT, true)
dependencies?.forEach { dependency ->
when (dependency.group) {
"com.google.dagger" -> sessionLogger.report(BooleanMetrics.ENABLED_DAGGER, true)
"com.android.databinding" -> sessionLogger.report(BooleanMetrics.ENABLED_DATABINDING, true)
}
}
}
API -> {
sessionLogger.report(NumericalMetrics.CONFIGURATION_API_COUNT, 1)
reportLibrariesVersions(dependencies)
}
IMPLEMENTATION -> {
sessionLogger.report(NumericalMetrics.CONFIGURATION_IMPLEMENTATION_COUNT, 1)
reportLibrariesVersions(dependencies)
}
COMPILE -> {
sessionLogger.report(NumericalMetrics.CONFIGURATION_COMPILE_COUNT, 1)
reportLibrariesVersions(dependencies)
}
RUNTIME -> {
sessionLogger.report(NumericalMetrics.CONFIGURATION_RUNTIME_COUNT, 1)
reportLibrariesVersions(dependencies)
}
}
}
sessionLogger.report(NumericalMetrics.NUMBER_OF_SUBPROJECTS, 1)
sessionLogger.report(BooleanMetrics.KOTLIN_KTS_USED, project.buildscript.sourceFile?.name?.endsWith(".kts") ?: false)
sessionLogger.report(NumericalMetrics.GRADLE_NUMBER_OF_TASKS, project.tasks.names.size.toLong())
sessionLogger.report(
NumericalMetrics.GRADLE_NUMBER_OF_UNCONFIGURED_TASKS,
project.tasks.names.count { name ->
try {
project.tasks.named(name).javaClass.name.contains("TaskCreatingProvider")
} catch (_: Exception) {
true
}
}.toLong()
)
if (buildSrcExists(project)) {
sessionLogger.report(NumericalMetrics.BUILD_SRC_COUNT, 1)
sessionLogger.report(BooleanMetrics.BUILD_SRC_EXISTS, true)
}
}
}
sessionLogger.report(NumericalMetrics.STATISTICS_VISIT_ALL_PROJECTS_OVERHEAD, statisticOverhead)
}
override fun projectsEvaluated(gradle: Gradle) { override fun projectsEvaluated(gradle: Gradle) {
runSafe("${DefaultKotlinBuildStatsService::class.java}.projectEvaluated") { runSafe("${DefaultKotlinBuildStatsService::class.java}.projectEvaluated") {
if (!sessionLogger.isBuildSessionStarted()) { if (!sessionLogger.isBuildSessionStarted()) {
@@ -351,41 +214,20 @@ internal class DefaultKotlinBuildStatsService internal constructor(
@Synchronized @Synchronized
override fun buildFinished(result: BuildResult) { override fun buildFinished(result: BuildResult) {
runSafe("${DefaultKotlinBuildStatsService::class.java}.buildFinished") { KotlinBuildStatHandler().buildFinished(result.gradle, beanName, sessionLogger, result.action, result.failure)
try { instance = null
try {
val gradle = result.gradle
if (gradle != null) reportGlobalMetrics(gradle)
} finally {
sessionLogger.finishBuildSession(result.action, result.failure)
}
} finally {
val mbs: MBeanServer = ManagementFactory.getPlatformMBeanServer()
if (mbs.isRegistered(beanName)) {
mbs.unregisterMBean(beanName)
}
instance = null
}
}
} }
override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?) { override fun report(metric: BooleanMetrics, value: Boolean, subprojectName: String?) {
runSafe("report metric ${metric.name}") { KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName)
sessionLogger.report(metric, value, subprojectName)
}
} }
override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?) { override fun report(metric: NumericalMetrics, value: Long, subprojectName: String?) {
runSafe("report metric ${metric.name}") { KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName)
sessionLogger.report(metric, value, subprojectName)
}
} }
override fun report(metric: StringMetrics, value: String, subprojectName: String?) { override fun report(metric: StringMetrics, value: String, subprojectName: String?) {
runSafe("report metric ${metric.name}") { KotlinBuildStatHandler().report(sessionLogger, metric, value, subprojectName)
sessionLogger.report(metric, value, subprojectName)
}
} }
override fun reportBoolean(name: String, value: Boolean, subprojectName: String?) { override fun reportBoolean(name: String, value: Boolean, subprojectName: String?) {
@@ -16,11 +16,10 @@ import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import java.io.File import java.io.File
import java.lang.StringBuilder
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.math.max
internal fun configureBuildReporter(gradle: Gradle, log: Logger) { internal fun configureBuildReporter(gradle: Gradle, log: Logger) {
val rootProject = gradle.rootProject val rootProject = gradle.rootProject
@@ -40,8 +39,11 @@ internal fun configureBuildReporter(gradle: Gradle, log: Logger) {
val ts = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Calendar.getInstance().time) val ts = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Calendar.getInstance().time)
val perfReportFile = perfLogDir.resolve("${gradle.rootProject.name}-build-$ts.txt") val perfReportFile = perfLogDir.resolve("${gradle.rootProject.name}-build-$ts.txt")
val reporter = KotlinBuildReporter(gradle, perfReportFile)
gradle.addBuildListener(reporter) if (!isConfigurationCacheAvailable(gradle)) {
val reporter = KotlinBuildReporter(gradle, perfReportFile)
gradle.addBuildListener(reporter)
}
val buildReportMode = if (properties.buildReportVerbose) BuildReportMode.VERBOSE else BuildReportMode.SIMPLE val buildReportMode = if (properties.buildReportVerbose) BuildReportMode.VERBOSE else BuildReportMode.SIMPLE
@@ -112,94 +114,7 @@ internal class KotlinBuildReporter(
@Synchronized @Synchronized
override fun buildFinished(result: BuildResult) { override fun buildFinished(result: BuildResult) {
val logger = result.gradle?.rootProject?.logger KotlinBuildReporterHandler().buildFinished(gradle, perfReportFile, kotlinTaskTimeNs.mapKeys{it.key.path}, allTasksTimeNs, result.failure)
try {
perfReportFile.writeText(buildInfo(result) + taskOverview() + tasksSb.toString())
logger?.lifecycle("Kotlin build report is written to ${perfReportFile.canonicalPath}")
} catch (e: Throwable) {
logger?.error("Could not write Kotlin build report to ${perfReportFile.canonicalPath}", e)
}
}
private fun buildInfo(result: BuildResult): String {
val startParams = arrayListOf<String>()
gradle.startParameter.apply {
startParams.add("tasks = ${taskRequests.joinToString { it.args.toString() }}")
startParams.add("excluded tasks = $excludedTaskNames")
startParams.add("current dir = $currentDir")
startParams.add("project properties args = $projectProperties")
startParams.add("system properties args = $systemPropertiesArgs")
}
return buildString {
appendln("Gradle start parameters:")
startParams.forEach {
appendln(" $it")
}
if (result.failure != null) {
appendln("Build failed: ${result.failure}")
}
appendln()
}
}
private fun taskOverview(): String {
if (kotlinTaskTimeNs.isEmpty()) return buildString { appendln("No Kotlin task was run") }
val sb = StringBuilder()
val kotlinTotalTimeNs = kotlinTaskTimeNs.values.sum()
val ktTaskPercent = (kotlinTotalTimeNs.toDouble() / allTasksTimeNs * 100).asString(1)
sb.appendln("Total time for Kotlin tasks: ${formatTime(kotlinTotalTimeNs)} ($ktTaskPercent % of all tasks time)")
val table = TextTable("Time", "% of Kotlin time", "Task")
kotlinTaskTimeNs.entries
.sortedByDescending { (_, timeNs) -> timeNs }
.forEach { (task, timeNs) ->
val percent = (timeNs.toDouble() / kotlinTotalTimeNs * 100).asString(1)
table.addRow(formatTime(timeNs), "$percent %", task.path)
}
table.printTo(sb)
sb.appendln()
return sb.toString()
}
private fun formatTime(ns: Long): String {
val seconds = ns.toDouble() / 1_000_000_000
return seconds.asString(2) + " s"
}
private fun Double.asString(decPoints: Int): String =
String.format("%.${decPoints}f", this)
private class TextTable(vararg columnNames: String) {
private val rows = ArrayList<List<String>>()
private val columnsCount = columnNames.size
private val maxLengths = IntArray(columnsCount) { columnNames[it].length }
init {
rows.add(columnNames.toList())
}
fun addRow(vararg row: String) {
check(row.size == columnsCount) { "Row size ${row.size} differs from columns count $columnsCount" }
rows.add(row.toList())
for ((i, col) in row.withIndex()) {
maxLengths[i] = max(maxLengths[i], col.length)
}
}
fun printTo(sb: StringBuilder) {
for (row in rows) {
sb.appendln()
for ((i, col) in row.withIndex()) {
if (i > 0) sb.append("|")
sb.append(col.padEnd(maxLengths[i], ' '))
}
}
}
} }
} }
@@ -0,0 +1,110 @@
/*
* 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.report
import org.gradle.api.invocation.Gradle
import java.io.File
import java.util.*
import kotlin.math.max
class KotlinBuildReporterHandler {
fun buildFinished(
gradle: Gradle,
perfReportFile: File,
kotlinTaskTimeNs: Map<String, Long>,
allTasksTimeNs: Long,
failure: Throwable? = null
) {
val logger = gradle.rootProject.logger
try {
perfReportFile.writeText(buildInfo(gradle, failure) + taskOverview(kotlinTaskTimeNs, allTasksTimeNs))
logger.lifecycle("Kotlin build report is written to ${perfReportFile.canonicalPath}")
} catch (e: Throwable) {
logger.error("Could not write Kotlin build report to ${perfReportFile.canonicalPath}", e)
}
}
private fun buildInfo(gradle: Gradle, failure: Throwable?): String {
val startParams = arrayListOf<String>()
gradle.startParameter.apply {
startParams.add("tasks = ${taskRequests.joinToString { it.args.toString() }}")
startParams.add("excluded tasks = $excludedTaskNames")
startParams.add("current dir = $currentDir")
startParams.add("project properties args = $projectProperties")
startParams.add("system properties args = $systemPropertiesArgs")
}
return buildString {
appendln("Gradle start parameters:")
startParams.forEach {
appendln(" $it")
}
if (failure != null) {
appendln("Build failed: ${failure}")
}
appendln()
}
}
internal fun taskOverview(kotlinTaskTimeNs: Map<String, Long>, allTasksTimeNs: Long): String {
if (kotlinTaskTimeNs.isEmpty()) return buildString { appendln("No Kotlin task was run") }
val sb = StringBuilder()
val kotlinTotalTimeNs = kotlinTaskTimeNs.values.sum()
val ktTaskPercent = (kotlinTotalTimeNs.toDouble() / allTasksTimeNs * 100).asString(1)
sb.appendln("Total time for Kotlin tasks: ${formatTime(kotlinTotalTimeNs)} ($ktTaskPercent % of all tasks time)")
val table = TextTable("Time", "% of Kotlin time", "Task")
kotlinTaskTimeNs.entries
.sortedByDescending { (_, timeNs) -> timeNs }
.forEach { (taskPath, timeNs) ->
val percent = (timeNs.toDouble() / kotlinTotalTimeNs * 100).asString(1)
table.addRow(formatTime(timeNs), "$percent %", taskPath)
}
table.printTo(sb)
sb.appendln()
return sb.toString()
}
internal class TextTable(vararg columnNames: String) {
private val rows = ArrayList<List<String>>()
private val columnsCount = columnNames.size
private val maxLengths = IntArray(columnsCount) { columnNames[it].length }
init {
rows.add(columnNames.toList())
}
fun addRow(vararg row: String) {
check(row.size == columnsCount) { "Row size ${row.size} differs from columns count $columnsCount" }
rows.add(row.toList())
for ((i, col) in row.withIndex()) {
maxLengths[i] = max(maxLengths[i], col.length)
}
}
fun printTo(sb: StringBuilder) {
for (row in rows) {
sb.appendln()
for ((i, col) in row.withIndex()) {
if (i > 0) sb.append("|")
sb.append(col.padEnd(maxLengths[i], ' '))
}
}
}
}
}
internal fun formatTime(ns: Long): String {
val seconds = ns.toDouble() / 1_000_000_000
return seconds.asString(2) + " s"
}
internal fun Double.asString(decPoints: Int): String =
String.format("%.${decPoints}f", this)
@@ -0,0 +1,34 @@
/*
* 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.report
import com.intellij.util.text.DateFormatUtil.formatTime
import org.gradle.api.invocation.Gradle
import org.gradle.tooling.events.FinishEvent
import org.gradle.tooling.events.OperationCompletionListener
import org.gradle.tooling.events.task.TaskOperationDescriptor
import java.io.File
import java.util.*
class KotlinBuildReporterListener(val gradle: Gradle, val perfReportFile: File) : OperationCompletionListener, AutoCloseable {
private val kotlinTaskTimeNs = HashMap<String, Long>()
internal var allTasksTimeNs: Long = 0L
override fun onFinish(event: FinishEvent?) {
val descriptor = event?.descriptor
if (descriptor is TaskOperationDescriptor) {
val taskPath = descriptor.taskPath
val executionTime = event.result.endTime - event.result.startTime
allTasksTimeNs += executionTime
kotlinTaskTimeNs[taskPath] = executionTime
}
}
override fun close() {
KotlinBuildReporterHandler().buildFinished(gradle, perfReportFile, kotlinTaskTimeNs, allTasksTimeNs)
}
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.targets.js.ir
import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.CacheableTask
import org.gradle.workers.WorkerExecutor import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers
import org.jetbrains.kotlin.gradle.tasks.GradleCompileTaskProvider
import javax.inject.Inject import javax.inject.Inject
@CacheableTask @CacheableTask
@@ -16,5 +17,5 @@ internal open class KotlinJsIrLinkWithWorkers
constructor( constructor(
private val workerExecutor: WorkerExecutor private val workerExecutor: WorkerExecutor
) : KotlinJsIrLink() { ) : KotlinJsIrLink() {
override fun compilerRunner() = GradleCompilerRunnerWithWorkers(this, workerExecutor) override fun compilerRunner() = GradleCompilerRunnerWithWorkers(GradleCompileTaskProvider(this), workerExecutor)
} }
@@ -10,6 +10,7 @@ import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.Dependency
import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.provider.Provider
import org.gradle.jvm.tasks.Jar import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
@@ -44,10 +45,12 @@ open class KotlinWithJavaTarget<KotlinOptionsType : KotlinCommonOptions>(
KotlinWithJavaCompilationFactory(project, this, kotlinOptionsFactory) KotlinWithJavaCompilationFactory(project, this, kotlinOptionsFactory)
) )
internal val defaultArtifactClassesListFile: File private val layout = project.layout
get() {
internal val defaultArtifactClassesListFile: Provider<File> =
layout.buildDirectory.dir(KOTLIN_BUILD_DIR_NAME).map {
val jarTask = project.tasks.getByName(artifactsTaskName) as Jar val jarTask = project.tasks.getByName(artifactsTaskName) as Jar
return File(File(project.buildDir, KOTLIN_BUILD_DIR_NAME), "${sanitizeFileName(jarTask.archiveFileName.get())}-classes.txt") it.file("${sanitizeFileName(jarTask.archiveFileName.get())}-classes.txt").asFile
} }
} }
@@ -457,6 +457,31 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
} }
} }
) )
/*
val transformedFilesByOriginalFiles = project.provider {
transformationTaskHolders
.flatMap { it.get().filesByOriginalFiles.toList() }
.groupBy({ it.first }, valueTransform = { it.second })
}
val builtBySet = mutableSetOf<FileCollection>()
val resultFiles = project.files(Callable {
val originalFiles = fromFiles.toSet()
val filesToAdd = mutableSetOf<File>()
val filesToExclude = mutableSetOf<File>()
transformedFilesByOriginalFiles.get().forEach { (original, replacement) ->
if (original.all { it in originalFiles }) {
builtBySet.addAll(replacement)
filesToAdd += replacement.flatMap { it.files }
filesToExclude += original
}
}
originalFiles - filesToExclude + filesToAdd
})
return resultFiles.builtBy(builtBySet)
*/
} }
private fun createCommonMainElementsConfiguration(target: KotlinMetadataTarget) { private fun createCommonMainElementsConfiguration(target: KotlinMetadataTarget) {
@@ -10,9 +10,7 @@ import groovy.lang.Closure
import org.gradle.api.Action import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.gradle.util.ConfigureUtil import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
@@ -156,7 +156,7 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget>(
val compileTaskProvider = registerTask<KotlinNativeCompile>( val compileTaskProvider = registerTask<KotlinNativeCompile>(
compilation.compileKotlinTaskName compilation.compileKotlinTaskName
) { ) {
it.compilation = compilation it.compilation.set(compilation)
it.group = BasePlugin.BUILD_GROUP it.group = BasePlugin.BUILD_GROUP
it.description = "Compiles a klibrary from the '${compilation.name}' " + it.description = "Compiles a klibrary from the '${compilation.name}' " +
"compilation for target '${compilation.platformType.name}'." "compilation for target '${compilation.platformType.name}'."
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.gradle.targets.native.internal.PlatformLibrariesGene
import org.jetbrains.kotlin.gradle.targets.native.internal.setUpKotlinNativePlatformDependencies import org.jetbrains.kotlin.gradle.targets.native.internal.setUpKotlinNativePlatformDependencies
import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import org.jetbrains.kotlin.konan.CompilerVersion import org.jetbrains.kotlin.konan.CompilerVersion
import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
@@ -89,11 +90,9 @@ abstract class AbstractKotlinNativeTargetPreset<T : KotlinNativeTarget>(
createTargetConfigurator().configureTarget(result) createTargetConfigurator().configureTarget(result)
SingleActionPerProject.run(project, "setUpKotlinNativePlatformDependencies") { SingleActionPerProject.run(project, "setUpKotlinNativePlatformDependencies") {
project.gradle.addListener(object : BuildAdapter() { project.gradle.projectsEvaluated {
override fun projectsEvaluated(gradle: Gradle) { project.setUpKotlinNativePlatformDependencies()
project.setUpKotlinNativePlatformDependencies() }
}
})
} }
if (!konanTarget.enabledOnCurrentHost) { if (!konanTarget.enabledOnCurrentHost) {
@@ -8,11 +8,9 @@ package org.jetbrains.kotlin.gradle.plugin.cocoapods
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Sync import org.gradle.api.tasks.Sync
import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
@@ -20,10 +18,7 @@ import org.jetbrains.kotlin.gradle.plugin.addExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
import org.jetbrains.kotlin.gradle.targets.native.tasks.* import org.jetbrains.kotlin.gradle.targets.native.tasks.*
import org.jetbrains.kotlin.gradle.tasks.DefFileTask import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.tasks.DummyFrameworkTask
import org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask
import org.jetbrains.kotlin.gradle.tasks.PodspecTask
import org.jetbrains.kotlin.gradle.utils.asValidTaskName import org.jetbrains.kotlin.gradle.utils.asValidTaskName
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.HostManager
@@ -16,8 +16,8 @@ import org.jetbrains.kotlin.gradle.plugin.addExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.disambiguateName import org.jetbrains.kotlin.gradle.plugin.mpp.disambiguateName
import org.jetbrains.kotlin.gradle.targets.native.tasks.NativePerformanceReport import org.jetbrains.kotlin.gradle.targets.native.tasks.NativePerformanceReport
import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.io.File
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
class TaskTime(val startTime: Long) { class TaskTime(val startTime: Long) {
@@ -66,17 +66,26 @@ open class KotlinPerformancePlugin : Plugin<Project> {
private fun configureTasks(project: Project, performanceExtension: PerformanceExtension) { private fun configureTasks(project: Project, performanceExtension: PerformanceExtension) {
// Add time listener. // Add time listener.
val timeListener = TaskTimerListener(project) if (!isConfigurationCacheAvailable(project.gradle)) {
project.gradle.addListener(timeListener) val timeListener = TaskTimerListener(project)
performanceExtension.trackedBinaries.forEach { binary -> project.gradle.addListener(timeListener)
val perfReport = project.registerTask<NativePerformanceReport>(binary.target.disambiguateName(lowerCamelCaseName("perfReport", binary.name))) { performanceExtension.trackedBinaries.forEach { binary ->
it.binary = binary val perfReport = project.registerTask<NativePerformanceReport>(
it.settings = performanceExtension binary.target.disambiguateName(
it.timeListener = timeListener lowerCamelCaseName(
it.group = TASK_GROUP "perfReport",
it.description = "Report performance measurement results for binary '${binary.name}' of target '${binary.target.name}'." binary.name
)
)
) {
it.binary = binary
it.settings = performanceExtension
it.timeListener = timeListener
it.group = TASK_GROUP
it.description = "Report performance measurement results for binary '${binary.name}' of target '${binary.target.name}'."
}
binary.linkTaskProvider.configure { linkTask -> linkTask.finalizedBy(perfReport) }
} }
binary.linkTaskProvider.configure { linkTask -> linkTask.finalizedBy(perfReport) }
} }
} }
@@ -70,15 +70,15 @@ open class NativePerformanceReport : DefaultTask() {
} }
private fun getPerformanceCompilerOptions() = private fun getPerformanceCompilerOptions() =
(compilerFlagsFromBinary() + binary.linkTask.compilation.kotlinOptions.freeCompilerArgs) (compilerFlagsFromBinary() + binary.linkTask.compilation.get().kotlinOptions.freeCompilerArgs)
.filter { it in listOf("-g", "-opt", "-Xg0") }.map { "\"$it\"" } .filter { it in listOf("-g", "-opt", "-Xg0") }.map { "\"$it\"" }
@TaskAction @TaskAction
fun generate() { fun generate() {
val compileTasks = if (settings.includeAssociatedTasks) val compileTasks = if (settings.includeAssociatedTasks)
getAllExecutedTasks(binary.linkTask.compilation) getAllExecutedTasks(binary.linkTask.compilation.get())
else else
listOf(binary.linkTask.compilation.compileKotlinTask) listOf(binary.linkTask.compilation.get().compileKotlinTask)
val allExecutedTasks = listOf(binary.linkTask) + compileTasks val allExecutedTasks = listOf(binary.linkTask) + compileTasks
val upToDateTasks = allExecutedTasks.filter { it.state.upToDate }.map { it.name } val upToDateTasks = allExecutedTasks.filter { it.state.upToDate }.map { it.name }
if (upToDateTasks.isNotEmpty()) { if (upToDateTasks.isNotEmpty()) {
@@ -13,8 +13,8 @@ import org.gradle.api.artifacts.*
import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileTree import org.gradle.api.file.FileTree
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.logging.Logger import org.gradle.api.logging.Logger
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.AbstractCompile
@@ -26,12 +26,12 @@ import org.jetbrains.kotlin.gradle.dsl.NativeCacheKind
import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated
import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.plugin.cocoapods.asValidFrameworkName import org.jetbrains.kotlin.gradle.plugin.cocoapods.asValidFrameworkName
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.plugin.mpp.* import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.targets.native.internal.isAllowCommonizer import org.jetbrains.kotlin.gradle.targets.native.internal.isAllowCommonizer
import org.jetbrains.kotlin.gradle.utils.getValue import org.jetbrains.kotlin.gradle.utils.getValue
import org.jetbrains.kotlin.gradle.utils.klibModuleName import org.jetbrains.kotlin.gradle.utils.klibModuleName
import org.jetbrains.kotlin.gradle.utils.newProperty
import org.jetbrains.kotlin.gradle.utils.listFilesOrEmpty import org.jetbrains.kotlin.gradle.utils.listFilesOrEmpty
import org.jetbrains.kotlin.konan.library.KLIB_INTEROP_IR_PROVIDER_IDENTIFIER import org.jetbrains.kotlin.konan.library.KLIB_INTEROP_IR_PROVIDER_IDENTIFIER
import org.jetbrains.kotlin.konan.properties.saveToFile import org.jetbrains.kotlin.konan.properties.saveToFile
@@ -112,7 +112,7 @@ private fun Collection<File>.filterKlibsPassedToCompiler(project: Project) = fil
} }
// endregion // endregion
abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions> : AbstractCompile() { abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions, K : AbstractKotlinNativeCompilation> : AbstractCompile() {
init { init {
sourceCompatibility = "1.6" sourceCompatibility = "1.6"
@@ -120,7 +120,7 @@ abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions> : Abstra
} }
@get:Internal @get:Internal
abstract val compilation: AbstractKotlinNativeCompilation abstract val compilation: Provider<K>
// region inputs/outputs // region inputs/outputs
@get:Input @get:Input
@@ -136,20 +136,21 @@ abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions> : Abstra
abstract val baseName: String abstract val baseName: String
// Inputs and outputs // Inputs and outputs
val libraries: FileCollection @get:InputFiles
@InputFiles get() = val libraries: FileCollection by project.provider {
// Avoid resolving these dependencies during task graph construction when we can't build the target: // Avoid resolving these dependencies during task graph construction when we can't build the target:
if (compilation.konanTarget.enabledOnCurrentHost) if (compilation.get().konanTarget.enabledOnCurrentHost)
compilation.compileDependencyFiles.filterOutPublishableInteropLibs(project) compilation.get().compileDependencyFiles.filterOutPublishableInteropLibs(project)
else project.files() else project.files()
}
override fun getClasspath(): FileCollection = libraries override fun getClasspath(): FileCollection = libraries
override fun setClasspath(configuration: FileCollection?) { override fun setClasspath(configuration: FileCollection?) {
throw UnsupportedOperationException("Setting classpath directly is unsupported.") throw UnsupportedOperationException("Setting classpath directly is unsupported.")
} }
val target: String @get:Input
@Input get() = compilation.konanTarget.name val target: String by project.provider { compilation.get().konanTarget.name }
// region Compiler options. // region Compiler options.
@get:Internal @get:Internal
@@ -158,11 +159,12 @@ abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions> : Abstra
abstract fun kotlinOptions(fn: Closure<*>) abstract fun kotlinOptions(fn: Closure<*>)
@get:Input @get:Input
abstract val additionalCompilerOptions: Collection<String> abstract val additionalCompilerOptions: Provider<Collection<String>>
@get:Internal @get:Internal
val languageSettings: LanguageSettingsBuilder val languageSettings: LanguageSettingsBuilder by lazy {
get() = compilation.defaultSourceSet.languageSettings compilation.get().defaultSourceSet.languageSettings
}
@get:Input @get:Input
val progressiveMode: Boolean val progressiveMode: Boolean
@@ -170,8 +172,8 @@ abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions> : Abstra
// endregion. // endregion.
@get:Input @get:Input
val enableEndorsedLibs: Boolean val enableEndorsedLibs by
get() = compilation.enableEndorsedLibs project.provider { compilation.map { it.enableEndorsedLibs }.get() }
val kotlinNativeVersion: String val kotlinNativeVersion: String
@Input get() = project.konanVersion.toString() @Input get() = project.konanVersion.toString()
@@ -179,11 +181,11 @@ abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions> : Abstra
// OutputFile is located under the destinationDir, so there is no need to register it as a separate output. // OutputFile is located under the destinationDir, so there is no need to register it as a separate output.
@Internal @Internal
val outputFile: Provider<File> = project.provider { val outputFile: Provider<File> = project.provider {
val konanTarget = compilation.konanTarget val konanTarget = compilation.get().konanTarget
val prefix = outputKind.prefix(konanTarget) val prefix = outputKind.prefix(konanTarget)
val suffix = outputKind.suffix(konanTarget) val suffix = outputKind.suffix(konanTarget)
val filename = "$prefix$baseName$suffix".let { val filename = "$prefix${baseName}$suffix".let {
when { when {
outputKind == FRAMEWORK -> outputKind == FRAMEWORK ->
it.asValidFrameworkName() it.asValidFrameworkName()
@@ -241,10 +243,10 @@ abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions> : Abstra
addKey("-progressive", progressiveMode) addKey("-progressive", progressiveMode)
if (!defaultsOnly) { if (!defaultsOnly) {
addAll(additionalCompilerOptions) addAll(additionalCompilerOptions.get())
} }
(compilation.defaultSourceSet.languageSettings as? DefaultLanguageSettingsBuilder)?.run { (compilation.get().defaultSourceSet.languageSettings as? DefaultLanguageSettingsBuilder)?.run {
addAll(freeCompilerArgs) addAll(freeCompilerArgs)
} }
} }
@@ -274,7 +276,7 @@ abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions> : Abstra
addArg("-target", target) addArg("-target", target)
addArg("-p", outputKind.name.toLowerCase()) addArg("-p", outputKind.name.toLowerCase())
if (compilation is KotlinSharedNativeCompilation) { if (compilation.get() is KotlinSharedNativeCompilation) {
add("-Xexpect-actual-linker") add("-Xexpect-actual-linker")
add("-Xmetadata-klib") add("-Xmetadata-klib")
addArg("-manifest", manifestFile.get().absolutePath) addArg("-manifest", manifestFile.get().absolutePath)
@@ -301,7 +303,7 @@ abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions> : Abstra
val output = outputFile.get() val output = outputFile.get()
output.parentFile.mkdirs() output.parentFile.mkdirs()
if (compilation is KotlinSharedNativeCompilation) { if (compilation.get() is KotlinSharedNativeCompilation) {
val manifestFile: File = manifestFile.get() val manifestFile: File = manifestFile.get()
manifestFile.ensureParentDirsCreated() manifestFile.ensureParentDirsCreated()
val properties = java.util.Properties() val properties = java.util.Properties()
@@ -316,9 +318,12 @@ abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions> : Abstra
/** /**
* A task producing a klibrary from a compilation. * A task producing a klibrary from a compilation.
*/ */
open class KotlinNativeCompile : AbstractKotlinNativeCompile<KotlinCommonOptions>(), KotlinCompile<KotlinCommonOptions> { open class KotlinNativeCompile : AbstractKotlinNativeCompile<KotlinCommonOptions, AbstractKotlinNativeCompilation>(),
KotlinCompile<KotlinCommonOptions> {
@Internal @Internal
final override lateinit var compilation: AbstractKotlinNativeCompilation @Transient // can't be serialized for Gradle configuration avoidance
final override val compilation: Property<AbstractKotlinNativeCompilation> =
project.newProperty()
@get:Input @get:Input
override val outputKind = LIBRARY override val outputKind = LIBRARY
@@ -330,8 +335,11 @@ open class KotlinNativeCompile : AbstractKotlinNativeCompile<KotlinCommonOptions
override val debuggable = true override val debuggable = true
@get:Internal @get:Internal
override val baseName: String override val baseName: String by
get() = if (compilation.isMain()) project.name else "${project.name}_${compilation.name}" compilation.map { if (it.isMain()) project.name else "${project.name}_${it.name}" }
// Store as an explicit provider in order to allow Gradle Instant Execution to capture the state
// private val allSourceProvider = compilation.map { project.files(it.allSources).asFileTree }
@get:Input @get:Input
val moduleName: String by project.provider { val moduleName: String by project.provider {
@@ -347,13 +355,21 @@ open class KotlinNativeCompile : AbstractKotlinNativeCompile<KotlinCommonOptions
@get:Internal // these sources are normally a subset of `source` ones which are already tracked @get:Internal // these sources are normally a subset of `source` ones which are already tracked
val commonSources: ConfigurableFileCollection = project.files() val commonSources: ConfigurableFileCollection = project.files()
// private val commonSources: FileCollection by lazy {
// // Already taken into account in getSources method.
// project.files(compilation.map { it.commonSources }).asFileTree
// }
private val commonSourcesTree: FileTree private val commonSourcesTree: FileTree
get() = commonSources.asFileTree get() = commonSources.asFileTree
private val friendModule: FileCollection?
get() = project.files( private val friendModule: FileCollection by compilation.map { compilationInstance ->
project.provider { compilation.associateWithTransitiveClosure.map { it.output.allOutputs } + compilation.friendArtifacts } project.files(
compilationInstance.associateWithTransitiveClosure.map { it.output.allOutputs },
compilationInstance.friendArtifacts
) )
}
// endregion. // endregion.
// region Language settings imported from a SourceSet. // region Language settings imported from a SourceSet.
@@ -372,11 +388,12 @@ open class KotlinNativeCompile : AbstractKotlinNativeCompile<KotlinCommonOptions
// region Kotlin options. // region Kotlin options.
override val kotlinOptions: KotlinCommonOptions override val kotlinOptions: KotlinCommonOptions
get() = compilation.kotlinOptions get() = compilation.get().kotlinOptions
@get:Input @get:Input
override val additionalCompilerOptions: Collection<String> override val additionalCompilerOptions: Provider<Collection<String>> = project.provider {
get() = kotlinOptions.freeCompilerArgs kotlinOptions.freeCompilerArgs
}
override fun kotlinOptions(fn: KotlinCommonOptions.() -> Unit) { override fun kotlinOptions(fn: KotlinCommonOptions.() -> Unit) {
kotlinOptions.fn() kotlinOptions.fn()
@@ -409,7 +426,7 @@ open class KotlinNativeCompile : AbstractKotlinNativeCompile<KotlinCommonOptions
// Configure FQ module name to avoid cyclic dependencies in klib manifests (see KT-36721). // Configure FQ module name to avoid cyclic dependencies in klib manifests (see KT-36721).
addArg("-module-name", moduleName) addArg("-module-name", moduleName)
add("-Xshort-module-name=$shortModuleName") add("-Xshort-module-name=$shortModuleName")
val friends = friendModule?.files val friends = friendModule.files
if (friends != null && friends.isNotEmpty()) { if (friends != null && friends.isNotEmpty()) {
addArg("-friend-modules", friends.map { it.absolutePath }.joinToString(File.pathSeparator)) addArg("-friend-modules", friends.map { it.absolutePath }.joinToString(File.pathSeparator))
} }
@@ -427,22 +444,23 @@ open class KotlinNativeCompile : AbstractKotlinNativeCompile<KotlinCommonOptions
/** /**
* A task producing a final binary from a compilation. * A task producing a final binary from a compilation.
*/ */
open class KotlinNativeLink : AbstractKotlinNativeCompile<KotlinCommonToolOptions>() { open class KotlinNativeLink : AbstractKotlinNativeCompile<KotlinCommonToolOptions, KotlinNativeCompilation>() {
@get:Internal
@Transient // can't be serialized for Gradle configuration avoidance
final override val compilation: Property<KotlinNativeCompilation> = project.newProperty() { binary.compilation }
init { init {
dependsOn(project.provider { compilation.compileKotlinTask }) dependsOn(project.provider { compilation.get().compileKotlinTask })
} }
@Internal @Internal
@Transient
lateinit var binary: NativeBinary lateinit var binary: NativeBinary
@get:Internal
override val compilation: KotlinNativeCompilation
get() = binary.compilation
@Internal // Taken into account by getSources(). @Internal // Taken into account by getSources().
val intermediateLibrary: Provider<File> = project.provider { val intermediateLibrary: Provider<File> = compilation.map { compilationInstance ->
compilation.compileKotlinTask.outputFile.get() compilationInstance.compileKotlinTask.outputFile.get()
} }
@InputFiles @InputFiles
@@ -459,27 +477,23 @@ open class KotlinNativeLink : AbstractKotlinNativeCompile<KotlinCommonToolOption
binary.outputDirectory = destinationDir binary.outputDirectory = destinationDir
} }
@get:Input
override val outputKind: CompilerOutputKind override val outputKind: CompilerOutputKind
get() = binary.outputKind.compilerOutputKind @Input get() = binary.outputKind.compilerOutputKind
@get:Input
override val optimized: Boolean override val optimized: Boolean
get() = binary.optimized @Input get() = binary.optimized
@get:Input
override val debuggable: Boolean override val debuggable: Boolean
get() = binary.debuggable @Input get() = binary.debuggable
@get:Internal
override val baseName: String override val baseName: String
get() = binary.baseName @Input get() = binary.baseName
@get:Input @get:Input
protected val konanCacheKind: NativeCacheKind protected val konanCacheKind: NativeCacheKind
get() = project.konanCacheKind get() = project.konanCacheKind
inner class NativeLinkOptions: KotlinCommonToolOptions { inner class NativeLinkOptions : KotlinCommonToolOptions {
override var allWarningsAsErrors: Boolean = false override var allWarningsAsErrors: Boolean = false
override var suppressWarnings: Boolean = false override var suppressWarnings: Boolean = false
override var verbose: Boolean = false override var verbose: Boolean = false
@@ -488,8 +502,9 @@ open class KotlinNativeLink : AbstractKotlinNativeCompile<KotlinCommonToolOption
// We propagate compilation free args to the link task for now (see KT-33717). // We propagate compilation free args to the link task for now (see KT-33717).
@get:Input @get:Input
override val additionalCompilerOptions: Collection<String> override val additionalCompilerOptions: Provider<Collection<String>> = compilation.map { compilationInstance ->
get() = kotlinOptions.freeCompilerArgs + compilation.kotlinOptions.freeCompilerArgs kotlinOptions.freeCompilerArgs + compilationInstance.kotlinOptions.freeCompilerArgs
}
override val kotlinOptions: KotlinCommonToolOptions = NativeLinkOptions() override val kotlinOptions: KotlinCommonToolOptions = NativeLinkOptions()
@@ -503,18 +518,16 @@ open class KotlinNativeLink : AbstractKotlinNativeCompile<KotlinCommonToolOption
} }
// Binary-specific options. // Binary-specific options.
@get:Optional
@get:Input
val entryPoint: String? val entryPoint: String?
@Input
@Optional
get() = (binary as? Executable)?.entryPoint get() = (binary as? Executable)?.entryPoint
@get:Input
val linkerOpts: List<String> val linkerOpts: List<String>
get() = binary.linkerOpts @Input get() = binary.linkerOpts
@get:Input
val processTests: Boolean val processTests: Boolean
get() = binary is TestExecutable @Input get() = binary is TestExecutable
@get:InputFiles @get:InputFiles
val exportLibraries: FileCollection val exportLibraries: FileCollection
@@ -527,12 +540,14 @@ open class KotlinNativeLink : AbstractKotlinNativeCompile<KotlinCommonToolOption
} }
@get:Input @get:Input
val isStaticFramework: Boolean val isStaticFramework: Boolean by project.provider {
get() = binary.let { it is Framework && it.isStatic } binary.let { it is Framework && it.isStatic }
}
@get:Input @get:Input
val embedBitcode: Framework.BitcodeEmbeddingMode val embedBitcode: Framework.BitcodeEmbeddingMode by project.provider {
get() = (binary as? Framework)?.embedBitcode ?: Framework.BitcodeEmbeddingMode.DISABLE (binary as? Framework)?.embedBitcode ?: Framework.BitcodeEmbeddingMode.DISABLE
}
override fun buildCompilerArgs(): List<String> = mutableListOf<String>().apply { override fun buildCompilerArgs(): List<String> = mutableListOf<String>().apply {
addAll(super.buildCompilerArgs()) addAll(super.buildCompilerArgs())
@@ -569,9 +584,12 @@ open class KotlinNativeLink : AbstractKotlinNativeCompile<KotlinCommonToolOption
override fun buildSourceArgs(): List<String> = override fun buildSourceArgs(): List<String> =
listOf("-Xinclude=${intermediateLibrary.get().absolutePath}") listOf("-Xinclude=${intermediateLibrary.get().absolutePath}")
@get:Internal
val apiFilesProvider = compilation.map {project.configurations.getByName(it.apiConfigurationName).files.filterKlibsPassedToCompiler(project)}
private fun validatedExportedLibraries() { private fun validatedExportedLibraries() {
val exportConfiguration = exportLibraries as? Configuration ?: return val exportConfiguration = exportLibraries as? Configuration ?: return
val apiFiles = project.configurations.getByName(compilation.apiConfigurationName).files.filterKlibsPassedToCompiler(project) val apiFiles = apiFilesProvider.get()
val failed = mutableSetOf<Dependency>() val failed = mutableSetOf<Dependency>()
exportConfiguration.allDependencies.forEach { exportConfiguration.allDependencies.forEach {
@@ -765,7 +783,11 @@ internal class CacheBuilder(val project: Project, val binary: NativeBinary) {
private val String.cachedName private val String.cachedName
get() = getCacheFileName(this, konanCacheKind) get() = getCacheFileName(this, konanCacheKind)
private fun ensureCompilerProvidedLibPrecached(platformLibName: String, platformLibs: Map<String, File>, visitedLibs: MutableSet<String>) { private fun ensureCompilerProvidedLibPrecached(
platformLibName: String,
platformLibs: Map<String, File>,
visitedLibs: MutableSet<String>
) {
if (platformLibName in visitedLibs) if (platformLibName in visitedLibs)
return return
visitedLibs += platformLibName visitedLibs += platformLibName
@@ -11,6 +11,7 @@ import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleJavaTargetExtension import org.jetbrains.kotlin.gradle.dsl.KotlinSingleJavaTargetExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.gradle.utils.newProperty import org.jetbrains.kotlin.gradle.utils.newProperty
import java.io.File import java.io.File
@@ -27,18 +28,35 @@ internal open class InspectClassesForMultiModuleIC : DefaultTask() {
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
@get:OutputFile @get:OutputFile
internal val classesListFile: File by lazy { internal val classesListFile: File by lazy {
(project.kotlinExtension as KotlinSingleJavaTargetExtension).target.defaultArtifactClassesListFile (project.kotlinExtension as KotlinSingleJavaTargetExtension).target.defaultArtifactClassesListFile.get()
} }
@get:InputFiles
internal val sourceSetOutputClassesDir by lazy {
project.convention.findPlugin(JavaPluginConvention::class.java)?.sourceSets?.findByName(sourceSetName)?.output?.classesDirs
}
@get:Internal
internal val fileTrees
get() = sourceSetOutputClassesDir?.map {
if (isGradleVersionAtLeast(6, 0)) {
objects.fileTree().from(it).include("**/*.class")
} else {
project.fileTree(it).include("**/*.class")
}
}
@get:Internal
internal val objects = project.objects
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
@get:InputFiles @get:InputFiles
internal val classFiles: FileCollection internal val classFiles: FileCollection
get() { get() {
val convention = project.convention.findPlugin(JavaPluginConvention::class.java) if (sourceSetOutputClassesDir != null) {
val sourceSet = convention?.sourceSets?.findByName(sourceSetName) ?: return project.files() return objects.fileCollection().from(fileTrees)
}
val fileTrees = sourceSet.output.classesDirs.map { project.fileTree(it).include("**/*.class") } return objects.fileCollection()
return project.files(fileTrees)
} }
@TaskAction @TaskAction
@@ -96,7 +96,7 @@ open class KotlinCompileCommon : AbstractKotlinCompile<K2MetadataCompilerArgumen
override fun callCompilerAsync(args: K2MetadataCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) { override fun callCompilerAsync(args: K2MetadataCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) {
val messageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors) val messageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors)
val outputItemCollector = OutputItemsCollectorImpl() val outputItemCollector = OutputItemsCollectorImpl()
val compilerRunner = compilerRunner() val compilerRunner = compilerRunner
val environment = GradleCompilerEnvironment( val environment = GradleCompilerEnvironment(
computedCompilerClasspath, messageCollector, outputItemCollector, computedCompilerClasspath, messageCollector, outputItemCollector,
buildReportMode = buildReportMode, buildReportMode = buildReportMode,
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.tasks
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task import org.gradle.api.Task
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.logging.Logger
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.AbstractCompile
@@ -20,6 +21,7 @@ import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.compilerRunner.* import org.jetbrains.kotlin.compilerRunner.*
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner.Companion.normalizeForFlagFile
import org.jetbrains.kotlin.daemon.common.MultiModuleICSettings import org.jetbrains.kotlin.daemon.common.MultiModuleICSettings
import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.incremental.ChangedFiles import org.jetbrains.kotlin.gradle.incremental.ChangedFiles
@@ -35,8 +37,12 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithTransitiveClosure import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithTransitiveClosure
import org.jetbrains.kotlin.gradle.plugin.mpp.ownModuleName import org.jetbrains.kotlin.gradle.plugin.mpp.ownModuleName
import org.jetbrains.kotlin.gradle.report.BuildReportMode import org.jetbrains.kotlin.gradle.report.BuildReportMode
import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.gradle.utils.getValue
import org.jetbrains.kotlin.gradle.utils.isParentOf
import org.jetbrains.kotlin.gradle.utils.pathsAsStringRelativeTo
import org.jetbrains.kotlin.gradle.utils.toSortedPathsArray
import org.jetbrains.kotlin.incremental.ChangedFiles import org.jetbrains.kotlin.incremental.ChangedFiles
import org.jetbrains.kotlin.incremental.IncrementalModuleInfo
import org.jetbrains.kotlin.library.impl.isKotlinLibrary import org.jetbrains.kotlin.library.impl.isKotlinLibrary
import org.jetbrains.kotlin.utils.JsLibraryUtils import org.jetbrains.kotlin.utils.JsLibraryUtils
import java.io.File import java.io.File
@@ -87,7 +93,7 @@ abstract class AbstractKotlinCompileTool<T : CommonToolArguments>
@get:Classpath @get:Classpath
@get:InputFiles @get:InputFiles
internal val computedCompilerClasspath: List<File> by project.provider { internal val computedCompilerClasspath: List<File> by lazy {
compilerClasspath?.takeIf { it.isNotEmpty() } compilerClasspath?.takeIf { it.isNotEmpty() }
?: compilerJarFile?.let { ?: compilerJarFile?.let {
// a hack to remove compiler jar from the cp, will be dropped when compilerJarFile will be removed // a hack to remove compiler jar from the cp, will be dropped when compilerJarFile will be removed
@@ -113,19 +119,51 @@ abstract class AbstractKotlinCompileTool<T : CommonToolArguments>
protected abstract fun findKotlinCompilerClasspath(project: Project): List<File> protected abstract fun findKotlinCompilerClasspath(project: Project): List<File>
} }
public class GradleCompileTaskProvider {
constructor(task: Task) {
buildDir = task.project.buildDir
projectDir = task.project.rootProject.projectDir
rootDir = task.project.rootProject.rootDir
sessionsDir = GradleCompilerRunner.sessionsDir(task.project)
projectName = task.project.rootProject.name.normalizeForFlagFile()
buildModulesInfo = GradleCompilerRunner.buildModulesInfo(task.project.gradle)
path = task.path
logger = task.logger
}
val path: String
val logger: Logger
val buildDir: File /*= project.buildDir*/
val projectDir: File /*= project.rootProject.projectDir*/
val rootDir: File /*= project.rootProject.rootDir*/
val sessionsDir: File/* = GradleCompilerRunner.sessionsDir(project)*/
val projectName: String /*= project.rootProject.name.normalizeForFlagFile()*/
val buildModulesInfo: IncrementalModuleInfo /*= GradleCompilerRunner.buildModulesInfo(project.gradle)*/
}
abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKotlinCompileTool<T>() { abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKotlinCompileTool<T>() {
init { init {
cacheOnlyIfEnabledForKotlin() cacheOnlyIfEnabledForKotlin()
} }
@get:Internal
private val layout = project.layout
// avoid creating directory in getter: this can lead to failure in parallel build // avoid creating directory in getter: this can lead to failure in parallel build
@get:LocalState @get:LocalState
internal val taskBuildDirectory: File by project.provider { internal val taskBuildDirectory: File by layout.buildDirectory.dir(KOTLIN_BUILD_DIR_NAME).map { it.file(name).asFile }
File(File(project.buildDir, KOTLIN_BUILD_DIR_NAME), name)
}
override fun localStateDirectories(): FileCollection = project.files(taskBuildDirectory)
@get:Internal
internal val projectObjects = project.objects
@get:LocalState
internal val localStateDirectoriesProvider: FileCollection = projectObjects.fileCollection().from(taskBuildDirectory)
override fun localStateDirectories(): FileCollection = localStateDirectoriesProvider
// indicates that task should compile kotlin incrementally if possible // indicates that task should compile kotlin incrementally if possible
// it's not possible when IncrementalTaskInputs#isIncremental returns false (i.e first build) // it's not possible when IncrementalTaskInputs#isIncremental returns false (i.e first build)
@@ -147,9 +185,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
internal var buildReportMode: BuildReportMode? = null internal var buildReportMode: BuildReportMode? = null
@get:Internal @get:Internal
internal val taskData: KotlinCompileTaskData by project.provider { internal val taskData: KotlinCompileTaskData = KotlinCompileTaskData.get(project, name)
KotlinCompileTaskData.get(project, name)
}
@get:Input @get:Input
internal open var useModuleDetection: Boolean internal open var useModuleDetection: Boolean
@@ -162,11 +198,9 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
protected val multiModuleICSettings: MultiModuleICSettings protected val multiModuleICSettings: MultiModuleICSettings
get() = MultiModuleICSettings(taskData.buildHistoryFile, useModuleDetection) get() = MultiModuleICSettings(taskData.buildHistoryFile, useModuleDetection)
@get:Classpath
@get:InputFiles @get:InputFiles
val pluginClasspath: FileCollection by project.provider { @get:Classpath
project.configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME) val pluginClasspath: FileCollection = project.configurations.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME)
}
@get:Internal @get:Internal
internal val pluginOptions = CompilerPluginOptions() internal val pluginOptions = CompilerPluginOptions()
@@ -175,10 +209,13 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
@get:InputFiles @get:InputFiles
protected val additionalClasspath = arrayListOf<File>() protected val additionalClasspath = arrayListOf<File>()
@get:Internal
internal val objects = project.objects
// Store this file collection before it is filtered by File::exists to ensure that Gradle Instant execution doesn't serialize the // Store this file collection before it is filtered by File::exists to ensure that Gradle Instant execution doesn't serialize the
// filtered files, losing those that don't exist yet and will only be created during build // filtered files, losing those that don't exist yet and will only be created during build
private val compileClasspathImpl by project.provider { private val compileClasspathImpl by project.provider {
classpath + project.files(additionalClasspath) classpath + objects.fileCollection().from(additionalClasspath)
} }
@get:Internal // classpath already participates in the checks @get:Internal // classpath already participates in the checks
@@ -197,8 +234,9 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
sourceFilesExtensionsSources.add(extensions) sourceFilesExtensionsSources.add(extensions)
} }
private val kotlinExt: KotlinProjectExtension @get:Internal
get() = project.extensions.findByType(KotlinProjectExtension::class.java)!! @field:Transient
internal val kotlinExtProvider: KotlinProjectExtension = project.extensions.findByType(KotlinProjectExtension::class.java)!!
override fun getDestinationDir(): File = override fun getDestinationDir(): File =
taskData.destinationDir.get() taskData.destinationDir.get()
@@ -218,13 +256,13 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
@get:Internal @get:Internal
internal var coroutinesFromGradleProperties: Coroutines? = null internal var coroutinesFromGradleProperties: Coroutines? = null
// Input is needed to force rebuild even if source files are not changed // Input is needed to force rebuild even if source files are not changed
@get:Input
internal val coroutinesStr: String
get() = coroutines.name
@get:Internal @get:Input
internal val coroutines: Coroutines by project.provider { internal val coroutinesStr: Provider<String> = project.provider {coroutines.get().name}
kotlinExt.experimental.coroutines
@get:Input
internal val coroutines: Provider<Coroutines> = project.provider {
kotlinExtProvider.experimental.coroutines
?: coroutinesFromGradleProperties ?: coroutinesFromGradleProperties
?: Coroutines.DEFAULT ?: Coroutines.DEFAULT
} }
@@ -242,7 +280,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
@get:InputFiles @get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE) @get:PathSensitive(PathSensitivity.RELATIVE)
internal var commonSourceSet: FileCollection = project.files() internal var commonSourceSet: FileCollection = project.files() //TODO
@get:Input @get:Input
internal val moduleName: String by project.provider { internal val moduleName: String by project.provider {
@@ -262,8 +300,10 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
private val kotlinLogger by lazy { GradleKotlinLogger(logger) } private val kotlinLogger by lazy { GradleKotlinLogger(logger) }
internal open fun compilerRunner(): GradleCompilerRunner = @get:Internal
GradleCompilerRunner(this) internal val compilerRunner by lazy { compilerRunner() }
internal open fun compilerRunner(): GradleCompilerRunner = GradleCompilerRunner(GradleCompileTaskProvider(this))
@TaskAction @TaskAction
fun execute(inputs: IncrementalTaskInputs) { fun execute(inputs: IncrementalTaskInputs) {
@@ -297,6 +337,9 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
return !inputs.isIncremental && getSourceRoots().kotlinSourceFiles.isEmpty() return !inputs.isIncremental && getSourceRoots().kotlinSourceFiles.isEmpty()
} }
@get:Internal
private val projectDir = project.rootProject.projectDir
private fun executeImpl(inputs: IncrementalTaskInputs) { private fun executeImpl(inputs: IncrementalTaskInputs) {
// Check that the JDK tools are available in Gradle (fail-fast, instead of a fail during the compiler run): // Check that the JDK tools are available in Gradle (fail-fast, instead of a fail during the compiler run):
findToolsJar() findToolsJar()
@@ -304,7 +347,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
val sourceRoots = getSourceRoots() val sourceRoots = getSourceRoots()
val allKotlinSources = sourceRoots.kotlinSourceFiles val allKotlinSources = sourceRoots.kotlinSourceFiles
logger.kotlinDebug { "All kotlin sources: ${allKotlinSources.pathsAsStringRelativeTo(project.rootProject.projectDir)}" } logger.kotlinDebug { "All kotlin sources: ${allKotlinSources.pathsAsStringRelativeTo(projectDir)}" }
if (skipCondition(inputs)) { if (skipCondition(inputs)) {
// Skip running only if non-incremental run. Otherwise, we may need to do some cleanup. // Skip running only if non-incremental run. Otherwise, we may need to do some cleanup.
@@ -327,13 +370,14 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
*/ */
internal abstract fun callCompilerAsync(args: T, sourceRoots: SourceRoots, changedFiles: ChangedFiles) internal abstract fun callCompilerAsync(args: T, sourceRoots: SourceRoots, changedFiles: ChangedFiles)
@get:Input
internal val isMultiplatform: Boolean by lazy { project.plugins.any { it is KotlinPlatformPluginBase || it is KotlinMultiplatformPluginWrapper } }
@get:Internal @get:Internal
internal val isMultiplatform: Boolean by project.provider { internal val abstractKotlinCompileArgumentsContributor by lazy { AbstractKotlinCompileArgumentsContributor(KotlinCompileArgumentsProvider(this)) }
project.plugins.any { it is KotlinPlatformPluginBase || it is KotlinMultiplatformPluginWrapper }
}
override fun setupCompilerArgs(args: T, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) { override fun setupCompilerArgs(args: T, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
AbstractKotlinCompileArgumentsContributor(thisTaskProvider).contributeArguments( abstractKotlinCompileArgumentsContributor.contributeArguments(
args, args,
compilerArgumentsConfigurationFlags(defaultsOnly, ignoreClasspathResolutionErrors) compilerArgumentsConfigurationFlags(defaultsOnly, ignoreClasspathResolutionErrors)
) )
@@ -350,6 +394,45 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
} }
} }
open class KotlinCompileArgumentsProvider<T : AbstractKotlinCompile<out CommonCompilerArguments>>(taskProvider: T) {
val coroutines: Provider<Coroutines>
val logger: Logger
val isMultiplatform: Boolean
val pluginClasspath: FileCollection
val pluginOptions: CompilerPluginOptions
init {
coroutines = taskProvider.coroutines
logger = taskProvider.logger
isMultiplatform = taskProvider.isMultiplatform
pluginClasspath = taskProvider.pluginClasspath
pluginOptions = taskProvider.pluginOptions
}
}
class KotlinJvmCompilerArgumentsProvider
(taskProvider: KotlinCompile) : KotlinCompileArgumentsProvider<KotlinCompile>(taskProvider) {
val moduleName: String
val friendPaths: Array<String>
val compileClasspath: Iterable<File>
val destinationDir: File
internal val kotlinOptions: List<KotlinJvmOptionsImpl?>
init {
// super(taskProvider)
moduleName = taskProvider.moduleName
friendPaths = taskProvider.friendPaths
compileClasspath = taskProvider.compileClasspath
destinationDir = taskProvider.destinationDir
kotlinOptions = listOfNotNull(
taskProvider.parentKotlinOptionsImpl as KotlinJvmOptionsImpl?,
taskProvider.kotlinOptions as KotlinJvmOptionsImpl
)
}
}
internal inline val <reified T : Task> T.thisTaskProvider: TaskProvider<out T> internal inline val <reified T : Task> T.thisTaskProvider: TaskProvider<out T>
get() = checkNotNull(project.locateTask<T>(name)) get() = checkNotNull(project.locateTask<T>(name))
@@ -397,8 +480,8 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
} }
@get:Internal @get:Internal
internal val compilerArgumentsContributor: CompilerArgumentsContributor<K2JVMCompilerArguments> by project.provider { internal val compilerArgumentsContributor: CompilerArgumentsContributor<K2JVMCompilerArguments> by lazy {
KotlinJvmCompilerArgumentsContributor(thisTaskProvider) KotlinJvmCompilerArgumentsContributor(KotlinJvmCompilerArgumentsProvider(this))
} }
@Internal @Internal
@@ -409,7 +492,7 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
val messageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors) val messageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors)
val outputItemCollector = OutputItemsCollectorImpl() val outputItemCollector = OutputItemsCollectorImpl()
val compilerRunner = compilerRunner() val compilerRunner = compilerRunner
val icEnv = if (isIncrementalCompilationEnabled()) { val icEnv = if (isIncrementalCompilationEnabled()) {
logger.info(USING_JVM_INCREMENTAL_COMPILATION_MESSAGE) logger.info(USING_JVM_INCREMENTAL_COMPILATION_MESSAGE)
@@ -417,7 +500,7 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
if (hasFilesInTaskBuildDirectory()) changedFiles else ChangedFiles.Unknown(), if (hasFilesInTaskBuildDirectory()) changedFiles else ChangedFiles.Unknown(),
taskBuildDirectory, taskBuildDirectory,
usePreciseJavaTracking = usePreciseJavaTracking, usePreciseJavaTracking = usePreciseJavaTracking,
disableMultiModuleIC = disableMultiModuleIC(), disableMultiModuleIC = disableMultiModuleIC,
multiModuleICSettings = multiModuleICSettings multiModuleICSettings = multiModuleICSettings
) )
} else null } else null
@@ -439,31 +522,32 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
) )
} }
private fun disableMultiModuleIC(): Boolean { @get:Input
if (!isIncrementalCompilationEnabled() || javaOutputDir == null) return false val disableMultiModuleIC: Boolean by lazy {
if (!isIncrementalCompilationEnabled() || javaOutputDir == null) {
false
} else {
var illegalTaskOrNull: AbstractCompile? = null var illegalTaskOrNull: AbstractCompile? = null
project.tasks.configureEach {
project.tasks.configureEach { if (it is AbstractCompile &&
if (it is AbstractCompile && it !is JavaCompile &&
it !is JavaCompile && it !is AbstractKotlinCompile<*> &&
it !is AbstractKotlinCompile<*> && javaOutputDir!!.isParentOf(it.destinationDir)
javaOutputDir!!.isParentOf(it.destinationDir) ) {
) { illegalTaskOrNull = illegalTaskOrNull ?: it
illegalTaskOrNull = illegalTaskOrNull ?: it }
} }
if (illegalTaskOrNull != null) {
val illegalTask = illegalTaskOrNull!!
logger.info(
"Kotlin inter-project IC is disabled: " +
"unknown task '$illegalTask' destination dir ${illegalTask.destinationDir} " +
"intersects with java destination dir $javaOutputDir"
)
}
illegalTaskOrNull != null
} }
illegalTaskOrNull?.let { illegalTask ->
logger.info(
"Kotlin inter-project IC is disabled: " +
"unknown task '$illegalTask' destination dir ${illegalTask.destinationDir} " +
"intersects with java destination dir $javaOutputDir"
)
return true
}
return false
} }
// override setSource to track source directory sets and files (for generated android folders) // override setSource to track source directory sets and files (for generated android folders)
@@ -483,21 +567,26 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
internal open class KotlinCompileWithWorkers @Inject constructor( internal open class KotlinCompileWithWorkers @Inject constructor(
private val workerExecutor: WorkerExecutor private val workerExecutor: WorkerExecutor
) : KotlinCompile() { ) : KotlinCompile() {
override fun compilerRunner() = GradleCompilerRunnerWithWorkers(this, workerExecutor)
@get:Internal
val compilerRunnerValue = GradleCompilerRunnerWithWorkers(GradleCompileTaskProvider(this), workerExecutor)
override fun compilerRunner() = compilerRunnerValue
} }
@CacheableTask @CacheableTask
internal open class Kotlin2JsCompileWithWorkers @Inject constructor( internal open class Kotlin2JsCompileWithWorkers @Inject constructor(
private val workerExecutor: WorkerExecutor private val workerExecutor: WorkerExecutor
) : Kotlin2JsCompile() { ) : Kotlin2JsCompile() {
override fun compilerRunner() = GradleCompilerRunnerWithWorkers(this, workerExecutor)
override fun compilerRunner() = GradleCompilerRunnerWithWorkers(GradleCompileTaskProvider(this), workerExecutor)
} }
@CacheableTask @CacheableTask
internal open class KotlinCompileCommonWithWorkers @Inject constructor( internal open class KotlinCompileCommonWithWorkers @Inject constructor(
private val workerExecutor: WorkerExecutor private val workerExecutor: WorkerExecutor
) : KotlinCompileCommon() { ) : KotlinCompileCommon() {
override fun compilerRunner() = GradleCompilerRunnerWithWorkers(this, workerExecutor) override fun compilerRunner() = GradleCompilerRunnerWithWorkers(GradleCompileTaskProvider(this), workerExecutor)
} }
@CacheableTask @CacheableTask
@@ -507,8 +596,7 @@ open class Kotlin2JsCompile : AbstractKotlinCompile<K2JSCompilerArguments>(), Ko
incremental = true incremental = true
} }
override val kotlinOptions: KotlinJsOptions override val kotlinOptions = taskData.compilation.kotlinOptions as KotlinJsOptions
get() = taskData.compilation.kotlinOptions as KotlinJsOptions
@get:Internal @get:Internal
protected val defaultOutputFile: File protected val defaultOutputFile: File
@@ -601,6 +689,9 @@ open class Kotlin2JsCompile : AbstractKotlinCompile<K2JSCompilerArguments>(), Ko
JsLibraryUtils::isKotlinJavascriptLibrary JsLibraryUtils::isKotlinJavascriptLibrary
} }
@get:Internal
internal val absolutePathProvider = project.projectDir.absolutePath
override fun callCompilerAsync(args: K2JSCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) { override fun callCompilerAsync(args: K2JSCompilerArguments, sourceRoots: SourceRoots, changedFiles: ChangedFiles) {
sourceRoots as SourceRoots.KotlinOnly sourceRoots as SourceRoots.KotlinOnly
@@ -624,14 +715,14 @@ open class Kotlin2JsCompile : AbstractKotlinCompile<K2JSCompilerArguments>(), Ko
args.friendModules = friendDependencies.joinToString(File.pathSeparator) args.friendModules = friendDependencies.joinToString(File.pathSeparator)
if (args.sourceMapBaseDirs == null && !args.sourceMapPrefix.isNullOrEmpty()) { if (args.sourceMapBaseDirs == null && !args.sourceMapPrefix.isNullOrEmpty()) {
args.sourceMapBaseDirs = project.projectDir.absolutePath args.sourceMapBaseDirs = absolutePathProvider
} }
logger.kotlinDebug("compiling with args ${ArgumentUtils.convertArgumentsToStringList(args)}") logger.kotlinDebug("compiling with args ${ArgumentUtils.convertArgumentsToStringList(args)}")
val messageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors) val messageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors)
val outputItemCollector = OutputItemsCollectorImpl() val outputItemCollector = OutputItemsCollectorImpl()
val compilerRunner = compilerRunner() val compilerRunner = compilerRunner
val icEnv = if (isIncrementalCompilationEnabled()) { val icEnv = if (isIncrementalCompilationEnabled()) {
logger.info(USING_JS_INCREMENTAL_COMPILATION_MESSAGE) logger.info(USING_JS_INCREMENTAL_COMPILATION_MESSAGE)
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.gradle.utils package org.jetbrains.kotlin.gradle.utils
import org.gradle.api.invocation.Gradle
import org.gradle.util.GradleVersion import org.gradle.util.GradleVersion
internal data class ParsedGradleVersion(val major: Int, val minor: Int) : Comparable<ParsedGradleVersion> { internal data class ParsedGradleVersion(val major: Int, val minor: Int) : Comparable<ParsedGradleVersion> {
@@ -56,4 +57,12 @@ internal data class ParsedGradleVersion(val major: Int, val minor: Int) : Compar
fun isGradleVersionAtLeast(major: Int, minor: Int) = fun isGradleVersionAtLeast(major: Int, minor: Int) =
ParsedGradleVersion.parse(GradleVersion.current().version) ParsedGradleVersion.parse(GradleVersion.current().version)
?.let { it >= ParsedGradleVersion(major, minor) } ?: false ?.let { it >= ParsedGradleVersion(major, minor) } ?: false
fun isConfigurationCacheAvailable(gradle: Gradle) =
try {
val startParameters = gradle.startParameter
startParameters.javaClass.getMethod("isConfigurationCache").invoke(startParameters) as? Boolean
} catch (_: Exception) {
null
} ?: false
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.gradle.utils package org.jetbrains.kotlin.gradle.utils
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.file.RegularFile
import org.gradle.api.file.RegularFileProperty import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
@@ -20,14 +19,6 @@ internal operator fun <T> Property<T>.setValue(thisRef: Any?, property: KPropert
set(value) set(value)
} }
internal fun <T : Any> Project.newProperty(initialize: (() -> T)? = null): Property<T> =
@Suppress("UNCHECKED_CAST")
// use Any and not T::class to allow using lists and maps as the property type, which is otherwise not allowed
(project.objects.property(Any::class.java) as Property<T>).apply {
if (initialize != null)
set(provider(initialize))
}
private class OptionalProviderDelegate<T>(private val provider: Provider<T?>) : ReadOnlyProperty<Any?, T?> { private class OptionalProviderDelegate<T>(private val provider: Provider<T?>) : ReadOnlyProperty<Any?, T?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T? = override fun getValue(thisRef: Any?, property: KProperty<*>): T? =
if (provider.isPresent) if (provider.isPresent)
@@ -38,6 +29,13 @@ private class OptionalProviderDelegate<T>(private val provider: Provider<T?>) :
internal fun <T> Project.optionalProvider(initialize: () -> T?): ReadOnlyProperty<Any?, T?> = internal fun <T> Project.optionalProvider(initialize: () -> T?): ReadOnlyProperty<Any?, T?> =
OptionalProviderDelegate(provider(initialize)) OptionalProviderDelegate(provider(initialize))
internal fun <T : Any> Project.newProperty(initialize: (() -> T)? = null): Property<T> =
@Suppress("UNCHECKED_CAST")
(project.objects.property(Any::class.java) as Property<T>).apply {
if (initialize != null)
set(provider(initialize))
}
// Before 5.0 fileProperty is created via ProjectLayout // Before 5.0 fileProperty is created via ProjectLayout
// https://docs.gradle.org/current/javadoc/org/gradle/api/model/ObjectFactory.html#fileProperty-- // https://docs.gradle.org/current/javadoc/org/gradle/api/model/ObjectFactory.html#fileProperty--
internal fun Project.newFileProperty(initialize: (() -> File)? = null): RegularFileProperty { internal fun Project.newFileProperty(initialize: (() -> File)? = null): RegularFileProperty {