blob: 6dcdfc78ade49ab9318c67de1b868e2f849635ee [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 Fagerburg5f794bf2020-02-12 13:01:36 -0700242 * fitimage_dir - directory for fitimage; prepend '/mnt/host/source/src/'
243 in chroot, prepend '~/chromiumos/src' outside the chroot
Paul Fagerburg7429c572020-11-20 19:28:27 -0700244 * fitimage_outputs_dir - directory under fitimage_dir where gen_fit_image.sh
245 leaves its outputs
Nick Vaccarof8d4e932020-12-11 01:52:23 +0000246 * fitimage_cmd - explanation of gen_fit_image command, i.e. tell the user
247 how to run gen_fit_image.sh
248 * fitimage_script - script to add fitimage sources, defaults
249 to 'files/add_fitimage.sh' if not present. Only volteer and volteer2
250 currently need to use this.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700251 * workon_pkgs - list of packages to cros_workon
252 * emerge_cmd - the emerge command, e.g. 'emerge-hatch'
253 * emerge_pkgs - list of packages to emerge
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600254 * config_workon_pkgs - list of packages to cros_workon to build the
255 project config
256 * config_emerge_pkgs - list of packages to emerge to build the project
257 config
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700258 * private_yaml_dir - directory for the private yaml file
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700259 * commits - map of commits for the various steps. Indexed by step name,
260 and the step names used are the same ones in step_names.py
261 * repo_upload_list - list of commits to upload using `repo upload`
262 * coreboot_push_list - list of commits to upload using `git push` to
263 coreboot
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600264 * depends - maps a step to a list of steps on which it depends, e.g.
265 depends[step_names.ADD_PRIV_YAML] is a list of other steps that
266 the 'add_priv_yaml' step depends on. This map is used to amend
267 the commits with CL numbers for Cq-Depend.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700268
269 Additionally, the following fields will be set:
270
Nick Vaccarof8d4e932020-12-11 01:52:23 +0000271 * board - the name of the reference board, e.g. 'hatch'. The --board
272 command line flag specifies this value, and new_variant derives the
273 the name of the python module to load from this value. However, in
274 certain cases (volteer2), the python module may override this value
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700275 * variant - the name of the variant, e.g. 'sushi'
276 * bug - optional text for a bug ID, used in the git commit messages.
277 Could be 'None' (as text, not the python None), or something like
278 'b:12345' for buganizer, or 'chromium:12345'
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700279 * branch - optional text for a BRANCH= value in the commit message for
280 repos that use the BRANCH field (coreboot and EC). If not specified,
281 then None.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700282 * step - internal state tracking, what step of the variant creation
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700283 we are at.
284 * yaml_file - internal, just the name of the file where all this data
285 gets saved.
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700286 * commit - a map of maps that tracks all of the git commit and gerrit CL
287 data for each of the steps in the process. For example,
288 status.commit['add_priv_yaml'] is a map that has all the information
289 about the 'add_priv_yaml' step. The keys in the maps allow us to
290 determine where the commit is, the change_id, if it has been uploaded
291 to gerrit and where.
292
293 branch_name - the name of the git branch
294 change_id - the change-id assigned by the commit hook. Gerrit
295 uses the change_id to track new patchsets in the CL
296 dir - the directory where the commit has been created
297 gerrit - the name of the gerrit instance to which the CL has
298 been uploaded, one of 'chromium', 'chrome-internal', or
299 'coreboot'
300 cl_number - the CL number on the gerrit instance
301
302 When the commit is created, branch_name, change_id, and dir are all
303 set. The gerrit and cl_number keys are not set until the CL has been
304 uploaded to a gerrit instance.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700305
306 These data might come from the status file (because we read it), or
307 they might be the initial values after we created the file (because
308 it did not already exist).
309
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700310 Args:
311 board: Name of the reference board
312 variant: Name of the variant being created
313 bug: Text for bug number, if any ('None' otherwise)
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700314 branch: Text for a BRANCH= value in the commit message (or 'None')
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700315 continue_flag: Flag if --continue was specified
Paul Fagerburg042a5252020-03-16 21:49:18 -0600316 abort_flag: Flag if --abort was specified
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700317
318 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700319 variant_status object with all the data mentioned above
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700320 """
321 status = variant_status.variant_status()
Paul Fagerburg042a5252020-03-16 21:49:18 -0600322 if continue_flag or abort_flag:
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700323 if status.yaml_file_exists():
Paul Fagerburg042a5252020-03-16 21:49:18 -0600324 return status
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600325
326 if continue_flag:
327 op = '--continue'
328 if abort_flag:
329 op = '--abort'
330 logging.error('%s does not exist; cannot %s', status.yaml_file, op)
331 return None
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700332
Paul Fagerburg042a5252020-03-16 21:49:18 -0600333 # If we get here, the user provided --board and --variant (because
334 # check_flags() returned Trued), but the yaml file already exists,
335 # so we print an error message and bail.
336 if status.yaml_file_exists():
337 logging.error(
338 'new_variant already in progress; did you forget --continue?')
339 return None
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700340
Paul Fagerburg042a5252020-03-16 21:49:18 -0600341 # At this point, it's not --continue, not --abort, the yaml file doesn't
342 # exist, and we have valid values for --board, --variant, and --bug (bug
343 # might be the default value of "None"). Create the yaml file with data
344 # from the reference board's loadable module.
345 status.board = board
346 status.variant = variant
347 status.bug = bug
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700348 status.branch = branch
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700349
Paul Fagerburg042a5252020-03-16 21:49:18 -0600350 # Load the appropriate module and copy all the data from it.
351 try:
352 module = importlib.import_module(board)
353 except ImportError:
354 print('Unsupported board "' + board + '"')
355 sys.exit(1)
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700356
Nick Vaccarof8d4e932020-12-11 01:52:23 +0000357 # Special case: allow the module to override the name of the reference
358 # board. Almost always, you want the module name (e.g. puff.py) and the
359 # name of the reference board (e.g. puff) to match. However, for some
360 # boards (volteer2), you want to have the reference board name still
361 # be 'volteer'.
362 status.board = getattr(module, 'board', status.board)
363
Paul Fagerburg042a5252020-03-16 21:49:18 -0600364 status.base = module.base
Paul Fagerburg3ef1cbb2020-06-02 16:38:36 -0600365 status.coreboot_base = getattr(module, 'coreboot_base', module.base)
Paul Fagerburg042a5252020-03-16 21:49:18 -0600366 status.coreboot_dir = module.coreboot_dir
Paul Fagerburgba508a02020-11-20 14:40:55 -0700367 status.coreboot_reference = getattr(module, 'coreboot_reference',
368 status.board)
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600369 status.cb_config_dir = getattr(module, 'cb_config_dir', None)
Paul Fagerburg042a5252020-03-16 21:49:18 -0600370 status.emerge_cmd = module.emerge_cmd
371 status.emerge_pkgs = module.emerge_pkgs
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600372 status.fitimage_dir = getattr(module, 'fitimage_dir', None)
Paul Fagerburg7429c572020-11-20 19:28:27 -0700373 status.fitimage_outputs_dir = getattr(module, 'fitimage_outputs_dir', None)
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600374 status.fitimage_pkg = getattr(module, 'fitimage_pkg', None)
375 status.fitimage_cmd = getattr(module, 'fitimage_cmd', None)
Nick Vaccarof8d4e932020-12-11 01:52:23 +0000376 status.fitimage_script = getattr(module, 'fitimage_script',
377 'files/add_fitimage.sh')
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600378 status.fsp = getattr(module, 'fsp', None)
379 status.private_yaml_dir = getattr(module, 'private_yaml_dir', None)
Paul Fagerburg042a5252020-03-16 21:49:18 -0600380 status.step_list = module.step_list
381 status.workon_pkgs = module.workon_pkgs
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600382 status.config_workon_pkgs = module.config_workon_pkgs
383 status.config_emerge_pkgs = module.config_emerge_pkgs
Paul Fagerburg042a5252020-03-16 21:49:18 -0600384 status.coreboot_push_list = module.coreboot_push_list
385 status.repo_upload_list = module.repo_upload_list
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600386 status.depends = module.depends
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700387
Paul Fagerburg042a5252020-03-16 21:49:18 -0600388 # Start at the first entry in the step list
389 status.step = status.step_list[0]
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700390
Paul Fagerburg042a5252020-03-16 21:49:18 -0600391 # Start an empty map for tracking CL data
392 status.commits = {}
393
394 status.save()
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700395
396 return status
397
398
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700399def perform_step(status):
400 """Call the appropriate function for the current step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700401
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700402 Args:
403 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700404
405 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700406 True if the step succeeded, False if it failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700407 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700408 # Function to call based on the step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700409 dispatch = {
Paul Fagerburgbc184242020-04-28 17:00:54 -0600410 step_names.PROJECT_CONFIG: project_config,
Paul Fagerburg8f60b802020-06-09 15:38:28 -0600411 step_names.FW_BUILD_CONFIG: fw_build_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700412 step_names.CB_VARIANT: create_coreboot_variant,
413 step_names.CB_CONFIG: create_coreboot_config,
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700414 step_names.CRAS_CONFIG: copy_cras_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700415 step_names.ADD_FIT: add_fitimage,
416 step_names.GEN_FIT: gen_fit_image_outside_chroot,
417 step_names.COMMIT_FIT: commit_fitimage,
418 step_names.EC_IMAGE: create_initial_ec_image,
419 step_names.EC_BUILDALL: ec_buildall,
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700420 step_names.ADD_PUB_YAML: add_variant_to_public_yaml,
421 step_names.ADD_PRIV_YAML: add_variant_to_private_yaml,
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600422 step_names.BUILD_CONFIG: build_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700423 step_names.EMERGE: emerge_all,
424 step_names.PUSH: push_coreboot,
425 step_names.UPLOAD: upload_CLs,
426 step_names.FIND: find_coreboot_upstream,
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600427 step_names.CALC_CQ_DEPEND: calc_cq_depend,
428 step_names.ADD_CQ_DEPEND: add_cq_depend,
429 step_names.RE_UPLOAD: re_upload,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700430 step_names.CLEAN_UP: clean_up,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600431 step_names.ABORT: abort,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700432 }
433
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700434 if status.step not in dispatch:
435 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700436 sys.exit(1)
437
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700438 return dispatch[status.step](status)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700439
440
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700441def move_to_next_step(status):
442 """Move to the next step in the list
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700443
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700444 Args:
445 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700446 """
Paul Fagerburg042a5252020-03-16 21:49:18 -0600447 # Special case: the next step after 'abort' is 'clean_up'. Always.
448 if status.step == step_names.ABORT:
449 status.step = step_names.CLEAN_UP
450 return
451
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700452 if status.step not in status.step_list:
453 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700454 sys.exit(1)
455
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700456 idx = status.step_list.index(status.step)
457 if idx == len(status.step_list)-1:
458 status.step = None
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700459 else:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700460 status.step = status.step_list[idx+1]
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700461
462
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700463def run_process(args, cwd=None, env=None, capture_output=False):
464 """Run a process, log debug messages, return text output of process
465
466 The capture_output parameter allows us to capture the output when we
467 care about it (and not sending it to the screen), or ignoring it when
468 we don't care, and letting the user see the output so they know that
469 the build is still running, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700470
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700471 Args:
472 args: List of the command and its params
473 cwd: If not None, cd to this directory before running
474 env: Environment to use for execution; if needed, get os.environ.copy()
475 and add variables. If None, just use the current environment
476 capture_output: True if we should capture the stdout, false if we
477 just care about success or not.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700478
479 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700480 If capture_output == True, we return the text output from running
481 the subprocess as a list of lines, or None if the process failed.
482 If capture_output == False, we return a True if it successed, or
483 None if the process failed.
484
485 The caller can evaluate as a bool, because bool(None) == False, and
486 bool() of a non-empty list is True, or the caller can use the returned
487 text for further processing.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700488 """
489 logging.debug('Run %s', str(args))
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700490 if cwd is not None:
491 logging.debug('cwd = %s', cwd)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700492 try:
493 if capture_output:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700494 output = subprocess.run(args, cwd=cwd, env=env, check=True,
495 stderr=subprocess.STDOUT, stdout=subprocess.PIPE).stdout
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700496 else:
497 subprocess.run(args, cwd=cwd, env=env, check=True)
498 # Just something to decode so we don't get an empty list
499 output = b'True'
500
501 logging.debug('process returns 0')
502 # Convert from byte string to ASCII
503 decoded = output.decode('utf-8')
504 # Split into array of individual lines
505 lines = decoded.split('\n')
506 return lines
507 except subprocess.CalledProcessError as err:
508 logging.debug('process returns %s', str(err.returncode))
509 return None
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700510
511
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700512def get_git_commit_data(cwd):
513 """Get the branch name and change id of the current commit
514
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700515 Args:
516 cwd: The current working directory, where we want to get the branch
517 name and change id
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700518
519 Returns:
520 Map with 'dir', 'branch_name' and 'change_id' keys. The 'dir'
521 key maps to the value of os.path.expanduser(cwd)
522 """
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700523 cwd = git.FindGitTopLevel(os.path.expanduser(cwd))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700524 logging.debug('get_git_commit_data(%s)', cwd)
525
526 branch_name = git.GetCurrentBranch(cwd)
527 if branch_name is None:
528 logging.error('Cannot determine git branch name in %s; exiting', cwd)
529 sys.exit(1)
530 logging.debug('git current branch is %s', branch_name)
531
532 change_id = git.GetChangeId(cwd)
533 if change_id is None:
534 logging.error('Cannot determine Change-Id in %s; exiting', cwd)
535 sys.exit(1)
536 logging.debug('git Change-Id is %s', change_id)
537
538 return {
539 'dir': cwd,
540 'branch_name': branch_name,
541 'change_id': change_id
542 }
543
544
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600545def change_id_to_sha(git_repo, change_id):
546 """Find the SHA for a given Change-Id.
547
548 Args:
549 git_repo: Directory of git repository.
550 change_id: The Change-Id to search for.
551
552 Returns:
553 The SHA hash for the Change-Id if only one commit is found.
554 None if the Change-Id was not found.
555 Raises a ValueError if more than one commit is found with the
556 same Change-Id.
557 """
558 output = git.Log(git_repo, max_count=1, format='format:%H',
559 grep=fr'^Change-Id: {change_id}$')
560 sha_hashes = output.splitlines()
561 if not sha_hashes:
562 return None
563 if len(sha_hashes) > 1:
564 raise ValueError('More than one SHA with that Change-Id found')
565 return sha_hashes[0]
566
567
568def get_commit_msg(git_repo, rev):
569 """Get the commit message for a given revision.
570
571 Because git.Log doesn't allow specifying check=False or getting the
572 returncode, we have to catch the CalledProcessError instead.
573
574 Args:
575 git_repo: Directory of git repository.
576 rev: The revision to search for, a SHA or a label.
577
578 Returns:
579 The commit message as a list of strings, if the revision exists.
580 None if the revision was not found.
581 """
582 try:
583 msg = git.Log(git_repo, max_count=1, format='format:%B', rev=rev)
584 return msg.splitlines()
Paul Fagerburgba508a02020-11-20 14:40:55 -0700585 except cros_build_lib.CalledProcessError as err:
586 raise ValueError('SHA was not found') from err
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600587
588
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600589def emerge_with_workon(status, workon_pkgs, emerge_cmd, emerge_pkgs, env=None):
590 """Emerge a list of packages after `cros_workon start`ing them
591
592 This function will `cros_workon start` a list of packages, then `emerge`
593 another list of packages, and finally, `cros_workon stop` only those
594 packages that were actually started by the `cros_workon start` command.
595 Any package already in a `cros_workon start` state prior to this function
596 will still be in that state when this function exits.
597
598 To determine which packages this program started and which ones were
599 already started, we query the list of packages being worked on, then
600 cros_workon start the entire list (which will produce a "package already
601 being worked on" type of message for anything already started), and then
602 query the list of packages being worked on again. The difference between
603 the before and after lists are the packages that this program started,
604 and so that's the list of packages to cros_workon stop after the emerge
605 is done.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700606
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700607 Args:
608 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600609 workon_pkgs: list of packages to `cros_workon start`
610 emerge_cmd: the emerge command to run, e.g. 'emerge-volteer'
611 emerge_pkgs: list of packages to `emerge`
612 env: environment to pass to run_process, or None to pass default
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700613
614 Returns:
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600615 True if everything succeeded, False if something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700616 """
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600617 # Get the list of packages that are already cros_workon started.
618 build_target = build_target_lib.BuildTarget(status.base)
619 workon = workon_helper.WorkonHelper(build_target.root, build_target.name)
620 before_workon = workon.ListAtoms()
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700621
Paul Fagerburg4376fe52020-06-16 10:22:35 -0600622 # Only cros_workon start if the list is non-empty
623 if workon_pkgs:
624 workon.StartWorkingOnPackages(workon_pkgs)
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600625
626 # Determine which packages we need to cros_workon stop.
627 after_workon = workon.ListAtoms()
628 stop_packages = list(set(after_workon) - set(before_workon))
629
630 # Run the emerge command.
631 emerge_result = run_process([emerge_cmd] + emerge_pkgs, env=env)
632
Paul Fagerburg4376fe52020-06-16 10:22:35 -0600633 # If the list is non-empty, cros_workon stop before returning the result.
634 if stop_packages:
635 workon.StopWorkingOnPackages(stop_packages)
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600636
637 return emerge_result
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700638
639
Paul Fagerburgbc184242020-04-28 17:00:54 -0600640def project_config(status):
641 """Check if the project config is correct and complete
642
643 For programs that use the new project/config structure with starlark
644 configuration files, this function will check that emerging the
645 project's configuration will result in a project-config.json that
646 includes the new name of the new hwdesign (a.k.a. "variant").
647
648 Args:
649 status: variant_status object tracking our board, variant, etc.
650
651 Returns:
652 True if everything succeeded, False if something failed
653 """
654 logging.info('Running step project_config')
655 try:
656 if not emerge_with_workon(status, status.config_workon_pkgs,
657 status.emerge_cmd, status.config_emerge_pkgs):
Paul Fagerburgba508a02020-11-20 14:40:55 -0700658 raise RuntimeError('Building the configuration failed.')
Paul Fagerburgbc184242020-04-28 17:00:54 -0600659
660 # Make sure project-config.json exists in the /build tree
661 emerged_json = os.path.join(
662 '/build',
663 status.base,
664 'usr/share/chromeos-config/yaml/project-config.json')
665 if not os.path.isfile(emerged_json):
666 raise RuntimeError(
667 f'project-config.json {emerged_json} does not exist.')
668
Paul Fagerburgacb0c552020-12-21 18:24:18 -0700669 # Search the JSON for a config with the new variant's name
670 with open(emerged_json, 'r') as fp:
671 pc = json.load(fp)
672
673 names = {config['name'] for config in pc['chromeos']['configs']}
674 if status.variant not in names:
Paul Fagerburgbc184242020-04-28 17:00:54 -0600675 raise RuntimeError(
676 f'variant name {status.variant} not found in {emerged_json}')
677
678 except RuntimeError as e:
679 logging.error(str(e))
680 logging.error('Please file a bug in Infra > ChromeOS > Product > Device'
681 ' to have the project configuration updated.')
682 logging.error('(https://bugs.chromium.org/p/chromium/issues/list?'
683 'q=component:Infra%3EChromeOS%3EProduct%3EDevice)')
684 return False
685
686 return True
687
688
Paul Fagerburg8f60b802020-06-09 15:38:28 -0600689def fw_build_config(status):
690 """Add the _FW_BUILD_CONFIG setting to the project config
691
692 For programs that use the new project/config structure with starlark
693 configuration files, this function calls fw_build_config.sh, which will
694 modify the config.star file to have a default _FW_BUILD_CONFIG entry.
695
696 Args:
697 status: variant_status object tracking our board, variant, etc.
698
699 Returns:
700 True if everything succeeded, False if something failed
701 """
702 logging.info('Running step fw_build_config')
703 fw_build_config_sh = os.path.join(status.my_loc, 'fw_build_config.sh')
704 rc = run_process(
705 [fw_build_config_sh,
706 status.base,
707 status.variant,
708 status.bug])
709 if rc:
710 status.commits[step_names.FW_BUILD_CONFIG] = get_git_commit_data(
Paul Fagerburgba508a02020-11-20 14:40:55 -0700711 os.path.join('/mnt/host/source/src/project',
712 status.base, status.variant))
Paul Fagerburg8f60b802020-06-09 15:38:28 -0600713 return rc
714
715
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700716def create_coreboot_variant(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700717 """Create source files for a new variant of the reference board in coreboot
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700718
719 This function calls create_coreboot_variant.sh to set up a new variant
Paul Fagerburge868e832020-01-22 17:14:04 -0700720 of the reference board.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700721
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700722 Args:
723 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700724
725 Returns:
726 True if everything succeeded, False if something failed
727 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700728 logging.info('Running step create_coreboot_variant')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700729 cb_src_dir = os.path.join('/mnt/host/source/src/', status.coreboot_dir)
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700730 environ = {**os.environ, 'CB_SRC_DIR': cb_src_dir,
731 'NEW_VARIANT_BRANCH': status.branch}
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700732 create_coreboot_variant_sh = os.path.join(status.my_loc,
733 'create_coreboot_variant.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600734 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700735 [create_coreboot_variant_sh,
Paul Fagerburg3ef1cbb2020-06-02 16:38:36 -0600736 status.coreboot_base,
Matt Ziegelbaum3e879df2020-11-09 16:08:44 -0500737 status.coreboot_reference,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700738 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600739 status.bug], env=environ)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700740 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700741 status.commits[step_names.CB_VARIANT] = get_git_commit_data(cb_src_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700742 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700743
744
745def create_coreboot_config(status):
746 """Create a coreboot configuration for a new variant
747
748 This function calls create_coreboot_config.sh, which will make a copy
749 of coreboot.${BOARD} into coreboot.${VARIANT}.
750
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700751 Args:
752 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700753
754 Returns:
755 True if the script and test build succeeded, False if something failed
756 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700757 logging.info('Running step create_coreboot_config')
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600758 # Only set CB_CONFIG_DIR if it's not None, so here we have to copy
759 # the environment first and then optionally add a key.
Paul Fagerburg4d343452020-01-24 15:23:53 -0700760 environ = os.environ.copy()
761 if status.cb_config_dir is not None:
762 environ['CB_CONFIG_DIR'] = status.cb_config_dir
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700763 create_coreboot_config_sh = os.path.join(status.my_loc,
764 'create_coreboot_config.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600765 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700766 [create_coreboot_config_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700767 status.base,
Matt Ziegelbaum3e879df2020-11-09 16:08:44 -0500768 status.coreboot_reference,
Paul Fagerburge868e832020-01-22 17:14:04 -0700769 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600770 status.bug], env=environ)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700771 if rc:
772 # Use status.cb_config_dir if defined, or if not, use
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700773 # '/mnt/host/source/src/third_party/chromiumos-overlay'
Paul Fagerburgba508a02020-11-20 14:40:55 -0700774 cb_config_dir = os.path.join(
775 '/mnt/host/source/src/',
776 status.cb_config_dir or 'third_party/chromiumos-overlay')
777 status.commits[step_names.CB_CONFIG] = get_git_commit_data(
778 cb_config_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700779 return rc
Paul Fagerburge868e832020-01-22 17:14:04 -0700780
781
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700782def copy_cras_config(status):
783 """Copy the cras config for a new variant
784
785 This is only necessary for the Zork baseboard right now.
786 This function calls copy_cras_config.sh, which will copy the
Paul Fagerburgba508a02020-11-20 14:40:55 -0700787 cras config in overlays/overlay-${BASE}/chromeos-base/\
788 chromeos-bsp-${BASE}/files/cras-config/${BASE} to .../${VARIANT}
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700789
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700790 Args:
791 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700792
793 Returns:
794 True if the script and test build succeeded, False if something failed
795 """
796 logging.info('Running step copy_cras_config')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700797 copy_cras_config_sh = os.path.join(status.my_loc, 'copy_cras_config.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600798 rc = run_process(
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700799 [copy_cras_config_sh,
800 status.base,
801 status.board,
802 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600803 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700804 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700805 status.commits[step_names.CRAS_CONFIG] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700806 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700807 return rc
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700808
809
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700810def add_fitimage(status):
811 """Add the source files for a fitimage for the new variant
812
813 This function calls add_fitimage.sh to create a new XSL file for the
Paul Fagerburge868e832020-01-22 17:14:04 -0700814 variant's fitimage, which can override settings from the reference board's
815 XSL. When this is done, the user will have to build the fitimage by running
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700816 gen_fit_image.sh outside of the chroot (and outside of this program's
817 control) because gen_fit_image.sh uses WINE, which is not installed in
818 the chroot. (There is a linux version of FIT, but it requires Open GL,
819 which is also not installed in the chroot.)
820
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700821 Args:
822 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700823
824 Returns:
825 True if the script succeeded, False otherwise
826 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700827 logging.info('Running step add_fitimage')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700828 add_fitimage_sh = os.path.expanduser(os.path.join(
Nick Vaccarof8d4e932020-12-11 01:52:23 +0000829 '/mnt/host/source/src', status.fitimage_dir, status.fitimage_script))
Paul Fagerburg042a5252020-03-16 21:49:18 -0600830 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700831 [add_fitimage_sh,
832 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600833 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700834 if rc:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700835 fitimage_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir)
Paul Fagerburgba508a02020-11-20 14:40:55 -0700836 status.commits[step_names.COMMIT_FIT] = get_git_commit_data(
837 fitimage_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700838 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700839
840
841def gen_fit_image_outside_chroot(status):
842 """Tell the user to run gen_fit_image.sh outside the chroot
843
844 As noted for add_Fitimage(), gen_fit_image.sh cannot run inside the
845 chroot. This function tells the user to run gen_fit_image.sh in
846 their normal environment, and then come back (--continue) when that
847 is done.
848
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700849 Args:
850 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700851
852 Returns:
853 True
854 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700855 logging.info('Running step gen_fit_image_outside_chroot')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700856 fit_image_files = check_fit_image_files(status)
857 # If the list is empty, then `not` of the list is True, so the files
858 # we need are all present and we can continue.
859 if not fit_image_files:
860 return True
861
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700862 logging.error('The following files need to be generated:')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700863 for filename in fit_image_files:
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700864 logging.error('* %s', filename)
Paul Fagerburgba508a02020-11-20 14:40:55 -0700865 logging.error(
866 'The fitimage sources are ready for gen_fit_image.sh to process.')
867 logging.error(
868 'gen_fit_image.sh cannot run inside the chroot. Open a new terminal,')
869 logging.error(
870 'change to the directory where gen_fit_image.sh is located, and run')
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700871 logging.error(status.fitimage_cmd, status.variant)
872 logging.error('Then re-start this program with --continue.')
Paul Fagerburgba508a02020-11-20 14:40:55 -0700873 logging.error(
874 'If your chroot is based in ~/chromiumos, then the folder you want is')
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700875 logging.error('~/chromiumos/src/%s/asset_generation', status.fitimage_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700876 return False
877
878
879def check_fit_image_files(status):
880 """Check if the fitimage has been generated
881
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700882 This function is not called directly as a step, and so it doesn't need
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700883 to produce any error messages to the user (except with --verbose).
884 gen_fit_image_outside_chroot will call this function to see if the
885 fitimage files exist, and if not, then that function will print the
886 message about how the user needs to run gen_fit_image.sh outside the
887 chroot.
888
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700889 Args:
890 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700891
892 Returns:
893 List of files that *DO NOT* exist and need to be created, [] if
894 all files are present.
895 """
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700896 outputs_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir,
Paul Fagerburg7429c572020-11-20 19:28:27 -0700897 status.fitimage_outputs_dir)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700898 logging.debug('outputs_dir = "%s"', outputs_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700899
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600900 files_not_found = []
901 fitimage_bin = 'fitimage-' + status.variant + '.bin'
902 if not os.path.isfile(os.path.join(outputs_dir, fitimage_bin)):
903 files_not_found.append(fitimage_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700904
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600905 fitimage_versions = 'fitimage-' + status.variant + '-versions.txt'
906 if not os.path.isfile(os.path.join(outputs_dir, fitimage_versions)):
907 files_not_found.append(fitimage_versions)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700908
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600909 return files_not_found
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700910
911
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700912def commit_fitimage(status):
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600913 """Add the fitimage files to the git commit
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700914
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600915 This function calls commit_fitimage.sh to move the fitimage binary and
916 -versions files from asset_generation/outputs to files/ and then adds
917 those files and fit.log to the existing git commit.
918 Depending on the baseboard, there may be different file names (such
919 as fit-${VARIANT}.log for volteer) and/or additional files (such as
920 files/blobs/description-${VARIANT}.bin for volteer)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700921
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700922 Args:
923 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700924
925 Returns:
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600926 True if the script succeeded, False if something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700927 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700928 logging.info('Running step commit_fitimage')
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600929 commit_fitimage_sh = os.path.expanduser(os.path.join(
Paul Fagerburgba508a02020-11-20 14:40:55 -0700930 '/mnt/host/source/src', status.fitimage_dir,
931 'files/commit_fitimage.sh'))
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600932 return run_process([commit_fitimage_sh, status.variant])
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700933
934
935def create_initial_ec_image(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700936 """Create an EC image for the variant as a clone of the reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700937
938 This function calls create_initial_ec_image.sh, which will clone the
Paul Fagerburge868e832020-01-22 17:14:04 -0700939 reference board to create the variant. The shell script will build the
Paul Fagerburgfcada622020-11-20 11:53:36 -0700940 EC code for the variant.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700941
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700942 Args:
943 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700944
945 Returns:
946 True if the script and test build succeeded, False if something failed
947 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700948 logging.info('Running step create_initial_ec_image')
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700949 environ = {**os.environ, 'NEW_VARIANT_BRANCH': status.branch}
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700950 create_initial_ec_image_sh = os.path.join(status.my_loc,
951 'create_initial_ec_image.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600952 if not run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700953 [create_initial_ec_image_sh,
954 status.board,
955 status.variant,
Paul Fagerburgfc4450e2020-11-20 14:43:14 -0700956 status.bug], env=environ):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700957 return False
958
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700959 # No need to `if rc:` because we already tested the run_process result above
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700960 status.commits[step_names.EC_IMAGE] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700961 '/mnt/host/source/src/platform/ec/board')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700962
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700963 # create_initial_ec_image.sh will build the ec.bin for this variant
964 # if successful.
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600965 ec_dir = '/mnt/host/source/src/platform/ec'
966 ec_bin = os.path.join(ec_dir, 'build', status.variant, 'ec.bin')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700967 logging.debug('ec.bin = "%s"', ec_bin)
968
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600969 if not os.path.isfile(ec_bin):
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700970 logging.error('EC binary %s not found', ec_bin)
971 return False
972 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700973
974
975def ec_buildall(status):
Paul Fagerburgfcada622020-11-20 11:53:36 -0700976 """Deprecated function that used to do a make buildall -j for the EC
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700977
Paul Fagerburgfcada622020-11-20 11:53:36 -0700978 The EC repo upload hook used to require a make buildall -j before
979 uploading. As of crrev.com/c/2436379 this requirement has been removed,
980 so this step is no longer necessary.
981
982 This function still exists so that if someone has a new variant already
983 in progress and they update new_variant.py, it won't break. Eventually,
984 this function will be removed completely.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700985
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700986 Args:
987 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700988
989 Returns:
Paul Fagerburgfcada622020-11-20 11:53:36 -0700990 True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700991 """
Paul Fagerburgfcada622020-11-20 11:53:36 -0700992 logging.info('Running deprecated step ec_buildall')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700993 del status # unused parameter
Paul Fagerburgfcada622020-11-20 11:53:36 -0700994 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700995
996
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700997def add_variant_to_public_yaml(status):
998 """Add the new variant to the public model.yaml file
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700999
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001000 This function calls add_variant_to_yaml.sh to add the new variant to
1001 the public model.yaml file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001002
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001003 Args:
1004 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001005
1006 Returns:
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001007 True if the script succeeded, False is something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001008 """
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001009 logging.info('Running step add_variant_to_public_yaml')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -07001010 add_variant_to_yaml_sh = os.path.join(status.my_loc,
1011 'add_variant_to_yaml.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -06001012 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001013 [add_variant_to_yaml_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -07001014 status.base,
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001015 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -06001016 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001017 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001018 status.commits[step_names.ADD_PUB_YAML] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -07001019 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001020 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001021
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001022
1023def add_variant_to_private_yaml(status):
1024 """Add the new variant to the private model.yaml file
1025
1026 This function calls add_variant.sh to add the new variant to
1027 the private model.yaml file.
1028
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001029 Args:
1030 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001031
1032 Returns:
1033 True if the script succeeded, False is something failed
1034 """
1035 logging.info('Running step add_variant_to_private_yaml')
Paul Fagerburgba508a02020-11-20 14:40:55 -07001036 add_variant_sh = os.path.expanduser(os.path.join(status.private_yaml_dir,
1037 'add_variant.sh'))
Paul Fagerburg042a5252020-03-16 21:49:18 -06001038 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001039 [add_variant_sh,
1040 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -06001041 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001042 if rc:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001043 status.commits[step_names.ADD_PRIV_YAML] = get_git_commit_data(
1044 status.private_yaml_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001045 return rc
1046
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001047
1048
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001049def build_config(status):
1050 """Build project config files, from yaml or starlark
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001051
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001052 This function builds the project config files that mosys and other tools
1053 use, then verifies that the new variant's name shows up in all of the
1054 output files. Depending on the baseboard, the input may be the model.yaml
1055 files, or the starlark configuration files.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001056
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001057 Args:
1058 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001059
1060 Returns:
1061 True if the scripts and build succeeded, False is something failed
1062 """
Paul Fagerburg4376fe52020-06-16 10:22:35 -06001063 logging.info('Running step build_config')
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001064 if not emerge_with_workon(status, status.config_workon_pkgs,
1065 status.emerge_cmd, status.config_emerge_pkgs):
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001066 return False
1067
Paul Fagerburga95dd162020-03-24 16:27:18 -06001068 # Check the generated config.yaml file for occurences of the variant
1069 # name to determine if the emerge was successful.
1070 config_yaml = os.path.join(
1071 '/build', status.base, 'usr/share/chromeos-config/yaml/config.yaml')
1072 logging.debug('config_yaml = "%s"', config_yaml)
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001073 if not os.path.isfile(config_yaml):
Paul Fagerburga95dd162020-03-24 16:27:18 -06001074 logging.error('%s does not exist', config_yaml)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001075 return False
1076
Paul Fagerburga95dd162020-03-24 16:27:18 -06001077 if not status.variant in osutils.ReadFile(config_yaml):
1078 logging.error('variant name %s not found in yaml file %s',
1079 status.variant, config_yaml)
1080 return False
1081
1082 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001083
1084
1085def emerge_all(status):
1086 """Build the coreboot BIOS and EC code for the new variant
1087
Paul Fagerburg75398072020-03-16 13:51:24 -06001088 This build step will cros_workon start a list of packages provided by
1089 the reference board data as status.workon_pkgs, then emerge a list of
1090 packages (status.emerge_pkgs), and then cros_workon stop any packages
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001091 that it started. Any packages that were already being worked on will
1092 not be stopped.
Paul Fagerburg75398072020-03-16 13:51:24 -06001093
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001094 Args:
1095 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001096
1097 Returns:
1098 True if the build succeeded, False if something failed
1099 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001100 logging.info('Running step emerge_all')
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001101 environ = {**os.environ, 'FW_NAME': status.variant}
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001102 if not emerge_with_workon(status, status.workon_pkgs,
1103 status.emerge_cmd, status.emerge_pkgs,
1104 env=environ):
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001105 return False
1106
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001107 # Check if the expected build outputs exist.
Paul Fagerburge868e832020-01-22 17:14:04 -07001108 build_path = '/build/' + status.base + '/firmware'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001109 logging.debug('build_path = "%s"', build_path)
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001110 image_bin = 'image-' + status.variant + '.bin'
1111 if not os.path.isfile(os.path.join(build_path, image_bin)):
1112 logging.error('emerge failed because %s does not exist', image_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001113 return False
1114
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001115 serial_bin = 'image-' + status.variant + '.serial.bin'
1116 if not os.path.isfile(os.path.join(build_path, serial_bin)):
1117 logging.error('emerge failed because %s does not exist', serial_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001118 return False
1119
1120 return True
1121
1122
1123def push_coreboot(status):
1124 """Push the coreboot CL to coreboot.org
1125
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001126 Args:
1127 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001128
1129 Returns:
1130 True if the build succeeded, False if something failed
1131 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001132 logging.info('Running step push_coreboot')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001133
1134 # Set up a return code that may change to False if we find that a
1135 # coreboot CL has not been uploaded.
1136 rc = True
1137
1138 for commit_key in status.coreboot_push_list:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001139 logging.debug('Processing key %s', commit_key)
Paul Fagerburg8d850932020-02-25 14:13:32 -07001140 commit = status.commits[commit_key]
1141 if 'gerrit' not in commit or 'cl_number' not in commit:
1142 change_id = commit['change_id']
1143 cl = find_change_id(change_id)
1144 if cl is not None:
1145 save_cl_data(status, commit_key, cl)
1146 else:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001147 logging.debug('Not found %s, need to upload', change_id)
Paul Fagerburgba508a02020-11-20 14:40:55 -07001148 logging.error(
1149 'The following commit needs to be pushed to coreboot.org:')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001150 logging.error(' Branch "%s"', commit['branch_name'])
1151 logging.error(' in directory "%s"', commit['dir'])
1152 logging.error(' with change-id "%s"', commit['change_id'])
1153 logging.error('Please push the branch to review.coreboot.org, '
1154 'and then re-start this program with --continue')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001155 # Since this commit needs to be uploaded, do not continue after
1156 # this step returns.
1157 rc = False
1158 else:
1159 instance_name = commit['gerrit']
1160 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001161 logging.debug('Already uploaded (%s, %s)', instance_name, cl_number)
Paul Fagerburg8d850932020-02-25 14:13:32 -07001162
1163 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001164
1165
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001166def query_gerrit(instance, change_id):
1167 """Search a gerrit instance for a specific change_id
1168
1169 Args:
1170 instance: gerrit instance to query. Suitable values come from
1171 gerrit.GetCrosInternal() and gerrit.GetCrosExternal()
1172 change_id: The change_id to search for
1173
1174 Returns:
1175 CL number if found, None if not
1176 """
1177 raw = instance.Query(change=change_id, raw=True)
1178 if raw:
1179 # If the CL was found by change_id, there will be only one,
1180 # because the change_id is used to recognize a new patchset
1181 # on an existing CL.
1182 return raw[0]['number']
1183
1184 return None
1185
1186
Paul Fagerburg8d850932020-02-25 14:13:32 -07001187def query_coreboot_gerrit(change_id):
1188 """Search the coreboot gerrit for a specific change_id
1189
1190 Use the REST API to look for the change_id. See
1191 https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html
1192 for details on the REST API to search for a change-id.
1193
1194 We can't use query_gerrit with a manually constructed GerritHelper
1195 because we need the user's private SSH key to access review.coreboot.org,
1196 but these are not available inside the chroot.
1197
1198 Args:
1199 change_id: The change_id to search for
1200
1201 Returns:
1202 CL number if found, None if not
1203 """
1204 r = requests.get('https://review.coreboot.org/changes/' + change_id)
1205 response = r.content.decode('utf-8')
1206 # Check if the response starts with 'Not found', in which case return None
1207 if response.startswith('Not found:'):
1208 return None
1209 # Strip off the initial )]}'\n that is used as XSS protections, see
1210 # https://gerrit-review.googlesource.com/Documentation/rest-api.html#output
1211 # and decode as JSON.
1212 data = json.loads(response[5:])
1213 if '_number' in data:
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001214 return str(data['_number'])
Paul Fagerburg8d850932020-02-25 14:13:32 -07001215 return None
1216
1217
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001218def find_change_id(change_id):
1219 """Search the public and private ChromeOS gerrit instances for a change-id
1220
1221 Args:
1222 change_id: Change-Id to search for in both gerrit instances
1223
1224 Returns:
1225 Tuple of the gerrit instance ('chromium' or 'chrome-internal') and
1226 the CL number if the Change-Id is found.
1227 None if not found.
1228 """
1229 cl_number = query_gerrit(gerrit.GetCrosExternal(), change_id)
1230 if cl_number:
1231 return 'chromium', cl_number
1232 cl_number = query_gerrit(gerrit.GetCrosInternal(), change_id)
1233 if cl_number:
1234 return 'chrome-internal', cl_number
Paul Fagerburg8d850932020-02-25 14:13:32 -07001235 cl_number = query_coreboot_gerrit(change_id)
1236 if cl_number:
1237 return 'coreboot', cl_number
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001238 return None
1239
1240
1241def save_cl_data(status, commit_key, cl):
1242 """Save the gerrit instance and CL number to the yaml file
1243
1244 Args:
1245 status: variant_status object tracking our board, variant, etc.
1246 commit_key: Which key in the commits map we're processing
1247 cl: Value returned by find_change_id, should be a tuple
1248 of instance_name, cl_number
1249 """
1250 instance_name, cl_number = cl
1251 print(f'Found ({instance_name}, {cl_number}), saving to yaml')
1252 status.commits[commit_key]['gerrit'] = instance_name
1253 status.commits[commit_key]['cl_number'] = cl_number
1254 status.save()
1255
1256
1257def repo_upload(branch_name, cwd):
1258 """Upload a branch to gerrit
1259
1260 This function runs `repo upload` in the specified directory to upload
1261 a branch to gerrit. Because it's operating in a directory and with a
1262 branch name, it could upload more than one commit, which is OK because
1263 we'll look for each commit by change-id before trying to upload in that
1264 directory. For example, this happens in Zork, where the cb_config step
1265 and the cras_config step both have a commit in src/overlays. When we're
1266 processing the cb_config step and we `repo upload` in src/overlays, it
1267 will also upload the commit for cras_config. Then we come around to the
1268 cras_config step, and since we can find a CL with the change-id, we don't
1269 try to upload again.
1270
1271 Args:
1272 branch_name: the name of the branch to upload. Gets passed to
1273 repo upload with the --br flag
1274 cwd: directory where we want to upload. Gets set as the working
1275 directory for executing repo upload.
1276
1277 Returns:
1278 True if repo upload exits with a successful error code, false otherwise
1279 """
Paul Fagerburg042a5252020-03-16 21:49:18 -06001280 return run_process(
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001281 ['repo',
1282 'upload',
1283 '.',
1284 '--br=' + branch_name,
1285 '--wip',
1286 '--verify',
Paul Fagerburgda6bc102020-08-31 19:27:04 -06001287 '--yes',
1288 '--hashtag=new_variant'],
Paul Fagerburg042a5252020-03-16 21:49:18 -06001289 cwd=cwd)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001290
1291
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001292def upload_CLs(status):
1293 """Upload all CLs to chromiumos
1294
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001295 Args:
1296 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001297
1298 Returns:
1299 True if the build succeeded, False if something failed
1300 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001301 logging.info('Running step upload_CLs')
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001302
1303 for commit_key in status.repo_upload_list:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001304 logging.debug('Processing key %s', commit_key)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001305 commit = status.commits[commit_key]
1306 if 'gerrit' not in commit or 'cl_number' not in commit:
1307 change_id = commit['change_id']
1308 cl = find_change_id(change_id)
1309 if cl is not None:
1310 save_cl_data(status, commit_key, cl)
1311 else:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001312 logging.debug('Not found %s, need to upload', change_id)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001313 if not repo_upload(commit['branch_name'], commit['dir']):
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001314 logging.error('Repo upload %s in %s failed!',
1315 commit['branch_name'],
1316 commit['dir'])
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001317 return False
1318 cl = find_change_id(change_id)
1319 if cl is None:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001320 logging.error(
1321 'repo upload %s succeeded, but change_id is not found!',
1322 commit_key)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001323 return False
1324 save_cl_data(status, commit_key, cl)
1325 else:
1326 instance_name = commit['gerrit']
1327 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001328 logging.debug('Already uploaded (%s, %s)', instance_name, cl_number)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001329
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001330 return True
1331
1332
1333def find_coreboot_upstream(status):
Paul Fagerburgba508a02020-11-20 14:40:55 -07001334 """Find the upstream coreboot CL in chromiumos
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001335
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001336 When the coreboot variant CL is first uploaded to review.coreboot.org,
1337 it is not visible in the chromiumos tree (and also cannot be used as
Paul Fagerburgba508a02020-11-20 14:40:55 -07001338 a target for cq-depend). There is a process for upstream CLs from
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001339 coreboot after they have been reviewed, approved, and merged. We can
1340 track a specific coreboot CL if we know the change-id that it used on
1341 the coreboot gerrit instance, by looking for that change-id as
1342 'original-change-id' in the public chromium gerrit instance.
1343
1344 The change-id for the coreboot variant will be under the 'cb_variant' key,
1345 but this is for the 'coreboot' gerrit instance.
1346
1347 When we find the upstreamed CL, we will record the gerrit instance and
1348 CL number in the yaml file under the 'find' key ("find upstream coreboot")
1349 so that we don't need to search coreboot again.
1350
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001351 Args:
1352 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001353
1354 Returns:
1355 True if the build succeeded, False if something failed
1356 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001357 logging.info('Running step find_coreboot_upstream')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001358
1359 # If we have already found the upstream coreboot CL, then exit with success
1360 if step_names.FIND in status.commits:
1361 commit = status.commits[step_names.FIND]
1362 if 'gerrit' in commit and 'cl_number' in commit:
1363 instance_name = commit['gerrit']
1364 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001365 logging.debug('Already found (%s, %s)', instance_name, cl_number)
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001366 return True
1367
1368 # Make sure we have a CB_VARIANT commit and a change_id for it
1369 if step_names.CB_VARIANT not in status.commits:
1370 logging.error('Key %s not found in status.commits',
1371 step_names.CB_VARIANT)
1372 return False
1373 if 'change_id' not in status.commits[step_names.CB_VARIANT]:
1374 logging.error('Key change_id not found in status.commits[%s]',
1375 step_names.CB_VARIANT)
1376 return False
1377
1378 # Find the CL by the Original-Change-Id
1379 original_change_id = status.commits[step_names.CB_VARIANT]['change_id']
1380 gerrit_query_args = {
1381 'Original-Change-Id': original_change_id
1382 }
1383 cros = gerrit.GetCrosExternal()
1384 upstream = cros.Query(**gerrit_query_args)
1385 # If nothing is found, the patch hasn't been upstreamed yet
1386 if not upstream:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001387 logging.error('Program cannot continue without upstream coreboot CL.')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001388 logging.error('(coreboot:%s, change-id %s)',
1389 status.commits[step_names.CB_VARIANT]['cl_number'],
1390 status.commits[step_names.CB_VARIANT]['change_id'])
Paul Fagerburgba508a02020-11-20 14:40:55 -07001391 logging.error('Please wait for the upstream CL, then run this program'
1392 ' again with --continue')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001393 return False
1394
1395 # If more than one CL is found, something is very wrong
1396 if len(upstream) != 1:
1397 logging.error('More than one CL was found with Original-Change-Id %s',
1398 original_change_id)
1399 return False
1400
1401 # At this point, we know there is only one CL and we can get the
1402 # repo and CL number by splitting on the colon between them.
1403 patchlink = upstream[0].PatchLink()
1404 instance_name, cl_number = patchlink.split(':')
1405
1406 # Can't use get_git_commit_data because we're not pulling this
1407 # information from a git commit, but rather from gerrit.
1408 # We only need the gerrit instance and the CL number so we can have
1409 # other CLs cq-depend on this CL. The other keys are not needed because:
1410 # dir - not needed because we're not going to `cd` there to `repo upload`
1411 # branch_name - not valid; the CL is already merged
1412 # change_id - we use the change_id to find a CL number, and since we
1413 # just found the CL number via original-change-id, this is moot.
1414 status.commits[step_names.FIND] = {
1415 'gerrit': instance_name,
1416 'cl_number': str(cl_number)
1417 }
1418
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001419 return True
1420
1421
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001422def calc_cq_depend(status):
1423 """Determine the list of CLs for each commit that has dependencies.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001424
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001425 status.depends is a map of dependencies from step name to a list of
1426 steps that the step depends on. For each step, find the SHA of the
1427 commit, then find the gerrit instance and CL number of the commits
1428 that it depends on. Construct the Cq-Depends list and save it under
1429 the 'cq_depend' key, i.e. commit['add_priv_yaml']['cq_depend'] or
1430 as it will be stored in the yaml:
1431 commits:
1432 add_priv_yaml:
1433 cq_depend: 'chromium:1629121, chromium:1638243'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001434
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001435 Args:
1436 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001437
1438 Returns:
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001439 True if all dependencies have been calculated. False if something
1440 failed, usually a commit not found by Change-Id.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001441 """
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001442 logging.info('Running step calc_cq_depend')
1443 # Iterate through the commits that have dependencies.
1444 for key in status.depends:
1445 logging.debug('Processing %s to add dependencies', key)
1446 # For every commit that has dependencies, find the gerrit instance
1447 # and CL number of the dependencies.
1448 cq_depend_list = []
1449 for depend_key in status.depends[key]:
1450 depend_commit = status.commits[depend_key]
1451 if not 'gerrit' in depend_commit:
1452 logging.error('Commit %s does not have a gerrit instance',
1453 depend_key)
1454 return False
1455 if not 'cl_number' in depend_commit:
1456 logging.error('Commit %s does not have a CL number',
1457 depend_key)
1458 return False
1459
1460 instance_name = depend_commit['gerrit']
1461 cl_number = depend_commit['cl_number']
1462 cq_depend_list.append(f'{instance_name}:{cl_number}')
1463
1464 # Add the 'cq_depend' key to the commit.
1465 cq_depend_str = 'Cq-Depend: %s' % ', '.join(cq_depend_list)
1466 logging.debug('Add to commit %s %s', key, cq_depend_str)
1467 status.commits[key]['cq_depend'] = cq_depend_str
1468
1469 return True
1470
1471
1472def add_cq_depend_to_commit_msg(git_repo, change_id, cq_depend_str):
1473 """Update the commit message with a Cq-Depends line.
1474
1475 Find the SHA of the commit, then use git filter-branch --msg-filter
1476 to add the Cq-Depend line just before the Change-Id line. See
1477 https://chromium.googlesource.com/chromiumos/docs/+/HEAD/contributing.md#cq-depend
1478 for details about Cq-Depend format and location.
1479
1480 Args:
1481 git_repo: Directory of git repository.
1482 change_id: The Change-Id to search for.
1483 cq_depend_str: The Cq-Depend string. It must be in the correct format
1484 per chromeos documentation, ready to insert into the commit msg
1485 on the line before Change-Id.
1486
1487 Returns:
1488 True if `git filter-branch` was successful. False if the command
1489 failed.
1490 """
1491 logging.debug('find SHA of Change-Id %s in %s', change_id, git_repo)
1492 sha = change_id_to_sha(git_repo, change_id)
1493 if sha is None:
1494 logging.error('Cannot find the SHA for Change-Id %s in %s',
1495 change_id, git_repo)
1496 return False
1497 logging.debug('SHA = %s', sha)
1498
1499 # Check if the commit message already has a Cq-Depend line.
1500 msg = get_commit_msg(git_repo, sha)
1501 if any('Cq-Depend' in tmpstr for tmpstr in msg):
1502 logging.debug('Already has Cq-Depend')
1503 return True
1504
1505 # Use git filter-branch --msg-filter to add the Cq-Depend line just
1506 # before the Change-Id line.
1507 environ = {**os.environ, 'FILTER_BRANCH_SQUELCH_WARNING': '1'}
1508 cmd = [
1509 'git',
1510 'filter-branch',
Paul Fagerburg5f6d2012020-06-24 11:29:36 -06001511 '-f',
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001512 '--msg-filter',
1513 f'sed -E "s/^(Change-Id: {change_id})$/{cq_depend_str}\\n\\1/"',
1514 '--',
1515 f'{sha}^..']
1516 return run_process(cmd, cwd=git_repo, env=environ)
1517
1518
1519def add_cq_depend(status):
1520 """Add Cq-Depend to commits and flag them for re-upload.
1521
1522 Args:
1523 status: variant_status object tracking our board, variant, etc.
1524
1525 Returns:
1526 True if the commit messages have been successfully amended, False if
1527 something failed.
1528 """
1529 logging.info('Running step add_cq_depend')
1530 for key in status.commits:
1531 commit = status.commits[key]
1532 if 'cq_depend' in commit:
1533 logging.debug('%s has %s', key, commit['cq_depend'])
1534 # Make sure the commit has a working directory and a change_id
1535 # before trying to amend its commit message.
1536 if 'dir' not in commit or 'change_id' not in commit:
1537 logging.error('Missing dir and/or change_id from %s', key)
1538 return False
1539
1540 if not add_cq_depend_to_commit_msg(commit['dir'],
1541 commit['change_id'],
1542 commit['cq_depend']):
1543 return False
1544 commit['needs_re_upload'] = True
1545 else:
1546 logging.debug('%s no dependencies', key)
1547
1548 return True
1549
1550
1551def re_upload(status):
1552 """Re-upload commits that have changed.
1553
1554 Args:
1555 status: variant_status object tracking our board, variant, etc.
1556
1557 Returns:
1558 True if the uploads succeeded. False if a repo upload failed.
1559 """
1560 logging.info('Running step re_upload')
1561 for key in status.commits:
1562 commit = status.commits[key]
1563 if commit.get('needs_re_upload'):
1564 logging.debug('Re-upload branch %s in %s', commit['branch_name'],
1565 commit['dir'])
1566 if not repo_upload(commit['branch_name'], commit['dir']):
1567 logging.error('Repo upload %s in %s failed!',
1568 commit['branch_name'],
1569 commit['dir'])
1570 return False
1571 commit['needs_re_upload'] = False
1572
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001573 return True
1574
1575
1576def clean_up(status):
1577 """Final clean-up, including delete the status file
1578
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001579 Args:
1580 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001581
1582 Returns:
1583 True
1584 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001585 logging.info('Running step clean_up')
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001586 status.rm()
1587 return True
1588
1589
Paul Fagerburg042a5252020-03-16 21:49:18 -06001590def abort(status):
1591 """Abort the creation of a new variant by abandoning commits
1592
1593 When the user specifies the --abort flag, we override status.step to
1594 be 'abort' and there is no transition from 'abort' to anything else.
1595 We look at status.commits and for each key, see if we have already
1596 been in that directory and abandoned that specific branch. If not,
1597 abandon the commit and then add the branch+dir to a list of abandoned
1598 commits. We do this because some boards (such as Zork) can have multiple
1599 commits in the same directory and with the same branch name, and we only
1600 want to repo abandon that branch once.
1601
1602 Args:
1603 status: variant_status object tracking our board, variant, etc.
1604
1605 Returns:
1606 True
1607 """
1608 logging.info('Running step abort')
1609 # Use the set 'abandoned' to keep track of each branch+dir abandoned.
1610 abandoned = set()
1611 for step in status.commits:
1612 logging.debug('Processing step %s', step)
1613 commit = status.commits[step]
1614 branch = commit['branch_name']
1615 cwd = commit['dir']
1616 if (branch, cwd) in abandoned:
1617 logging.debug('Branch %s in directory %s already abandoned',
1618 branch, cwd)
1619 else:
1620 logging.info('Abandoning branch %s in directory %s',
1621 branch, cwd)
1622 if run_process(['repo', 'abandon', branch, '.'], cwd=cwd):
1623 abandoned.add((branch, cwd))
1624 else:
1625 logging.error('Error while abandoning branch %s', branch)
1626 return False
1627
1628 return True
1629
1630
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001631if __name__ == '__main__':
1632 sys.exit(not int(main()))