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
This commit is contained in:
@@ -23,6 +23,7 @@ java_defaults {
|
|||||||
"src/com/android/checkflaggedapis/Main.kt",
|
"src/com/android/checkflaggedapis/Main.kt",
|
||||||
],
|
],
|
||||||
static_libs: [
|
static_libs: [
|
||||||
|
"metalava-signature-reader",
|
||||||
"metalava-tools-common-m2-deps",
|
"metalava-tools-common-m2-deps",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
@@ -17,10 +17,28 @@ package com.android.checkflaggedapis
|
|||||||
|
|
||||||
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
|
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
|
||||||
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
|
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
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)
|
@RunWith(DeviceJUnit4ClassRunner::class)
|
||||||
class CheckFlaggedApisTest : BaseHostJUnit4Test() {
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,8 +17,16 @@
|
|||||||
|
|
||||||
package com.android.checkflaggedapis
|
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.CliktCommand
|
||||||
import com.github.ajalt.clikt.core.ProgramResult
|
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.
|
* 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()
|
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() {
|
override fun run() {
|
||||||
println("hello world")
|
@Suppress("UNUSED_VARIABLE")
|
||||||
|
val flaggedSymbols =
|
||||||
|
apiSignaturePath.toFile().inputStream().use {
|
||||||
|
parseApiSignature(apiSignaturePath.toString(), it)
|
||||||
|
}
|
||||||
throw ProgramResult(0)
|
throw ProgramResult(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun parseApiSignature(path: String, input: InputStream): Set<Pair<Symbol, Flag>> {
|
||||||
|
// TODO(334870672): add support for classes and metods
|
||||||
|
val output = mutableSetOf<Pair<Symbol, Flag>>()
|
||||||
|
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<String>) = CheckCommand().main(args)
|
fun main(args: Array<String>) = CheckCommand().main(args)
|
||||||
|
Reference in New Issue
Block a user