firmware-kiev: buildbot: Archive experimental firmware images.

Adapted from change c914a0e3eafb6d6245730bedd9d59981e70dda5
to be compatible with branch.

BUG=chromium-os:30452
TEST=run local trybot on firmware-kiev branch for 'kiev-release'
verify firmware_from_source.tar.bz2 is in artifacts directory.

Change-Id: Ie789103c1abb2b4fe700b28073cdf96bd770f887
Original-Author: Hung-Te Lin <hungte@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/22790
Tested-by: Dave Parker <dparker@chromium.org>
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
Reviewed-by: David James <davidjames@chromium.org>
diff --git a/buildbot/cbuildbot_commands.py b/buildbot/cbuildbot_commands.py
index 6c4097b..6722181 100644
--- a/buildbot/cbuildbot_commands.py
+++ b/buildbot/cbuildbot_commands.py
@@ -895,6 +895,23 @@
                       cwd=scripts_dir)
 
 
+def BuildTarball(buildroot, input_list, tarball_output, cwd=None):
+  """Tars files and directories from input_list to tarball_output.
+
+  Args:
+    buildroot: Root directory where build occurs.
+    input_list: A list of files and directories to be archived.
+    tarball_output: Path of output tar archive file.
+    cwd: Current working directory when tar command is executed.
+  """
+  pbzip2 = os.path.join(buildroot, 'chroot', 'usr', 'bin', 'pbzip2')
+  cmd = ['tar',
+         '--use-compress-program=%s' % pbzip2,
+         '-cf', tarball_output]
+  cmd += input_list
+  cros_lib.RunCommand(cmd, cwd=cwd)
+
+
 def BuildAutotestTarball(buildroot, board, image_dir):
   """Tar up the autotest artifacts into image_dir.
 
@@ -936,6 +953,31 @@
   return filename
 
 
+def BuildFirmwareArchive(buildroot, board, archive_dir):
+  """Build firmware_from_source.tar.bz2 in archive_dir from build root.
+
+  Args:
+    buildroot: Root directory where build occurs.
+    board: Board name of build target.
+    archive_dir: Directory to store output file.
+
+  Returns the basename of the archived file, or None if the target board does
+  not have firmware from source.
+  """
+  files = ['image.bin', 'ec.bin']
+  firmware_root = os.path.join(buildroot, 'chroot', 'build', board, 'firmware')
+  source_list = [image_file
+                 for image_file in files
+                 if os.path.exists(os.path.join(firmware_root, image_file))]
+  if not source_list:
+    return None
+
+  archive_name = 'firmware_from_source.tar.bz2'
+  archive_file = os.path.join(archive_dir, archive_name)
+  BuildTarball(buildroot, source_list, archive_file, cwd=firmware_root)
+  return archive_name
+
+
 def BuildFactoryZip(archive_dir, image_root):
   """Build factory_image.zip in archive_dir.
 
diff --git a/buildbot/cbuildbot_stages.py b/buildbot/cbuildbot_stages.py
index 5c0d5be..99839f0 100644
--- a/buildbot/cbuildbot_stages.py
+++ b/buildbot/cbuildbot_stages.py
@@ -852,11 +852,12 @@
     if config['useflags']:
       extra_env['USE'] = ' '.join(config['useflags'])
 
-    # The following three functions are run in parallel.
+    # The following functions are run in parallel.
     #  1. UploadUpdatePayloads: Upload update payloads from test phase.
     #  2. UploadTestResults: Upload results from test phase.
     #  3. ArchiveDebugSymbols: Generate and upload debug symbols.
     #  4. BuildAndArchiveAllImages: Build and archive images.
+    #  5. BuildFirmwareImages: Build and archive firmware image.
 
     def UploadUpdatePayloads():
       """Uploads update payloads when ready."""
@@ -953,6 +954,12 @@
       shutil.copy(os.path.join(image_dir, filename), archive_path)
       commands.UploadArchivedFile(archive_path, upload_url, filename, debug)
 
+    def ArchiveFirmwareImages():
+      """Archive firmware images built from source if available."""
+      filename = commands.BuildFirmwareArchive(buildroot, board, archive_path)
+      if filename:
+        commands.UploadArchivedFile(archive_path, upload_url, filename, debug)
+
     def BuildAndArchiveAllImages():
       # If we're an official build, generate the recovery image. To conserve
       # loop devices, we try to only run one instance of build_image at a
@@ -968,7 +975,8 @@
       UploadUpdatePayloads,
       UploadTestResults,
       ArchiveDebugSymbols,
-      BuildAndArchiveAllImages])
+      BuildAndArchiveAllImages,
+      ArchiveFirmwareImages])
 
     # Update and upload LATEST file.
     commands.UpdateLatestFile(self._bot_archive_root, self._set_version)