blob: c35991bb242ac9b80ba8121f6377108863ed6eca [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
44import re
45import shutil
46import subprocess
47import sys
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 Fagerburg75398072020-03-16 13:51:24 -060050from chromite.lib import workon_helper
51from chromite.lib.build_target_lib import BuildTarget
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 """
68 board, variant, bug, continue_flag = get_args()
69
70 if not check_flags(board, variant, bug, continue_flag):
71 return False
72
73 status = get_status(board, variant, bug, continue_flag)
74 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 Fagerburgbab5dde2020-01-10 15:10:29 -070082 while status.step is not None:
Paul Fagerburg3b534f92019-11-07 15:05:22 -070083 status.save()
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070084 if not perform_step(status):
85 logging.debug('perform_step returned False; exiting ...')
Paul Fagerburg3b534f92019-11-07 15:05:22 -070086 return False
87
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070088 move_to_next_step(status)
Paul Fagerburg3b534f92019-11-07 15:05:22 -070089
90 return True
91
92
93def get_args():
94 """Parse the command-line arguments
95
96 There doesn't appear to be a way to specify that --continue is
97 mutually exclusive with --board, --variant, and --bug. As a result,
98 all arguments are optional, and another function will apply the logic
99 to check if there is an illegal combination of arguments.
100
101 Returns a list of:
Paul Fagerburge868e832020-01-22 17:14:04 -0700102 board Name of the reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700103 variant Name of the variant being created
104 bug Text for bug number, if any ('None' otherwise)
105 continue_flag Flag if --continue was specified
106 """
107 parser = argparse.ArgumentParser(
108 description=main.__doc__,
109 formatter_class=argparse.RawTextHelpFormatter)
Paul Fagerburge868e832020-01-22 17:14:04 -0700110 parser.add_argument('--board', type=str, help='Name of the reference board')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700111 parser.add_argument(
112 '--variant', type=str, help='Name of the new variant to create')
113 parser.add_argument(
114 '--bug', type=str, help='Bug number to reference in commits')
115 parser.add_argument(
116 '--continue', action='store_true',
117 dest='continue_flag', help='Continue the process from where it paused')
118 parser.add_argument(
119 '--verbose', action='store_true',
120 dest='verbose_flag', help='Enable verbose output of progress')
121 args = parser.parse_args()
122
123 if args.verbose_flag:
124 logging.basicConfig(level=logging.DEBUG)
125 else:
126 logging.basicConfig(level=logging.INFO)
127
128 board = args.board
129 if board is not None:
130 board = board.lower()
131
132 variant = args.variant
133 if variant is not None:
134 variant = variant.lower()
135
136 bug = args.bug or 'None'
137
138 return (board, variant, bug, args.continue_flag)
139
140
141def check_flags(board, variant, bug, continue_flag):
142 """Check the flags to ensure no invalid combinations
143
144 We allow any of the following:
145 --continue
146 --board=board_name --variant=variant_name
147 --board=board_name --variant=variant_name --bug=bug_text
148
149 The argument parser does have the functionality to represent the
150 combination of --board and --variant as a single mutually-exclusive
151 argument, so we have to use this function to do the checking.
152
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700153 Args:
154 board: Name of the reference board
155 variant: Name of the variant being created
156 bug: Text for bug number, if any ('None' otherwise)
157 continue_flag: Flag if --continue was specified
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700158
159 Returns:
160 True if the arguments are acceptable, False otherwise
161 """
162 if continue_flag:
163 if board is not None or variant is not None:
164 logging.error('--continue cannot have other options')
165 return False
166
167 if bug != 'None':
168 logging.error('--continue cannot have other options')
169 return False
170 else:
171 if board is None:
172 logging.error('--board must be specified')
173 return False
174
175 if variant is None:
176 logging.error('--variant must be specified')
177 return False
178
179 return True
180
181
182def file_exists(filepath, filename):
183 """Determine if a path and file exists
184
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700185 Args:
186 filepath: Path where build outputs should be found, e.g.
187 /build/hatch/firmware
188 filename: File that should exist in that path, e.g. image-sushi.bin
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700189
190 Returns:
191 True if file exists in that path, False otherwise
192 """
193 fullname = os.path.join(filepath, filename)
194 return os.path.exists(fullname) and os.path.isfile(fullname)
195
196
197def get_status(board, variant, bug, continue_flag):
198 """Create the status file or get the previous status
199
200 This program can stop at several places as we have to wait for CLs
201 to work through CQ or be upstreamed into the chromiumos tree, so just
202 like a git cherry-pick, there is a --continue option to pick up where
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700203 you left off by reading a specially-named status file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700204
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700205 If --continue is specified, the status file must exist.
206 If the status file exists, then --continue must be specified.
207 When --continue is specified, we read the status file and return
208 with the contents.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700209
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700210 If the status file does not exist, we will create the state file with
211 the board, variant, and (optional) bug details.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700212
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700213 To decouple the list of boards supported from this main program, we
Paul Fagerburge868e832020-01-22 17:14:04 -0700214 try to import a module with the same name as the reference board,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700215 so --board=hatch means that we import hatch.py. If we can't import
Paul Fagerburge868e832020-01-22 17:14:04 -0700216 the file, then we don't support that reference board.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700217
218 The board-specific module will set several variables, which we will
219 copy into the object that we return.
220
Paul Fagerburge868e832020-01-22 17:14:04 -0700221 * base - the name of the base board, such as Hatch, Volteer, or Zork.
222 This can be different from the reference board, e.g. the Trembyle
223 reference board in the Zork project.
Paul Fagerburg4d343452020-01-24 15:23:53 -0700224 * coreboot_dir - base directory for coreboot, usually third_party/coreboot
225 but could differ for processors that use a private repo
226 * cb_config_dir - base directory for coreboot configs, usually
227 third_party/chromiumos-overlay/sys-boot/coreboot/files/configs but
228 could differ for processors that use a private repo
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700229 * step_list - list of steps (named in step_names.py) to run in sequence
Paul Fagerburge868e832020-01-22 17:14:04 -0700230 to create the new variant of the reference board
231 * fsp - package name for FSP. This may be None, depending on the
232 processor on the reference board
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700233 * fitimage_pkg - package name for the fitimage
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700234 * fitimage_dir - directory for fitimage; prepend '/mnt/host/source/src/'
235 in chroot, prepend '~/chromiumos/src' outside the chroot
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700236 * workon_pkgs - list of packages to cros_workon
237 * emerge_cmd - the emerge command, e.g. 'emerge-hatch'
238 * emerge_pkgs - list of packages to emerge
239 * yaml_emerge_pkgs - list of packages to emerge just to build the yaml
240 * private_yaml_dir - directory for the private yaml file
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700241 * commits - map of commits for the various steps. Indexed by step name,
242 and the step names used are the same ones in step_names.py
243 * repo_upload_list - list of commits to upload using `repo upload`
244 * coreboot_push_list - list of commits to upload using `git push` to
245 coreboot
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700246
247 Additionally, the following fields will be set:
248
Paul Fagerburge868e832020-01-22 17:14:04 -0700249 * board - the name of the reference board, e.g. 'hatch'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700250 * variant - the name of the variant, e.g. 'sushi'
251 * bug - optional text for a bug ID, used in the git commit messages.
252 Could be 'None' (as text, not the python None), or something like
253 'b:12345' for buganizer, or 'chromium:12345'
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700254 * step - internal state tracking, what step of the variant creation
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700255 we are at.
256 * yaml_file - internal, just the name of the file where all this data
257 gets saved.
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700258 * commit - a map of maps that tracks all of the git commit and gerrit CL
259 data for each of the steps in the process. For example,
260 status.commit['add_priv_yaml'] is a map that has all the information
261 about the 'add_priv_yaml' step. The keys in the maps allow us to
262 determine where the commit is, the change_id, if it has been uploaded
263 to gerrit and where.
264
265 branch_name - the name of the git branch
266 change_id - the change-id assigned by the commit hook. Gerrit
267 uses the change_id to track new patchsets in the CL
268 dir - the directory where the commit has been created
269 gerrit - the name of the gerrit instance to which the CL has
270 been uploaded, one of 'chromium', 'chrome-internal', or
271 'coreboot'
272 cl_number - the CL number on the gerrit instance
273
274 When the commit is created, branch_name, change_id, and dir are all
275 set. The gerrit and cl_number keys are not set until the CL has been
276 uploaded to a gerrit instance.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700277
278 These data might come from the status file (because we read it), or
279 they might be the initial values after we created the file (because
280 it did not already exist).
281
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700282 Args:
283 board: Name of the reference board
284 variant: Name of the variant being created
285 bug: Text for bug number, if any ('None' otherwise)
286 continue_flag: Flag if --continue was specified
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700287
288 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700289 variant_status object with all the data mentioned above
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700290 """
291 status = variant_status.variant_status()
292 if continue_flag:
293 if not status.yaml_file_exists():
294 logging.error(
295 'new_variant is not in progress; nothing to --continue')
296 return None
297 else:
298 if status.yaml_file_exists():
299 logging.error(
300 'new_variant already in progress; did you forget --continue?')
301 return None
302
303 status.board = board
304 status.variant = variant
305 status.bug = bug
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700306
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700307 # We're just starting out, so load the appropriate module and copy
308 # all the data from it.
309 try:
310 module = importlib.import_module(board)
311 except ImportError:
Paul Fagerburge868e832020-01-22 17:14:04 -0700312 print('Unsupported board "' + board + '"')
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700313 sys.exit(1)
314
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700315 # pylint: disable=bad-whitespace
316 # Allow extra spaces around = so that we can line things up nicely
Paul Fagerburge868e832020-01-22 17:14:04 -0700317 status.base = module.base
Paul Fagerburg4d343452020-01-24 15:23:53 -0700318 status.coreboot_dir = module.coreboot_dir
319 status.cb_config_dir = module.cb_config_dir
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700320 status.emerge_cmd = module.emerge_cmd
321 status.emerge_pkgs = module.emerge_pkgs
322 status.fitimage_dir = module.fitimage_dir
323 status.fitimage_pkg = module.fitimage_pkg
Paul Fagerburg55dabf42020-01-27 15:30:09 -0700324 status.fitimage_cmd = module.fitimage_cmd
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700325 status.fsp = module.fsp
326 status.private_yaml_dir = module.private_yaml_dir
327 status.step_list = module.step_list
328 status.workon_pkgs = module.workon_pkgs
329 status.yaml_emerge_pkgs = module.yaml_emerge_pkgs
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700330 status.coreboot_push_list = module.coreboot_push_list
331 status.repo_upload_list = module.repo_upload_list
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700332 # pylint: enable=bad-whitespace
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700333
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700334 # Start at the first entry in the step list
335 status.step = status.step_list[0]
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700336
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700337 # Start a blank map for tracking CL data
338 status.commits = {}
339
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700340 status.save()
341
342 return status
343
344
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700345def perform_step(status):
346 """Call the appropriate function for the current step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700347
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700348 Args:
349 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700350
351 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700352 True if the step succeeded, False if it failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700353 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700354 # Function to call based on the step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700355 dispatch = {
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700356 step_names.CB_VARIANT: create_coreboot_variant,
357 step_names.CB_CONFIG: create_coreboot_config,
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700358 step_names.CRAS_CONFIG: copy_cras_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700359 step_names.ADD_FIT: add_fitimage,
360 step_names.GEN_FIT: gen_fit_image_outside_chroot,
361 step_names.COMMIT_FIT: commit_fitimage,
362 step_names.EC_IMAGE: create_initial_ec_image,
363 step_names.EC_BUILDALL: ec_buildall,
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700364 step_names.ADD_PUB_YAML: add_variant_to_public_yaml,
365 step_names.ADD_PRIV_YAML: add_variant_to_private_yaml,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700366 step_names.BUILD_YAML: build_yaml,
367 step_names.EMERGE: emerge_all,
368 step_names.PUSH: push_coreboot,
369 step_names.UPLOAD: upload_CLs,
370 step_names.FIND: find_coreboot_upstream,
371 step_names.CQ_DEPEND: add_cq_depends,
372 step_names.CLEAN_UP: clean_up,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700373 }
374
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700375 if status.step not in dispatch:
376 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700377 sys.exit(1)
378
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700379 return dispatch[status.step](status)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700380
381
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700382def move_to_next_step(status):
383 """Move to the next step in the list
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700384
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700385 Args:
386 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700387 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700388 if status.step not in status.step_list:
389 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700390 sys.exit(1)
391
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700392 idx = status.step_list.index(status.step)
393 if idx == len(status.step_list)-1:
394 status.step = None
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700395 else:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700396 status.step = status.step_list[idx+1]
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700397
398
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700399def run_process(args, cwd=None, env=None, capture_output=False):
400 """Run a process, log debug messages, return text output of process
401
402 The capture_output parameter allows us to capture the output when we
403 care about it (and not sending it to the screen), or ignoring it when
404 we don't care, and letting the user see the output so they know that
405 the build is still running, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700406
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700407 Args:
408 args: List of the command and its params
409 cwd: If not None, cd to this directory before running
410 env: Environment to use for execution; if needed, get os.environ.copy()
411 and add variables. If None, just use the current environment
412 capture_output: True if we should capture the stdout, false if we
413 just care about success or not.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700414
415 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700416 If capture_output == True, we return the text output from running
417 the subprocess as a list of lines, or None if the process failed.
418 If capture_output == False, we return a True if it successed, or
419 None if the process failed.
420
421 The caller can evaluate as a bool, because bool(None) == False, and
422 bool() of a non-empty list is True, or the caller can use the returned
423 text for further processing.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700424 """
425 logging.debug('Run %s', str(args))
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700426 if cwd is not None:
427 logging.debug('cwd = %s', cwd)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700428 try:
429 if capture_output:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700430 output = subprocess.run(args, cwd=cwd, env=env, check=True,
431 stderr=subprocess.STDOUT, stdout=subprocess.PIPE).stdout
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700432 else:
433 subprocess.run(args, cwd=cwd, env=env, check=True)
434 # Just something to decode so we don't get an empty list
435 output = b'True'
436
437 logging.debug('process returns 0')
438 # Convert from byte string to ASCII
439 decoded = output.decode('utf-8')
440 # Split into array of individual lines
441 lines = decoded.split('\n')
442 return lines
443 except subprocess.CalledProcessError as err:
444 logging.debug('process returns %s', str(err.returncode))
445 return None
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700446
447
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700448def get_git_commit_data(cwd):
449 """Get the branch name and change id of the current commit
450
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700451 Args:
452 cwd: The current working directory, where we want to get the branch
453 name and change id
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700454
455 Returns:
456 Map with 'dir', 'branch_name' and 'change_id' keys. The 'dir'
457 key maps to the value of os.path.expanduser(cwd)
458 """
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700459 cwd = git.FindGitTopLevel(os.path.expanduser(cwd))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700460 logging.debug('get_git_commit_data(%s)', cwd)
461
462 branch_name = git.GetCurrentBranch(cwd)
463 if branch_name is None:
464 logging.error('Cannot determine git branch name in %s; exiting', cwd)
465 sys.exit(1)
466 logging.debug('git current branch is %s', branch_name)
467
468 change_id = git.GetChangeId(cwd)
469 if change_id is None:
470 logging.error('Cannot determine Change-Id in %s; exiting', cwd)
471 sys.exit(1)
472 logging.debug('git Change-Id is %s', change_id)
473
474 return {
475 'dir': cwd,
476 'branch_name': branch_name,
477 'change_id': change_id
478 }
479
480
Paul Fagerburg75398072020-03-16 13:51:24 -0600481def cros_workon(status, action, workon_pkgs):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700482 """Call cros_workon for all the 9999 ebuilds we'll be touching
483
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700484 Args:
485 status: variant_status object tracking our board, variant, etc.
486 action: 'start' or 'stop'
Paul Fagerburg75398072020-03-16 13:51:24 -0600487 workon_pkgs: list of packages to start or stop
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700488
489 Returns:
490 True if the call to cros_workon was successful, False if failed
491 """
492
493 # Build up the command from all the packages in the list
Paul Fagerburg75398072020-03-16 13:51:24 -0600494 workon_cmd = ['cros_workon', '--board=' + status.base, action] + workon_pkgs
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700495 return bool(run_process(workon_cmd))
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700496
497
498def create_coreboot_variant(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700499 """Create source files for a new variant of the reference board in coreboot
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700500
501 This function calls create_coreboot_variant.sh to set up a new variant
Paul Fagerburge868e832020-01-22 17:14:04 -0700502 of the reference board.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700503
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700504 Args:
505 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700506
507 Returns:
508 True if everything succeeded, False if something failed
509 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700510 logging.info('Running step create_coreboot_variant')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700511 cb_src_dir = os.path.join('/mnt/host/source/src/', status.coreboot_dir)
512 environ = os.environ.copy()
513 environ['CB_SRC_DIR'] = cb_src_dir
514 create_coreboot_variant_sh = os.path.join(status.my_loc,
515 'create_coreboot_variant.sh')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700516 rc = bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700517 [create_coreboot_variant_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700518 status.base,
Paul Fagerburgabb15622020-02-07 15:41:29 -0700519 status.board,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700520 status.variant,
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700521 status.bug], env=environ))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700522 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700523 status.commits[step_names.CB_VARIANT] = get_git_commit_data(cb_src_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700524 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700525
526
527def create_coreboot_config(status):
528 """Create a coreboot configuration for a new variant
529
530 This function calls create_coreboot_config.sh, which will make a copy
531 of coreboot.${BOARD} into coreboot.${VARIANT}.
532
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700533 Args:
534 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700535
536 Returns:
537 True if the script and test build succeeded, False if something failed
538 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700539 logging.info('Running step create_coreboot_config')
Paul Fagerburg4d343452020-01-24 15:23:53 -0700540 environ = os.environ.copy()
541 if status.cb_config_dir is not None:
542 environ['CB_CONFIG_DIR'] = status.cb_config_dir
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700543 create_coreboot_config_sh = os.path.join(status.my_loc,
544 'create_coreboot_config.sh')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700545 rc = bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700546 [create_coreboot_config_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700547 status.base,
548 status.board,
549 status.variant,
Paul Fagerburg4d343452020-01-24 15:23:53 -0700550 status.bug], env=environ))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700551 if rc:
552 # Use status.cb_config_dir if defined, or if not, use
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700553 # '/mnt/host/source/src/third_party/chromiumos-overlay'
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700554 if status.cb_config_dir is not None:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700555 cb_config_dir = os.path.join('/mnt/host/source/src/', status.cb_config_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700556 else:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700557 cb_config_dir = '/mnt/host/source/src/third_party/chromiumos-overlay'
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700558 status.commits[step_names.CB_CONFIG] = get_git_commit_data(cb_config_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700559 return rc
Paul Fagerburge868e832020-01-22 17:14:04 -0700560
561
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700562def copy_cras_config(status):
563 """Copy the cras config for a new variant
564
565 This is only necessary for the Zork baseboard right now.
566 This function calls copy_cras_config.sh, which will copy the
567 cras config in
568 overlays/overlay-${BASE}/chromeos-base/chromeos-bsp-${BASE}/files/cras-config/${BASE}
569 to .../${VARIANT}
570
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700571 Args:
572 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700573
574 Returns:
575 True if the script and test build succeeded, False if something failed
576 """
577 logging.info('Running step copy_cras_config')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700578 copy_cras_config_sh = os.path.join(status.my_loc, 'copy_cras_config.sh')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700579 rc = bool(run_process(
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700580 [copy_cras_config_sh,
581 status.base,
582 status.board,
583 status.variant,
584 status.bug]))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700585 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700586 status.commits[step_names.CRAS_CONFIG] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700587 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700588 return rc
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700589
590
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700591def add_fitimage(status):
592 """Add the source files for a fitimage for the new variant
593
594 This function calls add_fitimage.sh to create a new XSL file for the
Paul Fagerburge868e832020-01-22 17:14:04 -0700595 variant's fitimage, which can override settings from the reference board's
596 XSL. When this is done, the user will have to build the fitimage by running
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700597 gen_fit_image.sh outside of the chroot (and outside of this program's
598 control) because gen_fit_image.sh uses WINE, which is not installed in
599 the chroot. (There is a linux version of FIT, but it requires Open GL,
600 which is also not installed in the chroot.)
601
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700602 Args:
603 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700604
605 Returns:
606 True if the script succeeded, False otherwise
607 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700608 logging.info('Running step add_fitimage')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700609 add_fitimage_sh = os.path.expanduser(os.path.join(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700610 '/mnt/host/source/src', status.fitimage_dir, 'files/add_fitimage.sh'))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700611 rc = bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700612 [add_fitimage_sh,
613 status.variant,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700614 status.bug]))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700615 if rc:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700616 fitimage_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir)
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700617 status.commits[step_names.COMMIT_FIT] = get_git_commit_data(fitimage_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700618 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700619
620
621def gen_fit_image_outside_chroot(status):
622 """Tell the user to run gen_fit_image.sh outside the chroot
623
624 As noted for add_Fitimage(), gen_fit_image.sh cannot run inside the
625 chroot. This function tells the user to run gen_fit_image.sh in
626 their normal environment, and then come back (--continue) when that
627 is done.
628
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700629 Args:
630 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700631
632 Returns:
633 True
634 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700635 logging.info('Running step gen_fit_image_outside_chroot')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700636 fit_image_files = check_fit_image_files(status)
637 # If the list is empty, then `not` of the list is True, so the files
638 # we need are all present and we can continue.
639 if not fit_image_files:
640 return True
641
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700642 logging.error('The following files need to be generated:')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700643 for filename in fit_image_files:
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700644 logging.error('* %s', filename)
645 logging.error('The fitimage sources are ready for gen_fit_image.sh to process.')
646 logging.error('gen_fit_image.sh cannot run inside the chroot. Please open a new terminal')
647 logging.error('window, change to the directory where gen_fit_image.sh is located, and run')
648 logging.error(status.fitimage_cmd, status.variant)
649 logging.error('Then re-start this program with --continue.')
650 logging.error('If your chroot is based in ~/chromiumos, then the folder you want is')
651 logging.error('~/chromiumos/src/%s/asset_generation', status.fitimage_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700652 return False
653
654
655def check_fit_image_files(status):
656 """Check if the fitimage has been generated
657
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700658 This function is not called directly as a step, and so it doesn't need
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700659 to produce any error messages to the user (except with --verbose).
660 gen_fit_image_outside_chroot will call this function to see if the
661 fitimage files exist, and if not, then that function will print the
662 message about how the user needs to run gen_fit_image.sh outside the
663 chroot.
664
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700665 Args:
666 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700667
668 Returns:
669 List of files that *DO NOT* exist and need to be created, [] if
670 all files are present.
671 """
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700672 outputs_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir,
673 'asset_generation/outputs')
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700674 logging.debug('outputs_dir = "%s"', outputs_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700675
676 files = []
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700677 if not file_exists(outputs_dir, 'fitimage-' + status.variant + '.bin'):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700678 files.append('fitimage-' + status.variant + '.bin')
679
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700680 if not file_exists(outputs_dir,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700681 'fitimage-' + status.variant + '-versions.txt'):
682 files.append('fitimage-' + status.variant + '-versions.txt')
683
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700684 if not file_exists(outputs_dir, 'fit.log'):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700685 files.append('fit.log')
686
687 return files
688
689
690def move_fitimage_file(fitimage_dir, filename):
691 """Move fitimage files from create-place to commit-place
692
693 commit_fitimage needs to move the fitimage files from the place where
694 they were created to a different directory in the tree. This utility
695 function handles joining paths and calling a file move function.
696
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700697 Args:
698 fitimage_dir: Directory where the fitimage files are
699 filename: Name of the file being moved
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700700
701 Returns:
702 True if the move succeeded, False if it failed
703 """
704 src_dir = os.path.join(fitimage_dir, 'asset_generation/outputs')
705 src = os.path.join(src_dir, filename)
706 dest_dir = os.path.join(fitimage_dir, 'files')
707 dest = os.path.join(dest_dir, filename)
708 # If src does not exist and dest does, the move is already done => success!
709 if not file_exists(src_dir, filename) and file_exists(dest_dir, filename):
710 logging.debug('move "%s", "%s" unnecessary because dest exists and'
711 ' src does not exist', src, dest)
712 return True
713
714 logging.debug('move "%s", "%s"', src, dest)
715 return shutil.move(src, dest)
716
717
718def commit_fitimage(status):
719 """Move the fitimage files and add them to a git commit
720
721 This function moves the fitimage binary and -versions files from
722 asset_generation/outputs to files/ and then adds those files and
723 fit.log to the existing git commit.
724
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700725 Args:
726 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700727
728 Returns:
729 True if the copy, git add, and git commit --amend all succeeded.
730 False if something failed.
731 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700732 logging.info('Running step commit_fitimage')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700733 fitimage_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700734 logging.debug('fitimage_dir = "%s"', fitimage_dir)
735
736 # The copy operation will check that the source file exists, so no
737 # need to check separately.
738 if not move_fitimage_file(fitimage_dir,
739 'fitimage-' + status.variant + '.bin'):
740 logging.error('Moving fitimage binary failed')
741 return False
742
743 if not move_fitimage_file(fitimage_dir,
744 'fitimage-' + status.variant + '-versions.txt'):
745 logging.error('Moving fitimage versions.txt failed')
746 return False
747
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700748 # TODO(pfagerburg) volteer also needs files/blobs/descriptor-${VARIANT}.bin
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700749 if not bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700750 ['git', 'add',
751 'asset_generation/outputs/fit.log',
752 'files/fitimage-' + status.variant + '.bin',
753 'files/fitimage-' + status.variant + '-versions.txt'
754 ],
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700755 cwd=fitimage_dir)):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700756 return False
757
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700758 return bool(run_process(['git', 'commit', '--amend', '--no-edit'],
759 cwd=fitimage_dir))
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700760
761
762def create_initial_ec_image(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700763 """Create an EC image for the variant as a clone of the reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700764
765 This function calls create_initial_ec_image.sh, which will clone the
Paul Fagerburge868e832020-01-22 17:14:04 -0700766 reference board to create the variant. The shell script will build the
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700767 EC code for the variant, but the repo upload hook insists that we
768 have done a `make buildall` before it will allow an upload, so this
769 function does the buildall.
770
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700771 Args:
772 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700773
774 Returns:
775 True if the script and test build succeeded, False if something failed
776 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700777 logging.info('Running step create_initial_ec_image')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700778 create_initial_ec_image_sh = os.path.join(status.my_loc,
779 'create_initial_ec_image.sh')
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700780 if not bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700781 [create_initial_ec_image_sh,
782 status.board,
783 status.variant,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700784 status.bug])):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700785 return False
786
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700787 # No need to `if rc:` because we already tested the run_process result above
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700788 status.commits[step_names.EC_IMAGE] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700789 '/mnt/host/source/src/platform/ec/board')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700790
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700791 # create_initial_ec_image.sh will build the ec.bin for this variant
792 # if successful.
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700793 ec = '/mnt/host/source/src/platform/ec'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700794 logging.debug('ec = "%s"', ec)
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700795 ec_bin = os.path.join('/build', status.base, 'firmware', status.variant,
796 'ec.bin')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700797 logging.debug('ec.bin = "%s"', ec_bin)
798
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700799 if not file_exists(ec, ec_bin):
800 logging.error('EC binary %s not found', ec_bin)
801 return False
802 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700803
804
805def ec_buildall(status):
806 """Do a make buildall -j for the EC, which is required for repo upload
807
808 The upload hook checks to ensure that the entire EC codebase builds
809 without error, so we have to run make buildall -j before uploading.
810
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700811 Args:
812 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700813
814 Returns:
815 True if the script and test build succeeded, False if something failed
816 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700817 logging.info('Running step ec_buildall')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700818 del status # unused parameter
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700819 ec = '/mnt/host/source/src/platform/ec'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700820 logging.debug('ec = "%s"', ec)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700821 return bool(run_process(['make', 'buildall', '-j'], cwd=ec))
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700822
823
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700824def add_variant_to_public_yaml(status):
825 """Add the new variant to the public model.yaml file
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700826
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700827 This function calls add_variant_to_yaml.sh to add the new variant to
828 the public model.yaml file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700829
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700830 Args:
831 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700832
833 Returns:
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700834 True if the script succeeded, False is something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700835 """
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700836 logging.info('Running step add_variant_to_public_yaml')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700837 add_variant_to_yaml_sh = os.path.join(status.my_loc,
838 'add_variant_to_yaml.sh')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700839 rc = bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700840 [add_variant_to_yaml_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700841 status.base,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700842 status.variant,
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700843 status.bug]))
844 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700845 status.commits[step_names.ADD_PUB_YAML] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700846 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700847 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700848
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700849
850def add_variant_to_private_yaml(status):
851 """Add the new variant to the private model.yaml file
852
853 This function calls add_variant.sh to add the new variant to
854 the private model.yaml file.
855
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700856 Args:
857 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700858
859 Returns:
860 True if the script succeeded, False is something failed
861 """
862 logging.info('Running step add_variant_to_private_yaml')
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700863 add_variant_sh = os.path.expanduser(os.path.join(status.private_yaml_dir, 'add_variant.sh'))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700864 rc = bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700865 [add_variant_sh,
866 status.variant,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700867 status.bug]))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700868 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700869 status.commits[step_names.ADD_PRIV_YAML] = get_git_commit_data(status.private_yaml_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700870 return rc
871
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700872
873
874def build_yaml(status):
875 """Build config files from the yaml files
876
877 This function builds the yaml files into the JSON and C code that
878 mosys and other tools use, then verifies that the new variant's name
879 shows up in all of the output files.
880
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700881 Args:
882 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700883
884 Returns:
885 True if the scripts and build succeeded, False is something failed
886 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700887 logging.info('Running step build_yaml')
888 if not bool(run_process([status.emerge_cmd] + status.yaml_emerge_pkgs)):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700889 return False
890
891 # Check generated files for occurences of the variant name.
892 # Each file should have at least one occurence, so use `grep -c` to
893 # count the occurrences of the variant name in each file.
894 # The results will be something like this:
895 # config.json:10
896 # yaml/config.c:6
897 # yaml/config.yaml:27
898 # yaml/model.yaml:6
899 # yaml/private-model.yaml:10
900 # If the variant name doesn't show up in the file, then the count
901 # will be 0, so we would see, e.g.
902 # config.json:0
Paul Fagerburge868e832020-01-22 17:14:04 -0700903 # Note that we leave out yaml/model.yaml (the public one) because for
904 # some boards, there is nothing in the public yaml file.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700905 # We gather the output from grep, then look for any of the strings
906 # ending in :0. If none of them match, then we're good, but if even
907 # one of them ends with :0 then there was a problem with generating
908 # the files from the yaml.
Paul Fagerburge868e832020-01-22 17:14:04 -0700909 chromeos_config = '/build/' + status.base + '/usr/share/chromeos-config'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700910 logging.debug('chromeos_config = "%s"', chromeos_config)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700911 grep = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700912 ['grep',
Paul Fagerburge868e832020-01-22 17:14:04 -0700913 '-ci',
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700914 status.variant,
915 'config.json',
916 'yaml/config.c',
917 'yaml/config.yaml',
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700918 'yaml/private-model.yaml'], cwd=chromeos_config, capture_output=True)
919
920 if grep is None:
921 return False
922
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700923 return not bool([s for s in grep if re.search(r':0$', s)])
924
925
926def emerge_all(status):
927 """Build the coreboot BIOS and EC code for the new variant
928
Paul Fagerburg75398072020-03-16 13:51:24 -0600929 This build step will cros_workon start a list of packages provided by
930 the reference board data as status.workon_pkgs, then emerge a list of
931 packages (status.emerge_pkgs), and then cros_workon stop any packages
932 that it started, as opposed to ones that were already being worked on.
933
934 To determine which packages this program started and which ones were
935 already started, we query the list of packages being worked on, then
936 cros_workon start the entire list (which will produce a "package already
937 being worked on" type of message for anything already started), and then
938 query the list of packages being worked on again. The difference between
939 the before and after lists are the packages that this program started,
940 and so that's the list of packages to cros_workon stop after the emerge
941 is done.
942
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700943 Args:
944 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700945
946 Returns:
947 True if the build succeeded, False if something failed
948 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700949 logging.info('Running step emerge_all')
Paul Fagerburg75398072020-03-16 13:51:24 -0600950 # Get the list of packages that are already cros_workon started.
951 build_target = BuildTarget(status.base)
952 workon = workon_helper.WorkonHelper(build_target.root, build_target.name)
953 before_workon = workon.ListAtoms()
954
955 # Start working on the list of packages specified by the reference board.
956 cros_workon(status, 'start', status.workon_pkgs)
957
958 # Get the list of packages that are cros_workon started now.
959 after_workon = workon.ListAtoms()
960 # Determine which packages we need to cros_workon stop.
961 stop_packages = list(set(after_workon) - set(before_workon))
962
963 # Build up the command for emerge from all the packages in the list.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700964 environ = os.environ.copy()
965 environ['FW_NAME'] = status.variant
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700966 emerge_cmd_and_params = [status.emerge_cmd] + status.emerge_pkgs
Paul Fagerburg75398072020-03-16 13:51:24 -0600967 emerge_result = bool(run_process(emerge_cmd_and_params, env=environ))
968
969 # cros_workon stop before possibly returning an error code.
970 cros_workon(status, 'stop', stop_packages)
971
972 # Check if emerge failed, and then check if the expected build outputs
973 # exist.
974 if not emerge_result:
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700975 return False
976
Paul Fagerburge868e832020-01-22 17:14:04 -0700977 build_path = '/build/' + status.base + '/firmware'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700978 logging.debug('build_path = "%s"', build_path)
979 if not file_exists(build_path, 'image-' + status.variant + '.bin'):
980 logging.error('emerge failed because image-%s.bin does not exist',
981 status.variant)
982 return False
983
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700984 if not file_exists(build_path, 'image-' + status.variant + '.serial.bin'):
985 logging.error('emerge failed because image-%s.serial.bin does not exist',
986 status.variant)
987 return False
988
989 return True
990
991
992def push_coreboot(status):
993 """Push the coreboot CL to coreboot.org
994
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700995 Args:
996 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700997
998 Returns:
999 True if the build succeeded, False if something failed
1000 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001001 logging.info('Running step push_coreboot')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001002
1003 # Set up a return code that may change to False if we find that a
1004 # coreboot CL has not been uploaded.
1005 rc = True
1006
1007 for commit_key in status.coreboot_push_list:
1008 logging.debug(f'Processing key {commit_key}')
1009 commit = status.commits[commit_key]
1010 if 'gerrit' not in commit or 'cl_number' not in commit:
1011 change_id = commit['change_id']
1012 cl = find_change_id(change_id)
1013 if cl is not None:
1014 save_cl_data(status, commit_key, cl)
1015 else:
1016 logging.debug(f'Not found {change_id}, need to upload')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001017 logging.error('The following commit needs to be pushed to coreboot.org:')
1018 logging.error(' Branch "%s"', commit['branch_name'])
1019 logging.error(' in directory "%s"', commit['dir'])
1020 logging.error(' with change-id "%s"', commit['change_id'])
1021 logging.error('Please push the branch to review.coreboot.org, '
1022 'and then re-start this program with --continue')
Paul Fagerburg8d850932020-02-25 14:13:32 -07001023 # Since this commit needs to be uploaded, do not continue after
1024 # this step returns.
1025 rc = False
1026 else:
1027 instance_name = commit['gerrit']
1028 cl_number = commit['cl_number']
1029 logging.debug(f'Already uploaded ({instance_name}, {cl_number})')
1030
1031 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001032
1033
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001034def query_gerrit(instance, change_id):
1035 """Search a gerrit instance for a specific change_id
1036
1037 Args:
1038 instance: gerrit instance to query. Suitable values come from
1039 gerrit.GetCrosInternal() and gerrit.GetCrosExternal()
1040 change_id: The change_id to search for
1041
1042 Returns:
1043 CL number if found, None if not
1044 """
1045 raw = instance.Query(change=change_id, raw=True)
1046 if raw:
1047 # If the CL was found by change_id, there will be only one,
1048 # because the change_id is used to recognize a new patchset
1049 # on an existing CL.
1050 return raw[0]['number']
1051
1052 return None
1053
1054
Paul Fagerburg8d850932020-02-25 14:13:32 -07001055def query_coreboot_gerrit(change_id):
1056 """Search the coreboot gerrit for a specific change_id
1057
1058 Use the REST API to look for the change_id. See
1059 https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html
1060 for details on the REST API to search for a change-id.
1061
1062 We can't use query_gerrit with a manually constructed GerritHelper
1063 because we need the user's private SSH key to access review.coreboot.org,
1064 but these are not available inside the chroot.
1065
1066 Args:
1067 change_id: The change_id to search for
1068
1069 Returns:
1070 CL number if found, None if not
1071 """
1072 r = requests.get('https://review.coreboot.org/changes/' + change_id)
1073 response = r.content.decode('utf-8')
1074 # Check if the response starts with 'Not found', in which case return None
1075 if response.startswith('Not found:'):
1076 return None
1077 # Strip off the initial )]}'\n that is used as XSS protections, see
1078 # https://gerrit-review.googlesource.com/Documentation/rest-api.html#output
1079 # and decode as JSON.
1080 data = json.loads(response[5:])
1081 if '_number' in data:
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001082 return str(data['_number'])
Paul Fagerburg8d850932020-02-25 14:13:32 -07001083 return None
1084
1085
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001086def find_change_id(change_id):
1087 """Search the public and private ChromeOS gerrit instances for a change-id
1088
1089 Args:
1090 change_id: Change-Id to search for in both gerrit instances
1091
1092 Returns:
1093 Tuple of the gerrit instance ('chromium' or 'chrome-internal') and
1094 the CL number if the Change-Id is found.
1095 None if not found.
1096 """
1097 cl_number = query_gerrit(gerrit.GetCrosExternal(), change_id)
1098 if cl_number:
1099 return 'chromium', cl_number
1100 cl_number = query_gerrit(gerrit.GetCrosInternal(), change_id)
1101 if cl_number:
1102 return 'chrome-internal', cl_number
Paul Fagerburg8d850932020-02-25 14:13:32 -07001103 cl_number = query_coreboot_gerrit(change_id)
1104 if cl_number:
1105 return 'coreboot', cl_number
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001106 return None
1107
1108
1109def save_cl_data(status, commit_key, cl):
1110 """Save the gerrit instance and CL number to the yaml file
1111
1112 Args:
1113 status: variant_status object tracking our board, variant, etc.
1114 commit_key: Which key in the commits map we're processing
1115 cl: Value returned by find_change_id, should be a tuple
1116 of instance_name, cl_number
1117 """
1118 instance_name, cl_number = cl
1119 print(f'Found ({instance_name}, {cl_number}), saving to yaml')
1120 status.commits[commit_key]['gerrit'] = instance_name
1121 status.commits[commit_key]['cl_number'] = cl_number
1122 status.save()
1123
1124
1125def repo_upload(branch_name, cwd):
1126 """Upload a branch to gerrit
1127
1128 This function runs `repo upload` in the specified directory to upload
1129 a branch to gerrit. Because it's operating in a directory and with a
1130 branch name, it could upload more than one commit, which is OK because
1131 we'll look for each commit by change-id before trying to upload in that
1132 directory. For example, this happens in Zork, where the cb_config step
1133 and the cras_config step both have a commit in src/overlays. When we're
1134 processing the cb_config step and we `repo upload` in src/overlays, it
1135 will also upload the commit for cras_config. Then we come around to the
1136 cras_config step, and since we can find a CL with the change-id, we don't
1137 try to upload again.
1138
1139 Args:
1140 branch_name: the name of the branch to upload. Gets passed to
1141 repo upload with the --br flag
1142 cwd: directory where we want to upload. Gets set as the working
1143 directory for executing repo upload.
1144
1145 Returns:
1146 True if repo upload exits with a successful error code, false otherwise
1147 """
1148 return bool(run_process(
1149 ['repo',
1150 'upload',
1151 '.',
1152 '--br=' + branch_name,
1153 '--wip',
1154 '--verify',
1155 '--yes'],
1156 cwd=cwd))
1157
1158
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001159def upload_CLs(status):
1160 """Upload all CLs to chromiumos
1161
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001162 Args:
1163 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001164
1165 Returns:
1166 True if the build succeeded, False if something failed
1167 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001168 logging.info('Running step upload_CLs')
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001169
1170 for commit_key in status.repo_upload_list:
1171 logging.debug(f'Processing key {commit_key}')
1172 commit = status.commits[commit_key]
1173 if 'gerrit' not in commit or 'cl_number' not in commit:
1174 change_id = commit['change_id']
1175 cl = find_change_id(change_id)
1176 if cl is not None:
1177 save_cl_data(status, commit_key, cl)
1178 else:
1179 logging.debug(f'Not found {change_id}, need to upload')
1180 if not repo_upload(commit['branch_name'], commit['dir']):
1181 branch_name = commit['branch_name']
1182 dirname = commit['dir']
1183 logging.error(f'Repo upload {branch_name} in {dirname} failed!')
1184 return False
1185 cl = find_change_id(change_id)
1186 if cl is None:
1187 logging.error(f'repo upload {commit_key} succeeded, ' \
1188 'but change_id is not found!')
1189 return False
1190 save_cl_data(status, commit_key, cl)
1191 else:
1192 instance_name = commit['gerrit']
1193 cl_number = commit['cl_number']
1194 logging.debug(f'Already uploaded ({instance_name}, {cl_number})')
1195
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001196 return True
1197
1198
1199def find_coreboot_upstream(status):
1200 """Find the coreboot CL after it has been upstreamed to chromiumos
1201
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001202 When the coreboot variant CL is first uploaded to review.coreboot.org,
1203 it is not visible in the chromiumos tree (and also cannot be used as
1204 a target for cq-depend). There is a process for upstreaming CLs from
1205 coreboot after they have been reviewed, approved, and merged. We can
1206 track a specific coreboot CL if we know the change-id that it used on
1207 the coreboot gerrit instance, by looking for that change-id as
1208 'original-change-id' in the public chromium gerrit instance.
1209
1210 The change-id for the coreboot variant will be under the 'cb_variant' key,
1211 but this is for the 'coreboot' gerrit instance.
1212
1213 When we find the upstreamed CL, we will record the gerrit instance and
1214 CL number in the yaml file under the 'find' key ("find upstream coreboot")
1215 so that we don't need to search coreboot again.
1216
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001217 Args:
1218 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001219
1220 Returns:
1221 True if the build succeeded, False if something failed
1222 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001223 logging.info('Running step find_coreboot_upstream')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001224
1225 # If we have already found the upstream coreboot CL, then exit with success
1226 if step_names.FIND in status.commits:
1227 commit = status.commits[step_names.FIND]
1228 if 'gerrit' in commit and 'cl_number' in commit:
1229 instance_name = commit['gerrit']
1230 cl_number = commit['cl_number']
1231 logging.debug(f'Already found ({instance_name}, {cl_number})')
1232 return True
1233
1234 # Make sure we have a CB_VARIANT commit and a change_id for it
1235 if step_names.CB_VARIANT not in status.commits:
1236 logging.error('Key %s not found in status.commits',
1237 step_names.CB_VARIANT)
1238 return False
1239 if 'change_id' not in status.commits[step_names.CB_VARIANT]:
1240 logging.error('Key change_id not found in status.commits[%s]',
1241 step_names.CB_VARIANT)
1242 return False
1243
1244 # Find the CL by the Original-Change-Id
1245 original_change_id = status.commits[step_names.CB_VARIANT]['change_id']
1246 gerrit_query_args = {
1247 'Original-Change-Id': original_change_id
1248 }
1249 cros = gerrit.GetCrosExternal()
1250 upstream = cros.Query(**gerrit_query_args)
1251 # If nothing is found, the patch hasn't been upstreamed yet
1252 if not upstream:
1253 logging.error('Program cannot continue until coreboot CL is upstreamed.')
1254 logging.error('(coreboot:%s, change-id %s)',
1255 status.commits[step_names.CB_VARIANT]['cl_number'],
1256 status.commits[step_names.CB_VARIANT]['change_id'])
1257 logging.error('Please wait for the CL to be upstreamed, then run this'
1258 ' program again with --continue')
1259 return False
1260
1261 # If more than one CL is found, something is very wrong
1262 if len(upstream) != 1:
1263 logging.error('More than one CL was found with Original-Change-Id %s',
1264 original_change_id)
1265 return False
1266
1267 # At this point, we know there is only one CL and we can get the
1268 # repo and CL number by splitting on the colon between them.
1269 patchlink = upstream[0].PatchLink()
1270 instance_name, cl_number = patchlink.split(':')
1271
1272 # Can't use get_git_commit_data because we're not pulling this
1273 # information from a git commit, but rather from gerrit.
1274 # We only need the gerrit instance and the CL number so we can have
1275 # other CLs cq-depend on this CL. The other keys are not needed because:
1276 # dir - not needed because we're not going to `cd` there to `repo upload`
1277 # branch_name - not valid; the CL is already merged
1278 # change_id - we use the change_id to find a CL number, and since we
1279 # just found the CL number via original-change-id, this is moot.
1280 status.commits[step_names.FIND] = {
1281 'gerrit': instance_name,
1282 'cl_number': str(cl_number)
1283 }
1284
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001285 return True
1286
1287
1288def add_cq_depends(status):
1289 """Add Cq-Depends to all of the CLs in chromiumos
1290
1291 The CL in coreboot needs to be pushed to coreboot.org, get merged,
1292 and then get upstreamed into the chromiumos tree before the other
1293 CLs can cq-depend on it and pass CQ.
1294
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001295 Args:
1296 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001297
1298 Returns:
1299 True if the build succeeded, False if something failed
1300 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001301 logging.info('Running step add_cq_depends')
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001302 del status # unused parameter
1303 logging.error('TODO (pfagerburg): implement add_cq_depends')
1304 return True
1305
1306
1307def clean_up(status):
1308 """Final clean-up, including delete the status file
1309
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001310 Args:
1311 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001312
1313 Returns:
1314 True
1315 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001316 logging.info('Running step clean_up')
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001317 status.rm()
1318 return True
1319
1320
1321if __name__ == '__main__':
1322 sys.exit(not int(main()))