blob: 7235b1994dc46992b2ec6d09e4fb75ad6a2c01fd [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2019 The ChromiumOS Authors
Alex Kleinda35fcf2019-03-07 16:01:15 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Sysroot controller."""
6
Chris McDonald1672ddb2021-07-21 11:48:23 -06007import logging
Michael Mortensen4ccfb082020-01-22 16:24:03 -07008import os
9
Alex Klein8cb365a2019-05-15 16:24:53 -060010from chromite.api import controller
Alex Klein076841b2019-08-29 15:19:39 -060011from chromite.api import faux
Alex Klein2b236722019-06-19 15:44:26 -060012from chromite.api import validate
Alex Kleina9d500b2019-04-22 15:37:51 -060013from chromite.api.controller import controller_util
Chris McDonald1672ddb2021-07-21 11:48:23 -060014from chromite.api.gen.chromiumos import common_pb2
Will Bradley7e5b8c12019-07-30 12:44:15 -060015from chromite.api.metrics import deserialize_metrics_log
LaMont Jonesfeffd1b2020-08-05 18:24:59 -060016from chromite.lib import binpkg
George Engelbrechtc9a8e812021-06-16 18:14:17 -060017from chromite.lib import build_target_lib
18from chromite.lib import chroot_lib
Alex Kleinda35fcf2019-03-07 16:01:15 -070019from chromite.lib import cros_build_lib
Michael Mortensen798ee192020-01-17 13:04:43 -070020from chromite.lib import goma_lib
Alex Kleinaef41942022-04-19 14:13:17 -060021from chromite.lib import metrics_lib
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -070022from chromite.lib import osutils
Alex Kleina9d64602019-05-17 14:55:37 -060023from chromite.lib import portage_util
Alex Kleinda35fcf2019-03-07 16:01:15 -070024from chromite.lib import sysroot_lib
25from chromite.service import sysroot
26
Chris McDonald1672ddb2021-07-21 11:48:23 -060027
Alex Klein1699fab2022-09-08 08:46:06 -060028_ACCEPTED_LICENSES = "@CHROMEOS"
Alex Kleinda35fcf2019-03-07 16:01:15 -070029
Alex Kleineaa48532022-07-28 08:51:32 -060030DEFAULT_BACKTRACK = 30
31
Alex Kleind4e1e422019-03-18 16:00:41 -060032
Yoshiki Iguchia43704b2021-12-16 13:31:48 +090033def _GetGomaLogDirectory():
Alex Klein1699fab2022-09-08 08:46:06 -060034 """Get goma's log directory based on the env variables.
Yoshiki Iguchia43704b2021-12-16 13:31:48 +090035
Alex Klein1699fab2022-09-08 08:46:06 -060036 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -060037 a string of a directory name where goma's log may exist, or None if no
38 potential directories exist.
Alex Klein1699fab2022-09-08 08:46:06 -060039 """
40 # TODO(crbug.com/1045001): Replace environment variable with query to
41 # goma object after goma refactoring allows this.
42 candidates = [
43 "GLOG_log_dir",
44 "GOOGLE_LOG_DIR",
45 "TEST_TMPDIR",
46 "TMPDIR",
47 "TMP",
48 ]
49 for candidate in candidates:
50 value = os.environ.get(candidate)
51 if value and os.path.isdir(value):
52 return value
Yoshiki Iguchia43704b2021-12-16 13:31:48 +090053
Alex Klein1699fab2022-09-08 08:46:06 -060054 # "/tmp" will always exist.
55 return "/tmp"
Yoshiki Iguchia43704b2021-12-16 13:31:48 +090056
57
George Engelbrechtc9a8e812021-06-16 18:14:17 -060058def ExampleGetResponse():
Alex Klein1699fab2022-09-08 08:46:06 -060059 """Give an example response to assemble upstream in caller artifacts."""
60 uabs = common_pb2.UploadedArtifactsByService
61 cabs = common_pb2.ArtifactsByService
62 return uabs.Sysroot(
63 artifacts=[
64 uabs.Sysroot.ArtifactPaths(
65 artifact_type=cabs.Sysroot.ArtifactType.SIMPLE_CHROME_SYSROOT,
66 paths=[
67 common_pb2.Path(
68 path="/tmp/sysroot_chromeos-base_chromeos-chrome.tar.xz",
69 location=common_pb2.Path.OUTSIDE,
70 )
71 ],
72 ),
73 uabs.Sysroot.ArtifactPaths(
74 artifact_type=cabs.Sysroot.ArtifactType.DEBUG_SYMBOLS,
75 paths=[
76 common_pb2.Path(
77 path="/tmp/debug.tgz", location=common_pb2.Path.OUTSIDE
78 )
79 ],
80 ),
81 uabs.Sysroot.ArtifactPaths(
82 artifact_type=cabs.Sysroot.ArtifactType.BREAKPAD_DEBUG_SYMBOLS,
83 paths=[
84 common_pb2.Path(
85 path="/tmp/debug_breakpad.tar.xz",
86 location=common_pb2.Path.OUTSIDE,
87 )
88 ],
89 ),
90 ]
91 )
George Engelbrechtc9a8e812021-06-16 18:14:17 -060092
93
Alex Klein1699fab2022-09-08 08:46:06 -060094def GetArtifacts(
95 in_proto: common_pb2.ArtifactsByService.Sysroot,
96 chroot: chroot_lib.Chroot,
97 sysroot_class: sysroot_lib.Sysroot,
98 build_target: build_target_lib.BuildTarget,
99 output_dir: str,
100) -> list:
101 """Builds and copies sysroot artifacts to specified output_dir.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600102
Alex Klein1699fab2022-09-08 08:46:06 -0600103 Copies sysroot artifacts to output_dir, returning a list of (output_dir: str)
104 paths to the desired files.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600105
Alex Klein1699fab2022-09-08 08:46:06 -0600106 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600107 in_proto: Proto request defining reqs.
108 chroot: The chroot class used for these artifacts.
109 sysroot_class: The sysroot class used for these artifacts.
110 build_target: The build target used for these artifacts.
111 output_dir: The path to write artifacts to.
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600112
Alex Klein1699fab2022-09-08 08:46:06 -0600113 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -0600114 A list of dictionary mappings of ArtifactType to list of paths.
Alex Klein1699fab2022-09-08 08:46:06 -0600115 """
116 generated = []
117 artifact_types = {
118 in_proto.ArtifactType.SIMPLE_CHROME_SYSROOT: sysroot.CreateSimpleChromeSysroot,
119 in_proto.ArtifactType.CHROME_EBUILD_ENV: sysroot.CreateChromeEbuildEnv,
120 in_proto.ArtifactType.BREAKPAD_DEBUG_SYMBOLS: sysroot.BundleBreakpadSymbols,
121 in_proto.ArtifactType.DEBUG_SYMBOLS: sysroot.BundleDebugSymbols,
Jack Neus11b6ebd2022-10-21 17:54:36 +0000122 in_proto.ArtifactType.FUZZER_SYSROOT: sysroot.CreateFuzzerSysroot,
Alex Klein1699fab2022-09-08 08:46:06 -0600123 }
Jack Neus5e56fef2021-06-18 16:57:28 +0000124
Alex Klein1699fab2022-09-08 08:46:06 -0600125 for output_artifact in in_proto.output_artifacts:
126 for artifact_type, func in artifact_types.items():
127 if artifact_type in output_artifact.artifact_types:
128 result = func(chroot, sysroot_class, build_target, output_dir)
129 if result:
130 generated.append(
131 {
132 "paths": [result]
133 if isinstance(result, str)
134 else result,
135 "type": artifact_type,
136 }
137 )
Jack Neus5e56fef2021-06-18 16:57:28 +0000138
Alex Klein1699fab2022-09-08 08:46:06 -0600139 return generated
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600140
141
Alex Klein076841b2019-08-29 15:19:39 -0600142@faux.all_empty
Alex Klein1699fab2022-09-08 08:46:06 -0600143@validate.require("build_target.name")
Alex Klein231d2da2019-07-22 16:44:45 -0600144@validate.validation_complete
145def Create(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600146 """Create or replace a sysroot."""
147 update_chroot = not input_proto.flags.chroot_current
148 replace_sysroot = input_proto.flags.replace
Alex Kleinda35fcf2019-03-07 16:01:15 -0700149
Alex Klein1699fab2022-09-08 08:46:06 -0600150 build_target = controller_util.ParseBuildTarget(
151 input_proto.build_target, input_proto.profile
152 )
153 package_indexes = [
154 binpkg.PackageIndexInfo.from_protobuf(x)
155 for x in input_proto.package_indexes
156 ]
157 run_configs = sysroot.SetupBoardRunConfig(
158 force=replace_sysroot,
159 upgrade_chroot=update_chroot,
160 package_indexes=package_indexes,
161 backtrack=DEFAULT_BACKTRACK,
162 )
Alex Kleinda35fcf2019-03-07 16:01:15 -0700163
Alex Klein1699fab2022-09-08 08:46:06 -0600164 try:
165 created = sysroot.Create(
166 build_target, run_configs, accept_licenses=_ACCEPTED_LICENSES
167 )
168 except sysroot.Error as e:
169 cros_build_lib.Die(e)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700170
Alex Klein1699fab2022-09-08 08:46:06 -0600171 output_proto.sysroot.path = created.path
172 output_proto.sysroot.build_target.name = build_target.name
Alex Kleinda35fcf2019-03-07 16:01:15 -0700173
Alex Klein1699fab2022-09-08 08:46:06 -0600174 return controller.RETURN_CODE_SUCCESS
Alex Kleinda35fcf2019-03-07 16:01:15 -0700175
Alex Klein076841b2019-08-29 15:19:39 -0600176
Michael Mortensen98592f62019-09-27 13:34:18 -0600177@faux.all_empty
Alex Klein1699fab2022-09-08 08:46:06 -0600178@validate.require("build_target.name", "packages")
179@validate.require_each("packages", ["category", "package_name"])
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -0700180@validate.validation_complete
181def GenerateArchive(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600182 """Generate a sysroot. Typically used by informational builders."""
183 build_target_name = input_proto.build_target.name
184 pkg_list = []
185 for package in input_proto.packages:
186 pkg_list.append("%s/%s" % (package.category, package.package_name))
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -0700187
Alex Klein1699fab2022-09-08 08:46:06 -0600188 with osutils.TempDir(delete=False) as temp_output_dir:
189 sysroot_tar_path = sysroot.GenerateArchive(
190 temp_output_dir, build_target_name, pkg_list
191 )
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -0700192
Alex Klein1699fab2022-09-08 08:46:06 -0600193 # By assigning this Path variable to the tar path, the tar file will be
194 # copied out to the input_proto's ResultPath location.
195 output_proto.sysroot_archive.path = sysroot_tar_path
196 output_proto.sysroot_archive.location = common_pb2.Path.INSIDE
Michael Mortensen3f6b4bd2020-02-07 14:16:43 -0700197
198
Alex Klein076841b2019-08-29 15:19:39 -0600199def _MockFailedPackagesResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600200 """Mock error response that populates failed packages."""
201 fail = output_proto.failed_package_data.add()
202 fail.name.package_name = "package"
203 fail.name.category = "category"
204 fail.name.version = "1.0.0_rc-r1"
205 fail.log_path.path = (
206 "/path/to/package:category-1.0.0_rc-r1:20210609-1337.log"
207 )
208 fail.log_path.location = common_pb2.Path.INSIDE
Lizzy Presland7e23a612021-11-09 21:49:42 +0000209
Alex Klein1699fab2022-09-08 08:46:06 -0600210 fail2 = output_proto.failed_package_data.add()
211 fail2.name.package_name = "bar"
212 fail2.name.category = "foo"
213 fail2.name.version = "3.7-r99"
214 fail2.log_path.path = "/path/to/foo:bar-3.7-r99:20210609-1620.log"
215 fail2.log_path.location = common_pb2.Path.INSIDE
Lizzy Presland7e23a612021-11-09 21:49:42 +0000216
Alex Klein076841b2019-08-29 15:19:39 -0600217
218@faux.empty_success
219@faux.error(_MockFailedPackagesResponse)
Alex Klein1699fab2022-09-08 08:46:06 -0600220@validate.require("sysroot.path", "sysroot.build_target.name")
221@validate.exists("sysroot.path")
Alex Klein231d2da2019-07-22 16:44:45 -0600222@validate.validation_complete
223def InstallToolchain(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600224 """Install the toolchain into a sysroot."""
225 compile_source = (
226 input_proto.flags.compile_source or input_proto.flags.toolchain_changed
227 )
Alex Kleinda35fcf2019-03-07 16:01:15 -0700228
Alex Klein1699fab2022-09-08 08:46:06 -0600229 sysroot_path = input_proto.sysroot.path
Alex Kleinda35fcf2019-03-07 16:01:15 -0700230
Alex Klein1699fab2022-09-08 08:46:06 -0600231 build_target = controller_util.ParseBuildTarget(
232 input_proto.sysroot.build_target
233 )
234 target_sysroot = sysroot_lib.Sysroot(sysroot_path)
235 run_configs = sysroot.SetupBoardRunConfig(usepkg=not compile_source)
Alex Kleinda35fcf2019-03-07 16:01:15 -0700236
Alex Klein1699fab2022-09-08 08:46:06 -0600237 _LogBinhost(build_target.name)
Alex Kleina9d64602019-05-17 14:55:37 -0600238
Alex Klein1699fab2022-09-08 08:46:06 -0600239 try:
240 sysroot.InstallToolchain(build_target, target_sysroot, run_configs)
241 except sysroot_lib.ToolchainInstallError as e:
242 controller_util.retrieve_package_log_paths(
243 e.failed_toolchain_info, output_proto, target_sysroot
244 )
Alex Kleinda35fcf2019-03-07 16:01:15 -0700245
Alex Klein1699fab2022-09-08 08:46:06 -0600246 return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE
Alex Kleind4e1e422019-03-18 16:00:41 -0600247
Alex Klein1699fab2022-09-08 08:46:06 -0600248 return controller.RETURN_CODE_SUCCESS
Alex Kleind4e1e422019-03-18 16:00:41 -0600249
Alex Klein076841b2019-08-29 15:19:39 -0600250
251@faux.empty_success
252@faux.error(_MockFailedPackagesResponse)
Alex Klein1699fab2022-09-08 08:46:06 -0600253@validate.require("sysroot.build_target.name")
254@validate.exists("sysroot.path")
255@validate.require_each("packages", ["category", "package_name"])
256@validate.require_each("use_flags", ["flag"])
Alex Klein231d2da2019-07-22 16:44:45 -0600257@validate.validation_complete
Alex Kleinaef41942022-04-19 14:13:17 -0600258@metrics_lib.collect_metrics
Alex Klein231d2da2019-07-22 16:44:45 -0600259def InstallPackages(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600260 """Install packages into a sysroot, building as necessary and permitted."""
261 compile_source = (
262 input_proto.flags.compile_source or input_proto.flags.toolchain_changed
263 )
Joanna Wang1ec0c812021-11-17 17:41:27 -0800264
Alex Klein1699fab2022-09-08 08:46:06 -0600265 use_remoteexec = bool(
266 input_proto.remoteexec_config.reproxy_cfg_file
267 and input_proto.remoteexec_config.reclient_dir
268 )
Joanna Wang1ec0c812021-11-17 17:41:27 -0800269
Alex Klein1699fab2022-09-08 08:46:06 -0600270 # Testing if Goma will support unknown compilers now.
271 use_goma = input_proto.flags.use_goma and not use_remoteexec
Alex Kleind4e1e422019-03-18 16:00:41 -0600272
Alex Klein1699fab2022-09-08 08:46:06 -0600273 target_sysroot = sysroot_lib.Sysroot(input_proto.sysroot.path)
274 build_target = controller_util.ParseBuildTarget(
275 input_proto.sysroot.build_target
276 )
Alex Kleinca572ee2020-09-03 10:47:14 -0600277
Alex Klein1699fab2022-09-08 08:46:06 -0600278 # Get the package atom for each specified package. The field is optional, so
279 # error only when we cannot parse an atom for each of the given packages.
280 packages = [
281 controller_util.PackageInfoToCPV(x).cp for x in input_proto.packages
282 ]
Alex Kleinca572ee2020-09-03 10:47:14 -0600283
Alex Klein1699fab2022-09-08 08:46:06 -0600284 package_indexes = [
285 binpkg.PackageIndexInfo.from_protobuf(x)
286 for x in input_proto.package_indexes
287 ]
Alex Kleind4e1e422019-03-18 16:00:41 -0600288
Alex Klein1699fab2022-09-08 08:46:06 -0600289 # Calculate which packages would have been merged, but don't install anything.
290 dryrun = input_proto.flags.dryrun
Navil Perez5766d1b2021-05-26 17:38:15 +0000291
Alex Klein1699fab2022-09-08 08:46:06 -0600292 if not target_sysroot.IsToolchainInstalled():
293 cros_build_lib.Die("Toolchain must first be installed.")
Alex Kleind4e1e422019-03-18 16:00:41 -0600294
Alex Klein1699fab2022-09-08 08:46:06 -0600295 _LogBinhost(build_target.name)
Alex Kleina9d64602019-05-17 14:55:37 -0600296
Alex Klein1699fab2022-09-08 08:46:06 -0600297 use_flags = [u.flag for u in input_proto.use_flags]
298 build_packages_config = sysroot.BuildPackagesRunConfig(
299 use_any_chrome=False,
300 usepkg=not compile_source,
301 install_debug_symbols=True,
302 packages=packages,
303 package_indexes=package_indexes,
304 use_flags=use_flags,
305 use_goma=use_goma,
306 use_remoteexec=use_remoteexec,
307 incremental_build=False,
308 dryrun=dryrun,
309 backtrack=DEFAULT_BACKTRACK,
310 )
Alex Kleind4e1e422019-03-18 16:00:41 -0600311
Alex Klein1699fab2022-09-08 08:46:06 -0600312 try:
313 sysroot.BuildPackages(
314 build_target, target_sysroot, build_packages_config
315 )
316 except sysroot_lib.PackageInstallError as e:
317 if not e.failed_packages:
318 # No packages to report, so just exit with an error code.
319 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Alex Klein2557b4f2019-07-11 14:34:00 -0600320
Alex Klein1699fab2022-09-08 08:46:06 -0600321 controller_util.retrieve_package_log_paths(
322 e.failed_packages, output_proto, target_sysroot
323 )
Alex Kleind4e1e422019-03-18 16:00:41 -0600324
Alex Klein1699fab2022-09-08 08:46:06 -0600325 return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE
326 finally:
327 # Copy goma logs to specified directory if there is a goma_config and
328 # it contains a log_dir to store artifacts.
329 if input_proto.goma_config.log_dir.dir:
330 log_source_dir = _GetGomaLogDirectory()
331 archiver = goma_lib.LogsArchiver(
332 log_source_dir,
333 dest_dir=input_proto.goma_config.log_dir.dir,
334 stats_file=input_proto.goma_config.stats_file,
335 counterz_file=input_proto.goma_config.counterz_file,
336 )
337 archiver_tuple = archiver.Archive()
338 if archiver_tuple.stats_file:
339 output_proto.goma_artifacts.stats_file = (
340 archiver_tuple.stats_file
341 )
342 if archiver_tuple.counterz_file:
343 output_proto.goma_artifacts.counterz_file = (
344 archiver_tuple.counterz_file
345 )
346 output_proto.goma_artifacts.log_files[:] = archiver_tuple.log_files
Alex Kleina9d64602019-05-17 14:55:37 -0600347
Alex Klein1699fab2022-09-08 08:46:06 -0600348 # Return without populating the response if it is a dryrun.
349 if dryrun:
350 return controller.RETURN_CODE_SUCCESS
Navil Perez5766d1b2021-05-26 17:38:15 +0000351
Alex Klein1699fab2022-09-08 08:46:06 -0600352 # Read metric events log and pipe them into output_proto.events.
353 deserialize_metrics_log(output_proto.events, prefix=build_target.name)
Will Bradley7e5b8c12019-07-30 12:44:15 -0600354
Alex Kleina9d64602019-05-17 14:55:37 -0600355
356def _LogBinhost(board):
Alex Klein1699fab2022-09-08 08:46:06 -0600357 """Log the portage binhost for the given board."""
358 binhost = portage_util.PortageqEnvvar(
359 "PORTAGE_BINHOST", board=board, allow_undefined=True
360 )
361 if not binhost:
362 logging.warning("Portage Binhost not found.")
363 else:
364 logging.info("Portage Binhost: %s", binhost)