blob: e812f6e8d702815dd75c95da95d2792116ee005f [file] [log] [blame]
# Copyright 2016 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.
"""Module containing the Android stages."""
from __future__ import print_function
import os
from chromite.cbuildbot import cbuildbot_run
from chromite.cbuildbot import commands
from chromite.cbuildbot.stages import generic_stages
from chromite.lib import config_lib
from chromite.lib import constants
from chromite.lib import cros_logging as logging
from chromite.lib import failures_lib
from chromite.lib import gs
from chromite.lib import osutils
from chromite.lib import portage_util
from chromite.lib import results_lib
ANDROIDPIN_MASK_PATH = os.path.join(constants.SOURCE_ROOT,
constants.CHROMIUMOS_OVERLAY_DIR,
'profiles', 'default', 'linux',
'package.mask', 'androidpin')
def _GetAndroidVersionFromMetadata(metadata):
"""Return the Android version from metadata; None if is does not exist.
Sync stages may have set this metadata to use a specific Android version.
"""
version_dict = metadata.GetDict().get('version', {})
return version_dict.get('android')
class UprevAndroidStage(generic_stages.BuilderStage,
generic_stages.ArchivingStageMixin):
"""Stage that uprevs Android container if needed."""
@failures_lib.SetFailureType(failures_lib.InfrastructureFailure)
def PerformStage(self):
if not self._android_rev:
logging.info('Not uprevving Android.')
return
android_package = self._run.config.android_package
android_build_branch = self._run.config.android_import_branch
android_version = _GetAndroidVersionFromMetadata(self._run.attrs.metadata)
logging.info('Android package: %s', android_package)
logging.info('Android branch: %s', android_build_branch)
logging.info('Android version: %s', android_version or 'LATEST')
try:
android_atom_to_build = commands.MarkAndroidAsStable(
buildroot=self._build_root,
tracking_branch=self._run.manifest_branch,
android_package=android_package,
android_build_branch=android_build_branch,
boards=self._boards,
android_version=android_version)
except commands.AndroidIsPinnedUprevError as e:
# If uprev failed due to a pin, record that failure (so that the
# build ultimately fails) but try again without the pin, to allow the
# slave to test the newer version anyway).
android_atom_to_build = e.new_android_atom
if android_atom_to_build:
results_lib.Results.Record(self.name, e)
logging.PrintBuildbotStepFailure()
logging.error('Android is pinned. Unpinning Android and continuing '
'build for Android atom %s. This stage will be marked '
'as failed to prevent an uprev.',
android_atom_to_build)
logging.info('Deleting pin file at %s and proceeding.',
ANDROIDPIN_MASK_PATH)
osutils.SafeUnlink(ANDROIDPIN_MASK_PATH)
else:
raise
logging.info('android_atom_to_build = %s', android_atom_to_build)
if (not android_atom_to_build and
self._run.options.buildbot and
self._run.config.build_type == constants.ANDROID_PFQ_TYPE):
logging.info('Android already uprevved. Nothing else to do.')
raise failures_lib.ExitEarlyException(
'UprevAndroidStage finished and exited early.')
class AndroidMetadataStage(generic_stages.BuilderStage,
generic_stages.ArchivingStageMixin):
"""Stage that records Android container version in metadata.
This should attempt to generate four types of metadata:
- a unique Android version if it exists.
- a unique Android branch if it exists.
- a per-board Android version for each board.
- a per-board arc USE flag value.
"""
def __init__(self, builder_run, **kwargs):
super(AndroidMetadataStage, self).__init__(builder_run, **kwargs)
# PerformStage() will fill this out for us.
self.android_version = None
self.android_branch = None
@failures_lib.SetFailureType(failures_lib.InfrastructureFailure)
def PerformStage(self):
# Initially get version from metadata in case the initial sync
# stage set it.
self.android_version = _GetAndroidVersionFromMetadata(
self._run.attrs.metadata)
# Need to always iterate through and generate the board-specific
# Android version metadata. Each board must be handled separately
# since there might be differing builds in the same release group.
versions = set([])
branches = set([])
for builder_run in self._run.GetUngroupedBuilderRuns():
for board in builder_run.config.boards:
try:
# Determine the version for each board and record metadata.
version = self._run.DetermineAndroidVersion(boards=[board])
builder_run.attrs.metadata.UpdateBoardDictWithDict(
board, {'android-container-version': version})
versions.add(version)
logging.info('Board %s has Android version %s', board, version)
except cbuildbot_run.NoAndroidVersionError as ex:
logging.info('Board %s does not contain Android (%s)', board, ex)
try:
# Determine the branch for each board and record metadata.
branch = self._run.DetermineAndroidBranch(board)
builder_run.attrs.metadata.UpdateBoardDictWithDict(
board, {'android-container-branch': branch})
branches.add(branch)
logging.info('Board %s has Android branch %s', board, branch)
except cbuildbot_run.NoAndroidBranchError as ex:
logging.info('Board %s does not contain Android (%s)', board, ex)
arc_use = self._run.HasUseFlag(board, 'arc')
logging.info('Board %s %s arc USE flag set.', board,
'has' if arc_use else 'does not have')
builder_run.attrs.metadata.UpdateBoardDictWithDict(
board, {'arc-use-set': arc_use})
# If there wasn't a version or branch specified in the manifest but there is
# a unique one across all the boards, treat it as the version for the
# entire step.
if self.android_version is None and len(versions) == 1:
self.android_version = versions.pop()
if self.android_branch is None and len(branches) == 1:
self.android_branch = branches.pop()
if self.android_version:
logging.PrintBuildbotStepText('tag %s' % self.android_version)
if self.android_branch:
logging.PrintBuildbotStepText('branch %s' % self.android_branch)
def _WriteAndroidVersionToMetadata(self):
"""Write Android version to metadata and upload partial json file."""
# Even if the stage failed, a None value for android_version still
# means something. In other words, this stage tried to run.
self._run.attrs.metadata.UpdateKeyDictWithDict(
'version',
{'android': self.android_version,
'android-branch': self.android_branch})
self.UploadMetadata(filename=constants.PARTIAL_METADATA_JSON)
def Finish(self):
"""Provide android_version to the rest of the run."""
self._WriteAndroidVersionToMetadata()
super(AndroidMetadataStage, self).Finish()
class DownloadAndroidDebugSymbolsStage(generic_stages.BoardSpecificBuilderStage,
generic_stages.ArchivingStageMixin):
"""Stage that downloads Android debug symbols.
Downloaded archive will be picked up by DebugSymbolsStage.
"""
@failures_lib.SetFailureType(failures_lib.InfrastructureFailure)
def PerformStage(self):
if not config_lib.IsCanaryType(self._run.config.build_type):
logging.info('This stage runs only in release builders.')
return
# Get the Android versions set by AndroidMetadataStage.
version_dict = self._run.attrs.metadata.GetDict().get('version', {})
android_build_branch = version_dict.get('android-branch')
android_version = version_dict.get('android')
# On boards not supporting Android, versions will be None.
if not (android_build_branch and android_version):
logging.info('Android is not enabled on this board. Skipping.')
return
logging.info(
'Downloading symbols of Android %s (%s)...',
android_version, android_build_branch)
board_use_flags = portage_util.GetBoardUseFlags(self._current_board)
arch = None
for arch_use_flag, arch in constants.ARC_USE_FLAG_TO_ARCH.items():
if arch_use_flag in board_use_flags:
break
if not arch:
raise AssertionError(
'Could not determine the arch of %s.' % self._current_board)
symbols_file_url = constants.ANDROID_SYMBOLS_URL_TEMPLATE % {
'branch': android_build_branch,
'arch': arch,
'version': android_version}
symbols_file = os.path.join(self.archive_path,
constants.ANDROID_SYMBOLS_FILE)
gs_context = gs.GSContext()
gs_context.Copy(symbols_file_url, symbols_file)