blob: a0c39073d261f151b21bdc10a5d40c578c1b1ca9 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2020 The ChromiumOS Authors
Jett Rink17ed0f52020-09-25 17:14:31 -06002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
LaMont Jonesed9a5de2021-02-03 11:06:12 -07004
Jett Rink17ed0f52020-09-25 17:14:31 -06005"""Firmware builder controller.
6
LaMont Jonesdec69ad2021-01-27 13:02:00 -07007Handle all firmware builder related functionality. Currently no service module
8exists: all of the work is done here.
Jett Rink17ed0f52020-09-25 17:14:31 -06009"""
Mike Frysinger93afe3e2023-09-05 14:18:52 -040010
Alex Kleineec90f72023-02-03 10:53:12 -070011import logging
Jett Rink17ed0f52020-09-25 17:14:31 -060012import os
13import tempfile
14
Mike Frysinger2c024062021-05-22 15:43:22 -040015from chromite.third_party.google.protobuf import json_format
Jett Rink17ed0f52020-09-25 17:14:31 -060016
17from chromite.api import controller
18from chromite.api import faux
19from chromite.api import validate
20from chromite.api.gen.chromite.api import firmware_pb2
LaMont Jonesdec69ad2021-01-27 13:02:00 -070021from chromite.api.gen.chromiumos import common_pb2
Jett Rink17ed0f52020-09-25 17:14:31 -060022from chromite.lib import constants
23from chromite.lib import cros_build_lib
LaMont Jonesed9a5de2021-02-03 11:06:12 -070024from chromite.lib import osutils
25
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040026
Jeremy Bettise87bbaf2022-07-07 11:57:16 -060027def get_fw_loc(fw_loc: int) -> str:
Alex Klein1699fab2022-09-08 08:46:06 -060028 """Get firmware_builder.py location.
Azizur Rahmanc6cf6e92021-10-21 18:58:19 +000029
Alex Klein1699fab2022-09-08 08:46:06 -060030 Args:
Alex Klein611dddd2022-10-11 17:02:01 -060031 fw_loc: FwLocation enum.
Azizur Rahmanc6cf6e92021-10-21 18:58:19 +000032
Alex Klein1699fab2022-09-08 08:46:06 -060033 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -060034 path to firmware_builder.py for valid fw_loc.
Alex Klein1699fab2022-09-08 08:46:06 -060035 """
36 return {
37 common_pb2.PLATFORM_EC: "src/platform/ec/",
38 common_pb2.PLATFORM_ZEPHYR: "src/platform/ec/zephyr/",
39 common_pb2.PLATFORM_TI50: "src/platform/ti50/common/",
40 common_pb2.PLATFORM_CR50: "src/platform/cr50/",
Jack Rosenthalf686c682023-03-16 10:45:30 -060041 common_pb2.PLATFORM_CHAMELEON: "src/platform/chameleon/v3/ec/",
Alex Klein1699fab2022-09-08 08:46:06 -060042 }.get(fw_loc, "")
Azizur Rahmanc6cf6e92021-10-21 18:58:19 +000043
Jett Rink17ed0f52020-09-25 17:14:31 -060044
LaMont Jonesb7be76b2021-02-16 16:22:14 -070045def _call_entry(fw_loc, metric_proto, subcmd, *args, **kwargs):
Alex Klein1699fab2022-09-08 08:46:06 -060046 """Calls into firmware_builder.py with the specified subcmd."""
Jett Rink17ed0f52020-09-25 17:14:31 -060047
Alex Klein1699fab2022-09-08 08:46:06 -060048 fw_path = get_fw_loc(fw_loc)
49 if not fw_path:
50 cros_build_lib.Die(f"Unknown firmware location {fw_loc}.")
Jett Rink17ed0f52020-09-25 17:14:31 -060051
Alex Klein1699fab2022-09-08 08:46:06 -060052 entry_point = os.path.join(
53 constants.SOURCE_ROOT, fw_path, "firmware_builder.py"
54 )
Jett Rink17ed0f52020-09-25 17:14:31 -060055
Alex Klein1699fab2022-09-08 08:46:06 -060056 with tempfile.NamedTemporaryFile() as tmpfile:
57 cmd = [entry_point, "--metrics", tmpfile.name] + list(args)
58 for key, value in kwargs.items():
59 cmd += [f'--{key.replace("_", "-")}', value]
60 cmd += [subcmd]
LaMont Jonesdec69ad2021-01-27 13:02:00 -070061
Alex Klein1699fab2022-09-08 08:46:06 -060062 result = cros_build_lib.run(cmd, check=False)
Mike Frysingerd97829b2023-02-24 16:09:20 -050063 with open(tmpfile.name, "r", encoding="utf-8") as f:
Alex Klein1699fab2022-09-08 08:46:06 -060064 response = f.read()
Jett Rink17ed0f52020-09-25 17:14:31 -060065
Alex Klein1699fab2022-09-08 08:46:06 -060066 if metric_proto:
Alex Kleineec90f72023-02-03 10:53:12 -070067 if not response:
68 logging.warning("Metrics data empty.")
69 else:
70 # Parse the entire metric file as our metric proto (as a passthru).
71 # TODO(b/177907747): BundleFirmwareArtifacts doesn't use this
72 # (yet?), but firmware_builder.py requires it.
73 json_format.Parse(response, metric_proto)
Jett Rink17ed0f52020-09-25 17:14:31 -060074
Alex Klein1699fab2022-09-08 08:46:06 -060075 if result.returncode == 0:
76 return controller.RETURN_CODE_SUCCESS
77 else:
78 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Jett Rink17ed0f52020-09-25 17:14:31 -060079
80
81def _BuildAllTotFirmwareResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -060082 """Add a fw region metric to a successful response."""
Jett Rink17ed0f52020-09-25 17:14:31 -060083
Alex Klein1699fab2022-09-08 08:46:06 -060084 metric = output_proto.success.value.add()
85 metric.target_name = "foo"
86 metric.platform_name = "bar"
87 fw_section = metric.fw_section.add()
88 fw_section.region = "EC_RO"
89 fw_section.used = 100
90 fw_section.total = 150
Jett Rink17ed0f52020-09-25 17:14:31 -060091
LaMont Jonesed9a5de2021-02-03 11:06:12 -070092
Jett Rink17ed0f52020-09-25 17:14:31 -060093@faux.success(_BuildAllTotFirmwareResponse)
94@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -060095@validate.require("firmware_location")
Jett Rink17ed0f52020-09-25 17:14:31 -060096@validate.validation_complete
97def BuildAllTotFirmware(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -060098 """Build all of the firmware targets at the specified location."""
Jett Rink17ed0f52020-09-25 17:14:31 -060099
Alex Klein1699fab2022-09-08 08:46:06 -0600100 args = ["--code-coverage"] if input_proto.code_coverage else []
101 return _call_entry(
102 input_proto.firmware_location, output_proto.metrics, "build", *args
103 )
Jett Rink17ed0f52020-09-25 17:14:31 -0600104
105
106def _TestAllTotFirmwareResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600107 """Add a fw region metric to a successful response."""
Jett Rink17ed0f52020-09-25 17:14:31 -0600108
Alex Klein1699fab2022-09-08 08:46:06 -0600109 metric = output_proto.success.value.add()
110 metric.name = "foo-test"
Jett Rink17ed0f52020-09-25 17:14:31 -0600111
LaMont Jonesed9a5de2021-02-03 11:06:12 -0700112
Jett Rink17ed0f52020-09-25 17:14:31 -0600113@faux.success(_TestAllTotFirmwareResponse)
114@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600115@validate.require("firmware_location")
Jett Rink17ed0f52020-09-25 17:14:31 -0600116@validate.validation_complete
117def TestAllTotFirmware(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600118 """Runs all of the firmware tests at the specified location."""
Jett Rink17ed0f52020-09-25 17:14:31 -0600119
Alex Klein1699fab2022-09-08 08:46:06 -0600120 args = ["--code-coverage"] if input_proto.code_coverage else []
121 return _call_entry(
122 input_proto.firmware_location, output_proto.metrics, "test", *args
123 )
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700124
125
126def _BuildAllFirmwareResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600127 """Add a fw region metric to a successful response."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700128
Alex Klein1699fab2022-09-08 08:46:06 -0600129 metric = output_proto.metrics.value.add()
130 metric.target_name = "foo"
131 metric.platform_name = "bar"
132 fw_section = metric.fw_section.add()
133 fw_section.region = "EC_RO"
134 fw_section.used = 100
135 fw_section.total = 150
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700136
LaMont Jonesed9a5de2021-02-03 11:06:12 -0700137
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700138@faux.success(_BuildAllFirmwareResponse)
139@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600140@validate.require("firmware_location")
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700141@validate.validation_complete
142def BuildAllFirmware(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600143 """Build all of the firmware targets at the specified location."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700144
Alex Klein1699fab2022-09-08 08:46:06 -0600145 args = ["--code-coverage"] if input_proto.code_coverage else []
146 return _call_entry(
147 input_proto.firmware_location, output_proto.metrics, "build", *args
148 )
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700149
150
151def _TestAllFirmwareResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600152 """Add a fw region metric to a successful response."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700153
Alex Klein1699fab2022-09-08 08:46:06 -0600154 metric = output_proto.success.value.add()
155 metric.name = "foo-test"
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700156
LaMont Jonesed9a5de2021-02-03 11:06:12 -0700157
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700158@faux.success(_TestAllFirmwareResponse)
159@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600160@validate.require("firmware_location")
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700161@validate.validation_complete
162def TestAllFirmware(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600163 """Runs all of the firmware tests at the specified location."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700164
Alex Klein1699fab2022-09-08 08:46:06 -0600165 args = ["--code-coverage"] if input_proto.code_coverage else []
166 return _call_entry(
167 input_proto.firmware_location, output_proto.metrics, "test", *args
168 )
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700169
170
171def _BundleFirmwareArtifactsResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600172 """Add a fw region metric to a successful response."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700173
Alex Klein1699fab2022-09-08 08:46:06 -0600174 metric = output_proto.success.value.add()
175 metric.name = "foo-test"
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700176
LaMont Jonesed9a5de2021-02-03 11:06:12 -0700177
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700178@faux.success(_BundleFirmwareArtifactsResponse)
179@faux.empty_completed_unsuccessfully_error
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700180@validate.validation_complete
181def BundleFirmwareArtifacts(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600182 """Runs all of the firmware tests at the specified location."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700183
Alex Klein1699fab2022-09-08 08:46:06 -0600184 if len(input_proto.artifacts.output_artifacts) > 1:
185 raise ValueError("Must have exactly one output_artifact entry")
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700186
Alex Klein1699fab2022-09-08 08:46:06 -0600187 with osutils.TempDir(delete=False) as tmpdir:
188 info = input_proto.artifacts.output_artifacts[0]
189 metadata_path = os.path.join(tmpdir, "firmware_metadata.jsonpb")
190 args = []
191 if input_proto.artifacts.FIRMWARE_LCOV in info.artifact_types:
192 args += ["--code-coverage"]
193 resp = _call_entry(
194 info.location,
195 None,
196 "bundle",
197 *args,
198 output_dir=tmpdir,
199 metadata=metadata_path,
200 )
201 file_paths = []
202 if os.path.exists(metadata_path):
Mike Frysingerd97829b2023-02-24 16:09:20 -0500203 with open(metadata_path, "r", encoding="utf-8") as f:
Alex Klein1699fab2022-09-08 08:46:06 -0600204 metadata = json_format.Parse(
205 f.read(), firmware_pb2.FirmwareArtifactInfo()
206 )
207 else:
208 metadata = firmware_pb2.FirmwareArtifactInfo()
209 if input_proto.artifacts.FIRMWARE_TARBALL_INFO in info.artifact_types:
210 output_proto.artifacts.artifacts.add(
211 artifact_type=input_proto.artifacts.FIRMWARE_TARBALL_INFO,
212 location=info.location,
213 paths=[
214 common_pb2.Path(
215 path=metadata_path, location=common_pb2.Path.INSIDE
216 )
217 ],
218 )
LaMont Jones9a0594d2021-03-17 15:51:20 -0600219
Alex Klein1699fab2022-09-08 08:46:06 -0600220 full_path = lambda x: common_pb2.Path(
221 path=os.path.join(tmpdir, x.file_name),
222 location=common_pb2.Path.INSIDE,
223 )
LaMont Jones9a0594d2021-03-17 15:51:20 -0600224
Alex Klein1699fab2022-09-08 08:46:06 -0600225 for typ, name in (
226 (input_proto.artifacts.FIRMWARE_TARBALL, "tarball_info"),
227 (input_proto.artifacts.FIRMWARE_LCOV, "lcov_info"),
228 (input_proto.artifacts.CODE_COVERAGE_HTML, "coverage_html"),
229 ):
230 file_paths = [
231 full_path(x)
232 for x in metadata.objects
233 if x.WhichOneof("firmware_object_info") == name
234 ]
235 if file_paths and typ in info.artifact_types:
236 output_proto.artifacts.artifacts.add(
237 artifact_type=typ, paths=file_paths, location=info.location
238 )
LaMont Jones9a0594d2021-03-17 15:51:20 -0600239
Alex Klein1699fab2022-09-08 08:46:06 -0600240 return resp