blob: fb0be5bc9bd73e79616f9cda4b14c02ef3f397fe [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 Fagerburg8d850932020-02-25 14:13:32 -070050import requests
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070051import step_names
Paul Fagerburg3b534f92019-11-07 15:05:22 -070052import variant_status
53
54
55def main():
Paul Fagerburge868e832020-01-22 17:14:04 -070056 """Create a new variant of an existing reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -070057
58 This program automates the creation of a new variant of an existing
Paul Fagerburge868e832020-01-22 17:14:04 -070059 reference board by calling various scripts that copy the reference board,
60 modify files for the new variant, stage commits, and upload to gerrit.
Paul Fagerburg3b534f92019-11-07 15:05:22 -070061
62 Note that one of the following is required:
63 * --continue
64 * --board=BOARD --variant=VARIANT [--bug=BUG]
65 """
66 board, variant, bug, continue_flag = get_args()
67
68 if not check_flags(board, variant, bug, continue_flag):
69 return False
70
71 status = get_status(board, variant, bug, continue_flag)
72 if status is None:
73 return False
74
75 status.load()
76
Paul Fagerburg5f794bf2020-02-12 13:01:36 -070077 # Where is new_variant.py located?
78 status.my_loc = os.path.dirname(os.path.abspath(__file__))
79
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070080 while status.step is not None:
Paul Fagerburg3b534f92019-11-07 15:05:22 -070081 status.save()
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070082 if not perform_step(status):
83 logging.debug('perform_step returned False; exiting ...')
Paul Fagerburg3b534f92019-11-07 15:05:22 -070084 return False
85
Paul Fagerburgbab5dde2020-01-10 15:10:29 -070086 move_to_next_step(status)
Paul Fagerburg3b534f92019-11-07 15:05:22 -070087
88 return True
89
90
91def get_args():
92 """Parse the command-line arguments
93
94 There doesn't appear to be a way to specify that --continue is
95 mutually exclusive with --board, --variant, and --bug. As a result,
96 all arguments are optional, and another function will apply the logic
97 to check if there is an illegal combination of arguments.
98
99 Returns a list of:
Paul Fagerburge868e832020-01-22 17:14:04 -0700100 board Name of the reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700101 variant Name of the variant being created
102 bug Text for bug number, if any ('None' otherwise)
103 continue_flag Flag if --continue was specified
104 """
105 parser = argparse.ArgumentParser(
106 description=main.__doc__,
107 formatter_class=argparse.RawTextHelpFormatter)
Paul Fagerburge868e832020-01-22 17:14:04 -0700108 parser.add_argument('--board', type=str, help='Name of the reference board')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700109 parser.add_argument(
110 '--variant', type=str, help='Name of the new variant to create')
111 parser.add_argument(
112 '--bug', type=str, help='Bug number to reference in commits')
113 parser.add_argument(
114 '--continue', action='store_true',
115 dest='continue_flag', help='Continue the process from where it paused')
116 parser.add_argument(
117 '--verbose', action='store_true',
118 dest='verbose_flag', help='Enable verbose output of progress')
119 args = parser.parse_args()
120
121 if args.verbose_flag:
122 logging.basicConfig(level=logging.DEBUG)
123 else:
124 logging.basicConfig(level=logging.INFO)
125
126 board = args.board
127 if board is not None:
128 board = board.lower()
129
130 variant = args.variant
131 if variant is not None:
132 variant = variant.lower()
133
134 bug = args.bug or 'None'
135
136 return (board, variant, bug, args.continue_flag)
137
138
139def check_flags(board, variant, bug, continue_flag):
140 """Check the flags to ensure no invalid combinations
141
142 We allow any of the following:
143 --continue
144 --board=board_name --variant=variant_name
145 --board=board_name --variant=variant_name --bug=bug_text
146
147 The argument parser does have the functionality to represent the
148 combination of --board and --variant as a single mutually-exclusive
149 argument, so we have to use this function to do the checking.
150
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700151 Args:
152 board: Name of the reference board
153 variant: Name of the variant being created
154 bug: Text for bug number, if any ('None' otherwise)
155 continue_flag: Flag if --continue was specified
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700156
157 Returns:
158 True if the arguments are acceptable, False otherwise
159 """
160 if continue_flag:
161 if board is not None or variant is not None:
162 logging.error('--continue cannot have other options')
163 return False
164
165 if bug != 'None':
166 logging.error('--continue cannot have other options')
167 return False
168 else:
169 if board is None:
170 logging.error('--board must be specified')
171 return False
172
173 if variant is None:
174 logging.error('--variant must be specified')
175 return False
176
177 return True
178
179
180def file_exists(filepath, filename):
181 """Determine if a path and file exists
182
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700183 Args:
184 filepath: Path where build outputs should be found, e.g.
185 /build/hatch/firmware
186 filename: File that should exist in that path, e.g. image-sushi.bin
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700187
188 Returns:
189 True if file exists in that path, False otherwise
190 """
191 fullname = os.path.join(filepath, filename)
192 return os.path.exists(fullname) and os.path.isfile(fullname)
193
194
195def get_status(board, variant, bug, continue_flag):
196 """Create the status file or get the previous status
197
198 This program can stop at several places as we have to wait for CLs
199 to work through CQ or be upstreamed into the chromiumos tree, so just
200 like a git cherry-pick, there is a --continue option to pick up where
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700201 you left off by reading a specially-named status file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700202
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700203 If --continue is specified, the status file must exist.
204 If the status file exists, then --continue must be specified.
205 When --continue is specified, we read the status file and return
206 with the contents.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700207
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700208 If the status file does not exist, we will create the state file with
209 the board, variant, and (optional) bug details.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700210
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700211 To decouple the list of boards supported from this main program, we
Paul Fagerburge868e832020-01-22 17:14:04 -0700212 try to import a module with the same name as the reference board,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700213 so --board=hatch means that we import hatch.py. If we can't import
Paul Fagerburge868e832020-01-22 17:14:04 -0700214 the file, then we don't support that reference board.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700215
216 The board-specific module will set several variables, which we will
217 copy into the object that we return.
218
Paul Fagerburge868e832020-01-22 17:14:04 -0700219 * base - the name of the base board, such as Hatch, Volteer, or Zork.
220 This can be different from the reference board, e.g. the Trembyle
221 reference board in the Zork project.
Paul Fagerburg4d343452020-01-24 15:23:53 -0700222 * coreboot_dir - base directory for coreboot, usually third_party/coreboot
223 but could differ for processors that use a private repo
224 * cb_config_dir - base directory for coreboot configs, usually
225 third_party/chromiumos-overlay/sys-boot/coreboot/files/configs but
226 could differ for processors that use a private repo
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700227 * step_list - list of steps (named in step_names.py) to run in sequence
Paul Fagerburge868e832020-01-22 17:14:04 -0700228 to create the new variant of the reference board
229 * fsp - package name for FSP. This may be None, depending on the
230 processor on the reference board
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700231 * fitimage_pkg - package name for the fitimage
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700232 * fitimage_dir - directory for fitimage; prepend '/mnt/host/source/src/'
233 in chroot, prepend '~/chromiumos/src' outside the chroot
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700234 * workon_pkgs - list of packages to cros_workon
235 * emerge_cmd - the emerge command, e.g. 'emerge-hatch'
236 * emerge_pkgs - list of packages to emerge
237 * yaml_emerge_pkgs - list of packages to emerge just to build the yaml
238 * private_yaml_dir - directory for the private yaml file
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700239 * commits - map of commits for the various steps. Indexed by step name,
240 and the step names used are the same ones in step_names.py
241 * repo_upload_list - list of commits to upload using `repo upload`
242 * coreboot_push_list - list of commits to upload using `git push` to
243 coreboot
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700244
245 Additionally, the following fields will be set:
246
Paul Fagerburge868e832020-01-22 17:14:04 -0700247 * board - the name of the reference board, e.g. 'hatch'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700248 * variant - the name of the variant, e.g. 'sushi'
249 * bug - optional text for a bug ID, used in the git commit messages.
250 Could be 'None' (as text, not the python None), or something like
251 'b:12345' for buganizer, or 'chromium:12345'
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700252 * step - internal state tracking, what step of the variant creation
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700253 we are at.
254 * yaml_file - internal, just the name of the file where all this data
255 gets saved.
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700256 * commit - a map of maps that tracks all of the git commit and gerrit CL
257 data for each of the steps in the process. For example,
258 status.commit['add_priv_yaml'] is a map that has all the information
259 about the 'add_priv_yaml' step. The keys in the maps allow us to
260 determine where the commit is, the change_id, if it has been uploaded
261 to gerrit and where.
262
263 branch_name - the name of the git branch
264 change_id - the change-id assigned by the commit hook. Gerrit
265 uses the change_id to track new patchsets in the CL
266 dir - the directory where the commit has been created
267 gerrit - the name of the gerrit instance to which the CL has
268 been uploaded, one of 'chromium', 'chrome-internal', or
269 'coreboot'
270 cl_number - the CL number on the gerrit instance
271
272 When the commit is created, branch_name, change_id, and dir are all
273 set. The gerrit and cl_number keys are not set until the CL has been
274 uploaded to a gerrit instance.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700275
276 These data might come from the status file (because we read it), or
277 they might be the initial values after we created the file (because
278 it did not already exist).
279
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700280 Args:
281 board: Name of the reference board
282 variant: Name of the variant being created
283 bug: Text for bug number, if any ('None' otherwise)
284 continue_flag: Flag if --continue was specified
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700285
286 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700287 variant_status object with all the data mentioned above
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700288 """
289 status = variant_status.variant_status()
290 if continue_flag:
291 if not status.yaml_file_exists():
292 logging.error(
293 'new_variant is not in progress; nothing to --continue')
294 return None
295 else:
296 if status.yaml_file_exists():
297 logging.error(
298 'new_variant already in progress; did you forget --continue?')
299 return None
300
301 status.board = board
302 status.variant = variant
303 status.bug = bug
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700304
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700305 # We're just starting out, so load the appropriate module and copy
306 # all the data from it.
307 try:
308 module = importlib.import_module(board)
309 except ImportError:
Paul Fagerburge868e832020-01-22 17:14:04 -0700310 print('Unsupported board "' + board + '"')
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700311 sys.exit(1)
312
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700313 # pylint: disable=bad-whitespace
314 # Allow extra spaces around = so that we can line things up nicely
Paul Fagerburge868e832020-01-22 17:14:04 -0700315 status.base = module.base
Paul Fagerburg4d343452020-01-24 15:23:53 -0700316 status.coreboot_dir = module.coreboot_dir
317 status.cb_config_dir = module.cb_config_dir
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700318 status.emerge_cmd = module.emerge_cmd
319 status.emerge_pkgs = module.emerge_pkgs
320 status.fitimage_dir = module.fitimage_dir
321 status.fitimage_pkg = module.fitimage_pkg
Paul Fagerburg55dabf42020-01-27 15:30:09 -0700322 status.fitimage_cmd = module.fitimage_cmd
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700323 status.fsp = module.fsp
324 status.private_yaml_dir = module.private_yaml_dir
325 status.step_list = module.step_list
326 status.workon_pkgs = module.workon_pkgs
327 status.yaml_emerge_pkgs = module.yaml_emerge_pkgs
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700328 status.coreboot_push_list = module.coreboot_push_list
329 status.repo_upload_list = module.repo_upload_list
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700330 # pylint: enable=bad-whitespace
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700331
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700332 # Start at the first entry in the step list
333 status.step = status.step_list[0]
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700334
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700335 # Start a blank map for tracking CL data
336 status.commits = {}
337
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700338 status.save()
339
340 return status
341
342
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700343def perform_step(status):
344 """Call the appropriate function for the current step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700345
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700346 Args:
347 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700348
349 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700350 True if the step succeeded, False if it failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700351 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700352 # Function to call based on the step
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700353 dispatch = {
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700354 step_names.CB_VARIANT: create_coreboot_variant,
355 step_names.CB_CONFIG: create_coreboot_config,
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700356 step_names.CRAS_CONFIG: copy_cras_config,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700357 step_names.ADD_FIT: add_fitimage,
358 step_names.GEN_FIT: gen_fit_image_outside_chroot,
359 step_names.COMMIT_FIT: commit_fitimage,
360 step_names.EC_IMAGE: create_initial_ec_image,
361 step_names.EC_BUILDALL: ec_buildall,
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700362 step_names.ADD_PUB_YAML: add_variant_to_public_yaml,
363 step_names.ADD_PRIV_YAML: add_variant_to_private_yaml,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700364 step_names.BUILD_YAML: build_yaml,
365 step_names.EMERGE: emerge_all,
366 step_names.PUSH: push_coreboot,
367 step_names.UPLOAD: upload_CLs,
368 step_names.FIND: find_coreboot_upstream,
369 step_names.CQ_DEPEND: add_cq_depends,
370 step_names.CLEAN_UP: clean_up,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700371 }
372
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700373 if status.step not in dispatch:
374 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700375 sys.exit(1)
376
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700377 return dispatch[status.step](status)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700378
379
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700380def move_to_next_step(status):
381 """Move to the next step in the list
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700382
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700383 Args:
384 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700385 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700386 if status.step not in status.step_list:
387 logging.error('Unknown step "%s", aborting...', status.step)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700388 sys.exit(1)
389
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700390 idx = status.step_list.index(status.step)
391 if idx == len(status.step_list)-1:
392 status.step = None
Paul Fagerburgdd3e9c32020-01-07 14:00:35 -0700393 else:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700394 status.step = status.step_list[idx+1]
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700395
396
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700397def run_process(args, cwd=None, env=None, capture_output=False):
398 """Run a process, log debug messages, return text output of process
399
400 The capture_output parameter allows us to capture the output when we
401 care about it (and not sending it to the screen), or ignoring it when
402 we don't care, and letting the user see the output so they know that
403 the build is still running, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700404
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700405 Args:
406 args: List of the command and its params
407 cwd: If not None, cd to this directory before running
408 env: Environment to use for execution; if needed, get os.environ.copy()
409 and add variables. If None, just use the current environment
410 capture_output: True if we should capture the stdout, false if we
411 just care about success or not.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700412
413 Returns:
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700414 If capture_output == True, we return the text output from running
415 the subprocess as a list of lines, or None if the process failed.
416 If capture_output == False, we return a True if it successed, or
417 None if the process failed.
418
419 The caller can evaluate as a bool, because bool(None) == False, and
420 bool() of a non-empty list is True, or the caller can use the returned
421 text for further processing.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700422 """
423 logging.debug('Run %s', str(args))
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700424 if cwd is not None:
425 logging.debug('cwd = %s', cwd)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700426 try:
427 if capture_output:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700428 output = subprocess.run(args, cwd=cwd, env=env, check=True,
429 stderr=subprocess.STDOUT, stdout=subprocess.PIPE).stdout
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700430 else:
431 subprocess.run(args, cwd=cwd, env=env, check=True)
432 # Just something to decode so we don't get an empty list
433 output = b'True'
434
435 logging.debug('process returns 0')
436 # Convert from byte string to ASCII
437 decoded = output.decode('utf-8')
438 # Split into array of individual lines
439 lines = decoded.split('\n')
440 return lines
441 except subprocess.CalledProcessError as err:
442 logging.debug('process returns %s', str(err.returncode))
443 return None
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700444
445
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700446def get_git_commit_data(cwd):
447 """Get the branch name and change id of the current commit
448
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700449 Args:
450 cwd: The current working directory, where we want to get the branch
451 name and change id
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700452
453 Returns:
454 Map with 'dir', 'branch_name' and 'change_id' keys. The 'dir'
455 key maps to the value of os.path.expanduser(cwd)
456 """
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700457 cwd = git.FindGitTopLevel(os.path.expanduser(cwd))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700458 logging.debug('get_git_commit_data(%s)', cwd)
459
460 branch_name = git.GetCurrentBranch(cwd)
461 if branch_name is None:
462 logging.error('Cannot determine git branch name in %s; exiting', cwd)
463 sys.exit(1)
464 logging.debug('git current branch is %s', branch_name)
465
466 change_id = git.GetChangeId(cwd)
467 if change_id is None:
468 logging.error('Cannot determine Change-Id in %s; exiting', cwd)
469 sys.exit(1)
470 logging.debug('git Change-Id is %s', change_id)
471
472 return {
473 'dir': cwd,
474 'branch_name': branch_name,
475 'change_id': change_id
476 }
477
478
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700479def cros_workon(status, action):
480 """Call cros_workon for all the 9999 ebuilds we'll be touching
481
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700482 Args:
483 status: variant_status object tracking our board, variant, etc.
484 action: 'start' or 'stop'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700485
486 Returns:
487 True if the call to cros_workon was successful, False if failed
488 """
489
490 # Build up the command from all the packages in the list
Paul Fagerburge868e832020-01-22 17:14:04 -0700491 workon_cmd = ['cros_workon', '--board=' + status.base, action] + status.workon_pkgs
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700492 return bool(run_process(workon_cmd))
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700493
494
495def create_coreboot_variant(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700496 """Create source files for a new variant of the reference board in coreboot
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700497
498 This function calls create_coreboot_variant.sh to set up a new variant
Paul Fagerburge868e832020-01-22 17:14:04 -0700499 of the reference board.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700500
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700501 Args:
502 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700503
504 Returns:
505 True if everything succeeded, False if something failed
506 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700507 logging.info('Running step create_coreboot_variant')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700508 cb_src_dir = os.path.join('/mnt/host/source/src/', status.coreboot_dir)
509 environ = os.environ.copy()
510 environ['CB_SRC_DIR'] = cb_src_dir
511 create_coreboot_variant_sh = os.path.join(status.my_loc,
512 'create_coreboot_variant.sh')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700513 rc = bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700514 [create_coreboot_variant_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700515 status.base,
Paul Fagerburgabb15622020-02-07 15:41:29 -0700516 status.board,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700517 status.variant,
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700518 status.bug], env=environ))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700519 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700520 status.commits[step_names.CB_VARIANT] = get_git_commit_data(cb_src_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700521 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700522
523
524def create_coreboot_config(status):
525 """Create a coreboot configuration for a new variant
526
527 This function calls create_coreboot_config.sh, which will make a copy
528 of coreboot.${BOARD} into coreboot.${VARIANT}.
529
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700530 Args:
531 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700532
533 Returns:
534 True if the script and test build succeeded, False if something failed
535 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700536 logging.info('Running step create_coreboot_config')
Paul Fagerburg4d343452020-01-24 15:23:53 -0700537 environ = os.environ.copy()
538 if status.cb_config_dir is not None:
539 environ['CB_CONFIG_DIR'] = status.cb_config_dir
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700540 create_coreboot_config_sh = os.path.join(status.my_loc,
541 'create_coreboot_config.sh')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700542 rc = bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700543 [create_coreboot_config_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700544 status.base,
545 status.board,
546 status.variant,
Paul Fagerburg4d343452020-01-24 15:23:53 -0700547 status.bug], env=environ))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700548 if rc:
549 # Use status.cb_config_dir if defined, or if not, use
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700550 # '/mnt/host/source/src/third_party/chromiumos-overlay'
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700551 if status.cb_config_dir is not None:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700552 cb_config_dir = os.path.join('/mnt/host/source/src/', status.cb_config_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700553 else:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700554 cb_config_dir = '/mnt/host/source/src/third_party/chromiumos-overlay'
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700555 status.commits[step_names.CB_CONFIG] = get_git_commit_data(cb_config_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700556 return rc
Paul Fagerburge868e832020-01-22 17:14:04 -0700557
558
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700559def copy_cras_config(status):
560 """Copy the cras config for a new variant
561
562 This is only necessary for the Zork baseboard right now.
563 This function calls copy_cras_config.sh, which will copy the
564 cras config in
565 overlays/overlay-${BASE}/chromeos-base/chromeos-bsp-${BASE}/files/cras-config/${BASE}
566 to .../${VARIANT}
567
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700568 Args:
569 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700570
571 Returns:
572 True if the script and test build succeeded, False if something failed
573 """
574 logging.info('Running step copy_cras_config')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700575 copy_cras_config_sh = os.path.join(status.my_loc, 'copy_cras_config.sh')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700576 rc = bool(run_process(
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700577 [copy_cras_config_sh,
578 status.base,
579 status.board,
580 status.variant,
581 status.bug]))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700582 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700583 status.commits[step_names.CRAS_CONFIG] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700584 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700585 return rc
Paul Fagerburg2fd23f42020-02-07 14:04:48 -0700586
587
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700588def add_fitimage(status):
589 """Add the source files for a fitimage for the new variant
590
591 This function calls add_fitimage.sh to create a new XSL file for the
Paul Fagerburge868e832020-01-22 17:14:04 -0700592 variant's fitimage, which can override settings from the reference board's
593 XSL. When this is done, the user will have to build the fitimage by running
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700594 gen_fit_image.sh outside of the chroot (and outside of this program's
595 control) because gen_fit_image.sh uses WINE, which is not installed in
596 the chroot. (There is a linux version of FIT, but it requires Open GL,
597 which is also not installed in the chroot.)
598
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700599 Args:
600 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700601
602 Returns:
603 True if the script succeeded, False otherwise
604 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700605 logging.info('Running step add_fitimage')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700606 add_fitimage_sh = os.path.expanduser(os.path.join(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700607 '/mnt/host/source/src', status.fitimage_dir, 'files/add_fitimage.sh'))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700608 rc = bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700609 [add_fitimage_sh,
610 status.variant,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700611 status.bug]))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700612 if rc:
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700613 fitimage_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir)
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700614 status.commits[step_names.COMMIT_FIT] = get_git_commit_data(fitimage_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700615 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700616
617
618def gen_fit_image_outside_chroot(status):
619 """Tell the user to run gen_fit_image.sh outside the chroot
620
621 As noted for add_Fitimage(), gen_fit_image.sh cannot run inside the
622 chroot. This function tells the user to run gen_fit_image.sh in
623 their normal environment, and then come back (--continue) when that
624 is done.
625
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700626 Args:
627 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700628
629 Returns:
630 True
631 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700632 logging.info('Running step gen_fit_image_outside_chroot')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700633 fit_image_files = check_fit_image_files(status)
634 # If the list is empty, then `not` of the list is True, so the files
635 # we need are all present and we can continue.
636 if not fit_image_files:
637 return True
638
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700639 logging.error('The following files need to be generated:')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700640 for filename in fit_image_files:
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700641 logging.error('* %s', filename)
642 logging.error('The fitimage sources are ready for gen_fit_image.sh to process.')
643 logging.error('gen_fit_image.sh cannot run inside the chroot. Please open a new terminal')
644 logging.error('window, change to the directory where gen_fit_image.sh is located, and run')
645 logging.error(status.fitimage_cmd, status.variant)
646 logging.error('Then re-start this program with --continue.')
647 logging.error('If your chroot is based in ~/chromiumos, then the folder you want is')
648 logging.error('~/chromiumos/src/%s/asset_generation', status.fitimage_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700649 return False
650
651
652def check_fit_image_files(status):
653 """Check if the fitimage has been generated
654
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700655 This function is not called directly as a step, and so it doesn't need
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700656 to produce any error messages to the user (except with --verbose).
657 gen_fit_image_outside_chroot will call this function to see if the
658 fitimage files exist, and if not, then that function will print the
659 message about how the user needs to run gen_fit_image.sh outside the
660 chroot.
661
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700662 Args:
663 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700664
665 Returns:
666 List of files that *DO NOT* exist and need to be created, [] if
667 all files are present.
668 """
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700669 outputs_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir,
670 'asset_generation/outputs')
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700671 logging.debug('outputs_dir = "%s"', outputs_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700672
673 files = []
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700674 if not file_exists(outputs_dir, 'fitimage-' + status.variant + '.bin'):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700675 files.append('fitimage-' + status.variant + '.bin')
676
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700677 if not file_exists(outputs_dir,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700678 'fitimage-' + status.variant + '-versions.txt'):
679 files.append('fitimage-' + status.variant + '-versions.txt')
680
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700681 if not file_exists(outputs_dir, 'fit.log'):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700682 files.append('fit.log')
683
684 return files
685
686
687def move_fitimage_file(fitimage_dir, filename):
688 """Move fitimage files from create-place to commit-place
689
690 commit_fitimage needs to move the fitimage files from the place where
691 they were created to a different directory in the tree. This utility
692 function handles joining paths and calling a file move function.
693
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700694 Args:
695 fitimage_dir: Directory where the fitimage files are
696 filename: Name of the file being moved
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700697
698 Returns:
699 True if the move succeeded, False if it failed
700 """
701 src_dir = os.path.join(fitimage_dir, 'asset_generation/outputs')
702 src = os.path.join(src_dir, filename)
703 dest_dir = os.path.join(fitimage_dir, 'files')
704 dest = os.path.join(dest_dir, filename)
705 # If src does not exist and dest does, the move is already done => success!
706 if not file_exists(src_dir, filename) and file_exists(dest_dir, filename):
707 logging.debug('move "%s", "%s" unnecessary because dest exists and'
708 ' src does not exist', src, dest)
709 return True
710
711 logging.debug('move "%s", "%s"', src, dest)
712 return shutil.move(src, dest)
713
714
715def commit_fitimage(status):
716 """Move the fitimage files and add them to a git commit
717
718 This function moves the fitimage binary and -versions files from
719 asset_generation/outputs to files/ and then adds those files and
720 fit.log to the existing git commit.
721
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700722 Args:
723 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700724
725 Returns:
726 True if the copy, git add, and git commit --amend all succeeded.
727 False if something failed.
728 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700729 logging.info('Running step commit_fitimage')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700730 fitimage_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir)
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700731 logging.debug('fitimage_dir = "%s"', fitimage_dir)
732
733 # The copy operation will check that the source file exists, so no
734 # need to check separately.
735 if not move_fitimage_file(fitimage_dir,
736 'fitimage-' + status.variant + '.bin'):
737 logging.error('Moving fitimage binary failed')
738 return False
739
740 if not move_fitimage_file(fitimage_dir,
741 'fitimage-' + status.variant + '-versions.txt'):
742 logging.error('Moving fitimage versions.txt failed')
743 return False
744
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700745 # TODO(pfagerburg) volteer also needs files/blobs/descriptor-${VARIANT}.bin
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700746 if not bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700747 ['git', 'add',
748 'asset_generation/outputs/fit.log',
749 'files/fitimage-' + status.variant + '.bin',
750 'files/fitimage-' + status.variant + '-versions.txt'
751 ],
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700752 cwd=fitimage_dir)):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700753 return False
754
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700755 return bool(run_process(['git', 'commit', '--amend', '--no-edit'],
756 cwd=fitimage_dir))
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700757
758
759def create_initial_ec_image(status):
Paul Fagerburge868e832020-01-22 17:14:04 -0700760 """Create an EC image for the variant as a clone of the reference board
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700761
762 This function calls create_initial_ec_image.sh, which will clone the
Paul Fagerburge868e832020-01-22 17:14:04 -0700763 reference board to create the variant. The shell script will build the
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700764 EC code for the variant, but the repo upload hook insists that we
765 have done a `make buildall` before it will allow an upload, so this
766 function does the buildall.
767
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700768 Args:
769 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700770
771 Returns:
772 True if the script and test build succeeded, False if something failed
773 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700774 logging.info('Running step create_initial_ec_image')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700775 create_initial_ec_image_sh = os.path.join(status.my_loc,
776 'create_initial_ec_image.sh')
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700777 if not bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700778 [create_initial_ec_image_sh,
779 status.board,
780 status.variant,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700781 status.bug])):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700782 return False
783
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700784 # No need to `if rc:` because we already tested the run_process result above
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700785 status.commits[step_names.EC_IMAGE] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700786 '/mnt/host/source/src/platform/ec/board')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700787
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700788 # create_initial_ec_image.sh will build the ec.bin for this variant
789 # if successful.
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700790 ec = '/mnt/host/source/src/platform/ec'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700791 logging.debug('ec = "%s"', ec)
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700792 ec_bin = os.path.join('/build', status.base, 'firmware', status.variant,
793 'ec.bin')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700794 logging.debug('ec.bin = "%s"', ec_bin)
795
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700796 if not file_exists(ec, ec_bin):
797 logging.error('EC binary %s not found', ec_bin)
798 return False
799 return True
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700800
801
802def ec_buildall(status):
803 """Do a make buildall -j for the EC, which is required for repo upload
804
805 The upload hook checks to ensure that the entire EC codebase builds
806 without error, so we have to run make buildall -j before uploading.
807
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700808 Args:
809 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700810
811 Returns:
812 True if the script and test build succeeded, False if something failed
813 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700814 logging.info('Running step ec_buildall')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700815 del status # unused parameter
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700816 ec = '/mnt/host/source/src/platform/ec'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700817 logging.debug('ec = "%s"', ec)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700818 return bool(run_process(['make', 'buildall', '-j'], cwd=ec))
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700819
820
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700821def add_variant_to_public_yaml(status):
822 """Add the new variant to the public model.yaml file
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700823
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700824 This function calls add_variant_to_yaml.sh to add the new variant to
825 the public model.yaml file.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700826
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700827 Args:
828 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700829
830 Returns:
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700831 True if the script succeeded, False is something failed
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700832 """
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700833 logging.info('Running step add_variant_to_public_yaml')
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700834 add_variant_to_yaml_sh = os.path.join(status.my_loc,
835 'add_variant_to_yaml.sh')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700836 rc = bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700837 [add_variant_to_yaml_sh,
Paul Fagerburge868e832020-01-22 17:14:04 -0700838 status.base,
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700839 status.variant,
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700840 status.bug]))
841 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700842 status.commits[step_names.ADD_PUB_YAML] = get_git_commit_data(
Paul Fagerburg5f794bf2020-02-12 13:01:36 -0700843 '/mnt/host/source/src/overlays')
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700844 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700845
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700846
847def add_variant_to_private_yaml(status):
848 """Add the new variant to the private model.yaml file
849
850 This function calls add_variant.sh to add the new variant to
851 the private model.yaml file.
852
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700853 Args:
854 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700855
856 Returns:
857 True if the script succeeded, False is something failed
858 """
859 logging.info('Running step add_variant_to_private_yaml')
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700860 add_variant_sh = os.path.expanduser(os.path.join(status.private_yaml_dir, 'add_variant.sh'))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700861 rc = bool(run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700862 [add_variant_sh,
863 status.variant,
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700864 status.bug]))
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700865 if rc:
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700866 status.commits[step_names.ADD_PRIV_YAML] = get_git_commit_data(status.private_yaml_dir)
Paul Fagerburg1d043c32020-02-03 08:57:08 -0700867 return rc
868
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700869
870
871def build_yaml(status):
872 """Build config files from the yaml files
873
874 This function builds the yaml files into the JSON and C code that
875 mosys and other tools use, then verifies that the new variant's name
876 shows up in all of the output files.
877
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700878 Args:
879 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700880
881 Returns:
882 True if the scripts and build succeeded, False is something failed
883 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700884 logging.info('Running step build_yaml')
885 if not bool(run_process([status.emerge_cmd] + status.yaml_emerge_pkgs)):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700886 return False
887
888 # Check generated files for occurences of the variant name.
889 # Each file should have at least one occurence, so use `grep -c` to
890 # count the occurrences of the variant name in each file.
891 # The results will be something like this:
892 # config.json:10
893 # yaml/config.c:6
894 # yaml/config.yaml:27
895 # yaml/model.yaml:6
896 # yaml/private-model.yaml:10
897 # If the variant name doesn't show up in the file, then the count
898 # will be 0, so we would see, e.g.
899 # config.json:0
Paul Fagerburge868e832020-01-22 17:14:04 -0700900 # Note that we leave out yaml/model.yaml (the public one) because for
901 # some boards, there is nothing in the public yaml file.
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700902 # We gather the output from grep, then look for any of the strings
903 # ending in :0. If none of them match, then we're good, but if even
904 # one of them ends with :0 then there was a problem with generating
905 # the files from the yaml.
Paul Fagerburge868e832020-01-22 17:14:04 -0700906 chromeos_config = '/build/' + status.base + '/usr/share/chromeos-config'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700907 logging.debug('chromeos_config = "%s"', chromeos_config)
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700908 grep = run_process(
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700909 ['grep',
Paul Fagerburge868e832020-01-22 17:14:04 -0700910 '-ci',
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700911 status.variant,
912 'config.json',
913 'yaml/config.c',
914 'yaml/config.yaml',
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700915 'yaml/private-model.yaml'], cwd=chromeos_config, capture_output=True)
916
917 if grep is None:
918 return False
919
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700920 return not bool([s for s in grep if re.search(r':0$', s)])
921
922
923def emerge_all(status):
924 """Build the coreboot BIOS and EC code for the new variant
925
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700926 Args:
927 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700928
929 Returns:
930 True if the build succeeded, False if something failed
931 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700932 logging.info('Running step emerge_all')
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700933 cros_workon(status, 'start')
934 environ = os.environ.copy()
935 environ['FW_NAME'] = status.variant
936 # Build up the command for emerge from all the packages in the list
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700937 emerge_cmd_and_params = [status.emerge_cmd] + status.emerge_pkgs
938 if not bool(run_process(emerge_cmd_and_params, env=environ)):
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700939 return False
940
941 cros_workon(status, 'stop')
Paul Fagerburge868e832020-01-22 17:14:04 -0700942 build_path = '/build/' + status.base + '/firmware'
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700943 logging.debug('build_path = "%s"', build_path)
944 if not file_exists(build_path, 'image-' + status.variant + '.bin'):
945 logging.error('emerge failed because image-%s.bin does not exist',
946 status.variant)
947 return False
948
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700949 if not file_exists(build_path, 'image-' + status.variant + '.serial.bin'):
950 logging.error('emerge failed because image-%s.serial.bin does not exist',
951 status.variant)
952 return False
953
954 return True
955
956
957def push_coreboot(status):
958 """Push the coreboot CL to coreboot.org
959
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -0700960 Args:
961 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700962
963 Returns:
964 True if the build succeeded, False if something failed
965 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -0700966 logging.info('Running step push_coreboot')
Paul Fagerburg8d850932020-02-25 14:13:32 -0700967
968 # Set up a return code that may change to False if we find that a
969 # coreboot CL has not been uploaded.
970 rc = True
971
972 for commit_key in status.coreboot_push_list:
973 logging.debug(f'Processing key {commit_key}')
974 commit = status.commits[commit_key]
975 if 'gerrit' not in commit or 'cl_number' not in commit:
976 change_id = commit['change_id']
977 cl = find_change_id(change_id)
978 if cl is not None:
979 save_cl_data(status, commit_key, cl)
980 else:
981 logging.debug(f'Not found {change_id}, need to upload')
Paul Fagerburgaec8d992020-02-27 15:51:47 -0700982 logging.error('The following commit needs to be pushed to coreboot.org:')
983 logging.error(' Branch "%s"', commit['branch_name'])
984 logging.error(' in directory "%s"', commit['dir'])
985 logging.error(' with change-id "%s"', commit['change_id'])
986 logging.error('Please push the branch to review.coreboot.org, '
987 'and then re-start this program with --continue')
Paul Fagerburg8d850932020-02-25 14:13:32 -0700988 # Since this commit needs to be uploaded, do not continue after
989 # this step returns.
990 rc = False
991 else:
992 instance_name = commit['gerrit']
993 cl_number = commit['cl_number']
994 logging.debug(f'Already uploaded ({instance_name}, {cl_number})')
995
996 return rc
Paul Fagerburg3b534f92019-11-07 15:05:22 -0700997
998
Paul Fagerburga8c7e342020-02-25 13:30:49 -0700999def query_gerrit(instance, change_id):
1000 """Search a gerrit instance for a specific change_id
1001
1002 Args:
1003 instance: gerrit instance to query. Suitable values come from
1004 gerrit.GetCrosInternal() and gerrit.GetCrosExternal()
1005 change_id: The change_id to search for
1006
1007 Returns:
1008 CL number if found, None if not
1009 """
1010 raw = instance.Query(change=change_id, raw=True)
1011 if raw:
1012 # If the CL was found by change_id, there will be only one,
1013 # because the change_id is used to recognize a new patchset
1014 # on an existing CL.
1015 return raw[0]['number']
1016
1017 return None
1018
1019
Paul Fagerburg8d850932020-02-25 14:13:32 -07001020def query_coreboot_gerrit(change_id):
1021 """Search the coreboot gerrit for a specific change_id
1022
1023 Use the REST API to look for the change_id. See
1024 https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html
1025 for details on the REST API to search for a change-id.
1026
1027 We can't use query_gerrit with a manually constructed GerritHelper
1028 because we need the user's private SSH key to access review.coreboot.org,
1029 but these are not available inside the chroot.
1030
1031 Args:
1032 change_id: The change_id to search for
1033
1034 Returns:
1035 CL number if found, None if not
1036 """
1037 r = requests.get('https://review.coreboot.org/changes/' + change_id)
1038 response = r.content.decode('utf-8')
1039 # Check if the response starts with 'Not found', in which case return None
1040 if response.startswith('Not found:'):
1041 return None
1042 # Strip off the initial )]}'\n that is used as XSS protections, see
1043 # https://gerrit-review.googlesource.com/Documentation/rest-api.html#output
1044 # and decode as JSON.
1045 data = json.loads(response[5:])
1046 if '_number' in data:
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001047 return str(data['_number'])
Paul Fagerburg8d850932020-02-25 14:13:32 -07001048 return None
1049
1050
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001051def find_change_id(change_id):
1052 """Search the public and private ChromeOS gerrit instances for a change-id
1053
1054 Args:
1055 change_id: Change-Id to search for in both gerrit instances
1056
1057 Returns:
1058 Tuple of the gerrit instance ('chromium' or 'chrome-internal') and
1059 the CL number if the Change-Id is found.
1060 None if not found.
1061 """
1062 cl_number = query_gerrit(gerrit.GetCrosExternal(), change_id)
1063 if cl_number:
1064 return 'chromium', cl_number
1065 cl_number = query_gerrit(gerrit.GetCrosInternal(), change_id)
1066 if cl_number:
1067 return 'chrome-internal', cl_number
Paul Fagerburg8d850932020-02-25 14:13:32 -07001068 cl_number = query_coreboot_gerrit(change_id)
1069 if cl_number:
1070 return 'coreboot', cl_number
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001071 return None
1072
1073
1074def save_cl_data(status, commit_key, cl):
1075 """Save the gerrit instance and CL number to the yaml file
1076
1077 Args:
1078 status: variant_status object tracking our board, variant, etc.
1079 commit_key: Which key in the commits map we're processing
1080 cl: Value returned by find_change_id, should be a tuple
1081 of instance_name, cl_number
1082 """
1083 instance_name, cl_number = cl
1084 print(f'Found ({instance_name}, {cl_number}), saving to yaml')
1085 status.commits[commit_key]['gerrit'] = instance_name
1086 status.commits[commit_key]['cl_number'] = cl_number
1087 status.save()
1088
1089
1090def repo_upload(branch_name, cwd):
1091 """Upload a branch to gerrit
1092
1093 This function runs `repo upload` in the specified directory to upload
1094 a branch to gerrit. Because it's operating in a directory and with a
1095 branch name, it could upload more than one commit, which is OK because
1096 we'll look for each commit by change-id before trying to upload in that
1097 directory. For example, this happens in Zork, where the cb_config step
1098 and the cras_config step both have a commit in src/overlays. When we're
1099 processing the cb_config step and we `repo upload` in src/overlays, it
1100 will also upload the commit for cras_config. Then we come around to the
1101 cras_config step, and since we can find a CL with the change-id, we don't
1102 try to upload again.
1103
1104 Args:
1105 branch_name: the name of the branch to upload. Gets passed to
1106 repo upload with the --br flag
1107 cwd: directory where we want to upload. Gets set as the working
1108 directory for executing repo upload.
1109
1110 Returns:
1111 True if repo upload exits with a successful error code, false otherwise
1112 """
1113 return bool(run_process(
1114 ['repo',
1115 'upload',
1116 '.',
1117 '--br=' + branch_name,
1118 '--wip',
1119 '--verify',
1120 '--yes'],
1121 cwd=cwd))
1122
1123
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001124def upload_CLs(status):
1125 """Upload all CLs to chromiumos
1126
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001127 Args:
1128 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001129
1130 Returns:
1131 True if the build succeeded, False if something failed
1132 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001133 logging.info('Running step upload_CLs')
Paul Fagerburga8c7e342020-02-25 13:30:49 -07001134
1135 for commit_key in status.repo_upload_list:
1136 logging.debug(f'Processing key {commit_key}')
1137 commit = status.commits[commit_key]
1138 if 'gerrit' not in commit or 'cl_number' not in commit:
1139 change_id = commit['change_id']
1140 cl = find_change_id(change_id)
1141 if cl is not None:
1142 save_cl_data(status, commit_key, cl)
1143 else:
1144 logging.debug(f'Not found {change_id}, need to upload')
1145 if not repo_upload(commit['branch_name'], commit['dir']):
1146 branch_name = commit['branch_name']
1147 dirname = commit['dir']
1148 logging.error(f'Repo upload {branch_name} in {dirname} failed!')
1149 return False
1150 cl = find_change_id(change_id)
1151 if cl is None:
1152 logging.error(f'repo upload {commit_key} succeeded, ' \
1153 'but change_id is not found!')
1154 return False
1155 save_cl_data(status, commit_key, cl)
1156 else:
1157 instance_name = commit['gerrit']
1158 cl_number = commit['cl_number']
1159 logging.debug(f'Already uploaded ({instance_name}, {cl_number})')
1160
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001161 return True
1162
1163
1164def find_coreboot_upstream(status):
1165 """Find the coreboot CL after it has been upstreamed to chromiumos
1166
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001167 When the coreboot variant CL is first uploaded to review.coreboot.org,
1168 it is not visible in the chromiumos tree (and also cannot be used as
1169 a target for cq-depend). There is a process for upstreaming CLs from
1170 coreboot after they have been reviewed, approved, and merged. We can
1171 track a specific coreboot CL if we know the change-id that it used on
1172 the coreboot gerrit instance, by looking for that change-id as
1173 'original-change-id' in the public chromium gerrit instance.
1174
1175 The change-id for the coreboot variant will be under the 'cb_variant' key,
1176 but this is for the 'coreboot' gerrit instance.
1177
1178 When we find the upstreamed CL, we will record the gerrit instance and
1179 CL number in the yaml file under the 'find' key ("find upstream coreboot")
1180 so that we don't need to search coreboot again.
1181
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001182 Args:
1183 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001184
1185 Returns:
1186 True if the build succeeded, False if something failed
1187 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001188 logging.info('Running step find_coreboot_upstream')
Paul Fagerburgaec8d992020-02-27 15:51:47 -07001189
1190 # If we have already found the upstream coreboot CL, then exit with success
1191 if step_names.FIND in status.commits:
1192 commit = status.commits[step_names.FIND]
1193 if 'gerrit' in commit and 'cl_number' in commit:
1194 instance_name = commit['gerrit']
1195 cl_number = commit['cl_number']
1196 logging.debug(f'Already found ({instance_name}, {cl_number})')
1197 return True
1198
1199 # Make sure we have a CB_VARIANT commit and a change_id for it
1200 if step_names.CB_VARIANT not in status.commits:
1201 logging.error('Key %s not found in status.commits',
1202 step_names.CB_VARIANT)
1203 return False
1204 if 'change_id' not in status.commits[step_names.CB_VARIANT]:
1205 logging.error('Key change_id not found in status.commits[%s]',
1206 step_names.CB_VARIANT)
1207 return False
1208
1209 # Find the CL by the Original-Change-Id
1210 original_change_id = status.commits[step_names.CB_VARIANT]['change_id']
1211 gerrit_query_args = {
1212 'Original-Change-Id': original_change_id
1213 }
1214 cros = gerrit.GetCrosExternal()
1215 upstream = cros.Query(**gerrit_query_args)
1216 # If nothing is found, the patch hasn't been upstreamed yet
1217 if not upstream:
1218 logging.error('Program cannot continue until coreboot CL is upstreamed.')
1219 logging.error('(coreboot:%s, change-id %s)',
1220 status.commits[step_names.CB_VARIANT]['cl_number'],
1221 status.commits[step_names.CB_VARIANT]['change_id'])
1222 logging.error('Please wait for the CL to be upstreamed, then run this'
1223 ' program again with --continue')
1224 return False
1225
1226 # If more than one CL is found, something is very wrong
1227 if len(upstream) != 1:
1228 logging.error('More than one CL was found with Original-Change-Id %s',
1229 original_change_id)
1230 return False
1231
1232 # At this point, we know there is only one CL and we can get the
1233 # repo and CL number by splitting on the colon between them.
1234 patchlink = upstream[0].PatchLink()
1235 instance_name, cl_number = patchlink.split(':')
1236
1237 # Can't use get_git_commit_data because we're not pulling this
1238 # information from a git commit, but rather from gerrit.
1239 # We only need the gerrit instance and the CL number so we can have
1240 # other CLs cq-depend on this CL. The other keys are not needed because:
1241 # dir - not needed because we're not going to `cd` there to `repo upload`
1242 # branch_name - not valid; the CL is already merged
1243 # change_id - we use the change_id to find a CL number, and since we
1244 # just found the CL number via original-change-id, this is moot.
1245 status.commits[step_names.FIND] = {
1246 'gerrit': instance_name,
1247 'cl_number': str(cl_number)
1248 }
1249
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001250 return True
1251
1252
1253def add_cq_depends(status):
1254 """Add Cq-Depends to all of the CLs in chromiumos
1255
1256 The CL in coreboot needs to be pushed to coreboot.org, get merged,
1257 and then get upstreamed into the chromiumos tree before the other
1258 CLs can cq-depend on it and pass CQ.
1259
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001260 Args:
1261 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001262
1263 Returns:
1264 True if the build succeeded, False if something failed
1265 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001266 logging.info('Running step add_cq_depends')
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001267 del status # unused parameter
1268 logging.error('TODO (pfagerburg): implement add_cq_depends')
1269 return True
1270
1271
1272def clean_up(status):
1273 """Final clean-up, including delete the status file
1274
Paul Fagerburg92fcb4b2020-02-19 21:21:43 -07001275 Args:
1276 status: variant_status object tracking our board, variant, etc.
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001277
1278 Returns:
1279 True
1280 """
Paul Fagerburgbab5dde2020-01-10 15:10:29 -07001281 logging.info('Running step clean_up')
Paul Fagerburg3b534f92019-11-07 15:05:22 -07001282 status.rm()
1283 return True
1284
1285
1286if __name__ == '__main__':
1287 sys.exit(not int(main()))