Skip to content

Commit 35ecfd1

Browse files
committed
Support binary tools/bazel wrappers
To do this, recognize new tools/bazel.<OSNAME>-<ARCH> and tools/bazel.<ARCH> names for the wrapper, and favor those over tools/bazel if they are present.
1 parent 0ba6d71 commit 35ecfd1

File tree

4 files changed

+123
-19
lines changed

4 files changed

+123
-19
lines changed

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -213,18 +213,19 @@ You can set `BAZELISK_CLEAN` to run `clean --expunge` between builds when migrat
213213

214214
## tools/bazel
215215

216-
If `tools/bazel` exists in your workspace root and is executable, Bazelisk will run this file, instead of the Bazel version it downloaded.
216+
Bazelisk will try to run a Bazel wrapper from the `tools` directory if present, instead of the Bazel version it downloaded. The environment variable `BAZEL_REAL`, unconditionally set by Bazelisk, can be used by the wrapper to execute the downloaded Bazel binary.
217217

218-
It will set the environment variable `BAZEL_REAL` to the path of the downloaded Bazel binary.
219-
This can be useful, if you have a wrapper script that e.g. ensures that environment variables are set to known good values.
220-
This behavior can be disabled by setting the environment variable `BAZELISK_SKIP_WRAPPER` to any value (except the empty string) before launching Bazelisk.
218+
Bazelisk looks for the following wrappers, in order:
221219

222-
You can control the user agent that Bazelisk sends in all HTTP requests by setting `BAZELISK_USER_AGENT` to the desired value.
220+
* `tools/bazel.<OSNAME>-<ARCH>`: An executable that's OS- and platform-specific.
221+
* `tools/bazel.<ARCH>`: An executable that's platform-specific (for cases where your project only supports one operating system anyway).
222+
* `tools/bazel`: An executable or shell script.
223+
* `tools/bazel.ps1`: A PowerShell script on Windows.
224+
* `tools/bazel.bat`: A batch file on Windows.
223225

224-
On Windows, Bazelisk will also consider the following files in addition to `tools/bazel`:
226+
This behavior can be disabled by setting the environment variable `BAZELISK_SKIP_WRAPPER` to any value (except the empty string) before launching Bazelisk.
225227

226-
* `tools/bazel.ps1` (PowerShell)
227-
* `tools/bazel.bat`
228+
You can control the user agent that Bazelisk sends in all HTTP requests by setting `BAZELISK_USER_AGENT` to the desired value.
228229

229230
# .bazeliskrc configuration file
230231

core/core.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -618,20 +618,38 @@ func maybeDelegateToWrapperFromDir(bazel string, wd string, config config.Config
618618
}
619619

620620
root := ws.FindWorkspaceRoot(wd)
621-
wrapper := filepath.Join(root, wrapperPath)
622-
if stat, err := os.Stat(wrapper); err == nil && !stat.Mode().IsDir() && stat.Mode().Perm()&0111 != 0 {
623-
return wrapper
624-
}
621+
exeSuffix := platforms.DetermineExecutableFilenameSuffix()
625622

