JPS: Support new MPP model.

Original commit: 839a6b5e60
This commit is contained in:
Sergey Rostov
2018-07-31 15:50:17 +03:00
committed by Sergey Igushkin
parent 5b1ea61cd1
commit c475e35489
5 changed files with 101 additions and 62 deletions
@@ -41,13 +41,17 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.INFO
import org.jetbrains.kotlin.cli.common.messages.MessageCollectorUtil
import org.jetbrains.kotlin.compilerRunner.*
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.config.KotlinModuleKind
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.daemon.common.isDaemonEnabled
import org.jetbrains.kotlin.incremental.*
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.jps.incremental.*
import org.jetbrains.kotlin.jps.platforms.*
import org.jetbrains.kotlin.jps.model.kotlinKind
import org.jetbrains.kotlin.jps.platforms.KotlinJvmModuleBuildTarget
import org.jetbrains.kotlin.jps.platforms.KotlinModuleBuildTarget
import org.jetbrains.kotlin.jps.platforms.kotlinBuildTargets
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.preloading.ClassCondition
import org.jetbrains.kotlin.utils.KotlinPaths
@@ -242,13 +246,28 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
return NOTHING_DONE
val kotlinTarget = context.kotlinBuildTargets[chunk.representativeTarget()] ?: return OK
val messageCollector = MessageCollectorAdapter(context, kotlinTarget)
// New mpp project model: modules which is imported from sources sets of the compilations shouldn't be compiled for now.
// It should be compiled only as one of source root of target compilation, which is added in [KotlinSourceRootProvider].
if (chunk.modules.any { it.kotlinKind == KotlinModuleKind.SOURCE_SET_HOLDER }) {
if (chunk.modules.size > 1) {
messageCollector.report(
CompilerMessageSeverity.ERROR,
"Cyclically dependent modules is not supported in multiplatform projects"
)
return ABORT
}
return NOTHING_DONE
}
val kotlinDirtyFilesHolder = KotlinDirtySourceFilesHolder(chunk, context, dirtyFilesHolder)
val fsOperations = FSOperationsHelper(context, chunk, kotlinDirtyFilesHolder, LOG)
try {
val proposedExitCode = doBuild(chunk, kotlinTarget, context, kotlinDirtyFilesHolder, messageCollector, outputConsumer, fsOperations)
val proposedExitCode =
doBuild(chunk, kotlinTarget, context, kotlinDirtyFilesHolder, messageCollector, outputConsumer, fsOperations)
val actualExitCode = if (proposedExitCode == OK && fsOperations.hasMarkedDirty) ADDITIONAL_PASS_REQUIRED else proposedExitCode
@@ -605,10 +624,10 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
val representativeTarget = chunk.representativeTarget()
fun SimpleOutputItem.target() =
sourceFiles.firstOrNull()?.let { sourceToTarget[it] } ?: chunk.targets.singleOrNull {
it.outputDir?.let {
outputFile.startsWith(it)
} ?: false
} ?: representativeTarget
it.outputDir?.let {
outputFile.startsWith(it)
} ?: false
} ?: representativeTarget
return outputItemCollector.outputs.groupBy(SimpleOutputItem::target, SimpleOutputItem::toGeneratedFile)
}
@@ -20,17 +20,9 @@ import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import org.jetbrains.kotlin.config.KotlinResourceRootType
import org.jetbrains.kotlin.config.KotlinSourceRootType
import org.jetbrains.kotlin.jps.model.expectedByModules
import org.jetbrains.kotlin.jps.model.sourceSetModules
import java.io.File
/**
* - adds roots with KotlinSourceRootType as JavaSourceRootDescriptor (see note below)
* - for Multiplatform Projects: adds all the source roots of the expectedBy modules to the platform modules.
*
* Note: `KotlinSourceRootType` cannot be supported directly, since `SourceRootDescriptors` are computed by
* `ModuleBuildTarget.computeAllTargets`. `ModuleBuildTarget` is required for incremental compilation.
* We cannot define our own `ModuleBuildTarget` since it is final and `ModuleBuildTarget` supports only `JavaSourceRootDescriptor`.
* So the only one way to support `KotlinSourceRootType` is to add a fake `JavaSourceRootDescriptor` for each source root with that type.
*/
class KotlinSourceRootProvider : AdditionalRootsProviderService<JavaSourceRootDescriptor>(JavaModuleBuildTargetType.ALL_TYPES) {
override fun getAdditionalRoots(
target: BuildTarget<JavaSourceRootDescriptor>,
@@ -41,7 +33,12 @@ class KotlinSourceRootProvider : AdditionalRootsProviderService<JavaSourceRootDe
val result = mutableListOf<JavaSourceRootDescriptor>()
// add source roots with type KotlinSourceRootType
// Add source roots with type KotlinSourceRootType.
//
// Note: `KotlinSourceRootType` cannot be supported directly, since `SourceRootDescriptors` are computed by
// `ModuleBuildTarget.computeAllTargets`. `ModuleBuildTarget` is required for incremental compilation.
// We cannot define our own `ModuleBuildTarget` since it is final and `ModuleBuildTarget` supports only `JavaSourceRootDescriptor`.
// So the only one way to support `KotlinSourceRootType` is to add a fake `JavaSourceRootDescriptor` for each source root with that type.
val kotlinSourceRootType = if (target.isTests) KotlinSourceRootType.TestSource else KotlinSourceRootType.Source
module.getSourceRoots(kotlinSourceRootType).forEach {
result.add(
@@ -56,26 +53,37 @@ class KotlinSourceRootProvider : AdditionalRootsProviderService<JavaSourceRootDe
)
}
// add source roots of the expectedBy modules
// new multiplatform model support:
module.sourceSetModules.forEach { sourceSetModule ->
addModuleSourceRoots(result, sourceSetModule, target, false)
}
// legacy multiplatform model support:
module.expectedByModules.forEach { commonModule ->
addCommonModuleSourceRoots(result, commonModule, target)
// At the moment, incremental compilation is not supported by K2Metadata compiler.
// To avoid long running compilation of common modules, we do not run K2Metadata at all:
// instead all the common source roots are transitively added to the final platform modules.
val isRecursive = true
addModuleSourceRoots(result, commonModule, target, isRecursive)
}
return result
}
private fun addCommonModuleSourceRoots(
private fun addModuleSourceRoots(
result: MutableList<JavaSourceRootDescriptor>,
commonModule: JpsModule,
target: ModuleBuildTarget
module: JpsModule,
target: ModuleBuildTarget,
recursive: Boolean = false
) {
for (commonSourceRoot in commonModule.sourceRoots) {
for (commonSourceRoot in module.sourceRoots) {
val isCommonTestsRootType = commonSourceRoot.rootType.isTestsRootType
if (isCommonTestsRootType != null && target.isTests == isCommonTestsRootType) {
val javaSourceRootProperties = commonSourceRoot.properties as? JavaSourceRootProperties
result.add(
KotlinCommonModuleSourceRoot(
KotlinIncludedModuleSourceRoot(
commonSourceRoot.file,
target,
javaSourceRootProperties?.isForGeneratedSources ?: false,
@@ -87,20 +95,20 @@ class KotlinSourceRootProvider : AdditionalRootsProviderService<JavaSourceRootDe
}
}
// At the moment, incremental compilation is not supported by K2Metadata compiler.
// To avoid long running compilation of common modules, we do not run K2Metadata at all:
// instead all the common source roots are transitively added to the final platform modules.
JpsJavaExtensionService.dependencies(commonModule)
.also {
if (!target.isTests) it.productionOnly()
}
.processModules {
addCommonModuleSourceRoots(
result,
it,
ModuleBuildTarget(it, target.targetType as JavaModuleBuildTargetType)
)
}
if (recursive) {
JpsJavaExtensionService.dependencies(module)
.also {
if (!target.isTests) it.productionOnly()
}
.processModules {
addModuleSourceRoots(
result,
it,
ModuleBuildTarget(it, target.targetType as JavaModuleBuildTargetType),
true
)
}
}
}
}
@@ -108,13 +116,13 @@ private val JpsModuleSourceRootType<*>.isTestsRootType
get() = when (this) {
is KotlinSourceRootType -> this == KotlinSourceRootType.TestSource
is KotlinResourceRootType -> this == KotlinResourceRootType.TestResource
// for compatibility:
// for compatibility:
is JavaSourceRootType -> this == JavaSourceRootType.TEST_SOURCE
is JavaResourceRootType -> this == JavaResourceRootType.TEST_RESOURCE
else -> null
}
class KotlinCommonModuleSourceRoot(
class KotlinIncludedModuleSourceRoot(
root: File,
target: ModuleBuildTarget,
isGenerated: Boolean,
@@ -32,7 +32,7 @@ class MessageCollectorAdapter(
val kind = kind(severity)
if (kind != null) {
// Report target when cross-compiling common files
if (location != null && kotlinTarget != null && kotlinTarget.isCommonModuleFile(File(location.path))) {
if (location != null && kotlinTarget != null && kotlinTarget.isFromIncludedSourceRoot(File(location.path))) {
val moduleName = kotlinTarget.module.name
prefix += "[$moduleName] "
}
@@ -5,15 +5,14 @@
package org.jetbrains.kotlin.jps.model
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
import org.jetbrains.jps.model.ex.JpsElementBase
import org.jetbrains.jps.model.ex.JpsElementChildRoleBase
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.module.JpsModule
import org.jetbrains.kotlin.cli.common.arguments.*
import org.jetbrains.kotlin.config.CompilerSettings
import org.jetbrains.kotlin.config.KotlinFacetSettings
import org.jetbrains.kotlin.config.KotlinModuleKind
import org.jetbrains.kotlin.config.TargetPlatformKind
val JpsModule.kotlinFacet: JpsKotlinFacetModuleExtension?
@@ -22,24 +21,37 @@ val JpsModule.kotlinFacet: JpsKotlinFacetModuleExtension?
val JpsModule.targetPlatform: TargetPlatformKind<*>?
get() = kotlinFacet?.settings?.targetPlatformKind
val JpsModule.kotlinKind: KotlinModuleKind
get() = kotlinFacet?.settings?.kind ?: KotlinModuleKind.DEFAULT
/**
* Modules which is imported from sources sets of the compilation represented by this module.
* This module is not included.
*/
val JpsModule.sourceSetModules: List<JpsModule>
get() = findDependencies(kotlinFacet?.settings?.sourceSetNames)
/**
* Legacy. List of modules with `expectedBy` dependency.
*/
val JpsModule.expectedByModules: List<JpsModule>
get() {
val kotlinFacetExtension = kotlinFacet
val implementedModuleNames = kotlinFacetExtension?.settings?.implementedModuleNames ?: return listOf()
if (implementedModuleNames.isEmpty()) return listOf()
get() = findDependencies(kotlinFacet?.settings?.implementedModuleNames)
val result = ArrayList<JpsModule>(implementedModuleNames.size)
private fun JpsModule.findDependencies(moduleNames: List<String>?): List<JpsModule> {
if (moduleNames == null || moduleNames.isEmpty()) return listOf()
JpsJavaExtensionService.dependencies(this)
.processModules {
if (it.name in implementedModuleNames) {
// Note, production sources should be added for both production and tests targets
result.add(it)
}
val result = ArrayList<JpsModule>(moduleNames.size)
JpsJavaExtensionService.dependencies(this)
.processModules {
if (it.name in moduleNames) {
// Note, production sources should be added for both production and tests targets
result.add(it)
}
}
return result
}
return result
}
val JpsModule.productionOutputFilePath: String?
get() {
@@ -32,7 +32,7 @@ import org.jetbrains.kotlin.incremental.LookupTrackerImpl
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.jps.build.KotlinBuilder
import org.jetbrains.kotlin.jps.build.KotlinCommonModuleSourceRoot
import org.jetbrains.kotlin.jps.build.KotlinIncludedModuleSourceRoot
import org.jetbrains.kotlin.jps.build.KotlinDirtySourceFilesHolder
import org.jetbrains.kotlin.jps.build.isKotlinSourceFile
import org.jetbrains.kotlin.jps.incremental.CacheVersionProvider
@@ -124,13 +124,13 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo>(
val buildRootIndex = context.projectDescriptor.buildRootIndex
val roots = buildRootIndex.getTargetRoots(jpsModuleBuildTarget, context)
roots.forEach { rootDescriptor ->
val isCommonRoot = rootDescriptor is KotlinCommonModuleSourceRoot
val isIncludedSourceRoot = rootDescriptor is KotlinIncludedModuleSourceRoot
rootDescriptor.root.walkTopDown()
.onEnter { file -> file !in moduleExcludes }
.forEach { file ->
if (!compilerExcludes.isExcluded(file) && file.isFile && file.isKotlinSourceFile) {
receiver[file] = Source(file, isCommonRoot)
receiver[file] = Source(file, isIncludedSourceRoot)
}
}
@@ -138,14 +138,14 @@ abstract class KotlinModuleBuildTarget<BuildMetaInfoType : BuildMetaInfo>(
}
/**
* @property isCommonModule for reporting errors during cross-compilation common module sources
* @property isIncludedSourceRoot for reporting errors during cross-compilation common module sources
*/
class Source(
val file: File,
val isCommonModule: Boolean
val isIncludedSourceRoot: Boolean
)
fun isCommonModuleFile(file: File): Boolean = sources[file]?.isCommonModule == true
fun isFromIncludedSourceRoot(file: File): Boolean = sources[file]?.isIncludedSourceRoot == true
val sourceFiles: Collection<File>
get() = sources.values.map { it.file }