/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.build.checkapi
import androidx.build.Version
import androidx.build.version
import java.io.File
import java.io.Serializable
import org.gradle.api.Project
import org.gradle.api.file.Directory
import org.gradle.api.provider.Provider
private const val BCV_DIR_NAME = "bcv"
/**
* Contains information about the files used to record a library's API surfaces. This class may
* represent a versioned API txt file or the "current" API txt file.
*
*
* This class is responsible for understanding the naming pattern used by various types of API
* files:
*
* - public
*
- restricted
*
- resource
*
*/
data class ApiLocation(
// Directory where the library's API files are stored
val apiFileDirectory: File,
// File where the library's public API surface is recorded
val publicApiFile: File,
// File where the library's public plus restricted (see @RestrictTo) API surfaces are recorded
val restrictedApiFile: File,
// File where the library's public resources are recorded
val resourceFile: File,
// Directory where the library's stable AIDL surface is recorded
val aidlApiDirectory: File,
// File where the API version history is recorded, for use in docs
val apiLevelsFile: File
) : Serializable {
/**
* Returns the library version represented by this API location, or {@code null} if this is a
* current API file.
*/
fun version(): Version? {
val baseName = publicApiFile.nameWithoutExtension
if (baseName == CURRENT) {
return null
}
return Version(baseName)
}
companion object {
fun fromPublicApiFile(f: File): ApiLocation {
return fromBaseName(f.parentFile, f.nameWithoutExtension)
}
fun fromVersion(apiFileDir: File, version: Version): ApiLocation {
return fromBaseName(apiFileDir, version.toApiFileBaseName())
}
fun fromCurrent(apiFileDir: File): ApiLocation {
return fromBaseName(apiFileDir, CURRENT)
}
fun isResourceApiFilename(filename: String): Boolean {
return filename.startsWith(PREFIX_RESOURCE)
}
private fun fromBaseName(apiFileDir: File, baseName: String): ApiLocation {
return ApiLocation(
apiFileDirectory = apiFileDir,
publicApiFile = File(apiFileDir, "$baseName$EXTENSION"),
restrictedApiFile = File(apiFileDir, "$PREFIX_RESTRICTED$baseName$EXTENSION"),
resourceFile = File(apiFileDir, "$PREFIX_RESOURCE$baseName$EXTENSION"),
aidlApiDirectory = File(apiFileDir, AIDL_API_DIRECTORY_NAME).resolve(baseName),
apiLevelsFile = File(apiFileDir, API_LEVELS)
)
}
/** File name extension used by API files. */
private const val EXTENSION = ".txt"
/** Base file name used by current API files. */
private const val CURRENT = "current"
/** Prefix used for restricted API surface files. */
private const val PREFIX_RESTRICTED = "restricted_"
/** Prefix used for resource-type API files. */
private const val PREFIX_RESOURCE = "res-"
/** Directory name for location of AIDL API files */
private const val AIDL_API_DIRECTORY_NAME = "aidl"
/** File name for API version history file. */
private const val API_LEVELS = "apiLevels.json"
}
}
/** Converts the version to a valid API file base name. */
private fun Version.toApiFileBaseName(): String {
return getApiFileVersion(this).toString()
}
/** Returns the directory containing the project's versioned and current ABI files. */
fun Project.getBcvFileDirectory(): Directory = project.layout.projectDirectory.dir(BCV_DIR_NAME)
/** Returns the directory containing the project's versioned and current API files. */
fun Project.getApiFileDirectory(): File {
return File(project.projectDir, "api")
}
/** Returns whether the project's API file directory exists. */
fun Project.hasApiFileDirectory(): Boolean {
return project.getApiFileDirectory().exists()
}
/** Returns the directory containing the project's built current API file. */
private fun Project.getBuiltApiFileDirectory(): File {
@Suppress("DEPRECATION") return File(project.buildDir, "api")
}
/** Returns the directory containing the project's built current ABI file. */
fun Project.getBuiltBcvFileDirectory(): Provider =
project.layout.buildDirectory.dir(BCV_DIR_NAME)
/**
* Returns an ApiLocation with the given version, or with the project's current version if not
* specified. This method is guaranteed to return an ApiLocation that represents a versioned API txt
* and not a current API txt.
*
* @param version the project version for which an API file should be returned
* @return an ApiLocation representing a versioned API file
*/
fun Project.getVersionedApiLocation(version: Version = project.version()): ApiLocation {
return ApiLocation.fromVersion(project.getApiFileDirectory(), version)
}
/**
* Returns an ApiLocation for the current version. This method is guaranteed to return an
* ApiLocation that represents a current API txt and not a versioned API txt.
*/
fun Project.getCurrentApiLocation(): ApiLocation {
return ApiLocation.fromCurrent(project.getApiFileDirectory())
}
/**
* Returns an ApiLocation for the "work-in-progress" current version which is built from tip-of-tree
* and lives in the build output directory.
*/
fun Project.getBuiltApiLocation(): ApiLocation {
return ApiLocation.fromCurrent(project.getBuiltApiFileDirectory())
}
/**
* Contains information about the files used to record a library's API compatibility and lint
* violation baselines.
*
*
* This class is responsible for understanding the naming pattern used by various types of API
* compatibility and linting violation baseline files:
*
* - public API compatibility
*
- restricted API compatibility
*
- API lint
*
*/
data class ApiBaselinesLocation(
val ignoreFileDirectory: File,
val publicApiFile: File,
val restrictedApiFile: File,
val apiLintFile: File
) : Serializable {
companion object {
fun fromApiLocation(apiLocation: ApiLocation): ApiBaselinesLocation {
val ignoreFileDirectory = apiLocation.apiFileDirectory
return ApiBaselinesLocation(
ignoreFileDirectory = ignoreFileDirectory,
publicApiFile =
File(
ignoreFileDirectory,
apiLocation.publicApiFile.nameWithoutExtension + EXTENSION
),
restrictedApiFile =
File(
ignoreFileDirectory,
apiLocation.restrictedApiFile.nameWithoutExtension + EXTENSION
),
apiLintFile = File(ignoreFileDirectory, "api_lint$EXTENSION")
)
}
private const val EXTENSION = ".ignore"
}
}