github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/buildtools/gocmd/go.go (about) 1 // Package gocmd provides functions for working with the Go tool. This package 2 // is not named "go" because that name collides with a reserved keyword. 3 package gocmd 4 5 import ( 6 "encoding/json" 7 "strings" 8 9 "github.com/pkg/errors" 10 11 "github.com/fossas/fossa-cli/exec" 12 ) 13 14 // Package represents a single Go package. 15 type Package struct { 16 Name string // Vendored import name (what would go into an `import` statement). 17 ImportPath string // Fully qualified import name (including `vendor` folders). 18 Dir string // Absolute location on filesystem. 19 IsInternal bool // Whether the package is an internal package. 20 IsStdLib bool // Whether the package is a part of the standard library. 21 Imports []string // Direct dependencies. 22 Deps []string // Transitive dependencies. 23 Error interface{} // A package loading error, if applicable. 24 } 25 26 // Name computes the vendored import path of a package given its fully qualified 27 // import name. 28 func Name(importPath string) string { 29 sections := strings.Split(importPath, "vendor") 30 return sections[len(sections)-1] 31 } 32 33 // Go contains configuration information for the Go tool. 34 type Go struct { 35 Cmd string 36 Dir string 37 } 38 39 // GoListOutput is a subset of the output of `go list`. See `go help list` for 40 // details. 41 type GoListOutput struct { 42 Name string 43 ImportPath string 44 Dir string 45 Standard bool 46 Imports []string 47 Deps []string 48 Error *GoListPackageError 49 DepsErrors []*GoListPackageError 50 } 51 52 // GoListPackageError is the PackageError struct defined in `go help list`. 53 type GoListPackageError struct { 54 ImportStack []string 55 Pos string 56 Err string 57 } 58 59 // ListOne runs List for a single package. 60 func (g *Go) ListOne(pkg string, flags []string) (Package, error) { 61 pkgs, err := g.List([]string{pkg}, flags) 62 if err != nil { 63 return Package{}, err 64 } 65 if len(pkgs) != 1 { 66 return Package{}, errors.New("go build target specifies multiple packages") 67 } 68 return pkgs[0], nil 69 } 70 71 // List runs `go list` to return information about packages. 72 func (g *Go) List(pkgs, flags []string) ([]Package, error) { 73 // Run `go list -json $PKG` and unmarshal output. 74 var output []GoListOutput 75 argv := append(flags, pkgs...) 76 stdout, stderr, err := exec.Run(exec.Cmd{ 77 Name: g.Cmd, 78 Argv: append([]string{"list", "-json"}, argv...), 79 Dir: g.Dir, 80 }) 81 if err != nil && stdout == "" { 82 if strings.Contains(stderr, "build constraints exclude all Go files") { 83 // TODO: add better documentation around this error, and rename it to be 84 // more useful. 85 return nil, errors.New("bad OS/architecture target") 86 } 87 return nil, errors.Errorf("could not run go list: %s (%s)", strings.TrimSpace(stderr), err) 88 } 89 // The output for each package is valid JSON, but the output overall is not 90 // valid JSON until we massage it a bit. 91 err = json.Unmarshal([]byte("["+strings.Replace(stdout, "}\n{", "},{", -1)+"]"), &output) 92 if err != nil { 93 return nil, errors.Wrap(err, "could not unmarshal go list output") 94 } 95 96 // Parse output into Packages. 97 var ret []Package 98 for _, pkg := range output { 99 p := Package{ 100 Name: pkg.Name, 101 ImportPath: pkg.ImportPath, 102 Dir: pkg.Dir, 103 IsInternal: strings.Contains(pkg.ImportPath, "internal"), 104 IsStdLib: pkg.Standard, 105 Imports: pkg.Imports, 106 Deps: pkg.Deps, 107 } 108 if pkg.DepsErrors != nil { 109 p.Error = pkg.DepsErrors 110 } 111 if pkg.Error != nil { 112 p.Error = pkg.Error 113 } 114 ret = append(ret, p) 115 } 116 return ret, nil 117 } 118 119 // Build runs `go build` for packages. 120 func (g *Go) Build(pkgs []string) error { 121 _, _, err := exec.Run(exec.Cmd{ 122 Name: g.Cmd, 123 Argv: append([]string{"build"}, pkgs...), 124 }) 125 return err 126 } 127 128 // Clean runs `go clean` for packages. 129 func (g *Go) Clean(pkgs []string) error { 130 _, _, err := exec.Run(exec.Cmd{ 131 Name: g.Cmd, 132 Argv: append([]string{"clean"}, pkgs...), 133 }) 134 return err 135 }