blob: 50921644cae1635afd3d16f582f7222c1fcda7c8 [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 Fagerburgfcada622020-11-20 11:53:36 -0700907 EC code for the variant.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700908
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700909 Args:
910 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700911
912 Returns:
913 True if the script and test build succeeded, False if something failed
914 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700915 logging.info('Running step create_initial_ec_image')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700916 create_initial_ec_image_sh = os.path.join(status.my_loc,
917 'create_initial_ec_image.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600918 if not run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700919 [create_initial_ec_image_sh,
920 status.board,
921 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600922 status.bug]):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700923 return False
924
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700925 # No need to `if rc:` because we already tested the run_process result above
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700926 status.commits[step_names.EC_IMAGE] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700927 '/mnt/host/source/src/platform/ec/board')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700928
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700929 # create_initial_ec_image.sh will build the ec.bin for this variant
930 # if successful.
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600931 ec_dir = '/mnt/host/source/src/platform/ec'
932 ec_bin = os.path.join(ec_dir, 'build', status.variant, 'ec.bin')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700933 logging.debug('ec.bin = "%s"', ec_bin)
934
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600935 if not os.path.isfile(ec_bin):
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700936 logging.error('EC binary %s not found', ec_bin)
937 return False
938 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700939
940
941def ec_buildall(status):
Paul Fagerburgfcada622020-11-20 11:53:36 -0700942 """Deprecated function that used to do a make buildall -j for the EC
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700943
Paul Fagerburgfcada622020-11-20 11:53:36 -0700944 The EC repo upload hook used to require a make buildall -j before
945 uploading. As of crrev.com/c/2436379 this requirement has been removed,
946 so this step is no longer necessary.
947
948 This function still exists so that if someone has a new variant already
949 in progress and they update new_variant.py, it won't break. Eventually,
950 this function will be removed completely.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700951
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700952 Args:
953 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700954
955 Returns:
Paul Fagerburgfcada622020-11-20 11:53:36 -0700956 True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700957 """
Paul Fagerburgfcada622020-11-20 11:53:36 -0700958 logging.info('Running deprecated step ec_buildall')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700959 del status # unused parameter
Paul Fagerburgfcada622020-11-20 11:53:36 -0700960 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700961
962
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700963def add_variant_to_public_yaml(status):
964 """Add the new variant to the public model.yaml file
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700965
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700966 This function calls add_variant_to_yaml.sh to add the new variant to
967 the public model.yaml file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700968
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700969 Args:
970 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700971
972 Returns:
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700973 True if the script succeeded, False is something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700974 """
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700975 logging.info('Running step add_variant_to_public_yaml')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700976 add_variant_to_yaml_sh = os.path.join(status.my_loc,
977 'add_variant_to_yaml.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600978 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700979 [add_variant_to_yaml_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700980 status.base,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700981 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600982 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700983 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700984 status.commits[step_names.ADD_PUB_YAML] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700985 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700986 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700987
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700988
989def add_variant_to_private_yaml(status):
990 """Add the new variant to the private model.yaml file
991
992 This function calls add_variant.sh to add the new variant to
993 the private model.yaml file.
994
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700995 Args:
996 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700997
998 Returns:
999 True if the script succeeded, False is something failed
1000 """
1001 logging.info('Running step add_variant_to_private_yaml')
Paul Fagerburgba508a02020-11-20 14:40:55 -07001002 add_variant_sh = os.path.expanduser(os.path.join(status.private_yaml_dir,
1003 'add_variant.sh'))
Paul Fagerburg042a5252020-03-16 21:49:18 -06001004 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001005 [add_variant_sh,
1006 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -06001007 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001008 if rc:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001009 status.commits[step_names.ADD_PRIV_YAML] = get_git_commit_data(
1010 status.private_yaml_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001011 return rc
1012
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001013
1014
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001015def build_config(status):
1016 """Build project config files, from yaml or starlark
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001017
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001018 This function builds the project config files that mosys and other tools
1019 use, then verifies that the new variant's name shows up in all of the
1020 output files. Depending on the baseboard, the input may be the model.yaml
1021 files, or the starlark configuration files.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001022
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001023 Args:
1024 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001025
1026 Returns:
1027 True if the scripts and build succeeded, False is something failed
1028 """
Paul Fagerburg4376fe52020-06-16 10:22:35 -06001029 logging.info('Running step build_config')
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001030 if not emerge_with_workon(status, status.config_workon_pkgs,
1031 status.emerge_cmd, status.config_emerge_pkgs):
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001032 return False
1033
Paul Fagerburga95dd162020-03-24 16:27:18 -06001034 # Check the generated config.yaml file for occurences of the variant
1035 # name to determine if the emerge was successful.
1036 config_yaml = os.path.join(
1037 '/build', status.base, 'usr/share/chromeos-config/yaml/config.yaml')
1038 logging.debug('config_yaml = "%s"', config_yaml)
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001039 if not os.path.isfile(config_yaml):
Paul Fagerburga95dd162020-03-24 16:27:18 -06001040 logging.error('%s does not exist', config_yaml)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001041 return False
1042
Paul Fagerburga95dd162020-03-24 16:27:18 -06001043 if not status.variant in osutils.ReadFile(config_yaml):
1044 logging.error('variant name %s not found in yaml file %s',
1045 status.variant, config_yaml)
1046 return False
1047
1048 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001049
1050
1051def emerge_all(status):
1052 """Build the coreboot BIOS and EC code for the new variant
1053
Paul Fagerburg75398072020-03-16 13:51:24 -06001054 This build step will cros_workon start a list of packages provided by
1055 the reference board data as status.workon_pkgs, then emerge a list of
1056 packages (status.emerge_pkgs), and then cros_workon stop any packages
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001057 that it started. Any packages that were already being worked on will
1058 not be stopped.
Paul Fagerburg75398072020-03-16 13:51:24 -06001059
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001060 Args:
1061 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001062
1063 Returns:
1064 True if the build succeeded, False if something failed
1065 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001066 logging.info('Running step emerge_all')
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001067 environ = {**os.environ, 'FW_NAME': status.variant}
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001068 if not emerge_with_workon(status, status.workon_pkgs,
1069 status.emerge_cmd, status.emerge_pkgs,
1070 env=environ):
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001071 return False
1072
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001073 # Check if the expected build outputs exist.
Paul Fagerburge868e832020-01-22 17:14:04 -07001074 build_path = '/build/' + status.base + '/firmware'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001075 logging.debug('build_path = "%s"', build_path)
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001076 image_bin = 'image-' + status.variant + '.bin'
1077 if not os.path.isfile(os.path.join(build_path, image_bin)):
1078 logging.error('emerge failed because %s does not exist', image_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001079 return False
1080
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001081 serial_bin = 'image-' + status.variant + '.serial.bin'
1082 if not os.path.isfile(os.path.join(build_path, serial_bin)):
1083 logging.error('emerge failed because %s does not exist', serial_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001084 return False
1085
1086 return True
1087
1088
1089def push_coreboot(status):
1090 """Push the coreboot CL to coreboot.org
1091
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001092 Args:
1093 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001094
1095 Returns:
1096 True if the build succeeded, False if something failed
1097 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001098 logging.info('Running step push_coreboot')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001099
1100 # Set up a return code that may change to False if we find that a
1101 # coreboot CL has not been uploaded.
1102 rc = True
1103
1104 for commit_key in status.coreboot_push_list:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001105 logging.debug('Processing key %s', commit_key)
Paul Fagerburg8d850932020-02-25 14:13:32 -07001106 commit = status.commits[commit_key]
1107 if 'gerrit' not in commit or 'cl_number' not in commit:
1108 change_id = commit['change_id']
1109 cl = find_change_id(change_id)
1110 if cl is not None:
1111 save_cl_data(status, commit_key, cl)
1112 else:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001113 logging.debug('Not found %s, need to upload', change_id)
Paul Fagerburgba508a02020-11-20 14:40:55 -07001114 logging.error(
1115 'The following commit needs to be pushed to coreboot.org:')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001116 logging.error(' Branch "%s"', commit['branch_name'])
1117 logging.error(' in directory "%s"', commit['dir'])
1118 logging.error(' with change-id "%s"', commit['change_id'])
1119 logging.error('Please push the branch to review.coreboot.org, '
1120 'and then re-start this program with --continue')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001121 # Since this commit needs to be uploaded, do not continue after
1122 # this step returns.
1123 rc = False
1124 else:
1125 instance_name = commit['gerrit']
1126 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001127 logging.debug('Already uploaded (%s, %s)', instance_name, cl_number)
Paul Fagerburg8d850932020-02-25 14:13:32 -07001128
1129 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001130
1131
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001132def query_gerrit(instance, change_id):
1133 """Search a gerrit instance for a specific change_id
1134
1135 Args:
1136 instance: gerrit instance to query. Suitable values come from
1137 gerrit.GetCrosInternal() and gerrit.GetCrosExternal()
1138 change_id: The change_id to search for
1139
1140 Returns:
1141 CL number if found, None if not
1142 """
1143 raw = instance.Query(change=change_id, raw=True)
1144 if raw:
1145 # If the CL was found by change_id, there will be only one,
1146 # because the change_id is used to recognize a new patchset
1147 # on an existing CL.
1148 return raw[0]['number']
1149
1150 return None
1151
1152
Paul Fagerburg8d850932020-02-25 14:13:32 -07001153def query_coreboot_gerrit(change_id):
1154 """Search the coreboot gerrit for a specific change_id
1155
1156 Use the REST API to look for the change_id. See
1157 https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html
1158 for details on the REST API to search for a change-id.
1159
1160 We can't use query_gerrit with a manually constructed GerritHelper
1161 because we need the user's private SSH key to access review.coreboot.org,
1162 but these are not available inside the chroot.
1163
1164 Args:
1165 change_id: The change_id to search for
1166
1167 Returns:
1168 CL number if found, None if not
1169 """
1170 r = requests.get('https://review.coreboot.org/changes/' + change_id)
1171 response = r.content.decode('utf-8')
1172 # Check if the response starts with 'Not found', in which case return None
1173 if response.startswith('Not found:'):
1174 return None
1175 # Strip off the initial )]}'\n that is used as XSS protections, see
1176 # https://gerrit-review.googlesource.com/Documentation/rest-api.html#output
1177 # and decode as JSON.
1178 data = json.loads(response[5:])
1179 if '_number' in data:
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001180 return str(data['_number'])
Paul Fagerburg8d850932020-02-25 14:13:32 -07001181 return None
1182
1183
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001184def find_change_id(change_id):
1185 """Search the public and private ChromeOS gerrit instances for a change-id
1186
1187 Args:
1188 change_id: Change-Id to search for in both gerrit instances
1189
1190 Returns:
1191 Tuple of the gerrit instance ('chromium' or 'chrome-internal') and
1192 the CL number if the Change-Id is found.
1193 None if not found.
1194 """
1195 cl_number = query_gerrit(gerrit.GetCrosExternal(), change_id)
1196 if cl_number:
1197 return 'chromium', cl_number
1198 cl_number = query_gerrit(gerrit.GetCrosInternal(), change_id)
1199 if cl_number:
1200 return 'chrome-internal', cl_number
Paul Fagerburg8d850932020-02-25 14:13:32 -07001201 cl_number = query_coreboot_gerrit(change_id)
1202 if cl_number:
1203 return 'coreboot', cl_number
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001204 return None
1205
1206
1207def save_cl_data(status, commit_key, cl):
1208 """Save the gerrit instance and CL number to the yaml file
1209
1210 Args:
1211 status: variant_status object tracking our board, variant, etc.
1212 commit_key: Which key in the commits map we're processing
1213 cl: Value returned by find_change_id, should be a tuple
1214 of instance_name, cl_number
1215 """
1216 instance_name, cl_number = cl
1217 print(f'Found ({instance_name}, {cl_number}), saving to yaml')
1218 status.commits[commit_key]['gerrit'] = instance_name
1219 status.commits[commit_key]['cl_number'] = cl_number
1220 status.save()
1221
1222
1223def repo_upload(branch_name, cwd):
1224 """Upload a branch to gerrit
1225
1226 This function runs `repo upload` in the specified directory to upload
1227 a branch to gerrit. Because it's operating in a directory and with a
1228 branch name, it could upload more than one commit, which is OK because
1229 we'll look for each commit by change-id before trying to upload in that
1230 directory. For example, this happens in Zork, where the cb_config step
1231 and the cras_config step both have a commit in src/overlays. When we're
1232 processing the cb_config step and we `repo upload` in src/overlays, it
1233 will also upload the commit for cras_config. Then we come around to the
1234 cras_config step, and since we can find a CL with the change-id, we don't
1235 try to upload again.
1236
1237 Args:
1238 branch_name: the name of the branch to upload. Gets passed to
1239 repo upload with the --br flag
1240 cwd: directory where we want to upload. Gets set as the working
1241 directory for executing repo upload.
1242
1243 Returns:
1244 True if repo upload exits with a successful error code, false otherwise
1245 """
Paul Fagerburg042a5252020-03-16 21:49:18 -06001246 return run_process(
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001247 ['repo',
1248 'upload',
1249 '.',
1250 '--br=' + branch_name,
1251 '--wip',
1252 '--verify',
Paul Fagerburgda6bc102020-08-31 19:27:04 -06001253 '--yes',
1254 '--hashtag=new_variant'],
Paul Fagerburg042a5252020-03-16 21:49:18 -06001255 cwd=cwd)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001256
1257
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001258def upload_CLs(status):
1259 """Upload all CLs to chromiumos
1260
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001261 Args:
1262 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001263
1264 Returns:
1265 True if the build succeeded, False if something failed
1266 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001267 logging.info('Running step upload_CLs')
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001268
1269 for commit_key in status.repo_upload_list:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001270 logging.debug('Processing key %s', commit_key)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001271 commit = status.commits[commit_key]
1272 if 'gerrit' not in commit or 'cl_number' not in commit:
1273 change_id = commit['change_id']
1274 cl = find_change_id(change_id)
1275 if cl is not None:
1276 save_cl_data(status, commit_key, cl)
1277 else:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001278 logging.debug('Not found %s, need to upload', change_id)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001279 if not repo_upload(commit['branch_name'], commit['dir']):
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001280 logging.error('Repo upload %s in %s failed!',
1281 commit['branch_name'],
1282 commit['dir'])
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001283 return False
1284 cl = find_change_id(change_id)
1285 if cl is None:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001286 logging.error(
1287 'repo upload %s succeeded, but change_id is not found!',
1288 commit_key)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001289 return False
1290 save_cl_data(status, commit_key, cl)
1291 else:
1292 instance_name = commit['gerrit']
1293 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001294 logging.debug('Already uploaded (%s, %s)', instance_name, cl_number)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001295
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001296 return True
1297
1298
1299def find_coreboot_upstream(status):
Paul Fagerburgba508a02020-11-20 14:40:55 -07001300 """Find the upstream coreboot CL in chromiumos
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001301
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001302 When the coreboot variant CL is first uploaded to review.coreboot.org,
1303 it is not visible in the chromiumos tree (and also cannot be used as
Paul Fagerburgba508a02020-11-20 14:40:55 -07001304 a target for cq-depend). There is a process for upstream CLs from
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001305 coreboot after they have been reviewed, approved, and merged. We can
1306 track a specific coreboot CL if we know the change-id that it used on
1307 the coreboot gerrit instance, by looking for that change-id as
1308 'original-change-id' in the public chromium gerrit instance.
1309
1310 The change-id for the coreboot variant will be under the 'cb_variant' key,
1311 but this is for the 'coreboot' gerrit instance.
1312
1313 When we find the upstreamed CL, we will record the gerrit instance and
1314 CL number in the yaml file under the 'find' key ("find upstream coreboot")
1315 so that we don't need to search coreboot again.
1316
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001317 Args:
1318 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001319
1320 Returns:
1321 True if the build succeeded, False if something failed
1322 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001323 logging.info('Running step find_coreboot_upstream')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001324
1325 # If we have already found the upstream coreboot CL, then exit with success
1326 if step_names.FIND in status.commits:
1327 commit = status.commits[step_names.FIND]
1328 if 'gerrit' in commit and 'cl_number' in commit:
1329 instance_name = commit['gerrit']
1330 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001331 logging.debug('Already found (%s, %s)', instance_name, cl_number)
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001332 return True
1333
1334 # Make sure we have a CB_VARIANT commit and a change_id for it
1335 if step_names.CB_VARIANT not in status.commits:
1336 logging.error('Key %s not found in status.commits',
1337 step_names.CB_VARIANT)
1338 return False
1339 if 'change_id' not in status.commits[step_names.CB_VARIANT]:
1340 logging.error('Key change_id not found in status.commits[%s]',
1341 step_names.CB_VARIANT)
1342 return False
1343
1344 # Find the CL by the Original-Change-Id
1345 original_change_id = status.commits[step_names.CB_VARIANT]['change_id']
1346 gerrit_query_args = {
1347 'Original-Change-Id': original_change_id
1348 }
1349 cros = gerrit.GetCrosExternal()
1350 upstream = cros.Query(**gerrit_query_args)
1351 # If nothing is found, the patch hasn't been upstreamed yet
1352 if not upstream:
Paul Fagerburgba508a02020-11-20 14:40:55 -07001353 logging.error('Program cannot continue without upstream coreboot CL.')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001354 logging.error('(coreboot:%s, change-id %s)',
1355 status.commits[step_names.CB_VARIANT]['cl_number'],
1356 status.commits[step_names.CB_VARIANT]['change_id'])
Paul Fagerburgba508a02020-11-20 14:40:55 -07001357 logging.error('Please wait for the upstream CL, then run this program'
1358 ' again with --continue')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001359 return False
1360
1361 # If more than one CL is found, something is very wrong
1362 if len(upstream) != 1:
1363 logging.error('More than one CL was found with Original-Change-Id %s',
1364 original_change_id)
1365 return False
1366
1367 # At this point, we know there is only one CL and we can get the
1368 # repo and CL number by splitting on the colon between them.
1369 patchlink = upstream[0].PatchLink()
1370 instance_name, cl_number = patchlink.split(':')
1371
1372 # Can't use get_git_commit_data because we're not pulling this
1373 # information from a git commit, but rather from gerrit.
1374 # We only need the gerrit instance and the CL number so we can have
1375 # other CLs cq-depend on this CL. The other keys are not needed because:
1376 # dir - not needed because we're not going to `cd` there to `repo upload`
1377 # branch_name - not valid; the CL is already merged
1378 # change_id - we use the change_id to find a CL number, and since we
1379 # just found the CL number via original-change-id, this is moot.
1380 status.commits[step_names.FIND] = {
1381 'gerrit': instance_name,
1382 'cl_number': str(cl_number)
1383 }
1384
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001385 return True
1386
1387
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001388def calc_cq_depend(status):
1389 """Determine the list of CLs for each commit that has dependencies.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001390
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001391 status.depends is a map of dependencies from step name to a list of
1392 steps that the step depends on. For each step, find the SHA of the
1393 commit, then find the gerrit instance and CL number of the commits
1394 that it depends on. Construct the Cq-Depends list and save it under
1395 the 'cq_depend' key, i.e. commit['add_priv_yaml']['cq_depend'] or
1396 as it will be stored in the yaml:
1397 commits:
1398 add_priv_yaml:
1399 cq_depend: 'chromium:1629121, chromium:1638243'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001400
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001401 Args:
1402 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001403
1404 Returns:
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001405 True if all dependencies have been calculated. False if something
1406 failed, usually a commit not found by Change-Id.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001407 """
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001408 logging.info('Running step calc_cq_depend')
1409 # Iterate through the commits that have dependencies.
1410 for key in status.depends:
1411 logging.debug('Processing %s to add dependencies', key)
1412 # For every commit that has dependencies, find the gerrit instance
1413 # and CL number of the dependencies.
1414 cq_depend_list = []
1415 for depend_key in status.depends[key]:
1416 depend_commit = status.commits[depend_key]
1417 if not 'gerrit' in depend_commit:
1418 logging.error('Commit %s does not have a gerrit instance',
1419 depend_key)
1420 return False
1421 if not 'cl_number' in depend_commit:
1422 logging.error('Commit %s does not have a CL number',
1423 depend_key)
1424 return False
1425
1426 instance_name = depend_commit['gerrit']
1427 cl_number = depend_commit['cl_number']
1428 cq_depend_list.append(f'{instance_name}:{cl_number}')
1429
1430 # Add the 'cq_depend' key to the commit.
1431 cq_depend_str = 'Cq-Depend: %s' % ', '.join(cq_depend_list)
1432 logging.debug('Add to commit %s %s', key, cq_depend_str)
1433 status.commits[key]['cq_depend'] = cq_depend_str
1434
1435 return True
1436
1437
1438def add_cq_depend_to_commit_msg(git_repo, change_id, cq_depend_str):
1439 """Update the commit message with a Cq-Depends line.
1440
1441 Find the SHA of the commit, then use git filter-branch --msg-filter
1442 to add the Cq-Depend line just before the Change-Id line. See
1443 https://chromium.googlesource.com/chromiumos/docs/+/HEAD/contributing.md#cq-depend
1444 for details about Cq-Depend format and location.
1445
1446 Args:
1447 git_repo: Directory of git repository.
1448 change_id: The Change-Id to search for.
1449 cq_depend_str: The Cq-Depend string. It must be in the correct format
1450 per chromeos documentation, ready to insert into the commit msg
1451 on the line before Change-Id.
1452
1453 Returns:
1454 True if `git filter-branch` was successful. False if the command
1455 failed.
1456 """
1457 logging.debug('find SHA of Change-Id %s in %s', change_id, git_repo)
1458 sha = change_id_to_sha(git_repo, change_id)
1459 if sha is None:
1460 logging.error('Cannot find the SHA for Change-Id %s in %s',
1461 change_id, git_repo)
1462 return False
1463 logging.debug('SHA = %s', sha)
1464
1465 # Check if the commit message already has a Cq-Depend line.
1466 msg = get_commit_msg(git_repo, sha)
1467 if any('Cq-Depend' in tmpstr for tmpstr in msg):
1468 logging.debug('Already has Cq-Depend')
1469 return True
1470
1471 # Use git filter-branch --msg-filter to add the Cq-Depend line just
1472 # before the Change-Id line.
1473 environ = {**os.environ, 'FILTER_BRANCH_SQUELCH_WARNING': '1'}
1474 cmd = [
1475 'git',
1476 'filter-branch',
Paul Fagerburg5f6d2012020-06-24 11:29:36 -06001477 '-f',
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001478 '--msg-filter',
1479 f'sed -E "s/^(Change-Id: {change_id})$/{cq_depend_str}\\n\\1/"',
1480 '--',
1481 f'{sha}^..']
1482 return run_process(cmd, cwd=git_repo, env=environ)
1483
1484
1485def add_cq_depend(status):
1486 """Add Cq-Depend to commits and flag them for re-upload.
1487
1488 Args:
1489 status: variant_status object tracking our board, variant, etc.
1490
1491 Returns:
1492 True if the commit messages have been successfully amended, False if
1493 something failed.
1494 """
1495 logging.info('Running step add_cq_depend')
1496 for key in status.commits:
1497 commit = status.commits[key]
1498 if 'cq_depend' in commit:
1499 logging.debug('%s has %s', key, commit['cq_depend'])
1500 # Make sure the commit has a working directory and a change_id
1501 # before trying to amend its commit message.
1502 if 'dir' not in commit or 'change_id' not in commit:
1503 logging.error('Missing dir and/or change_id from %s', key)
1504 return False
1505
1506 if not add_cq_depend_to_commit_msg(commit['dir'],
1507 commit['change_id'],
1508 commit['cq_depend']):
1509 return False
1510 commit['needs_re_upload'] = True
1511 else:
1512 logging.debug('%s no dependencies', key)
1513
1514 return True
1515
1516
1517def re_upload(status):
1518 """Re-upload commits that have changed.
1519
1520 Args:
1521 status: variant_status object tracking our board, variant, etc.
1522
1523 Returns:
1524 True if the uploads succeeded. False if a repo upload failed.
1525 """
1526 logging.info('Running step re_upload')
1527 for key in status.commits:
1528 commit = status.commits[key]
1529 if commit.get('needs_re_upload'):
1530 logging.debug('Re-upload branch %s in %s', commit['branch_name'],
1531 commit['dir'])
1532 if not repo_upload(commit['branch_name'], commit['dir']):
1533 logging.error('Repo upload %s in %s failed!',
1534 commit['branch_name'],
1535 commit['dir'])
1536 return False
1537 commit['needs_re_upload'] = False
1538
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001539 return True
1540
1541
1542def clean_up(status):
1543 """Final clean-up, including delete the status file
1544
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001545 Args:
1546 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001547
1548 Returns:
1549 True
1550 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001551 logging.info('Running step clean_up')
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001552 status.rm()
1553 return True
1554
1555
Paul Fagerburg042a5252020-03-16 21:49:18 -06001556def abort(status):
1557 """Abort the creation of a new variant by abandoning commits
1558
1559 When the user specifies the --abort flag, we override status.step to
1560 be 'abort' and there is no transition from 'abort' to anything else.
1561 We look at status.commits and for each key, see if we have already
1562 been in that directory and abandoned that specific branch. If not,
1563 abandon the commit and then add the branch+dir to a list of abandoned
1564 commits. We do this because some boards (such as Zork) can have multiple
1565 commits in the same directory and with the same branch name, and we only
1566 want to repo abandon that branch once.
1567
1568 Args:
1569 status: variant_status object tracking our board, variant, etc.
1570
1571 Returns:
1572 True
1573 """
1574 logging.info('Running step abort')
1575 # Use the set 'abandoned' to keep track of each branch+dir abandoned.
1576 abandoned = set()
1577 for step in status.commits:
1578 logging.debug('Processing step %s', step)
1579 commit = status.commits[step]
1580 branch = commit['branch_name']
1581 cwd = commit['dir']
1582 if (branch, cwd) in abandoned:
1583 logging.debug('Branch %s in directory %s already abandoned',
1584 branch, cwd)
1585 else:
1586 logging.info('Abandoning branch %s in directory %s',
1587 branch, cwd)
1588 if run_process(['repo', 'abandon', branch, '.'], cwd=cwd):
1589 abandoned.add((branch, cwd))
1590 else:
1591 logging.error('Error while abandoning branch %s', branch)
1592 return False
1593
1594 return True
1595
1596
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001597if __name__ == '__main__':
1598 sys.exit(not int(main()))