func (b *Builder) importPackage(dir string, userRequested bool) (*tc.Package, error) {
	klog.V(5).Infof("importPackage %s", dir)
	var pkgPath = importPathString(dir)
	if buildPkg := b.buildPackages[dir]; buildPkg != nil {
		canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath)
		klog.V(5).Infof("importPackage %s, canonical path is %s", dir, canonicalPackage)
		pkgPath = canonicalPackage
	}
	ignoreError := false
	if _, found := b.parsed[pkgPath]; !found {
		ignoreError = true
		if err := b.addDir(dir, userRequested); err != nil {
			return nil, err
		}
		if buildPkg := b.buildPackages[dir]; buildPkg != nil {
			canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath)
			klog.V(5).Infof("importPackage %s, canonical path is %s", dir, canonicalPackage)
			pkgPath = canonicalPackage
		}
	}
	b.userRequested[pkgPath] = userRequested || b.userRequested[pkgPath]
	pkg, err := b.typeCheckPackage(pkgPath)
	if err != nil {
		switch {
		case ignoreError && pkg != nil:
			klog.V(2).Infof("type checking encountered some issues in %q, but ignoring.\n", pkgPath)
		case !ignoreError && pkg != nil:
			klog.V(2).Infof("type checking encountered some errors in %q\n", pkgPath)
			return nil, err
		default:
			return nil, err
		}
	}
	return pkg, nil
}
importPackage is a function that will be called by the type check package when it
needs to import a go package. 'path' is the import path.
package parser
import (
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	tc "go/types"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"k8s.io/gengo/types"
	"k8s.io/klog"
)
type importPathString string
type Builder struct {
	context *build.Context
	IncludeTestFiles bool
	buildPackages map[string]*build.Package
	fset *token.FileSet
	parsed map[importPathString][]parsedFile
	absPaths map[importPathString]string
	typeCheckedPackages map[importPathString]*tc.Package
	userRequested map[importPathString]bool
	endLineToCommentGroup map[fileLine]*ast.CommentGroup
	importGraph map[importPathString]map[string]struct{}
}
type parsedFile struct {
	name string
	file *ast.File
}
type fileLine struct {
	file string
	line int
}
func New() *Builder {
	c := build.Default
	if c.GOROOT == "" {
		if p, err := exec.Command("which", "go").CombinedOutput(); err == nil {
			c.GOROOT = filepath.Dir(filepath.Dir(strings.Trim(string(p), "\n")))
		} else {
			klog.Warningf("Warning: $GOROOT not set, and unable to run `which go` to find it: %v\n", err)
		}
	}
	c.CgoEnabled = false
	return &Builder{
		context:               &c,
		buildPackages:         map[string]*build.Package{},
		typeCheckedPackages:   map[importPathString]*tc.Package{},
		fset:                  token.NewFileSet(),
		parsed:                map[importPathString][]parsedFile{},
		absPaths:              map[importPathString]string{},
		userRequested:         map[importPathString]bool{},
		endLineToCommentGroup: map[fileLine]*ast.CommentGroup{},
		importGraph:           map[importPathString]map[string]struct{}{},
	}
}
func (b *Builder) AddBuildTags(tags ...string) {
	b.context.BuildTags = append(b.context.BuildTags, tags...)
}
func (b *Builder) importBuildPackage(dir string) (*build.Package, error) {
	if buildPkg, ok := b.buildPackages[dir]; ok {
		return buildPkg, nil
	}
	buildPkg, err := b.importWithMode(dir, build.ImportComment)
	if err != nil {
		if _, ok := err.(*build.NoGoError); !ok {
			return nil, fmt.Errorf("unable to import %q: %v", dir, err)
		}
	}
	if buildPkg == nil {
		buildPkg, err = b.importWithMode(dir, build.FindOnly)
		if err != nil {
			return nil, err
		}
	}
	klog.V(5).Infof("saving buildPackage %s", dir)
	b.buildPackages[dir] = buildPkg
	canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath)
	if dir != string(canonicalPackage) {
		if buildPkg, ok := b.buildPackages[string(canonicalPackage)]; ok {
			return buildPkg, nil
		}
		klog.V(5).Infof("saving buildPackage %s", canonicalPackage)
		b.buildPackages[string(canonicalPackage)] = buildPkg
	}
	return buildPkg, nil
}
func (b *Builder) AddFileForTest(pkg string, path string, src []byte) error {
	if err := b.addFile(importPathString(pkg), path, src, true); err != nil {
		return err
	}
	if _, err := b.typeCheckPackage(importPathString(pkg)); err != nil {
		return err
	}
	return nil
}
func (b *Builder) addFile(pkgPath importPathString, path string, src []byte, userRequested bool) error {
	for _, p := range b.parsed[pkgPath] {
		if path == p.name {
			klog.V(5).Infof("addFile %s %s already parsed, skipping", pkgPath, path)
			return nil
		}
	}
	klog.V(6).Infof("addFile %s %s", pkgPath, path)
	p, err := parser.ParseFile(b.fset, path, src, parser.DeclarationErrors|parser.ParseComments)
	if err != nil {
		return err
	}
	b.userRequested[pkgPath] = userRequested || b.userRequested[pkgPath]
	b.parsed[pkgPath] = append(b.parsed[pkgPath], parsedFile{path, p})
	for _, c := range p.Comments {
		position := b.fset.Position(c.End())
		b.endLineToCommentGroup[fileLine{position.Filename, position.Line}] = c
	}
	if b.importGraph[pkgPath] == nil {
		b.importGraph[pkgPath] = map[string]struct{}{}
	}
	for _, im := range p.Imports {
		importedPath := strings.Trim(im.Path.Value, `"`)
		b.importGraph[pkgPath][importedPath] = struct{}{}
	}
	return nil
}
// AddDir adds an entire directory, scanning it for go files. 'dir' should have
// a single go package in it. GOPATH, GOROOT, and the location of your go
// binary (`which go`) will all be searched if dir doesn't literally resolve.
func (b *Builder) AddDir(dir string) error {
	_, err := b.importPackage(dir, true)
	return err
}
// AddDirRecursive is just like AddDir, but it also recursively adds
// subdirectories; it returns an error only if the path couldn't be resolved;
// any directories recursed into without go source are ignored.
func (b *Builder) AddDirRecursive(dir string) error {
	// Add the root.
	if _, err := b.importPackage(dir, true); err != nil {
		klog.Warningf("Ignoring directory %v: %v", dir, err)
	}
	// filepath.Walk includes the root dir, but we already did that, so we'll
	// remove that prefix and rebuild a package import path.
	prefix := b.buildPackages[dir].Dir
	fn := func(filePath string, info os.FileInfo, err error) error {
		if info != nil && info.IsDir() {
			rel := filepath.ToSlash(strings.TrimPrefix(filePath, prefix))
			if rel != "" {
				// Make a pkg path.
				pkg := path.Join(string(canonicalizeImportPath(b.buildPackages[dir].ImportPath)), rel)
				// Add it.
				if _, err := b.importPackage(pkg, true); err != nil {
					klog.Warningf("Ignoring child directory %v: %v", pkg, err)
				}
			}
		}
		return nil
	}
	if err := filepath.Walk(b.buildPackages[dir].Dir, fn); err != nil {
		return err
	}
	return nil
}
// AddDirTo adds an entire directory to a given Universe. Unlike AddDir, this
// processes the package immediately, which makes it safe to use from within a
// generator (rather than just at init time. 'dir' must be a single go package.
// GOPATH, GOROOT, and the location of your go binary (`which go`) will all be
// searched if dir doesn't literally resolve.
// Deprecated. Please use AddDirectoryTo.
func (b *Builder) AddDirTo(dir string, u *types.Universe) error {
	// We want all types from this package, as if they were directly added
	// by the user.  They WERE added by the user, in effect.
	if _, err := b.importPackage(dir, true); err != nil {
		return err
	}
	return b.findTypesIn(canonicalizeImportPath(b.buildPackages[dir].ImportPath), u)
}
// AddDirectoryTo adds an entire directory to a given Universe. Unlike AddDir,
// this processes the package immediately, which makes it safe to use from
// within a generator (rather than just at init time. 'dir' must be a single go
// package. GOPATH, GOROOT, and the location of your go binary (`which go`)
// will all be searched if dir doesn't literally resolve.
func (b *Builder) AddDirectoryTo(dir string, u *types.Universe) (*types.Package, error) {
	// We want all types from this package, as if they were directly added
	// by the user.  They WERE added by the user, in effect.
	if _, err := b.importPackage(dir, true); err != nil {
		return nil, err
	}
	path := canonicalizeImportPath(b.buildPackages[dir].ImportPath)
	if err := b.findTypesIn(path, u); err != nil {
		return nil, err
	}
	return u.Package(string(path)), nil
}
// The implementation of AddDir. A flag indicates whether this directory was
// user-requested or just from following the import graph.
func (b *Builder) addDir(dir string, userRequested bool) error {
	klog.V(5).Infof("addDir %s", dir)
	buildPkg, err := b.importBuildPackage(dir)
	if err != nil {
		return err
	}
	canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath)
	pkgPath := canonicalPackage
	if dir != string(canonicalPackage) {
		klog.V(5).Infof("addDir %s, canonical path is %s", dir, pkgPath)
	}
	// Sanity check the pkg dir has not changed.
	if prev, found := b.absPaths[pkgPath]; found {
		if buildPkg.Dir != prev {
			return fmt.Errorf("package %q (%s) previously resolved to %s", pkgPath, buildPkg.Dir, prev)
		}
	} else {
		b.absPaths[pkgPath] = buildPkg.Dir
	}
	files := []string{}
	files = append(files, buildPkg.GoFiles...)
	if b.IncludeTestFiles {
		files = append(files, buildPkg.TestGoFiles...)
	}
	for _, file := range files {
		if !strings.HasSuffix(file, ".go") {
			continue
		}
		absPath := filepath.Join(buildPkg.Dir, file)
		data, err := ioutil.ReadFile(absPath)
		if err != nil {
			return fmt.Errorf("while loading %q: %v", absPath, err)
		}
		err = b.addFile(pkgPath, absPath, data, userRequested)
		if err != nil {
			return fmt.Errorf("while parsing %q: %v", absPath, err)
		}
	}
	return nil
}