blob: deb05e58eda4ab0e1e9f35200636876cd47e1e61 [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 Fagerburg3ef1cbb2020-06-02 16:38:36 -0600222 * coreboot_base - the name of the base board in coreboot. Usually the same
223 as base, but can differ, e.g. for Puff, the base is Puff, but the
224 coreboot_base is Hatch because Puff is based on Hatch.
Paul Fagerburg4d343452020-01-24 15:23:53 -0700225 * coreboot_dir - base directory for coreboot, usually third_party/coreboot
226 but could differ for processors that use a private repo
Matt Ziegelbaum3e879df2020-11-09 16:08:44 -0500227 * coreboot_reference - the name of the reference board that we're using to
228 make the variant. This can be different from base (e.g. Ambassador is
229 its own board, but uses Puff as a coreboot reference).
Paul Fagerburg4d343452020-01-24 15:23:53 -0700230 * cb_config_dir - base directory for coreboot configs, usually
231 third_party/chromiumos-overlay/sys-boot/coreboot/files/configs but
232 could differ for processors that use a private repo
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700233 * step_list - list of steps (named in step_names.py) to run in sequence
Paul Fagerburge868e832020-01-22 17:14:04 -0700234 to create the new variant of the reference board
235 * fsp - package name for FSP. This may be None, depending on the
236 processor on the reference board
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700237 * fitimage_pkg - package name for the fitimage
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700238 * fitimage_dir - directory for fitimage; prepend '/mnt/host/source/src/'
239 in chroot, prepend '~/chromiumos/src' outside the chroot
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700240 * workon_pkgs - list of packages to cros_workon
241 * emerge_cmd - the emerge command, e.g. 'emerge-hatch'
242 * emerge_pkgs - list of packages to emerge
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600243 * config_workon_pkgs - list of packages to cros_workon to build the
244 project config
245 * config_emerge_pkgs - list of packages to emerge to build the project
246 config
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700247 * private_yaml_dir - directory for the private yaml file
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700248 * commits - map of commits for the various steps. Indexed by step name,
249 and the step names used are the same ones in step_names.py
250 * repo_upload_list - list of commits to upload using `repo upload`
251 * coreboot_push_list - list of commits to upload using `git push` to
252 coreboot
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600253 * depends - maps a step to a list of steps on which it depends, e.g.
254 depends[step_names.ADD_PRIV_YAML] is a list of other steps that
255 the 'add_priv_yaml' step depends on. This map is used to amend
256 the commits with CL numbers for Cq-Depend.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700257
258 Additionally, the following fields will be set:
259
Paul Fagerburge868e832020-01-22 17:14:04 -0700260 * board - the name of the reference board, e.g. 'hatch'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700261 * variant - the name of the variant, e.g. 'sushi'
262 * bug - optional text for a bug ID, used in the git commit messages.
263 Could be 'None' (as text, not the python None), or something like
264 'b:12345' for buganizer, or 'chromium:12345'
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700265 * step - internal state tracking, what step of the variant creation
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700266 we are at.
267 * yaml_file - internal, just the name of the file where all this data
268 gets saved.
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700269 * commit - a map of maps that tracks all of the git commit and gerrit CL
270 data for each of the steps in the process. For example,
271 status.commit['add_priv_yaml'] is a map that has all the information
272 about the 'add_priv_yaml' step. The keys in the maps allow us to
273 determine where the commit is, the change_id, if it has been uploaded
274 to gerrit and where.
275
276 branch_name - the name of the git branch
277 change_id - the change-id assigned by the commit hook. Gerrit
278 uses the change_id to track new patchsets in the CL
279 dir - the directory where the commit has been created
280 gerrit - the name of the gerrit instance to which the CL has
281 been uploaded, one of 'chromium', 'chrome-internal', or
282 'coreboot'
283 cl_number - the CL number on the gerrit instance
284
285 When the commit is created, branch_name, change_id, and dir are all
286 set. The gerrit and cl_number keys are not set until the CL has been
287 uploaded to a gerrit instance.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700288
289 These data might come from the status file (because we read it), or
290 they might be the initial values after we created the file (because
291 it did not already exist).
292
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700293 Args:
294 board: Name of the reference board
295 variant: Name of the variant being created
296 bug: Text for bug number, if any ('None' otherwise)
297 continue_flag: Flag if --continue was specified
Paul Fagerburg042a5252020-03-16 21:49:18 -0600298 abort_flag: Flag if --abort was specified
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700299
300 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700301 variant_status object with all the data mentioned above
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700302 """
303 status = variant_status.variant_status()
Paul Fagerburg042a5252020-03-16 21:49:18 -0600304 if continue_flag or abort_flag:
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700305 if status.yaml_file_exists():
Paul Fagerburg042a5252020-03-16 21:49:18 -0600306 return status
Paul Fagerburg6cba1492020-06-02 16:44:05 -0600307
308 if continue_flag:
309 op = '--continue'
310 if abort_flag:
311 op = '--abort'
312 logging.error('%s does not exist; cannot %s', status.yaml_file, op)
313 return None
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700314
Paul Fagerburg042a5252020-03-16 21:49:18 -0600315 # If we get here, the user provided --board and --variant (because
316 # check_flags() returned Trued), but the yaml file already exists,
317 # so we print an error message and bail.
318 if status.yaml_file_exists():
319 logging.error(
320 'new_variant already in progress; did you forget --continue?')
321 return None
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700322
Paul Fagerburg042a5252020-03-16 21:49:18 -0600323 # At this point, it's not --continue, not --abort, the yaml file doesn't
324 # exist, and we have valid values for --board, --variant, and --bug (bug
325 # might be the default value of "None"). Create the yaml file with data
326 # from the reference board's loadable module.
327 status.board = board
328 status.variant = variant
329 status.bug = bug
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700330
Paul Fagerburg042a5252020-03-16 21:49:18 -0600331 # Load the appropriate module and copy all the data from it.
332 try:
333 module = importlib.import_module(board)
334 except ImportError:
335 print('Unsupported board "' + board + '"')
336 sys.exit(1)
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700337
Paul Fagerburg042a5252020-03-16 21:49:18 -0600338 # pylint: disable=bad-whitespace
339 # Allow extra spaces around = so that we can line things up nicely
340 status.base = module.base
Paul Fagerburg3ef1cbb2020-06-02 16:38:36 -0600341 status.coreboot_base = getattr(module, 'coreboot_base', module.base)
Paul Fagerburg042a5252020-03-16 21:49:18 -0600342 status.coreboot_dir = module.coreboot_dir
Matt Ziegelbaum3e879df2020-11-09 16:08:44 -0500343 status.coreboot_reference = getattr(module, 'coreboot_reference', 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 Fagerburg042a5252020-03-16 21:49:18 -0600359 # pylint: enable=bad-whitespace
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700360
Paul Fagerburg042a5252020-03-16 21:49:18 -0600361 # Start at the first entry in the step list
362 status.step = status.step_list[0]
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700363
Paul Fagerburg042a5252020-03-16 21:49:18 -0600364 # Start an empty map for tracking CL data
365 status.commits = {}
366
367 status.save()
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700368
369 return status
370
371
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700372def perform_step(status):
373 """Call the appropriate function for the current step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700374
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700375 Args:
376 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700377
378 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700379 True if the step succeeded, False if it failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700380 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700381 # Function to call based on the step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700382 dispatch = {
Paul Fagerburgbc184242020-04-28 17:00:54 -0600383 step_names.PROJECT_CONFIG: project_config,
Paul Fagerburg8f60b802020-06-09 15:38:28 -0600384 step_names.FW_BUILD_CONFIG: fw_build_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700385 step_names.CB_VARIANT: create_coreboot_variant,
386 step_names.CB_CONFIG: create_coreboot_config,
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700387 step_names.CRAS_CONFIG: copy_cras_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700388 step_names.ADD_FIT: add_fitimage,
389 step_names.GEN_FIT: gen_fit_image_outside_chroot,
390 step_names.COMMIT_FIT: commit_fitimage,
391 step_names.EC_IMAGE: create_initial_ec_image,
392 step_names.EC_BUILDALL: ec_buildall,
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700393 step_names.ADD_PUB_YAML: add_variant_to_public_yaml,
394 step_names.ADD_PRIV_YAML: add_variant_to_private_yaml,
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600395 step_names.BUILD_CONFIG: build_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700396 step_names.EMERGE: emerge_all,
397 step_names.PUSH: push_coreboot,
398 step_names.UPLOAD: upload_CLs,
399 step_names.FIND: find_coreboot_upstream,
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600400 step_names.CALC_CQ_DEPEND: calc_cq_depend,
401 step_names.ADD_CQ_DEPEND: add_cq_depend,
402 step_names.RE_UPLOAD: re_upload,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700403 step_names.CLEAN_UP: clean_up,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600404 step_names.ABORT: abort,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700405 }
406
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700407 if status.step not in dispatch:
408 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700409 sys.exit(1)
410
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700411 return dispatch[status.step](status)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700412
413
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700414def move_to_next_step(status):
415 """Move to the next step in the list
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700416
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700417 Args:
418 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700419 """
Paul Fagerburg042a5252020-03-16 21:49:18 -0600420 # Special case: the next step after 'abort' is 'clean_up'. Always.
421 if status.step == step_names.ABORT:
422 status.step = step_names.CLEAN_UP
423 return
424
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700425 if status.step not in status.step_list:
426 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700427 sys.exit(1)
428
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700429 idx = status.step_list.index(status.step)
430 if idx == len(status.step_list)-1:
431 status.step = None
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700432 else:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700433 status.step = status.step_list[idx+1]
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700434
435
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700436def run_process(args, cwd=None, env=None, capture_output=False):
437 """Run a process, log debug messages, return text output of process
438
439 The capture_output parameter allows us to capture the output when we
440 care about it (and not sending it to the screen), or ignoring it when
441 we don't care, and letting the user see the output so they know that
442 the build is still running, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700443
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700444 Args:
445 args: List of the command and its params
446 cwd: If not None, cd to this directory before running
447 env: Environment to use for execution; if needed, get os.environ.copy()
448 and add variables. If None, just use the current environment
449 capture_output: True if we should capture the stdout, false if we
450 just care about success or not.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700451
452 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700453 If capture_output == True, we return the text output from running
454 the subprocess as a list of lines, or None if the process failed.
455 If capture_output == False, we return a True if it successed, or
456 None if the process failed.
457
458 The caller can evaluate as a bool, because bool(None) == False, and
459 bool() of a non-empty list is True, or the caller can use the returned
460 text for further processing.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700461 """
462 logging.debug('Run %s', str(args))
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700463 if cwd is not None:
464 logging.debug('cwd = %s', cwd)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700465 try:
466 if capture_output:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700467 output = subprocess.run(args, cwd=cwd, env=env, check=True,
468 stderr=subprocess.STDOUT, stdout=subprocess.PIPE).stdout
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700469 else:
470 subprocess.run(args, cwd=cwd, env=env, check=True)
471 # Just something to decode so we don't get an empty list
472 output = b'True'
473
474 logging.debug('process returns 0')
475 # Convert from byte string to ASCII
476 decoded = output.decode('utf-8')
477 # Split into array of individual lines
478 lines = decoded.split('\n')
479 return lines
480 except subprocess.CalledProcessError as err:
481 logging.debug('process returns %s', str(err.returncode))
482 return None
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700483
484
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700485def get_git_commit_data(cwd):
486 """Get the branch name and change id of the current commit
487
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700488 Args:
489 cwd: The current working directory, where we want to get the branch
490 name and change id
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700491
492 Returns:
493 Map with 'dir', 'branch_name' and 'change_id' keys. The 'dir'
494 key maps to the value of os.path.expanduser(cwd)
495 """
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700496 cwd = git.FindGitTopLevel(os.path.expanduser(cwd))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700497 logging.debug('get_git_commit_data(%s)', cwd)
498
499 branch_name = git.GetCurrentBranch(cwd)
500 if branch_name is None:
501 logging.error('Cannot determine git branch name in %s; exiting', cwd)
502 sys.exit(1)
503 logging.debug('git current branch is %s', branch_name)
504
505 change_id = git.GetChangeId(cwd)
506 if change_id is None:
507 logging.error('Cannot determine Change-Id in %s; exiting', cwd)
508 sys.exit(1)
509 logging.debug('git Change-Id is %s', change_id)
510
511 return {
512 'dir': cwd,
513 'branch_name': branch_name,
514 'change_id': change_id
515 }
516
517
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -0600518def change_id_to_sha(git_repo, change_id):
519 """Find the SHA for a given Change-Id.
520
521 Args:
522 git_repo: Directory of git repository.
523 change_id: The Change-Id to search for.
524
525 Returns:
526 The SHA hash for the Change-Id if only one commit is found.
527 None if the Change-Id was not found.
528 Raises a ValueError if more than one commit is found with the
529 same Change-Id.
530 """
531 output = git.Log(git_repo, max_count=1, format='format:%H',
532 grep=fr'^Change-Id: {change_id}$')
533 sha_hashes = output.splitlines()
534 if not sha_hashes:
535 return None
536 if len(sha_hashes) > 1:
537 raise ValueError('More than one SHA with that Change-Id found')
538 return sha_hashes[0]
539
540
541def get_commit_msg(git_repo, rev):
542 """Get the commit message for a given revision.
543
544 Because git.Log doesn't allow specifying check=False or getting the
545 returncode, we have to catch the CalledProcessError instead.
546
547 Args:
548 git_repo: Directory of git repository.
549 rev: The revision to search for, a SHA or a label.
550
551 Returns:
552 The commit message as a list of strings, if the revision exists.
553 None if the revision was not found.
554 """
555 try:
556 msg = git.Log(git_repo, max_count=1, format='format:%B', rev=rev)
557 return msg.splitlines()
558 except cros_build_lib.CalledProcessError:
559 raise ValueError('SHA was not found')
560
561
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600562def emerge_with_workon(status, workon_pkgs, emerge_cmd, emerge_pkgs, env=None):
563 """Emerge a list of packages after `cros_workon start`ing them
564
565 This function will `cros_workon start` a list of packages, then `emerge`
566 another list of packages, and finally, `cros_workon stop` only those
567 packages that were actually started by the `cros_workon start` command.
568 Any package already in a `cros_workon start` state prior to this function
569 will still be in that state when this function exits.
570
571 To determine which packages this program started and which ones were
572 already started, we query the list of packages being worked on, then
573 cros_workon start the entire list (which will produce a "package already
574 being worked on" type of message for anything already started), and then
575 query the list of packages being worked on again. The difference between
576 the before and after lists are the packages that this program started,
577 and so that's the list of packages to cros_workon stop after the emerge
578 is done.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700579
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700580 Args:
581 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600582 workon_pkgs: list of packages to `cros_workon start`
583 emerge_cmd: the emerge command to run, e.g. 'emerge-volteer'
584 emerge_pkgs: list of packages to `emerge`
585 env: environment to pass to run_process, or None to pass default
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700586
587 Returns:
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600588 True if everything succeeded, False if something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700589 """
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600590 # Get the list of packages that are already cros_workon started.
591 build_target = build_target_lib.BuildTarget(status.base)
592 workon = workon_helper.WorkonHelper(build_target.root, build_target.name)
593 before_workon = workon.ListAtoms()
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700594
Paul Fagerburg4376fe52020-06-16 10:22:35 -0600595 # Only cros_workon start if the list is non-empty
596 if workon_pkgs:
597 workon.StartWorkingOnPackages(workon_pkgs)
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600598
599 # Determine which packages we need to cros_workon stop.
600 after_workon = workon.ListAtoms()
601 stop_packages = list(set(after_workon) - set(before_workon))
602
603 # Run the emerge command.
604 emerge_result = run_process([emerge_cmd] + emerge_pkgs, env=env)
605
Paul Fagerburg4376fe52020-06-16 10:22:35 -0600606 # If the list is non-empty, cros_workon stop before returning the result.
607 if stop_packages:
608 workon.StopWorkingOnPackages(stop_packages)
Paul Fagerburg20559bc2020-04-29 10:37:14 -0600609
610 return emerge_result
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700611
612
Paul Fagerburgbc184242020-04-28 17:00:54 -0600613def project_config(status):
614 """Check if the project config is correct and complete
615
616 For programs that use the new project/config structure with starlark
617 configuration files, this function will check that emerging the
618 project's configuration will result in a project-config.json that
619 includes the new name of the new hwdesign (a.k.a. "variant").
620
621 Args:
622 status: variant_status object tracking our board, variant, etc.
623
624 Returns:
625 True if everything succeeded, False if something failed
626 """
627 logging.info('Running step project_config')
628 try:
629 if not emerge_with_workon(status, status.config_workon_pkgs,
630 status.emerge_cmd, status.config_emerge_pkgs):
631 raise RuntimeError(f'Building the configuration failed.')
632
633 # Make sure project-config.json exists in the /build tree
634 emerged_json = os.path.join(
635 '/build',
636 status.base,
637 'usr/share/chromeos-config/yaml/project-config.json')
638 if not os.path.isfile(emerged_json):
639 raise RuntimeError(
640 f'project-config.json {emerged_json} does not exist.')
641
642 # Search emerged_json to see if the new variant's name shows up there.
643 if not status.variant in osutils.ReadFile(emerged_json):
644 raise RuntimeError(
645 f'variant name {status.variant} not found in {emerged_json}')
646
647 except RuntimeError as e:
648 logging.error(str(e))
649 logging.error('Please file a bug in Infra > ChromeOS > Product > Device'
650 ' to have the project configuration updated.')
651 logging.error('(https://bugs.chromium.org/p/chromium/issues/list?'
652 'q=component:Infra%3EChromeOS%3EProduct%3EDevice)')
653 return False
654
655 return True
656
657
Paul Fagerburg8f60b802020-06-09 15:38:28 -0600658def fw_build_config(status):
659 """Add the _FW_BUILD_CONFIG setting to the project config
660
661 For programs that use the new project/config structure with starlark
662 configuration files, this function calls fw_build_config.sh, which will
663 modify the config.star file to have a default _FW_BUILD_CONFIG entry.
664
665 Args:
666 status: variant_status object tracking our board, variant, etc.
667
668 Returns:
669 True if everything succeeded, False if something failed
670 """
671 logging.info('Running step fw_build_config')
672 fw_build_config_sh = os.path.join(status.my_loc, 'fw_build_config.sh')
673 rc = run_process(
674 [fw_build_config_sh,
675 status.base,
676 status.variant,
677 status.bug])
678 if rc:
679 status.commits[step_names.FW_BUILD_CONFIG] = get_git_commit_data(
680 os.path.join('/mnt/host/source/src/project', status.base, status.variant))
681 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 Fagerburg1d043c32020-02-03 08:57:08 -0700741 if status.cb_config_dir is not None:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700742 cb_config_dir = os.path.join('/mnt/host/source/src/', status.cb_config_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700743 else:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700744 cb_config_dir = '/mnt/host/source/src/third_party/chromiumos-overlay'
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700745 status.commits[step_names.CB_CONFIG] = get_git_commit_data(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
754 cras config in
755 overlays/overlay-${BASE}/chromeos-base/chromeos-bsp-${BASE}/files/cras-config/${BASE}
756 to .../${VARIANT}
757
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700758 Args:
759 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700760
761 Returns:
762 True if the script and test build succeeded, False if something failed
763 """
764 logging.info('Running step copy_cras_config')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700765 copy_cras_config_sh = os.path.join(status.my_loc, 'copy_cras_config.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600766 rc = run_process(
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700767 [copy_cras_config_sh,
768 status.base,
769 status.board,
770 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600771 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700772 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700773 status.commits[step_names.CRAS_CONFIG] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700774 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700775 return rc
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700776
777
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700778def add_fitimage(status):
779 """Add the source files for a fitimage for the new variant
780
781 This function calls add_fitimage.sh to create a new XSL file for the
Paul Fagerburge868e832020-01-22 17:14:04 -0700782 variant's fitimage, which can override settings from the reference board's
783 XSL. When this is done, the user will have to build the fitimage by running
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700784 gen_fit_image.sh outside of the chroot (and outside of this program's
785 control) because gen_fit_image.sh uses WINE, which is not installed in
786 the chroot. (There is a linux version of FIT, but it requires Open GL,
787 which is also not installed in the chroot.)
788
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700789 Args:
790 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700791
792 Returns:
793 True if the script succeeded, False otherwise
794 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700795 logging.info('Running step add_fitimage')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700796 add_fitimage_sh = os.path.expanduser(os.path.join(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700797 '/mnt/host/source/src', status.fitimage_dir, 'files/add_fitimage.sh'))
Paul Fagerburg042a5252020-03-16 21:49:18 -0600798 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700799 [add_fitimage_sh,
800 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600801 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700802 if rc:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700803 fitimage_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir)
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700804 status.commits[step_names.COMMIT_FIT] = get_git_commit_data(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)
832 logging.error('The fitimage sources are ready for gen_fit_image.sh to process.')
833 logging.error('gen_fit_image.sh cannot run inside the chroot. Please open a new terminal')
834 logging.error('window, change to the directory where gen_fit_image.sh is located, and run')
835 logging.error(status.fitimage_cmd, status.variant)
836 logging.error('Then re-start this program with --continue.')
837 logging.error('If your chroot is based in ~/chromiumos, then the folder you want is')
838 logging.error('~/chromiumos/src/%s/asset_generation', status.fitimage_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700839 return False
840
841
842def check_fit_image_files(status):
843 """Check if the fitimage has been generated
844
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700845 This function is not called directly as a step, and so it doesn't need
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700846 to produce any error messages to the user (except with --verbose).
847 gen_fit_image_outside_chroot will call this function to see if the
848 fitimage files exist, and if not, then that function will print the
849 message about how the user needs to run gen_fit_image.sh outside the
850 chroot.
851
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700852 Args:
853 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700854
855 Returns:
856 List of files that *DO NOT* exist and need to be created, [] if
857 all files are present.
858 """
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700859 outputs_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir,
860 'asset_generation/outputs')
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700861 logging.debug('outputs_dir = "%s"', outputs_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700862
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600863 files_not_found = []
864 fitimage_bin = 'fitimage-' + status.variant + '.bin'
865 if not os.path.isfile(os.path.join(outputs_dir, fitimage_bin)):
866 files_not_found.append(fitimage_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700867
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600868 fitimage_versions = 'fitimage-' + status.variant + '-versions.txt'
869 if not os.path.isfile(os.path.join(outputs_dir, fitimage_versions)):
870 files_not_found.append(fitimage_versions)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700871
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600872 return files_not_found
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700873
874
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700875def commit_fitimage(status):
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600876 """Add the fitimage files to the git commit
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700877
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600878 This function calls commit_fitimage.sh to move the fitimage binary and
879 -versions files from asset_generation/outputs to files/ and then adds
880 those files and fit.log to the existing git commit.
881 Depending on the baseboard, there may be different file names (such
882 as fit-${VARIANT}.log for volteer) and/or additional files (such as
883 files/blobs/description-${VARIANT}.bin for volteer)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700884
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700885 Args:
886 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700887
888 Returns:
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600889 True if the script succeeded, False if something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700890 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700891 logging.info('Running step commit_fitimage')
Paul Fagerburg8f71ac02020-04-22 10:53:45 -0600892 commit_fitimage_sh = os.path.expanduser(os.path.join(
893 '/mnt/host/source/src', status.fitimage_dir, 'files/commit_fitimage.sh'))
894 return run_process([commit_fitimage_sh, status.variant])
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700895
896
897def create_initial_ec_image(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700898 """Create an EC image for the variant as a clone of the reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700899
900 This function calls create_initial_ec_image.sh, which will clone the
Paul Fagerburge868e832020-01-22 17:14:04 -0700901 reference board to create the variant. The shell script will build the
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700902 EC code for the variant, but the repo upload hook insists that we
903 have done a `make buildall` before it will allow an upload, so this
904 function does the buildall.
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 create_initial_ec_image')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700913 create_initial_ec_image_sh = os.path.join(status.my_loc,
914 'create_initial_ec_image.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600915 if not run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700916 [create_initial_ec_image_sh,
917 status.board,
918 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600919 status.bug]):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700920 return False
921
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700922 # No need to `if rc:` because we already tested the run_process result above
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700923 status.commits[step_names.EC_IMAGE] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700924 '/mnt/host/source/src/platform/ec/board')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700925
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700926 # create_initial_ec_image.sh will build the ec.bin for this variant
927 # if successful.
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600928 ec_dir = '/mnt/host/source/src/platform/ec'
929 ec_bin = os.path.join(ec_dir, 'build', status.variant, 'ec.bin')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700930 logging.debug('ec.bin = "%s"', ec_bin)
931
Paul Fagerburg8021dab2020-03-25 21:23:33 -0600932 if not os.path.isfile(ec_bin):
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700933 logging.error('EC binary %s not found', ec_bin)
934 return False
935 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700936
937
938def ec_buildall(status):
939 """Do a make buildall -j for the EC, which is required for repo upload
940
941 The upload hook checks to ensure that the entire EC codebase builds
942 without error, so we have to run make buildall -j before uploading.
943
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700944 Args:
945 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700946
947 Returns:
948 True if the script and test build succeeded, False if something failed
949 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700950 logging.info('Running step ec_buildall')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700951 del status # unused parameter
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700952 ec = '/mnt/host/source/src/platform/ec'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700953 logging.debug('ec = "%s"', ec)
Paul Fagerburg042a5252020-03-16 21:49:18 -0600954 return run_process(['make', 'buildall', '-j'], cwd=ec)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700955
956
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700957def add_variant_to_public_yaml(status):
958 """Add the new variant to the public model.yaml file
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700959
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700960 This function calls add_variant_to_yaml.sh to add the new variant to
961 the public model.yaml file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700962
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700963 Args:
964 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700965
966 Returns:
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700967 True if the script succeeded, False is something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700968 """
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700969 logging.info('Running step add_variant_to_public_yaml')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700970 add_variant_to_yaml_sh = os.path.join(status.my_loc,
971 'add_variant_to_yaml.sh')
Paul Fagerburg042a5252020-03-16 21:49:18 -0600972 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700973 [add_variant_to_yaml_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700974 status.base,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700975 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -0600976 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700977 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700978 status.commits[step_names.ADD_PUB_YAML] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700979 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700980 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700981
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700982
983def add_variant_to_private_yaml(status):
984 """Add the new variant to the private model.yaml file
985
986 This function calls add_variant.sh to add the new variant to
987 the private model.yaml file.
988
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700989 Args:
990 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700991
992 Returns:
993 True if the script succeeded, False is something failed
994 """
995 logging.info('Running step add_variant_to_private_yaml')
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700996 add_variant_sh = os.path.expanduser(os.path.join(status.private_yaml_dir, 'add_variant.sh'))
Paul Fagerburg042a5252020-03-16 21:49:18 -0600997 rc = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700998 [add_variant_sh,
999 status.variant,
Paul Fagerburg042a5252020-03-16 21:49:18 -06001000 status.bug])
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001001 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001002 status.commits[step_names.ADD_PRIV_YAML] = get_git_commit_data(status.private_yaml_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -07001003 return rc
1004
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001005
1006
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001007def build_config(status):
1008 """Build project config files, from yaml or starlark
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001009
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001010 This function builds the project config files that mosys and other tools
1011 use, then verifies that the new variant's name shows up in all of the
1012 output files. Depending on the baseboard, the input may be the model.yaml
1013 files, or the starlark configuration files.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001014
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001015 Args:
1016 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001017
1018 Returns:
1019 True if the scripts and build succeeded, False is something failed
1020 """
Paul Fagerburg4376fe52020-06-16 10:22:35 -06001021 logging.info('Running step build_config')
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001022 if not emerge_with_workon(status, status.config_workon_pkgs,
1023 status.emerge_cmd, status.config_emerge_pkgs):
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001024 return False
1025
Paul Fagerburga95dd162020-03-24 16:27:18 -06001026 # Check the generated config.yaml file for occurences of the variant
1027 # name to determine if the emerge was successful.
1028 config_yaml = os.path.join(
1029 '/build', status.base, 'usr/share/chromeos-config/yaml/config.yaml')
1030 logging.debug('config_yaml = "%s"', config_yaml)
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001031 if not os.path.isfile(config_yaml):
Paul Fagerburga95dd162020-03-24 16:27:18 -06001032 logging.error('%s does not exist', config_yaml)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001033 return False
1034
Paul Fagerburga95dd162020-03-24 16:27:18 -06001035 if not status.variant in osutils.ReadFile(config_yaml):
1036 logging.error('variant name %s not found in yaml file %s',
1037 status.variant, config_yaml)
1038 return False
1039
1040 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001041
1042
1043def emerge_all(status):
1044 """Build the coreboot BIOS and EC code for the new variant
1045
Paul Fagerburg75398072020-03-16 13:51:24 -06001046 This build step will cros_workon start a list of packages provided by
1047 the reference board data as status.workon_pkgs, then emerge a list of
1048 packages (status.emerge_pkgs), and then cros_workon stop any packages
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001049 that it started. Any packages that were already being worked on will
1050 not be stopped.
Paul Fagerburg75398072020-03-16 13:51:24 -06001051
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001052 Args:
1053 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001054
1055 Returns:
1056 True if the build succeeded, False if something failed
1057 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001058 logging.info('Running step emerge_all')
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001059 environ = {**os.environ, 'FW_NAME': status.variant}
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001060 if not emerge_with_workon(status, status.workon_pkgs,
1061 status.emerge_cmd, status.emerge_pkgs,
1062 env=environ):
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001063 return False
1064
Paul Fagerburg20559bc2020-04-29 10:37:14 -06001065 # Check if the expected build outputs exist.
Paul Fagerburge868e832020-01-22 17:14:04 -07001066 build_path = '/build/' + status.base + '/firmware'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001067 logging.debug('build_path = "%s"', build_path)
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001068 image_bin = 'image-' + status.variant + '.bin'
1069 if not os.path.isfile(os.path.join(build_path, image_bin)):
1070 logging.error('emerge failed because %s does not exist', image_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001071 return False
1072
Paul Fagerburg8021dab2020-03-25 21:23:33 -06001073 serial_bin = 'image-' + status.variant + '.serial.bin'
1074 if not os.path.isfile(os.path.join(build_path, serial_bin)):
1075 logging.error('emerge failed because %s does not exist', serial_bin)
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001076 return False
1077
1078 return True
1079
1080
1081def push_coreboot(status):
1082 """Push the coreboot CL to coreboot.org
1083
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001084 Args:
1085 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001086
1087 Returns:
1088 True if the build succeeded, False if something failed
1089 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001090 logging.info('Running step push_coreboot')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001091
1092 # Set up a return code that may change to False if we find that a
1093 # coreboot CL has not been uploaded.
1094 rc = True
1095
1096 for commit_key in status.coreboot_push_list:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001097 logging.debug('Processing key %s', commit_key)
Paul Fagerburg8d850932020-02-25 14:13:32 -07001098 commit = status.commits[commit_key]
1099 if 'gerrit' not in commit or 'cl_number' not in commit:
1100 change_id = commit['change_id']
1101 cl = find_change_id(change_id)
1102 if cl is not None:
1103 save_cl_data(status, commit_key, cl)
1104 else:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001105 logging.debug('Not found %s, need to upload', change_id)
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001106 logging.error('The following commit needs to be pushed to coreboot.org:')
1107 logging.error(' Branch "%s"', commit['branch_name'])
1108 logging.error(' in directory "%s"', commit['dir'])
1109 logging.error(' with change-id "%s"', commit['change_id'])
1110 logging.error('Please push the branch to review.coreboot.org, '
1111 'and then re-start this program with --continue')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001112 # Since this commit needs to be uploaded, do not continue after
1113 # this step returns.
1114 rc = False
1115 else:
1116 instance_name = commit['gerrit']
1117 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001118 logging.debug('Already uploaded (%s, %s)', instance_name, cl_number)
Paul Fagerburg8d850932020-02-25 14:13:32 -07001119
1120 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001121
1122
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001123def query_gerrit(instance, change_id):
1124 """Search a gerrit instance for a specific change_id
1125
1126 Args:
1127 instance: gerrit instance to query. Suitable values come from
1128 gerrit.GetCrosInternal() and gerrit.GetCrosExternal()
1129 change_id: The change_id to search for
1130
1131 Returns:
1132 CL number if found, None if not
1133 """
1134 raw = instance.Query(change=change_id, raw=True)
1135 if raw:
1136 # If the CL was found by change_id, there will be only one,
1137 # because the change_id is used to recognize a new patchset
1138 # on an existing CL.
1139 return raw[0]['number']
1140
1141 return None
1142
1143
Paul Fagerburg8d850932020-02-25 14:13:32 -07001144def query_coreboot_gerrit(change_id):
1145 """Search the coreboot gerrit for a specific change_id
1146
1147 Use the REST API to look for the change_id. See
1148 https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html
1149 for details on the REST API to search for a change-id.
1150
1151 We can't use query_gerrit with a manually constructed GerritHelper
1152 because we need the user's private SSH key to access review.coreboot.org,
1153 but these are not available inside the chroot.
1154
1155 Args:
1156 change_id: The change_id to search for
1157
1158 Returns:
1159 CL number if found, None if not
1160 """
1161 r = requests.get('https://review.coreboot.org/changes/' + change_id)
1162 response = r.content.decode('utf-8')
1163 # Check if the response starts with 'Not found', in which case return None
1164 if response.startswith('Not found:'):
1165 return None
1166 # Strip off the initial )]}'\n that is used as XSS protections, see
1167 # https://gerrit-review.googlesource.com/Documentation/rest-api.html#output
1168 # and decode as JSON.
1169 data = json.loads(response[5:])
1170 if '_number' in data:
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001171 return str(data['_number'])
Paul Fagerburg8d850932020-02-25 14:13:32 -07001172 return None
1173
1174
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001175def find_change_id(change_id):
1176 """Search the public and private ChromeOS gerrit instances for a change-id
1177
1178 Args:
1179 change_id: Change-Id to search for in both gerrit instances
1180
1181 Returns:
1182 Tuple of the gerrit instance ('chromium' or 'chrome-internal') and
1183 the CL number if the Change-Id is found.
1184 None if not found.
1185 """
1186 cl_number = query_gerrit(gerrit.GetCrosExternal(), change_id)
1187 if cl_number:
1188 return 'chromium', cl_number
1189 cl_number = query_gerrit(gerrit.GetCrosInternal(), change_id)
1190 if cl_number:
1191 return 'chrome-internal', cl_number
Paul Fagerburg8d850932020-02-25 14:13:32 -07001192 cl_number = query_coreboot_gerrit(change_id)
1193 if cl_number:
1194 return 'coreboot', cl_number
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001195 return None
1196
1197
1198def save_cl_data(status, commit_key, cl):
1199 """Save the gerrit instance and CL number to the yaml file
1200
1201 Args:
1202 status: variant_status object tracking our board, variant, etc.
1203 commit_key: Which key in the commits map we're processing
1204 cl: Value returned by find_change_id, should be a tuple
1205 of instance_name, cl_number
1206 """
1207 instance_name, cl_number = cl
1208 print(f'Found ({instance_name}, {cl_number}), saving to yaml')
1209 status.commits[commit_key]['gerrit'] = instance_name
1210 status.commits[commit_key]['cl_number'] = cl_number
1211 status.save()
1212
1213
1214def repo_upload(branch_name, cwd):
1215 """Upload a branch to gerrit
1216
1217 This function runs `repo upload` in the specified directory to upload
1218 a branch to gerrit. Because it's operating in a directory and with a
1219 branch name, it could upload more than one commit, which is OK because
1220 we'll look for each commit by change-id before trying to upload in that
1221 directory. For example, this happens in Zork, where the cb_config step
1222 and the cras_config step both have a commit in src/overlays. When we're
1223 processing the cb_config step and we `repo upload` in src/overlays, it
1224 will also upload the commit for cras_config. Then we come around to the
1225 cras_config step, and since we can find a CL with the change-id, we don't
1226 try to upload again.
1227
1228 Args:
1229 branch_name: the name of the branch to upload. Gets passed to
1230 repo upload with the --br flag
1231 cwd: directory where we want to upload. Gets set as the working
1232 directory for executing repo upload.
1233
1234 Returns:
1235 True if repo upload exits with a successful error code, false otherwise
1236 """
Paul Fagerburg042a5252020-03-16 21:49:18 -06001237 return run_process(
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001238 ['repo',
1239 'upload',
1240 '.',
1241 '--br=' + branch_name,
1242 '--wip',
1243 '--verify',
Paul Fagerburgda6bc102020-08-31 19:27:04 -06001244 '--yes',
1245 '--hashtag=new_variant'],
Paul Fagerburg042a5252020-03-16 21:49:18 -06001246 cwd=cwd)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001247
1248
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001249def upload_CLs(status):
1250 """Upload all CLs to chromiumos
1251
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001252 Args:
1253 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001254
1255 Returns:
1256 True if the build succeeded, False if something failed
1257 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001258 logging.info('Running step upload_CLs')
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001259
1260 for commit_key in status.repo_upload_list:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001261 logging.debug('Processing key %s', commit_key)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001262 commit = status.commits[commit_key]
1263 if 'gerrit' not in commit or 'cl_number' not in commit:
1264 change_id = commit['change_id']
1265 cl = find_change_id(change_id)
1266 if cl is not None:
1267 save_cl_data(status, commit_key, cl)
1268 else:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001269 logging.debug('Not found %s, need to upload', change_id)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001270 if not repo_upload(commit['branch_name'], commit['dir']):
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001271 logging.error('Repo upload %s in %s failed!',
1272 commit['branch_name'],
1273 commit['dir'])
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001274 return False
1275 cl = find_change_id(change_id)
1276 if cl is None:
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001277 logging.error('repo upload %s succeeded, but change_id is not found!',
1278 commit_key)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001279 return False
1280 save_cl_data(status, commit_key, cl)
1281 else:
1282 instance_name = commit['gerrit']
1283 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001284 logging.debug('Already uploaded (%s, %s)', instance_name, cl_number)
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001285
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001286 return True
1287
1288
1289def find_coreboot_upstream(status):
1290 """Find the coreboot CL after it has been upstreamed to chromiumos
1291
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001292 When the coreboot variant CL is first uploaded to review.coreboot.org,
1293 it is not visible in the chromiumos tree (and also cannot be used as
1294 a target for cq-depend). There is a process for upstreaming CLs from
1295 coreboot after they have been reviewed, approved, and merged. We can
1296 track a specific coreboot CL if we know the change-id that it used on
1297 the coreboot gerrit instance, by looking for that change-id as
1298 'original-change-id' in the public chromium gerrit instance.
1299
1300 The change-id for the coreboot variant will be under the 'cb_variant' key,
1301 but this is for the 'coreboot' gerrit instance.
1302
1303 When we find the upstreamed CL, we will record the gerrit instance and
1304 CL number in the yaml file under the 'find' key ("find upstream coreboot")
1305 so that we don't need to search coreboot again.
1306
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001307 Args:
1308 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001309
1310 Returns:
1311 True if the build succeeded, False if something failed
1312 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001313 logging.info('Running step find_coreboot_upstream')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001314
1315 # If we have already found the upstream coreboot CL, then exit with success
1316 if step_names.FIND in status.commits:
1317 commit = status.commits[step_names.FIND]
1318 if 'gerrit' in commit and 'cl_number' in commit:
1319 instance_name = commit['gerrit']
1320 cl_number = commit['cl_number']
Paul Fagerburg6cba1492020-06-02 16:44:05 -06001321 logging.debug('Already found (%s, %s)', instance_name, cl_number)
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001322 return True
1323
1324 # Make sure we have a CB_VARIANT commit and a change_id for it
1325 if step_names.CB_VARIANT not in status.commits:
1326 logging.error('Key %s not found in status.commits',
1327 step_names.CB_VARIANT)
1328 return False
1329 if 'change_id' not in status.commits[step_names.CB_VARIANT]:
1330 logging.error('Key change_id not found in status.commits[%s]',
1331 step_names.CB_VARIANT)
1332 return False
1333
1334 # Find the CL by the Original-Change-Id
1335 original_change_id = status.commits[step_names.CB_VARIANT]['change_id']
1336 gerrit_query_args = {
1337 'Original-Change-Id': original_change_id
1338 }
1339 cros = gerrit.GetCrosExternal()
1340 upstream = cros.Query(**gerrit_query_args)
1341 # If nothing is found, the patch hasn't been upstreamed yet
1342 if not upstream:
1343 logging.error('Program cannot continue until coreboot CL is upstreamed.')
1344 logging.error('(coreboot:%s, change-id %s)',
1345 status.commits[step_names.CB_VARIANT]['cl_number'],
1346 status.commits[step_names.CB_VARIANT]['change_id'])
1347 logging.error('Please wait for the CL to be upstreamed, then run this'
1348 ' program again with --continue')
1349 return False
1350
1351 # If more than one CL is found, something is very wrong
1352 if len(upstream) != 1:
1353 logging.error('More than one CL was found with Original-Change-Id %s',
1354 original_change_id)
1355 return False
1356
1357 # At this point, we know there is only one CL and we can get the
1358 # repo and CL number by splitting on the colon between them.
1359 patchlink = upstream[0].PatchLink()
1360 instance_name, cl_number = patchlink.split(':')
1361
1362 # Can't use get_git_commit_data because we're not pulling this
1363 # information from a git commit, but rather from gerrit.
1364 # We only need the gerrit instance and the CL number so we can have
1365 # other CLs cq-depend on this CL. The other keys are not needed because:
1366 # dir - not needed because we're not going to `cd` there to `repo upload`
1367 # branch_name - not valid; the CL is already merged
1368 # change_id - we use the change_id to find a CL number, and since we
1369 # just found the CL number via original-change-id, this is moot.
1370 status.commits[step_names.FIND] = {
1371 'gerrit': instance_name,
1372 'cl_number': str(cl_number)
1373 }
1374
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001375 return True
1376
1377
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001378def calc_cq_depend(status):
1379 """Determine the list of CLs for each commit that has dependencies.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001380
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001381 status.depends is a map of dependencies from step name to a list of
1382 steps that the step depends on. For each step, find the SHA of the
1383 commit, then find the gerrit instance and CL number of the commits
1384 that it depends on. Construct the Cq-Depends list and save it under
1385 the 'cq_depend' key, i.e. commit['add_priv_yaml']['cq_depend'] or
1386 as it will be stored in the yaml:
1387 commits:
1388 add_priv_yaml:
1389 cq_depend: 'chromium:1629121, chromium:1638243'
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001390
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001391 Args:
1392 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001393
1394 Returns:
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001395 True if all dependencies have been calculated. False if something
1396 failed, usually a commit not found by Change-Id.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001397 """
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001398 logging.info('Running step calc_cq_depend')
1399 # Iterate through the commits that have dependencies.
1400 for key in status.depends:
1401 logging.debug('Processing %s to add dependencies', key)
1402 # For every commit that has dependencies, find the gerrit instance
1403 # and CL number of the dependencies.
1404 cq_depend_list = []
1405 for depend_key in status.depends[key]:
1406 depend_commit = status.commits[depend_key]
1407 if not 'gerrit' in depend_commit:
1408 logging.error('Commit %s does not have a gerrit instance',
1409 depend_key)
1410 return False
1411 if not 'cl_number' in depend_commit:
1412 logging.error('Commit %s does not have a CL number',
1413 depend_key)
1414 return False
1415
1416 instance_name = depend_commit['gerrit']
1417 cl_number = depend_commit['cl_number']
1418 cq_depend_list.append(f'{instance_name}:{cl_number}')
1419
1420 # Add the 'cq_depend' key to the commit.
1421 cq_depend_str = 'Cq-Depend: %s' % ', '.join(cq_depend_list)
1422 logging.debug('Add to commit %s %s', key, cq_depend_str)
1423 status.commits[key]['cq_depend'] = cq_depend_str
1424
1425 return True
1426
1427
1428def add_cq_depend_to_commit_msg(git_repo, change_id, cq_depend_str):
1429 """Update the commit message with a Cq-Depends line.
1430
1431 Find the SHA of the commit, then use git filter-branch --msg-filter
1432 to add the Cq-Depend line just before the Change-Id line. See
1433 https://chromium.googlesource.com/chromiumos/docs/+/HEAD/contributing.md#cq-depend
1434 for details about Cq-Depend format and location.
1435
1436 Args:
1437 git_repo: Directory of git repository.
1438 change_id: The Change-Id to search for.
1439 cq_depend_str: The Cq-Depend string. It must be in the correct format
1440 per chromeos documentation, ready to insert into the commit msg
1441 on the line before Change-Id.
1442
1443 Returns:
1444 True if `git filter-branch` was successful. False if the command
1445 failed.
1446 """
1447 logging.debug('find SHA of Change-Id %s in %s', change_id, git_repo)
1448 sha = change_id_to_sha(git_repo, change_id)
1449 if sha is None:
1450 logging.error('Cannot find the SHA for Change-Id %s in %s',
1451 change_id, git_repo)
1452 return False
1453 logging.debug('SHA = %s', sha)
1454
1455 # Check if the commit message already has a Cq-Depend line.
1456 msg = get_commit_msg(git_repo, sha)
1457 if any('Cq-Depend' in tmpstr for tmpstr in msg):
1458 logging.debug('Already has Cq-Depend')
1459 return True
1460
1461 # Use git filter-branch --msg-filter to add the Cq-Depend line just
1462 # before the Change-Id line.
1463 environ = {**os.environ, 'FILTER_BRANCH_SQUELCH_WARNING': '1'}
1464 cmd = [
1465 'git',
1466 'filter-branch',
Paul Fagerburg5f6d2012020-06-24 11:29:36 -06001467 '-f',
Paul Fagerburgf5f8c7e2020-03-25 10:06:52 -06001468 '--msg-filter',
1469 f'sed -E "s/^(Change-Id: {change_id})$/{cq_depend_str}\\n\\1/"',
1470 '--',
1471 f'{sha}^..']
1472 return run_process(cmd, cwd=git_repo, env=environ)
1473
1474
1475def add_cq_depend(status):
1476 """Add Cq-Depend to commits and flag them for re-upload.
1477
1478 Args:
1479 status: variant_status object tracking our board, variant, etc.
1480
1481 Returns:
1482 True if the commit messages have been successfully amended, False if
1483 something failed.
1484 """
1485 logging.info('Running step add_cq_depend')
1486 for key in status.commits:
1487 commit = status.commits[key]
1488 if 'cq_depend' in commit:
1489 logging.debug('%s has %s', key, commit['cq_depend'])
1490 # Make sure the commit has a working directory and a change_id
1491 # before trying to amend its commit message.
1492 if 'dir' not in commit or 'change_id' not in commit:
1493 logging.error('Missing dir and/or change_id from %s', key)
1494 return False
1495
1496 if not add_cq_depend_to_commit_msg(commit['dir'],
1497 commit['change_id'],
1498 commit['cq_depend']):
1499 return False
1500 commit['needs_re_upload'] = True
1501 else:
1502 logging.debug('%s no dependencies', key)
1503
1504 return True
1505
1506
1507def re_upload(status):
1508 """Re-upload commits that have changed.
1509
1510 Args:
1511 status: variant_status object tracking our board, variant, etc.
1512
1513 Returns:
1514 True if the uploads succeeded. False if a repo upload failed.
1515 """
1516 logging.info('Running step re_upload')
1517 for key in status.commits:
1518 commit = status.commits[key]
1519 if commit.get('needs_re_upload'):
1520 logging.debug('Re-upload branch %s in %s', commit['branch_name'],
1521 commit['dir'])
1522 if not repo_upload(commit['branch_name'], commit['dir']):
1523 logging.error('Repo upload %s in %s failed!',
1524 commit['branch_name'],
1525 commit['dir'])
1526 return False
1527 commit['needs_re_upload'] = False
1528
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001529 return True
1530
1531
1532def clean_up(status):
1533 """Final clean-up, including delete the status file
1534
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001535 Args:
1536 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001537
1538 Returns:
1539 True
1540 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001541 logging.info('Running step clean_up')
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001542 status.rm()
1543 return True
1544
1545
Paul Fagerburg042a5252020-03-16 21:49:18 -06001546def abort(status):
1547 """Abort the creation of a new variant by abandoning commits
1548
1549 When the user specifies the --abort flag, we override status.step to
1550 be 'abort' and there is no transition from 'abort' to anything else.
1551 We look at status.commits and for each key, see if we have already
1552 been in that directory and abandoned that specific branch. If not,
1553 abandon the commit and then add the branch+dir to a list of abandoned
1554 commits. We do this because some boards (such as Zork) can have multiple
1555 commits in the same directory and with the same branch name, and we only
1556 want to repo abandon that branch once.
1557
1558 Args:
1559 status: variant_status object tracking our board, variant, etc.
1560
1561 Returns:
1562 True
1563 """
1564 logging.info('Running step abort')
1565 # Use the set 'abandoned' to keep track of each branch+dir abandoned.
1566 abandoned = set()
1567 for step in status.commits:
1568 logging.debug('Processing step %s', step)
1569 commit = status.commits[step]
1570 branch = commit['branch_name']
1571 cwd = commit['dir']
1572 if (branch, cwd) in abandoned:
1573 logging.debug('Branch %s in directory %s already abandoned',
1574 branch, cwd)
1575 else:
1576 logging.info('Abandoning branch %s in directory %s',
1577 branch, cwd)
1578 if run_process(['repo', 'abandon', branch, '.'], cwd=cwd):
1579 abandoned.add((branch, cwd))
1580 else:
1581 logging.error('Error while abandoning branch %s', branch)
1582 return False
1583
1584 return True
1585
1586
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001587if __name__ == '__main__':
1588 sys.exit(not int(main()))