blob: 68048cce95a5f98351d81fcf06efab79f18d43f0 [file] [log] [blame]
# Copyright 2017 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from recipe_engine import recipe_api
import os
class TarApi(recipe_api.RecipeApi):
"""Provides steps to tar and untar files."""
COMPRESSION_OPTS = ['gzip', 'bzip2', 'xz', 'lzma']
def __init__(self, *args, **kwargs):
super(TarApi, self).__init__(*args, **kwargs)
self._bsdtar_path = None
def ensure_tar(self, version=None):
"""Ensures that bsdtar is installed."""
if self._bsdtar_path:
return self._bsdtar_path
with self.m.step.nest('ensure bsdtar'):
with self.m.context(infra_steps=True):
pkgs = self.m.cipd.EnsureFile()
pkgs.add_package('fuchsia/tools/bsdtar/${platform}', version or 'latest')
cipd_dir = self.m.path['start_dir'].join('cipd', 'bsdtar')
self.m.cipd.ensure(cipd_dir, pkgs)
self._bsdtar_path = cipd_dir.join('bsdtar')
return self._bsdtar_path
def create(self, path, compression=None):
"""Returns TarArchive object that can be used to compress a set of files.
Args:
path: path of the archive file to be created.
compression: str, one of COMPRESSION_OPTS or None to disable compression.
"""
assert not compression or compression in TarApi.COMPRESSION_OPTS, (
'compression must be one of %s', TarApi.COMPRESSION_OPTS)
return TarArchive(self, path, compression)
def extract(self, step_name, path, directory=None, strip_components=None):
"""Uncompress |archive| file.
Args:
step_name: name of the step.
path: absolute path to archive file.
directory: directory to extract the archive in.
strip_components: strip number of leading components from file names.
"""
assert self._bsdtar_path
# We use long-form options whenever possible, but for options with
# arguments, we have to use the short form. The recipe engine tests require
# objects which might be placeholders (in this case |path|) to be their own
# argument, and the version of tar we're using doesn't support
# '--long-opt arg'. It only supports '--long-opt=arg' or short-form like
# '-s arg'.
cmd = [
self._bsdtar_path,
'--extract',
'--verbose',
'-f',
path,
]
if directory:
cmd.extend(['-C', directory])
if strip_components:
cmd.extend(['--strip-components', str(int(strip_components))])
return self.m.step(step_name, cmd)
class TarArchive(object):
"""Used to gather a list of files to tar."""
def __init__(self, module, path, compression):
self._module = module
self._path = path
self._compression = compression
self._entries = {}
@property
def path(self):
return self._path
def add(self, path, directory=None):
"""Stages single file to be added to the package.
Args:
path: absolute path to a file, should be a child of |directory|.
directory: ancestor directory of |path|. The name of the file
inside the archive will not include |directory|. Defaults to $CWD.
"""
if not directory:
directory = self._module.m.context.cwd
assert directory.is_parent_of(path), (
'directory must be a parent of path. directory: %s.%s, path: %s.%s'
% (directory.base, directory.pieces, path.base, path.pieces))
self._entries.setdefault(str(directory), []).append(str(path))
def tar(self, step_name):
"""Step to tar all staged files."""
assert self._module._bsdtar_path
cmd = [
self._module._bsdtar_path,
'--create',
'-f',
self._path
]
if self._compression:
cmd.append('--%s' % self._compression)
for directory in sorted(self._entries):
cmd.extend(['-C', directory] + [
os.path.relpath(p, directory) for p in self._entries[directory]])
step_result = self._module.m.step(step_name, cmd)
self._module.m.path.mock_add_paths(self._path)
return step_result