[tools] Suppress empty fidl_api_summarize output
* Generates empty output if the library only has a library
declaration at the target API level.
* Adds a flag.Setter for the summary format in pkg summarize.
* Combines Write and WriteJSON into GenerateSummary since
they're only used by this binary which selects the summary
format based on a flag value. Also moves this logic to
pkg summarize.
Bug: 102484
Change-Id: Iecc463fdaed7f34f25a2838b438389a314881f2a
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/692721
Reviewed-by: Hunter Freyer <hjfreyer@google.com>
Commit-Queue: Kendal Harland <kjharland@google.com>
Reviewed-by: Alex Zaslavsky <azaslavsky@google.com>
Reviewed-by: Filip Filmar <fmil@google.com>
diff --git a/build/fidl/fidl_summary.gni b/build/fidl/fidl_summary.gni
index 73b3d57..d33a2aa 100644
--- a/build/fidl/fidl_summary.gni
+++ b/build/fidl/fidl_summary.gni
@@ -117,6 +117,7 @@
rebase_path(_json_representation, root_build_dir),
"--output-file",
rebase_path(_summary_file_json, root_build_dir),
+ "--suppress-empty-library",
]
metadata = {
# Refer to //sdk/cts/plasa/plasa_artifacts.gni for the explanation of
diff --git a/sdk/history/7/fuchsia.bluetooth.gatt2.api_summary.json b/sdk/history/7/fuchsia.bluetooth.gatt2.api_summary.json
index 9c7f40b..e69de29 100644
--- a/sdk/history/7/fuchsia.bluetooth.gatt2.api_summary.json
+++ b/sdk/history/7/fuchsia.bluetooth.gatt2.api_summary.json
@@ -1,6 +0,0 @@
-[
- {
- "kind": "library",
- "name": "fuchsia.bluetooth.gatt2"
- }
-]
diff --git a/sdk/history/7/fuchsia.debugdata.api_summary.json b/sdk/history/7/fuchsia.debugdata.api_summary.json
index ce21c9d..e69de29 100644
--- a/sdk/history/7/fuchsia.debugdata.api_summary.json
+++ b/sdk/history/7/fuchsia.debugdata.api_summary.json
@@ -1,6 +0,0 @@
-[
- {
- "kind": "library",
- "name": "fuchsia.debugdata"
- }
-]
diff --git a/sdk/history/7/fuchsia.net.reachability.api_summary.json b/sdk/history/7/fuchsia.net.reachability.api_summary.json
index 7d4fbe19..e69de29 100644
--- a/sdk/history/7/fuchsia.net.reachability.api_summary.json
+++ b/sdk/history/7/fuchsia.net.reachability.api_summary.json
@@ -1,6 +0,0 @@
-[
- {
- "kind": "library",
- "name": "fuchsia.net.reachability"
- }
-]
diff --git a/sdk/history/7/fuchsia.tracing.perfetto.api_summary.json b/sdk/history/7/fuchsia.tracing.perfetto.api_summary.json
index f679d14..e69de29 100644
--- a/sdk/history/7/fuchsia.tracing.perfetto.api_summary.json
+++ b/sdk/history/7/fuchsia.tracing.perfetto.api_summary.json
@@ -1,6 +0,0 @@
-[
- {
- "kind": "library",
- "name": "fuchsia.tracing.perfetto"
- }
-]
diff --git a/tools/fidl/fidl_api_summarize/main.go b/tools/fidl/fidl_api_summarize/main.go
index f5b79f4..433eb96 100644
--- a/tools/fidl/fidl_api_summarize/main.go
+++ b/tools/fidl/fidl_api_summarize/main.go
@@ -8,9 +8,10 @@
package main
import (
+ "bytes"
"flag"
"fmt"
- "io"
+ "io/ioutil"
"os"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
@@ -18,11 +19,17 @@
)
var (
- fir = flag.String("fidl-ir-file", "", "The FIDL IR input file to produce an API summary for.")
- out = flag.String("output-file", "", "The output file to write the summary into.")
- format = flag.String("format", "text", "Specify the output format (text|json)")
+ fir = flag.String("fidl-ir-file", "", "The FIDL IR input file to produce an API summary for.")
+ out = flag.String("output-file", "", "The output file to write the summary into.")
+ suppressEmptyLibrary = flag.Bool("suppress-empty-library", false, "Generate empty output for libraries with no declarations")
+ format = summarize.TextSummaryFormat
)
+// TODO(kjharland): Move flags into main().
+func init() {
+ flag.Var(&format, "format", "Specify the output format (text|json)")
+}
+
// usage prints a user-friendly usage message when the flag --help is provided.
func usage() {
fmt.Fprintf(flag.CommandLine.Output(),
@@ -33,19 +40,6 @@
flag.PrintDefaults()
}
-// getWriter returns the appropriate function for writing output, based on the
-// format chosen through the --format flag.
-func getWriter() (func(fidlgen.Root, io.Writer) error, error) {
- switch *format {
- case "text":
- return summarize.Write, nil
- case "json":
- return summarize.WriteJSON, nil
- default:
- return nil, fmt.Errorf("not a recognized flag value: %v", *format)
- }
-}
-
func main() {
flag.Usage = usage
flag.Parse()
@@ -57,10 +51,6 @@
}
func mainImpl() error {
- writerFn, err := getWriter()
- if err != nil {
- return fmt.Errorf("While parsing --format: %v", err)
- }
if *fir == "" {
return fmt.Errorf("The flag --fidl-ir-file=... is required")
}
@@ -72,20 +62,26 @@
return fmt.Errorf("The flag --output-file=... is required")
}
- f, err := os.Create(*out)
- if err != nil {
- return fmt.Errorf("Could not create file: %v: %w", *out, err)
- }
-
root, err := fidlgen.DecodeJSONIr(in)
if err != nil {
- f.Close() // decode error takes precedence.
return fmt.Errorf("Could not parse FIDL IR from: %v: %w", *in, err)
}
- if err := writerFn(root, f); err != nil {
- f.Close() // writerFn error takes precedence.
+
+ b, err := summarize.GenerateSummary(root, format)
+ if err != nil {
return fmt.Errorf("While summarizing %v into %v: %w", *in, *out, err)
}
- return f.Close()
+ if *suppressEmptyLibrary {
+ emptyLibRoot := fidlgen.Root{Name: root.Name}
+ emptyLibBytes, err := summarize.GenerateSummary(emptyLibRoot, format)
+ if err != nil {
+ return err
+ }
+ if bytes.Equal(b, emptyLibBytes) {
+ return ioutil.WriteFile(*out, []byte{}, 0644)
+ }
+ }
+
+ return ioutil.WriteFile(*out, b, 0644)
}
diff --git a/tools/fidl/lib/apidiff/apidiff_test.go b/tools/fidl/lib/apidiff/apidiff_test.go
index 5c52f151..58a0cd8 100644
--- a/tools/fidl/lib/apidiff/apidiff_test.go
+++ b/tools/fidl/lib/apidiff/apidiff_test.go
@@ -1248,7 +1248,7 @@
func summarizeOne(t *testing.T, r fidlgen.Root) string {
t.Helper()
var buf strings.Builder
- if err := summarize.WriteJSON(r, &buf); err != nil {
+ if err := summarize.WriteSummary(&buf, r, summarize.JSONSummaryFormat); err != nil {
t.Fatalf("error while summarizing: %v", err)
}
return buf.String()
diff --git a/tools/fidl/lib/summarize/summary.go b/tools/fidl/lib/summarize/summary.go
index d9775ff..0d47762 100644
--- a/tools/fidl/lib/summarize/summary.go
+++ b/tools/fidl/lib/summarize/summary.go
@@ -8,6 +8,7 @@
package summarize
import (
+ "bytes"
"encoding/json"
"fmt"
"io"
@@ -16,6 +17,78 @@
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
)
+type summary []Element
+
+// SummaryFormat is a representation of a FIDL API summary.
+//
+// SummaryFormat implements flag.Setter for convenient use in command-line tools.
+type SummaryFormat string
+
+// SummaryFormat constants.
+const (
+ TextSummaryFormat SummaryFormat = "text"
+ JSONSummaryFormat SummaryFormat = "json"
+)
+
+// String implements flag.Setter.
+func (f *SummaryFormat) String() string {
+ return string(*f)
+}
+
+// Set implements flag.Setter.
+func (f *SummaryFormat) Set(value string) error {
+ format := SummaryFormat(value)
+ if _, err := format.formatter(); err != nil {
+ return err
+ }
+ *f = format
+ return nil
+}
+
+func (f SummaryFormat) formatter() (func(io.Writer, summary) error, error) {
+ switch f {
+ case TextSummaryFormat:
+ return formatTextSummary, nil
+ case JSONSummaryFormat:
+ return formatJSONSummary, nil
+ }
+ return nil, fmt.Errorf("unimplemented summary format %q", string(f))
+}
+
+// GenerateSummary summarizes root and returns the serialized result in the given format.
+func GenerateSummary(root fidlgen.Root, format SummaryFormat) ([]byte, error) {
+ var b bytes.Buffer
+ if err := WriteSummary(&b, root, format); err != nil {
+ return nil, err
+ }
+ return b.Bytes(), nil
+}
+
+// WriteSummary summarizes root and writes the serialized result to the given Writer.
+func WriteSummary(w io.Writer, root fidlgen.Root, format SummaryFormat) error {
+ s := summarize(root)
+ f, err := format.formatter()
+ if err != nil {
+ return err
+ }
+ return f(w, s)
+}
+
+func formatTextSummary(w io.Writer, s summary) error {
+ for _, e := range s {
+ fmt.Fprintf(w, "%v\n", e)
+ }
+ return nil
+}
+
+func formatJSONSummary(w io.Writer, s summary) error {
+ e := json.NewEncoder(w)
+ // 4-level indent is chosen to match `fx format-code`.
+ e.SetIndent("", " ")
+ e.SetEscapeHTML(false)
+ return e.Encode(serialize([]Element(s)))
+}
+
// Element describes a single platform surface element. Use Summarize to
// convert a FIDL AST into Elements.
type Element interface {
@@ -116,25 +189,6 @@
s.symbols.addPayloads(payloads)
}
-// Write produces an API summary for the FIDL AST from the root into the supplied
-// writer.
-func Write(root fidlgen.Root, out io.Writer) error {
- for _, e := range Elements(root) {
- fmt.Fprintf(out, "%v\n", e)
- }
- return nil
-}
-
-// WriteJSON produces an API summary for the FIDL AST from the root into the
-// supplied writer, and formats the data as JSON.
-func WriteJSON(root fidlgen.Root, out io.Writer) error {
- e := json.NewEncoder(out)
- // 4-level indent is chosen to match `fx format-code`.
- e.SetIndent("", " ")
- e.SetEscapeHTML(false)
- return e.Encode(serialize(Elements(root)))
-}
-
func serialize(e []Element) []ElementStr {
var ret []ElementStr
for _, l := range e {
@@ -192,7 +246,7 @@
// Elements returns the API elements found in the supplied AST root in a
// canonical ordering.
-func Elements(root fidlgen.Root) []Element {
+func summarize(root fidlgen.Root) summary {
var s summarizer
// Do a first pass of the protocols, creating a map of all names of types that
@@ -220,5 +274,5 @@
s.addProtocols(root.Protocols)
s.addElement(library{r: root})
- return s.Elements()
+ return summary(s.Elements())
}
diff --git a/tools/fidl/lib/summarize/summary_test.go b/tools/fidl/lib/summarize/summary_test.go
index bdbfff2..904cbaa 100644
--- a/tools/fidl/lib/summarize/summary_test.go
+++ b/tools/fidl/lib/summarize/summary_test.go
@@ -5,12 +5,10 @@
package summarize
import (
- "io"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
- "go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgentest"
)
@@ -48,7 +46,7 @@
expected string
}
-func TestWrite(t *testing.T) {
+func TestTextSummaryFormat(t *testing.T) {
tests := []summaryTestCase{
{
name: "library only",
@@ -670,10 +668,10 @@
`,
},
}
- runWriteTests(t, tests, Write)
+ runGenerateSummaryTests(t, tests, TextSummaryFormat)
}
-func TestWriteJSON(t *testing.T) {
+func TestJSONSummaryFormat(t *testing.T) {
tests := []summaryTestCase{
{
name: "library only",
@@ -1884,10 +1882,10 @@
`,
},
}
- runWriteTests(t, tests, WriteJSON)
+ runGenerateSummaryTests(t, tests, JSONSummaryFormat)
}
-func runWriteTests(t *testing.T, tests []summaryTestCase, writeFn func(fidlgen.Root, io.Writer) error) {
+func runGenerateSummaryTests(t *testing.T, tests []summaryTestCase, format SummaryFormat) {
t.Helper()
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
@@ -1896,17 +1894,18 @@
c = c.WithDependency(test.dep)
}
r := c.Single(test.fidl)
- var sb strings.Builder
- if err := writeFn(r, &sb); err != nil {
+ b, err := GenerateSummary(r, format)
+ if err != nil {
t.Fatalf("while summarizing file: %v", err)
}
- actual := strings.Split(sb.String(), "\n")
+ summary := string(b)
+ actual := strings.Split(summary, "\n")
expected := strings.Split(test.expected, "\n")
if !cmp.Equal(expected, actual) {
t.Errorf("expected:\n---BEGIN---\n%+v\n---END---\n\n"+
"actual:\n---BEGIN---\n%+v\n---END---\n\ndiff:\n%v\n\nroot: %+v",
- test.expected, sb.String(),
+ test.expected, summary,
cmp.Diff(expected, actual), r)
}
})