blob: b8b1e70270568c4bc703e2446dc5a44444a525d2 [file] [log] [blame]
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// stdlibsymbol compares database information with
// the stdlib API data at
// https://go.googlesource.com/go/+/refs/heads/master/api.
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"golang.org/x/pkgsite/cmd/internal/cmdconfig"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/config"
"golang.org/x/pkgsite/internal/frontend"
"golang.org/x/pkgsite/internal/log"
"golang.org/x/pkgsite/internal/stdlib"
"golang.org/x/pkgsite/internal/symbol"
)
var (
path = flag.String("path", "", "path to compare against dataset")
)
func main() {
flag.Parse()
log.SetLevel("error")
ctx := context.Background()
cfg, err := config.Init(ctx)
if err != nil {
log.Fatal(ctx, err)
}
if *path != "" {
if err := compareVersionsPage(*path); err != nil {
log.Fatal(ctx, err)
}
return
}
if err := compareDB(ctx, cfg); err != nil {
log.Fatal(ctx, err)
}
}
func compareDB(ctx context.Context, cfg *config.Config) error {
db, err := cmdconfig.OpenDB(ctx, cfg, false)
if err != nil {
return err
}
pkgToErrors, err := db.CompareStdLib(ctx)
if err != nil {
return err
}
for path, errs := range pkgToErrors {
fmt.Printf("----- %s -----\n", path)
for _, e := range errs {
fmt.Print(e)
}
}
return nil
}
func compareVersionsPage(path string) error {
if os.Getenv("GO_DISCOVERY_SERVE_STATS") != "true" {
fmt.Println("Ensure that GO_DISCOVERY_SERVE_STATS=true in the window running the frontend, otherwise the JSON endpoint will be disabled.")
}
url := fmt.Sprintf("http://localhost:8080/%s?tab=versions&m=json", path)
r, err := http.Get(url)
if err != nil {
return err
}
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return err
}
var vd frontend.VersionsDetails
if err := json.Unmarshal(body, &vd); err != nil {
return fmt.Errorf("json.Unmarshal: %v", err)
}
versionToNameToSymbol := map[string]map[string]*internal.UnitSymbol{}
for _, vl := range vd.ThisModule {
for _, vs := range vl.Versions {
v := stdlib.VersionForTag(vs.Version)
versionToNameToSymbol[v] = map[string]*internal.UnitSymbol{}
for _, syms := range vs.Symbols {
for _, s := range syms {
if s.New {
versionToNameToSymbol[v][s.Name] = unitSymbol(s)
}
for _, c := range s.Children {
versionToNameToSymbol[v][c.Name] = unitSymbol(c)
}
}
}
}
}
apiVersions, err := symbol.ParsePackageAPIInfo()
if err != nil {
return err
}
errs := symbol.CompareStdLib(path, apiVersions[path], versionToNameToSymbol)
for _, e := range errs {
fmt.Print(e)
}
return nil
}
func unitSymbol(s *frontend.Symbol) *internal.UnitSymbol {
us := &internal.UnitSymbol{Name: s.Name}
if len(s.Builds) == 0 {
us.AddBuildContext(internal.BuildContextAll)
}
for _, b := range s.Builds {
parts := strings.SplitN(b, "/", 2)
var build internal.BuildContext
switch parts[0] {
case "linux":
build = internal.BuildContextLinux
case "darwin":
build = internal.BuildContextDarwin
case "windows":
build = internal.BuildContextWindows
case "js":
build = internal.BuildContextJS
}
if us.SupportsBuild(build) {
fmt.Printf("duplicate build context for %q: %v\n", s.Name, build)
}
us.AddBuildContext(build)
}
return us
}