diff --git a/compiler/frontend/src/org/jetbrains/kotlin/config/CompilerConfiguration.java b/compiler/frontend/src/org/jetbrains/kotlin/config/CompilerConfiguration.java
index 3bc34cf7ae4..2158f3e439b 100644
--- a/compiler/frontend/src/org/jetbrains/kotlin/config/CompilerConfiguration.java
+++ b/compiler/frontend/src/org/jetbrains/kotlin/config/CompilerConfiguration.java
@@ -132,6 +132,9 @@ public class CompilerConfiguration {
else if (object instanceof Map) {
return (T) Collections.unmodifiableMap((Map) object);
}
+ else if (object instanceof Set) {
+ return (T) Collections.unmodifiableSet((Set) object);
+ }
else if (object instanceof Collection) {
return (T) Collections.unmodifiableCollection((Collection) object);
}
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 b4ac8578c64..cbc8eaeaff0 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
@@ -331,6 +331,11 @@ fun getSomething() = 10
@Test
fun testAndroidExtensionsManyVariants() {
+ if (isLegacyAndroidGradleVersion(androidGradlePluginVersion)) {
+ // Library dependencies are not supported in older versions of Android Gradle plugin (< 3.0)
+ return
+ }
+
val project = Project("AndroidExtensionsManyVariants", gradleVersion)
val options = defaultBuildOptions().copy(incremental = false)
@@ -346,6 +351,30 @@ fun getSomething() = 10
}
}
+ @Test
+ fun testAndroidExtensionsSpecificFeatures() {
+ val project = Project("AndroidExtensionsSpecificFeatures", gradleVersion)
+ val options = defaultBuildOptions().copy(incremental = false)
+
+ project.build("assemble", options = options) {
+ assertFailed()
+ assertContains("Unresolved reference: textView")
+ }
+
+ File(project.projectDir, "app/build.gradle").modify { it.replace("[\"parcelize\"]", "[\"views\"]") }
+
+ project.build("assemble", options = options) {
+ assertFailed()
+ assertContains("Class 'User' is not abstract and does not implement abstract member public abstract fun writeToParcel")
+ }
+
+ File(project.projectDir, "app/build.gradle").modify { it.replace("[\"views\"]", "[\"parcelize\", \"views\"]") }
+
+ project.build("assemble", options = options) {
+ assertSuccessful()
+ }
+ }
+
@Test
fun testMultiplatformAndroidCompile() = with(Project("multiplatformAndroidProject", gradleVersion)) {
setupWorkingDir()
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsManyVariants/app/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsManyVariants/app/build.gradle
index 7092441fe4d..1503f5b6373 100644
--- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsManyVariants/app/build.gradle
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsManyVariants/app/build.gradle
@@ -36,6 +36,7 @@ android {
androidExtensions {
experimental = true
+ features = ["views", "parcelize"]
}
dependencies {
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsManyVariants/app/src/main/java/MainActivity.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsManyVariants/app/src/main/java/MainActivity.kt
index 9ccfe49f352..6bc4499268b 100644
--- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsManyVariants/app/src/main/java/MainActivity.kt
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsManyVariants/app/src/main/java/MainActivity.kt
@@ -4,6 +4,8 @@ import android.app.Activity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.layout_in_library.text_view
+import kotlinx.android.parcel.Parcelize
+import android.os.Parcelable
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle) {
@@ -13,3 +15,6 @@ class MainActivity : Activity() {
text_view
}
}
+
+@Parcelize
+class User(val firstName: String, val lastName: String) : Parcelable
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/build.gradle
new file mode 100644
index 00000000000..ba281a60b98
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/build.gradle
@@ -0,0 +1,29 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "25.0.2"
+
+ defaultConfig {
+ applicationId "com.example.dagger.kotlin"
+ minSdkVersion 14
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ }
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+}
+
+androidExtensions {
+ experimental = true
+ features = ["parcelize"]
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/src/main/AndroidManifest.xml b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000000..5063f2b77f8
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/src/main/java/com/example/androidextensions/MyActivity.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/src/main/java/com/example/androidextensions/MyActivity.kt
new file mode 100644
index 00000000000..f86369c601d
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/src/main/java/com/example/androidextensions/MyActivity.kt
@@ -0,0 +1,35 @@
+/*
+ * 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 com.example.androidextensions
+
+import android.os.Bundle
+import android.app.Activity
+import kotlinx.android.synthetic.main.activity_main.textView
+import kotlinx.android.parcel.Parcelize
+import android.os.Parcelable
+
+class HomeActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+
+ this.textView.setText("Hello, world!")
+ }
+}
+
+@Parcelize
+class User(val firstName: String, val lastName: String) : Parcelable
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/src/main/res/layout/activity_main.xml b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000000..02b0685b4a4
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/src/main/res/values/strings.xml b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000000..67b44b1a8cd
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ kotlin
+
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/build.gradle
new file mode 100644
index 00000000000..a0cc7dccb08
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/build.gradle
@@ -0,0 +1,22 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+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' }
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/gradle.properties
new file mode 100644
index 00000000000..5fcc74643cf
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/gradle.properties
@@ -0,0 +1 @@
+org.gradle.jvmargs=-ea -XX:MaxPermSize=512m
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/settings.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/settings.gradle
new file mode 100644
index 00000000000..e7b4def49cb
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/AndroidExtensionsSpecificFeatures/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidExtensionsExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidExtensionsExtension.kt
index b9a3585ca7d..d3f137c59d2 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidExtensionsExtension.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidExtensionsExtension.kt
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.gradle.internal
import groovy.lang.Closure
import org.gradle.util.Configurable
+import java.util.*
enum class CacheImplementation(val optionName: String) {
HASH_MAP("hashMap"),
@@ -25,6 +26,19 @@ enum class CacheImplementation(val optionName: String) {
NONE("none")
}
+enum class AndroidExtensionsFeature(val featureName: String) {
+ VIEWS("views"),
+ PARCELIZE("parcelize");
+
+ internal companion object {
+ internal fun parseFeatures(features: Set): SortedSet {
+ fun find(name: String) = AndroidExtensionsFeature.values().firstOrNull { it.featureName == name }
+ ?: error("Can't find Android Extensions feature $name")
+ return features.mapTo(sortedSetOf()) { find(it) }
+ }
+ }
+}
+
open class AndroidExtensionsExtension : Configurable {
private lateinit var onEvaluatedHandler: (AndroidExtensionsExtension) -> Unit
@@ -34,6 +48,8 @@ open class AndroidExtensionsExtension : Configurable
open var isExperimental: Boolean = false
+ open var features: Set = AndroidExtensionsFeature.values().mapTo(mutableSetOf()) { it.featureName }
+
open var defaultCacheImplementation: CacheImplementation = CacheImplementation.HASH_MAP
override fun configure(closure: Closure<*>): AndroidExtensionsExtension {
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt
index 687c16971b6..49ec59edfee 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt
@@ -104,6 +104,7 @@ class AndroidSubplugin : KotlinGradleSubplugin {
val sourceSets = androidExtension.sourceSets
val pluginOptions = arrayListOf()
+ pluginOptions += SubpluginOption("features", AndroidExtensionsFeature.VIEWS.featureName)
val mainSourceSet = sourceSets.getByName("main")
val manifestFile = mainSourceSet.manifest.srcFile
@@ -157,6 +158,8 @@ class AndroidSubplugin : KotlinGradleSubplugin {
androidProjectHandler as? AbstractAndroidProjectHandler ?: return emptyList()
val pluginOptions = arrayListOf()
+ pluginOptions += SubpluginOption("features",
+ AndroidExtensionsFeature.parseFeatures(androidExtensionsExtension.features).joinToString(",") { it.featureName })
pluginOptions += SubpluginOption("experimental", "true")
pluginOptions += SubpluginOption("defaultCacheImplementation", androidExtensionsExtension.defaultCacheImplementation.optionName)
@@ -208,6 +211,8 @@ class AndroidSubplugin : KotlinGradleSubplugin {
addVariant(sourceSetName, (srcDirs - commonResDirectories).sorted())
}
+ project.logger.warn("Blah: " + allResDirectories)
+
return wrapPluginOptions(pluginOptions, "configuration")
}
diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt
index 64db07f61f7..f5c78e74135 100644
--- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt
+++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt
@@ -54,6 +54,12 @@ object AndroidConfigurationKeys {
val PACKAGE = CompilerConfigurationKey.create("application package fq name")
val EXPERIMENTAL = CompilerConfigurationKey.create("enable experimental features")
val DEFAULT_CACHE_IMPL = CompilerConfigurationKey.create("default cache implementation")
+ val FEATURES = CompilerConfigurationKey.create>("enabled features")
+}
+
+enum class AndroidExtensionsFeature(val featureName: String) {
+ VIEWS("views"),
+ PARCELIZE("parcelize")
}
class AndroidCommandLineProcessor : CommandLineProcessor {
@@ -68,6 +74,9 @@ class AndroidCommandLineProcessor : CommandLineProcessor {
val DEFAULT_CACHE_IMPL_OPTION = CliOption(
"defaultCacheImplementation", "hashMap/sparseArray/none", "Default cache implementation for module", required = false)
+ val FEATURES_OPTION = CliOption(
+ "features", AndroidExtensionsFeature.values().joinToString(" | "), "Enabled features", required = false)
+
/* This option is just for saving Android Extensions status in Kotlin facet. It should not be supported from CLI. */
val ENABLED_OPTION: CliOption = CliOption("enabled", "true/false", "Enable Android Extensions", required = false)
}
@@ -75,7 +84,7 @@ class AndroidCommandLineProcessor : CommandLineProcessor {
override val pluginId: String = ANDROID_COMPILER_PLUGIN_ID
override val pluginOptions: Collection
- = listOf(VARIANT_OPTION, PACKAGE_OPTION, EXPERIMENTAL_OPTION, DEFAULT_CACHE_IMPL_OPTION, CONFIGURATION)
+ = listOf(VARIANT_OPTION, PACKAGE_OPTION, EXPERIMENTAL_OPTION, DEFAULT_CACHE_IMPL_OPTION, CONFIGURATION, FEATURES_OPTION)
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) {
when (option) {
@@ -84,6 +93,12 @@ class AndroidCommandLineProcessor : CommandLineProcessor {
EXPERIMENTAL_OPTION -> configuration.put(AndroidConfigurationKeys.EXPERIMENTAL, value)
DEFAULT_CACHE_IMPL_OPTION -> configuration.put(AndroidConfigurationKeys.DEFAULT_CACHE_IMPL, value)
CONFIGURATION -> configuration.applyOptionsFrom(decodePluginOptions(value), pluginOptions)
+ FEATURES_OPTION -> {
+ val features = value.split(',').mapNotNullTo(mutableSetOf()) {
+ name -> AndroidExtensionsFeature.values().firstOrNull { it.featureName == name }
+ }
+ configuration.put(AndroidConfigurationKeys.FEATURES, features)
+ }
else -> throw CliOptionProcessingException("Unknown option: ${option.name}")
}
}
@@ -97,6 +112,37 @@ class AndroidComponentRegistrar : ComponentRegistrar {
ClassBuilderInterceptorExtension.registerExtension(project, ParcelableClinitClassBuilderInterceptorExtension())
}
+ private fun parseVariant(s: String): AndroidVariant? {
+ val parts = s.split(';')
+ if (parts.size < 2) return null
+ return AndroidVariant(parts[0], parts.drop(1))
+ }
+
+ fun registerViewExtensions(configuration: CompilerConfiguration, isExperimental: Boolean, project: MockProject) {
+ val applicationPackage = configuration.get(AndroidConfigurationKeys.PACKAGE) ?: return
+ val variants = configuration.get(AndroidConfigurationKeys.VARIANT)?.mapNotNull { parseVariant(it) } ?: return
+ val globalCacheImpl = parseCacheImplementationType(configuration.get(AndroidConfigurationKeys.DEFAULT_CACHE_IMPL))
+
+ if (variants.isEmpty() || applicationPackage.isEmpty()) {
+ return
+ }
+
+ val layoutXmlFileManager = CliAndroidLayoutXmlFileManager(project, applicationPackage, variants)
+ project.registerService(AndroidLayoutXmlFileManager::class.java, layoutXmlFileManager)
+
+ ExpressionCodegenExtension.registerExtension(project,
+ CliAndroidExtensionsExpressionCodegenExtension(isExperimental, globalCacheImpl))
+
+ StorageComponentContainerContributor.registerExtension(project,
+ AndroidExtensionPropertiesComponentContainerContributor())
+
+ ClassBuilderInterceptorExtension.registerExtension(project,
+ CliAndroidOnDestroyClassBuilderInterceptorExtension(globalCacheImpl))
+
+ PackageFragmentProviderExtension.registerExtension(project,
+ CliAndroidPackageFragmentProviderExtension(isExperimental))
+ }
+
fun parseCacheImplementationType(s: String?): CacheImplementation = when (s) {
"sparseArray" -> CacheImplementation.SPARSE_ARRAY
"none" -> CacheImplementation.NO_CACHE
@@ -105,31 +151,17 @@ class AndroidComponentRegistrar : ComponentRegistrar {
}
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
- val applicationPackage = configuration.get(AndroidConfigurationKeys.PACKAGE) ?: return
- val variants = configuration.get(AndroidConfigurationKeys.VARIANT)?.mapNotNull { parseVariant(it) } ?: return
+ val features = configuration.get(AndroidConfigurationKeys.FEATURES) ?: AndroidExtensionsFeature.values().toSet()
val isExperimental = configuration.get(AndroidConfigurationKeys.EXPERIMENTAL) == "true"
- val globalCacheImpl = parseCacheImplementationType(configuration.get(AndroidConfigurationKeys.DEFAULT_CACHE_IMPL))
- if (isExperimental) {
+ if (isExperimental && AndroidExtensionsFeature.PARCELIZE in features) {
registerParcelExtensions(project)
}
- if (variants.isNotEmpty() && !applicationPackage.isNullOrBlank()) {
- val layoutXmlFileManager = CliAndroidLayoutXmlFileManager(project, applicationPackage!!, variants)
- project.registerService(AndroidLayoutXmlFileManager::class.java, layoutXmlFileManager)
-
- ExpressionCodegenExtension.registerExtension(project, CliAndroidExtensionsExpressionCodegenExtension(isExperimental, globalCacheImpl))
- StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor())
- ClassBuilderInterceptorExtension.registerExtension(project, CliAndroidOnDestroyClassBuilderInterceptorExtension(globalCacheImpl))
- PackageFragmentProviderExtension.registerExtension(project, CliAndroidPackageFragmentProviderExtension(isExperimental))
+ if (AndroidExtensionsFeature.VIEWS in features) {
+ registerViewExtensions(configuration, isExperimental, project)
}
}
-
- private fun parseVariant(s: String): AndroidVariant? {
- val parts = s.split(';')
- if (parts.size < 2) return null
- return AndroidVariant(parts[0], parts.drop(1))
- }
}
class AndroidExtensionPropertiesComponentContainerContributor : StorageComponentContainerContributor {