Android Extensions: Support Android variants and library dependencies (KT-14086, KT-16934)

This commit is contained in:
Yan Zhulanow
2017-05-15 20:55:33 +03:00
committed by Yan Zhulanow
parent 8e9a62d553
commit f4acf404ca
32 changed files with 397 additions and 49 deletions
@@ -33,6 +33,7 @@ class ExampleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
): List<SubpluginOption> {
println("ExampleSubplugin loaded")
@@ -79,6 +79,7 @@ class AllOpenKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
): List<SubpluginOption> {
if (!AllOpenGradleSubplugin.isEnabled(project)) return emptyList()
@@ -22,7 +22,7 @@ import org.gradle.api.tasks.compile.AbstractCompile
class SubpluginOption(val key: String, val value: String)
interface KotlinGradleSubplugin<KotlinCompile : AbstractCompile> {
interface KotlinGradleSubplugin<in KotlinCompile : AbstractCompile> {
fun isApplicable(project: Project, task: KotlinCompile): Boolean
fun apply(
@@ -30,6 +30,7 @@ interface KotlinGradleSubplugin<KotlinCompile : AbstractCompile> {
kotlinCompile: KotlinCompile,
javaCompile: AbstractCompile,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
): List<SubpluginOption>
@@ -210,6 +210,16 @@ fun getSomething() = 10
}
}
@Test
fun testAndroidExtensionsManyVariants() {
val project = Project("AndroidExtensionsManyVariants", gradleVersion)
val options = defaultBuildOptions().copy(incremental = false)
project.build("assemble", options = options) {
assertSuccessful()
}
}
@Test
fun testAndroidKaptChangingDependencies() {
val project = Project("AndroidKaptChangingDependencies", gradleVersion)
@@ -0,0 +1,40 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 23
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "org.example.manyvariants"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
}
}
flavorDimensions "color"
productFlavors {
demo {
dimension "color"
applicationIdSuffix ".demo"
versionNameSuffix "-demo"
}
full {
dimension "color"
applicationIdSuffix ".full"
versionNameSuffix "-full"
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
@@ -0,0 +1,10 @@
package org.example.manyvariants
import android.app.Activity
import kotlinx.android.synthetic.debug.activity_debug.*
import kotlinx.android.synthetic.main.activity_main.*
fun Activity.debug() {
viewMain
viewDebug
}
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/viewDebug"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
@@ -0,0 +1,10 @@
package org.example.manyvariants
import android.app.Activity
import kotlinx.android.synthetic.demo.activity_demo.*
import kotlinx.android.synthetic.main.activity_main.*
fun Activity.demo() {
viewMain
viewDemo
}
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/viewDemo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
@@ -0,0 +1,14 @@
package org.example.manyvariants
import android.app.Activity
import kotlinx.android.synthetic.debug.activity_debug.*
import kotlinx.android.synthetic.demo.activity_demo.*
import kotlinx.android.synthetic.demoDebug.activity_demo_debug.*
import kotlinx.android.synthetic.main.activity_main.*
fun Activity.demoDebug() {
viewMain
viewDemo
viewDebug
viewDemoDebug
}
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/viewDemoDebug"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
@@ -0,0 +1,10 @@
package org.example.manyvariants
import android.app.Activity
import kotlinx.android.synthetic.full.activity_full.*
import kotlinx.android.synthetic.main.activity_main.*
fun Activity.full() {
viewMain
viewFull
}
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/viewFull"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
@@ -0,0 +1,14 @@
package org.example.manyvariants
import android.app.Activity
import kotlinx.android.synthetic.full.activity_full.*
import kotlinx.android.synthetic.fullRelease.activity_full_release.*
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.release.activity_release.*
fun Activity.fullRelease() {
viewMain
viewFull
viewRelease
viewFullRelease
}
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/viewFullRelease"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.example.manyvariants">
<application android:allowBackup="true" android:supportsRtl="true">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,13 @@
package org.example.manyvariants
import android.app.Activity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewMain
}
}
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/viewMain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
@@ -0,0 +1,10 @@
package org.example.manyvariants
import android.app.Activity
import kotlinx.android.synthetic.release.activity_release.*
import kotlinx.android.synthetic.main.activity_main.*
fun Activity.release() {
viewMain
viewRelease
}
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/viewRelease"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
@@ -0,0 +1,23 @@
buildscript {
repositories {
mavenLocal()
jcenter()
maven { url 'https://maven.google.com' }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.android.tools.build:gradle:$android_tools_version"
}
}
allprojects {
repositories {
mavenLocal()
jcenter()
maven { url 'https://maven.google.com' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
@@ -2,7 +2,9 @@ package org.jetbrains.kotlin.gradle.plugin
import com.android.build.gradle.*
import com.android.build.gradle.api.*
import com.android.build.gradle.tasks.MergeResources
import com.android.builder.model.SourceProvider
import com.android.ide.common.res2.ResourceSet
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.compile.AbstractCompile
@@ -117,6 +119,10 @@ class Android25ProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools
// to some degree -- the dependent projects will rebuild non-incrementally when a library project changes
}
override fun getResDirectories(variantData: BaseVariant): List<File> {
return variantData.mergeResources?.computeResourceSetList0()?.flatMap { it.sourceFiles } ?: emptyList()
}
private inner class KaptVariant(variantData: BaseVariant) : KaptVariantData<BaseVariant>(variantData) {
override val name: String = getVariantName(variantData)
override val sourceProviders: Iterable<SourceProvider> = getSourceProviders(variantData)
@@ -137,6 +143,22 @@ class Android25ProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools
}
}
//TODO once the Android plugin reaches its 3.0.0 release, consider compiling against it (remove the reflective call)
//TODO this is a private API for now
private fun MergeResources.computeResourceSetList0(): List<ResourceSet>? {
val computeResourceSetListMethod = MergeResources::class.java.declaredMethods
.firstOrNull { it.name == "computeResourceSetList" && it.parameterCount == 0 } ?: return null
val oldIsAccessible = computeResourceSetListMethod.isAccessible
try {
computeResourceSetListMethod.isAccessible = true
@Suppress("UNCHECKED_CAST")
return computeResourceSetListMethod.invoke(this) as? List<ResourceSet>
} finally {
computeResourceSetListMethod.isAccessible = oldIsAccessible
}
}
//TODO once the Android plugin reaches its 3.0.0 release, consider compiling against it (remove the reflective call)
private val BaseVariant.dataBindingDependencyArtifactsIfSupported: FileCollection?
get() = ReflectionUtil.findMethod(
@@ -18,6 +18,10 @@ package org.jetbrains.kotlin.gradle.internal
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.internal.variant.BaseVariantData
import com.android.build.gradle.internal.variant.TestVariantData
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.UnknownDomainObjectException
@@ -25,7 +29,6 @@ import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.w3c.dom.Document
import java.io.File
@@ -54,38 +57,96 @@ class AndroidSubplugin : KotlinGradleSubplugin<KotlinCompile> {
project: Project,
kotlinCompile: KotlinCompile,
javaCompile: AbstractCompile,
variantData: Any?,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
): List<SubpluginOption> {
val androidExtension = project.extensions.getByName("android") as? BaseExtension ?: return emptyList()
val sourceSets = androidExtension.sourceSets
@Suppress("UNCHECKED_CAST")
androidProjectHandler as? AbstractAndroidProjectHandler<Any?> ?: return emptyList()
val androidExtension = project.extensions.getByName("android") as? BaseExtension ?: return emptyList()
val pluginOptions = arrayListOf<SubpluginOption>()
val mainSourceSet = sourceSets.getByName("main")
val manifestFile = mainSourceSet.manifest.srcFile
val applicationPackage = getApplicationPackageFromManifest(manifestFile) ?: run {
project.logger.warn(
"Application package name is not present in the manifest file (${manifestFile.absolutePath})")
""
}
pluginOptions += SubpluginOption("package", applicationPackage)
val mainSourceSet = androidExtension.sourceSets.getByName("main")
pluginOptions += SubpluginOption("package", getApplicationPackage(project, mainSourceSet))
fun addVariant(sourceSet: AndroidSourceSet) {
pluginOptions += SubpluginOption("variant", sourceSet.name + ';' +
sourceSet.res.srcDirs.joinToString(";") { it.absolutePath })
fun addVariant(name: String, resDirectories: List<File>) {
pluginOptions += SubpluginOption("variant", buildString {
append(name)
append(';')
resDirectories.joinTo(this, separator = ";") { it.canonicalPath }
})
}
addVariant(mainSourceSet)
fun addSourceSetAsVariant(name: String) {
val sourceSet = androidExtension.sourceSets.findByName(name) ?: return
val srcDirs = sourceSet.res.srcDirs.toList()
if (srcDirs.isNotEmpty()) {
addVariant(sourceSet.name, srcDirs)
}
}
val flavorSourceSets = AndroidGradleWrapper.getProductFlavorsSourceSets(androidExtension).filterNotNull()
for (sourceSet in flavorSourceSets) {
addVariant(sourceSet)
val resDirectoriesForAllVariants = mutableListOf<List<File>>()
androidProjectHandler.forEachVariant(project) { variant ->
if (androidProjectHandler.getTestedVariantData(variant) != null) return@forEachVariant
resDirectoriesForAllVariants += androidProjectHandler.getResDirectories(variant)
}
val commonResDirectories = getCommonResDirectories(resDirectoriesForAllVariants)
addVariant("main", commonResDirectories.toList())
getVariantComponentNames(variantData)?.let { (variantName, flavorName, buildTypeName) ->
addSourceSetAsVariant(buildTypeName)
if (flavorName.isNotEmpty()) {
addSourceSetAsVariant(flavorName)
}
addSourceSetAsVariant(variantName)
}
return pluginOptions
}
// Android25ProjectHandler.KaptVariant actually contains BaseVariant, not BaseVariantData
private fun getVariantComponentNames(flavorData: Any?): VariantComponentNames? = when(flavorData) {
is KaptVariantData<*> -> getVariantComponentNames(flavorData.variantData)
is TestVariantData -> getVariantComponentNames(flavorData.testedVariantData)
is TestVariant -> getVariantComponentNames(flavorData.testedVariant)
is BaseVariant -> VariantComponentNames(flavorData.name, flavorData.flavorName, flavorData.buildType.name)
is BaseVariantData<*> -> VariantComponentNames(flavorData.name, flavorData.variantConfiguration.flavorName,
flavorData.variantConfiguration.buildType.name)
else -> null
}
private data class VariantComponentNames(val variantName: String, val flavorName: String, val buildTypeName: String)
private fun getCommonResDirectories(resDirectories: List<List<File>>): Set<File> {
var common = resDirectories.firstOrNull()?.toSet() ?: return emptySet()
for (resDirs in resDirectories.drop(1)) {
common = common.intersect(resDirs)
}
return common
}
private fun getApplicationPackage(project: Project, mainSourceSet: AndroidSourceSet): String {
val manifestFile = mainSourceSet.manifest.srcFile
val applicationPackage = getApplicationPackageFromManifest(manifestFile)
if (applicationPackage == null) {
project.logger.warn("Application package name is not present in the manifest file " +
"(${manifestFile.absolutePath})")
return ""
} else {
return applicationPackage
}
}
private fun getApplicationPackageFromManifest(manifestFile: File): String? {
try {
return manifestFile.parseXml().documentElement.getAttribute("package")
@@ -101,7 +162,7 @@ class AndroidSubplugin : KotlinGradleSubplugin<KotlinCompile> {
override fun getArtifactName() = "kotlin-android-extensions"
fun File.parseXml(): Document {
private fun File.parseXml(): Document {
val factory = DocumentBuilderFactory.newInstance()
val builder = factory.newDocumentBuilder()
return builder.parse(this)
@@ -47,7 +47,8 @@ internal fun Project.initKapt(
variantName: String,
kotlinOptions: KotlinJvmOptionsImpl?,
subpluginEnvironment: SubpluginEnvironment,
tasksProvider: KotlinTasksProvider
tasksProvider: KotlinTasksProvider,
androidProjectHandler: AbstractAndroidProjectHandler<*>?
): KotlinCompile? {
val kaptExtension = extensions.getByType(KaptExtension::class.java)
val kotlinAfterJavaTask: KotlinCompile?
@@ -72,7 +73,7 @@ internal fun Project.initKapt(
kotlinAfterJavaTask.source(kaptManager.generatedKotlinSourceDir)
kotlinAfterJavaTask.source(kaptManager.aptOutputDir)
subpluginEnvironment.addSubpluginOptions(this, kotlinAfterJavaTask, javaTask, null, null)
subpluginEnvironment.addSubpluginOptions(this, kotlinAfterJavaTask, javaTask, null, androidProjectHandler, null)
javaTask.doLast {
moveGeneratedJavaFilesToCorrespondingDirectories(kaptManager.aptOutputDir)
@@ -125,6 +125,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
kotlinCompile: KotlinCompile,
javaCompile: AbstractCompile,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
): List<SubpluginOption> {
assert((variantData != null) xor (javaSourceSet != null))
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.com.intellij.util.ReflectionUtil
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
import org.jetbrains.kotlin.gradle.internal.*
import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin.Companion.getKaptClasssesDir
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.utils.ParsedGradleVersion
import org.jetbrains.kotlin.incremental.configureMultiProjectIncrementalCompilation
@@ -135,7 +134,7 @@ internal class Kotlin2JvmSourceSetProcessor(
val subpluginEnvironment = loadSubplugins(project)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTask, javaTask as JavaCompile, null, sourceSet)
project, kotlinTask, javaTask as JavaCompile, null, null, sourceSet)
var kotlinAfterJavaTask: KotlinCompile? = null
@@ -148,7 +147,7 @@ internal class Kotlin2JvmSourceSetProcessor(
aptConfiguration.resolve(), aptOutputDir, aptWorkingDir)
kotlinAfterJavaTask = project.initKapt(kotlinTask, javaTask, kaptManager,
sourceSetName, null, subpluginEnvironment, tasksProvider)
sourceSetName, null, subpluginEnvironment, tasksProvider, null)
} else {
removeAnnotationProcessingPluginClasspathEntry(kotlinTask)
}
@@ -409,12 +408,13 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
protected val logger = Logging.getLogger(this.javaClass)
protected abstract fun forEachVariant(project: Project, action: (V) -> Unit): Unit
abstract fun forEachVariant(project: Project, action: (V) -> Unit): Unit
abstract fun getTestedVariantData(variantData: V): V?
abstract fun getResDirectories(variantData: V): List<File>
protected abstract fun getSourceProviders(variantData: V): Iterable<SourceProvider>
protected abstract fun getAllJavaSources(variantData: V): Iterable<File>
protected abstract fun getVariantName(variant: V): String
protected abstract fun getTestedVariantData(variantData: V): V?
protected abstract fun getJavaTask(variantData: V): AbstractCompile?
protected abstract fun addJavaSourceDirectoryToVariantModel(variantData: V, javaSourceDirectory: File): Unit
@@ -541,7 +541,7 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
aptFiles.toSet(), aptOutputDir, aptWorkingDir, variantData)
kotlinAfterJavaTask = project.initKapt(kotlinTask, javaTask, kaptManager,
variantDataName, rootKotlinOptions, subpluginEnvironment, tasksProvider)
variantDataName, rootKotlinOptions, subpluginEnvironment, tasksProvider, this)
}
for (task in listOfNotNull(kotlinTask, kotlinAfterJavaTask)) {
@@ -554,7 +554,7 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
configureMultiProjectIc(project, variantData, javaTask, kotlinTask, kotlinAfterJavaTask)
val appliedPlugins = subpluginEnvironment.addSubpluginOptions(
project, kotlinTask, javaTask, wrapVariantDataForKapt(variantData), null)
project, kotlinTask, javaTask, wrapVariantDataForKapt(variantData), this, null)
appliedPlugins.flatMap { it.getSubpluginKotlinTasks(project, kotlinTask) }
.forEach { configureSources(it, variantData) }
@@ -700,6 +700,7 @@ internal class SubpluginEnvironment(
kotlinTask: KotlinCompile,
javaTask: AbstractCompile,
variantData: Any?,
androidProjectHandler: AbstractAndroidProjectHandler<out Any?>?,
javaSourceSet: SourceSet?
): List<KotlinGradleSubplugin<KotlinCompile>> {
val pluginOptions = kotlinTask.pluginOptions
@@ -715,7 +716,7 @@ internal class SubpluginEnvironment(
val subpluginClasspath = subpluginClasspaths[subplugin] ?: continue
subpluginClasspath.forEach { pluginOptions.addClasspathEntry(it) }
for (option in subplugin.apply(project, kotlinTask, javaTask, variantData, javaSourceSet)) {
for (option in subplugin.apply(project, kotlinTask, javaTask, variantData, androidProjectHandler, javaSourceSet)) {
pluginOptions.addPluginArgument(subplugin.getCompilerPluginId(), option.key, option.value)
}
}
@@ -91,6 +91,10 @@ internal class LegacyAndroidAndroidProjectHandler(kotlinConfigurationTools: Kotl
override fun getTestedVariantData(variantData: BaseVariantData<*>): BaseVariantData<*>? =
((variantData as? TestVariantData)?.testedVariantData as? BaseVariantData<*>)
override fun getResDirectories(variantData: BaseVariantData<out BaseVariantOutputData>): List<File> {
return variantData.mergeResourcesTask?.rawInputFolders?.toList() ?: emptyList()
}
private fun Project.tryGetSingleArtifact(variantData: BaseVariantData<*>): File? {
val log = logger
log.kotlinDebug { "Trying to determine single artifact for project $path" }
@@ -81,6 +81,7 @@ class NoArgKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
): List<SubpluginOption> {
if (!NoArgGradleSubplugin.isEnabled(project)) return emptyList()
@@ -80,6 +80,7 @@ class SamWithReceiverKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompi
kotlinCompile: AbstractCompile,
javaCompile: AbstractCompile,
variantData: Any?,
androidProjectHandler: Any?,
javaSourceSet: SourceSet?
): List<SubpluginOption> {
if (!SamWithReceiverGradleSubplugin.isEnabled(project)) return emptyList()
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.android.synthetic.idea.res
import com.android.builder.model.SourceProvider
import com.android.tools.idea.gradle.AndroidGradleModel
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ProjectRootModificationTracker
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.PsiTreeChangePreprocessor
@@ -47,7 +48,9 @@ class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileM
else {
if (_moduleData == null) {
_moduleData = cachedValue(project) {
CachedValueProvider.Result.create(super.getModuleData(), getPsiTreeChangePreprocessor())
CachedValueProvider.Result.create(
super.getModuleData(),
getPsiTreeChangePreprocessor(), ProjectRootModificationTracker.getInstance(project))
}
}
}
@@ -103,31 +106,33 @@ class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileM
return listOf()
}
private fun SourceProvider.toVariant() = AndroidVariant(name, resDirectories.map { it.canonicalPath })
private val Module.androidFacet: AndroidFacet?
get() = AndroidFacet.getInstance(this)
private fun SourceProvider.toVariant() = AndroidVariant(name, resDirectories.map { it.absolutePath })
private fun AndroidFacet.toAndroidModuleInfo(): AndroidModule? {
val applicationPackage = manifest?.`package`?.toString()
val applicationPackage = manifest?.`package`?.toString() ?: return null
if (applicationPackage != null) {
val mainVariant = mainSourceProvider.toVariant()
val allResDirectories = getAppResources(true)?.resourceDirs.orEmpty().mapNotNull { it.canonicalPath }
val method = try { this::class.java.getMethod("getFlavorSourceProviders") } catch (e: NoSuchMethodException) { null }
val variants: List<AndroidVariant>? = if (method != null) {
val sourceProviders = method.invoke(this) as List<SourceProvider>?
sourceProviders?.map { it.toVariant() } ?: listOf()
}
else {
val model = AndroidGradleModel.get(module)
model?.flavorSourceProviders?.map { it.toVariant() } ?: listOf(this.mainSourceProvider.toVariant())
}
val resDirectoriesForMainVariant = run {
val resDirsFromSourceProviders = AndroidGradleModel.get(module)?.allSourceProviders.orEmpty()
.filter { it.name != "main" }
.flatMap { it.resDirectories }
.map { it.canonicalPath }
if (variants != null) {
return AndroidModule(applicationPackage, listOf(mainVariant) + variants)
allResDirectories - resDirsFromSourceProviders
}
val variants = mutableListOf(AndroidVariant("main", resDirectoriesForMainVariant))
AndroidGradleModel.get(module)?.let { androidGradleModel ->
androidGradleModel.activeSourceProviders.filter { it.name != "main" }.forEach { sourceProvider ->
variants += sourceProvider.toVariant()
}
}
return null
return AndroidModule(applicationPackage, variants)
}
}