Switch to 181 platform

Original commit: bc403ce744
This commit is contained in:
Vyacheslav Gerasimov
2018-04-27 18:25:17 +03:00
parent f4d5649cd5
commit 440d218436
8 changed files with 737 additions and 38 deletions
+2 -2
View File
@@ -16,7 +16,7 @@ dependencies {
compile(projectRuntimeJar(":kotlin-preloader"))
compile(project(":idea:idea-jps-common"))
compileOnly(group = "org.jetbrains", name = "annotations", version = "13.0")
compileOnly(intellijDep()) { includeJars("jdom", "trove4j", "jps-model", "openapi", "util", "asm-all") }
compileOnly(intellijDep()) { includeJars("jdom", "trove4j", "jps-model", "openapi", "platform-api", "util", "asm-all") }
compileOnly(intellijDep("jps-standalone")) { includeJars("jps-builders", "jps-builders-6") }
testCompileOnly(project(":kotlin-reflect-api"))
testCompile(project(":compiler:incremental-compilation-impl"))
@@ -26,7 +26,7 @@ dependencies {
testCompile(projectDist(":kotlin-test:kotlin-test-jvm"))
testCompile(projectTests(":kotlin-build-common"))
testCompileOnly(intellijDep("jps-standalone")) { includeJars("jps-builders", "jps-builders-6") }
testCompileOnly(intellijDep()) { includeJars("openapi", "idea", "log4j") }
testCompileOnly(intellijDep()) { includeJars("openapi", "idea", "platform-api", "log4j") }
testCompile(intellijDep("jps-build-test"))
compilerModules.forEach {
testRuntime(project(it))
+52
View File
@@ -0,0 +1,52 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
val compilerModules: Array<String> by rootProject.extra
dependencies {
compile(project(":kotlin-build-common"))
compile(project(":core:descriptors"))
compile(project(":core:descriptors.jvm"))
compile(project(":kotlin-compiler-runner"))
compile(project(":compiler:daemon-common"))
compile(projectRuntimeJar(":kotlin-daemon-client"))
compile(project(":compiler:frontend.java"))
compile(projectRuntimeJar(":kotlin-preloader"))
compile(project(":idea:idea-jps-common"))
compileOnly(group = "org.jetbrains", name = "annotations", version = "13.0")
compileOnly(intellijDep()) { includeJars("jdom", "trove4j", "jps-model", "openapi", "util", "asm-all") }
compileOnly(intellijDep("jps-standalone")) { includeJars("jps-builders", "jps-builders-6") }
testCompileOnly(project(":kotlin-reflect-api"))
testCompile(project(":compiler:incremental-compilation-impl"))
testCompile(projectTests(":compiler:tests-common"))
testCompile(projectTests(":compiler:incremental-compilation-impl"))
testCompile(commonDep("junit:junit"))
testCompile(projectDist(":kotlin-test:kotlin-test-jvm"))
testCompile(projectTests(":kotlin-build-common"))
testCompileOnly(intellijDep("jps-standalone")) { includeJars("jps-builders", "jps-builders-6") }
testCompileOnly(intellijDep()) { includeJars("openapi", "idea", "log4j") }
testCompile(intellijDep("jps-build-test"))
compilerModules.forEach {
testRuntime(project(it))
}
testRuntime(intellijDep())
testRuntime(projectDist(":kotlin-reflect"))
}
sourceSets {
"main" { projectDefault() }
"test" {
java.srcDirs("jps-tests/test"
/*, "kannotator-jps-plugin-test/test"*/ // Obsolete
)
}
}
projectTest {
dependsOn(":kotlin-compiler:dist")
workingDir = rootDir
}
testsJar {}
@@ -46,6 +46,7 @@ import org.jetbrains.jps.util.JpsPathUtil
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.incremental.CacheVersion
import org.jetbrains.kotlin.incremental.LookupSymbol
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
@@ -132,8 +133,9 @@ abstract class AbstractIncrementalJpsTest(
// JPS forces rebuild of all files when JVM constant has been changed and Callbacks.ConstantAffectionResolver
// is not provided, so ConstantAffectionResolver is mocked with empty implementation
protected open val mockConstantSearch: Callbacks.ConstantAffectionResolver?
get() = MockConstantSearch(workDir)
// Usages in Kotlin files are expected to be found by KotlinLookupConstantSearch
private val mockConstantSearch: Callbacks.ConstantAffectionResolver?
get() = MockJavaConstantSearch(workDir)
private fun build(scope: CompileScopeTestBuilder = CompileScopeTestBuilder.make().allModules()): MakeResult {
val workDirPath = FileUtil.toSystemIndependentName(workDir.absolutePath)
@@ -493,7 +495,16 @@ abstract class AbstractIncrementalJpsTest(
}
}
private class MockConstantSearch(private val workDir: File) : Callbacks.ConstantAffectionResolver {
/**
* Mocks Intellij Java constant search.
* When JPS is run from Intellij, it sends find usages request to IDE (it only searches for references inside Java files).
*
* We rely on heuristics instead of precise usages search.
* A Java file is considered affected if:
* 1. It contains changed field name as a content substring.
* 2. Its simple file name is not equal to a field's owner class simple name (to avoid recompiling field's declaration again)
*/
private class MockJavaConstantSearch(private val workDir: File) : Callbacks.ConstantAffectionResolver {
override fun request(
ownerClassName: String,
fieldName: String,
@@ -501,13 +512,19 @@ private class MockConstantSearch(private val workDir: File) : Callbacks.Constant
fieldRemoved: Boolean,
accessChanged: Boolean
): Future<Callbacks.ConstantAffection> {
val affectedFiles = workDir.walk().filter { it.isFile && it.isNameUsage() }
return FixedFuture(Callbacks.ConstantAffection(affectedFiles.toList()))
}
fun File.isAffected(): Boolean {
if (!isJavaFile()) return false
private fun File.isNameUsage(): Boolean =
name.equals("usage.kt", ignoreCase = true)
|| name.equals("usage.java", ignoreCase = true)
if (nameWithoutExtension == ownerClassName.substringAfterLast(".")) return false
val code = readText()
return code.contains(fieldName)
}
val affectedJavaFiles = workDir.walk().filter(File::isAffected).toList()
return FixedFuture(Callbacks.ConstantAffection(affectedJavaFiles))
}
}
internal val ProjectDescriptor.allModuleTargets: Collection<ModuleBuildTarget>
@@ -0,0 +1,521 @@
/*
* 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.Disposable
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.testFramework.TestLoggerFactory
import com.intellij.testFramework.UsefulTestCase
import com.intellij.util.concurrency.FixedFuture
import junit.framework.TestCase
import org.apache.log4j.ConsoleAppender
import org.apache.log4j.Level
import org.apache.log4j.Logger
import org.apache.log4j.PatternLayout
import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.api.CanceledStatus
import org.jetbrains.jps.builders.BuildResult
import org.jetbrains.jps.builders.CompileScopeTestBuilder
import org.jetbrains.jps.builders.impl.BuildDataPathsImpl
import org.jetbrains.jps.builders.impl.logging.ProjectBuilderLoggerBase
import org.jetbrains.jps.builders.java.dependencyView.Callbacks
import org.jetbrains.jps.builders.logging.BuildLoggingManager
import org.jetbrains.jps.cmdline.ProjectDescriptor
import org.jetbrains.jps.incremental.*
import org.jetbrains.jps.incremental.messages.BuildMessage
import org.jetbrains.jps.model.JpsModuleRootModificationUtil
import org.jetbrains.jps.model.java.JpsJavaDependencyScope
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.util.JpsPathUtil
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.incremental.CacheVersion
import org.jetbrains.kotlin.incremental.LookupSymbol
import org.jetbrains.kotlin.incremental.testingUtils.*
import org.jetbrains.kotlin.jps.incremental.getKotlinCache
import org.jetbrains.kotlin.jps.incremental.withLookupStorage
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.utils.keysToMap
import java.io.*
import java.util.*
import java.util.concurrent.Future
import kotlin.reflect.jvm.javaField
abstract class AbstractIncrementalJpsTest(
private val allowNoFilesWithSuffixInTestData: Boolean = false,
private val checkDumpsCaseInsensitively: Boolean = false,
private val allowNoBuildLogFileInTestData: Boolean = false
) : BaseKotlinJpsBuildTestCase() {
companion object {
private val COMPILATION_FAILED = "COMPILATION FAILED"
// change to "/tmp" or anything when default is too long (for easier debugging)
private val TEMP_DIRECTORY_TO_USE = File(FileUtilRt.getTempDirectory())
private val DEBUG_LOGGING_ENABLED = System.getProperty("debug.logging.enabled") == "true"
}
protected lateinit var testDataDir: File
protected lateinit var workDir: File
protected lateinit var projectDescriptor: ProjectDescriptor
// is used to compare lookup dumps in a human readable way (lookup symbols are hashed in an actual lookup storage)
protected lateinit var lookupsDuringTest: MutableSet<LookupSymbol>
private var isICEnabledBackup: Boolean = false
protected var mapWorkingToOriginalFile: MutableMap<File, File> = hashMapOf()
protected open val buildLogFinder: BuildLogFinder
get() = BuildLogFinder()
private fun enableDebugLogging() {
com.intellij.openapi.diagnostic.Logger.setFactory(TestLoggerFactory::class.java)
TestLoggerFactory.dumpLogToStdout("")
TestLoggerFactory.enableDebugLogging(testRootDisposable, "#org")
val console = ConsoleAppender()
console.layout = PatternLayout("%d [%p|%c|%C{1}] %m%n")
console.threshold = Level.ALL
console.activateOptions()
Logger.getRootLogger().addAppender(console)
}
private var systemPropertiesBackup = run {
val props = System.getProperties()
val output = ByteArrayOutputStream()
props.store(output, "System properties backup")
output.toByteArray()
}
private fun restoreSystemProperties() {
val input = ByteArrayInputStream(systemPropertiesBackup)
val props = Properties()
props.load(input)
System.setProperties(props)
}
override fun setUp() {
super.setUp()
lookupsDuringTest = hashSetOf()
isICEnabledBackup = IncrementalCompilation.isEnabled()
IncrementalCompilation.setIsEnabled(true)
if (DEBUG_LOGGING_ENABLED) {
enableDebugLogging()
}
}
override fun tearDown() {
restoreSystemProperties()
(AbstractIncrementalJpsTest::myProject).javaField!![this] = null
(AbstractIncrementalJpsTest::projectDescriptor).javaField!![this] = null
(AbstractIncrementalJpsTest::systemPropertiesBackup).javaField!![this] = null
lookupsDuringTest.clear()
IncrementalCompilation.setIsEnabled(isICEnabledBackup)
super.tearDown()
}
// JPS forces rebuild of all files when JVM constant has been changed and Callbacks.ConstantAffectionResolver
// is not provided, so ConstantAffectionResolver is mocked with empty implementation
protected open val mockConstantSearch: Callbacks.ConstantAffectionResolver?
get() = MockConstantSearch(workDir)
private fun build(scope: CompileScopeTestBuilder = CompileScopeTestBuilder.make().allModules()): MakeResult {
val workDirPath = FileUtil.toSystemIndependentName(workDir.absolutePath)
val logger = MyLogger(workDirPath)
projectDescriptor = createProjectDescriptor(BuildLoggingManager(logger))
val lookupTracker = TestLookupTracker()
projectDescriptor.project.setTestingContext(TestingContext(lookupTracker, logger))
try {
val builder = IncProjectBuilder(projectDescriptor, BuilderRegistry.getInstance(), myBuildParams, CanceledStatus.NULL, mockConstantSearch, true)
val buildResult = BuildResult()
builder.addMessageHandler(buildResult)
builder.build(scope.build(), false)
lookupTracker.lookups.mapTo(lookupsDuringTest) { LookupSymbol(it.name, it.scopeFqName) }
if (!buildResult.isSuccessful) {
val errorMessages =
buildResult
.getMessages(BuildMessage.Kind.ERROR)
.map { it.messageText }
.map { it.replace("^.+:\\d+:\\s+".toRegex(), "").trim() }
.joinToString("\n")
return MakeResult(logger.log + "$COMPILATION_FAILED\n" + errorMessages + "\n", true, null)
}
else {
return MakeResult(logger.log, false, createMappingsDump(projectDescriptor))
}
}
finally {
projectDescriptor.dataManager.flush(false)
projectDescriptor.release()
}
}
private fun initialMake(): MakeResult {
val makeResult = build()
val initBuildLogFile = File(testDataDir, "init-build.log")
if (initBuildLogFile.exists()) {
UsefulTestCase.assertSameLinesWithFile(initBuildLogFile.absolutePath, makeResult.log)
}
else {
assertFalse("Initial make failed:\n$makeResult", makeResult.makeFailed)
}
return makeResult
}
private fun make(): MakeResult {
return build()
}
private fun rebuild(): MakeResult {
return build(CompileScopeTestBuilder.rebuild().allModules())
}
private fun rebuildAndCheckOutput(makeOverallResult: MakeResult) {
val outDir = File(getAbsolutePath("out"))
val outAfterMake = File(getAbsolutePath("out-after-make"))
if (outDir.exists()) {
FileUtil.copyDir(outDir, outAfterMake)
}
val rebuildResult = rebuild()
assertEquals("Rebuild failed: ${rebuildResult.makeFailed}, last make failed: ${makeOverallResult.makeFailed}. Rebuild result: $rebuildResult",
rebuildResult.makeFailed, makeOverallResult.makeFailed)
if (!outAfterMake.exists()) {
assertFalse(outDir.exists())
}
else {
assertEqualDirectories(outDir, outAfterMake, makeOverallResult.makeFailed)
}
if (!makeOverallResult.makeFailed) {
if (checkDumpsCaseInsensitively && rebuildResult.mappingsDump?.toLowerCase() == makeOverallResult.mappingsDump?.toLowerCase()) {
// do nothing
}
else {
TestCase.assertEquals(rebuildResult.mappingsDump, makeOverallResult.mappingsDump)
}
}
FileUtil.delete(outAfterMake)
}
private fun clearCachesRebuildAndCheckOutput(makeOverallResult: MakeResult) {
FileUtil.delete(BuildDataPathsImpl(myDataStorageRoot).dataStorageRoot!!)
rebuildAndCheckOutput(makeOverallResult)
}
private fun readModuleDependencies(): Map<String, List<DependencyDescriptor>>? {
val dependenciesTxt = File(testDataDir, "dependencies.txt")
if (!dependenciesTxt.exists()) return null
val result = HashMap<String, List<DependencyDescriptor>>()
for (line in dependenciesTxt.readLines()) {
val split = line.split("->")
val module = split[0]
val dependencies = if (split.size > 1) split[1] else ""
val dependencyList = dependencies.split(",").filterNot { it.isEmpty() }
result[module] = dependencyList.map(::parseDependency)
}
return result
}
protected open fun createBuildLog(incrementalMakeResults: List<AbstractIncrementalJpsTest.MakeResult>): String =
buildString {
incrementalMakeResults.forEachIndexed { i, makeResult ->
if (i > 0) append("\n")
append("================ Step #${i + 1} =================\n\n")
append(makeResult.log)
}
}
protected open fun doTest(testDataPath: String) {
testDataDir = File(testDataPath)
workDir = FileUtilRt.createTempDirectory(TEMP_DIRECTORY_TO_USE, "jps-build", null)
Disposer.register(testRootDisposable, Disposable { FileUtilRt.delete(workDir) })
val moduleNames = configureModules()
initialMake()
val otherMakeResults = performModificationsAndMake(moduleNames)
val buildLogFile = buildLogFinder.findBuildLog(testDataDir)
val logs = createBuildLog(otherMakeResults)
if (buildLogFile != null && buildLogFile.exists()) {
UsefulTestCase.assertSameLinesWithFile(buildLogFile.absolutePath, logs)
}
else if (!allowNoBuildLogFileInTestData) {
throw IllegalStateException("No build log file in $testDataDir")
}
val lastMakeResult = otherMakeResults.last()
rebuildAndCheckOutput(lastMakeResult)
clearCachesRebuildAndCheckOutput(lastMakeResult)
}
private fun createMappingsDump(project: ProjectDescriptor) =
createKotlinIncrementalCacheDump(project) + "\n\n\n" +
createLookupCacheDump(project) + "\n\n\n" +
createCommonMappingsDump(project) + "\n\n\n" +
createJavaMappingsDump(project)
private fun createKotlinIncrementalCacheDump(project: ProjectDescriptor): String {
return buildString {
for (target in project.allModuleTargets.sortedBy { it.presentableName }) {
append("<target $target>\n")
append(project.dataManager.getKotlinCache(target).dump())
append("</target $target>\n\n\n")
}
}
}
private fun createLookupCacheDump(project: ProjectDescriptor): String {
val sb = StringBuilder()
val p = Printer(sb)
p.println("Begin of Lookup Maps")
p.println()
project.dataManager.withLookupStorage { lookupStorage ->
lookupStorage.forceGC()
p.print(lookupStorage.dump(lookupsDuringTest))
}
p.println()
p.println("End of Lookup Maps")
return sb.toString()
}
private fun createCommonMappingsDump(project: ProjectDescriptor): String {
val resultBuf = StringBuilder()
val result = Printer(resultBuf)
result.println("Begin of SourceToOutputMap")
result.pushIndent()
for (target in project.allModuleTargets) {
result.println(target)
result.pushIndent()
val mapping = project.dataManager.getSourceToOutputMap(target)
mapping.sources.sorted().forEach {
val outputs = mapping.getOutputs(it)!!.sorted()
if (outputs.isNotEmpty()) {
result.println("source $it -> $outputs")
}
}
result.popIndent()
}
result.popIndent()
result.println("End of SourceToOutputMap")
return resultBuf.toString()
}
private fun createJavaMappingsDump(project: ProjectDescriptor): String {
val byteArrayOutputStream = ByteArrayOutputStream()
PrintStream(byteArrayOutputStream).use {
project.dataManager.mappings.toStream(it)
}
return byteArrayOutputStream.toString()
}
protected data class MakeResult(val log: String, val makeFailed: Boolean, val mappingsDump: String?)
private fun performModificationsAndMake(moduleNames: Set<String>?): List<MakeResult> {
val results = arrayListOf<MakeResult>()
val modifications = getModificationsToPerform(testDataDir, moduleNames, allowNoFilesWithSuffixInTestData, TouchPolicy.TIMESTAMP)
for (step in modifications) {
step.forEach { it.perform(workDir, mapWorkingToOriginalFile) }
performAdditionalModifications(step)
if (moduleNames == null) {
preProcessSources(File(workDir, "src"))
}
else {
moduleNames.forEach { preProcessSources(File(workDir, "$it/src")) }
}
results.add(make())
}
return results
}
protected open fun performAdditionalModifications(modifications: List<Modification>) {
}
// null means one module
private fun configureModules(): Set<String>? {
fun prepareModuleSources(moduleName: String?) {
val sourceDirName = moduleName?.let { "$it/src" } ?: "src"
val filePrefix = moduleName?.let { "${it}_" } ?: ""
val sourceDestinationDir = File(workDir, sourceDirName)
val sourcesMapping = copyTestSources(testDataDir, sourceDestinationDir, filePrefix)
mapWorkingToOriginalFile.putAll(sourcesMapping)
preProcessSources(sourceDestinationDir)
}
JpsJavaExtensionService.getInstance().getOrCreateProjectExtension(myProject).outputUrl = JpsPathUtil.pathToUrl(getAbsolutePath("out"))
val jdk = addJdk("my jdk")
val moduleDependencies = readModuleDependencies()
mapWorkingToOriginalFile = hashMapOf()
val moduleNames: Set<String>?
if (moduleDependencies == null) {
addModule("module", arrayOf(getAbsolutePath("src")), null, null, jdk)
prepareModuleSources(moduleName = null)
moduleNames = null
}
else {
val nameToModule = moduleDependencies.keys
.keysToMap { addModule(it, arrayOf(getAbsolutePath("$it/src")), null, null, jdk)!! }
for ((moduleName, dependencies) in moduleDependencies) {
val module = nameToModule[moduleName]!!
for (dependency in dependencies) {
JpsModuleRootModificationUtil.addDependency(module, nameToModule[dependency.name],
JpsJavaDependencyScope.COMPILE, dependency.exported)
}
}
for (module in nameToModule.values) {
prepareModuleSources(module.name)
}
moduleNames = nameToModule.keys
}
AbstractKotlinJpsBuildTestCase.addKotlinStdlibDependency(myProject)
AbstractKotlinJpsBuildTestCase.addKotlinTestDependency(myProject)
return moduleNames
}
protected open fun preProcessSources(srcDir: File) {
}
override fun doGetProjectDir(): File? = workDir
private class MyLogger(val rootPath: String) : ProjectBuilderLoggerBase(), BuildLogger {
private val markedDirtyBeforeRound = ArrayList<File>()
private val markedDirtyAfterRound = ArrayList<File>()
override fun actionsOnCacheVersionChanged(actions: List<CacheVersion.Action>) {
if (actions.size > 1 && actions.any { it != CacheVersion.Action.DO_NOTHING }) {
logLine("Actions after cache changed: $actions")
}
}
override fun markedAsDirtyBeforeRound(files: Iterable<File>) {
markedDirtyBeforeRound.addAll(files)
}
override fun markedAsDirtyAfterRound(files: Iterable<File>) {
markedDirtyAfterRound.addAll(files)
}
override fun buildStarted(context: CompileContext, chunk: ModuleChunk) {
if (!chunk.isDummy(context) && context.projectDescriptor.project.modules.size > 1) {
logLine("Building ${chunk.modules.sortedBy { it.name }.joinToString { it.name }}")
}
}
override fun afterBuildStarted(context: CompileContext, chunk: ModuleChunk) {
logDirtyFiles(markedDirtyBeforeRound)
}
override fun buildFinished(exitCode: ModuleLevelBuilder.ExitCode) {
logDirtyFiles(markedDirtyAfterRound)
logLine("Exit code: $exitCode")
logLine("------------------------------------------")
}
private fun logDirtyFiles(files: MutableList<File>) {
if (files.isEmpty()) return
logLine("Marked as dirty by Kotlin:")
files.apply {
map { FileUtil.toSystemIndependentName(it.path) }
.sorted()
.forEach { logLine(it) }
clear()
}
}
private val logBuf = StringBuilder()
val log: String
get() = logBuf.toString()
val compiledFiles = hashSetOf<File>()
override fun isEnabled(): Boolean = true
override fun logCompiledFiles(files: MutableCollection<File>?, builderName: String?, description: String?) {
super.logCompiledFiles(files, builderName, description)
if (builderName == KotlinBuilder.KOTLIN_BUILDER_NAME) {
compiledFiles.addAll(files!!)
}
}
override fun logLine(message: String?) {
logBuf.append(KotlinTestUtils.replaceHashWithStar(message!!.replace("^$rootPath/".toRegex(), " "))).append('\n')
}
}
}
private class MockConstantSearch(private val workDir: File) : Callbacks.ConstantAffectionResolver {
override fun request(
ownerClassName: String,
fieldName: String,
accessFlags: Int,
fieldRemoved: Boolean,
accessChanged: Boolean
): Future<Callbacks.ConstantAffection> {
val affectedFiles = workDir.walk().filter { it.isFile && it.isNameUsage() }
return FixedFuture(Callbacks.ConstantAffection(affectedFiles.toList()))
}
private fun File.isNameUsage(): Boolean =
name.equals("usage.kt", ignoreCase = true)
|| name.equals("usage.java", ignoreCase = true)
}
internal val ProjectDescriptor.allModuleTargets: Collection<ModuleBuildTarget>
get() = buildTargetIndex.allTargets.filterIsInstance<ModuleBuildTarget>()
private class DependencyDescriptor(val name: String, val exported: Boolean)
private fun parseDependency(dependency: String): DependencyDescriptor =
DependencyDescriptor(dependency.removeSuffix(EXPORTED_SUFFIX), dependency.endsWith(EXPORTED_SUFFIX))
private val EXPORTED_SUFFIX = "[exported]"
@@ -16,11 +16,6 @@
package org.jetbrains.kotlin.jps.build
import org.jetbrains.jps.builders.java.dependencyView.Callbacks
import com.intellij.util.concurrency.FixedFuture
import java.io.File
import java.util.concurrent.Future
class IncrementalConstantSearchTest : AbstractIncrementalJpsTest() {
fun testJavaConstantChangedUsedInKotlin() {
doTest("jps-plugin/testData/incremental/custom/javaConstantChangedUsedInKotlin/")
@@ -45,26 +40,4 @@ class IncrementalConstantSearchTest : AbstractIncrementalJpsTest() {
fun testKotlinJvmFieldUnchangedUsedInJava() {
doTest("jps-plugin/testData/incremental/custom/kotlinJvmFieldUnchangedUsedInJava/")
}
override val mockConstantSearch: Callbacks.ConstantAffectionResolver?
get() = object : Callbacks.ConstantAffectionResolver {
override fun request(
ownerClassName: String?,
fieldName: String?,
accessFlags: Int,
fieldRemoved: Boolean,
accessChanged: Boolean
): Future<Callbacks.ConstantAffection> {
// We emulate how constant affection service works in IDEA:
// it is able to find Kotlin usages of Java constant, but can't find Java usages of Kotlin constant
val affectedFiles =
when {
ownerClassName == "JavaClass" && fieldName == "CONST" -> listOf(File(workDir, "src/usage.kt"))
ownerClassName == "test.Klass" && fieldName == "CONST" -> listOf(File(workDir, "src/Usage.java"))
else -> emptyList()
}
return FixedFuture(Callbacks.ConstantAffection(affectedFiles))
}
}
}
@@ -0,0 +1,70 @@
/*
* 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 org.jetbrains.jps.builders.java.dependencyView.Callbacks
import com.intellij.util.concurrency.FixedFuture
import java.io.File
import java.util.concurrent.Future
class IncrementalConstantSearchTest : AbstractIncrementalJpsTest() {
fun testJavaConstantChangedUsedInKotlin() {
doTest("jps-plugin/testData/incremental/custom/javaConstantChangedUsedInKotlin/")
}
fun testJavaConstantUnchangedUsedInKotlin() {
doTest("jps-plugin/testData/incremental/custom/javaConstantUnchangedUsedInKotlin/")
}
fun testKotlinConstantChangedUsedInJava() {
doTest("jps-plugin/testData/incremental/custom/kotlinConstantChangedUsedInJava/")
}
fun testKotlinJvmFieldChangedUsedInJava() {
doTest("jps-plugin/testData/incremental/custom/kotlinJvmFieldChangedUsedInJava/")
}
fun testKotlinConstantUnchangedUsedInJava() {
doTest("jps-plugin/testData/incremental/custom/kotlinConstantUnchangedUsedInJava/")
}
fun testKotlinJvmFieldUnchangedUsedInJava() {
doTest("jps-plugin/testData/incremental/custom/kotlinJvmFieldUnchangedUsedInJava/")
}
override val mockConstantSearch: Callbacks.ConstantAffectionResolver?
get() = object : Callbacks.ConstantAffectionResolver {
override fun request(
ownerClassName: String?,
fieldName: String?,
accessFlags: Int,
fieldRemoved: Boolean,
accessChanged: Boolean
): Future<Callbacks.ConstantAffection> {
// We emulate how constant affection service works in IDEA:
// it is able to find Kotlin usages of Java constant, but can't find Java usages of Kotlin constant
val affectedFiles =
when {
ownerClassName == "JavaClass" && fieldName == "CONST" -> listOf(File(workDir, "src/usage.kt"))
ownerClassName == "test.Klass" && fieldName == "CONST" -> listOf(File(workDir, "src/Usage.java"))
else -> emptyList()
}
return FixedFuture(Callbacks.ConstantAffection(affectedFiles))
}
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.jps.build.KotlinJavaBuilderExtension
@@ -0,0 +1,65 @@
/*
* Copyright 2000-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.build
import org.jetbrains.jps.api.BasicFuture
import org.jetbrains.jps.builders.java.JavaBuilderExtension
import org.jetbrains.jps.builders.java.dependencyView.Callbacks
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.kotlin.incremental.LookupSymbol
import org.jetbrains.kotlin.jps.incremental.withLookupStorage
import java.io.File
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
class KotlinJavaBuilderExtension : JavaBuilderExtension() {
override fun getConstantSearch(context: CompileContext): Callbacks.ConstantAffectionResolver {
return KotlinLookupConstantSearch(context)
}
}
private class KotlinLookupConstantSearch(context: CompileContext) : Callbacks.ConstantAffectionResolver {
private val pool = Executors.newSingleThreadExecutor()
private val dataManager = context.projectDescriptor.dataManager
override fun request(
ownerClassName: String,
fieldName: String,
accessFlags: Int,
fieldRemoved: Boolean,
accessChanged: Boolean
): Future<Callbacks.ConstantAffection> {
val future = object : BasicFuture<Callbacks.ConstantAffection>() {
@Volatile
private var result: Callbacks.ConstantAffection = Callbacks.ConstantAffection.EMPTY
fun result(files: Collection<File>) {
result = Callbacks.ConstantAffection(files)
setDone()
}
override fun get(): Callbacks.ConstantAffection {
super.get()
return result
}
override fun get(timeout: Long, unit: TimeUnit): Callbacks.ConstantAffection {
super.get(timeout, unit)
return result
}
}
pool.submit {
if (!future.isCancelled) {
dataManager.withLookupStorage { storage ->
val paths = storage.get(LookupSymbol(name = fieldName, scope = ownerClassName))
future.result(paths.map { File(it) })
}
}
}
return future
}
}