blob: 63e02260dfe843dbcc08fecc438d9dcf5b8b729b [file] [log] [blame] [edit]
# Copyright 2022 The ChromiumOS Authors
# 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,
)
group.add_argument(
"--backtrack",
type=int,
default=sysroot.BACKTRACK_DEFAULT,
help="See emerge --backtrack.",
)
# 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 is 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,
"When set, (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,
backtrack=opts.backtrack,
)
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,
backtrack=opts.backtrack,
)
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)