Beispiel #1
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, "/")
}
Beispiel #2
0
// 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
}
Beispiel #3
0
// 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)
}
Beispiel #4
0
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
}
Beispiel #5
0
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
}
Beispiel #6
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
}
Beispiel #7
0
// 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
}
Beispiel #8
0
// 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
}