blob: a1dbb19a690cac05fc1ebd3fd8ab2ad77ea25f35 [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 Fagerburg042a5252020-03-16 21:49:18 -060069 board, variant, bug, 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 Fagerburg042a5252020-03-16 21:49:18 -060074 status = get_status(board, variant, bug, 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 Fagerburgba508a02020-11-20 14:40:55 -0700121 # Use a group so that we can enforce mutually-exclusive arguments.
Paul Fagerburg2e48a192020-03-24 17:57:05 -0600122 # argparse does not support nesting groups, so we can't put board,
123 # variant, and bug into a group and have that group as another mutually
124 # exclusive option.
125 group = parser.add_mutually_exclusive_group()
126 group.add_argument(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700127 '--continue', action='store_true',
128 dest='continue_flag', help='Continue the process from where it paused')
Paul Fagerburg2e48a192020-03-24 17:57:05 -0600129 group.add_argument(
Paul Fagerburg042a5252020-03-16 21:49:18 -0600130 '--abort', action='store_true',
131 dest='abort_flag', help='Cancel the process and abandon all commits')
132 parser.add_argument(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700133 '--verbose', action='store_true',
134 dest='verbose_flag', help='Enable verbose output of progress')
135 args = parser.parse_args()
136
137 if args.verbose_flag:
138 logging.basicConfig(level=logging.DEBUG)
139 else:
140 logging.basicConfig(level=logging.INFO)
141
142 board = args.board
143 if board is not None:
144 board = board.lower()
145
146 variant = args.variant
147 if variant is not None:
148 variant = variant.lower()
149
150 bug = args.bug or 'None'
151
Paul Fagerburg042a5252020-03-16 21:49:18 -0600152 return (board, variant, bug, args.continue_flag, args.abort_flag)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700153
154
Paul Fagerburg042a5252020-03-16 21:49:18 -0600155def check_flags(board, variant, bug, continue_flag, abort_flag):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700156 """Check the flags to ensure no invalid combinations
157
158 We allow any of the following:
Paul Fagerburg042a5252020-03-16 21:49:18 -0600159 --abort
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700160 --continue
161 --board=board_name --variant=variant_name
162 --board=board_name --variant=variant_name --bug=bug_text
163
164 The argument parser does have the functionality to represent the
165 combination of --board and --variant as a single mutually-exclusive
166 argument, so we have to use this function to do the checking.
167
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700168 Args:
169 board: Name of the reference board
170 variant: Name of the variant being created
171 bug: Text for bug number, if any ('None' otherwise)
172 continue_flag: Flag if --continue was specified
Paul Fagerburg042a5252020-03-16 21:49:18 -0600173 abort_flag: Flag if --abort was specified
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700174
175 Returns:
176 True if the arguments are acceptable, False otherwise
177 """
Paul Fagerburg042a5252020-03-16 21:49:18 -0600178 # If either --abort or --continue is set, then disallow any of the
179 # board name, variant name, or bug number to be set.
180 if continue_flag or abort_flag:
181 if board is not None or variant is not None or bug != 'None':
182 logging.error('Do not use --board, --variant, or --bug with '
183 '--continue or --abort')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700184 return False
Paul Fagerburg042a5252020-03-16 21:49:18 -0600185 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700186
Paul Fagerburg042a5252020-03-16 21:49:18 -0600187 # At this point, neither --continue nor --abort are set, so we must have
188 # both --board and --variant values.
189 if board is None or variant is None:
190 logging.error('Both --board and --variant must be specified')
191 return False
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700192
193 return True
194
195
Paul Fagerburg042a5252020-03-16 21:49:18 -0600196def get_status(board, variant, bug, continue_flag, abort_flag):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700197 """Create the status file or get the previous status
198
199 This program can stop at several places as we have to wait for CLs
200 to work through CQ or be upstreamed into the chromiumos tree, so just
201 like a git cherry-pick, there is a --continue option to pick up where
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700202 you left off by reading a specially-named status file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700203
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700204 If --continue is specified, the status file must exist.
205 If the status file exists, then --continue must be specified.
206 When --continue is specified, we read the status file and return
207 with the contents.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700208
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700209 If the status file does not exist, we will create the state file with
210 the board, variant, and (optional) bug details.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700211
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700212 To decouple the list of boards supported from this main program, we
Paul Fagerburge868e832020-01-22 17:14:04 -0700213 try to import a module with the same name as the reference board,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700214 so --board=hatch means that we import hatch.py. If we can't import
Paul Fagerburge868e832020-01-22 17:14:04 -0700215 the file, then we don't support that reference board.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700216
217 The board-specific module will set several variables, which we will
218 copy into the object that we return.
219
Paul Fagerburge868e832020-01-22 17:14:04 -0700220 * base - the name of the base board, such as Hatch, Volteer, or Zork.
221 This can be different from the reference board, e.g. the Trembyle
222 reference board in the Zork project.
Paul Fagerburg3ef1cbb2020-06-02 16:38:36 -0600223 * coreboot_base - the name of the base board in coreboot. Usually the same
224 as base, but can differ, e.g. for Puff, the base is Puff, but the
225 coreboot_base is Hatch because Puff is based on Hatch.
Paul Fagerburg4d343452020-01-24 15:23:53 -0700226 * coreboot_dir - base directory for coreboot, usually third_party/coreboot
227 but could differ for processors that use a private repo
Matt Ziegelbaum3e879df2020-11-09 16:08:44 -0500228 * coreboot_reference - the name of the reference board that we're using to
229 make the variant. This can be different from base (e.g. Ambassador is
230 its own board, but uses Puff as a coreboot reference).
Paul Fagerburg4d343452020-01-24 15:23:53 -0700231 * cb_config_dir - base directory for coreboot configs, usually
232 third_party/chromiumos-overlay/sys-boot/coreboot/files/configs but
233 could differ for processors that use a private repo
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700234 * step_list - list of steps (named in step_names.py) to run in sequence
Paul Fagerburge868e832020-01-22 17:14:04 -0700235 to create the new variant of the reference board
236 * fsp - package name for FSP. This may be None, depending on the
237 processor on the reference board
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700238 * fitimage_pkg - package name for the fitimage
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700239 * fitimage_dir - directory for fitimage; prepend '/mnt/host/source/src/'
240 in chroot, prepend '~/chromiumos/src' outside the chroot
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700241 * workon_pkgs - list of packages to cros_workon
242 * emerge_cmd - the emerge command, e.g. 'emerge-hatch'
243 * emerge_pkgs - list of packages to emerge
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600244 * config_workon_pkgs - list of packages to cros_workon to build the
245 project config
246 * config_emerge_pkgs - list of packages to emerge to build the project
247 config
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700248 * private_yaml_dir - directory for the private yaml file
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700249 * commits - map of commits for the various steps. Indexed by step name,
250 and the step names used are the same ones in step_names.py
251 * repo_upload_list - list of commits to upload using `repo upload`
252 * coreboot_push_list - list of commits to upload using `git push` to
253 coreboot
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600254 * depends - maps a step to a list of steps on which it depends, e.g.
255 depends[step_names.ADD_PRIV_YAML] is a list of other steps that
256 the 'add_priv_yaml' step depends on. This map is used to amend
257 the commits with CL numbers for Cq-Depend.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700258
259 Additionally, the following fields will be set:
260
Paul Fagerburge868e832020-01-22 17:14:04 -0700261 * board - the name of the reference board, e.g. 'hatch'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700262 * variant - the name of the variant, e.g. 'sushi'
263 * bug - optional text for a bug ID, used in the git commit messages.
264 Could be 'None' (as text, not the python None), or something like
265 'b:12345' for buganizer, or 'chromium:12345'
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700266 * step - internal state tracking, what step of the variant creation
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700267 we are at.
268 * yaml_file - internal, just the name of the file where all this data
269 gets saved.
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700270 * commit - a map of maps that tracks all of the git commit and gerrit CL
271 data for each of the steps in the process. For example,
272 status.commit['add_priv_yaml'] is a map that has all the information
273 about the 'add_priv_yaml' step. The keys in the maps allow us to
274 determine where the commit is, the change_id, if it has been uploaded
275 to gerrit and where.
276
277 branch_name - the name of the git branch
278 change_id - the change-id assigned by the commit hook. Gerrit
279 uses the change_id to track new patchsets in the CL
280 dir - the directory where the commit has been created
281 gerrit - the name of the gerrit instance to which the CL has
282 been uploaded, one of 'chromium', 'chrome-internal', or
283 'coreboot'
284 cl_number - the CL number on the gerrit instance
285
286 When the commit is created, branch_name, change_id, and dir are all
287 set. The gerrit and cl_number keys are not set until the CL has been
288 uploaded to a gerrit instance.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700289
290 These data might come from the status file (because we read it), or
291 they might be the initial values after we created the file (because
292 it did not already exist).
293
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700294 Args:
295 board: Name of the reference board
296 variant: Name of the variant being created
297 bug: Text for bug number, if any ('None' otherwise)
298 continue_flag: Flag if --continue was specified
Paul Fagerburg042a5252020-03-16 21:49:18 -0600299 abort_flag: Flag if --abort was specified
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700300
301 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700302 variant_status object with all the data mentioned above
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700303 """
304 status = variant_status.variant_status()
Paul Fagerburg042a5252020-03-16 21:49:18 -0600305 if continue_flag or abort_flag:
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700306 if status.yaml_file_exists():
Paul Fagerburg042a5252020-03-16 21:49:18 -0600307 return status
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600308
309 if continue_flag:
310 op = '--continue'
311 if abort_flag:
312 op = '--abort'
313 logging.error('%s does not exist; cannot %s', status.yaml_file, op)
314 return None
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700315
Paul Fagerburg042a5252020-03-16 21:49:18 -0600316 # If we get here, the user provided --board and --variant (because
317 # check_flags() returned Trued), but the yaml file already exists,
318 # so we print an error message and bail.
319 if status.yaml_file_exists():
320 logging.error(
321 'new_variant already in progress; did you forget --continue?')
322 return None
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700323
Paul Fagerburg042a5252020-03-16 21:49:18 -0600324 # At this point, it's not --continue, not --abort, the yaml file doesn't
325 # exist, and we have valid values for --board, --variant, and --bug (bug
326 # might be the default value of "None"). Create the yaml file with data
327 # from the reference board's loadable module.
328 status.board = board
329 status.variant = variant
330 status.bug = bug
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700331
Paul Fagerburg042a5252020-03-16 21:49:18 -0600332 # Load the appropriate module and copy all the data from it.
333 try:
334 module = importlib.import_module(board)
335 except ImportError:
336 print('Unsupported board "' + board + '"')
337 sys.exit(1)
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700338
Paul Fagerburg042a5252020-03-16 21:49:18 -0600339 status.base = module.base
Paul Fagerburg3ef1cbb2020-06-02 16:38:36 -0600340 status.coreboot_base = getattr(module, 'coreboot_base', module.base)
Paul Fagerburg042a5252020-03-16 21:49:18 -0600341 status.coreboot_dir = module.coreboot_dir
Paul Fagerburgba508a02020-11-20 14:40:55 -0700342 status.coreboot_reference = getattr(module, 'coreboot_reference',
343 status.board)
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600344 status.cb_config_dir = getattr(module, 'cb_config_dir', None)
Paul Fagerburg042a5252020-03-16 21:49:18 -0600345 status.emerge_cmd = module.emerge_cmd
346 status.emerge_pkgs = module.emerge_pkgs
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600347 status.fitimage_dir = getattr(module, 'fitimage_dir', None)
348 status.fitimage_pkg = getattr(module, 'fitimage_pkg', None)
349 status.fitimage_cmd = getattr(module, 'fitimage_cmd', None)
350 status.fsp = getattr(module, 'fsp', None)
351 status.private_yaml_dir = getattr(module, 'private_yaml_dir', None)
Paul Fagerburg042a5252020-03-16 21:49:18 -0600352 status.step_list = module.step_list
353 status.workon_pkgs = module.workon_pkgs
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600354 status.config_workon_pkgs = module.config_workon_pkgs
355 status.config_emerge_pkgs = module.config_emerge_pkgs
Paul Fagerburg042a5252020-03-16 21:49:18 -0600356 status.coreboot_push_list = module.coreboot_push_list
357 status.repo_upload_list = module.repo_upload_list
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600358 status.depends = module.depends
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700359
Paul Fagerburg042a5252020-03-16 21:49:18 -0600360 # Start at the first entry in the step list
361 status.step = status.step_list[0]
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700362
Paul Fagerburg042a5252020-03-16 21:49:18 -0600363 # Start an empty map for tracking CL data
364 status.commits = {}
365
366 status.save()
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700367
368 return status
369
370
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700371def perform_step(status):
372 """Call the appropriate function for the current step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700373
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700374 Args:
375 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700376
377 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700378 True if the step succeeded, False if it failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700379 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700380 # Function to call based on the step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700381 dispatch = {
Paul Fagerburgbc184242020-04-28 17:00:54 -0600382 step_names.PROJECT_CONFIG: project_config,
Paul Fagerburg8f60b802020-06-09 15:38:28 -0600383 step_names.FW_BUILD_CONFIG: fw_build_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700384 step_names.CB_VARIANT: create_coreboot_variant,
385 step_names.CB_CONFIG: create_coreboot_config,
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700386 step_names.CRAS_CONFIG: copy_cras_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700387 step_names.ADD_FIT: add_fitimage,
388 step_names.GEN_FIT: gen_fit_image_outside_chroot,
389 step_names.COMMIT_FIT: commit_fitimage,
390 step_names.EC_IMAGE: create_initial_ec_image,
391 step_names.EC_BUILDALL: ec_buildall,
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700392 step_names.ADD_PUB_YAML: add_variant_to_public_yaml,
393 step_names.ADD_PRIV_YAML: add_variant_to_private_yaml,
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600394 step_names.BUILD_CONFIG: build_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700395 step_names.EMERGE: emerge_all,
396 step_names.PUSH: push_coreboot,
397 step_names.UPLOAD: upload_CLs,
398 step_names.FIND: find_coreboot_upstream,
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600399 step_names.CALC_CQ_DEPEND: calc_cq_depend,
400 step_names.ADD_CQ_DEPEND: add_cq_depend,
401 step_names.RE_UPLOAD: re_upload,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700402 step_names.CLEAN_UP: clean_up,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600403 step_names.ABORT: abort,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700404 }
405
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700406 if status.step not in dispatch:
407 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700408 sys.exit(1)
409
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700410 return dispatch[status.step](status)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700411
412
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700413def move_to_next_step(status):
414 """Move to the next step in the list
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700415
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700416 Args:
417 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700418 """
Paul Fagerburg042a5252020-03-16 21:49:18 -0600419 # Special case: the next step after 'abort' is 'clean_up'. Always.
420 if status.step == step_names.ABORT:
421 status.step = step_names.CLEAN_UP
422 return
423
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700424 if status.step not in status.step_list:
425 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700426 sys.exit(1)
427
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700428 idx = status.step_list.index(status.step)
429 if idx == len(status.step_list)-1:
430 status.step = None
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700431 else:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700432 status.step = status.step_list[idx+1]
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700433
434
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700435def run_process(args, cwd=None, env=None, capture_output=False):
436 """Run a process, log debug messages, return text output of process
437
438 The capture_output parameter allows us to capture the output when we
439 care about it (and not sending it to the screen), or ignoring it when
440 we don't care, and letting the user see the output so they know that
441 the build is still running, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700442
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700443 Args:
444 args: List of the command and its params
445 cwd: If not None, cd to this directory before running
446 env: Environment to use for execution; if needed, get os.environ.copy()
447 and add variables. If None, just use the current environment
448 capture_output: True if we should capture the stdout, false if we
449 just care about success or not.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700450
451 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700452 If capture_output == True, we return the text output from running
453 the subprocess as a list of lines, or None if the process failed.
454 If capture_output == False, we return a True if it successed, or
455 None if the process failed.
456
457 The caller can evaluate as a bool, because bool(None) == False, and
458 bool() of a non-empty list is True, or the caller can use the returned
459 text for further processing.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700460 """
461 logging.debug('Run %s', str(args))
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700462 if cwd is not None:
463 logging.debug('cwd = %s', cwd)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700464 try:
465 if capture_output:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700466 output = subprocess.run(args, cwd=cwd, env=env, check=True,
467 stderr=subprocess.STDOUT, stdout=subprocess.PIPE).stdout
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700468 else:
469 subprocess.run(args, cwd=cwd, env=env, check=True)
470 # Just something to decode so we don't get an empty list
471 output = b'True'
472
473 logging.debug('process returns 0')
474 # Convert from byte string to ASCII
475 decoded = output.decode('utf-8')
476 # Split into array of individual lines
477 lines = decoded.split('\n')
478 return lines
479 except subprocess.CalledProcessError as err:
480 logging.debug('process returns %s', str(err.returncode))
481 return None
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700482
483
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700484def get_git_commit_data(cwd):
485 """Get the branch name and change id of the current commit
486
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700487 Args:
488 cwd: The current working directory, where we want to get the branch
489 name and change id
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700490
491 Returns:
492 Map with 'dir', 'branch_name' and 'change_id' keys. The 'dir'
493 key maps to the value of os.path.expanduser(cwd)
494 """
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700495 cwd = git.FindGitTopLevel(os.path.expanduser(cwd))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700496 logging.debug('get_git_commit_data(%s)', cwd)
497
498 branch_name = git.GetCurrentBranch(cwd)
499 if branch_name is None:
500 logging.error('Cannot determine git branch name in %s; exiting', cwd)
501 sys.exit(1)
502 logging.debug('git current branch is %s', branch_name)
503
504 change_id = git.GetChangeId(cwd)
505 if change_id is None:
506 logging.error('Cannot determine Change-Id in %s; exiting', cwd)
507 sys.exit(1)
508 logging.debug('git Change-Id is %s', change_id)
509
510 return {
511 'dir': cwd,
512 'branch_name': branch_name,
513 'change_id': change_id
514 }
515
516
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600517def change_id_to_sha(git_repo, change_id):
518 """Find the SHA for a given Change-Id.
519
520 Args:
521 git_repo: Directory of git repository.
522 change_id: The Change-Id to search for.
523
524 Returns:
525 The SHA hash for the Change-Id if only one commit is found.
526 None if the Change-Id was not found.
527 Raises a ValueError if more than one commit is found with the
528 same Change-Id.
529 """
530 output = git.Log(git_repo, max_count=1, format='format:%H',
531 grep=fr'^Change-Id: {change_id}$')
532 sha_hashes = output.splitlines()
533 if not sha_hashes:
534 return None
535 if len(sha_hashes) > 1:
536 raise ValueError('More than one SHA with that Change-Id found')
537 return sha_hashes[0]
538
539
540def get_commit_msg(git_repo, rev):
541 """Get the commit message for a given revision.
542
543 Because git.Log doesn't allow specifying check=False or getting the
544 returncode, we have to catch the CalledProcessError instead.
545
546 Args:
547 git_repo: Directory of git repository.
548 rev: The revision to search for, a SHA or a label.
549
550 Returns:
551 The commit message as a list of strings, if the revision exists.
552 None if the revision was not found.
553 """
554 try:
555 msg = git.Log(git_repo, max_count=1, format='format:%B', rev=rev)
556 return msg.splitlines()
Paul Fagerburgba508a02020-11-20 14:40:55 -0700557 except cros_build_lib.CalledProcessError as err:
558 raise ValueError('SHA was not found') from err
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600559
560
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600561def emerge_with_workon(status, workon_pkgs, emerge_cmd, emerge_pkgs, env=None):
562 """Emerge a list of packages after `cros_workon start`ing them
563
564 This function will `cros_workon start` a list of packages, then `emerge`
565 another list of packages, and finally, `cros_workon stop` only those
566 packages that were actually started by the `cros_workon start` command.
567 Any package already in a `cros_workon start` state prior to this function
568 will still be in that state when this function exits.
569
570 To determine which packages this program started and which ones were
571 already started, we query the list of packages being worked on, then
572 cros_workon start the entire list (which will produce a "package already
573 being worked on" type of message for anything already started), and then
574 query the list of packages being worked on again. The difference between
575 the before and after lists are the packages that this program started,
576 and so that's the list of packages to cros_workon stop after the emerge
577 is done.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700578
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700579 Args:
580 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600581 workon_pkgs: list of packages to `cros_workon start`
582 emerge_cmd: the emerge command to run, e.g. 'emerge-volteer'
583 emerge_pkgs: list of packages to `emerge`
584 env: environment to pass to run_process, or None to pass default
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700585
586 Returns:
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600587 True if everything succeeded, False if something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700588 """
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600589 # Get the list of packages that are already cros_workon started.
590 build_target = build_target_lib.BuildTarget(status.base)
591 workon = workon_helper.WorkonHelper(build_target.root, build_target.name)
592 before_workon = workon.ListAtoms()
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700593
Paul Fagerburg4376fe52020-06-16 10:22:35 -0600594 # Only cros_workon start if the list is non-empty
595 if workon_pkgs:
596 workon.StartWorkingOnPackages(workon_pkgs)
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600597
598 # Determine which packages we need to cros_workon stop.
599 after_workon = workon.ListAtoms()
600 stop_packages = list(set(after_workon) - set(before_workon))
601
602 # Run the emerge command.
603 emerge_result = run_process([emerge_cmd] + emerge_pkgs, env=env)
604
Paul Fagerburg4376fe52020-06-16 10:22:35 -0600605 # If the list is non-empty, cros_workon stop before returning the result.
606 if stop_packages:
607 workon.StopWorkingOnPackages(stop_packages)
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600608
609 return emerge_result
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700610
611
Paul Fagerburgbc184242020-04-28 17:00:54 -0600612def project_config(status):
613 """Check if the project config is correct and complete
614
615 For programs that use the new project/config structure with starlark
616 configuration files, this function will check that emerging the
617 project's configuration will result in a project-config.json that
618 includes the new name of the new hwdesign (a.k.a. "variant").
619
620 Args:
621 status: variant_status object tracking our board, variant, etc.
622
623 Returns:
624 True if everything succeeded, False if something failed
625 """
626 logging.info('Running step project_config')
627 try:
628 if not emerge_with_workon(status, status.config_workon_pkgs,
629 status.emerge_cmd, status.config_emerge_pkgs):
Paul Fagerburgba508a02020-11-20 14:40:55 -0700630 raise RuntimeError('Building the configuration failed.')
Paul Fagerburgbc184242020-04-28 17:00:54 -0600631
632 # Make sure project-config.json exists in the /build tree
633 emerged_json = os.path.join(
634 '/build',
635 status.base,
636 'usr/share/chromeos-config/yaml/project-config.json')
637 if not os.path.isfile(emerged_json):
638 raise RuntimeError(
639 f'project-config.json {emerged_json} does not exist.')
640
641 # Search emerged_json to see if the new variant's name shows up there.
642 if not status.variant in osutils.ReadFile(emerged_json):
643 raise RuntimeError(
644 f'variant name {status.variant} not found in {emerged_json}')
645
646 except RuntimeError as e:
647 logging.error(str(e))
648 logging.error('Please file a bug in Infra > ChromeOS > Product > Device'
649 ' to have the project configuration updated.')
650 logging.error('(https://bugs.chromium.org/p/chromium/issues/list?'
651 'q=component:Infra%3EChromeOS%3EProduct%3EDevice)')
652 return False
653
654 return True
655
656
Paul Fagerburg8f60b802020-06-09 15:38:28 -0600657def fw_build_config(status):
658 """Add the _FW_BUILD_CONFIG setting to the project config
659
660 For programs that use the new project/config structure with starlark
661 configuration files, this function calls fw_build_config.sh, which will
662 modify the config.star file to have a default _FW_BUILD_CONFIG entry.
663
664 Args:
665 status: variant_status object tracking our board, variant, etc.
666
667 Returns:
668 True if everything succeeded, False if something failed
669 """
670 logging.info('Running step fw_build_config')
671 fw_build_config_sh = os.path.join(status.my_loc, 'fw_build_config.sh')
672 rc = run_process(
673 [fw_build_config_sh,
674 status.base,
675 status.variant,
676 status.bug])
677 if rc:
678 status.commits[step_names.FW_BUILD_CONFIG] = get_git_commit_data(
Paul Fagerburgba508a02020-11-20 14:40:55 -0700679 os.path.join('/mnt/host/source/src/project',
680 status.base, status.variant))
Paul Fagerburg8f60b802020-06-09 15:38:28 -0600681 return rc
682
683
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700684def create_coreboot_variant(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700685 """Create source files for a new variant of the reference board in coreboot
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700686
687 This function calls create_coreboot_variant.sh to set up a new variant
Paul Fagerburge868e832020-01-22 17:14:04 -0700688 of the reference board.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700689
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700690 Args:
691 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700692
693 Returns:
694 True if everything succeeded, False if something failed
695 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700696 logging.info('Running step create_coreboot_variant')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700697 cb_src_dir = os.path.join('/mnt/host/source/src/', status.coreboot_dir)
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600698 environ = {**os.environ, 'CB_SRC_DIR': cb_src_dir}
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700699 create_coreboot_variant_sh = os.path.join(status.my_loc,
700 'create_coreboot_variant.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600701 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700702 [create_coreboot_variant_sh,
Paul Fagerburg3ef1cbb2020-06-02 16:38:36 -0600703 status.coreboot_base,
Matt Ziegelbaum3e879df2020-11-09 16:08:44 -0500704 status.coreboot_reference,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700705 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600706 status.bug], env=environ)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700707 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700708 status.commits[step_names.CB_VARIANT] = get_git_commit_data(cb_src_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700709 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700710
711
712def create_coreboot_config(status):
713 """Create a coreboot configuration for a new variant
714
715 This function calls create_coreboot_config.sh, which will make a copy
716 of coreboot.${BOARD} into coreboot.${VARIANT}.
717
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700718 Args:
719 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700720
721 Returns:
722 True if the script and test build succeeded, False if something failed
723 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700724 logging.info('Running step create_coreboot_config')
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600725 # Only set CB_CONFIG_DIR if it's not None, so here we have to copy
726 # the environment first and then optionally add a key.
Paul Fagerburg4d343452020-01-24 15:23:53 -0700727 environ = os.environ.copy()
728 if status.cb_config_dir is not None:
729 environ['CB_CONFIG_DIR'] = status.cb_config_dir
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700730 create_coreboot_config_sh = os.path.join(status.my_loc,
731 'create_coreboot_config.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600732 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700733 [create_coreboot_config_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700734 status.base,
Matt Ziegelbaum3e879df2020-11-09 16:08:44 -0500735 status.coreboot_reference,
Paul Fagerburge868e832020-01-22 17:14:04 -0700736 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600737 status.bug], env=environ)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700738 if rc:
739 # Use status.cb_config_dir if defined, or if not, use
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700740 # '/mnt/host/source/src/third_party/chromiumos-overlay'
Paul Fagerburgba508a02020-11-20 14:40:55 -0700741 cb_config_dir = os.path.join(
742 '/mnt/host/source/src/',
743 status.cb_config_dir or 'third_party/chromiumos-overlay')
744 status.commits[step_names.CB_CONFIG] = get_git_commit_data(
745 cb_config_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700746 return rc
Paul Fagerburge868e832020-01-22 17:14:04 -0700747
748
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700749def copy_cras_config(status):
750 """Copy the cras config for a new variant
751
752 This is only necessary for the Zork baseboard right now.
753 This function calls copy_cras_config.sh, which will copy the
Paul Fagerburgba508a02020-11-20 14:40:55 -0700754 cras config in overlays/overlay-${BASE}/chromeos-base/\
755 chromeos-bsp-${BASE}/files/cras-config/${BASE} to .../${VARIANT}
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700756
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700757 Args:
758 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700759
760 Returns:
761 True if the script and test build succeeded, False if something failed
762 """
763 logging.info('Running step copy_cras_config')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700764 copy_cras_config_sh = os.path.join(status.my_loc, 'copy_cras_config.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600765 rc = run_process(
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700766 [copy_cras_config_sh,
767 status.base,
768 status.board,
769 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600770 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700771 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700772 status.commits[step_names.CRAS_CONFIG] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700773 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700774 return rc
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700775
776
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700777def add_fitimage(status):
778 """Add the source files for a fitimage for the new variant
779
780 This function calls add_fitimage.sh to create a new XSL file for the
Paul Fagerburge868e832020-01-22 17:14:04 -0700781 variant's fitimage, which can override settings from the reference board's
782 XSL. When this is done, the user will have to build the fitimage by running
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700783 gen_fit_image.sh outside of the chroot (and outside of this program's
784 control) because gen_fit_image.sh uses WINE, which is not installed in
785 the chroot. (There is a linux version of FIT, but it requires Open GL,
786 which is also not installed in the chroot.)
787
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700788 Args:
789 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700790
791 Returns:
792 True if the script succeeded, False otherwise
793 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700794 logging.info('Running step add_fitimage')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700795 add_fitimage_sh = os.path.expanduser(os.path.join(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700796 '/mnt/host/source/src', status.fitimage_dir, 'files/add_fitimage.sh'))
Paul Fagerburg042a5252020-03-16 21:49:18 -0600797 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700798 [add_fitimage_sh,
799 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600800 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700801 if rc:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700802 fitimage_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir)
Paul Fagerburgba508a02020-11-20 14:40:55 -0700803 status.commits[step_names.COMMIT_FIT] = get_git_commit_data(
804 fitimage_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700805 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700806
807
808def gen_fit_image_outside_chroot(status):
809 """Tell the user to run gen_fit_image.sh outside the chroot
810
811 As noted for add_Fitimage(), gen_fit_image.sh cannot run inside the
812 chroot. This function tells the user to run gen_fit_image.sh in
813 their normal environment, and then come back (--continue) when that
814 is done.
815
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700816 Args:
817 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700818
819 Returns:
820 True
821 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700822 logging.info('Running step gen_fit_image_outside_chroot')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700823 fit_image_files = check_fit_image_files(status)
824 # If the list is empty, then `not` of the list is True, so the files
825 # we need are all present and we can continue.
826 if not fit_image_files:
827 return True
828
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700829 logging.error('The following files need to be generated:')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700830 for filename in fit_image_files:
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700831 logging.error('* %s', filename)
Paul Fagerburgba508a02020-11-20 14:40:55 -0700832 logging.error(
833 'The fitimage sources are ready for gen_fit_image.sh to process.')
834 logging.error(
835 'gen_fit_image.sh cannot run inside the chroot. Open a new terminal,')
836 logging.error(
837 'change to the directory where gen_fit_image.sh is located, and run')
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700838 logging.error(status.fitimage_cmd, status.variant)
839 logging.error('Then re-start this program with --continue.')
Paul Fagerburgba508a02020-11-20 14:40:55 -0700840 logging.error(
841 'If your chroot is based in ~/chromiumos, then the folder you want is')
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700842 logging.error('~/chromiumos/src/%s/asset_generation', status.fitimage_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700843 return False
844
845
846def check_fit_image_files(status):
847 """Check if the fitimage has been generated
848
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700849 This function is not called directly as a step, and so it doesn't need
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700850 to produce any error messages to the user (except with --verbose).
851 gen_fit_image_outside_chroot will call this function to see if the
852 fitimage files exist, and if not, then that function will print the
853 message about how the user needs to run gen_fit_image.sh outside the
854 chroot.
855
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700856 Args:
857 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700858
859 Returns:
860 List of files that *DO NOT* exist and need to be created, [] if
861 all files are present.
862 """
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700863 outputs_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir,
864 'asset_generation/outputs')
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700865 logging.debug('outputs_dir = "%s"', outputs_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700866
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600867 files_not_found = []
868 fitimage_bin = 'fitimage-' + status.variant + '.bin'
869 if not os.path.isfile(os.path.join(outputs_dir, fitimage_bin)):
870 files_not_found.append(fitimage_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700871
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600872 fitimage_versions = 'fitimage-' + status.variant + '-versions.txt'
873 if not os.path.isfile(os.path.join(outputs_dir, fitimage_versions)):
874 files_not_found.append(fitimage_versions)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700875
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600876 return files_not_found
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700877
878
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700879def commit_fitimage(status):
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600880 """Add the fitimage files to the git commit
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700881
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600882 This function calls commit_fitimage.sh to move the fitimage binary and
883 -versions files from asset_generation/outputs to files/ and then adds
884 those files and fit.log to the existing git commit.
885 Depending on the baseboard, there may be different file names (such
886 as fit-${VARIANT}.log for volteer) and/or additional files (such as
887 files/blobs/description-${VARIANT}.bin for volteer)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700888
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:
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600893 True if the script succeeded, False if something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700894 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700895 logging.info('Running step commit_fitimage')
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600896 commit_fitimage_sh = os.path.expanduser(os.path.join(
Paul Fagerburgba508a02020-11-20 14:40:55 -0700897 '/mnt/host/source/src', status.fitimage_dir,
898 'files/commit_fitimage.sh'))
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600899 return run_process([commit_fitimage_sh, status.variant])
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700900
901
902def create_initial_ec_image(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700903 """Create an EC image for the variant as a clone of the reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700904
905 This function calls create_initial_ec_image.sh, which will clone the
Paul Fagerburge868e832020-01-22 17:14:04 -0700906 reference board to create the variant. The shell script will build the
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700907 EC code for the variant, but the repo upload hook insists that we
908 have done a `make buildall` before it will allow an upload, so this
909 function does the buildall.
910
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700911 Args:
912 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700913
914 Returns:
915 True if the script and test build succeeded, False if something failed
916 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700917 logging.info('Running step create_initial_ec_image')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700918 create_initial_ec_image_sh = os.path.join(status.my_loc,
919 'create_initial_ec_image.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600920 if not run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700921 [create_initial_ec_image_sh,
922 status.board,
923 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600924 status.bug]):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700925 return False
926
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700927 # No need to `if rc:` because we already tested the run_process result above
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700928 status.commits[step_names.EC_IMAGE] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700929 '/mnt/host/source/src/platform/ec/board')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700930
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700931 # create_initial_ec_image.sh will build the ec.bin for this variant
932 # if successful.
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600933 ec_dir = '/mnt/host/source/src/platform/ec'
934 ec_bin = os.path.join(ec_dir, 'build', status.variant, 'ec.bin')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700935 logging.debug('ec.bin = "%s"', ec_bin)
936
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600937 if not os.path.isfile(ec_bin):
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700938 logging.error('EC binary %s not found', ec_bin)
939 return False
940 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700941
942
943def ec_buildall(status):
944 """Do a make buildall -j for the EC, which is required for repo upload
945
946 The upload hook checks to ensure that the entire EC codebase builds
947 without error, so we have to run make buildall -j before uploading.
948
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700949 Args:
950 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700951
952 Returns:
953 True if the script and test build succeeded, False if something failed
954 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700955 logging.info('Running step ec_buildall')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700956 del status # unused parameter
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700957 ec = '/mnt/host/source/src/platform/ec'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700958 logging.debug('ec = "%s"', ec)
Paul Fagerburg042a5252020-03-16 21:49:18 -0600959 return run_process(['make', 'buildall', '-j'], cwd=ec)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700960
961
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700962def add_variant_to_public_yaml(status):
963 """Add the new variant to the public model.yaml file
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700964
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700965 This function calls add_variant_to_yaml.sh to add the new variant to
966 the public model.yaml file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700967
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700968 Args:
969 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700970
971 Returns:
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700972 True if the script succeeded, False is something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700973 """
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700974 logging.info('Running step add_variant_to_public_yaml')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700975 add_variant_to_yaml_sh = os.path.join(status.my_loc,
976 'add_variant_to_yaml.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600977 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700978 [add_variant_to_yaml_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700979 status.base,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700980 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600981 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700982 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700983 status.commits[step_names.ADD_PUB_YAML] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700984 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700985 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700986
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700987
988def add_variant_to_private_yaml(status):
989 """Add the new variant to the private model.yaml file
990
991 This function calls add_variant.sh to add the new variant to
992 the private model.yaml file.
993
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700994 Args:
995 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700996
997 Returns:
998 True if the script succeeded, False is something failed
999 """
1000 logging.info('Running step add_variant_to_private_yaml')
Paul Fagerburgba508a02020-11-20 14:40:55 -07001001 add_variant_sh = os.path.expanduser(os.path.join(status.private_yaml_dir,
1002 'add_variant.sh'))
Paul Fagerburg042a5252020-03-16 21:49:18 -06001003 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001004 [add_variant_sh,
1005 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -06001006 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001007 if rc:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001008 status.commits[step_names.ADD_PRIV_YAML] = get_git_commit_data(
1009 status.private_yaml_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001010 return rc
1011
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001012
1013
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001014def build_config(status):
1015 """Build project config files, from yaml or starlark
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001016
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001017 This function builds the project config files that mosys and other tools
1018 use, then verifies that the new variant's name shows up in all of the
1019 output files. Depending on the baseboard, the input may be the model.yaml
1020 files, or the starlark configuration files.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001021
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001022 Args:
1023 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001024
1025 Returns:
1026 True if the scripts and build succeeded, False is something failed
1027 """
Paul Fagerburg4376fe52020-06-16 10:22:35 -06001028 logging.info('Running step build_config')
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001029 if not emerge_with_workon(status, status.config_workon_pkgs,
1030 status.emerge_cmd, status.config_emerge_pkgs):
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001031 return False
1032
Paul Fagerburga95dd162020-03-24 16:27:18 -06001033 # Check the generated config.yaml file for occurences of the variant
1034 # name to determine if the emerge was successful.
1035 config_yaml = os.path.join(
1036 '/build', status.base, 'usr/share/chromeos-config/yaml/config.yaml')
1037 logging.debug('config_yaml = "%s"', config_yaml)
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001038 if not os.path.isfile(config_yaml):
Paul Fagerburga95dd162020-03-24 16:27:18 -06001039 logging.error('%s does not exist', config_yaml)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001040 return False
1041
Paul Fagerburga95dd162020-03-24 16:27:18 -06001042 if not status.variant in osutils.ReadFile(config_yaml):
1043 logging.error('variant name %s not found in yaml file %s',
1044 status.variant, config_yaml)
1045 return False
1046
1047 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001048
1049
1050def emerge_all(status):
1051 """Build the coreboot BIOS and EC code for the new variant
1052
Paul Fagerburg75398072020-03-16 13:51:24 -06001053 This build step will cros_workon start a list of packages provided by
1054 the reference board data as status.workon_pkgs, then emerge a list of
1055 packages (status.emerge_pkgs), and then cros_workon stop any packages
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001056 that it started. Any packages that were already being worked on will
1057 not be stopped.
Paul Fagerburg75398072020-03-16 13:51:24 -06001058
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001059 Args:
1060 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001061
1062 Returns:
1063 True if the build succeeded, False if something failed
1064 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001065 logging.info('Running step emerge_all')
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001066 environ = {**os.environ, 'FW_NAME': status.variant}
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001067 if not emerge_with_workon(status, status.workon_pkgs,
1068 status.emerge_cmd, status.emerge_pkgs,
1069 env=environ):
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001070 return False
1071
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001072 # Check if the expected build outputs exist.
Paul Fagerburge868e832020-01-22 17:14:04 -07001073 build_path = '/build/' + status.base + '/firmware'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001074 logging.debug('build_path = "%s"', build_path)
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001075 image_bin = 'image-' + status.variant + '.bin'
1076 if not os.path.isfile(os.path.join(build_path, image_bin)):
1077 logging.error('emerge failed because %s does not exist', image_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001078 return False
1079
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001080 serial_bin = 'image-' + status.variant + '.serial.bin'
1081 if not os.path.isfile(os.path.join(build_path, serial_bin)):
1082 logging.error('emerge failed because %s does not exist', serial_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001083 return False
1084
1085 return True
1086
1087
1088def push_coreboot(status):
1089 """Push the coreboot CL to coreboot.org
1090
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001091 Args:
1092 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001093
1094 Returns:
1095 True if the build succeeded, False if something failed
1096 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001097 logging.info('Running step push_coreboot')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001098
1099 # Set up a return code that may change to False if we find that a
1100 # coreboot CL has not been uploaded.
1101 rc = True
1102
1103 for commit_key in status.coreboot_push_list:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001104 logging.debug('Processing key %s', commit_key)
Paul Fagerburg8d850932020-02-25 14:13:32 -07001105 commit = status.commits[commit_key]
1106 if 'gerrit' not in commit or 'cl_number' not in commit:
1107 change_id = commit['change_id']
1108 cl = find_change_id(change_id)
1109 if cl is not None:
1110 save_cl_data(status, commit_key, cl)
1111 else:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001112 logging.debug('Not found %s, need to upload', change_id)
Paul Fagerburgba508a02020-11-20 14:40:55 -07001113 logging.error(
1114 'The following commit needs to be pushed to coreboot.org:')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001115 logging.error(' Branch "%s"', commit['branch_name'])
1116 logging.error(' in directory "%s"', commit['dir'])
1117 logging.error(' with change-id "%s"', commit['change_id'])
1118 logging.error('Please push the branch to review.coreboot.org, '
1119 'and then re-start this program with --continue')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001120 # Since this commit needs to be uploaded, do not continue after
1121 # this step returns.
1122 rc = False
1123 else:
1124 instance_name = commit['gerrit']
1125 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001126 logging.debug('Already uploaded (%s, %s)', instance_name, cl_number)
Paul Fagerburg8d850932020-02-25 14:13:32 -07001127
1128 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001129
1130
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001131def query_gerrit(instance, change_id):
1132 """Search a gerrit instance for a specific change_id
1133
1134 Args:
1135 instance: gerrit instance to query. Suitable values come from
1136 gerrit.GetCrosInternal() and gerrit.GetCrosExternal()
1137 change_id: The change_id to search for
1138
1139 Returns:
1140 CL number if found, None if not
1141 """
1142 raw = instance.Query(change=change_id, raw=True)
1143 if raw:
1144 # If the CL was found by change_id, there will be only one,
1145 # because the change_id is used to recognize a new patchset
1146 # on an existing CL.
1147 return raw[0]['number']
1148
1149 return None
1150
1151
Paul Fagerburg8d850932020-02-25 14:13:32 -07001152def query_coreboot_gerrit(change_id):
1153 """Search the coreboot gerrit for a specific change_id
1154
1155 Use the REST API to look for the change_id. See
1156 https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html
1157 for details on the REST API to search for a change-id.
1158
1159 We can't use query_gerrit with a manually constructed GerritHelper
1160 because we need the user's private SSH key to access review.coreboot.org,
1161 but these are not available inside the chroot.
1162
1163 Args:
1164 change_id: The change_id to search for
1165
1166 Returns:
1167 CL number if found, None if not
1168 """
1169 r = requests.get('https://review.coreboot.org/changes/' + change_id)
1170 response = r.content.decode('utf-8')
1171 # Check if the response starts with 'Not found', in which case return None
1172 if response.startswith('Not found:'):
1173 return None
1174 # Strip off the initial )]}'\n that is used as XSS protections, see
1175 # https://gerrit-review.googlesource.com/Documentation/rest-api.html#output
1176 # and decode as JSON.
1177 data = json.loads(response[5:])
1178 if '_number' in data:
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001179 return str(data['_number'])
Paul Fagerburg8d850932020-02-25 14:13:32 -07001180 return None
1181
1182
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001183def find_change_id(change_id):
1184 """Search the public and private ChromeOS gerrit instances for a change-id
1185
1186 Args:
1187 change_id: Change-Id to search for in both gerrit instances
1188
1189 Returns:
1190 Tuple of the gerrit instance ('chromium' or 'chrome-internal') and
1191 the CL number if the Change-Id is found.
1192 None if not found.
1193 """
1194 cl_number = query_gerrit(gerrit.GetCrosExternal(), change_id)
1195 if cl_number:
1196 return 'chromium', cl_number
1197 cl_number = query_gerrit(gerrit.GetCrosInternal(), change_id)
1198 if cl_number:
1199 return 'chrome-internal', cl_number
Paul Fagerburg8d850932020-02-25 14:13:32 -07001200 cl_number = query_coreboot_gerrit(change_id)
1201 if cl_number:
1202 return 'coreboot', cl_number
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001203 return None
1204
1205
1206def save_cl_data(status, commit_key, cl):
1207 """Save the gerrit instance and CL number to the yaml file
1208
1209 Args:
1210 status: variant_status object tracking our board, variant, etc.
1211 commit_key: Which key in the commits map we're processing
1212 cl: Value returned by find_change_id, should be a tuple
1213 of instance_name, cl_number
1214 """
1215 instance_name, cl_number = cl
1216 print(f'Found ({instance_name}, {cl_number}), saving to yaml')
1217 status.commits[commit_key]['gerrit'] = instance_name
1218 status.commits[commit_key]['cl_number'] = cl_number
1219 status.save()
1220
1221
1222def repo_upload(branch_name, cwd):
1223 """Upload a branch to gerrit
1224
1225 This function runs `repo upload` in the specified directory to upload
1226 a branch to gerrit. Because it's operating in a directory and with a
1227 branch name, it could upload more than one commit, which is OK because
1228 we'll look for each commit by change-id before trying to upload in that
1229 directory. For example, this happens in Zork, where the cb_config step
1230 and the cras_config step both have a commit in src/overlays. When we're
1231 processing the cb_config step and we `repo upload` in src/overlays, it
1232 will also upload the commit for cras_config. Then we come around to the
1233 cras_config step, and since we can find a CL with the change-id, we don't
1234 try to upload again.
1235
1236 Args:
1237 branch_name: the name of the branch to upload. Gets passed to
1238 repo upload with the --br flag
1239 cwd: directory where we want to upload. Gets set as the working
1240 directory for executing repo upload.
1241
1242 Returns:
1243 True if repo upload exits with a successful error code, false otherwise
1244 """
Paul Fagerburg042a5252020-03-16 21:49:18 -06001245 return run_process(
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001246 ['repo',
1247 'upload',
1248 '.',
1249 '--br=' + branch_name,
1250 '--wip',
1251 '--verify',
Paul Fagerburgda6bc102020-08-31 19:27:04 -06001252 '--yes',
1253 '--hashtag=new_variant'],
Paul Fagerburg042a5252020-03-16 21:49:18 -06001254 cwd=cwd)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001255
1256
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001257def upload_CLs(status):
1258 """Upload all CLs to chromiumos
1259
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001260 Args:
1261 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001262
1263 Returns:
1264 True if the build succeeded, False if something failed
1265 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001266 logging.info('Running step upload_CLs')
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001267
1268 for commit_key in status.repo_upload_list:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001269 logging.debug('Processing key %s', commit_key)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001270 commit = status.commits[commit_key]
1271 if 'gerrit' not in commit or 'cl_number' not in commit:
1272 change_id = commit['change_id']
1273 cl = find_change_id(change_id)
1274 if cl is not None:
1275 save_cl_data(status, commit_key, cl)
1276 else:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001277 logging.debug('Not found %s, need to upload', change_id)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001278 if not repo_upload(commit['branch_name'], commit['dir']):
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001279 logging.error('Repo upload %s in %s failed!',
1280 commit['branch_name'],
1281 commit['dir'])
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001282 return False
1283 cl = find_change_id(change_id)
1284 if cl is None:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001285 logging.error(
1286 'repo upload %s succeeded, but change_id is not found!',
1287 commit_key)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001288 return False
1289 save_cl_data(status, commit_key, cl)
1290 else:
1291 instance_name = commit['gerrit']
1292 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001293 logging.debug('Already uploaded (%s, %s)', instance_name, cl_number)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001294
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001295 return True
1296
1297
1298def find_coreboot_upstream(status):
Paul Fagerburgba508a02020-11-20 14:40:55 -07001299 """Find the upstream coreboot CL in chromiumos
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001300
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001301 When the coreboot variant CL is first uploaded to review.coreboot.org,
1302 it is not visible in the chromiumos tree (and also cannot be used as
Paul Fagerburgba508a02020-11-20 14:40:55 -07001303 a target for cq-depend). There is a process for upstream CLs from
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001304 coreboot after they have been reviewed, approved, and merged. We can
1305 track a specific coreboot CL if we know the change-id that it used on
1306 the coreboot gerrit instance, by looking for that change-id as
1307 'original-change-id' in the public chromium gerrit instance.
1308
1309 The change-id for the coreboot variant will be under the 'cb_variant' key,
1310 but this is for the 'coreboot' gerrit instance.
1311
1312 When we find the upstreamed CL, we will record the gerrit instance and
1313 CL number in the yaml file under the 'find' key ("find upstream coreboot")
1314 so that we don't need to search coreboot again.
1315
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001316 Args:
1317 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001318
1319 Returns:
1320 True if the build succeeded, False if something failed
1321 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001322 logging.info('Running step find_coreboot_upstream')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001323
1324 # If we have already found the upstream coreboot CL, then exit with success
1325 if step_names.FIND in status.commits:
1326 commit = status.commits[step_names.FIND]
1327 if 'gerrit' in commit and 'cl_number' in commit:
1328 instance_name = commit['gerrit']
1329 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001330 logging.debug('Already found (%s, %s)', instance_name, cl_number)
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001331 return True
1332
1333 # Make sure we have a CB_VARIANT commit and a change_id for it
1334 if step_names.CB_VARIANT not in status.commits:
1335 logging.error('Key %s not found in status.commits',
1336 step_names.CB_VARIANT)
1337 return False
1338 if 'change_id' not in status.commits[step_names.CB_VARIANT]:
1339 logging.error('Key change_id not found in status.commits[%s]',
1340 step_names.CB_VARIANT)
1341 return False
1342
1343 # Find the CL by the Original-Change-Id
1344 original_change_id = status.commits[step_names.CB_VARIANT]['change_id']
1345 gerrit_query_args = {
1346 'Original-Change-Id': original_change_id
1347 }
1348 cros = gerrit.GetCrosExternal()
1349 upstream = cros.Query(**gerrit_query_args)
1350 # If nothing is found, the patch hasn't been upstreamed yet
1351 if not upstream:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001352 logging.error('Program cannot continue without upstream coreboot CL.')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001353 logging.error('(coreboot:%s, change-id %s)',
1354 status.commits[step_names.CB_VARIANT]['cl_number'],
1355 status.commits[step_names.CB_VARIANT]['change_id'])
Paul Fagerburgba508a02020-11-20 14:40:55 -07001356 logging.error('Please wait for the upstream CL, then run this program'
1357 ' again with --continue')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001358 return False
1359
1360 # If more than one CL is found, something is very wrong
1361 if len(upstream) != 1:
1362 logging.error('More than one CL was found with Original-Change-Id %s',
1363 original_change_id)
1364 return False
1365
1366 # At this point, we know there is only one CL and we can get the
1367 # repo and CL number by splitting on the colon between them.
1368 patchlink = upstream[0].PatchLink()
1369 instance_name, cl_number = patchlink.split(':')
1370
1371 # Can't use get_git_commit_data because we're not pulling this
1372 # information from a git commit, but rather from gerrit.
1373 # We only need the gerrit instance and the CL number so we can have
1374 # other CLs cq-depend on this CL. The other keys are not needed because:
1375 # dir - not needed because we're not going to `cd` there to `repo upload`
1376 # branch_name - not valid; the CL is already merged
1377 # change_id - we use the change_id to find a CL number, and since we
1378 # just found the CL number via original-change-id, this is moot.
1379 status.commits[step_names.FIND] = {
1380 'gerrit': instance_name,
1381 'cl_number': str(cl_number)
1382 }
1383
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001384 return True
1385
1386
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001387def calc_cq_depend(status):
1388 """Determine the list of CLs for each commit that has dependencies.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001389
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001390 status.depends is a map of dependencies from step name to a list of
1391 steps that the step depends on. For each step, find the SHA of the
1392 commit, then find the gerrit instance and CL number of the commits
1393 that it depends on. Construct the Cq-Depends list and save it under
1394 the 'cq_depend' key, i.e. commit['add_priv_yaml']['cq_depend'] or
1395 as it will be stored in the yaml:
1396 commits:
1397 add_priv_yaml:
1398 cq_depend: 'chromium:1629121, chromium:1638243'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001399
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001400 Args:
1401 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001402
1403 Returns:
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001404 True if all dependencies have been calculated. False if something
1405 failed, usually a commit not found by Change-Id.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001406 """
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001407 logging.info('Running step calc_cq_depend')
1408 # Iterate through the commits that have dependencies.
1409 for key in status.depends:
1410 logging.debug('Processing %s to add dependencies', key)
1411 # For every commit that has dependencies, find the gerrit instance
1412 # and CL number of the dependencies.
1413 cq_depend_list = []
1414 for depend_key in status.depends[key]:
1415 depend_commit = status.commits[depend_key]
1416 if not 'gerrit' in depend_commit:
1417 logging.error('Commit %s does not have a gerrit instance',
1418 depend_key)
1419 return False
1420 if not 'cl_number' in depend_commit:
1421 logging.error('Commit %s does not have a CL number',
1422 depend_key)
1423 return False
1424
1425 instance_name = depend_commit['gerrit']
1426 cl_number = depend_commit['cl_number']
1427 cq_depend_list.append(f'{instance_name}:{cl_number}')
1428
1429 # Add the 'cq_depend' key to the commit.
1430 cq_depend_str = 'Cq-Depend: %s' % ', '.join(cq_depend_list)
1431 logging.debug('Add to commit %s %s', key, cq_depend_str)
1432 status.commits[key]['cq_depend'] = cq_depend_str
1433
1434 return True
1435
1436
1437def add_cq_depend_to_commit_msg(git_repo, change_id, cq_depend_str):
1438 """Update the commit message with a Cq-Depends line.
1439
1440 Find the SHA of the commit, then use git filter-branch --msg-filter
1441 to add the Cq-Depend line just before the Change-Id line. See
1442 https://chromium.googlesource.com/chromiumos/docs/+/HEAD/contributing.md#cq-depend
1443 for details about Cq-Depend format and location.
1444
1445 Args:
1446 git_repo: Directory of git repository.
1447 change_id: The Change-Id to search for.
1448 cq_depend_str: The Cq-Depend string. It must be in the correct format
1449 per chromeos documentation, ready to insert into the commit msg
1450 on the line before Change-Id.
1451
1452 Returns:
1453 True if `git filter-branch` was successful. False if the command
1454 failed.
1455 """
1456 logging.debug('find SHA of Change-Id %s in %s', change_id, git_repo)
1457 sha = change_id_to_sha(git_repo, change_id)
1458 if sha is None:
1459 logging.error('Cannot find the SHA for Change-Id %s in %s',
1460 change_id, git_repo)
1461 return False
1462 logging.debug('SHA = %s', sha)
1463
1464 # Check if the commit message already has a Cq-Depend line.
1465 msg = get_commit_msg(git_repo, sha)
1466 if any('Cq-Depend' in tmpstr for tmpstr in msg):
1467 logging.debug('Already has Cq-Depend')
1468 return True
1469
1470 # Use git filter-branch --msg-filter to add the Cq-Depend line just
1471 # before the Change-Id line.
1472 environ = {**os.environ, 'FILTER_BRANCH_SQUELCH_WARNING': '1'}
1473 cmd = [
1474 'git',
1475 'filter-branch',
Paul Fagerburg5f6d2012020-06-24 11:29:36 -06001476 '-f',
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001477 '--msg-filter',
1478 f'sed -E "s/^(Change-Id: {change_id})$/{cq_depend_str}\\n\\1/"',
1479 '--',
1480 f'{sha}^..']
1481 return run_process(cmd, cwd=git_repo, env=environ)
1482
1483
1484def add_cq_depend(status):
1485 """Add Cq-Depend to commits and flag them for re-upload.
1486
1487 Args:
1488 status: variant_status object tracking our board, variant, etc.
1489
1490 Returns:
1491 True if the commit messages have been successfully amended, False if
1492 something failed.
1493 """
1494 logging.info('Running step add_cq_depend')
1495 for key in status.commits:
1496 commit = status.commits[key]
1497 if 'cq_depend' in commit:
1498 logging.debug('%s has %s', key, commit['cq_depend'])
1499 # Make sure the commit has a working directory and a change_id
1500 # before trying to amend its commit message.
1501 if 'dir' not in commit or 'change_id' not in commit:
1502 logging.error('Missing dir and/or change_id from %s', key)
1503 return False
1504
1505 if not add_cq_depend_to_commit_msg(commit['dir'],
1506 commit['change_id'],
1507 commit['cq_depend']):
1508 return False
1509 commit['needs_re_upload'] = True
1510 else:
1511 logging.debug('%s no dependencies', key)
1512
1513 return True
1514
1515
1516def re_upload(status):
1517 """Re-upload commits that have changed.
1518
1519 Args:
1520 status: variant_status object tracking our board, variant, etc.
1521
1522 Returns:
1523 True if the uploads succeeded. False if a repo upload failed.
1524 """
1525 logging.info('Running step re_upload')
1526 for key in status.commits:
1527 commit = status.commits[key]
1528 if commit.get('needs_re_upload'):
1529 logging.debug('Re-upload branch %s in %s', commit['branch_name'],
1530 commit['dir'])
1531 if not repo_upload(commit['branch_name'], commit['dir']):
1532 logging.error('Repo upload %s in %s failed!',
1533 commit['branch_name'],
1534 commit['dir'])
1535 return False
1536 commit['needs_re_upload'] = False
1537
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001538 return True
1539
1540
1541def clean_up(status):
1542 """Final clean-up, including delete the status file
1543
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001544 Args:
1545 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001546
1547 Returns:
1548 True
1549 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001550 logging.info('Running step clean_up')
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001551 status.rm()
1552 return True
1553
1554
Paul Fagerburg042a5252020-03-16 21:49:18 -06001555def abort(status):
1556 """Abort the creation of a new variant by abandoning commits
1557
1558 When the user specifies the --abort flag, we override status.step to
1559 be 'abort' and there is no transition from 'abort' to anything else.
1560 We look at status.commits and for each key, see if we have already
1561 been in that directory and abandoned that specific branch. If not,
1562 abandon the commit and then add the branch+dir to a list of abandoned
1563 commits. We do this because some boards (such as Zork) can have multiple
1564 commits in the same directory and with the same branch name, and we only
1565 want to repo abandon that branch once.
1566
1567 Args:
1568 status: variant_status object tracking our board, variant, etc.
1569
1570 Returns:
1571 True
1572 """
1573 logging.info('Running step abort')
1574 # Use the set 'abandoned' to keep track of each branch+dir abandoned.
1575 abandoned = set()
1576 for step in status.commits:
1577 logging.debug('Processing step %s', step)
1578 commit = status.commits[step]
1579 branch = commit['branch_name']
1580 cwd = commit['dir']
1581 if (branch, cwd) in abandoned:
1582 logging.debug('Branch %s in directory %s already abandoned',
1583 branch, cwd)
1584 else:
1585 logging.info('Abandoning branch %s in directory %s',
1586 branch, cwd)
1587 if run_process(['repo', 'abandon', branch, '.'], cwd=cwd):
1588 abandoned.add((branch, cwd))
1589 else:
1590 logging.error('Error while abandoning branch %s', branch)
1591 return False
1592
1593 return True
1594
1595
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001596if __name__ == '__main__':
1597 sys.exit(not int(main()))