All Files / internal/utils / pkg.go

0% Statements 0/28
0% Functions 0/1
0% Lines 0/43
package utils

import (
        "bytes"
        "encoding/json"
        "fmt"
        "io"
        "os/exec"
        "path"
        "path/filepath"
        "runtime"
        "strings"

        "golang.org/x/tools/cover"
)

// Pkg describes a single package, compatible with the JSON output from 'go list'; see 'go help list'.
type Pkg struct {
        ImportPath string
        Dir        string
        Error      *struct {
                Err string
        }
}

func FindPkgs(profiles []*cover.Profile) (map[string]*Pkg, error) {
        // Run go list to find the location of every package we care about.
        pkgs := make(map[string]*Pkg)
        var list []string
        for _, profile := range profiles {
                if strings.HasPrefix(profile.FileName, ".") || filepath.IsAbs(profile.FileName) {
                        // Relative or absolute path.
                        continue
                }
                pkg := path.Dir(profile.FileName)
                if _, ok := pkgs[pkg]; !ok {
                        pkgs[pkg] = nil
                        list = append(list, pkg)
                }
        }

        if len(list) == 0 {
                return pkgs, nil
        }

        // Note: usually run as "go tool cover" in which case $GOROOT is set,
        // in which case runtime.GOROOT() does exactly what we want.
        goTool := filepath.Join(runtime.GOROOT(), "bin/go")
        cmd := exec.Command(goTool, append([]string{"list", "-e", "-json"}, list...)...)
        var stderr bytes.Buffer
        cmd.Stderr = &stderr
        stdout, err := cmd.Output()
        if err != nil {
                return nil, fmt.Errorf("cannot run go list: %w\n%s", err, stderr.Bytes())
        }
        dec := json.NewDecoder(bytes.NewReader(stdout))
        for {
                var pkg Pkg
                decodeErr := dec.Decode(&pkg)
                if decodeErr == io.EOF {
                        break
                }
                if decodeErr != nil {
                        return nil, fmt.Errorf("decoding go list json: %w", decodeErr)
                }
                pkgs[pkg.ImportPath] = &pkg
        }
        return pkgs, nil
}