blob: 4a9426abdc2cf4509fc099064bf912741be8c3ed [file] [log] [blame]
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07003#
4# Copyright 2020 The Chromium OS Authors. All rights reserved.
5# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7#
Paul Fagerburge868e832020-01-22 17:14:04 -07008"""Create a new variant of an existing reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -07009
10This program will call all of the scripts that create the various pieces
11of a new variant. For example to create a new variant of the hatch base
12board, the following scripts are called:
13
Paul Fagerburg5f794bf2020-02-12 13:01:36 -070014* platform/dev/contrib/variant/create_coreboot_variant.sh
Paul Fagerburg3b534f92019-11-07 15:05:22 -070015* platform/dev/contrib/variant/create_coreboot_config.sh
16* private-overlays/baseboard-hatch-private/sys-boot/
17 * coreboot-private-files-hatch/files/add_fitimage.sh
18 * coreboot-private-files-hatch/asset_generation/gen_fit_image.sh
19 * Outside the chroot, because it uses WINE to run the FIT tools
20* platform/dev/contrib/variant/create_initial_ec_image.sh
21* platform/dev/contrib/variant/add_variant_to_yaml.sh
22* private-overlays/overlay-hatch-private/chromeos-base/
23 * chromeos-config-bsp-hatch-private/add_variant.sh
24
25Once the scripts are done, the following repos have changes
26
27* third_party/coreboot
28* third_party/chromiumos-overlay
29* private-overlays/baseboard-hatch-private
30* platform/ec
31* private-overlays/overlay-hatch-private
32* overlays
33
Paul Fagerburgba508a02020-11-20 14:40:55 -070034The program has support for multiple reference boards, so the repos,
35directories, and scripts above can change depending on what the reference
36board is.
Paul Fagerburg3b534f92019-11-07 15:05:22 -070037"""
38
39from __future__ import print_function
40import argparse
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070041import importlib
Paul Fagerburg8d850932020-02-25 14:13:32 -070042import json
Paul Fagerburg3b534f92019-11-07 15:05:22 -070043import logging
44import os
Paul Fagerburg3b534f92019-11-07 15:05:22 -070045import subprocess
46import sys
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -060047from chromite.lib import build_target_lib
48from chromite.lib import cros_build_lib
Paul Fagerburg1d043c32020-02-03 08:57:08 -070049from chromite.lib import git
Paul Fagerburga8c7e342020-02-25 13:30:49 -070050from chromite.lib import gerrit
Paul Fagerburga95dd162020-03-24 16:27:18 -060051from chromite.lib import osutils
Paul Fagerburg75398072020-03-16 13:51:24 -060052from chromite.lib import workon_helper
Paul Fagerburg8d850932020-02-25 14:13:32 -070053import requests
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070054import step_names
Paul Fagerburg3b534f92019-11-07 15:05:22 -070055import variant_status
56
57
58def main():
Paul Fagerburge868e832020-01-22 17:14:04 -070059 """Create a new variant of an existing reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -070060
61 This program automates the creation of a new variant of an existing
Paul Fagerburge868e832020-01-22 17:14:04 -070062 reference board by calling various scripts that copy the reference board,
63 modify files for the new variant, stage commits, and upload to gerrit.
Paul Fagerburg3b534f92019-11-07 15:05:22 -070064
65 Note that one of the following is required:
66 * --continue
67 * --board=BOARD --variant=VARIANT [--bug=BUG]
68 """
Paul Fagerburgfc4450e2020-11-20 14:43:14 -070069 board, variant, bug, branch, continue_flag, abort_flag = get_args()
Paul Fagerburg3b534f92019-11-07 15:05:22 -070070
Paul Fagerburg042a5252020-03-16 21:49:18 -060071 if not check_flags(board, variant, bug, continue_flag, abort_flag):
Paul Fagerburg3b534f92019-11-07 15:05:22 -070072 return False
73
Paul Fagerburgfc4450e2020-11-20 14:43:14 -070074 status = get_status(board, variant, bug, branch, continue_flag, abort_flag)
Paul Fagerburg3b534f92019-11-07 15:05:22 -070075 if status is None:
76 return False
77
78 status.load()
79
Paul Fagerburg5f794bf2020-02-12 13:01:36 -070080 # Where is new_variant.py located?
81 status.my_loc = os.path.dirname(os.path.abspath(__file__))
82
Paul Fagerburg042a5252020-03-16 21:49:18 -060083 # If the user specified --abort, override the current step.
84 if abort_flag:
85 status.step = step_names.ABORT
86
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070087 while status.step is not None:
Paul Fagerburg3b534f92019-11-07 15:05:22 -070088 status.save()
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070089 if not perform_step(status):
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -060090 logging.debug('perform_step %s returned False; exiting ...',
91 status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -070092 return False
93
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070094 move_to_next_step(status)
Paul Fagerburg3b534f92019-11-07 15:05:22 -070095
96 return True
97
98
99def get_args():
100 """Parse the command-line arguments
101
102 There doesn't appear to be a way to specify that --continue is
103 mutually exclusive with --board, --variant, and --bug. As a result,
104 all arguments are optional, and another function will apply the logic
105 to check if there is an illegal combination of arguments.
106
107 Returns a list of:
Paul Fagerburge868e832020-01-22 17:14:04 -0700108 board Name of the reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700109 variant Name of the variant being created
110 bug Text for bug number, if any ('None' otherwise)
111 continue_flag Flag if --continue was specified
112 """
113 parser = argparse.ArgumentParser(
114 description=main.__doc__,
115 formatter_class=argparse.RawTextHelpFormatter)
Paul Fagerburge868e832020-01-22 17:14:04 -0700116 parser.add_argument('--board', type=str, help='Name of the reference board')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700117 parser.add_argument(
118 '--variant', type=str, help='Name of the new variant to create')
119 parser.add_argument(
120 '--bug', type=str, help='Bug number to reference in commits')
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700121 parser.add_argument(
122 '--branch', type=str, help='Value for BRANCH= in commit messages')
Paul Fagerburgba508a02020-11-20 14:40:55 -0700123 # Use a group so that we can enforce mutually-exclusive arguments.
Paul Fagerburg2e48a192020-03-24 17:57:05 -0600124 # argparse does not support nesting groups, so we can't put board,
125 # variant, and bug into a group and have that group as another mutually
126 # exclusive option.
127 group = parser.add_mutually_exclusive_group()
128 group.add_argument(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700129 '--continue', action='store_true',
130 dest='continue_flag', help='Continue the process from where it paused')
Paul Fagerburg2e48a192020-03-24 17:57:05 -0600131 group.add_argument(
Paul Fagerburg042a5252020-03-16 21:49:18 -0600132 '--abort', action='store_true',
133 dest='abort_flag', help='Cancel the process and abandon all commits')
134 parser.add_argument(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700135 '--verbose', action='store_true',
136 dest='verbose_flag', help='Enable verbose output of progress')
137 args = parser.parse_args()
138
139 if args.verbose_flag:
140 logging.basicConfig(level=logging.DEBUG)
141 else:
142 logging.basicConfig(level=logging.INFO)
143
144 board = args.board
145 if board is not None:
146 board = board.lower()
147
148 variant = args.variant
149 if variant is not None:
150 variant = variant.lower()
151
152 bug = args.bug or 'None'
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700153 branch = args.branch or 'None'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700154
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700155 return (board, variant, bug, branch, args.continue_flag, args.abort_flag)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700156
157
Paul Fagerburg042a5252020-03-16 21:49:18 -0600158def check_flags(board, variant, bug, continue_flag, abort_flag):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700159 """Check the flags to ensure no invalid combinations
160
161 We allow any of the following:
Paul Fagerburg042a5252020-03-16 21:49:18 -0600162 --abort
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700163 --continue
164 --board=board_name --variant=variant_name
165 --board=board_name --variant=variant_name --bug=bug_text
166
167 The argument parser does have the functionality to represent the
168 combination of --board and --variant as a single mutually-exclusive
169 argument, so we have to use this function to do the checking.
170
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700171 Args:
172 board: Name of the reference board
173 variant: Name of the variant being created
174 bug: Text for bug number, if any ('None' otherwise)
175 continue_flag: Flag if --continue was specified
Paul Fagerburg042a5252020-03-16 21:49:18 -0600176 abort_flag: Flag if --abort was specified
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700177
178 Returns:
179 True if the arguments are acceptable, False otherwise
180 """
Paul Fagerburg042a5252020-03-16 21:49:18 -0600181 # If either --abort or --continue is set, then disallow any of the
182 # board name, variant name, or bug number to be set.
183 if continue_flag or abort_flag:
184 if board is not None or variant is not None or bug != 'None':
185 logging.error('Do not use --board, --variant, or --bug with '
186 '--continue or --abort')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700187 return False
Paul Fagerburg042a5252020-03-16 21:49:18 -0600188 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700189
Paul Fagerburg042a5252020-03-16 21:49:18 -0600190 # At this point, neither --continue nor --abort are set, so we must have
191 # both --board and --variant values.
192 if board is None or variant is None:
193 logging.error('Both --board and --variant must be specified')
194 return False
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700195
196 return True
197
198
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700199def get_status(board, variant, bug, branch, continue_flag, abort_flag):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700200 """Create the status file or get the previous status
201
202 This program can stop at several places as we have to wait for CLs
203 to work through CQ or be upstreamed into the chromiumos tree, so just
204 like a git cherry-pick, there is a --continue option to pick up where
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700205 you left off by reading a specially-named status file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700206
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700207 If --continue is specified, the status file must exist.
208 If the status file exists, then --continue must be specified.
209 When --continue is specified, we read the status file and return
210 with the contents.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700211
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700212 If the status file does not exist, we will create the state file with
213 the board, variant, and (optional) bug details.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700214
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700215 To decouple the list of boards supported from this main program, we
Paul Fagerburge868e832020-01-22 17:14:04 -0700216 try to import a module with the same name as the reference board,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700217 so --board=hatch means that we import hatch.py. If we can't import
Paul Fagerburge868e832020-01-22 17:14:04 -0700218 the file, then we don't support that reference board.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700219
220 The board-specific module will set several variables, which we will
221 copy into the object that we return.
222
Paul Fagerburge868e832020-01-22 17:14:04 -0700223 * base - the name of the base board, such as Hatch, Volteer, or Zork.
224 This can be different from the reference board, e.g. the Trembyle
225 reference board in the Zork project.
Paul Fagerburg3ef1cbb2020-06-02 16:38:36 -0600226 * coreboot_base - the name of the base board in coreboot. Usually the same
227 as base, but can differ, e.g. for Puff, the base is Puff, but the
228 coreboot_base is Hatch because Puff is based on Hatch.
Paul Fagerburg4d343452020-01-24 15:23:53 -0700229 * coreboot_dir - base directory for coreboot, usually third_party/coreboot
230 but could differ for processors that use a private repo
Matt Ziegelbaum3e879df2020-11-09 16:08:44 -0500231 * coreboot_reference - the name of the reference board that we're using to
232 make the variant. This can be different from base (e.g. Ambassador is
233 its own board, but uses Puff as a coreboot reference).
Paul Fagerburg4d343452020-01-24 15:23:53 -0700234 * cb_config_dir - base directory for coreboot configs, usually
235 third_party/chromiumos-overlay/sys-boot/coreboot/files/configs but
236 could differ for processors that use a private repo
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700237 * step_list - list of steps (named in step_names.py) to run in sequence
Paul Fagerburge868e832020-01-22 17:14:04 -0700238 to create the new variant of the reference board
239 * fsp - package name for FSP. This may be None, depending on the
240 processor on the reference board
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700241 * fitimage_pkg - package name for the fitimage
Paul Fagerburg4039c152021-02-18 13:58:44 -0700242 * fitimage_dir - base directory for fitimage; prepend
243 '/mnt/host/source/src/' in chroot, prepend '~/chromiumos/src' outside
244 the chroot
245 * fitimage_bin_dir - directory under fitimage_dir where the fitimage
246 binary will be
247 * fitimage_versions_dir - directory under fitimage_dir where the fitimage
248 versions file will be, defaults to the value of fitimage_bin_dir
249 * fitimage_bin - name of the fitimage binary to format with the variant
250 name, default of `fitimage-%s.bin`
Nick Vaccarof8d4e932020-12-11 01:52:23 +0000251 * fitimage_cmd - explanation of gen_fit_image command, i.e. tell the user
252 how to run gen_fit_image.sh
253 * fitimage_script - script to add fitimage sources, defaults
254 to 'files/add_fitimage.sh' if not present. Only volteer and volteer2
255 currently need to use this.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700256 * workon_pkgs - list of packages to cros_workon
257 * emerge_cmd - the emerge command, e.g. 'emerge-hatch'
258 * emerge_pkgs - list of packages to emerge
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600259 * config_workon_pkgs - list of packages to cros_workon to build the
260 project config
261 * config_emerge_pkgs - list of packages to emerge to build the project
262 config
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700263 * private_yaml_dir - directory for the private yaml file
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700264 * commits - map of commits for the various steps. Indexed by step name,
265 and the step names used are the same ones in step_names.py
266 * repo_upload_list - list of commits to upload using `repo upload`
267 * coreboot_push_list - list of commits to upload using `git push` to
268 coreboot
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600269 * depends - maps a step to a list of steps on which it depends, e.g.
270 depends[step_names.ADD_PRIV_YAML] is a list of other steps that
271 the 'add_priv_yaml' step depends on. This map is used to amend
272 the commits with CL numbers for Cq-Depend.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700273
274 Additionally, the following fields will be set:
275
Nick Vaccarof8d4e932020-12-11 01:52:23 +0000276 * board - the name of the reference board, e.g. 'hatch'. The --board
277 command line flag specifies this value, and new_variant derives the
278 the name of the python module to load from this value. However, in
279 certain cases (volteer2), the python module may override this value
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700280 * variant - the name of the variant, e.g. 'sushi'
281 * bug - optional text for a bug ID, used in the git commit messages.
282 Could be 'None' (as text, not the python None), or something like
283 'b:12345' for buganizer, or 'chromium:12345'
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700284 * branch - optional text for a BRANCH= value in the commit message for
285 repos that use the BRANCH field (coreboot and EC). If not specified,
286 then None.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700287 * step - internal state tracking, what step of the variant creation
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700288 we are at.
289 * yaml_file - internal, just the name of the file where all this data
290 gets saved.
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700291 * commit - a map of maps that tracks all of the git commit and gerrit CL
292 data for each of the steps in the process. For example,
293 status.commit['add_priv_yaml'] is a map that has all the information
294 about the 'add_priv_yaml' step. The keys in the maps allow us to
295 determine where the commit is, the change_id, if it has been uploaded
296 to gerrit and where.
297
298 branch_name - the name of the git branch
299 change_id - the change-id assigned by the commit hook. Gerrit
300 uses the change_id to track new patchsets in the CL
301 dir - the directory where the commit has been created
302 gerrit - the name of the gerrit instance to which the CL has
303 been uploaded, one of 'chromium', 'chrome-internal', or
304 'coreboot'
305 cl_number - the CL number on the gerrit instance
306
307 When the commit is created, branch_name, change_id, and dir are all
308 set. The gerrit and cl_number keys are not set until the CL has been
309 uploaded to a gerrit instance.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700310
311 These data might come from the status file (because we read it), or
312 they might be the initial values after we created the file (because
313 it did not already exist).
314
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700315 Args:
316 board: Name of the reference board
317 variant: Name of the variant being created
318 bug: Text for bug number, if any ('None' otherwise)
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700319 branch: Text for a BRANCH= value in the commit message (or 'None')
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700320 continue_flag: Flag if --continue was specified
Paul Fagerburg042a5252020-03-16 21:49:18 -0600321 abort_flag: Flag if --abort was specified
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700322
323 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700324 variant_status object with all the data mentioned above
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700325 """
326 status = variant_status.variant_status()
Paul Fagerburg042a5252020-03-16 21:49:18 -0600327 if continue_flag or abort_flag:
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700328 if status.yaml_file_exists():
Paul Fagerburg042a5252020-03-16 21:49:18 -0600329 return status
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600330
331 if continue_flag:
332 op = '--continue'
333 if abort_flag:
334 op = '--abort'
335 logging.error('%s does not exist; cannot %s', status.yaml_file, op)
336 return None
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700337
Paul Fagerburg042a5252020-03-16 21:49:18 -0600338 # If we get here, the user provided --board and --variant (because
339 # check_flags() returned Trued), but the yaml file already exists,
340 # so we print an error message and bail.
341 if status.yaml_file_exists():
342 logging.error(
343 'new_variant already in progress; did you forget --continue?')
344 return None
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700345
Paul Fagerburg042a5252020-03-16 21:49:18 -0600346 # At this point, it's not --continue, not --abort, the yaml file doesn't
347 # exist, and we have valid values for --board, --variant, and --bug (bug
348 # might be the default value of "None"). Create the yaml file with data
349 # from the reference board's loadable module.
350 status.board = board
351 status.variant = variant
352 status.bug = bug
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700353 status.branch = branch
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700354
Paul Fagerburg042a5252020-03-16 21:49:18 -0600355 # Load the appropriate module and copy all the data from it.
356 try:
357 module = importlib.import_module(board)
358 except ImportError:
359 print('Unsupported board "' + board + '"')
360 sys.exit(1)
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700361
Nick Vaccarof8d4e932020-12-11 01:52:23 +0000362 # Special case: allow the module to override the name of the reference
363 # board. Almost always, you want the module name (e.g. puff.py) and the
364 # name of the reference board (e.g. puff) to match. However, for some
365 # boards (volteer2), you want to have the reference board name still
366 # be 'volteer'.
367 status.board = getattr(module, 'board', status.board)
368
Paul Fagerburg042a5252020-03-16 21:49:18 -0600369 status.base = module.base
Paul Fagerburg3ef1cbb2020-06-02 16:38:36 -0600370 status.coreboot_base = getattr(module, 'coreboot_base', module.base)
Paul Fagerburg042a5252020-03-16 21:49:18 -0600371 status.coreboot_dir = module.coreboot_dir
Paul Fagerburgba508a02020-11-20 14:40:55 -0700372 status.coreboot_reference = getattr(module, 'coreboot_reference',
373 status.board)
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600374 status.cb_config_dir = getattr(module, 'cb_config_dir', None)
Paul Fagerburg042a5252020-03-16 21:49:18 -0600375 status.emerge_cmd = module.emerge_cmd
376 status.emerge_pkgs = module.emerge_pkgs
Paul Fagerburg4039c152021-02-18 13:58:44 -0700377 status.fitimage_bin = getattr(module, 'fitimage_bin',
378 'fitimage-%s.bin')
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600379 status.fitimage_dir = getattr(module, 'fitimage_dir', None)
Paul Fagerburg4039c152021-02-18 13:58:44 -0700380 status.fitimage_bin_dir = getattr(module, 'fitimage_bin_dir', None)
381 status.fitimage_versions_dir= getattr(module, 'fitimage_versions_dir',
382 status.fitimage_bin_dir)
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600383 status.fitimage_pkg = getattr(module, 'fitimage_pkg', None)
384 status.fitimage_cmd = getattr(module, 'fitimage_cmd', None)
Nick Vaccarof8d4e932020-12-11 01:52:23 +0000385 status.fitimage_script = getattr(module, 'fitimage_script',
386 'files/add_fitimage.sh')
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600387 status.fsp = getattr(module, 'fsp', None)
388 status.private_yaml_dir = getattr(module, 'private_yaml_dir', None)
Paul Fagerburg042a5252020-03-16 21:49:18 -0600389 status.step_list = module.step_list
390 status.workon_pkgs = module.workon_pkgs
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600391 status.config_workon_pkgs = module.config_workon_pkgs
392 status.config_emerge_pkgs = module.config_emerge_pkgs
Paul Fagerburg042a5252020-03-16 21:49:18 -0600393 status.coreboot_push_list = module.coreboot_push_list
394 status.repo_upload_list = module.repo_upload_list
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600395 status.depends = module.depends
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700396
Paul Fagerburg042a5252020-03-16 21:49:18 -0600397 # Start at the first entry in the step list
398 status.step = status.step_list[0]
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700399
Paul Fagerburg042a5252020-03-16 21:49:18 -0600400 # Start an empty map for tracking CL data
401 status.commits = {}
402
403 status.save()
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700404
405 return status
406
407
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700408def perform_step(status):
409 """Call the appropriate function for the current step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700410
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700411 Args:
412 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700413
414 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700415 True if the step succeeded, False if it failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700416 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700417 # Function to call based on the step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700418 dispatch = {
Paul Fagerburgbc184242020-04-28 17:00:54 -0600419 step_names.PROJECT_CONFIG: project_config,
Paul Fagerburg8f60b802020-06-09 15:38:28 -0600420 step_names.FW_BUILD_CONFIG: fw_build_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700421 step_names.CB_VARIANT: create_coreboot_variant,
422 step_names.CB_CONFIG: create_coreboot_config,
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700423 step_names.CRAS_CONFIG: copy_cras_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700424 step_names.ADD_FIT: add_fitimage,
425 step_names.GEN_FIT: gen_fit_image_outside_chroot,
426 step_names.COMMIT_FIT: commit_fitimage,
427 step_names.EC_IMAGE: create_initial_ec_image,
428 step_names.EC_BUILDALL: ec_buildall,
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700429 step_names.ADD_PUB_YAML: add_variant_to_public_yaml,
430 step_names.ADD_PRIV_YAML: add_variant_to_private_yaml,
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600431 step_names.BUILD_CONFIG: build_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700432 step_names.EMERGE: emerge_all,
433 step_names.PUSH: push_coreboot,
434 step_names.UPLOAD: upload_CLs,
435 step_names.FIND: find_coreboot_upstream,
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600436 step_names.CALC_CQ_DEPEND: calc_cq_depend,
437 step_names.ADD_CQ_DEPEND: add_cq_depend,
438 step_names.RE_UPLOAD: re_upload,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700439 step_names.CLEAN_UP: clean_up,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600440 step_names.ABORT: abort,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700441 }
442
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700443 if status.step not in dispatch:
444 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700445 sys.exit(1)
446
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700447 return dispatch[status.step](status)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700448
449
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700450def move_to_next_step(status):
451 """Move to the next step in the list
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700452
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700453 Args:
454 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700455 """
Paul Fagerburg042a5252020-03-16 21:49:18 -0600456 # Special case: the next step after 'abort' is 'clean_up'. Always.
457 if status.step == step_names.ABORT:
458 status.step = step_names.CLEAN_UP
459 return
460
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700461 if status.step not in status.step_list:
462 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700463 sys.exit(1)
464
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700465 idx = status.step_list.index(status.step)
466 if idx == len(status.step_list)-1:
467 status.step = None
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700468 else:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700469 status.step = status.step_list[idx+1]
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700470
471
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700472def run_process(args, cwd=None, env=None, capture_output=False):
473 """Run a process, log debug messages, return text output of process
474
475 The capture_output parameter allows us to capture the output when we
476 care about it (and not sending it to the screen), or ignoring it when
477 we don't care, and letting the user see the output so they know that
478 the build is still running, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700479
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700480 Args:
481 args: List of the command and its params
482 cwd: If not None, cd to this directory before running
483 env: Environment to use for execution; if needed, get os.environ.copy()
484 and add variables. If None, just use the current environment
485 capture_output: True if we should capture the stdout, false if we
486 just care about success or not.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700487
488 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700489 If capture_output == True, we return the text output from running
490 the subprocess as a list of lines, or None if the process failed.
491 If capture_output == False, we return a True if it successed, or
492 None if the process failed.
493
494 The caller can evaluate as a bool, because bool(None) == False, and
495 bool() of a non-empty list is True, or the caller can use the returned
496 text for further processing.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700497 """
498 logging.debug('Run %s', str(args))
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700499 if cwd is not None:
500 logging.debug('cwd = %s', cwd)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700501 try:
502 if capture_output:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700503 output = subprocess.run(args, cwd=cwd, env=env, check=True,
504 stderr=subprocess.STDOUT, stdout=subprocess.PIPE).stdout
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700505 else:
506 subprocess.run(args, cwd=cwd, env=env, check=True)
507 # Just something to decode so we don't get an empty list
508 output = b'True'
509
510 logging.debug('process returns 0')
511 # Convert from byte string to ASCII
512 decoded = output.decode('utf-8')
513 # Split into array of individual lines
514 lines = decoded.split('\n')
515 return lines
516 except subprocess.CalledProcessError as err:
517 logging.debug('process returns %s', str(err.returncode))
518 return None
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700519
520
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700521def get_git_commit_data(cwd):
522 """Get the branch name and change id of the current commit
523
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700524 Args:
525 cwd: The current working directory, where we want to get the branch
526 name and change id
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700527
528 Returns:
529 Map with 'dir', 'branch_name' and 'change_id' keys. The 'dir'
530 key maps to the value of os.path.expanduser(cwd)
531 """
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700532 cwd = git.FindGitTopLevel(os.path.expanduser(cwd))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700533 logging.debug('get_git_commit_data(%s)', cwd)
534
535 branch_name = git.GetCurrentBranch(cwd)
536 if branch_name is None:
537 logging.error('Cannot determine git branch name in %s; exiting', cwd)
538 sys.exit(1)
539 logging.debug('git current branch is %s', branch_name)
540
541 change_id = git.GetChangeId(cwd)
542 if change_id is None:
543 logging.error('Cannot determine Change-Id in %s; exiting', cwd)
544 sys.exit(1)
545 logging.debug('git Change-Id is %s', change_id)
546
547 return {
548 'dir': cwd,
549 'branch_name': branch_name,
550 'change_id': change_id
551 }
552
553
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600554def change_id_to_sha(git_repo, change_id):
555 """Find the SHA for a given Change-Id.
556
557 Args:
558 git_repo: Directory of git repository.
559 change_id: The Change-Id to search for.
560
561 Returns:
562 The SHA hash for the Change-Id if only one commit is found.
563 None if the Change-Id was not found.
564 Raises a ValueError if more than one commit is found with the
565 same Change-Id.
566 """
567 output = git.Log(git_repo, max_count=1, format='format:%H',
568 grep=fr'^Change-Id: {change_id}$')
569 sha_hashes = output.splitlines()
570 if not sha_hashes:
571 return None
572 if len(sha_hashes) > 1:
573 raise ValueError('More than one SHA with that Change-Id found')
574 return sha_hashes[0]
575
576
577def get_commit_msg(git_repo, rev):
578 """Get the commit message for a given revision.
579
580 Because git.Log doesn't allow specifying check=False or getting the
581 returncode, we have to catch the CalledProcessError instead.
582
583 Args:
584 git_repo: Directory of git repository.
585 rev: The revision to search for, a SHA or a label.
586
587 Returns:
588 The commit message as a list of strings, if the revision exists.
589 None if the revision was not found.
590 """
591 try:
592 msg = git.Log(git_repo, max_count=1, format='format:%B', rev=rev)
593 return msg.splitlines()
Paul Fagerburgba508a02020-11-20 14:40:55 -0700594 except cros_build_lib.CalledProcessError as err:
595 raise ValueError('SHA was not found') from err
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600596
597
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600598def emerge_with_workon(status, workon_pkgs, emerge_cmd, emerge_pkgs, env=None):
599 """Emerge a list of packages after `cros_workon start`ing them
600
601 This function will `cros_workon start` a list of packages, then `emerge`
602 another list of packages, and finally, `cros_workon stop` only those
603 packages that were actually started by the `cros_workon start` command.
604 Any package already in a `cros_workon start` state prior to this function
605 will still be in that state when this function exits.
606
607 To determine which packages this program started and which ones were
608 already started, we query the list of packages being worked on, then
609 cros_workon start the entire list (which will produce a "package already
610 being worked on" type of message for anything already started), and then
611 query the list of packages being worked on again. The difference between
612 the before and after lists are the packages that this program started,
613 and so that's the list of packages to cros_workon stop after the emerge
614 is done.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700615
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700616 Args:
617 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600618 workon_pkgs: list of packages to `cros_workon start`
619 emerge_cmd: the emerge command to run, e.g. 'emerge-volteer'
620 emerge_pkgs: list of packages to `emerge`
621 env: environment to pass to run_process, or None to pass default
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700622
623 Returns:
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600624 True if everything succeeded, False if something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700625 """
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600626 # Get the list of packages that are already cros_workon started.
627 build_target = build_target_lib.BuildTarget(status.base)
628 workon = workon_helper.WorkonHelper(build_target.root, build_target.name)
629 before_workon = workon.ListAtoms()
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700630
Paul Fagerburg4376fe52020-06-16 10:22:35 -0600631 # Only cros_workon start if the list is non-empty
632 if workon_pkgs:
633 workon.StartWorkingOnPackages(workon_pkgs)
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600634
635 # Determine which packages we need to cros_workon stop.
636 after_workon = workon.ListAtoms()
637 stop_packages = list(set(after_workon) - set(before_workon))
638
639 # Run the emerge command.
640 emerge_result = run_process([emerge_cmd] + emerge_pkgs, env=env)
641
Paul Fagerburg4376fe52020-06-16 10:22:35 -0600642 # If the list is non-empty, cros_workon stop before returning the result.
643 if stop_packages:
644 workon.StopWorkingOnPackages(stop_packages)
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600645
646 return emerge_result
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700647
648
Paul Fagerburgbc184242020-04-28 17:00:54 -0600649def project_config(status):
650 """Check if the project config is correct and complete
651
652 For programs that use the new project/config structure with starlark
653 configuration files, this function will check that emerging the
654 project's configuration will result in a project-config.json that
655 includes the new name of the new hwdesign (a.k.a. "variant").
656
657 Args:
658 status: variant_status object tracking our board, variant, etc.
659
660 Returns:
661 True if everything succeeded, False if something failed
662 """
663 logging.info('Running step project_config')
664 try:
665 if not emerge_with_workon(status, status.config_workon_pkgs,
666 status.emerge_cmd, status.config_emerge_pkgs):
Paul Fagerburgba508a02020-11-20 14:40:55 -0700667 raise RuntimeError('Building the configuration failed.')
Paul Fagerburgbc184242020-04-28 17:00:54 -0600668
669 # Make sure project-config.json exists in the /build tree
670 emerged_json = os.path.join(
671 '/build',
672 status.base,
673 'usr/share/chromeos-config/yaml/project-config.json')
674 if not os.path.isfile(emerged_json):
675 raise RuntimeError(
676 f'project-config.json {emerged_json} does not exist.')
677
Paul Fagerburgacb0c552020-12-21 18:24:18 -0700678 # Search the JSON for a config with the new variant's name
679 with open(emerged_json, 'r') as fp:
680 pc = json.load(fp)
681
682 names = {config['name'] for config in pc['chromeos']['configs']}
683 if status.variant not in names:
Paul Fagerburgbc184242020-04-28 17:00:54 -0600684 raise RuntimeError(
685 f'variant name {status.variant} not found in {emerged_json}')
686
687 except RuntimeError as e:
688 logging.error(str(e))
689 logging.error('Please file a bug in Infra > ChromeOS > Product > Device'
690 ' to have the project configuration updated.')
691 logging.error('(https://bugs.chromium.org/p/chromium/issues/list?'
692 'q=component:Infra%3EChromeOS%3EProduct%3EDevice)')
693 return False
694
695 return True
696
697
Paul Fagerburg8f60b802020-06-09 15:38:28 -0600698def fw_build_config(status):
699 """Add the _FW_BUILD_CONFIG setting to the project config
700
701 For programs that use the new project/config structure with starlark
702 configuration files, this function calls fw_build_config.sh, which will
703 modify the config.star file to have a default _FW_BUILD_CONFIG entry.
704
705 Args:
706 status: variant_status object tracking our board, variant, etc.
707
708 Returns:
709 True if everything succeeded, False if something failed
710 """
711 logging.info('Running step fw_build_config')
712 fw_build_config_sh = os.path.join(status.my_loc, 'fw_build_config.sh')
713 rc = run_process(
714 [fw_build_config_sh,
715 status.base,
716 status.variant,
717 status.bug])
718 if rc:
719 status.commits[step_names.FW_BUILD_CONFIG] = get_git_commit_data(
Paul Fagerburgba508a02020-11-20 14:40:55 -0700720 os.path.join('/mnt/host/source/src/project',
721 status.base, status.variant))
Paul Fagerburg8f60b802020-06-09 15:38:28 -0600722 return rc
723
724
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700725def create_coreboot_variant(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700726 """Create source files for a new variant of the reference board in coreboot
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700727
728 This function calls create_coreboot_variant.sh to set up a new variant
Paul Fagerburge868e832020-01-22 17:14:04 -0700729 of the reference board.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700730
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700731 Args:
732 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700733
734 Returns:
735 True if everything succeeded, False if something failed
736 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700737 logging.info('Running step create_coreboot_variant')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700738 cb_src_dir = os.path.join('/mnt/host/source/src/', status.coreboot_dir)
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700739 environ = {**os.environ, 'CB_SRC_DIR': cb_src_dir,
740 'NEW_VARIANT_BRANCH': status.branch}
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700741 create_coreboot_variant_sh = os.path.join(status.my_loc,
742 'create_coreboot_variant.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600743 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700744 [create_coreboot_variant_sh,
Paul Fagerburg3ef1cbb2020-06-02 16:38:36 -0600745 status.coreboot_base,
Matt Ziegelbaum3e879df2020-11-09 16:08:44 -0500746 status.coreboot_reference,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700747 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600748 status.bug], env=environ)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700749 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700750 status.commits[step_names.CB_VARIANT] = get_git_commit_data(cb_src_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700751 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700752
753
754def create_coreboot_config(status):
755 """Create a coreboot configuration for a new variant
756
757 This function calls create_coreboot_config.sh, which will make a copy
758 of coreboot.${BOARD} into coreboot.${VARIANT}.
759
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700760 Args:
761 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700762
763 Returns:
764 True if the script and test build succeeded, False if something failed
765 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700766 logging.info('Running step create_coreboot_config')
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600767 # Only set CB_CONFIG_DIR if it's not None, so here we have to copy
768 # the environment first and then optionally add a key.
Paul Fagerburg4d343452020-01-24 15:23:53 -0700769 environ = os.environ.copy()
770 if status.cb_config_dir is not None:
771 environ['CB_CONFIG_DIR'] = status.cb_config_dir
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700772 create_coreboot_config_sh = os.path.join(status.my_loc,
773 'create_coreboot_config.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600774 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700775 [create_coreboot_config_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700776 status.base,
Matt Ziegelbaum3e879df2020-11-09 16:08:44 -0500777 status.coreboot_reference,
Paul Fagerburge868e832020-01-22 17:14:04 -0700778 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600779 status.bug], env=environ)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700780 if rc:
781 # Use status.cb_config_dir if defined, or if not, use
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700782 # '/mnt/host/source/src/third_party/chromiumos-overlay'
Paul Fagerburgba508a02020-11-20 14:40:55 -0700783 cb_config_dir = os.path.join(
784 '/mnt/host/source/src/',
785 status.cb_config_dir or 'third_party/chromiumos-overlay')
786 status.commits[step_names.CB_CONFIG] = get_git_commit_data(
787 cb_config_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700788 return rc
Paul Fagerburge868e832020-01-22 17:14:04 -0700789
790
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700791def copy_cras_config(status):
792 """Copy the cras config for a new variant
793
794 This is only necessary for the Zork baseboard right now.
795 This function calls copy_cras_config.sh, which will copy the
Paul Fagerburgba508a02020-11-20 14:40:55 -0700796 cras config in overlays/overlay-${BASE}/chromeos-base/\
797 chromeos-bsp-${BASE}/files/cras-config/${BASE} to .../${VARIANT}
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700798
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700799 Args:
800 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700801
802 Returns:
803 True if the script and test build succeeded, False if something failed
804 """
805 logging.info('Running step copy_cras_config')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700806 copy_cras_config_sh = os.path.join(status.my_loc, 'copy_cras_config.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600807 rc = run_process(
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700808 [copy_cras_config_sh,
809 status.base,
810 status.board,
811 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600812 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700813 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700814 status.commits[step_names.CRAS_CONFIG] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700815 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700816 return rc
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700817
818
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700819def add_fitimage(status):
820 """Add the source files for a fitimage for the new variant
821
822 This function calls add_fitimage.sh to create a new XSL file for the
Paul Fagerburge868e832020-01-22 17:14:04 -0700823 variant's fitimage, which can override settings from the reference board's
824 XSL. When this is done, the user will have to build the fitimage by running
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700825 gen_fit_image.sh outside of the chroot (and outside of this program's
826 control) because gen_fit_image.sh uses WINE, which is not installed in
827 the chroot. (There is a linux version of FIT, but it requires Open GL,
828 which is also not installed in the chroot.)
829
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700830 Args:
831 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700832
833 Returns:
834 True if the script succeeded, False otherwise
835 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700836 logging.info('Running step add_fitimage')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700837 add_fitimage_sh = os.path.expanduser(os.path.join(
Nick Vaccarof8d4e932020-12-11 01:52:23 +0000838 '/mnt/host/source/src', status.fitimage_dir, status.fitimage_script))
Paul Fagerburg042a5252020-03-16 21:49:18 -0600839 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700840 [add_fitimage_sh,
841 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600842 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700843 if rc:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700844 fitimage_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir)
Paul Fagerburgba508a02020-11-20 14:40:55 -0700845 status.commits[step_names.COMMIT_FIT] = get_git_commit_data(
846 fitimage_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700847 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700848
849
850def gen_fit_image_outside_chroot(status):
851 """Tell the user to run gen_fit_image.sh outside the chroot
852
853 As noted for add_Fitimage(), gen_fit_image.sh cannot run inside the
854 chroot. This function tells the user to run gen_fit_image.sh in
855 their normal environment, and then come back (--continue) when that
856 is done.
857
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700858 Args:
859 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700860
861 Returns:
862 True
863 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700864 logging.info('Running step gen_fit_image_outside_chroot')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700865 fit_image_files = check_fit_image_files(status)
866 # If the list is empty, then `not` of the list is True, so the files
867 # we need are all present and we can continue.
868 if not fit_image_files:
869 return True
870
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700871 logging.error('The following files need to be generated:')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700872 for filename in fit_image_files:
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700873 logging.error('* %s', filename)
Paul Fagerburgba508a02020-11-20 14:40:55 -0700874 logging.error(
875 'The fitimage sources are ready for gen_fit_image.sh to process.')
876 logging.error(
877 'gen_fit_image.sh cannot run inside the chroot. Open a new terminal,')
878 logging.error(
879 'change to the directory where gen_fit_image.sh is located, and run')
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700880 logging.error(status.fitimage_cmd, status.variant)
881 logging.error('Then re-start this program with --continue.')
Paul Fagerburgba508a02020-11-20 14:40:55 -0700882 logging.error(
883 'If your chroot is based in ~/chromiumos, then the folder you want is')
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700884 logging.error('~/chromiumos/src/%s/asset_generation', status.fitimage_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700885 return False
886
887
888def check_fit_image_files(status):
889 """Check if the fitimage has been generated
890
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700891 This function is not called directly as a step, and so it doesn't need
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700892 to produce any error messages to the user (except with --verbose).
893 gen_fit_image_outside_chroot will call this function to see if the
894 fitimage files exist, and if not, then that function will print the
895 message about how the user needs to run gen_fit_image.sh outside the
896 chroot.
897
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700898 Args:
899 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700900
901 Returns:
902 List of files that *DO NOT* exist and need to be created, [] if
903 all files are present.
904 """
Paul Fagerburg4039c152021-02-18 13:58:44 -0700905 fitimage_bin_dir = os.path.join('/mnt/host/source/src',
906 status.fitimage_dir, status.fitimage_bin_dir)
907 logging.debug('fitimage_bin_dir = "%s"', fitimage_bin_dir)
908 fitimage_versions_dir = os.path.join('/mnt/host/source/src',
909 status.fitimage_dir, status.fitimage_versions_dir)
910 logging.debug('fitimage_versions_dir = "%s"', fitimage_versions_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700911
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600912 files_not_found = []
Paul Fagerburg4039c152021-02-18 13:58:44 -0700913 fitimage_bin = status.fitimage_bin % status.variant
914 if not os.path.isfile(os.path.join(fitimage_bin_dir, fitimage_bin)):
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600915 files_not_found.append(fitimage_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700916
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600917 fitimage_versions = 'fitimage-' + status.variant + '-versions.txt'
Paul Fagerburg4039c152021-02-18 13:58:44 -0700918 if not os.path.isfile(os.path.join(fitimage_versions_dir,
919 fitimage_versions)):
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600920 files_not_found.append(fitimage_versions)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700921
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600922 return files_not_found
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700923
924
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700925def commit_fitimage(status):
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600926 """Add the fitimage files to the git commit
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700927
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600928 This function calls commit_fitimage.sh to move the fitimage binary and
929 -versions files from asset_generation/outputs to files/ and then adds
930 those files and fit.log to the existing git commit.
931 Depending on the baseboard, there may be different file names (such
932 as fit-${VARIANT}.log for volteer) and/or additional files (such as
933 files/blobs/description-${VARIANT}.bin for volteer)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700934
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700935 Args:
936 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700937
938 Returns:
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600939 True if the script succeeded, False if something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700940 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700941 logging.info('Running step commit_fitimage')
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600942 commit_fitimage_sh = os.path.expanduser(os.path.join(
Paul Fagerburgba508a02020-11-20 14:40:55 -0700943 '/mnt/host/source/src', status.fitimage_dir,
944 'files/commit_fitimage.sh'))
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600945 return run_process([commit_fitimage_sh, status.variant])
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700946
947
948def create_initial_ec_image(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700949 """Create an EC image for the variant as a clone of the reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700950
951 This function calls create_initial_ec_image.sh, which will clone the
Paul Fagerburge868e832020-01-22 17:14:04 -0700952 reference board to create the variant. The shell script will build the
Paul Fagerburgfcada622020-11-20 11:53:36 -0700953 EC code for the variant.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700954
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700955 Args:
956 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700957
958 Returns:
959 True if the script and test build succeeded, False if something failed
960 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700961 logging.info('Running step create_initial_ec_image')
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700962 environ = {**os.environ, 'NEW_VARIANT_BRANCH': status.branch}
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700963 create_initial_ec_image_sh = os.path.join(status.my_loc,
964 'create_initial_ec_image.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600965 if not run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700966 [create_initial_ec_image_sh,
967 status.board,
968 status.variant,
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700969 status.bug], env=environ):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700970 return False
971
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700972 # No need to `if rc:` because we already tested the run_process result above
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700973 status.commits[step_names.EC_IMAGE] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700974 '/mnt/host/source/src/platform/ec/board')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700975
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700976 # create_initial_ec_image.sh will build the ec.bin for this variant
977 # if successful.
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600978 ec_dir = '/mnt/host/source/src/platform/ec'
979 ec_bin = os.path.join(ec_dir, 'build', status.variant, 'ec.bin')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700980 logging.debug('ec.bin = "%s"', ec_bin)
981
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600982 if not os.path.isfile(ec_bin):
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700983 logging.error('EC binary %s not found', ec_bin)
984 return False
985 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700986
987
988def ec_buildall(status):
Paul Fagerburgfcada622020-11-20 11:53:36 -0700989 """Deprecated function that used to do a make buildall -j for the EC
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700990
Paul Fagerburgfcada622020-11-20 11:53:36 -0700991 The EC repo upload hook used to require a make buildall -j before
992 uploading. As of crrev.com/c/2436379 this requirement has been removed,
993 so this step is no longer necessary.
994
995 This function still exists so that if someone has a new variant already
996 in progress and they update new_variant.py, it won't break. Eventually,
997 this function will be removed completely.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700998
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700999 Args:
1000 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001001
1002 Returns:
Paul Fagerburgfcada622020-11-20 11:53:36 -07001003 True
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001004 """
Paul Fagerburgfcada622020-11-20 11:53:36 -07001005 logging.info('Running deprecated step ec_buildall')
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001006 del status # unused parameter
Paul Fagerburgfcada622020-11-20 11:53:36 -07001007 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001008
1009
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001010def add_variant_to_public_yaml(status):
1011 """Add the new variant to the public model.yaml file
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001012
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001013 This function calls add_variant_to_yaml.sh to add the new variant to
1014 the public model.yaml file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001015
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001016 Args:
1017 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001018
1019 Returns:
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001020 True if the script succeeded, False is something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001021 """
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001022 logging.info('Running step add_variant_to_public_yaml')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -07001023 add_variant_to_yaml_sh = os.path.join(status.my_loc,
1024 'add_variant_to_yaml.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -06001025 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001026 [add_variant_to_yaml_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -07001027 status.base,
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001028 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -06001029 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001030 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001031 status.commits[step_names.ADD_PUB_YAML] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -07001032 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001033 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001034
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001035
1036def add_variant_to_private_yaml(status):
1037 """Add the new variant to the private model.yaml file
1038
1039 This function calls add_variant.sh to add the new variant to
1040 the private model.yaml file.
1041
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001042 Args:
1043 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001044
1045 Returns:
1046 True if the script succeeded, False is something failed
1047 """
1048 logging.info('Running step add_variant_to_private_yaml')
Paul Fagerburgba508a02020-11-20 14:40:55 -07001049 add_variant_sh = os.path.expanduser(os.path.join(status.private_yaml_dir,
1050 'add_variant.sh'))
Paul Fagerburg042a5252020-03-16 21:49:18 -06001051 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001052 [add_variant_sh,
1053 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -06001054 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001055 if rc:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001056 status.commits[step_names.ADD_PRIV_YAML] = get_git_commit_data(
1057 status.private_yaml_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001058 return rc
1059
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001060
1061
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001062def build_config(status):
1063 """Build project config files, from yaml or starlark
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001064
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001065 This function builds the project config files that mosys and other tools
1066 use, then verifies that the new variant's name shows up in all of the
1067 output files. Depending on the baseboard, the input may be the model.yaml
1068 files, or the starlark configuration files.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001069
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001070 Args:
1071 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001072
1073 Returns:
1074 True if the scripts and build succeeded, False is something failed
1075 """
Paul Fagerburg4376fe52020-06-16 10:22:35 -06001076 logging.info('Running step build_config')
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001077 if not emerge_with_workon(status, status.config_workon_pkgs,
1078 status.emerge_cmd, status.config_emerge_pkgs):
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001079 return False
1080
Paul Fagerburga95dd162020-03-24 16:27:18 -06001081 # Check the generated config.yaml file for occurences of the variant
1082 # name to determine if the emerge was successful.
1083 config_yaml = os.path.join(
1084 '/build', status.base, 'usr/share/chromeos-config/yaml/config.yaml')
1085 logging.debug('config_yaml = "%s"', config_yaml)
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001086 if not os.path.isfile(config_yaml):
Paul Fagerburga95dd162020-03-24 16:27:18 -06001087 logging.error('%s does not exist', config_yaml)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001088 return False
1089
Paul Fagerburga95dd162020-03-24 16:27:18 -06001090 if not status.variant in osutils.ReadFile(config_yaml):
1091 logging.error('variant name %s not found in yaml file %s',
1092 status.variant, config_yaml)
1093 return False
1094
1095 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001096
1097
1098def emerge_all(status):
1099 """Build the coreboot BIOS and EC code for the new variant
1100
Paul Fagerburg75398072020-03-16 13:51:24 -06001101 This build step will cros_workon start a list of packages provided by
1102 the reference board data as status.workon_pkgs, then emerge a list of
1103 packages (status.emerge_pkgs), and then cros_workon stop any packages
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001104 that it started. Any packages that were already being worked on will
1105 not be stopped.
Paul Fagerburg75398072020-03-16 13:51:24 -06001106
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001107 Args:
1108 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001109
1110 Returns:
1111 True if the build succeeded, False if something failed
1112 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001113 logging.info('Running step emerge_all')
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001114 environ = {**os.environ, 'FW_NAME': status.variant}
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001115 if not emerge_with_workon(status, status.workon_pkgs,
1116 status.emerge_cmd, status.emerge_pkgs,
1117 env=environ):
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001118 return False
1119
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001120 # Check if the expected build outputs exist.
Paul Fagerburge868e832020-01-22 17:14:04 -07001121 build_path = '/build/' + status.base + '/firmware'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001122 logging.debug('build_path = "%s"', build_path)
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001123 image_bin = 'image-' + status.variant + '.bin'
1124 if not os.path.isfile(os.path.join(build_path, image_bin)):
1125 logging.error('emerge failed because %s does not exist', image_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001126 return False
1127
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001128 serial_bin = 'image-' + status.variant + '.serial.bin'
1129 if not os.path.isfile(os.path.join(build_path, serial_bin)):
1130 logging.error('emerge failed because %s does not exist', serial_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001131 return False
1132
1133 return True
1134
1135
1136def push_coreboot(status):
1137 """Push the coreboot CL to coreboot.org
1138
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001139 Args:
1140 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001141
1142 Returns:
1143 True if the build succeeded, False if something failed
1144 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001145 logging.info('Running step push_coreboot')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001146
1147 # Set up a return code that may change to False if we find that a
1148 # coreboot CL has not been uploaded.
1149 rc = True
1150
1151 for commit_key in status.coreboot_push_list:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001152 logging.debug('Processing key %s', commit_key)
Paul Fagerburg8d850932020-02-25 14:13:32 -07001153 commit = status.commits[commit_key]
1154 if 'gerrit' not in commit or 'cl_number' not in commit:
1155 change_id = commit['change_id']
1156 cl = find_change_id(change_id)
1157 if cl is not None:
1158 save_cl_data(status, commit_key, cl)
1159 else:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001160 logging.debug('Not found %s, need to upload', change_id)
Paul Fagerburgba508a02020-11-20 14:40:55 -07001161 logging.error(
1162 'The following commit needs to be pushed to coreboot.org:')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001163 logging.error(' Branch "%s"', commit['branch_name'])
1164 logging.error(' in directory "%s"', commit['dir'])
1165 logging.error(' with change-id "%s"', commit['change_id'])
1166 logging.error('Please push the branch to review.coreboot.org, '
1167 'and then re-start this program with --continue')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001168 # Since this commit needs to be uploaded, do not continue after
1169 # this step returns.
1170 rc = False
1171 else:
1172 instance_name = commit['gerrit']
1173 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001174 logging.debug('Already uploaded (%s, %s)', instance_name, cl_number)
Paul Fagerburg8d850932020-02-25 14:13:32 -07001175
1176 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001177
1178
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001179def query_gerrit(instance, change_id):
1180 """Search a gerrit instance for a specific change_id
1181
1182 Args:
1183 instance: gerrit instance to query. Suitable values come from
1184 gerrit.GetCrosInternal() and gerrit.GetCrosExternal()
1185 change_id: The change_id to search for
1186
1187 Returns:
1188 CL number if found, None if not
1189 """
1190 raw = instance.Query(change=change_id, raw=True)
1191 if raw:
1192 # If the CL was found by change_id, there will be only one,
1193 # because the change_id is used to recognize a new patchset
1194 # on an existing CL.
1195 return raw[0]['number']
1196
1197 return None
1198
1199
Paul Fagerburg8d850932020-02-25 14:13:32 -07001200def query_coreboot_gerrit(change_id):
1201 """Search the coreboot gerrit for a specific change_id
1202
1203 Use the REST API to look for the change_id. See
1204 https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html
1205 for details on the REST API to search for a change-id.
1206
1207 We can't use query_gerrit with a manually constructed GerritHelper
1208 because we need the user's private SSH key to access review.coreboot.org,
1209 but these are not available inside the chroot.
1210
1211 Args:
1212 change_id: The change_id to search for
1213
1214 Returns:
1215 CL number if found, None if not
1216 """
1217 r = requests.get('https://review.coreboot.org/changes/' + change_id)
1218 response = r.content.decode('utf-8')
1219 # Check if the response starts with 'Not found', in which case return None
1220 if response.startswith('Not found:'):
1221 return None
1222 # Strip off the initial )]}'\n that is used as XSS protections, see
1223 # https://gerrit-review.googlesource.com/Documentation/rest-api.html#output
1224 # and decode as JSON.
1225 data = json.loads(response[5:])
1226 if '_number' in data:
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001227 return str(data['_number'])
Paul Fagerburg8d850932020-02-25 14:13:32 -07001228 return None
1229
1230
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001231def find_change_id(change_id):
1232 """Search the public and private ChromeOS gerrit instances for a change-id
1233
1234 Args:
1235 change_id: Change-Id to search for in both gerrit instances
1236
1237 Returns:
1238 Tuple of the gerrit instance ('chromium' or 'chrome-internal') and
1239 the CL number if the Change-Id is found.
1240 None if not found.
1241 """
1242 cl_number = query_gerrit(gerrit.GetCrosExternal(), change_id)
1243 if cl_number:
1244 return 'chromium', cl_number
1245 cl_number = query_gerrit(gerrit.GetCrosInternal(), change_id)
1246 if cl_number:
1247 return 'chrome-internal', cl_number
Paul Fagerburg8d850932020-02-25 14:13:32 -07001248 cl_number = query_coreboot_gerrit(change_id)
1249 if cl_number:
1250 return 'coreboot', cl_number
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001251 return None
1252
1253
1254def save_cl_data(status, commit_key, cl):
1255 """Save the gerrit instance and CL number to the yaml file
1256
1257 Args:
1258 status: variant_status object tracking our board, variant, etc.
1259 commit_key: Which key in the commits map we're processing
1260 cl: Value returned by find_change_id, should be a tuple
1261 of instance_name, cl_number
1262 """
1263 instance_name, cl_number = cl
1264 print(f'Found ({instance_name}, {cl_number}), saving to yaml')
1265 status.commits[commit_key]['gerrit'] = instance_name
1266 status.commits[commit_key]['cl_number'] = cl_number
1267 status.save()
1268
1269
1270def repo_upload(branch_name, cwd):
1271 """Upload a branch to gerrit
1272
1273 This function runs `repo upload` in the specified directory to upload
1274 a branch to gerrit. Because it's operating in a directory and with a
1275 branch name, it could upload more than one commit, which is OK because
1276 we'll look for each commit by change-id before trying to upload in that
1277 directory. For example, this happens in Zork, where the cb_config step
1278 and the cras_config step both have a commit in src/overlays. When we're
1279 processing the cb_config step and we `repo upload` in src/overlays, it
1280 will also upload the commit for cras_config. Then we come around to the
1281 cras_config step, and since we can find a CL with the change-id, we don't
1282 try to upload again.
1283
1284 Args:
1285 branch_name: the name of the branch to upload. Gets passed to
1286 repo upload with the --br flag
1287 cwd: directory where we want to upload. Gets set as the working
1288 directory for executing repo upload.
1289
1290 Returns:
1291 True if repo upload exits with a successful error code, false otherwise
1292 """
Paul Fagerburg042a5252020-03-16 21:49:18 -06001293 return run_process(
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001294 ['repo',
1295 'upload',
1296 '.',
1297 '--br=' + branch_name,
1298 '--wip',
1299 '--verify',
Paul Fagerburgda6bc102020-08-31 19:27:04 -06001300 '--yes',
1301 '--hashtag=new_variant'],
Paul Fagerburg042a5252020-03-16 21:49:18 -06001302 cwd=cwd)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001303
1304
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001305def upload_CLs(status):
1306 """Upload all CLs to chromiumos
1307
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001308 Args:
1309 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001310
1311 Returns:
1312 True if the build succeeded, False if something failed
1313 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001314 logging.info('Running step upload_CLs')
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001315
1316 for commit_key in status.repo_upload_list:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001317 logging.debug('Processing key %s', commit_key)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001318 commit = status.commits[commit_key]
1319 if 'gerrit' not in commit or 'cl_number' not in commit:
1320 change_id = commit['change_id']
1321 cl = find_change_id(change_id)
1322 if cl is not None:
1323 save_cl_data(status, commit_key, cl)
1324 else:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001325 logging.debug('Not found %s, need to upload', change_id)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001326 if not repo_upload(commit['branch_name'], commit['dir']):
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001327 logging.error('Repo upload %s in %s failed!',
1328 commit['branch_name'],
1329 commit['dir'])
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001330 return False
1331 cl = find_change_id(change_id)
1332 if cl is None:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001333 logging.error(
1334 'repo upload %s succeeded, but change_id is not found!',
1335 commit_key)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001336 return False
1337 save_cl_data(status, commit_key, cl)
1338 else:
1339 instance_name = commit['gerrit']
1340 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001341 logging.debug('Already uploaded (%s, %s)', instance_name, cl_number)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001342
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001343 return True
1344
1345
1346def find_coreboot_upstream(status):
Paul Fagerburgba508a02020-11-20 14:40:55 -07001347 """Find the upstream coreboot CL in chromiumos
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001348
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001349 When the coreboot variant CL is first uploaded to review.coreboot.org,
1350 it is not visible in the chromiumos tree (and also cannot be used as
Paul Fagerburgba508a02020-11-20 14:40:55 -07001351 a target for cq-depend). There is a process for upstream CLs from
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001352 coreboot after they have been reviewed, approved, and merged. We can
1353 track a specific coreboot CL if we know the change-id that it used on
1354 the coreboot gerrit instance, by looking for that change-id as
1355 'original-change-id' in the public chromium gerrit instance.
1356
1357 The change-id for the coreboot variant will be under the 'cb_variant' key,
1358 but this is for the 'coreboot' gerrit instance.
1359
1360 When we find the upstreamed CL, we will record the gerrit instance and
1361 CL number in the yaml file under the 'find' key ("find upstream coreboot")
1362 so that we don't need to search coreboot again.
1363
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001364 Args:
1365 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001366
1367 Returns:
1368 True if the build succeeded, False if something failed
1369 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001370 logging.info('Running step find_coreboot_upstream')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001371
1372 # If we have already found the upstream coreboot CL, then exit with success
1373 if step_names.FIND in status.commits:
1374 commit = status.commits[step_names.FIND]
1375 if 'gerrit' in commit and 'cl_number' in commit:
1376 instance_name = commit['gerrit']
1377 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001378 logging.debug('Already found (%s, %s)', instance_name, cl_number)
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001379 return True
1380
1381 # Make sure we have a CB_VARIANT commit and a change_id for it
1382 if step_names.CB_VARIANT not in status.commits:
1383 logging.error('Key %s not found in status.commits',
1384 step_names.CB_VARIANT)
1385 return False
1386 if 'change_id' not in status.commits[step_names.CB_VARIANT]:
1387 logging.error('Key change_id not found in status.commits[%s]',
1388 step_names.CB_VARIANT)
1389 return False
1390
1391 # Find the CL by the Original-Change-Id
1392 original_change_id = status.commits[step_names.CB_VARIANT]['change_id']
1393 gerrit_query_args = {
Paul Fagerburg0d443e72021-03-25 10:27:45 -06001394 'message': f'Original-Change-Id:{original_change_id}'
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001395 }
1396 cros = gerrit.GetCrosExternal()
1397 upstream = cros.Query(**gerrit_query_args)
1398 # If nothing is found, the patch hasn't been upstreamed yet
1399 if not upstream:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001400 logging.error('Program cannot continue without upstream coreboot CL.')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001401 logging.error('(coreboot:%s, change-id %s)',
1402 status.commits[step_names.CB_VARIANT]['cl_number'],
1403 status.commits[step_names.CB_VARIANT]['change_id'])
Paul Fagerburgba508a02020-11-20 14:40:55 -07001404 logging.error('Please wait for the upstream CL, then run this program'
1405 ' again with --continue')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001406 return False
1407
1408 # If more than one CL is found, something is very wrong
1409 if len(upstream) != 1:
1410 logging.error('More than one CL was found with Original-Change-Id %s',
1411 original_change_id)
1412 return False
1413
1414 # At this point, we know there is only one CL and we can get the
1415 # repo and CL number by splitting on the colon between them.
1416 patchlink = upstream[0].PatchLink()
1417 instance_name, cl_number = patchlink.split(':')
1418
1419 # Can't use get_git_commit_data because we're not pulling this
1420 # information from a git commit, but rather from gerrit.
1421 # We only need the gerrit instance and the CL number so we can have
1422 # other CLs cq-depend on this CL. The other keys are not needed because:
1423 # dir - not needed because we're not going to `cd` there to `repo upload`
1424 # branch_name - not valid; the CL is already merged
1425 # change_id - we use the change_id to find a CL number, and since we
1426 # just found the CL number via original-change-id, this is moot.
1427 status.commits[step_names.FIND] = {
1428 'gerrit': instance_name,
1429 'cl_number': str(cl_number)
1430 }
1431
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001432 return True
1433
1434
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001435def calc_cq_depend(status):
1436 """Determine the list of CLs for each commit that has dependencies.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001437
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001438 status.depends is a map of dependencies from step name to a list of
1439 steps that the step depends on. For each step, find the SHA of the
1440 commit, then find the gerrit instance and CL number of the commits
1441 that it depends on. Construct the Cq-Depends list and save it under
1442 the 'cq_depend' key, i.e. commit['add_priv_yaml']['cq_depend'] or
1443 as it will be stored in the yaml:
1444 commits:
1445 add_priv_yaml:
1446 cq_depend: 'chromium:1629121, chromium:1638243'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001447
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001448 Args:
1449 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001450
1451 Returns:
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001452 True if all dependencies have been calculated. False if something
1453 failed, usually a commit not found by Change-Id.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001454 """
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001455 logging.info('Running step calc_cq_depend')
1456 # Iterate through the commits that have dependencies.
1457 for key in status.depends:
1458 logging.debug('Processing %s to add dependencies', key)
1459 # For every commit that has dependencies, find the gerrit instance
1460 # and CL number of the dependencies.
1461 cq_depend_list = []
1462 for depend_key in status.depends[key]:
1463 depend_commit = status.commits[depend_key]
1464 if not 'gerrit' in depend_commit:
1465 logging.error('Commit %s does not have a gerrit instance',
1466 depend_key)
1467 return False
1468 if not 'cl_number' in depend_commit:
1469 logging.error('Commit %s does not have a CL number',
1470 depend_key)
1471 return False
1472
1473 instance_name = depend_commit['gerrit']
1474 cl_number = depend_commit['cl_number']
1475 cq_depend_list.append(f'{instance_name}:{cl_number}')
1476
1477 # Add the 'cq_depend' key to the commit.
1478 cq_depend_str = 'Cq-Depend: %s' % ', '.join(cq_depend_list)
1479 logging.debug('Add to commit %s %s', key, cq_depend_str)
1480 status.commits[key]['cq_depend'] = cq_depend_str
1481
1482 return True
1483
1484
1485def add_cq_depend_to_commit_msg(git_repo, change_id, cq_depend_str):
1486 """Update the commit message with a Cq-Depends line.
1487
1488 Find the SHA of the commit, then use git filter-branch --msg-filter
1489 to add the Cq-Depend line just before the Change-Id line. See
1490 https://chromium.googlesource.com/chromiumos/docs/+/HEAD/contributing.md#cq-depend
1491 for details about Cq-Depend format and location.
1492
1493 Args:
1494 git_repo: Directory of git repository.
1495 change_id: The Change-Id to search for.
1496 cq_depend_str: The Cq-Depend string. It must be in the correct format
1497 per chromeos documentation, ready to insert into the commit msg
1498 on the line before Change-Id.
1499
1500 Returns:
1501 True if `git filter-branch` was successful. False if the command
1502 failed.
1503 """
1504 logging.debug('find SHA of Change-Id %s in %s', change_id, git_repo)
1505 sha = change_id_to_sha(git_repo, change_id)
1506 if sha is None:
1507 logging.error('Cannot find the SHA for Change-Id %s in %s',
1508 change_id, git_repo)
1509 return False
1510 logging.debug('SHA = %s', sha)
1511
1512 # Check if the commit message already has a Cq-Depend line.
1513 msg = get_commit_msg(git_repo, sha)
1514 if any('Cq-Depend' in tmpstr for tmpstr in msg):
1515 logging.debug('Already has Cq-Depend')
1516 return True
1517
1518 # Use git filter-branch --msg-filter to add the Cq-Depend line just
1519 # before the Change-Id line.
1520 environ = {**os.environ, 'FILTER_BRANCH_SQUELCH_WARNING': '1'}
1521 cmd = [
1522 'git',
1523 'filter-branch',
Paul Fagerburg5f6d2012020-06-24 11:29:36 -06001524 '-f',
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001525 '--msg-filter',
1526 f'sed -E "s/^(Change-Id: {change_id})$/{cq_depend_str}\\n\\1/"',
1527 '--',
1528 f'{sha}^..']
1529 return run_process(cmd, cwd=git_repo, env=environ)
1530
1531
1532def add_cq_depend(status):
1533 """Add Cq-Depend to commits and flag them for re-upload.
1534
1535 Args:
1536 status: variant_status object tracking our board, variant, etc.
1537
1538 Returns:
1539 True if the commit messages have been successfully amended, False if
1540 something failed.
1541 """
1542 logging.info('Running step add_cq_depend')
1543 for key in status.commits:
1544 commit = status.commits[key]
1545 if 'cq_depend' in commit:
1546 logging.debug('%s has %s', key, commit['cq_depend'])
1547 # Make sure the commit has a working directory and a change_id
1548 # before trying to amend its commit message.
1549 if 'dir' not in commit or 'change_id' not in commit:
1550 logging.error('Missing dir and/or change_id from %s', key)
1551 return False
1552
1553 if not add_cq_depend_to_commit_msg(commit['dir'],
1554 commit['change_id'],
1555 commit['cq_depend']):
1556 return False
1557 commit['needs_re_upload'] = True
1558 else:
1559 logging.debug('%s no dependencies', key)
1560
1561 return True
1562
1563
1564def re_upload(status):
1565 """Re-upload commits that have changed.
1566
1567 Args:
1568 status: variant_status object tracking our board, variant, etc.
1569
1570 Returns:
1571 True if the uploads succeeded. False if a repo upload failed.
1572 """
1573 logging.info('Running step re_upload')
1574 for key in status.commits:
1575 commit = status.commits[key]
1576 if commit.get('needs_re_upload'):
1577 logging.debug('Re-upload branch %s in %s', commit['branch_name'],
1578 commit['dir'])
1579 if not repo_upload(commit['branch_name'], commit['dir']):
1580 logging.error('Repo upload %s in %s failed!',
1581 commit['branch_name'],
1582 commit['dir'])
1583 return False
1584 commit['needs_re_upload'] = False
1585
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001586 return True
1587
1588
1589def clean_up(status):
1590 """Final clean-up, including delete the status file
1591
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001592 Args:
1593 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001594
1595 Returns:
1596 True
1597 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001598 logging.info('Running step clean_up')
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001599 status.rm()
1600 return True
1601
1602
Paul Fagerburg042a5252020-03-16 21:49:18 -06001603def abort(status):
1604 """Abort the creation of a new variant by abandoning commits
1605
1606 When the user specifies the --abort flag, we override status.step to
1607 be 'abort' and there is no transition from 'abort' to anything else.
1608 We look at status.commits and for each key, see if we have already
1609 been in that directory and abandoned that specific branch. If not,
1610 abandon the commit and then add the branch+dir to a list of abandoned
1611 commits. We do this because some boards (such as Zork) can have multiple
1612 commits in the same directory and with the same branch name, and we only
1613 want to repo abandon that branch once.
1614
1615 Args:
1616 status: variant_status object tracking our board, variant, etc.
1617
1618 Returns:
1619 True
1620 """
1621 logging.info('Running step abort')
1622 # Use the set 'abandoned' to keep track of each branch+dir abandoned.
1623 abandoned = set()
1624 for step in status.commits:
1625 logging.debug('Processing step %s', step)
1626 commit = status.commits[step]
1627 branch = commit['branch_name']
1628 cwd = commit['dir']
1629 if (branch, cwd) in abandoned:
1630 logging.debug('Branch %s in directory %s already abandoned',
1631 branch, cwd)
1632 else:
1633 logging.info('Abandoning branch %s in directory %s',
1634 branch, cwd)
1635 if run_process(['repo', 'abandon', branch, '.'], cwd=cwd):
1636 abandoned.add((branch, cwd))
1637 else:
1638 logging.error('Error while abandoning branch %s', branch)
1639 return False
1640
1641 return True
1642
1643
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001644if __name__ == '__main__':
1645 sys.exit(not int(main()))