Improve flags for compliance tools.
Test: m droid dist reportmissinglicenses
Change-Id: I4090dae3d5d33d1908d67dff31aeee92d2b261da
diff --git a/tools/compliance/cmd/rtrace/rtrace.go b/tools/compliance/cmd/rtrace/rtrace.go
index 91171c4..667cdce 100644
--- a/tools/compliance/cmd/rtrace/rtrace.go
+++ b/tools/compliance/cmd/rtrace/rtrace.go
@@ -15,6 +15,7 @@
package main
import (
+ "bytes"
"flag"
"fmt"
"io"
@@ -24,21 +25,19 @@
"sort"
"strings"
+ "android/soong/response"
"android/soong/tools/compliance"
)
var (
- sources = newMultiString("rtrace", "Projects or metadata files to trace back from. (required; multiple allowed)")
- stripPrefix = newMultiString("strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
-
failNoneRequested = fmt.Errorf("\nNo license metadata files requested")
failNoSources = fmt.Errorf("\nNo projects or metadata files to trace back from")
failNoLicenses = fmt.Errorf("No licenses found")
)
type context struct {
- sources []string
- stripPrefix []string
+ sources []string
+ stripPrefix []string
}
func (ctx context) strip(installPath string) string {
@@ -54,8 +53,44 @@
return installPath
}
-func init() {
- flag.Usage = func() {
+// newMultiString creates a flag that allows multiple values in an array.
+func newMultiString(flags *flag.FlagSet, name, usage string) *multiString {
+ var f multiString
+ flags.Var(&f, name, usage)
+ return &f
+}
+
+// multiString implements the flag `Value` interface for multiple strings.
+type multiString []string
+
+func (ms *multiString) String() string { return strings.Join(*ms, ", ") }
+func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
+
+func main() {
+ var expandedArgs []string
+ for _, arg := range os.Args[1:] {
+ if strings.HasPrefix(arg, "@") {
+ f, err := os.Open(strings.TrimPrefix(arg, "@"))
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+
+ respArgs, err := response.ReadRspFile(f)
+ f.Close()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
+ expandedArgs = append(expandedArgs, respArgs...)
+ } else {
+ expandedArgs = append(expandedArgs, arg)
+ }
+ }
+
+ flags := flag.NewFlagSet("flags", flag.ExitOnError)
+
+ flags.Usage = func() {
fmt.Fprintf(os.Stderr, `Usage: %s {options} file.meta_lic {file.meta_lic...}
Outputs a space-separated Target ActsOn Origin Condition tuple for each
@@ -72,50 +107,75 @@
Options:
`, filepath.Base(os.Args[0]))
- flag.PrintDefaults()
+ flags.PrintDefaults()
}
-}
-// newMultiString creates a flag that allows multiple values in an array.
-func newMultiString(name, usage string) *multiString {
- var f multiString
- flag.Var(&f, name, usage)
- return &f
-}
+ outputFile := flags.String("o", "-", "Where to write the output. (default stdout)")
+ sources := newMultiString(flags, "rtrace", "Projects or metadata files to trace back from. (required; multiple allowed)")
+ stripPrefix := newMultiString(flags, "strip_prefix", "Prefix to remove from paths. i.e. path to root (multiple allowed)")
-// multiString implements the flag `Value` interface for multiple strings.
-type multiString []string
-
-func (ms *multiString) String() string { return strings.Join(*ms, ", ") }
-func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
-
-func main() {
- flag.Parse()
+ flags.Parse(expandedArgs)
// Must specify at least one root target.
- if flag.NArg() == 0 {
- flag.Usage()
+ if flags.NArg() == 0 {
+ flags.Usage()
os.Exit(2)
}
if len(*sources) == 0 {
- flag.Usage()
+ flags.Usage()
fmt.Fprintf(os.Stderr, "\nMust specify at least 1 --rtrace source.\n")
os.Exit(2)
}
- ctx := &context{
- sources: *sources,
- stripPrefix: *stripPrefix,
+ if len(*outputFile) == 0 {
+ flags.Usage()
+ fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
+ os.Exit(2)
+ } else {
+ dir, err := filepath.Abs(filepath.Dir(*outputFile))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
+ os.Exit(1)
+ }
+ fi, err := os.Stat(dir)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
+ os.Exit(1)
+ }
+ if !fi.IsDir() {
+ fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
+ os.Exit(1)
+ }
}
- _, err := traceRestricted(ctx, os.Stdout, os.Stderr, compliance.FS, flag.Args()...)
+
+ var ofile io.Writer
+ ofile = os.Stdout
+ var obuf *bytes.Buffer
+ if *outputFile != "-" {
+ obuf = &bytes.Buffer{}
+ ofile = obuf
+ }
+
+ ctx := &context{
+ sources: *sources,
+ stripPrefix: *stripPrefix,
+ }
+ _, err := traceRestricted(ctx, ofile, os.Stderr, compliance.FS, flags.Args()...)
if err != nil {
if err == failNoneRequested {
- flag.Usage()
+ flags.Usage()
}
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
os.Exit(1)
}
+ if *outputFile != "-" {
+ err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err)
+ os.Exit(1)
+ }
+ }
os.Exit(0)
}