@@ -76,26 +76,75 @@ func MakeDefaultConfig() config.Config {
7676}
7777
7878// RunBazelisk runs the main Bazelisk logic for the given arguments and Bazel repositories.
79+ //
80+ // This will run Bazel in a subprocess and return its exit code.
7981func RunBazelisk (args []string , repos * Repositories ) (int , error ) {
8082 return RunBazeliskWithArgsFunc (func (_ string ) []string { return args }, repos )
8183}
8284
8385// RunBazeliskWithArgsFunc runs the main Bazelisk logic for the given ArgsFunc and Bazel
8486// repositories.
87+ //
88+ // This will run Bazel in a subprocess and return its exit code.
8589func RunBazeliskWithArgsFunc (argsFunc ArgsFunc , repos * Repositories ) (int , error ) {
86-
8790 return RunBazeliskWithArgsFuncAndConfig (argsFunc , repos , MakeDefaultConfig ())
8891}
8992
9093// RunBazeliskWithArgsFuncAndConfig runs the main Bazelisk logic for the given ArgsFunc and Bazel
9194// repositories and config.
95+ //
96+ // This will run Bazel in a subprocess and return its exit code.
9297func RunBazeliskWithArgsFuncAndConfig (argsFunc ArgsFunc , repos * Repositories , config config.Config ) (int , error ) {
9398 return RunBazeliskWithArgsFuncAndConfigAndOut (argsFunc , repos , config , nil )
9499}
95100
96101// RunBazeliskWithArgsFuncAndConfigAndOut runs the main Bazelisk logic for the given ArgsFunc and Bazel
97102// repositories and config, writing its stdout to the passed writer.
103+ //
104+ // This will run Bazel in a subprocess and return its exit code.
98105func RunBazeliskWithArgsFuncAndConfigAndOut (argsFunc ArgsFunc , repos * Repositories , config config.Config , out io.Writer ) (int , error ) {
106+ return RunOrExecBazeliskWithArgsFuncAndConfigAndOut (argsFunc , repos , config , out , false )
107+ }
108+
109+ // ExecBazelisk runs the main Bazelisk logic for the given arguments and Bazel repositories.
110+ //
111+ // If possible (i.e. on non-Windows platforms), this will replace the current process with Bazel
112+ // and will not return. On Windows, this will execute Bazel in a new process and return its exit
113+ // code.
114+ func ExecBazelisk (args []string , repos * Repositories ) (int , error ) {
115+ return ExecBazeliskWithArgsFunc (func (_ string ) []string { return args }, repos )
116+ }
117+
118+ // ExecBazeliskWithArgsFunc runs the main Bazelisk logic for the given ArgsFunc and Bazel
119+ // repositories.
120+ //
121+ // If possible (i.e. on non-Windows platforms), this will replace the current process with Bazel
122+ // and will not return. On Windows, this will execute Bazel in a new process and return its exit
123+ // code.
124+ func ExecBazeliskWithArgsFunc (argsFunc ArgsFunc , repos * Repositories ) (int , error ) {
125+ return ExecBazeliskWithArgsFuncAndConfig (argsFunc , repos , MakeDefaultConfig ())
126+ }
127+
128+ // ExecBazeliskWithArgsFuncAndConfig runs the main Bazelisk logic for the given ArgsFunc and Bazel
129+ // repositories and config.
130+ //
131+ // If possible (i.e. on non-Windows platforms), this will replace the current process with Bazel
132+ // and will not return. On Windows, this will execute Bazel in a new process and return its exit
133+ // code.
134+ func ExecBazeliskWithArgsFuncAndConfig (argsFunc ArgsFunc , repos * Repositories , config config.Config ) (int , error ) {
135+ return RunOrExecBazeliskWithArgsFuncAndConfigAndOut (argsFunc , repos , config , nil , true )
136+ }
137+
138+ // RunOrExecBazeliskWithArgsFuncAndConfigAndOut runs the main Bazelisk logic for the given ArgsFunc and Bazel
139+ // repositories and config.
140+ //
141+ // If exec is true, this will replace the current process with Bazel and will not return (this is
142+ // not possible on Windows; on Windows this will execute Bazel in a new process and return its exit
143+ // code even if exec is true). `out` is not supported in exec mode.
144+ //
145+ // If exec is false, this will run Bazel in a subprocess and return its exit code, writing its stdout
146+ // to the passed writer if provided.
147+ func RunOrExecBazeliskWithArgsFuncAndConfigAndOut (argsFunc ArgsFunc , repos * Repositories , config config.Config , out io.Writer , bool exec ) (int , error ) {
99148 httputil .UserAgent = getUserAgent (config )
100149
101150 bazelInstallation , err := GetBazelInstallation (repos , config )
@@ -108,8 +157,8 @@ func RunBazeliskWithArgsFuncAndConfigAndOut(argsFunc ArgsFunc, repos *Repositori
108157 // --print_env must be the first argument.
109158 if len (args ) > 0 && args [0 ] == "--print_env" {
110159 // print environment variables for sub-processes
111- cmd := makeBazelCmd (bazelInstallation .Path , args , nil , config )
112- for _ , val := range cmd . Env {
160+ _ , _ , env := makeBazelCmd (bazelInstallation .Path , args , config )
161+ for _ , val := range env {
113162 fmt .Println (val )
114163 }
115164 return 0 , nil
@@ -161,11 +210,22 @@ func RunBazeliskWithArgsFuncAndConfigAndOut(argsFunc ArgsFunc, repos *Repositori
161210 }
162211 }
163212
164- exitCode , err := runBazel (bazelInstallation .Path , args , out , config )
165- if err != nil {
166- return - 1 , fmt .Errorf ("could not run Bazel: %v" , err )
213+ if exec {
214+ if out != nil {
215+ return - 1 , fmt .Errorf ("cannot run bazelisk in exec mode with a non-nil output writer" )
216+ }
217+ exitCode , err := execBazel (bazelInstallation .Path , args , config )
218+ if err != nil {
219+ return - 1 , fmt .Errorf ("could not run Bazel: %v" , err )
220+ }
221+ return exitCode , nil
222+ } else {
223+ exitCode , err := runBazel (bazelInstallation .Path , args , out , config )
224+ if err != nil {
225+ return - 1 , fmt .Errorf ("could not run Bazel: %v" , err )
226+ }
227+ return exitCode , nil
167228 }
168- return exitCode , nil
169229}
170230
171231func isVersionCommand (args []string ) (result bool , gnuFormat bool ) {
@@ -636,50 +696,68 @@ func maybeDelegateToWrapper(bazel string, config config.Config) string {
636696 return maybeDelegateToWrapperFromDir (bazel , wd , config )
637697}
638698
639- func prependDirToPathList (cmd * exec. Cmd , dir string ) {
699+ func prependDirToPathList (env [] string , dir string ) {
640700 found := false
641- for idx , val := range cmd . Env {
701+ for idx , val := range env {
642702 splits := strings .Split (val , "=" )
643703 if len (splits ) != 2 {
644704 continue
645705 }
646706 if strings .EqualFold (splits [0 ], "PATH" ) {
647707 found = true
648- cmd . Env [idx ] = fmt .Sprintf ("PATH=%s%s%s" , dir , string (os .PathListSeparator ), splits [1 ])
708+ env [idx ] = fmt .Sprintf ("PATH=%s%s%s" , dir , string (os .PathListSeparator ), splits [1 ])
649709 break
650710 }
651711 }
652712
653713 if ! found {
654- cmd . Env = append (cmd . Env , fmt .Sprintf ("PATH=%s" , dir ))
714+ env = append (env , fmt .Sprintf ("PATH=%s" , dir ))
655715 }
656716}
657717
658- func makeBazelCmd (bazel string , args []string , out io. Writer , config config.Config ) * exec. Cmd {
718+ func makeBazelCmd (bazel string , args []string , config config.Config ) ( string , [] string , [] string ) {
659719 execPath := maybeDelegateToWrapper (bazel , config )
660720
661- cmd := exec .Command (execPath , args ... )
662- cmd .Env = append (os .Environ (), skipWrapperEnv + "=true" )
721+ env := append (os .Environ (), skipWrapperEnv + "=true" )
663722 if execPath != bazel {
664- cmd . Env = append (cmd . Env , fmt .Sprintf ("%s=%s" , bazelReal , bazel ))
723+ env = append (env , fmt .Sprintf ("%s=%s" , bazelReal , bazel ))
665724 }
666725 selfPath , err := os .Executable ()
667726 if err != nil {
668- cmd . Env = append (cmd . Env , bazeliskEnv + "=" + selfPath )
727+ env = append (env , bazeliskEnv + "=" + selfPath )
669728 }
670- prependDirToPathList (cmd , filepath .Dir (execPath ))
729+ prependDirToPathList (env , filepath .Dir (execPath ))
730+
731+ commandLine := []string {execPath }
732+ commandLine = append (commandLine , args ... )
733+ return execPath , commandLine , env
734+ }
735+
736+ func execBazel (bazel string , args []string , config config.Config ) (int , error ) {
737+ if runtime .GOOS == "windows" {
738+ // syscall.Exec is not supported on windows
739+ return runBazel (bazel , args , nil , config )
740+ }
741+
742+ execPath , args , env := makeBazelCmd (bazel , args , config )
743+
744+ err := syscall .Exec (execPath , args , env )
745+ return 1 , fmt .Errorf ("could not start Bazel: %v" , err )
746+ }
747+
748+ func runBazel (bazel string , args []string , out io.Writer , config config.Config ) (int , error ) {
749+ execPath , commandLine , env := makeBazelCmd (bazel , args , config )
750+
751+ cmd := exec .Command (execPath , commandLine [1 :]... )
752+ cmd .Env = env
671753 cmd .Stdin = os .Stdin
672754 if out == nil {
673755 cmd .Stdout = os .Stdout
674756 } else {
675757 cmd .Stdout = out
676758 }
677759 cmd .Stderr = os .Stderr
678- return cmd
679- }
680760
681- func runBazel (bazel string , args []string , out io.Writer , config config.Config ) (int , error ) {
682- cmd := makeBazelCmd (bazel , args , out , config )
683761 err := cmd .Start ()
684762 if err != nil {
685763 return 1 , fmt .Errorf ("could not start Bazel: %v" , err )
0 commit comments