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, "/") }
// parseFiles parses the Go source files within directory dir and // returns the ASTs of the ones that could be at least partially parsed, // along with a list of I/O and parse errors encountered. // // I/O is done via ctxt, which may specify a virtual file system. // displayPath is used to transform the filenames attached to the ASTs. // func parseFiles(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, files []string, mode parser.Mode) ([]*ast.File, []error) { if displayPath == nil { displayPath = func(path string) string { return path } } var wg sync.WaitGroup n := len(files) parsed := make([]*ast.File, n) errors := make([]error, n) for i, file := range files { if !buildutil.IsAbsPath(ctxt, file) { file = buildutil.JoinPath(ctxt, dir, file) } wg.Add(1) go func(i int, file string) { ioLimit <- true // wait defer func() { wg.Done() <-ioLimit // signal }() var rd io.ReadCloser var err error if ctxt.OpenFile != nil { rd, err = ctxt.OpenFile(file) } else { rd, err = os.Open(file) } if err != nil { errors[i] = err // open failed return } // ParseFile may return both an AST and an error. parsed[i], errors[i] = parser.ParseFile(fset, displayPath(file), rd, mode) rd.Close() }(i, file) } wg.Wait() // Eliminate nils, preserving order. var o int for _, f := range parsed { if f != nil { parsed[o] = f o++ } } parsed = parsed[:o] o = 0 for _, err := range errors { if err != nil { errors[o] = err o++ } } errors = errors[:o] return parsed, errors }
// 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 addCompletions(completions []string, ctx *build.Context, root, dir, name string) []string { fis, err := buildutil.ReadDir(ctx, buildutil.JoinPath(ctx, root, dir)) if err != nil { return completions } 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())+"/") } } return completions }
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 }
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 }
// Move, given a package path and a destination package path, will try // to move the given package to the new path. The Move function will // first check for any conflicts preventing the move, such as a // package already existing at the destination package path. If the // move can proceed, it builds an import graph to find all imports of // the packages whose paths need to be renamed. This includes uses of // the subpackages of the package to be moved as those packages will // also need to be moved. It then renames all imports to point to the // new paths, and then moves the packages to their new paths. func Move(ctxt *build.Context, from, to, moveTmpl string) error { srcDir, err := srcDir(ctxt, from) if err != nil { return err } // This should be the only place in the program that constructs // file paths. // TODO(matloob): test on Microsoft Windows. fromDir := buildutil.JoinPath(ctxt, srcDir, filepath.FromSlash(from)) toDir := buildutil.JoinPath(ctxt, srcDir, filepath.FromSlash(to)) toParent := filepath.Dir(toDir) if !buildutil.IsDir(ctxt, toParent) { return fmt.Errorf("parent directory does not exist for path %s", toDir) } // Build the import graph and figure out which packages to update. fwd, rev, errors := importgraph.Build(ctxt) if len(errors) > 0 { fmt.Fprintf(os.Stderr, "While scanning Go workspace:\n") for path, err := range errors { fmt.Fprintf(os.Stderr, "Package %q: %s.\n", path, err) } return fmt.Errorf("failed to construct import graph") } // Determine the affected packages---the set of packages whose import // statements need updating. affectedPackages := map[string]bool{from: true} destinations := map[string]string{} // maps old dir to new dir for pkg := range subpackages(ctxt, srcDir, from) { for r := range rev[pkg] { affectedPackages[r] = true } destinations[pkg] = strings.Replace(pkg, // Ensure directories have a trailing "/". filepath.Join(from, ""), filepath.Join(to, ""), 1) } // Load all the affected packages. iprog, err := loadProgram(ctxt, affectedPackages) if err != nil { return err } // Prepare the move command, if one was supplied. var cmd string if moveTmpl != "" { if cmd, err = moveCmd(moveTmpl, fromDir, toDir); err != nil { return err } } m := mover{ ctxt: ctxt, fwd: fwd, rev: rev, iprog: iprog, from: from, to: to, fromDir: fromDir, toDir: toDir, affectedPackages: affectedPackages, destinations: destinations, cmd: cmd, } if err := m.checkValid(); err != nil { return err } m.move() return nil }
// XXX should the return value be a map from dir to files? (currently assumed importPath to files) func listDirFiles(dir *DirSpec, recurse bool) (map[string][]string, error) { ctx, err := dir.buildContext() if err != nil { return nil, err } packages := map[string][]string{} var mode build.ImportMode p, err := ctx.ImportDir(dir.Path, mode) if err != nil { if _, ok := err.(*build.NoGoError); ok { // nop } else { return nil, fmt.Errorf("while loading %s: %s", dir, err) } } else { importPath := p.ImportPath if importPath == "." { importPath = p.Dir } if dir.pkgOverride != "" { importPath = dir.pkgOverride } // XXX something's wrong if packages[importPath] exists already packages[importPath] = make([]string, len(p.GoFiles)) for i, file := range p.GoFiles { packages[importPath][i] = buildutil.JoinPath(ctx, dir.Path, file) } } if recurse == false { return packages, nil } entries, err := dir.ReadDir() if err != nil { return nil, err } for _, e := range entries { if e.IsDir() == false { continue } if name := e.Name(); name[0] == '.' || name[0] == '_' { continue } // copy subdir := *dir subdir.Path = buildutil.JoinPath(ctx, dir.Path, e.Name()) pkgs, err := listDirFiles(&subdir, recurse) if err != nil { return nil, err } for path, files := range pkgs { packages[path] = files } } return packages, nil }