golang.org/x/tools@v0.21.0/internal/imports/mkindex.go (about)

     1  //go:build ignore
     2  // +build ignore
     3  
     4  // Copyright 2013 The Go Authors. All rights reserved.
     5  // Use of this source code is governed by a BSD-style
     6  // license that can be found in the LICENSE file.
     7  
     8  // Command mkindex creates the file "pkgindex.go" containing an index of the Go
     9  // standard library. The file is intended to be built as part of the imports
    10  // package, so that the package may be used in environments where a GOROOT is
    11  // not available (such as App Engine).
    12  package imports
    13  
    14  import (
    15  	"bytes"
    16  	"fmt"
    17  	"go/ast"
    18  	"go/build"
    19  	"go/format"
    20  	"go/parser"
    21  	"go/token"
    22  	"log"
    23  	"os"
    24  	"path"
    25  	"path/filepath"
    26  	"strings"
    27  )
    28  
    29  var (
    30  	pkgIndex = make(map[string][]pkg)
    31  	exports  = make(map[string]map[string]bool)
    32  )
    33  
    34  func main() {
    35  	// Don't use GOPATH.
    36  	ctx := build.Default
    37  	ctx.GOPATH = ""
    38  
    39  	// Populate pkgIndex global from GOROOT.
    40  	for _, path := range ctx.SrcDirs() {
    41  		f, err := os.Open(path)
    42  		if err != nil {
    43  			log.Print(err)
    44  			continue
    45  		}
    46  		children, err := f.Readdir(-1)
    47  		f.Close()
    48  		if err != nil {
    49  			log.Print(err)
    50  			continue
    51  		}
    52  		for _, child := range children {
    53  			if child.IsDir() {
    54  				loadPkg(path, child.Name())
    55  			}
    56  		}
    57  	}
    58  	// Populate exports global.
    59  	for _, ps := range pkgIndex {
    60  		for _, p := range ps {
    61  			e := loadExports(p.dir)
    62  			if e != nil {
    63  				exports[p.dir] = e
    64  			}
    65  		}
    66  	}
    67  
    68  	// Construct source file.
    69  	var buf bytes.Buffer
    70  	fmt.Fprint(&buf, pkgIndexHead)
    71  	fmt.Fprintf(&buf, "var pkgIndexMaster = %#v\n", pkgIndex)
    72  	fmt.Fprintf(&buf, "var exportsMaster = %#v\n", exports)
    73  	src := buf.Bytes()
    74  
    75  	// Replace main.pkg type name with pkg.
    76  	src = bytes.Replace(src, []byte("main.pkg"), []byte("pkg"), -1)
    77  	// Replace actual GOROOT with "/go".
    78  	src = bytes.Replace(src, []byte(ctx.GOROOT), []byte("/go"), -1)
    79  	// Add some line wrapping.
    80  	src = bytes.Replace(src, []byte("}, "), []byte("},\n"), -1)
    81  	src = bytes.Replace(src, []byte("true, "), []byte("true,\n"), -1)
    82  
    83  	var err error
    84  	src, err = format.Source(src)
    85  	if err != nil {
    86  		log.Fatal(err)
    87  	}
    88  
    89  	// Write out source file.
    90  	err = os.WriteFile("pkgindex.go", src, 0644)
    91  	if err != nil {
    92  		log.Fatal(err)
    93  	}
    94  }
    95  
    96  const pkgIndexHead = `package imports
    97  
    98  func init() {
    99  	pkgIndexOnce.Do(func() {
   100  		pkgIndex.m = pkgIndexMaster
   101  	})
   102  	loadExports = func(dir string) map[string]bool {
   103  		return exportsMaster[dir]
   104  	}
   105  }
   106  `
   107  
   108  type pkg struct {
   109  	importpath string // full pkg import path, e.g. "net/http"
   110  	dir        string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt"
   111  }
   112  
   113  var fset = token.NewFileSet()
   114  
   115  func loadPkg(root, importpath string) {
   116  	shortName := path.Base(importpath)
   117  	if shortName == "testdata" {
   118  		return
   119  	}
   120  
   121  	dir := filepath.Join(root, importpath)
   122  	pkgIndex[shortName] = append(pkgIndex[shortName], pkg{
   123  		importpath: importpath,
   124  		dir:        dir,
   125  	})
   126  
   127  	pkgDir, err := os.Open(dir)
   128  	if err != nil {
   129  		return
   130  	}
   131  	children, err := pkgDir.Readdir(-1)
   132  	pkgDir.Close()
   133  	if err != nil {
   134  		return
   135  	}
   136  	for _, child := range children {
   137  		name := child.Name()
   138  		if name == "" {
   139  			continue
   140  		}
   141  		if c := name[0]; c == '.' || ('0' <= c && c <= '9') {
   142  			continue
   143  		}
   144  		if child.IsDir() {
   145  			loadPkg(root, filepath.Join(importpath, name))
   146  		}
   147  	}
   148  }
   149  
   150  func loadExports(dir string) map[string]bool {
   151  	exports := make(map[string]bool)
   152  	buildPkg, err := build.ImportDir(dir, 0)
   153  	if err != nil {
   154  		if strings.Contains(err.Error(), "no buildable Go source files in") {
   155  			return nil
   156  		}
   157  		log.Printf("could not import %q: %v", dir, err)
   158  		return nil
   159  	}
   160  	for _, file := range buildPkg.GoFiles {
   161  		f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
   162  		if err != nil {
   163  			log.Printf("could not parse %q: %v", file, err)
   164  			continue
   165  		}
   166  		for name := range f.Scope.Objects {
   167  			if ast.IsExported(name) {
   168  				exports[name] = true
   169  			}
   170  		}
   171  	}
   172  	return exports
   173  }