blob: 6f37060a4404b398cfe5005d78a512d0f8012392 [file] [log] [blame]
# Copyright 2022 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.
"""build_packages updates the set of binary packages needed by Chrome OS.
The build_packages process cross compiles all packages that have been
updated into the given sysroot and builds binary packages as a side-effect.
The output packages will be used by the build_image script to create a
bootable Chrome OS image.
If packages are specified in cli, only build those specific packages and any
dependencies they might need.
For the fastest builds, use --nowithautotest --noworkon.
"""
import argparse
import logging
import os
from typing import List, Optional, Tuple
import urllib.error
import urllib.request
from chromite.lib import build_target_lib
from chromite.lib import commandline
from chromite.lib import cros_build_lib
from chromite.lib import sysroot_lib
from chromite.service import sysroot
from chromite.utils import timer
def build_shell_bool_style_args(parser: commandline.ArgumentParser,
name: str,
default_val: bool,
help_str: str,
deprecation_note: str,
alternate_name: Optional[str] = None) -> None:
"""Build the shell boolean input argument equivalent.
There are two cases which we will need to handle,
case 1: A shell boolean arg, which doesn't need to be re-worded in python.
case 2: A shell boolean arg, which needs to be re-worded in python.
Example below.
For Case 1, for a given input arg name 'argA', we create three python
arguments.
--argA, --noargA, --no-argA. The arguments --argA and --no-argA will be
retained after deprecating --noargA.
For Case 2, for a given input arg name 'arg_A' we need to use alternate
argument name 'arg-A'. we create four python arguments in this case.
--arg_A, --noarg_A, --arg-A, --no-arg-A. The first two arguments will be
deprecated later.
TODO(b/218522717): Remove the creation of --noargA in case 1 and --arg_A and
--noarg_A in case 2.
Args:
parser: The parser to update.
name: The input argument name. This will be used as 'dest' variable name.
default_val: The default value to assign.
help_str: The help string for the input argument.
deprecation_note: A deprecation note to use.
alternate_name: Alternate argument to be used after deprecation.
"""
arg = f'--{name}'
shell_narg = f'--no{name}'
py_narg = f'--no-{name}'
alt_arg = f'--{alternate_name}' if alternate_name else None
alt_py_narg = f'--no-{alternate_name}' if alternate_name else None
default_val_str = f'{help_str} (Default: %(default)s).'
if alternate_name:
parser.add_argument(
alt_arg,
action='store_true',
default=default_val,
dest=name,
help=default_val_str)
parser.add_argument(
alt_py_narg,
action='store_false',
dest=name,
help="Don't " + help_str.lower())
parser.add_argument(
arg,
action='store_true',
default=default_val,
dest=name,
deprecated=deprecation_note % alt_arg if alternate_name else None,
help=default_val_str if not alternate_name else argparse.SUPPRESS)
parser.add_argument(
shell_narg,
action='store_false',
dest=name,
deprecated=deprecation_note %
(alt_py_narg if alternate_name else py_narg),
help=argparse.SUPPRESS)
if not alternate_name:
parser.add_argument(
py_narg,
action='store_false',
dest=name,
help="Don't " + help_str.lower())
def get_parser() -> commandline.ArgumentParser:
"""Creates the cmdline argparser, populates the options and description.
Returns:
Argument parser.
"""
deprecation_note = 'Argument will be removed July, 2022. Use %s instead.'
parser = commandline.ArgumentParser(description=__doc__)
parser.add_argument(
'-b',
'--board',
'--build-target',
dest='board',
default=cros_build_lib.GetDefaultBoard(),
help='The board to build packages for.')
build_shell_bool_style_args(
parser, 'usepkg', True, 'Use binary packages to bootstrap when possible.',
deprecation_note)
build_shell_bool_style_args(
parser, 'usepkgonly', False,
'Use binary packages only to bootstrap; abort if any are missing.',
deprecation_note)
build_shell_bool_style_args(parser, 'workon', True,
'Force-build workon packages.', deprecation_note)
build_shell_bool_style_args(
parser, 'withrevdeps', True,
'Calculate reverse dependencies on changed ebuilds.', deprecation_note)
build_shell_bool_style_args(
parser, 'cleanbuild', False,
'Perform a clean build; delete sysroot if it exists before building.',
deprecation_note)
build_shell_bool_style_args(
parser, 'pretend', False,
'Pretend building packages, just display which packages would have '
'been installed.', deprecation_note)
# The --sysroot flag specifies the environment variables ROOT and PKGDIR.
# This allows fetching and emerging of all packages to specified sysroot.
# Note that --sysroot will setup the board normally in /build/$BOARD, if
# it's not setup yet. It also expects the toolchain to already be installed
# in the sysroot.
# --usepkgonly and --norebuild are required, because building is not
# supported when board_root is set.
parser.add_argument(
'--sysroot', type='path', help='Emerge packages to sysroot.')
parser.add_argument(
'--board_root',
type='path',
dest='sysroot',
deprecated=deprecation_note % '--sysroot',
help=argparse.SUPPRESS)
# CPU Governor related options.
group = parser.add_argument_group('CPU Governor Options')
build_shell_bool_style_args(
group, 'autosetgov', False,
"Automatically set cpu governor to 'performance'.", deprecation_note)
build_shell_bool_style_args(
group,
'autosetgov_sticky',
False,
'Remember --autosetgov setting for future runs.',
deprecation_note,
alternate_name='autosetgov-sticky')
# Chrome building related options.
group = parser.add_argument_group('Chrome Options')
build_shell_bool_style_args(
group,
'use_any_chrome',
True,
"Use any Chrome prebuilt available, even if the prebuilt doesn't "
'match exactly.',
deprecation_note,
alternate_name='use-any-chrome')
build_shell_bool_style_args(
group, 'internal', False,
'Build the internal version of chrome(set the chrome_internal USE flag).',
deprecation_note)
build_shell_bool_style_args(
group, 'chrome', False, 'Ensure chrome instead of chromium. Alias for '
'--internal --no-use-any-chrome.', deprecation_note)
# Setup board related options.
group = parser.add_argument_group('Setup Board Config Options')
build_shell_bool_style_args(
group,
'skip_chroot_upgrade',
False,
'Skip the automatic chroot upgrade; use with care.',
deprecation_note,
alternate_name='skip-chroot-upgrade')
build_shell_bool_style_args(
group,
'skip_toolchain_update',
False,
'Skip automatic toolchain update',
deprecation_note,
alternate_name='skip-toolchain-update')
build_shell_bool_style_args(
group,
'skip_setup_board',
False,
'Skip running setup_board. Implies '
'--skip-chroot-upgrade --skip-toolchain-update.',
deprecation_note,
alternate_name='skip-setup-board')
# Image Type selection related options.
group = parser.add_argument_group('Image Type Options')
build_shell_bool_style_args(group, 'withdev', True,
'Build useful developer friendly utilities.',
deprecation_note)
build_shell_bool_style_args(
group, 'withdebug', True,
'Build debug versions of Chromium-OS-specific packages.',
deprecation_note)
build_shell_bool_style_args(group, 'withfactory', True,
'Build factory installer.', deprecation_note)
build_shell_bool_style_args(group, 'withtest', True,
'Build packages required for testing.',
deprecation_note)
build_shell_bool_style_args(group, 'withautotest', True,
'Build autotest client code.', deprecation_note)
build_shell_bool_style_args(group, 'withdebugsymbols', False,
'Install the debug symbols for all packages.',
deprecation_note)
# Advanced Options.
group = parser.add_argument_group('Advanced Options')
group.add_argument(
'--accept-licenses', help='Licenses to append to the accept list.')
group.add_argument(
'--accept_licenses',
deprecated=deprecation_note % '--accept-licenses',
help=argparse.SUPPRESS)
build_shell_bool_style_args(group, 'eclean', True,
'Run eclean to delete old binpkgs.',
deprecation_note)
group.add_argument(
'--jobs',
type=int,
default=os.cpu_count(),
help='Number of packages to build in parallel. '
'(Default: %(default)s)')
build_shell_bool_style_args(group, 'rebuild', True,
'Automatically rebuild dependencies.',
deprecation_note)
# TODO(b/218522717): Remove the --nonorebuild argument support.
group.add_argument(
'--nonorebuild',
action='store_true',
dest='rebuild',
deprecated=deprecation_note % '--rebuild',
help=argparse.SUPPRESS)
build_shell_bool_style_args(group, 'expandedbinhosts', True,
'Allow expanded binhost inheritance.',
deprecation_note)
# The --reuse-pkgs-from-local-boards flag tells Portage to share binary
# packages between boards that are built locally, so that the total time
# required to build several boards is reduced. This flag is only useful
# when you are not able to use remote binary packages, since remote binary
# packages are usually more up to date than anything you have locally.
build_shell_bool_style_args(
group,
'reuse_pkgs_from_local_boards',
False,
'Bootstrap from local packages instead of remote packages.',
deprecation_note,
alternate_name='reuse-pkgs-from-local-boards')
# --run-goma option is designed to be used on bots.
# If you're trying to build packages with goma in your local dev env, this is
# *not* the option you're looking for. Please see comments below.
# This option; 1) starts goma, 2) builds packages (expecting that goma is
# used), then 3) stops goma explicitly.
# 4) is a request from the goma team, so that stats/logs can be taken.
# Note: GOMA_DIR and GOMA_SERVICE_ACCOUNT_JSON_FILE are expected to be passed
# via env var.
#
# In local dev env cases, compiler_proxy is expected to keep running.
# In such a case;
# $ python ${GOMA_DIR}/goma_ctl.py ensure_start
# $ ./build_packages (... and options without --run-goma ...)
# is an expected commandline sequence. If you set --run-goma flag while
# compiler_proxy is already running, the existing compiler_proxy will be
# stopped.
build_shell_bool_style_args(
group,
'run_goma',
False,
'If set to true, (re)starts goma, builds packages, and then stops goma..',
deprecation_note,
alternate_name='run-goma')
# This option is for building chrome remotely.
# 1) starts reproxy 2) builds chrome with reproxy and 3) stops reproxy so
# logs/stats can be collected.
# Note: RECLIENT_DIR and REPROXY_CFG env var will be deprecated July, 2022.
# Use --reclient-dir and --reproxy-cfg input options instead.
build_shell_bool_style_args(
group, 'run_remoteexec', False,
'If set to true, starts RBE reproxy, builds packages, and then stops '
'reproxy.', deprecation_note)
parser.add_argument('packages', nargs='*', help='Packages to build.')
return parser
def parse_args(argv: List[str]) -> Tuple[commandline.ArgumentParser,
commandline.ArgumentNamespace]:
"""Parse and validate CLI arguments.
Args:
argv: Arguments passed via CLI.
Returns:
Tuple having the below two,
Argument Parser
Validated argument namespace.
"""
parser = get_parser()
opts = parser.parse_args(argv)
if opts.chrome:
opts.internal_chrome = True
opts.use_any_chrome = False
opts.setup_board_run_config = sysroot.SetupBoardRunConfig(
force=opts.cleanbuild,
usepkg=opts.usepkg,
jobs=opts.jobs,
quiet=True,
update_toolchain=not opts.skip_toolchain_update,
upgrade_chroot=not opts.skip_chroot_upgrade,
local_build=opts.reuse_pkgs_from_local_boards,
expanded_binhost_inheritance=opts.expandedbinhosts)
opts.build_run_config = sysroot.BuildPackagesRunConfig(
usepkg=opts.usepkg,
install_debug_symbols=opts.withdebugsymbols,
packages=opts.packages,
use_goma=opts.run_goma,
use_remoteexec=opts.run_remoteexec,
incremental_build=opts.withrevdeps,
dryrun=opts.pretend,
usepkgonly=opts.usepkgonly,
workon=opts.workon,
install_auto_test=opts.withautotest,
autosetgov=opts.autosetgov,
autosetgov_sticky=opts.autosetgov_sticky,
use_any_chrome=opts.use_any_chrome,
internal_chrome=opts.internal,
clean_build=opts.cleanbuild,
eclean=opts.eclean,
rebuild_dep=opts.rebuild,
jobs=opts.jobs,
local_pkg=opts.reuse_pkgs_from_local_boards,
dev_image=opts.withdev,
factory_image=opts.withfactory,
test_image=opts.withtest,
debug_version=opts.withdebug)
opts.Freeze()
return parser, opts
@timer.timed('Elapsed time (build_packages)')
def main(argv: Optional[List[str]] = None) -> Optional[int]:
commandline.RunInsideChroot()
parser, opts = parse_args(argv)
# If the opts.board is not set, then it means user hasn't specified a default
# board in 'src/scripts/.default_board' and didn't specify it as input
# argument.
if not opts.board:
parser.error('--board is required')
build_target = build_target_lib.BuildTarget(
opts.board, build_root=opts.sysroot)
board_root = sysroot_lib.Sysroot(build_target.root)
try:
# TODO(xcl): Update run_configs to have a common base set of configs for
# setup_board and build_packages.
if not opts.skip_setup_board:
sysroot.SetupBoard(
build_target,
accept_licenses=opts.accept_licenses,
run_configs=opts.setup_board_run_config)
sysroot.BuildPackages(build_target, board_root, opts.build_run_config)
except sysroot_lib.PackageInstallError as e:
try:
with urllib.request.urlopen(
'https://chromiumos-status.appspot.com/current?format=raw'
) as request:
logging.notice('Tree Status: %s', request.read().decode())
except urllib.error.HTTPError:
pass
cros_build_lib.Die(e)