[Gradle] Split Tasks.kt into several source files (per class) (2/2)
^KT-57142 Verification Pending
This commit is contained in:
committed by
Space Team
parent
02ac54fe5f
commit
d23a4f46cc
+26
-924
@@ -1,226 +1,58 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2023 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.
|
* 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.tasks
|
package org.jetbrains.kotlin.gradle.tasks
|
||||||
|
|
||||||
import groovy.lang.Closure
|
import org.gradle.api.file.ConfigurableFileCollection
|
||||||
import org.gradle.api.*
|
import org.gradle.api.file.DirectoryProperty
|
||||||
import org.gradle.api.file.*
|
import org.gradle.api.file.FileCollection
|
||||||
import org.gradle.api.invocation.Gradle
|
import org.gradle.api.file.FileSystemOperations
|
||||||
import org.gradle.api.logging.Logger
|
|
||||||
import org.gradle.api.model.ObjectFactory
|
import org.gradle.api.model.ObjectFactory
|
||||||
import org.gradle.api.provider.ListProperty
|
|
||||||
import org.gradle.api.provider.Property
|
import org.gradle.api.provider.Property
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.provider.SetProperty
|
import org.gradle.api.provider.SetProperty
|
||||||
import org.gradle.api.specs.Spec
|
|
||||||
import org.gradle.api.tasks.*
|
import org.gradle.api.tasks.*
|
||||||
import org.gradle.api.tasks.compile.AbstractCompile
|
|
||||||
import org.gradle.api.tasks.compile.JavaCompile
|
|
||||||
import org.gradle.api.tasks.util.PatternFilterable
|
|
||||||
import org.gradle.api.tasks.util.PatternSet
|
|
||||||
import org.gradle.util.GradleVersion
|
|
||||||
import org.gradle.work.ChangeType
|
import org.gradle.work.ChangeType
|
||||||
import org.gradle.work.Incremental
|
import org.gradle.work.Incremental
|
||||||
import org.gradle.work.InputChanges
|
import org.gradle.work.InputChanges
|
||||||
import org.gradle.work.NormalizeLineEndings
|
import org.gradle.work.NormalizeLineEndings
|
||||||
import org.gradle.workers.WorkerExecutor
|
import org.gradle.workers.WorkerExecutor
|
||||||
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
|
import org.jetbrains.kotlin.build.report.metrics.BuildPerformanceMetric
|
||||||
import org.jetbrains.kotlin.build.report.metrics.*
|
import org.jetbrains.kotlin.build.report.metrics.BuildTime
|
||||||
|
import org.jetbrains.kotlin.build.report.metrics.measure
|
||||||
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
|
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
|
import org.jetbrains.kotlin.compilerRunner.CompilerExecutionSettings
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers
|
||||||
import org.jetbrains.kotlin.compilerRunner.*
|
import org.jetbrains.kotlin.compilerRunner.UsesCompilerSystemPropertiesService
|
||||||
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner.Companion.normalizeForFlagFile
|
|
||||||
import org.jetbrains.kotlin.config.JvmTarget
|
|
||||||
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.incremental.UsesIncrementalModuleInfoBuildService
|
||||||
import org.jetbrains.kotlin.gradle.incremental.*
|
import org.jetbrains.kotlin.gradle.internal.AbstractKotlinCompileArgumentsContributor
|
||||||
import org.jetbrains.kotlin.gradle.internal.*
|
import org.jetbrains.kotlin.gradle.internal.compilerArgumentsConfigurationFlags
|
||||||
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
|
import org.jetbrains.kotlin.gradle.internal.prepareCompilerArguments
|
||||||
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
|
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
|
||||||
import org.jetbrains.kotlin.gradle.logging.GradleErrorMessageCollector
|
|
||||||
import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
|
import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
|
||||||
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
|
|
||||||
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||||
import org.jetbrains.kotlin.gradle.plugin.*
|
import org.jetbrains.kotlin.gradle.plugin.UsesVariantImplementationFactories
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
|
||||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
|
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
|
||||||
import org.jetbrains.kotlin.gradle.report.*
|
import org.jetbrains.kotlin.gradle.report.*
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.internal.LibraryFilterCachingService
|
import org.jetbrains.kotlin.gradle.report.UsesBuildMetricsService
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.internal.UsesLibraryFilterCachingService
|
import org.jetbrains.kotlin.gradle.report.UsesBuildReportsService
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.ir.*
|
|
||||||
import org.jetbrains.kotlin.gradle.tasks.internal.KotlinJsOptionsCompat
|
|
||||||
import org.jetbrains.kotlin.gradle.tasks.internal.KotlinJvmOptionsCompat
|
|
||||||
import org.jetbrains.kotlin.gradle.utils.*
|
import org.jetbrains.kotlin.gradle.utils.*
|
||||||
import org.jetbrains.kotlin.incremental.*
|
import org.jetbrains.kotlin.gradle.utils.newInstance
|
||||||
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotDisabled
|
import org.jetbrains.kotlin.gradle.utils.property
|
||||||
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.*
|
import org.jetbrains.kotlin.gradle.utils.propertyWithConvention
|
||||||
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.IncrementalRun.*
|
import org.jetbrains.kotlin.gradle.utils.propertyWithNewInstance
|
||||||
import org.jetbrains.kotlin.library.impl.isKotlinLibrary
|
import org.jetbrains.kotlin.incremental.ChangedFiles
|
||||||
|
import org.jetbrains.kotlin.incremental.IncrementalCompilerRunner
|
||||||
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
|
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
|
||||||
import org.jetbrains.kotlin.utils.JsLibraryUtils
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Files.*
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import org.jetbrains.kotlin.gradle.tasks.cleanOutputsAndLocalState as cleanOutputsAndLocalStateUtil
|
import org.jetbrains.kotlin.gradle.tasks.cleanOutputsAndLocalState as cleanOutputsAndLocalStateUtil
|
||||||
|
|
||||||
const val KOTLIN_BUILD_DIR_NAME = "kotlin"
|
|
||||||
const val USING_JVM_INCREMENTAL_COMPILATION_MESSAGE = "Using Kotlin/JVM incremental compilation"
|
|
||||||
const val USING_JS_INCREMENTAL_COMPILATION_MESSAGE = "Using Kotlin/JS incremental compilation"
|
|
||||||
const val USING_JS_IR_BACKEND_MESSAGE = "Using Kotlin/JS IR backend"
|
|
||||||
|
|
||||||
abstract class AbstractKotlinCompileTool<T : CommonToolArguments> @Inject constructor(
|
|
||||||
objectFactory: ObjectFactory
|
|
||||||
) : DefaultTask(),
|
|
||||||
KotlinCompileTool,
|
|
||||||
CompilerArgumentAware<T>,
|
|
||||||
TaskWithLocalState {
|
|
||||||
|
|
||||||
private val patternFilterable = PatternSet()
|
|
||||||
|
|
||||||
init {
|
|
||||||
patternFilterable.include(
|
|
||||||
DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS.flatMap { ext -> ext.fileExtensionCasePermutations().map { "**/*.$it" } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val sourceFiles = objectFactory.fileCollection()
|
|
||||||
|
|
||||||
override val sources: FileCollection = objectFactory.fileCollection()
|
|
||||||
.from(
|
|
||||||
{ sourceFiles.asFileTree.matching(patternFilterable) }
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun source(vararg sources: Any) {
|
|
||||||
sourceFiles.from(sources)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setSource(vararg sources: Any) {
|
|
||||||
sourceFiles.from(sources)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun disallowSourceChanges() {
|
|
||||||
sourceFiles.disallowChanges()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
final override fun getIncludes(): MutableSet<String> = patternFilterable.includes
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
final override fun getExcludes(): MutableSet<String> = patternFilterable.excludes
|
|
||||||
|
|
||||||
final override fun setIncludes(includes: Iterable<String>): PatternFilterable = also {
|
|
||||||
patternFilterable.setIncludes(includes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun setExcludes(excludes: Iterable<String>): PatternFilterable = also {
|
|
||||||
patternFilterable.setExcludes(excludes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun include(vararg includes: String?): PatternFilterable = also {
|
|
||||||
patternFilterable.include(*includes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun include(includes: Iterable<String>): PatternFilterable = also {
|
|
||||||
patternFilterable.include(includes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun include(includeSpec: Spec<FileTreeElement>): PatternFilterable = also {
|
|
||||||
patternFilterable.include(includeSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun include(includeSpec: Closure<*>): PatternFilterable = also {
|
|
||||||
patternFilterable.include(includeSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun exclude(vararg excludes: String?): PatternFilterable = also {
|
|
||||||
patternFilterable.exclude(*excludes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun exclude(excludes: Iterable<String>): PatternFilterable = also {
|
|
||||||
patternFilterable.exclude(excludes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun exclude(excludeSpec: Spec<FileTreeElement>): PatternFilterable = also {
|
|
||||||
patternFilterable.exclude(excludeSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun exclude(excludeSpec: Closure<*>): PatternFilterable = also {
|
|
||||||
patternFilterable.exclude(excludeSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
final override val metrics: Property<BuildMetricsReporter> = project.objects
|
|
||||||
.property(BuildMetricsReporterImpl())
|
|
||||||
|
|
||||||
/**
|
|
||||||
* By default, should be set by plugin from [COMPILER_CLASSPATH_CONFIGURATION_NAME] configuration.
|
|
||||||
*
|
|
||||||
* Empty classpath will fail the build.
|
|
||||||
*/
|
|
||||||
@get:Classpath
|
|
||||||
internal val defaultCompilerClasspath: ConfigurableFileCollection =
|
|
||||||
project.objects.fileCollection()
|
|
||||||
|
|
||||||
protected fun validateCompilerClasspath() {
|
|
||||||
// Note that the check triggers configuration resolution
|
|
||||||
require(!defaultCompilerClasspath.isEmpty) {
|
|
||||||
"Default Kotlin compiler classpath is empty! Task: $path (${this::class.qualifiedName})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class GradleCompileTaskProvider @Inject constructor(
|
|
||||||
objectFactory: ObjectFactory,
|
|
||||||
projectLayout: ProjectLayout,
|
|
||||||
gradle: Gradle,
|
|
||||||
task: Task,
|
|
||||||
project: Project,
|
|
||||||
incrementalModuleInfoProvider: Provider<IncrementalModuleInfoProvider>
|
|
||||||
) {
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val path: Provider<String> = objectFactory.property(task.path)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val logger: Provider<Logger> = objectFactory.property(task.logger)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val buildDir: DirectoryProperty = projectLayout.buildDirectory
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val projectDir: Provider<File> = objectFactory
|
|
||||||
.property(project.rootProject.projectDir)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val projectCacheDir: Provider<File> = objectFactory
|
|
||||||
.property(gradle.projectCacheDir)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val sessionsDir: Provider<File> = objectFactory
|
|
||||||
.property(GradleCompilerRunner.sessionsDir(gradle.projectCacheDir))
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val projectName: Provider<String> = objectFactory
|
|
||||||
.property(project.rootProject.name.normalizeForFlagFile())
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val buildModulesInfo: Provider<out IncrementalModuleInfoProvider> = objectFactory
|
|
||||||
.property(incrementalModuleInfoProvider)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val errorsFile: Provider<File?> = objectFactory
|
|
||||||
.property(
|
|
||||||
gradle.rootProject.rootDir.resolve(".gradle/kotlin/errors/").also { it.mkdirs() }
|
|
||||||
.resolve("errors-${System.currentTimeMillis()}.log"))
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constructor(
|
abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constructor(
|
||||||
objectFactory: ObjectFactory,
|
objectFactory: ObjectFactory,
|
||||||
workerExecutor: WorkerExecutor
|
workerExecutor: WorkerExecutor
|
||||||
@@ -496,734 +328,4 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constr
|
|||||||
args.reportPerf = true
|
args.reportPerf = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class KotlinCompileArgumentsProvider<T : AbstractKotlinCompile<out CommonCompilerArguments>>(taskProvider: T) {
|
|
||||||
|
|
||||||
val logger: Logger = taskProvider.logger
|
|
||||||
val isMultiplatform: Boolean = taskProvider.multiPlatformEnabled.get()
|
|
||||||
private val pluginData = taskProvider.kotlinPluginData?.orNull
|
|
||||||
val pluginClasspath: FileCollection = listOfNotNull(taskProvider.pluginClasspath, pluginData?.classpath).reduce(FileCollection::plus)
|
|
||||||
val pluginOptions: CompilerPluginOptions = taskProvider.pluginOptions.toSingleCompilerPluginOptions() + pluginData?.options
|
|
||||||
}
|
|
||||||
|
|
||||||
class KotlinJvmCompilerArgumentsProvider
|
|
||||||
(taskProvider: KotlinCompile) : KotlinCompileArgumentsProvider<KotlinCompile>(taskProvider) {
|
|
||||||
val taskName: String = taskProvider.name
|
|
||||||
val moduleName: String = taskProvider.moduleName.get()
|
|
||||||
val friendPaths: FileCollection = taskProvider.friendPaths
|
|
||||||
val compileClasspath: Iterable<File> = taskProvider.libraries
|
|
||||||
val destinationDir: File = taskProvider.destinationDirectory.get().asFile
|
|
||||||
internal val compilerOptions: KotlinJvmCompilerOptions = taskProvider.compilerOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
internal inline val <reified T : Task> T.thisTaskProvider: TaskProvider<out T>
|
|
||||||
get() = checkNotNull(project.locateTask<T>(name))
|
|
||||||
|
|
||||||
@CacheableTask
|
|
||||||
abstract class KotlinCompile @Inject constructor(
|
|
||||||
final override val compilerOptions: KotlinJvmCompilerOptions,
|
|
||||||
workerExecutor: WorkerExecutor,
|
|
||||||
objectFactory: ObjectFactory
|
|
||||||
) : AbstractKotlinCompile<K2JVMCompilerArguments>(objectFactory, workerExecutor),
|
|
||||||
K2MultiplatformCompilationTask,
|
|
||||||
@Suppress("TYPEALIAS_EXPANSION_DEPRECATION") KotlinJvmCompileDsl,
|
|
||||||
KotlinCompilationTask<KotlinJvmCompilerOptions>,
|
|
||||||
UsesKotlinJavaToolchain {
|
|
||||||
|
|
||||||
init {
|
|
||||||
compilerOptions.moduleName.convention(moduleName)
|
|
||||||
compilerOptions.verbose.convention(logger.isDebugEnabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override val kotlinOptions: KotlinJvmOptions = KotlinJvmOptionsCompat(
|
|
||||||
{ this },
|
|
||||||
compilerOptions
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
@Deprecated("Configure compilerOptions directly", replaceWith = ReplaceWith("compilerOptions"))
|
|
||||||
override val parentKotlinOptions: Property<KotlinJvmOptions> = objectFactory
|
|
||||||
.property(kotlinOptions)
|
|
||||||
.chainedDisallowChanges()
|
|
||||||
|
|
||||||
@get:Nested
|
|
||||||
override val multiplatformStructure: K2MultiplatformStructure = objectFactory.newInstance()
|
|
||||||
|
|
||||||
/** A package prefix that is used for locating Java sources in a directory structure with non-full-depth packages.
|
|
||||||
*
|
|
||||||
* Example: a Java source file with `package com.example.my.package` is located in directory `src/main/java/my/package`.
|
|
||||||
* Then, for the Kotlin compilation to locate the source file, use package prefix `"com.example"` */
|
|
||||||
@get:Input
|
|
||||||
@get:Optional
|
|
||||||
var javaPackagePrefix: String? = null
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
var usePreciseJavaTracking: Boolean = true
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
logger.kotlinDebug { "Set $this.usePreciseJavaTracking=$value" }
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Internal // To support compile avoidance (ClasspathSnapshotProperties.classpathSnapshot will be used as input instead)
|
|
||||||
abstract override val libraries: ConfigurableFileCollection
|
|
||||||
|
|
||||||
@Deprecated(
|
|
||||||
"Replaced with 'libraries' input",
|
|
||||||
replaceWith = ReplaceWith("libraries"),
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
@get:Internal
|
|
||||||
var classpath: FileCollection
|
|
||||||
set(value) = libraries.setFrom(value)
|
|
||||||
get() = libraries
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
abstract val useKotlinAbiSnapshot: Property<Boolean>
|
|
||||||
|
|
||||||
@get:Nested
|
|
||||||
abstract val classpathSnapshotProperties: ClasspathSnapshotProperties
|
|
||||||
|
|
||||||
/** Properties related to the `kotlin.incremental.useClasspathSnapshot` feature. */
|
|
||||||
abstract class ClasspathSnapshotProperties {
|
|
||||||
@get:Input
|
|
||||||
abstract val useClasspathSnapshot: Property<Boolean>
|
|
||||||
|
|
||||||
@get:Classpath
|
|
||||||
@get:Incremental
|
|
||||||
@get:Optional // Set if useClasspathSnapshot == true
|
|
||||||
abstract val classpathSnapshot: ConfigurableFileCollection
|
|
||||||
|
|
||||||
@get:Classpath
|
|
||||||
@get:Incremental
|
|
||||||
@get:Optional // Set if useClasspathSnapshot == false (to restore the existing classpath annotations when the feature is disabled)
|
|
||||||
abstract val classpath: ConfigurableFileCollection
|
|
||||||
|
|
||||||
@get:OutputDirectory
|
|
||||||
@get:Optional // Set if useClasspathSnapshot == true
|
|
||||||
abstract val classpathSnapshotDir: DirectoryProperty
|
|
||||||
}
|
|
||||||
|
|
||||||
override val incrementalProps: List<FileCollection>
|
|
||||||
get() = listOf(
|
|
||||||
sources,
|
|
||||||
javaSources,
|
|
||||||
scriptSources,
|
|
||||||
androidLayoutResources,
|
|
||||||
commonSourceSet,
|
|
||||||
classpathSnapshotProperties.classpath,
|
|
||||||
classpathSnapshotProperties.classpathSnapshot
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
final override val defaultKotlinJavaToolchain: Provider<DefaultKotlinJavaToolchain> = objectFactory
|
|
||||||
.propertyWithNewInstance({ this })
|
|
||||||
|
|
||||||
final override val kotlinJavaToolchainProvider: Provider<KotlinJavaToolchain> = defaultKotlinJavaToolchain.cast()
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal abstract val associatedJavaCompileTaskTargetCompatibility: Property<String>
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal abstract val associatedJavaCompileTaskName: Property<String>
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
internal abstract val jvmTargetValidationMode: Property<PropertiesProvider.JvmTargetValidationMode>
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val scriptDefinitions: ConfigurableFileCollection = objectFactory.fileCollection()
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
@get:Optional
|
|
||||||
internal val scriptExtensions: SetProperty<String> = objectFactory.setPropertyWithLazyValue {
|
|
||||||
scriptDefinitions
|
|
||||||
.map { definitionFile ->
|
|
||||||
definitionFile.readLines().filter(String::isNotBlank)
|
|
||||||
}
|
|
||||||
.flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ScriptFilterSpec(
|
|
||||||
private val scriptExtensions: SetProperty<String>
|
|
||||||
) : Spec<FileTreeElement> {
|
|
||||||
override fun isSatisfiedBy(element: FileTreeElement): Boolean {
|
|
||||||
val extensions = scriptExtensions.get()
|
|
||||||
return extensions.isNotEmpty() &&
|
|
||||||
(element.isDirectory || extensions.contains(element.file.extension))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val scriptSourceFiles = objectFactory.fileCollection()
|
|
||||||
|
|
||||||
@get:InputFiles
|
|
||||||
@get:SkipWhenEmpty
|
|
||||||
@get:IgnoreEmptyDirectories
|
|
||||||
@get:NormalizeLineEndings
|
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
internal open val scriptSources: FileCollection = scriptSourceFiles
|
|
||||||
.asFileTree
|
|
||||||
.matching { patternFilterable ->
|
|
||||||
patternFilterable.include(ScriptFilterSpec(scriptExtensions))
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
incremental = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun skipCondition(): Boolean = sources.isEmpty && scriptSources.isEmpty
|
|
||||||
|
|
||||||
override fun createCompilerArgs(): K2JVMCompilerArguments =
|
|
||||||
K2JVMCompilerArguments()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Workaround for those "nasty" plugins that are adding 'freeCompilerArgs' on task execution phase.
|
|
||||||
* With properties api it is not possible to update property value after task configuration is finished.
|
|
||||||
*
|
|
||||||
* Marking it as `@Internal` as anyway on the configuration phase, when Gradle does task inputs snapshot,
|
|
||||||
* this input will always be empty.
|
|
||||||
*/
|
|
||||||
@get:Internal
|
|
||||||
internal var executionTimeFreeCompilerArgs: List<String>? = null
|
|
||||||
|
|
||||||
override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
|
|
||||||
compilerArgumentsContributor.contributeArguments(
|
|
||||||
args, compilerArgumentsConfigurationFlags(
|
|
||||||
defaultsOnly,
|
|
||||||
ignoreClasspathResolutionErrors
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
args.configureMultiplatform(
|
|
||||||
compilerOptions,
|
|
||||||
k1CommonSources = commonSourceSet.asFileTree,
|
|
||||||
k2MultiplatformFragments = multiplatformStructure
|
|
||||||
)
|
|
||||||
|
|
||||||
if (reportingSettings().buildReportMode == BuildReportMode.VERBOSE) {
|
|
||||||
args.reportPerf = true
|
|
||||||
}
|
|
||||||
|
|
||||||
val localExecutionTimeFreeCompilerArgs = executionTimeFreeCompilerArgs
|
|
||||||
if (localExecutionTimeFreeCompilerArgs != null) {
|
|
||||||
args.freeArgs = localExecutionTimeFreeCompilerArgs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val compilerArgumentsContributor: CompilerArgumentsContributor<K2JVMCompilerArguments> by lazy {
|
|
||||||
KotlinJvmCompilerArgumentsContributor(KotlinJvmCompilerArgumentsProvider(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun callCompilerAsync(
|
|
||||||
args: K2JVMCompilerArguments,
|
|
||||||
kotlinSources: Set<File>,
|
|
||||||
inputChanges: InputChanges,
|
|
||||||
taskOutputsBackup: TaskOutputsBackup?
|
|
||||||
) {
|
|
||||||
validateKotlinAndJavaHasSameTargetCompatibility(args)
|
|
||||||
|
|
||||||
val scriptSources = scriptSources.asFileTree.files
|
|
||||||
val javaSources = javaSources.files
|
|
||||||
val gradlePrintingMessageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors)
|
|
||||||
val gradleMessageCollector = GradleErrorMessageCollector(gradlePrintingMessageCollector, kotlinPluginVersion = getKotlinPluginVersion(logger))
|
|
||||||
val outputItemCollector = OutputItemsCollectorImpl()
|
|
||||||
val compilerRunner = compilerRunner.get()
|
|
||||||
|
|
||||||
val icEnv = if (isIncrementalCompilationEnabled()) {
|
|
||||||
logger.info(USING_JVM_INCREMENTAL_COMPILATION_MESSAGE)
|
|
||||||
IncrementalCompilationEnvironment(
|
|
||||||
changedFiles = getChangedFiles(inputChanges, incrementalProps),
|
|
||||||
classpathChanges = getClasspathChanges(inputChanges),
|
|
||||||
workingDir = taskBuildCacheableOutputDirectory.get().asFile,
|
|
||||||
usePreciseJavaTracking = usePreciseJavaTracking,
|
|
||||||
disableMultiModuleIC = disableMultiModuleIC,
|
|
||||||
multiModuleICSettings = multiModuleICSettings,
|
|
||||||
withAbiSnapshot = useKotlinAbiSnapshot.get(),
|
|
||||||
preciseCompilationResultsBackup = preciseCompilationResultsBackup.get(),
|
|
||||||
)
|
|
||||||
} else null
|
|
||||||
|
|
||||||
@Suppress("ConvertArgumentToSet")
|
|
||||||
val environment = GradleCompilerEnvironment(
|
|
||||||
defaultCompilerClasspath, gradleMessageCollector, outputItemCollector,
|
|
||||||
// In the incremental compiler, outputFiles will be cleaned on rebuild. However, because classpathSnapshotDir is not included in
|
|
||||||
// TaskOutputsBackup, we don't want classpathSnapshotDir to be cleaned immediately on rebuild, and therefore we exclude it from
|
|
||||||
// outputFiles here. (See TaskOutputsBackup's kdoc for more info.)
|
|
||||||
outputFiles = allOutputFiles()
|
|
||||||
- (classpathSnapshotProperties.classpathSnapshotDir.orNull?.asFile?.let { setOf(it) } ?: emptySet()),
|
|
||||||
reportingSettings = reportingSettings(),
|
|
||||||
incrementalCompilationEnvironment = icEnv,
|
|
||||||
kotlinScriptExtensions = scriptExtensions.get().toTypedArray()
|
|
||||||
)
|
|
||||||
logger.info("Kotlin source files: ${kotlinSources.joinToString()}")
|
|
||||||
logger.info("Java source files: ${javaSources.joinToString()}")
|
|
||||||
logger.info("Script source files: ${scriptSources.joinToString()}")
|
|
||||||
logger.info("Script file extensions: ${scriptExtensions.get().joinToString()}")
|
|
||||||
compilerRunner.runJvmCompilerAsync(
|
|
||||||
(kotlinSources + scriptSources + javaSources).toList(),
|
|
||||||
javaPackagePrefix,
|
|
||||||
args,
|
|
||||||
environment,
|
|
||||||
defaultKotlinJavaToolchain.get().buildJvm.get().javaHome,
|
|
||||||
taskOutputsBackup
|
|
||||||
)
|
|
||||||
compilerRunner.errorsFile?.also { gradleMessageCollector.flush(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun validateKotlinAndJavaHasSameTargetCompatibility(
|
|
||||||
args: K2JVMCompilerArguments,
|
|
||||||
) {
|
|
||||||
associatedJavaCompileTaskTargetCompatibility.orNull?.let { targetCompatibility ->
|
|
||||||
val normalizedJavaTarget = when (targetCompatibility) {
|
|
||||||
"6" -> "1.6"
|
|
||||||
"7" -> "1.7"
|
|
||||||
"8" -> "1.8"
|
|
||||||
"1.9" -> "9"
|
|
||||||
else -> targetCompatibility
|
|
||||||
}
|
|
||||||
|
|
||||||
val jvmTarget = args.jvmTarget ?: JvmTarget.DEFAULT.toString()
|
|
||||||
if (normalizedJavaTarget != jvmTarget) {
|
|
||||||
val javaTaskName = associatedJavaCompileTaskName.get()
|
|
||||||
|
|
||||||
val errorMessage = buildString {
|
|
||||||
append("'$javaTaskName' task (current target is $targetCompatibility) and ")
|
|
||||||
append("'$name' task (current target is $jvmTarget) ")
|
|
||||||
appendLine("jvm target compatibility should be set to the same Java version.")
|
|
||||||
if (GradleVersion.current().baseVersion < GradleVersion.version("8.0")) {
|
|
||||||
append("By default will become an error since Gradle 8.0+! ")
|
|
||||||
appendLine("Read more: https://kotl.in/gradle/jvm/target-validation")
|
|
||||||
}
|
|
||||||
appendLine("Consider using JVM toolchain: https://kotl.in/gradle/jvm/toolchain")
|
|
||||||
}
|
|
||||||
|
|
||||||
when (jvmTargetValidationMode.get()) {
|
|
||||||
PropertiesProvider.JvmTargetValidationMode.ERROR -> throw GradleException(errorMessage)
|
|
||||||
PropertiesProvider.JvmTargetValidationMode.WARNING -> logger.warn(errorMessage)
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
val disableMultiModuleIC: Boolean by lazy {
|
|
||||||
if (!isIncrementalCompilationEnabled() || !javaOutputDir.isPresent) {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
|
|
||||||
var illegalTaskOrNull: AbstractCompile? = null
|
|
||||||
project.tasks.configureEach {
|
|
||||||
if (it is AbstractCompile &&
|
|
||||||
it !is JavaCompile &&
|
|
||||||
it !is AbstractKotlinCompile<*> &&
|
|
||||||
javaOutputDir.get().asFile.isParentOf(it.destinationDirectory.get().asFile)
|
|
||||||
) {
|
|
||||||
illegalTaskOrNull = illegalTaskOrNull ?: it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (illegalTaskOrNull != null) {
|
|
||||||
val illegalTask = illegalTaskOrNull!!
|
|
||||||
logger.info(
|
|
||||||
"Kotlin inter-project IC is disabled: " +
|
|
||||||
"unknown task '$illegalTask' destination dir ${illegalTask.destinationDirectory.get().asFile} " +
|
|
||||||
"intersects with java destination dir $javaOutputDir"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
illegalTaskOrNull != null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val javaSourceFiles = objectFactory.fileCollection()
|
|
||||||
|
|
||||||
private fun javaFilesPatternFilter(patternFilterable: PatternFilterable) {
|
|
||||||
patternFilterable.include(
|
|
||||||
"java".fileExtensionCasePermutations().map { "**/*.$it" }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Incremental
|
|
||||||
@get:InputFiles
|
|
||||||
@get:IgnoreEmptyDirectories
|
|
||||||
@get:NormalizeLineEndings
|
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
open val javaSources: FileCollection = objectFactory.fileCollection()
|
|
||||||
.from(
|
|
||||||
javaSourceFiles
|
|
||||||
.asFileTree
|
|
||||||
.matching(::javaFilesPatternFilter)
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val androidLayoutResourceFiles = objectFactory.fileCollection()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This input is used by android-extensions plugin
|
|
||||||
*/
|
|
||||||
@get:Incremental
|
|
||||||
@get:InputFiles
|
|
||||||
@get:IgnoreEmptyDirectories
|
|
||||||
@get:NormalizeLineEndings
|
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
internal open val androidLayoutResources: FileCollection = androidLayoutResourceFiles
|
|
||||||
.asFileTree
|
|
||||||
.matching { patternFilterable ->
|
|
||||||
patternFilterable.include("xml".fileExtensionCasePermutations().map { "**/*.$it" })
|
|
||||||
}
|
|
||||||
|
|
||||||
// override setSource to track Java and script sources as well
|
|
||||||
override fun source(vararg sources: Any) {
|
|
||||||
javaSourceFiles.from(sources)
|
|
||||||
scriptSourceFiles.from(sources)
|
|
||||||
super.setSource(sources)
|
|
||||||
}
|
|
||||||
|
|
||||||
// override source to track Java and script sources as well
|
|
||||||
override fun setSource(vararg sources: Any) {
|
|
||||||
javaSourceFiles.from(*sources)
|
|
||||||
scriptSourceFiles.from(*sources)
|
|
||||||
super.setSource(*sources)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getClasspathChanges(inputChanges: InputChanges): ClasspathChanges = when {
|
|
||||||
!classpathSnapshotProperties.useClasspathSnapshot.get() -> ClasspathSnapshotDisabled
|
|
||||||
else -> {
|
|
||||||
val classpathSnapshotFiles = ClasspathSnapshotFiles(
|
|
||||||
classpathSnapshotProperties.classpathSnapshot.files.toList(),
|
|
||||||
classpathSnapshotProperties.classpathSnapshotDir.get().asFile
|
|
||||||
)
|
|
||||||
when {
|
|
||||||
!inputChanges.isIncremental -> NotAvailableForNonIncrementalRun(classpathSnapshotFiles)
|
|
||||||
// When `inputChanges.isIncremental == true`, we want to compile incrementally. However, if the incremental state (e.g.
|
|
||||||
// lookup caches or previous classpath snapshot) is missing, we need to compile non-incrementally. There are a few cases:
|
|
||||||
// 1. Previous compilation happened using Kotlin daemon and with IC enabled (the usual case) => Incremental state
|
|
||||||
// including classpath snapshot must have been produced (we have that guarantee in `IncrementalCompilerRunner`).
|
|
||||||
// 2. Previous compilation happened using Kotlin daemon but with IC disabled (e.g., by setting `kotlin.incremental=false`)
|
|
||||||
// => This run will have `inputChanges.isIncremental = false` as `isIncrementalCompilationEnabled` is a task input.
|
|
||||||
// 3. Previous compilation happened without using Kotlin daemon (set by the user or caused by a fallback).
|
|
||||||
// 4. Previous compilation was skipped because there were no sources to compile (see `AbstractKotlinCompile.executeImpl`).
|
|
||||||
// In case 3 and 4, it is possible that `inputChanges.isIncremental == true` in this run and incremental state is missing.
|
|
||||||
!classpathSnapshotFiles.shrunkPreviousClasspathSnapshotFile.exists() -> {
|
|
||||||
NotAvailableDueToMissingClasspathSnapshot(classpathSnapshotFiles)
|
|
||||||
}
|
|
||||||
|
|
||||||
inputChanges.getFileChanges(classpathSnapshotProperties.classpathSnapshot).none() -> NoChanges(classpathSnapshotFiles)
|
|
||||||
else -> ToBeComputedByIncrementalCompiler(classpathSnapshotFiles)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@CacheableTask
|
|
||||||
abstract class Kotlin2JsCompile @Inject constructor(
|
|
||||||
override val compilerOptions: KotlinJsCompilerOptions,
|
|
||||||
objectFactory: ObjectFactory,
|
|
||||||
workerExecutor: WorkerExecutor
|
|
||||||
) : AbstractKotlinCompile<K2JSCompilerArguments>(objectFactory, workerExecutor),
|
|
||||||
KotlinCompilationTask<KotlinJsCompilerOptions>,
|
|
||||||
UsesLibraryFilterCachingService,
|
|
||||||
KotlinJsCompile,
|
|
||||||
K2MultiplatformCompilationTask {
|
|
||||||
|
|
||||||
init {
|
|
||||||
incremental = true
|
|
||||||
compilerOptions.verbose.convention(logger.isDebugEnabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val kotlinOptions: KotlinJsOptions = KotlinJsOptionsCompat(
|
|
||||||
{ this },
|
|
||||||
compilerOptions
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
internal var incrementalJsKlib: Boolean = true
|
|
||||||
|
|
||||||
override fun isIncrementalCompilationEnabled(): Boolean {
|
|
||||||
val freeArgs = enhancedFreeCompilerArgs.get()
|
|
||||||
return when {
|
|
||||||
PRODUCE_JS in freeArgs -> false
|
|
||||||
|
|
||||||
PRODUCE_UNZIPPED_KLIB in freeArgs -> {
|
|
||||||
KotlinBuildStatsService.applyIfInitialised {
|
|
||||||
it.report(BooleanMetrics.JS_KLIB_INCREMENTAL, incrementalJsKlib)
|
|
||||||
}
|
|
||||||
incrementalJsKlib
|
|
||||||
}
|
|
||||||
|
|
||||||
PRODUCE_ZIPPED_KLIB in freeArgs -> {
|
|
||||||
KotlinBuildStatsService.applyIfInitialised {
|
|
||||||
it.report(BooleanMetrics.JS_KLIB_INCREMENTAL, incrementalJsKlib)
|
|
||||||
}
|
|
||||||
incrementalJsKlib
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> incremental
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Workaround to be able to use default value and change it later based on external input
|
|
||||||
@get:Internal
|
|
||||||
internal abstract val defaultDestinationDirectory: DirectoryProperty
|
|
||||||
|
|
||||||
@Deprecated("Use destinationDirectory and moduleName instead")
|
|
||||||
@get:Internal
|
|
||||||
abstract val outputFileProperty: Property<File>
|
|
||||||
|
|
||||||
// Workaround to add additional compiler args based on the exising one
|
|
||||||
// Currently there is a logic to add additional compiler arguments based on already existing one.
|
|
||||||
// And it is not possible to update compilerOptions.freeCompilerArgs using some kind of .map
|
|
||||||
// or .flatMap call - this will cause StackOverlowException as upstream source will be updated
|
|
||||||
// and .map will be called again.
|
|
||||||
@get:Input
|
|
||||||
internal abstract val enhancedFreeCompilerArgs: ListProperty<String>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Workaround for those "nasty" plugins that are adding 'freeCompilerArgs' on task execution phase.
|
|
||||||
* With properties api it is not possible to update property value after task configuration is finished.
|
|
||||||
*
|
|
||||||
* Marking it as `@Internal` as anyway on the configuration phase, when Gradle does task inputs snapshot,
|
|
||||||
* this input will always be empty.
|
|
||||||
*/
|
|
||||||
@get:Internal
|
|
||||||
internal var executionTimeFreeCompilerArgs: List<String>? = null
|
|
||||||
|
|
||||||
@get:Nested
|
|
||||||
override val multiplatformStructure: K2MultiplatformStructure = objectFactory.newInstance()
|
|
||||||
|
|
||||||
override fun createCompilerArgs(): K2JSCompilerArguments =
|
|
||||||
K2JSCompilerArguments()
|
|
||||||
|
|
||||||
override fun setupCompilerArgs(args: K2JSCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
|
|
||||||
(compilerOptions as KotlinJsCompilerOptionsDefault).fillDefaultValues(args)
|
|
||||||
super.setupCompilerArgs(args, defaultsOnly = defaultsOnly, ignoreClasspathResolutionErrors = ignoreClasspathResolutionErrors)
|
|
||||||
|
|
||||||
if (defaultsOnly) return
|
|
||||||
|
|
||||||
(compilerOptions as KotlinJsCompilerOptionsDefault).fillCompilerArguments(args)
|
|
||||||
if (!args.sourceMapPrefix.isNullOrEmpty()) {
|
|
||||||
args.sourceMapBaseDirs = sourceMapBaseDir.get().asFile.absolutePath
|
|
||||||
}
|
|
||||||
if (isIrBackendEnabled()) {
|
|
||||||
val outputFilePath: String? = compilerOptions.outputFile.orNull
|
|
||||||
if (outputFilePath != null) {
|
|
||||||
val outputFile = File(outputFilePath)
|
|
||||||
args.outputDir = (if (outputFile.extension == "") outputFile else outputFile.parentFile).normalize().absolutePath
|
|
||||||
args.moduleName = outputFile.nameWithoutExtension
|
|
||||||
} else {
|
|
||||||
args.outputDir = destinationDirectory.get().asFile.normalize().absolutePath
|
|
||||||
args.moduleName = compilerOptions.moduleName.get()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
args.outputFile = outputFileProperty.get().absoluteFile.normalize().absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
args.configureMultiplatform(
|
|
||||||
compilerOptions,
|
|
||||||
k1CommonSources = commonSourceSet.asFileTree,
|
|
||||||
k2MultiplatformFragments = multiplatformStructure
|
|
||||||
)
|
|
||||||
|
|
||||||
// Overriding freeArgs from compilerOptions with enhanced one + additional one set on execution phase
|
|
||||||
// containing additional arguments based on the js compilation configuration
|
|
||||||
val localExecutionTimeFreeCompilerArgs = executionTimeFreeCompilerArgs
|
|
||||||
args.freeArgs = if (localExecutionTimeFreeCompilerArgs != null) localExecutionTimeFreeCompilerArgs else enhancedFreeCompilerArgs.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:InputFiles
|
|
||||||
@get:IgnoreEmptyDirectories
|
|
||||||
@get:Incremental
|
|
||||||
@get:Optional
|
|
||||||
@get:NormalizeLineEndings
|
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
internal val friendDependencies: FileCollection = objectFactory
|
|
||||||
.fileCollection()
|
|
||||||
.from(friendPaths)
|
|
||||||
.filter {
|
|
||||||
// .jar files are not required for js compilation as friend modules
|
|
||||||
// and, because of `@InputFiles` and different normalization strategy from `@Classpath`,
|
|
||||||
// they produce build cache misses
|
|
||||||
it.exists() && !it.name.endsWith(".jar") && libraryFilter(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val sourceMapBaseDir: Property<Directory> = objectFactory
|
|
||||||
.directoryProperty()
|
|
||||||
.value(project.layout.projectDirectory)
|
|
||||||
|
|
||||||
private fun isHybridKotlinJsLibrary(file: File): Boolean =
|
|
||||||
JsLibraryUtils.isKotlinJavascriptLibrary(file) && isKotlinLibrary(file)
|
|
||||||
|
|
||||||
private val preIrBackendCompilerFlags = listOf(
|
|
||||||
DISABLE_PRE_IR,
|
|
||||||
PRODUCE_JS,
|
|
||||||
PRODUCE_ZIPPED_KLIB
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun isPreIrBackendDisabled(): Boolean = enhancedFreeCompilerArgs
|
|
||||||
.get()
|
|
||||||
.any { preIrBackendCompilerFlags.contains(it) }
|
|
||||||
|
|
||||||
// see also isIncrementalCompilationEnabled
|
|
||||||
private val irBackendCompilerFlags = listOf(
|
|
||||||
PRODUCE_UNZIPPED_KLIB,
|
|
||||||
PRODUCE_JS,
|
|
||||||
PRODUCE_ZIPPED_KLIB
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun isIrBackendEnabled(): Boolean = enhancedFreeCompilerArgs
|
|
||||||
.get()
|
|
||||||
.any { irBackendCompilerFlags.contains(it) }
|
|
||||||
|
|
||||||
private val File.asLibraryFilterCacheKey: LibraryFilterCachingService.LibraryFilterCacheKey
|
|
||||||
get() = LibraryFilterCachingService.LibraryFilterCacheKey(
|
|
||||||
this,
|
|
||||||
irEnabled = isIrBackendEnabled(),
|
|
||||||
preIrDisabled = isPreIrBackendDisabled()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Kotlin/JS can operate in 3 modes:
|
|
||||||
// 1) purely pre-IR backend
|
|
||||||
// 2) purely IR backend
|
|
||||||
// 3) hybrid pre-IR and IR backend. Can only accept libraries with both JS and IR parts.
|
|
||||||
private val libraryFilterBody: (File) -> Boolean
|
|
||||||
get() = if (isIrBackendEnabled()) {
|
|
||||||
if (isPreIrBackendDisabled()) {
|
|
||||||
//::isKotlinLibrary
|
|
||||||
// Workaround for KT-47797
|
|
||||||
{ isKotlinLibrary(it) }
|
|
||||||
} else {
|
|
||||||
::isHybridKotlinJsLibrary
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
JsLibraryUtils::isKotlinJavascriptLibrary
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
internal val jsLegacyNoWarn: Provider<Boolean> = objectFactory.property(
|
|
||||||
PropertiesProvider(project).jsCompilerNoWarn
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
protected val libraryFilter: (File) -> Boolean
|
|
||||||
get() = { file ->
|
|
||||||
libraryFilterCacheService.get().getOrCompute(file.asLibraryFilterCacheKey, libraryFilterBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val incrementalProps: List<FileCollection>
|
|
||||||
get() = super.incrementalProps + listOf(friendDependencies)
|
|
||||||
|
|
||||||
open fun processArgs(
|
|
||||||
args: K2JSCompilerArguments
|
|
||||||
) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun callCompilerAsync(
|
|
||||||
args: K2JSCompilerArguments,
|
|
||||||
kotlinSources: Set<File>,
|
|
||||||
inputChanges: InputChanges,
|
|
||||||
taskOutputsBackup: TaskOutputsBackup?
|
|
||||||
) {
|
|
||||||
logger.debug("Calling compiler")
|
|
||||||
|
|
||||||
validateOutputDirectory()
|
|
||||||
|
|
||||||
if (isIrBackendEnabled()) {
|
|
||||||
logger.info(USING_JS_IR_BACKEND_MESSAGE)
|
|
||||||
}
|
|
||||||
|
|
||||||
val dependencies = libraries
|
|
||||||
.filter { it.exists() && libraryFilter(it) }
|
|
||||||
.map { it.normalize().absolutePath }
|
|
||||||
|
|
||||||
args.libraries = dependencies.distinct().let {
|
|
||||||
if (it.isNotEmpty())
|
|
||||||
it.joinToString(File.pathSeparator) else
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
args.friendModules = friendDependencies.files.joinToString(File.pathSeparator) { it.absolutePath }
|
|
||||||
if (!isIrBackendEnabled()) {
|
|
||||||
args.legacyDeprecatedNoWarn = true
|
|
||||||
args.useDeprecatedLegacyCompiler = true
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.kotlinDebug("compiling with args ${ArgumentUtils.convertArgumentsToStringList(args)}")
|
|
||||||
|
|
||||||
val gradlePrintingMessageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors)
|
|
||||||
val gradleMessageCollector = GradleErrorMessageCollector(gradlePrintingMessageCollector, kotlinPluginVersion = getKotlinPluginVersion(logger))
|
|
||||||
val outputItemCollector = OutputItemsCollectorImpl()
|
|
||||||
val compilerRunner = compilerRunner.get()
|
|
||||||
|
|
||||||
val icEnv = if (isIncrementalCompilationEnabled()) {
|
|
||||||
logger.info(USING_JS_INCREMENTAL_COMPILATION_MESSAGE)
|
|
||||||
IncrementalCompilationEnvironment(
|
|
||||||
getChangedFiles(inputChanges, incrementalProps),
|
|
||||||
ClasspathChanges.NotAvailableForJSCompiler,
|
|
||||||
taskBuildCacheableOutputDirectory.get().asFile,
|
|
||||||
multiModuleICSettings = multiModuleICSettings,
|
|
||||||
preciseCompilationResultsBackup = preciseCompilationResultsBackup.get(),
|
|
||||||
)
|
|
||||||
} else null
|
|
||||||
|
|
||||||
val environment = GradleCompilerEnvironment(
|
|
||||||
defaultCompilerClasspath, gradleMessageCollector, outputItemCollector,
|
|
||||||
outputFiles = allOutputFiles(),
|
|
||||||
reportingSettings = reportingSettings(),
|
|
||||||
incrementalCompilationEnvironment = icEnv
|
|
||||||
)
|
|
||||||
processArgs(args)
|
|
||||||
compilerRunner.runJsCompilerAsync(
|
|
||||||
kotlinSources.toList(),
|
|
||||||
args,
|
|
||||||
environment,
|
|
||||||
taskOutputsBackup
|
|
||||||
)
|
|
||||||
compilerRunner.errorsFile?.also { gradleMessageCollector.flush(it) }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private val projectRootDir = project.rootDir
|
|
||||||
|
|
||||||
private fun validateOutputDirectory() {
|
|
||||||
val outputFile = outputFileProperty.get()
|
|
||||||
val outputDir = outputFile.parentFile
|
|
||||||
|
|
||||||
if (outputDir.isParentOf(projectRootDir)) {
|
|
||||||
throw InvalidUserDataException(
|
|
||||||
"The output directory '$outputDir' (defined by outputFile of ':$name') contains or " +
|
|
||||||
"matches the project root directory '${projectRootDir}'.\n" +
|
|
||||||
"Gradle will not be able to build the project because of the root directory lock.\n" +
|
|
||||||
"To fix this, consider using the default outputFile location instead of providing it explicitly."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class KotlinCompilerPluginData(
|
|
||||||
@get:Classpath
|
|
||||||
val classpath: FileCollection,
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val options: CompilerPluginOptions,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used only for Up-to-date checks
|
|
||||||
*/
|
|
||||||
@get:Nested
|
|
||||||
val inputsOutputsState: InputsOutputsState
|
|
||||||
) {
|
|
||||||
data class InputsOutputsState(
|
|
||||||
@get:Input
|
|
||||||
val inputs: Map<String, String>,
|
|
||||||
|
|
||||||
@get:InputFiles
|
|
||||||
@get:IgnoreEmptyDirectories
|
|
||||||
@get:NormalizeLineEndings
|
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
val inputFiles: Set<File>,
|
|
||||||
|
|
||||||
@get:OutputFiles
|
|
||||||
val outputFiles: Set<File>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
+13
-1113
File diff suppressed because it is too large
Load Diff
+11
-1172
File diff suppressed because it is too large
Load Diff
+24
-915
@@ -1,918 +1,55 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2023 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.
|
* 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.tasks
|
package org.jetbrains.kotlin.gradle.tasks
|
||||||
|
|
||||||
import groovy.lang.Closure
|
import org.gradle.api.InvalidUserDataException
|
||||||
import org.gradle.api.*
|
import org.gradle.api.file.Directory
|
||||||
import org.gradle.api.file.*
|
import org.gradle.api.file.DirectoryProperty
|
||||||
import org.gradle.api.invocation.Gradle
|
import org.gradle.api.file.FileCollection
|
||||||
import org.gradle.api.logging.Logger
|
|
||||||
import org.gradle.api.model.ObjectFactory
|
import org.gradle.api.model.ObjectFactory
|
||||||
import org.gradle.api.provider.ListProperty
|
import org.gradle.api.provider.ListProperty
|
||||||
import org.gradle.api.provider.Property
|
import org.gradle.api.provider.Property
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.provider.SetProperty
|
|
||||||
import org.gradle.api.specs.Spec
|
|
||||||
import org.gradle.api.tasks.*
|
import org.gradle.api.tasks.*
|
||||||
import org.gradle.api.tasks.compile.AbstractCompile
|
|
||||||
import org.gradle.api.tasks.compile.JavaCompile
|
|
||||||
import org.gradle.api.tasks.util.PatternFilterable
|
|
||||||
import org.gradle.api.tasks.util.PatternSet
|
|
||||||
import org.gradle.util.GradleVersion
|
|
||||||
import org.gradle.work.ChangeType
|
|
||||||
import org.gradle.work.Incremental
|
import org.gradle.work.Incremental
|
||||||
import org.gradle.work.InputChanges
|
import org.gradle.work.InputChanges
|
||||||
import org.gradle.work.NormalizeLineEndings
|
import org.gradle.work.NormalizeLineEndings
|
||||||
import org.gradle.workers.WorkerExecutor
|
import org.gradle.workers.WorkerExecutor
|
||||||
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
|
|
||||||
import org.jetbrains.kotlin.build.report.metrics.*
|
|
||||||
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
|
||||||
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.compilerRunner.ArgumentUtils
|
||||||
import org.jetbrains.kotlin.compilerRunner.*
|
import org.jetbrains.kotlin.compilerRunner.GradleCompilerEnvironment
|
||||||
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner.Companion.normalizeForFlagFile
|
import org.jetbrains.kotlin.compilerRunner.IncrementalCompilationEnvironment
|
||||||
import org.jetbrains.kotlin.config.JvmTarget
|
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
|
||||||
import org.jetbrains.kotlin.daemon.common.MultiModuleICSettings
|
import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile
|
||||||
import org.jetbrains.kotlin.gradle.dsl.*
|
import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompilerOptions
|
||||||
import org.jetbrains.kotlin.gradle.incremental.*
|
import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompilerOptionsDefault
|
||||||
import org.jetbrains.kotlin.gradle.internal.*
|
import org.jetbrains.kotlin.gradle.dsl.KotlinJsOptions
|
||||||
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
|
|
||||||
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
|
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
|
||||||
import org.jetbrains.kotlin.gradle.logging.GradleErrorMessageCollector
|
import org.jetbrains.kotlin.gradle.logging.GradleErrorMessageCollector
|
||||||
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.logging.kotlinDebug
|
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||||
import org.jetbrains.kotlin.gradle.plugin.*
|
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
|
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
|
||||||
import org.jetbrains.kotlin.gradle.report.*
|
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.internal.LibraryFilterCachingService
|
import org.jetbrains.kotlin.gradle.targets.js.internal.LibraryFilterCachingService
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.internal.UsesLibraryFilterCachingService
|
import org.jetbrains.kotlin.gradle.targets.js.internal.UsesLibraryFilterCachingService
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.ir.*
|
import org.jetbrains.kotlin.gradle.targets.js.ir.DISABLE_PRE_IR
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.ir.PRODUCE_JS
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.ir.PRODUCE_UNZIPPED_KLIB
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.ir.PRODUCE_ZIPPED_KLIB
|
||||||
import org.jetbrains.kotlin.gradle.tasks.internal.KotlinJsOptionsCompat
|
import org.jetbrains.kotlin.gradle.tasks.internal.KotlinJsOptionsCompat
|
||||||
import org.jetbrains.kotlin.gradle.tasks.internal.KotlinJvmOptionsCompat
|
import org.jetbrains.kotlin.gradle.utils.isParentOf
|
||||||
import org.jetbrains.kotlin.gradle.utils.*
|
import org.jetbrains.kotlin.gradle.utils.newInstance
|
||||||
import org.jetbrains.kotlin.incremental.*
|
import org.jetbrains.kotlin.gradle.utils.property
|
||||||
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotDisabled
|
import org.jetbrains.kotlin.incremental.ClasspathChanges
|
||||||
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.*
|
|
||||||
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.IncrementalRun.*
|
|
||||||
import org.jetbrains.kotlin.library.impl.isKotlinLibrary
|
import org.jetbrains.kotlin.library.impl.isKotlinLibrary
|
||||||
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
|
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
|
||||||
import org.jetbrains.kotlin.utils.JsLibraryUtils
|
import org.jetbrains.kotlin.utils.JsLibraryUtils
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Files.*
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import org.jetbrains.kotlin.gradle.tasks.cleanOutputsAndLocalState as cleanOutputsAndLocalStateUtil
|
|
||||||
|
|
||||||
const val KOTLIN_BUILD_DIR_NAME = "kotlin"
|
|
||||||
const val USING_JVM_INCREMENTAL_COMPILATION_MESSAGE = "Using Kotlin/JVM incremental compilation"
|
|
||||||
const val USING_JS_INCREMENTAL_COMPILATION_MESSAGE = "Using Kotlin/JS incremental compilation"
|
|
||||||
const val USING_JS_IR_BACKEND_MESSAGE = "Using Kotlin/JS IR backend"
|
|
||||||
|
|
||||||
abstract class AbstractKotlinCompileTool<T : CommonToolArguments> @Inject constructor(
|
|
||||||
objectFactory: ObjectFactory
|
|
||||||
) : DefaultTask(),
|
|
||||||
KotlinCompileTool,
|
|
||||||
CompilerArgumentAware<T>,
|
|
||||||
TaskWithLocalState {
|
|
||||||
|
|
||||||
private val patternFilterable = PatternSet()
|
|
||||||
|
|
||||||
init {
|
|
||||||
patternFilterable.include(
|
|
||||||
DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS.flatMap { ext -> ext.fileExtensionCasePermutations().map { "**/*.$it" } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val sourceFiles = objectFactory.fileCollection()
|
|
||||||
|
|
||||||
override val sources: FileCollection = objectFactory.fileCollection()
|
|
||||||
.from(
|
|
||||||
{ sourceFiles.asFileTree.matching(patternFilterable) }
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun source(vararg sources: Any) {
|
|
||||||
sourceFiles.from(sources)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setSource(vararg sources: Any) {
|
|
||||||
sourceFiles.from(sources)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun disallowSourceChanges() {
|
|
||||||
sourceFiles.disallowChanges()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
final override fun getIncludes(): MutableSet<String> = patternFilterable.includes
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
final override fun getExcludes(): MutableSet<String> = patternFilterable.excludes
|
|
||||||
|
|
||||||
final override fun setIncludes(includes: Iterable<String>): PatternFilterable = also {
|
|
||||||
patternFilterable.setIncludes(includes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun setExcludes(excludes: Iterable<String>): PatternFilterable = also {
|
|
||||||
patternFilterable.setExcludes(excludes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun include(vararg includes: String?): PatternFilterable = also {
|
|
||||||
patternFilterable.include(*includes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun include(includes: Iterable<String>): PatternFilterable = also {
|
|
||||||
patternFilterable.include(includes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun include(includeSpec: Spec<FileTreeElement>): PatternFilterable = also {
|
|
||||||
patternFilterable.include(includeSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun include(includeSpec: Closure<*>): PatternFilterable = also {
|
|
||||||
patternFilterable.include(includeSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun exclude(vararg excludes: String?): PatternFilterable = also {
|
|
||||||
patternFilterable.exclude(*excludes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun exclude(excludes: Iterable<String>): PatternFilterable = also {
|
|
||||||
patternFilterable.exclude(excludes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun exclude(excludeSpec: Spec<FileTreeElement>): PatternFilterable = also {
|
|
||||||
patternFilterable.exclude(excludeSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun exclude(excludeSpec: Closure<*>): PatternFilterable = also {
|
|
||||||
patternFilterable.exclude(excludeSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
final override val metrics: Property<BuildMetricsReporter> = project.objects
|
|
||||||
.property(BuildMetricsReporterImpl())
|
|
||||||
|
|
||||||
/**
|
|
||||||
* By default, should be set by plugin from [COMPILER_CLASSPATH_CONFIGURATION_NAME] configuration.
|
|
||||||
*
|
|
||||||
* Empty classpath will fail the build.
|
|
||||||
*/
|
|
||||||
@get:Classpath
|
|
||||||
internal val defaultCompilerClasspath: ConfigurableFileCollection =
|
|
||||||
project.objects.fileCollection()
|
|
||||||
|
|
||||||
protected fun validateCompilerClasspath() {
|
|
||||||
// Note that the check triggers configuration resolution
|
|
||||||
require(!defaultCompilerClasspath.isEmpty) {
|
|
||||||
"Default Kotlin compiler classpath is empty! Task: $path (${this::class.qualifiedName})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class GradleCompileTaskProvider @Inject constructor(
|
|
||||||
objectFactory: ObjectFactory,
|
|
||||||
projectLayout: ProjectLayout,
|
|
||||||
gradle: Gradle,
|
|
||||||
task: Task,
|
|
||||||
project: Project,
|
|
||||||
incrementalModuleInfoProvider: Provider<IncrementalModuleInfoProvider>
|
|
||||||
) {
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val path: Provider<String> = objectFactory.property(task.path)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val logger: Provider<Logger> = objectFactory.property(task.logger)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val buildDir: DirectoryProperty = projectLayout.buildDirectory
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val projectDir: Provider<File> = objectFactory
|
|
||||||
.property(project.rootProject.projectDir)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val projectCacheDir: Provider<File> = objectFactory
|
|
||||||
.property(gradle.projectCacheDir)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val sessionsDir: Provider<File> = objectFactory
|
|
||||||
.property(GradleCompilerRunner.sessionsDir(gradle.projectCacheDir))
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val projectName: Provider<String> = objectFactory
|
|
||||||
.property(project.rootProject.name.normalizeForFlagFile())
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val buildModulesInfo: Provider<out IncrementalModuleInfoProvider> = objectFactory
|
|
||||||
.property(incrementalModuleInfoProvider)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val errorsFile: Provider<File?> = objectFactory
|
|
||||||
.property(
|
|
||||||
gradle.rootProject.rootDir.resolve(".gradle/kotlin/errors/").also { it.mkdirs() }
|
|
||||||
.resolve("errors-${System.currentTimeMillis()}.log"))
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constructor(
|
|
||||||
objectFactory: ObjectFactory,
|
|
||||||
workerExecutor: WorkerExecutor
|
|
||||||
) : AbstractKotlinCompileTool<T>(objectFactory),
|
|
||||||
CompileUsingKotlinDaemonWithNormalization,
|
|
||||||
UsesBuildMetricsService,
|
|
||||||
UsesBuildReportsService,
|
|
||||||
UsesIncrementalModuleInfoBuildService,
|
|
||||||
UsesCompilerSystemPropertiesService,
|
|
||||||
UsesVariantImplementationFactories,
|
|
||||||
BaseKotlinCompile {
|
|
||||||
|
|
||||||
init {
|
|
||||||
cacheOnlyIfEnabledForKotlin()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val layout = project.layout
|
|
||||||
|
|
||||||
@get:Inject
|
|
||||||
internal abstract val fileSystemOperations: FileSystemOperations
|
|
||||||
|
|
||||||
// avoid creating directory in getter: this can lead to failure in parallel build
|
|
||||||
@get:OutputDirectory
|
|
||||||
internal open val taskBuildCacheableOutputDirectory: DirectoryProperty = objectFactory.directoryProperty()
|
|
||||||
|
|
||||||
// avoid creating directory in getter: this can lead to failure in parallel build
|
|
||||||
@get:LocalState
|
|
||||||
internal abstract val taskBuildLocalStateDirectory: DirectoryProperty
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val buildHistoryFile
|
|
||||||
get() = taskBuildLocalStateDirectory.file("build-history.bin")
|
|
||||||
|
|
||||||
// indicates that task should compile kotlin incrementally if possible
|
|
||||||
// it's not possible when IncrementalTaskInputs#isIncremental returns false (i.e first build)
|
|
||||||
// todo: deprecate and remove (we may need to design api for configuring IC)
|
|
||||||
// don't rely on it to check if IC is enabled, use isIncrementalCompilationEnabled instead
|
|
||||||
@get:Internal
|
|
||||||
var incremental: Boolean = false
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
logger.kotlinDebug { "Set $this.incremental=$value" }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Input
|
|
||||||
internal open fun isIncrementalCompilationEnabled(): Boolean =
|
|
||||||
incremental
|
|
||||||
|
|
||||||
@Deprecated("Scheduled for removal with Kotlin 1.9", ReplaceWith("moduleName"))
|
|
||||||
@get:Input
|
|
||||||
abstract val ownModuleName: Property<String>
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val startParameters = BuildReportsService.getStartParameters(project)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal abstract val suppressKotlinOptionsFreeArgsModificationWarning: Property<Boolean>
|
|
||||||
|
|
||||||
internal fun reportingSettings() = buildReportsService.orNull?.parameters?.reportingSettings?.orNull ?: ReportingSettings()
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
protected val multiModuleICSettings: MultiModuleICSettings
|
|
||||||
get() = MultiModuleICSettings(buildHistoryFile.get().asFile, useModuleDetection.get())
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plugin Data provided by [KpmCompilerPlugin]
|
|
||||||
*/
|
|
||||||
@get:Optional
|
|
||||||
@get:Nested
|
|
||||||
// TODO: replace with objects.property and introduce task configurator
|
|
||||||
internal var kotlinPluginData: Provider<KotlinCompilerPluginData>? = null
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val javaOutputDir: DirectoryProperty = objectFactory.directoryProperty()
|
|
||||||
|
|
||||||
@get:InputFiles
|
|
||||||
@get:IgnoreEmptyDirectories
|
|
||||||
@get:Incremental
|
|
||||||
@get:NormalizeLineEndings
|
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
internal val commonSourceSet: ConfigurableFileCollection = objectFactory.fileCollection()
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val abiSnapshotFile
|
|
||||||
get() = taskBuildCacheableOutputDirectory.file(IncrementalCompilerRunner.ABI_SNAPSHOT_FILE_NAME)
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
val abiSnapshotRelativePath: Property<String> = objectFactory.property(String::class.java).value(
|
|
||||||
//TODO update to support any jar changes
|
|
||||||
"$name/${IncrementalCompilerRunner.ABI_SNAPSHOT_FILE_NAME}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val friendSourceSets = objectFactory.listProperty(String::class.java)
|
|
||||||
|
|
||||||
private val kotlinLogger by lazy { GradleKotlinLogger(logger) }
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
protected val gradleCompileTaskProvider: Provider<GradleCompileTaskProvider> = objectFactory
|
|
||||||
.property(
|
|
||||||
objectFactory.newInstance<GradleCompileTaskProvider>(project.gradle, this, project, incrementalModuleInfoProvider)
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal open val defaultKotlinJavaToolchain: Provider<DefaultKotlinJavaToolchain> = objectFactory
|
|
||||||
.propertyWithNewInstance({ null })
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val compilerRunner: Provider<GradleCompilerRunner> =
|
|
||||||
objectFactory.propertyWithConvention(
|
|
||||||
gradleCompileTaskProvider.flatMap { taskProvider ->
|
|
||||||
compilerExecutionStrategy
|
|
||||||
.zip(metrics) { executionStrategy, metrics ->
|
|
||||||
metrics to executionStrategy
|
|
||||||
}
|
|
||||||
.flatMap { params ->
|
|
||||||
defaultKotlinJavaToolchain
|
|
||||||
.map {
|
|
||||||
val toolsJar = it.currentJvmJdkToolsJar.orNull
|
|
||||||
GradleCompilerRunnerWithWorkers(
|
|
||||||
taskProvider,
|
|
||||||
toolsJar,
|
|
||||||
CompilerExecutionSettings(
|
|
||||||
normalizedKotlinDaemonJvmArguments.orNull,
|
|
||||||
params.second,
|
|
||||||
useDaemonFallbackStrategy.get()
|
|
||||||
),
|
|
||||||
params.first,
|
|
||||||
workerExecutor
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal abstract val preciseCompilationResultsBackup: Property<Boolean>
|
|
||||||
|
|
||||||
/** Task outputs that we don't want to include in [TaskOutputsBackup] (see [TaskOutputsBackup.outputsToRestore] for more info). */
|
|
||||||
@get:Internal
|
|
||||||
internal abstract val taskOutputsBackupExcludes: SetProperty<File>
|
|
||||||
|
|
||||||
@TaskAction
|
|
||||||
fun execute(inputChanges: InputChanges) {
|
|
||||||
val buildMetrics = metrics.get()
|
|
||||||
buildMetrics.addTimeMetric(BuildPerformanceMetric.START_TASK_ACTION_EXECUTION)
|
|
||||||
buildMetrics.measure(BuildTime.OUT_OF_WORKER_TASK_ACTION) {
|
|
||||||
KotlinBuildStatsService.applyIfInitialised {
|
|
||||||
if (name.contains("Test"))
|
|
||||||
it.report(BooleanMetrics.TESTS_EXECUTED, true)
|
|
||||||
else
|
|
||||||
it.report(BooleanMetrics.COMPILATION_STARTED, true)
|
|
||||||
}
|
|
||||||
validateCompilerClasspath()
|
|
||||||
systemPropertiesService.get().startIntercept()
|
|
||||||
CompilerSystemProperties.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY.value = "true"
|
|
||||||
|
|
||||||
// If task throws exception, but its outputs are changed during execution,
|
|
||||||
// then Gradle forces next build to be non-incremental (see Gradle's DefaultTaskArtifactStateRepository#persistNewOutputs)
|
|
||||||
// To prevent this, we backup outputs before incremental build and restore when exception is thrown
|
|
||||||
val outputsBackup: TaskOutputsBackup? =
|
|
||||||
if (isIncrementalCompilationEnabled() && inputChanges.isIncremental)
|
|
||||||
buildMetrics.measure(BuildTime.BACKUP_OUTPUT) {
|
|
||||||
TaskOutputsBackup(
|
|
||||||
fileSystemOperations,
|
|
||||||
layout.buildDirectory,
|
|
||||||
layout.buildDirectory.dir("snapshot/kotlin/$name"),
|
|
||||||
outputsToRestore = allOutputFiles() - taskOutputsBackupExcludes.get(),
|
|
||||||
logger
|
|
||||||
).also {
|
|
||||||
it.createSnapshot()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else null
|
|
||||||
|
|
||||||
if (!isIncrementalCompilationEnabled()) {
|
|
||||||
cleanOutputsAndLocalState("IC is disabled")
|
|
||||||
} else if (!inputChanges.isIncremental) {
|
|
||||||
cleanOutputsAndLocalState("Task cannot run incrementally")
|
|
||||||
}
|
|
||||||
|
|
||||||
executeImpl(inputChanges, outputsBackup)
|
|
||||||
}
|
|
||||||
|
|
||||||
buildMetricsService.orNull?.also { it.addTask(path, this.javaClass, buildMetrics) }
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun cleanOutputsAndLocalState(reason: String?) {
|
|
||||||
cleanOutputsAndLocalStateUtil(reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun skipCondition(): Boolean = sources.isEmpty
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
protected open val incrementalProps: List<FileCollection>
|
|
||||||
get() = listOfNotNull(
|
|
||||||
sources,
|
|
||||||
libraries,
|
|
||||||
commonSourceSet
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun executeImpl(
|
|
||||||
inputChanges: InputChanges,
|
|
||||||
taskOutputsBackup: TaskOutputsBackup?
|
|
||||||
) {
|
|
||||||
val allKotlinSources = sources.asFileTree.files
|
|
||||||
|
|
||||||
logger.kotlinDebug { "All kotlin sources: ${allKotlinSources.pathsAsStringRelativeTo(gradleCompileTaskProvider.get().projectDir.get())}" }
|
|
||||||
|
|
||||||
if (!inputChanges.isIncremental && skipCondition()) {
|
|
||||||
// Skip running only if non-incremental run. Otherwise, we may need to do some cleanup.
|
|
||||||
logger.kotlinDebug { "No Kotlin files found, skipping Kotlin compiler task" }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val args = prepareCompilerArguments()
|
|
||||||
taskBuildCacheableOutputDirectory.get().asFile.mkdirs()
|
|
||||||
taskBuildLocalStateDirectory.get().asFile.mkdirs()
|
|
||||||
callCompilerAsync(
|
|
||||||
args,
|
|
||||||
allKotlinSources,
|
|
||||||
inputChanges,
|
|
||||||
taskOutputsBackup
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun getChangedFiles(
|
|
||||||
inputChanges: InputChanges,
|
|
||||||
incrementalProps: List<FileCollection>
|
|
||||||
) = if (!inputChanges.isIncremental) {
|
|
||||||
ChangedFiles.Unknown()
|
|
||||||
} else {
|
|
||||||
incrementalProps
|
|
||||||
.fold(mutableListOf<File>() to mutableListOf<File>()) { (modified, removed), prop ->
|
|
||||||
inputChanges.getFileChanges(prop).forEach {
|
|
||||||
when (it.changeType) {
|
|
||||||
ChangeType.ADDED, ChangeType.MODIFIED -> modified.add(it.file)
|
|
||||||
ChangeType.REMOVED -> removed.add(it.file)
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modified to removed
|
|
||||||
}
|
|
||||||
.run {
|
|
||||||
ChangedFiles.Known(first, second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compiler might be executed asynchronously. Do not do anything requiring end of compilation after this function is called.
|
|
||||||
* @see [GradleKotlinCompilerWork]
|
|
||||||
*/
|
|
||||||
internal abstract fun callCompilerAsync(
|
|
||||||
args: T,
|
|
||||||
kotlinSources: Set<File>,
|
|
||||||
inputChanges: InputChanges,
|
|
||||||
taskOutputsBackup: TaskOutputsBackup?
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val abstractKotlinCompileArgumentsContributor by lazy {
|
|
||||||
AbstractKotlinCompileArgumentsContributor(
|
|
||||||
KotlinCompileArgumentsProvider(this)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setupCompilerArgs(args: T, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
|
|
||||||
abstractKotlinCompileArgumentsContributor.contributeArguments(
|
|
||||||
args,
|
|
||||||
compilerArgumentsConfigurationFlags(defaultsOnly, ignoreClasspathResolutionErrors)
|
|
||||||
)
|
|
||||||
if (reportingSettings().buildReportMode == BuildReportMode.VERBOSE) {
|
|
||||||
args.reportPerf = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open class KotlinCompileArgumentsProvider<T : AbstractKotlinCompile<out CommonCompilerArguments>>(taskProvider: T) {
|
|
||||||
|
|
||||||
val logger: Logger = taskProvider.logger
|
|
||||||
val isMultiplatform: Boolean = taskProvider.multiPlatformEnabled.get()
|
|
||||||
private val pluginData = taskProvider.kotlinPluginData?.orNull
|
|
||||||
val pluginClasspath: FileCollection = listOfNotNull(taskProvider.pluginClasspath, pluginData?.classpath).reduce(FileCollection::plus)
|
|
||||||
val pluginOptions: CompilerPluginOptions = taskProvider.pluginOptions.toSingleCompilerPluginOptions() + pluginData?.options
|
|
||||||
}
|
|
||||||
|
|
||||||
class KotlinJvmCompilerArgumentsProvider
|
|
||||||
(taskProvider: KotlinCompile) : KotlinCompileArgumentsProvider<KotlinCompile>(taskProvider) {
|
|
||||||
val taskName: String = taskProvider.name
|
|
||||||
val moduleName: String = taskProvider.moduleName.get()
|
|
||||||
val friendPaths: FileCollection = taskProvider.friendPaths
|
|
||||||
val compileClasspath: Iterable<File> = taskProvider.libraries
|
|
||||||
val destinationDir: File = taskProvider.destinationDirectory.get().asFile
|
|
||||||
internal val compilerOptions: KotlinJvmCompilerOptions = taskProvider.compilerOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
internal inline val <reified T : Task> T.thisTaskProvider: TaskProvider<out T>
|
|
||||||
get() = checkNotNull(project.locateTask<T>(name))
|
|
||||||
|
|
||||||
@CacheableTask
|
|
||||||
abstract class KotlinCompile @Inject constructor(
|
|
||||||
final override val compilerOptions: KotlinJvmCompilerOptions,
|
|
||||||
workerExecutor: WorkerExecutor,
|
|
||||||
objectFactory: ObjectFactory
|
|
||||||
) : AbstractKotlinCompile<K2JVMCompilerArguments>(objectFactory, workerExecutor),
|
|
||||||
K2MultiplatformCompilationTask,
|
|
||||||
@Suppress("TYPEALIAS_EXPANSION_DEPRECATION") KotlinJvmCompileDsl,
|
|
||||||
KotlinCompilationTask<KotlinJvmCompilerOptions>,
|
|
||||||
UsesKotlinJavaToolchain {
|
|
||||||
|
|
||||||
init {
|
|
||||||
compilerOptions.moduleName.convention(moduleName)
|
|
||||||
compilerOptions.verbose.convention(logger.isDebugEnabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override val kotlinOptions: KotlinJvmOptions = KotlinJvmOptionsCompat(
|
|
||||||
{ this },
|
|
||||||
compilerOptions
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
@Deprecated("Configure compilerOptions directly", replaceWith = ReplaceWith("compilerOptions"))
|
|
||||||
override val parentKotlinOptions: Property<KotlinJvmOptions> = objectFactory
|
|
||||||
.property(kotlinOptions)
|
|
||||||
.chainedDisallowChanges()
|
|
||||||
|
|
||||||
@get:Nested
|
|
||||||
override val multiplatformStructure: K2MultiplatformStructure = objectFactory.newInstance()
|
|
||||||
|
|
||||||
/** A package prefix that is used for locating Java sources in a directory structure with non-full-depth packages.
|
|
||||||
*
|
|
||||||
* Example: a Java source file with `package com.example.my.package` is located in directory `src/main/java/my/package`.
|
|
||||||
* Then, for the Kotlin compilation to locate the source file, use package prefix `"com.example"` */
|
|
||||||
@get:Input
|
|
||||||
@get:Optional
|
|
||||||
var javaPackagePrefix: String? = null
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
var usePreciseJavaTracking: Boolean = true
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
logger.kotlinDebug { "Set $this.usePreciseJavaTracking=$value" }
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Internal // To support compile avoidance (ClasspathSnapshotProperties.classpathSnapshot will be used as input instead)
|
|
||||||
abstract override val libraries: ConfigurableFileCollection
|
|
||||||
|
|
||||||
@Deprecated(
|
|
||||||
"Replaced with 'libraries' input",
|
|
||||||
replaceWith = ReplaceWith("libraries"),
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
@get:Internal
|
|
||||||
var classpath: FileCollection
|
|
||||||
set(value) = libraries.setFrom(value)
|
|
||||||
get() = libraries
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
abstract val useKotlinAbiSnapshot: Property<Boolean>
|
|
||||||
|
|
||||||
@get:Nested
|
|
||||||
abstract val classpathSnapshotProperties: ClasspathSnapshotProperties
|
|
||||||
|
|
||||||
/** Properties related to the `kotlin.incremental.useClasspathSnapshot` feature. */
|
|
||||||
abstract class ClasspathSnapshotProperties {
|
|
||||||
@get:Input
|
|
||||||
abstract val useClasspathSnapshot: Property<Boolean>
|
|
||||||
|
|
||||||
@get:Classpath
|
|
||||||
@get:Incremental
|
|
||||||
@get:Optional // Set if useClasspathSnapshot == true
|
|
||||||
abstract val classpathSnapshot: ConfigurableFileCollection
|
|
||||||
|
|
||||||
@get:Classpath
|
|
||||||
@get:Incremental
|
|
||||||
@get:Optional // Set if useClasspathSnapshot == false (to restore the existing classpath annotations when the feature is disabled)
|
|
||||||
abstract val classpath: ConfigurableFileCollection
|
|
||||||
|
|
||||||
@get:OutputDirectory
|
|
||||||
@get:Optional // Set if useClasspathSnapshot == true
|
|
||||||
abstract val classpathSnapshotDir: DirectoryProperty
|
|
||||||
}
|
|
||||||
|
|
||||||
override val incrementalProps: List<FileCollection>
|
|
||||||
get() = listOf(
|
|
||||||
sources,
|
|
||||||
javaSources,
|
|
||||||
scriptSources,
|
|
||||||
androidLayoutResources,
|
|
||||||
commonSourceSet,
|
|
||||||
classpathSnapshotProperties.classpath,
|
|
||||||
classpathSnapshotProperties.classpathSnapshot
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
final override val defaultKotlinJavaToolchain: Provider<DefaultKotlinJavaToolchain> = objectFactory
|
|
||||||
.propertyWithNewInstance({ this })
|
|
||||||
|
|
||||||
final override val kotlinJavaToolchainProvider: Provider<KotlinJavaToolchain> = defaultKotlinJavaToolchain.cast()
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal abstract val associatedJavaCompileTaskTargetCompatibility: Property<String>
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal abstract val associatedJavaCompileTaskName: Property<String>
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
internal abstract val jvmTargetValidationMode: Property<PropertiesProvider.JvmTargetValidationMode>
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val scriptDefinitions: ConfigurableFileCollection = objectFactory.fileCollection()
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
@get:Optional
|
|
||||||
internal val scriptExtensions: SetProperty<String> = objectFactory.setPropertyWithLazyValue {
|
|
||||||
scriptDefinitions
|
|
||||||
.map { definitionFile ->
|
|
||||||
definitionFile.readLines().filter(String::isNotBlank)
|
|
||||||
}
|
|
||||||
.flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ScriptFilterSpec(
|
|
||||||
private val scriptExtensions: SetProperty<String>
|
|
||||||
) : Spec<FileTreeElement> {
|
|
||||||
override fun isSatisfiedBy(element: FileTreeElement): Boolean {
|
|
||||||
val extensions = scriptExtensions.get()
|
|
||||||
return extensions.isNotEmpty() &&
|
|
||||||
(element.isDirectory || extensions.contains(element.file.extension))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val scriptSourceFiles = objectFactory.fileCollection()
|
|
||||||
|
|
||||||
@get:InputFiles
|
|
||||||
@get:SkipWhenEmpty
|
|
||||||
@get:IgnoreEmptyDirectories
|
|
||||||
@get:NormalizeLineEndings
|
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
internal open val scriptSources: FileCollection = scriptSourceFiles
|
|
||||||
.asFileTree
|
|
||||||
.matching { patternFilterable ->
|
|
||||||
patternFilterable.include(ScriptFilterSpec(scriptExtensions))
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
incremental = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun skipCondition(): Boolean = sources.isEmpty && scriptSources.isEmpty
|
|
||||||
|
|
||||||
override fun createCompilerArgs(): K2JVMCompilerArguments =
|
|
||||||
K2JVMCompilerArguments()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Workaround for those "nasty" plugins that are adding 'freeCompilerArgs' on task execution phase.
|
|
||||||
* With properties api it is not possible to update property value after task configuration is finished.
|
|
||||||
*
|
|
||||||
* Marking it as `@Internal` as anyway on the configuration phase, when Gradle does task inputs snapshot,
|
|
||||||
* this input will always be empty.
|
|
||||||
*/
|
|
||||||
@get:Internal
|
|
||||||
internal var executionTimeFreeCompilerArgs: List<String>? = null
|
|
||||||
|
|
||||||
override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
|
|
||||||
compilerArgumentsContributor.contributeArguments(
|
|
||||||
args, compilerArgumentsConfigurationFlags(
|
|
||||||
defaultsOnly,
|
|
||||||
ignoreClasspathResolutionErrors
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
args.configureMultiplatform(
|
|
||||||
compilerOptions,
|
|
||||||
k1CommonSources = commonSourceSet.asFileTree,
|
|
||||||
k2MultiplatformFragments = multiplatformStructure
|
|
||||||
)
|
|
||||||
|
|
||||||
if (reportingSettings().buildReportMode == BuildReportMode.VERBOSE) {
|
|
||||||
args.reportPerf = true
|
|
||||||
}
|
|
||||||
|
|
||||||
val localExecutionTimeFreeCompilerArgs = executionTimeFreeCompilerArgs
|
|
||||||
if (localExecutionTimeFreeCompilerArgs != null) {
|
|
||||||
args.freeArgs = localExecutionTimeFreeCompilerArgs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val compilerArgumentsContributor: CompilerArgumentsContributor<K2JVMCompilerArguments> by lazy {
|
|
||||||
KotlinJvmCompilerArgumentsContributor(KotlinJvmCompilerArgumentsProvider(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun callCompilerAsync(
|
|
||||||
args: K2JVMCompilerArguments,
|
|
||||||
kotlinSources: Set<File>,
|
|
||||||
inputChanges: InputChanges,
|
|
||||||
taskOutputsBackup: TaskOutputsBackup?
|
|
||||||
) {
|
|
||||||
validateKotlinAndJavaHasSameTargetCompatibility(args)
|
|
||||||
|
|
||||||
val scriptSources = scriptSources.asFileTree.files
|
|
||||||
val javaSources = javaSources.files
|
|
||||||
val gradlePrintingMessageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors)
|
|
||||||
val gradleMessageCollector = GradleErrorMessageCollector(gradlePrintingMessageCollector, kotlinPluginVersion = getKotlinPluginVersion(logger))
|
|
||||||
val outputItemCollector = OutputItemsCollectorImpl()
|
|
||||||
val compilerRunner = compilerRunner.get()
|
|
||||||
|
|
||||||
val icEnv = if (isIncrementalCompilationEnabled()) {
|
|
||||||
logger.info(USING_JVM_INCREMENTAL_COMPILATION_MESSAGE)
|
|
||||||
IncrementalCompilationEnvironment(
|
|
||||||
changedFiles = getChangedFiles(inputChanges, incrementalProps),
|
|
||||||
classpathChanges = getClasspathChanges(inputChanges),
|
|
||||||
workingDir = taskBuildCacheableOutputDirectory.get().asFile,
|
|
||||||
usePreciseJavaTracking = usePreciseJavaTracking,
|
|
||||||
disableMultiModuleIC = disableMultiModuleIC,
|
|
||||||
multiModuleICSettings = multiModuleICSettings,
|
|
||||||
withAbiSnapshot = useKotlinAbiSnapshot.get(),
|
|
||||||
preciseCompilationResultsBackup = preciseCompilationResultsBackup.get(),
|
|
||||||
)
|
|
||||||
} else null
|
|
||||||
|
|
||||||
@Suppress("ConvertArgumentToSet")
|
|
||||||
val environment = GradleCompilerEnvironment(
|
|
||||||
defaultCompilerClasspath, gradleMessageCollector, outputItemCollector,
|
|
||||||
// In the incremental compiler, outputFiles will be cleaned on rebuild. However, because classpathSnapshotDir is not included in
|
|
||||||
// TaskOutputsBackup, we don't want classpathSnapshotDir to be cleaned immediately on rebuild, and therefore we exclude it from
|
|
||||||
// outputFiles here. (See TaskOutputsBackup's kdoc for more info.)
|
|
||||||
outputFiles = allOutputFiles()
|
|
||||||
- (classpathSnapshotProperties.classpathSnapshotDir.orNull?.asFile?.let { setOf(it) } ?: emptySet()),
|
|
||||||
reportingSettings = reportingSettings(),
|
|
||||||
incrementalCompilationEnvironment = icEnv,
|
|
||||||
kotlinScriptExtensions = scriptExtensions.get().toTypedArray()
|
|
||||||
)
|
|
||||||
logger.info("Kotlin source files: ${kotlinSources.joinToString()}")
|
|
||||||
logger.info("Java source files: ${javaSources.joinToString()}")
|
|
||||||
logger.info("Script source files: ${scriptSources.joinToString()}")
|
|
||||||
logger.info("Script file extensions: ${scriptExtensions.get().joinToString()}")
|
|
||||||
compilerRunner.runJvmCompilerAsync(
|
|
||||||
(kotlinSources + scriptSources + javaSources).toList(),
|
|
||||||
javaPackagePrefix,
|
|
||||||
args,
|
|
||||||
environment,
|
|
||||||
defaultKotlinJavaToolchain.get().buildJvm.get().javaHome,
|
|
||||||
taskOutputsBackup
|
|
||||||
)
|
|
||||||
compilerRunner.errorsFile?.also { gradleMessageCollector.flush(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun validateKotlinAndJavaHasSameTargetCompatibility(
|
|
||||||
args: K2JVMCompilerArguments,
|
|
||||||
) {
|
|
||||||
associatedJavaCompileTaskTargetCompatibility.orNull?.let { targetCompatibility ->
|
|
||||||
val normalizedJavaTarget = when (targetCompatibility) {
|
|
||||||
"6" -> "1.6"
|
|
||||||
"7" -> "1.7"
|
|
||||||
"8" -> "1.8"
|
|
||||||
"1.9" -> "9"
|
|
||||||
else -> targetCompatibility
|
|
||||||
}
|
|
||||||
|
|
||||||
val jvmTarget = args.jvmTarget ?: JvmTarget.DEFAULT.toString()
|
|
||||||
if (normalizedJavaTarget != jvmTarget) {
|
|
||||||
val javaTaskName = associatedJavaCompileTaskName.get()
|
|
||||||
|
|
||||||
val errorMessage = buildString {
|
|
||||||
append("'$javaTaskName' task (current target is $targetCompatibility) and ")
|
|
||||||
append("'$name' task (current target is $jvmTarget) ")
|
|
||||||
appendLine("jvm target compatibility should be set to the same Java version.")
|
|
||||||
if (GradleVersion.current().baseVersion < GradleVersion.version("8.0")) {
|
|
||||||
append("By default will become an error since Gradle 8.0+! ")
|
|
||||||
appendLine("Read more: https://kotl.in/gradle/jvm/target-validation")
|
|
||||||
}
|
|
||||||
appendLine("Consider using JVM toolchain: https://kotl.in/gradle/jvm/toolchain")
|
|
||||||
}
|
|
||||||
|
|
||||||
when (jvmTargetValidationMode.get()) {
|
|
||||||
PropertiesProvider.JvmTargetValidationMode.ERROR -> throw GradleException(errorMessage)
|
|
||||||
PropertiesProvider.JvmTargetValidationMode.WARNING -> logger.warn(errorMessage)
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
val disableMultiModuleIC: Boolean by lazy {
|
|
||||||
if (!isIncrementalCompilationEnabled() || !javaOutputDir.isPresent) {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
|
|
||||||
var illegalTaskOrNull: AbstractCompile? = null
|
|
||||||
project.tasks.configureEach {
|
|
||||||
if (it is AbstractCompile &&
|
|
||||||
it !is JavaCompile &&
|
|
||||||
it !is AbstractKotlinCompile<*> &&
|
|
||||||
javaOutputDir.get().asFile.isParentOf(it.destinationDirectory.get().asFile)
|
|
||||||
) {
|
|
||||||
illegalTaskOrNull = illegalTaskOrNull ?: it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (illegalTaskOrNull != null) {
|
|
||||||
val illegalTask = illegalTaskOrNull!!
|
|
||||||
logger.info(
|
|
||||||
"Kotlin inter-project IC is disabled: " +
|
|
||||||
"unknown task '$illegalTask' destination dir ${illegalTask.destinationDirectory.get().asFile} " +
|
|
||||||
"intersects with java destination dir $javaOutputDir"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
illegalTaskOrNull != null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val javaSourceFiles = objectFactory.fileCollection()
|
|
||||||
|
|
||||||
private fun javaFilesPatternFilter(patternFilterable: PatternFilterable) {
|
|
||||||
patternFilterable.include(
|
|
||||||
"java".fileExtensionCasePermutations().map { "**/*.$it" }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Incremental
|
|
||||||
@get:InputFiles
|
|
||||||
@get:IgnoreEmptyDirectories
|
|
||||||
@get:NormalizeLineEndings
|
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
open val javaSources: FileCollection = objectFactory.fileCollection()
|
|
||||||
.from(
|
|
||||||
javaSourceFiles
|
|
||||||
.asFileTree
|
|
||||||
.matching(::javaFilesPatternFilter)
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val androidLayoutResourceFiles = objectFactory.fileCollection()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This input is used by android-extensions plugin
|
|
||||||
*/
|
|
||||||
@get:Incremental
|
|
||||||
@get:InputFiles
|
|
||||||
@get:IgnoreEmptyDirectories
|
|
||||||
@get:NormalizeLineEndings
|
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
internal open val androidLayoutResources: FileCollection = androidLayoutResourceFiles
|
|
||||||
.asFileTree
|
|
||||||
.matching { patternFilterable ->
|
|
||||||
patternFilterable.include("xml".fileExtensionCasePermutations().map { "**/*.$it" })
|
|
||||||
}
|
|
||||||
|
|
||||||
// override setSource to track Java and script sources as well
|
|
||||||
override fun source(vararg sources: Any) {
|
|
||||||
javaSourceFiles.from(sources)
|
|
||||||
scriptSourceFiles.from(sources)
|
|
||||||
super.setSource(sources)
|
|
||||||
}
|
|
||||||
|
|
||||||
// override source to track Java and script sources as well
|
|
||||||
override fun setSource(vararg sources: Any) {
|
|
||||||
javaSourceFiles.from(*sources)
|
|
||||||
scriptSourceFiles.from(*sources)
|
|
||||||
super.setSource(*sources)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getClasspathChanges(inputChanges: InputChanges): ClasspathChanges = when {
|
|
||||||
!classpathSnapshotProperties.useClasspathSnapshot.get() -> ClasspathSnapshotDisabled
|
|
||||||
else -> {
|
|
||||||
val classpathSnapshotFiles = ClasspathSnapshotFiles(
|
|
||||||
classpathSnapshotProperties.classpathSnapshot.files.toList(),
|
|
||||||
classpathSnapshotProperties.classpathSnapshotDir.get().asFile
|
|
||||||
)
|
|
||||||
when {
|
|
||||||
!inputChanges.isIncremental -> NotAvailableForNonIncrementalRun(classpathSnapshotFiles)
|
|
||||||
// When `inputChanges.isIncremental == true`, we want to compile incrementally. However, if the incremental state (e.g.
|
|
||||||
// lookup caches or previous classpath snapshot) is missing, we need to compile non-incrementally. There are a few cases:
|
|
||||||
// 1. Previous compilation happened using Kotlin daemon and with IC enabled (the usual case) => Incremental state
|
|
||||||
// including classpath snapshot must have been produced (we have that guarantee in `IncrementalCompilerRunner`).
|
|
||||||
// 2. Previous compilation happened using Kotlin daemon but with IC disabled (e.g., by setting `kotlin.incremental=false`)
|
|
||||||
// => This run will have `inputChanges.isIncremental = false` as `isIncrementalCompilationEnabled` is a task input.
|
|
||||||
// 3. Previous compilation happened without using Kotlin daemon (set by the user or caused by a fallback).
|
|
||||||
// 4. Previous compilation was skipped because there were no sources to compile (see `AbstractKotlinCompile.executeImpl`).
|
|
||||||
// In case 3 and 4, it is possible that `inputChanges.isIncremental == true` in this run and incremental state is missing.
|
|
||||||
!classpathSnapshotFiles.shrunkPreviousClasspathSnapshotFile.exists() -> {
|
|
||||||
NotAvailableDueToMissingClasspathSnapshot(classpathSnapshotFiles)
|
|
||||||
}
|
|
||||||
|
|
||||||
inputChanges.getFileChanges(classpathSnapshotProperties.classpathSnapshot).none() -> NoChanges(classpathSnapshotFiles)
|
|
||||||
else -> ToBeComputedByIncrementalCompiler(classpathSnapshotFiles)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@CacheableTask
|
@CacheableTask
|
||||||
abstract class Kotlin2JsCompile @Inject constructor(
|
abstract class Kotlin2JsCompile @Inject constructor(
|
||||||
@@ -1198,32 +335,4 @@ abstract class Kotlin2JsCompile @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class KotlinCompilerPluginData(
|
|
||||||
@get:Classpath
|
|
||||||
val classpath: FileCollection,
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val options: CompilerPluginOptions,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used only for Up-to-date checks
|
|
||||||
*/
|
|
||||||
@get:Nested
|
|
||||||
val inputsOutputsState: InputsOutputsState
|
|
||||||
) {
|
|
||||||
data class InputsOutputsState(
|
|
||||||
@get:Input
|
|
||||||
val inputs: Map<String, String>,
|
|
||||||
|
|
||||||
@get:InputFiles
|
|
||||||
@get:IgnoreEmptyDirectories
|
|
||||||
@get:NormalizeLineEndings
|
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
val inputFiles: Set<File>,
|
|
||||||
|
|
||||||
@get:OutputFiles
|
|
||||||
val outputFiles: Set<File>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
+28
-804
@@ -1,17 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2023 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.
|
* 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.tasks
|
package org.jetbrains.kotlin.gradle.tasks
|
||||||
|
|
||||||
import groovy.lang.Closure
|
import org.gradle.api.GradleException
|
||||||
import org.gradle.api.*
|
import org.gradle.api.file.ConfigurableFileCollection
|
||||||
import org.gradle.api.file.*
|
import org.gradle.api.file.DirectoryProperty
|
||||||
import org.gradle.api.invocation.Gradle
|
import org.gradle.api.file.FileCollection
|
||||||
import org.gradle.api.logging.Logger
|
import org.gradle.api.file.FileTreeElement
|
||||||
import org.gradle.api.model.ObjectFactory
|
import org.gradle.api.model.ObjectFactory
|
||||||
import org.gradle.api.provider.ListProperty
|
|
||||||
import org.gradle.api.provider.Property
|
import org.gradle.api.provider.Property
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.provider.SetProperty
|
import org.gradle.api.provider.SetProperty
|
||||||
@@ -20,505 +19,44 @@ 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.api.tasks.util.PatternFilterable
|
import org.gradle.api.tasks.util.PatternFilterable
|
||||||
import org.gradle.api.tasks.util.PatternSet
|
|
||||||
import org.gradle.util.GradleVersion
|
import org.gradle.util.GradleVersion
|
||||||
import org.gradle.work.ChangeType
|
|
||||||
import org.gradle.work.Incremental
|
import org.gradle.work.Incremental
|
||||||
import org.gradle.work.InputChanges
|
import org.gradle.work.InputChanges
|
||||||
import org.gradle.work.NormalizeLineEndings
|
import org.gradle.work.NormalizeLineEndings
|
||||||
import org.gradle.workers.WorkerExecutor
|
import org.gradle.workers.WorkerExecutor
|
||||||
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
|
|
||||||
import org.jetbrains.kotlin.build.report.metrics.*
|
|
||||||
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
|
|
||||||
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.GradleCompilerEnvironment
|
||||||
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner.Companion.normalizeForFlagFile
|
import org.jetbrains.kotlin.compilerRunner.IncrementalCompilationEnvironment
|
||||||
|
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
|
||||||
import org.jetbrains.kotlin.config.JvmTarget
|
import org.jetbrains.kotlin.config.JvmTarget
|
||||||
import org.jetbrains.kotlin.daemon.common.MultiModuleICSettings
|
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions
|
||||||
import org.jetbrains.kotlin.gradle.dsl.*
|
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
|
||||||
import org.jetbrains.kotlin.gradle.incremental.*
|
import org.jetbrains.kotlin.gradle.internal.CompilerArgumentsContributor
|
||||||
import org.jetbrains.kotlin.gradle.internal.*
|
import org.jetbrains.kotlin.gradle.internal.KotlinJvmCompilerArgumentsContributor
|
||||||
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
|
import org.jetbrains.kotlin.gradle.internal.compilerArgumentsConfigurationFlags
|
||||||
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
|
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
|
||||||
import org.jetbrains.kotlin.gradle.logging.GradleErrorMessageCollector
|
import org.jetbrains.kotlin.gradle.logging.GradleErrorMessageCollector
|
||||||
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.logging.kotlinDebug
|
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||||
import org.jetbrains.kotlin.gradle.plugin.*
|
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
|
import org.jetbrains.kotlin.gradle.report.BuildReportMode
|
||||||
import org.jetbrains.kotlin.gradle.report.*
|
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.internal.LibraryFilterCachingService
|
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.internal.UsesLibraryFilterCachingService
|
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.ir.*
|
|
||||||
import org.jetbrains.kotlin.gradle.tasks.internal.KotlinJsOptionsCompat
|
|
||||||
import org.jetbrains.kotlin.gradle.tasks.internal.KotlinJvmOptionsCompat
|
import org.jetbrains.kotlin.gradle.tasks.internal.KotlinJvmOptionsCompat
|
||||||
import org.jetbrains.kotlin.gradle.utils.*
|
import org.jetbrains.kotlin.gradle.utils.*
|
||||||
import org.jetbrains.kotlin.incremental.*
|
import org.jetbrains.kotlin.gradle.utils.chainedDisallowChanges
|
||||||
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotDisabled
|
import org.jetbrains.kotlin.gradle.utils.newInstance
|
||||||
|
import org.jetbrains.kotlin.gradle.utils.property
|
||||||
|
import org.jetbrains.kotlin.gradle.utils.propertyWithNewInstance
|
||||||
|
import org.jetbrains.kotlin.incremental.ClasspathChanges
|
||||||
|
import org.jetbrains.kotlin.incremental.ClasspathChanges.*
|
||||||
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.*
|
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.*
|
||||||
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.IncrementalRun.*
|
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.IncrementalRun.NoChanges
|
||||||
import org.jetbrains.kotlin.library.impl.isKotlinLibrary
|
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.IncrementalRun.ToBeComputedByIncrementalCompiler
|
||||||
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
|
import org.jetbrains.kotlin.incremental.ClasspathSnapshotFiles
|
||||||
import org.jetbrains.kotlin.utils.JsLibraryUtils
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Files.*
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import org.jetbrains.kotlin.gradle.tasks.cleanOutputsAndLocalState as cleanOutputsAndLocalStateUtil
|
import kotlin.text.appendLine
|
||||||
|
|
||||||
const val KOTLIN_BUILD_DIR_NAME = "kotlin"
|
|
||||||
const val USING_JVM_INCREMENTAL_COMPILATION_MESSAGE = "Using Kotlin/JVM incremental compilation"
|
|
||||||
const val USING_JS_INCREMENTAL_COMPILATION_MESSAGE = "Using Kotlin/JS incremental compilation"
|
|
||||||
const val USING_JS_IR_BACKEND_MESSAGE = "Using Kotlin/JS IR backend"
|
|
||||||
|
|
||||||
abstract class AbstractKotlinCompileTool<T : CommonToolArguments> @Inject constructor(
|
|
||||||
objectFactory: ObjectFactory
|
|
||||||
) : DefaultTask(),
|
|
||||||
KotlinCompileTool,
|
|
||||||
CompilerArgumentAware<T>,
|
|
||||||
TaskWithLocalState {
|
|
||||||
|
|
||||||
private val patternFilterable = PatternSet()
|
|
||||||
|
|
||||||
init {
|
|
||||||
patternFilterable.include(
|
|
||||||
DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS.flatMap { ext -> ext.fileExtensionCasePermutations().map { "**/*.$it" } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val sourceFiles = objectFactory.fileCollection()
|
|
||||||
|
|
||||||
override val sources: FileCollection = objectFactory.fileCollection()
|
|
||||||
.from(
|
|
||||||
{ sourceFiles.asFileTree.matching(patternFilterable) }
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun source(vararg sources: Any) {
|
|
||||||
sourceFiles.from(sources)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setSource(vararg sources: Any) {
|
|
||||||
sourceFiles.from(sources)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun disallowSourceChanges() {
|
|
||||||
sourceFiles.disallowChanges()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
final override fun getIncludes(): MutableSet<String> = patternFilterable.includes
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
final override fun getExcludes(): MutableSet<String> = patternFilterable.excludes
|
|
||||||
|
|
||||||
final override fun setIncludes(includes: Iterable<String>): PatternFilterable = also {
|
|
||||||
patternFilterable.setIncludes(includes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun setExcludes(excludes: Iterable<String>): PatternFilterable = also {
|
|
||||||
patternFilterable.setExcludes(excludes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun include(vararg includes: String?): PatternFilterable = also {
|
|
||||||
patternFilterable.include(*includes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun include(includes: Iterable<String>): PatternFilterable = also {
|
|
||||||
patternFilterable.include(includes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun include(includeSpec: Spec<FileTreeElement>): PatternFilterable = also {
|
|
||||||
patternFilterable.include(includeSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun include(includeSpec: Closure<*>): PatternFilterable = also {
|
|
||||||
patternFilterable.include(includeSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun exclude(vararg excludes: String?): PatternFilterable = also {
|
|
||||||
patternFilterable.exclude(*excludes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun exclude(excludes: Iterable<String>): PatternFilterable = also {
|
|
||||||
patternFilterable.exclude(excludes)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun exclude(excludeSpec: Spec<FileTreeElement>): PatternFilterable = also {
|
|
||||||
patternFilterable.exclude(excludeSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun exclude(excludeSpec: Closure<*>): PatternFilterable = also {
|
|
||||||
patternFilterable.exclude(excludeSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
final override val metrics: Property<BuildMetricsReporter> = project.objects
|
|
||||||
.property(BuildMetricsReporterImpl())
|
|
||||||
|
|
||||||
/**
|
|
||||||
* By default, should be set by plugin from [COMPILER_CLASSPATH_CONFIGURATION_NAME] configuration.
|
|
||||||
*
|
|
||||||
* Empty classpath will fail the build.
|
|
||||||
*/
|
|
||||||
@get:Classpath
|
|
||||||
internal val defaultCompilerClasspath: ConfigurableFileCollection =
|
|
||||||
project.objects.fileCollection()
|
|
||||||
|
|
||||||
protected fun validateCompilerClasspath() {
|
|
||||||
// Note that the check triggers configuration resolution
|
|
||||||
require(!defaultCompilerClasspath.isEmpty) {
|
|
||||||
"Default Kotlin compiler classpath is empty! Task: $path (${this::class.qualifiedName})"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class GradleCompileTaskProvider @Inject constructor(
|
|
||||||
objectFactory: ObjectFactory,
|
|
||||||
projectLayout: ProjectLayout,
|
|
||||||
gradle: Gradle,
|
|
||||||
task: Task,
|
|
||||||
project: Project,
|
|
||||||
incrementalModuleInfoProvider: Provider<IncrementalModuleInfoProvider>
|
|
||||||
) {
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val path: Provider<String> = objectFactory.property(task.path)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val logger: Provider<Logger> = objectFactory.property(task.logger)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val buildDir: DirectoryProperty = projectLayout.buildDirectory
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val projectDir: Provider<File> = objectFactory
|
|
||||||
.property(project.rootProject.projectDir)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val projectCacheDir: Provider<File> = objectFactory
|
|
||||||
.property(gradle.projectCacheDir)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val sessionsDir: Provider<File> = objectFactory
|
|
||||||
.property(GradleCompilerRunner.sessionsDir(gradle.projectCacheDir))
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val projectName: Provider<String> = objectFactory
|
|
||||||
.property(project.rootProject.name.normalizeForFlagFile())
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val buildModulesInfo: Provider<out IncrementalModuleInfoProvider> = objectFactory
|
|
||||||
.property(incrementalModuleInfoProvider)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val errorsFile: Provider<File?> = objectFactory
|
|
||||||
.property(
|
|
||||||
gradle.rootProject.rootDir.resolve(".gradle/kotlin/errors/").also { it.mkdirs() }
|
|
||||||
.resolve("errors-${System.currentTimeMillis()}.log"))
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constructor(
|
|
||||||
objectFactory: ObjectFactory,
|
|
||||||
workerExecutor: WorkerExecutor
|
|
||||||
) : AbstractKotlinCompileTool<T>(objectFactory),
|
|
||||||
CompileUsingKotlinDaemonWithNormalization,
|
|
||||||
UsesBuildMetricsService,
|
|
||||||
UsesBuildReportsService,
|
|
||||||
UsesIncrementalModuleInfoBuildService,
|
|
||||||
UsesCompilerSystemPropertiesService,
|
|
||||||
UsesVariantImplementationFactories,
|
|
||||||
BaseKotlinCompile {
|
|
||||||
|
|
||||||
init {
|
|
||||||
cacheOnlyIfEnabledForKotlin()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val layout = project.layout
|
|
||||||
|
|
||||||
@get:Inject
|
|
||||||
internal abstract val fileSystemOperations: FileSystemOperations
|
|
||||||
|
|
||||||
// avoid creating directory in getter: this can lead to failure in parallel build
|
|
||||||
@get:OutputDirectory
|
|
||||||
internal open val taskBuildCacheableOutputDirectory: DirectoryProperty = objectFactory.directoryProperty()
|
|
||||||
|
|
||||||
// avoid creating directory in getter: this can lead to failure in parallel build
|
|
||||||
@get:LocalState
|
|
||||||
internal abstract val taskBuildLocalStateDirectory: DirectoryProperty
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val buildHistoryFile
|
|
||||||
get() = taskBuildLocalStateDirectory.file("build-history.bin")
|
|
||||||
|
|
||||||
// indicates that task should compile kotlin incrementally if possible
|
|
||||||
// it's not possible when IncrementalTaskInputs#isIncremental returns false (i.e first build)
|
|
||||||
// todo: deprecate and remove (we may need to design api for configuring IC)
|
|
||||||
// don't rely on it to check if IC is enabled, use isIncrementalCompilationEnabled instead
|
|
||||||
@get:Internal
|
|
||||||
var incremental: Boolean = false
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
logger.kotlinDebug { "Set $this.incremental=$value" }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Input
|
|
||||||
internal open fun isIncrementalCompilationEnabled(): Boolean =
|
|
||||||
incremental
|
|
||||||
|
|
||||||
@Deprecated("Scheduled for removal with Kotlin 1.9", ReplaceWith("moduleName"))
|
|
||||||
@get:Input
|
|
||||||
abstract val ownModuleName: Property<String>
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val startParameters = BuildReportsService.getStartParameters(project)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal abstract val suppressKotlinOptionsFreeArgsModificationWarning: Property<Boolean>
|
|
||||||
|
|
||||||
internal fun reportingSettings() = buildReportsService.orNull?.parameters?.reportingSettings?.orNull ?: ReportingSettings()
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
protected val multiModuleICSettings: MultiModuleICSettings
|
|
||||||
get() = MultiModuleICSettings(buildHistoryFile.get().asFile, useModuleDetection.get())
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plugin Data provided by [KpmCompilerPlugin]
|
|
||||||
*/
|
|
||||||
@get:Optional
|
|
||||||
@get:Nested
|
|
||||||
// TODO: replace with objects.property and introduce task configurator
|
|
||||||
internal var kotlinPluginData: Provider<KotlinCompilerPluginData>? = null
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val javaOutputDir: DirectoryProperty = objectFactory.directoryProperty()
|
|
||||||
|
|
||||||
@get:InputFiles
|
|
||||||
@get:IgnoreEmptyDirectories
|
|
||||||
@get:Incremental
|
|
||||||
@get:NormalizeLineEndings
|
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
internal val commonSourceSet: ConfigurableFileCollection = objectFactory.fileCollection()
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val abiSnapshotFile
|
|
||||||
get() = taskBuildCacheableOutputDirectory.file(IncrementalCompilerRunner.ABI_SNAPSHOT_FILE_NAME)
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
val abiSnapshotRelativePath: Property<String> = objectFactory.property(String::class.java).value(
|
|
||||||
//TODO update to support any jar changes
|
|
||||||
"$name/${IncrementalCompilerRunner.ABI_SNAPSHOT_FILE_NAME}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val friendSourceSets = objectFactory.listProperty(String::class.java)
|
|
||||||
|
|
||||||
private val kotlinLogger by lazy { GradleKotlinLogger(logger) }
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
protected val gradleCompileTaskProvider: Provider<GradleCompileTaskProvider> = objectFactory
|
|
||||||
.property(
|
|
||||||
objectFactory.newInstance<GradleCompileTaskProvider>(project.gradle, this, project, incrementalModuleInfoProvider)
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal open val defaultKotlinJavaToolchain: Provider<DefaultKotlinJavaToolchain> = objectFactory
|
|
||||||
.propertyWithNewInstance({ null })
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val compilerRunner: Provider<GradleCompilerRunner> =
|
|
||||||
objectFactory.propertyWithConvention(
|
|
||||||
gradleCompileTaskProvider.flatMap { taskProvider ->
|
|
||||||
compilerExecutionStrategy
|
|
||||||
.zip(metrics) { executionStrategy, metrics ->
|
|
||||||
metrics to executionStrategy
|
|
||||||
}
|
|
||||||
.flatMap { params ->
|
|
||||||
defaultKotlinJavaToolchain
|
|
||||||
.map {
|
|
||||||
val toolsJar = it.currentJvmJdkToolsJar.orNull
|
|
||||||
GradleCompilerRunnerWithWorkers(
|
|
||||||
taskProvider,
|
|
||||||
toolsJar,
|
|
||||||
CompilerExecutionSettings(
|
|
||||||
normalizedKotlinDaemonJvmArguments.orNull,
|
|
||||||
params.second,
|
|
||||||
useDaemonFallbackStrategy.get()
|
|
||||||
),
|
|
||||||
params.first,
|
|
||||||
workerExecutor
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal abstract val preciseCompilationResultsBackup: Property<Boolean>
|
|
||||||
|
|
||||||
/** Task outputs that we don't want to include in [TaskOutputsBackup] (see [TaskOutputsBackup.outputsToRestore] for more info). */
|
|
||||||
@get:Internal
|
|
||||||
internal abstract val taskOutputsBackupExcludes: SetProperty<File>
|
|
||||||
|
|
||||||
@TaskAction
|
|
||||||
fun execute(inputChanges: InputChanges) {
|
|
||||||
val buildMetrics = metrics.get()
|
|
||||||
buildMetrics.addTimeMetric(BuildPerformanceMetric.START_TASK_ACTION_EXECUTION)
|
|
||||||
buildMetrics.measure(BuildTime.OUT_OF_WORKER_TASK_ACTION) {
|
|
||||||
KotlinBuildStatsService.applyIfInitialised {
|
|
||||||
if (name.contains("Test"))
|
|
||||||
it.report(BooleanMetrics.TESTS_EXECUTED, true)
|
|
||||||
else
|
|
||||||
it.report(BooleanMetrics.COMPILATION_STARTED, true)
|
|
||||||
}
|
|
||||||
validateCompilerClasspath()
|
|
||||||
systemPropertiesService.get().startIntercept()
|
|
||||||
CompilerSystemProperties.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY.value = "true"
|
|
||||||
|
|
||||||
// If task throws exception, but its outputs are changed during execution,
|
|
||||||
// then Gradle forces next build to be non-incremental (see Gradle's DefaultTaskArtifactStateRepository#persistNewOutputs)
|
|
||||||
// To prevent this, we backup outputs before incremental build and restore when exception is thrown
|
|
||||||
val outputsBackup: TaskOutputsBackup? =
|
|
||||||
if (isIncrementalCompilationEnabled() && inputChanges.isIncremental)
|
|
||||||
buildMetrics.measure(BuildTime.BACKUP_OUTPUT) {
|
|
||||||
TaskOutputsBackup(
|
|
||||||
fileSystemOperations,
|
|
||||||
layout.buildDirectory,
|
|
||||||
layout.buildDirectory.dir("snapshot/kotlin/$name"),
|
|
||||||
outputsToRestore = allOutputFiles() - taskOutputsBackupExcludes.get(),
|
|
||||||
logger
|
|
||||||
).also {
|
|
||||||
it.createSnapshot()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else null
|
|
||||||
|
|
||||||
if (!isIncrementalCompilationEnabled()) {
|
|
||||||
cleanOutputsAndLocalState("IC is disabled")
|
|
||||||
} else if (!inputChanges.isIncremental) {
|
|
||||||
cleanOutputsAndLocalState("Task cannot run incrementally")
|
|
||||||
}
|
|
||||||
|
|
||||||
executeImpl(inputChanges, outputsBackup)
|
|
||||||
}
|
|
||||||
|
|
||||||
buildMetricsService.orNull?.also { it.addTask(path, this.javaClass, buildMetrics) }
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun cleanOutputsAndLocalState(reason: String?) {
|
|
||||||
cleanOutputsAndLocalStateUtil(reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun skipCondition(): Boolean = sources.isEmpty
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
protected open val incrementalProps: List<FileCollection>
|
|
||||||
get() = listOfNotNull(
|
|
||||||
sources,
|
|
||||||
libraries,
|
|
||||||
commonSourceSet
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun executeImpl(
|
|
||||||
inputChanges: InputChanges,
|
|
||||||
taskOutputsBackup: TaskOutputsBackup?
|
|
||||||
) {
|
|
||||||
val allKotlinSources = sources.asFileTree.files
|
|
||||||
|
|
||||||
logger.kotlinDebug { "All kotlin sources: ${allKotlinSources.pathsAsStringRelativeTo(gradleCompileTaskProvider.get().projectDir.get())}" }
|
|
||||||
|
|
||||||
if (!inputChanges.isIncremental && skipCondition()) {
|
|
||||||
// Skip running only if non-incremental run. Otherwise, we may need to do some cleanup.
|
|
||||||
logger.kotlinDebug { "No Kotlin files found, skipping Kotlin compiler task" }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val args = prepareCompilerArguments()
|
|
||||||
taskBuildCacheableOutputDirectory.get().asFile.mkdirs()
|
|
||||||
taskBuildLocalStateDirectory.get().asFile.mkdirs()
|
|
||||||
callCompilerAsync(
|
|
||||||
args,
|
|
||||||
allKotlinSources,
|
|
||||||
inputChanges,
|
|
||||||
taskOutputsBackup
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun getChangedFiles(
|
|
||||||
inputChanges: InputChanges,
|
|
||||||
incrementalProps: List<FileCollection>
|
|
||||||
) = if (!inputChanges.isIncremental) {
|
|
||||||
ChangedFiles.Unknown()
|
|
||||||
} else {
|
|
||||||
incrementalProps
|
|
||||||
.fold(mutableListOf<File>() to mutableListOf<File>()) { (modified, removed), prop ->
|
|
||||||
inputChanges.getFileChanges(prop).forEach {
|
|
||||||
when (it.changeType) {
|
|
||||||
ChangeType.ADDED, ChangeType.MODIFIED -> modified.add(it.file)
|
|
||||||
ChangeType.REMOVED -> removed.add(it.file)
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modified to removed
|
|
||||||
}
|
|
||||||
.run {
|
|
||||||
ChangedFiles.Known(first, second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compiler might be executed asynchronously. Do not do anything requiring end of compilation after this function is called.
|
|
||||||
* @see [GradleKotlinCompilerWork]
|
|
||||||
*/
|
|
||||||
internal abstract fun callCompilerAsync(
|
|
||||||
args: T,
|
|
||||||
kotlinSources: Set<File>,
|
|
||||||
inputChanges: InputChanges,
|
|
||||||
taskOutputsBackup: TaskOutputsBackup?
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val abstractKotlinCompileArgumentsContributor by lazy {
|
|
||||||
AbstractKotlinCompileArgumentsContributor(
|
|
||||||
KotlinCompileArgumentsProvider(this)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setupCompilerArgs(args: T, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
|
|
||||||
abstractKotlinCompileArgumentsContributor.contributeArguments(
|
|
||||||
args,
|
|
||||||
compilerArgumentsConfigurationFlags(defaultsOnly, ignoreClasspathResolutionErrors)
|
|
||||||
)
|
|
||||||
if (reportingSettings().buildReportMode == BuildReportMode.VERBOSE) {
|
|
||||||
args.reportPerf = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open class KotlinCompileArgumentsProvider<T : AbstractKotlinCompile<out CommonCompilerArguments>>(taskProvider: T) {
|
|
||||||
|
|
||||||
val logger: Logger = taskProvider.logger
|
|
||||||
val isMultiplatform: Boolean = taskProvider.multiPlatformEnabled.get()
|
|
||||||
private val pluginData = taskProvider.kotlinPluginData?.orNull
|
|
||||||
val pluginClasspath: FileCollection = listOfNotNull(taskProvider.pluginClasspath, pluginData?.classpath).reduce(FileCollection::plus)
|
|
||||||
val pluginOptions: CompilerPluginOptions = taskProvider.pluginOptions.toSingleCompilerPluginOptions() + pluginData?.options
|
|
||||||
}
|
|
||||||
|
|
||||||
class KotlinJvmCompilerArgumentsProvider
|
|
||||||
(taskProvider: KotlinCompile) : KotlinCompileArgumentsProvider<KotlinCompile>(taskProvider) {
|
|
||||||
val taskName: String = taskProvider.name
|
|
||||||
val moduleName: String = taskProvider.moduleName.get()
|
|
||||||
val friendPaths: FileCollection = taskProvider.friendPaths
|
|
||||||
val compileClasspath: Iterable<File> = taskProvider.libraries
|
|
||||||
val destinationDir: File = taskProvider.destinationDirectory.get().asFile
|
|
||||||
internal val compilerOptions: KotlinJvmCompilerOptions = taskProvider.compilerOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
internal inline val <reified T : Task> T.thisTaskProvider: TaskProvider<out T>
|
|
||||||
get() = checkNotNull(project.locateTask<T>(name))
|
|
||||||
|
|
||||||
@CacheableTask
|
@CacheableTask
|
||||||
abstract class KotlinCompile @Inject constructor(
|
abstract class KotlinCompile @Inject constructor(
|
||||||
@@ -912,318 +450,4 @@ abstract class KotlinCompile @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@CacheableTask
|
|
||||||
abstract class Kotlin2JsCompile @Inject constructor(
|
|
||||||
override val compilerOptions: KotlinJsCompilerOptions,
|
|
||||||
objectFactory: ObjectFactory,
|
|
||||||
workerExecutor: WorkerExecutor
|
|
||||||
) : AbstractKotlinCompile<K2JSCompilerArguments>(objectFactory, workerExecutor),
|
|
||||||
KotlinCompilationTask<KotlinJsCompilerOptions>,
|
|
||||||
UsesLibraryFilterCachingService,
|
|
||||||
KotlinJsCompile,
|
|
||||||
K2MultiplatformCompilationTask {
|
|
||||||
|
|
||||||
init {
|
|
||||||
incremental = true
|
|
||||||
compilerOptions.verbose.convention(logger.isDebugEnabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val kotlinOptions: KotlinJsOptions = KotlinJsOptionsCompat(
|
|
||||||
{ this },
|
|
||||||
compilerOptions
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
internal var incrementalJsKlib: Boolean = true
|
|
||||||
|
|
||||||
override fun isIncrementalCompilationEnabled(): Boolean {
|
|
||||||
val freeArgs = enhancedFreeCompilerArgs.get()
|
|
||||||
return when {
|
|
||||||
PRODUCE_JS in freeArgs -> false
|
|
||||||
|
|
||||||
PRODUCE_UNZIPPED_KLIB in freeArgs -> {
|
|
||||||
KotlinBuildStatsService.applyIfInitialised {
|
|
||||||
it.report(BooleanMetrics.JS_KLIB_INCREMENTAL, incrementalJsKlib)
|
|
||||||
}
|
|
||||||
incrementalJsKlib
|
|
||||||
}
|
|
||||||
|
|
||||||
PRODUCE_ZIPPED_KLIB in freeArgs -> {
|
|
||||||
KotlinBuildStatsService.applyIfInitialised {
|
|
||||||
it.report(BooleanMetrics.JS_KLIB_INCREMENTAL, incrementalJsKlib)
|
|
||||||
}
|
|
||||||
incrementalJsKlib
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> incremental
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Workaround to be able to use default value and change it later based on external input
|
|
||||||
@get:Internal
|
|
||||||
internal abstract val defaultDestinationDirectory: DirectoryProperty
|
|
||||||
|
|
||||||
@Deprecated("Use destinationDirectory and moduleName instead")
|
|
||||||
@get:Internal
|
|
||||||
abstract val outputFileProperty: Property<File>
|
|
||||||
|
|
||||||
// Workaround to add additional compiler args based on the exising one
|
|
||||||
// Currently there is a logic to add additional compiler arguments based on already existing one.
|
|
||||||
// And it is not possible to update compilerOptions.freeCompilerArgs using some kind of .map
|
|
||||||
// or .flatMap call - this will cause StackOverlowException as upstream source will be updated
|
|
||||||
// and .map will be called again.
|
|
||||||
@get:Input
|
|
||||||
internal abstract val enhancedFreeCompilerArgs: ListProperty<String>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Workaround for those "nasty" plugins that are adding 'freeCompilerArgs' on task execution phase.
|
|
||||||
* With properties api it is not possible to update property value after task configuration is finished.
|
|
||||||
*
|
|
||||||
* Marking it as `@Internal` as anyway on the configuration phase, when Gradle does task inputs snapshot,
|
|
||||||
* this input will always be empty.
|
|
||||||
*/
|
|
||||||
@get:Internal
|
|
||||||
internal var executionTimeFreeCompilerArgs: List<String>? = null
|
|
||||||
|
|
||||||
@get:Nested
|
|
||||||
override val multiplatformStructure: K2MultiplatformStructure = objectFactory.newInstance()
|
|
||||||
|
|
||||||
override fun createCompilerArgs(): K2JSCompilerArguments =
|
|
||||||
K2JSCompilerArguments()
|
|
||||||
|
|
||||||
override fun setupCompilerArgs(args: K2JSCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) {
|
|
||||||
(compilerOptions as KotlinJsCompilerOptionsDefault).fillDefaultValues(args)
|
|
||||||
super.setupCompilerArgs(args, defaultsOnly = defaultsOnly, ignoreClasspathResolutionErrors = ignoreClasspathResolutionErrors)
|
|
||||||
|
|
||||||
if (defaultsOnly) return
|
|
||||||
|
|
||||||
(compilerOptions as KotlinJsCompilerOptionsDefault).fillCompilerArguments(args)
|
|
||||||
if (!args.sourceMapPrefix.isNullOrEmpty()) {
|
|
||||||
args.sourceMapBaseDirs = sourceMapBaseDir.get().asFile.absolutePath
|
|
||||||
}
|
|
||||||
if (isIrBackendEnabled()) {
|
|
||||||
val outputFilePath: String? = compilerOptions.outputFile.orNull
|
|
||||||
if (outputFilePath != null) {
|
|
||||||
val outputFile = File(outputFilePath)
|
|
||||||
args.outputDir = (if (outputFile.extension == "") outputFile else outputFile.parentFile).normalize().absolutePath
|
|
||||||
args.moduleName = outputFile.nameWithoutExtension
|
|
||||||
} else {
|
|
||||||
args.outputDir = destinationDirectory.get().asFile.normalize().absolutePath
|
|
||||||
args.moduleName = compilerOptions.moduleName.get()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
args.outputFile = outputFileProperty.get().absoluteFile.normalize().absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
args.configureMultiplatform(
|
|
||||||
compilerOptions,
|
|
||||||
k1CommonSources = commonSourceSet.asFileTree,
|
|
||||||
k2MultiplatformFragments = multiplatformStructure
|
|
||||||
)
|
|
||||||
|
|
||||||
// Overriding freeArgs from compilerOptions with enhanced one + additional one set on execution phase
|
|
||||||
// containing additional arguments based on the js compilation configuration
|
|
||||||
val localExecutionTimeFreeCompilerArgs = executionTimeFreeCompilerArgs
|
|
||||||
args.freeArgs = if (localExecutionTimeFreeCompilerArgs != null) localExecutionTimeFreeCompilerArgs else enhancedFreeCompilerArgs.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:InputFiles
|
|
||||||
@get:IgnoreEmptyDirectories
|
|
||||||
@get:Incremental
|
|
||||||
@get:Optional
|
|
||||||
@get:NormalizeLineEndings
|
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
internal val friendDependencies: FileCollection = objectFactory
|
|
||||||
.fileCollection()
|
|
||||||
.from(friendPaths)
|
|
||||||
.filter {
|
|
||||||
// .jar files are not required for js compilation as friend modules
|
|
||||||
// and, because of `@InputFiles` and different normalization strategy from `@Classpath`,
|
|
||||||
// they produce build cache misses
|
|
||||||
it.exists() && !it.name.endsWith(".jar") && libraryFilter(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
internal val sourceMapBaseDir: Property<Directory> = objectFactory
|
|
||||||
.directoryProperty()
|
|
||||||
.value(project.layout.projectDirectory)
|
|
||||||
|
|
||||||
private fun isHybridKotlinJsLibrary(file: File): Boolean =
|
|
||||||
JsLibraryUtils.isKotlinJavascriptLibrary(file) && isKotlinLibrary(file)
|
|
||||||
|
|
||||||
private val preIrBackendCompilerFlags = listOf(
|
|
||||||
DISABLE_PRE_IR,
|
|
||||||
PRODUCE_JS,
|
|
||||||
PRODUCE_ZIPPED_KLIB
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun isPreIrBackendDisabled(): Boolean = enhancedFreeCompilerArgs
|
|
||||||
.get()
|
|
||||||
.any { preIrBackendCompilerFlags.contains(it) }
|
|
||||||
|
|
||||||
// see also isIncrementalCompilationEnabled
|
|
||||||
private val irBackendCompilerFlags = listOf(
|
|
||||||
PRODUCE_UNZIPPED_KLIB,
|
|
||||||
PRODUCE_JS,
|
|
||||||
PRODUCE_ZIPPED_KLIB
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun isIrBackendEnabled(): Boolean = enhancedFreeCompilerArgs
|
|
||||||
.get()
|
|
||||||
.any { irBackendCompilerFlags.contains(it) }
|
|
||||||
|
|
||||||
private val File.asLibraryFilterCacheKey: LibraryFilterCachingService.LibraryFilterCacheKey
|
|
||||||
get() = LibraryFilterCachingService.LibraryFilterCacheKey(
|
|
||||||
this,
|
|
||||||
irEnabled = isIrBackendEnabled(),
|
|
||||||
preIrDisabled = isPreIrBackendDisabled()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Kotlin/JS can operate in 3 modes:
|
|
||||||
// 1) purely pre-IR backend
|
|
||||||
// 2) purely IR backend
|
|
||||||
// 3) hybrid pre-IR and IR backend. Can only accept libraries with both JS and IR parts.
|
|
||||||
private val libraryFilterBody: (File) -> Boolean
|
|
||||||
get() = if (isIrBackendEnabled()) {
|
|
||||||
if (isPreIrBackendDisabled()) {
|
|
||||||
//::isKotlinLibrary
|
|
||||||
// Workaround for KT-47797
|
|
||||||
{ isKotlinLibrary(it) }
|
|
||||||
} else {
|
|
||||||
::isHybridKotlinJsLibrary
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
JsLibraryUtils::isKotlinJavascriptLibrary
|
|
||||||
}
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
internal val jsLegacyNoWarn: Provider<Boolean> = objectFactory.property(
|
|
||||||
PropertiesProvider(project).jsCompilerNoWarn
|
|
||||||
)
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
protected val libraryFilter: (File) -> Boolean
|
|
||||||
get() = { file ->
|
|
||||||
libraryFilterCacheService.get().getOrCompute(file.asLibraryFilterCacheKey, libraryFilterBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val incrementalProps: List<FileCollection>
|
|
||||||
get() = super.incrementalProps + listOf(friendDependencies)
|
|
||||||
|
|
||||||
open fun processArgs(
|
|
||||||
args: K2JSCompilerArguments
|
|
||||||
) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun callCompilerAsync(
|
|
||||||
args: K2JSCompilerArguments,
|
|
||||||
kotlinSources: Set<File>,
|
|
||||||
inputChanges: InputChanges,
|
|
||||||
taskOutputsBackup: TaskOutputsBackup?
|
|
||||||
) {
|
|
||||||
logger.debug("Calling compiler")
|
|
||||||
|
|
||||||
validateOutputDirectory()
|
|
||||||
|
|
||||||
if (isIrBackendEnabled()) {
|
|
||||||
logger.info(USING_JS_IR_BACKEND_MESSAGE)
|
|
||||||
}
|
|
||||||
|
|
||||||
val dependencies = libraries
|
|
||||||
.filter { it.exists() && libraryFilter(it) }
|
|
||||||
.map { it.normalize().absolutePath }
|
|
||||||
|
|
||||||
args.libraries = dependencies.distinct().let {
|
|
||||||
if (it.isNotEmpty())
|
|
||||||
it.joinToString(File.pathSeparator) else
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
args.friendModules = friendDependencies.files.joinToString(File.pathSeparator) { it.absolutePath }
|
|
||||||
if (!isIrBackendEnabled()) {
|
|
||||||
args.legacyDeprecatedNoWarn = true
|
|
||||||
args.useDeprecatedLegacyCompiler = true
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.kotlinDebug("compiling with args ${ArgumentUtils.convertArgumentsToStringList(args)}")
|
|
||||||
|
|
||||||
val gradlePrintingMessageCollector = GradlePrintingMessageCollector(logger, args.allWarningsAsErrors)
|
|
||||||
val gradleMessageCollector = GradleErrorMessageCollector(gradlePrintingMessageCollector, kotlinPluginVersion = getKotlinPluginVersion(logger))
|
|
||||||
val outputItemCollector = OutputItemsCollectorImpl()
|
|
||||||
val compilerRunner = compilerRunner.get()
|
|
||||||
|
|
||||||
val icEnv = if (isIncrementalCompilationEnabled()) {
|
|
||||||
logger.info(USING_JS_INCREMENTAL_COMPILATION_MESSAGE)
|
|
||||||
IncrementalCompilationEnvironment(
|
|
||||||
getChangedFiles(inputChanges, incrementalProps),
|
|
||||||
ClasspathChanges.NotAvailableForJSCompiler,
|
|
||||||
taskBuildCacheableOutputDirectory.get().asFile,
|
|
||||||
multiModuleICSettings = multiModuleICSettings,
|
|
||||||
preciseCompilationResultsBackup = preciseCompilationResultsBackup.get(),
|
|
||||||
)
|
|
||||||
} else null
|
|
||||||
|
|
||||||
val environment = GradleCompilerEnvironment(
|
|
||||||
defaultCompilerClasspath, gradleMessageCollector, outputItemCollector,
|
|
||||||
outputFiles = allOutputFiles(),
|
|
||||||
reportingSettings = reportingSettings(),
|
|
||||||
incrementalCompilationEnvironment = icEnv
|
|
||||||
)
|
|
||||||
processArgs(args)
|
|
||||||
compilerRunner.runJsCompilerAsync(
|
|
||||||
kotlinSources.toList(),
|
|
||||||
args,
|
|
||||||
environment,
|
|
||||||
taskOutputsBackup
|
|
||||||
)
|
|
||||||
compilerRunner.errorsFile?.also { gradleMessageCollector.flush(it) }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private val projectRootDir = project.rootDir
|
|
||||||
|
|
||||||
private fun validateOutputDirectory() {
|
|
||||||
val outputFile = outputFileProperty.get()
|
|
||||||
val outputDir = outputFile.parentFile
|
|
||||||
|
|
||||||
if (outputDir.isParentOf(projectRootDir)) {
|
|
||||||
throw InvalidUserDataException(
|
|
||||||
"The output directory '$outputDir' (defined by outputFile of ':$name') contains or " +
|
|
||||||
"matches the project root directory '${projectRootDir}'.\n" +
|
|
||||||
"Gradle will not be able to build the project because of the root directory lock.\n" +
|
|
||||||
"To fix this, consider using the default outputFile location instead of providing it explicitly."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class KotlinCompilerPluginData(
|
|
||||||
@get:Classpath
|
|
||||||
val classpath: FileCollection,
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
val options: CompilerPluginOptions,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used only for Up-to-date checks
|
|
||||||
*/
|
|
||||||
@get:Nested
|
|
||||||
val inputsOutputsState: InputsOutputsState
|
|
||||||
) {
|
|
||||||
data class InputsOutputsState(
|
|
||||||
@get:Input
|
|
||||||
val inputs: Map<String, String>,
|
|
||||||
|
|
||||||
@get:InputFiles
|
|
||||||
@get:IgnoreEmptyDirectories
|
|
||||||
@get:NormalizeLineEndings
|
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
val inputFiles: Set<File>,
|
|
||||||
|
|
||||||
@get:OutputFiles
|
|
||||||
val outputFiles: Set<File>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
+4
-1202
File diff suppressed because it is too large
Load Diff
+5
-1193
File diff suppressed because it is too large
Load Diff
+18
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* 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.tasks
|
||||||
|
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.tasks.TaskProvider
|
||||||
|
|
||||||
|
const val KOTLIN_BUILD_DIR_NAME = "kotlin"
|
||||||
|
const val USING_JVM_INCREMENTAL_COMPILATION_MESSAGE = "Using Kotlin/JVM incremental compilation"
|
||||||
|
const val USING_JS_INCREMENTAL_COMPILATION_MESSAGE = "Using Kotlin/JS incremental compilation"
|
||||||
|
const val USING_JS_IR_BACKEND_MESSAGE = "Using Kotlin/JS IR backend"
|
||||||
|
|
||||||
|
internal inline val <reified T : Task> T.thisTaskProvider: TaskProvider<out T>
|
||||||
|
get() = checkNotNull(project.locateTask<T>(name))
|
||||||
|
|
||||||
Reference in New Issue
Block a user