1 /*
<lambda>null2 * Copyright 2023 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.metalava
18
19 import androidx.build.Version
20 import androidx.build.checkapi.ApiLocation
21 import androidx.build.registerAsComponentForPublishing
22 import java.io.File
23 import org.gradle.api.Project
24 import org.gradle.api.attributes.Bundling
25 import org.gradle.api.attributes.Category
26 import org.gradle.api.attributes.Usage
27 import org.gradle.api.tasks.TaskProvider
28 import org.gradle.kotlin.dsl.named
29
30 /**
31 * Returns the API files that should be used to generate the API levels metadata. This will not
32 * include the current version because the source is used as the current version.
33 */
34 fun getFilesForApiLevels(apiFiles: Collection<File>, currentVersion: Version): List<File> {
35 // Create a map from known versions of the library to signature files
36 val versionToFileMap =
37 apiFiles
38 .mapNotNull { file ->
39 // Resource API files are not included
40 if (ApiLocation.isResourceApiFilename(file.name)) return@mapNotNull null
41 val version = Version.parseFilenameOrNull(file.name)
42 if (version != null) {
43 version to file
44 } else {
45 null
46 }
47 }
48 .toMap()
49
50 val filteredVersions = filterVersions(versionToFileMap, currentVersion)
51 return filteredVersions.map { versionToFileMap.getValue(it) }
52 }
53
54 /**
55 * From the full set of versions, generates a sorted list of the versions to use when generating the
56 * API levels metadata. For previous major-minor version cycles, this only includes the latest
57 * signature file, because we only want one file per stable release. Does not include any files for
58 * the current major-minor version cycle.
59 */
filterVersionsnull60 private fun filterVersions(
61 versionToFileMap: Map<Version, File>,
62 currentVersion: Version
63 ): List<Version> {
64 val filteredVersions = mutableListOf<Version>()
65 var prev: Version? = null
66 for (version in versionToFileMap.keys.sorted()) {
67 // Add the previous version in the list only if this version is a different major.minor
68 // version cycle.
69 if (prev != null && !sameMajorMinor(prev, version)) {
70 filteredVersions.add(prev)
71 }
72 prev = version
73 }
74 // Do not include the current version, as the source is used instead of an API file.
75 if (prev != null && !sameMajorMinor(prev, currentVersion)) {
76 filteredVersions.add(prev)
77 }
78
79 return filteredVersions
80 }
81
sameMajorMinornull82 private fun sameMajorMinor(v1: Version, v2: Version) = v1.major == v2.major && v1.minor == v2.minor
83
84 /** Usage attribute to specify the version metadata component. */
85 internal val Project.versionMetadataUsage: Usage
86 get() = objects.named("library-version-metadata")
87
88 /** Creates a component for the version metadata JSON and registers it for publishing. */
89 internal fun Project.registerVersionMetadataComponent(
90 generateApiTask: TaskProvider<GenerateApiTask>
91 ) {
92 configurations.create("libraryVersionMetadata") { configuration ->
93 configuration.isVisible = false
94 configuration.isCanBeResolved = false
95
96 configuration.attributes.attribute(Usage.USAGE_ATTRIBUTE, project.versionMetadataUsage)
97 configuration.attributes.attribute(
98 Category.CATEGORY_ATTRIBUTE,
99 objects.named<Category>(Category.DOCUMENTATION)
100 )
101 configuration.attributes.attribute(
102 Bundling.BUNDLING_ATTRIBUTE,
103 objects.named<Bundling>(Bundling.EXTERNAL)
104 )
105
106 // The generate API task has many output files, only add the version metadata as an artifact
107 val levelsFile =
108 generateApiTask.map { task ->
109 task.apiLocation.map { location -> location.apiLevelsFile }
110 }
111 configuration.outgoing.artifact(levelsFile) { it.classifier = "versionMetadata" }
112
113 registerAsComponentForPublishing(configuration)
114 }
115 }
116