func toolNameConflicted(p1, p2 string) bool {
	if p1 == p2 {
		return false
	}
	c1, c2 := filepath.Base(p1), filepath.Base(p2)
	return c1 == c2
}
toolNameConflicted returns whether each tool in p1 and p2 conflicted.
Note that filepath.Base(p) is the tool name.

If p1 and p2 are the same value, it will be regarded as not conflicted.
package cmd
import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"github.com/ktr0731/dept/deptfile"
	"github.com/ktr0731/dept/filegen"
	"github.com/ktr0731/dept/gocmd"
	"github.com/ktr0731/dept/logger"
	"github.com/mitchellh/cli"
	"github.com/mitchellh/copystructure"
	"github.com/pkg/errors"
	"golang.org/x/sync/errgroup"
)
type outputFlagValue struct {
	Values []struct{ Out, Path string }
	f      *flag.FlagSet
}
func (v *outputFlagValue) Set(out string) error {
	path := v.f.Arg(0)
	if v.f.NArg() < 1 || (len(out) > 0 && out[0] == '-') || (len(path) > 0 && path[0] == '-') {
		return errShowHelp
	}
	v.Values = append(v.Values, struct{ Out, Path string }{out, path})
	return v.f.Parse(v.f.Args()[1:])
}
func (v *outputFlagValue) String() string {
	s := make([]string, 0, len(v.Values))
	for _, v := range v.Values {
		s = append(s, fmt.Sprintf("%s=%s", v.Path, v.Out))
	}
	return strings.Join(s, ", ")
}
type getFlagSet struct {
	*flag.FlagSet
	outputDir   string
	update      bool
	outputNames *outputFlagValue
}
func newGetFlagSet() *getFlagSet {
	gf := &getFlagSet{FlagSet: flag.NewFlagSet("get", flag.ContinueOnError)}
	gf.SetOutput(ioutil.Discard)
	gf.StringVar(&gf.outputDir, "d", "", "Output dir to store built Go tools")
	gf.BoolVar(&gf.update, "u", false, "Update the specified tool to the latest version")
	gf.outputNames = &outputFlagValue{Values: []struct{ Out, Path string }{}, f: gf.FlagSet}
	gf.Var(gf.outputNames, "o", "Output name (first arg is output name, second arg is path)")
	return gf
}
type getCommand struct {
	ui        cli.Ui
	gocmd     gocmd.Command
	workspace deptfile.Workspacer
	f *getFlagSet
}
func (c *getCommand) UI() cli.Ui {
	return c.ui
}
var getHelpTmpl = `Usage: dept get <path [path ...]>
get installs the passed Go tools to the specified directory.
If $GOBIN enabled, it will be used preferentially.
-u flag updates the passed Go tools. If there are no args,
updates all Go tools which is already installed.
%s
%s
Examples:
    $ dept get github.com/mitchellh/gox
    $ dept get github.com/mitchellh/gox@v0.3.0
    $ dept get github.com/mitchellh/gox@v0.1.0
    $ dept get -o it github.com/ktr0731/itunes-cli/itunes
    $ dept get -d bin github.com/mitchellh/gox
    $ GOBIN=$PWD/bin dept get github.com/mitchellh/gox
`
func (c *getCommand) Help() string {
	return fmt.Sprintf(
		getHelpTmpl,
		ExcludeFlagUsage(c.f.FlagSet, false, []string{"o"}),
		ExcludeFlagUsage(c.f.FlagSet, true, []string{"d", "u"}))
}
func (c *getCommand) Synopsis() string {
	return "Get a new tool as a dependency"
}
func (c *getCommand) Run(args []string) int {
	if err := c.f.Parse(args); err != nil {
		c.UI().Error(err.Error())
		return 1
	}
	args = c.f.Args()
	outputDir := c.f.outputDir
	if outputDir != "" {
		outputDir, _ = filepath.Abs(outputDir)
	}
	update := c.f.update
	return run(c, func(ctx context.Context) error {
		err := c.workspace.Do(func(projRoot string, df *deptfile.File) error {
			paths, err := c.initModPaths(ctx, c.f.outputNames.Values, args)
			if err != nil {
				return err
			}
			if len(paths) == 0 && !update {
				return errShowHelp
			}
			cleanup, err := generateGoFile(df, paths)
			if err != nil {
				return err
			}
			defer cleanup()
			if len(paths) == 0 && update {
				logger.Println("updating all tools to the latest version")
				if err := c.gocmd.Get(ctx, "-u", "-d"); err != nil {
					return errors.Wrap(err, "failed to update Go tools")
				}
				return nil
			}
			getArgs := make([]string, 0, 1+len(paths))
			getArgs = append(getArgs, "-d")
			for _, p := range paths {
				getArgs = append(getArgs, p.modPath())
			}
			logger.Println("getting all dependencies")
			if err := c.gocmd.Get(ctx, append(getArgs, ".")...); err != nil {
				return errors.Wrap(err, "failed to get Go tools dependencies")
			}
			eg, ctx := errgroup.WithContext(ctx)
			outputDir = resolveOutputDir(projRoot, outputDir)
			for _, path := range paths {
				path := path
				eg.Go(func() error {
					if update && path.Ver == "" {
						logger.Printf("updating %s to the latest version", path.Repo)
						if err := c.gocmd.Get(ctx, "-u", "-d", path.Repo); err != nil {
							return errors.Wrap(err, "failed to get Go tools dependencies")
						}
					}
					var binPath string
					if path.Out != "" {
						binPath = filepath.Join(outputDir, path.Out)
					} else {
						binPath = filepath.Join(outputDir, filepath.Base(path.Repo))
					}
					logger.Printf("building %s to %s", path.Repo, binPath)
					if err := c.gocmd.Build(ctx, "-o", binPath, path.Repo); err != nil {
						return errors.Wrapf(err, "failed to buld %s (bin path = %s)", path.Repo, binPath)
					}
					return nil
				})
			}
			if err := eg.Wait(); errors.Cause(err) == context.Canceled {
				return context.Canceled
			} else if err != nil {
				return errors.Wrap(err, "failed to build tools")
			}
			return nil
		})
		return err
	})
}
func (c *getCommand) initModPaths(ctx context.Context, argsWithFlag []struct{ Out, Path string }, args []string) ([]*path, error) {
	getPaths := make([]string, 0, len(argsWithFlag))
	for _, a := range argsWithFlag {
		if len(a.Path) > 0 && a.Path[0] == '-' {
			return nil, errors.Errorf("found '%s' after args. all flags must be put before args", a.Path)
		}
		getPaths = append(getPaths, a.Path)
	}
	if len(getPaths) > 0 {
		logger.Println("getting all dependencies passed as command-line args")
		if err := c.gocmd.Get(ctx, getPaths...); err != nil {
			return nil, errors.Wrap(err, "failed to get additional dependencies")
		}
	}
	found := map[string]interface{}{}
	paths := make([]*path, 0, len(argsWithFlag)+len(args))
	if len(argsWithFlag) == 0 && len(args) == 0 {
		return nil, nil
	}
	eg, ctx := errgroup.WithContext(ctx)
	appendPath := func(p, out string) error {
		repo, ver, err := normalizePath(p)
		if err != nil {
			return err
		}
		if _, ok := found[repo]; ok {
			return nil
		}
		path := &path{Val: p, Repo: repo, Ver: ver, Out: out}
		paths = append(paths, path)
		eg.Go(func() (err error) {
			path.ModRoot, err = getModuleRoot(ctx, c.gocmd, path.Repo)
			return
		})
		return nil
	}
	for _, a := range argsWithFlag {
		if err := appendPath(a.Path, a.Out); err != nil {
			return nil, err
		}
	}
	for _, path := range args {
		if err := appendPath(path, ""); err != nil {
			return nil, err
		}
	}
	return paths, eg.Wait()
}
func generateGoFile(df *deptfile.File, paths []*path) (func(), error) {
	importPaths := make([]string, 0, len(df.Require))
	for _, path := range paths {
		var targetReq *deptfile.Require
		for _, r := range df.Require {
			if r.Path == path.ModRoot {
				tmp, err := copystructure.Copy(r)
				if err != nil {
					return nil, errors.Wrap(err, "failed to deepcopy a Require")
				}
				targetReq = tmp.(*deptfile.Require)
			}
			var err error
			var i int
			forTools(r, func(importPath string) bool {
				importPaths = append(importPaths, importPath)
				if toolNameConflicted(importPath, path.Repo) {
					err = errors.Errorf("tool names conflicted: %s and %s. please rename tool name by -o option.", path.Repo, importPath)
					return false
				}
				if importPath == path.Repo && path.Out != "" {
					r.ToolPaths[i].Name = path.Out
				}
				i++
				return true
			})
			if err != nil {
				return nil, err
			}
		}
		importPaths = append(importPaths, path.Repo)
		df.Require = appendRequire(df.Require, targetReq, path)
	}
	f, err := os.Create("tools.go")
	if err != nil {
		return nil, errors.Wrap(err, "failed to create a temp file which contains required Go tools in the import statement")
	}
	defer f.Close()
	filegen.Generate(f, importPaths)
	return func() {
		if err := recover(); err != nil {
			os.Remove("tools.go")
			panic(err)
		}
		os.Remove("tools.go")
	}, nil
}
func getModuleRoot(ctx context.Context, gocmd gocmd.Command, path string) (string, error) {
	logger.Printf("get the module root of %s", path)
	res, err := gocmd.List(ctx, "-f", `{{ .Module.Path }}`, path)
	if err != nil {
		return "", errors.Wrapf(err, "failed to get the module root of %s", path)
	}
	b, err := ioutil.ReadAll(res)
	if err != nil {
		return "", errors.Wrap(err, "failed to convert io.Reader to string")
	}
	return strings.TrimSpace(string(b)), nil
}