@@ -24,10 +24,98 @@ import (
2424 "log"
2525 "os"
2626 "os/exec"
27+ "path/filepath"
2728 "strings"
2829 "time"
2930)
3031
32+ // bazelNpmPath looks up a relative path to a binary from @bazel/bazel
33+ // This is used as an alternate resolution when no bazel binary is in the $PATH
34+ // When running from the @bazel/ibazel npm package, our binary is
35+ // /DIR/node_modules/@bazel/ibazel/bin/darwin_amd64/ibazel
36+ // We can find bazel in
37+ // /DIR/node_modules/@bazel/bazel-darwin_x64/bazel-0.28.0-darwin-x86_64
38+ func bazelNpmPath (ibazelBinPath string ) (string , error ) {
39+ s := strings .Split (ibazelBinPath , "/" )
40+ for i := 0 ; i + 4 < len (s ); i ++ {
41+ prefix , nm , scope , pkg , dir , bin := s [0 :i ], s [i ], s [i + 1 ], s [i + 2 ], s [i + 3 ], s [i + 4 ]
42+ if nm == "node_modules" && scope == "@bazel" && pkg == "ibazel" && dir == "bin" {
43+ // See mapping in release/npm/index.js - ibazel is named with "amd64" arch
44+ // but @bazel/bazel uses node arch names
45+ arch := strings .Replace (bin , "amd64" , "x64" , 1 )
46+ dir := strings .Join (append (prefix , nm , scope , "bazel-" + arch ), "/" )
47+ // Find the bazel binary in the directory - it will have a version number in the name
48+ // so we list all the files and find a bazel-*-$ARCH
49+ if fd , err := os .Open (filepath .FromSlash (dir )); err == nil {
50+ if names , err := fd .Readdirnames (0 ); err == nil {
51+ for j := 0 ; j < len (names ); j ++ {
52+ if strings .HasPrefix (names [j ], "bazel-" ) {
53+ return dir + "/" + names [j ], nil
54+ }
55+ }
56+ }
57+ }
58+ }
59+ }
60+ return "" , errors .New ("bazel binary not found in @bazel/bazel package" )
61+ }
62+
63+ // bazeliskNpmPath looks up a relative path to a binary from @bazel/bazelisk
64+ // This is used as an alternate resolution when no bazel binary is in the $PATH
65+ // When running from the @bazel/ibazel npm package, our binary is
66+ // /DIR/node_modules/@bazel/ibazel/bin/darwin_amd64/ibazel
67+ // We can find bazelisk in
68+ // /DIR/node_modules/@bazel/bazelisk/bazelizk-darwin_amd64
69+ func bazeliskNpmPath (ibazelBinPath string ) (string , error ) {
70+ s := strings .Split (ibazelBinPath , "/" )
71+ for i := 0 ; i + 4 < len (s ); i ++ {
72+ prefix , nm , scope , pkg , dir , bin := s [0 :i ], s [i ], s [i + 1 ], s [i + 2 ], s [i + 3 ], s [i + 4 ]
73+ if nm == "node_modules" && scope == "@bazel" && pkg == "ibazel" && dir == "bin" {
74+ var ext string
75+ if strings .HasPrefix (bin , "windows_" ) {
76+ ext = ".exe"
77+ }
78+ name := strings .Join (append (prefix , nm , scope , "bazelisk" , "bazelisk-" + bin + ext ), "/" )
79+ _ , err := os .Stat (name )
80+ if err != nil {
81+ if ! os .IsNotExist (err ) {
82+ return "" , err
83+ }
84+ continue
85+ }
86+ return name , nil
87+ }
88+ }
89+ return "" , errors .New ("bazelisk binary not found in @bazel/bazelisk package" )
90+ }
91+
92+ func findBazel () string {
93+ // Frontend devs may have installed @bazel/bazelisk and @bazel/ibazel from npm
94+ // If they also have bazelisk in the $PATH, we want to resolve this one, to avoid version skew
95+ if npmPath , err := bazeliskNpmPath (filepath .ToSlash (os .Args [0 ])); err == nil {
96+ return filepath .FromSlash (npmPath )
97+ }
98+ // Frontend devs may have installed @bazel/bazel and @bazel/ibazel from npm
99+ // If they also have bazel in the $PATH, we want to resolve this one, to avoid version skew
100+ if npmPath , err := bazelNpmPath (filepath .ToSlash (os .Args [0 ])); err == nil {
101+ return filepath .FromSlash (npmPath )
102+ }
103+ // Check in $PATH for system-installed Bazelisk
104+ if path , err := exec .LookPath ("bazelisk" ); err == nil {
105+ return path
106+ }
107+ // Check in $PATH for system-installed Bazel
108+ if path , err := exec .LookPath ("bazel" ); err == nil {
109+ return path
110+ }
111+
112+ // If we've fallen through to here, the lookup won't succeed.
113+ // Return "bazel" so that we'll later fail with an error
114+ // exec: "bazel": executable file not found in $PATH
115+ // which helps the user understand that we looked in the $PATH
116+ return "bazel"
117+ }
118+
31119type Bazel interface {
32120 SetArguments ([]string )
33121 SetStartupArgs ([]string )
@@ -58,6 +146,10 @@ type bazel struct {
58146}
59147
60148func NewBazel (path string , workingDirectory string ) Bazel {
149+ if path == "" {
150+ path = findBazel ()
151+ }
152+
61153 return & bazel {
62154 path : path ,
63155 workingDirectory : workingDirectory ,
0 commit comments