[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
parent
a8946f3d83
commit
6a505ad9ab
@ -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:]))
|
@ -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…
Reference in New Issue