blob: 96e193ff01843fafefb9db874da9c2d76b659d6d [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 Fagerburge868e832020-01-22 17:14:04 -070034The program has support for multiple reference boards, so the repos, directories,
35and scripts above can change depending on what the reference board is.
Paul Fagerburg3b534f92019-11-07 15:05:22 -070036"""
37
38from __future__ import print_function
39import argparse
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070040import importlib
Paul Fagerburg8d850932020-02-25 14:13:32 -070041import json
Paul Fagerburg3b534f92019-11-07 15:05:22 -070042import logging
43import os
Paul Fagerburg3b534f92019-11-07 15:05:22 -070044import subprocess
45import sys
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -060046from chromite.lib import build_target_lib
47from chromite.lib import cros_build_lib
Paul Fagerburg1d043c32020-02-03 08:57:08 -070048from chromite.lib import git
Paul Fagerburga8c7e342020-02-25 13:30:49 -070049from chromite.lib import gerrit
Paul Fagerburga95dd162020-03-24 16:27:18 -060050from chromite.lib import osutils
Paul Fagerburg75398072020-03-16 13:51:24 -060051from chromite.lib import workon_helper
Paul Fagerburg8d850932020-02-25 14:13:32 -070052import requests
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070053import step_names
Paul Fagerburg3b534f92019-11-07 15:05:22 -070054import variant_status
55
56
57def main():
Paul Fagerburge868e832020-01-22 17:14:04 -070058 """Create a new variant of an existing reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -070059
60 This program automates the creation of a new variant of an existing
Paul Fagerburge868e832020-01-22 17:14:04 -070061 reference board by calling various scripts that copy the reference board,
62 modify files for the new variant, stage commits, and upload to gerrit.
Paul Fagerburg3b534f92019-11-07 15:05:22 -070063
64 Note that one of the following is required:
65 * --continue
66 * --board=BOARD --variant=VARIANT [--bug=BUG]
67 """
Paul Fagerburg042a5252020-03-16 21:49:18 -060068 board, variant, bug, continue_flag, abort_flag = get_args()
Paul Fagerburg3b534f92019-11-07 15:05:22 -070069
Paul Fagerburg042a5252020-03-16 21:49:18 -060070 if not check_flags(board, variant, bug, continue_flag, abort_flag):
Paul Fagerburg3b534f92019-11-07 15:05:22 -070071 return False
72
Paul Fagerburg042a5252020-03-16 21:49:18 -060073 status = get_status(board, variant, bug, continue_flag, abort_flag)
Paul Fagerburg3b534f92019-11-07 15:05:22 -070074 if status is None:
75 return False
76
77 status.load()
78
Paul Fagerburg5f794bf2020-02-12 13:01:36 -070079 # Where is new_variant.py located?
80 status.my_loc = os.path.dirname(os.path.abspath(__file__))
81
Paul Fagerburg042a5252020-03-16 21:49:18 -060082 # If the user specified --abort, override the current step.
83 if abort_flag:
84 status.step = step_names.ABORT
85
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070086 while status.step is not None:
Paul Fagerburg3b534f92019-11-07 15:05:22 -070087 status.save()
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070088 if not perform_step(status):
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -060089 logging.debug('perform_step %s returned False; exiting ...',
90 status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -070091 return False
92
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070093 move_to_next_step(status)
Paul Fagerburg3b534f92019-11-07 15:05:22 -070094
95 return True
96
97
98def get_args():
99 """Parse the command-line arguments
100
101 There doesn't appear to be a way to specify that --continue is
102 mutually exclusive with --board, --variant, and --bug. As a result,
103 all arguments are optional, and another function will apply the logic
104 to check if there is an illegal combination of arguments.
105
106 Returns a list of:
Paul Fagerburge868e832020-01-22 17:14:04 -0700107 board Name of the reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700108 variant Name of the variant being created
109 bug Text for bug number, if any ('None' otherwise)
110 continue_flag Flag if --continue was specified
111 """
112 parser = argparse.ArgumentParser(
113 description=main.__doc__,
114 formatter_class=argparse.RawTextHelpFormatter)
Paul Fagerburge868e832020-01-22 17:14:04 -0700115 parser.add_argument('--board', type=str, help='Name of the reference board')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700116 parser.add_argument(
117 '--variant', type=str, help='Name of the new variant to create')
118 parser.add_argument(
119 '--bug', type=str, help='Bug number to reference in commits')
Paul Fagerburg2e48a192020-03-24 17:57:05 -0600120 # Use a group so that we can enforce mutually-exclusive argurments.
121 # argparse does not support nesting groups, so we can't put board,
122 # variant, and bug into a group and have that group as another mutually
123 # exclusive option.
124 group = parser.add_mutually_exclusive_group()
125 group.add_argument(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700126 '--continue', action='store_true',
127 dest='continue_flag', help='Continue the process from where it paused')
Paul Fagerburg2e48a192020-03-24 17:57:05 -0600128 group.add_argument(
Paul Fagerburg042a5252020-03-16 21:49:18 -0600129 '--abort', action='store_true',
130 dest='abort_flag', help='Cancel the process and abandon all commits')
131 parser.add_argument(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700132 '--verbose', action='store_true',
133 dest='verbose_flag', help='Enable verbose output of progress')
134 args = parser.parse_args()
135
136 if args.verbose_flag:
137 logging.basicConfig(level=logging.DEBUG)
138 else:
139 logging.basicConfig(level=logging.INFO)
140
141 board = args.board
142 if board is not None:
143 board = board.lower()
144
145 variant = args.variant
146 if variant is not None:
147 variant = variant.lower()
148
149 bug = args.bug or 'None'
150
Paul Fagerburg042a5252020-03-16 21:49:18 -0600151 return (board, variant, bug, args.continue_flag, args.abort_flag)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700152
153
Paul Fagerburg042a5252020-03-16 21:49:18 -0600154def check_flags(board, variant, bug, continue_flag, abort_flag):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700155 """Check the flags to ensure no invalid combinations
156
157 We allow any of the following:
Paul Fagerburg042a5252020-03-16 21:49:18 -0600158 --abort
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700159 --continue
160 --board=board_name --variant=variant_name
161 --board=board_name --variant=variant_name --bug=bug_text
162
163 The argument parser does have the functionality to represent the
164 combination of --board and --variant as a single mutually-exclusive
165 argument, so we have to use this function to do the checking.
166
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700167 Args:
168 board: Name of the reference board
169 variant: Name of the variant being created
170 bug: Text for bug number, if any ('None' otherwise)
171 continue_flag: Flag if --continue was specified
Paul Fagerburg042a5252020-03-16 21:49:18 -0600172 abort_flag: Flag if --abort was specified
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700173
174 Returns:
175 True if the arguments are acceptable, False otherwise
176 """
Paul Fagerburg042a5252020-03-16 21:49:18 -0600177 # If either --abort or --continue is set, then disallow any of the
178 # board name, variant name, or bug number to be set.
179 if continue_flag or abort_flag:
180 if board is not None or variant is not None or bug != 'None':
181 logging.error('Do not use --board, --variant, or --bug with '
182 '--continue or --abort')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700183 return False
Paul Fagerburg042a5252020-03-16 21:49:18 -0600184 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700185
Paul Fagerburg042a5252020-03-16 21:49:18 -0600186 # At this point, neither --continue nor --abort are set, so we must have
187 # both --board and --variant values.
188 if board is None or variant is None:
189 logging.error('Both --board and --variant must be specified')
190 return False
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700191
192 return True
193
194
Paul Fagerburg042a5252020-03-16 21:49:18 -0600195def get_status(board, variant, bug, continue_flag, abort_flag):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700196 """Create the status file or get the previous status
197
198 This program can stop at several places as we have to wait for CLs
199 to work through CQ or be upstreamed into the chromiumos tree, so just
200 like a git cherry-pick, there is a --continue option to pick up where
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700201 you left off by reading a specially-named status file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700202
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700203 If --continue is specified, the status file must exist.
204 If the status file exists, then --continue must be specified.
205 When --continue is specified, we read the status file and return
206 with the contents.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700207
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700208 If the status file does not exist, we will create the state file with
209 the board, variant, and (optional) bug details.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700210
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700211 To decouple the list of boards supported from this main program, we
Paul Fagerburge868e832020-01-22 17:14:04 -0700212 try to import a module with the same name as the reference board,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700213 so --board=hatch means that we import hatch.py. If we can't import
Paul Fagerburge868e832020-01-22 17:14:04 -0700214 the file, then we don't support that reference board.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700215
216 The board-specific module will set several variables, which we will
217 copy into the object that we return.
218
Paul Fagerburge868e832020-01-22 17:14:04 -0700219 * base - the name of the base board, such as Hatch, Volteer, or Zork.
220 This can be different from the reference board, e.g. the Trembyle
221 reference board in the Zork project.
Paul Fagerburg4d343452020-01-24 15:23:53 -0700222 * coreboot_dir - base directory for coreboot, usually third_party/coreboot
223 but could differ for processors that use a private repo
224 * cb_config_dir - base directory for coreboot configs, usually
225 third_party/chromiumos-overlay/sys-boot/coreboot/files/configs but
226 could differ for processors that use a private repo
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700227 * step_list - list of steps (named in step_names.py) to run in sequence
Paul Fagerburge868e832020-01-22 17:14:04 -0700228 to create the new variant of the reference board
229 * fsp - package name for FSP. This may be None, depending on the
230 processor on the reference board
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700231 * fitimage_pkg - package name for the fitimage
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700232 * fitimage_dir - directory for fitimage; prepend '/mnt/host/source/src/'
233 in chroot, prepend '~/chromiumos/src' outside the chroot
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700234 * workon_pkgs - list of packages to cros_workon
235 * emerge_cmd - the emerge command, e.g. 'emerge-hatch'
236 * emerge_pkgs - list of packages to emerge
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600237 * config_workon_pkgs - list of packages to cros_workon to build the
238 project config
239 * config_emerge_pkgs - list of packages to emerge to build the project
240 config
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700241 * private_yaml_dir - directory for the private yaml file
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700242 * commits - map of commits for the various steps. Indexed by step name,
243 and the step names used are the same ones in step_names.py
244 * repo_upload_list - list of commits to upload using `repo upload`
245 * coreboot_push_list - list of commits to upload using `git push` to
246 coreboot
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600247 * depends - maps a step to a list of steps on which it depends, e.g.
248 depends[step_names.ADD_PRIV_YAML] is a list of other steps that
249 the 'add_priv_yaml' step depends on. This map is used to amend
250 the commits with CL numbers for Cq-Depend.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700251
252 Additionally, the following fields will be set:
253
Paul Fagerburge868e832020-01-22 17:14:04 -0700254 * board - the name of the reference board, e.g. 'hatch'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700255 * variant - the name of the variant, e.g. 'sushi'
256 * bug - optional text for a bug ID, used in the git commit messages.
257 Could be 'None' (as text, not the python None), or something like
258 'b:12345' for buganizer, or 'chromium:12345'
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700259 * step - internal state tracking, what step of the variant creation
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700260 we are at.
261 * yaml_file - internal, just the name of the file where all this data
262 gets saved.
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700263 * commit - a map of maps that tracks all of the git commit and gerrit CL
264 data for each of the steps in the process. For example,
265 status.commit['add_priv_yaml'] is a map that has all the information
266 about the 'add_priv_yaml' step. The keys in the maps allow us to
267 determine where the commit is, the change_id, if it has been uploaded
268 to gerrit and where.
269
270 branch_name - the name of the git branch
271 change_id - the change-id assigned by the commit hook. Gerrit
272 uses the change_id to track new patchsets in the CL
273 dir - the directory where the commit has been created
274 gerrit - the name of the gerrit instance to which the CL has
275 been uploaded, one of 'chromium', 'chrome-internal', or
276 'coreboot'
277 cl_number - the CL number on the gerrit instance
278
279 When the commit is created, branch_name, change_id, and dir are all
280 set. The gerrit and cl_number keys are not set until the CL has been
281 uploaded to a gerrit instance.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700282
283 These data might come from the status file (because we read it), or
284 they might be the initial values after we created the file (because
285 it did not already exist).
286
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700287 Args:
288 board: Name of the reference board
289 variant: Name of the variant being created
290 bug: Text for bug number, if any ('None' otherwise)
291 continue_flag: Flag if --continue was specified
Paul Fagerburg042a5252020-03-16 21:49:18 -0600292 abort_flag: Flag if --abort was specified
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700293
294 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700295 variant_status object with all the data mentioned above
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700296 """
297 status = variant_status.variant_status()
Paul Fagerburg042a5252020-03-16 21:49:18 -0600298 if continue_flag or abort_flag:
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700299 if status.yaml_file_exists():
Paul Fagerburg042a5252020-03-16 21:49:18 -0600300 return status
301 else:
302 if continue_flag:
303 op = '--continue'
304 if abort_flag:
305 op = '--abort'
306 logging.error('%s does not exist; cannot %s', status.yaml_file, op)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700307 return None
308
Paul Fagerburg042a5252020-03-16 21:49:18 -0600309 # If we get here, the user provided --board and --variant (because
310 # check_flags() returned Trued), but the yaml file already exists,
311 # so we print an error message and bail.
312 if status.yaml_file_exists():
313 logging.error(
314 'new_variant already in progress; did you forget --continue?')
315 return None
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700316
Paul Fagerburg042a5252020-03-16 21:49:18 -0600317 # At this point, it's not --continue, not --abort, the yaml file doesn't
318 # exist, and we have valid values for --board, --variant, and --bug (bug
319 # might be the default value of "None"). Create the yaml file with data
320 # from the reference board's loadable module.
321 status.board = board
322 status.variant = variant
323 status.bug = bug
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700324
Paul Fagerburg042a5252020-03-16 21:49:18 -0600325 # Load the appropriate module and copy all the data from it.
326 try:
327 module = importlib.import_module(board)
328 except ImportError:
329 print('Unsupported board "' + board + '"')
330 sys.exit(1)
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700331
Paul Fagerburg042a5252020-03-16 21:49:18 -0600332 # pylint: disable=bad-whitespace
333 # Allow extra spaces around = so that we can line things up nicely
334 status.base = module.base
335 status.coreboot_dir = module.coreboot_dir
336 status.cb_config_dir = module.cb_config_dir
337 status.emerge_cmd = module.emerge_cmd
338 status.emerge_pkgs = module.emerge_pkgs
339 status.fitimage_dir = module.fitimage_dir
340 status.fitimage_pkg = module.fitimage_pkg
341 status.fitimage_cmd = module.fitimage_cmd
342 status.fsp = module.fsp
343 status.private_yaml_dir = module.private_yaml_dir
344 status.step_list = module.step_list
345 status.workon_pkgs = module.workon_pkgs
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600346 status.config_workon_pkgs = module.config_workon_pkgs
347 status.config_emerge_pkgs = module.config_emerge_pkgs
Paul Fagerburg042a5252020-03-16 21:49:18 -0600348 status.coreboot_push_list = module.coreboot_push_list
349 status.repo_upload_list = module.repo_upload_list
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600350 status.depends = module.depends
Paul Fagerburg042a5252020-03-16 21:49:18 -0600351 # pylint: enable=bad-whitespace
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700352
Paul Fagerburg042a5252020-03-16 21:49:18 -0600353 # Start at the first entry in the step list
354 status.step = status.step_list[0]
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700355
Paul Fagerburg042a5252020-03-16 21:49:18 -0600356 # Start an empty map for tracking CL data
357 status.commits = {}
358
359 status.save()
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700360
361 return status
362
363
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700364def perform_step(status):
365 """Call the appropriate function for the current step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700366
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700367 Args:
368 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700369
370 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700371 True if the step succeeded, False if it failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700372 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700373 # Function to call based on the step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700374 dispatch = {
Paul Fagerburgbc184242020-04-28 17:00:54 -0600375 step_names.PROJECT_CONFIG: project_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700376 step_names.CB_VARIANT: create_coreboot_variant,
377 step_names.CB_CONFIG: create_coreboot_config,
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700378 step_names.CRAS_CONFIG: copy_cras_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700379 step_names.ADD_FIT: add_fitimage,
380 step_names.GEN_FIT: gen_fit_image_outside_chroot,
381 step_names.COMMIT_FIT: commit_fitimage,
382 step_names.EC_IMAGE: create_initial_ec_image,
383 step_names.EC_BUILDALL: ec_buildall,
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700384 step_names.ADD_PUB_YAML: add_variant_to_public_yaml,
385 step_names.ADD_PRIV_YAML: add_variant_to_private_yaml,
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600386 step_names.BUILD_CONFIG: build_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700387 step_names.EMERGE: emerge_all,
388 step_names.PUSH: push_coreboot,
389 step_names.UPLOAD: upload_CLs,
390 step_names.FIND: find_coreboot_upstream,
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600391 step_names.CALC_CQ_DEPEND: calc_cq_depend,
392 step_names.ADD_CQ_DEPEND: add_cq_depend,
393 step_names.RE_UPLOAD: re_upload,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700394 step_names.CLEAN_UP: clean_up,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600395 step_names.ABORT: abort,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700396 }
397
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700398 if status.step not in dispatch:
399 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700400 sys.exit(1)
401
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700402 return dispatch[status.step](status)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700403
404
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700405def move_to_next_step(status):
406 """Move to the next step in the list
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700407
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700408 Args:
409 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700410 """
Paul Fagerburg042a5252020-03-16 21:49:18 -0600411 # Special case: the next step after 'abort' is 'clean_up'. Always.
412 if status.step == step_names.ABORT:
413 status.step = step_names.CLEAN_UP
414 return
415
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700416 if status.step not in status.step_list:
417 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700418 sys.exit(1)
419
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700420 idx = status.step_list.index(status.step)
421 if idx == len(status.step_list)-1:
422 status.step = None
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700423 else:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700424 status.step = status.step_list[idx+1]
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700425
426
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700427def run_process(args, cwd=None, env=None, capture_output=False):
428 """Run a process, log debug messages, return text output of process
429
430 The capture_output parameter allows us to capture the output when we
431 care about it (and not sending it to the screen), or ignoring it when
432 we don't care, and letting the user see the output so they know that
433 the build is still running, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700434
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700435 Args:
436 args: List of the command and its params
437 cwd: If not None, cd to this directory before running
438 env: Environment to use for execution; if needed, get os.environ.copy()
439 and add variables. If None, just use the current environment
440 capture_output: True if we should capture the stdout, false if we
441 just care about success or not.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700442
443 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700444 If capture_output == True, we return the text output from running
445 the subprocess as a list of lines, or None if the process failed.
446 If capture_output == False, we return a True if it successed, or
447 None if the process failed.
448
449 The caller can evaluate as a bool, because bool(None) == False, and
450 bool() of a non-empty list is True, or the caller can use the returned
451 text for further processing.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700452 """
453 logging.debug('Run %s', str(args))
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700454 if cwd is not None:
455 logging.debug('cwd = %s', cwd)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700456 try:
457 if capture_output:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700458 output = subprocess.run(args, cwd=cwd, env=env, check=True,
459 stderr=subprocess.STDOUT, stdout=subprocess.PIPE).stdout
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700460 else:
461 subprocess.run(args, cwd=cwd, env=env, check=True)
462 # Just something to decode so we don't get an empty list
463 output = b'True'
464
465 logging.debug('process returns 0')
466 # Convert from byte string to ASCII
467 decoded = output.decode('utf-8')
468 # Split into array of individual lines
469 lines = decoded.split('\n')
470 return lines
471 except subprocess.CalledProcessError as err:
472 logging.debug('process returns %s', str(err.returncode))
473 return None
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700474
475
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700476def get_git_commit_data(cwd):
477 """Get the branch name and change id of the current commit
478
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700479 Args:
480 cwd: The current working directory, where we want to get the branch
481 name and change id
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700482
483 Returns:
484 Map with 'dir', 'branch_name' and 'change_id' keys. The 'dir'
485 key maps to the value of os.path.expanduser(cwd)
486 """
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700487 cwd = git.FindGitTopLevel(os.path.expanduser(cwd))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700488 logging.debug('get_git_commit_data(%s)', cwd)
489
490 branch_name = git.GetCurrentBranch(cwd)
491 if branch_name is None:
492 logging.error('Cannot determine git branch name in %s; exiting', cwd)
493 sys.exit(1)
494 logging.debug('git current branch is %s', branch_name)
495
496 change_id = git.GetChangeId(cwd)
497 if change_id is None:
498 logging.error('Cannot determine Change-Id in %s; exiting', cwd)
499 sys.exit(1)
500 logging.debug('git Change-Id is %s', change_id)
501
502 return {
503 'dir': cwd,
504 'branch_name': branch_name,
505 'change_id': change_id
506 }
507
508
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600509def change_id_to_sha(git_repo, change_id):
510 """Find the SHA for a given Change-Id.
511
512 Args:
513 git_repo: Directory of git repository.
514 change_id: The Change-Id to search for.
515
516 Returns:
517 The SHA hash for the Change-Id if only one commit is found.
518 None if the Change-Id was not found.
519 Raises a ValueError if more than one commit is found with the
520 same Change-Id.
521 """
522 output = git.Log(git_repo, max_count=1, format='format:%H',
523 grep=fr'^Change-Id: {change_id}$')
524 sha_hashes = output.splitlines()
525 if not sha_hashes:
526 return None
527 if len(sha_hashes) > 1:
528 raise ValueError('More than one SHA with that Change-Id found')
529 return sha_hashes[0]
530
531
532def get_commit_msg(git_repo, rev):
533 """Get the commit message for a given revision.
534
535 Because git.Log doesn't allow specifying check=False or getting the
536 returncode, we have to catch the CalledProcessError instead.
537
538 Args:
539 git_repo: Directory of git repository.
540 rev: The revision to search for, a SHA or a label.
541
542 Returns:
543 The commit message as a list of strings, if the revision exists.
544 None if the revision was not found.
545 """
546 try:
547 msg = git.Log(git_repo, max_count=1, format='format:%B', rev=rev)
548 return msg.splitlines()
549 except cros_build_lib.CalledProcessError:
550 raise ValueError('SHA was not found')
551
552
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600553def emerge_with_workon(status, workon_pkgs, emerge_cmd, emerge_pkgs, env=None):
554 """Emerge a list of packages after `cros_workon start`ing them
555
556 This function will `cros_workon start` a list of packages, then `emerge`
557 another list of packages, and finally, `cros_workon stop` only those
558 packages that were actually started by the `cros_workon start` command.
559 Any package already in a `cros_workon start` state prior to this function
560 will still be in that state when this function exits.
561
562 To determine which packages this program started and which ones were
563 already started, we query the list of packages being worked on, then
564 cros_workon start the entire list (which will produce a "package already
565 being worked on" type of message for anything already started), and then
566 query the list of packages being worked on again. The difference between
567 the before and after lists are the packages that this program started,
568 and so that's the list of packages to cros_workon stop after the emerge
569 is done.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700570
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700571 Args:
572 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600573 workon_pkgs: list of packages to `cros_workon start`
574 emerge_cmd: the emerge command to run, e.g. 'emerge-volteer'
575 emerge_pkgs: list of packages to `emerge`
576 env: environment to pass to run_process, or None to pass default
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700577
578 Returns:
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600579 True if everything succeeded, False if something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700580 """
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600581 # Get the list of packages that are already cros_workon started.
582 build_target = build_target_lib.BuildTarget(status.base)
583 workon = workon_helper.WorkonHelper(build_target.root, build_target.name)
584 before_workon = workon.ListAtoms()
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700585
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600586 workon.StartWorkingOnPackages(workon_pkgs)
587
588 # Determine which packages we need to cros_workon stop.
589 after_workon = workon.ListAtoms()
590 stop_packages = list(set(after_workon) - set(before_workon))
591
592 # Run the emerge command.
593 emerge_result = run_process([emerge_cmd] + emerge_pkgs, env=env)
594
595 # cros_workon stop before returning the result.
596 workon.StopWorkingOnPackages(stop_packages)
597
598 return emerge_result
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700599
600
Paul Fagerburgbc184242020-04-28 17:00:54 -0600601def project_config(status):
602 """Check if the project config is correct and complete
603
604 For programs that use the new project/config structure with starlark
605 configuration files, this function will check that emerging the
606 project's configuration will result in a project-config.json that
607 includes the new name of the new hwdesign (a.k.a. "variant").
608
609 Args:
610 status: variant_status object tracking our board, variant, etc.
611
612 Returns:
613 True if everything succeeded, False if something failed
614 """
615 logging.info('Running step project_config')
616 try:
617 if not emerge_with_workon(status, status.config_workon_pkgs,
618 status.emerge_cmd, status.config_emerge_pkgs):
619 raise RuntimeError(f'Building the configuration failed.')
620
621 # Make sure project-config.json exists in the /build tree
622 emerged_json = os.path.join(
623 '/build',
624 status.base,
625 'usr/share/chromeos-config/yaml/project-config.json')
626 if not os.path.isfile(emerged_json):
627 raise RuntimeError(
628 f'project-config.json {emerged_json} does not exist.')
629
630 # Search emerged_json to see if the new variant's name shows up there.
631 if not status.variant in osutils.ReadFile(emerged_json):
632 raise RuntimeError(
633 f'variant name {status.variant} not found in {emerged_json}')
634
635 except RuntimeError as e:
636 logging.error(str(e))
637 logging.error('Please file a bug in Infra > ChromeOS > Product > Device'
638 ' to have the project configuration updated.')
639 logging.error('(https://bugs.chromium.org/p/chromium/issues/list?'
640 'q=component:Infra%3EChromeOS%3EProduct%3EDevice)')
641 return False
642
643 return True
644
645
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700646def create_coreboot_variant(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700647 """Create source files for a new variant of the reference board in coreboot
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700648
649 This function calls create_coreboot_variant.sh to set up a new variant
Paul Fagerburge868e832020-01-22 17:14:04 -0700650 of the reference board.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700651
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700652 Args:
653 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700654
655 Returns:
656 True if everything succeeded, False if something failed
657 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700658 logging.info('Running step create_coreboot_variant')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700659 cb_src_dir = os.path.join('/mnt/host/source/src/', status.coreboot_dir)
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600660 environ = {**os.environ, 'CB_SRC_DIR': cb_src_dir}
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700661 create_coreboot_variant_sh = os.path.join(status.my_loc,
662 'create_coreboot_variant.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600663 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700664 [create_coreboot_variant_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700665 status.base,
Paul Fagerburgabb15622020-02-07 15:41:29 -0700666 status.board,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700667 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600668 status.bug], env=environ)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700669 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700670 status.commits[step_names.CB_VARIANT] = get_git_commit_data(cb_src_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700671 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700672
673
674def create_coreboot_config(status):
675 """Create a coreboot configuration for a new variant
676
677 This function calls create_coreboot_config.sh, which will make a copy
678 of coreboot.${BOARD} into coreboot.${VARIANT}.
679
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700680 Args:
681 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700682
683 Returns:
684 True if the script and test build succeeded, False if something failed
685 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700686 logging.info('Running step create_coreboot_config')
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600687 # Only set CB_CONFIG_DIR if it's not None, so here we have to copy
688 # the environment first and then optionally add a key.
Paul Fagerburg4d343452020-01-24 15:23:53 -0700689 environ = os.environ.copy()
690 if status.cb_config_dir is not None:
691 environ['CB_CONFIG_DIR'] = status.cb_config_dir
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700692 create_coreboot_config_sh = os.path.join(status.my_loc,
693 'create_coreboot_config.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600694 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700695 [create_coreboot_config_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700696 status.base,
697 status.board,
698 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600699 status.bug], env=environ)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700700 if rc:
701 # Use status.cb_config_dir if defined, or if not, use
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700702 # '/mnt/host/source/src/third_party/chromiumos-overlay'
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700703 if status.cb_config_dir is not None:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700704 cb_config_dir = os.path.join('/mnt/host/source/src/', status.cb_config_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700705 else:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700706 cb_config_dir = '/mnt/host/source/src/third_party/chromiumos-overlay'
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700707 status.commits[step_names.CB_CONFIG] = get_git_commit_data(cb_config_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700708 return rc
Paul Fagerburge868e832020-01-22 17:14:04 -0700709
710
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700711def copy_cras_config(status):
712 """Copy the cras config for a new variant
713
714 This is only necessary for the Zork baseboard right now.
715 This function calls copy_cras_config.sh, which will copy the
716 cras config in
717 overlays/overlay-${BASE}/chromeos-base/chromeos-bsp-${BASE}/files/cras-config/${BASE}
718 to .../${VARIANT}
719
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700720 Args:
721 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700722
723 Returns:
724 True if the script and test build succeeded, False if something failed
725 """
726 logging.info('Running step copy_cras_config')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700727 copy_cras_config_sh = os.path.join(status.my_loc, 'copy_cras_config.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600728 rc = run_process(
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700729 [copy_cras_config_sh,
730 status.base,
731 status.board,
732 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600733 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700734 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700735 status.commits[step_names.CRAS_CONFIG] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700736 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700737 return rc
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700738
739
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700740def add_fitimage(status):
741 """Add the source files for a fitimage for the new variant
742
743 This function calls add_fitimage.sh to create a new XSL file for the
Paul Fagerburge868e832020-01-22 17:14:04 -0700744 variant's fitimage, which can override settings from the reference board's
745 XSL. When this is done, the user will have to build the fitimage by running
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700746 gen_fit_image.sh outside of the chroot (and outside of this program's
747 control) because gen_fit_image.sh uses WINE, which is not installed in
748 the chroot. (There is a linux version of FIT, but it requires Open GL,
749 which is also not installed in the chroot.)
750
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700751 Args:
752 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700753
754 Returns:
755 True if the script succeeded, False otherwise
756 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700757 logging.info('Running step add_fitimage')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700758 add_fitimage_sh = os.path.expanduser(os.path.join(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700759 '/mnt/host/source/src', status.fitimage_dir, 'files/add_fitimage.sh'))
Paul Fagerburg042a5252020-03-16 21:49:18 -0600760 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700761 [add_fitimage_sh,
762 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600763 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700764 if rc:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700765 fitimage_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir)
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700766 status.commits[step_names.COMMIT_FIT] = get_git_commit_data(fitimage_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700767 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700768
769
770def gen_fit_image_outside_chroot(status):
771 """Tell the user to run gen_fit_image.sh outside the chroot
772
773 As noted for add_Fitimage(), gen_fit_image.sh cannot run inside the
774 chroot. This function tells the user to run gen_fit_image.sh in
775 their normal environment, and then come back (--continue) when that
776 is done.
777
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700778 Args:
779 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700780
781 Returns:
782 True
783 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700784 logging.info('Running step gen_fit_image_outside_chroot')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700785 fit_image_files = check_fit_image_files(status)
786 # If the list is empty, then `not` of the list is True, so the files
787 # we need are all present and we can continue.
788 if not fit_image_files:
789 return True
790
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700791 logging.error('The following files need to be generated:')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700792 for filename in fit_image_files:
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700793 logging.error('* %s', filename)
794 logging.error('The fitimage sources are ready for gen_fit_image.sh to process.')
795 logging.error('gen_fit_image.sh cannot run inside the chroot. Please open a new terminal')
796 logging.error('window, change to the directory where gen_fit_image.sh is located, and run')
797 logging.error(status.fitimage_cmd, status.variant)
798 logging.error('Then re-start this program with --continue.')
799 logging.error('If your chroot is based in ~/chromiumos, then the folder you want is')
800 logging.error('~/chromiumos/src/%s/asset_generation', status.fitimage_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700801 return False
802
803
804def check_fit_image_files(status):
805 """Check if the fitimage has been generated
806
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700807 This function is not called directly as a step, and so it doesn't need
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700808 to produce any error messages to the user (except with --verbose).
809 gen_fit_image_outside_chroot will call this function to see if the
810 fitimage files exist, and if not, then that function will print the
811 message about how the user needs to run gen_fit_image.sh outside the
812 chroot.
813
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700814 Args:
815 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700816
817 Returns:
818 List of files that *DO NOT* exist and need to be created, [] if
819 all files are present.
820 """
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700821 outputs_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir,
822 'asset_generation/outputs')
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700823 logging.debug('outputs_dir = "%s"', outputs_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700824
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600825 files_not_found = []
826 fitimage_bin = 'fitimage-' + status.variant + '.bin'
827 if not os.path.isfile(os.path.join(outputs_dir, fitimage_bin)):
828 files_not_found.append(fitimage_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700829
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600830 fitimage_versions = 'fitimage-' + status.variant + '-versions.txt'
831 if not os.path.isfile(os.path.join(outputs_dir, fitimage_versions)):
832 files_not_found.append(fitimage_versions)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700833
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600834 return files_not_found
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700835
836
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700837def commit_fitimage(status):
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600838 """Add the fitimage files to the git commit
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700839
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600840 This function calls commit_fitimage.sh to move the fitimage binary and
841 -versions files from asset_generation/outputs to files/ and then adds
842 those files and fit.log to the existing git commit.
843 Depending on the baseboard, there may be different file names (such
844 as fit-${VARIANT}.log for volteer) and/or additional files (such as
845 files/blobs/description-${VARIANT}.bin for volteer)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700846
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700847 Args:
848 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700849
850 Returns:
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600851 True if the script succeeded, False if something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700852 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700853 logging.info('Running step commit_fitimage')
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600854 commit_fitimage_sh = os.path.expanduser(os.path.join(
855 '/mnt/host/source/src', status.fitimage_dir, 'files/commit_fitimage.sh'))
856 return run_process([commit_fitimage_sh, status.variant])
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700857
858
859def create_initial_ec_image(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700860 """Create an EC image for the variant as a clone of the reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700861
862 This function calls create_initial_ec_image.sh, which will clone the
Paul Fagerburge868e832020-01-22 17:14:04 -0700863 reference board to create the variant. The shell script will build the
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700864 EC code for the variant, but the repo upload hook insists that we
865 have done a `make buildall` before it will allow an upload, so this
866 function does the buildall.
867
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700868 Args:
869 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700870
871 Returns:
872 True if the script and test build succeeded, False if something failed
873 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700874 logging.info('Running step create_initial_ec_image')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700875 create_initial_ec_image_sh = os.path.join(status.my_loc,
876 'create_initial_ec_image.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600877 if not run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700878 [create_initial_ec_image_sh,
879 status.board,
880 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600881 status.bug]):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700882 return False
883
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700884 # No need to `if rc:` because we already tested the run_process result above
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700885 status.commits[step_names.EC_IMAGE] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700886 '/mnt/host/source/src/platform/ec/board')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700887
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700888 # create_initial_ec_image.sh will build the ec.bin for this variant
889 # if successful.
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600890 ec_dir = '/mnt/host/source/src/platform/ec'
891 ec_bin = os.path.join(ec_dir, 'build', status.variant, 'ec.bin')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700892 logging.debug('ec.bin = "%s"', ec_bin)
893
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600894 if not os.path.isfile(ec_bin):
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700895 logging.error('EC binary %s not found', ec_bin)
896 return False
897 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700898
899
900def ec_buildall(status):
901 """Do a make buildall -j for the EC, which is required for repo upload
902
903 The upload hook checks to ensure that the entire EC codebase builds
904 without error, so we have to run make buildall -j before uploading.
905
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700906 Args:
907 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700908
909 Returns:
910 True if the script and test build succeeded, False if something failed
911 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700912 logging.info('Running step ec_buildall')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700913 del status # unused parameter
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700914 ec = '/mnt/host/source/src/platform/ec'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700915 logging.debug('ec = "%s"', ec)
Paul Fagerburg042a5252020-03-16 21:49:18 -0600916 return run_process(['make', 'buildall', '-j'], cwd=ec)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700917
918
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700919def add_variant_to_public_yaml(status):
920 """Add the new variant to the public model.yaml file
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700921
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700922 This function calls add_variant_to_yaml.sh to add the new variant to
923 the public model.yaml file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700924
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700925 Args:
926 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700927
928 Returns:
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700929 True if the script succeeded, False is something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700930 """
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700931 logging.info('Running step add_variant_to_public_yaml')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700932 add_variant_to_yaml_sh = os.path.join(status.my_loc,
933 'add_variant_to_yaml.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600934 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700935 [add_variant_to_yaml_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700936 status.base,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700937 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600938 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700939 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700940 status.commits[step_names.ADD_PUB_YAML] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700941 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700942 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700943
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700944
945def add_variant_to_private_yaml(status):
946 """Add the new variant to the private model.yaml file
947
948 This function calls add_variant.sh to add the new variant to
949 the private model.yaml file.
950
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700951 Args:
952 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700953
954 Returns:
955 True if the script succeeded, False is something failed
956 """
957 logging.info('Running step add_variant_to_private_yaml')
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700958 add_variant_sh = os.path.expanduser(os.path.join(status.private_yaml_dir, 'add_variant.sh'))
Paul Fagerburg042a5252020-03-16 21:49:18 -0600959 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700960 [add_variant_sh,
961 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600962 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700963 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700964 status.commits[step_names.ADD_PRIV_YAML] = get_git_commit_data(status.private_yaml_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700965 return rc
966
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700967
968
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600969def build_config(status):
970 """Build project config files, from yaml or starlark
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700971
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600972 This function builds the project config files that mosys and other tools
973 use, then verifies that the new variant's name shows up in all of the
974 output files. Depending on the baseboard, the input may be the model.yaml
975 files, or the starlark configuration files.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700976
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700977 Args:
978 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700979
980 Returns:
981 True if the scripts and build succeeded, False is something failed
982 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700983 logging.info('Running step build_yaml')
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600984 if not emerge_with_workon(status, status.config_workon_pkgs,
985 status.emerge_cmd, status.config_emerge_pkgs):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700986 return False
987
Paul Fagerburga95dd162020-03-24 16:27:18 -0600988 # Check the generated config.yaml file for occurences of the variant
989 # name to determine if the emerge was successful.
990 config_yaml = os.path.join(
991 '/build', status.base, 'usr/share/chromeos-config/yaml/config.yaml')
992 logging.debug('config_yaml = "%s"', config_yaml)
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600993 if not os.path.isfile(config_yaml):
Paul Fagerburga95dd162020-03-24 16:27:18 -0600994 logging.error('%s does not exist', config_yaml)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700995 return False
996
Paul Fagerburga95dd162020-03-24 16:27:18 -0600997 if not status.variant in osutils.ReadFile(config_yaml):
998 logging.error('variant name %s not found in yaml file %s',
999 status.variant, config_yaml)
1000 return False
1001
1002 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001003
1004
1005def emerge_all(status):
1006 """Build the coreboot BIOS and EC code for the new variant
1007
Paul Fagerburg75398072020-03-16 13:51:24 -06001008 This build step will cros_workon start a list of packages provided by
1009 the reference board data as status.workon_pkgs, then emerge a list of
1010 packages (status.emerge_pkgs), and then cros_workon stop any packages
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001011 that it started. Any packages that were already being worked on will
1012 not be stopped.
Paul Fagerburg75398072020-03-16 13:51:24 -06001013
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001014 Args:
1015 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001016
1017 Returns:
1018 True if the build succeeded, False if something failed
1019 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001020 logging.info('Running step emerge_all')
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001021 environ = {**os.environ, 'FW_NAME': status.variant}
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001022 if not emerge_with_workon(status, status.workon_pkgs,
1023 status.emerge_cmd, status.emerge_pkgs,
1024 env=environ):
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001025 return False
1026
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001027 # Check if the expected build outputs exist.
Paul Fagerburge868e832020-01-22 17:14:04 -07001028 build_path = '/build/' + status.base + '/firmware'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001029 logging.debug('build_path = "%s"', build_path)
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001030 image_bin = 'image-' + status.variant + '.bin'
1031 if not os.path.isfile(os.path.join(build_path, image_bin)):
1032 logging.error('emerge failed because %s does not exist', image_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001033 return False
1034
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001035 serial_bin = 'image-' + status.variant + '.serial.bin'
1036 if not os.path.isfile(os.path.join(build_path, serial_bin)):
1037 logging.error('emerge failed because %s does not exist', serial_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001038 return False
1039
1040 return True
1041
1042
1043def push_coreboot(status):
1044 """Push the coreboot CL to coreboot.org
1045
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001046 Args:
1047 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001048
1049 Returns:
1050 True if the build succeeded, False if something failed
1051 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001052 logging.info('Running step push_coreboot')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001053
1054 # Set up a return code that may change to False if we find that a
1055 # coreboot CL has not been uploaded.
1056 rc = True
1057
1058 for commit_key in status.coreboot_push_list:
1059 logging.debug(f'Processing key {commit_key}')
1060 commit = status.commits[commit_key]
1061 if 'gerrit' not in commit or 'cl_number' not in commit:
1062 change_id = commit['change_id']
1063 cl = find_change_id(change_id)
1064 if cl is not None:
1065 save_cl_data(status, commit_key, cl)
1066 else:
1067 logging.debug(f'Not found {change_id}, need to upload')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001068 logging.error('The following commit needs to be pushed to coreboot.org:')
1069 logging.error(' Branch "%s"', commit['branch_name'])
1070 logging.error(' in directory "%s"', commit['dir'])
1071 logging.error(' with change-id "%s"', commit['change_id'])
1072 logging.error('Please push the branch to review.coreboot.org, '
1073 'and then re-start this program with --continue')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001074 # Since this commit needs to be uploaded, do not continue after
1075 # this step returns.
1076 rc = False
1077 else:
1078 instance_name = commit['gerrit']
1079 cl_number = commit['cl_number']
1080 logging.debug(f'Already uploaded ({instance_name}, {cl_number})')
1081
1082 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001083
1084
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001085def query_gerrit(instance, change_id):
1086 """Search a gerrit instance for a specific change_id
1087
1088 Args:
1089 instance: gerrit instance to query. Suitable values come from
1090 gerrit.GetCrosInternal() and gerrit.GetCrosExternal()
1091 change_id: The change_id to search for
1092
1093 Returns:
1094 CL number if found, None if not
1095 """
1096 raw = instance.Query(change=change_id, raw=True)
1097 if raw:
1098 # If the CL was found by change_id, there will be only one,
1099 # because the change_id is used to recognize a new patchset
1100 # on an existing CL.
1101 return raw[0]['number']
1102
1103 return None
1104
1105
Paul Fagerburg8d850932020-02-25 14:13:32 -07001106def query_coreboot_gerrit(change_id):
1107 """Search the coreboot gerrit for a specific change_id
1108
1109 Use the REST API to look for the change_id. See
1110 https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html
1111 for details on the REST API to search for a change-id.
1112
1113 We can't use query_gerrit with a manually constructed GerritHelper
1114 because we need the user's private SSH key to access review.coreboot.org,
1115 but these are not available inside the chroot.
1116
1117 Args:
1118 change_id: The change_id to search for
1119
1120 Returns:
1121 CL number if found, None if not
1122 """
1123 r = requests.get('https://review.coreboot.org/changes/' + change_id)
1124 response = r.content.decode('utf-8')
1125 # Check if the response starts with 'Not found', in which case return None
1126 if response.startswith('Not found:'):
1127 return None
1128 # Strip off the initial )]}'\n that is used as XSS protections, see
1129 # https://gerrit-review.googlesource.com/Documentation/rest-api.html#output
1130 # and decode as JSON.
1131 data = json.loads(response[5:])
1132 if '_number' in data:
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001133 return str(data['_number'])
Paul Fagerburg8d850932020-02-25 14:13:32 -07001134 return None
1135
1136
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001137def find_change_id(change_id):
1138 """Search the public and private ChromeOS gerrit instances for a change-id
1139
1140 Args:
1141 change_id: Change-Id to search for in both gerrit instances
1142
1143 Returns:
1144 Tuple of the gerrit instance ('chromium' or 'chrome-internal') and
1145 the CL number if the Change-Id is found.
1146 None if not found.
1147 """
1148 cl_number = query_gerrit(gerrit.GetCrosExternal(), change_id)
1149 if cl_number:
1150 return 'chromium', cl_number
1151 cl_number = query_gerrit(gerrit.GetCrosInternal(), change_id)
1152 if cl_number:
1153 return 'chrome-internal', cl_number
Paul Fagerburg8d850932020-02-25 14:13:32 -07001154 cl_number = query_coreboot_gerrit(change_id)
1155 if cl_number:
1156 return 'coreboot', cl_number
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001157 return None
1158
1159
1160def save_cl_data(status, commit_key, cl):
1161 """Save the gerrit instance and CL number to the yaml file
1162
1163 Args:
1164 status: variant_status object tracking our board, variant, etc.
1165 commit_key: Which key in the commits map we're processing
1166 cl: Value returned by find_change_id, should be a tuple
1167 of instance_name, cl_number
1168 """
1169 instance_name, cl_number = cl
1170 print(f'Found ({instance_name}, {cl_number}), saving to yaml')
1171 status.commits[commit_key]['gerrit'] = instance_name
1172 status.commits[commit_key]['cl_number'] = cl_number
1173 status.save()
1174
1175
1176def repo_upload(branch_name, cwd):
1177 """Upload a branch to gerrit
1178
1179 This function runs `repo upload` in the specified directory to upload
1180 a branch to gerrit. Because it's operating in a directory and with a
1181 branch name, it could upload more than one commit, which is OK because
1182 we'll look for each commit by change-id before trying to upload in that
1183 directory. For example, this happens in Zork, where the cb_config step
1184 and the cras_config step both have a commit in src/overlays. When we're
1185 processing the cb_config step and we `repo upload` in src/overlays, it
1186 will also upload the commit for cras_config. Then we come around to the
1187 cras_config step, and since we can find a CL with the change-id, we don't
1188 try to upload again.
1189
1190 Args:
1191 branch_name: the name of the branch to upload. Gets passed to
1192 repo upload with the --br flag
1193 cwd: directory where we want to upload. Gets set as the working
1194 directory for executing repo upload.
1195
1196 Returns:
1197 True if repo upload exits with a successful error code, false otherwise
1198 """
Paul Fagerburg042a5252020-03-16 21:49:18 -06001199 return run_process(
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001200 ['repo',
1201 'upload',
1202 '.',
1203 '--br=' + branch_name,
1204 '--wip',
1205 '--verify',
1206 '--yes'],
Paul Fagerburg042a5252020-03-16 21:49:18 -06001207 cwd=cwd)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001208
1209
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001210def upload_CLs(status):
1211 """Upload all CLs to chromiumos
1212
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001213 Args:
1214 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001215
1216 Returns:
1217 True if the build succeeded, False if something failed
1218 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001219 logging.info('Running step upload_CLs')
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001220
1221 for commit_key in status.repo_upload_list:
1222 logging.debug(f'Processing key {commit_key}')
1223 commit = status.commits[commit_key]
1224 if 'gerrit' not in commit or 'cl_number' not in commit:
1225 change_id = commit['change_id']
1226 cl = find_change_id(change_id)
1227 if cl is not None:
1228 save_cl_data(status, commit_key, cl)
1229 else:
1230 logging.debug(f'Not found {change_id}, need to upload')
1231 if not repo_upload(commit['branch_name'], commit['dir']):
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001232 logging.error('Repo upload %s in %s failed!',
1233 commit['branch_name'],
1234 commit['dir'])
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001235 return False
1236 cl = find_change_id(change_id)
1237 if cl is None:
1238 logging.error(f'repo upload {commit_key} succeeded, ' \
1239 'but change_id is not found!')
1240 return False
1241 save_cl_data(status, commit_key, cl)
1242 else:
1243 instance_name = commit['gerrit']
1244 cl_number = commit['cl_number']
1245 logging.debug(f'Already uploaded ({instance_name}, {cl_number})')
1246
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001247 return True
1248
1249
1250def find_coreboot_upstream(status):
1251 """Find the coreboot CL after it has been upstreamed to chromiumos
1252
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001253 When the coreboot variant CL is first uploaded to review.coreboot.org,
1254 it is not visible in the chromiumos tree (and also cannot be used as
1255 a target for cq-depend). There is a process for upstreaming CLs from
1256 coreboot after they have been reviewed, approved, and merged. We can
1257 track a specific coreboot CL if we know the change-id that it used on
1258 the coreboot gerrit instance, by looking for that change-id as
1259 'original-change-id' in the public chromium gerrit instance.
1260
1261 The change-id for the coreboot variant will be under the 'cb_variant' key,
1262 but this is for the 'coreboot' gerrit instance.
1263
1264 When we find the upstreamed CL, we will record the gerrit instance and
1265 CL number in the yaml file under the 'find' key ("find upstream coreboot")
1266 so that we don't need to search coreboot again.
1267
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001268 Args:
1269 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001270
1271 Returns:
1272 True if the build succeeded, False if something failed
1273 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001274 logging.info('Running step find_coreboot_upstream')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001275
1276 # If we have already found the upstream coreboot CL, then exit with success
1277 if step_names.FIND in status.commits:
1278 commit = status.commits[step_names.FIND]
1279 if 'gerrit' in commit and 'cl_number' in commit:
1280 instance_name = commit['gerrit']
1281 cl_number = commit['cl_number']
1282 logging.debug(f'Already found ({instance_name}, {cl_number})')
1283 return True
1284
1285 # Make sure we have a CB_VARIANT commit and a change_id for it
1286 if step_names.CB_VARIANT not in status.commits:
1287 logging.error('Key %s not found in status.commits',
1288 step_names.CB_VARIANT)
1289 return False
1290 if 'change_id' not in status.commits[step_names.CB_VARIANT]:
1291 logging.error('Key change_id not found in status.commits[%s]',
1292 step_names.CB_VARIANT)
1293 return False
1294
1295 # Find the CL by the Original-Change-Id
1296 original_change_id = status.commits[step_names.CB_VARIANT]['change_id']
1297 gerrit_query_args = {
1298 'Original-Change-Id': original_change_id
1299 }
1300 cros = gerrit.GetCrosExternal()
1301 upstream = cros.Query(**gerrit_query_args)
1302 # If nothing is found, the patch hasn't been upstreamed yet
1303 if not upstream:
1304 logging.error('Program cannot continue until coreboot CL is upstreamed.')
1305 logging.error('(coreboot:%s, change-id %s)',
1306 status.commits[step_names.CB_VARIANT]['cl_number'],
1307 status.commits[step_names.CB_VARIANT]['change_id'])
1308 logging.error('Please wait for the CL to be upstreamed, then run this'
1309 ' program again with --continue')
1310 return False
1311
1312 # If more than one CL is found, something is very wrong
1313 if len(upstream) != 1:
1314 logging.error('More than one CL was found with Original-Change-Id %s',
1315 original_change_id)
1316 return False
1317
1318 # At this point, we know there is only one CL and we can get the
1319 # repo and CL number by splitting on the colon between them.
1320 patchlink = upstream[0].PatchLink()
1321 instance_name, cl_number = patchlink.split(':')
1322
1323 # Can't use get_git_commit_data because we're not pulling this
1324 # information from a git commit, but rather from gerrit.
1325 # We only need the gerrit instance and the CL number so we can have
1326 # other CLs cq-depend on this CL. The other keys are not needed because:
1327 # dir - not needed because we're not going to `cd` there to `repo upload`
1328 # branch_name - not valid; the CL is already merged
1329 # change_id - we use the change_id to find a CL number, and since we
1330 # just found the CL number via original-change-id, this is moot.
1331 status.commits[step_names.FIND] = {
1332 'gerrit': instance_name,
1333 'cl_number': str(cl_number)
1334 }
1335
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001336 return True
1337
1338
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001339def calc_cq_depend(status):
1340 """Determine the list of CLs for each commit that has dependencies.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001341
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001342 status.depends is a map of dependencies from step name to a list of
1343 steps that the step depends on. For each step, find the SHA of the
1344 commit, then find the gerrit instance and CL number of the commits
1345 that it depends on. Construct the Cq-Depends list and save it under
1346 the 'cq_depend' key, i.e. commit['add_priv_yaml']['cq_depend'] or
1347 as it will be stored in the yaml:
1348 commits:
1349 add_priv_yaml:
1350 cq_depend: 'chromium:1629121, chromium:1638243'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001351
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001352 Args:
1353 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001354
1355 Returns:
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001356 True if all dependencies have been calculated. False if something
1357 failed, usually a commit not found by Change-Id.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001358 """
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001359 logging.info('Running step calc_cq_depend')
1360 # Iterate through the commits that have dependencies.
1361 for key in status.depends:
1362 logging.debug('Processing %s to add dependencies', key)
1363 # For every commit that has dependencies, find the gerrit instance
1364 # and CL number of the dependencies.
1365 cq_depend_list = []
1366 for depend_key in status.depends[key]:
1367 depend_commit = status.commits[depend_key]
1368 if not 'gerrit' in depend_commit:
1369 logging.error('Commit %s does not have a gerrit instance',
1370 depend_key)
1371 return False
1372 if not 'cl_number' in depend_commit:
1373 logging.error('Commit %s does not have a CL number',
1374 depend_key)
1375 return False
1376
1377 instance_name = depend_commit['gerrit']
1378 cl_number = depend_commit['cl_number']
1379 cq_depend_list.append(f'{instance_name}:{cl_number}')
1380
1381 # Add the 'cq_depend' key to the commit.
1382 cq_depend_str = 'Cq-Depend: %s' % ', '.join(cq_depend_list)
1383 logging.debug('Add to commit %s %s', key, cq_depend_str)
1384 status.commits[key]['cq_depend'] = cq_depend_str
1385
1386 return True
1387
1388
1389def add_cq_depend_to_commit_msg(git_repo, change_id, cq_depend_str):
1390 """Update the commit message with a Cq-Depends line.
1391
1392 Find the SHA of the commit, then use git filter-branch --msg-filter
1393 to add the Cq-Depend line just before the Change-Id line. See
1394 https://chromium.googlesource.com/chromiumos/docs/+/HEAD/contributing.md#cq-depend
1395 for details about Cq-Depend format and location.
1396
1397 Args:
1398 git_repo: Directory of git repository.
1399 change_id: The Change-Id to search for.
1400 cq_depend_str: The Cq-Depend string. It must be in the correct format
1401 per chromeos documentation, ready to insert into the commit msg
1402 on the line before Change-Id.
1403
1404 Returns:
1405 True if `git filter-branch` was successful. False if the command
1406 failed.
1407 """
1408 logging.debug('find SHA of Change-Id %s in %s', change_id, git_repo)
1409 sha = change_id_to_sha(git_repo, change_id)
1410 if sha is None:
1411 logging.error('Cannot find the SHA for Change-Id %s in %s',
1412 change_id, git_repo)
1413 return False
1414 logging.debug('SHA = %s', sha)
1415
1416 # Check if the commit message already has a Cq-Depend line.
1417 msg = get_commit_msg(git_repo, sha)
1418 if any('Cq-Depend' in tmpstr for tmpstr in msg):
1419 logging.debug('Already has Cq-Depend')
1420 return True
1421
1422 # Use git filter-branch --msg-filter to add the Cq-Depend line just
1423 # before the Change-Id line.
1424 environ = {**os.environ, 'FILTER_BRANCH_SQUELCH_WARNING': '1'}
1425 cmd = [
1426 'git',
1427 'filter-branch',
1428 '--msg-filter',
1429 f'sed -E "s/^(Change-Id: {change_id})$/{cq_depend_str}\\n\\1/"',
1430 '--',
1431 f'{sha}^..']
1432 return run_process(cmd, cwd=git_repo, env=environ)
1433
1434
1435def add_cq_depend(status):
1436 """Add Cq-Depend to commits and flag them for re-upload.
1437
1438 Args:
1439 status: variant_status object tracking our board, variant, etc.
1440
1441 Returns:
1442 True if the commit messages have been successfully amended, False if
1443 something failed.
1444 """
1445 logging.info('Running step add_cq_depend')
1446 for key in status.commits:
1447 commit = status.commits[key]
1448 if 'cq_depend' in commit:
1449 logging.debug('%s has %s', key, commit['cq_depend'])
1450 # Make sure the commit has a working directory and a change_id
1451 # before trying to amend its commit message.
1452 if 'dir' not in commit or 'change_id' not in commit:
1453 logging.error('Missing dir and/or change_id from %s', key)
1454 return False
1455
1456 if not add_cq_depend_to_commit_msg(commit['dir'],
1457 commit['change_id'],
1458 commit['cq_depend']):
1459 return False
1460 commit['needs_re_upload'] = True
1461 else:
1462 logging.debug('%s no dependencies', key)
1463
1464 return True
1465
1466
1467def re_upload(status):
1468 """Re-upload commits that have changed.
1469
1470 Args:
1471 status: variant_status object tracking our board, variant, etc.
1472
1473 Returns:
1474 True if the uploads succeeded. False if a repo upload failed.
1475 """
1476 logging.info('Running step re_upload')
1477 for key in status.commits:
1478 commit = status.commits[key]
1479 if commit.get('needs_re_upload'):
1480 logging.debug('Re-upload branch %s in %s', commit['branch_name'],
1481 commit['dir'])
1482 if not repo_upload(commit['branch_name'], commit['dir']):
1483 logging.error('Repo upload %s in %s failed!',
1484 commit['branch_name'],
1485 commit['dir'])
1486 return False
1487 commit['needs_re_upload'] = False
1488
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001489 return True
1490
1491
1492def clean_up(status):
1493 """Final clean-up, including delete the status file
1494
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001495 Args:
1496 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001497
1498 Returns:
1499 True
1500 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001501 logging.info('Running step clean_up')
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001502 status.rm()
1503 return True
1504
1505
Paul Fagerburg042a5252020-03-16 21:49:18 -06001506def abort(status):
1507 """Abort the creation of a new variant by abandoning commits
1508
1509 When the user specifies the --abort flag, we override status.step to
1510 be 'abort' and there is no transition from 'abort' to anything else.
1511 We look at status.commits and for each key, see if we have already
1512 been in that directory and abandoned that specific branch. If not,
1513 abandon the commit and then add the branch+dir to a list of abandoned
1514 commits. We do this because some boards (such as Zork) can have multiple
1515 commits in the same directory and with the same branch name, and we only
1516 want to repo abandon that branch once.
1517
1518 Args:
1519 status: variant_status object tracking our board, variant, etc.
1520
1521 Returns:
1522 True
1523 """
1524 logging.info('Running step abort')
1525 # Use the set 'abandoned' to keep track of each branch+dir abandoned.
1526 abandoned = set()
1527 for step in status.commits:
1528 logging.debug('Processing step %s', step)
1529 commit = status.commits[step]
1530 branch = commit['branch_name']
1531 cwd = commit['dir']
1532 if (branch, cwd) in abandoned:
1533 logging.debug('Branch %s in directory %s already abandoned',
1534 branch, cwd)
1535 else:
1536 logging.info('Abandoning branch %s in directory %s',
1537 branch, cwd)
1538 if run_process(['repo', 'abandon', branch, '.'], cwd=cwd):
1539 abandoned.add((branch, cwd))
1540 else:
1541 logging.error('Error while abandoning branch %s', branch)
1542 return False
1543
1544 return True
1545
1546
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001547if __name__ == '__main__':
1548 sys.exit(not int(main()))