Drop support for Gradle 3.x
#KT-27885 Fixed
This commit is contained in:
+2
-7
@@ -47,14 +47,9 @@ enum class KotlinPlatformType: Named, Serializable {
|
||||
|
||||
fun setupAttributesMatchingStrategy(attributesSchema: AttributesSchema) {
|
||||
attributesSchema.attribute(KotlinPlatformType.attribute).run {
|
||||
if (isGradleAtLeast(4, 0)) {
|
||||
compatibilityRules.add(CompatibilityRule::class.java)
|
||||
disambiguationRules.add(DisambiguationRule::class.java)
|
||||
}
|
||||
compatibilityRules.add(CompatibilityRule::class.java)
|
||||
disambiguationRules.add(DisambiguationRule::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isGradleAtLeast(major: Int, minor: Int) =
|
||||
GradleVersion.current() >= GradleVersion.version("$major.$minor")
|
||||
+5
-18
@@ -14,7 +14,7 @@ import kotlin.test.assertTrue
|
||||
|
||||
class KotlinAndroidGradleIT : AbstractKotlinAndroidGradleTests(androidGradlePluginVersion = "2.3.0") {
|
||||
override val defaultGradleVersion: GradleVersionRequired
|
||||
get() = GradleVersionRequired.InRange("3.4", "4.10.2")
|
||||
get() = GradleVersionRequired.InRange("4.0", "4.10.2")
|
||||
}
|
||||
|
||||
// TODO If we there is a way to fetch the latest Android plugin version, test against the latest version
|
||||
@@ -329,17 +329,6 @@ abstract class AbstractKotlinAndroidGradleTests(val androidGradlePluginVersion:
|
||||
// Execute 'assembleAndroidTest' first, without 'build' side effects
|
||||
project.build("assembleAndroidTest") {
|
||||
assertSuccessful()
|
||||
if (project.testGradleVersionBelow("4.0")) {
|
||||
val tasks = ArrayList<String>().apply {
|
||||
for (subProject in listOf("Android", "Lib")) {
|
||||
for (flavor in listOf("Flavor1", "Flavor2")) {
|
||||
add(":$subProject:copy${flavor}DebugKotlinClasses")
|
||||
}
|
||||
}
|
||||
}
|
||||
// with the new AGP we don't need copy classes tasks
|
||||
assertTasksExecuted(tasks)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,12 +579,10 @@ fun getSomething() = 10
|
||||
":libAndroid:compileReleaseUnitTestKotlin"
|
||||
)
|
||||
|
||||
val kotlinFolder = if (project.testGradleVersionAtLeast("4.0")) "kotlin" else ""
|
||||
|
||||
assertFileExists("lib/build/classes/$kotlinFolder/main/foo/PlatformClass.kotlin_metadata")
|
||||
assertFileExists("lib/build/classes/$kotlinFolder/test/foo/PlatformTest.kotlin_metadata")
|
||||
assertFileExists("libJvm/build/classes/$kotlinFolder/main/foo/PlatformClass.class")
|
||||
assertFileExists("libJvm/build/classes/$kotlinFolder/test/foo/PlatformTest.class")
|
||||
assertFileExists("lib/build/classes/kotlin/main/foo/PlatformClass.kotlin_metadata")
|
||||
assertFileExists("lib/build/classes/kotlin/test/foo/PlatformTest.kotlin_metadata")
|
||||
assertFileExists("libJvm/build/classes/kotlin/main/foo/PlatformClass.class")
|
||||
assertFileExists("libJvm/build/classes/kotlin/test/foo/PlatformTest.class")
|
||||
|
||||
assertFileExists("libAndroid/build/tmp/kotlin-classes/debug/foo/PlatformClass.class")
|
||||
assertFileExists("libAndroid/build/tmp/kotlin-classes/release/foo/PlatformClass.class")
|
||||
|
||||
+2
-6
@@ -511,14 +511,10 @@ abstract class BaseGradleIT {
|
||||
assertSameFiles(sources, compiledJavaSources.projectRelativePaths(this.project), "Compiled Java files differ:\n ")
|
||||
|
||||
fun Project.resourcesDir(subproject: String? = null, sourceSet: String = "main"): String =
|
||||
(subproject?.plus("/") ?: "") + "build/" +
|
||||
(if (testGradleVersionBelow("4.0")) "classes/" else "resources/") +
|
||||
sourceSet + "/"
|
||||
(subproject?.plus("/") ?: "") + "build/resources/$sourceSet/"
|
||||
|
||||
fun Project.classesDir(subproject: String? = null, sourceSet: String = "main", language: String = "kotlin"): String =
|
||||
(subproject?.plus("/") ?: "") + "build/classes/" +
|
||||
(if (testGradleVersionAtLeast("4.0")) "$language/" else "") +
|
||||
sourceSet + "/"
|
||||
(subproject?.plus("/") ?: "") + "build/classes/$language/$sourceSet/"
|
||||
|
||||
fun Project.testGradleVersionAtLeast(version: String): Boolean =
|
||||
GradleVersion.version(chooseWrapperVersionOrFinishTest()) >= GradleVersion.version(version)
|
||||
|
||||
+1
-4
@@ -21,10 +21,7 @@ import org.junit.Assume
|
||||
|
||||
sealed class GradleVersionRequired(val minVersion: String, val maxVersion: String?) {
|
||||
companion object {
|
||||
//TODO once the plugin is compiled with API level 1.0, replace with the really supported one (3.2)
|
||||
// Currently, it will lead to failing tests due to the conflict with the pre-release kotlin-reflect
|
||||
// bundled into Gradle 3.2...3.4
|
||||
const val OLDEST_SUPPORTED = "3.5"
|
||||
const val OLDEST_SUPPORTED = "4.0"
|
||||
}
|
||||
|
||||
class Exact(version: String) : GradleVersionRequired(version, version)
|
||||
|
||||
-22
@@ -308,28 +308,6 @@ open class Kapt3IT : Kapt3BaseIT() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testKaptClassesDirSync() {
|
||||
val project = Project("autoService", GradleVersionRequired.Exact("3.5"), directoryPrefix = "kapt2")
|
||||
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
assertKaptSuccessful()
|
||||
assertFileExists("processor/build/classes/main/META-INF/services/javax.annotation.processing.Processor")
|
||||
assertFileExists("processor/build/classes/main/processor/MyProcessor.class")
|
||||
}
|
||||
|
||||
project.projectDir.getFileByName("MyProcessor.kt").modify {
|
||||
it.replace("@AutoService(Processor::class)", "")
|
||||
}
|
||||
|
||||
project.build(":processor:build") {
|
||||
assertSuccessful()
|
||||
assertNoSuchFile("processor/build/classes/main/META-INF/services/javax.annotation.processing.Processor")
|
||||
assertFileExists("processor/build/classes/main/processor/MyProcessor.class")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that compile arguments are properly copied from compileKotlin to kaptTask
|
||||
*/
|
||||
|
||||
+4
-4
@@ -133,7 +133,7 @@ class Kotlin2JsGradlePluginIT : BaseGradleIT() {
|
||||
|
||||
@Test
|
||||
fun testCompilerTestAccessInternalProduction() {
|
||||
val project = Project("kotlin2JsInternalTest", GradleVersionRequired.Exact("3.5"))
|
||||
val project = Project("kotlin2JsInternalTest")
|
||||
|
||||
project.build("runRhino") {
|
||||
assertSuccessful()
|
||||
@@ -167,7 +167,7 @@ class Kotlin2JsGradlePluginIT : BaseGradleIT() {
|
||||
|
||||
@Test
|
||||
fun testKotlinJsBuiltins() {
|
||||
val project = Project("kotlinBuiltins", GradleVersionRequired.AtLeast("4.0"))
|
||||
val project = Project("kotlinBuiltins")
|
||||
|
||||
project.setupWorkingDir()
|
||||
val buildGradle = File(project.projectDir, "app").getFileByName("build.gradle")
|
||||
@@ -309,7 +309,7 @@ class Kotlin2JsGradlePluginIT : BaseGradleIT() {
|
||||
/** Issue: KT-18495 */
|
||||
@Test
|
||||
fun testNoSeparateClassesDirWarning() {
|
||||
val project = Project("kotlin2JsProject", GradleVersionRequired.AtLeast("4.0"))
|
||||
val project = Project("kotlin2JsProject")
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
assertNotContains("this build assumes a single directory for all classes from a source set")
|
||||
@@ -317,7 +317,7 @@ class Kotlin2JsGradlePluginIT : BaseGradleIT() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIncrementalCompilation() = Project("kotlin2JsICProject", GradleVersionRequired.AtLeast("4.0")).run {
|
||||
fun testIncrementalCompilation() = Project("kotlin2JsICProject").run {
|
||||
build("build") {
|
||||
assertSuccessful()
|
||||
assertContains(USING_JS_INCREMENTAL_COMPILATION_MESSAGE)
|
||||
|
||||
+2
-135
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.jetbrains.kotlin.gradle.plugin.CopyClassesToJavaOutputStatus
|
||||
import org.jetbrains.kotlin.gradle.tasks.USING_JVM_INCREMENTAL_COMPILATION_MESSAGE
|
||||
import org.jetbrains.kotlin.gradle.util.*
|
||||
import org.junit.Test
|
||||
@@ -259,24 +258,6 @@ class KotlinGradleIT : BaseGradleIT() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWipeClassesDirectoryBetweenBuilds() {
|
||||
val project = Project("kotlinJavaProject", GradleVersionRequired.Exact("3.5"))
|
||||
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
}
|
||||
|
||||
val javaOutputDir = File(project.projectDir, "build/classes")
|
||||
assert(javaOutputDir.isDirectory) { "Classes directory does not exist $javaOutputDir" }
|
||||
javaOutputDir.deleteRecursively()
|
||||
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
assertTasksUpToDate(":compileKotlin")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMoveClassToOtherModule() {
|
||||
val project = Project("moveClassToOtherModule")
|
||||
@@ -317,7 +298,7 @@ class KotlinGradleIT : BaseGradleIT() {
|
||||
|
||||
@Test
|
||||
fun testKotlinBuiltins() {
|
||||
val project = Project("kotlinBuiltins", GradleVersionRequired.AtLeast("4.0"))
|
||||
val project = Project("kotlinBuiltins")
|
||||
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
@@ -361,42 +342,6 @@ class KotlinGradleIT : BaseGradleIT() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testChangeDestinationDir() {
|
||||
val project = Project("kotlinProject", GradleVersionRequired.Exact("3.5"))
|
||||
project.setupWorkingDir()
|
||||
|
||||
val fileToRemove = File(project.projectDir, "src/main/kotlin/removeMe.kt")
|
||||
fileToRemove.writeText("val x = 1")
|
||||
val classFilePath = "build/classes/main/RemoveMeKt.class"
|
||||
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
assertFileExists(classFilePath)
|
||||
}
|
||||
|
||||
// Check that after the change the build succeeds and no stale classes remain in the java classes dir
|
||||
File(project.projectDir, "build.gradle").modify {
|
||||
"$it\n\ncompileKotlin.destinationDir = file(\"\${project.buildDir}/compileKotlin\")"
|
||||
}
|
||||
fileToRemove.delete()
|
||||
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
assertNoSuchFile(classFilePath)
|
||||
// Check that the fallback to non-incremental copying was chosen
|
||||
assertContains("Non-incremental copying files")
|
||||
}
|
||||
|
||||
// Check that the classes are copied incrementally under normal conditions
|
||||
fileToRemove.writeText("val x = 1")
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
assertFileExists(classFilePath)
|
||||
assertNotContains("Non-incremental copying files")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDowngradeTo106() {
|
||||
val project = Project("kotlinProject")
|
||||
@@ -515,7 +460,7 @@ class KotlinGradleIT : BaseGradleIT() {
|
||||
|
||||
@Test
|
||||
fun testSeparateOutputGradle40() {
|
||||
val project = Project("kotlinJavaProject", GradleVersionRequired.AtLeast("4.0"))
|
||||
val project = Project("kotlinJavaProject")
|
||||
project.build("compileDeployKotlin", "assemble") {
|
||||
assertSuccessful()
|
||||
|
||||
@@ -529,10 +474,6 @@ class KotlinGradleIT : BaseGradleIT() {
|
||||
|
||||
// Check that the Java output is intact:
|
||||
assertFileExists("build/classes/java/main/demo/Greeter.class")
|
||||
|
||||
// Check that the sync output task is not used with Gradle 4.0+ and there's no old Kotlin output layout
|
||||
assertNotContains(":copyMainKotlinClasses")
|
||||
assertNoSuchFile("build/kotlin-classes")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,80 +515,6 @@ class KotlinGradleIT : BaseGradleIT() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDisableSeparateClassesDirs() {
|
||||
|
||||
fun CompiledProject.check(
|
||||
copyClassesToJavaOutput: Boolean?,
|
||||
expectBuildCacheWarning: Boolean,
|
||||
expectGradleLowVersionWarning: Boolean
|
||||
) {
|
||||
|
||||
val separateDirPath = kotlinClassesDir() + "demo/KotlinGreetingJoiner.class"
|
||||
val singleDirPath = javaClassesDir() + "demo/KotlinGreetingJoiner.class"
|
||||
|
||||
assertSuccessful()
|
||||
when (copyClassesToJavaOutput) {
|
||||
true -> {
|
||||
assertNoSuchFile(separateDirPath)
|
||||
assertFileExists(singleDirPath)
|
||||
}
|
||||
false -> {
|
||||
assertFileExists(separateDirPath)
|
||||
assertNoSuchFile(singleDirPath)
|
||||
}
|
||||
}
|
||||
|
||||
if (expectBuildCacheWarning)
|
||||
assertContains(CopyClassesToJavaOutputStatus.buildCacheWarningMessage)
|
||||
else
|
||||
assertNotContains(CopyClassesToJavaOutputStatus.buildCacheWarningMessage)
|
||||
|
||||
if (expectGradleLowVersionWarning)
|
||||
assertContains(CopyClassesToJavaOutputStatus.gradleVersionTooLowWarningMessage)
|
||||
else
|
||||
assertNotContains(CopyClassesToJavaOutputStatus.gradleVersionTooLowWarningMessage)
|
||||
}
|
||||
|
||||
Project("simpleProject", GradleVersionRequired.Exact("4.0")).apply {
|
||||
build("build") {
|
||||
check(
|
||||
copyClassesToJavaOutput = false,
|
||||
expectBuildCacheWarning = false,
|
||||
expectGradleLowVersionWarning = false
|
||||
)
|
||||
}
|
||||
File(projectDir, "build.gradle").appendText("\nkotlin.copyClassesToJavaOutput = true")
|
||||
build("clean", "build") {
|
||||
check(
|
||||
copyClassesToJavaOutput = true,
|
||||
expectBuildCacheWarning = false,
|
||||
expectGradleLowVersionWarning = false
|
||||
)
|
||||
}
|
||||
build("clean", "build", options = defaultBuildOptions().copy(withBuildCache = true)) {
|
||||
check(
|
||||
copyClassesToJavaOutput = true,
|
||||
expectBuildCacheWarning = true,
|
||||
expectGradleLowVersionWarning = false
|
||||
)
|
||||
}
|
||||
projectDir.deleteRecursively()
|
||||
}
|
||||
|
||||
Project("simpleProject", GradleVersionRequired.Exact("3.4")).apply {
|
||||
setupWorkingDir()
|
||||
File(projectDir, "build.gradle").appendText("\nkotlin.copyClassesToJavaOutput = true")
|
||||
build("build") {
|
||||
check(
|
||||
copyClassesToJavaOutput = null,
|
||||
expectBuildCacheWarning = false,
|
||||
expectGradleLowVersionWarning = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSrcDirTaskDependency() {
|
||||
Project("simpleProject", GradleVersionRequired.AtLeast("4.1")).apply {
|
||||
|
||||
+2
-2
@@ -29,7 +29,7 @@ class MultiplatformGradleIT : BaseGradleIT() {
|
||||
|
||||
@Test
|
||||
fun testMultiplatformCompile() {
|
||||
val project = Project("multiplatformProject", GradleVersionRequired.AtLeast("4.0"))
|
||||
val project = Project("multiplatformProject")
|
||||
|
||||
project.build("build") {
|
||||
assertSuccessful()
|
||||
@@ -89,7 +89,7 @@ class MultiplatformGradleIT : BaseGradleIT() {
|
||||
|
||||
@Test
|
||||
fun testSubprojectWithAnotherClassLoader() {
|
||||
with(Project("multiplatformProject", GradleVersionRequired.AtLeast("4.0"))) {
|
||||
with(Project("multiplatformProject")) {
|
||||
setupWorkingDir()
|
||||
|
||||
// Make sure there is a plugin applied with the plugins DSL, so that Gradle loads the
|
||||
|
||||
+1
-6
@@ -137,12 +137,7 @@ class UpToDateIT : BaseGradleIT() {
|
||||
lateinit var helloWorldKtClass: File
|
||||
|
||||
override fun mutateProject(project: Project) = with(project) {
|
||||
val kotlinOutputPath =
|
||||
if (testGradleVersionAtLeast("4.0"))
|
||||
project.classesDir()
|
||||
else
|
||||
// Before 4.0, we should delete the classes from the temporary dir to make compileKotlin rerun:
|
||||
"build/kotlin-classes/main/"
|
||||
val kotlinOutputPath = project.classesDir()
|
||||
|
||||
helloWorldKtClass = File(projectDir, kotlinOutputPath + "demo/KotlinGreetingJoiner.class")
|
||||
Assume.assumeTrue(helloWorldKtClass.exists())
|
||||
|
||||
-4
@@ -6,16 +6,12 @@
|
||||
package org.jetbrains.kotlin.gradle.model
|
||||
|
||||
import org.jetbrains.kotlin.gradle.BaseGradleIT
|
||||
import org.jetbrains.kotlin.gradle.GradleVersionRequired
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class AllOpenModelIT : BaseGradleIT() {
|
||||
override val defaultGradleVersion: GradleVersionRequired
|
||||
get() = GradleVersionRequired.AtLeast("4.0")
|
||||
|
||||
@Test
|
||||
fun testAllOpenSimple() {
|
||||
val project = Project("allOpenSimple")
|
||||
|
||||
-5
@@ -6,16 +6,11 @@
|
||||
package org.jetbrains.kotlin.gradle.model
|
||||
|
||||
import org.jetbrains.kotlin.gradle.BaseGradleIT
|
||||
import org.jetbrains.kotlin.gradle.GradleVersionRequired
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class KaptModelIT : BaseGradleIT() {
|
||||
override val defaultGradleVersion: GradleVersionRequired
|
||||
get() = GradleVersionRequired.AtLeast("4.0")
|
||||
|
||||
@Test
|
||||
fun testKaptSimple() {
|
||||
val project = Project("simple", directoryPrefix = "kapt2")
|
||||
|
||||
-3
@@ -14,9 +14,6 @@ import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class NoArgModelIT : BaseGradleIT() {
|
||||
override val defaultGradleVersion: GradleVersionRequired
|
||||
get() = GradleVersionRequired.AtLeast("4.0")
|
||||
|
||||
@Test
|
||||
fun testNoArgKt18668() {
|
||||
val project = Project("noArgKt18668")
|
||||
|
||||
-4
@@ -6,16 +6,12 @@
|
||||
package org.jetbrains.kotlin.gradle.model
|
||||
|
||||
import org.jetbrains.kotlin.gradle.BaseGradleIT
|
||||
import org.jetbrains.kotlin.gradle.GradleVersionRequired
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class SamWithReceiverModelIT : BaseGradleIT() {
|
||||
override val defaultGradleVersion: GradleVersionRequired
|
||||
get() = GradleVersionRequired.AtLeast("4.0")
|
||||
|
||||
@Test
|
||||
fun testSamWithReceiverSimple() {
|
||||
val project = Project("samWithReceiverSimple")
|
||||
|
||||
+1
-7
@@ -51,13 +51,7 @@ open class KotlinSingleJavaTargetExtension : KotlinProjectExtension() {
|
||||
internal lateinit var target: KotlinWithJavaTarget<*>
|
||||
}
|
||||
|
||||
open class KotlinJvmProjectExtension : KotlinSingleJavaTargetExtension() {
|
||||
/**
|
||||
* With Gradle 4.0+, disables the separate output directory for Kotlin, falling back to sharing the deprecated
|
||||
* single classes directory per source set. With Gradle < 4.0, has no effect.
|
||||
* */
|
||||
var copyClassesToJavaOutput = false
|
||||
}
|
||||
open class KotlinJvmProjectExtension : KotlinSingleJavaTargetExtension()
|
||||
|
||||
open class ExperimentalExtension {
|
||||
var coroutines: Coroutines? = null
|
||||
|
||||
+2
-3
@@ -379,9 +379,8 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
||||
|
||||
kotlinCompilation?.run {
|
||||
output.apply {
|
||||
if (tryAddClassesDir { project.files(classesOutputDir).builtBy(kaptTask) }) {
|
||||
kotlinCompile.attachClassesDir { classesOutputDir }
|
||||
}
|
||||
addClassesDir { project.files(classesOutputDir).builtBy(kaptTask) }
|
||||
kotlinCompile.attachClassesDir { classesOutputDir }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
package org.jetbrains.kotlin.gradle.plugin
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.specs.Spec
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.logging.kotlinWarn
|
||||
import org.jetbrains.kotlin.gradle.utils.ParsedGradleVersion
|
||||
import java.util.*
|
||||
|
||||
object CopyClassesToJavaOutputStatus {
|
||||
fun isEnabled(project: Project): Boolean {
|
||||
val kotlinJvmExt = project.kotlinJvmExt ?: return false
|
||||
if (kotlinJvmExt.copyClassesToJavaOutput) {
|
||||
if (isGradleVersionTooLow(project)) {
|
||||
if (warningReportedForProject.add(project)) {
|
||||
project.logger.kotlinWarn(gradleVersionTooLowWarningMessage)
|
||||
}
|
||||
} else {
|
||||
addCacheWarningToJavaTasks(project)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private val warningReportedForProject = Collections.newSetFromMap<Project>(WeakHashMap())
|
||||
|
||||
private fun isGradleVersionTooLow(project: Project): Boolean {
|
||||
return (ParsedGradleVersion.parse(project.gradle.gradleVersion) ?: return false) < ParsedGradleVersion(4, 0)
|
||||
}
|
||||
|
||||
private fun addCacheWarningToJavaTasks(project: Project) {
|
||||
project.tasks.withType(JavaCompile::class.java).all {
|
||||
val warningCacheIfSpec = Spec<Task> {
|
||||
if (warningReportedForProject.add(project)) {
|
||||
project.logger.kotlinWarn(buildCacheWarningMessage)
|
||||
}
|
||||
return@Spec true
|
||||
}
|
||||
it.outputs.javaClass.getMethod("cacheIf", Spec::class.java).invoke(it.outputs, warningCacheIfSpec)
|
||||
}
|
||||
}
|
||||
|
||||
const val buildCacheWarningMessage = "The 'kotlin.copyClassesToJavaOutput' option should not be used with Gradle " +
|
||||
"build cache"
|
||||
|
||||
const val gradleVersionTooLowWarningMessage = "The 'kotlin.copyClassesToJavaOutput' option has no effect when " +
|
||||
"used with Gradle < 4.0"
|
||||
|
||||
private val Project.kotlinJvmExt: KotlinJvmProjectExtension?
|
||||
get() = extensions.findByType(KotlinJvmProjectExtension::class.java)
|
||||
}
|
||||
+16
-76
@@ -59,29 +59,21 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
|
||||
protected abstract fun doTargetSpecificProcessing()
|
||||
protected val logger = Logging.getLogger(this.javaClass)!!
|
||||
|
||||
protected val isSeparateClassesDirSupported: Boolean by lazy {
|
||||
!CopyClassesToJavaOutputStatus.isEnabled(project) && isGradleVersionAtLeast(4, 0)
|
||||
}
|
||||
|
||||
protected val sourceSetName: String = kotlinCompilation.compilationName
|
||||
|
||||
protected val kotlinTask: TaskHolder<out T> = registerKotlinCompileTask()
|
||||
|
||||
protected val javaSourceSet: SourceSet? = (kotlinCompilation as? KotlinWithJavaCompilation<*>)?.javaSourceSet
|
||||
|
||||
protected open val defaultKotlinDestinationDir: File
|
||||
private val defaultKotlinDestinationDir: File
|
||||
get() {
|
||||
return if (isSeparateClassesDirSupported) {
|
||||
val kotlinExt = project.kotlinExtension
|
||||
val targetSubDirectory =
|
||||
if (kotlinExt is KotlinSingleJavaTargetExtension)
|
||||
"" // In single-target projects, don't add the target name part to this path
|
||||
else
|
||||
kotlinCompilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
|
||||
File(project.buildDir, "classes/kotlin/$targetSubDirectory${kotlinCompilation.compilationName}")
|
||||
} else {
|
||||
kotlinCompilation.output.classesDirs.singleFile
|
||||
}
|
||||
val kotlinExt = project.kotlinExtension
|
||||
val targetSubDirectory =
|
||||
if (kotlinExt is KotlinSingleJavaTargetExtension)
|
||||
"" // In single-target projects, don't add the target name part to this path
|
||||
else
|
||||
kotlinCompilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
|
||||
return File(project.buildDir, "classes/kotlin/$targetSubDirectory${kotlinCompilation.compilationName}")
|
||||
}
|
||||
|
||||
private fun registerKotlinCompileTask(): TaskHolder<out T> {
|
||||
@@ -91,7 +83,7 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
|
||||
it.description = taskDescription
|
||||
it.mapClasspath { kotlinCompilation.compileDependencyFiles }
|
||||
it.setDestinationDir { defaultKotlinDestinationDir }
|
||||
kotlinCompilation.output.tryAddClassesDir { project.files(kotlinTask.doGetTask().destinationDir).builtBy(kotlinTask.doGetTask()) }
|
||||
kotlinCompilation.output.addClassesDir { project.files(kotlinTask.doGetTask().destinationDir).builtBy(kotlinTask.doGetTask()) }
|
||||
}
|
||||
return kotlinCompile
|
||||
}
|
||||
@@ -171,11 +163,6 @@ internal class Kotlin2JvmSourceSetProcessor(
|
||||
) : KotlinSourceSetProcessor<KotlinCompile>(
|
||||
project, tasksProvider, "Compiles the $kotlinCompilation.", kotlinCompilation
|
||||
) {
|
||||
override val defaultKotlinDestinationDir: File
|
||||
get() = if (!isSeparateClassesDirSupported)
|
||||
File(project.buildDir, "kotlin-classes/$sourceSetName") else
|
||||
super.defaultKotlinDestinationDir
|
||||
|
||||
override fun doRegisterTask(project: Project, taskName: String, configureAction: (KotlinCompile)->(Unit)): TaskHolder<out KotlinCompile> =
|
||||
tasksProvider.registerKotlinJVMTask(project, taskName, kotlinCompilation, configureAction)
|
||||
|
||||
@@ -201,19 +188,8 @@ internal class Kotlin2JvmSourceSetProcessor(
|
||||
|
||||
javaTask?.let { configureJavaTask(kotlinTaskInstance, it, logger) }
|
||||
|
||||
var syncOutputTask: SyncOutputTask? = null
|
||||
|
||||
if (!isSeparateClassesDirSupported && javaTask != null) {
|
||||
syncOutputTask = registerSyncOutputTask(project, kotlinTaskInstance, javaTask, sourceSetName)
|
||||
}
|
||||
|
||||
if (project.pluginManager.hasPlugin("java-library") && sourceSetName == SourceSet.MAIN_SOURCE_SET_NAME) {
|
||||
val (classesProviderTask, classesDirectory) = when {
|
||||
isSeparateClassesDirSupported -> kotlinTaskInstance.let { it to it.destinationDir }
|
||||
else -> syncOutputTask!!.let { it to it.javaOutputDir }
|
||||
}
|
||||
|
||||
registerKotlinOutputForJavaLibrary(classesDirectory, classesProviderTask)
|
||||
registerKotlinOutputForJavaLibrary(kotlinTaskInstance.destinationDir, kotlinTaskInstance)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,15 +225,9 @@ internal class Kotlin2JvmSourceSetProcessor(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KotlinCompilationOutput.tryAddClassesDir(
|
||||
classesDirProvider: () -> FileCollection
|
||||
): Boolean =
|
||||
if (isGradleVersionAtLeast(4, 0)) {
|
||||
classesDirs.from(Callable { classesDirProvider() })
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
internal fun KotlinCompilationOutput.addClassesDir(classesDirProvider: () -> FileCollection) {
|
||||
classesDirs.from(Callable { classesDirProvider() })
|
||||
}
|
||||
|
||||
internal class Kotlin2JsSourceSetProcessor(
|
||||
project: Project,
|
||||
@@ -305,10 +275,6 @@ internal class Kotlin2JsSourceSetProcessor(
|
||||
|
||||
kotlinTaskInstance.destinationDir = outputDir
|
||||
|
||||
if (!isSeparateClassesDirSupported && kotlinCompilation is KotlinWithJavaCompilation<*>) {
|
||||
kotlinCompilation.javaSourceSet.output.setClassesDirCompatible(kotlinTaskInstance.destinationDir)
|
||||
}
|
||||
|
||||
appliedPlugins
|
||||
.flatMap { it.getSubpluginKotlinTasks(project, kotlinTaskInstance) }
|
||||
.forEach { task -> kotlinCompilation.allKotlinSourceSets.forEach { sourceSet -> task.source(sourceSet.kotlin) } }
|
||||
@@ -621,6 +587,8 @@ internal open class KotlinAndroidPlugin(
|
||||
) : Plugin<Project> {
|
||||
|
||||
override fun apply(project: Project) {
|
||||
checkGradleCompatibility()
|
||||
|
||||
val androidTarget = KotlinAndroidTarget("", project)
|
||||
applyToTarget(kotlinPluginVersion, androidTarget)
|
||||
registry.register(KotlinModelBuilder(kotlinPluginVersion, androidTarget))
|
||||
@@ -873,38 +841,10 @@ internal fun configureJavaTask(kotlinTask: KotlinCompile, javaTask: AbstractComp
|
||||
* ex. it adds some support libraries jars after execution of prepareComAndroidSupportSupportV42311Library task,
|
||||
* so it's only safe to modify javaTask.classpath right before its usage
|
||||
*/
|
||||
// todo: remove?
|
||||
javaTask.appendClasspathDynamically(kotlinTask.destinationDir!!)
|
||||
}
|
||||
|
||||
internal fun syncOutputTaskName(variantName: String) = "copy${variantName.capitalize()}KotlinClasses"
|
||||
|
||||
internal fun registerSyncOutputTask(
|
||||
project: Project,
|
||||
kotlinCompile: KotlinCompile,
|
||||
javaTask: AbstractCompile,
|
||||
variantName: String
|
||||
): SyncOutputTask {
|
||||
val kotlinDir = kotlinCompile.destinationDir
|
||||
val javaDir = javaTask.destinationDir
|
||||
val taskName = syncOutputTaskName(variantName)
|
||||
|
||||
// registerTask(project: Project, name: String, type: Class<T>, disableLazy: Boolean, body: (T)->(Unit)) : TaskHolder<T> {
|
||||
val syncTask = registerTask(project, taskName, SyncOutputTask::class.java) {
|
||||
it.kotlinOutputDir = kotlinDir
|
||||
it.javaOutputDir = javaDir
|
||||
it.kotlinTask = kotlinCompile
|
||||
it.kaptClassesDir = getKaptGeneratedClassesDir(project, variantName)
|
||||
// copying should be executed after a latter task
|
||||
javaTask.finalizedByIfNotFailed(it)
|
||||
}
|
||||
|
||||
kotlinCompile.javaOutputDir = javaDir
|
||||
|
||||
project.logger.kotlinDebug { "Created task ${syncTask.doGetTask().path} to copy kotlin classes from $kotlinDir to $javaDir" }
|
||||
|
||||
return syncTask.doGetTask()
|
||||
}
|
||||
|
||||
private fun ifKaptEnabled(project: Project, block: () -> Unit) {
|
||||
var triggered = false
|
||||
|
||||
|
||||
+3
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSetFactory
|
||||
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_COMPILER_EMBEDDABLE
|
||||
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_MODULE_GROUP
|
||||
import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility
|
||||
import java.io.FileNotFoundException
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
@@ -49,6 +50,8 @@ abstract class KotlinBasePluginWrapper(
|
||||
DefaultKotlinSourceSetFactory(project, fileResolver)
|
||||
|
||||
override fun apply(project: Project) {
|
||||
checkGradleCompatibility()
|
||||
|
||||
project.configurations.maybeCreate(COMPILER_CLASSPATH_CONFIGURATION_NAME).defaultDependencies {
|
||||
it.add(project.dependencies.create("$KOTLIN_MODULE_GROUP:$KOTLIN_COMPILER_EMBEDDABLE:$kotlinPluginVersion"))
|
||||
}
|
||||
|
||||
+2
-10
@@ -35,7 +35,6 @@ import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
|
||||
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
@@ -517,7 +516,7 @@ open class KotlinNativeTargetConfigurator(
|
||||
|
||||
destinationDir = klibOutputDirectory(compilation)
|
||||
addCompilerPlugins()
|
||||
compilation.output.tryAddClassesDir {
|
||||
compilation.output.addClassesDir {
|
||||
project.files(this.outputFile).builtBy(this)
|
||||
}
|
||||
}
|
||||
@@ -777,14 +776,7 @@ open class KotlinNativeTargetConfigurator(
|
||||
}
|
||||
|
||||
internal fun Project.usageByName(usageName: String): Usage =
|
||||
if (isGradleVersionAtLeast(4, 0)) {
|
||||
// `project.objects` is an API introduced in Gradle 4.0
|
||||
project.objects.named(Usage::class.java, usageName)
|
||||
} else {
|
||||
val usagesClass = Class.forName("org.gradle.api.internal.attributes.Usages")
|
||||
val usagesMethod = usagesClass.getMethod("usage", String::class.java)
|
||||
usagesMethod(null, usageName) as Usage
|
||||
}
|
||||
project.objects.named(Usage::class.java, usageName)
|
||||
|
||||
fun Configuration.usesPlatformOf(target: KotlinTarget): Configuration {
|
||||
attributes.attribute(KotlinPlatformType.attribute, target.platformType)
|
||||
|
||||
-7
@@ -63,14 +63,7 @@ internal class LegacyAndroidAndroidProjectHandler(kotlinConfigurationTools: Kotl
|
||||
javaTask.classpath + project.files(AndroidGradleWrapper.getRuntimeJars(androidPlugin, androidExt))
|
||||
}
|
||||
|
||||
getTestedVariantData(variantData)?.let { testedVariantData ->
|
||||
// Android Gradle plugin bypasses the Gradle finalizedBy for its tasks in some cases, and
|
||||
// the Kotlin classes may not be copied for the tested variant. Make sure they are.
|
||||
kotlinTask.dependsOn(syncOutputTaskName(getVariantName(testedVariantData)))
|
||||
}
|
||||
|
||||
configureJavaTask(kotlinTask, javaTask, logger)
|
||||
registerSyncOutputTask(project, kotlinTask, javaTask, getVariantName(variantData))
|
||||
|
||||
// In lib modules, the androidTest variants get the classes jar in their classpath instead of the Java
|
||||
// destination dir. Attach the JAR to be consumed as friend path:
|
||||
|
||||
+7
-16
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.gradle.plugin
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.attributes.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
|
||||
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
|
||||
|
||||
object ProjectLocalConfigurations {
|
||||
val ATTRIBUTE: Attribute<String> = Attribute.of("org.jetbrains.kotlin.localToProject", String::class.java)
|
||||
@@ -18,10 +17,8 @@ object ProjectLocalConfigurations {
|
||||
|
||||
fun setupAttributesMatchingStrategy(attributesSchema: AttributesSchema) = with(attributesSchema) {
|
||||
attribute(ATTRIBUTE) {
|
||||
if (gradleVersionSupportsAttributeRules) {
|
||||
it.compatibilityRules.add(ProjectLocalCompatibility::class.java)
|
||||
it.disambiguationRules.add(ProjectLocalDisambiguation::class.java)
|
||||
}
|
||||
it.compatibilityRules.add(ProjectLocalCompatibility::class.java)
|
||||
it.disambiguationRules.add(ProjectLocalDisambiguation::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,23 +38,17 @@ object ProjectLocalConfigurations {
|
||||
}
|
||||
|
||||
internal fun Configuration.setupAsLocalTargetSpecificConfigurationIfSupported(target: KotlinTarget) {
|
||||
if (gradleVersionSupportsAttributeRules &&
|
||||
// don't setup in old MPP common modules, as their output configurations with KotlinPlatformType attribute would
|
||||
// fail to resolve as transitive dependencies of the platform modules, just as we don't mark their
|
||||
// `api/RuntimeElements` with the KotlinPlatformType
|
||||
(target !is KotlinWithJavaTarget<*> || target.platformType != KotlinPlatformType.common)
|
||||
) {
|
||||
// don't setup in old MPP common modules, as their output configurations with KotlinPlatformType attribute would
|
||||
// fail to resolve as transitive dependencies of the platform modules, just as we don't mark their
|
||||
// `api/RuntimeElements` with the KotlinPlatformType
|
||||
if ((target !is KotlinWithJavaTarget<*> || target.platformType != KotlinPlatformType.common)) {
|
||||
usesPlatformOf(target)
|
||||
attributes.attribute(ProjectLocalConfigurations.ATTRIBUTE, ProjectLocalConfigurations.LOCAL_TO_PROJECT_PREFIX + target.project.path)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Configuration.setupAsPublicConfigurationIfSupported(target: KotlinTarget) {
|
||||
if (gradleVersionSupportsAttributeRules &&
|
||||
(target !is KotlinWithJavaTarget<*> || target.platformType != KotlinPlatformType.common)
|
||||
) {
|
||||
if ((target !is KotlinWithJavaTarget<*> || target.platformType != KotlinPlatformType.common)) {
|
||||
attributes.attribute(ProjectLocalConfigurations.ATTRIBUTE, ProjectLocalConfigurations.PUBLIC_VALUE)
|
||||
}
|
||||
}
|
||||
|
||||
internal val gradleVersionSupportsAttributeRules = isGradleVersionAtLeast(4, 0)
|
||||
+3
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
|
||||
import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild
|
||||
import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.jetbrains.kotlin.konan.target.presetName
|
||||
@@ -59,6 +60,8 @@ class KotlinMultiplatformPlugin(
|
||||
}
|
||||
|
||||
override fun apply(project: Project) {
|
||||
checkGradleCompatibility()
|
||||
|
||||
project.plugins.apply(JavaBasePlugin::class.java)
|
||||
SingleWarningPerBuild.show(project, "Kotlin Multiplatform Projects are an experimental feature.")
|
||||
|
||||
|
||||
+3
-5
@@ -97,11 +97,9 @@ object KotlinUsages {
|
||||
}
|
||||
|
||||
internal fun setupAttributesMatchingStrategy(attributesSchema: AttributesSchema) {
|
||||
if (isGradleVersionAtLeast(4, 0)) {
|
||||
attributesSchema.attribute(Usage.USAGE_ATTRIBUTE) { strategy ->
|
||||
strategy.compatibilityRules.add(KotlinJavaRuntimeJarsCompatibility::class.java)
|
||||
strategy.disambiguationRules.add(KotlinUsagesDisambiguation::class.java)
|
||||
}
|
||||
attributesSchema.attribute(Usage.USAGE_ATTRIBUTE) { strategy ->
|
||||
strategy.compatibilityRules.add(KotlinJavaRuntimeJarsCompatibility::class.java)
|
||||
strategy.disambiguationRules.add(KotlinUsagesDisambiguation::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-7
@@ -9,7 +9,6 @@ import org.gradle.api.Project
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationOutput
|
||||
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
|
||||
import java.io.File
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
@@ -48,12 +47,7 @@ class KotlinWithJavaCompilationOutput(
|
||||
}
|
||||
|
||||
override val classesDirs: ConfigurableFileCollection =
|
||||
if (isGradleVersionAtLeast(4, 0))
|
||||
javaSourceSetOutput.classesDirs as ConfigurableFileCollection
|
||||
else
|
||||
// In Gradle < 4.0 `SourceSetOutput` does not have `classesDirs`
|
||||
@Suppress("DEPRECATION")
|
||||
compilation.target.project.files(Callable { javaSourceSetOutput.classesDir })
|
||||
javaSourceSetOutput.classesDirs as ConfigurableFileCollection
|
||||
|
||||
override val allOutputs: FileCollection
|
||||
get() = javaSourceSetOutput
|
||||
|
||||
+2
-9
@@ -10,8 +10,6 @@ import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.plugins.JavaPluginConvention
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.jetbrains.kotlin.gradle.utils.isClassFile
|
||||
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
|
||||
import java.io.File
|
||||
|
||||
internal open class InspectClassesForMultiModuleIC : DefaultTask() {
|
||||
@@ -33,13 +31,8 @@ internal open class InspectClassesForMultiModuleIC : DefaultTask() {
|
||||
val convention = project.convention.findPlugin(JavaPluginConvention::class.java)
|
||||
val sourceSet = convention?.sourceSets?.findByName(sourceSetName) ?: return project.files()
|
||||
|
||||
return if (isGradleVersionAtLeast(4, 0)) {
|
||||
val fileTrees = sourceSet.output.classesDirs.map { project.fileTree(it).include("**/*.class") }
|
||||
project.files(fileTrees)
|
||||
} else {
|
||||
sourceSet.output.asFileTree.filter { it.isClassFile() }
|
||||
}
|
||||
|
||||
val fileTrees = sourceSet.output.classesDirs.map { project.fileTree(it).include("**/*.class") }
|
||||
return project.files(fileTrees)
|
||||
}
|
||||
|
||||
@get:Input
|
||||
|
||||
-216
@@ -1,216 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
|
||||
import org.gradle.api.tasks.incremental.InputFileDetails
|
||||
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||
import org.jetbrains.kotlin.gradle.utils.isParentOf
|
||||
import java.io.File
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
import java.util.*
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/**
|
||||
* Copies kotlin classes to java output directory.
|
||||
* No other way to get our classes into apk for android until API is provided https://code.google.com/p/android/issues/detail?id=200714
|
||||
*
|
||||
* Utilises gradle for tracking input/output changes.
|
||||
* 0. Gradle takes snapshots of annotated properties after compilation.
|
||||
* 1. kotlinOutputDir as input files. These files will be copied.
|
||||
* Copying will be done incrementally unless output classes are wiped out
|
||||
* (by IncrementalJavaCompilationSafeguard task from android gradle plugin for example).
|
||||
* 2. kotlinClassesInJavaOutputDir as output files -- copied classes in java output dir.
|
||||
* When they are changed or removed gradle tells us that incremental build is not possible (inputs.isIncremental is false).
|
||||
* 3. We do non-incremental sync in the following way:
|
||||
* a. previously copied files are deleted (to do so we save the list of copied files after copying);
|
||||
* b. all kotlin files are copied to java output dir.
|
||||
*
|
||||
* It is possible that some file was converted from kotlin to java.
|
||||
* In this case gradle will tell us to make non-incremental sync.
|
||||
* To workaround this scenario we also save timestamp of copied class.
|
||||
* On step (a) of sync process we do not delete the class in java output dir
|
||||
* if it's timestamp now is newer than when we copied it
|
||||
* (assuming it was modified by javac when class was converted to java).
|
||||
*/
|
||||
internal open class SyncOutputTask : DefaultTask() {
|
||||
@get:InputFiles
|
||||
var kotlinOutputDir: File by Delegates.notNull()
|
||||
|
||||
@get:InputFiles
|
||||
var kaptClassesDir: File by Delegates.notNull()
|
||||
|
||||
// Marked as input to make Gradle fall back to non-incremental build when it changes.
|
||||
@get:Input
|
||||
protected val classesDirs: List<File>
|
||||
get() = listOf(kotlinOutputDir, kaptClassesDir).filter(File::exists)
|
||||
|
||||
@get:OutputDirectory
|
||||
var javaOutputDir: File by Delegates.notNull()
|
||||
|
||||
@get:Internal
|
||||
var kotlinTask: KotlinCompile by Delegates.notNull()
|
||||
|
||||
// OutputDirectory needed for task to be incremental
|
||||
@get:OutputDirectory
|
||||
val workingDir: File by lazy {
|
||||
File(kotlinTask.taskBuildDirectory, "sync")
|
||||
}
|
||||
|
||||
private val timestampsFile: File by lazy {
|
||||
File(workingDir, TIMESTAMP_FILE_NAME)
|
||||
}
|
||||
|
||||
private val timestamps: MutableMap<File, Long> by lazy {
|
||||
readTimestamps(timestampsFile, javaOutputDir)
|
||||
}
|
||||
|
||||
init {
|
||||
outputs.upToDateWhen {
|
||||
for ((file, ts) in timestamps) {
|
||||
if (!file.exists()) {
|
||||
logger.kotlinDebug { "$file does not exist" }
|
||||
return@upToDateWhen false
|
||||
}
|
||||
|
||||
if (file.lastModified() != ts) {
|
||||
logger.kotlinDebug { "$file ts is different" }
|
||||
return@upToDateWhen false
|
||||
}
|
||||
}
|
||||
|
||||
return@upToDateWhen true
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
@TaskAction
|
||||
fun execute(inputs: IncrementalTaskInputs): Unit {
|
||||
val sourceDirs = classesDirs.joinToString()
|
||||
if (inputs.isIncremental) {
|
||||
logger.kotlinDebug { "Incremental copying files from $sourceDirs to $javaOutputDir" }
|
||||
inputs.outOfDate { processIncrementally(it) }
|
||||
inputs.removed { processIncrementally(it) }
|
||||
} else {
|
||||
logger.kotlinDebug { "Non-incremental copying files from $sourceDirs to $javaOutputDir" }
|
||||
processNonIncrementally()
|
||||
}
|
||||
|
||||
saveTimestamps(timestampsFile, timestamps, javaOutputDir)
|
||||
}
|
||||
|
||||
private fun processNonIncrementally() {
|
||||
for ((file, timestamp) in timestamps) {
|
||||
// wipe all files written by us
|
||||
// do not remove files converted to java (newer timestamp)
|
||||
if (file.lastModified() == timestamp) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
timestampsFile.delete()
|
||||
timestamps.clear()
|
||||
|
||||
for (dir in classesDirs) {
|
||||
dir.walkTopDown().forEach {
|
||||
copy(it, it.siblingInJavaDir(baseDir = dir))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processIncrementally(input: InputFileDetails) {
|
||||
val fileInKotlinDir = input.file
|
||||
val fileInJavaDir = fileInKotlinDir.siblingInJavaDir()
|
||||
|
||||
if (input.isRemoved) {
|
||||
// file was removed in kotlin dir, remove from java as well
|
||||
remove(fileInJavaDir)
|
||||
} else {
|
||||
// copy modified or added file from kotlin to java
|
||||
copy(fileInKotlinDir, fileInJavaDir)
|
||||
}
|
||||
}
|
||||
|
||||
private fun remove(fileInJavaDir: File) {
|
||||
if (!fileInJavaDir.isFile) return
|
||||
|
||||
fileInJavaDir.delete()
|
||||
timestamps.remove(fileInJavaDir)
|
||||
|
||||
logger.kotlinDebug {
|
||||
"Removed kotlin class ${fileInJavaDir.relativeTo(javaOutputDir).path} from $javaOutputDir"
|
||||
}
|
||||
}
|
||||
|
||||
private fun copy(fileInKotlinDir: File, fileInJavaDir: File) {
|
||||
if (!fileInKotlinDir.isFile) return
|
||||
|
||||
fileInJavaDir.parentFile.mkdirs()
|
||||
fileInKotlinDir.copyTo(fileInJavaDir, overwrite = true)
|
||||
|
||||
timestamps[fileInJavaDir] = fileInJavaDir.lastModified()
|
||||
|
||||
logger.kotlinDebug {
|
||||
"Copied kotlin class ${fileInKotlinDir.relativeTo(kotlinOutputDir).path} from $kotlinOutputDir to $javaOutputDir"
|
||||
}
|
||||
}
|
||||
|
||||
private fun File.siblingInJavaDir(baseDir: File? = null): File {
|
||||
val base = baseDir ?: classesDirs.find { it.isParentOf(this, strict = true) }!!
|
||||
return File(javaOutputDir, this.relativeTo(base).path)
|
||||
}
|
||||
}
|
||||
|
||||
private val TIMESTAMP_FILE_NAME = "kotlin-files-in-java-timestamps.bin"
|
||||
|
||||
private fun readTimestamps(tsFile: File, filesBaseDir: File): MutableMap<File, Long> {
|
||||
val result = HashMap<File, Long>()
|
||||
if (!tsFile.isFile) return result
|
||||
|
||||
ObjectInputStream(tsFile.inputStream()).use { input ->
|
||||
val size = input.readInt()
|
||||
|
||||
repeat(size) {
|
||||
val path = input.readUTF()
|
||||
val timestamp = input.readLong()
|
||||
result[File(filesBaseDir, path)] = timestamp
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun saveTimestamps(snapshotFile: File, timestamps: Map<File, Long>, filesBaseDir: File) {
|
||||
if (!snapshotFile.exists()) {
|
||||
snapshotFile.parentFile.mkdirs()
|
||||
snapshotFile.createNewFile()
|
||||
} else {
|
||||
snapshotFile.delete()
|
||||
}
|
||||
|
||||
ObjectOutputStream(snapshotFile.outputStream()).use { out ->
|
||||
out.writeInt(timestamps.size)
|
||||
|
||||
for ((file, timestamp) in timestamps) {
|
||||
out.writeUTF(file.relativeTo(filesBaseDir).path)
|
||||
out.writeLong(timestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -16,10 +16,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.utils
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.tasks.SourceSetOutput
|
||||
import org.gradle.api.tasks.TaskInputs
|
||||
import org.gradle.api.tasks.TaskOutputs
|
||||
import org.gradle.util.GradleVersion
|
||||
|
||||
internal val Task.inputsCompatible: TaskInputs get() = inputs
|
||||
|
||||
@@ -93,4 +95,14 @@ private val setClassesDirMethod by lazy {
|
||||
|
||||
internal fun SourceSetOutput.setClassesDirCompatible(dirPath: Any) {
|
||||
setClassesDirMethod(this, dirPath)
|
||||
}
|
||||
|
||||
internal fun checkGradleCompatibility(minSupportedVersion: GradleVersion = GradleVersion.version("4.0")) {
|
||||
val currentVersion = GradleVersion.current()
|
||||
if (currentVersion < minSupportedVersion) {
|
||||
throw GradleException(
|
||||
"Current version of Gradle $currentVersion is not compatible with Kotlin plugin. " +
|
||||
"Please use Gradle $minSupportedVersion or newer or previous version of Kotlin plugin."
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user