| # 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 os |
| import logging |
| |
| from chromite.cbuildbot import constants |
| from chromite.lib import commandline |
| from chromite.lib import cros_build_lib |
| from chromite.lib import parallel |
| from chromite.scripts import cros_list_modified_packages as workon |
| from chromite.scripts import cros_setup_toolchains as toolchain |
| from chromite import cros |
| |
| _HOST_PKGS = ('virtual/target-sdk', 'world',) |
| |
| |
| def GetToolchainPackages(): |
| """Get a list of host toolchain packages.""" |
| # Load crossdev cache first for faster performance. |
| toolchain.Crossdev.Load(False) |
| packages = toolchain.GetTargetPackages('host') |
| return [toolchain.GetPortagePackage('host', x) for x in packages] |
| |
| @cros.CommandDecorator('build') |
| class BuildCommand(cros.CrosCommand): |
| """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): |
| cros.CrosCommand.__init__(self, options) |
| self.chroot_update = options.chroot_update and options.deps |
| if options.chroot_update and not options.deps: |
| cros_build_lib.Debug('Skipping chroot update due to --nodeps') |
| |
| |
| @classmethod |
| def AddParser(cls, parser): |
| super(cls, BuildCommand).AddParser(parser) |
| default_board = cros_build_lib.GetDefaultBoard() |
| board = parser.add_mutually_exclusive_group( |
| required=(default_board is None)) |
| board.add_argument('--board', help='The board to build packages for', |
| default=default_board) |
| board.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('--no-chroot-update', help="Don't update chroot", |
| default=True, dest='chroot_update', |
| action='store_false') |
| 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') |
| parser.add_argument('packages', help='Packages to build', nargs='+') |
| |
| # Advanced options. |
| advanced = parser.add_argument_group('Advanced options') |
| advanced.add_argument('--nofast', help='Disable parallel emerge.', |
| default=True, action='store_false', dest='fast') |
| advanced.add_argument('--jobs', default=None, type=int, |
| help='Maximium job count to run in parallel ' |
| '(Default: Use all available cores)') |
| advanced.add_argument('--norebuild', default=True, dest='rebuild_deps', |
| action='store_false', |
| help='Don\'t automatically rebuild dependencies.') |
| |
| 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.options.host: |
| cmd = self._GetEmergeCommand(self.options.board) |
| cmd += ['-pe', '--backtrack=0'] + self.options.packages |
| try: |
| cros_build_lib.RunCommand(cmd, combine_stdout_stderr=True, |
| debug_level=logging.DEBUG) |
| except cros_build_lib.RunCommandError as ex: |
| ex.msg += self._BAD_DEPEND_MSG |
| raise |
| |
| def _ListModifiedPackages(self, board): |
| return list(workon.ListModifiedWorkonPackages(board, board is None)) |
| |
| def _GetEmergeCommand(self, board): |
| if self.options.fast: |
| cmd = [os.path.join(constants.CHROMITE_BIN_DIR, 'parallel_emerge')] |
| if board is not None: |
| cmd += ['--board=%s' % board] |
| else: |
| cmd = ['emerge'] if board is None else ['emerge-%s' % board] |
| return cmd |
| |
| def _Emerge(self, packages, board=None): |
| """Emerge the specified packages to the specified board. |
| |
| Args: |
| packages: Packages to emerge. |
| board: Board to emerge to. If None, emerge to host. |
| """ |
| modified_packages = self._ListModifiedPackages(board) |
| cmd = self._GetEmergeCommand(board) + [ |
| '-uNv', |
| '--reinstall-atoms=%s' % ' '.join(modified_packages), |
| '--usepkg-exclude=%s' % ' '.join(modified_packages), |
| ] |
| cmd.append('--deep' if self.options.deps else '--nodeps') |
| if self.options.binary: |
| cmd += ['-g', '--with-bdeps=y'] |
| if board is None: |
| # Only update toolchains in the chroot when binpkgs are available. The |
| # toolchain rollout process only takes place when the chromiumos sdk |
| # builder finishes a successful build and pushes out binpkgs. |
| cmd += ['--useoldpkg-atoms=%s' % ' '.join(GetToolchainPackages())] |
| if self.options.rebuild_deps: |
| cmd.append('--rebuild-if-unbuilt') |
| if self.options.jobs: |
| cmd.append('--jobs=%d' % self.options.jobs) |
| if self.options.log_level.lower() == 'debug': |
| cmd.append('--show-output') |
| cros_build_lib.SudoRunCommand(cmd + packages) |
| |
| def _UpdateChroot(self): |
| """Update the chroot if needed.""" |
| if self.chroot_update: |
| # Run chroot update hooks. |
| cmd = [os.path.join(constants.CROSUTILS_DIR, 'run_chroot_version_hooks')] |
| cros_build_lib.RunCommand(cmd, debug_level=logging.DEBUG) |
| |
| # Update toolchains. |
| cmd = [os.path.join(constants.CHROMITE_BIN_DIR, 'cros_setup_toolchains')] |
| cros_build_lib.SudoRunCommand(cmd, debug_level=logging.DEBUG) |
| |
| # Update the host before updating the board. |
| self._Emerge(list(_HOST_PKGS)) |
| |
| # Automatically discard all CONFIG_PROTECT'ed files. Those that are |
| # protected should not be overwritten until the variable is changed. |
| # Autodiscard is option "-9" followed by the "YES" confirmation. |
| cros_build_lib.SudoRunCommand(['etc-update'], input='-9\nYES\n', |
| debug_level=logging.DEBUG) |
| self.chroot_update = False |
| |
| def _Build(self): |
| """Update the chroot, then merge the requested packages.""" |
| self._UpdateChroot() |
| board = None if self.options.host else self.options.board |
| self._Emerge(self.options.packages, board) |
| |
| def _SetupBoardIfNeeded(self): |
| """Create the board if it's missing.""" |
| board = self.options.board |
| if not self.options.host: |
| self._UpdateChroot() |
| cmd = [os.path.join(constants.CROSUTILS_DIR, 'setup_board'), |
| '--skip_toolchain_update', '--skip_chroot_upgrade'] |
| cmd.append('--board=%s' % board) |
| if not self.options.binary: |
| cmd.append('--nousepkg') |
| cros_build_lib.RunCommand(cmd) |
| |
| def Run(self): |
| """Run cros build.""" |
| if not cros_build_lib.IsInsideChroot(): |
| raise commandline.ChrootRequiredError() |
| self._SetupBoardIfNeeded() |
| parallel.RunParallelSteps([self._CheckDependencies, self._Build]) |