| # -*- 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 specific to firmware builds. |
| |
| Firmware builds use a mix of standard stages, and custom stages |
| related to how build artifacts are generated and published. |
| """ |
| |
| from __future__ import print_function |
| |
| import datetime |
| import json |
| import os |
| |
| from chromite.cbuildbot import commands |
| from chromite.cbuildbot.stages import generic_stages |
| from chromite.cbuildbot.stages import workspace_stages |
| from chromite.lib import config_lib |
| from chromite.lib import constants |
| from chromite.lib import cros_build_lib |
| from chromite.lib import cros_logging as logging |
| from chromite.lib import gs |
| from chromite.lib import osutils |
| |
| |
| class UnsafeBuildForPushImage(Exception): |
| """Raised if push_image is run against a non-signable build.""" |
| |
| |
| class FirmwareArchiveStage(workspace_stages.WorkspaceStageBase, |
| generic_stages.BoardSpecificBuilderStage, |
| generic_stages.ArchivingStageMixin): |
| """Generates and publishes firmware specific build artifacts. |
| |
| This stage publishes <board>_firmware_from_source.tar.bz2 to this |
| builds standard build artifacts, and also generates a 'fake' build |
| result (called a Dummy result) that looks like it came from a |
| traditional style firmware builder for a single board on the |
| firmware branch. |
| |
| The secondary result only contains firmware_from_source.tar.bz2 and |
| a dummy version of metadata.json. |
| """ |
| |
| def __init__(self, builder_run, buildstore, build_root, **kwargs): |
| """Initializer. |
| |
| Args: |
| builder_run: BuilderRun object. |
| buildstore: BuildStore instance to make DB calls with. |
| build_root: Fully qualified path to use as a string. |
| """ |
| super(FirmwareArchiveStage, self).__init__( |
| builder_run, buildstore, build_root=build_root, **kwargs) |
| |
| self.dummy_firmware_config = None |
| self.workspace_version_info = None |
| self.firmware_version = None |
| |
| @property |
| def metadata_name(self): |
| """Uniqify the name across boards.""" |
| return '%s_%s' % (self._current_board, constants.METADATA_JSON) |
| |
| @property |
| def firmware_name(self): |
| """Uniqify the name across boards.""" |
| return '%s_%s' % (self._current_board, constants.FIRMWARE_ARCHIVE_NAME) |
| |
| @property |
| def dummy_archive_url(self): |
| """Uniqify the name across boards.""" |
| return os.path.join( |
| config_lib.GetSiteParams().ARCHIVE_URL, |
| self.dummy_firmware_config, |
| self.firmware_version) |
| |
| def CreateBoardFirmwareArchive(self): |
| """Create/publish the firmware build artifact for the current board.""" |
| logging.info('Create %s', self.firmware_name) |
| |
| commands.BuildFirmwareArchive( |
| self._build_root, self._current_board, |
| self.archive_path, self.firmware_name) |
| self.UploadArtifact(self.firmware_name, archive=False) |
| |
| def CreateBoardMetadataJson(self): |
| """Create/publish the firmware build artifact for the current board.""" |
| logging.info('Create %s', self.metadata_name) |
| |
| board_metadata_path = os.path.join(self.archive_path, self.metadata_name) |
| |
| # Use the metadata for the main build, with selected fields modified. |
| board_metadata = self._run.attrs.metadata.GetDict() |
| board_metadata['boards'] = [self._current_board] |
| board_metadata['branch'] = self._run.config.workspace_branch |
| board_metadata['version_full'] = self.firmware_version |
| board_metadata['version_milestone'] = \ |
| self.workspace_version_info.chrome_branch |
| board_metadata['version_platform'] = \ |
| self.workspace_version_info.VersionString() |
| board_metadata['version'] = { |
| 'platform': self.workspace_version_info.VersionString(), |
| 'full': self.firmware_version, |
| 'milestone': self.workspace_version_info.chrome_branch, |
| } |
| |
| current_time = datetime.datetime.now() |
| current_time_stamp = cros_build_lib.UserDateTimeFormat(timeval=current_time) |
| |
| # We report the build as passing, since we can't get here if isn't. |
| board_metadata['status'] = { |
| 'status': 'pass', |
| 'summary': '', |
| 'current-time': current_time_stamp, |
| } |
| |
| logging.info('Writing firmware metadata to %s.', board_metadata_path) |
| osutils.WriteFile(board_metadata_path, json.dumps(board_metadata, |
| indent=2, sort_keys=True), |
| atomic=True, makedirs=True) |
| |
| self.UploadArtifact(self.metadata_name, archive=False) |
| |
| def CreateBoardBuildResult(self): |
| """Publish a dummy build result for a traditional firmware build.""" |
| logging.info('Publish "Dummy" firmware build for GE. %s', |
| self._current_board) |
| |
| # What file are we uploading? |
| firmware_path = os.path.join(self.archive_path, self.firmware_name) |
| metadata_path = os.path.join(self.archive_path, self.metadata_name) |
| |
| gs_context = gs.GSContext(acl=self.acl, dry_run=self._run.options.debug) |
| gs_context.CopyInto(firmware_path, self.dummy_archive_url, |
| filename=constants.FIRMWARE_ARCHIVE_NAME) |
| gs_context.CopyInto(metadata_path, self.dummy_archive_url, |
| filename=constants.METADATA_JSON) |
| |
| dummy_http_url = gs.GsUrlToHttp(self.dummy_archive_url, |
| public=False, directory=True) |
| |
| label = '%s firmware [%s]' % (self._current_board, self.firmware_version) |
| logging.PrintBuildbotLink(label, dummy_http_url) |
| |
| def PushBoardImage(self): |
| """Helper method to run push_image against the dummy boards artifacts.""" |
| # This helper script is only available on internal manifests currently. |
| if not self._run.config['internal']: |
| raise UnsafeBuildForPushImage("Can't use push_image on external builds.") |
| |
| logging.info('Use pushimage to publish signing artifacts for: %s', |
| self._current_board) |
| |
| # Push build artifacts to gs://chromeos-releases for signing and release. |
| # This runs TOT pushimage against the build artifacts for the branch. |
| commands.PushImages( |
| board=self._current_board, |
| archive_url=self.dummy_archive_url, |
| dryrun=self._run.options.debug or not self._run.config['push_image'], |
| profile=self._run.options.profile or self._run.config['profile'], |
| sign_types=self._run.config['sign_types'] or [], |
| buildroot=self._build_root) |
| |
| def PerformStage(self): |
| """Archive and publish the firmware build artifacts.""" |
| # Populate shared values. |
| self.workspace_version_info = self.GetWorkspaceVersionInfo() |
| |
| if self._run.options.debug: |
| build_id, _ = self._run.GetCIDBHandle() |
| self.dummy_firmware_config = '%s-firmware-tryjob' % self._current_board |
| self.firmware_version = 'R%s-%s-b%s' % ( |
| self.workspace_version_info.chrome_branch, |
| self.workspace_version_info.VersionString(), |
| build_id) |
| else: |
| self.dummy_firmware_config = '%s-firmware' % self._current_board |
| self.firmware_version = 'R%s-%s' % ( |
| self.workspace_version_info.chrome_branch, |
| self.workspace_version_info.VersionString()) |
| |
| logging.info('Firmware version: %s', self.firmware_version) |
| logging.info('Archive firmware build as: %s', self.dummy_firmware_config) |
| |
| self.CreateBoardFirmwareArchive() |
| self.CreateBoardMetadataJson() |
| self.CreateBoardBuildResult() |
| self.PushBoardImage() |