blob: b2e16484a6165f6b8e4c7a3727187c173ce51c51 [file] [log] [blame]
Jett Rink17ed0f52020-09-25 17:14:31 -06001# Copyright 2020 The Chromium OS Authors. All rights reserved.
2# 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"""
10
11import 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:
30 fw_loc: FwLocation enum.
Azizur Rahmanc6cf6e92021-10-21 18:58:19 +000031
Alex Klein1699fab2022-09-08 08:46:06 -060032 Returns:
33 path to firmware_builder.py for valid fw_loc.
34 """
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/",
40 }.get(fw_loc, "")
Azizur Rahmanc6cf6e92021-10-21 18:58:19 +000041
Jett Rink17ed0f52020-09-25 17:14:31 -060042
LaMont Jonesb7be76b2021-02-16 16:22:14 -070043def _call_entry(fw_loc, metric_proto, subcmd, *args, **kwargs):
Alex Klein1699fab2022-09-08 08:46:06 -060044 """Calls into firmware_builder.py with the specified subcmd."""
Jett Rink17ed0f52020-09-25 17:14:31 -060045
Alex Klein1699fab2022-09-08 08:46:06 -060046 fw_path = get_fw_loc(fw_loc)
47 if not fw_path:
48 cros_build_lib.Die(f"Unknown firmware location {fw_loc}.")
Jett Rink17ed0f52020-09-25 17:14:31 -060049
Alex Klein1699fab2022-09-08 08:46:06 -060050 entry_point = os.path.join(
51 constants.SOURCE_ROOT, fw_path, "firmware_builder.py"
52 )
Jett Rink17ed0f52020-09-25 17:14:31 -060053
Alex Klein1699fab2022-09-08 08:46:06 -060054 with tempfile.NamedTemporaryFile() as tmpfile:
55 cmd = [entry_point, "--metrics", tmpfile.name] + list(args)
56 for key, value in kwargs.items():
57 cmd += [f'--{key.replace("_", "-")}', value]
58 cmd += [subcmd]
LaMont Jonesdec69ad2021-01-27 13:02:00 -070059
Alex Klein1699fab2022-09-08 08:46:06 -060060 result = cros_build_lib.run(cmd, check=False)
61 with open(tmpfile.name, "r") as f:
62 response = f.read()
Jett Rink17ed0f52020-09-25 17:14:31 -060063
Alex Klein1699fab2022-09-08 08:46:06 -060064 if metric_proto:
65 # Parse the entire metric file as our metric proto (as a passthru).
66 # TODO(b/177907747): BundleFirmwareArtifacts doesn't use this (yet?), but
67 # firmware_builder.py requires it.
68 json_format.Parse(response, metric_proto)
Jett Rink17ed0f52020-09-25 17:14:31 -060069
Alex Klein1699fab2022-09-08 08:46:06 -060070 if result.returncode == 0:
71 return controller.RETURN_CODE_SUCCESS
72 else:
73 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Jett Rink17ed0f52020-09-25 17:14:31 -060074
75
76def _BuildAllTotFirmwareResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -060077 """Add a fw region metric to a successful response."""
Jett Rink17ed0f52020-09-25 17:14:31 -060078
Alex Klein1699fab2022-09-08 08:46:06 -060079 metric = output_proto.success.value.add()
80 metric.target_name = "foo"
81 metric.platform_name = "bar"
82 fw_section = metric.fw_section.add()
83 fw_section.region = "EC_RO"
84 fw_section.used = 100
85 fw_section.total = 150
Jett Rink17ed0f52020-09-25 17:14:31 -060086
LaMont Jonesed9a5de2021-02-03 11:06:12 -070087
Jett Rink17ed0f52020-09-25 17:14:31 -060088@faux.success(_BuildAllTotFirmwareResponse)
89@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -060090@validate.require("firmware_location")
Jett Rink17ed0f52020-09-25 17:14:31 -060091@validate.validation_complete
92def BuildAllTotFirmware(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -060093 """Build all of the firmware targets at the specified location."""
Jett Rink17ed0f52020-09-25 17:14:31 -060094
Alex Klein1699fab2022-09-08 08:46:06 -060095 args = ["--code-coverage"] if input_proto.code_coverage else []
96 return _call_entry(
97 input_proto.firmware_location, output_proto.metrics, "build", *args
98 )
Jett Rink17ed0f52020-09-25 17:14:31 -060099
100
101def _TestAllTotFirmwareResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600102 """Add a fw region metric to a successful response."""
Jett Rink17ed0f52020-09-25 17:14:31 -0600103
Alex Klein1699fab2022-09-08 08:46:06 -0600104 metric = output_proto.success.value.add()
105 metric.name = "foo-test"
Jett Rink17ed0f52020-09-25 17:14:31 -0600106
LaMont Jonesed9a5de2021-02-03 11:06:12 -0700107
Jett Rink17ed0f52020-09-25 17:14:31 -0600108@faux.success(_TestAllTotFirmwareResponse)
109@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600110@validate.require("firmware_location")
Jett Rink17ed0f52020-09-25 17:14:31 -0600111@validate.validation_complete
112def TestAllTotFirmware(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600113 """Runs all of the firmware tests at the specified location."""
Jett Rink17ed0f52020-09-25 17:14:31 -0600114
Alex Klein1699fab2022-09-08 08:46:06 -0600115 args = ["--code-coverage"] if input_proto.code_coverage else []
116 return _call_entry(
117 input_proto.firmware_location, output_proto.metrics, "test", *args
118 )
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700119
120
121def _BuildAllFirmwareResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600122 """Add a fw region metric to a successful response."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700123
Alex Klein1699fab2022-09-08 08:46:06 -0600124 metric = output_proto.metrics.value.add()
125 metric.target_name = "foo"
126 metric.platform_name = "bar"
127 fw_section = metric.fw_section.add()
128 fw_section.region = "EC_RO"
129 fw_section.used = 100
130 fw_section.total = 150
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700131
LaMont Jonesed9a5de2021-02-03 11:06:12 -0700132
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700133@faux.success(_BuildAllFirmwareResponse)
134@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600135@validate.require("firmware_location")
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700136@validate.validation_complete
137def BuildAllFirmware(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600138 """Build all of the firmware targets at the specified location."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700139
Alex Klein1699fab2022-09-08 08:46:06 -0600140 args = ["--code-coverage"] if input_proto.code_coverage else []
141 return _call_entry(
142 input_proto.firmware_location, output_proto.metrics, "build", *args
143 )
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700144
145
146def _TestAllFirmwareResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600147 """Add a fw region metric to a successful response."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700148
Alex Klein1699fab2022-09-08 08:46:06 -0600149 metric = output_proto.success.value.add()
150 metric.name = "foo-test"
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700151
LaMont Jonesed9a5de2021-02-03 11:06:12 -0700152
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700153@faux.success(_TestAllFirmwareResponse)
154@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -0600155@validate.require("firmware_location")
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700156@validate.validation_complete
157def TestAllFirmware(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600158 """Runs all of the firmware tests at the specified location."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700159
Alex Klein1699fab2022-09-08 08:46:06 -0600160 args = ["--code-coverage"] if input_proto.code_coverage else []
161 return _call_entry(
162 input_proto.firmware_location, output_proto.metrics, "test", *args
163 )
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700164
165
166def _BundleFirmwareArtifactsResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600167 """Add a fw region metric to a successful response."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700168
Alex Klein1699fab2022-09-08 08:46:06 -0600169 metric = output_proto.success.value.add()
170 metric.name = "foo-test"
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700171
LaMont Jonesed9a5de2021-02-03 11:06:12 -0700172
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700173@faux.success(_BundleFirmwareArtifactsResponse)
174@faux.empty_completed_unsuccessfully_error
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700175@validate.validation_complete
176def BundleFirmwareArtifacts(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600177 """Runs all of the firmware tests at the specified location."""
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700178
Alex Klein1699fab2022-09-08 08:46:06 -0600179 if len(input_proto.artifacts.output_artifacts) > 1:
180 raise ValueError("Must have exactly one output_artifact entry")
LaMont Jonesdec69ad2021-01-27 13:02:00 -0700181
Alex Klein1699fab2022-09-08 08:46:06 -0600182 with osutils.TempDir(delete=False) as tmpdir:
183 info = input_proto.artifacts.output_artifacts[0]
184 metadata_path = os.path.join(tmpdir, "firmware_metadata.jsonpb")
185 args = []
186 if input_proto.artifacts.FIRMWARE_LCOV in info.artifact_types:
187 args += ["--code-coverage"]
188 resp = _call_entry(
189 info.location,
190 None,
191 "bundle",
192 *args,
193 output_dir=tmpdir,
194 metadata=metadata_path,
195 )
196 file_paths = []
197 if os.path.exists(metadata_path):
198 with open(metadata_path, "r") as f:
199 metadata = json_format.Parse(
200 f.read(), firmware_pb2.FirmwareArtifactInfo()
201 )
202 else:
203 metadata = firmware_pb2.FirmwareArtifactInfo()
204 if input_proto.artifacts.FIRMWARE_TARBALL_INFO in info.artifact_types:
205 output_proto.artifacts.artifacts.add(
206 artifact_type=input_proto.artifacts.FIRMWARE_TARBALL_INFO,
207 location=info.location,
208 paths=[
209 common_pb2.Path(
210 path=metadata_path, location=common_pb2.Path.INSIDE
211 )
212 ],
213 )
LaMont Jones9a0594d2021-03-17 15:51:20 -0600214
Alex Klein1699fab2022-09-08 08:46:06 -0600215 full_path = lambda x: common_pb2.Path(
216 path=os.path.join(tmpdir, x.file_name),
217 location=common_pb2.Path.INSIDE,
218 )
LaMont Jones9a0594d2021-03-17 15:51:20 -0600219
Alex Klein1699fab2022-09-08 08:46:06 -0600220 for typ, name in (
221 (input_proto.artifacts.FIRMWARE_TARBALL, "tarball_info"),
222 (input_proto.artifacts.FIRMWARE_LCOV, "lcov_info"),
223 (input_proto.artifacts.CODE_COVERAGE_HTML, "coverage_html"),
224 ):
225 file_paths = [
226 full_path(x)
227 for x in metadata.objects
228 if x.WhichOneof("firmware_object_info") == name
229 ]
230 if file_paths and typ in info.artifact_types:
231 output_proto.artifacts.artifacts.add(
232 artifact_type=typ, paths=file_paths, location=info.location
233 )
LaMont Jones9a0594d2021-03-17 15:51:20 -0600234
Alex Klein1699fab2022-09-08 08:46:06 -0600235 return resp