623+
// The list of candidate wrappers must go from most specific to least specific.
624+
candidates := []string{}
625+
osName, err := platforms.DetermineOperatingSystem()
626+
if err == nil {
627+
// We pass platforms.DarwinArm64MinVersion to disable the Darwin x86_64 fallback because this feature
628+
// was added years after Apple Silicon launched and it's not worth trying to be backwards compatible.
629+
arch, err := platforms.DetermineArchitecture(osName, platforms.DarwinArm64MinVersion)
630+
if err == nil {
631+
candidates = append(candidates, filepath.Join(root, wrapperPath+"."+runtime.GOOS+"-"+arch+exeSuffix))
632+
candidates = append(candidates, filepath.Join(root, wrapperPath+"."+arch+exeSuffix))
633+
}
634+
}
635+
candidates = append(candidates, filepath.Join(root, wrapperPath))
626636
if runtime.GOOS == "windows" {
627-
powershellWrapper := filepath.Join(root, wrapperPath+".ps1")
628-
if stat, err := os.Stat(powershellWrapper); err == nil && !stat.Mode().IsDir() {
629-
return powershellWrapper
637+
candidates = append(candidates, filepath.Join(root, wrapperPath+".ps1"))
638+
candidates = append(candidates, filepath.Join(root, wrapperPath+".bat"))
639+
}
640+
641+
for _, wrapper := range candidates {
642+
stat, err := os.Stat(wrapper)
643+
if err != nil {
644+
continue
630645
}
631646

632-
batchWrapper := filepath.Join(root, wrapperPath+".bat")
633-
if stat, err := os.Stat(batchWrapper); err == nil && !stat.Mode().IsDir() {
634-
return batchWrapper
647+
valid := !stat.Mode().IsDir()
648+
if runtime.GOOS != "windows" {
649+
valid = valid && stat.Mode().Perm()&0111 != 0
650+
}
651+
if valid {
652+
return wrapper
635653
}
636654
}
637655

core/core_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,88 @@ func TestMaybeDelegateToNoNonExecutableWrapper(t *testing.T) {
6565
}
6666
}
6767

68+
func TestMaybeDelegateToOsAndArchSpecificWrapper(t *testing.T) {
69+
// It's not guaranteed that `tools/bazel` is executable on the
70+
// Windows host running this test. Thus the test is skipped on
71+
// this platform to guarantee consistent results.
72+
if runtime.GOOS == "windows" {
73+
return
74+
}
75+
76+
var tmpDir, err = os.MkdirTemp("", "TestMaybeDelegateToOsAndArchSpecificWrapper")
77+
if err != nil {
78+
log.Fatal(err)
79+
}
80+
defer os.RemoveAll(tmpDir)
81+
82+
osName, err := platforms.DetermineOperatingSystem()
83+
if err != nil {
84+
log.Fatal(err)
85+
}
86+
arch, err := platforms.DetermineArchitecture(osName, platforms.DarwinArm64MinVersion)
87+
if err != nil {
88+
log.Fatal(err)
89+
}
90+
91+
os.MkdirAll(tmpDir, os.ModeDir|0700)
92+
os.WriteFile(filepath.Join(tmpDir, "WORKSPACE"), []byte(""), 0600)
93+
os.WriteFile(filepath.Join(tmpDir, "BUILD"), []byte(""), 0600)
94+
95+
os.MkdirAll(filepath.Join(tmpDir, "tools"), os.ModeDir|0700)
96+
os.WriteFile(filepath.Join(tmpDir, "tools", "bazel."+runtime.GOOS+"-"+arch), []byte(""), 0700)
97+
// Also create the standard wrapper to ensure we prefer the os/arch-specific wrapper.
98+
os.WriteFile(filepath.Join(tmpDir, "tools", "bazel"), []byte(""), 0700)
99+
// Also create the plaform-specific wrapper to ensure we prefer the os/arch-specific wrapper.
100+
os.WriteFile(filepath.Join(tmpDir, "tools", "bazel."+arch), []byte(""), 0700)
101+
102+
entrypoint := maybeDelegateToWrapperFromDir("bazel_real", tmpDir, config.Null())
103+
expected := filepath.Join(tmpDir, "tools", "bazel."+runtime.GOOS+"-"+arch)
104+
105+
if entrypoint != expected {
106+
t.Fatalf("Expected to delegate bazel to %q, but got %q", expected, entrypoint)
107+
}
108+
}
109+
110+
func TestMaybeDelegateToArchSpecificWrapper(t *testing.T) {
111+
// It's not guaranteed that `tools/bazel` is executable on the
112+
// Windows host running this test. Thus the test is skipped on
113+
// this platform to guarantee consistent results.
114+
if runtime.GOOS == "windows" {
115+
return
116+
}
117+
118+
var tmpDir, err = os.MkdirTemp("", "TestMaybeDelegateToArchSpecificWrapper")
119+
if err != nil {
120+
log.Fatal(err)
121+
}
122+
defer os.RemoveAll(tmpDir)
123+
124+
osName, err := platforms.DetermineOperatingSystem()
125+
if err != nil {
126+
log.Fatal(err)
127+
}
128+
arch, err := platforms.DetermineArchitecture(osName, platforms.DarwinArm64MinVersion)
129+
if err != nil {
130+
log.Fatal(err)
131+
}
132+
133+
os.MkdirAll(tmpDir, os.ModeDir|0700)
134+
os.WriteFile(filepath.Join(tmpDir, "WORKSPACE"), []byte(""), 0600)
135+
os.WriteFile(filepath.Join(tmpDir, "BUILD"), []byte(""), 0600)
136+
137+
os.MkdirAll(filepath.Join(tmpDir, "tools"), os.ModeDir|0700)
138+
os.WriteFile(filepath.Join(tmpDir, "tools", "bazel."+arch), []byte(""), 0700)
139+
// Also create the standard wrapper to ensure we prefer the arch-specific wrapper.
140+
os.WriteFile(filepath.Join(tmpDir, "tools", "bazel"), []byte(""), 0700)
141+
142+
entrypoint := maybeDelegateToWrapperFromDir("bazel_real", tmpDir, config.Null())
143+
expected := filepath.Join(tmpDir, "tools", "bazel."+arch)
144+
145+
if entrypoint != expected {
146+
t.Fatalf("Expected to delegate bazel to %q, but got %q", expected, entrypoint)
147+
}
148+
}
149+
68150
func TestMaybeDelegateToStandardWrapper(t *testing.T) {
69151
// It's not guaranteed that `tools/bazel` is executable on the
70152
// Windows host running this test. Thus the test is skipped on

platforms/platforms.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ func DetermineBazelInstallerFilename(version string, config config.Config) (stri
127127
return fmt.Sprintf("bazel-%s-installer-%s-%s.sh", version, osName, machineName), nil
128128
}
129129

130+
// DarwinArm64MinVersion represents the minimum Darwin version that supported arm64.
131+
const DarwinArm64MinVersion = "4.1.0"
132+
130133
// DarwinFallback Darwin arm64 was supported since 4.1.0, before 4.1.0, fall back to x86_64
131134
func DarwinFallback(machineName string, version string) (alterMachineName string) {
132135
// Do not use fallback for commits since they are likely newer than Bazel 4.1
@@ -139,7 +142,7 @@ func DarwinFallback(machineName string, version string) (alterMachineName string
139142
return machineName
140143
}
141144

142-
armSupportVer, _ := semver.NewVersion("4.1.0")
145+
armSupportVer, _ := semver.NewVersion(DarwinArm64MinVersion)
143146

144147
if machineName == "arm64" && v.LessThan(armSupportVer) {
145148
log.Printf("WARN: Fallback to x86_64 because arm64 is not supported on Apple Silicon until 4.1.0")

0 commit comments

Comments
 (0)