Add argument for base API during compat checks
When feeding metalava a partial codebase as source, there is currently
no way to provide it with the "rest" of the codebase for purposes of
compat tracking. Add an argument for that.
Bug: 174847574
Test: m frameworks-base-api-system-current-compat
Change-Id: I32f245921155ce55a1146b56e98dcafae6fc223a
diff --git a/src/main/java/com/android/tools/metalava/Driver.kt b/src/main/java/com/android/tools/metalava/Driver.kt
index cc1dded..a826dc0 100644
--- a/src/main/java/com/android/tools/metalava/Driver.kt
+++ b/src/main/java/com/android/tools/metalava/Driver.kt
@@ -602,6 +602,12 @@
kotlinStyleNulls = options.inputKotlinStyleNulls
)
} else if (!options.showUnannotated || apiType != ApiType.PUBLIC_API) {
+ if (options.baseApiForCompatCheck != null) {
+ // This option does not make sense with showAnnotation, as the "base" in that case
+ // is the non-annotated APIs.
+ throw DriverException(ARG_CHECK_COMPATIBILITY_BASE_API +
+ " is not compatible with --showAnnotation.")
+ }
val apiFile = apiType.getSignatureFile(codebase, "compat-check-signatures-$apiType")
// Fast path: if the signature files are identical, we're already good!
@@ -622,6 +628,14 @@
return
}
+ val baseApiFile = options.baseApiForCompatCheck
+ if (baseApiFile != null) {
+ base = SignatureFileLoader.load(
+ file = baseApiFile,
+ kotlinStyleNulls = options.inputKotlinStyleNulls
+ )
+ }
+
codebase
}
diff --git a/src/main/java/com/android/tools/metalava/Options.kt b/src/main/java/com/android/tools/metalava/Options.kt
index 850d8f4..52bd926 100644
--- a/src/main/java/com/android/tools/metalava/Options.kt
+++ b/src/main/java/com/android/tools/metalava/Options.kt
@@ -93,6 +93,7 @@
const val ARG_CHECK_COMPATIBILITY_API_RELEASED = "--check-compatibility:api:released"
const val ARG_CHECK_COMPATIBILITY_REMOVED_CURRENT = "--check-compatibility:removed:current"
const val ARG_CHECK_COMPATIBILITY_REMOVED_RELEASED = "--check-compatibility:removed:released"
+const val ARG_CHECK_COMPATIBILITY_BASE_API = "--check-compatibility:base"
const val ARG_ALLOW_COMPATIBLE_DIFFERENCES = "--allow-compatible-differences"
const val ARG_NO_NATIVE_DIFF = "--no-native-diff"
const val ARG_INPUT_KOTLIN_NULLS = "--input-kotlin-nulls"
@@ -500,6 +501,9 @@
/** The list of compatibility checks to run */
val compatibilityChecks: List<CheckRequest> = mutableCompatibilityChecks
+ /** The API to use a base for the otherwise checked API during compat checks. */
+ var baseApiForCompatCheck: File? = null
+
/**
* When checking signature files, whether compatible differences in signature
* files are allowed. This is normally not allowed (since it means the next
@@ -1100,6 +1104,11 @@
mutableCompatibilityChecks.add(CheckRequest(file, ApiType.REMOVED, ReleaseType.RELEASED))
}
+ ARG_CHECK_COMPATIBILITY_BASE_API -> {
+ val file = stringToExistingFile(getValue(args, ++index))
+ baseApiForCompatCheck = file
+ }
+
ARG_ALLOW_COMPATIBLE_DIFFERENCES -> allowCompatibleDifferences = true
ARG_NO_NATIVE_DIFF -> noNativeDiff = true
@@ -2384,6 +2393,11 @@
"released API, respectively. Different compatibility checks apply in the two scenarios. " +
"For example, to check the code base against the current public API, use " +
"$ARG_CHECK_COMPATIBILITY:api:current.",
+ "$ARG_CHECK_COMPATIBILITY_BASE_API <file>", "When performing a compat check, use the provided signature " +
+ "file as a base api, which is treated as part of the API being checked. This allows us to compute the " +
+ "full API surface from a partial API surface (e.g. the current @SystemApi txt file), which allows us to " +
+ "recognize when an API is moved from the partial API to the base API and avoid incorrectly flagging this " +
+ "as an API removal.",
"$ARG_API_LINT [api file]", "Check API for Android API best practices. If a signature file is " +
"provided, only the APIs that are new since the API will be checked.",
"$ARG_API_LINT_IGNORE_PREFIX [prefix]", "A list of package prefixes to ignore API issues in " +
diff --git a/src/test/java/com/android/tools/metalava/CompatibilityCheckTest.kt b/src/test/java/com/android/tools/metalava/CompatibilityCheckTest.kt
index cabb328..d61a6a7 100644
--- a/src/test/java/com/android/tools/metalava/CompatibilityCheckTest.kt
+++ b/src/test/java/com/android/tools/metalava/CompatibilityCheckTest.kt
@@ -2258,6 +2258,39 @@
}
@Test
+ fun `Test check release with base api`() {
+ check(
+ expectedIssues = "",
+ checkCompatibilityApiReleased = """
+ package test.pkg {
+ public class SomeClass {
+ method public static void publicMethodA();
+ method public static void publicMethodB();
+ }
+ }
+ """,
+ sourceFiles = arrayOf(
+ java(
+ """
+ package test.pkg;
+
+ public class SomeClass {
+ public static void publicMethodA();
+ }
+ """
+ )
+ ),
+ checkCompatibilityBaseApi = """
+ package test.pkg {
+ public class SomeClass {
+ method public static void publicMethodB();
+ }
+ }
+ """
+ )
+ }
+
+ @Test
fun `Implicit nullness`() {
check(
compatibilityMode = false,
diff --git a/src/test/java/com/android/tools/metalava/DriverTest.kt b/src/test/java/com/android/tools/metalava/DriverTest.kt
index b7f99b3..21b9c70 100644
--- a/src/test/java/com/android/tools/metalava/DriverTest.kt
+++ b/src/test/java/com/android/tools/metalava/DriverTest.kt
@@ -311,6 +311,9 @@
/** An optional API signature to check the last released removed API's compatibility with */
@Language("TEXT")
checkCompatibilityRemovedApiReleased: String? = null,
+ /** An optional API signature to use as the base API codebase during compat checks */
+ @Language("TEXT")
+ checkCompatibilityBaseApi: String? = null,
/** An optional API signature to compute nullness migration status from */
allowCompatibleDifferences: Boolean = true,
@Language("TEXT")
@@ -658,6 +661,19 @@
null
}
+ val checkCompatibilityBaseApiFile = if (checkCompatibilityBaseApi != null) {
+ val maybeFile = File(checkCompatibilityBaseApi)
+ if (maybeFile.isFile) {
+ maybeFile
+ } else {
+ val file = File(project, "compatibility-base-api.txt")
+ file.writeText(checkCompatibilityBaseApi.trimIndent())
+ file
+ }
+ } else {
+ null
+ }
+
val migrateNullsApiFile = if (migrateNullsApi != null) {
val jar = File(migrateNullsApi)
if (jar.isFile) {
@@ -702,6 +718,12 @@
emptyArray()
}
+ val checkCompatibilityBaseApiArguments = if (checkCompatibilityBaseApiFile != null) {
+ arrayOf(ARG_CHECK_COMPATIBILITY_BASE_API, checkCompatibilityBaseApiFile.path)
+ } else {
+ emptyArray()
+ }
+
val checkCompatibilityRemovedCurrentArguments = if (checkCompatibilityRemovedApiCurrentFile != null) {
val extra: Array<String> = if (allowCompatibleDifferences) {
arrayOf(ARG_ALLOW_COMPATIBLE_DIFFERENCES)
@@ -1146,6 +1168,7 @@
*migrateNullsArguments,
*checkCompatibilityArguments,
*checkCompatibilityApiReleasedArguments,
+ *checkCompatibilityBaseApiArguments,
*checkCompatibilityRemovedCurrentArguments,
*checkCompatibilityRemovedReleasedArguments,
*proguardKeepArguments,
diff --git a/src/test/java/com/android/tools/metalava/OptionsTest.kt b/src/test/java/com/android/tools/metalava/OptionsTest.kt
index fcb4770..6797b24 100644
--- a/src/test/java/com/android/tools/metalava/OptionsTest.kt
+++ b/src/test/java/com/android/tools/metalava/OptionsTest.kt
@@ -262,6 +262,13 @@
publicly released API, respectively. Different compatibility checks apply
in the two scenarios. For example, to check the code base against the
current public API, use --check-compatibility:api:current.
+--check-compatibility:base <file>
+ When performing a compat check, use the provided signature file as a base
+ api, which is treated as part of the API being checked. This allows us to
+ compute the full API surface from a partial API surface (e.g. the current
+ @SystemApi txt file), which allows us to recognize when an API is moved
+ from the partial API to the base API and avoid incorrectly flagging this as
+ an API removal.
--api-lint [api file]
Check API for Android API best practices. If a signature file is provided,
only the APIs that are new since the API will be checked.