blob: c2585d351dd06ee0ccad58155009f98e4ea2d609 [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright 2018 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.
"""Sysroot service."""
from __future__ import print_function
import os
from chromite.lib import constants
from chromite.lib import cros_build_lib
from chromite.lib import cros_logging as logging
from chromite.lib import sysroot_lib
from chromite.lib import workon_helper
class Error(Exception):
"""Base error class for the module."""
class InvalidArgumentsError(Error):
"""Invalid arguments."""
class NotInChrootError(Error):
"""When SetupBoard is run outside of the chroot."""
class UpdateChrootError(Error):
"""Error occurred when running update chroot."""
class SetupBoardRunConfig(object):
"""Value object for full setup board run configurations."""
def __init__(self, set_default=False, force=False, usepkg=True, jobs=None,
regen_configs=False, quiet=False, update_toolchain=True,
upgrade_chroot=True, init_board_pkgs=True, local_build=False):
"""Initialize method.
Args:
set_default (bool): Whether to set the passed board as the default.
force (bool): Force a new sysroot creation when it already exists.
usepkg (bool): Whether to use binary packages to bootstrap.
jobs (int): Max number of simultaneous packages to build.
regen_configs (bool): Whether to only regen the configs.
quiet (bool): Whether to print notification when sysroot exists.
update_toolchain (bool): Update the toolchain?
upgrade_chroot (bool): Upgrade the chroot before building?
init_board_pkgs (bool): Emerging packages to sysroot?
local_build (bool): Bootstrap only from local packages?
"""
self.set_default = set_default
self.force = force
self.usepkg = usepkg
self.jobs = jobs
self.regen_configs = regen_configs
self.quiet = quiet
self.update_toolchain = update_toolchain
self.update_chroot = upgrade_chroot
self.init_board_pkgs = init_board_pkgs
self.local_build = local_build
def GetUpdateChrootArgs(self):
"""Create a list containing the relevant update_chroot arguments.
Returns:
list[str]
"""
args = []
if self.usepkg:
args += ['--usepkg']
else:
args += ['--nousepkg']
if self.jobs > 0:
args += ['--jobs', str(self.jobs)]
if not self.update_toolchain:
args += ['--skip_toolchain_update']
return args
def SetupBoard(target, accept_licenses=None, run_configs=None):
"""Run the full process to setup a board's sysroot.
This is the entry point to run the setup_board script.
Args:
target (build_target_util.BuildTarget): The build target configuration.
accept_licenses (str|None): The additional licenses to accept.
run_configs (SetupBoardRunConfig): The run configs.
Raises:
sysroot_lib.ToolchainInstallError when the toolchain fails to install.
"""
if not cros_build_lib.IsInsideChroot():
# TODO(saklein) switch to build out command and run inside chroot.
raise NotInChrootError('SetupBoard must be run from inside the chroot')
# Make sure we have valid run configs setup.
run_configs = run_configs or SetupBoardRunConfig()
sysroot = Create(target, run_configs, accept_licenses)
if run_configs.regen_configs:
# We're now done if we're only regenerating the configs.
return
InstallToolchain(target, sysroot, run_configs)
def Create(target, run_configs, accept_licenses):
"""Create a sysroot.
This entry point is the subset of the full setup process that does the
creation and configuration of a sysroot, including installing portage.
Args:
target (build_target.BuildTarget): The build target being installed in the
sysroot being created.
run_configs (SetupBoardRunConfig): The run configs.
accept_licenses (str|None): The additional licenses to accept.
"""
cros_build_lib.AssertInsideChroot()
sysroot = sysroot_lib.Sysroot(target.root)
if sysroot.Exists() and not run_configs.force and not run_configs.quiet:
logging.warning('Board output directory already exists: %s\n'
'Use --force to clobber the board root and start again.',
sysroot.path)
# Override regen_configs setting to force full setup run if the sysroot does
# not exist.
run_configs.regen_configs = run_configs.regen_configs and sysroot.Exists()
# Make sure the chroot is fully up to date before we start unless the
# chroot update is explicitly disabled.
if run_configs.update_chroot:
logging.info('Updating chroot.')
update_chroot = [os.path.join(constants.CROSUTILS_DIR, 'update_chroot'),
'--toolchain_boards', target.name]
update_chroot += run_configs.GetUpdateChrootArgs()
try:
cros_build_lib.RunCommand(update_chroot)
except cros_build_lib.RunCommandError:
raise UpdateChrootError('Error occurred while updating the chroot.'
'See the logs for more information.')
# Delete old sysroot to force a fresh start if requested.
if sysroot.Exists() and run_configs.force:
sysroot.Delete(async=True)
# Step 1: Create folders.
# Dependencies: None.
# Create the skeleton.
logging.info('Creating sysroot directories.')
_CreateSysrootSkeleton(sysroot)
# Step 2: Standalone configurations.
# Dependencies: Folders exist.
# Install main, board setup, and user make.conf files.
logging.info('Installing configurations into sysroot.')
_InstallConfigs(sysroot, target)
# Step 3: Portage configurations.
# Dependencies: make.conf.board_setup.
# Create the command wrappers, choose profile, and make.conf.board.
# Refresh the workon symlinks to compensate for crbug.com/679831.
logging.info('Setting up portage in the sysroot.')
_InstallPortageConfigs(sysroot, target, accept_licenses,
run_configs.local_build)
# Developer Experience Step: Set default board (if requested) to allow
# running later commands without needing to pass the --board argument.
if run_configs.set_default:
cros_build_lib.SetDefaultBoard(target.name)
return sysroot
def InstallToolchain(target, sysroot, run_configs):
"""Update the toolchain to a sysroot.
This entry point just installs the target's toolchain into the sysroot.
Everything else must have been done already for this to be successful.
Args:
target (build_target_util.BuildTarget): The target whose toolchain is being
installed.
sysroot (sysroot_lib.Sysroot): The sysroot where the toolchain is being
installed.
run_configs (SetupBoardRunConfig): The run configs.
"""
cros_build_lib.AssertInsideChroot()
if not sysroot.Exists():
# Sanity check before we try installing anything.
raise ValueError('The sysroot must exist, run Create first.')
# Step 4: Install toolchain and packages.
# Dependencies: Portage configs and wrappers have been installed.
if run_configs.init_board_pkgs:
logging.info('Updating toolchain.')
# Use the local packages if we're doing a local only build or usepkg is set.
local_init = run_configs.usepkg or run_configs.local_build
_InstallToolchain(sysroot, target, local_init=local_init)
def _CreateSysrootSkeleton(sysroot):
"""Create the sysroot skeleton.
Dependencies: None.
Creates the sysroot directory structure and installs the portage hooks.
Args:
sysroot (sysroot_lib.Sysroot): The sysroot.
"""
sysroot.CreateSkeleton()
def _InstallConfigs(sysroot, target):
"""Install standalone configuration files into the sysroot.
Dependencies: The sysroot exists (i.e. CreateSysrootSkeleton).
Installs the main, board setup, and user make.conf files.
Args:
sysroot (sysroot_lib.Sysroot): The sysroot.
target (build_target.BuildTarget): The build target being setup in
the sysroot.
"""
sysroot.InstallMakeConf()
sysroot.InstallMakeConfBoardSetup(target.name)
sysroot.InstallMakeConfUser()
def _InstallPortageConfigs(sysroot, target, accept_licenses, local_build):
"""Install portage wrappers and configurations.
Dependencies: make.conf.board_setup (InstallConfigs).
Create the command wrappers, choose profile, and generate make.conf.board.
Refresh the workon symlinks to compensate for crbug.com/679831.
Args:
sysroot (sysroot_lib.Sysroot): The sysroot.
target (build_target.BuildTarget): The build target being installed
in the sysroot.
accept_licenses (str): Additional accepted licenses as a string.
local_build (bool): If the build is a local only build.
"""
sysroot.CreateAllWrappers(friendly_name=target.name)
_ChooseProfile(target, sysroot)
_RefreshWorkonSymlinks(target.name, sysroot)
# Must be done after the profile is chosen or binhosts may be incomplete.
sysroot.InstallMakeConfBoard(accepted_licenses=accept_licenses,
local_only=local_build)
def _InstallToolchain(sysroot, target, local_init=True):
"""Install toolchain and packages.
Dependencies: Portage configs and wrappers have been installed
(InstallPortageConfigs).
Install the toolchain and the implicit dependencies.
Args:
sysroot (sysroot_lib.Sysroot): The sysroot to install to.
target (build_target.BuildTarget): The build target whose toolchain is
being installed.
local_init (bool): Whether to use local packages to bootstrap implicit
dependencies.
"""
sysroot.UpdateToolchain(target.name, local_init=local_init)
def _RefreshWorkonSymlinks(target, sysroot):
"""Force refresh the workon symlinks.
Create an instance of the WorkonHelper, which will recreate all symlinks
to masked/unmasked packages currently worked on in case the sysroot was
recreated (crbug.com/679831).
This was done with a call to `cros_workon list` in the bash version of
the script, but all we actually need is for the WorkonHelper to be
instantiated since it refreshes the symlinks in its __init__.
Args:
target (str): The build target name.
sysroot (sysroot_lib.Sysroot): The board's sysroot.
"""
workon_helper.WorkonHelper(sysroot.path, friendly_name=target)
def _ChooseProfile(target, sysroot):
"""Helper function to execute cros_choose_profile.
TODO(saklein) Refactor cros_choose_profile to avoid needing the RunCommand
call here, and by extension this method all together.
Args:
target (build_target_util.BuildTarget): The build target whose profile is
being chosen.
sysroot (sysroot_lib.Sysroot): The sysroot for which the profile is
being chosen.
"""
choose_profile = ['cros_choose_profile', '--board', target.name,
'--board-root', sysroot.path]
if target.profile:
# Chooses base by default, only override when we have a passed param.
choose_profile += ['--profile', target.profile]
try:
cros_build_lib.RunCommand(choose_profile, print_cmd=False)
except cros_build_lib.RunCommandError as e:
logging.error('Selecting profile failed, removing incomplete board '
'directory!')
sysroot.Delete()
raise e