Avoid rebuilds after changes of layouts xml-files
This commit makes IC react more granularly on these changes Precisely, it marks dirty only kt-files that having lookups into synthetic package built upon a changed layout-file #KT-21622 Fixed
This commit is contained in:
+1
-1
@@ -221,7 +221,7 @@ object TopDownAnalyzerFacadeForJVM {
|
|||||||
|
|
||||||
// TODO: consider putting extension package fragment providers into the dependency module
|
// TODO: consider putting extension package fragment providers into the dependency module
|
||||||
PackageFragmentProviderExtension.getInstances(project).mapNotNullTo(additionalProviders) { extension ->
|
PackageFragmentProviderExtension.getInstances(project).mapNotNullTo(additionalProviders) { extension ->
|
||||||
extension.getPackageFragmentProvider(project, module, storageManager, trace, null)
|
extension.getPackageFragmentProvider(project, module, storageManager, trace, null, lookupTracker)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove dependencyModule from friends
|
// TODO: remove dependencyModule from friends
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.context.ModuleContext
|
|||||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||||
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
|
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||||
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
|
|
||||||
import org.jetbrains.kotlin.frontend.java.di.createContainerForLazyResolveWithJava
|
import org.jetbrains.kotlin.frontend.java.di.createContainerForLazyResolveWithJava
|
||||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||||
import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl
|
import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl
|
||||||
@@ -86,6 +85,7 @@ object JvmAnalyzerFacade : AnalyzerFacade() {
|
|||||||
|
|
||||||
val trace = CodeAnalyzerInitializer.getInstance(project).createTrace()
|
val trace = CodeAnalyzerInitializer.getInstance(project).createTrace()
|
||||||
|
|
||||||
|
val lookupTracker = LookupTracker.DO_NOTHING
|
||||||
val container = createContainerForLazyResolveWithJava(
|
val container = createContainerForLazyResolveWithJava(
|
||||||
moduleContext,
|
moduleContext,
|
||||||
trace,
|
trace,
|
||||||
@@ -93,7 +93,7 @@ object JvmAnalyzerFacade : AnalyzerFacade() {
|
|||||||
moduleContentScope,
|
moduleContentScope,
|
||||||
moduleClassResolver,
|
moduleClassResolver,
|
||||||
targetEnvironment,
|
targetEnvironment,
|
||||||
LookupTracker.DO_NOTHING,
|
lookupTracker,
|
||||||
packagePartProvider,
|
packagePartProvider,
|
||||||
jvmTarget,
|
jvmTarget,
|
||||||
languageVersionSettings,
|
languageVersionSettings,
|
||||||
@@ -107,8 +107,13 @@ object JvmAnalyzerFacade : AnalyzerFacade() {
|
|||||||
resolveSession.packageFragmentProvider,
|
resolveSession.packageFragmentProvider,
|
||||||
javaDescriptorResolver.packageFragmentProvider)
|
javaDescriptorResolver.packageFragmentProvider)
|
||||||
|
|
||||||
providersForModule += PackageFragmentProviderExtension.getInstances(project)
|
providersForModule +=
|
||||||
.mapNotNull { it.getPackageFragmentProvider(project, moduleDescriptor, moduleContext.storageManager, trace, moduleInfo) }
|
PackageFragmentProviderExtension.getInstances(project)
|
||||||
|
.mapNotNull {
|
||||||
|
it.getPackageFragmentProvider(
|
||||||
|
project, moduleDescriptor, moduleContext.storageManager, trace, moduleInfo, lookupTracker
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return ResolverForModule(CompositePackageFragmentProvider(providersForModule), container)
|
return ResolverForModule(CompositePackageFragmentProvider(providersForModule), container)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -18,8 +18,10 @@ package org.jetbrains.kotlin.resolve.jvm.extensions
|
|||||||
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
|
||||||
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
||||||
|
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
import org.jetbrains.kotlin.storage.StorageManager
|
import org.jetbrains.kotlin.storage.StorageManager
|
||||||
|
|
||||||
@@ -34,6 +36,7 @@ interface PackageFragmentProviderExtension {
|
|||||||
module: ModuleDescriptor,
|
module: ModuleDescriptor,
|
||||||
storageManager: StorageManager,
|
storageManager: StorageManager,
|
||||||
trace: BindingTrace,
|
trace: BindingTrace,
|
||||||
moduleInfo: ModuleInfo?
|
moduleInfo: ModuleInfo?,
|
||||||
|
lookupTracker: LookupTracker
|
||||||
): PackageFragmentProvider?
|
): PackageFragmentProvider?
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-3
@@ -212,10 +212,9 @@ class IncrementalJvmCompilerRunner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((changedFiles.modified + changedFiles.removed).any { it.extension.toLowerCase() == "xml" }) {
|
val androidLayoutChanges = processLookupSymbolsForAndroidLayouts(changedFiles)
|
||||||
return CompilationMode.Rebuild { "XML resource files were changed" }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
markDirtyBy(androidLayoutChanges)
|
||||||
markDirtyBy(classpathChanges.lookupSymbols)
|
markDirtyBy(classpathChanges.lookupSymbols)
|
||||||
markDirtyBy(classpathChanges.fqNames)
|
markDirtyBy(classpathChanges.fqNames)
|
||||||
|
|
||||||
@@ -260,6 +259,17 @@ class IncrementalJvmCompilerRunner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun processLookupSymbolsForAndroidLayouts(changedFiles: ChangedFiles.Known): Collection<LookupSymbol> {
|
||||||
|
val result = mutableListOf<LookupSymbol>()
|
||||||
|
for (file in changedFiles.modified + changedFiles.removed) {
|
||||||
|
if (file.extension.toLowerCase() != "xml") continue
|
||||||
|
val layoutName = file.name.substringBeforeLast('.')
|
||||||
|
result.add(LookupSymbol(ANDROID_LAYOUT_CONTENT_LOOKUP_NAME, layoutName))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
private fun getClasspathChanges(
|
private fun getClasspathChanges(
|
||||||
modifiedClasspath: List<File>,
|
modifiedClasspath: List<File>,
|
||||||
lastBuildInfo: BuildInfo?
|
lastBuildInfo: BuildInfo?
|
||||||
|
|||||||
@@ -35,8 +35,14 @@ fun LookupTracker.record(from: LookupLocation, scopeOwner: ClassDescriptor, name
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun LookupTracker.record(from: LookupLocation, scopeOwner: PackageFragmentDescriptor, name: Name) {
|
fun LookupTracker.record(from: LookupLocation, scopeOwner: PackageFragmentDescriptor, name: Name) {
|
||||||
|
recordPackageLookup(from, scopeOwner.fqName.asString(), name.asString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LookupTracker.recordPackageLookup(from: LookupLocation, packageFqName: String, name: String) {
|
||||||
if (this === LookupTracker.DO_NOTHING) return
|
if (this === LookupTracker.DO_NOTHING) return
|
||||||
val location = from.location ?: return
|
val location = from.location ?: return
|
||||||
val position = if (requiresPosition) location.position else Position.NO_POSITION
|
val position = if (requiresPosition) location.position else Position.NO_POSITION
|
||||||
record(location.filePath, position, scopeOwner.fqName.asString(), ScopeKind.PACKAGE, name.asString())
|
record(location.filePath, position, packageFqName, ScopeKind.PACKAGE, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const val ANDROID_LAYOUT_CONTENT_LOOKUP_NAME = "<LAYOUT-CONTENT>"
|
||||||
|
|||||||
+27
@@ -1,6 +1,7 @@
|
|||||||
package org.jetbrains.kotlin.gradle
|
package org.jetbrains.kotlin.gradle
|
||||||
|
|
||||||
import org.jetbrains.kotlin.gradle.util.getFileByName
|
import org.jetbrains.kotlin.gradle.util.getFileByName
|
||||||
|
import org.jetbrains.kotlin.gradle.util.getFilesByNames
|
||||||
import org.jetbrains.kotlin.gradle.util.isLegacyAndroidGradleVersion
|
import org.jetbrains.kotlin.gradle.util.isLegacyAndroidGradleVersion
|
||||||
import org.jetbrains.kotlin.gradle.util.modify
|
import org.jetbrains.kotlin.gradle.util.modify
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -212,6 +213,32 @@ fun getSomething() = 10
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAndroidExtensionsIncremental() {
|
||||||
|
val project = Project("AndroidExtensionsProject", gradleVersion)
|
||||||
|
val options = defaultBuildOptions().copy(incremental = true)
|
||||||
|
|
||||||
|
project.build("assembleDebug", options = options) {
|
||||||
|
assertSuccessful()
|
||||||
|
val affectedSources = project.projectDir.getFilesByNames(
|
||||||
|
"MyActivity.kt", "noLayoutUsages.kt"
|
||||||
|
)
|
||||||
|
val relativePaths = project.relativize(affectedSources)
|
||||||
|
assertCompiledKotlinSources(relativePaths)
|
||||||
|
}
|
||||||
|
|
||||||
|
val activityLayout = project.projectFile("activity_main.xml")
|
||||||
|
activityLayout.modify { it.replace("textView", "newTextView") }
|
||||||
|
|
||||||
|
project.build("assembleDebug", options = options) {
|
||||||
|
assertFailed()
|
||||||
|
val affectedSources = project.projectDir.getFilesByNames("MyActivity.kt")
|
||||||
|
val relativePaths = project.relativize(affectedSources)
|
||||||
|
assertCompiledKotlinSources(relativePaths)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAndroidExtensionsManyVariants() {
|
fun testAndroidExtensionsManyVariants() {
|
||||||
val project = Project("AndroidExtensionsManyVariants", gradleVersion)
|
val project = Project("AndroidExtensionsManyVariants", gradleVersion)
|
||||||
|
|||||||
+1
@@ -22,6 +22,7 @@ import kotlinx.android.synthetic.main.activity_main.textView
|
|||||||
|
|
||||||
class HomeActivity : Activity() {
|
class HomeActivity : Activity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
myUtilFunction()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package com.example.androidextensions
|
||||||
|
|
||||||
|
fun myUtilFunction() = "OK"
|
||||||
+14
-2
@@ -20,7 +20,10 @@ import org.jetbrains.kotlin.android.synthetic.res.*
|
|||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.incremental.ANDROID_LAYOUT_CONTENT_LOOKUP_NAME
|
||||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||||
|
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||||
|
import org.jetbrains.kotlin.incremental.recordPackageLookup
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
@@ -43,7 +46,9 @@ class AndroidSyntheticPackageFragmentDescriptor(
|
|||||||
val packageData: AndroidSyntheticPackageData,
|
val packageData: AndroidSyntheticPackageData,
|
||||||
private val lazyContext: LazySyntheticElementResolveContext,
|
private val lazyContext: LazySyntheticElementResolveContext,
|
||||||
private val storageManager: StorageManager,
|
private val storageManager: StorageManager,
|
||||||
private val isExperimental: Boolean
|
private val isExperimental: Boolean,
|
||||||
|
private val lookupTracker: LookupTracker,
|
||||||
|
private val layoutName: String
|
||||||
) : PackageFragmentDescriptorImpl(module, fqName) {
|
) : PackageFragmentDescriptorImpl(module, fqName) {
|
||||||
private val scope = AndroidExtensionPropertiesScope()
|
private val scope = AndroidExtensionPropertiesScope()
|
||||||
override fun getMemberScope(): MemberScope = scope
|
override fun getMemberScope(): MemberScope = scope
|
||||||
@@ -81,7 +86,14 @@ class AndroidSyntheticPackageFragmentDescriptor(
|
|||||||
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean) =
|
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean) =
|
||||||
properties().filter { kindFilter.acceptsKinds(DescriptorKindFilter.VARIABLES_MASK) && nameFilter(it.name) }
|
properties().filter { kindFilter.acceptsKinds(DescriptorKindFilter.VARIABLES_MASK) && nameFilter(it.name) }
|
||||||
|
|
||||||
override fun getContributedVariables(name: Name, location: LookupLocation) = properties().filter { it.name == name }
|
override fun getContributedVariables(name: Name, location: LookupLocation): List<PropertyDescriptor> {
|
||||||
|
recordLookup(name, location)
|
||||||
|
return properties().filter { it.name == name }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun recordLookup(name: Name, location: LookupLocation) {
|
||||||
|
lookupTracker.recordPackageLookup(location, layoutName, ANDROID_LAYOUT_CONTENT_LOOKUP_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
override fun printScopeStructure(p: Printer) {
|
override fun printScopeStructure(p: Printer) {
|
||||||
p.println(this::class.java.simpleName)
|
p.println(this::class.java.simpleName)
|
||||||
|
|||||||
+13
-4
@@ -19,9 +19,15 @@ package org.jetbrains.kotlin.android.synthetic.res
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
import org.jetbrains.kotlin.android.synthetic.AndroidConst
|
||||||
import org.jetbrains.kotlin.android.synthetic.descriptors.*
|
import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageData
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageFragmentDescriptor
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.descriptors.LazySyntheticElementResolveContext
|
||||||
|
import org.jetbrains.kotlin.android.synthetic.descriptors.PredefinedPackageFragmentDescriptor
|
||||||
import org.jetbrains.kotlin.android.synthetic.forEachUntilLast
|
import org.jetbrains.kotlin.android.synthetic.forEachUntilLast
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
|
||||||
|
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
@@ -38,7 +44,8 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider
|
|||||||
module: ModuleDescriptor,
|
module: ModuleDescriptor,
|
||||||
storageManager: StorageManager,
|
storageManager: StorageManager,
|
||||||
trace: BindingTrace,
|
trace: BindingTrace,
|
||||||
moduleInfo: ModuleInfo?
|
moduleInfo: ModuleInfo?,
|
||||||
|
lookupTracker: LookupTracker
|
||||||
): PackageFragmentProvider? {
|
): PackageFragmentProvider? {
|
||||||
val isExperimental = isExperimental(moduleInfo)
|
val isExperimental = isExperimental(moduleInfo)
|
||||||
|
|
||||||
@@ -58,7 +65,9 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider
|
|||||||
val resources = layoutXmlFileManager.extractResources(AndroidLayoutGroupData(layoutName, layouts), module)
|
val resources = layoutXmlFileManager.extractResources(AndroidLayoutGroupData(layoutName, layouts), module)
|
||||||
val packageData = AndroidSyntheticPackageData(moduleData, forView, isDeprecated, resources)
|
val packageData = AndroidSyntheticPackageData(moduleData, forView, isDeprecated, resources)
|
||||||
val packageDescriptor = AndroidSyntheticPackageFragmentDescriptor(
|
val packageDescriptor = AndroidSyntheticPackageFragmentDescriptor(
|
||||||
module, FqName(fqName), packageData, lazyContext, storageManager, isExperimental)
|
module, FqName(fqName), packageData, lazyContext, storageManager, isExperimental,
|
||||||
|
lookupTracker, layoutName
|
||||||
|
)
|
||||||
packagesToLookupInCompletion += packageDescriptor
|
packagesToLookupInCompletion += packageDescriptor
|
||||||
allPackageDescriptors += packageDescriptor
|
allPackageDescriptors += packageDescriptor
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.android.synthetic.test
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidPackageFragmentProviderExtension
|
import org.jetbrains.kotlin.android.synthetic.res.AndroidPackageFragmentProviderExtension
|
||||||
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticPackageFragmentProvider
|
import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticPackageFragmentProvider
|
||||||
|
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||||
import org.jetbrains.kotlin.resolve.MemberComparator
|
import org.jetbrains.kotlin.resolve.MemberComparator
|
||||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||||
@@ -39,8 +40,11 @@ abstract class AbstractAndroidSyntheticPropertyDescriptorTest : KtUsefulTestCase
|
|||||||
|
|
||||||
val analysisResult = JvmResolveUtil.analyzeAndCheckForErrors(listOf(), env)
|
val analysisResult = JvmResolveUtil.analyzeAndCheckForErrors(listOf(), env)
|
||||||
|
|
||||||
val fragmentProvider = ext.getPackageFragmentProvider(project, analysisResult.moduleDescriptor, LockBasedStorageManager.NO_LOCKS,
|
val fragmentProvider =
|
||||||
KotlinTestUtils.DUMMY_EXCEPTION_ON_ERROR_TRACE, null) as AndroidSyntheticPackageFragmentProvider
|
ext.getPackageFragmentProvider(
|
||||||
|
project, analysisResult.moduleDescriptor, LockBasedStorageManager.NO_LOCKS,
|
||||||
|
KotlinTestUtils.DUMMY_EXCEPTION_ON_ERROR_TRACE, null, LookupTracker.DO_NOTHING
|
||||||
|
) as AndroidSyntheticPackageFragmentProvider
|
||||||
|
|
||||||
val renderer = DescriptorRenderer.COMPACT_WITH_MODIFIERS
|
val renderer = DescriptorRenderer.COMPACT_WITH_MODIFIERS
|
||||||
val expected = fragmentProvider.packageFragments.sortedBy { it.fqName.asString() }.map {
|
val expected = fragmentProvider.packageFragments.sortedBy { it.fqName.asString() }.map {
|
||||||
|
|||||||
Reference in New Issue
Block a user