From 20de405dd58c8bd93e562eb88cd91ff906c9fa43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Tue, 16 Apr 2024 11:33:56 +0200 Subject: [PATCH] check-flagged-apis: parse API signature files Teach check-flagged-apis to extract flagged APIs from API signature files. To keep things simple, only consider fields for now: support for classes and methods will be added in a later CL. Note: `m frameworks-base-api-current.txt` will generate an API signature file that includes both the platform and mainline APIs. Bug: 334870672 Test: atest --host check-flagged-apis-test Test: check-flagged-apis --api-signature out/target/product/mainline_x86/obj/ETC/frameworks-base-api-current.txt_intermediates/frameworks-base-api-current.txt Change-Id: Ic244b896672569f44af793796189b34c1f9d0c36 --- tools/check-flagged-apis/Android.bp | 1 + .../checkflaggedapis/CheckFlaggedApisTest.kt | 20 ++++++- .../src/com/android/checkflaggedapis/Main.kt | 59 ++++++++++++++++++- 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/tools/check-flagged-apis/Android.bp b/tools/check-flagged-apis/Android.bp index c17c9b2895..7d0d33b383 100644 --- a/tools/check-flagged-apis/Android.bp +++ b/tools/check-flagged-apis/Android.bp @@ -23,6 +23,7 @@ java_defaults { "src/com/android/checkflaggedapis/Main.kt", ], static_libs: [ + "metalava-signature-reader", "metalava-tools-common-m2-deps", ], } diff --git a/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt b/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt index badcdeed13..d7890d7583 100644 --- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt +++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt @@ -17,10 +17,28 @@ package com.android.checkflaggedapis import com.android.tradefed.testtype.DeviceJUnit4ClassRunner import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test +import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith +private val API_SIGNATURE = + """ + // Signature format: 2.0 + package android { + public final class Clazz { + ctor public Clazz(); + field @FlaggedApi("android.flag.foo") public static final int FOO = 1; // 0x1 + } + } +""" + .trim() + @RunWith(DeviceJUnit4ClassRunner::class) class CheckFlaggedApisTest : BaseHostJUnit4Test() { - @Test fun testPlaceholder() {} + @Test + fun testParseApiSignature() { + val expected = setOf(Pair(Symbol("android.Clazz.FOO"), Flag("android.flag.foo"))) + val actual = parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()) + assertEquals(expected, actual) + } } diff --git a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt index af8be11606..5fede7b86c 100644 --- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt +++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt @@ -17,8 +17,16 @@ package com.android.checkflaggedapis +import com.android.tools.metalava.model.BaseItemVisitor +import com.android.tools.metalava.model.FieldItem +import com.android.tools.metalava.model.text.ApiFile import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.ProgramResult +import com.github.ajalt.clikt.parameters.options.help +import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.required +import com.github.ajalt.clikt.parameters.types.path +import java.io.InputStream /** * Class representing the fully qualified name of a class, method or field. @@ -70,11 +78,58 @@ internal value class Flag(val name: String) { override fun toString(): String = name.toString() } -class CheckCommand : CliktCommand() { +class CheckCommand : + CliktCommand( + help = + """ +Check that all flagged APIs are used in the correct way. + +This tool reads the API signature file and checks that all flagged APIs are used in the correct way. + +The tool will exit with a non-zero exit code if any flagged APIs are found to be used in the incorrect way. +""") { + private val apiSignaturePath by + option("--api-signature") + .help( + """ + Path to API signature file. + Usually named *current.txt. + Tip: `m frameworks-base-api-current.txt` will generate a file that includes all platform and mainline APIs. + """) + .path(mustExist = true, canBeDir = false, mustBeReadable = true) + .required() + override fun run() { - println("hello world") + @Suppress("UNUSED_VARIABLE") + val flaggedSymbols = + apiSignaturePath.toFile().inputStream().use { + parseApiSignature(apiSignaturePath.toString(), it) + } throw ProgramResult(0) } } +internal fun parseApiSignature(path: String, input: InputStream): Set> { + // TODO(334870672): add support for classes and metods + val output = mutableSetOf>() + val visitor = + object : BaseItemVisitor() { + override fun visitField(field: FieldItem) { + val flag = + field.modifiers + .findAnnotation("android.annotation.FlaggedApi") + ?.findAttribute("value") + ?.value + ?.value() as? String + if (flag != null) { + val symbol = Symbol.create(field.baselineElementId()) + output.add(Pair(symbol, Flag(flag))) + } + } + } + val codebase = ApiFile.parseApi(path, input) + codebase.accept(visitor) + return output +} + fun main(args: Array) = CheckCommand().main(args)