Skip to content

Commit 392722c

Browse files
committed
Exec bazel instead of subprocessing
1 parent 1f9a1ac commit 392722c

File tree

1 file changed

+62
-20
lines changed

1 file changed

+62
-20
lines changed

core/core.go

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -76,25 +76,41 @@ func MakeDefaultConfig() config.Config {
7676
}
7777

7878
// RunBazelisk runs the main Bazelisk logic for the given arguments and Bazel repositories.
79+
//
80+
// If possible (i.e. on non-Windows platforms), this will replace the current process with Bazel
81+
// and will not return. On Windows, this will execute Bazel in a new process and return its exit
82+
// code.
7983
func RunBazelisk(args []string, repos *Repositories) (int, error) {
8084
return RunBazeliskWithArgsFunc(func(_ string) []string { return args }, repos)
8185
}
8286

8387
// RunBazeliskWithArgsFunc runs the main Bazelisk logic for the given ArgsFunc and Bazel
8488
// repositories.
89+
//
90+
// If possible (i.e. on non-Windows platforms), this will replace the current process with Bazel
91+
// and will not return. On Windows, this will execute Bazel in a new process and return its exit
92+
// code.
8593
func RunBazeliskWithArgsFunc(argsFunc ArgsFunc, repos *Repositories) (int, error) {
8694

8795
return RunBazeliskWithArgsFuncAndConfig(argsFunc, repos, MakeDefaultConfig())
8896
}
8997

9098
// RunBazeliskWithArgsFuncAndConfig runs the main Bazelisk logic for the given ArgsFunc and Bazel
9199
// repositories and config.
100+
//
101+
// If possible (i.e. on non-Windows platforms), this will replace the current process with Bazel
102+
// and will not return. On Windows, this will execute Bazel in a new process and return its exit
103+
// code.
92104
func RunBazeliskWithArgsFuncAndConfig(argsFunc ArgsFunc, repos *Repositories, config config.Config) (int, error) {
93105
return RunBazeliskWithArgsFuncAndConfigAndOut(argsFunc, repos, config, nil)
94106
}
95107

