summaryrefslogtreecommitdiff
path: root/cli/cros/cros_build.py
blob: b868c8f132facc732fa2b0fa4211ac5c6c6884f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# 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

from chromite.cli import command
from chromite.lib import blueprint_lib
from chromite.lib import brick_lib
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 toolchain
from chromite.lib import workon_helper


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
    self.brick = None
    self.blueprint = None

    if self.options.host:
      self.host = True
    elif self.options.board:
      self.board = self.options.board
    elif self.options.blueprint:
      self.blueprint = blueprint_lib.Blueprint(self.options.blueprint)

      if not self.build_pkgs:
        self.build_pkgs = self.blueprint.GetPackages()
    elif self.options.brick or self.curr_brick_locator:
      self.brick = brick_lib.Brick(self.options.brick
                                   or self.curr_brick_locator)
      self.board = self.brick.FriendlyName()
      if not self.build_pkgs:
        self.build_pkgs = self.brick.MainPackages()
    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.blueprint.FriendlyName()
                                             if self.blueprint else 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('--brick', type='brick_path',
                        help='The brick to build packages for.')
    target.add_argument('--blueprint', type='blueprint_path',
                        help='The blueprint 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.')
    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 and not self.blueprint:
      cmd = chroot_util.GetEmergeCommand(sysroot=self.sysroot)
      cmd += ['-pe', '--backtrack=0'] + self.build_pkgs
      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 _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 Run(self):
    """Run cros build."""
    self.options.Freeze()

    if not self.host:
      if not (self.board or self.brick or self.blueprint):
        cros_build_lib.Die('You did not specify a board/brick to build for. '
                           'You need to be in a brick directory or set '
                           '--board/--brick/--host')

      if self.brick and self.brick.legacy:
        cros_build_lib.Die('--brick should not be used with board names. Use '
                           '--board=%s instead.' % self.brick.config['name'])

    if self.blueprint:
      chroot_args = ['--toolchains',
                     ','.join(toolchain.GetToolchainsForBrick(
                         self.blueprint.GetBSP()).iterkeys())]
    elif 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.blueprint:
      if self.chroot_update:
        chroot_util.UpdateChroot(
            update_host_packages=self.options.host_packages_update,
            brick=brick_lib.Brick(self.blueprint.GetBSP()))
      chroot_util.InitializeSysroots(self.blueprint)
    elif self.brick or self.board:
      chroot_util.SetupBoard(
          brick=self.brick, 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)
      else:
        parallel.RunParallelSteps([self._CheckDependencies, self._Build])
      logging.notice('Build completed successfully.')