| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| """Create a new variant of an existing base 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: |
| |
| * third_party/coreboot/util/mainboard/google/hatch/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 |
| |
| Copyright 2019 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 logging |
| import os |
| import re |
| import shutil |
| import subprocess |
| import sys |
| import variant_status |
| |
| |
| def main(): |
| """Create a new variant of an existing base board |
| |
| This program automates the creation of a new variant of an existing |
| base board by calling various scripts that clone the base 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() |
| |
| while status.stage is not None: |
| status.save() |
| if not perform_stage(status): |
| logging.debug('perform_stage returned False; exiting ...') |
| return False |
| |
| move_to_next_stage(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 base 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 base 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 base 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. |
| |
| If the --continue flag is present, make sure that the status file |
| exists, and fail if it doesn't. |
| |
| If the --continue flag is not present, then create the status file |
| with the board, variant, and bug details. If the status file already |
| exists, this is an error case. |
| |
| The function returns an object with several fields: |
| * board - the name of the baseboard, 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' |
| * workon - list of packages that will need `cros_workon start` before |
| we can `emerge`. Each function can add package names to this list. |
| * emerge - list of packages that we need to `emerge` at the end. Each |
| functions can add package names to this list. |
| * stage - internal state tracking, what stage 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 base 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 that points to the yaml file |
| """ |
| 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 |
| status.stage = 'cb_variant' |
| status.save() |
| |
| return status |
| |
| |
| # Constants for the stages, so we don't have to worry about misspelling them |
| # pylint: disable=bad-whitespace |
| # Allow extra spaces around = so that we can line things up nicely |
| CB_VARIANT = 'cb_variant' |
| CB_CONFIG = 'cb_config' |
| ADD_FIT = 'add_fit' |
| GEN_FIT = 'gen_fit' |
| COMMIT_FIT = 'commit_fit' |
| EC_IMAGE = 'ec_image' |
| EC_BUILDALL = 'ec_buildall' |
| ADD_YAML = 'add_yaml' |
| BUILD_YAML = 'build_yaml' |
| EMERGE = 'emerge' |
| PUSH = 'push' |
| UPLOAD = 'upload' |
| FIND = 'find' |
| CQ_DEPEND = 'cq_depend' |
| CLEAN_UP = 'clean_up' |
| # pylint: enable=bad-whitespace |
| |
| |
| def perform_stage(status): |
| """Call the appropriate function for the current stage |
| |
| Params: |
| st dictionary that provides details including |
| the board name, variant name, and bug ID |
| |
| Returns: |
| True if the stage succeeded, False if it failed |
| """ |
| # Function to call based on the stage |
| dispatch = { |
| CB_VARIANT: create_coreboot_variant, |
| CB_CONFIG: create_coreboot_config, |
| ADD_FIT: add_fitimage, |
| GEN_FIT: gen_fit_image_outside_chroot, |
| COMMIT_FIT: commit_fitimage, |
| EC_IMAGE: create_initial_ec_image, |
| EC_BUILDALL: ec_buildall, |
| ADD_YAML: add_variant_to_yaml, |
| BUILD_YAML: build_yaml, |
| EMERGE: emerge_all, |
| PUSH: push_coreboot, |
| UPLOAD: upload_CLs, |
| FIND: find_coreboot_upstream, |
| CQ_DEPEND: add_cq_depends, |
| CLEAN_UP: clean_up, |
| } |
| |
| if status.stage not in dispatch: |
| logging.error('Unknown stage "%s", aborting...', status.stage) |
| sys.exit(1) |
| |
| return dispatch[status.stage](status) |
| |
| |
| def move_to_next_stage(status): |
| """Move to the next state |
| |
| Params: |
| status variant_status object tracking our board, variant, etc. |
| """ |
| |
| # Stage to move to if the current one is successful |
| # TODO(pfagerburg) instead of using a map, have a list of stages that |
| # is saved to the yaml file. Create logic to populate that list at |
| # the beginning of the process, based on the --board argument; hatch |
| # can have a different list of stages compared to e.g. volteer. |
| next_stage = { |
| CB_VARIANT: CB_CONFIG, |
| CB_CONFIG: ADD_FIT, |
| ADD_FIT: GEN_FIT, |
| GEN_FIT: COMMIT_FIT, |
| COMMIT_FIT: EC_IMAGE, |
| EC_IMAGE: EC_BUILDALL, |
| EC_BUILDALL: ADD_YAML, |
| ADD_YAML: BUILD_YAML, |
| BUILD_YAML: EMERGE, |
| EMERGE: PUSH, |
| PUSH: UPLOAD, |
| UPLOAD: FIND, |
| FIND: CQ_DEPEND, |
| CQ_DEPEND: CLEAN_UP, |
| CLEAN_UP: None |
| } |
| |
| if status.stage not in next_stage: |
| logging.error('Unknown stage "%s", aborting...', status.stage) |
| sys.exit(1) |
| |
| status.stage = next_stage[status.stage] |
| |
| |
| def run_process(args, *, cwd=None, env=None): |
| """Wrapper for subprocess.run that will produce debug-level messages |
| |
| Params: |
| LImited subset, same as for subprocess.run |
| |
| Returns: |
| Return value from subprocess.run |
| """ |
| logging.debug('Run %s', str(args)) |
| retval = subprocess.run(args, cwd=cwd, env=env).returncode |
| logging.debug('process returns %s', str(retval)) |
| return retval |
| |
| |
| def cros_workon(status, action): |
| """Call cros_workon for all the 9999 ebuilds we'll be touching |
| |
| TODO(pfagerburg) detect 9999 ebuild to know if we have to workon the package |
| |
| 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.board, action] + status.workon |
| return run_process(workon_cmd) == 0 |
| |
| |
| def create_coreboot_variant(status): |
| """Create source files for a new variant of the base board in coreboot |
| |
| This function calls create_coreboot_variant.sh to set up a new variant |
| of the base board. |
| |
| Params: |
| status variant_status object tracking our board, variant, etc. |
| |
| Returns: |
| True if everything succeeded, False if something failed |
| """ |
| logging.info('Running stage create_coreboot_variant') |
| status.workon += ['coreboot', 'libpayload', 'vboot_reference', |
| 'depthcharge'] |
| # Despite doing the workon here, we don't add this to emerge, because |
| # without the configuration (create_coreboot_config), `emerge coreboot` |
| # won't build the new variant. |
| status.emerge += ['libpayload', 'vboot_reference', 'depthcharge', |
| 'chromeos-bootimage'] |
| |
| create_coreboot_variant_sh = os.path.join( |
| os.path.expanduser('~/trunk/src/third_party/coreboot'), |
| 'util/mainboard/google/', status.board, 'create_coreboot_variant.sh') |
| return run_process( |
| [create_coreboot_variant_sh, |
| status.variant, |
| status.bug]) == 0 |
| |
| |
| 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 stage create_coreboot_config') |
| status.emerge += ['coreboot'] |
| create_coreboot_config_sh = os.path.expanduser( |
| '~/trunk/src/platform/dev/contrib/variant/create_coreboot_config.sh') |
| return run_process( |
| [create_coreboot_config_sh, |
| status.board, |
| status.variant, |
| status.bug]) == 0 |
| |
| |
| 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 base 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 stage add_fitimage') |
| status.workon += ['intel-cmlfsp'] |
| status.emerge += ['intel-cmlfsp'] |
| add_fitimage_sh = os.path.expanduser(os.path.join( |
| '~/trunk/src/private-overlays', |
| 'baseboard-' + status.board + '-private', |
| 'sys-boot', |
| 'coreboot-private-files-' + status.board, |
| 'files/add_fitimage.sh')) |
| return run_process( |
| [add_fitimage_sh, |
| status.variant, |
| status.bug]) == 0 |
| |
| |
| 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 stage 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('./gen_fit_image.sh %s [location of FIT] -b', 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/private-overlays/baseboard-%s-private/sys-boot' |
| '/coreboot-private-files-%s/asset_generation', status.board, status.board) |
| return False |
| |
| |
| def check_fit_image_files(status): |
| """Check if the fitimage has been generated |
| |
| This function is not called directly as a stage, 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. |
| """ |
| fitimage_dir = os.path.expanduser(os.path.join( |
| '~/trunk/src/private-overlays', |
| 'baseboard-' + status.board + '-private', |
| 'sys-boot', |
| 'coreboot-private-files-' + status.board, |
| 'asset_generation/outputs')) |
| logging.debug('fitimage_dir = "%s"', fitimage_dir) |
| |
| files = [] |
| if not file_exists(fitimage_dir, 'fitimage-' + status.variant + '.bin'): |
| files.append('fitimage-' + status.variant + '.bin') |
| |
| if not file_exists(fitimage_dir, |
| 'fitimage-' + status.variant + '-versions.txt'): |
| files.append('fitimage-' + status.variant + '-versions.txt') |
| |
| if not file_exists(fitimage_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 stage commit_fitimage') |
| fitimage_dir = os.path.expanduser(os.path.join( |
| '~/trunk/src/private-overlays', |
| 'baseboard-' + status.board + '-private', |
| 'sys-boot', |
| 'coreboot-private-files-' + status.board)) |
| 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 |
| |
| if run_process( |
| ['git', 'add', |
| 'asset_generation/outputs/fit.log', |
| 'files/fitimage-' + status.variant + '.bin', |
| 'files/fitimage-' + status.variant + '-versions.txt' |
| ], |
| cwd=fitimage_dir) != 0: |
| return False |
| |
| return run_process(['git', 'commit', '--amend', '--no-edit'], |
| cwd=fitimage_dir) == 0 |
| |
| |
| def create_initial_ec_image(status): |
| """Create an EC image for the variant as a clone of the base board |
| |
| This function calls create_initial_ec_image.sh, which will clone the |
| base 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 stage create_initial_ec_image') |
| status.workon += ['chromeos-ec'] |
| status.emerge += ['chromeos-ec'] |
| create_initial_ec_image_sh = os.path.expanduser( |
| '~/trunk/src/platform/dev/contrib/variant/create_initial_ec_image.sh') |
| if run_process( |
| [create_initial_ec_image_sh, |
| status.board, |
| status.variant, |
| status.bug]) != 0: |
| return False |
| |
| # create_initial_ec_image.sh will build the ec.bin for this variant |
| # if successful. |
| ec = os.path.expanduser('~/trunk/src/platform/ec') |
| logging.debug('ec = "%s"', ec) |
| ec_bin = '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 stage ec_buildall') |
| del status # unused parameter |
| ec = os.path.expanduser('~/trunk/src/platform/ec') |
| logging.debug('ec = "%s"', ec) |
| return run_process(['make', 'buildall', '-j'], cwd=ec) == 0 |
| |
| |
| def add_variant_to_yaml(status): |
| """Add the new variant to the public and private model.yaml files |
| |
| This function calls add_variant_to_yaml.sh (the public yaml) and |
| add_variant.sh (the private yaml) to add the new variant to |
| the yaml files. |
| |
| Params: |
| st dictionary that provides details including |
| the board name, variant name, and bug ID |
| |
| Returns: |
| True if the scripts and build succeeded, False is something failed |
| """ |
| logging.info('Running stage add_variant_to_yaml') |
| status.workon += ['chromeos-config-bsp-hatch-private'] |
| status.emerge += ['chromeos-config', 'chromeos-config-bsp', |
| 'chromeos-config-bsp-hatch', 'chromeos-config-bsp-hatch-private', |
| 'coreboot-private-files', 'coreboot-private-files-hatch'] |
| add_variant_to_yaml_sh = os.path.expanduser( |
| '~/trunk/src/platform/dev/contrib/variant/add_variant_to_yaml.sh') |
| if run_process( |
| [add_variant_to_yaml_sh, |
| status.board, |
| status.variant, |
| status.bug |
| ]) != 0: |
| return False |
| |
| add_variant_sh = os.path.expanduser(os.path.join( |
| '~/trunk/src/private-overlays', |
| 'overlay-' + status.board + '-private', |
| 'chromeos-base', |
| 'chromeos-config-bsp-' + status.board + '-private', |
| 'add_variant.sh')) |
| if run_process( |
| [add_variant_sh, |
| status.variant, |
| status.bug |
| ]) != 0: |
| return False |
| |
| return True |
| |
| |
| 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 stage build_yaml') |
| if run_process( |
| ['emerge-' + status.board, |
| 'chromeos-config-bsp-' + status.board, |
| 'chromeos-config-bsp-' + status.board + '-private', |
| 'chromeos-config-bsp', |
| 'chromeos-config']) != 0: |
| 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 |
| # We gather the output from grep, decode as UTF-8, split along newlines, |
| # and 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.board + '/usr/share/chromeos-config' |
| logging.debug('chromeos_config = "%s"', chromeos_config) |
| # Can't use run because we need to capture the output instead |
| # of a status code. |
| grep = subprocess.check_output( |
| ['grep', |
| '-c', |
| status.variant, |
| 'config.json', |
| 'yaml/config.c', |
| 'yaml/config.yaml', |
| 'yaml/model.yaml', |
| 'yaml/private-model.yaml'], cwd=chromeos_config) |
| # Convert from byte string to ASCII |
| grep = grep.decode('utf-8') |
| # Split into array of individual lines |
| grep = grep.split('\n') |
| 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 stage 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 = ['emerge-' + status.board] + status.emerge |
| if run_process(emerge_cmd_and_params, env=environ) != 0: |
| return False |
| |
| cros_workon(status, 'stop') |
| build_path = '/build/' + status.board + '/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 + '.dev.bin'): |
| logging.error('emerge failed because image-%s.dev.bin does not exist', |
| status.variant) |
| return False |
| |
| if not file_exists(build_path, 'image-' + status.variant + '.net.bin'): |
| logging.error('emerge failed because image-%s.net.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 stage 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 stage 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 stage 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 stage 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 stage clean_up') |
| status.rm() |
| return True |
| |
| |
| if __name__ == '__main__': |
| sys.exit(not int(main())) |