Merge "Add --delete-empty-removed-signatures"
diff --git a/src/main/java/com/android/tools/metalava/Driver.kt b/src/main/java/com/android/tools/metalava/Driver.kt
index 60f27d8..9ed9598 100644
--- a/src/main/java/com/android/tools/metalava/Driver.kt
+++ b/src/main/java/com/android/tools/metalava/Driver.kt
@@ -59,6 +59,7 @@
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.io.PrintWriter
+import java.io.StringWriter
import java.util.concurrent.TimeUnit.SECONDS
import java.util.function.Predicate
import kotlin.system.exitProcess
@@ -324,8 +325,8 @@
val removedEmit = apiType.getEmitFilter()
val removedReference = apiType.getReferenceFilter()
- createReportFile(unfiltered, apiFile, "removed API") { printWriter ->
- SignatureWriter(printWriter, removedEmit, removedReference, codebase.original != null)
+ createReportFile(unfiltered, apiFile, "removed API", options.deleteEmptyRemovedSignatures) { printWriter ->
+ SignatureWriter(printWriter, removedEmit, removedReference, codebase.original != null, options.includeSignatureFormatVersionRemoved)
}
}
@@ -1057,6 +1058,7 @@
codebase: Codebase,
apiFile: File,
description: String?,
+ deleteEmptyFiles: Boolean = false,
createVisitor: (PrintWriter) -> ApiVisitor
) {
if (description != null) {
@@ -1064,11 +1066,16 @@
}
val localTimer = Stopwatch.createStarted()
try {
- val writer = PrintWriter(Files.asCharSink(apiFile, UTF_8).openBufferedStream())
+ val stringWriter = StringWriter()
+ val writer = PrintWriter(stringWriter)
writer.use { printWriter ->
val apiWriter = createVisitor(printWriter)
codebase.accept(apiWriter)
}
+ val text = stringWriter.toString()
+ if (text.length > 0 || !deleteEmptyFiles) {
+ apiFile.writeText(text)
+ }
} catch (e: IOException) {
reporter.report(Issues.IO_ERROR, apiFile, "Cannot open file for write.")
}
diff --git a/src/main/java/com/android/tools/metalava/Options.kt b/src/main/java/com/android/tools/metalava/Options.kt
index 866d01f..76396c4 100644
--- a/src/main/java/com/android/tools/metalava/Options.kt
+++ b/src/main/java/com/android/tools/metalava/Options.kt
@@ -161,6 +161,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_DELETE_EMPTY_REMOVED_SIGNATURES = "--delete-empty-removed-signatures"
const val ARG_SUBTRACT_API = "--subtract-api"
const val ARG_TYPEDEFS_IN_SIGNATURES = "--typedefs-in-signatures"
const val ARG_FORCE_CONVERT_TO_WARNING_NULLABILITY_ANNOTATIONS = "--force-convert-to-warning-nullability-annotations"
@@ -570,9 +571,29 @@
/** Level to include for javadoc */
var docLevel = DocLevel.PROTECTED
- /** Whether to include the signature file format version header in signature files */
+ /** Whether to include the signature file format version header in most signature files */
var includeSignatureFormatVersion: Boolean = true
+ /** Whether to include the signature file format version header in removed signature files */
+ val includeSignatureFormatVersionNonRemoved: EmitFileHeader get() =
+ if (includeSignatureFormatVersion) {
+ EmitFileHeader.ALWAYS
+ } else {
+ EmitFileHeader.NEVER
+ }
+
+ /** Whether to include the signature file format version header in removed signature files */
+ val includeSignatureFormatVersionRemoved: EmitFileHeader get() =
+ if (includeSignatureFormatVersion) {
+ if (deleteEmptyRemovedSignatures) {
+ EmitFileHeader.IF_NONEMPTY_FILE
+ } else {
+ EmitFileHeader.ALWAYS
+ }
+ } else {
+ EmitFileHeader.NEVER
+ }
+
/** A baseline to check against */
var baseline: Baseline? = null
@@ -625,6 +646,9 @@
/** If updating baselines and the baseline is empty, delete the file */
var deleteEmptyBaselines = false
+ /** If generating a removed signature file and it is empty, delete it */
+ var deleteEmptyRemovedSignatures = false
+
/** Whether the baseline should only contain errors */
var baselineErrorsOnly = false
@@ -1023,6 +1047,7 @@
ARG_PASS_BASELINE_UPDATES -> passBaselineUpdates = true
ARG_DELETE_EMPTY_BASELINES -> deleteEmptyBaselines = true
+ ARG_DELETE_EMPTY_REMOVED_SIGNATURES -> deleteEmptyRemovedSignatures = true
ARG_PUBLIC, "-public" -> docLevel = DocLevel.PUBLIC
ARG_PROTECTED, "-protected" -> docLevel = DocLevel.PROTECTED
diff --git a/src/main/java/com/android/tools/metalava/SignatureWriter.kt b/src/main/java/com/android/tools/metalava/SignatureWriter.kt
index aebd5b6..9d7a90a 100644
--- a/src/main/java/com/android/tools/metalava/SignatureWriter.kt
+++ b/src/main/java/com/android/tools/metalava/SignatureWriter.kt
@@ -35,7 +35,8 @@
private val writer: PrintWriter,
filterEmit: Predicate<Item>,
filterReference: Predicate<Item>,
- private val preFiltered: Boolean
+ private val preFiltered: Boolean,
+ var emitHeader: EmitFileHeader = options.includeSignatureFormatVersionNonRemoved
) : ApiVisitor(
visitConstructorsAsMethods = false,
nestInnerClasses = false,
@@ -47,101 +48,112 @@
showUnannotated = options.showUnannotated
) {
init {
- if (options.includeSignatureFormatVersion) {
+ if (emitHeader == EmitFileHeader.ALWAYS) {
writer.print(options.outputFormat.header())
+ emitHeader = EmitFileHeader.NEVER
}
}
+ fun write(text: String) {
+ if (emitHeader == EmitFileHeader.IF_NONEMPTY_FILE) {
+ if (options.includeSignatureFormatVersion) {
+ writer.print(options.outputFormat.header())
+ }
+ emitHeader = EmitFileHeader.NEVER
+ }
+ writer.print(text)
+ }
+
override fun visitPackage(pkg: PackageItem) {
- writer.print("package ")
+ write("package ")
writeModifiers(pkg)
- writer.print("${pkg.qualifiedName()} {\n\n")
+ write("${pkg.qualifiedName()} {\n\n")
}
override fun afterVisitPackage(pkg: PackageItem) {
- writer.print("}\n\n")
+ write("}\n\n")
}
override fun visitConstructor(constructor: ConstructorItem) {
- writer.print(" ctor ")
+ write(" ctor ")
writeModifiers(constructor)
// Note - we don't write out the type parameter list (constructor.typeParameterList()) in signature files!
// writeTypeParameterList(constructor.typeParameterList(), addSpace = true)
- writer.print(constructor.containingClass().fullName())
+ write(constructor.containingClass().fullName())
writeParameterList(constructor)
writeThrowsList(constructor)
- writer.print(";\n")
+ write(";\n")
}
override fun visitField(field: FieldItem) {
val name = if (field.isEnumConstant()) "enum_constant" else "field"
- writer.print(" ")
- writer.print(name)
- writer.print(" ")
+ write(" ")
+ write(name)
+ write(" ")
writeModifiers(field)
writeType(field, field.type())
- writer.print(' ')
- writer.print(field.name())
+ write(" ")
+ write(field.name())
field.writeValueWithSemicolon(writer, allowDefaultValue = false, requireInitialValue = false)
- writer.print("\n")
+ write("\n")
}
override fun visitProperty(property: PropertyItem) {
- writer.print(" property ")
+ write(" property ")
writeModifiers(property)
writeType(property, property.type())
- writer.print(' ')
- writer.print(property.name())
- writer.print(";\n")
+ write(" ")
+ write(property.name())
+ write(";\n")
}
override fun visitMethod(method: MethodItem) {
- writer.print(" method ")
+ write(" method ")
writeModifiers(method)
writeTypeParameterList(method.typeParameterList(), addSpace = true)
writeType(method, method.returnType())
- writer.print(' ')
- writer.print(method.name())
+ write(" ")
+ write(method.name())
writeParameterList(method)
writeThrowsList(method)
if (method.containingClass().isAnnotationType()) {
val default = method.defaultValue()
if (default.isNotEmpty()) {
- writer.print(" default ")
- writer.print(default)
+ write(" default ")
+ write(default)
}
}
- writer.print(";\n")
+ write(";\n")
}
override fun visitClass(cls: ClassItem) {
- writer.print(" ")
+ write(" ")
writeModifiers(cls)
if (cls.isAnnotationType()) {
- writer.print("@interface")
+ write("@interface")
} else if (cls.isInterface()) {
- writer.print("interface")
+ write("interface")
} else if (cls.isEnum()) {
- writer.print("enum")
+ write("enum")
} else {
- writer.print("class")
+ write("class")
}
- writer.print(" ")
- writer.print(cls.fullName())
+ write(" ")
+ write(cls.fullName())
writeTypeParameterList(cls.typeParameterList(), addSpace = false)
writeSuperClassStatement(cls)
writeInterfaceList(cls)
- writer.print(" {\n")
+ write(" {\n")
}
override fun afterVisitClass(cls: ClassItem) {
- writer.print(" }\n\n")
+ write(" }\n\n")
}
private fun writeModifiers(item: Item) {
@@ -171,8 +183,8 @@
context = superClass.asClass(),
filter = filterReference
)
- writer.print(" extends ")
- writer.print(superClassString)
+ write(" extends ")
+ write(superClassString)
}
}
@@ -200,10 +212,10 @@
} else {
" implements"
}
- writer.print(label)
+ write(label)
interfaces.sortedWith(TypeItem.comparator).forEach { item ->
- writer.print(" ")
- writer.print(
+ write(" ")
+ write(
item.toTypeString(
kotlinStyleNulls = false,
context = item.asClass(),
@@ -217,48 +229,48 @@
private fun writeTypeParameterList(typeList: TypeParameterList, addSpace: Boolean) {
val typeListString = typeList.toString()
if (typeListString.isNotEmpty()) {
- writer.print(typeListString)
+ write(typeListString)
if (addSpace) {
- writer.print(' ')
+ write(" ")
}
}
}
private fun writeParameterList(method: MethodItem) {
- writer.print("(")
+ write("(")
method.parameters().asSequence().forEachIndexed { i, parameter ->
if (i > 0) {
- writer.print(", ")
+ write(", ")
}
if (parameter.hasDefaultValue() &&
options.outputDefaultValues &&
options.outputConciseDefaultValues
) {
// Concise representation of a parameter with a default
- writer.print("optional ")
+ write("optional ")
}
writeModifiers(parameter)
writeType(parameter, parameter.type())
val name = parameter.publicName()
if (name != null) {
- writer.print(" ")
- writer.print(name)
+ write(" ")
+ write(name)
}
if (parameter.isDefaultValueKnown() &&
options.outputDefaultValues &&
!options.outputConciseDefaultValues
) {
- writer.print(" = ")
+ write(" = ")
val defaultValue = parameter.defaultValue()
if (defaultValue != null) {
- writer.print(defaultValue)
+ write(defaultValue)
} else {
// null is a valid default value!
- writer.print("null")
+ write("null")
}
}
}
- writer.print(")")
+ write(")")
}
private fun writeType(
@@ -280,7 +292,7 @@
// Strip java.lang. prefix
typeString = TypeItem.shortenTypes(typeString)
- writer.print(typeString)
+ write(typeString)
}
private fun writeThrowsList(method: MethodItem) {
@@ -289,13 +301,19 @@
else -> method.filteredThrowsTypes(filterReference).asSequence()
}
if (throws.any()) {
- writer.print(" throws ")
+ write(" throws ")
throws.asSequence().sortedWith(ClassItem.fullNameComparator).forEachIndexed { i, type ->
if (i > 0) {
- writer.print(", ")
+ write(", ")
}
- writer.print(type.qualifiedName())
+ write(type.qualifiedName())
}
}
}
}
+
+enum class EmitFileHeader {
+ ALWAYS,
+ NEVER,
+ IF_NONEMPTY_FILE
+}