[ChromiumOS] Add a launcher for Bazel

CrOS intends to provide a Bazel executable for our users in
chromite/bin/bazel in our tree.  We'd like the "bazel" command in
depot_tools to call this executable.

This adds a new launcher to depot_tools which searches for that bazel
executable when located inside of a ChromiumOS checkout, and executes
it.  When located outside of a ChromiumOS checkout, this launcher
"disappears", searching elsewhere in the PATH for another Bazel
executable.

Since other teams using depot_tools may want to start using Bazel in
the future, this launcher is intended to have shared ownership: other
teams are welcome to come add their search functions to the launcher
if they require the same functionality as us.

Bug: b:253268519
Change-Id: I61f6383d8b69b9eea622f37277678f898cc7fd6b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/4718785
Reviewed-by: Shuhei Takahashi <nya@chromium.org>
Reviewed-by: Josip Sokcevic <sokcevic@chromium.org>
Reviewed-by: Aaron Massey <aaronmassey@google.com>
Commit-Queue: Jack Rosenthal <jrosenth@chromium.org>
Auto-Submit: Jack Rosenthal <jrosenth@chromium.org>
Commit-Queue: Josip Sokcevic <sokcevic@chromium.org>
changes/85/4718785/9
Jack Rosenthal 2 years ago committed by LUCI CQ
parent a8946f3d83
commit 6a505ad9ab

@ -28,6 +28,8 @@ per-file presubmit*.py=brucedawson@chromium.org
per-file pylint*=vapier@chromium.org
per-file bazel=file://CROS_OWNERS
per-file bazel.py=file://CROS_OWNERS
per-file cbuildbot=file://CROS_OWNERS
per-file cros=file://CROS_OWNERS
per-file *cros_python2*=file://CROS_OWNERS

@ -0,0 +1 @@
bazel.py

@ -0,0 +1,85 @@
#!/usr/bin/env vpython3
# Copyright 2023 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# [VPYTHON:BEGIN]
# python_version: "3.8"
# [VPYTHON:END]
"""Bazel launcher wrapper.
This script starts Bazel appropriate for the project you're working in. It's
currently used by ChromiumOS, but is intended for use and to be updated by any
depot_tools users who are using Bazel.
In the case this script is not able to detect which project you're working in,
it will fall back to using the next "bazel" executable in your PATH.
"""
import itertools
import os
from pathlib import Path
import shutil
import sys
from typing import List, Optional
def _find_bazel_cros() -> Optional[Path]:
"""Find the bazel launcher for ChromiumOS."""
cwd = Path.cwd()
for parent in itertools.chain([cwd], cwd.parents):
bazel_launcher = parent / "chromite" / "bin" / "bazel"
if bazel_launcher.exists():
return bazel_launcher
return None
def _find_next_bazel_in_path() -> Optional[Path]:
"""The fallback method: search the remainder of PATH for bazel."""
# Remove depot_tools from PATH if present.
depot_tools = Path(__file__).resolve().parent
path_env = os.environ.get("PATH", os.defpath)
search_paths = []
for path in path_env.split(os.pathsep):
if Path(path).resolve() != depot_tools:
search_paths.append(path)
new_path_env = os.pathsep.join(search_paths)
bazel = shutil.which("bazel", path=new_path_env)
if bazel:
return Path(bazel)
return None
# All functions used to search for Bazel (in order of search).
_SEARCH_FUNCTIONS = (
_find_bazel_cros,
_find_next_bazel_in_path,
)
_FIND_FAILURE_MSG = """\
ERROR: The depot_tools bazel launcher was unable to find an appropriate bazel
executable to use.
For ChromiumOS developers:
Make sure your current directory is inside a ChromiumOS checkout (e.g.,
~/chromiumos). If you're already in a ChromiumOS checkout, it may be because
you're working on a branch that's too old (i.e., prior to Bazel).
If you're not working on any of the above listed projects, this launcher assumes
that you have Bazel installed on your system somewhere else in PATH. Check that
it's actually installed."""
def main(argv: List[str]) -> int:
"""Main."""
for search_func in _SEARCH_FUNCTIONS:
bazel = search_func()
if bazel:
os.execv(bazel, [str(bazel), *argv])
print(_FIND_FAILURE_MSG, file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))

