blob: 51eb77eb455512facd99cb26254823e9a68e3633 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The program fidl_api_diff computes a FIDL API surface diff from the
// FIDL API summary files. Please refer to README.md in this directory
// for more details.
package main
import (
"flag"
"fmt"
"io"
"os"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/apidiff"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/summarize"
)
var (
beforeFile = flag.String("before-file", "", "The FIDL API summary JSON file for the original API surface")
afterFile = flag.String("after-file", "", "The FIDL API summary JSON file for the modified API surface")
outFile = flag.String("api-diff-file", "", "The JSON file to write the API diff format into")
format = flag.String("format", "text", "Specify the output format (text|json)")
lenient = flag.Bool("lenient", false, "If set, the program will always exit with the exit code zero.")
)
// usage prints a user-friendly usage message when the flag --help is provided.
func usage() {
fmt.Fprintf(flag.CommandLine.Output(),
`%v extracts FIDL API information from the FIDL intermediate representation files.
Usage:
`, os.Args[0])
flag.PrintDefaults()
}
type writerFn func(before, after io.Reader, out io.Writer) error
// getWriter selects a writer based on the passed-in format.
func writeReport(d apidiff.Report, w io.Writer) error {
switch *format {
case "":
fallthrough
case "text":
return d.WriteText(w)
case "json":
return d.WriteJSON(w)
default:
return fmt.Errorf("not a recognized flag value for --format: %v", *format)
}
}
// errExitCode returns the exit code to use on error. Allows lenient error-free
// exit even in case of an encountered error.
func errExitCode() int {
if *lenient {
return 0
}
return 1
}
func getReaders(beforeFile, afterFile string) (before, after io.Reader, err error) {
if beforeFile == "" {
return nil, nil, fmt.Errorf("The flag --before-file=... is required.")
}
before, err = os.Open(beforeFile)
if err != nil {
return nil, nil, fmt.Errorf("Error while opening: %v: %w", beforeFile, err)
}
if afterFile == "" {
return nil, nil, fmt.Errorf("The flag --after-file=... is required")
}
after, err = os.Open(afterFile)
if err != nil {
return nil, nil, fmt.Errorf("Error while opening: %v: %v", afterFile, err)
}
return
}
func getWriter(outFile string) (io.WriteCloser, error) {
if outFile == "" {
return nil, fmt.Errorf("The flag --api-diff-file=... is required")
}
out, err := os.Create(outFile)
if err != nil {
return nil, fmt.Errorf("Error while creating: %v: %w", outFile, err)
}
return out, nil
}
func main() {
flag.Usage = usage
flag.Parse()
lenient := errExitCode()
before, after, err := getReaders(*beforeFile, *afterFile)
if err != nil {
fmt.Fprint(os.Stderr, err)
os.Exit(lenient)
}
out, err := getWriter(*outFile)
if err != nil {
fmt.Fprint(os.Stderr, err)
os.Exit(lenient)
}
defer func() {
if err := out.Close(); err != nil {
fmt.Fprintf(os.Stderr,
"Error while closing: %v: %v", *outFile, err)
os.Exit(lenient)
}
}()
summaries, err := summarize.LoadSummariesJSON(before, after)
if err != nil {
fmt.Fprintf(os.Stderr,
"Error while loading summaries: %v: %v", *outFile, err)
os.Exit(lenient)
}
bs := summaries[0]
as := summaries[1]
report, err := apidiff.Compute(bs, as)
if err != nil {
fmt.Fprintf(os.Stderr,
"Error while computing API diff: %v: %v", *outFile, err)
os.Exit(lenient)
}
if err := writeReport(report, out); err != nil {
fmt.Fprintf(os.Stderr,
"Error while diffing:\n\tbefore: %v\n\tafter: %v\n\toutput: %v\n\t%v",
*beforeFile, *afterFile, *outFile, err)
os.Exit(lenient)
}
}