summaryrefslogtreecommitdiffstats
path: root/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/apidiff/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/apidiff/main.go')
-rw-r--r--dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/apidiff/main.go163
1 files changed, 163 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/apidiff/main.go b/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/apidiff/main.go
new file mode 100644
index 0000000..92b763c
--- /dev/null
+++ b/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/apidiff/main.go
@@ -0,0 +1,163 @@
+// Command apidiff determines whether two versions of a package are compatible
+package main
+
+import (
+ "bufio"
+ "flag"
+ "fmt"
+ "go/token"
+ "go/types"
+ "os"
+ "strings"
+
+ "golang.org/x/exp/apidiff"
+ "golang.org/x/tools/go/gcexportdata"
+ "golang.org/x/tools/go/packages"
+)
+
+var (
+ exportDataOutfile = flag.String("w", "", "file for export data")
+ incompatibleOnly = flag.Bool("incompatible", false, "display only incompatible changes")
+ allowInternal = flag.Bool("allow-internal", false, "allow apidiff to compare internal packages")
+)
+
+func main() {
+ flag.Usage = func() {
+ w := flag.CommandLine.Output()
+ fmt.Fprintf(w, "usage:\n")
+ fmt.Fprintf(w, "apidiff OLD NEW\n")
+ fmt.Fprintf(w, " compares OLD and NEW package APIs\n")
+ fmt.Fprintf(w, " where OLD and NEW are either import paths or files of export data\n")
+ fmt.Fprintf(w, "apidiff -w FILE IMPORT_PATH\n")
+ fmt.Fprintf(w, " writes export data of the package at IMPORT_PATH to FILE\n")
+ fmt.Fprintf(w, " NOTE: In a GOPATH-less environment, this option consults the\n")
+ fmt.Fprintf(w, " module cache by default, unless used in the directory that\n")
+ fmt.Fprintf(w, " contains the go.mod module definition that IMPORT_PATH belongs\n")
+ fmt.Fprintf(w, " to. In most cases users want the latter behavior, so be sure\n")
+ fmt.Fprintf(w, " to cd to the exact directory which contains the module\n")
+ fmt.Fprintf(w, " definition of IMPORT_PATH.\n")
+ flag.PrintDefaults()
+ }
+
+ flag.Parse()
+ if *exportDataOutfile != "" {
+ if len(flag.Args()) != 1 {
+ flag.Usage()
+ os.Exit(2)
+ }
+ pkg := mustLoadPackage(flag.Arg(0))
+ if err := writeExportData(pkg, *exportDataOutfile); err != nil {
+ die("writing export data: %v", err)
+ }
+ } else {
+ if len(flag.Args()) != 2 {
+ flag.Usage()
+ os.Exit(2)
+ }
+ oldpkg := mustLoadOrRead(flag.Arg(0))
+ newpkg := mustLoadOrRead(flag.Arg(1))
+ if !*allowInternal {
+ if isInternalPackage(oldpkg.Path()) && isInternalPackage(newpkg.Path()) {
+ fmt.Fprintf(os.Stderr, "Ignoring internal package %s\n", oldpkg.Path())
+ os.Exit(0)
+ }
+ }
+ report := apidiff.Changes(oldpkg, newpkg)
+ var err error
+ if *incompatibleOnly {
+ err = report.TextIncompatible(os.Stdout, false)
+ } else {
+ err = report.Text(os.Stdout)
+ }
+ if err != nil {
+ die("writing report: %v", err)
+ }
+ }
+}
+
+func mustLoadOrRead(importPathOrFile string) *types.Package {
+ fileInfo, err := os.Stat(importPathOrFile)
+ if err == nil && fileInfo.Mode().IsRegular() {
+ pkg, err := readExportData(importPathOrFile)
+ if err != nil {
+ die("reading export data from %s: %v", importPathOrFile, err)
+ }
+ return pkg
+ } else {
+ return mustLoadPackage(importPathOrFile).Types
+ }
+}
+
+func mustLoadPackage(importPath string) *packages.Package {
+ pkg, err := loadPackage(importPath)
+ if err != nil {
+ die("loading %s: %v", importPath, err)
+ }
+ return pkg
+}
+
+func loadPackage(importPath string) (*packages.Package, error) {
+ cfg := &packages.Config{Mode: packages.LoadTypes |
+ packages.NeedName | packages.NeedTypes | packages.NeedImports | packages.NeedDeps,
+ }
+ pkgs, err := packages.Load(cfg, importPath)
+ if err != nil {
+ return nil, err
+ }
+ if len(pkgs) == 0 {
+ return nil, fmt.Errorf("found no packages for import %s", importPath)
+ }
+ if len(pkgs[0].Errors) > 0 {
+ return nil, pkgs[0].Errors[0]
+ }
+ return pkgs[0], nil
+}
+
+func readExportData(filename string) (*types.Package, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ r := bufio.NewReader(f)
+ m := map[string]*types.Package{}
+ pkgPath, err := r.ReadString('\n')
+ if err != nil {
+ return nil, err
+ }
+ pkgPath = pkgPath[:len(pkgPath)-1] // remove delimiter
+ return gcexportdata.Read(r, token.NewFileSet(), m, pkgPath)
+}
+
+func writeExportData(pkg *packages.Package, filename string) error {
+ f, err := os.Create(filename)
+ if err != nil {
+ return err
+ }
+ // Include the package path in the file. The exportdata format does
+ // not record the path of the package being written.
+ fmt.Fprintln(f, pkg.PkgPath)
+ err1 := gcexportdata.Write(f, pkg.Fset, pkg.Types)
+ err2 := f.Close()
+ if err1 != nil {
+ return err1
+ }
+ return err2
+}
+
+func die(format string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, format+"\n", args...)
+ os.Exit(1)
+}
+
+func isInternalPackage(pkgPath string) bool {
+ switch {
+ case strings.HasSuffix(pkgPath, "/internal"):
+ return true
+ case strings.Contains(pkgPath, "/internal/"):
+ return true
+ case pkgPath == "internal", strings.HasPrefix(pkgPath, "internal/"):
+ return true
+ }
+ return false
+}