...
1 package bazel
2
3 import (
4 "bytes"
5 "context"
6 "fmt"
7 "log"
8 "os"
9 "os/exec"
10 "os/signal"
11
12 syscall "golang.org/x/sys/unix"
13 )
14
15 const (
16
17
18
19 BuildWorkspaceDir = "BUILD_WORKSPACE_DIRECTORY"
20
21
22
23
24 TestWorkspace = "TEST_WORKSPACE"
25
26
27
28
29 TestTmpDir = "TEST_TMPDIR"
30
31
32
33
34
35 RootRepoName = "_main"
36 )
37
38 var workspaceFiles = []string{"WORKSPACE.bazel", "WORKSPACE"}
39
40
41 func IsBazelTest() bool {
42 _, exists := os.LookupEnv(TestWorkspace)
43 return exists
44 }
45
46
47 func IsBazelRun() bool {
48 _, exists := os.LookupEnv(BuildWorkspaceDir)
49 return exists
50 }
51
52
53
54
55
56 func Binary() string {
57 if bin, ok := os.LookupEnv("BAZEL_BINARY_PATH"); ok {
58 return bin
59 }
60 return "bazel"
61 }
62
63
64 func Build(args ...string) *Cmd {
65 return Command(append([]string{"build"}, args...)...)
66 }
67
68
69 func Test(args ...string) *Cmd {
70 return Command(append([]string{"test"}, args...)...)
71 }
72
73
74 func Run(args ...string) *Cmd {
75 return Command(append([]string{"run"}, args...)...)
76 }
77
78
79
80 type Bazel struct {
81 ctx context.Context
82
83
84 configs Configs
85
86 bin string
87 }
88
89 func New(opts ...Option) *Bazel {
90 o := &options{ctx: setupSignalHandler(), bin: Binary()}
91 for _, opt := range opts {
92 opt(o)
93 }
94
95 return &Bazel{o.ctx, o.configs, o.bin}
96 }
97
98 func (b *Bazel) Cmd(args ...string) *Cmd {
99
100
101
102 args = append(args[:1], append(b.configs.ToArgs(), args[1:]...)...)
103
104 return makeBazelCmd(b.ctx, b.bin, args...)
105 }
106
107 func (b *Bazel) Run(args ...string) *Cmd {
108 return b.Cmd(append([]string{"run"}, args...)...)
109 }
110
111 func (b *Bazel) Build(args ...string) *Cmd {
112 return b.Cmd(append([]string{"build"}, args...)...)
113 }
114
115 func (b *Bazel) Test(args ...string) *Cmd {
116 return b.Cmd(append([]string{"test"}, args...)...)
117 }
118
119
120
121 type Cmd struct {
122 *exec.Cmd
123 }
124
125
126
127
128 func Command(args ...string) *Cmd {
129 return makeBazelCmd(setupSignalHandler(), Binary(), args...)
130 }
131
132
133
134
135 func (c *Cmd) Output() ([]byte, error) {
136
137
138
139 var stderr bytes.Buffer
140 c.Stderr = &stderr
141
142 output, err := c.Cmd.Output()
143 if err != nil {
144 return nil, fmt.Errorf("failed to run '%s': %w: %s",
145 c.Cmd.String(), err, stderr.String())
146 }
147 return output, nil
148 }
149
150
151 func (c *Cmd) Run() error {
152 c.Cmd.Stdout = os.Stdout
153 c.Cmd.Stderr = os.Stderr
154
155 if err := c.Cmd.Run(); err != nil {
156 return fmt.Errorf("failed to run '%s': %w", c.String(), err)
157 }
158 return nil
159 }
160
161
162
163 func makeBazelCmd(ctx context.Context, bazel string, args ...string) *Cmd {
164
165
166 if len(args) == 0 {
167 panic("bazel.Cmd: no command provided")
168 }
169
170 cmd := exec.CommandContext(ctx, bazel, args...)
171
172
173 cmd.Env = os.Environ()
174
175 dir, err := ResolveWd()
176 if err != nil {
177 log.Fatal(err)
178 }
179 cmd.Dir = dir
180
181 return &Cmd{cmd}
182 }
183
184
185
186 func setupSignalHandler() context.Context {
187 ctx, cancel := context.WithCancel(context.Background())
188
189 c := make(chan os.Signal, 2)
190 signal.Notify(c, os.Interrupt, syscall.SIGTERM)
191 go func() {
192 <-c
193 cancel()
194 <-c
195 os.Exit(1)
196 }()
197
198 return ctx
199 }
200
View as plain text