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