• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2024 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from __future__ import annotations
6
7import functools
8from typing import TYPE_CHECKING, Iterable, Optional, Tuple, Union
9
10from crossbench import path as pth
11
12if TYPE_CHECKING:
13  from crossbench.plt.base import Platform
14
15
16class BinaryNotFoundError(RuntimeError):
17
18  def __init__(self, binary: Binary, platform: Platform):
19    self.binary = binary
20    self.platform = platform
21    super().__init__(self._create_message())
22
23  def _create_message(self) -> str:
24    return (f"Could not find binary '{self.binary}' on {self.platform}. "
25            f"Please install {self.binary.name} or use the "
26            f"--bin-{self.binary.name} "
27            "command line flag to manually specify a path.")
28
29
30class UnsupportedPlatformError(BinaryNotFoundError):
31
32  def __init__(self, binary: Binary, platform: Platform, expected: str):
33    self.expected_platform_name: str = expected
34    super().__init__(binary, platform)
35
36  def _create_message(self) -> str:
37    return (f"Could not find binary '{self.binary}' on {self.platform}. "
38            f"Only supported on {self.expected_platform_name}")
39
40
41BinaryLookup = Union[pth.AnyPathLike, Iterable[pth.AnyPathLike]]
42
43
44class Binary:
45  """A binary abstraction for multiple platforms.
46  Use this implementation to define binaries that exist on multiple platforms.
47  For platform-specific binaries use subclasses of Binary."""
48
49  def __init__(self,
50               name: str,
51               default: Optional[BinaryLookup] = None,
52               posix: Optional[BinaryLookup] = None,
53               linux: Optional[BinaryLookup] = None,
54               android: Optional[BinaryLookup] = None,
55               macos: Optional[BinaryLookup] = None,
56               win: Optional[BinaryLookup] = None) -> None:
57    self._name = name
58    self._default = self._convert(default)
59    self._posix = self._convert(posix)
60    self._linux = self._convert(linux)
61    self._android = self._convert(android)
62    self._macos = self._convert(macos)
63    self._win = self._convert(win)
64    self._validate_win()
65    if not any((default, posix, linux, android, macos, win)):
66      raise ValueError("At least one platform binary must be provided")
67
68  def _convert(self,
69               paths: Optional[BinaryLookup] = None) -> Tuple[pth.AnyPath, ...]:
70    if paths is None:
71      return tuple()
72    if isinstance(paths, str):
73      path: str = paths
74      if not path:
75        raise ValueError("Got unexpected empty string as binary path")
76      paths = [path]
77    elif isinstance(paths, pth.AnyPath):
78      paths = [paths]
79    return tuple(pth.AnyPath(path) for path in paths)
80
81  def _validate_win(self) -> None:
82    for path in self._win:
83      if path.suffix != ".exe":
84        raise ValueError(f"Windows binary {path} should have '.exe' suffix")
85
86  @property
87  def name(self) -> str:
88    return self._name
89
90  def __str__(self) -> str:
91    return self._name
92
93  @functools.lru_cache(maxsize=None)  # pylint: disable=method-cache-max-size-none
94  def resolve_cached(self, platform: Platform) -> pth.AnyPath:
95    return self.resolve(platform)
96
97  def resolve(self, platform: Platform) -> pth.AnyPath:
98    self._validate_platform(platform)
99    for binary in self.platform_path(platform):
100      binary_path = platform.path(binary)
101      if result := platform.search_binary(binary_path):
102        return result
103    raise BinaryNotFoundError(self, platform)
104
105  def platform_path(self, platform: Platform) -> Tuple[pth.AnyPath, ...]:
106    if self._linux and platform.is_linux:
107      return self._linux
108    if self._android and platform.is_android:
109      return self._android
110    if self._macos and platform.is_macos:
111      return self._macos
112    if self._posix and platform.is_posix:
113      return self._posix
114    if platform.is_win:
115      if self._win:
116        return self._win
117      if self._default:
118        return self._win_default()
119    return self._default
120
121  def _win_default(self) -> Tuple[pth.AnyPath, ...]:
122    return tuple(
123        default if default.suffix == ".exe" else default.with_suffix(".exe")
124        for default in self._default)
125
126  def _validate_platform(self, platform: Platform) -> None:
127    pass
128
129
130class PosixBinary(Binary):
131
132  def __init__(self, name: pth.AnyPathLike):
133    super().__init__(pth.AnyPosixPath(name).name, posix=name)
134
135  def _validate_platform(self, platform: Platform) -> None:
136    if not platform.is_posix:
137      raise UnsupportedPlatformError(self, platform, "posix")
138
139
140class MacOsBinary(Binary):
141
142  def __init__(self, name: pth.AnyPathLike):
143    super().__init__(pth.AnyPosixPath(name).name, macos=name)
144
145  def _validate_platform(self, platform: Platform) -> None:
146    if not platform.is_macos:
147      raise UnsupportedPlatformError(self, platform, "macos")
148
149
150class LinuxBinary(Binary):
151
152  def __init__(self, name: pth.AnyPathLike):
153    super().__init__(pth.AnyPosixPath(name).name, linux=name)
154
155  def _validate_platform(self, platform: Platform) -> None:
156    if not platform.is_posix:
157      raise UnsupportedPlatformError(self, platform, "linux")
158
159
160class AndroidBinary(Binary):
161
162  def __init__(self, name: pth.AnyPathLike):
163    super().__init__(pth.AnyPosixPath(name).name, android=name)
164
165  def _validate_platform(self, platform: Platform) -> None:
166    if not platform.is_android:
167      raise UnsupportedPlatformError(self, platform, "android")
168
169
170class WinBinary(Binary):
171
172  def __init__(self, name: pth.AnyPathLike):
173    super().__init__(pth.AnyWindowsPath(name).name, win=name)
174
175  def _validate_platform(self, platform: Platform) -> None:
176    if not platform.is_win:
177      raise UnsupportedPlatformError(self, platform, "windows")
178
179
180class Binaries:
181  CPIO = LinuxBinary("cpio")
182  FFMPEG = Binary("ffmpeg", posix="ffmpeg")
183  GCERTSTATUS = Binary("gcertstatus", posix="gcertstatus")
184  GO = Binary("go", posix="go")
185  GSUTIL = Binary("gsutil", posix="gsutil")
186  LSCPU = LinuxBinary("lscpu")
187  MONTAGE = Binary("montage", posix="montage")
188  ON_AC_POWER = LinuxBinary("on_ac_power")
189  PERF = LinuxBinary("perf")
190  PPROF = LinuxBinary("pprof")
191  PYTHON3 = Binary("python3", default="python3", win="python3.exe")
192  RPM2CPIO = LinuxBinary("rpm2cpio")
193  SIMPLEPERF = AndroidBinary("simpleperf")
194  XCTRACE = MacOsBinary("xctrace")
195
196
197class Browsers:
198  SAFARI = MacOsBinary("Safari.app")
199  SAFARI_TECH_PREVIEW = MacOsBinary("Safari Technology Preview.app")
200  FIREFOX_STABLE = Binary(
201      "firefox stable",
202      macos="Firefox.app",
203      linux="firefox",
204      win="Mozilla Firefox/firefox.exe")
205  FIREFOX_DEV = Binary(
206      "firefox developer edition",
207      macos="Firefox Developer Edition.app",
208      linux="firefox-developer-edition",
209      win="Firefox Developer Edition/firefox.exe")
210  FIREFOX_NIGHTLY = Binary(
211      "Firefox nightly",
212      macos="Firefox Nightly.app",
213      linux=["firefox-nightly", "firefox-trunk"],
214      win="Firefox Nightly/firefox.exe")
215