Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli: support passing --config options in tools/bazel #6643

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion cli/bazelisk/bazelisk.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ package bazelisk
import (
"fmt"
"io"
goLog "log"
"os"
"path/filepath"
"strings"
"sync"
"syscall"

goLog "log"

"github.com/bazelbuild/bazelisk/config"
"github.com/bazelbuild/bazelisk/core"
"github.com/bazelbuild/bazelisk/repositories"
Expand All @@ -23,6 +24,43 @@ var (
setVersionErr error
)

// HandleWrapper re-invokes the CLI using the tools/bazel script if present,
// setting BAZEL_REAL to point to the CLI itself.
//
// This needs to be handled by us, rather than bazelisk, in order to support
// passing --config options using tools/bazel. Otherwise, we'd canonicalize args
// before invoking tools/bazel, which sets --ignore_all_rc_files and prevents
// --config flags from working.
//
// Note that this behavior subtly differs from bazelisk in that BAZEL_REAL will
// point to bb, which is a bazel wrapper rather than a "real" bazel binary.
// Despite this difference, in practice we expect this to be a net improvement
// in compatibility.
func HandleWrapper() error {
if os.Getenv("BAZELISK_SKIP_WRAPPER") == "true" {
return nil
}
os.Setenv("BAZELISK_SKIP_WRAPPER", "true")
ws, err := workspace.Path()
if err != nil {
return nil
}
scriptPath := filepath.Join(ws, "tools/bazel")
os.Setenv("BAZEL_REAL", os.Args[0])

// Try an exec() call to invoke tools/bazel. If tools/bazel exists and is
// executable then the exec call should replace the current process with a
// tools/bazel process which should then call back into `bb` by invoking
// $BAZEL_REAL, which we've set to args[0].
//
// If tools/bazel doesn't exist or isn't executable then the exec call will
// just fail and we just silently ignore the error, which is what bazelisk
// does as well.

_ = syscall.Exec(scriptPath, append([]string{scriptPath}, os.Args[1:]...), os.Environ())
return nil
}

type RunOpts struct {
// Stdout is the Writer where bazelisk should write its stdout.
// Defaults to os.Stdout if nil.
Expand Down
2 changes: 2 additions & 0 deletions cli/cmd/bb/bb.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ func main() {
}

func run() (exitCode int, err error) {
bazelisk.HandleWrapper()

start := time.Now()
// Record original arguments so we can show them in the UI.
originalArgs := append([]string{}, os.Args...)
Expand Down
6 changes: 5 additions & 1 deletion cli/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,11 @@ func runGit(dir string, args ...string) (output string, err error) {
cmd.Dir = dir
b, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("command `git %s` failed: %s", strings.Join(args, " "), string(b))
msg := strings.TrimSpace(string(b))
if msg == "" {
msg = err.Error()
}
return "", fmt.Errorf("command `git %s` failed: %s", strings.Join(args, " "), msg)
}
return strings.TrimSpace(string(b)), nil
}
Expand Down
29 changes: 29 additions & 0 deletions cli/test/integration/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,35 @@ func TestInvokeViaBazelisk(t *testing.T) {
}
}

func TestInvokeViaBazeliskWithToolsBazelWrapper(t *testing.T) {
ws := testcli.NewWorkspace(t)
testfs.WriteAllFileContents(t, ws, map[string]string{
".bazelversion": fmt.Sprintf("%s\n%s\n", testcli.BinaryPath(t), testbazel.BinaryPath(t)),
".bazelrc": `
build:foo --action_env=EXIT_CODE=0
`,
"WORKSPACE": "",
"BUILD": `
load(":defs.bzl", "run_shell")
run_shell(name = "exit_command", command = "exit ${EXIT_CODE:-1}")
`,
// Note: the tools/bazel script passes a --config flag. After expanding
// and canonicalizing flags, we set --ignore_all_rc_files, which means
// that --config flags are no longer allowed. So, this is testing that
// we invoke tools/bazel *after* calling tools/bazel.
"tools/bazel": `#!/bin/sh
exec "$BAZEL_REAL" "$@" --config=foo
`,
})

testfs.MakeExecutable(t, ws, "tools/bazel")

cmd := testcli.BazeliskCommand(t, ws, "--verbose=1", "build", ":all")
b, err := testcli.CombinedOutput(cmd)

require.NoError(t, err, "output: %s", string(b))
}

func TestBazelHelp(t *testing.T) {
ws := testcli.NewWorkspace(t)
cmd := testcli.Command(t, ws, "help", "completion")
Expand Down
19 changes: 19 additions & 0 deletions cli/testutil/testcli/testcli.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,25 @@ func NewWorkspace(t *testing.T) string {
ws := testbazel.MakeTempWorkspace(t, map[string]string{
"WORKSPACE": "",
".bazelversion": testbazel.BinaryPath(t),
// Add some basic rules for convenience. The run_shell rule is helpful
// for running simple bazel actions. (Built-in rules like sh_test and
// genrule are relatively heavy and can slow down test execution time)
"defs.bzl": `
def _run_shell_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.run_shell(
outputs = [out],
use_default_shell_env = True, # respect --action_env
command = """
set -e
touch "%s"
%s
""" % (out.path, ctx.attr.command),
)
return [DefaultInfo(files = depset([out]))]

run_shell = rule(implementation = _run_shell_impl, attrs = {"command": attr.string()})
`,
})
// Make it a git workspace to test git metadata.
testgit.Init(t, ws)
Expand Down
Loading