1 /*
<lambda>null2  * Copyright 2018 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.checkapi
18 
19 import androidx.build.Version
20 import androidx.build.checkapi.ApiLocation.Companion.isResourceApiFilename
21 import androidx.build.version
22 import java.io.File
23 import java.nio.file.Files
24 import java.nio.file.Path
25 import kotlin.io.path.name
26 import org.gradle.api.GradleException
27 import org.gradle.api.Project
28 
29 enum class ApiType {
30     CLASSAPI,
31     RESOURCEAPI
32 }
33 
34 /**
35  * Returns the API file containing the public API that this library promises to support This is API
36  * file that checkApiRelease validates against
37  *
38  * @return the API file
39  */
Projectnull40 fun Project.getRequiredCompatibilityApiFile(): File? {
41     return getRequiredCompatibilityApiFileFromDir(
42         project.getApiFileDirectory(),
43         project.version(),
44         ApiType.CLASSAPI
45     )
46 }
47 
48 /*
49  * Same as getRequiredCompatibilityApiFile but also contains a restricted API file
50  */
Projectnull51 fun Project.getRequiredCompatibilityApiLocation(): ApiLocation? {
52     val publicFile = project.getRequiredCompatibilityApiFile() ?: return null
53     return ApiLocation.fromPublicApiFile(publicFile)
54 }
55 
56 /**
57  * Sometimes the version of an API file might be not equal to the version of its artifact. This is
58  * because under certain circumstances, APIs are not allowed to change, and in those cases we may
59  * stop versioning the API. This functions returns the version of API file to use given the version
60  * of an artifact
61  */
getApiFileVersionnull62 fun getApiFileVersion(version: Version): Version {
63     if (!isValidArtifactVersion(version)) {
64         val suggestedVersion = Version("${version.major}.${version.minor}.${version.patch}-rc01")
65         throw GradleException(
66             "Illegal version $version . It is not allowed to have a nonzero " +
67                 "patch number and be alpha or beta at the same time.\n" +
68                 "Did you mean $suggestedVersion?"
69         )
70     }
71     val extra = if (version.patch != 0) "" else version.extra ?: ""
72     return Version(version.major, version.minor, 0, extra)
73 }
74 
75 /** Whether it is allowed for an artifact to have this version */
isValidArtifactVersionnull76 fun isValidArtifactVersion(version: Version): Boolean {
77     if (version.patch != 0 && (version.isAlpha() || version.isBeta() || version.isDev())) {
78         return false
79     }
80     return true
81 }
82 
83 /**
84  * Returns the api file that version <version> is required to be compatible with. If apiType is
85  * RESOURCEAPI, it will return the resource api file and if it is CLASSAPI, it will return the
86  * regular api file.
87  */
getRequiredCompatibilityApiFileFromDirnull88 fun getRequiredCompatibilityApiFileFromDir(
89     apiDir: File,
90     apiVersion: Version,
91     apiType: ApiType
92 ): File? {
93     var highestPath: Path? = null
94     var highestVersion: Version? = null
95 
96     if (!apiDir.exists()) {
97         return null
98     }
99     // Find the path with highest version that is lower than the current API version.
100     Files.newDirectoryStream(apiDir.toPath()).forEach { path ->
101         val pathName = path.name
102         if (
103             (apiType == ApiType.RESOURCEAPI && isResourceApiFilename(pathName)) ||
104                 (apiType == ApiType.CLASSAPI && !isResourceApiFilename(pathName))
105         ) {
106             val pathVersion = Version.parseFilenameOrNull(pathName)
107             if (
108                 pathVersion != null &&
109                     (highestVersion == null || pathVersion > highestVersion!!) &&
110                     pathVersion <= apiVersion &&
111                     pathVersion.isFinalApi() &&
112                     pathVersion.major == apiVersion.major
113             ) {
114                 highestPath = path
115                 highestVersion = pathVersion
116             }
117         }
118     }
119 
120     return highestPath?.toFile()
121 }
122