blob: 0211304dd65ea25cee3f69f989d0e7a878c9445b [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"""
Alex Kleineec90f72023-02-03 10:53:12 -070010import logging
Jett Rink17ed0f52020-09-25 17:14:31 -060011import os
12import tempfile
13
Mike Frysinger2c024062021-05-22 15:43:22 -040014from chromite.third_party.google.protobuf import json_format
Jett Rink17ed0f52020-09-25 17:14:31 -060015
16from chromite.api import controller
17from chromite.api import faux
18from chromite.api import validate
19from chromite.api.gen.chromite.api import firmware_pb2
LaMont Jonesdec69ad2021-01-27 13:02:00 -070020from chromite.api.gen.chromiumos import common_pb2
Jett Rink17ed0f52020-09-25 17:14:31 -060021from chromite.lib import constants
22from chromite.lib import cros_build_lib
LaMont Jonesed9a5de2021-02-03 11:06:12 -070023from chromite.lib import osutils
24
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040025
Jeremy Bettise87bbaf2022-07-07 11:57:16 -060026def get_fw_loc(fw_loc: int) -> str:
Alex Klein1699fab2022-09-08 08:46:06 -060027 """Get firmware_builder.py location.
Azizur Rahmanc6cf6e92021-10-21 18:58:19 +000028
Alex Klein1699fab2022-09-08 08:46:06 -060029 Args:
Alex Klein611dddd2022-10-11 17:02:01 -060030 fw_loc: FwLocation enum.
Azizur Rahmanc6cf6e92021-10-21 18:58:19 +000031
Alex Klein1699fab2022-09-08 08:46:06 -060032 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -060033 path to firmware_builder.py for valid fw_loc.
Alex Klein1699fab2022-09-08 08:46:06 -060034 """
35 return {
36 common_pb2.PLATFORM_EC: "src/platform/ec/",
37 common_pb2.PLATFORM_ZEPHYR: "src/platform/ec/zephyr/",
38 common_pb2.PLATFORM_TI50: "src/platform/ti50/common/",
39 common_pb2.PLATFORM_CR50: "src/platform/cr50/",
Jack Rosenthalf686c682023-03-16 10:45:30 -060040 common_pb2.PLATFORM_CHAMELEON: "src/platform/chameleon/v3/ec/",
Alex Klein1699fab2022-09-08 08:46:06 -060041 }.get(fw_loc, "")
Azizur Rahmanc6cf6e92021-10-21 18:58:19 +000042
Jett Rink17ed0f52020-09-25 17:14:31 -060043
LaMont Jonesb7be76b2021-02-16 16:22:14 -070044def _call_entry(fw_loc, metric_proto, subcmd, *args, **kwargs):
Alex Klein1699fab2022-09-08 08:46:06 -060045 """Calls into firmware_builder.py with the specified subcmd."""
Jett Rink17ed0f52020-09-25 17:14:31 -060046
Alex Klein1699fab2022-09-08 08:46:06 -060047 fw_path = get_fw_loc(fw_loc)
48 if not fw_path:
49 cros_build_lib.Die(f"Unknown firmware location {fw_loc}.")
Jett Rink17ed0f52020-09-25 17:14:31 -060050
Alex Klein1699fab2022-09-08 08:46:06 -060051 entry_point = os.path.join(
52 constants.SOURCE_ROOT, fw_path, "firmware_builder.py"
53 )
Jett Rink17ed0f52020-09-25 17:14:31 -060054
Alex Klein1699fab2022-09-08 08:46:06 -060055 with tempfile.NamedTemporaryFile() as tmpfile:
56 cmd = [entry_point, "--metrics", tmpfile.name] + list(args)
57 for key, value in kwargs.items():
58 cmd += [f'--{key.replace("_", "-")}', value]
59 cmd += [subcmd]
LaMont Jonesdec69ad2021-01-27 13:02:00 -070060
Alex Klein1699fab2022-09-08 08:46:06 -060061 result = cros_build_lib.run(cmd, check=False)
Mike Frysingerd97829b2023-02-24 16:09:20 -050062 with open(tmpfile.name, "r", encoding="utf-8") as f:
Alex Klein1699fab2022-09-08 08:46:06 -060063 response = f.read()
Jett Rink17ed0f52020-09-25 17:14:31 -060064
Alex Klein1699fab2022-09-08 08:46:06 -060065 if metric_proto:
Alex Kleineec90f72023-02-03 10:53:12 -070066 if not response:
67 logging.warning("Metrics data empty.")
68 else:
69 # Parse the entire metric file as our metric proto (as a passthru).
70 # TODO(b/177907747): BundleFirmwareArtifacts doesn't use this
71 # (yet?), but firmware_builder.py requires it.
72 json_format.Parse(response, metric_proto)
Jett Rink17ed0f52020-09-25 17:14:31 -060073
Alex Klein1699fab2022-09-08 08:46:06 -060074 if result.returncode == 0:
75 return controller.RETURN_CODE_SUCCESS
76 else:
77 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Jett Rink17ed0f52020-09-25 17:14:31 -060078
79
80def _BuildAllTotFirmwareResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -060081 """Add a fw region metric to a successful response."""
Jett Rink17ed0f52020-09-25 17:14:31 -060082
Alex Klein1699fab2022-09-08 08:46:06 -060083 metric = output_proto.success.value.add()
84 metric.target_name = "foo"
85 metric.platform_name = "bar"
86 fw_section = metric.fw_section.add()
87 fw_section.region = "EC_RO"
88 fw_section.used = 100
89 fw_section.total = 150
Jett Rink17ed0f52020-09-25 17:14:31 -060090
LaMont Jonesed9a5de2021-02-03 11:06:12 -070091
Jett Rink17ed0f52020-09-25 17:14:31 -060092@faux.success(_BuildAllTotFirmwareResponse)
93@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -060094@validate.require("firmware_location")
Jett Rink17ed0f52020-09-25 17:14:31 -060095@validate.validation_complete
96def BuildAllTotFirmware(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -060097 """Build all of the firmware targets at the specified location."""
Jett Rink17ed0f52020-09-25 17:14:31 -060098
Alex Klein1699fab2022-09-08 08:46:06 -060099 args = ["--code-coverage"] if input_proto.code_coverage else []
100 return _call_entry(
101 input_proto.firmware_location, output_proto.metrics, "build", *args
102 )
Jett Rink17ed0f52020-09-25 17:14:31 -0600103
104
105def _TestAllTotFirmwareResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600106 """Add a fw region metric to a successful response."""
Jett Rink17ed0f52020-09-25 17:14:31 -0600107
Alex Klein1699fab2022-09-08 08:46:06 -0600108 metric = output_proto.success.value.add()
109 metric.name = "foo-test"
Jett Rink17ed0f52020-09-25 17:14:31 -0600110
LaMont Jonesed9a5de2021-02-03 11:06:12 -0700111
Jett Rink17ed0f52020-09-25 17:14:31 -0600112@faux.success(_TestAllTotFirmwareResponse)
113@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600114@validate.require("firmware_location")
Jett Rink17ed0f52020-09-25 17:14:31 -0600115@validate.validation_complete
116def TestAllTotFirmware(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600117 """Runs all of the firmware tests at the specified location."""
Jett Rink17ed0f52020-09-25 17:14:31 -0600118
Alex Klein1699fab2022-09-08 08:46:06 -0600119 args = ["--code-coverage"] if input_proto.code_coverage else []
120 return _call_entry(
121 input_proto.firmware_location, output_proto.metrics, "test", *args
122 )
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700123
124
125def _BuildAllFirmwareResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600126 """Add a fw region metric to a successful response."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700127
Alex Klein1699fab2022-09-08 08:46:06 -0600128 metric = output_proto.metrics.value.add()
129 metric.target_name = "foo"
130 metric.platform_name = "bar"
131 fw_section = metric.fw_section.add()
132 fw_section.region = "EC_RO"
133 fw_section.used = 100
134 fw_section.total = 150
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700135
LaMont Jonesed9a5de2021-02-03 11:06:12 -0700136
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700137@faux.success(_BuildAllFirmwareResponse)
138@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600139@validate.require("firmware_location")
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700140@validate.validation_complete
141def BuildAllFirmware(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600142 """Build all of the firmware targets at the specified location."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700143
Alex Klein1699fab2022-09-08 08:46:06 -0600144 args = ["--code-coverage"] if input_proto.code_coverage else []
145 return _call_entry(
146 input_proto.firmware_location, output_proto.metrics, "build", *args
147 )
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700148
149
150def _TestAllFirmwareResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600151 """Add a fw region metric to a successful response."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700152
Alex Klein1699fab2022-09-08 08:46:06 -0600153 metric = output_proto.success.value.add()
154 metric.name = "foo-test"
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700155
LaMont Jonesed9a5de2021-02-03 11:06:12 -0700156
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700157@faux.success(_TestAllFirmwareResponse)
158@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600159@validate.require("firmware_location")
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700160@validate.validation_complete
161def TestAllFirmware(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600162 """Runs all of the firmware tests at the specified location."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700163
Alex Klein1699fab2022-09-08 08:46:06 -0600164 args = ["--code-coverage"] if input_proto.code_coverage else []
165 return _call_entry(
166 input_proto.firmware_location, output_proto.metrics, "test", *args
167 )
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700168
169
170def _BundleFirmwareArtifactsResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600171 """Add a fw region metric to a successful response."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700172
Alex Klein1699fab2022-09-08 08:46:06 -0600173 metric = output_proto.success.value.add()
174 metric.name = "foo-test"
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700175
LaMont Jonesed9a5de2021-02-03 11:06:12 -0700176
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700177@faux.success(_BundleFirmwareArtifactsResponse)
178@faux.empty_completed_unsuccessfully_error
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700179@validate.validation_complete
180def BundleFirmwareArtifacts(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600181 """Runs all of the firmware tests at the specified location."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700182
Alex Klein1699fab2022-09-08 08:46:06 -0600183 if len(input_proto.artifacts.output_artifacts) > 1:
184 raise ValueError("Must have exactly one output_artifact entry")
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700185
Alex Klein1699fab2022-09-08 08:46:06 -0600186 with osutils.TempDir(delete=False) as tmpdir:
187 info = input_proto.artifacts.output_artifacts[0]
188 metadata_path = os.path.join(tmpdir, "firmware_metadata.jsonpb")
189 args = []
190 if input_proto.artifacts.FIRMWARE_LCOV in info.artifact_types:
191 args += ["--code-coverage"]
192 resp = _call_entry(
193 info.location,
194 None,
195 "bundle",
196 *args,
197 output_dir=tmpdir,
198 metadata=metadata_path,
199 )
200 file_paths = []
201 if os.path.exists(metadata_path):
Mike Frysingerd97829b2023-02-24 16:09:20 -0500202 with open(metadata_path, "r", encoding="utf-8") as f:
Alex Klein1699fab2022-09-08 08:46:06 -0600203 metadata = json_format.Parse(
204 f.read(), firmware_pb2.FirmwareArtifactInfo()
205 )
206 else:
207 metadata = firmware_pb2.FirmwareArtifactInfo()
208 if input_proto.artifacts.FIRMWARE_TARBALL_INFO in info.artifact_types:
209 output_proto.artifacts.artifacts.add(
210 artifact_type=input_proto.artifacts.FIRMWARE_TARBALL_INFO,
211 location=info.location,
212 paths=[
213 common_pb2.Path(
214 path=metadata_path, location=common_pb2.Path.INSIDE
215 )
216 ],
217 )
LaMont Jones9a0594d2021-03-17 15:51:20 -0600218
Alex Klein1699fab2022-09-08 08:46:06 -0600219 full_path = lambda x: common_pb2.Path(
220 path=os.path.join(tmpdir, x.file_name),
221 location=common_pb2.Path.INSIDE,
222 )
LaMont Jones9a0594d2021-03-17 15:51:20 -0600223
Alex Klein1699fab2022-09-08 08:46:06 -0600224 for typ, name in (
225 (input_proto.artifacts.FIRMWARE_TARBALL, "tarball_info"),
226 (input_proto.artifacts.FIRMWARE_LCOV, "lcov_info"),
227 (input_proto.artifacts.CODE_COVERAGE_HTML, "coverage_html"),
228 ):
229 file_paths = [
230 full_path(x)
231 for x in metadata.objects
232 if x.WhichOneof("firmware_object_info") == name
233 ]
234 if file_paths and typ in info.artifact_types:
235 output_proto.artifacts.artifacts.add(
236 artifact_type=typ, paths=file_paths, location=info.location
237 )
LaMont Jones9a0594d2021-03-17 15:51:20 -0600238
Alex Klein1699fab2022-09-08 08:46:06 -0600239 return resp