diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:36:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:36:04 +0000 |
commit | b09c6d56832eb1718c07d74abf3bc6ae3fe4e030 (patch) | |
tree | d2caec2610d4ea887803ec9e9c3cd77136c448ba /dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/apidiff/main.go | |
parent | Initial commit. (diff) | |
download | icingadb-b09c6d56832eb1718c07d74abf3bc6ae3fe4e030.tar.xz icingadb-b09c6d56832eb1718c07d74abf3bc6ae3fe4e030.zip |
Adding upstream version 1.1.0.upstream/1.1.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/apidiff/main.go | 163 |
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 +} |