blob: f9534118baece1151e629a101703f19abd7ed648 [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright (c) 2012 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.
"""cros build: Build the requested packages."""
from __future__ import print_function
import subprocess
import sys
from chromite.cli import command
from chromite.lib import chroot_util
from chromite.lib import commandline
from chromite.lib import cros_build_lib
from chromite.lib import cros_logging as logging
from chromite.lib import operation
from chromite.lib import parallel
from chromite.lib import workon_helper
assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
class BrilloBuildOperation(operation.ParallelEmergeOperation):
"""Wrapper around operation.ParallelEmergeOperation.
Currently, this class is empty as the main component is just
operation.ParallelEmergeOperation. However, self._CheckDependencies also
produces output, then that output can be captured here.
"""
@command.CommandDecorator('build')
class BuildCommand(command.CliCommand):
"""Build the requested packages."""
_BAD_DEPEND_MSG = '\nemerge detected broken ebuilds. See error message above.'
EPILOG = """
To update specified package and all dependencies:
cros build --board=lumpy power_manager
cros build --host cros-devutils
To just build a single package:
cros build --board=lumpy --no-deps power_manager
"""
def __init__(self, options):
super(BuildCommand, self).__init__(options)
self.chroot_update = options.chroot_update and options.deps
if options.chroot_update and not options.deps:
logging.debug('Skipping chroot update due to --nodeps')
self.build_pkgs = options.packages
self.host = False
self.board = None
if self.options.host:
self.host = True
elif self.options.board:
self.board = self.options.board
else:
# If nothing is explicitly set, use the default board.
self.board = cros_build_lib.GetDefaultBoard()
# Set sysroot and friendly name. The latter is None if building for host.
self.sysroot = cros_build_lib.GetSysroot(self.board)
@classmethod
def AddParser(cls, parser):
super(cls, BuildCommand).AddParser(parser)
target = parser.add_mutually_exclusive_group()
target.add_argument('--board', help='The board to build packages for.')
target.add_argument('--host', help='Build packages for the chroot itself.',
default=False, action='store_true')
parser.add_argument('--no-binary', help="Don't use binary packages.",
default=True, dest='binary', action='store_false')
parser.add_argument('--init-only', action='store_true',
help="Initialize build environment but don't build "
'anything.')
deps = parser.add_mutually_exclusive_group()
deps.add_argument('--no-deps', help="Don't update dependencies.",
default=True, dest='deps', action='store_false')
deps.add_argument('--rebuild-deps', default=False, action='store_true',
help='Automatically rebuild dependencies.')
deps.add_argument('--test', default=False, action='store_true',
help='Build/run the package tests.')
parser.add_argument('packages',
help='Packages to build. If no packages listed, uses '
'the current brick main package.',
nargs='*')
# Advanced options.
advanced = parser.add_argument_group('Advanced options')
advanced.add_argument('--no-host-packages-update',
dest='host_packages_update', default=True,
action='store_false',
help="Don't update host packages during chroot "
'update.')
advanced.add_argument('--no-chroot-update', default=True,
dest='chroot_update', action='store_false',
help="Don't update chroot at all.")
advanced.add_argument('--no-enable-only-latest', default=True,
dest='enable_only_latest', action='store_false',
help="Don't enable packages with only live ebuilds.")
advanced.add_argument('--jobs', default=None, type=int,
help='Maximum job count to run in parallel '
'(uses all available cores by default).')
# Legacy options, for backward compatibiltiy.
legacy = parser.add_argument_group('Options for backward compatibility')
legacy.add_argument('--norebuild', default=True, dest='rebuild_deps',
action='store_false', help='Inverse of --rebuild-deps.')
def _CheckDependencies(self):
"""Verify emerge dependencies.
Verify all board packages can be emerged from scratch, without any
backtracking. This ensures that no updates are skipped by Portage due to
the fallback behavior enabled by the backtrack option, and helps catch
cases where Portage skips an update due to a typo in the ebuild.
Only print the output if this step fails or if we're in debug mode.
"""
if self.options.deps and not self.host:
cmd = chroot_util.GetEmergeCommand(sysroot=self.sysroot)
cmd += ['-pe', '--backtrack=0'] + self.build_pkgs
try:
cros_build_lib.run(cmd, stderr=subprocess.STDOUT,
debug_level=logging.DEBUG)
except cros_build_lib.RunCommandError as ex:
ex.msg += self._BAD_DEPEND_MSG
raise
def _Build(self):
"""Update the chroot, then merge the requested packages."""
if self.chroot_update and self.host:
chroot_util.UpdateChroot()
chroot_util.Emerge(self.build_pkgs, self.sysroot,
with_deps=self.options.deps,
rebuild_deps=self.options.rebuild_deps,
use_binary=self.options.binary, jobs=self.options.jobs,
debug_output=(self.options.log_level.lower() == 'debug'))
def _Test(self):
"""Update the chroot, then merge the requested packages."""
# This ALWAYS runs after build, so we don't need to update the chroot.
chroot_util.RunUnittests(
self.sysroot, self.build_pkgs,
verbose=(self.options.log_level.lower() == 'debug'))
def Run(self):
"""Run cros build."""
self.options.Freeze()
if not self.host:
if not self.board:
cros_build_lib.Die('You did not specify a board to build for. '
'You need to use --board or --host.')
if self.board:
chroot_args = ['--board', self.board]
else:
chroot_args = None
commandline.RunInsideChroot(self, chroot_args=chroot_args)
if not (self.build_pkgs or self.options.init_only):
cros_build_lib.Die('No packages found, nothing to build.')
# Set up the sysroots if not building for host.
if self.board:
chroot_util.SetupBoard(
board=self.board,
update_chroot=self.chroot_update,
update_host_packages=self.options.host_packages_update,
use_binary=self.options.binary)
if not self.options.init_only:
# Preliminary: enable all packages that only have a live ebuild.
if self.options.enable_only_latest:
workon = workon_helper.WorkonHelper(self.sysroot)
workon.StartWorkingOnPackages([], use_workon_only=True)
if command.UseProgressBar():
op = BrilloBuildOperation()
op.Run(
parallel.RunParallelSteps, [self._CheckDependencies, self._Build],
log_level=logging.DEBUG)
if self.options.test:
self._Test()
else:
parallel.RunParallelSteps([self._CheckDependencies, self._Build])
if self.options.test:
self._Test()
logging.notice('Build completed successfully.')