Add a persistent bazel server between builds

This feature is toggled on with USE_PERSISTENT_BAZEL, which is off by
default. Those that opt-in will have a bazel server running between
builds (with a 3hr default TTL) which will greatly improve analysis on
subsequent builds. (As Bazel maintains a cache of analysis results).

Bug: 266983462
Test: Manual `m nothing` runs (timing with and without the feature)
Test: New integration test
Test: Presubmits
Change-Id: I3af4948baa0c490e9b87c48ffdbe9f67732586c7
This commit is contained in:
Chris Parsons
2023-02-23 17:28:06 -05:00
parent 0897df1466
commit 9402ca881c
9 changed files with 362 additions and 12 deletions

View File

@@ -607,7 +607,7 @@ func NewBazelContext(c *config) (BazelContext, error) {
dclaEnabledModules := map[string]bool{}
addToStringSet(dclaEnabledModules, dclaMixedBuildsEnabledList)
return &mixedBuildBazelContext{
bazelRunner: &builtinBazelRunner{},
bazelRunner: &builtinBazelRunner{c.UseBazelProxy, absolutePath(c.outDir)},
paths: &paths,
modulesDefaultToBazel: c.BuildMode == BazelDevMode,
bazelEnabledModules: enabledModules,
@@ -684,23 +684,46 @@ func (r *mockBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, _ *metrics.Event
return "", "", nil
}
type builtinBazelRunner struct{}
type builtinBazelRunner struct {
useBazelProxy bool
outDir string
}
// Issues the given bazel command with given build label and additional flags.
// Returns (stdout, stderr, error). The first and second return values are strings
// containing the stdout and stderr of the run command, and an error is returned if
// the invocation returned an error code.
func (r *builtinBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (string, string, error) {
eventHandler.Begin("bazel command")
defer eventHandler.End("bazel command")
stderr := &bytes.Buffer{}
bazelCmd.Stderr = stderr
if output, err := bazelCmd.Output(); err != nil {
return "", string(stderr.Bytes()),
fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr)
if r.useBazelProxy {
eventHandler.Begin("client_proxy")
defer eventHandler.End("client_proxy")
proxyClient := bazel.NewProxyClient(r.outDir)
// Omit the arg containing the Bazel binary, as that is handled by the proxy
// server.
bazelFlags := bazelCmd.Args[1:]
// TODO(b/270989498): Refactor these functions to not take exec.Cmd, as its
// not actually executed for client proxying.
resp, err := proxyClient.IssueCommand(bazel.CmdRequest{bazelFlags, bazelCmd.Env})
if err != nil {
return "", "", err
}
if len(resp.ErrorString) > 0 {
return "", "", fmt.Errorf(resp.ErrorString)
}
return resp.Stdout, resp.Stderr, nil
} else {
return string(output), string(stderr.Bytes()), nil
eventHandler.Begin("bazel command")
defer eventHandler.End("bazel command")
stderr := &bytes.Buffer{}
bazelCmd.Stderr = stderr
if output, err := bazelCmd.Output(); err != nil {
return "", string(stderr.Bytes()),
fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr)
} else {
return string(output), string(stderr.Bytes()), nil
}
}
}