blob: 6a23e11ac3d48e74f6825b9725c2b91ce0199cff [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright 2017 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.
"""Build stages related to a secondary workspace directory.
A workspace is a compelete ChromeOS checkout and may contain it's own chroot,
.cache directory, etc. Conceptually, cbuildbot_launch creates a workspace for
the intitial ChromeOS build, but these stages are for creating a secondary
build.
This might be useful if a build needs to work with more than one branch at a
time, or make changes to ChromeOS code without changing the code it is currently
running.
A secondary workspace may not be inside an existing ChromeOS repo checkout.
Also, the initial sync will usually take about 40 minutes, so performance should
be considered carefully.
"""
from __future__ import print_function
import os
from chromite.cbuildbot import commands
from chromite.cbuildbot import manifest_version
from chromite.cbuildbot import repository
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 cros_sdk_lib
from chromite.lib import failures_lib
BUILD_PACKAGES_PREBUILTS = '10774.0.0'
BUILD_PACKAGES_WITH_DEBUG_SYMBOLS = '6302.0.0'
class InvalidWorkspace(failures_lib.StepFailure):
"""Raised when a workspace isn't usable."""
class WorkspaceStageBase(generic_stages.BuilderStage):
"""Base class for Workspace stages."""
def __init__(self, builder_run, build_root, **kwargs):
"""Initializer.
Properties for subclasses:
self._build_root to access the workspace directory,
self._orig_root to access the original buildroot.
Args:
builder_run: BuilderRun object.
build_root: Fully qualified path to use as a string.
"""
super(WorkspaceStageBase, self).__init__(
builder_run, build_root=build_root,
**kwargs)
self._orig_root = builder_run.buildroot
def GetWorkspaceRepo(self):
"""Fetch a repo object for the workspace.
Returns:
repository.RepoRepository instance for the workspace.
"""
# TODO: Properly select the manifest. Currently hard coded to internal
# branch checkouts.
site_config = config_lib.GetConfig()
manifest_url = site_config.params['MANIFEST_INT_URL']
return repository.RepoRepository(
manifest_url, self._build_root,
branch=self._run.config.workspace_branch,
git_cache_dir=self._run.options.git_cache_dir)
def GetWorkspaceVersionInfo(self):
"""Fetch a VersionInfo for the workspace.
Only valid after the workspace has been synced.
Returns:
manifest-version.VersionInfo object based on the workspace checkout.
"""
return manifest_version.VersionInfo.from_repo(self._build_root)
def AfterLimit(self, limit):
"""Is worksapce version newer than cutoff limit?
Args:
limit: String version of format '123.0.0'
Returns:
bool: True if workspace has newer version than limit.
"""
version_info = self.GetWorkspaceVersionInfo()
return version_info > manifest_version.VersionInfo(limit)
class WorkspaceSyncStage(WorkspaceStageBase):
"""Clean a working directory checkout."""
category = constants.CI_INFRA_STAGE
def PerformStage(self):
"""Sync stuff!."""
logging.info('Syncing %s branch into %s',
self._run.config.workspace_branch, self._build_root)
repo = self.GetWorkspaceRepo()
repo.PreLoad()
repo.BuildRootGitCleanup(prune_all=True)
repo.Sync(detach=True)
class WorkspaceUprevAndPublishStage(WorkspaceStageBase):
"""Uprev ebuilds, and immediately publish them.
This stage updates ebuilds to top of branch with no verification, or prebuilt
generation. This is generally intended only for branch builds.
"""
config = 'push_overlays'
def __init__(self, builder_run, boards=None, **kwargs):
super(WorkspaceUprevAndPublishStage, self).__init__(builder_run, **kwargs)
if boards is not None:
self._boards = boards
def PerformStage(self):
"""Perform the uprev and push."""
commands.UprevPackages(self._orig_root, self._boards,
overlay_type=self._run.config.overlays,
workspace=self._build_root)
logging.info('Pushing.')
commands.UprevPush(self._orig_root,
overlay_type=self._run.config.push_overlays,
dryrun=self._run.options.debug,
workspace=self._build_root)
class WorkspacePublishBuildspecStage(WorkspaceStageBase):
"""Increment the ChromeOS version, and publish a buildspec."""
def PerformStage(self):
"""Increment ChromeOS version, and publish buildpec."""
site_params = config_lib.GetSiteParams()
# Use the manifest-versions directories that exist in the original
# checkout. They may already be populated.
manifest_versions_int = os.path.join(
self._orig_root, site_params.INTERNAL_MANIFEST_VERSIONS_PATH)
manifest_versions_ext = os.path.join(
self._orig_root, site_params.EXTERNAL_MANIFEST_VERSIONS_PATH)
repo = self.GetWorkspaceRepo()
# TODO: Add 'patch' support somehow,
if repo.branch == 'master':
incr_type = 'build'
else:
incr_type = 'branch'
build_spec_path = manifest_version.GenerateAndPublishOfficialBuildSpec(
repo,
incr_type,
manifest_versions_int=manifest_versions_int,
manifest_versions_ext=manifest_versions_ext,
dryrun=self._run.options.debug)
if self._run.options.debug:
msg = 'DEBUG: Would have defined: %s' % build_spec_path
else:
msg = 'Defined: %s' % build_spec_path
logging.PrintBuildbotStepText(msg)
class WorkspaceInitSDKStage(WorkspaceStageBase):
"""Stage that is responsible for initializing the SDK."""
category = constants.CI_INFRA_STAGE
def PerformStage(self):
chroot_path = os.path.join(self._build_root,
constants.DEFAULT_CHROOT_DIR)
# Worksapce chroots are always wiped by cleanup stage, no need to update.
cmd = ['cros_sdk', '--create']
commands.RunBuildScript(self._build_root, cmd, chromite_cmd=True)
post_ver = cros_sdk_lib.GetChrootVersion(chroot_path)
logging.PrintBuildbotStepText(post_ver)
class WorkspaceSetupBoardStage(generic_stages.BoardSpecificBuilderStage,
WorkspaceStageBase):
"""Stage that is responsible for building host pkgs and setting up a board."""
category = constants.CI_INFRA_STAGE
def PerformStage(self):
usepkg = self._run.config.usepkg_build_packages
commands.SetupBoard(
self._build_root, board=self._current_board, usepkg=usepkg,
force=self._run.config.board_replace,
extra_env=self._portage_extra_env, chroot_upgrade=True,
profile=self._run.options.profile or self._run.config.profile)
class WorkspaceBuildPackagesStage(generic_stages.BoardSpecificBuilderStage,
WorkspaceStageBase):
"""Build Chromium OS packages."""
category = constants.PRODUCT_OS_STAGE
def PerformStage(self):
usepkg = False
if self.AfterLimit(BUILD_PACKAGES_PREBUILTS):
usepkg = self._run.config.usepkg_build_packages
packages = self.GetListOfPackagesToBuild()
cmd = ['./build_packages', '--board=%s' % self._current_board,
'--accept_licenses=@CHROMEOS', '--skip_chroot_upgrade',
'--nowithautotest', ]
if self.AfterLimit(BUILD_PACKAGES_WITH_DEBUG_SYMBOLS):
cmd.append('--withdebugsymbols')
if not usepkg:
cmd.extend(commands.LOCAL_BUILD_FLAGS)
if self._run.config.nobuildretry:
cmd.append('--nobuildretry')
chroot_args = []
if self._run.options.chrome_root:
chroot_args.append('--chrome_root=%s' % self._run.options.chrome_root)
# TODO: Add event file handling, for build package performance tracking.
#if self.AfterLimit(BUILD_PACKAGES_EVENTS):
# cmd.append('--withevents')
# cmd.append('--eventfile=%s' % event_file)
cmd.extend(packages)
commands.RunBuildScript(
self._build_root, cmd, chroot_args=chroot_args, enter_chroot=True)