blob: f46e864f22faa99e3f91cff3175da46b13ebd91c [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2018 The ChromiumOS Authors
Alex Kleinf4dc4f52018-12-05 13:55:12 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Alex Kleinf9859972019-03-14 17:11:42 -06005"""Compile the Build API's proto.
6
7Install proto using CIPD to ensure a consistent protoc version.
8"""
Alex Kleinf4dc4f52018-12-05 13:55:12 -07009
Alex Klein098f7982021-03-01 13:15:29 -070010import enum
Chris McDonald1672ddb2021-07-21 11:48:23 -060011import logging
Alex Kleinb382e4b2022-05-23 16:29:19 -060012from pathlib import Path
Sean McAllister6a5eaa02021-05-26 10:47:14 -060013import tempfile
Alex Klein177bb942022-05-24 13:32:27 -060014from typing import Iterable, Optional
Alex Kleinf4dc4f52018-12-05 13:55:12 -070015
Alex Kleinb382e4b2022-05-23 16:29:19 -060016from chromite.lib import cipd
Alex Kleinf4dc4f52018-12-05 13:55:12 -070017from chromite.lib import commandline
Alex Kleinc33c1912019-02-15 10:29:13 -070018from chromite.lib import constants
Alex Kleinf4dc4f52018-12-05 13:55:12 -070019from chromite.lib import cros_build_lib
Sean McAllister6a5eaa02021-05-26 10:47:14 -060020from chromite.lib import git
Alex Kleinf9859972019-03-14 17:11:42 -060021from chromite.lib import osutils
22
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040023
Alex Klein098f7982021-03-01 13:15:29 -070024# Chromite's protobuf library version (third_party/google/protobuf).
Trent Apted5a2038f2023-07-26 15:23:46 +100025PROTOC_VERSION = "21.9"
Alex Kleinf9859972019-03-14 17:11:42 -060026
Trent Apted5a2038f2023-07-26 15:23:46 +100027# Protobuf dropped the major version number after 3.20, jumping to 21.0. But
28# some places (e.g., in protobuf/__init__.py) refer to this as 4.21.0.
29PROTOC_MAJOR_VERSION = "4"
30
31_CIPD_PACKAGE = "infra/3pp/tools/protoc/linux-amd64"
32_CIPD_PACKAGE_VERSION = f"version:2@{PROTOC_VERSION}"
Alex Kleinb382e4b2022-05-23 16:29:19 -060033
Alex Kleinf9859972019-03-14 17:11:42 -060034
Alex Klein5534f992019-09-16 16:31:23 -060035class Error(Exception):
Alex Klein1699fab2022-09-08 08:46:06 -060036 """Base error class for the module."""
Alex Klein5534f992019-09-16 16:31:23 -060037
38
39class GenerationError(Error):
Alex Klein1699fab2022-09-08 08:46:06 -060040 """A failure we can't recover from."""
Alex Klein5534f992019-09-16 16:31:23 -060041
42
Alex Klein098f7982021-03-01 13:15:29 -070043@enum.unique
44class ProtocVersion(enum.Enum):
Alex Klein1699fab2022-09-08 08:46:06 -060045 """Enum for possible protoc versions."""
Alex Klein098f7982021-03-01 13:15:29 -070046
Alex Klein1699fab2022-09-08 08:46:06 -060047 # The SDK version of the bindings use the protoc in the SDK, and so is
48 # compatible with the protobuf library in the SDK, i.e. the one installed
49 # via the ebuild.
50 SDK = enum.auto()
51 # The Chromite version of the bindings uses a protoc binary downloaded from
52 # CIPD that matches the version of the protobuf library in
53 # chromite/third_party/google/protobuf.
54 CHROMITE = enum.auto()
Trent Apted8f5782a2023-07-11 10:25:02 +100055 # Type annotations for the chromite bindings.
56 CHROMITE_PYI = enum.auto()
Alex Klein177bb942022-05-24 13:32:27 -060057
Alex Klein1699fab2022-09-08 08:46:06 -060058 def get_gen_dir(self) -> Path:
59 """Get the chromite/api directory path."""
60 if self is ProtocVersion.SDK:
Mike Frysingera69df982023-03-21 16:52:27 -040061 return constants.CHROMITE_DIR / "api" / "gen_sdk"
Trent Apted8f5782a2023-07-11 10:25:02 +100062 return constants.CHROMITE_DIR / "api" / "gen"
Alex Klein177bb942022-05-24 13:32:27 -060063
Alex Klein1699fab2022-09-08 08:46:06 -060064 def get_proto_dir(self) -> Path:
65 """Get the proto directory for the target protoc."""
Mike Frysingera69df982023-03-21 16:52:27 -040066 return constants.CHROMITE_DIR / "infra" / "proto"
Alex Klein1699fab2022-09-08 08:46:06 -060067
68 def get_protoc_command(self, cipd_root: Optional[Path] = None) -> Path:
69 """Get protoc command path."""
Alex Klein1699fab2022-09-08 08:46:06 -060070 if self is ProtocVersion.SDK:
71 return Path("protoc")
Trent Apted8f5782a2023-07-11 10:25:02 +100072 assert cipd_root
73 return cipd_root / "bin" / "protoc"
74
75 def get_suffix(self) -> str:
76 """Get the file suffix of generated output."""
77 return "pyi" if self is ProtocVersion.CHROMITE_PYI else "py"
Alex Kleindfad94c2022-05-23 16:59:47 -060078
Alex Klein098f7982021-03-01 13:15:29 -070079
Alex Klein851f4ee2022-03-29 16:03:45 -060080@enum.unique
81class SubdirectorySet(enum.Enum):
Alex Klein1699fab2022-09-08 08:46:06 -060082 """Enum for the subsets of the proto to compile."""
Alex Klein851f4ee2022-03-29 16:03:45 -060083
Alex Klein1699fab2022-09-08 08:46:06 -060084 ALL = enum.auto()
85 DEFAULT = enum.auto()
Alex Klein851f4ee2022-03-29 16:03:45 -060086
Alex Klein1699fab2022-09-08 08:46:06 -060087 def get_source_dirs(
88 self, source: Path, chromeos_config_path: Path
89 ) -> Iterable[Path]:
90 """Get the directories for the given subdirectory set."""
91 if self is self.ALL:
92 return [
93 source,
94 chromeos_config_path / "proto" / "chromiumos",
95 ]
96
97 subdirs = [
98 source / "analysis_service",
99 source / "chromite",
100 source / "chromiumos",
101 source / "config",
102 source / "test_platform",
103 source / "device",
104 chromeos_config_path / "proto" / "chromiumos",
105 ]
106 return subdirs
Alex Klein851f4ee2022-03-29 16:03:45 -0600107
108
Trent Apteddb63e142023-09-26 15:46:09 +1000109def check_upstream_changes(repo: Path) -> None:
110 """Ensures the checked-out branch at `repo` includes the upstream HEAD."""
111 branch = git.GetCurrentBranch(repo)
112 if branch:
113 upstream = git.GetTrackingBranchViaGitConfig(
114 repo, branch, for_checkout=False
115 )
116 if not upstream:
117 cros_build_lib.Die("Failed to get upstream for %s.", repo)
118 else:
119 # Detached head.
120 branch = "HEAD"
121 upstream = git.RemoteRef("cros", "refs/heads/main")
122 remote_head = git.RunGit(
123 repo,
124 ["ls-remote", upstream.remote, upstream.ref],
125 ).stdout.split(maxsplit=1)[0]
126 logging.notice(
127 "Ensuring proto dir contains %s from %s",
128 upstream.ref,
129 upstream.remote,
130 )
131 branches = git.RunGit(
132 repo,
133 ["branch", "--contains", remote_head, branch],
134 print_cmd=True,
135 check=False,
136 )
137 # Git emits the branch name if the commit is in the history. Otherwise, the
138 # commit is either not found (git exits with an error - 129), or missing
139 # from the branch. In either case, there is no output to stdout.
140 if not branches.stdout:
141 cros_build_lib.Die(
142 "The checked-out branch (%s) at %s is missing the remote head.\n"
143 "Please repo sync (and rebase), or skip this check by passing"
144 " --no-check-upstream-proto-changes-included.",
145 branch,
146 repo,
147 )
148
149
Alex Kleindfad94c2022-05-23 16:59:47 -0600150def InstallProtoc(protoc_version: ProtocVersion) -> Path:
Alex Klein1699fab2022-09-08 08:46:06 -0600151 """Install protoc from CIPD."""
Trent Apted8f5782a2023-07-11 10:25:02 +1000152 if protoc_version is ProtocVersion.SDK:
Alex Klein1699fab2022-09-08 08:46:06 -0600153 cipd_root = None
154 else:
155 cipd_root = Path(
156 cipd.InstallPackage(
157 cipd.GetCIPDFromCache(), _CIPD_PACKAGE, _CIPD_PACKAGE_VERSION
158 )
159 )
160 return protoc_version.get_protoc_command(cipd_root)
Alex Klein5534f992019-09-16 16:31:23 -0600161
Alex Kleinf9859972019-03-14 17:11:42 -0600162
Trent Apted8f5782a2023-07-11 10:25:02 +1000163def _CleanTargetDirectory(directory: Path, protoc_version: ProtocVersion):
Alex Klein1699fab2022-09-08 08:46:06 -0600164 """Remove any existing generated files in the directory.
Alex Kleinf9859972019-03-14 17:11:42 -0600165
Alex Klein1699fab2022-09-08 08:46:06 -0600166 This clean only removes the generated files to avoid accidentally destroying
167 __init__.py customizations down the line. That will leave otherwise empty
168 directories in place if things get moved. Neither case is relevant at the
169 time of writing, but lingering empty directories seemed better than
170 diagnosing accidental __init__.py changes.
Alex Kleinf9859972019-03-14 17:11:42 -0600171
Alex Klein1699fab2022-09-08 08:46:06 -0600172 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600173 directory: Path to be cleaned up.
Trent Apted8f5782a2023-07-11 10:25:02 +1000174 protoc_version: The type of generated files to be cleaned.
Alex Klein1699fab2022-09-08 08:46:06 -0600175 """
176 logging.info("Cleaning old files from %s.", directory)
Trent Apted8f5782a2023-07-11 10:25:02 +1000177 for current in directory.rglob(f"*_pb2.{protoc_version.get_suffix()}"):
Alex Klein1699fab2022-09-08 08:46:06 -0600178 # Remove old generated files.
179 current.unlink()
Trent Apted8f5782a2023-07-11 10:25:02 +1000180
181 # Note the generator does not currently make __init__.pyi files but, if it
182 # did, we'd want them to be cleaned up here.
183 for current in directory.rglob(f"__init__.{protoc_version.get_suffix()}"):
Alex Klein1699fab2022-09-08 08:46:06 -0600184 # Remove empty init files to clean up otherwise empty directories.
185 if not current.stat().st_size:
186 current.unlink()
Alex Kleinf9859972019-03-14 17:11:42 -0600187
Alex Klein5534f992019-09-16 16:31:23 -0600188
Alex Klein1699fab2022-09-08 08:46:06 -0600189def _GenerateFiles(
190 source: Path,
191 output: Path,
192 protoc_version: ProtocVersion,
193 dir_subset: SubdirectorySet,
194 protoc_bin_path: Path,
195):
196 """Generate the proto files from the |source| tree into |output|.
Alex Kleinf9859972019-03-14 17:11:42 -0600197
Alex Klein1699fab2022-09-08 08:46:06 -0600198 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600199 source: Path to the proto source root directory.
200 output: Path to the output root directory.
201 protoc_version: Which protoc to use.
202 dir_subset: The subset of the proto to compile.
203 protoc_bin_path: The protoc command to use.
Alex Klein1699fab2022-09-08 08:46:06 -0600204 """
Trent Aptedc1618e72023-09-25 10:30:14 +1000205 logging.info("Generating %s files to %s.", protoc_version, output)
Alex Klein1699fab2022-09-08 08:46:06 -0600206 osutils.SafeMakedirs(output)
Alex Klein098f7982021-03-01 13:15:29 -0700207
Alex Klein1699fab2022-09-08 08:46:06 -0600208 targets = []
Alex Kleinf9859972019-03-14 17:11:42 -0600209
Mike Frysinger903c1f72023-08-08 14:05:10 -0400210 chromeos_config_path = constants.SOURCE_ROOT / "src" / "config"
Alex Klein098f7982021-03-01 13:15:29 -0700211
Alex Klein1699fab2022-09-08 08:46:06 -0600212 with tempfile.TemporaryDirectory() as tempdir:
213 if not chromeos_config_path.exists():
214 chromeos_config_path = Path(tempdir) / "config"
Alex Klein5534f992019-09-16 16:31:23 -0600215
Alex Klein1699fab2022-09-08 08:46:06 -0600216 logging.info("Creating shallow clone of chromiumos/config")
217 git.Clone(
218 chromeos_config_path,
219 "%s/chromiumos/config" % constants.EXTERNAL_GOB_URL,
220 depth=1,
221 )
Sean McAllister6a5eaa02021-05-26 10:47:14 -0600222
Alex Klein1699fab2022-09-08 08:46:06 -0600223 for src_dir in dir_subset.get_source_dirs(source, chromeos_config_path):
224 targets.extend(list(src_dir.rglob("*.proto")))
Andrew Lamb59ed32e2021-07-26 15:14:37 -0600225
Trent Apted8f5782a2023-07-11 10:25:02 +1000226 output_type = (
227 "pyi" if protoc_version is ProtocVersion.CHROMITE_PYI else "python"
228 )
Alex Klein1699fab2022-09-08 08:46:06 -0600229 cmd = [
230 protoc_bin_path,
231 "-I",
232 chromeos_config_path / "proto",
Trent Apted8f5782a2023-07-11 10:25:02 +1000233 f"--{output_type}_out",
Alex Klein1699fab2022-09-08 08:46:06 -0600234 output,
235 "--proto_path",
236 source,
237 ]
238 cmd.extend(targets)
Sean McAllister6a5eaa02021-05-26 10:47:14 -0600239
Alex Klein1699fab2022-09-08 08:46:06 -0600240 result = cros_build_lib.dbg_run(
241 cmd,
242 cwd=source,
243 check=False,
244 enter_chroot=protoc_version is ProtocVersion.SDK,
245 )
Sean McAllister6a5eaa02021-05-26 10:47:14 -0600246
Alex Klein1699fab2022-09-08 08:46:06 -0600247 if result.returncode:
248 raise GenerationError(
249 "Error compiling the proto. See the output for a " "message."
250 )
Alex Kleinf9859972019-03-14 17:11:42 -0600251
252
Trent Apted8f5782a2023-07-11 10:25:02 +1000253def _InstallMissingInits(directory: Path, protoc_version: ProtocVersion):
Alex Klein54c891a2023-01-24 10:45:41 -0700254 """Add missing __init__.py files in the generated protobuf folders."""
Trent Apted8f5782a2023-07-11 10:25:02 +1000255 if protoc_version is ProtocVersion.CHROMITE_PYI:
256 # For pyi, rely on module markers left behind by CHROMITE flows to avoid
257 # additional clutter.
258 return
259
Alex Klein1699fab2022-09-08 08:46:06 -0600260 logging.info("Adding missing __init__.py files in %s.", directory)
261 # glob ** returns only directories.
262 for current in directory.rglob("**"):
263 (current / "__init__.py").touch()
Alex Kleinf9859972019-03-14 17:11:42 -0600264
265
Alex Klein177bb942022-05-24 13:32:27 -0600266def _PostprocessFiles(directory: Path, protoc_version: ProtocVersion):
Alex Klein1699fab2022-09-08 08:46:06 -0600267 """Do postprocessing on the generated files.
Alex Kleinf9859972019-03-14 17:11:42 -0600268
Alex Klein1699fab2022-09-08 08:46:06 -0600269 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600270 directory: The root directory containing the generated files that are
271 to be processed.
272 protoc_version: Which protoc is being used to generate the files.
Alex Klein1699fab2022-09-08 08:46:06 -0600273 """
274 logging.info("Postprocessing: Fix imports in %s.", directory)
275 # We are using a negative address here (the /address/! portion of the sed
276 # command) to make sure we don't change any imports from protobuf itself.
277 address = "^from google.protobuf"
278 # Find: 'from x import y_pb2 as x_dot_y_pb2'.
279 # "\(^google.protobuf[^ ]*\)" matches the module we're importing from.
280 # - \( and \) are for groups in sed.
281 # - ^google.protobuf prevents changing the import for protobuf's files.
Alex Klein54c891a2023-01-24 10:45:41 -0700282 # - [^ ] = Not a space. The [:space:] character set is too broad, but
283 # would technically work too.
Alex Klein1699fab2022-09-08 08:46:06 -0600284 find = r"^from \([^ ]*\) import \([^ ]*\)_pb2 as \([^ ]*\)$"
285 # Substitute: 'from chromite.api.gen[_sdk].x import y_pb2 as x_dot_y_pb2'.
286 if protoc_version is ProtocVersion.SDK:
287 sub = "from chromite.api.gen_sdk.\\1 import \\2_pb2 as \\3"
288 else:
289 sub = "from chromite.api.gen.\\1 import \\2_pb2 as \\3"
Alex Klein098f7982021-03-01 13:15:29 -0700290
Trent Aptedc1618e72023-09-25 10:30:14 +1000291 seds = [f"/{address}/!s/{find}/{sub}/g"]
Trent Apted8f5782a2023-07-11 10:25:02 +1000292
Alex Klein1699fab2022-09-08 08:46:06 -0600293 if protoc_version is ProtocVersion.CHROMITE:
294 # We also need to change the google.protobuf imports to point directly
295 # at the chromite.third_party version of the library.
296 # The SDK version of the proto is meant to be used with the protobuf
297 # libraries installed in the SDK, so leave those as google.protobuf.
Trent Apted8f5782a2023-07-11 10:25:02 +1000298 # For CHROMITE_PYI, types for the protobuf imports come from type stubs,
299 # which won't map if the imports are renamed to third_party.
Alex Klein1699fab2022-09-08 08:46:06 -0600300 g_p_address = "^from google.protobuf"
301 g_p_find = r"from \([^ ]*\) import \(.*\)$"
302 g_p_sub = "from chromite.third_party.\\1 import \\2"
Trent Aptedc1618e72023-09-25 10:30:14 +1000303 seds.append(f"/{g_p_address}/s/{g_p_find}/{g_p_sub}/")
Alex Klein1699fab2022-09-08 08:46:06 -0600304
Trent Apted8f5782a2023-07-11 10:25:02 +1000305 if protoc_version is ProtocVersion.CHROMITE_PYI:
306 # Workaround https://github.com/protocolbuffers/protobuf/issues/11402
307 # until cl/560557754 in in the protoc version used.
Trent Aptedc1618e72023-09-25 10:30:14 +1000308 untyped_empty_slot_list = "s/\\(^ *__slots__ = \\)\\[]/\\1()/"
Trent Apted8f5782a2023-07-11 10:25:02 +1000309
310 # Suppress errors on GRPC options field numbers using ClassVar outside
311 # of a class body. See b/297782342.
Trent Aptedc1618e72023-09-25 10:30:14 +1000312 ignore_class_var_in_file_scope = (
313 "s/^[A-Z_]*: _ClassVar\\[int]/& # type: ignore/"
314 )
Trent Apted8f5782a2023-07-11 10:25:02 +1000315 seds.extend((untyped_empty_slot_list, ignore_class_var_in_file_scope))
316
317 pb2 = list(directory.rglob(f"*_pb2.{protoc_version.get_suffix()}"))
Alex Klein1699fab2022-09-08 08:46:06 -0600318 if pb2:
Trent Aptedc1618e72023-09-25 10:30:14 +1000319 cros_build_lib.dbg_run(["sed", "-i", ";".join(seds)] + pb2)
Alex Kleinf9859972019-03-14 17:11:42 -0600320
321
Alex Klein1699fab2022-09-08 08:46:06 -0600322def CompileProto(
323 protoc_version: ProtocVersion,
324 output: Optional[Path] = None,
325 dir_subset: SubdirectorySet = SubdirectorySet.DEFAULT,
326 postprocess: bool = True,
327):
328 """Compile the Build API protobuf files.
Alex Kleinf9859972019-03-14 17:11:42 -0600329
Alex Kleinb6d52022022-10-18 08:55:06 -0600330 By default, this will compile from infra/proto/src to api/gen. The output
Alex Klein1699fab2022-09-08 08:46:06 -0600331 directory may be changed, but the imports will always be treated as if it is
332 in the default location.
Alex Kleinf9859972019-03-14 17:11:42 -0600333
Alex Klein1699fab2022-09-08 08:46:06 -0600334 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600335 output: The output directory.
Alex Kleinb6d52022022-10-18 08:55:06 -0600336 protoc_version: Which protoc to use for the compilation.
Alex Kleina0442682022-10-10 13:47:38 -0600337 dir_subset: What proto to compile.
338 postprocess: Whether to run the postprocess step.
Alex Klein1699fab2022-09-08 08:46:06 -0600339 """
Alex Klein1699fab2022-09-08 08:46:06 -0600340 source = protoc_version.get_proto_dir() / "src"
341 if not output:
342 output = protoc_version.get_gen_dir()
Alex Kleinf9859972019-03-14 17:11:42 -0600343
Alex Klein1699fab2022-09-08 08:46:06 -0600344 protoc_bin_path = InstallProtoc(protoc_version)
Trent Apted8f5782a2023-07-11 10:25:02 +1000345 _CleanTargetDirectory(output, protoc_version)
Alex Klein1699fab2022-09-08 08:46:06 -0600346 _GenerateFiles(source, output, protoc_version, dir_subset, protoc_bin_path)
Trent Apted8f5782a2023-07-11 10:25:02 +1000347 _InstallMissingInits(output, protoc_version)
Alex Klein1699fab2022-09-08 08:46:06 -0600348 if postprocess:
349 _PostprocessFiles(output, protoc_version)
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700350
351
352def GetParser():
Alex Klein1699fab2022-09-08 08:46:06 -0600353 """Build the argument parser."""
354 parser = commandline.ArgumentParser(description=__doc__)
Trent Apteddb63e142023-09-26 15:46:09 +1000355 parser.add_bool_argument(
356 "--check-upstream-proto-changes-included",
357 True,
358 "Check whether the checked-out proto branch includes changes"
359 " from refs/heads/main on the remote.",
360 "Skip the check that validates whether upstream proto changes"
361 " are included in the current branch.",
362 )
Alex Klein1699fab2022-09-08 08:46:06 -0600363 standard_group = parser.add_argument_group(
364 "Committed Bindings",
365 description="Options for generating the bindings in chromite/api/.",
366 )
367 standard_group.add_argument(
368 "--chromite",
369 dest="protoc_version",
370 action="append_const",
371 const=ProtocVersion.CHROMITE,
Alex Klein54c891a2023-01-24 10:45:41 -0700372 help="Generate only the chromite bindings. Generates all by default. "
373 "The chromite bindings are compatible with the version of protobuf "
374 "in chromite/third_party.",
Alex Klein1699fab2022-09-08 08:46:06 -0600375 )
376 standard_group.add_argument(
Trent Apted8f5782a2023-07-11 10:25:02 +1000377 "--pyi",
378 dest="protoc_version",
379 action="append_const",
380 const=ProtocVersion.CHROMITE_PYI,
381 help="Generate only the pyi type annotations.",
382 )
383 standard_group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600384 "--sdk",
385 dest="protoc_version",
386 action="append_const",
387 const=ProtocVersion.SDK,
Alex Klein54c891a2023-01-24 10:45:41 -0700388 help="Generate only the SDK bindings. Generates all by default. The "
389 "SDK bindings are compiled by protoc in the SDK, and is compatible "
Alex Klein1699fab2022-09-08 08:46:06 -0600390 "with the version of protobuf in the SDK (i.e. the one installed by "
391 "the ebuild).",
392 )
Alex Klein098f7982021-03-01 13:15:29 -0700393
Alex Klein1699fab2022-09-08 08:46:06 -0600394 dest_group = parser.add_argument_group(
395 "Out of Tree Bindings",
396 description="Options for generating bindings in a custom location.",
397 )
398 dest_group.add_argument(
399 "--destination",
400 type="path",
401 help="A directory where a single version of the proto should be "
402 "generated. When not given, the proto generates in all default "
403 "locations instead.",
404 )
405 dest_group.add_argument(
406 "--dest-sdk",
407 action="store_const",
408 dest="dest_protoc",
409 default=ProtocVersion.CHROMITE,
410 const=ProtocVersion.SDK,
Alex Klein54c891a2023-01-24 10:45:41 -0700411 help="Generate the SDK version of the protos in --destination instead "
412 "of the chromite version.",
Alex Klein1699fab2022-09-08 08:46:06 -0600413 )
414 dest_group.add_argument(
415 "--all-proto",
416 action="store_const",
417 dest="dir_subset",
418 default=SubdirectorySet.DEFAULT,
419 const=SubdirectorySet.ALL,
420 help="Compile ALL proto instead of just the subset needed for the API. "
421 "Only considered when generating out of tree bindings.",
422 )
423 dest_group.add_argument(
424 "--skip-postprocessing",
425 action="store_false",
426 dest="postprocess",
427 default=True,
428 help="Skip postprocessing files.",
429 )
430 return parser
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700431
432
433def _ParseArguments(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600434 """Parse and validate arguments."""
435 parser = GetParser()
436 opts = parser.parse_args(argv)
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700437
Alex Klein1699fab2022-09-08 08:46:06 -0600438 if not opts.protoc_version:
Trent Apted8f5782a2023-07-11 10:25:02 +1000439 opts.protoc_version = [
440 ProtocVersion.CHROMITE,
441 ProtocVersion.SDK,
442 ProtocVersion.CHROMITE_PYI,
443 ]
Alex Klein098f7982021-03-01 13:15:29 -0700444
Alex Klein1699fab2022-09-08 08:46:06 -0600445 if opts.destination:
446 opts.destination = Path(opts.destination)
Alex Klein177bb942022-05-24 13:32:27 -0600447
Alex Klein1699fab2022-09-08 08:46:06 -0600448 opts.Freeze()
449 return opts
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700450
451
452def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600453 opts = _ParseArguments(argv)
Alex Kleinf4dc4f52018-12-05 13:55:12 -0700454
Alex Klein1699fab2022-09-08 08:46:06 -0600455 if opts.destination:
456 # Destination set, only compile a single version in the destination.
457 try:
458 CompileProto(
459 protoc_version=opts.dest_protoc,
460 output=opts.destination,
461 dir_subset=opts.dir_subset,
462 postprocess=opts.postprocess,
463 )
464 except Error as e:
Trent Apted8f5782a2023-07-11 10:25:02 +1000465 cros_build_lib.Die("Error compiling bindings to destination: %s", e)
Alex Klein1699fab2022-09-08 08:46:06 -0600466 else:
467 return 0
Alex Klein098f7982021-03-01 13:15:29 -0700468
Alex Klein1699fab2022-09-08 08:46:06 -0600469 if ProtocVersion.CHROMITE in opts.protoc_version:
Trent Apteddb63e142023-09-26 15:46:09 +1000470 # Validate here to avoid checking again inside the chroot.
471 if opts.check_upstream_proto_changes_included:
472 check_upstream_changes(ProtocVersion.CHROMITE.get_proto_dir())
473
Alex Klein1699fab2022-09-08 08:46:06 -0600474 # Compile the chromite bindings.
475 try:
476 CompileProto(protoc_version=ProtocVersion.CHROMITE)
477 except Error as e:
Trent Apted8f5782a2023-07-11 10:25:02 +1000478 cros_build_lib.Die("Error compiling chromite bindings: %s", e)
479
480 if ProtocVersion.CHROMITE_PYI in opts.protoc_version:
481 try:
482 CompileProto(protoc_version=ProtocVersion.CHROMITE_PYI)
483 except Error as e:
484 cros_build_lib.Die("Error compiling type annotations: %s", e)
Alex Klein098f7982021-03-01 13:15:29 -0700485
Alex Klein1699fab2022-09-08 08:46:06 -0600486 if ProtocVersion.SDK in opts.protoc_version:
487 # Compile the SDK bindings.
488 if not cros_build_lib.IsInsideChroot():
Alex Kleinb6d52022022-10-18 08:55:06 -0600489 # Rerun inside the SDK instead of trying to map all the paths.
Alex Klein1699fab2022-09-08 08:46:06 -0600490 cmd = [
491 (
Mike Frysinger83e7ff22023-08-07 21:42:28 -0400492 constants.CHROOT_SOURCE_ROOT
Alex Klein1699fab2022-09-08 08:46:06 -0600493 / "chromite"
494 / "api"
495 / "compile_build_api_proto"
496 ),
497 "--sdk",
498 ]
499 result = cros_build_lib.dbg_run(cmd, enter_chroot=True, check=False)
500 return result.returncode
501 else:
502 try:
503 CompileProto(protoc_version=ProtocVersion.SDK)
504 except Error as e:
Trent Apted8f5782a2023-07-11 10:25:02 +1000505 cros_build_lib.Die("Error compiling SDK bindings: %s", e)