@ -1,5 +1,6 @@
per-file autoninja_test.py=brucedawson@chromium.org
per-file autoninja_test.py=tikuta@chromium.org
per-file bazel_test.py=file://CROS_OWNERS
per-file ninjalog_uploader_test.py=tikuta@chromium.org
per-file ninja_reclient_test.py=file://BUILD_OWNERS
per-file ninja_reclient_test.py=file://RECLIENT_OWNERS

@ -0,0 +1,94 @@
#!/usr/bin/env vpython3
# Copyright 2023 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# [VPYTHON:BEGIN]
# python_version: "3.8"
# [VPYTHON:END]
"""Tests for Bazel launcher."""
import os
from pathlib import Path
import site
import sys
import unittest
DEPOT_TOOLS_DIR = Path(__file__).resolve().parent.parent
site.addsitedir(DEPOT_TOOLS_DIR)
import bazel
from testing_support import trial_dir
class FindCrosUnittest(trial_dir.TestCase):
"""Test the _find_bazel_cros function."""
def setUp(self):
"""Create the checkout and chromite files."""
super().setUp()
self.checkout_dir = Path(self.root_dir) / "chromiumos"
self.chromite_dir = self.checkout_dir / "chromite"
self.launcher = self.chromite_dir / "bin" / "bazel"
self.launcher.parent.mkdir(exist_ok=True, parents=True)
self.launcher.write_bytes(b"")
self.launcher.chmod(0o775)
self.orig_dir = Path.cwd()
def tearDown(self):
os.chdir(self.orig_dir)
super().tearDown()
def test_at_checkout_base(self):
"""Test we find the launcher at the base of the checkout."""
os.chdir(self.checkout_dir)
self.assertEqual(bazel._find_bazel_cros(), self.launcher)
def test_in_checkout_subdir(self):
"""Test we find the launcher in a subdir of the checkout."""
os.chdir(self.chromite_dir)
self.assertEqual(bazel._find_bazel_cros(), self.launcher)
def test_out_of_checkout(self):
"""Test we don't find the launcher outside of the checkout."""
os.chdir(self.root_dir)
self.assertIsNone(bazel._find_bazel_cros())
class FindPathUnittest(trial_dir.TestCase):
"""Test the _find_next_bazel_in_path function."""
def setUp(self):
"""Create the checkout and chromite files."""
super().setUp()
self.bin_dir = Path(self.root_dir) / "bin"
self.bin_dir.mkdir(exist_ok=True, parents=True)
self.orig_path = os.environ.get("PATH", os.defpath)
# DEPOT_TOOLS_DIR is located twice in PATH for spice.
os.environ["PATH"] = os.pathsep.join([
str(DEPOT_TOOLS_DIR),
str(self.bin_dir),
str(DEPOT_TOOLS_DIR),
])
def tearDown(self):
"""Restore actions from setUp()."""
os.environ["PATH"] = self.orig_path
def test_not_in_path(self):
"""Test we don't find anything in PATH when not present."""
self.assertIsNone(bazel._find_next_bazel_in_path())
def test_in_path(self):
"""Test we find the next Bazel in PATH when present."""
if sys.platform == "win32":
launcher = self.bin_dir / "bazel.exe"
else:
launcher = self.bin_dir / "bazel"
launcher.write_bytes(b"")
launcher.chmod(0o755)
self.assertEqual(bazel._find_next_bazel_in_path(), launcher)
if __name__ == '__main__':
unittest.main()
Loading…
Cancel
Save