96108
// RunBazeliskWithArgsFuncAndConfigAndOut runs the main Bazelisk logic for the given ArgsFunc and Bazel
97109
// repositories and config, writing its stdout to the passed writer.
110+
//
111+
// If possible (i.e. on non-Windows platforms when `out` is nil), this will replace the current
112+
// process with Bazel and will not return. If that's not possible, this will execute Bazel in a new
113+
// process and return its exit code.
98114
func RunBazeliskWithArgsFuncAndConfigAndOut(argsFunc ArgsFunc, repos *Repositories, config config.Config, out io.Writer) (int, error) {
99115
httputil.UserAgent = getUserAgent(config)
100116

@@ -108,8 +124,8 @@ func RunBazeliskWithArgsFuncAndConfigAndOut(argsFunc ArgsFunc, repos *Repositori
108124
// --print_env must be the first argument.
109125
if len(args) > 0 && args[0] == "--print_env" {
110126
// print environment variables for sub-processes
111-
cmd := makeBazelCmd(bazelInstallation.Path, args, nil, config)
112-
for _, val := range cmd.Env {
127+
_, _, env := makeBazelCmd(bazelInstallation.Path, args, config)
128+
for _, val := range env {
113129
fmt.Println(val)
114130
}
115131
return 0, nil
@@ -161,11 +177,19 @@ func RunBazeliskWithArgsFuncAndConfigAndOut(argsFunc ArgsFunc, repos *Repositori
161177
}
162178
}
163179

164-
exitCode, err := runBazel(bazelInstallation.Path, args, out, config)
165-
if err != nil {
166-
return -1, fmt.Errorf("could not run Bazel: %v", err)
180+
if out == nil {
181+
exitCode, err := execBazel(bazelInstallation.Path, args, config)
182+
if err != nil {
183+
return -1, fmt.Errorf("could not run Bazel: %v", err)
184+
}
185+
return exitCode, nil
186+
} else {
187+
exitCode, err := runBazel(bazelInstallation.Path, args, out, config)
188+
if err != nil {
189+
return -1, fmt.Errorf("could not run Bazel: %v", err)
190+
}
191+
return exitCode, nil
167192
}
168-
return exitCode, nil
169193
}
170194

171195
func isVersionCommand(args []string) (result bool, gnuFormat bool) {
@@ -636,50 +660,68 @@ func maybeDelegateToWrapper(bazel string, config config.Config) string {
636660
return maybeDelegateToWrapperFromDir(bazel, wd, config)
637661
}
638662

639-
func prependDirToPathList(cmd *exec.Cmd, dir string) {
663+
func prependDirToPathList(env []string, dir string) {
640664
found := false
641-
for idx, val := range cmd.Env {
665+
for idx, val := range env {
642666
splits := strings.Split(val, "=")
643667
if len(splits) != 2 {
644668
continue
645669
}
646670
if strings.EqualFold(splits[0], "PATH") {
647671
found = true
648-
cmd.Env[idx] = fmt.Sprintf("PATH=%s%s%s", dir, string(os.PathListSeparator), splits[1])
672+
env[idx] = fmt.Sprintf("PATH=%s%s%s", dir, string(os.PathListSeparator), splits[1])
649673
break
650674
}
651675
}
652676

653677
if !found {
654-
cmd.Env = append(cmd.Env, fmt.Sprintf("PATH=%s", dir))
678+
env = append(env, fmt.Sprintf("PATH=%s", dir))
655679
}
656680
}
657681

658-
func makeBazelCmd(bazel string, args []string, out io.Writer, config config.Config) *exec.Cmd {
682+
func makeBazelCmd(bazel string, args []string, config config.Config) (string, []string, []string) {
659683
execPath := maybeDelegateToWrapper(bazel, config)
660684

661-
cmd := exec.Command(execPath, args...)
662-
cmd.Env = append(os.Environ(), skipWrapperEnv+"=true")
685+
env := append(os.Environ(), skipWrapperEnv+"=true")
663686
if execPath != bazel {
664-
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", bazelReal, bazel))
687+
env = append(env, fmt.Sprintf("%s=%s", bazelReal, bazel))
665688
}
666689
selfPath, err := os.Executable()
667690
if err != nil {
668-
cmd.Env = append(cmd.Env, bazeliskEnv+"="+selfPath)
691+
env = append(env, bazeliskEnv+"="+selfPath)
669692
}
670-
prependDirToPathList(cmd, filepath.Dir(execPath))
693+
prependDirToPathList(env, filepath.Dir(execPath))
694+
695+
commandLine := []string{execPath}
696+
commandLine = append(commandLine, args...)
697+
return execPath, commandLine, env
698+
}
699+
700+
func execBazel(bazel string, args []string, config config.Config) (int, error) {
701+
if runtime.GOOS == "windows" {
702+
// syscall.Exec is not supported on windows
703+
return runBazel(bazel, args, nil, config)
704+
}
705+
706+
execPath, args, env := makeBazelCmd(bazel, args, config)
707+
708+
err := syscall.Exec(execPath, args, env)
709+
return 1, fmt.Errorf("could not start Bazel: %v", err)
710+
}
711+
712+
func runBazel(bazel string, args []string, out io.Writer, config config.Config) (int, error) {
713+
execPath, commandLine, env := makeBazelCmd(bazel, args, config)
714+
715+
cmd := exec.Command(execPath, commandLine[1:]...)
716+
cmd.Env = env
671717
cmd.Stdin = os.Stdin
672718
if out == nil {
673719
cmd.Stdout = os.Stdout
674720
} else {
675721
cmd.Stdout = out
676722
}
677723
cmd.Stderr = os.Stderr
678-
return cmd
679-
}
680724

681-
func runBazel(bazel string, args []string, out io.Writer, config config.Config) (int, error) {
682-
cmd := makeBazelCmd(bazel, args, out, config)
683725
err := cmd.Start()
684726
if err != nil {
685727
return 1, fmt.Errorf("could not start Bazel: %v", err)

0 commit comments

Comments
 (0)