jps(refactor): Extract platform related code
#KT-23656 Fixed
(cherry picked from commit 18a751d)
Original commit: 75e6be7802
This commit is contained in:
+3
@@ -50,6 +50,9 @@ import org.jetbrains.kotlin.incremental.isJavaFile
|
||||
import org.jetbrains.kotlin.incremental.testingUtils.*
|
||||
import org.jetbrains.kotlin.jps.incremental.getKotlinCache
|
||||
import org.jetbrains.kotlin.jps.incremental.withLookupStorage
|
||||
import org.jetbrains.kotlin.jps.model.JpsKotlinFacetModuleExtension
|
||||
import org.jetbrains.kotlin.jps.model.kotlinFacetExtension
|
||||
import org.jetbrains.kotlin.jps.platforms.clearKotlinModuleBuildTargetDataBindings
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
|
||||
+2
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.jps.build
|
||||
|
||||
import org.jetbrains.jps.builders.JpsBuildTestCase
|
||||
import org.jetbrains.kotlin.jps.platforms.clearKotlinModuleBuildTargetDataBindings
|
||||
|
||||
abstract class BaseKotlinJpsBuildTestCase : JpsBuildTestCase() {
|
||||
@Throws(Exception::class)
|
||||
@@ -33,5 +34,6 @@ abstract class BaseKotlinJpsBuildTestCase : JpsBuildTestCase() {
|
||||
super.tearDown()
|
||||
myModel = null
|
||||
myBuildParams.clear()
|
||||
clearKotlinModuleBuildTargetDataBindings()
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,10 @@ import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.incremental.withIC
|
||||
import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings
|
||||
import org.jetbrains.kotlin.jps.build.KotlinJpsBuildTest.LibraryDependency.*
|
||||
import org.jetbrains.kotlin.jps.platforms.KotlinJsModuleBuildTarget
|
||||
import org.jetbrains.kotlin.jps.platforms.clearKotlinModuleBuildTargetDataBindings
|
||||
import org.jetbrains.kotlin.jps.platforms.kotlinData
|
||||
import org.jetbrains.kotlin.jps.platforms.productionBuildTarget
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
@@ -248,17 +252,22 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() {
|
||||
}
|
||||
|
||||
private fun k2jsOutput(vararg moduleNames: String): Array<String> {
|
||||
val list = arrayListOf<String>()
|
||||
for (moduleName in moduleNames) {
|
||||
val outputDir = File("out/production/$moduleName")
|
||||
list.add(toSystemIndependentName(JpsJsModuleUtils.getOutputFile(outputDir, moduleName, false).path))
|
||||
list.add(toSystemIndependentName(JpsJsModuleUtils.getOutputMetaFile(outputDir, moduleName, false).path))
|
||||
val moduleNamesSet = moduleNames.toSet()
|
||||
val list = mutableListOf<String>()
|
||||
|
||||
val kjsmFiles = File(workDir, outputDir.path).walk()
|
||||
.filter { it.isFile && it.extension.equals("kjsm", ignoreCase = true) }
|
||||
list.addAll(kjsmFiles.map { toSystemIndependentName(it.relativeTo(workDir).path) })
|
||||
myProject.modules.forEach {
|
||||
if (it.name in moduleNamesSet) {
|
||||
val productionTarget = it.productionBuildTarget.kotlinData as KotlinJsModuleBuildTarget
|
||||
list.add(toSystemIndependentName(productionTarget.outputFile.relativeTo(workDir).path))
|
||||
list.add(toSystemIndependentName(productionTarget.outputMetaFile.relativeTo(workDir).path))
|
||||
|
||||
val kjsmFiles = productionTarget.outputDir.walk()
|
||||
.filter { it.isFile && it.extension.equals("kjsm", ignoreCase = true) }
|
||||
|
||||
list.addAll(kjsmFiles.map { toSystemIndependentName(it.relativeTo(workDir).path) })
|
||||
}
|
||||
}
|
||||
|
||||
return list.toTypedArray()
|
||||
}
|
||||
|
||||
@@ -1057,6 +1066,8 @@ open class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() {
|
||||
}
|
||||
|
||||
protected fun checkWhen(actions: Array<Action>, pathsToCompile: Array<String>?, pathsToDelete: Array<String>?) {
|
||||
clearKotlinModuleBuildTargetDataBindings()
|
||||
|
||||
for (action in actions) {
|
||||
action.apply()
|
||||
}
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.jps.build
|
||||
|
||||
import com.intellij.util.Consumer
|
||||
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType
|
||||
import org.jetbrains.jps.incremental.ModuleBuildTarget
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType
|
||||
import org.jetbrains.jps.model.java.JpsJavaModuleType
|
||||
import org.jetbrains.jps.model.library.JpsOrderRootType
|
||||
import org.jetbrains.jps.model.module.JpsModule
|
||||
import org.jetbrains.jps.util.JpsPathUtil
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
object JpsJsModuleUtils {
|
||||
fun getLibraryFilesAndDependencies(target: ModuleBuildTarget): List<String> {
|
||||
val result = ArrayList<String>()
|
||||
getLibraryFiles(target, result)
|
||||
getDependencyModulesAndSources(target, result)
|
||||
return result
|
||||
}
|
||||
|
||||
fun getLibraryFiles(target: ModuleBuildTarget, result: MutableList<String>) {
|
||||
val libraries = JpsUtils.getAllDependencies(target).libraries
|
||||
for (library in libraries) {
|
||||
for (root in library.getRoots(JpsOrderRootType.COMPILED)) {
|
||||
result.add(JpsPathUtil.urlToPath(root.url))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getDependencyModulesAndSources(target: ModuleBuildTarget, result: MutableList<String>) {
|
||||
JpsUtils.getAllDependencies(target).processModules(object : Consumer<JpsModule> {
|
||||
override fun consume(module: JpsModule) {
|
||||
if (module.moduleType != JpsJavaModuleType.INSTANCE) return
|
||||
|
||||
if ((module != target.module || target.isTests) && module.hasProductionSourceRoot) {
|
||||
addTarget(module, isTests = false)
|
||||
}
|
||||
|
||||
if (module != target.module && target.isTests && module.hasTestSourceRoot) {
|
||||
addTarget(module, isTests = true)
|
||||
}
|
||||
}
|
||||
|
||||
fun addTarget(module: JpsModule, isTests: Boolean) {
|
||||
val metaInfoFile = getOutputMetaFile(module, isTests)
|
||||
if (metaInfoFile.exists()) {
|
||||
result.add(metaInfoFile.absolutePath)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getOutputMetaFile(module: JpsModule, isTests: Boolean): File {
|
||||
val moduleBuildTarget =
|
||||
ModuleBuildTarget(module, if (isTests) JavaModuleBuildTargetType.TEST else JavaModuleBuildTargetType.PRODUCTION)
|
||||
val outputDir = KotlinBuilderModuleScriptGenerator.getOutputDirSafe(moduleBuildTarget)
|
||||
return getOutputMetaFile(outputDir, module.name, isTests)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getOutputFile(outputDir: File, moduleName: String, isTests: Boolean) =
|
||||
File(outputDir, moduleName + suffix(isTests) + KotlinJavascriptMetadataUtils.JS_EXT)
|
||||
|
||||
@JvmStatic
|
||||
fun getOutputMetaFile(outputDir: File, moduleName: String, isTests: Boolean) =
|
||||
File(outputDir, moduleName + suffix(isTests) + KotlinJavascriptMetadataUtils.META_JS_SUFFIX)
|
||||
|
||||
private fun suffix(isTests: Boolean) = if (isTests) "_test" else ""
|
||||
}
|
||||
|
||||
val JpsModule.hasProductionSourceRoot
|
||||
get() = sourceRoots.any { it.rootType == JavaSourceRootType.SOURCE }
|
||||
|
||||
val JpsModule.hasTestSourceRoot
|
||||
get() = sourceRoots.any { it.rootType == JavaSourceRootType.TEST_SOURCE }
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.jps.build
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
@@ -32,8 +31,6 @@ import org.jetbrains.jps.builders.java.JavaBuilderUtil
|
||||
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
|
||||
import org.jetbrains.jps.incremental.*
|
||||
import org.jetbrains.jps.incremental.ModuleLevelBuilder.ExitCode.*
|
||||
import org.jetbrains.jps.incremental.Utils.*
|
||||
import org.jetbrains.jps.incremental.fs.CompilationRound
|
||||
import org.jetbrains.jps.incremental.java.JavaBuilder
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage
|
||||
import org.jetbrains.jps.incremental.messages.CompilerMessage
|
||||
@@ -58,23 +55,21 @@ import org.jetbrains.kotlin.daemon.common.isDaemonEnabled
|
||||
import org.jetbrains.kotlin.incremental.*
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings
|
||||
import org.jetbrains.kotlin.jps.build.JpsJsModuleUtils.getOutputMetaFile
|
||||
import org.jetbrains.kotlin.jps.incremental.*
|
||||
import org.jetbrains.kotlin.jps.productionOutputFilePath
|
||||
import org.jetbrains.kotlin.jps.testOutputFilePath
|
||||
import org.jetbrains.kotlin.jps.platforms.KotlinJsModuleBuildTarget
|
||||
import org.jetbrains.kotlin.jps.platforms.kotlinData
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
||||
import org.jetbrains.kotlin.modules.TargetId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.preloading.ClassCondition
|
||||
import org.jetbrains.kotlin.progress.CompilationCanceledException
|
||||
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
|
||||
import org.jetbrains.kotlin.utils.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
|
||||
import org.jetbrains.kotlin.utils.KotlinPaths
|
||||
import org.jetbrains.kotlin.utils.KotlinPathsFromHomeDir
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
import java.util.*
|
||||
import kotlin.collections.HashSet
|
||||
|
||||
@@ -218,7 +213,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
||||
val allVersions = cacheVersionsProvider.allVersions(targets)
|
||||
val actions = allVersions.map { it.checkVersion() }.toMutableSet()
|
||||
|
||||
if (!JpsUtils.isJsKotlinModule(chunk.representativeTarget())) {
|
||||
if (chunk.representativeTarget().kotlinData !is KotlinJsModuleBuildTarget) {
|
||||
val args = compilerArgumentsForChunk(chunk)
|
||||
val currentBuildMetaInfo = JvmBuildMetaInfo(args)
|
||||
|
||||
@@ -395,10 +390,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
||||
rebuildAfterCacheVersionChanged.clean(target)
|
||||
}
|
||||
|
||||
if (JpsUtils.isJsKotlinModule(chunk.representativeTarget())) {
|
||||
copyJsLibraryFilesIfNeeded(chunk)
|
||||
return OK
|
||||
}
|
||||
chunk.representativeTarget().kotlinData?.doAfterBuild()
|
||||
|
||||
updateJavaMappings(chunk, context, dirtyFilesHolder, filesToCompile, generatedFiles, incrementalCaches)
|
||||
|
||||
@@ -543,11 +535,6 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
||||
LOG.debug("Plugin loaded: ${argumentProvider::class.java.simpleName}")
|
||||
}
|
||||
|
||||
if (JpsUtils.isJsKotlinModule(chunk.representativeTarget())) {
|
||||
LOG.debug("Compiling to JS ${filesToCompile.values().size} files in ${filesToCompile.keySet().joinToString { it.presentableName }}")
|
||||
return compileToJs(chunk, commonArguments, environment, project)
|
||||
}
|
||||
|
||||
if (IncrementalCompilation.isEnabled()) {
|
||||
for (target in chunk.targets) {
|
||||
val cache = incrementalCaches[target]!!
|
||||
@@ -556,7 +543,12 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
||||
}
|
||||
}
|
||||
|
||||
return compileToJvm(allCompiledFiles, chunk, commonArguments, context, dirtyFilesHolder, environment, filesToCompile)
|
||||
val isDoneSomething = representativeTarget.kotlinData?.compileModuleChunk(
|
||||
allCompiledFiles, chunk, commonArguments, context,
|
||||
dirtyFilesHolder, environment, filesToCompile
|
||||
) ?: false
|
||||
|
||||
return if (isDoneSomething) environment.outputItemsCollector else null
|
||||
}
|
||||
|
||||
private fun createCompileEnvironment(
|
||||
@@ -569,7 +561,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
||||
register(LookupTracker::class.java, lookupTracker)
|
||||
register(
|
||||
IncrementalCompilationComponents::class.java,
|
||||
IncrementalCompilationComponentsImpl(incrementalCaches.mapKeys { TargetId(it.key) })
|
||||
IncrementalCompilationComponentsImpl(incrementalCaches.mapKeys { it.key.kotlinData!!.moduleId })
|
||||
)
|
||||
register(CompilationCanceledStatus::class.java, object : CompilationCanceledStatus {
|
||||
override fun checkCanceled() {
|
||||
@@ -626,8 +618,8 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
||||
val sourceToTarget = HashMap<File, ModuleBuildTarget>()
|
||||
if (chunk.targets.size > 1) {
|
||||
for (target in chunk.targets) {
|
||||
for (file in KotlinSourceFileCollector.getAllKotlinSourceFiles(target)) {
|
||||
sourceToTarget[file] = target
|
||||
target.kotlinData?.sources?.forEach {
|
||||
sourceToTarget[it] = target
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -720,146 +712,6 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
||||
}
|
||||
}
|
||||
|
||||
// if null is returned, nothing was done
|
||||
private fun compileToJs(
|
||||
chunk: ModuleChunk,
|
||||
commonArguments: CommonCompilerArguments,
|
||||
environment: JpsCompilerEnvironment,
|
||||
project: JpsProject
|
||||
): OutputItemsCollector? {
|
||||
val representativeTarget = chunk.representativeTarget()
|
||||
if (chunk.modules.size > 1) {
|
||||
// We do not support circular dependencies, but if they are present, we do our best should not break the build,
|
||||
// so we simply yield a warning and report NOTHING_DONE
|
||||
environment.messageCollector.report(
|
||||
STRONG_WARNING,
|
||||
"Circular dependencies are not supported. The following JS modules depend on each other: "
|
||||
+ chunk.modules.joinToString(", ") { it.name } + ". "
|
||||
+ "Kotlin is not compiled for these modules"
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
val sourceFiles = KotlinSourceFileCollector.getAllKotlinSourceFiles(representativeTarget)
|
||||
if (sourceFiles.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val outputDir = KotlinBuilderModuleScriptGenerator.getOutputDirSafe(representativeTarget)
|
||||
|
||||
val representativeModule = representativeTarget.module
|
||||
val moduleName = representativeModule.name
|
||||
val isTests = representativeTarget.isTests
|
||||
val explicitOutputPath = if (isTests) representativeModule.testOutputFilePath else representativeModule.productionOutputFilePath
|
||||
val outputFile = explicitOutputPath?.let { File(it) } ?: JpsJsModuleUtils.getOutputFile(outputDir, moduleName, isTests)
|
||||
val libraries = JpsJsModuleUtils.getLibraryFilesAndDependencies(representativeTarget)
|
||||
val compilerSettings = JpsKotlinCompilerSettings.getCompilerSettings(representativeModule)
|
||||
val k2JsArguments = JpsKotlinCompilerSettings.getK2JsCompilerArguments(representativeModule)
|
||||
|
||||
// Compiler starts to produce path relative to base dirs in source maps if at least one statement is true:
|
||||
// 1) base dirs are specified;
|
||||
// 2) prefix is specified (i.e. non-empty)
|
||||
// Otherwise compiler produces paths relative to source maps location.
|
||||
// We don't have UI to configure base dirs, but we have UI to configure prefix.
|
||||
// If prefix is not specified (empty) in UI, we want to produce paths relative to source maps location
|
||||
val sourceRoots = if (k2JsArguments.sourceMapPrefix.isNullOrBlank()) {
|
||||
emptyList()
|
||||
} else {
|
||||
representativeModule.contentRootsList.urls
|
||||
.map { URI.create(it) }
|
||||
.filter { it.scheme == "file" }
|
||||
.map { File(it.path) }
|
||||
}
|
||||
|
||||
val friendPaths = KotlinBuilderModuleScriptGenerator.getProductionModulesWhichInternalsAreVisible(representativeTarget).mapNotNull {
|
||||
val file = getOutputMetaFile(it, false)
|
||||
if (file.exists()) file.absolutePath.toString() else null
|
||||
}
|
||||
|
||||
val compilerRunner = JpsKotlinCompilerRunner()
|
||||
compilerRunner.runK2JsCompiler(
|
||||
commonArguments, k2JsArguments, compilerSettings, environment, sourceFiles, sourceRoots,
|
||||
libraries, friendPaths, outputFile
|
||||
)
|
||||
return environment.outputItemsCollector
|
||||
}
|
||||
|
||||
private fun copyJsLibraryFilesIfNeeded(chunk: ModuleChunk) {
|
||||
val representativeTarget = chunk.representativeTarget()
|
||||
val outputDir = KotlinBuilderModuleScriptGenerator.getOutputDirSafe(representativeTarget)
|
||||
val compilerSettings = JpsKotlinCompilerSettings.getCompilerSettings(representativeTarget.module)
|
||||
val k2jsCompilerSettings = JpsKotlinCompilerSettings.getK2JsCompilerArguments(representativeTarget.module)
|
||||
if (compilerSettings.copyJsLibraryFiles) {
|
||||
val outputLibraryRuntimeDirectory = File(outputDir, compilerSettings.outputDirectoryForJsLibraryFiles).absolutePath
|
||||
val libraryFilesToCopy = arrayListOf<String>()
|
||||
JpsJsModuleUtils.getLibraryFiles(representativeTarget, libraryFilesToCopy)
|
||||
JsLibraryUtils.copyJsFilesFromLibraries(
|
||||
libraryFilesToCopy, outputLibraryRuntimeDirectory,
|
||||
copySourceMap = k2jsCompilerSettings.sourceMap
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// if null is returned, nothing was done
|
||||
private fun compileToJvm(
|
||||
allCompiledFiles: MutableSet<File>,
|
||||
chunk: ModuleChunk,
|
||||
commonArguments: CommonCompilerArguments,
|
||||
context: CompileContext,
|
||||
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
|
||||
environment: JpsCompilerEnvironment,
|
||||
filesToCompile: MultiMap<ModuleBuildTarget, File>
|
||||
): OutputItemsCollector? {
|
||||
if (chunk.modules.size > 1) {
|
||||
environment.messageCollector.report(
|
||||
STRONG_WARNING,
|
||||
"Circular dependencies are only partially supported. The following modules depend on each other: "
|
||||
+ chunk.modules.joinToString(", ") { it.name } + ". "
|
||||
+ "Kotlin will compile them, but some strange effect may happen"
|
||||
)
|
||||
}
|
||||
|
||||
allCompiledFiles.addAll(filesToCompile.values())
|
||||
|
||||
val processedTargetsWithRemoved = getProcessedTargetsWithRemovedFilesContainer(context)
|
||||
|
||||
var totalRemovedFiles = 0
|
||||
for (target in chunk.targets) {
|
||||
val removedFilesInTarget = KotlinSourceFileCollector.getRemovedKotlinFiles(dirtyFilesHolder, target)
|
||||
if (!removedFilesInTarget.isEmpty()) {
|
||||
if (processedTargetsWithRemoved.add(target)) {
|
||||
totalRemovedFiles += removedFilesInTarget.size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val moduleFile =
|
||||
KotlinBuilderModuleScriptGenerator.generateModuleDescription(context, chunk, filesToCompile, totalRemovedFiles != 0)
|
||||
if (moduleFile == null) {
|
||||
KotlinBuilder.LOG.debug("Not compiling, because no files affected: " + filesToCompile.keySet().joinToString { it.presentableName })
|
||||
// No Kotlin sources found
|
||||
return null
|
||||
}
|
||||
|
||||
val module = chunk.representativeTarget().module
|
||||
val k2JvmArguments = JpsKotlinCompilerSettings.getK2JvmCompilerArguments(module)
|
||||
val compilerSettings = JpsKotlinCompilerSettings.getCompilerSettings(module)
|
||||
|
||||
KotlinBuilder.LOG.debug("Compiling to JVM ${filesToCompile.values().size} files"
|
||||
+ (if (totalRemovedFiles == 0) "" else " ($totalRemovedFiles removed files)")
|
||||
+ " in " + filesToCompile.keySet().joinToString { it.presentableName })
|
||||
|
||||
try {
|
||||
val compilerRunner = JpsKotlinCompilerRunner()
|
||||
compilerRunner.runK2JvmCompiler(commonArguments, k2JvmArguments, compilerSettings, environment, moduleFile)
|
||||
} finally {
|
||||
if (System.getProperty("kotlin.jps.delete.module.file.after.build") != "false") {
|
||||
moduleFile.delete()
|
||||
}
|
||||
}
|
||||
|
||||
return environment.outputItemsCollector
|
||||
}
|
||||
|
||||
class MessageCollectorAdapter(private val context: CompileContext) : MessageCollector {
|
||||
private var hasErrors = false
|
||||
@@ -1006,7 +858,7 @@ private fun getDependenciesRecursively(module: JpsModule, kind: JpsJavaClasspath
|
||||
// TODO: investigate thread safety
|
||||
private val ALL_COMPILED_FILES_KEY = Key.create<MutableSet<File>>("_all_kotlin_compiled_files_")
|
||||
|
||||
private fun getAllCompiledFilesContainer(context: CompileContext): MutableSet<File> {
|
||||
fun getAllCompiledFilesContainer(context: CompileContext): MutableSet<File> {
|
||||
var allCompiledFiles = ALL_COMPILED_FILES_KEY.get(context)
|
||||
if (allCompiledFiles == null) {
|
||||
allCompiledFiles = THashSet(FileUtil.FILE_HASHING_STRATEGY)
|
||||
@@ -1018,7 +870,7 @@ private fun getAllCompiledFilesContainer(context: CompileContext): MutableSet<Fi
|
||||
// TODO: investigate thread safety
|
||||
private val PROCESSED_TARGETS_WITH_REMOVED_FILES = Key.create<MutableSet<ModuleBuildTarget>>("_processed_targets_with_removed_files_")
|
||||
|
||||
private fun getProcessedTargetsWithRemovedFilesContainer(context: CompileContext): MutableSet<ModuleBuildTarget> {
|
||||
fun getProcessedTargetsWithRemovedFilesContainer(context: CompileContext): MutableSet<ModuleBuildTarget> {
|
||||
var set = PROCESSED_TARGETS_WITH_REMOVED_FILES.get(context)
|
||||
if (set == null) {
|
||||
set = HashSet()
|
||||
|
||||
-197
@@ -1,197 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.jps.build
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import com.intellij.util.io.URLUtil
|
||||
import org.jetbrains.jps.ModuleChunk
|
||||
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType
|
||||
import org.jetbrains.jps.incremental.CompileContext
|
||||
import org.jetbrains.jps.incremental.ModuleBuildTarget
|
||||
import org.jetbrains.jps.incremental.ProjectBuildException
|
||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService
|
||||
import org.jetbrains.jps.model.module.JpsModule
|
||||
import org.jetbrains.jps.model.module.JpsSdkDependency
|
||||
import org.jetbrains.kotlin.build.JvmSourceRoot
|
||||
import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||
import org.jetbrains.kotlin.jps.build.JpsUtils.getAllDependencies
|
||||
import org.jetbrains.kotlin.jps.productionOutputFilePath
|
||||
import org.jetbrains.kotlin.jps.testOutputFilePath
|
||||
import org.jetbrains.kotlin.modules.KotlinModuleXmlBuilder
|
||||
import org.jetbrains.kotlin.modules.TargetId
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
object KotlinBuilderModuleScriptGenerator {
|
||||
fun getRelatedProductionModule(module: JpsModule): JpsModule? =
|
||||
JpsJavaExtensionService.getInstance().getTestModuleProperties(module)?.productionModule
|
||||
|
||||
fun generateModuleDescription(
|
||||
context: CompileContext,
|
||||
chunk: ModuleChunk,
|
||||
sourceFiles: MultiMap<ModuleBuildTarget, File>, // ignored for non-incremental compilation
|
||||
hasRemovedFiles: Boolean
|
||||
): File? {
|
||||
val builder = KotlinModuleXmlBuilder()
|
||||
|
||||
var noSources = true
|
||||
|
||||
val outputDirs = HashSet<File>()
|
||||
for (target in chunk.targets) {
|
||||
outputDirs.add(getOutputDirSafe(target))
|
||||
}
|
||||
|
||||
val logger = context.loggingManager.projectBuilderLogger
|
||||
for (target in chunk.targets) {
|
||||
val outputDir = getOutputDirSafe(target)
|
||||
val friendDirs = getAdditionalOutputDirsWhereInternalsAreVisible(target)
|
||||
|
||||
val moduleSources = ArrayList(
|
||||
if (IncrementalCompilation.isEnabled())
|
||||
sourceFiles.get(target)
|
||||
else
|
||||
KotlinSourceFileCollector.getAllKotlinSourceFiles(target)
|
||||
)
|
||||
|
||||
if (moduleSources.size > 0 || hasRemovedFiles) {
|
||||
noSources = false
|
||||
|
||||
if (logger.isEnabled) {
|
||||
logger.logCompiledFiles(moduleSources, KotlinBuilder.KOTLIN_BUILDER_NAME, "Compiling files:")
|
||||
}
|
||||
}
|
||||
|
||||
val targetType = target.targetType
|
||||
assert(targetType is JavaModuleBuildTargetType)
|
||||
val targetId = TargetId(target)
|
||||
builder.addModule(
|
||||
targetId.name,
|
||||
outputDir.absolutePath,
|
||||
moduleSources,
|
||||
findSourceRoots(context, target),
|
||||
findClassPathRoots(target),
|
||||
findModularJdkRoot(target),
|
||||
targetId.type,
|
||||
(targetType as JavaModuleBuildTargetType).isTests,
|
||||
// this excludes the output directories from the class path, to be removed for true incremental compilation
|
||||
outputDirs,
|
||||
friendDirs
|
||||
)
|
||||
}
|
||||
|
||||
if (noSources) return null
|
||||
|
||||
val scriptFile = createTempFileForModuleDesc(chunk)
|
||||
FileUtil.writeToFile(scriptFile, builder.asText().toString())
|
||||
return scriptFile
|
||||
}
|
||||
|
||||
private fun createTempFileForModuleDesc(chunk: ModuleChunk): File {
|
||||
val readableSuffix = buildString {
|
||||
append(StringUtil.sanitizeJavaIdentifier(chunk.representativeTarget().module.name))
|
||||
if (chunk.containsTests()) {
|
||||
append("-test")
|
||||
}
|
||||
}
|
||||
val dir = System.getProperty("kotlin.jps.dir.for.module.files")?.let { File(it) }?.takeIf { it.isDirectory }
|
||||
return try {
|
||||
File.createTempFile("kjps", readableSuffix + ".script.xml", dir)
|
||||
} catch (e: IOException) {
|
||||
// sometimes files cannot be created, because file name is too long (Windows, Mac OS)
|
||||
// see https://bugs.openjdk.java.net/browse/JDK-8148023
|
||||
try {
|
||||
File.createTempFile("kjps", ".script.xml", dir)
|
||||
} catch (e: IOException) {
|
||||
val message = buildString {
|
||||
append("Could not create module file when building chunk $chunk")
|
||||
if (dir != null) {
|
||||
append(" in dir $dir")
|
||||
}
|
||||
}
|
||||
throw RuntimeException(message, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getOutputDirSafe(target: ModuleBuildTarget): File {
|
||||
val explicitOutputPath = if (target.isTests) target.module.testOutputFilePath else target.module.productionOutputFilePath
|
||||
val explicitOutputDir = explicitOutputPath?.let { File(it).absoluteFile.parentFile }
|
||||
return explicitOutputDir ?: target.outputDir ?: throw ProjectBuildException("No output directory found for " + target)
|
||||
}
|
||||
|
||||
fun getProductionModulesWhichInternalsAreVisible(from: ModuleBuildTarget): List<JpsModule> {
|
||||
if (!from.isTests) return emptyList()
|
||||
|
||||
val result = SmartList<JpsModule>(from.module)
|
||||
result.addIfNotNull(getRelatedProductionModule(from.module))
|
||||
|
||||
return result.filter { it.hasProductionSourceRoot }
|
||||
}
|
||||
|
||||
fun getAdditionalOutputDirsWhereInternalsAreVisible(target: ModuleBuildTarget): List<File> {
|
||||
return getProductionModulesWhichInternalsAreVisible(target).mapNotNullTo(SmartList<File>()) {
|
||||
JpsJavaExtensionService.getInstance().getOutputDirectory(it, false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findClassPathRoots(target: ModuleBuildTarget): Collection<File> {
|
||||
return getAllDependencies(target).classes().roots.filter { file ->
|
||||
if (!file.exists()) {
|
||||
val extension = file.extension
|
||||
|
||||
// Don't filter out files, we want to report warnings about absence through the common place
|
||||
if (!(extension == "class" || extension == "jar")) {
|
||||
return@filter false
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun findModularJdkRoot(target: ModuleBuildTarget): File? {
|
||||
// List of paths to JRE modules in the following format:
|
||||
// jrt:///Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home!/java.base
|
||||
val urls = JpsJavaExtensionService.dependencies(target.module)
|
||||
.satisfying { dependency -> dependency is JpsSdkDependency }
|
||||
.classes().urls
|
||||
|
||||
val url = urls.firstOrNull { it.startsWith(StandardFileSystems.JRT_PROTOCOL_PREFIX) } ?: return null
|
||||
|
||||
return File(url.substringAfter(StandardFileSystems.JRT_PROTOCOL_PREFIX).substringBeforeLast(URLUtil.JAR_SEPARATOR))
|
||||
}
|
||||
|
||||
private fun findSourceRoots(context: CompileContext, target: ModuleBuildTarget): List<JvmSourceRoot> {
|
||||
val roots = context.projectDescriptor.buildRootIndex.getTargetRoots(target, context)
|
||||
val result = ContainerUtil.newArrayList<JvmSourceRoot>()
|
||||
for (root in roots) {
|
||||
val file = root.rootFile
|
||||
val prefix = root.packagePrefix
|
||||
if (file.exists()) {
|
||||
result.add(JvmSourceRoot(file, if (prefix.isEmpty()) null else prefix))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,7 @@ import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
|
||||
import org.jetbrains.jps.incremental.ModuleBuildTarget
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootProperties
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType
|
||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRoot
|
||||
import org.jetbrains.jps.util.JpsPathUtil
|
||||
|
||||
import java.io.File
|
||||
|
||||
@@ -52,22 +50,6 @@ object KotlinSourceFileCollector {
|
||||
.getRemovedFiles(target)
|
||||
.mapNotNull { if (FileUtilRt.extensionEquals(it, "kt")) File(it) else null }
|
||||
|
||||
fun getAllKotlinSourceFiles(target: ModuleBuildTarget): List<File> {
|
||||
val moduleExcludes = target.module.excludeRootsList.urls.mapTo(HashSet(), JpsPathUtil::urlToFile)
|
||||
|
||||
val compilerExcludes = JpsJavaExtensionService.getInstance()
|
||||
.getOrCreateCompilerConfiguration(target.module.project)
|
||||
.compilerExcludes
|
||||
|
||||
val allKotlinFiles = ArrayList<File>()
|
||||
for (sourceRoot in getRelevantSourceRoots(target)) {
|
||||
sourceRoot.file.walkTopDown()
|
||||
.onEnter { it !in moduleExcludes }
|
||||
.filterTo(allKotlinFiles) { !compilerExcludes.isExcluded(it) && it.isFile && isKotlinSourceFile(it) }
|
||||
}
|
||||
return allKotlinFiles
|
||||
}
|
||||
|
||||
private fun getRelevantSourceRoots(target: ModuleBuildTarget): Iterable<JpsModuleSourceRoot> {
|
||||
val sourceRootType = if (target.isTests) JavaSourceRootType.TEST_SOURCE else JavaSourceRootType.SOURCE
|
||||
return target.module.getSourceRoots<JavaSourceRootProperties>(sourceRootType)
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.jps.platforms
|
||||
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.jps.ModuleChunk
|
||||
import org.jetbrains.jps.builders.DirtyFilesHolder
|
||||
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
|
||||
import org.jetbrains.jps.incremental.CompileContext
|
||||
import org.jetbrains.jps.incremental.ModuleBuildTarget
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment
|
||||
import java.io.File
|
||||
|
||||
class KotlinCommonModuleBuildTarget(jpsModuleBuildTarget: ModuleBuildTarget) :
|
||||
KotlinModuleBuilderTarget(jpsModuleBuildTarget) {
|
||||
override fun compileModuleChunk(
|
||||
allCompiledFiles: MutableSet<File>,
|
||||
chunk: ModuleChunk,
|
||||
commonArguments: CommonCompilerArguments,
|
||||
context: CompileContext,
|
||||
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
|
||||
environment: JpsCompilerEnvironment,
|
||||
filesToCompile: MultiMap<ModuleBuildTarget, File>
|
||||
): Boolean {
|
||||
require(chunk.representativeTarget() == jpsModuleBuildTarget)
|
||||
if (reportAndSkipCircular(chunk, environment)) return false
|
||||
|
||||
// TODO: Compile metadata
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.jps.platforms
|
||||
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.jps.ModuleChunk
|
||||
import org.jetbrains.jps.builders.DirtyFilesHolder
|
||||
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
|
||||
import org.jetbrains.jps.incremental.CompileContext
|
||||
import org.jetbrains.jps.incremental.ModuleBuildTarget
|
||||
import org.jetbrains.jps.model.library.JpsOrderRootType
|
||||
import org.jetbrains.jps.model.module.JpsModule
|
||||
import org.jetbrains.jps.util.JpsPathUtil
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment
|
||||
import org.jetbrains.kotlin.compilerRunner.JpsKotlinCompilerRunner
|
||||
import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings
|
||||
import org.jetbrains.kotlin.jps.productionOutputFilePath
|
||||
import org.jetbrains.kotlin.jps.testOutputFilePath
|
||||
import org.jetbrains.kotlin.utils.JsLibraryUtils
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils.JS_EXT
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils.META_JS_SUFFIX
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
|
||||
class KotlinJsModuleBuildTarget(jpsModuleBuildTarget: ModuleBuildTarget) : KotlinModuleBuilderTarget(jpsModuleBuildTarget) {
|
||||
val jsCompilerArguments
|
||||
get() = JpsKotlinCompilerSettings.getK2JsCompilerArguments(module)
|
||||
|
||||
val outputFile
|
||||
get() = explicitOutputPath?.let { File(it) } ?: implicitOutputFile
|
||||
|
||||
val explicitOutputPath
|
||||
get() = if (isTests) module.testOutputFilePath else module.productionOutputFilePath
|
||||
|
||||
val implicitOutputFile: File
|
||||
get() {
|
||||
val suffix = if (isTests) "_test" else ""
|
||||
|
||||
return File(outputDir, module.name + suffix + JS_EXT)
|
||||
}
|
||||
|
||||
val outputFileBaseName: String
|
||||
get() = outputFile.path.substringBeforeLast(".")
|
||||
|
||||
val outputMetaFile: File
|
||||
get() = File(outputFileBaseName + META_JS_SUFFIX)
|
||||
|
||||
val libraryFiles: List<String>
|
||||
get() = mutableListOf<String>().also { result ->
|
||||
for (library in allDependencies.libraries) {
|
||||
for (root in library.getRoots(JpsOrderRootType.COMPILED)) {
|
||||
result.add(JpsPathUtil.urlToPath(root.url))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val dependenciesMetaFiles: List<String>
|
||||
get() = mutableListOf<String>().also { result ->
|
||||
allDependencies.processModules { module ->
|
||||
if (isTests) addDependencyMetaFile(module, result, isTests = true)
|
||||
|
||||
// production targets should be also added as dependency to test targets
|
||||
addDependencyMetaFile(module, result, isTests = false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addDependencyMetaFile(
|
||||
module: JpsModule,
|
||||
result: MutableList<String>,
|
||||
isTests: Boolean
|
||||
) {
|
||||
val dependencyBuildTarget = ModuleBuildTarget(module, isTests).kotlinData
|
||||
|
||||
if (dependencyBuildTarget != this@KotlinJsModuleBuildTarget &&
|
||||
dependencyBuildTarget is KotlinJsModuleBuildTarget &&
|
||||
dependencyBuildTarget.sources.isNotEmpty()
|
||||
) {
|
||||
val metaFile = dependencyBuildTarget.outputMetaFile
|
||||
if (metaFile.exists()) {
|
||||
result.add(metaFile.absolutePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun compileModuleChunk(
|
||||
allCompiledFiles: MutableSet<File>,
|
||||
chunk: ModuleChunk,
|
||||
commonArguments: CommonCompilerArguments,
|
||||
context: CompileContext,
|
||||
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
|
||||
environment: JpsCompilerEnvironment,
|
||||
filesToCompile: MultiMap<ModuleBuildTarget, File>
|
||||
): Boolean {
|
||||
require(chunk.representativeTarget() == jpsModuleBuildTarget)
|
||||
if (reportAndSkipCircular(chunk, environment)) return false
|
||||
|
||||
val sources = sources
|
||||
if (sources.isEmpty()) return false
|
||||
|
||||
// Compiler starts to produce path relative to base dirs in source maps if at least one statement is true:
|
||||
// 1) base dirs are specified;
|
||||
// 2) prefix is specified (i.e. non-empty)
|
||||
// Otherwise compiler produces paths relative to source maps location.
|
||||
// We don't have UI to configure base dirs, but we have UI to configure prefix.
|
||||
// If prefix is not specified (empty) in UI, we want to produce paths relative to source maps location
|
||||
val sourceRoots = if (jsCompilerArguments.sourceMapPrefix.isNullOrBlank()) {
|
||||
emptyList()
|
||||
} else {
|
||||
module.contentRootsList.urls
|
||||
.map { URI.create(it) }
|
||||
.filter { it.scheme == "file" }
|
||||
.map { File(it.path) }
|
||||
}
|
||||
|
||||
val friendPaths = friendBuildTargets.mapNotNull {
|
||||
(it as? KotlinJsModuleBuildTarget)?.outputMetaFile?.absoluteFile?.toString()
|
||||
}
|
||||
|
||||
val libraries = libraryFiles + dependenciesMetaFiles
|
||||
|
||||
val compilerRunner = JpsKotlinCompilerRunner()
|
||||
compilerRunner.runK2JsCompiler(
|
||||
commonArguments,
|
||||
jsCompilerArguments,
|
||||
compilerSettings,
|
||||
environment,
|
||||
sources,
|
||||
sourceRoots,
|
||||
libraries,
|
||||
friendPaths,
|
||||
outputFile
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun doAfterBuild() {
|
||||
copyJsLibraryFilesIfNeeded()
|
||||
}
|
||||
|
||||
private fun copyJsLibraryFilesIfNeeded() {
|
||||
if (compilerSettings.copyJsLibraryFiles) {
|
||||
val outputLibraryRuntimeDirectory = File(outputDir, compilerSettings.outputDirectoryForJsLibraryFiles).absolutePath
|
||||
JsLibraryUtils.copyJsFilesFromLibraries(
|
||||
libraryFiles, outputLibraryRuntimeDirectory,
|
||||
copySourceMap = jsCompilerArguments.sourceMap
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.jps.platforms
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import com.intellij.util.io.URLUtil
|
||||
import org.jetbrains.jps.ModuleChunk
|
||||
import org.jetbrains.jps.builders.DirtyFilesHolder
|
||||
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType
|
||||
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
|
||||
import org.jetbrains.jps.incremental.CompileContext
|
||||
import org.jetbrains.jps.incremental.ModuleBuildTarget
|
||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService
|
||||
import org.jetbrains.jps.model.module.JpsSdkDependency
|
||||
import org.jetbrains.kotlin.build.JvmSourceRoot
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment
|
||||
import org.jetbrains.kotlin.compilerRunner.JpsKotlinCompilerRunner
|
||||
import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||
import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings
|
||||
import org.jetbrains.kotlin.jps.build.*
|
||||
import org.jetbrains.kotlin.modules.KotlinModuleXmlBuilder
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
class KotlinJvmModuleBuildTarget(jpsModuleBuildTarget: ModuleBuildTarget) : KotlinModuleBuilderTarget(jpsModuleBuildTarget) {
|
||||
override fun compileModuleChunk(
|
||||
allCompiledFiles: MutableSet<File>,
|
||||
chunk: ModuleChunk,
|
||||
commonArguments: CommonCompilerArguments,
|
||||
context: CompileContext,
|
||||
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
|
||||
environment: JpsCompilerEnvironment,
|
||||
filesToCompile: MultiMap<ModuleBuildTarget, File>
|
||||
): Boolean {
|
||||
if (chunk.modules.size > 1) {
|
||||
environment.messageCollector.report(
|
||||
CompilerMessageSeverity.STRONG_WARNING,
|
||||
"Circular dependencies are only partially supported. The following modules depend on each other: "
|
||||
+ chunk.modules.joinToString(", ") { it.name } + " "
|
||||
+ "Kotlin will compile them, but some strange effect may happen"
|
||||
)
|
||||
}
|
||||
|
||||
allCompiledFiles.addAll(filesToCompile.values())
|
||||
|
||||
val processedTargetsWithRemoved = getProcessedTargetsWithRemovedFilesContainer(context)
|
||||
|
||||
var totalRemovedFiles = 0
|
||||
for (target in chunk.targets) {
|
||||
val removedFilesInTarget = KotlinSourceFileCollector.getRemovedKotlinFiles(dirtyFilesHolder, target)
|
||||
if (!removedFilesInTarget.isEmpty()) {
|
||||
if (processedTargetsWithRemoved.add(target)) {
|
||||
totalRemovedFiles += removedFilesInTarget.size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val moduleFile = generateModuleDescription(context, chunk, filesToCompile, totalRemovedFiles != 0)
|
||||
if (moduleFile == null) {
|
||||
KotlinBuilder.LOG.debug(
|
||||
"Not compiling, because no files affected: " + filesToCompile.keySet().joinToString { it.presentableName }
|
||||
)
|
||||
// No Kotlin sources found
|
||||
return false
|
||||
}
|
||||
|
||||
val module = chunk.representativeTarget().module
|
||||
val k2JvmArguments = JpsKotlinCompilerSettings.getK2JvmCompilerArguments(module)
|
||||
val compilerSettings = JpsKotlinCompilerSettings.getCompilerSettings(module)
|
||||
|
||||
KotlinBuilder.LOG.debug("Compiling to JVM ${filesToCompile.values().size} files"
|
||||
+ (if (totalRemovedFiles == 0) "" else " ($totalRemovedFiles removed files)")
|
||||
+ " in " + filesToCompile.keySet().joinToString { it.presentableName })
|
||||
|
||||
try {
|
||||
val compilerRunner = JpsKotlinCompilerRunner()
|
||||
compilerRunner.runK2JvmCompiler(commonArguments, k2JvmArguments, compilerSettings, environment, moduleFile)
|
||||
} finally {
|
||||
if (System.getProperty("kotlin.jps.delete.module.file.after.build") != "false") {
|
||||
moduleFile.delete()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun generateModuleDescription(
|
||||
context: CompileContext,
|
||||
chunk: ModuleChunk,
|
||||
sourceFiles: MultiMap<ModuleBuildTarget, File>, // ignored for non-incremental compilation
|
||||
hasRemovedFiles: Boolean
|
||||
): File? {
|
||||
val builder = KotlinModuleXmlBuilder()
|
||||
|
||||
var noSources = true
|
||||
|
||||
val targets = chunk.targets.mapNotNull { it.kotlinData as? KotlinJvmModuleBuildTarget }
|
||||
|
||||
val outputDirs = targets.map { it.outputDir }.toSet()
|
||||
|
||||
val logger = context.loggingManager.projectBuilderLogger
|
||||
for (target in targets) {
|
||||
val outputDir = target.outputDir
|
||||
val friendDirs = target.friendOutputDirs
|
||||
|
||||
val moduleSources =
|
||||
if (IncrementalCompilation.isEnabled() && target.expectedBy.isEmpty()) {
|
||||
sourceFiles.get(target.jpsModuleBuildTarget)
|
||||
} else {
|
||||
target.sources
|
||||
}
|
||||
|
||||
if (moduleSources.isNotEmpty() || hasRemovedFiles) {
|
||||
noSources = false
|
||||
|
||||
if (logger.isEnabled) {
|
||||
logger.logCompiledFiles(moduleSources, KotlinBuilder.KOTLIN_BUILDER_NAME, "Compiling files:")
|
||||
}
|
||||
}
|
||||
|
||||
val kotlinModuleId = target.moduleId
|
||||
builder.addModule(
|
||||
kotlinModuleId.name,
|
||||
outputDir.absolutePath,
|
||||
moduleSources,
|
||||
target.findSourceRoots(context),
|
||||
target.findClassPathRoots(),
|
||||
target.findModularJdkRoot(),
|
||||
kotlinModuleId.type,
|
||||
isTests,
|
||||
// this excludes the output directories from the class path, to be removed for true incremental compilation
|
||||
outputDirs,
|
||||
friendDirs
|
||||
)
|
||||
}
|
||||
|
||||
if (noSources) return null
|
||||
|
||||
val scriptFile = createTempFileForModuleDesc(chunk)
|
||||
FileUtil.writeToFile(scriptFile, builder.asText().toString())
|
||||
return scriptFile
|
||||
}
|
||||
|
||||
private fun createTempFileForModuleDesc(chunk: ModuleChunk): File {
|
||||
val readableSuffix = buildString {
|
||||
append(StringUtil.sanitizeJavaIdentifier(chunk.representativeTarget().module.name))
|
||||
if (chunk.containsTests()) {
|
||||
append("-test")
|
||||
}
|
||||
}
|
||||
val dir = System.getProperty("kotlin.jps.dir.for.module.files")?.let { File(it) }?.takeIf { it.isDirectory }
|
||||
return try {
|
||||
File.createTempFile("kjps", readableSuffix + ".script.xml", dir)
|
||||
} catch (e: IOException) {
|
||||
// sometimes files cannot be created, because file name is too long (Windows, Mac OS)
|
||||
// see https://bugs.openjdk.java.net/browse/JDK-8148023
|
||||
try {
|
||||
File.createTempFile("kjps", ".script.xml", dir)
|
||||
} catch (e: IOException) {
|
||||
val message = buildString {
|
||||
append("Could not create module file when building chunk $chunk")
|
||||
if (dir != null) {
|
||||
append(" in dir $dir")
|
||||
}
|
||||
}
|
||||
throw RuntimeException(message, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun findClassPathRoots(): Collection<File> {
|
||||
return allDependencies.classes().roots.filter { file ->
|
||||
if (!file.exists()) {
|
||||
val extension = file.extension
|
||||
|
||||
// Don't filter out files, we want to report warnings about absence through the common place
|
||||
if (!(extension == "class" || extension == "jar")) {
|
||||
return@filter false
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fun findModularJdkRoot(): File? {
|
||||
// List of paths to JRE modules in the following format:
|
||||
// jrt:///Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home!/java.base
|
||||
val urls = JpsJavaExtensionService.dependencies(module)
|
||||
.satisfying { dependency -> dependency is JpsSdkDependency }
|
||||
.classes().urls
|
||||
|
||||
val url = urls.firstOrNull { it.startsWith(StandardFileSystems.JRT_PROTOCOL_PREFIX) } ?: return null
|
||||
|
||||
return File(url.substringAfter(StandardFileSystems.JRT_PROTOCOL_PREFIX).substringBeforeLast(URLUtil.JAR_SEPARATOR))
|
||||
}
|
||||
|
||||
fun findSourceRoots(context: CompileContext): List<JvmSourceRoot> {
|
||||
val roots = context.projectDescriptor.buildRootIndex.getTargetRoots(jpsModuleBuildTarget, context)
|
||||
val result = ContainerUtil.newArrayList<JvmSourceRoot>()
|
||||
for (root in roots) {
|
||||
val file = root.rootFile
|
||||
val prefix = root.packagePrefix
|
||||
if (file.exists()) {
|
||||
result.add(JvmSourceRoot(file, if (prefix.isEmpty()) null else prefix))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.jps.platforms
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.jps.ModuleChunk
|
||||
import org.jetbrains.jps.builders.DirtyFilesHolder
|
||||
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
|
||||
import org.jetbrains.jps.incremental.CompileContext
|
||||
import org.jetbrains.jps.incremental.ModuleBuildTarget
|
||||
import org.jetbrains.jps.incremental.ProjectBuildException
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType
|
||||
import org.jetbrains.jps.model.java.JpsJavaClasspathKind
|
||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService
|
||||
import org.jetbrains.jps.model.module.JpsModule
|
||||
import org.jetbrains.jps.util.JpsPathUtil
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.compilerRunner.JpsCompilerEnvironment
|
||||
import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings
|
||||
import org.jetbrains.kotlin.jps.build.KotlinSourceFileCollector
|
||||
import org.jetbrains.kotlin.jps.model.kotlinFacetExtension
|
||||
import org.jetbrains.kotlin.jps.productionOutputFilePath
|
||||
import org.jetbrains.kotlin.jps.testOutputFilePath
|
||||
import org.jetbrains.kotlin.modules.TargetId
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.io.File
|
||||
|
||||
abstract class KotlinModuleBuilderTarget(val jpsModuleBuildTarget: ModuleBuildTarget) {
|
||||
val module: JpsModule
|
||||
get() = jpsModuleBuildTarget.module
|
||||
|
||||
val isTests: Boolean
|
||||
get() = jpsModuleBuildTarget.isTests
|
||||
|
||||
val moduleId: TargetId
|
||||
get() {
|
||||
// Since IDEA 2016 each gradle source root is imported as a separate module.
|
||||
// One gradle module X is imported as two JPS modules:
|
||||
// 1. X-production with one production target;
|
||||
// 2. X-test with one test target.
|
||||
// This breaks kotlin code since internal members' names are mangled using module name.
|
||||
// For example, a declaration of a function 'f' in 'X-production' becomes 'fXProduction', but a call 'f' in 'X-test' becomes 'fXTest()'.
|
||||
// The workaround is to replace a name of such test target with the name of corresponding production module.
|
||||
// See KT-11993.
|
||||
val name = relatedProductionModule?.name ?: jpsModuleBuildTarget.id
|
||||
return TargetId(name, jpsModuleBuildTarget.targetType.typeId)
|
||||
}
|
||||
|
||||
val kotlinFacetExtension by lazy { module.kotlinFacetExtension }
|
||||
|
||||
val outputDir by lazy {
|
||||
val explicitOutputPath = if (isTests) module.testOutputFilePath else module.productionOutputFilePath
|
||||
val explicitOutputDir = explicitOutputPath?.let { File(it).absoluteFile.parentFile }
|
||||
return@lazy explicitOutputDir
|
||||
?: jpsModuleBuildTarget.outputDir
|
||||
?: throw ProjectBuildException("No output directory found for " + this)
|
||||
}
|
||||
|
||||
val compilerSettings by lazy { JpsKotlinCompilerSettings.getCompilerSettings(module) }
|
||||
|
||||
val friendBuildTargets: List<KotlinModuleBuilderTarget>
|
||||
get() {
|
||||
val result = mutableListOf<KotlinModuleBuilderTarget>()
|
||||
|
||||
if (isTests) {
|
||||
result.addIfNotNull(module.productionBuildTarget.kotlinData)
|
||||
result.addIfNotNull(relatedProductionModule?.productionBuildTarget?.kotlinData)
|
||||
}
|
||||
|
||||
return result.filter { it.sources.isNotEmpty() }
|
||||
}
|
||||
|
||||
val friendOutputDirs: List<File>
|
||||
get() = friendBuildTargets.mapNotNull {
|
||||
JpsJavaExtensionService.getInstance().getOutputDirectory(it.module, false)
|
||||
}
|
||||
|
||||
val relatedProductionModule: JpsModule?
|
||||
get() = JpsJavaExtensionService.getInstance().getTestModuleProperties(module)?.productionModule
|
||||
|
||||
val allDependencies by lazy {
|
||||
JpsJavaExtensionService.dependencies(module).recursively().exportedOnly()
|
||||
.includedIn(JpsJavaClasspathKind.compile(isTests))
|
||||
}
|
||||
|
||||
val expectedBy by lazy(::findExpectedBy)
|
||||
|
||||
private fun findExpectedBy(): List<KotlinModuleBuilderTarget> {
|
||||
val kotlinFacetExtension = module.kotlinFacetExtension
|
||||
val implementedModuleNames = kotlinFacetExtension?.settings?.implementedModuleNames ?: return listOf()
|
||||
if (implementedModuleNames.isEmpty()) return listOf()
|
||||
|
||||
return allDependencies.modules
|
||||
.filter { it.name in implementedModuleNames }
|
||||
.map { ModuleBuildTarget(it, isTests).kotlinData!! }
|
||||
}
|
||||
|
||||
fun getRemovedKotlinFiles(dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>): List<File> =
|
||||
dirtyFilesHolder
|
||||
.getRemovedFiles(jpsModuleBuildTarget)
|
||||
.mapNotNull { if (FileUtilRt.extensionEquals(it, "kt")) File(it) else null }
|
||||
|
||||
val sources by lazy {
|
||||
mutableListOf<File>().also { result ->
|
||||
// add all common libs sources
|
||||
expectedBy.forEach {
|
||||
it.collectSources(result)
|
||||
}
|
||||
|
||||
collectSources(result)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectSources(receiver: MutableList<File>) {
|
||||
val moduleExcludes = module.excludeRootsList.urls.mapTo(java.util.HashSet(), JpsPathUtil::urlToFile)
|
||||
|
||||
val compilerExcludes = JpsJavaExtensionService.getInstance()
|
||||
.getOrCreateCompilerConfiguration(module.project)
|
||||
.compilerExcludes
|
||||
|
||||
val sourceRootType = if (isTests) JavaSourceRootType.TEST_SOURCE else JavaSourceRootType.SOURCE
|
||||
module.getSourceRoots(sourceRootType).forEach {
|
||||
it.file.walkTopDown()
|
||||
.onEnter { it !in moduleExcludes }
|
||||
.filterTo(receiver) {
|
||||
!compilerExcludes.isExcluded(it) &&
|
||||
it.isFile &&
|
||||
KotlinSourceFileCollector.isKotlinSourceFile(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() = jpsModuleBuildTarget.toString()
|
||||
|
||||
abstract fun compileModuleChunk(
|
||||
allCompiledFiles: MutableSet<File>,
|
||||
chunk: ModuleChunk,
|
||||
commonArguments: CommonCompilerArguments,
|
||||
context: CompileContext,
|
||||
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
|
||||
environment: JpsCompilerEnvironment,
|
||||
filesToCompile: MultiMap<ModuleBuildTarget, File>
|
||||
): Boolean
|
||||
|
||||
protected fun reportAndSkipCircular(
|
||||
chunk: ModuleChunk,
|
||||
environment: JpsCompilerEnvironment
|
||||
): Boolean {
|
||||
if (chunk.modules.size > 1) {
|
||||
// We do not support circular dependencies, but if they are present, we do our best should not break the build,
|
||||
// so we simply yield a warning and report NOTHING_DONE
|
||||
environment.messageCollector.report(
|
||||
CompilerMessageSeverity.STRONG_WARNING,
|
||||
"Circular dependencies are not supported. The following modules depend on each other: "
|
||||
+ chunk.modules.joinToString(", ") { it.name } + " "
|
||||
+ "Kotlin is not compiled for these modules"
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
open fun doAfterBuild() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.jps.platforms
|
||||
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType
|
||||
import org.jetbrains.jps.incremental.ModuleBuildTarget
|
||||
import org.jetbrains.jps.model.java.JpsJavaModuleType
|
||||
import org.jetbrains.jps.model.library.JpsOrderRootType
|
||||
import org.jetbrains.jps.model.module.JpsModule
|
||||
import org.jetbrains.jps.util.JpsPathUtil
|
||||
import org.jetbrains.kotlin.config.TargetPlatformKind
|
||||
import org.jetbrains.kotlin.jps.targetPlatform
|
||||
import org.jetbrains.kotlin.utils.LibraryUtils
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
fun ModuleBuildTarget(module: JpsModule, isTests: Boolean) =
|
||||
ModuleBuildTarget(module, if (isTests) JavaModuleBuildTargetType.TEST else JavaModuleBuildTargetType.PRODUCTION)
|
||||
|
||||
val JpsModule.productionBuildTarget
|
||||
get() = ModuleBuildTarget(this, false)
|
||||
|
||||
val JpsModule.testBuildTarget
|
||||
get() = ModuleBuildTarget(this, true)
|
||||
|
||||
private val kotlinBuildTargetsData = ConcurrentHashMap<ModuleBuildTarget, KotlinModuleBuilderTarget>()
|
||||
|
||||
val ModuleBuildTarget.kotlinData: KotlinModuleBuilderTarget?
|
||||
get() {
|
||||
if (module.moduleType != JpsJavaModuleType.INSTANCE) return null
|
||||
|
||||
return kotlinBuildTargetsData.computeIfAbsent(this) {
|
||||
when (module.targetPlatform ?: detectTargetPlatform()) {
|
||||
is TargetPlatformKind.Common -> KotlinCommonModuleBuildTarget(this)
|
||||
is TargetPlatformKind.JavaScript -> KotlinJsModuleBuildTarget(this)
|
||||
is TargetPlatformKind.Jvm -> KotlinJvmModuleBuildTarget(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility for KT-14082
|
||||
* todo: remove when all projects migrated to facets
|
||||
*/
|
||||
private fun ModuleBuildTarget.detectTargetPlatform(): TargetPlatformKind<*> {
|
||||
if (hasJsStdLib()) return TargetPlatformKind.JavaScript
|
||||
|
||||
return TargetPlatformKind.DEFAULT_PLATFORM
|
||||
}
|
||||
|
||||
private val IS_KOTLIN_JS_STDLIB_JAR_CACHE = ConcurrentHashMap<String, Boolean>()
|
||||
|
||||
private fun ModuleBuildTarget.hasJsStdLib(): Boolean {
|
||||
KotlinJvmModuleBuildTarget(this).allDependencies.libraries.forEach { library ->
|
||||
for (root in library.getRoots(JpsOrderRootType.COMPILED)) {
|
||||
val url = root.url
|
||||
|
||||
val isKotlinJsLib = IS_KOTLIN_JS_STDLIB_JAR_CACHE.computeIfAbsent(url) {
|
||||
LibraryUtils.isKotlinJavascriptStdLibrary(JpsPathUtil.urlToFile(url))
|
||||
}
|
||||
|
||||
if (isKotlinJsLib) return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
internal fun clearKotlinModuleBuildTargetDataBindings() {
|
||||
kotlinBuildTargetsData.clear()
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.modules
|
||||
|
||||
import org.jetbrains.jps.incremental.ModuleBuildTarget
|
||||
import org.jetbrains.kotlin.jps.build.KotlinBuilderModuleScriptGenerator.getRelatedProductionModule
|
||||
|
||||
fun TargetId(moduleBuildTarget: ModuleBuildTarget): TargetId {
|
||||
// Since IDEA 2016 each gradle source root is imported as a separate module.
|
||||
// One gradle module X is imported as two JPS modules:
|
||||
// 1. X-production with one production target;
|
||||
// 2. X-test with one test target.
|
||||
// This breaks kotlin code since internal members' names are mangled using module name.
|
||||
// For example, a declaration of a function 'f' in 'X-production' becomes 'fXProduction', but a call 'f' in 'X-test' becomes 'fXTest()'.
|
||||
// The workaround is to replace a name of such test target with the name of corresponding production module.
|
||||
// See KT-11993.
|
||||
val name = getRelatedProductionModule(moduleBuildTarget.module)?.name ?: moduleBuildTarget.id
|
||||
return TargetId(name, moduleBuildTarget.targetType.typeId)
|
||||
}
|
||||
Reference in New Issue
Block a user