From d23a4f46ccac2e53eafc475036b8cb06e5af3430 Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Mon, 6 Mar 2023 14:56:43 +0100 Subject: [PATCH] [Gradle] Split Tasks.kt into several source files (per class) (2/2) ^KT-57142 Verification Pending --- .../gradle/tasks/AbstractKotlinCompile.kt | 950 +------------ .../gradle/tasks/AbstractKotlinCompileTool.kt | 1126 +-------------- .../gradle/tasks/GradleCompileTaskProvider.kt | 1183 +--------------- .../kotlin/gradle/tasks/Kotlin2JsCompile.kt | 939 +------------ .../kotlin/gradle/tasks/KotlinCompile.kt | 832 +----------- .../tasks/KotlinCompileArgumentsProvider.kt | 1206 +---------------- .../gradle/tasks/KotlinCompilerPluginData.kt | 1198 +--------------- .../jetbrains/kotlin/gradle/tasks/Tasks.kt | 18 + 8 files changed, 129 insertions(+), 7323 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/AbstractKotlinCompile.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/AbstractKotlinCompile.kt index 3685be8fdba..86d04295d07 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/AbstractKotlinCompile.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/AbstractKotlinCompile.kt @@ -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. */ package org.jetbrains.kotlin.gradle.tasks -import groovy.lang.Closure -import org.gradle.api.* -import org.gradle.api.file.* -import org.gradle.api.invocation.Gradle -import org.gradle.api.logging.Logger +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileCollection +import org.gradle.api.file.FileSystemOperations import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property 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.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.InputChanges import org.gradle.work.NormalizeLineEndings 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.build.report.metrics.BuildPerformanceMetric +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.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.compilerRunner.* -import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner.Companion.normalizeForFlagFile -import org.jetbrains.kotlin.config.JvmTarget +import org.jetbrains.kotlin.compilerRunner.CompilerExecutionSettings +import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner +import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers +import org.jetbrains.kotlin.compilerRunner.UsesCompilerSystemPropertiesService import org.jetbrains.kotlin.daemon.common.MultiModuleICSettings -import org.jetbrains.kotlin.gradle.dsl.* -import org.jetbrains.kotlin.gradle.incremental.* -import org.jetbrains.kotlin.gradle.internal.* -import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState +import org.jetbrains.kotlin.gradle.incremental.UsesIncrementalModuleInfoBuildService +import org.jetbrains.kotlin.gradle.internal.AbstractKotlinCompileArgumentsContributor +import org.jetbrains.kotlin.gradle.internal.compilerArgumentsConfigurationFlags +import org.jetbrains.kotlin.gradle.internal.prepareCompilerArguments 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.GradlePrintingMessageCollector import org.jetbrains.kotlin.gradle.logging.kotlinDebug -import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.gradle.plugin.mpp.* +import org.jetbrains.kotlin.gradle.plugin.UsesVariantImplementationFactories 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.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.report.UsesBuildMetricsService +import org.jetbrains.kotlin.gradle.report.UsesBuildReportsService import org.jetbrains.kotlin.gradle.utils.* -import org.jetbrains.kotlin.incremental.* -import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotDisabled -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.gradle.utils.newInstance +import org.jetbrains.kotlin.gradle.utils.property +import org.jetbrains.kotlin.gradle.utils.propertyWithConvention +import org.jetbrains.kotlin.gradle.utils.propertyWithNewInstance +import org.jetbrains.kotlin.incremental.ChangedFiles +import org.jetbrains.kotlin.incremental.IncrementalCompilerRunner 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.nio.file.Files.* 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 @Inject constructor( - objectFactory: ObjectFactory -) : DefaultTask(), - KotlinCompileTool, - CompilerArgumentAware, - 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 = patternFilterable.includes - - @Internal - final override fun getExcludes(): MutableSet = patternFilterable.excludes - - final override fun setIncludes(includes: Iterable): PatternFilterable = also { - patternFilterable.setIncludes(includes) - } - - final override fun setExcludes(excludes: Iterable): PatternFilterable = also { - patternFilterable.setExcludes(excludes) - } - - final override fun include(vararg includes: String?): PatternFilterable = also { - patternFilterable.include(*includes) - } - - final override fun include(includes: Iterable): PatternFilterable = also { - patternFilterable.include(includes) - } - - final override fun include(includeSpec: Spec): 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): PatternFilterable = also { - patternFilterable.exclude(excludes) - } - - final override fun exclude(excludeSpec: Spec): PatternFilterable = also { - patternFilterable.exclude(excludeSpec) - } - - final override fun exclude(excludeSpec: Closure<*>): PatternFilterable = also { - patternFilterable.exclude(excludeSpec) - } - - @get:Internal - final override val metrics: Property = 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 -) { - - @get:Internal - val path: Provider = objectFactory.property(task.path) - - @get:Internal - val logger: Provider = objectFactory.property(task.logger) - - @get:Internal - val buildDir: DirectoryProperty = projectLayout.buildDirectory - - @get:Internal - val projectDir: Provider = objectFactory - .property(project.rootProject.projectDir) - - @get:Internal - val projectCacheDir: Provider = objectFactory - .property(gradle.projectCacheDir) - - @get:Internal - val sessionsDir: Provider = objectFactory - .property(GradleCompilerRunner.sessionsDir(gradle.projectCacheDir)) - - @get:Internal - val projectName: Provider = objectFactory - .property(project.rootProject.name.normalizeForFlagFile()) - - @get:Internal - val buildModulesInfo: Provider = objectFactory - .property(incrementalModuleInfoProvider) - - @get:Internal - val errorsFile: Provider = objectFactory - .property( - gradle.rootProject.rootDir.resolve(".gradle/kotlin/errors/").also { it.mkdirs() } - .resolve("errors-${System.currentTimeMillis()}.log")) -} - abstract class AbstractKotlinCompile @Inject constructor( objectFactory: ObjectFactory, workerExecutor: WorkerExecutor @@ -496,734 +328,4 @@ abstract class AbstractKotlinCompile @Inject constr args.reportPerf = true } } -} - -open class KotlinCompileArgumentsProvider>(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(taskProvider) { - val taskName: String = taskProvider.name - val moduleName: String = taskProvider.moduleName.get() - val friendPaths: FileCollection = taskProvider.friendPaths - val compileClasspath: Iterable = taskProvider.libraries - val destinationDir: File = taskProvider.destinationDirectory.get().asFile - internal val compilerOptions: KotlinJvmCompilerOptions = taskProvider.compilerOptions -} - -internal inline val T.thisTaskProvider: TaskProvider - get() = checkNotNull(project.locateTask(name)) - -@CacheableTask -abstract class KotlinCompile @Inject constructor( - final override val compilerOptions: KotlinJvmCompilerOptions, - workerExecutor: WorkerExecutor, - objectFactory: ObjectFactory -) : AbstractKotlinCompile(objectFactory, workerExecutor), - K2MultiplatformCompilationTask, - @Suppress("TYPEALIAS_EXPANSION_DEPRECATION") KotlinJvmCompileDsl, - KotlinCompilationTask, - 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 = 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 - - @get:Nested - abstract val classpathSnapshotProperties: ClasspathSnapshotProperties - - /** Properties related to the `kotlin.incremental.useClasspathSnapshot` feature. */ - abstract class ClasspathSnapshotProperties { - @get:Input - abstract val useClasspathSnapshot: Property - - @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 - get() = listOf( - sources, - javaSources, - scriptSources, - androidLayoutResources, - commonSourceSet, - classpathSnapshotProperties.classpath, - classpathSnapshotProperties.classpathSnapshot - ) - - @get:Internal - final override val defaultKotlinJavaToolchain: Provider = objectFactory - .propertyWithNewInstance({ this }) - - final override val kotlinJavaToolchainProvider: Provider = defaultKotlinJavaToolchain.cast() - - @get:Internal - internal abstract val associatedJavaCompileTaskTargetCompatibility: Property - - @get:Internal - internal abstract val associatedJavaCompileTaskName: Property - - @get:Input - internal abstract val jvmTargetValidationMode: Property - - @get:Internal - internal val scriptDefinitions: ConfigurableFileCollection = objectFactory.fileCollection() - - @get:Input - @get:Optional - internal val scriptExtensions: SetProperty = objectFactory.setPropertyWithLazyValue { - scriptDefinitions - .map { definitionFile -> - definitionFile.readLines().filter(String::isNotBlank) - } - .flatten() - } - - private class ScriptFilterSpec( - private val scriptExtensions: SetProperty - ) : Spec { - 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? = 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 by lazy { - KotlinJvmCompilerArgumentsContributor(KotlinJvmCompilerArgumentsProvider(this)) - } - - override fun callCompilerAsync( - args: K2JVMCompilerArguments, - kotlinSources: Set, - 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(objectFactory, workerExecutor), - KotlinCompilationTask, - 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 - - // 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 - - /** - * 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? = 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 = 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 = objectFactory.property( - PropertiesProvider(project).jsCompilerNoWarn - ) - - @get:Internal - protected val libraryFilter: (File) -> Boolean - get() = { file -> - libraryFilterCacheService.get().getOrCompute(file.asLibraryFilterCacheKey, libraryFilterBody) - } - - override val incrementalProps: List - get() = super.incrementalProps + listOf(friendDependencies) - - open fun processArgs( - args: K2JSCompilerArguments - ) { - - } - - override fun callCompilerAsync( - args: K2JSCompilerArguments, - kotlinSources: Set, - 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, - - @get:InputFiles - @get:IgnoreEmptyDirectories - @get:NormalizeLineEndings - @get:PathSensitive(PathSensitivity.RELATIVE) - val inputFiles: Set, - - @get:OutputFiles - val outputFiles: Set - ) -} +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/AbstractKotlinCompileTool.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/AbstractKotlinCompileTool.kt index 3685be8fdba..95e328bb018 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/AbstractKotlinCompileTool.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/AbstractKotlinCompileTool.kt @@ -1,79 +1,31 @@ /* - * 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. */ package org.jetbrains.kotlin.gradle.tasks import groovy.lang.Closure -import org.gradle.api.* -import org.gradle.api.file.* -import org.gradle.api.invocation.Gradle -import org.gradle.api.logging.Logger +import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.FileCollection +import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property -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.compile.AbstractCompile -import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.Internal 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.InputChanges -import org.gradle.work.NormalizeLineEndings -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.build.report.metrics.BuildMetricsReporter +import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporterImpl 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.compilerRunner.* -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.gradle.dsl.* -import org.jetbrains.kotlin.gradle.incremental.* -import org.jetbrains.kotlin.gradle.internal.* +import org.jetbrains.kotlin.gradle.internal.CompilerArgumentAware import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState -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.GradlePrintingMessageCollector -import org.jetbrains.kotlin.gradle.logging.kotlinDebug -import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.gradle.plugin.mpp.* -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.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.utils.* -import org.jetbrains.kotlin.incremental.* -import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotDisabled -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.statistics.metrics.BooleanMetrics -import org.jetbrains.kotlin.utils.JsLibraryUtils -import org.jetbrains.kotlin.utils.addToStdlib.cast -import java.io.File -import java.nio.file.Files.* +import org.jetbrains.kotlin.gradle.utils.fileExtensionCasePermutations +import org.jetbrains.kotlin.gradle.utils.property 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 @Inject constructor( objectFactory: ObjectFactory @@ -174,1056 +126,4 @@ abstract class AbstractKotlinCompileTool @Inject constr "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 -) { - - @get:Internal - val path: Provider = objectFactory.property(task.path) - - @get:Internal - val logger: Provider = objectFactory.property(task.logger) - - @get:Internal - val buildDir: DirectoryProperty = projectLayout.buildDirectory - - @get:Internal - val projectDir: Provider = objectFactory - .property(project.rootProject.projectDir) - - @get:Internal - val projectCacheDir: Provider = objectFactory - .property(gradle.projectCacheDir) - - @get:Internal - val sessionsDir: Provider = objectFactory - .property(GradleCompilerRunner.sessionsDir(gradle.projectCacheDir)) - - @get:Internal - val projectName: Provider = objectFactory - .property(project.rootProject.name.normalizeForFlagFile()) - - @get:Internal - val buildModulesInfo: Provider = objectFactory - .property(incrementalModuleInfoProvider) - - @get:Internal - val errorsFile: Provider = objectFactory - .property( - gradle.rootProject.rootDir.resolve(".gradle/kotlin/errors/").also { it.mkdirs() } - .resolve("errors-${System.currentTimeMillis()}.log")) -} - -abstract class AbstractKotlinCompile @Inject constructor( - objectFactory: ObjectFactory, - workerExecutor: WorkerExecutor -) : AbstractKotlinCompileTool(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 - - @get:Internal - val startParameters = BuildReportsService.getStartParameters(project) - - @get:Internal - internal abstract val suppressKotlinOptionsFreeArgsModificationWarning: Property - - 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? = 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 = 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 = objectFactory - .property( - objectFactory.newInstance(project.gradle, this, project, incrementalModuleInfoProvider) - ) - - @get:Internal - internal open val defaultKotlinJavaToolchain: Provider = objectFactory - .propertyWithNewInstance({ null }) - - @get:Internal - internal val compilerRunner: Provider = - 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 - - /** Task outputs that we don't want to include in [TaskOutputsBackup] (see [TaskOutputsBackup.outputsToRestore] for more info). */ - @get:Internal - internal abstract val taskOutputsBackupExcludes: SetProperty - - @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 - 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 - ) = if (!inputChanges.isIncremental) { - ChangedFiles.Unknown() - } else { - incrementalProps - .fold(mutableListOf() to mutableListOf()) { (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, - 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>(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(taskProvider) { - val taskName: String = taskProvider.name - val moduleName: String = taskProvider.moduleName.get() - val friendPaths: FileCollection = taskProvider.friendPaths - val compileClasspath: Iterable = taskProvider.libraries - val destinationDir: File = taskProvider.destinationDirectory.get().asFile - internal val compilerOptions: KotlinJvmCompilerOptions = taskProvider.compilerOptions -} - -internal inline val T.thisTaskProvider: TaskProvider - get() = checkNotNull(project.locateTask(name)) - -@CacheableTask -abstract class KotlinCompile @Inject constructor( - final override val compilerOptions: KotlinJvmCompilerOptions, - workerExecutor: WorkerExecutor, - objectFactory: ObjectFactory -) : AbstractKotlinCompile(objectFactory, workerExecutor), - K2MultiplatformCompilationTask, - @Suppress("TYPEALIAS_EXPANSION_DEPRECATION") KotlinJvmCompileDsl, - KotlinCompilationTask, - 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 = 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 - - @get:Nested - abstract val classpathSnapshotProperties: ClasspathSnapshotProperties - - /** Properties related to the `kotlin.incremental.useClasspathSnapshot` feature. */ - abstract class ClasspathSnapshotProperties { - @get:Input - abstract val useClasspathSnapshot: Property - - @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 - get() = listOf( - sources, - javaSources, - scriptSources, - androidLayoutResources, - commonSourceSet, - classpathSnapshotProperties.classpath, - classpathSnapshotProperties.classpathSnapshot - ) - - @get:Internal - final override val defaultKotlinJavaToolchain: Provider = objectFactory - .propertyWithNewInstance({ this }) - - final override val kotlinJavaToolchainProvider: Provider = defaultKotlinJavaToolchain.cast() - - @get:Internal - internal abstract val associatedJavaCompileTaskTargetCompatibility: Property - - @get:Internal - internal abstract val associatedJavaCompileTaskName: Property - - @get:Input - internal abstract val jvmTargetValidationMode: Property - - @get:Internal - internal val scriptDefinitions: ConfigurableFileCollection = objectFactory.fileCollection() - - @get:Input - @get:Optional - internal val scriptExtensions: SetProperty = objectFactory.setPropertyWithLazyValue { - scriptDefinitions - .map { definitionFile -> - definitionFile.readLines().filter(String::isNotBlank) - } - .flatten() - } - - private class ScriptFilterSpec( - private val scriptExtensions: SetProperty - ) : Spec { - 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? = 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 by lazy { - KotlinJvmCompilerArgumentsContributor(KotlinJvmCompilerArgumentsProvider(this)) - } - - override fun callCompilerAsync( - args: K2JVMCompilerArguments, - kotlinSources: Set, - 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(objectFactory, workerExecutor), - KotlinCompilationTask, - 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 - - // 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 - - /** - * 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? = 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 = 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 = objectFactory.property( - PropertiesProvider(project).jsCompilerNoWarn - ) - - @get:Internal - protected val libraryFilter: (File) -> Boolean - get() = { file -> - libraryFilterCacheService.get().getOrCompute(file.asLibraryFilterCacheKey, libraryFilterBody) - } - - override val incrementalProps: List - get() = super.incrementalProps + listOf(friendDependencies) - - open fun processArgs( - args: K2JSCompilerArguments - ) { - - } - - override fun callCompilerAsync( - args: K2JSCompilerArguments, - kotlinSources: Set, - 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, - - @get:InputFiles - @get:IgnoreEmptyDirectories - @get:NormalizeLineEndings - @get:PathSensitive(PathSensitivity.RELATIVE) - val inputFiles: Set, - - @get:OutputFiles - val outputFiles: Set - ) -} +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/GradleCompileTaskProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/GradleCompileTaskProvider.kt index 3685be8fdba..e5f06e76914 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/GradleCompileTaskProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/GradleCompileTaskProvider.kt @@ -1,180 +1,26 @@ /* - * 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. */ package org.jetbrains.kotlin.gradle.tasks -import groovy.lang.Closure -import org.gradle.api.* -import org.gradle.api.file.* +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.ProjectLayout import org.gradle.api.invocation.Gradle import org.gradle.api.logging.Logger import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property 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.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.InputChanges -import org.gradle.work.NormalizeLineEndings -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.compilerRunner.* +import org.gradle.api.tasks.Internal +import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner 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.gradle.dsl.* -import org.jetbrains.kotlin.gradle.incremental.* -import org.jetbrains.kotlin.gradle.internal.* -import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState -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.GradlePrintingMessageCollector -import org.jetbrains.kotlin.gradle.logging.kotlinDebug -import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.gradle.plugin.mpp.* -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.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.utils.* -import org.jetbrains.kotlin.incremental.* -import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotDisabled -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.statistics.metrics.BooleanMetrics -import org.jetbrains.kotlin.utils.JsLibraryUtils -import org.jetbrains.kotlin.utils.addToStdlib.cast +import org.jetbrains.kotlin.gradle.incremental.IncrementalModuleInfoProvider +import org.jetbrains.kotlin.gradle.utils.projectCacheDir +import org.jetbrains.kotlin.gradle.utils.property import java.io.File -import java.nio.file.Files.* 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 @Inject constructor( - objectFactory: ObjectFactory -) : DefaultTask(), - KotlinCompileTool, - CompilerArgumentAware, - 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 = patternFilterable.includes - - @Internal - final override fun getExcludes(): MutableSet = patternFilterable.excludes - - final override fun setIncludes(includes: Iterable): PatternFilterable = also { - patternFilterable.setIncludes(includes) - } - - final override fun setExcludes(excludes: Iterable): PatternFilterable = also { - patternFilterable.setExcludes(excludes) - } - - final override fun include(vararg includes: String?): PatternFilterable = also { - patternFilterable.include(*includes) - } - - final override fun include(includes: Iterable): PatternFilterable = also { - patternFilterable.include(includes) - } - - final override fun include(includeSpec: Spec): 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): PatternFilterable = also { - patternFilterable.exclude(excludes) - } - - final override fun exclude(excludeSpec: Spec): PatternFilterable = also { - patternFilterable.exclude(excludeSpec) - } - - final override fun exclude(excludeSpec: Closure<*>): PatternFilterable = also { - patternFilterable.exclude(excludeSpec) - } - - @get:Internal - final override val metrics: Property = 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, @@ -219,1011 +65,4 @@ abstract class GradleCompileTaskProvider @Inject constructor( .property( gradle.rootProject.rootDir.resolve(".gradle/kotlin/errors/").also { it.mkdirs() } .resolve("errors-${System.currentTimeMillis()}.log")) -} - -abstract class AbstractKotlinCompile @Inject constructor( - objectFactory: ObjectFactory, - workerExecutor: WorkerExecutor -) : AbstractKotlinCompileTool(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 - - @get:Internal - val startParameters = BuildReportsService.getStartParameters(project) - - @get:Internal - internal abstract val suppressKotlinOptionsFreeArgsModificationWarning: Property - - 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? = 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 = 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 = objectFactory - .property( - objectFactory.newInstance(project.gradle, this, project, incrementalModuleInfoProvider) - ) - - @get:Internal - internal open val defaultKotlinJavaToolchain: Provider = objectFactory - .propertyWithNewInstance({ null }) - - @get:Internal - internal val compilerRunner: Provider = - 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 - - /** Task outputs that we don't want to include in [TaskOutputsBackup] (see [TaskOutputsBackup.outputsToRestore] for more info). */ - @get:Internal - internal abstract val taskOutputsBackupExcludes: SetProperty - - @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 - 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 - ) = if (!inputChanges.isIncremental) { - ChangedFiles.Unknown() - } else { - incrementalProps - .fold(mutableListOf() to mutableListOf()) { (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, - 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>(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(taskProvider) { - val taskName: String = taskProvider.name - val moduleName: String = taskProvider.moduleName.get() - val friendPaths: FileCollection = taskProvider.friendPaths - val compileClasspath: Iterable = taskProvider.libraries - val destinationDir: File = taskProvider.destinationDirectory.get().asFile - internal val compilerOptions: KotlinJvmCompilerOptions = taskProvider.compilerOptions -} - -internal inline val T.thisTaskProvider: TaskProvider - get() = checkNotNull(project.locateTask(name)) - -@CacheableTask -abstract class KotlinCompile @Inject constructor( - final override val compilerOptions: KotlinJvmCompilerOptions, - workerExecutor: WorkerExecutor, - objectFactory: ObjectFactory -) : AbstractKotlinCompile(objectFactory, workerExecutor), - K2MultiplatformCompilationTask, - @Suppress("TYPEALIAS_EXPANSION_DEPRECATION") KotlinJvmCompileDsl, - KotlinCompilationTask, - 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 = 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 - - @get:Nested - abstract val classpathSnapshotProperties: ClasspathSnapshotProperties - - /** Properties related to the `kotlin.incremental.useClasspathSnapshot` feature. */ - abstract class ClasspathSnapshotProperties { - @get:Input - abstract val useClasspathSnapshot: Property - - @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 - get() = listOf( - sources, - javaSources, - scriptSources, - androidLayoutResources, - commonSourceSet, - classpathSnapshotProperties.classpath, - classpathSnapshotProperties.classpathSnapshot - ) - - @get:Internal - final override val defaultKotlinJavaToolchain: Provider = objectFactory - .propertyWithNewInstance({ this }) - - final override val kotlinJavaToolchainProvider: Provider = defaultKotlinJavaToolchain.cast() - - @get:Internal - internal abstract val associatedJavaCompileTaskTargetCompatibility: Property - - @get:Internal - internal abstract val associatedJavaCompileTaskName: Property - - @get:Input - internal abstract val jvmTargetValidationMode: Property - - @get:Internal - internal val scriptDefinitions: ConfigurableFileCollection = objectFactory.fileCollection() - - @get:Input - @get:Optional - internal val scriptExtensions: SetProperty = objectFactory.setPropertyWithLazyValue { - scriptDefinitions - .map { definitionFile -> - definitionFile.readLines().filter(String::isNotBlank) - } - .flatten() - } - - private class ScriptFilterSpec( - private val scriptExtensions: SetProperty - ) : Spec { - 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? = 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 by lazy { - KotlinJvmCompilerArgumentsContributor(KotlinJvmCompilerArgumentsProvider(this)) - } - - override fun callCompilerAsync( - args: K2JVMCompilerArguments, - kotlinSources: Set, - 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(objectFactory, workerExecutor), - KotlinCompilationTask, - 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 - - // 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 - - /** - * 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? = 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 = 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 = objectFactory.property( - PropertiesProvider(project).jsCompilerNoWarn - ) - - @get:Internal - protected val libraryFilter: (File) -> Boolean - get() = { file -> - libraryFilterCacheService.get().getOrCompute(file.asLibraryFilterCacheKey, libraryFilterBody) - } - - override val incrementalProps: List - get() = super.incrementalProps + listOf(friendDependencies) - - open fun processArgs( - args: K2JSCompilerArguments - ) { - - } - - override fun callCompilerAsync( - args: K2JSCompilerArguments, - kotlinSources: Set, - 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, - - @get:InputFiles - @get:IgnoreEmptyDirectories - @get:NormalizeLineEndings - @get:PathSensitive(PathSensitivity.RELATIVE) - val inputFiles: Set, - - @get:OutputFiles - val outputFiles: Set - ) -} +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Kotlin2JsCompile.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Kotlin2JsCompile.kt index 3685be8fdba..54c9766f2ac 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Kotlin2JsCompile.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Kotlin2JsCompile.kt @@ -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. */ package org.jetbrains.kotlin.gradle.tasks -import groovy.lang.Closure -import org.gradle.api.* -import org.gradle.api.file.* -import org.gradle.api.invocation.Gradle -import org.gradle.api.logging.Logger +import org.gradle.api.InvalidUserDataException +import org.gradle.api.file.Directory +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileCollection import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property 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.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.InputChanges import org.gradle.work.NormalizeLineEndings 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.compilerRunner.* -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.gradle.dsl.* -import org.jetbrains.kotlin.gradle.incremental.* -import org.jetbrains.kotlin.gradle.internal.* -import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState +import org.jetbrains.kotlin.compilerRunner.ArgumentUtils +import org.jetbrains.kotlin.compilerRunner.GradleCompilerEnvironment +import org.jetbrains.kotlin.compilerRunner.IncrementalCompilationEnvironment +import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl +import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile +import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompilerOptions +import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompilerOptionsDefault +import org.jetbrains.kotlin.gradle.dsl.KotlinJsOptions 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.GradlePrintingMessageCollector import org.jetbrains.kotlin.gradle.logging.kotlinDebug -import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.gradle.plugin.mpp.* +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion 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.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.KotlinJvmOptionsCompat -import org.jetbrains.kotlin.gradle.utils.* -import org.jetbrains.kotlin.incremental.* -import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotDisabled -import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.* -import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.IncrementalRun.* +import org.jetbrains.kotlin.gradle.utils.isParentOf +import org.jetbrains.kotlin.gradle.utils.newInstance +import org.jetbrains.kotlin.gradle.utils.property +import org.jetbrains.kotlin.incremental.ClasspathChanges import org.jetbrains.kotlin.library.impl.isKotlinLibrary 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.nio.file.Files.* 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 @Inject constructor( - objectFactory: ObjectFactory -) : DefaultTask(), - KotlinCompileTool, - CompilerArgumentAware, - 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 = patternFilterable.includes - - @Internal - final override fun getExcludes(): MutableSet = patternFilterable.excludes - - final override fun setIncludes(includes: Iterable): PatternFilterable = also { - patternFilterable.setIncludes(includes) - } - - final override fun setExcludes(excludes: Iterable): PatternFilterable = also { - patternFilterable.setExcludes(excludes) - } - - final override fun include(vararg includes: String?): PatternFilterable = also { - patternFilterable.include(*includes) - } - - final override fun include(includes: Iterable): PatternFilterable = also { - patternFilterable.include(includes) - } - - final override fun include(includeSpec: Spec): 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): PatternFilterable = also { - patternFilterable.exclude(excludes) - } - - final override fun exclude(excludeSpec: Spec): PatternFilterable = also { - patternFilterable.exclude(excludeSpec) - } - - final override fun exclude(excludeSpec: Closure<*>): PatternFilterable = also { - patternFilterable.exclude(excludeSpec) - } - - @get:Internal - final override val metrics: Property = 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 -) { - - @get:Internal - val path: Provider = objectFactory.property(task.path) - - @get:Internal - val logger: Provider = objectFactory.property(task.logger) - - @get:Internal - val buildDir: DirectoryProperty = projectLayout.buildDirectory - - @get:Internal - val projectDir: Provider = objectFactory - .property(project.rootProject.projectDir) - - @get:Internal - val projectCacheDir: Provider = objectFactory - .property(gradle.projectCacheDir) - - @get:Internal - val sessionsDir: Provider = objectFactory - .property(GradleCompilerRunner.sessionsDir(gradle.projectCacheDir)) - - @get:Internal - val projectName: Provider = objectFactory - .property(project.rootProject.name.normalizeForFlagFile()) - - @get:Internal - val buildModulesInfo: Provider = objectFactory - .property(incrementalModuleInfoProvider) - - @get:Internal - val errorsFile: Provider = objectFactory - .property( - gradle.rootProject.rootDir.resolve(".gradle/kotlin/errors/").also { it.mkdirs() } - .resolve("errors-${System.currentTimeMillis()}.log")) -} - -abstract class AbstractKotlinCompile @Inject constructor( - objectFactory: ObjectFactory, - workerExecutor: WorkerExecutor -) : AbstractKotlinCompileTool(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 - - @get:Internal - val startParameters = BuildReportsService.getStartParameters(project) - - @get:Internal - internal abstract val suppressKotlinOptionsFreeArgsModificationWarning: Property - - 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? = 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 = 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 = objectFactory - .property( - objectFactory.newInstance(project.gradle, this, project, incrementalModuleInfoProvider) - ) - - @get:Internal - internal open val defaultKotlinJavaToolchain: Provider = objectFactory - .propertyWithNewInstance({ null }) - - @get:Internal - internal val compilerRunner: Provider = - 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 - - /** Task outputs that we don't want to include in [TaskOutputsBackup] (see [TaskOutputsBackup.outputsToRestore] for more info). */ - @get:Internal - internal abstract val taskOutputsBackupExcludes: SetProperty - - @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 - 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 - ) = if (!inputChanges.isIncremental) { - ChangedFiles.Unknown() - } else { - incrementalProps - .fold(mutableListOf() to mutableListOf()) { (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, - 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>(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(taskProvider) { - val taskName: String = taskProvider.name - val moduleName: String = taskProvider.moduleName.get() - val friendPaths: FileCollection = taskProvider.friendPaths - val compileClasspath: Iterable = taskProvider.libraries - val destinationDir: File = taskProvider.destinationDirectory.get().asFile - internal val compilerOptions: KotlinJvmCompilerOptions = taskProvider.compilerOptions -} - -internal inline val T.thisTaskProvider: TaskProvider - get() = checkNotNull(project.locateTask(name)) - -@CacheableTask -abstract class KotlinCompile @Inject constructor( - final override val compilerOptions: KotlinJvmCompilerOptions, - workerExecutor: WorkerExecutor, - objectFactory: ObjectFactory -) : AbstractKotlinCompile(objectFactory, workerExecutor), - K2MultiplatformCompilationTask, - @Suppress("TYPEALIAS_EXPANSION_DEPRECATION") KotlinJvmCompileDsl, - KotlinCompilationTask, - 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 = 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 - - @get:Nested - abstract val classpathSnapshotProperties: ClasspathSnapshotProperties - - /** Properties related to the `kotlin.incremental.useClasspathSnapshot` feature. */ - abstract class ClasspathSnapshotProperties { - @get:Input - abstract val useClasspathSnapshot: Property - - @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 - get() = listOf( - sources, - javaSources, - scriptSources, - androidLayoutResources, - commonSourceSet, - classpathSnapshotProperties.classpath, - classpathSnapshotProperties.classpathSnapshot - ) - - @get:Internal - final override val defaultKotlinJavaToolchain: Provider = objectFactory - .propertyWithNewInstance({ this }) - - final override val kotlinJavaToolchainProvider: Provider = defaultKotlinJavaToolchain.cast() - - @get:Internal - internal abstract val associatedJavaCompileTaskTargetCompatibility: Property - - @get:Internal - internal abstract val associatedJavaCompileTaskName: Property - - @get:Input - internal abstract val jvmTargetValidationMode: Property - - @get:Internal - internal val scriptDefinitions: ConfigurableFileCollection = objectFactory.fileCollection() - - @get:Input - @get:Optional - internal val scriptExtensions: SetProperty = objectFactory.setPropertyWithLazyValue { - scriptDefinitions - .map { definitionFile -> - definitionFile.readLines().filter(String::isNotBlank) - } - .flatten() - } - - private class ScriptFilterSpec( - private val scriptExtensions: SetProperty - ) : Spec { - 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? = 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 by lazy { - KotlinJvmCompilerArgumentsContributor(KotlinJvmCompilerArgumentsProvider(this)) - } - - override fun callCompilerAsync( - args: K2JVMCompilerArguments, - kotlinSources: Set, - 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( @@ -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, - - @get:InputFiles - @get:IgnoreEmptyDirectories - @get:NormalizeLineEndings - @get:PathSensitive(PathSensitivity.RELATIVE) - val inputFiles: Set, - - @get:OutputFiles - val outputFiles: Set - ) -} +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompile.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompile.kt index 3685be8fdba..1166eae1904 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompile.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompile.kt @@ -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. */ package org.jetbrains.kotlin.gradle.tasks -import groovy.lang.Closure -import org.gradle.api.* -import org.gradle.api.file.* -import org.gradle.api.invocation.Gradle -import org.gradle.api.logging.Logger +import org.gradle.api.GradleException +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileCollection +import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider 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.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.InputChanges import org.gradle.work.NormalizeLineEndings 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.compilerRunner.* -import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner.Companion.normalizeForFlagFile +import org.jetbrains.kotlin.compilerRunner.GradleCompilerEnvironment +import org.jetbrains.kotlin.compilerRunner.IncrementalCompilationEnvironment +import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl import org.jetbrains.kotlin.config.JvmTarget -import org.jetbrains.kotlin.daemon.common.MultiModuleICSettings -import org.jetbrains.kotlin.gradle.dsl.* -import org.jetbrains.kotlin.gradle.incremental.* -import org.jetbrains.kotlin.gradle.internal.* -import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions +import org.jetbrains.kotlin.gradle.internal.CompilerArgumentsContributor +import org.jetbrains.kotlin.gradle.internal.KotlinJvmCompilerArgumentsContributor +import org.jetbrains.kotlin.gradle.internal.compilerArgumentsConfigurationFlags 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.GradlePrintingMessageCollector import org.jetbrains.kotlin.gradle.logging.kotlinDebug -import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.gradle.plugin.mpp.* -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.UsesLibraryFilterCachingService -import org.jetbrains.kotlin.gradle.targets.js.ir.* -import org.jetbrains.kotlin.gradle.tasks.internal.KotlinJsOptionsCompat +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion +import org.jetbrains.kotlin.gradle.report.BuildReportMode import org.jetbrains.kotlin.gradle.tasks.internal.KotlinJvmOptionsCompat import org.jetbrains.kotlin.gradle.utils.* -import org.jetbrains.kotlin.incremental.* -import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotDisabled +import org.jetbrains.kotlin.gradle.utils.chainedDisallowChanges +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.IncrementalRun.* -import org.jetbrains.kotlin.library.impl.isKotlinLibrary -import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics -import org.jetbrains.kotlin.utils.JsLibraryUtils +import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.IncrementalRun.NoChanges +import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.IncrementalRun.ToBeComputedByIncrementalCompiler +import org.jetbrains.kotlin.incremental.ClasspathSnapshotFiles import org.jetbrains.kotlin.utils.addToStdlib.cast import java.io.File -import java.nio.file.Files.* 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 @Inject constructor( - objectFactory: ObjectFactory -) : DefaultTask(), - KotlinCompileTool, - CompilerArgumentAware, - 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 = patternFilterable.includes - - @Internal - final override fun getExcludes(): MutableSet = patternFilterable.excludes - - final override fun setIncludes(includes: Iterable): PatternFilterable = also { - patternFilterable.setIncludes(includes) - } - - final override fun setExcludes(excludes: Iterable): PatternFilterable = also { - patternFilterable.setExcludes(excludes) - } - - final override fun include(vararg includes: String?): PatternFilterable = also { - patternFilterable.include(*includes) - } - - final override fun include(includes: Iterable): PatternFilterable = also { - patternFilterable.include(includes) - } - - final override fun include(includeSpec: Spec): 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): PatternFilterable = also { - patternFilterable.exclude(excludes) - } - - final override fun exclude(excludeSpec: Spec): PatternFilterable = also { - patternFilterable.exclude(excludeSpec) - } - - final override fun exclude(excludeSpec: Closure<*>): PatternFilterable = also { - patternFilterable.exclude(excludeSpec) - } - - @get:Internal - final override val metrics: Property = 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 -) { - - @get:Internal - val path: Provider = objectFactory.property(task.path) - - @get:Internal - val logger: Provider = objectFactory.property(task.logger) - - @get:Internal - val buildDir: DirectoryProperty = projectLayout.buildDirectory - - @get:Internal - val projectDir: Provider = objectFactory - .property(project.rootProject.projectDir) - - @get:Internal - val projectCacheDir: Provider = objectFactory - .property(gradle.projectCacheDir) - - @get:Internal - val sessionsDir: Provider = objectFactory - .property(GradleCompilerRunner.sessionsDir(gradle.projectCacheDir)) - - @get:Internal - val projectName: Provider = objectFactory - .property(project.rootProject.name.normalizeForFlagFile()) - - @get:Internal - val buildModulesInfo: Provider = objectFactory - .property(incrementalModuleInfoProvider) - - @get:Internal - val errorsFile: Provider = objectFactory - .property( - gradle.rootProject.rootDir.resolve(".gradle/kotlin/errors/").also { it.mkdirs() } - .resolve("errors-${System.currentTimeMillis()}.log")) -} - -abstract class AbstractKotlinCompile @Inject constructor( - objectFactory: ObjectFactory, - workerExecutor: WorkerExecutor -) : AbstractKotlinCompileTool(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 - - @get:Internal - val startParameters = BuildReportsService.getStartParameters(project) - - @get:Internal - internal abstract val suppressKotlinOptionsFreeArgsModificationWarning: Property - - 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? = 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 = 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 = objectFactory - .property( - objectFactory.newInstance(project.gradle, this, project, incrementalModuleInfoProvider) - ) - - @get:Internal - internal open val defaultKotlinJavaToolchain: Provider = objectFactory - .propertyWithNewInstance({ null }) - - @get:Internal - internal val compilerRunner: Provider = - 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 - - /** Task outputs that we don't want to include in [TaskOutputsBackup] (see [TaskOutputsBackup.outputsToRestore] for more info). */ - @get:Internal - internal abstract val taskOutputsBackupExcludes: SetProperty - - @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 - 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 - ) = if (!inputChanges.isIncremental) { - ChangedFiles.Unknown() - } else { - incrementalProps - .fold(mutableListOf() to mutableListOf()) { (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, - 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>(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(taskProvider) { - val taskName: String = taskProvider.name - val moduleName: String = taskProvider.moduleName.get() - val friendPaths: FileCollection = taskProvider.friendPaths - val compileClasspath: Iterable = taskProvider.libraries - val destinationDir: File = taskProvider.destinationDirectory.get().asFile - internal val compilerOptions: KotlinJvmCompilerOptions = taskProvider.compilerOptions -} - -internal inline val T.thisTaskProvider: TaskProvider - get() = checkNotNull(project.locateTask(name)) +import kotlin.text.appendLine @CacheableTask 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(objectFactory, workerExecutor), - KotlinCompilationTask, - 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 - - // 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 - - /** - * 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? = 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 = 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 = objectFactory.property( - PropertiesProvider(project).jsCompilerNoWarn - ) - - @get:Internal - protected val libraryFilter: (File) -> Boolean - get() = { file -> - libraryFilterCacheService.get().getOrCompute(file.asLibraryFilterCacheKey, libraryFilterBody) - } - - override val incrementalProps: List - get() = super.incrementalProps + listOf(friendDependencies) - - open fun processArgs( - args: K2JSCompilerArguments - ) { - - } - - override fun callCompilerAsync( - args: K2JSCompilerArguments, - kotlinSources: Set, - 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, - - @get:InputFiles - @get:IgnoreEmptyDirectories - @get:NormalizeLineEndings - @get:PathSensitive(PathSensitivity.RELATIVE) - val inputFiles: Set, - - @get:OutputFiles - val outputFiles: Set - ) -} +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileArgumentsProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileArgumentsProvider.kt index 3685be8fdba..e8e9b312784 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileArgumentsProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileArgumentsProvider.kt @@ -1,505 +1,18 @@ /* - * 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. */ package org.jetbrains.kotlin.gradle.tasks -import groovy.lang.Closure -import org.gradle.api.* -import org.gradle.api.file.* -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.provider.ListProperty -import org.gradle.api.provider.Property -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.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.InputChanges -import org.gradle.work.NormalizeLineEndings -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.compilerRunner.* -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.gradle.dsl.* -import org.jetbrains.kotlin.gradle.incremental.* -import org.jetbrains.kotlin.gradle.internal.* -import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState -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.GradlePrintingMessageCollector -import org.jetbrains.kotlin.gradle.logging.kotlinDebug -import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.gradle.plugin.mpp.* -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.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.utils.* -import org.jetbrains.kotlin.incremental.* -import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotDisabled -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.statistics.metrics.BooleanMetrics -import org.jetbrains.kotlin.utils.JsLibraryUtils -import org.jetbrains.kotlin.utils.addToStdlib.cast +import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions import java.io.File -import java.nio.file.Files.* -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 @Inject constructor( - objectFactory: ObjectFactory -) : DefaultTask(), - KotlinCompileTool, - CompilerArgumentAware, - 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 = patternFilterable.includes - - @Internal - final override fun getExcludes(): MutableSet = patternFilterable.excludes - - final override fun setIncludes(includes: Iterable): PatternFilterable = also { - patternFilterable.setIncludes(includes) - } - - final override fun setExcludes(excludes: Iterable): PatternFilterable = also { - patternFilterable.setExcludes(excludes) - } - - final override fun include(vararg includes: String?): PatternFilterable = also { - patternFilterable.include(*includes) - } - - final override fun include(includes: Iterable): PatternFilterable = also { - patternFilterable.include(includes) - } - - final override fun include(includeSpec: Spec): 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): PatternFilterable = also { - patternFilterable.exclude(excludes) - } - - final override fun exclude(excludeSpec: Spec): PatternFilterable = also { - patternFilterable.exclude(excludeSpec) - } - - final override fun exclude(excludeSpec: Closure<*>): PatternFilterable = also { - patternFilterable.exclude(excludeSpec) - } - - @get:Internal - final override val metrics: Property = 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 -) { - - @get:Internal - val path: Provider = objectFactory.property(task.path) - - @get:Internal - val logger: Provider = objectFactory.property(task.logger) - - @get:Internal - val buildDir: DirectoryProperty = projectLayout.buildDirectory - - @get:Internal - val projectDir: Provider = objectFactory - .property(project.rootProject.projectDir) - - @get:Internal - val projectCacheDir: Provider = objectFactory - .property(gradle.projectCacheDir) - - @get:Internal - val sessionsDir: Provider = objectFactory - .property(GradleCompilerRunner.sessionsDir(gradle.projectCacheDir)) - - @get:Internal - val projectName: Provider = objectFactory - .property(project.rootProject.name.normalizeForFlagFile()) - - @get:Internal - val buildModulesInfo: Provider = objectFactory - .property(incrementalModuleInfoProvider) - - @get:Internal - val errorsFile: Provider = objectFactory - .property( - gradle.rootProject.rootDir.resolve(".gradle/kotlin/errors/").also { it.mkdirs() } - .resolve("errors-${System.currentTimeMillis()}.log")) -} - -abstract class AbstractKotlinCompile @Inject constructor( - objectFactory: ObjectFactory, - workerExecutor: WorkerExecutor -) : AbstractKotlinCompileTool(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 - - @get:Internal - val startParameters = BuildReportsService.getStartParameters(project) - - @get:Internal - internal abstract val suppressKotlinOptionsFreeArgsModificationWarning: Property - - 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? = 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 = 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 = objectFactory - .property( - objectFactory.newInstance(project.gradle, this, project, incrementalModuleInfoProvider) - ) - - @get:Internal - internal open val defaultKotlinJavaToolchain: Provider = objectFactory - .propertyWithNewInstance({ null }) - - @get:Internal - internal val compilerRunner: Provider = - 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 - - /** Task outputs that we don't want to include in [TaskOutputsBackup] (see [TaskOutputsBackup.outputsToRestore] for more info). */ - @get:Internal - internal abstract val taskOutputsBackupExcludes: SetProperty - - @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 - 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 - ) = if (!inputChanges.isIncremental) { - ChangedFiles.Unknown() - } else { - incrementalProps - .fold(mutableListOf() to mutableListOf()) { (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, - 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>(taskProvider: T) { - val logger: Logger = taskProvider.logger val isMultiplatform: Boolean = taskProvider.multiPlatformEnabled.get() private val pluginData = taskProvider.kotlinPluginData?.orNull @@ -516,714 +29,3 @@ class KotlinJvmCompilerArgumentsProvider val destinationDir: File = taskProvider.destinationDirectory.get().asFile internal val compilerOptions: KotlinJvmCompilerOptions = taskProvider.compilerOptions } - -internal inline val T.thisTaskProvider: TaskProvider - get() = checkNotNull(project.locateTask(name)) - -@CacheableTask -abstract class KotlinCompile @Inject constructor( - final override val compilerOptions: KotlinJvmCompilerOptions, - workerExecutor: WorkerExecutor, - objectFactory: ObjectFactory -) : AbstractKotlinCompile(objectFactory, workerExecutor), - K2MultiplatformCompilationTask, - @Suppress("TYPEALIAS_EXPANSION_DEPRECATION") KotlinJvmCompileDsl, - KotlinCompilationTask, - 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 = 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 - - @get:Nested - abstract val classpathSnapshotProperties: ClasspathSnapshotProperties - - /** Properties related to the `kotlin.incremental.useClasspathSnapshot` feature. */ - abstract class ClasspathSnapshotProperties { - @get:Input - abstract val useClasspathSnapshot: Property - - @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 - get() = listOf( - sources, - javaSources, - scriptSources, - androidLayoutResources, - commonSourceSet, - classpathSnapshotProperties.classpath, - classpathSnapshotProperties.classpathSnapshot - ) - - @get:Internal - final override val defaultKotlinJavaToolchain: Provider = objectFactory - .propertyWithNewInstance({ this }) - - final override val kotlinJavaToolchainProvider: Provider = defaultKotlinJavaToolchain.cast() - - @get:Internal - internal abstract val associatedJavaCompileTaskTargetCompatibility: Property - - @get:Internal - internal abstract val associatedJavaCompileTaskName: Property - - @get:Input - internal abstract val jvmTargetValidationMode: Property - - @get:Internal - internal val scriptDefinitions: ConfigurableFileCollection = objectFactory.fileCollection() - - @get:Input - @get:Optional - internal val scriptExtensions: SetProperty = objectFactory.setPropertyWithLazyValue { - scriptDefinitions - .map { definitionFile -> - definitionFile.readLines().filter(String::isNotBlank) - } - .flatten() - } - - private class ScriptFilterSpec( - private val scriptExtensions: SetProperty - ) : Spec { - 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? = 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 by lazy { - KotlinJvmCompilerArgumentsContributor(KotlinJvmCompilerArgumentsProvider(this)) - } - - override fun callCompilerAsync( - args: K2JVMCompilerArguments, - kotlinSources: Set, - 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(objectFactory, workerExecutor), - KotlinCompilationTask, - 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 - - // 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 - - /** - * 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? = 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 = 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 = objectFactory.property( - PropertiesProvider(project).jsCompilerNoWarn - ) - - @get:Internal - protected val libraryFilter: (File) -> Boolean - get() = { file -> - libraryFilterCacheService.get().getOrCompute(file.asLibraryFilterCacheKey, libraryFilterBody) - } - - override val incrementalProps: List - get() = super.incrementalProps + listOf(friendDependencies) - - open fun processArgs( - args: K2JSCompilerArguments - ) { - - } - - override fun callCompilerAsync( - args: K2JSCompilerArguments, - kotlinSources: Set, - 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, - - @get:InputFiles - @get:IgnoreEmptyDirectories - @get:NormalizeLineEndings - @get:PathSensitive(PathSensitivity.RELATIVE) - val inputFiles: Set, - - @get:OutputFiles - val outputFiles: Set - ) -} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompilerPluginData.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompilerPluginData.kt index 3685be8fdba..536dac1103b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompilerPluginData.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompilerPluginData.kt @@ -1,1205 +1,17 @@ /* - * 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. */ package org.jetbrains.kotlin.gradle.tasks -import groovy.lang.Closure -import org.gradle.api.* -import org.gradle.api.file.* -import org.gradle.api.invocation.Gradle -import org.gradle.api.logging.Logger -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider -import org.gradle.api.provider.SetProperty -import org.gradle.api.specs.Spec +import org.gradle.api.file.FileCollection 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.InputChanges import org.gradle.work.NormalizeLineEndings -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.compilerRunner.* -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.gradle.dsl.* -import org.jetbrains.kotlin.gradle.incremental.* -import org.jetbrains.kotlin.gradle.internal.* -import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState -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.GradlePrintingMessageCollector -import org.jetbrains.kotlin.gradle.logging.kotlinDebug -import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.gradle.plugin.mpp.* -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.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.utils.* -import org.jetbrains.kotlin.incremental.* -import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotDisabled -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.statistics.metrics.BooleanMetrics -import org.jetbrains.kotlin.utils.JsLibraryUtils -import org.jetbrains.kotlin.utils.addToStdlib.cast +import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi import java.io.File -import java.nio.file.Files.* -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 @Inject constructor( - objectFactory: ObjectFactory -) : DefaultTask(), - KotlinCompileTool, - CompilerArgumentAware, - 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 = patternFilterable.includes - - @Internal - final override fun getExcludes(): MutableSet = patternFilterable.excludes - - final override fun setIncludes(includes: Iterable): PatternFilterable = also { - patternFilterable.setIncludes(includes) - } - - final override fun setExcludes(excludes: Iterable): PatternFilterable = also { - patternFilterable.setExcludes(excludes) - } - - final override fun include(vararg includes: String?): PatternFilterable = also { - patternFilterable.include(*includes) - } - - final override fun include(includes: Iterable): PatternFilterable = also { - patternFilterable.include(includes) - } - - final override fun include(includeSpec: Spec): 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): PatternFilterable = also { - patternFilterable.exclude(excludes) - } - - final override fun exclude(excludeSpec: Spec): PatternFilterable = also { - patternFilterable.exclude(excludeSpec) - } - - final override fun exclude(excludeSpec: Closure<*>): PatternFilterable = also { - patternFilterable.exclude(excludeSpec) - } - - @get:Internal - final override val metrics: Property = 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 -) { - - @get:Internal - val path: Provider = objectFactory.property(task.path) - - @get:Internal - val logger: Provider = objectFactory.property(task.logger) - - @get:Internal - val buildDir: DirectoryProperty = projectLayout.buildDirectory - - @get:Internal - val projectDir: Provider = objectFactory - .property(project.rootProject.projectDir) - - @get:Internal - val projectCacheDir: Provider = objectFactory - .property(gradle.projectCacheDir) - - @get:Internal - val sessionsDir: Provider = objectFactory - .property(GradleCompilerRunner.sessionsDir(gradle.projectCacheDir)) - - @get:Internal - val projectName: Provider = objectFactory - .property(project.rootProject.name.normalizeForFlagFile()) - - @get:Internal - val buildModulesInfo: Provider = objectFactory - .property(incrementalModuleInfoProvider) - - @get:Internal - val errorsFile: Provider = objectFactory - .property( - gradle.rootProject.rootDir.resolve(".gradle/kotlin/errors/").also { it.mkdirs() } - .resolve("errors-${System.currentTimeMillis()}.log")) -} - -abstract class AbstractKotlinCompile @Inject constructor( - objectFactory: ObjectFactory, - workerExecutor: WorkerExecutor -) : AbstractKotlinCompileTool(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 - - @get:Internal - val startParameters = BuildReportsService.getStartParameters(project) - - @get:Internal - internal abstract val suppressKotlinOptionsFreeArgsModificationWarning: Property - - 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? = 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 = 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 = objectFactory - .property( - objectFactory.newInstance(project.gradle, this, project, incrementalModuleInfoProvider) - ) - - @get:Internal - internal open val defaultKotlinJavaToolchain: Provider = objectFactory - .propertyWithNewInstance({ null }) - - @get:Internal - internal val compilerRunner: Provider = - 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 - - /** Task outputs that we don't want to include in [TaskOutputsBackup] (see [TaskOutputsBackup.outputsToRestore] for more info). */ - @get:Internal - internal abstract val taskOutputsBackupExcludes: SetProperty - - @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 - 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 - ) = if (!inputChanges.isIncremental) { - ChangedFiles.Unknown() - } else { - incrementalProps - .fold(mutableListOf() to mutableListOf()) { (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, - 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>(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(taskProvider) { - val taskName: String = taskProvider.name - val moduleName: String = taskProvider.moduleName.get() - val friendPaths: FileCollection = taskProvider.friendPaths - val compileClasspath: Iterable = taskProvider.libraries - val destinationDir: File = taskProvider.destinationDirectory.get().asFile - internal val compilerOptions: KotlinJvmCompilerOptions = taskProvider.compilerOptions -} - -internal inline val T.thisTaskProvider: TaskProvider - get() = checkNotNull(project.locateTask(name)) - -@CacheableTask -abstract class KotlinCompile @Inject constructor( - final override val compilerOptions: KotlinJvmCompilerOptions, - workerExecutor: WorkerExecutor, - objectFactory: ObjectFactory -) : AbstractKotlinCompile(objectFactory, workerExecutor), - K2MultiplatformCompilationTask, - @Suppress("TYPEALIAS_EXPANSION_DEPRECATION") KotlinJvmCompileDsl, - KotlinCompilationTask, - 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 = 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 - - @get:Nested - abstract val classpathSnapshotProperties: ClasspathSnapshotProperties - - /** Properties related to the `kotlin.incremental.useClasspathSnapshot` feature. */ - abstract class ClasspathSnapshotProperties { - @get:Input - abstract val useClasspathSnapshot: Property - - @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 - get() = listOf( - sources, - javaSources, - scriptSources, - androidLayoutResources, - commonSourceSet, - classpathSnapshotProperties.classpath, - classpathSnapshotProperties.classpathSnapshot - ) - - @get:Internal - final override val defaultKotlinJavaToolchain: Provider = objectFactory - .propertyWithNewInstance({ this }) - - final override val kotlinJavaToolchainProvider: Provider = defaultKotlinJavaToolchain.cast() - - @get:Internal - internal abstract val associatedJavaCompileTaskTargetCompatibility: Property - - @get:Internal - internal abstract val associatedJavaCompileTaskName: Property - - @get:Input - internal abstract val jvmTargetValidationMode: Property - - @get:Internal - internal val scriptDefinitions: ConfigurableFileCollection = objectFactory.fileCollection() - - @get:Input - @get:Optional - internal val scriptExtensions: SetProperty = objectFactory.setPropertyWithLazyValue { - scriptDefinitions - .map { definitionFile -> - definitionFile.readLines().filter(String::isNotBlank) - } - .flatten() - } - - private class ScriptFilterSpec( - private val scriptExtensions: SetProperty - ) : Spec { - 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? = 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 by lazy { - KotlinJvmCompilerArgumentsContributor(KotlinJvmCompilerArgumentsProvider(this)) - } - - override fun callCompilerAsync( - args: K2JVMCompilerArguments, - kotlinSources: Set, - 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(objectFactory, workerExecutor), - KotlinCompilationTask, - 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 - - // 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 - - /** - * 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? = 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 = 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 = objectFactory.property( - PropertiesProvider(project).jsCompilerNoWarn - ) - - @get:Internal - protected val libraryFilter: (File) -> Boolean - get() = { file -> - libraryFilterCacheService.get().getOrCompute(file.asLibraryFilterCacheKey, libraryFilterBody) - } - - override val incrementalProps: List - get() = super.incrementalProps + listOf(friendDependencies) - - open fun processArgs( - args: K2JSCompilerArguments - ) { - - } - - override fun callCompilerAsync( - args: K2JSCompilerArguments, - kotlinSources: Set, - 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." - ) - } - } -} +@InternalKotlinGradlePluginApi data class KotlinCompilerPluginData( @get:Classpath val classpath: FileCollection, @@ -1226,4 +38,4 @@ data class KotlinCompilerPluginData( @get:OutputFiles val outputFiles: Set ) -} +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt new file mode 100644 index 00000000000..a868377fe51 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -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 T.thisTaskProvider: TaskProvider + get() = checkNotNull(project.locateTask(name)) +