From e24e71120888de7fb70ae267a75cf91c525aafbf Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 5 Dec 2017 17:52:21 +0300 Subject: [PATCH] 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 --- .../compiler/TopDownAnalyzerFacadeForJVM.kt | 2 +- .../kotlin/resolve/jvm/JvmAnalyzerFacade.kt | 13 ++++++--- .../PackageFragmentProviderExtension.kt | 7 +++-- .../IncrementalJvmCompilerRunner.kt | 16 ++++++++--- .../org/jetbrains/kotlin/incremental/utils.kt | 8 +++++- .../AbstractKotlinAndroidGradleTests.kt | 27 +++++++++++++++++++ .../example/androidextensions/MyActivity.kt | 1 + .../androidextensions/noLayoutUsages.kt | 3 +++ ...droidSyntheticPackageFragmentDescriptor.kt | 16 +++++++++-- ...AndroidPackageFragmentProviderExtension.kt | 17 +++++++++--- ...tAndroidSyntheticPropertyDescriptorTest.kt | 8 ++++-- 11 files changed, 99 insertions(+), 19 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsProject/app/src/main/java/com/example/androidextensions/noLayoutUsages.kt diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt index 93382149105..5bbcd5ed25c 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt @@ -221,7 +221,7 @@ object TopDownAnalyzerFacadeForJVM { // TODO: consider putting extension package fragment providers into the dependency module 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 diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAnalyzerFacade.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAnalyzerFacade.kt index 396074aee87..05b1dd8272c 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAnalyzerFacade.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAnalyzerFacade.kt @@ -25,7 +25,6 @@ import org.jetbrains.kotlin.context.ModuleContext import org.jetbrains.kotlin.descriptors.PackagePartProvider import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider 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.incremental.components.LookupTracker import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl @@ -86,6 +85,7 @@ object JvmAnalyzerFacade : AnalyzerFacade() { val trace = CodeAnalyzerInitializer.getInstance(project).createTrace() + val lookupTracker = LookupTracker.DO_NOTHING val container = createContainerForLazyResolveWithJava( moduleContext, trace, @@ -93,7 +93,7 @@ object JvmAnalyzerFacade : AnalyzerFacade() { moduleContentScope, moduleClassResolver, targetEnvironment, - LookupTracker.DO_NOTHING, + lookupTracker, packagePartProvider, jvmTarget, languageVersionSettings, @@ -107,8 +107,13 @@ object JvmAnalyzerFacade : AnalyzerFacade() { resolveSession.packageFragmentProvider, javaDescriptorResolver.packageFragmentProvider) - providersForModule += PackageFragmentProviderExtension.getInstances(project) - .mapNotNull { it.getPackageFragmentProvider(project, moduleDescriptor, moduleContext.storageManager, trace, moduleInfo) } + providersForModule += + PackageFragmentProviderExtension.getInstances(project) + .mapNotNull { + it.getPackageFragmentProvider( + project, moduleDescriptor, moduleContext.storageManager, trace, moduleInfo, lookupTracker + ) + } return ResolverForModule(CompositePackageFragmentProvider(providersForModule), container) } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/extensions/PackageFragmentProviderExtension.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/extensions/PackageFragmentProviderExtension.kt index 87371c8b066..3886b9f7474 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/extensions/PackageFragmentProviderExtension.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/extensions/PackageFragmentProviderExtension.kt @@ -18,8 +18,10 @@ package org.jetbrains.kotlin.resolve.jvm.extensions import com.intellij.openapi.project.Project 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.incremental.components.LookupTracker import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.storage.StorageManager @@ -34,6 +36,7 @@ interface PackageFragmentProviderExtension { module: ModuleDescriptor, storageManager: StorageManager, trace: BindingTrace, - moduleInfo: ModuleInfo? + moduleInfo: ModuleInfo?, + lookupTracker: LookupTracker ): PackageFragmentProvider? } diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt index 3f174e21500..24472ada58b 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt @@ -212,10 +212,9 @@ class IncrementalJvmCompilerRunner( } } - if ((changedFiles.modified + changedFiles.removed).any { it.extension.toLowerCase() == "xml" }) { - return CompilationMode.Rebuild { "XML resource files were changed" } - } + val androidLayoutChanges = processLookupSymbolsForAndroidLayouts(changedFiles) + markDirtyBy(androidLayoutChanges) markDirtyBy(classpathChanges.lookupSymbols) markDirtyBy(classpathChanges.fqNames) @@ -260,6 +259,17 @@ class IncrementalJvmCompilerRunner( } } + private fun processLookupSymbolsForAndroidLayouts(changedFiles: ChangedFiles.Known): Collection { + val result = mutableListOf() + 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( modifiedClasspath: List, lastBuildInfo: BuildInfo? diff --git a/core/descriptors/src/org/jetbrains/kotlin/incremental/utils.kt b/core/descriptors/src/org/jetbrains/kotlin/incremental/utils.kt index b7f5c8776e2..283c399c23f 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/incremental/utils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/incremental/utils.kt @@ -35,8 +35,14 @@ fun LookupTracker.record(from: LookupLocation, scopeOwner: ClassDescriptor, 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 val location = from.location ?: return 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 = "" diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt index 1ee1aec8069..f33be635e62 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt @@ -1,6 +1,7 @@ package org.jetbrains.kotlin.gradle 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.modify 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 fun testAndroidExtensionsManyVariants() { val project = Project("AndroidExtensionsManyVariants", gradleVersion) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsProject/app/src/main/java/com/example/androidextensions/MyActivity.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsProject/app/src/main/java/com/example/androidextensions/MyActivity.kt index b9c0985d26d..0b8cb004d46 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsProject/app/src/main/java/com/example/androidextensions/MyActivity.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsProject/app/src/main/java/com/example/androidextensions/MyActivity.kt @@ -22,6 +22,7 @@ import kotlinx.android.synthetic.main.activity_main.textView class HomeActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { + myUtilFunction() super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsProject/app/src/main/java/com/example/androidextensions/noLayoutUsages.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsProject/app/src/main/java/com/example/androidextensions/noLayoutUsages.kt new file mode 100644 index 00000000000..7834a2ddc6d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsProject/app/src/main/java/com/example/androidextensions/noLayoutUsages.kt @@ -0,0 +1,3 @@ +package com.example.androidextensions + +fun myUtilFunction() = "OK" diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt index 3f6f0434b73..a126dd8e83d 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/descriptors/AndroidSyntheticPackageFragmentDescriptor.kt @@ -20,7 +20,10 @@ import org.jetbrains.kotlin.android.synthetic.res.* import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor 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.LookupTracker +import org.jetbrains.kotlin.incremental.recordPackageLookup import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.module @@ -43,7 +46,9 @@ class AndroidSyntheticPackageFragmentDescriptor( val packageData: AndroidSyntheticPackageData, private val lazyContext: LazySyntheticElementResolveContext, private val storageManager: StorageManager, - private val isExperimental: Boolean + private val isExperimental: Boolean, + private val lookupTracker: LookupTracker, + private val layoutName: String ) : PackageFragmentDescriptorImpl(module, fqName) { private val scope = AndroidExtensionPropertiesScope() override fun getMemberScope(): MemberScope = scope @@ -81,7 +86,14 @@ class AndroidSyntheticPackageFragmentDescriptor( override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean) = 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 { + 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) { p.println(this::class.java.simpleName) diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt index b117f11fa5c..e544a2567da 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/res/AndroidPackageFragmentProviderExtension.kt @@ -19,9 +19,15 @@ package org.jetbrains.kotlin.android.synthetic.res import com.intellij.openapi.project.Project import org.jetbrains.kotlin.analyzer.ModuleInfo 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.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.Name import org.jetbrains.kotlin.resolve.BindingTrace @@ -38,7 +44,8 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider module: ModuleDescriptor, storageManager: StorageManager, trace: BindingTrace, - moduleInfo: ModuleInfo? + moduleInfo: ModuleInfo?, + lookupTracker: LookupTracker ): PackageFragmentProvider? { val isExperimental = isExperimental(moduleInfo) @@ -58,7 +65,9 @@ abstract class AndroidPackageFragmentProviderExtension : PackageFragmentProvider val resources = layoutXmlFileManager.extractResources(AndroidLayoutGroupData(layoutName, layouts), module) val packageData = AndroidSyntheticPackageData(moduleData, forView, isDeprecated, resources) val packageDescriptor = AndroidSyntheticPackageFragmentDescriptor( - module, FqName(fqName), packageData, lazyContext, storageManager, isExperimental) + module, FqName(fqName), packageData, lazyContext, storageManager, isExperimental, + lookupTracker, layoutName + ) packagesToLookupInCompletion += packageDescriptor allPackageDescriptors += packageDescriptor } diff --git a/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/android/synthetic/test/AbstractAndroidSyntheticPropertyDescriptorTest.kt b/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/android/synthetic/test/AbstractAndroidSyntheticPropertyDescriptorTest.kt index e5026dc1818..fcb0f9c0032 100755 --- a/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/android/synthetic/test/AbstractAndroidSyntheticPropertyDescriptorTest.kt +++ b/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/android/synthetic/test/AbstractAndroidSyntheticPropertyDescriptorTest.kt @@ -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.AndroidSyntheticPackageFragmentProvider +import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.MemberComparator import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension @@ -39,8 +40,11 @@ abstract class AbstractAndroidSyntheticPropertyDescriptorTest : KtUsefulTestCase val analysisResult = JvmResolveUtil.analyzeAndCheckForErrors(listOf(), env) - val fragmentProvider = ext.getPackageFragmentProvider(project, analysisResult.moduleDescriptor, LockBasedStorageManager.NO_LOCKS, - KotlinTestUtils.DUMMY_EXCEPTION_ON_ERROR_TRACE, null) as AndroidSyntheticPackageFragmentProvider + val fragmentProvider = + 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 expected = fragmentProvider.packageFragments.sortedBy { it.fqName.asString() }.map {