forked from u-root/u-root
/
build.go
140 lines (124 loc) · 3.14 KB
/
build.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package golang
import (
"encoding/json"
"fmt"
"go/build"
"os"
"os/exec"
"path/filepath"
"strings"
)
type Environ struct {
build.Context
}
func Default() Environ {
return Environ{Context: build.Default}
}
// FindPackageByPath gives the full Go package name for the package in `path`.
//
// This currently assumes that packages are named after the directory they are
// in.
func (c Environ) FindPackageByPath(path string) (string, error) {
abs, err := filepath.Abs(path)
if err != nil {
return "", err
}
p, err := c.Context.ImportDir(abs, 0)
if err != nil {
return "", fmt.Errorf("failed to find package in %q: %v", path, err)
}
return p.ImportPath, nil
}
// FindPackageDir returns the full path to `pkg` according to the context's Gopaths.
func (c Environ) FindPackageDir(pkg string) (string, error) {
p, err := c.Context.Import(pkg, "", 0)
if err != nil {
return "", fmt.Errorf("failed to find package %q in gopath %q", pkg, c.Context.GOPATH)
}
return p.Dir, nil
}
func (c Environ) ListPackage(pkg string) (*build.Package, error) {
return c.Context.Import(pkg, "", 0)
}
type ListPackage struct {
Dir string
Deps []string
GoFiles []string
SFiles []string
HFiles []string
Goroot bool
Root string
ImportPath string
}
func (c Environ) ListDeps(pkg string) (*ListPackage, error) {
// The output of this is almost the same as build.Import, except for
// the dependencies.
cmd := exec.Command("go", "list", "-json", pkg)
env := os.Environ()
env = append(env, c.Env()...)
cmd.Env = env
out, err := cmd.CombinedOutput()
if err != nil {
return nil, err
}
var p ListPackage
if err := json.Unmarshal(out, &p); err != nil {
return nil, err
}
return &p, nil
}
func (c Environ) Env() []string {
var env []string
if c.GOARCH != "" {
env = append(env, fmt.Sprintf("GOARCH=%s", c.GOARCH))
}
if c.GOOS != "" {
env = append(env, fmt.Sprintf("GOOS=%s", c.GOOS))
}
if c.GOROOT != "" {
env = append(env, fmt.Sprintf("GOROOT=%s", c.GOROOT))
}
if c.GOPATH != "" {
env = append(env, fmt.Sprintf("GOPATH=%s", c.GOPATH))
}
var cgo int8
if c.CgoEnabled {
cgo = 1
}
env = append(env, fmt.Sprintf("CGO_ENABLED=%d", cgo))
return env
}
func (c Environ) String() string {
return strings.Join(c.Env(), " ")
}
// Optional arguments to Environ.Build.
type BuildOpts struct {
// ExtraArgs to `go build`.
ExtraArgs []string
}
// Build compiles `pkg`, writing the executable or object to `binaryPath`.
func (c Environ) Build(pkg string, binaryPath string, opts BuildOpts) error {
path, err := c.FindPackageDir(pkg)
if err != nil {
return err
}
args := []string{
"build",
"-a", // Force rebuilding of packages.
"-o", binaryPath,
"-installsuffix", "uroot",
"-ldflags", "-s -w", // Strip all symbols.
}
if opts.ExtraArgs != nil {
args = append(args, opts.ExtraArgs...)
}
// We always set the working directory, so this is always '.'.
args = append(args, ".")
cmd := exec.Command("go", args...)
cmd.Dir = path
cmd.Env = append(os.Environ(), c.Env()...)
if o, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("error building go package %v: %v, %v", pkg, string(o), err)
}
return nil
}