Skip to content
Snippets Groups Projects
Commit b09ebd8d authored by Tor Norbye's avatar Tor Norbye Committed by android-build-merger
Browse files

127458147: Include IntDef annotations in current.txt am: bff0085a

am: 854e54f7

Change-Id: If81c2ab311f5ee817914a23257cb21f8f94e1b66
parents 2d088c13 854e54f7
No related branches found
No related tags found
No related merge requests found
......@@ -148,6 +148,7 @@ const val ARG_STUB_PACKAGES = "--stub-packages"
const val ARG_STUB_IMPORT_PACKAGES = "--stub-import-packages"
const val ARG_DELETE_EMPTY_BASELINES = "--delete-empty-baselines"
const val ARG_SUBTRACT_API = "--subtract-api"
const val ARG_TYPEDEFS_IN_SIGNATURES = "--typedefs-in-signatures"
class Options(
private val args: Array<String>,
......@@ -556,6 +557,15 @@ class Options(
/** List of signature files to export as JDiff files */
val convertToXmlFiles: List<ConvertFile> = mutableConvertToXmlFiles
enum class TypedefMode {
NONE,
REFERENCE,
INLINE
}
/** How to handle typedef annotations in signature files; corresponds to $ARG_TYPEDEFS_IN_SIGNATURES */
var typedefMode = TypedefMode.NONE
/** File conversion tasks */
data class ConvertFile(
val fromApiFile: File,
......@@ -769,6 +779,17 @@ class Options(
mutableSkipEmitPackages += packages.split(File.pathSeparatorChar)
}
ARG_TYPEDEFS_IN_SIGNATURES -> {
val type = getValue(args, ++index)
typedefMode = when (type) {
"ref" -> TypedefMode.REFERENCE
"inline" -> TypedefMode.INLINE
"none" -> TypedefMode.NONE
else -> throw DriverException(
stderr = "$ARG_TYPEDEFS_IN_SIGNATURES must be one of ref, inline, none; was $type")
}
}
ARG_BASELINE -> {
val relative = getValue(args, ++index)
assert(baselineFile == null) { "Only one baseline is allowed; found both $baselineFile and $relative" }
......@@ -1922,6 +1943,12 @@ class Options(
"$ARG_SUBTRACT_API <api file>", "Subtracts the API in the given signature or jar file from the " +
"current API being emitted via $ARG_API, $ARG_STUBS, $ARG_DOC_STUBS, etc. " +
"Note that the subtraction only applies to classes; it does not subtract members.",
"$ARG_TYPEDEFS_IN_SIGNATURES <ref|inline>", "Whether to include typedef annotations in signature " +
"files. `$ARG_TYPEDEFS_IN_SIGNATURES ref` will include just a reference to the typedef class, " +
"which is not itself part of the API and is not included as a class, and " +
"`$ARG_TYPEDEFS_IN_SIGNATURES inline` will include the constants themselves into each usage " +
"site. You can also supply `$ARG_TYPEDEFS_IN_SIGNATURES none` to explicitly turn it off, if the " +
"default ever changes.",
"", "\nDocumentation:",
ARG_PUBLIC, "Only include elements that are public",
......
......@@ -32,6 +32,7 @@ import com.android.tools.metalava.ANDROID_NULLABLE
import com.android.tools.metalava.ANDROID_SUPPORT_ANNOTATION_PREFIX
import com.android.tools.metalava.Compatibility
import com.android.tools.metalava.JAVA_LANG_PREFIX
import com.android.tools.metalava.Options
import com.android.tools.metalava.RECENTLY_NONNULL
import com.android.tools.metalava.RECENTLY_NULLABLE
import com.android.tools.metalava.doclava1.ApiPredicate
......@@ -85,6 +86,9 @@ interface AnnotationItem {
/** True if this annotation represents @IntDef, @LongDef or @StringDef */
fun isTypeDefAnnotation(): Boolean {
val name = qualifiedName() ?: return false
if (!(name.endsWith("Def"))) {
return false
}
return (INT_DEF_ANNOTATION.isEquals(name) ||
STRING_DEF_ANNOTATION.isEquals(name) ||
LONG_DEF_ANNOTATION.isEquals(name) ||
......@@ -120,6 +124,12 @@ interface AnnotationItem {
return codebase.findClass(qualifiedName() ?: return null)
}
/** If this annotation has a typedef annotation associated with it, return it */
fun findTypedefAnnotation(): AnnotationItem? {
val className = originalName() ?: return null
return codebase.findClass(className)?.modifiers?.annotations()?.firstOrNull { it.isTypeDefAnnotation() }
}
/** Returns the retention of this annotation */
val retention: AnnotationRetention
get() {
......@@ -466,6 +476,12 @@ interface AnnotationItem {
// See if the annotation is pointing to an annotation class that is part of the API; if not, skip it.
val cls = codebase.findClass(qualifiedName) ?: return NO_ANNOTATION_TARGETS
if (!ApiPredicate().test(cls)) {
if (options.typedefMode != Options.TypedefMode.NONE) {
if (cls.modifiers.annotations().any { it.isTypeDefAnnotation() }) {
return ANNOTATION_SIGNATURE_ONLY
}
}
return NO_ANNOTATION_TARGETS
}
......
......@@ -16,6 +16,9 @@
package com.android.tools.metalava.model
import com.android.tools.metalava.Options
import com.android.tools.metalava.options
/** Various places where a given annotation can be written */
enum class AnnotationTarget {
/** Write the annotation into the signature file */
......@@ -80,4 +83,11 @@ val ANNOTATION_IN_DOC_STUBS_AND_EXTERNAL = setOf(
val ANNOTATION_EXTERNAL = setOf(AnnotationTarget.SIGNATURE_FILE, AnnotationTarget.EXTERNAL_ANNOTATIONS_FILE)
/** Write it only into the external annotations file, not the signature file */
val ANNOTATION_EXTERNAL_ONLY = setOf(AnnotationTarget.SIGNATURE_FILE, AnnotationTarget.EXTERNAL_ANNOTATIONS_FILE)
val ANNOTATION_EXTERNAL_ONLY = if (options.typedefMode == Options.TypedefMode.INLINE ||
options.typedefMode == Options.TypedefMode.NONE) // just here for compatibility purposes
setOf(AnnotationTarget.SIGNATURE_FILE, AnnotationTarget.EXTERNAL_ANNOTATIONS_FILE)
else
setOf(AnnotationTarget.EXTERNAL_ANNOTATIONS_FILE)
/** Write it only into the he signature file */
val ANNOTATION_SIGNATURE_ONLY = setOf(AnnotationTarget.SIGNATURE_FILE)
......@@ -493,6 +493,7 @@ interface ModifierList {
continue
}
var printAnnotation = annotation
if (!annotation.targets().contains(target)) {
continue
} else if ((annotation.isNullnessAnnotation())) {
......@@ -502,6 +503,23 @@ interface ModifierList {
} else if (annotation.qualifiedName() == "java.lang.Deprecated") {
// Special cased in stubs and signature files: emitted first
continue
} else if (options.typedefMode == Options.TypedefMode.INLINE) {
val typedef = annotation.findTypedefAnnotation()
if (typedef != null) {
printAnnotation = typedef
}
} else if (options.typedefMode == Options.TypedefMode.REFERENCE &&
annotation.targets() === ANNOTATION_SIGNATURE_ONLY &&
annotation.findTypedefAnnotation() != null) {
// For annotation references, only include the simple name
writer.write("@")
writer.write(annotation.resolve()?.simpleName() ?: annotation.qualifiedName()!!)
if (separateLines) {
writer.write("\n")
} else {
writer.write(" ")
}
continue
}
// Optionally filter out duplicates
......@@ -520,7 +538,8 @@ interface ModifierList {
}
}
val source = annotation.toSource(target)
val source = printAnnotation.toSource(target)
if (omitCommonPackages) {
writer.write(AnnotationItem.shortenAnnotation(source))
} else {
......
......@@ -21,13 +21,9 @@ import org.junit.Test
@SuppressWarnings("ALL") // Sample code
class ExtractAnnotationsTest : DriverTest() {
@Test
fun `Check java typedef extraction and warning about non-source retention of typedefs`() {
check(
includeSourceRetentionAnnotations = false,
sourceFiles = *arrayOf(
java(
"""
private val sourceFiles1 = arrayOf(
java(
"""
package test.pkg;
import android.annotation.IntDef;
......@@ -70,10 +66,17 @@ class ExtractAnnotationsTest : DriverTest() {
}
}
"""
).indented(),
intDefAnnotationSource,
intRangeAnnotationSource
),
).indented(),
intDefAnnotationSource,
intRangeAnnotationSource
)
@Test
fun `Check java typedef extraction and warning about non-source retention of typedefs`() {
check(
includeSourceRetentionAnnotations = false,
format = FileFormat.V2,
sourceFiles = *sourceFiles1,
warnings = "src/test/pkg/IntDefTest.java:11: error: This typedef annotation class should have @Retention(RetentionPolicy.SOURCE) [AnnotationExtraction]",
extractAnnotations = mapOf(
"test.pkg" to """
......@@ -509,4 +512,112 @@ class ExtractAnnotationsTest : DriverTest() {
)
)
}
@Test
fun `No typedef signatures in api files`() {
check(
includeSourceRetentionAnnotations = false,
extraArguments = arrayOf(
ARG_HIDE_PACKAGE, "android.annotation",
ARG_TYPEDEFS_IN_SIGNATURES, "none"
),
format = FileFormat.V2,
sourceFiles = *sourceFiles1,
api = """
// Signature format: 2.0
package test.pkg {
public class IntDefTest {
ctor public IntDefTest();
method public void setFlags(Object, int);
method public void setStyle(int, int);
method public void testIntDef(int);
field public static final int STYLE_NORMAL = 0; // 0x0
field public static final int STYLE_NO_FRAME = 2; // 0x2
field public static final int STYLE_NO_INPUT = 3; // 0x3
field public static final int STYLE_NO_TITLE = 1; // 0x1
field public static final String TYPE_1 = "type1";
field public static final String TYPE_2 = "type2";
field public static final int UNRELATED = 3; // 0x3
field public static final String UNRELATED_TYPE = "other";
}
public static class IntDefTest.Inner {
ctor public IntDefTest.Inner();
method public void setInner(int);
}
}
"""
)
}
@Test
fun `Inlining typedef signatures in api files`() {
check(
includeSourceRetentionAnnotations = false,
extraArguments = arrayOf(
ARG_HIDE_PACKAGE, "android.annotation",
ARG_TYPEDEFS_IN_SIGNATURES, "inline"
),
format = FileFormat.V2,
sourceFiles = *sourceFiles1,
api = """
// Signature format: 2.0
package test.pkg {
public class IntDefTest {
ctor public IntDefTest();
method public void setFlags(Object, @IntDef(value={test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT, 3, 3 + 1}, flag=true) int);
method public void setStyle(@IntDef({test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}) int, int);
method public void testIntDef(int);
field public static final int STYLE_NORMAL = 0; // 0x0
field public static final int STYLE_NO_FRAME = 2; // 0x2
field public static final int STYLE_NO_INPUT = 3; // 0x3
field public static final int STYLE_NO_TITLE = 1; // 0x1
field public static final String TYPE_1 = "type1";
field public static final String TYPE_2 = "type2";
field public static final int UNRELATED = 3; // 0x3
field public static final String UNRELATED_TYPE = "other";
}
public static class IntDefTest.Inner {
ctor public IntDefTest.Inner();
method public void setInner(@IntDef(value={test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT, 3, 3 + 1}, flag=true) int);
}
}
"""
)
}
@Test
fun `Referencing typedef signatures in api files`() {
check(
includeSourceRetentionAnnotations = false,
extraArguments = arrayOf(
ARG_HIDE_PACKAGE, "android.annotation",
ARG_TYPEDEFS_IN_SIGNATURES, "ref"
),
format = FileFormat.V2,
sourceFiles = *sourceFiles1,
api = """
// Signature format: 2.0
package test.pkg {
public class IntDefTest {
ctor public IntDefTest();
method public void setFlags(Object, @DialogFlags int);
method public void setStyle(@DialogStyle int, int);
method public void testIntDef(int);
field public static final int STYLE_NORMAL = 0; // 0x0
field public static final int STYLE_NO_FRAME = 2; // 0x2
field public static final int STYLE_NO_INPUT = 3; // 0x3
field public static final int STYLE_NO_TITLE = 1; // 0x1
field public static final String TYPE_1 = "type1";
field public static final String TYPE_2 = "type2";
field public static final int UNRELATED = 3; // 0x3
field public static final String UNRELATED_TYPE = "other";
}
public static class IntDefTest.Inner {
ctor public IntDefTest.Inner();
method public void setInner(@DialogFlags int);
}
}
"""
)
}
}
\ No newline at end of file
......@@ -121,6 +121,16 @@ API sources:
--api, --stubs, --doc-stubs, etc. Note that the
subtraction only applies to classes; it does not
subtract members.
--typedefs-in-signatures <ref|inline> Whether to include typedef annotations in
signature files. `--typedefs-in-signatures ref`
will include just a reference to the typedef
class, which is not itself part of the API and
is not included as a class, and
`--typedefs-in-signatures inline` will include
the constants themselves into each usage site.
You can also supply `--typedefs-in-signatures
none` to explicitly turn it off, if the default
ever changes.
Documentation:
--public Only include elements that are public
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment