174 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			174 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|   | // +build ignore | ||
|  | 
 | ||
|  | // Copyright 2013 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. | ||
|  | 
 | ||
|  | // Command mkindex creates the file "pkgindex.go" containing an index of the Go | ||
|  | // standard library. The file is intended to be built as part of the imports | ||
|  | // package, so that the package may be used in environments where a GOROOT is | ||
|  | // not available (such as App Engine). | ||
|  | package main | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"bytes" | ||
|  | 	"fmt" | ||
|  | 	"go/ast" | ||
|  | 	"go/build" | ||
|  | 	"go/format" | ||
|  | 	"go/parser" | ||
|  | 	"go/token" | ||
|  | 	"io/ioutil" | ||
|  | 	"log" | ||
|  | 	"os" | ||
|  | 	"path" | ||
|  | 	"path/filepath" | ||
|  | 	"strings" | ||
|  | ) | ||
|  | 
 | ||
|  | var ( | ||
|  | 	pkgIndex = make(map[string][]pkg) | ||
|  | 	exports  = make(map[string]map[string]bool) | ||
|  | ) | ||
|  | 
 | ||
|  | func main() { | ||
|  | 	// Don't use GOPATH. | ||
|  | 	ctx := build.Default | ||
|  | 	ctx.GOPATH = "" | ||
|  | 
 | ||
|  | 	// Populate pkgIndex global from GOROOT. | ||
|  | 	for _, path := range ctx.SrcDirs() { | ||
|  | 		f, err := os.Open(path) | ||
|  | 		if err != nil { | ||
|  | 			log.Print(err) | ||
|  | 			continue | ||
|  | 		} | ||
|  | 		children, err := f.Readdir(-1) | ||
|  | 		f.Close() | ||
|  | 		if err != nil { | ||
|  | 			log.Print(err) | ||
|  | 			continue | ||
|  | 		} | ||
|  | 		for _, child := range children { | ||
|  | 			if child.IsDir() { | ||
|  | 				loadPkg(path, child.Name()) | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	// Populate exports global. | ||
|  | 	for _, ps := range pkgIndex { | ||
|  | 		for _, p := range ps { | ||
|  | 			e := loadExports(p.dir) | ||
|  | 			if e != nil { | ||
|  | 				exports[p.dir] = e | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Construct source file. | ||
|  | 	var buf bytes.Buffer | ||
|  | 	fmt.Fprint(&buf, pkgIndexHead) | ||
|  | 	fmt.Fprintf(&buf, "var pkgIndexMaster = %#v\n", pkgIndex) | ||
|  | 	fmt.Fprintf(&buf, "var exportsMaster = %#v\n", exports) | ||
|  | 	src := buf.Bytes() | ||
|  | 
 | ||
|  | 	// Replace main.pkg type name with pkg. | ||
|  | 	src = bytes.Replace(src, []byte("main.pkg"), []byte("pkg"), -1) | ||
|  | 	// Replace actual GOROOT with "/go". | ||
|  | 	src = bytes.Replace(src, []byte(ctx.GOROOT), []byte("/go"), -1) | ||
|  | 	// Add some line wrapping. | ||
|  | 	src = bytes.Replace(src, []byte("}, "), []byte("},\n"), -1) | ||
|  | 	src = bytes.Replace(src, []byte("true, "), []byte("true,\n"), -1) | ||
|  | 
 | ||
|  | 	var err error | ||
|  | 	src, err = format.Source(src) | ||
|  | 	if err != nil { | ||
|  | 		log.Fatal(err) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Write out source file. | ||
|  | 	err = ioutil.WriteFile("pkgindex.go", src, 0644) | ||
|  | 	if err != nil { | ||
|  | 		log.Fatal(err) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | const pkgIndexHead = `package imports | ||
|  | 
 | ||
|  | func init() { | ||
|  | 	pkgIndexOnce.Do(func() { | ||
|  | 		pkgIndex.m = pkgIndexMaster | ||
|  | 	}) | ||
|  | 	loadExports = func(dir string) map[string]bool { | ||
|  | 		return exportsMaster[dir] | ||
|  | 	} | ||
|  | } | ||
|  | ` | ||
|  | 
 | ||
|  | type pkg struct { | ||
|  | 	importpath string // full pkg import path, e.g. "net/http" | ||
|  | 	dir        string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt" | ||
|  | } | ||
|  | 
 | ||
|  | var fset = token.NewFileSet() | ||
|  | 
 | ||
|  | func loadPkg(root, importpath string) { | ||
|  | 	shortName := path.Base(importpath) | ||
|  | 	if shortName == "testdata" { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	dir := filepath.Join(root, importpath) | ||
|  | 	pkgIndex[shortName] = append(pkgIndex[shortName], pkg{ | ||
|  | 		importpath: importpath, | ||
|  | 		dir:        dir, | ||
|  | 	}) | ||
|  | 
 | ||
|  | 	pkgDir, err := os.Open(dir) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 	children, err := pkgDir.Readdir(-1) | ||
|  | 	pkgDir.Close() | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 	for _, child := range children { | ||
|  | 		name := child.Name() | ||
|  | 		if name == "" { | ||
|  | 			continue | ||
|  | 		} | ||
|  | 		if c := name[0]; c == '.' || ('0' <= c && c <= '9') { | ||
|  | 			continue | ||
|  | 		} | ||
|  | 		if child.IsDir() { | ||
|  | 			loadPkg(root, filepath.Join(importpath, name)) | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func loadExports(dir string) map[string]bool { | ||
|  | 	exports := make(map[string]bool) | ||
|  | 	buildPkg, err := build.ImportDir(dir, 0) | ||
|  | 	if err != nil { | ||
|  | 		if strings.Contains(err.Error(), "no buildable Go source files in") { | ||
|  | 			return nil | ||
|  | 		} | ||
|  | 		log.Printf("could not import %q: %v", dir, err) | ||
|  | 		return nil | ||
|  | 	} | ||
|  | 	for _, file := range buildPkg.GoFiles { | ||
|  | 		f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0) | ||
|  | 		if err != nil { | ||
|  | 			log.Printf("could not parse %q: %v", file, err) | ||
|  | 			continue | ||
|  | 		} | ||
|  | 		for name := range f.Scope.Objects { | ||
|  | 			if ast.IsExported(name) { | ||
|  | 				exports[name] = true | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return exports | ||
|  | } |