blob: 4ea73920d4c12f284a838244e60dbdd0e7a51693 [file] [log] [blame] [edit]
# Copyright 2023 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""cros build-packages updates the set of binary packages needed by ChromiumOS.
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 `cros build-image` to create a bootable
ChromiumOS image.
If packages are specified in the command line, only build those specific
packages and any dependencies they might need.
"""
import argparse
import logging
import os
from typing import Optional
import urllib.error
import urllib.request
from chromite.third_party.opentelemetry import trace
from chromite.third_party.opentelemetry.trace import status
from chromite.cli import command
from chromite.lib import build_target_lib
from chromite.lib import chromite_config
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 telemetry
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(),
)
tracer = trace.get_tracer(__name__)
@command.command_decorator("build-packages")
class BuildPackagesCommand(command.CliCommand):
"""Update the set of binary packages used by ChromiumOS."""
@classmethod
def AddParser(cls, parser: commandline.ArgumentParser):
"""Build the parser.
Args:
parser: The argument parser.
"""
super().AddParser(parser)
deprecation_note = "Argument will be removed July 2022. Use %s instead."
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,
"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
# $ cros build-image (... 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,
)
build_shell_bool_style_args(
group,
"bazel",
False,
"Use Bazel to build packages.",
deprecation_note,
)
parser.add_argument("packages", nargs="*", help="Packages to build.")
return parser
@classmethod
def ProcessOptions(
cls,
parser: commandline.ArgumentParser,
options: commandline.ArgumentNamespace,
):
options.parser = parser
if options.chrome:
options.internal_chrome = True
options.use_any_chrome = False
options.setup_board_run_config = sysroot.SetupBoardRunConfig(
force=options.cleanbuild,
usepkg=options.usepkg,
jobs=options.jobs,
quiet=True,
update_toolchain=not options.skip_toolchain_update,
upgrade_chroot=not options.skip_chroot_upgrade,
local_build=options.reuse_pkgs_from_local_boards,
expanded_binhost_inheritance=options.expandedbinhosts,
use_cq_prebuilts=options.usepkg,
backtrack=options.backtrack,
)
options.build_run_config = sysroot.BuildPackagesRunConfig(
usepkg=options.usepkg,
install_debug_symbols=options.withdebugsymbols,
packages=options.packages,
use_goma=options.run_goma,
use_remoteexec=options.run_remoteexec,
incremental_build=options.withrevdeps,
dryrun=options.pretend,
usepkgonly=options.usepkgonly,
workon=options.workon,
install_auto_test=options.withautotest,
autosetgov=options.autosetgov,
autosetgov_sticky=options.autosetgov_sticky,
use_any_chrome=options.use_any_chrome,
internal_chrome=options.internal,
clean_build=options.cleanbuild,
eclean=options.eclean,
rebuild_dep=options.rebuild,
jobs=options.jobs,
local_pkg=options.reuse_pkgs_from_local_boards,
dev_image=options.withdev,
factory_image=options.withfactory,
test_image=options.withtest,
debug_version=options.withdebug,
backtrack=options.backtrack,
bazel=options.bazel,
)
@timer.timed("Elapsed time (cros build-packages)")
def Run(self):
commandline.RunInsideChroot()
chromite_config.initialize()
telemetry.initialize(
chromite_config.TELEMETRY_CONFIG, debug=self.options.debug
)
build_packages(self.options)
@tracer.start_as_current_span("cli.cros.cros_build_packages.build_packages")
def build_packages(opts: commandline.ArgumentNamespace):
span = trace.get_current_span()
# 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:
span.add_event(
"exception",
attributes={
"exception.type": "ArgumentMissingError",
"exception.message": "--board is required",
},
)
span.set_status(status.StatusCode.ERROR)
opts.parser.error("--board is required")
build_target = build_target_lib.BuildTarget(
opts.board, build_root=opts.sysroot
)
span.set_attributes(
{
"board": build_target.name,
"packages": opts.packages or [],
"workon": opts.workon is True,
"bazel": opts.bazel is True,
}
)
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 cros 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
span.record_exception(e)
span.set_status(status.StatusCode.ERROR, str(e))
cros_build_lib.Die(e)