github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/internal/imports/mkstdlib.go (about)

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build ignore
     6  // +build ignore
     7  
     8  // mkstdlib generates the zstdlib.go file, containing the Go standard
     9  // library API symbols. It's baked into the binary to avoid scanning
    10  // GOPATH in the common case.
    11  package main
    12  
    13  import (
    14  	"bufio"
    15  	"bytes"
    16  	"fmt"
    17  	"go/format"
    18  	"go/token"
    19  	"io"
    20  	"io/ioutil"
    21  	"log"
    22  	"os"
    23  	"path/filepath"
    24  	"regexp"
    25  	"runtime"
    26  	"sort"
    27  	"strings"
    28  
    29  	"golang.org/x/tools/go/packages"
    30  )
    31  
    32  func mustOpen(name string) io.Reader {
    33  	f, err := os.Open(name)
    34  	if err != nil {
    35  		log.Fatal(err)
    36  	}
    37  	return f
    38  }
    39  
    40  func api(base string) string {
    41  	return filepath.Join(runtime.GOROOT(), "api", base)
    42  }
    43  
    44  var sym = regexp.MustCompile(`^pkg (\S+).*?, (?:var|func|type|const) ([A-Z]\w*)`)
    45  
    46  func main() {
    47  	var buf bytes.Buffer
    48  	outf := func(format string, args ...interface{}) {
    49  		fmt.Fprintf(&buf, format, args...)
    50  	}
    51  	outf(`// Copyright 2022 The Go Authors. All rights reserved.
    52  // Use of this source code is governed by a BSD-style
    53  // license that can be found in the LICENSE file.
    54  
    55  `)
    56  	outf("// Code generated by mkstdlib.go. DO NOT EDIT.\n\n")
    57  	outf("package imports\n")
    58  	outf("var stdlib = map[string][]string{\n")
    59  	f := readAPI()
    60  	sc := bufio.NewScanner(f)
    61  
    62  	// The APIs of the syscall/js and unsafe packages need to be computed explicitly,
    63  	// because they're not included in the GOROOT/api/go1.*.txt files at this time.
    64  	pkgs := map[string]map[string]bool{
    65  		"syscall/js": syms("syscall/js", "GOOS=js", "GOARCH=wasm"),
    66  		"unsafe":     syms("unsafe"),
    67  	}
    68  	paths := []string{"syscall/js", "unsafe"}
    69  
    70  	for sc.Scan() {
    71  		l := sc.Text()
    72  		if m := sym.FindStringSubmatch(l); m != nil {
    73  			path, sym := m[1], m[2]
    74  
    75  			if _, ok := pkgs[path]; !ok {
    76  				pkgs[path] = map[string]bool{}
    77  				paths = append(paths, path)
    78  			}
    79  			pkgs[path][sym] = true
    80  		}
    81  	}
    82  	if err := sc.Err(); err != nil {
    83  		log.Fatal(err)
    84  	}
    85  	sort.Strings(paths)
    86  	for _, path := range paths {
    87  		outf("\t%q: {\n", path)
    88  		pkg := pkgs[path]
    89  		var syms []string
    90  		for sym := range pkg {
    91  			syms = append(syms, sym)
    92  		}
    93  		sort.Strings(syms)
    94  		for _, sym := range syms {
    95  			outf("\t\t%q,\n", sym)
    96  		}
    97  		outf("},\n")
    98  	}
    99  	outf("}\n")
   100  	fmtbuf, err := format.Source(buf.Bytes())
   101  	if err != nil {
   102  		log.Fatal(err)
   103  	}
   104  	err = ioutil.WriteFile("zstdlib.go", fmtbuf, 0666)
   105  	if err != nil {
   106  		log.Fatal(err)
   107  	}
   108  }
   109  
   110  // readAPI opens an io.Reader that reads all stdlib API content.
   111  func readAPI() io.Reader {
   112  	entries, err := os.ReadDir(filepath.Join(runtime.GOROOT(), "api"))
   113  	if err != nil {
   114  		log.Fatal(err)
   115  	}
   116  	var readers []io.Reader
   117  	for _, entry := range entries {
   118  		name := entry.Name()
   119  		if strings.HasPrefix(name, "go") && strings.HasSuffix(name, ".txt") {
   120  			readers = append(readers, mustOpen(api(name)))
   121  		}
   122  	}
   123  	return io.MultiReader(readers...)
   124  }
   125  
   126  // syms computes the exported symbols in the specified package.
   127  func syms(pkg string, extraEnv ...string) map[string]bool {
   128  	var env []string
   129  	if len(extraEnv) != 0 {
   130  		env = append(os.Environ(), extraEnv...)
   131  	}
   132  	pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedTypes, Env: env}, pkg)
   133  	if err != nil {
   134  		log.Fatalln(err)
   135  	} else if len(pkgs) != 1 {
   136  		log.Fatalf("got %d packages, want one package %q", len(pkgs), pkg)
   137  	}
   138  	syms := make(map[string]bool)
   139  	for _, name := range pkgs[0].Types.Scope().Names() {
   140  		if token.IsExported(name) {
   141  			syms[name] = true
   142  		}
   143  	}
   144  	return syms
   145  }