github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/pkg/golang/build.go (about) 1 // Copyright 2015-2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package golang is an API to the Go compiler. 6 package golang 7 8 import ( 9 "encoding/json" 10 "fmt" 11 "go/build" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "strings" 16 ) 17 18 type Environ struct { 19 build.Context 20 } 21 22 // Default is the default build environment comprised of the default GOPATH, 23 // GOROOT, GOOS, GOARCH, and CGO_ENABLED values. 24 func Default() Environ { 25 return Environ{Context: build.Default} 26 } 27 28 // PackageByPath retrieves information about a package by its file system path. 29 // 30 // `path` is assumed to be the directory containing the package. 31 func (c Environ) PackageByPath(path string) (*build.Package, error) { 32 abs, err := filepath.Abs(path) 33 if err != nil { 34 return nil, err 35 } 36 return c.Context.ImportDir(abs, 0) 37 } 38 39 // Package retrieves information about a package by its Go import path. 40 func (c Environ) Package(importPath string) (*build.Package, error) { 41 return c.Context.Import(importPath, "", 0) 42 } 43 44 // ListPackage matches a subset of the JSON output of the `go list -json` 45 // command. 46 // 47 // See `go help list` for the full structure. 48 // 49 // This currently contains an incomplete list of dependencies. 50 type ListPackage struct { 51 Dir string 52 Deps []string 53 GoFiles []string 54 SFiles []string 55 HFiles []string 56 Goroot bool 57 Root string 58 ImportPath string 59 } 60 61 func (c Environ) goCmd(args ...string) *exec.Cmd { 62 cmd := exec.Command(filepath.Join(c.GOROOT, "bin", "go"), args...) 63 cmd.Env = append(os.Environ(), c.Env()...) 64 return cmd 65 } 66 67 // Version returns the Go version string that runtime.Version would return for 68 // the Go compiler in this environ. 69 func (c Environ) Version() (string, error) { 70 cmd := c.goCmd("version") 71 v, err := cmd.CombinedOutput() 72 if err != nil { 73 return "", err 74 } 75 s := strings.Fields(string(v)) 76 if len(s) < 3 { 77 return "", fmt.Errorf("unknown go version, tool returned weird output for 'go version': %v", string(v)) 78 } 79 return s[2], nil 80 } 81 82 // Deps lists all dependencies of the package given by `importPath`. 83 func (c Environ) Deps(importPath string) (*ListPackage, error) { 84 // The output of this is almost the same as build.Import, except for 85 // the dependencies. 86 cmd := c.goCmd("list", "-json", importPath) 87 out, err := cmd.CombinedOutput() 88 if err != nil { 89 return nil, err 90 } 91 92 var p ListPackage 93 if err := json.Unmarshal(out, &p); err != nil { 94 return nil, err 95 } 96 return &p, nil 97 } 98 99 func (c Environ) Env() []string { 100 var env []string 101 if c.GOARCH != "" { 102 env = append(env, fmt.Sprintf("GOARCH=%s", c.GOARCH)) 103 } 104 if c.GOOS != "" { 105 env = append(env, fmt.Sprintf("GOOS=%s", c.GOOS)) 106 } 107 if c.GOROOT != "" { 108 env = append(env, fmt.Sprintf("GOROOT=%s", c.GOROOT)) 109 } 110 if c.GOPATH != "" { 111 env = append(env, fmt.Sprintf("GOPATH=%s", c.GOPATH)) 112 } 113 var cgo int8 114 if c.CgoEnabled { 115 cgo = 1 116 } 117 env = append(env, fmt.Sprintf("CGO_ENABLED=%d", cgo)) 118 return env 119 } 120 121 func (c Environ) String() string { 122 return strings.Join(c.Env(), " ") 123 } 124 125 // Optional arguments to Environ.Build. 126 type BuildOpts struct { 127 // ExtraArgs to `go build`. 128 ExtraArgs []string 129 } 130 131 // Build compiles the package given by `importPath`, writing the build object 132 // to `binaryPath`. 133 func (c Environ) Build(importPath string, binaryPath string, opts BuildOpts) error { 134 p, err := c.Package(importPath) 135 if err != nil { 136 return err 137 } 138 139 return c.BuildDir(p.Dir, binaryPath, opts) 140 } 141 142 // BuildDir compiles the package in the directory `dirPath`, writing the build 143 // object to `binaryPath`. 144 func (c Environ) BuildDir(dirPath string, binaryPath string, opts BuildOpts) error { 145 args := []string{ 146 "build", 147 "-a", // Force rebuilding of packages. 148 "-o", binaryPath, 149 "-installsuffix", "uroot", 150 "-gcflags=all=-l", // Disable "function inlining" to get a smaller binary 151 "-ldflags", "-s -w", // Strip all symbols. 152 } 153 if len(c.BuildTags) > 0 { 154 args = append(args, []string{"-tags", strings.Join(c.BuildTags, " ")}...) 155 } 156 if opts.ExtraArgs != nil { 157 args = append(args, opts.ExtraArgs...) 158 } 159 // We always set the working directory, so this is always '.'. 160 args = append(args, ".") 161 162 cmd := c.goCmd(args...) 163 cmd.Dir = dirPath 164 165 if o, err := cmd.CombinedOutput(); err != nil { 166 return fmt.Errorf("error building go package in %q: %v, %v", dirPath, string(o), err) 167 } 168 return nil 169 }