1 /*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package androidx.build
18
19 import androidx.build.SoftwareType.Companion.BENCHMARK
20 import androidx.build.SoftwareType.Companion.SAMPLES
21 import androidx.build.SoftwareType.Companion.TEST_APPLICATION
22 import androidx.build.SoftwareType.Companion.UNSET
23 import kotlin.collections.contains
24
25 /**
26 * Represents the purpose and configuration of a software project, including how it is published,
27 * whether it enforces API compatibility checks, and which environment it targets. By using
28 * [SoftwareType], developers can select from predefined library configurations or create their own
29 * through [ConfigurableSoftwareType]. This reduces complexity by capturing a library's behavior and
30 * rationale in one place, rather than requiring manual configuration of multiple independent
31 * properties.
32 *
33 * The key properties controlled by [SoftwareType] are:
34 * - [publish]: Defines how (or if) the software is published to external repositories (e.g.,
35 * GMaven).
36 * - [checkApi]: Determines whether API compatibility tasks are run, which enforce semantic
37 * versioning and API stability.
38 * - [compilationTarget]: Specifies whether the software runs on a host machine or an Android
39 * device.
40 * - [allowCallingVisibleForTestsApis]: Indicates whether calling `@VisibleForTesting` APIs is
41 * allowed, useful for test libraries.
42 * - [targetsKotlinConsumersOnly]: When `true`, the software is intended for Kotlin consumers only,
43 * allowing for more Kotlin-centric API design.
44 * - [isForTesting]: When `true`, the library is intended to serve as a testing artifact only, not
45 * meant for usage in production.
46 *
47 * [SoftwareType] includes a variety of predefined configurations commonly used in Android projects:
48 * - Conventional published libraries ([PUBLISHED_LIBRARY], [PUBLISHED_PROTO_LIBRARY], etc.)
49 * - Internal libraries not published externally ([INTERNAL_TEST_LIBRARY],
50 * [INTERNAL_HOST_TEST_LIBRARY])
51 * - Test libraries that allow testing internal or unstable APIs ([PUBLISHED_TEST_LIBRARY],
52 * [INTERNAL_TEST_LIBRARY])
53 * - Lint rule sets ([LINT], [STANDALONE_PUBLISHED_LINT]) for guiding correct usage of a library
54 * - Libraries containing samples to supplement documentation ([SAMPLES])
55 * - Host-only libraries such as Gradle plugins, annotation processors, and code generators
56 * ([GRADLE_PLUGIN], [ANNOTATION_PROCESSOR], [OTHER_CODE_PROCESSOR])
57 * - Libraries specifically meant for IDE consumption ([IDE_PLUGIN])
58 * - Snapshot-only libraries for early access or development use cases
59 * ([SNAPSHOT_ONLY_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS], etc.)
60 * - Libraries that do not publish artifacts but still run API tasks, or vice versa
61 * ([INTERNAL_LIBRARY_WITH_API_TASKS], [SNAPSHOT_ONLY_LIBRARY_WITH_API_TASKS])
62 * - [UNSET]: a default or transitional state indicating the library's type isn't fully determined
63 *
64 * Although predefined software types cover many common scenarios, you can create new
65 * [ConfigurableSoftwareType] instances if your project requires a unique combination of publish
66 * settings, API checking, and compilation targeting. In doing so, you ensure the project's
67 * configuration is concise, clear, and consistently applied.
68 */
69 sealed class SoftwareType(
70 val name: String,
71 val publish: Publish = Publish.NONE,
72 val checkApi: RunApiTasks = RunApiTasks.No("Unknown Software Type"),
73 val compilationTarget: CompilationTarget = CompilationTarget.DEVICE,
74 val allowCallingVisibleForTestsApis: Boolean = false,
75 val targetsKotlinConsumersOnly: Boolean = false,
76 val isForTesting: Boolean = false,
77 ) {
78 class ConfigurableSoftwareType(
79 name: String,
80 publish: Publish = Publish.NONE,
81 checkApi: RunApiTasks = RunApiTasks.No("Unknown Software Type"),
82 compilationTarget: CompilationTarget = CompilationTarget.DEVICE,
83 allowCallingVisibleForTestsApis: Boolean = false,
84 targetsKotlinConsumersOnly: Boolean = false,
85 isForTesting: Boolean = true
86 ) :
87 SoftwareType(
88 name,
89 publish,
90 checkApi,
91 compilationTarget,
92 allowCallingVisibleForTestsApis,
93 targetsKotlinConsumersOnly,
94 isForTesting,
95 )
96
97 companion object {
98 // Host-only tooling libraries
99 @JvmStatic
100 val ANNOTATION_PROCESSOR =
101 ConfigurableSoftwareType(
102 name = "ANNOTATION_PROCESSOR",
103 publish = Publish.SNAPSHOT_AND_RELEASE,
104 checkApi = RunApiTasks.No("Annotation Processor"),
105 compilationTarget = CompilationTarget.HOST
106 )
107
108 @JvmStatic
109 val ANNOTATION_PROCESSOR_UTILS =
110 ConfigurableSoftwareType(
111 name = "ANNOTATION_PROCESSOR_UTILS",
112 publish = Publish.SNAPSHOT_AND_RELEASE,
113 checkApi = RunApiTasks.No("Annotation Processor Helper Library"),
114 compilationTarget = CompilationTarget.HOST
115 )
116
117 @JvmStatic
118 val GRADLE_PLUGIN =
119 ConfigurableSoftwareType(
120 name = "GRADLE_PLUGIN",
121 publish = Publish.SNAPSHOT_AND_RELEASE,
122 checkApi = RunApiTasks.No("Gradle Plugin (Host-only)"),
123 compilationTarget = CompilationTarget.HOST
124 )
125
126 @JvmStatic
127 val OTHER_CODE_PROCESSOR =
128 ConfigurableSoftwareType(
129 name = "OTHER_CODE_PROCESSOR",
130 publish = Publish.SNAPSHOT_AND_RELEASE,
131 checkApi = RunApiTasks.No("Code Processor (Host-only)"),
132 compilationTarget = CompilationTarget.HOST
133 )
134
135 // Lint libraries
136 @JvmStatic
137 val LINT =
138 ConfigurableSoftwareType(
139 name = "LINT",
140 checkApi = RunApiTasks.No("Lint Library"),
141 compilationTarget = CompilationTarget.HOST
142 )
143
144 @JvmStatic
145 val STANDALONE_PUBLISHED_LINT =
146 ConfigurableSoftwareType(
147 name = "STANDALONE_PUBLISHED_LINT",
148 publish = Publish.SNAPSHOT_AND_RELEASE,
149 checkApi = RunApiTasks.No("Lint Library"),
150 compilationTarget = CompilationTarget.HOST
151 )
152
153 // Published libraries
154 @JvmStatic
155 val PUBLISHED_LIBRARY =
156 ConfigurableSoftwareType(
157 name = "PUBLISHED_LIBRARY",
158 publish = Publish.SNAPSHOT_AND_RELEASE,
159 checkApi = RunApiTasks.Yes()
160 )
161
162 @JvmStatic
163 val PUBLISHED_PROTO_LIBRARY =
164 ConfigurableSoftwareType(
165 name = "PUBLISHED_PROTO_LIBRARY",
166 publish = Publish.SNAPSHOT_AND_RELEASE,
167 checkApi =
168 RunApiTasks.No("Metalava doesn't properly parse the proto sources b/180579063")
169 )
170
171 @JvmStatic
172 val PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS =
173 ConfigurableSoftwareType(
174 name = "PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS",
175 publish = Publish.SNAPSHOT_AND_RELEASE,
176 checkApi = RunApiTasks.Yes(),
177 targetsKotlinConsumersOnly = true
178 )
179
180 // Published test libraries
181 @JvmStatic
182 val PUBLISHED_TEST_LIBRARY =
183 ConfigurableSoftwareType(
184 name = "PUBLISHED_TEST_LIBRARY",
185 publish = Publish.SNAPSHOT_AND_RELEASE,
186 checkApi = RunApiTasks.Yes(),
187 allowCallingVisibleForTestsApis = true,
188 isForTesting = true,
189 )
190
191 @JvmStatic
192 val PUBLISHED_KOTLIN_ONLY_TEST_LIBRARY =
193 ConfigurableSoftwareType(
194 name = "PUBLISHED_KOTLIN_ONLY_TEST_LIBRARY",
195 publish = Publish.SNAPSHOT_AND_RELEASE,
196 checkApi = RunApiTasks.Yes(),
197 allowCallingVisibleForTestsApis = true,
198 targetsKotlinConsumersOnly = true,
199 isForTesting = true,
200 )
201
202 // Snapshot-only libraries
203 @JvmStatic
204 val SNAPSHOT_ONLY_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS =
205 ConfigurableSoftwareType(
206 name = "SNAPSHOT_ONLY_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS",
207 publish = Publish.SNAPSHOT_ONLY,
208 checkApi = RunApiTasks.Yes(),
209 targetsKotlinConsumersOnly = true
210 )
211
212 @JvmStatic
213 val SNAPSHOT_ONLY_TEST_LIBRARY_WITH_API_TASKS =
214 ConfigurableSoftwareType(
215 name = "SNAPSHOT_ONLY_TEST_LIBRARY_WITH_API_TASKS",
216 publish = Publish.SNAPSHOT_ONLY,
217 checkApi = RunApiTasks.Yes(),
218 allowCallingVisibleForTestsApis = true
219 )
220
221 @JvmStatic
222 val SNAPSHOT_ONLY_LIBRARY_WITH_API_TASKS =
223 ConfigurableSoftwareType(
224 name = "SNAPSHOT_ONLY_LIBRARY_WITH_API_TASKS",
225 publish = Publish.SNAPSHOT_ONLY,
226 checkApi = RunApiTasks.Yes("Snapshot-only library that runs API tasks")
227 )
228
229 @JvmStatic
230 val SNAPSHOT_ONLY_LIBRARY =
231 ConfigurableSoftwareType(
232 name = "SNAPSHOT_ONLY_LIBRARY",
233 publish = Publish.SNAPSHOT_ONLY,
234 checkApi = RunApiTasks.No("Snapshot-only library that does not run API tasks")
235 )
236
237 // Samples library
238 @JvmStatic
239 val SAMPLES =
240 ConfigurableSoftwareType(
241 name = "SAMPLES",
242 publish = Publish.SNAPSHOT_AND_RELEASE,
243 checkApi = RunApiTasks.No("Sample Library")
244 )
245
246 // IDE libraries
247 @JvmStatic
248 val IDE_PLUGIN =
249 ConfigurableSoftwareType(
250 name = "IDE_PLUGIN",
251 checkApi = RunApiTasks.No("IDE Plugin (consumed only by Android Studio)"),
252 compilationTarget = CompilationTarget.DEVICE
253 )
254
255 // Internal libraries
256 @JvmStatic
257 val INTERNAL_GRADLE_PLUGIN =
258 ConfigurableSoftwareType(
259 name = "INTERNAL_GRADLE_PLUGIN",
260 checkApi = RunApiTasks.No("Internal Gradle Plugin"),
261 compilationTarget = CompilationTarget.HOST
262 )
263
264 @JvmStatic
265 val INTERNAL_HOST_TEST_LIBRARY =
266 ConfigurableSoftwareType(
267 name = "INTERNAL_HOST_TEST_LIBRARY",
268 checkApi = RunApiTasks.No("Internal Library"),
269 compilationTarget = CompilationTarget.HOST,
270 isForTesting = true,
271 )
272
273 @JvmStatic
274 val INTERNAL_LIBRARY_WITH_API_TASKS =
275 ConfigurableSoftwareType(
276 name = "INTERNAL_LIBRARY_WITH_API_TASKS",
277 checkApi = RunApiTasks.Yes("Always run API tasks even if not published")
278 )
279
280 @JvmStatic
281 val INTERNAL_OTHER_CODE_PROCESSOR =
282 ConfigurableSoftwareType(
283 name = "INTERNAL_OTHER_CODE_PROCESSOR",
284 checkApi = RunApiTasks.No("Code Processor (Host-only)"),
285 compilationTarget = CompilationTarget.HOST
286 )
287
288 @JvmStatic
289 val INTERNAL_TEST_LIBRARY =
290 ConfigurableSoftwareType(
291 name = "INTERNAL_TEST_LIBRARY",
292 checkApi = RunApiTasks.No("Internal Library"),
293 allowCallingVisibleForTestsApis = true,
294 isForTesting = true,
295 )
296
297 // Misc libraries
298 @JvmStatic
299 val BENCHMARK =
300 ConfigurableSoftwareType(
301 name = "BENCHMARK",
302 checkApi = RunApiTasks.No("Benchmark Library"),
303 allowCallingVisibleForTestsApis = true
304 )
305
306 @JvmStatic
307 val TEST_APPLICATION =
308 ConfigurableSoftwareType(
309 name = "TEST_APPLICATION",
310 checkApi = RunApiTasks.No("Test App")
311 )
312
313 val UNSET = ConfigurableSoftwareType(name = "UNSET")
314
<lambda>null315 private val allTypes: Map<String, SoftwareType> by lazy {
316 listOf(
317 PUBLISHED_LIBRARY,
318 PUBLISHED_PROTO_LIBRARY,
319 PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS,
320 PUBLISHED_TEST_LIBRARY,
321 PUBLISHED_KOTLIN_ONLY_TEST_LIBRARY,
322 INTERNAL_GRADLE_PLUGIN,
323 INTERNAL_HOST_TEST_LIBRARY,
324 INTERNAL_LIBRARY_WITH_API_TASKS,
325 INTERNAL_OTHER_CODE_PROCESSOR,
326 INTERNAL_TEST_LIBRARY,
327 SAMPLES,
328 SNAPSHOT_ONLY_LIBRARY,
329 SNAPSHOT_ONLY_LIBRARY_WITH_API_TASKS,
330 SNAPSHOT_ONLY_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS,
331 SNAPSHOT_ONLY_TEST_LIBRARY_WITH_API_TASKS,
332 TEST_APPLICATION,
333 LINT,
334 STANDALONE_PUBLISHED_LINT,
335 GRADLE_PLUGIN,
336 ANNOTATION_PROCESSOR,
337 ANNOTATION_PROCESSOR_UTILS,
338 BENCHMARK,
339 OTHER_CODE_PROCESSOR,
340 IDE_PLUGIN,
341 UNSET
342 )
343 .associateBy { it.name }
344 }
345
valueOfnull346 fun valueOf(name: String): SoftwareType {
347 return requireNotNull(allTypes[name]) { "SoftwareType with name $name not found" }
348 }
349 }
350 }
351
requiresDependencyVerificationnull352 fun SoftwareType.requiresDependencyVerification(): Boolean =
353 this !in listOf(BENCHMARK, SAMPLES, TEST_APPLICATION, UNSET)
354
355 enum class CompilationTarget {
356 /** This library is meant to run on the host machine (like an annotation processor). */
357 HOST,
358 /** This library is meant to run on an Android device. */
359 DEVICE
360 }
361
362 /**
363 * Publish Enum: Publish.NONE -> Generates no artifacts; does not generate snapshot artifacts or
364 * releasable maven artifacts Publish.SNAPSHOT_ONLY -> Only generates snapshot artifacts
365 * Publish.SNAPSHOT_AND_RELEASE -> Generates both snapshot artifacts and releasable maven artifact
366 */
367 enum class Publish {
368 NONE,
369 SNAPSHOT_ONLY,
370 SNAPSHOT_AND_RELEASE;
371
shouldReleasenull372 fun shouldRelease() = this == SNAPSHOT_AND_RELEASE
373
374 fun shouldPublish() = shouldRelease() || this == SNAPSHOT_ONLY
375 }
376
377 sealed class RunApiTasks {
378
379 /** Always run API tasks regardless of other project properties. */
380 data class Yes(val reason: String? = null) : RunApiTasks()
381
382 /** Do not run any API tasks. */
383 data class No(val reason: String) : RunApiTasks()
384 }
385
isLintnull386 fun SoftwareType.isLint() =
387 this == SoftwareType.LINT || this == SoftwareType.STANDALONE_PUBLISHED_LINT
388