blob: d62cd914105a66d03601ba45958df8fe7f5bd131 [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))
Paul Fagerburgb4ec2ec2021-05-10 13:31:14 -0600689 logging.error('Please file a bug in ChromeOS > Infra'
Paul Fagerburgbc184242020-04-28 17:00:54 -0600690 ' to have the project configuration updated.')
Paul Fagerburgb4ec2ec2021-05-10 13:31:14 -0600691 logging.error('(go/cros-boxster-bug or '
692 'https://b.corp.google.com/issues/new?'
693 'component=167276&template=1022133)')
Paul Fagerburgbc184242020-04-28 17:00:54 -0600694 return False
695
696 return True
697
698
Paul Fagerburg8f60b802020-06-09 15:38:28 -0600699def fw_build_config(status):
700 """Add the _FW_BUILD_CONFIG setting to the project config
701
702 For programs that use the new project/config structure with starlark
703 configuration files, this function calls fw_build_config.sh, which will
704 modify the config.star file to have a default _FW_BUILD_CONFIG entry.
705
706 Args:
707 status: variant_status object tracking our board, variant, etc.
708
709 Returns:
710 True if everything succeeded, False if something failed
711 """
712 logging.info('Running step fw_build_config')
713 fw_build_config_sh = os.path.join(status.my_loc, 'fw_build_config.sh')
714 rc = run_process(
715 [fw_build_config_sh,
716 status.base,
717 status.variant,
718 status.bug])
719 if rc:
720 status.commits[step_names.FW_BUILD_CONFIG] = get_git_commit_data(
Paul Fagerburgba508a02020-11-20 14:40:55 -0700721 os.path.join('/mnt/host/source/src/project',
722 status.base, status.variant))
Paul Fagerburg8f60b802020-06-09 15:38:28 -0600723 return rc
724
725
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700726def create_coreboot_variant(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700727 """Create source files for a new variant of the reference board in coreboot
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700728
729 This function calls create_coreboot_variant.sh to set up a new variant
Paul Fagerburge868e832020-01-22 17:14:04 -0700730 of the reference board.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700731
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700732 Args:
733 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700734
735 Returns:
736 True if everything succeeded, False if something failed
737 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700738 logging.info('Running step create_coreboot_variant')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700739 cb_src_dir = os.path.join('/mnt/host/source/src/', status.coreboot_dir)
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700740 environ = {**os.environ, 'CB_SRC_DIR': cb_src_dir,
741 'NEW_VARIANT_BRANCH': status.branch}
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700742 create_coreboot_variant_sh = os.path.join(status.my_loc,
743 'create_coreboot_variant.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600744 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700745 [create_coreboot_variant_sh,
Paul Fagerburg3ef1cbb2020-06-02 16:38:36 -0600746 status.coreboot_base,
Matt Ziegelbaum3e879df2020-11-09 16:08:44 -0500747 status.coreboot_reference,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700748 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600749 status.bug], env=environ)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700750 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700751 status.commits[step_names.CB_VARIANT] = get_git_commit_data(cb_src_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700752 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700753
754
755def create_coreboot_config(status):
756 """Create a coreboot configuration for a new variant
757
758 This function calls create_coreboot_config.sh, which will make a copy
759 of coreboot.${BOARD} into coreboot.${VARIANT}.
760
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700761 Args:
762 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700763
764 Returns:
765 True if the script and test build succeeded, False if something failed
766 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700767 logging.info('Running step create_coreboot_config')
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600768 # Only set CB_CONFIG_DIR if it's not None, so here we have to copy
769 # the environment first and then optionally add a key.
Paul Fagerburg4d343452020-01-24 15:23:53 -0700770 environ = os.environ.copy()
771 if status.cb_config_dir is not None:
772 environ['CB_CONFIG_DIR'] = status.cb_config_dir
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700773 create_coreboot_config_sh = os.path.join(status.my_loc,
774 'create_coreboot_config.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600775 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700776 [create_coreboot_config_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700777 status.base,
Matt Ziegelbaum3e879df2020-11-09 16:08:44 -0500778 status.coreboot_reference,
Paul Fagerburge868e832020-01-22 17:14:04 -0700779 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600780 status.bug], env=environ)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700781 if rc:
782 # Use status.cb_config_dir if defined, or if not, use
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700783 # '/mnt/host/source/src/third_party/chromiumos-overlay'
Paul Fagerburgba508a02020-11-20 14:40:55 -0700784 cb_config_dir = os.path.join(
785 '/mnt/host/source/src/',
786 status.cb_config_dir or 'third_party/chromiumos-overlay')
787 status.commits[step_names.CB_CONFIG] = get_git_commit_data(
788 cb_config_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700789 return rc
Paul Fagerburge868e832020-01-22 17:14:04 -0700790
791
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700792def copy_cras_config(status):
793 """Copy the cras config for a new variant
794
795 This is only necessary for the Zork baseboard right now.
796 This function calls copy_cras_config.sh, which will copy the
Paul Fagerburgba508a02020-11-20 14:40:55 -0700797 cras config in overlays/overlay-${BASE}/chromeos-base/\
798 chromeos-bsp-${BASE}/files/cras-config/${BASE} to .../${VARIANT}
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700799
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700800 Args:
801 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700802
803 Returns:
804 True if the script and test build succeeded, False if something failed
805 """
806 logging.info('Running step copy_cras_config')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700807 copy_cras_config_sh = os.path.join(status.my_loc, 'copy_cras_config.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600808 rc = run_process(
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700809 [copy_cras_config_sh,
810 status.base,
811 status.board,
812 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600813 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700814 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700815 status.commits[step_names.CRAS_CONFIG] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700816 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700817 return rc
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700818
819
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700820def add_fitimage(status):
821 """Add the source files for a fitimage for the new variant
822
823 This function calls add_fitimage.sh to create a new XSL file for the
Paul Fagerburge868e832020-01-22 17:14:04 -0700824 variant's fitimage, which can override settings from the reference board's
825 XSL. When this is done, the user will have to build the fitimage by running
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700826 gen_fit_image.sh outside of the chroot (and outside of this program's
827 control) because gen_fit_image.sh uses WINE, which is not installed in
828 the chroot. (There is a linux version of FIT, but it requires Open GL,
829 which is also not installed in the chroot.)
830
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700831 Args:
832 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700833
834 Returns:
835 True if the script succeeded, False otherwise
836 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700837 logging.info('Running step add_fitimage')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700838 add_fitimage_sh = os.path.expanduser(os.path.join(
Nick Vaccarof8d4e932020-12-11 01:52:23 +0000839 '/mnt/host/source/src', status.fitimage_dir, status.fitimage_script))
Paul Fagerburg042a5252020-03-16 21:49:18 -0600840 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700841 [add_fitimage_sh,
842 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600843 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700844 if rc:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700845 fitimage_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir)
Paul Fagerburgba508a02020-11-20 14:40:55 -0700846 status.commits[step_names.COMMIT_FIT] = get_git_commit_data(
847 fitimage_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700848 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700849
850
851def gen_fit_image_outside_chroot(status):
852 """Tell the user to run gen_fit_image.sh outside the chroot
853
854 As noted for add_Fitimage(), gen_fit_image.sh cannot run inside the
855 chroot. This function tells the user to run gen_fit_image.sh in
856 their normal environment, and then come back (--continue) when that
857 is done.
858
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700859 Args:
860 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700861
862 Returns:
863 True
864 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700865 logging.info('Running step gen_fit_image_outside_chroot')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700866 fit_image_files = check_fit_image_files(status)
867 # If the list is empty, then `not` of the list is True, so the files
868 # we need are all present and we can continue.
869 if not fit_image_files:
870 return True
871
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700872 logging.error('The following files need to be generated:')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700873 for filename in fit_image_files:
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700874 logging.error('* %s', filename)
Paul Fagerburgba508a02020-11-20 14:40:55 -0700875 logging.error(
876 'The fitimage sources are ready for gen_fit_image.sh to process.')
877 logging.error(
878 'gen_fit_image.sh cannot run inside the chroot. Open a new terminal,')
879 logging.error(
880 'change to the directory where gen_fit_image.sh is located, and run')
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700881 logging.error(status.fitimage_cmd, status.variant)
882 logging.error('Then re-start this program with --continue.')
Paul Fagerburgba508a02020-11-20 14:40:55 -0700883 logging.error(
884 'If your chroot is based in ~/chromiumos, then the folder you want is')
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700885 logging.error('~/chromiumos/src/%s/asset_generation', status.fitimage_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700886 return False
887
888
889def check_fit_image_files(status):
890 """Check if the fitimage has been generated
891
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700892 This function is not called directly as a step, and so it doesn't need
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700893 to produce any error messages to the user (except with --verbose).
894 gen_fit_image_outside_chroot will call this function to see if the
895 fitimage files exist, and if not, then that function will print the
896 message about how the user needs to run gen_fit_image.sh outside the
897 chroot.
898
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700899 Args:
900 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700901
902 Returns:
903 List of files that *DO NOT* exist and need to be created, [] if
904 all files are present.
905 """
Paul Fagerburg4039c152021-02-18 13:58:44 -0700906 fitimage_bin_dir = os.path.join('/mnt/host/source/src',
907 status.fitimage_dir, status.fitimage_bin_dir)
908 logging.debug('fitimage_bin_dir = "%s"', fitimage_bin_dir)
909 fitimage_versions_dir = os.path.join('/mnt/host/source/src',
910 status.fitimage_dir, status.fitimage_versions_dir)
911 logging.debug('fitimage_versions_dir = "%s"', fitimage_versions_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700912
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600913 files_not_found = []
Paul Fagerburg4039c152021-02-18 13:58:44 -0700914 fitimage_bin = status.fitimage_bin % status.variant
915 if not os.path.isfile(os.path.join(fitimage_bin_dir, fitimage_bin)):
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600916 files_not_found.append(fitimage_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700917
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600918 fitimage_versions = 'fitimage-' + status.variant + '-versions.txt'
Paul Fagerburg4039c152021-02-18 13:58:44 -0700919 if not os.path.isfile(os.path.join(fitimage_versions_dir,
920 fitimage_versions)):
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600921 files_not_found.append(fitimage_versions)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700922
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600923 return files_not_found
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700924
925
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700926def commit_fitimage(status):
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600927 """Add the fitimage files to the git commit
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700928
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600929 This function calls commit_fitimage.sh to move the fitimage binary and
930 -versions files from asset_generation/outputs to files/ and then adds
931 those files and fit.log to the existing git commit.
932 Depending on the baseboard, there may be different file names (such
933 as fit-${VARIANT}.log for volteer) and/or additional files (such as
934 files/blobs/description-${VARIANT}.bin for volteer)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700935
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700936 Args:
937 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700938
939 Returns:
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600940 True if the script succeeded, False if something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700941 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700942 logging.info('Running step commit_fitimage')
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600943 commit_fitimage_sh = os.path.expanduser(os.path.join(
Paul Fagerburgba508a02020-11-20 14:40:55 -0700944 '/mnt/host/source/src', status.fitimage_dir,
945 'files/commit_fitimage.sh'))
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600946 return run_process([commit_fitimage_sh, status.variant])
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700947
948
949def create_initial_ec_image(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700950 """Create an EC image for the variant as a clone of the reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700951
952 This function calls create_initial_ec_image.sh, which will clone the
Paul Fagerburge868e832020-01-22 17:14:04 -0700953 reference board to create the variant. The shell script will build the
Paul Fagerburgfcada622020-11-20 11:53:36 -0700954 EC code for the variant.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700955
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700956 Args:
957 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700958
959 Returns:
960 True if the script and test build succeeded, False if something failed
961 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700962 logging.info('Running step create_initial_ec_image')
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700963 environ = {**os.environ, 'NEW_VARIANT_BRANCH': status.branch}
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700964 create_initial_ec_image_sh = os.path.join(status.my_loc,
965 'create_initial_ec_image.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600966 if not run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700967 [create_initial_ec_image_sh,
968 status.board,
969 status.variant,
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700970 status.bug], env=environ):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700971 return False
972
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700973 # No need to `if rc:` because we already tested the run_process result above
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700974 status.commits[step_names.EC_IMAGE] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700975 '/mnt/host/source/src/platform/ec/board')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700976
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700977 # create_initial_ec_image.sh will build the ec.bin for this variant
978 # if successful.
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600979 ec_dir = '/mnt/host/source/src/platform/ec'
980 ec_bin = os.path.join(ec_dir, 'build', status.variant, 'ec.bin')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700981 logging.debug('ec.bin = "%s"', ec_bin)
982
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600983 if not os.path.isfile(ec_bin):
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700984 logging.error('EC binary %s not found', ec_bin)
985 return False
986 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700987
988
989def ec_buildall(status):
Paul Fagerburgfcada622020-11-20 11:53:36 -0700990 """Deprecated function that used to do a make buildall -j for the EC
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700991
Paul Fagerburgfcada622020-11-20 11:53:36 -0700992 The EC repo upload hook used to require a make buildall -j before
993 uploading. As of crrev.com/c/2436379 this requirement has been removed,
994 so this step is no longer necessary.
995
996 This function still exists so that if someone has a new variant already
997 in progress and they update new_variant.py, it won't break. Eventually,
998 this function will be removed completely.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700999
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001000 Args:
1001 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001002
1003 Returns:
Paul Fagerburgfcada622020-11-20 11:53:36 -07001004 True
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001005 """
Paul Fagerburgfcada622020-11-20 11:53:36 -07001006 logging.info('Running deprecated step ec_buildall')
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001007 del status # unused parameter
Paul Fagerburgfcada622020-11-20 11:53:36 -07001008 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001009
1010
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001011def add_variant_to_public_yaml(status):
1012 """Add the new variant to the public model.yaml file
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001013
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001014 This function calls add_variant_to_yaml.sh to add the new variant to
1015 the public model.yaml file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001016
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001017 Args:
1018 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001019
1020 Returns:
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001021 True if the script succeeded, False is something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001022 """
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001023 logging.info('Running step add_variant_to_public_yaml')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -07001024 add_variant_to_yaml_sh = os.path.join(status.my_loc,
1025 'add_variant_to_yaml.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -06001026 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001027 [add_variant_to_yaml_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -07001028 status.base,
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001029 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -06001030 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001031 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001032 status.commits[step_names.ADD_PUB_YAML] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -07001033 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001034 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001035
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001036
1037def add_variant_to_private_yaml(status):
1038 """Add the new variant to the private model.yaml file
1039
1040 This function calls add_variant.sh to add the new variant to
1041 the private model.yaml file.
1042
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001043 Args:
1044 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001045
1046 Returns:
1047 True if the script succeeded, False is something failed
1048 """
1049 logging.info('Running step add_variant_to_private_yaml')
Paul Fagerburgba508a02020-11-20 14:40:55 -07001050 add_variant_sh = os.path.expanduser(os.path.join(status.private_yaml_dir,
1051 'add_variant.sh'))
Paul Fagerburg042a5252020-03-16 21:49:18 -06001052 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001053 [add_variant_sh,
1054 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -06001055 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001056 if rc:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001057 status.commits[step_names.ADD_PRIV_YAML] = get_git_commit_data(
1058 status.private_yaml_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001059 return rc
1060
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001061
1062
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001063def build_config(status):
1064 """Build project config files, from yaml or starlark
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001065
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001066 This function builds the project config files that mosys and other tools
1067 use, then verifies that the new variant's name shows up in all of the
1068 output files. Depending on the baseboard, the input may be the model.yaml
1069 files, or the starlark configuration files.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001070
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001071 Args:
1072 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001073
1074 Returns:
1075 True if the scripts and build succeeded, False is something failed
1076 """
Paul Fagerburg4376fe52020-06-16 10:22:35 -06001077 logging.info('Running step build_config')
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001078 if not emerge_with_workon(status, status.config_workon_pkgs,
1079 status.emerge_cmd, status.config_emerge_pkgs):
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001080 return False
1081
Paul Fagerburga95dd162020-03-24 16:27:18 -06001082 # Check the generated config.yaml file for occurences of the variant
1083 # name to determine if the emerge was successful.
1084 config_yaml = os.path.join(
1085 '/build', status.base, 'usr/share/chromeos-config/yaml/config.yaml')
1086 logging.debug('config_yaml = "%s"', config_yaml)
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001087 if not os.path.isfile(config_yaml):
Paul Fagerburga95dd162020-03-24 16:27:18 -06001088 logging.error('%s does not exist', config_yaml)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001089 return False
1090
Paul Fagerburga95dd162020-03-24 16:27:18 -06001091 if not status.variant in osutils.ReadFile(config_yaml):
1092 logging.error('variant name %s not found in yaml file %s',
1093 status.variant, config_yaml)
1094 return False
1095
1096 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001097
1098
1099def emerge_all(status):
1100 """Build the coreboot BIOS and EC code for the new variant
1101
Paul Fagerburg75398072020-03-16 13:51:24 -06001102 This build step will cros_workon start a list of packages provided by
1103 the reference board data as status.workon_pkgs, then emerge a list of
1104 packages (status.emerge_pkgs), and then cros_workon stop any packages
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001105 that it started. Any packages that were already being worked on will
1106 not be stopped.
Paul Fagerburg75398072020-03-16 13:51:24 -06001107
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001108 Args:
1109 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001110
1111 Returns:
1112 True if the build succeeded, False if something failed
1113 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001114 logging.info('Running step emerge_all')
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001115 environ = {**os.environ, 'FW_NAME': status.variant}
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001116 if not emerge_with_workon(status, status.workon_pkgs,
1117 status.emerge_cmd, status.emerge_pkgs,
1118 env=environ):
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001119 return False
1120
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001121 # Check if the expected build outputs exist.
Paul Fagerburge868e832020-01-22 17:14:04 -07001122 build_path = '/build/' + status.base + '/firmware'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001123 logging.debug('build_path = "%s"', build_path)
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001124 image_bin = 'image-' + status.variant + '.bin'
1125 if not os.path.isfile(os.path.join(build_path, image_bin)):
1126 logging.error('emerge failed because %s does not exist', image_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001127 return False
1128
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001129 serial_bin = 'image-' + status.variant + '.serial.bin'
1130 if not os.path.isfile(os.path.join(build_path, serial_bin)):
1131 logging.error('emerge failed because %s does not exist', serial_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001132 return False
1133
1134 return True
1135
1136
1137def push_coreboot(status):
1138 """Push the coreboot CL to coreboot.org
1139
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001140 Args:
1141 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001142
1143 Returns:
1144 True if the build succeeded, False if something failed
1145 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001146 logging.info('Running step push_coreboot')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001147
1148 # Set up a return code that may change to False if we find that a
1149 # coreboot CL has not been uploaded.
1150 rc = True
1151
1152 for commit_key in status.coreboot_push_list:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001153 logging.debug('Processing key %s', commit_key)
Paul Fagerburg8d850932020-02-25 14:13:32 -07001154 commit = status.commits[commit_key]
1155 if 'gerrit' not in commit or 'cl_number' not in commit:
1156 change_id = commit['change_id']
1157 cl = find_change_id(change_id)
1158 if cl is not None:
1159 save_cl_data(status, commit_key, cl)
1160 else:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001161 logging.debug('Not found %s, need to upload', change_id)
Paul Fagerburgba508a02020-11-20 14:40:55 -07001162 logging.error(
1163 'The following commit needs to be pushed to coreboot.org:')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001164 logging.error(' Branch "%s"', commit['branch_name'])
1165 logging.error(' in directory "%s"', commit['dir'])
1166 logging.error(' with change-id "%s"', commit['change_id'])
1167 logging.error('Please push the branch to review.coreboot.org, '
1168 'and then re-start this program with --continue')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001169 # Since this commit needs to be uploaded, do not continue after
1170 # this step returns.
1171 rc = False
1172 else:
1173 instance_name = commit['gerrit']
1174 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001175 logging.debug('Already uploaded (%s, %s)', instance_name, cl_number)
Paul Fagerburg8d850932020-02-25 14:13:32 -07001176
1177 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001178
1179
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001180def query_gerrit(instance, change_id):
1181 """Search a gerrit instance for a specific change_id
1182
1183 Args:
1184 instance: gerrit instance to query. Suitable values come from
1185 gerrit.GetCrosInternal() and gerrit.GetCrosExternal()
1186 change_id: The change_id to search for
1187
1188 Returns:
1189 CL number if found, None if not
1190 """
1191 raw = instance.Query(change=change_id, raw=True)
1192 if raw:
1193 # If the CL was found by change_id, there will be only one,
1194 # because the change_id is used to recognize a new patchset
1195 # on an existing CL.
1196 return raw[0]['number']
1197
1198 return None
1199
1200
Paul Fagerburg8d850932020-02-25 14:13:32 -07001201def query_coreboot_gerrit(change_id):
1202 """Search the coreboot gerrit for a specific change_id
1203
1204 Use the REST API to look for the change_id. See
1205 https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html
1206 for details on the REST API to search for a change-id.
1207
1208 We can't use query_gerrit with a manually constructed GerritHelper
1209 because we need the user's private SSH key to access review.coreboot.org,
1210 but these are not available inside the chroot.
1211
1212 Args:
1213 change_id: The change_id to search for
1214
1215 Returns:
1216 CL number if found, None if not
1217 """
1218 r = requests.get('https://review.coreboot.org/changes/' + change_id)
1219 response = r.content.decode('utf-8')
1220 # Check if the response starts with 'Not found', in which case return None
1221 if response.startswith('Not found:'):
1222 return None
1223 # Strip off the initial )]}'\n that is used as XSS protections, see
1224 # https://gerrit-review.googlesource.com/Documentation/rest-api.html#output
1225 # and decode as JSON.
1226 data = json.loads(response[5:])
1227 if '_number' in data:
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001228 return str(data['_number'])
Paul Fagerburg8d850932020-02-25 14:13:32 -07001229 return None
1230
1231
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001232def find_change_id(change_id):
1233 """Search the public and private ChromeOS gerrit instances for a change-id
1234
1235 Args:
1236 change_id: Change-Id to search for in both gerrit instances
1237
1238 Returns:
1239 Tuple of the gerrit instance ('chromium' or 'chrome-internal') and
1240 the CL number if the Change-Id is found.
1241 None if not found.
1242 """
1243 cl_number = query_gerrit(gerrit.GetCrosExternal(), change_id)
1244 if cl_number:
1245 return 'chromium', cl_number
1246 cl_number = query_gerrit(gerrit.GetCrosInternal(), change_id)
1247 if cl_number:
1248 return 'chrome-internal', cl_number
Paul Fagerburg8d850932020-02-25 14:13:32 -07001249 cl_number = query_coreboot_gerrit(change_id)
1250 if cl_number:
1251 return 'coreboot', cl_number
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001252 return None
1253
1254
1255def save_cl_data(status, commit_key, cl):
1256 """Save the gerrit instance and CL number to the yaml file
1257
1258 Args:
1259 status: variant_status object tracking our board, variant, etc.
1260 commit_key: Which key in the commits map we're processing
1261 cl: Value returned by find_change_id, should be a tuple
1262 of instance_name, cl_number
1263 """
1264 instance_name, cl_number = cl
1265 print(f'Found ({instance_name}, {cl_number}), saving to yaml')
1266 status.commits[commit_key]['gerrit'] = instance_name
1267 status.commits[commit_key]['cl_number'] = cl_number
1268 status.save()
1269
1270
1271def repo_upload(branch_name, cwd):
1272 """Upload a branch to gerrit
1273
1274 This function runs `repo upload` in the specified directory to upload
1275 a branch to gerrit. Because it's operating in a directory and with a
1276 branch name, it could upload more than one commit, which is OK because
1277 we'll look for each commit by change-id before trying to upload in that
1278 directory. For example, this happens in Zork, where the cb_config step
1279 and the cras_config step both have a commit in src/overlays. When we're
1280 processing the cb_config step and we `repo upload` in src/overlays, it
1281 will also upload the commit for cras_config. Then we come around to the
1282 cras_config step, and since we can find a CL with the change-id, we don't
1283 try to upload again.
1284
1285 Args:
1286 branch_name: the name of the branch to upload. Gets passed to
1287 repo upload with the --br flag
1288 cwd: directory where we want to upload. Gets set as the working
1289 directory for executing repo upload.
1290
1291 Returns:
1292 True if repo upload exits with a successful error code, false otherwise
1293 """
Paul Fagerburg042a5252020-03-16 21:49:18 -06001294 return run_process(
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001295 ['repo',
1296 'upload',
1297 '.',
1298 '--br=' + branch_name,
1299 '--wip',
1300 '--verify',
Paul Fagerburgda6bc102020-08-31 19:27:04 -06001301 '--yes',
1302 '--hashtag=new_variant'],
Paul Fagerburg042a5252020-03-16 21:49:18 -06001303 cwd=cwd)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001304
1305
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001306def upload_CLs(status):
1307 """Upload all CLs to chromiumos
1308
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001309 Args:
1310 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001311
1312 Returns:
1313 True if the build succeeded, False if something failed
1314 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001315 logging.info('Running step upload_CLs')
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001316
1317 for commit_key in status.repo_upload_list:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001318 logging.debug('Processing key %s', commit_key)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001319 commit = status.commits[commit_key]
1320 if 'gerrit' not in commit or 'cl_number' not in commit:
1321 change_id = commit['change_id']
1322 cl = find_change_id(change_id)
1323 if cl is not None:
1324 save_cl_data(status, commit_key, cl)
1325 else:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001326 logging.debug('Not found %s, need to upload', change_id)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001327 if not repo_upload(commit['branch_name'], commit['dir']):
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001328 logging.error('Repo upload %s in %s failed!',
1329 commit['branch_name'],
1330 commit['dir'])
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001331 return False
1332 cl = find_change_id(change_id)
1333 if cl is None:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001334 logging.error(
1335 'repo upload %s succeeded, but change_id is not found!',
1336 commit_key)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001337 return False
1338 save_cl_data(status, commit_key, cl)
1339 else:
1340 instance_name = commit['gerrit']
1341 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001342 logging.debug('Already uploaded (%s, %s)', instance_name, cl_number)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001343
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001344 return True
1345
1346
1347def find_coreboot_upstream(status):
Paul Fagerburgba508a02020-11-20 14:40:55 -07001348 """Find the upstream coreboot CL in chromiumos
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001349
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001350 When the coreboot variant CL is first uploaded to review.coreboot.org,
1351 it is not visible in the chromiumos tree (and also cannot be used as
Paul Fagerburgba508a02020-11-20 14:40:55 -07001352 a target for cq-depend). There is a process for upstream CLs from
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001353 coreboot after they have been reviewed, approved, and merged. We can
1354 track a specific coreboot CL if we know the change-id that it used on
1355 the coreboot gerrit instance, by looking for that change-id as
1356 'original-change-id' in the public chromium gerrit instance.
1357
1358 The change-id for the coreboot variant will be under the 'cb_variant' key,
1359 but this is for the 'coreboot' gerrit instance.
1360
1361 When we find the upstreamed CL, we will record the gerrit instance and
1362 CL number in the yaml file under the 'find' key ("find upstream coreboot")
1363 so that we don't need to search coreboot again.
1364
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001365 Args:
1366 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001367
1368 Returns:
1369 True if the build succeeded, False if something failed
1370 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001371 logging.info('Running step find_coreboot_upstream')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001372
1373 # If we have already found the upstream coreboot CL, then exit with success
1374 if step_names.FIND in status.commits:
1375 commit = status.commits[step_names.FIND]
1376 if 'gerrit' in commit and 'cl_number' in commit:
1377 instance_name = commit['gerrit']
1378 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001379 logging.debug('Already found (%s, %s)', instance_name, cl_number)
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001380 return True
1381
1382 # Make sure we have a CB_VARIANT commit and a change_id for it
1383 if step_names.CB_VARIANT not in status.commits:
1384 logging.error('Key %s not found in status.commits',
1385 step_names.CB_VARIANT)
1386 return False
1387 if 'change_id' not in status.commits[step_names.CB_VARIANT]:
1388 logging.error('Key change_id not found in status.commits[%s]',
1389 step_names.CB_VARIANT)
1390 return False
1391
1392 # Find the CL by the Original-Change-Id
1393 original_change_id = status.commits[step_names.CB_VARIANT]['change_id']
1394 gerrit_query_args = {
Paul Fagerburg0d443e72021-03-25 10:27:45 -06001395 'message': f'Original-Change-Id:{original_change_id}'
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001396 }
1397 cros = gerrit.GetCrosExternal()
1398 upstream = cros.Query(**gerrit_query_args)
1399 # If nothing is found, the patch hasn't been upstreamed yet
1400 if not upstream:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001401 logging.error('Program cannot continue without upstream coreboot CL.')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001402 logging.error('(coreboot:%s, change-id %s)',
1403 status.commits[step_names.CB_VARIANT]['cl_number'],
1404 status.commits[step_names.CB_VARIANT]['change_id'])
Paul Fagerburgba508a02020-11-20 14:40:55 -07001405 logging.error('Please wait for the upstream CL, then run this program'
1406 ' again with --continue')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001407 return False
1408
1409 # If more than one CL is found, something is very wrong
1410 if len(upstream) != 1:
1411 logging.error('More than one CL was found with Original-Change-Id %s',
1412 original_change_id)
1413 return False
1414
1415 # At this point, we know there is only one CL and we can get the
1416 # repo and CL number by splitting on the colon between them.
1417 patchlink = upstream[0].PatchLink()
1418 instance_name, cl_number = patchlink.split(':')
1419
1420 # Can't use get_git_commit_data because we're not pulling this
1421 # information from a git commit, but rather from gerrit.
1422 # We only need the gerrit instance and the CL number so we can have
1423 # other CLs cq-depend on this CL. The other keys are not needed because:
1424 # dir - not needed because we're not going to `cd` there to `repo upload`
1425 # branch_name - not valid; the CL is already merged
1426 # change_id - we use the change_id to find a CL number, and since we
1427 # just found the CL number via original-change-id, this is moot.
1428 status.commits[step_names.FIND] = {
1429 'gerrit': instance_name,
1430 'cl_number': str(cl_number)
1431 }
1432
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001433 return True
1434
1435
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001436def calc_cq_depend(status):
1437 """Determine the list of CLs for each commit that has dependencies.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001438
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001439 status.depends is a map of dependencies from step name to a list of
1440 steps that the step depends on. For each step, find the SHA of the
1441 commit, then find the gerrit instance and CL number of the commits
1442 that it depends on. Construct the Cq-Depends list and save it under
1443 the 'cq_depend' key, i.e. commit['add_priv_yaml']['cq_depend'] or
1444 as it will be stored in the yaml:
1445 commits:
1446 add_priv_yaml:
1447 cq_depend: 'chromium:1629121, chromium:1638243'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001448
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001449 Args:
1450 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001451
1452 Returns:
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001453 True if all dependencies have been calculated. False if something
1454 failed, usually a commit not found by Change-Id.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001455 """
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001456 logging.info('Running step calc_cq_depend')
1457 # Iterate through the commits that have dependencies.
1458 for key in status.depends:
1459 logging.debug('Processing %s to add dependencies', key)
1460 # For every commit that has dependencies, find the gerrit instance
1461 # and CL number of the dependencies.
1462 cq_depend_list = []
1463 for depend_key in status.depends[key]:
1464 depend_commit = status.commits[depend_key]
1465 if not 'gerrit' in depend_commit:
1466 logging.error('Commit %s does not have a gerrit instance',
1467 depend_key)
1468 return False
1469 if not 'cl_number' in depend_commit:
1470 logging.error('Commit %s does not have a CL number',
1471 depend_key)
1472 return False
1473
1474 instance_name = depend_commit['gerrit']
1475 cl_number = depend_commit['cl_number']
1476 cq_depend_list.append(f'{instance_name}:{cl_number}')
1477
1478 # Add the 'cq_depend' key to the commit.
1479 cq_depend_str = 'Cq-Depend: %s' % ', '.join(cq_depend_list)
1480 logging.debug('Add to commit %s %s', key, cq_depend_str)
1481 status.commits[key]['cq_depend'] = cq_depend_str
1482
1483 return True
1484
1485
1486def add_cq_depend_to_commit_msg(git_repo, change_id, cq_depend_str):
1487 """Update the commit message with a Cq-Depends line.
1488
1489 Find the SHA of the commit, then use git filter-branch --msg-filter
1490 to add the Cq-Depend line just before the Change-Id line. See
1491 https://chromium.googlesource.com/chromiumos/docs/+/HEAD/contributing.md#cq-depend
1492 for details about Cq-Depend format and location.
1493
1494 Args:
1495 git_repo: Directory of git repository.
1496 change_id: The Change-Id to search for.
1497 cq_depend_str: The Cq-Depend string. It must be in the correct format
1498 per chromeos documentation, ready to insert into the commit msg
1499 on the line before Change-Id.
1500
1501 Returns:
1502 True if `git filter-branch` was successful. False if the command
1503 failed.
1504 """
1505 logging.debug('find SHA of Change-Id %s in %s', change_id, git_repo)
1506 sha = change_id_to_sha(git_repo, change_id)
1507 if sha is None:
1508 logging.error('Cannot find the SHA for Change-Id %s in %s',
1509 change_id, git_repo)
1510 return False
1511 logging.debug('SHA = %s', sha)
1512
1513 # Check if the commit message already has a Cq-Depend line.
1514 msg = get_commit_msg(git_repo, sha)
1515 if any('Cq-Depend' in tmpstr for tmpstr in msg):
1516 logging.debug('Already has Cq-Depend')
1517 return True
1518
1519 # Use git filter-branch --msg-filter to add the Cq-Depend line just
1520 # before the Change-Id line.
1521 environ = {**os.environ, 'FILTER_BRANCH_SQUELCH_WARNING': '1'}
1522 cmd = [
1523 'git',
1524 'filter-branch',
Paul Fagerburg5f6d2012020-06-24 11:29:36 -06001525 '-f',
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001526 '--msg-filter',
1527 f'sed -E "s/^(Change-Id: {change_id})$/{cq_depend_str}\\n\\1/"',
1528 '--',
1529 f'{sha}^..']
1530 return run_process(cmd, cwd=git_repo, env=environ)
1531
1532
1533def add_cq_depend(status):
1534 """Add Cq-Depend to commits and flag them for re-upload.
1535
1536 Args:
1537 status: variant_status object tracking our board, variant, etc.
1538
1539 Returns:
1540 True if the commit messages have been successfully amended, False if
1541 something failed.
1542 """
1543 logging.info('Running step add_cq_depend')
1544 for key in status.commits:
1545 commit = status.commits[key]
1546 if 'cq_depend' in commit:
1547 logging.debug('%s has %s', key, commit['cq_depend'])
1548 # Make sure the commit has a working directory and a change_id
1549 # before trying to amend its commit message.
1550 if 'dir' not in commit or 'change_id' not in commit:
1551 logging.error('Missing dir and/or change_id from %s', key)
1552 return False
1553
1554 if not add_cq_depend_to_commit_msg(commit['dir'],
1555 commit['change_id'],
1556 commit['cq_depend']):
1557 return False
1558 commit['needs_re_upload'] = True
1559 else:
1560 logging.debug('%s no dependencies', key)
1561
1562 return True
1563
1564
1565def re_upload(status):
1566 """Re-upload commits that have changed.
1567
1568 Args:
1569 status: variant_status object tracking our board, variant, etc.
1570
1571 Returns:
1572 True if the uploads succeeded. False if a repo upload failed.
1573 """
1574 logging.info('Running step re_upload')
1575 for key in status.commits:
1576 commit = status.commits[key]
1577 if commit.get('needs_re_upload'):
1578 logging.debug('Re-upload branch %s in %s', commit['branch_name'],
1579 commit['dir'])
1580 if not repo_upload(commit['branch_name'], commit['dir']):
1581 logging.error('Repo upload %s in %s failed!',
1582 commit['branch_name'],
1583 commit['dir'])
1584 return False
1585 commit['needs_re_upload'] = False
1586
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001587 return True
1588
1589
1590def clean_up(status):
1591 """Final clean-up, including delete the status file
1592
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001593 Args:
1594 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001595
1596 Returns:
1597 True
1598 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001599 logging.info('Running step clean_up')
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001600 status.rm()
1601 return True
1602
1603
Paul Fagerburg042a5252020-03-16 21:49:18 -06001604def abort(status):
1605 """Abort the creation of a new variant by abandoning commits
1606
1607 When the user specifies the --abort flag, we override status.step to
1608 be 'abort' and there is no transition from 'abort' to anything else.
1609 We look at status.commits and for each key, see if we have already
1610 been in that directory and abandoned that specific branch. If not,
1611 abandon the commit and then add the branch+dir to a list of abandoned
1612 commits. We do this because some boards (such as Zork) can have multiple
1613 commits in the same directory and with the same branch name, and we only
1614 want to repo abandon that branch once.
1615
1616 Args:
1617 status: variant_status object tracking our board, variant, etc.
1618
1619 Returns:
1620 True
1621 """
1622 logging.info('Running step abort')
1623 # Use the set 'abandoned' to keep track of each branch+dir abandoned.
1624 abandoned = set()
1625 for step in status.commits:
1626 logging.debug('Processing step %s', step)
1627 commit = status.commits[step]
1628 branch = commit['branch_name']
1629 cwd = commit['dir']
1630 if (branch, cwd) in abandoned:
1631 logging.debug('Branch %s in directory %s already abandoned',
1632 branch, cwd)
1633 else:
1634 logging.info('Abandoning branch %s in directory %s',
1635 branch, cwd)
1636 if run_process(['repo', 'abandon', branch, '.'], cwd=cwd):
1637 abandoned.add((branch, cwd))
1638 else:
1639 logging.error('Error while abandoning branch %s', branch)
1640 return False
1641
1642 return True
1643
1644
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001645if __name__ == '__main__':
1646 sys.exit(not int(main()))