diff --git a/internal/run.go b/internal/run.go index 2c127fb0..79b4f049 100644 --- a/internal/run.go +++ b/internal/run.go @@ -57,9 +57,9 @@ func OutputDebug(cmd string, args ...string) (string, error) { return strings.TrimSpace(buf.String()), nil } -// splitEnv takes the results from os.Environ() (a []string of foo=bar values) +// SplitEnv takes the results from os.Environ() (a []string of foo=bar values) // and makes a map[string]string out of it. -func splitEnv(env []string) (map[string]string, error) { +func SplitEnv(env []string) (map[string]string, error) { out := map[string]string{} for _, s := range env { @@ -85,7 +85,7 @@ func joinEnv(env map[string]string) []string { // EnvWithCurrentGOOS returns a copy of os.Environ with the GOOS and GOARCH set // to runtime.GOOS and runtime.GOARCH. func EnvWithCurrentGOOS() ([]string, error) { - vals, err := splitEnv(os.Environ()) + vals, err := SplitEnv(os.Environ()) if err != nil { return nil, err } @@ -97,7 +97,7 @@ func EnvWithCurrentGOOS() ([]string, error) { // EnvWithGOOS retuns the os.Environ() values with GOOS and/or GOARCH either set // to their runtime value, or the given value if non-empty. func EnvWithGOOS(goos, goarch string) ([]string, error) { - env, err := splitEnv(os.Environ()) + env, err := SplitEnv(os.Environ()) if err != nil { return nil, err } diff --git a/mage/main.go b/mage/main.go index a41e7435..65964228 100644 --- a/mage/main.go +++ b/mage/main.go @@ -6,6 +6,7 @@ import ( "errors" "flag" "fmt" + "go/build" "io" "io/ioutil" "log" @@ -468,35 +469,53 @@ type mainfileTemplateData struct { BinaryName string } -func listGoFiles(magePath, goCmd, tags string, env []string) ([]string, error) { - args := []string{"list"} - if tags != "" { - args = append(args, fmt.Sprintf("-tags=%s", tags)) - } - args = append(args, "-e", "-f", `{{join .GoFiles "||"}}`) - cmd := exec.Command(goCmd, args...) - cmd.Env = env - buf := &bytes.Buffer{} - cmd.Stderr = buf - cmd.Dir = magePath - b, err := cmd.Output() +// listGoFiles returns a list of all .go files in a given directory, +// matching the provided tag +func listGoFiles(magePath, goCmd, tag string, envStr []string) ([]string, error) { + origMagePath := magePath + if !filepath.IsAbs(magePath) { + cwd, err := os.Getwd() + if err != nil { + return nil, fmt.Errorf("can't get current working directory: %v", err) + } + magePath = filepath.Join(cwd, magePath) + } + + env, err := internal.SplitEnv(envStr) if err != nil { - stderr := buf.String() - // if the error is "cannot find module", that can mean that there's no - // non-mage files, which is fine, so ignore it. - if !strings.Contains(stderr, "cannot find module for path") { - if tags == "" { - return nil, fmt.Errorf("failed to list un-tagged gofiles: %v: %s", err, stderr) - } - return nil, fmt.Errorf("failed to list gofiles tagged with %q: %v: %s", tags, err, stderr) + return nil, fmt.Errorf("error parsing environment variables: %v", err) + } + + bctx := build.Default + bctx.BuildTags = []string{tag} + + if _, ok := env["GOOS"]; ok { + bctx.GOOS = env["GOOS"] + } + + if _, ok := env["GOARCH"]; ok { + bctx.GOARCH = env["GOARCH"] + } + + pkg, err := bctx.Import(".", magePath, 0) + if err != nil { + if _, ok := err.(*build.NoGoError); ok { + return []string{}, nil + } + + // Allow multiple packages in the same directory + if _, ok := err.(*build.MultiplePackageError); !ok { + return nil, fmt.Errorf("failed to parse go source files: %v", err) } } - out := strings.TrimSpace(string(b)) - list := strings.Split(out, "||") - for i := range list { - list[i] = filepath.Join(magePath, list[i]) + + goFiles := make([]string, len(pkg.GoFiles)) + for i := range pkg.GoFiles { + goFiles[i] = filepath.Join(origMagePath, pkg.GoFiles[i]) } - return list, nil + + debug.Printf("found %d go files with build tag %s (files: %v)", len(goFiles), tag, goFiles) + return goFiles, nil } // Magefiles returns the list of magefiles in dir. @@ -549,6 +568,7 @@ func Magefiles(magePath, goos, goarch, goCmd string, stderr io.Writer, isMagefil files = append(files, f) } } + return files, nil }