-
Notifications
You must be signed in to change notification settings - Fork 5.6k
/
AndroidComponentRegistrar.kt
174 lines (147 loc) · 9.15 KB
/
AndroidComponentRegistrar.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
* 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.android.synthetic
import com.intellij.mock.MockProject
import com.intellij.openapi.project.Project
import kotlinx.android.extensions.CacheImplementation
import org.jetbrains.kotlin.android.parcel.ParcelableAnnotationChecker
import org.jetbrains.kotlin.android.parcel.ParcelableCodegenExtension
import org.jetbrains.kotlin.android.parcel.ParcelableDeclarationChecker
import org.jetbrains.kotlin.android.parcel.ParcelableResolveExtension
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidExtensionsExpressionCodegenExtension
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidOnDestroyClassBuilderInterceptorExtension
import org.jetbrains.kotlin.android.synthetic.codegen.ParcelableClinitClassBuilderInterceptorExtension
import org.jetbrains.kotlin.android.synthetic.diagnostic.AndroidExtensionPropertiesCallChecker
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
import org.jetbrains.kotlin.android.synthetic.res.AndroidVariant
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidLayoutXmlFileManager
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidPackageFragmentProviderExtension
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.compiler.plugin.*
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.utils.decodePluginOptions
object AndroidConfigurationKeys {
val VARIANT = CompilerConfigurationKey.create<List<String>>("Android build variant")
val PACKAGE = CompilerConfigurationKey.create<String>("application package fq name")
val EXPERIMENTAL = CompilerConfigurationKey.create<String>("enable experimental features")
val DEFAULT_CACHE_IMPL = CompilerConfigurationKey.create<String>("default cache implementation")
val FEATURES = CompilerConfigurationKey.create<Set<AndroidExtensionsFeature>>("enabled features")
}
enum class AndroidExtensionsFeature(val featureName: String) {
VIEWS("views"),
PARCELIZE("parcelize")
}
class AndroidCommandLineProcessor : CommandLineProcessor {
companion object {
val ANDROID_COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.android"
val CONFIGURATION = CliOption("configuration", "<encoded>", "Encoded configuration", required = false)
val VARIANT_OPTION = CliOption("variant", "<name;path>", "Android build variant", allowMultipleOccurrences = true, required = false)
val PACKAGE_OPTION = CliOption("package", "<fq name>", "Application package", required = false)
val EXPERIMENTAL_OPTION = CliOption("experimental", "true/false", "Enable experimental features", required = false)
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)
}
override val pluginId: String = ANDROID_COMPILER_PLUGIN_ID
override val pluginOptions: Collection<AbstractCliOption>
= listOf(VARIANT_OPTION, PACKAGE_OPTION, EXPERIMENTAL_OPTION, DEFAULT_CACHE_IMPL_OPTION, CONFIGURATION, FEATURES_OPTION)
override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) {
when (option) {
VARIANT_OPTION -> configuration.appendList(AndroidConfigurationKeys.VARIANT, value)
PACKAGE_OPTION -> configuration.put(AndroidConfigurationKeys.PACKAGE, value)
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.optionName}")
}
}
}
class AndroidComponentRegistrar : ComponentRegistrar {
companion object {
fun registerParcelExtensions(project: Project) {
ExpressionCodegenExtension.registerExtension(project, ParcelableCodegenExtension())
SyntheticResolveExtension.registerExtension(project, ParcelableResolveExtension())
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
else -> CacheImplementation.DEFAULT
}
}
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
val features = configuration.get(AndroidConfigurationKeys.FEATURES) ?: AndroidExtensionsFeature.values().toSet()
val isExperimental = configuration.get(AndroidConfigurationKeys.EXPERIMENTAL) == "true"
if (AndroidExtensionsFeature.PARCELIZE in features) {
registerParcelExtensions(project)
}
if (AndroidExtensionsFeature.VIEWS in features) {
registerViewExtensions(configuration, isExperimental, project)
}
}
}
class AndroidExtensionPropertiesComponentContainerContributor : StorageComponentContainerContributor {
override fun registerModuleComponents(
container: StorageComponentContainer, platform: TargetPlatform, moduleDescriptor: ModuleDescriptor
) {
if (platform != JvmPlatform) return
container.useInstance(AndroidExtensionPropertiesCallChecker())
container.useInstance(ParcelableDeclarationChecker())
container.useInstance(ParcelableAnnotationChecker())
}
}