diff options
Diffstat (limited to 'src/cmd/api')
| -rw-r--r-- | src/cmd/api/goapi.go | 105 |
1 files changed, 73 insertions, 32 deletions
diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go index 3d5151754b..533636cd8a 100644 --- a/src/cmd/api/goapi.go +++ b/src/cmd/api/goapi.go @@ -35,8 +35,12 @@ import ( // Flags var ( + // TODO(bradfitz): once Go 1.1 comes out, allow the -c flag to take a comma-separated + // list of files, rather than just one. checkFile = flag.String("c", "", "optional filename to check API against") - verbose = flag.Bool("v", false, "Verbose debugging") + allowNew = flag.Bool("allow_new", true, "allow API additions") + nextFile = flag.String("next", "", "optional filename of tentative upcoming API features for the next release. This file can be lazily maintained. It only affects the delta warnings from the -c file printed on success.") + verbose = flag.Bool("v", false, "verbose debugging") ) var contexts = []*build.Context{ @@ -123,45 +127,82 @@ func main() { } sort.Strings(features) + fail := false + defer func() { + if fail { + os.Exit(1) + } + }() + bw := bufio.NewWriter(os.Stdout) defer bw.Flush() - if *checkFile != "" { - bs, err := ioutil.ReadFile(*checkFile) - if err != nil { - log.Fatalf("Error reading file %s: %v", *checkFile, err) + if *checkFile == "" { + for _, f := range features { + fmt.Fprintf(bw, "%s\n", f) } - v1 := strings.Split(strings.TrimSpace(string(bs)), "\n") - sort.Strings(v1) - v2 := features - take := func(sl *[]string) string { - s := (*sl)[0] - *sl = (*sl)[1:] - return s + return + } + + var required []string + for _, filename := range []string{*checkFile} { + required = append(required, fileFeatures(filename)...) + } + sort.Strings(required) + + var optional = make(map[string]bool) // feature => true + if *nextFile != "" { + for _, feature := range fileFeatures(*nextFile) { + optional[feature] = true } - changes := false - for len(v1) > 0 || len(v2) > 0 { - switch { - case len(v2) == 0 || v1[0] < v2[0]: - fmt.Fprintf(bw, "-%s\n", take(&v1)) - changes = true - case len(v1) == 0 || v1[0] > v2[0]: - fmt.Fprintf(bw, "+%s\n", take(&v2)) - // we allow API additions now - default: - take(&v1) - take(&v2) + } + + take := func(sl *[]string) string { + s := (*sl)[0] + *sl = (*sl)[1:] + return s + } + + for len(required) > 0 || len(features) > 0 { + switch { + case len(features) == 0 || required[0] < features[0]: + fmt.Fprintf(bw, "-%s\n", take(&required)) + fail = true // broke compatibility + case len(required) == 0 || required[0] > features[0]: + newFeature := take(&features) + if optional[newFeature] { + // Known added feature to the upcoming release. + // Delete it from the map so we can detect any upcoming features + // which were never seen. (so we can clean up the nextFile) + delete(optional, newFeature) + } else { + fmt.Fprintf(bw, "+%s\n", newFeature) + if !*allowNew { + fail = true // we're in lock-down mode for next release + } } + default: + take(&required) + take(&features) } - if changes { - bw.Flush() - os.Exit(1) - } - } else { - for _, f := range features { - fmt.Fprintf(bw, "%s\n", f) - } } + + var missing []string + for feature := range optional { + missing = append(missing, feature) + } + sort.Strings(missing) + for _, feature := range missing { + fmt.Fprintf(bw, "(in next file, but not in API) -%s\n", feature) + } +} + +func fileFeatures(filename string) []string { + bs, err := ioutil.ReadFile(filename) + if err != nil { + log.Fatalf("Error reading file %s: %v", filename, err) + } + return strings.Split(strings.TrimSpace(string(bs)), "\n") } // pkgSymbol represents a symbol in a package |
