blob: 36ba597748faa36a0eaeebfb8da8ed3ba4609791 [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Create a new variant of an existing reference board
This program will call all of the scripts that create the various pieces
of a new variant. For example to create a new variant of the hatch base
board, the following scripts are called:
* platform/dev/contrib/variant/create_coreboot_variant.sh
* platform/dev/contrib/variant/create_coreboot_config.sh
* private-overlays/baseboard-hatch-private/sys-boot/
* coreboot-private-files-hatch/files/add_fitimage.sh
* coreboot-private-files-hatch/asset_generation/gen_fit_image.sh
* Outside the chroot, because it uses WINE to run the FIT tools
* platform/dev/contrib/variant/create_initial_ec_image.sh
* platform/dev/contrib/variant/add_variant_to_yaml.sh
* private-overlays/overlay-hatch-private/chromeos-base/
* chromeos-config-bsp-hatch-private/add_variant.sh
Once the scripts are done, the following repos have changes
* third_party/coreboot
* third_party/chromiumos-overlay
* private-overlays/baseboard-hatch-private
* platform/ec
* private-overlays/overlay-hatch-private
* overlays
The program has support for multiple reference boards, so the repos, directories,
and scripts above can change depending on what the reference board is.
Copyright 2020 The Chromium OS Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
"""
from __future__ import print_function
import argparse
import importlib
import logging
import os
import re
import shutil
import subprocess
import sys
from chromite.lib import git
import step_names
import variant_status
def main():
"""Create a new variant of an existing reference board
This program automates the creation of a new variant of an existing
reference board by calling various scripts that copy the reference board,
modify files for the new variant, stage commits, and upload to gerrit.
Note that one of the following is required:
* --continue
* --board=BOARD --variant=VARIANT [--bug=BUG]
"""
board, variant, bug, continue_flag = get_args()
if not check_flags(board, variant, bug, continue_flag):
return False
status = get_status(board, variant, bug, continue_flag)
if status is None:
return False
status.load()
# Where is new_variant.py located?
status.my_loc = os.path.dirname(os.path.abspath(__file__))
while status.step is not None:
status.save()
if not perform_step(status):
logging.debug('perform_step returned False; exiting ...')
return False
move_to_next_step(status)
return True
def get_args():
"""Parse the command-line arguments
There doesn't appear to be a way to specify that --continue is
mutually exclusive with --board, --variant, and --bug. As a result,
all arguments are optional, and another function will apply the logic
to check if there is an illegal combination of arguments.
Returns a list of:
board Name of the reference board
variant Name of the variant being created
bug Text for bug number, if any ('None' otherwise)
continue_flag Flag if --continue was specified
"""
parser = argparse.ArgumentParser(
description=main.__doc__,
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('--board', type=str, help='Name of the reference board')
parser.add_argument(
'--variant', type=str, help='Name of the new variant to create')
parser.add_argument(
'--bug', type=str, help='Bug number to reference in commits')
parser.add_argument(
'--continue', action='store_true',
dest='continue_flag', help='Continue the process from where it paused')
parser.add_argument(
'--verbose', action='store_true',
dest='verbose_flag', help='Enable verbose output of progress')
args = parser.parse_args()
if args.verbose_flag:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
board = args.board
if board is not None:
board = board.lower()
variant = args.variant
if variant is not None:
variant = variant.lower()
bug = args.bug or 'None'
return (board, variant, bug, args.continue_flag)
def check_flags(board, variant, bug, continue_flag):
"""Check the flags to ensure no invalid combinations
We allow any of the following:
--continue
--board=board_name --variant=variant_name
--board=board_name --variant=variant_name --bug=bug_text
The argument parser does have the functionality to represent the
combination of --board and --variant as a single mutually-exclusive
argument, so we have to use this function to do the checking.
Params:
board Name of the reference board
variant Name of the variant being created
bug Text for bug number, if any ('None' otherwise)
continue_flag Flag if --continue was specified
Returns:
True if the arguments are acceptable, False otherwise
"""
if continue_flag:
if board is not None or variant is not None:
logging.error('--continue cannot have other options')
return False
if bug != 'None':
logging.error('--continue cannot have other options')
return False
else:
if board is None:
logging.error('--board must be specified')
return False
if variant is None:
logging.error('--variant must be specified')
return False
return True
def file_exists(filepath, filename):
"""Determine if a path and file exists
Params:
filepath Path where build outputs should be found, e.g.
/build/hatch/firmware
filename File that should exist in that path, e.g.
image-sushi.bin
Returns:
True if file exists in that path, False otherwise
"""
fullname = os.path.join(filepath, filename)
return os.path.exists(fullname) and os.path.isfile(fullname)
def get_status(board, variant, bug, continue_flag):
"""Create the status file or get the previous status
This program can stop at several places as we have to wait for CLs
to work through CQ or be upstreamed into the chromiumos tree, so just
like a git cherry-pick, there is a --continue option to pick up where
you left off by reading a specially-named status file.
If --continue is specified, the status file must exist.
If the status file exists, then --continue must be specified.
When --continue is specified, we read the status file and return
with the contents.
If the status file does not exist, we will create the state file with
the board, variant, and (optional) bug details.
To decouple the list of boards supported from this main program, we
try to import a module with the same name as the reference board,
so --board=hatch means that we import hatch.py. If we can't import
the file, then we don't support that reference board.
The board-specific module will set several variables, which we will
copy into the object that we return.
* base - the name of the base board, such as Hatch, Volteer, or Zork.
This can be different from the reference board, e.g. the Trembyle
reference board in the Zork project.
* coreboot_dir - base directory for coreboot, usually third_party/coreboot
but could differ for processors that use a private repo
* cb_config_dir - base directory for coreboot configs, usually
third_party/chromiumos-overlay/sys-boot/coreboot/files/configs but
could differ for processors that use a private repo
* step_list - list of steps (named in step_names.py) to run in sequence
to create the new variant of the reference board
* fsp - package name for FSP. This may be None, depending on the
processor on the reference board
* fitimage_pkg - package name for the fitimage
* fitimage_dir - directory for fitimage; prepend '/mnt/host/source/src/'
in chroot, prepend '~/chromiumos/src' outside the chroot
* workon_pkgs - list of packages to cros_workon
* emerge_cmd - the emerge command, e.g. 'emerge-hatch'
* emerge_pkgs - list of packages to emerge
* yaml_emerge_pkgs - list of packages to emerge just to build the yaml
* private_yaml_dir - directory for the private yaml file
Additionally, the following fields will be set:
* board - the name of the reference board, e.g. 'hatch'
* variant - the name of the variant, e.g. 'sushi'
* bug - optional text for a bug ID, used in the git commit messages.
Could be 'None' (as text, not the python None), or something like
'b:12345' for buganizer, or 'chromium:12345'
* step - internal state tracking, what step of the variant creation
we are at.
* yaml_file - internal, just the name of the file where all this data
gets saved.
These data might come from the status file (because we read it), or
they might be the initial values after we created the file (because
it did not already exist).
Params:
board Name of the reference board
variant Name of the variant being created
bug Text for bug number, if any ('None' otherwise)
continue_flag Flag if --continue was specified
Returns:
variant_status object with all the data mentioned above
"""
status = variant_status.variant_status()
if continue_flag:
if not status.yaml_file_exists():
logging.error(
'new_variant is not in progress; nothing to --continue')
return None
else:
if status.yaml_file_exists():
logging.error(
'new_variant already in progress; did you forget --continue?')
return None
status.board = board
status.variant = variant
status.bug = bug
# We're just starting out, so load the appropriate module and copy
# all the data from it.
try:
module = importlib.import_module(board)
except ImportError:
print('Unsupported board "' + board + '"')
sys.exit(1)
# pylint: disable=bad-whitespace
# Allow extra spaces around = so that we can line things up nicely
status.base = module.base
status.coreboot_dir = module.coreboot_dir
status.cb_config_dir = module.cb_config_dir
status.emerge_cmd = module.emerge_cmd
status.emerge_pkgs = module.emerge_pkgs
status.fitimage_dir = module.fitimage_dir
status.fitimage_pkg = module.fitimage_pkg
status.fitimage_cmd = module.fitimage_cmd
status.fsp = module.fsp
status.private_yaml_dir = module.private_yaml_dir
status.step_list = module.step_list
status.workon_pkgs = module.workon_pkgs
status.yaml_emerge_pkgs = module.yaml_emerge_pkgs
# pylint: enable=bad-whitespace
# Start at the first entry in the step list
status.step = status.step_list[0]
# Start a blank map for tracking CL data
status.commits = {}
status.save()
return status
def perform_step(status):
"""Call the appropriate function for the current step
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the step succeeded, False if it failed
"""
# Function to call based on the step
dispatch = {
step_names.CB_VARIANT: create_coreboot_variant,
step_names.CB_CONFIG: create_coreboot_config,
step_names.CRAS_CONFIG: copy_cras_config,
step_names.ADD_FIT: add_fitimage,
step_names.GEN_FIT: gen_fit_image_outside_chroot,
step_names.COMMIT_FIT: commit_fitimage,
step_names.EC_IMAGE: create_initial_ec_image,
step_names.EC_BUILDALL: ec_buildall,
step_names.ADD_PUB_YAML: add_variant_to_public_yaml,
step_names.ADD_PRIV_YAML: add_variant_to_private_yaml,
step_names.BUILD_YAML: build_yaml,
step_names.EMERGE: emerge_all,
step_names.PUSH: push_coreboot,
step_names.UPLOAD: upload_CLs,
step_names.FIND: find_coreboot_upstream,
step_names.CQ_DEPEND: add_cq_depends,
step_names.CLEAN_UP: clean_up,
}
if status.step not in dispatch:
logging.error('Unknown step "%s", aborting...', status.step)
sys.exit(1)
return dispatch[status.step](status)
def move_to_next_step(status):
"""Move to the next step in the list
Params:
status variant_status object tracking our board, variant, etc.
"""
if status.step not in status.step_list:
logging.error('Unknown step "%s", aborting...', status.step)
sys.exit(1)
idx = status.step_list.index(status.step)
if idx == len(status.step_list)-1:
status.step = None
else:
status.step = status.step_list[idx+1]
def run_process(args, cwd=None, env=None, capture_output=False):
"""Run a process, log debug messages, return text output of process
The capture_output parameter allows us to capture the output when we
care about it (and not sending it to the screen), or ignoring it when
we don't care, and letting the user see the output so they know that
the build is still running, etc.
Params:
args List of the command and its params
cwd If not None, cd to this directory before running
env Environment to use for execution; if needed, get
os.environ.copy() and add variables. If None, just
use the current environment
capture_output True if we should capture the stdout, false
if we just care about success or not.
Returns:
If capture_output == True, we return the text output from running
the subprocess as a list of lines, or None if the process failed.
If capture_output == False, we return a True if it successed, or
None if the process failed.
The caller can evaluate as a bool, because bool(None) == False, and
bool() of a non-empty list is True, or the caller can use the returned
text for further processing.
"""
logging.debug('Run %s', str(args))
try:
if capture_output:
output = subprocess.check_output(args, cwd=cwd, env=env,
stderr=subprocess.STDOUT)
else:
subprocess.run(args, cwd=cwd, env=env, check=True)
# Just something to decode so we don't get an empty list
output = b'True'
logging.debug('process returns 0')
# Convert from byte string to ASCII
decoded = output.decode('utf-8')
# Split into array of individual lines
lines = decoded.split('\n')
return lines
except subprocess.CalledProcessError as err:
logging.debug('process returns %s', str(err.returncode))
return None
def get_git_commit_data(cwd):
"""Get the branch name and change id of the current commit
Params:
cwd The current working directory, where we want to get the
branch name and change id
Returns:
Map with 'dir', 'branch_name' and 'change_id' keys. The 'dir'
key maps to the value of os.path.expanduser(cwd)
"""
cwd = os.path.expanduser(cwd)
logging.debug('get_git_commit_data(%s)', cwd)
branch_name = git.GetCurrentBranch(cwd)
if branch_name is None:
logging.error('Cannot determine git branch name in %s; exiting', cwd)
sys.exit(1)
logging.debug('git current branch is %s', branch_name)
change_id = git.GetChangeId(cwd)
if change_id is None:
logging.error('Cannot determine Change-Id in %s; exiting', cwd)
sys.exit(1)
logging.debug('git Change-Id is %s', change_id)
return {
'dir': cwd,
'branch_name': branch_name,
'change_id': change_id
}
def cros_workon(status, action):
"""Call cros_workon for all the 9999 ebuilds we'll be touching
Params:
status variant_status object tracking our board, variant, etc.
action 'start' or 'stop'
Returns:
True if the call to cros_workon was successful, False if failed
"""
# Build up the command from all the packages in the list
workon_cmd = ['cros_workon', '--board=' + status.base, action] + status.workon_pkgs
return bool(run_process(workon_cmd))
def create_coreboot_variant(status):
"""Create source files for a new variant of the reference board in coreboot
This function calls create_coreboot_variant.sh to set up a new variant
of the reference board.
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if everything succeeded, False if something failed
"""
logging.info('Running step create_coreboot_variant')
cb_src_dir = os.path.join('/mnt/host/source/src/', status.coreboot_dir)
environ = os.environ.copy()
environ['CB_SRC_DIR'] = cb_src_dir
create_coreboot_variant_sh = os.path.join(status.my_loc,
'create_coreboot_variant.sh')
rc = bool(run_process(
[create_coreboot_variant_sh,
status.base,
status.board,
status.variant,
status.bug], env=environ))
if rc:
status.commits['cb_variant'] = get_git_commit_data(cb_src_dir)
return rc
def create_coreboot_config(status):
"""Create a coreboot configuration for a new variant
This function calls create_coreboot_config.sh, which will make a copy
of coreboot.${BOARD} into coreboot.${VARIANT}.
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the script and test build succeeded, False if something failed
"""
logging.info('Running step create_coreboot_config')
environ = os.environ.copy()
if status.cb_config_dir is not None:
environ['CB_CONFIG_DIR'] = status.cb_config_dir
create_coreboot_config_sh = os.path.join(status.my_loc,
'create_coreboot_config.sh')
rc = bool(run_process(
[create_coreboot_config_sh,
status.base,
status.board,
status.variant,
status.bug], env=environ))
if rc:
# Use status.cb_config_dir if defined, or if not, use
# '/mnt/host/source/src/third_party/chromiumos-overlay'
if status.cb_config_dir is not None:
cb_config_dir = os.path.join('/mnt/host/source/src/', status.cb_config_dir)
else:
cb_config_dir = '/mnt/host/source/src/third_party/chromiumos-overlay'
status.commits['cb_config'] = get_git_commit_data(cb_config_dir)
return rc
def copy_cras_config(status):
"""Copy the cras config for a new variant
This is only necessary for the Zork baseboard right now.
This function calls copy_cras_config.sh, which will copy the
cras config in
overlays/overlay-${BASE}/chromeos-base/chromeos-bsp-${BASE}/files/cras-config/${BASE}
to .../${VARIANT}
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the script and test build succeeded, False if something failed
"""
logging.info('Running step copy_cras_config')
copy_cras_config_sh = os.path.join(status.my_loc, 'copy_cras_config.sh')
rc = bool(run_process(
[copy_cras_config_sh,
status.base,
status.board,
status.variant,
status.bug]))
if rc:
status.commits['copy_cras_config'] = get_git_commit_data(
'/mnt/host/source/src/overlays')
return rc
def add_fitimage(status):
"""Add the source files for a fitimage for the new variant
This function calls add_fitimage.sh to create a new XSL file for the
variant's fitimage, which can override settings from the reference board's
XSL. When this is done, the user will have to build the fitimage by running
gen_fit_image.sh outside of the chroot (and outside of this program's
control) because gen_fit_image.sh uses WINE, which is not installed in
the chroot. (There is a linux version of FIT, but it requires Open GL,
which is also not installed in the chroot.)
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the script succeeded, False otherwise
"""
logging.info('Running step add_fitimage')
add_fitimage_sh = os.path.expanduser(os.path.join(
'/mnt/host/source/src', status.fitimage_dir, 'files/add_fitimage.sh'))
rc = bool(run_process(
[add_fitimage_sh,
status.variant,
status.bug]))
if rc:
fitimage_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir)
status.commits['fitimage'] = get_git_commit_data(fitimage_dir)
return rc
def gen_fit_image_outside_chroot(status):
"""Tell the user to run gen_fit_image.sh outside the chroot
As noted for add_Fitimage(), gen_fit_image.sh cannot run inside the
chroot. This function tells the user to run gen_fit_image.sh in
their normal environment, and then come back (--continue) when that
is done.
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True
"""
logging.info('Running step gen_fit_image_outside_chroot')
fit_image_files = check_fit_image_files(status)
# If the list is empty, then `not` of the list is True, so the files
# we need are all present and we can continue.
if not fit_image_files:
return True
logging.info('The following files need to be generated:')
for filename in fit_image_files:
logging.info('* %s', filename)
logging.info('The fitimage sources are ready for gen_fit_image.sh to process.')
logging.info('gen_fit_image.sh cannot run inside the chroot. Please open a new terminal')
logging.info('window, change to the directory where gen_fit_image.sh is located, and run')
logging.info(status.fitimage_cmd, status.variant)
logging.info('Then re-start this program with --continue.')
logging.info('If your chroot is based in ~/chromiumos, then the folder you want is')
logging.info('~/chromiumos/src/%s/asset_generation', status.fitimage_dir)
return False
def check_fit_image_files(status):
"""Check if the fitimage has been generated
This function is not called directly as a step, and so it doesn't need
to produce any error messages to the user (except with --verbose).
gen_fit_image_outside_chroot will call this function to see if the
fitimage files exist, and if not, then that function will print the
message about how the user needs to run gen_fit_image.sh outside the
chroot.
Params:
status variant_status object tracking our board, variant, etc.
Returns:
List of files that *DO NOT* exist and need to be created, [] if
all files are present.
"""
outputs_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir,
'asset_generation/outputs')
logging.debug('outputs_dir = "%s"', outputs_dir)
files = []
if not file_exists(outputs_dir, 'fitimage-' + status.variant + '.bin'):
files.append('fitimage-' + status.variant + '.bin')
if not file_exists(outputs_dir,
'fitimage-' + status.variant + '-versions.txt'):
files.append('fitimage-' + status.variant + '-versions.txt')
if not file_exists(outputs_dir, 'fit.log'):
files.append('fit.log')
return files
def move_fitimage_file(fitimage_dir, filename):
"""Move fitimage files from create-place to commit-place
commit_fitimage needs to move the fitimage files from the place where
they were created to a different directory in the tree. This utility
function handles joining paths and calling a file move function.
Params:
fitimage_dir Directory where the fitimage files are
filename Name of the file being moved
Returns:
True if the move succeeded, False if it failed
"""
src_dir = os.path.join(fitimage_dir, 'asset_generation/outputs')
src = os.path.join(src_dir, filename)
dest_dir = os.path.join(fitimage_dir, 'files')
dest = os.path.join(dest_dir, filename)
# If src does not exist and dest does, the move is already done => success!
if not file_exists(src_dir, filename) and file_exists(dest_dir, filename):
logging.debug('move "%s", "%s" unnecessary because dest exists and'
' src does not exist', src, dest)
return True
logging.debug('move "%s", "%s"', src, dest)
return shutil.move(src, dest)
def commit_fitimage(status):
"""Move the fitimage files and add them to a git commit
This function moves the fitimage binary and -versions files from
asset_generation/outputs to files/ and then adds those files and
fit.log to the existing git commit.
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the copy, git add, and git commit --amend all succeeded.
False if something failed.
"""
logging.info('Running step commit_fitimage')
fitimage_dir = os.path.join('/mnt/host/source/src', status.fitimage_dir)
logging.debug('fitimage_dir = "%s"', fitimage_dir)
# The copy operation will check that the source file exists, so no
# need to check separately.
if not move_fitimage_file(fitimage_dir,
'fitimage-' + status.variant + '.bin'):
logging.error('Moving fitimage binary failed')
return False
if not move_fitimage_file(fitimage_dir,
'fitimage-' + status.variant + '-versions.txt'):
logging.error('Moving fitimage versions.txt failed')
return False
# TODO(pfagerburg) volteer also needs files/blobs/descriptor-${VARIANT}.bin
if not bool(run_process(
['git', 'add',
'asset_generation/outputs/fit.log',
'files/fitimage-' + status.variant + '.bin',
'files/fitimage-' + status.variant + '-versions.txt'
],
cwd=fitimage_dir)):
return False
return bool(run_process(['git', 'commit', '--amend', '--no-edit'],
cwd=fitimage_dir))
def create_initial_ec_image(status):
"""Create an EC image for the variant as a clone of the reference board
This function calls create_initial_ec_image.sh, which will clone the
reference board to create the variant. The shell script will build the
EC code for the variant, but the repo upload hook insists that we
have done a `make buildall` before it will allow an upload, so this
function does the buildall.
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the script and test build succeeded, False if something failed
"""
logging.info('Running step create_initial_ec_image')
create_initial_ec_image_sh = os.path.join(status.my_loc,
'create_initial_ec_image.sh')
if not bool(run_process(
[create_initial_ec_image_sh,
status.board,
status.variant,
status.bug])):
return False
# No need to `if rc:` because we already tested the run_process result above
status.commits['ec_image'] = get_git_commit_data(
'/mnt/host/source/src/platform/ec/board')
# create_initial_ec_image.sh will build the ec.bin for this variant
# if successful.
ec = '/mnt/host/source/src/platform/ec'
logging.debug('ec = "%s"', ec)
ec_bin = os.path.join('/build', status.variant, 'ec.bin')
logging.debug('ec.bin = "%s"', ec_bin)
return file_exists(ec, ec_bin)
def ec_buildall(status):
"""Do a make buildall -j for the EC, which is required for repo upload
The upload hook checks to ensure that the entire EC codebase builds
without error, so we have to run make buildall -j before uploading.
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the script and test build succeeded, False if something failed
"""
logging.info('Running step ec_buildall')
del status # unused parameter
ec = '/mnt/host/source/src/platform/ec'
logging.debug('ec = "%s"', ec)
return bool(run_process(['make', 'buildall', '-j'], cwd=ec))
def add_variant_to_public_yaml(status):
"""Add the new variant to the public model.yaml file
This function calls add_variant_to_yaml.sh to add the new variant to
the public model.yaml file.
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the script succeeded, False is something failed
"""
logging.info('Running step add_variant_to_public_yaml')
add_variant_to_yaml_sh = os.path.join(status.my_loc,
'add_variant_to_yaml.sh')
rc = bool(run_process(
[add_variant_to_yaml_sh,
status.base,
status.variant,
status.bug]))
if rc:
status.commits['public_yaml'] = get_git_commit_data(
'/mnt/host/source/src/overlays')
return rc
def add_variant_to_private_yaml(status):
"""Add the new variant to the private model.yaml file
This function calls add_variant.sh to add the new variant to
the private model.yaml file.
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the script succeeded, False is something failed
"""
logging.info('Running step add_variant_to_private_yaml')
add_variant_sh = os.path.expanduser(os.path.join(status.private_yaml_dir, 'add_variant.sh'))
rc = bool(run_process(
[add_variant_sh,
status.variant,
status.bug]))
if rc:
status.commits['private_yaml'] = get_git_commit_data(status.private_yaml_dir)
return rc
def build_yaml(status):
"""Build config files from the yaml files
This function builds the yaml files into the JSON and C code that
mosys and other tools use, then verifies that the new variant's name
shows up in all of the output files.
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the scripts and build succeeded, False is something failed
"""
logging.info('Running step build_yaml')
if not bool(run_process([status.emerge_cmd] + status.yaml_emerge_pkgs)):
return False
# Check generated files for occurences of the variant name.
# Each file should have at least one occurence, so use `grep -c` to
# count the occurrences of the variant name in each file.
# The results will be something like this:
# config.json:10
# yaml/config.c:6
# yaml/config.yaml:27
# yaml/model.yaml:6
# yaml/private-model.yaml:10
# If the variant name doesn't show up in the file, then the count
# will be 0, so we would see, e.g.
# config.json:0
# Note that we leave out yaml/model.yaml (the public one) because for
# some boards, there is nothing in the public yaml file.
# We gather the output from grep, then look for any of the strings
# ending in :0. If none of them match, then we're good, but if even
# one of them ends with :0 then there was a problem with generating
# the files from the yaml.
chromeos_config = '/build/' + status.base + '/usr/share/chromeos-config'
logging.debug('chromeos_config = "%s"', chromeos_config)
grep = run_process(
['grep',
'-ci',
status.variant,
'config.json',
'yaml/config.c',
'yaml/config.yaml',
'yaml/private-model.yaml'], cwd=chromeos_config, capture_output=True)
if grep is None:
return False
return not bool([s for s in grep if re.search(r':0$', s)])
def emerge_all(status):
"""Build the coreboot BIOS and EC code for the new variant
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the build succeeded, False if something failed
"""
logging.info('Running step emerge_all')
cros_workon(status, 'start')
environ = os.environ.copy()
environ['FW_NAME'] = status.variant
# Build up the command for emerge from all the packages in the list
emerge_cmd_and_params = [status.emerge_cmd] + status.emerge_pkgs
if not bool(run_process(emerge_cmd_and_params, env=environ)):
return False
cros_workon(status, 'stop')
build_path = '/build/' + status.base + '/firmware'
logging.debug('build_path = "%s"', build_path)
if not file_exists(build_path, 'image-' + status.variant + '.bin'):
logging.error('emerge failed because image-%s.bin does not exist',
status.variant)
return False
if not file_exists(build_path, 'image-' + status.variant + '.serial.bin'):
logging.error('emerge failed because image-%s.serial.bin does not exist',
status.variant)
return False
return True
def push_coreboot(status):
"""Push the coreboot CL to coreboot.org
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the build succeeded, False if something failed
"""
logging.info('Running step push_coreboot')
del status # unused parameter
logging.error('TODO (pfagerburg): implement push_coreboot')
return True
def upload_CLs(status):
"""Upload all CLs to chromiumos
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the build succeeded, False if something failed
"""
logging.info('Running step upload_CLs')
del status # unused parameter
logging.error('TODO (pfagerburg): implement upload_CLs')
return True
def find_coreboot_upstream(status):
"""Find the coreboot CL after it has been upstreamed to chromiumos
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the build succeeded, False if something failed
"""
logging.info('Running step find_coreboot_upstream')
del status # unused parameter
logging.error('TODO (pfagerburg): implement find_coreboot_upstream')
return True
def add_cq_depends(status):
"""Add Cq-Depends to all of the CLs in chromiumos
The CL in coreboot needs to be pushed to coreboot.org, get merged,
and then get upstreamed into the chromiumos tree before the other
CLs can cq-depend on it and pass CQ.
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True if the build succeeded, False if something failed
"""
logging.info('Running step add_cq_depends')
del status # unused parameter
logging.error('TODO (pfagerburg): implement add_cq_depends')
return True
def clean_up(status):
"""Final clean-up, including delete the status file
Params:
status variant_status object tracking our board, variant, etc.
Returns:
True
"""
logging.info('Running step clean_up')
status.rm()
return True
if __name__ == '__main__':
sys.exit(not int(main()))