// 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) }
// ForEachPackage calls the found function with the package path of // each Go package it finds in any source directory of the specified // build context (e.g. $GOROOT or an element of $GOPATH). // All package paths are canonical, and thus may contain "/vendor/". // // If the package directory exists but could not be read, the second // argument to the found function provides the error. // // All I/O is done via the build.Context file system interface, // which must be concurrency-safe. // func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) { // We use a counting semaphore to limit // the number of parallel calls to ReadDir. sema := make(chan bool, 20) ch := make(chan item) var wg sync.WaitGroup for _, root := range ctxt.SrcDirs() { root := root wg.Add(1) go func() { allPackages(ctxt, sema, root, ch) wg.Done() }() } go func() { wg.Wait() close(ch) }() // All calls to found occur in the caller's goroutine. for i := range ch { found(i.importPath, i.err) } }
// guessImportPath finds the package containing filename, and returns // its source directory (an element of $GOPATH) and its import path // relative to it. // // TODO(adonovan): what about _test.go files that are not part of the // package? // func guessImportPath(filename string, buildContext *build.Context) (srcdir, importPath string, err error) { absFile, err := filepath.Abs(filename) if err != nil { err = fmt.Errorf("can't form absolute path of %s", filename) return } absFileDir := segments(filepath.Dir(absFile)) // Find the innermost directory in $GOPATH that encloses filename. minD := 1024 for _, gopathDir := range buildContext.SrcDirs() { absDir, err := filepath.Abs(gopathDir) if err != nil { continue // e.g. non-existent dir on $GOPATH } d := prefixLen(segments(absDir), absFileDir) // If there are multiple matches, // prefer the innermost enclosing directory // (smallest d). if d >= 0 && d < minD { minD = d srcdir = gopathDir importPath = strings.Join(absFileDir[len(absFileDir)-minD:], string(os.PathSeparator)) } } if srcdir == "" { err = fmt.Errorf("can't find package for file %s", filename) } return }
// srcDir returns the absolute path of the srcdir containing pkg. func srcDir(ctxt *build.Context, pkg string) (string, error) { for _, srcDir := range ctxt.SrcDirs() { path := buildutil.JoinPath(ctxt, srcDir, pkg) if buildutil.IsDir(ctxt, path) { return srcDir, nil } } return "", fmt.Errorf("src dir not found for package: %s", pkg) }
func ImportPaths(srcDir string, bctx *build.Context, pathFilter margo.PathFilterFunc) map[string]string { rootDirs := bctx.SrcDirs() importDir := func(dir string) *build.Package { p := quickImportDir(bctx, rootDirs, dir) if p != nil && p.Name != "" && p.ImportPath != "" { return p } return nil } srcImportPath := quickImportPath(srcDir) var pkgs []*build.Package for _, dir := range rootDirs { pkgs = append(pkgs, importablePackages(dir, importDir, pathFilter)...) } res := make(map[string]string, len(pkgs)) res["unsafe"] = "" // this package doesn't exist on-disk const vdir = "/vendor/" var vendored []*build.Package for _, p := range pkgs { switch { case p.Name == "main": // it's rarely useful to import `package main` case p.ImportPath == "builtin": // this package exists for documentation only case strings.HasPrefix(p.ImportPath, vdir[1:]) || strings.Contains(p.ImportPath, vdir): // fill these in after everything else so we can tag them vendored = append(vendored, p) default: res[p.ImportPath] = importsName(p) } } if srcImportPath != "" { sfx := srcImportPath + "/" for _, p := range vendored { name := importsName(p) + " [vendored]" ipath := p.ImportPath vpos := strings.LastIndex(ipath, vdir) switch { case vpos > 0: pfx := ipath[:vpos+1] if strings.HasPrefix(sfx, pfx) { ipath := ipath[vpos+len(vdir):] res[ipath] = name } case strings.HasPrefix(ipath, vdir[1:]): ipath := ipath[len(vdir)-1:] res[ipath] = name } } } return res }
func getImportPath(ctxt *build.Context, pathOrFilename string) (string, error) { dirSlash := filepath.ToSlash(pathOrFilename) // We assume that no source root (GOPATH[i] or GOROOT) contains any other. for _, srcdir := range ctxt.SrcDirs() { srcdirSlash := filepath.ToSlash(srcdir) + "/" if strings.HasPrefix(dirSlash, srcdirSlash) { importPath := dirSlash[len(srcdirSlash):len(dirSlash)] return importPath, nil } } return "", errNotGoSourcePath }
// ForEachPackage calls the found function with the import path of // each Go package it finds in any source directory of the specified // build context (e.g. $GOROOT or an element of $GOPATH). // // If the package directory exists but could not be read, the second // argument to the found function provides the error. // // The found function and the build.Context virtual file system // accessors must be concurrency safe. // func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) { // We use a counting semaphore to limit // the number of parallel calls to ReadDir. sema := make(chan bool, 20) var wg sync.WaitGroup for _, root := range ctxt.SrcDirs() { root := root wg.Add(1) go func() { allPackages(ctxt, sema, root, found) wg.Done() }() } wg.Wait() }
// FindAll returns a list of all packages in all of the GOPATH trees // in the given build context. If prefix is non-empty, only packages // whose import paths begin with prefix are returned. func FindAll(prefix string, buildContext build.Context, mode FindMode) (pkgs []*build.Package, err error) { have := map[string]bool{ "builtin": true, // ignore pseudo-package that exists only for documentation } if !buildContext.CgoEnabled { have["runtime/cgo"] = true // ignore during walk } // TODO(sqs): find cmd packages as well var gorootSrcPkg = filepath.Join(buildContext.GOROOT, "src/pkg") for _, src := range buildContext.SrcDirs() { if src == gorootSrcPkg && mode&IncludeStdlib == 0 { continue // skip stdlib } src = filepath.Clean(src) + string(filepath.Separator) start := filepath.Join(src, prefix) filepath.Walk(start, func(path string, fi os.FileInfo, err error) error { if err != nil || !fi.IsDir() || path == src { return nil } // Avoid .foo, _foo, and testdata directory trees. _, elem := filepath.Split(path) if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { return filepath.SkipDir } name := filepath.ToSlash(path[len(start):]) if have[name] { return nil } have[name] = true pkg, err := buildContext.ImportDir(path, 0) if err != nil && strings.Contains(err.Error(), "no Go source files") { return nil } pkgs = append(pkgs, pkg) return nil }) } return pkgs, nil }
func completePackageArgByPath(ctx *build.Context, cwd, arg string) []string { var completions []string dir, name := path.Split(arg[1:]) for _, root := range ctx.SrcDirs() { if sub, ok := hasSubDir(ctx, root, cwd); ok { for { completions = addCompletions(completions, ctx, buildutil.JoinPath(ctx, root, sub, "vendor"), dir, name) i := strings.LastIndex(sub, "/") if i < 0 { break } sub = sub[:i] } } completions = addCompletions(completions, ctx, root, dir, name) } return completions }
// ForEachPackage calls the found function with the package path of // each Go package it finds in any source directory of the specified // build context (e.g. $GOROOT or an element of $GOPATH). // All package paths are canonical, and thus may contain "/vendor/". // // If the package directory exists but could not be read, the second // argument to the found function provides the error. // // All I/O is done via the build.Context file system interface, // which must be concurrency-safe. // func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) { ch := make(chan item) var wg sync.WaitGroup for _, root := range ctxt.SrcDirs() { root := root wg.Add(1) go func() { allPackages(ctxt, root, ch) wg.Done() }() } go func() { wg.Wait() close(ch) }() // All calls to found occur in the caller's goroutine. for i := range ch { found(i.importPath, i.err) } }
// guessImportPath finds the package containing filename, and returns // its source directory (an element of $GOPATH) and its import path // relative to it. // // TODO(adonovan): what about _test.go files that are not part of the // package? // func guessImportPath(filename string, buildContext *build.Context) (srcdir, importPath string, err error) { absFile, err := filepath.Abs(filename) if err != nil { err = fmt.Errorf("can't form absolute path of %s", filename) return } absFileDir := segments(filepath.Dir(absFile)) // Find the innermost directory in $GOPATH that encloses filename. minD := 1024 for _, gopathDir := range buildContext.SrcDirs() { absDir, err := filepath.Abs(gopathDir) if err != nil { continue // e.g. non-existent dir on $GOPATH } d := prefixLen(segments(absDir), absFileDir) // If there are multiple matches, // prefer the innermost enclosing directory // (smallest d). if d >= 0 && d < minD { minD = d srcdir = gopathDir importPath = strings.Join(absFileDir[len(absFileDir)-minD:], string(os.PathSeparator)) } } if srcdir == "" { return "", "", fmt.Errorf("directory %s is not beneath any of these GOROOT/GOPATH directories: %s", filepath.Dir(absFile), strings.Join(buildContext.SrcDirs(), ", ")) } if importPath == "" { // This happens for e.g. $GOPATH/src/a.go, but // "" is not a valid path for (*go/build).Import. return "", "", fmt.Errorf("cannot load package in root of source directory %s", srcdir) } return srcdir, importPath, nil }