-
Notifications
You must be signed in to change notification settings - Fork 6
/
DokkatooAndroidAdapter.kt
214 lines (186 loc) · 7.34 KB
/
DokkatooAndroidAdapter.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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
package dev.adamko.dokkatoo.adapters
import com.android.build.api.dsl.CommonExtension
import com.android.build.gradle.AppExtension
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.TestExtension
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.internal.dependency.VariantDependencies
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.CLASSES_JAR
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.PROCESSED_JAR
import dev.adamko.dokkatoo.DokkatooBasePlugin
import dev.adamko.dokkatoo.DokkatooExtension
import dev.adamko.dokkatoo.dokka.parameters.KotlinPlatform
import dev.adamko.dokkatoo.internal.DokkatooInternalApi
import dev.adamko.dokkatoo.internal.collectIncomingFiles
import javax.inject.Inject
import org.gradle.api.DomainObjectSet
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.type.ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE
import org.gradle.api.file.FileCollection
import org.gradle.api.logging.Logging
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ProviderFactory
import org.gradle.kotlin.dsl.*
@DokkatooInternalApi
abstract class DokkatooAndroidAdapter @Inject constructor(
private val objects: ObjectFactory,
) : Plugin<Project> {
override fun apply(project: Project) {
logger.info("applied DokkatooAndroidAdapter to ${project.path}")
project.plugins.withType<DokkatooBasePlugin>().configureEach {
project.pluginManager.apply {
withPlugin("com.android.base") { configure(project) }
withPlugin("com.android.application") { configure(project) }
withPlugin("com.android.library") { configure(project) }
}
}
}
protected fun configure(project: Project) {
val dokkatooExtension = project.extensions.getByType<DokkatooExtension>()
val androidExt = AndroidExtensionWrapper(project)
if (androidExt == null) {
logger.warn("DokkatooAndroidAdapter could not get Android Extension for project ${project.path}")
return
}
dokkatooExtension.dokkatooSourceSets.configureEach {
classpath.from(
analysisPlatform.map { analysisPlatform ->
when (analysisPlatform) {
KotlinPlatform.AndroidJVM ->
AndroidClasspathCollector(
androidExt = androidExt,
configurations = project.configurations,
objects = objects,
)
else ->
objects.fileCollection()
}
}
)
}
}
@DokkatooInternalApi
companion object {
private val logger = Logging.getLogger(DokkatooAndroidAdapter::class.java)
}
}
private fun AndroidExtensionWrapper(
project: Project
): AndroidExtensionWrapper? {
// fetching _all_ configuration names is very brute force and should probably be refined to
// only fetch those that match a specific DokkaSourceSetSpec
return runCatching {
val androidExt = project.extensions.getByType<BaseExtension>()
AndroidExtensionWrapper.forBaseExtension(
androidExt = androidExt,
providers = project.providers,
objects = project.objects
)
}.recoverCatching {
val androidExt = project.extensions.getByType(CommonExtension::class)
AndroidExtensionWrapper.forCommonExtension(androidExt)
}.getOrNull()
}
/**
* Android Gradle Plugin is having a refactor. Try to wrap the Android extension so that Dokkatoo
* can still access the configuration names without caring about which AGP version is in use.
*/
private interface AndroidExtensionWrapper {
fun variantConfigurationNames(): Set<String>
companion object {
@Suppress("DEPRECATION")
fun forBaseExtension(
androidExt: BaseExtension,
providers: ProviderFactory,
objects: ObjectFactory,
): AndroidExtensionWrapper {
return object : AndroidExtensionWrapper {
/** Fetch all configuration names used by all variants. */
override fun variantConfigurationNames(): Set<String> {
val collector = objects.domainObjectSet(BaseVariant::class)
val variants: DomainObjectSet<BaseVariant> =
collector.apply {
addAllLater(providers.provider {
when (androidExt) {
is LibraryExtension -> androidExt.libraryVariants
is AppExtension -> androidExt.applicationVariants
is TestExtension -> androidExt.applicationVariants
else -> emptyList()
}
})
}
return buildSet {
variants.forEach {
add(it.compileConfiguration.name)
add(it.runtimeConfiguration.name)
add(it.annotationProcessorConfiguration.name)
}
}
}
}
}
fun forCommonExtension(
androidExt: CommonExtension<*, *, *, *>
): AndroidExtensionWrapper {
return object : AndroidExtensionWrapper {
/** Fetch all configuration names used by all variants. */
override fun variantConfigurationNames(): Set<String> {
return buildSet {
@Suppress("UnstableApiUsage")
androidExt.sourceSets.forEach {
add(it.apiConfigurationName)
add(it.compileOnlyConfigurationName)
add(it.implementationConfigurationName)
add(it.runtimeOnlyConfigurationName)
add(it.wearAppConfigurationName)
add(it.annotationProcessorConfigurationName)
}
}
}
}
}
}
}
/**
* A utility for determining the classpath of an Android compilation.
*
* It's important that this class is separate from [DokkatooAndroidAdapter]. It must be separate
* because it uses Android Gradle Plugin classes (like [BaseExtension]). Were it not separate, and
* these classes were present in the function signatures of [DokkatooAndroidAdapter], then when
* Gradle tries to create a decorated instance of [DokkatooAndroidAdapter] it will if the project
* does not have the Android Gradle Plugin applied, because the classes will be missing.
*/
private object AndroidClasspathCollector {
operator fun invoke(
androidExt: AndroidExtensionWrapper,
configurations: ConfigurationContainer,
objects: ObjectFactory,
): FileCollection {
val compilationClasspath = objects.fileCollection()
fun collectConfiguration(named: String) {
listOf(
// need to fetch multiple different types of files, because AGP is weird and doesn't seem
// to have a 'just give me normal JVM classes' option
ARTIFACT_TYPE_ATTRIBUTE to PROCESSED_JAR.type,
ARTIFACT_TYPE_ATTRIBUTE to CLASSES_JAR.type,
).forEach { (attribute, attributeValue) ->
configurations.collectIncomingFiles(named, collector = compilationClasspath) {
attributes {
attribute(attribute, attributeValue)
}
lenient(true)
}
}
}
// fetch android.jar
collectConfiguration(named = VariantDependencies.CONFIG_NAME_ANDROID_APIS)
val variantConfigurations = androidExt.variantConfigurationNames()
for (variantConfig in variantConfigurations) {
collectConfiguration(named = variantConfig)
}
return compilationClasspath
}
}