Ejemplo n.º 1
0
Archivo: pkg.go Proyecto: zmb3/gogetdoc
// PackageDoc gets the documentation for the package with the specified import
// path and writes it to out.
func PackageDoc(ctxt *build.Context, fset *token.FileSet, srcDir string, importPath string) (*Doc, error) {
	buildPkg, err := ctxt.Import(importPath, srcDir, build.ImportComment)
	if err != nil {
		return nil, err
	}
	// only parse .go files in the specified package
	filter := func(info os.FileInfo) bool {
		for _, fname := range buildPkg.GoFiles {
			if fname == info.Name() {
				return true
			}
		}
		return false
	}
	// TODO we've already parsed the files via go/loader...can we avoid doing it again?
	pkgs, err := parser.ParseDir(fset, buildPkg.Dir, filter, parser.PackageClauseOnly|parser.ParseComments)
	if err != nil {
		return nil, err
	}
	if astPkg, ok := pkgs[buildPkg.Name]; ok {
		docPkg := doc.New(astPkg, importPath, 0)
		// TODO: we could also include package-level constants, vars, and functions (like the go doc command)
		return &Doc{
			Name: buildPkg.Name,
			Decl: "package " + buildPkg.Name, // TODO: add '// import "pkg"' (like godoc)
			Doc:  docPkg.Doc,
		}, nil
	}
	return nil, errors.New("No documentation found for " + buildPkg.Name)
}
Ejemplo n.º 2
0
func resolvePackageSpec(ctx *build.Context, cwd string, src io.Reader, spec string) string {
	if strings.HasSuffix(spec, ".go") {
		d := path.Dir(spec)
		if !buildutil.IsAbsPath(ctx, d) {
			d = buildutil.JoinPath(ctx, cwd, d)
		}
		if bpkg, err := ctx.ImportDir(d, build.FindOnly); err == nil {
			return bpkg.ImportPath
		}
	}
	path := spec
	switch {
	case strings.HasPrefix(spec, "."):
		if bpkg, err := ctx.Import(spec, cwd, build.FindOnly); err == nil {
			path = bpkg.ImportPath
		}
	case strings.HasPrefix(spec, "/"):
		path = spec[1:]
	default:
		if p, ok := readImports(cwd, src)[spec]; ok {
			path = p
		}
	}
	return strings.TrimSuffix(path, "/")
}
Ejemplo n.º 3
0
// ContainingPackage returns the package containing filename.
//
// If filename is not absolute, it is interpreted relative to working directory dir.
// All I/O is via the build context's file system interface, if any.
//
// The '...Files []string' fields of the resulting build.Package are not
// populated (build.FindOnly mode).
//
// TODO(adonovan): call this from oracle when the tree thaws.
//
func ContainingPackage(ctxt *build.Context, dir, filename string) (*build.Package, error) {
	if !IsAbsPath(ctxt, filename) {
		filename = JoinPath(ctxt, dir, filename)
	}

	// We must not assume the file tree uses
	// "/" always,
	// `\` always,
	// or os.PathSeparator (which varies by platform),
	// but to make any progress, we are forced to assume that
	// paths will not use `\` unless the PathSeparator
	// is also `\`, thus we can rely on filepath.ToSlash for some sanity.

	dirSlash := path.Dir(filepath.ToSlash(filename)) + "/"

	// We assume that no source root (GOPATH[i] or GOROOT) contains any other.
	for _, srcdir := range ctxt.SrcDirs() {
		srcdirSlash := filepath.ToSlash(srcdir) + "/"
		if dirHasPrefix(dirSlash, srcdirSlash) {
			importPath := dirSlash[len(srcdirSlash) : len(dirSlash)-len("/")]
			return ctxt.Import(importPath, dir, build.FindOnly)
		}
	}

	return nil, fmt.Errorf("can't find package containing %s", filename)
}
Ejemplo n.º 4
0
// subpackages returns the set of packages in the given srcDir whose
// import paths start with dir.
func subpackages(ctxt *build.Context, srcDir string, dir string) map[string]bool {
	subs := map[string]bool{dir: true}

	// Find all packages under srcDir whose import paths start with dir.
	buildutil.ForEachPackage(ctxt, func(pkg string, err error) {
		if err != nil {
			log.Fatalf("unexpected error in ForEachPackage: %v", err)
		}

		if !strings.HasPrefix(pkg, path.Join(dir, "")) {
			return
		}

		p, err := ctxt.Import(pkg, "", build.FindOnly)
		if err != nil {
			log.Fatalf("unexpected: package %s can not be located by build context: %s", pkg, err)
		}
		if p.SrcRoot == "" {
			log.Fatalf("unexpected: could not determine srcDir for package %s: %s", pkg, err)
		}
		if p.SrcRoot != srcDir {
			return
		}

		subs[pkg] = true
	})

	return subs
}
Ejemplo n.º 5
0
//Import imports a package.
//
//path is run through ToImport.
//
//If ctx is nil, the default context is used.
//
//N.B. we require a pointer to a build.Context for caching.
//Two build contexts with identical values that are not represented
//by the same pointer will have all packages imported by them
//cached separately.
func Import(ctx *build.Context, path string) (*Package, error) {
	if ctx == nil {
		ctx = defaultctx
	}
	root, path, err := ToImport(path)
	if err != nil {
		return nil, err
	}

	ident := ident{ctx, path}
	if pkg := pkgget(ident); pkg != nil {
		return pkg, nil
	}

	p, err := ctx.Import(path, root, 0)
	if err != nil {
		return nil, err
	}

	pkg := &Package{
		Context: ctx,
		Build:   p,
	}
	pkgset(ident, pkg)

	return pkg, nil
}
Ejemplo n.º 6
0
// defaultFindPackage locates the specified (possibly empty) package
// using go/build logic.  It returns an error if not found.
func defaultFindPackage(ctxt *build.Context, path string) (*build.Package, error) {
	// Import(srcDir="") disables local imports, e.g. import "./foo".
	bp, err := ctxt.Import(path, "", 0)
	if _, ok := err.(*build.NoGoError); ok {
		return bp, nil // empty directory is not an error
	}
	return bp, err
}
Ejemplo n.º 7
0
// find_global_file returns the file path of the compiled package corresponding to the specified
// import, and a boolean stating whether such path is valid.
// TODO: Return only one value, possibly empty string if not found.
func find_global_file(imp string, context build.Context) (string, bool) {
	// gocode synthetically generates the builtin package
	// "unsafe", since the "unsafe.a" package doesn't really exist.
	// Thus, when the user request for the package "unsafe" we
	// would return synthetic global file that would be used
	// just as a key name to find this synthetic package
	if imp == "unsafe" {
		return "unsafe", true
	}

	pkgfile := fmt.Sprintf("%s.a", imp)

	// if lib-path is defined, use it
	if g_config.LibPath != "" {
		for _, p := range filepath.SplitList(g_config.LibPath) {
			pkg_path := filepath.Join(p, pkgfile)
			if file_exists(pkg_path) {
				log_found_package_maybe(imp, pkg_path)
				return pkg_path, true
			}
			// Also check the relevant pkg/OS_ARCH dir for the libpath, if provided.
			pkgdir := fmt.Sprintf("%s_%s", context.GOOS, context.GOARCH)
			pkg_path = filepath.Join(p, "pkg", pkgdir, pkgfile)
			if file_exists(pkg_path) {
				log_found_package_maybe(imp, pkg_path)
				return pkg_path, true
			}
			// Also check the relevant pkg/OS/ARCH dir for the libpath, if provided.
			pkg_path = filepath.Join(p, "pkg", context.GOOS, context.GOARCH, pkgfile)
			if file_exists(pkg_path) {
				log_found_package_maybe(imp, pkg_path)
				return pkg_path, true
			}
		}
	}

	p, err := context.Import(imp, "", build.AllowBinary|build.FindOnly)
	if err == nil {
		if g_config.Autobuild {
			err = autobuild(p)
			if err != nil && *g_debug {
				log.Printf("Autobuild error: %s\n", err)
			}
		}
		if file_exists(p.PkgObj) {
			log_found_package_maybe(imp, p.PkgObj)
			return p.PkgObj, true
		}
	}

	if *g_debug {
		log.Printf("Import path %q was not resolved\n", imp)
		log.Println("Gocode's build context is:")
		log_build_context(context)
	}
	return "", false
}
Ejemplo n.º 8
0
// importRuntime locates the the runtime package and parses its files
// to *ast.Files. This is used to generate runtime type structures.
func parseRuntime(buildctx *build.Context, fset *token.FileSet) ([]*ast.File, error) {
	buildpkg, err := buildctx.Import("github.com/go-llvm/llgo/pkg/runtime", "", 0)
	if err != nil {
		return nil, err
	}
	filenames := make([]string, len(buildpkg.GoFiles))
	for i, f := range buildpkg.GoFiles {
		filenames[i] = path.Join(buildpkg.Dir, f)
	}
	return parseFiles(fset, filenames)
}
Ejemplo n.º 9
0
// findPackageMember returns the type and position of the declaration of
// pkg.member by loading and parsing the files of that package.
// srcdir is the directory in which the import appears.
func findPackageMember(ctxt *build.Context, fset *token.FileSet, srcdir, pkg, member string) (token.Token, token.Pos, error) {
	bp, err := ctxt.Import(pkg, srcdir, 0)
	if err != nil {
		return 0, token.NoPos, err // no files for package
	}

	// TODO(adonovan): opt: parallelize.
	for _, fname := range bp.GoFiles {
		filename := filepath.Join(bp.Dir, fname)

		// Parse the file, opening it the file via the build.Context
		// so that we observe the effects of the -modified flag.
		f, _ := buildutil.ParseFile(fset, ctxt, nil, ".", filename, parser.Mode(0))
		if f == nil {
			continue
		}

		// Find a package-level decl called 'member'.
		for _, decl := range f.Decls {
			switch decl := decl.(type) {
			case *ast.GenDecl:
				for _, spec := range decl.Specs {
					switch spec := spec.(type) {
					case *ast.ValueSpec:
						// const or var
						for _, id := range spec.Names {
							if id.Name == member {
								return decl.Tok, id.Pos(), nil
							}
						}
					case *ast.TypeSpec:
						if spec.Name.Name == member {
							return token.TYPE, spec.Name.Pos(), nil
						}
					case *ast.AliasSpec:
						if spec.Name.Name == member {
							return decl.Tok, spec.Name.Pos(), nil
						}
					}
				}
			case *ast.FuncDecl:
				if decl.Recv == nil && decl.Name.Name == member {
					return token.FUNC, decl.Name.Pos(), nil
				}
			}
		}
	}

	return 0, token.NoPos, fmt.Errorf("couldn't find declaration of %s in %q", member, pkg)
}
Ejemplo n.º 10
0
func explort(ctx build.Context, pkg *build.Package, visited map[string]bool) {
	for _, packageName := range pkg.Imports {
		if !visited[packageName] {
			visited[packageName] = true
			if *verbose || strings.Contains(packageName, ".") {
				fmt.Printf("%s\n", packageName)
			}
			if ! (packageName == "C") {
				child, err := ctx.Import(packageName, pkg.Dir, build.AllowBinary)
				if err != nil {
					log.Fatalf("error on import: %s", err);
				}					
				explort(ctx, child, visited)
			}
		}
	}
}
Ejemplo n.º 11
0
func completePackageArg(ctx *build.Context, cwd string, src io.Reader, arg string) (completions []string) {
	switch {
	case arg == ".":
		completions = []string{"./", "../"}
	case arg == "..":
		completions = []string{"../"}
	case strings.HasPrefix(arg, "."):
		// Complete using relative directory.
		bpkg, err := ctx.Import(".", cwd, build.FindOnly)
		if err != nil {
			return nil
		}
		dir, name := path.Split(arg)
		fis, err := buildutil.ReadDir(ctx, buildutil.JoinPath(ctx, bpkg.Dir, dir))
		if err != nil {
			return nil
		}
		for _, fi := range fis {
			if !fi.IsDir() || strings.HasPrefix(fi.Name(), ".") {
				continue
			}
			if strings.HasPrefix(fi.Name(), name) {
				completions = append(completions, path.Join(dir, fi.Name())+"/")
			}
		}
	case strings.HasPrefix(arg, "/"):
		// Complete using full import path.
		completions = completePackageArgByPath(ctx, cwd, arg)
	default:
		// Complete with package names imported in current file.
		for n := range readImports(cwd, src) {
			if strings.HasPrefix(n, arg) {
				completions = append(completions, n)
			}
		}
	}
	if len(completions) == 0 {
		completions = []string{arg}
	}
	sort.Strings(completions)
	return completions
}
Ejemplo n.º 12
0
func importer(ctx *build.Context, srcDir string, vendor map[string]string) ast.Importer {
	return func(imports map[string]*ast.Object, importPath string) (*ast.Object, error) {
		pkg := imports[importPath]
		if pkg != nil {
			return pkg, nil
		}

		var name string
		bpkg, err := ctx.Import(importPath, srcDir, 0)
		if err != nil {
			name = guessPackageNameFromPath(importPath)
		} else {
			name = bpkg.Name
			vendor[importPath] = bpkg.ImportPath
		}

		pkg = ast.NewObj(ast.Pkg, name)
		pkg.Data = ast.NewScope(nil)
		imports[importPath] = pkg
		return pkg, nil
	}
}
Ejemplo n.º 13
0
// MakeGoBuildLoader returns an implementation of the SourceLoader
// function prototype that locates packages using the go/build
// libraries.  It may return nil upon gross misconfiguration
// (e.g. os.Getwd() failed).
//
// ctxt specifies the go/build.Context to use; if nil, the default
// Context is used.
//
func MakeGoBuildLoader(ctxt *build.Context) SourceLoader {
	srcDir, err := os.Getwd()
	if err != nil {
		return nil // serious misconfiguration
	}
	if ctxt == nil {
		ctxt = &build.Default
	}
	return func(fset *token.FileSet, path string) (files []*ast.File, err error) {
		// TODO(adonovan): fix: Do we need cwd? Shouldn't
		// ImportDir(path) / $GOROOT suffice?
		bp, err := ctxt.Import(path, srcDir, 0)
		if err != nil {
			return // import failed
		}
		files, err = ParseFiles(fset, bp.Dir, bp.GoFiles...)
		if err != nil {
			return nil, err
		}
		return
	}
}
Ejemplo n.º 14
0
// Build scans the specified Go workspace and builds the forward and
// reverse import dependency graphs for all its packages.
// It also returns a mapping from canonical import paths to errors for packages
// whose loading was not entirely successful.
// A package may appear in the graph and in the errors mapping.
// All package paths are canonical and may contain "/vendor/".
func Build(ctxt *build.Context) (forward, reverse Graph, errors map[string]error) {
	type importEdge struct {
		from, to string
	}
	type pathError struct {
		path string
		err  error
	}

	ch := make(chan interface{})

	go func() {
		sema := make(chan int, 20) // I/O concurrency limiting semaphore
		var wg sync.WaitGroup
		buildutil.ForEachPackage(ctxt, func(path string, err error) {
			if err != nil {
				ch <- pathError{path, err}
				return
			}

			wg.Add(1)
			go func() {
				defer wg.Done()

				sema <- 1
				bp, err := ctxt.Import(path, "", 0)
				<-sema

				if err != nil {
					if _, ok := err.(*build.NoGoError); ok {
						// empty directory is not an error
					} else {
						ch <- pathError{path, err}
					}
					// Even in error cases, Import usually returns a package.
				}

				// absolutize resolves an import path relative
				// to the current package bp.
				// The absolute form may contain "vendor".
				//
				// The vendoring feature slows down Build by 3×.
				// Here are timings from a 1400 package workspace:
				//    1100ms: current code (with vendor check)
				//     880ms: with a nonblocking cache around ctxt.IsDir
				//     840ms: nonblocking cache with duplicate suppression
				//     340ms: original code (no vendor check)
				// TODO(adonovan): optimize, somehow.
				memo := make(map[string]string)
				absolutize := func(path string) string {
					canon, ok := memo[path]
					if !ok {
						sema <- 1
						bp2, _ := ctxt.Import(path, bp.Dir, build.FindOnly)
						<-sema

						if bp2 != nil {
							canon = bp2.ImportPath
						} else {
							canon = path
						}
						memo[path] = canon
					}
					return canon
				}

				if bp != nil {
					for _, imp := range bp.Imports {
						ch <- importEdge{path, absolutize(imp)}
					}
					for _, imp := range bp.TestImports {
						ch <- importEdge{path, absolutize(imp)}
					}
					for _, imp := range bp.XTestImports {
						ch <- importEdge{path, absolutize(imp)}
					}
				}

			}()
		})
		wg.Wait()
		close(ch)
	}()

	forward = make(Graph)
	reverse = make(Graph)

	for e := range ch {
		switch e := e.(type) {
		case pathError:
			if errors == nil {
				errors = make(map[string]error)
			}
			errors[e.path] = e.err

		case importEdge:
			if e.to == "C" {
				continue // "C" is fake
			}
			forward.addEdge(e.from, e.to)
			reverse.addEdge(e.to, e.from)
		}
	}

	return forward, reverse, errors
}
Ejemplo n.º 15
0
// loadPackage returns details about the Go package named by the import
// path, interpreting local import paths relative to the srcDir directory.
func loadPackage(ctx *build.Context, importPath string, srcDir string, flags int) (*pkg, error) {
	bpkg, err := ctx.Import(importPath, srcDir, build.ImportComment)
	if _, ok := err.(*build.NoGoError); ok {
		return &pkg{Build: bpkg}, nil
	}
	if err != nil {
		return nil, err
	}

	pkg := &pkg{
		FSet:  token.NewFileSet(),
		Build: bpkg,
	}

	files := make(map[string]*ast.File)
	for _, name := range append(pkg.Build.GoFiles, pkg.Build.CgoFiles...) {
		file, err := pkg.parseFile(ctx, name)
		if err != nil {
			pkg.Errors = append(pkg.Errors, err)
			continue
		}
		files[name] = file
	}

	vendor := make(map[string]string)
	pkg.AST, _ = ast.NewPackage(pkg.FSet, files, importer(ctx, bpkg.Dir, vendor), nil)

	if flags&loadPackageFixVendor != 0 {
		for _, f := range pkg.AST.Files {
			for _, i := range f.Imports {
				if lit := i.Path; lit != nil {
					if s, err := strconv.Unquote(lit.Value); err != nil {
						if p, ok := vendor[s]; ok {
							lit.Value = strconv.Quote(p)
						}
					}
				}
			}
		}
	}

	if flags&loadPackageDoc != 0 {
		mode := godoc.Mode(0)
		if pkg.Build.ImportPath == "builtin" || flags&loadPackageUnexported != 0 {
			mode |= godoc.AllDecls
		}
		pkg.GoDoc = godoc.New(pkg.AST, pkg.Build.ImportPath, mode)
		if pkg.Build.ImportPath == "builtin" {
			for _, t := range pkg.GoDoc.Types {
				pkg.GoDoc.Funcs = append(pkg.GoDoc.Funcs, t.Funcs...)
				t.Funcs = nil
			}
			sort.Sort(byFuncName(pkg.GoDoc.Funcs))
		}
	}

	if flags&loadPackageExamples != 0 {
		for _, name := range append(pkg.Build.TestGoFiles, pkg.Build.XTestGoFiles...) {
			file, err := pkg.parseFile(ctx, name)
			if err != nil {
				pkg.Errors = append(pkg.Errors, err)
				continue
			}
			pkg.Examples = append(pkg.Examples, godoc.Examples(file)...)
		}
	}

	return pkg, nil
}
Ejemplo n.º 16
0
func getPackage(pkgpath string) (*gobuild.Package, error) {
	var ctx gobuild.Context = gobuild.Default
	ctx.GOARCH = GOARCH
	ctx.GOOS = GOOS
	ctx.BuildTags = append(ctx.BuildTags[:], "llgo")
	//ctx.Compiler = "llgo"

	// Attempt to find an overlay package path,
	// which we'll use in ReadDir below.
	overlayentries := make(map[string]bool)
	overlaypkgpath := llgoPkgPrefix + pkgpath
	overlaypkg, err := ctx.Import(overlaypkgpath, "", gobuild.FindOnly)
	if err != nil {
		overlaypkg = nil
	}

	// ReadDir is overridden to return a fake ".s"
	// file for each ".ll" file in the directory.
	ctx.ReadDir = func(dir string) (fi []os.FileInfo, err error) {
		fi, err = ioutil.ReadDir(dir)
		if err != nil {
			return nil, err
		}
		entries := make(map[string]os.FileInfo)
		for _, info := range fi {
			entries[info.Name()] = info
		}
		// Overlay all files in the overlay package dir.
		// If we find any .ll files, replace the suffix
		// with .s.
		if overlaypkg != nil {
			fi, err = ioutil.ReadDir(overlaypkg.Dir)
		}
		if err == nil {
			// Check for .ll files in the overlay dir if
			// we have one, else in the standard package dir.
			for _, info := range fi {
				name := info.Name()
				if strings.HasSuffix(name, ".ll") {
					name = name[:len(name)-3] + ".s"
					info = &renamedFileInfo{info, name}
				}
				overlayentries[name] = true
				entries[name] = info
			}
		}
		fi = make([]os.FileInfo, 0, len(entries))
		for _, info := range entries {
			fi = append(fi, info)
		}
		return fi, nil
	}

	// OpenFile is overridden to return the contents
	// of the ".ll" file found in ReadDir above. The
	// returned ReadCloser is wrapped to transform
	// LLVM IR comments to use "//", as expected by
	// go/build when looking for build tags.
	ctx.OpenFile = func(path string) (io.ReadCloser, error) {
		base := filepath.Base(path)
		overlay := overlayentries[base]
		if overlay {
			if overlaypkg != nil {
				path = filepath.Join(overlaypkg.Dir, base)
			}
			if strings.HasSuffix(path, ".s") {
				path := path[:len(path)-2] + ".ll"
				var r io.ReadCloser
				var err error
				r, err = os.Open(path)
				if err == nil {
					r = build.NewLLVMIRReader(r)
				}
				return r, err
			}
		}
		return os.Open(path)
	}

	pkg, err := ctx.Import(pkgpath, "", 0)
	if err != nil {
		return nil, err
	} else {
		if overlaypkg == nil {
			overlaypkg = pkg
		}
		for i, filename := range pkg.GoFiles {
			pkgdir := pkg.Dir
			if overlayentries[filename] {
				pkgdir = overlaypkg.Dir
			}
			pkg.GoFiles[i] = path.Join(pkgdir, filename)
		}
		for i, filename := range pkg.CFiles {
			pkgdir := pkg.Dir
			if overlayentries[filename] {
				pkgdir = overlaypkg.Dir
			}
			pkg.CFiles[i] = path.Join(pkgdir, filename)
		}
		for i, filename := range pkg.SFiles {
			pkgdir := pkg.Dir
			if overlayentries[filename] {
				pkgdir = overlaypkg.Dir
				filename = filename[:len(filename)-2] + ".ll"
			} else {
				err := fmt.Errorf("No matching .ll file for %q", filename)
				return nil, err
			}
			pkg.SFiles[i] = path.Join(pkgdir, filename)
		}
	}
	return pkg, nil
}
Ejemplo n.º 17
0
func (p *Package) parser(ctx build.Context) (*doc.Package, error) {
	bp, err := ctx.Import(p.pkg, "", 0)
	if err != nil {
		return nil, err
	}
	ctxName := contextName(&ctx)
	p.bps[ctxName] = bp
	var files []string
	files = append(files, bp.GoFiles...)
	files = append(files, bp.CgoFiles...)

	fs := make(map[string]*ast.File)
	var afiles []*ast.File
	for _, file := range files {
		fileName := filepath.Join(bp.Dir, file)
		f, ok := p.files[fileName]
		if !ok {
			f, err = parser.ParseFile(p.fset, fileName, nil, parser.ParseComments)
			if err != nil {
				return nil, err
			}
			p.files[fileName] = f
		}
		pkgName := f.Name.Name
		if pkgName == bp.Name {
			fs[fileName] = f
		}
		afiles = append(afiles, f)
	}

	apkg, err := ast.NewPackage(p.fset, fs, nil, nil)

	if err != nil {
		//fmt.Println(err)
	}

	dpkg := doc.New(apkg, bp.Name, doc.AllMethods|doc.AllDecls)

	p.dpkg[contextName(&ctx)] = dpkg

	updata := func(typ DocType, ctx string, key string, value interface{}) {
		if p.keys[typ] == nil {
			p.keys[typ] = make(map[string]map[string]interface{})
		}
		if p.keys[typ][key] == nil {
			p.keys[typ][key] = make(map[string]interface{})
		}
		p.keys[typ][key][ctx] = value
	}

	//types
	for _, v := range dpkg.Types {
		typ := parserTypeDecl(v.Decl)
		if typ == NilType {
			continue
		}
		updata(typ, ctxName, v.Name, v)
		//type func
		for _, f := range v.Funcs {
			updata(Factor, ctxName, f.Name, f)
		}
		//type var
		for _, f := range v.Vars {
			for _, name := range f.Names {
				updata(Var, ctxName, name, f)
			}
		}
		//type const
		for _, f := range v.Consts {
			for _, name := range f.Names {
				updata(Const, ctxName, name, f)
			}
		}
	}

	//funcs
	for _, v := range dpkg.Funcs {
		updata(Func, ctxName, v.Name, v)
	}
	//consts
	for _, v := range dpkg.Consts {
		for _, name := range v.Names {
			updata(Const, ctxName, name, v)
		}
	}
	//vars
	for _, v := range dpkg.Vars {
		for _, name := range v.Names {
			updata(Var, ctxName, name, v)
		}
	}
	return dpkg, nil
}
Ejemplo n.º 18
0
// parseFromFlag interprets the "-from" flag value as a renaming specification.
// See FromFlagUsage for valid formats.
func parseFromFlag(ctxt *build.Context, fromFlag string) (*spec, error) {
	var spec spec
	var main string // sans "::x" suffix
	switch parts := strings.Split(fromFlag, "::"); len(parts) {
	case 1:
		main = parts[0]
	case 2:
		main = parts[0]
		spec.searchFor = parts[1]
		if parts[1] == "" {
			// error
		}
	default:
		return nil, fmt.Errorf("-from %q: invalid identifier specification (see -help for formats)", fromFlag)
	}

	// main is one of:
	//  filename.go
	//  importpath
	//  importpath.member
	//  (importpath.type).fieldormethod

	if strings.HasSuffix(main, ".go") {
		// filename.go
		if spec.searchFor == "" {
			return nil, fmt.Errorf("-from: filename %q must have a ::name suffix", main)
		}
		spec.filename = main
		if !buildutil.FileExists(ctxt, spec.filename) {
			return nil, fmt.Errorf("no such file: %s", spec.filename)
		}

		bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename)
		if err != nil {
			return nil, err
		}
		spec.pkg = bp.ImportPath

	} else if a, b := splitAtLastDot(main); b == "" {
		// importpath e.g. "encoding/json"
		if spec.searchFor == "" {
			return nil, fmt.Errorf("-from %q: package import path %q must have a ::name suffix",
				main, a)
		}
		spec.pkg = a

	} else if strings.HasPrefix(a, "(") && strings.HasSuffix(a, ")") {
		// field/method of type e.g. (encoding/json.Decoder).Decode
		c, d := splitAtLastDot(a[1 : len(a)-1])
		if d == "" {
			return nil, fmt.Errorf("-from %q: not a package-level named type: %q", a)
		}
		spec.pkg = c        // e.g. "encoding/json"
		spec.pkgMember = d  // e.g. "Decoder"
		spec.typeMember = b // e.g. "Decode"
		spec.fromName = b

	} else {
		// package member e.g. "encoding/json.HTMLEscape"
		spec.pkg = a       // e.g. "encoding/json"
		spec.pkgMember = b // e.g.  "HTMLEscape"
		spec.fromName = b
	}

	if spec.searchFor != "" {
		spec.fromName = spec.searchFor
	}

	// Sanitize the package.
	// TODO(adonovan): test with relative packages.  May need loader changes.
	bp, err := ctxt.Import(spec.pkg, ".", build.FindOnly)
	if err != nil {
		return nil, fmt.Errorf("can't find package %q", spec.pkg)
	}
	spec.pkg = bp.ImportPath

	if !isValidIdentifier(spec.fromName) {
		return nil, fmt.Errorf("-from: invalid identifier %q", spec.fromName)
	}

	if Verbose {
		fmt.Fprintf(os.Stderr, "-from spec: %+v\n", spec)
	}

	return &spec, nil
}
Ejemplo n.º 19
0
// parseFromFlag interprets the "-from" flag value as a renaming specification.
// See Usage in rename.go for valid formats.
func parseFromFlag(ctxt *build.Context, fromFlag string) (*spec, error) {
	var spec spec
	var main string // sans "::x" suffix
	switch parts := strings.Split(fromFlag, "::"); len(parts) {
	case 1:
		main = parts[0]
	case 2:
		main = parts[0]
		spec.searchFor = parts[1]
		if parts[1] == "" {
			// error
		}
	default:
		return nil, fmt.Errorf("-from %q: invalid identifier specification (see -help for formats)", fromFlag)
	}

	if strings.HasSuffix(main, ".go") {
		// main is "filename.go"
		if spec.searchFor == "" {
			return nil, fmt.Errorf("-from: filename %q must have a ::name suffix", main)
		}
		spec.filename = main
		if !buildutil.FileExists(ctxt, spec.filename) {
			return nil, fmt.Errorf("no such file: %s", spec.filename)
		}

		bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename)
		if err != nil {
			return nil, err
		}
		spec.pkg = bp.ImportPath

	} else {
		// main is one of:
		//  "importpath"
		//  "importpath".member
		//  (*"importpath".type).fieldormethod           (parens and star optional)
		if err := parseObjectSpec(&spec, main); err != nil {
			return nil, err
		}
	}

	if spec.searchFor != "" {
		spec.fromName = spec.searchFor
	}

	cwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	// Sanitize the package.
	bp, err := ctxt.Import(spec.pkg, cwd, build.FindOnly)
	if err != nil {
		return nil, fmt.Errorf("can't find package %q", spec.pkg)
	}
	spec.pkg = bp.ImportPath

	if !isValidIdentifier(spec.fromName) {
		return nil, fmt.Errorf("-from: invalid identifier %q", spec.fromName)
	}

	if Verbose {
		log.Printf("-from spec: %+v", spec)
	}

	return &spec, nil
}
Ejemplo n.º 20
0
// Builds scans the specified Go workspace and builds the forward and
// reverse import dependency graphs for all its packages.
// It also returns a mapping from import paths to errors for packages
// that could not be loaded.
func Build(ctxt *build.Context) (forward, reverse Graph, errors map[string]error) {
	// The (sole) graph builder goroutine receives a stream of import
	// edges from the package loading goroutine.
	forward = make(Graph)
	reverse = make(Graph)
	edgec := make(chan [2]string)
	go func() {
		for edge := range edgec {
			if edge[1] == "C" {
				continue // "C" is fake
			}
			forward.addEdge(edge[0], edge[1])
			reverse.addEdge(edge[1], edge[0])
		}
	}()

	// The (sole) error goroutine receives a stream of ReadDir and
	// Import errors.
	type pathError struct {
		path string
		err  error
	}
	errorc := make(chan pathError)
	go func() {
		for e := range errorc {
			if errors == nil {
				errors = make(map[string]error)
			}
			errors[e.path] = e.err
		}
	}()

	var wg sync.WaitGroup
	buildutil.ForEachPackage(ctxt, func(path string, err error) {
		if err != nil {
			errorc <- pathError{path, err}
			return
		}
		wg.Add(1)
		// The import goroutines load the metadata for each package.
		go func(path string) {
			defer wg.Done()
			bp, err := ctxt.Import(path, "", 0)
			if _, ok := err.(*build.NoGoError); ok {
				return // empty directory is not an error
			}
			if err != nil {
				errorc <- pathError{path, err}
				return
			}
			for _, imp := range bp.Imports {
				edgec <- [2]string{path, imp}
			}
			for _, imp := range bp.TestImports {
				edgec <- [2]string{path, imp}
			}
			for _, imp := range bp.XTestImports {
				edgec <- [2]string{path, imp}
			}
		}(path)
	})
	wg.Wait()

	close(edgec)
	close(errorc)

	return forward, reverse, errors
}
Ejemplo n.º 21
0
// Builds scans the specified Go workspace and builds the forward and
// reverse import dependency graphs for all its packages.
// It also returns a mapping from import paths to errors for packages
// that could not be loaded.
func Build(ctxt *build.Context) (forward, reverse Graph, errors map[string]error) {
	type importEdge struct {
		from, to string
	}
	type pathError struct {
		path string
		err  error
	}

	ch := make(chan interface{})

	var wg sync.WaitGroup
	buildutil.ForEachPackage(ctxt, func(path string, err error) {
		wg.Add(1)
		go func() {
			defer wg.Done()
			if err != nil {
				ch <- pathError{path, err}
				return
			}
			bp, err := ctxt.Import(path, "", 0)
			if _, ok := err.(*build.NoGoError); ok {
				return // empty directory is not an error
			}
			if err != nil {
				ch <- pathError{path, err}
				return
			}
			for _, imp := range bp.Imports {
				ch <- importEdge{path, imp}
			}
			for _, imp := range bp.TestImports {
				ch <- importEdge{path, imp}
			}
			for _, imp := range bp.XTestImports {
				ch <- importEdge{path, imp}
			}
		}()
	})
	go func() {
		wg.Wait()
		close(ch)
	}()

	forward = make(Graph)
	reverse = make(Graph)

	for e := range ch {
		switch e := e.(type) {
		case pathError:
			if errors == nil {
				errors = make(map[string]error)
			}
			errors[e.path] = e.err

		case importEdge:
			if e.to == "C" {
				continue // "C" is fake
			}
			forward.addEdge(e.from, e.to)
			reverse.addEdge(e.to, e.from)
		}
	}

	return forward, reverse, errors
}
Ejemplo n.º 22
0
// imports returns a map of all import directories used by the app.
// The return value maps full directory names to original import names.
func imports(ctxt *build.Context, srcDir string) (map[string]string, error) {
	result := make(map[string]string)

	type importFrom struct {
		path, fromDir string
	}
	var imports []importFrom
	visited := make(map[importFrom]bool)

	pkg, err := ctxt.ImportDir(srcDir, 0)
	if err != nil {
		return nil, err
	}
	for _, v := range pkg.Imports {
		imports = append(imports, importFrom{
			path:    v,
			fromDir: srcDir,
		})
	}

	// Resolve all non-standard-library imports
	for len(imports) != 0 {
		i := imports[0]
		imports = imports[1:] // shift
		if i.path == "C" {
			// ignore cgo
			continue
		}
		if _, ok := visited[i]; ok {
			// already scanned
			continue
		}
		visited[i] = true

		abs, err := filepath.Abs(i.fromDir)
		if err != nil {
			return nil, fmt.Errorf("unable to get absolute directory of %q: %v", i.fromDir, err)
		}
		pkg, err := ctxt.Import(i.path, abs, 0)
		if err != nil {
			return nil, fmt.Errorf("unable to find import %s, imported from %q: %v", i.path, i.fromDir, err)
		}

		// TODO(cbro): handle packages that are vendored by multiple imports correctly.

		if pkg.Goroot {
			// ignore standard library imports
			continue
		}

		vlogf("Located %q (imported from %q) -> %q", i.path, i.fromDir, pkg.Dir)
		result[pkg.Dir] = i.path

		for _, v := range pkg.Imports {
			imports = append(imports, importFrom{
				path:    v,
				fromDir: pkg.Dir,
			})
		}
	}

	return result, nil
}