github.com/rck/u-root@v0.0.0-20180106144920-7eb602e381bb/pkg/golang/build.go (about) 1 package golang 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "go/build" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strings" 11 ) 12 13 type Environ struct { 14 build.Context 15 } 16 17 // Default is the default build environment comprised of the default GOPATH, 18 // GOROOT, GOOS, GOARCH, and CGO_ENABLED values. 19 func Default() Environ { 20 return Environ{Context: build.Default} 21 } 22 23 // PackageByPath retrieves information about a package by its file system path. 24 // 25 // `path` is assumed to be the directory containing the package. 26 func (c Environ) PackageByPath(path string) (*build.Package, error) { 27 abs, err := filepath.Abs(path) 28 if err != nil { 29 return nil, err 30 } 31 return c.Context.ImportDir(abs, 0) 32 } 33 34 // Package retrieves information about a package by its Go import path. 35 func (c Environ) Package(importPath string) (*build.Package, error) { 36 return c.Context.Import(importPath, "", 0) 37 } 38 39 // ListPackage matches a subset of the JSON output of the `go list -json` 40 // command. 41 // 42 // See `go help list` for the full structure. 43 // 44 // This currently contains an incomplete list of dependencies. 45 type ListPackage struct { 46 Dir string 47 Deps []string 48 GoFiles []string 49 SFiles []string 50 HFiles []string 51 Goroot bool 52 Root string 53 ImportPath string 54 } 55 56 // Deps lists all dependencies of the package given by `importPath`. 57 func (c Environ) Deps(importPath string) (*ListPackage, error) { 58 // The output of this is almost the same as build.Import, except for 59 // the dependencies. 60 cmd := exec.Command("go", "list", "-json", importPath) 61 env := os.Environ() 62 env = append(env, c.Env()...) 63 cmd.Env = env 64 out, err := cmd.CombinedOutput() 65 if err != nil { 66 return nil, err 67 } 68 69 var p ListPackage 70 if err := json.Unmarshal(out, &p); err != nil { 71 return nil, err 72 } 73 return &p, nil 74 } 75 76 func (c Environ) Env() []string { 77 var env []string 78 if c.GOARCH != "" { 79 env = append(env, fmt.Sprintf("GOARCH=%s", c.GOARCH)) 80 } 81 if c.GOOS != "" { 82 env = append(env, fmt.Sprintf("GOOS=%s", c.GOOS)) 83 } 84 if c.GOROOT != "" { 85 env = append(env, fmt.Sprintf("GOROOT=%s", c.GOROOT)) 86 } 87 if c.GOPATH != "" { 88 env = append(env, fmt.Sprintf("GOPATH=%s", c.GOPATH)) 89 } 90 var cgo int8 91 if c.CgoEnabled { 92 cgo = 1 93 } 94 env = append(env, fmt.Sprintf("CGO_ENABLED=%d", cgo)) 95 return env 96 } 97 98 func (c Environ) String() string { 99 return strings.Join(c.Env(), " ") 100 } 101 102 // Optional arguments to Environ.Build. 103 type BuildOpts struct { 104 // ExtraArgs to `go build`. 105 ExtraArgs []string 106 } 107 108 // Build compiles the package given by `importPath`, writing the build object 109 // to `binaryPath`. 110 func (c Environ) Build(importPath string, binaryPath string, opts BuildOpts) error { 111 p, err := c.Package(importPath) 112 if err != nil { 113 return err 114 } 115 116 args := []string{ 117 "build", 118 "-a", // Force rebuilding of packages. 119 "-o", binaryPath, 120 "-installsuffix", "uroot", 121 "-ldflags", "-s -w", // Strip all symbols. 122 } 123 if opts.ExtraArgs != nil { 124 args = append(args, opts.ExtraArgs...) 125 } 126 // We always set the working directory, so this is always '.'. 127 args = append(args, ".") 128 129 cmd := exec.Command("go", args...) 130 cmd.Dir = p.Dir 131 cmd.Env = append(os.Environ(), c.Env()...) 132 133 if o, err := cmd.CombinedOutput(); err != nil { 134 return fmt.Errorf("error building go package %v: %v, %v", importPath, string(o), err) 135 } 136 return nil 137 }