github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/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  	"io/ioutil"
    23  	"log"
    24  	"os"
    25  	"path"
    26  	"path/filepath"
    27  	"strings"
    28  )
    29  
    30  var (
    31  	pkgIndex = make(map[string][]pkg)
    32  	exports  = make(map[string]map[string]bool)
    33  )
    34  
    35  func main() {
    36  	// Don't use GOPATH.
    37  	ctx := build.Default
    38  	ctx.GOPATH = ""
    39  
    40  	// Populate pkgIndex global from GOROOT.
    41  	for _, path := range ctx.SrcDirs() {
    42  		f, err := os.Open(path)
    43  		if err != nil {
    44  			log.Print(err)
    45  			continue
    46  		}
    47  		children, err := f.Readdir(-1)
    48  		f.Close()
    49  		if err != nil {
    50  			log.Print(err)
    51  			continue
    52  		}
    53  		for _, child := range children {
    54  			if child.IsDir() {
    55  				loadPkg(path, child.Name())
    56  			}
    57  		}
    58  	}
    59  	// Populate exports global.
    60  	for _, ps := range pkgIndex {
    61  		for _, p := range ps {
    62  			e := loadExports(p.dir)
    63  			if e != nil {
    64  				exports[p.dir] = e
    65  			}
    66  		}
    67  	}
    68  
    69  	// Construct source file.
    70  	var buf bytes.Buffer
    71  	fmt.Fprint(&buf, pkgIndexHead)
    72  	fmt.Fprintf(&buf, "var pkgIndexMaster = %#v\n", pkgIndex)
    73  	fmt.Fprintf(&buf, "var exportsMaster = %#v\n", exports)
    74  	src := buf.Bytes()
    75  
    76  	// Replace main.pkg type name with pkg.
    77  	src = bytes.Replace(src, []byte("main.pkg"), []byte("pkg"), -1)
    78  	// Replace actual GOROOT with "/go".
    79  	src = bytes.Replace(src, []byte(ctx.GOROOT), []byte("/go"), -1)
    80  	// Add some line wrapping.
    81  	src = bytes.Replace(src, []byte("}, "), []byte("},\n"), -1)
    82  	src = bytes.Replace(src, []byte("true, "), []byte("true,\n"), -1)
    83  
    84  	var err error
    85  	src, err = format.Source(src)
    86  	if err != nil {
    87  		log.Fatal(err)
    88  	}
    89  
    90  	// Write out source file.
    91  	err = ioutil.WriteFile("pkgindex.go", src, 0644)
    92  	if err != nil {
    93  		log.Fatal(err)
    94  	}
    95  }
    96  
    97  const pkgIndexHead = `package imports
    98  
    99  func init() {
   100  	pkgIndexOnce.Do(func() {
   101  		pkgIndex.m = pkgIndexMaster
   102  	})
   103  	loadExports = func(dir string) map[string]bool {
   104  		return exportsMaster[dir]
   105  	}
   106  }
   107  `
   108  
   109  type pkg struct {
   110  	importpath string // full pkg import path, e.g. "net/http"
   111  	dir        string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt"
   112  }
   113  
   114  var fset = token.NewFileSet()
   115  
   116  func loadPkg(root, importpath string) {
   117  	shortName := path.Base(importpath)
   118  	if shortName == "testdata" {
   119  		return
   120  	}
   121  
   122  	dir := filepath.Join(root, importpath)
   123  	pkgIndex[shortName] = append(pkgIndex[shortName], pkg{
   124  		importpath: importpath,
   125  		dir:        dir,
   126  	})
   127  
   128  	pkgDir, err := os.Open(dir)
   129  	if err != nil {
   130  		return
   131  	}
   132  	children, err := pkgDir.Readdir(-1)
   133  	pkgDir.Close()
   134  	if err != nil {
   135  		return
   136  	}
   137  	for _, child := range children {
   138  		name := child.Name()
   139  		if name == "" {
   140  			continue
   141  		}
   142  		if c := name[0]; c == '.' || ('0' <= c && c <= '9') {
   143  			continue
   144  		}
   145  		if child.IsDir() {
   146  			loadPkg(root, filepath.Join(importpath, name))
   147  		}
   148  	}
   149  }
   150  
   151  func loadExports(dir string) map[string]bool {
   152  	exports := make(map[string]bool)
   153  	buildPkg, err := build.ImportDir(dir, 0)
   154  	if err != nil {
   155  		if strings.Contains(err.Error(), "no buildable Go source files in") {
   156  			return nil
   157  		}
   158  		log.Printf("could not import %q: %v", dir, err)
   159  		return nil
   160  	}
   161  	for _, file := range buildPkg.GoFiles {
   162  		f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
   163  		if err != nil {
   164  			log.Printf("could not parse %q: %v", file, err)
   165  			continue
   166  		}
   167  		for name := range f.Scope.Objects {
   168  			if ast.IsExported(name) {
   169  				exports[name] = true
   170  			}
   171  		}
   172  	}
   173  	return exports
   174  }