blob: 2a13a1afa384a9c484202bccbce833868547eebd [file] [log] [blame]
#!/usr/bin/env python2.7
# Copyright 2018 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.
import contextlib
import json
import os
import shutil
import tarfile
import tempfile
from files import make_dir
class Frontend(object):
'''Processes the contents of an SDK tarball and runs them through various
transformation methods.
In order to process atoms of type "foo", a frontend needs to define a
`install_foo_atom` method that accepts a single argument representing
the atom's metadata in JSON format.
'''
def __init__(self, output='', archive='', directory=''):
self._archive = archive
self._directory = directory
self.output = os.path.realpath(output)
self._source_dir = ''
def source(self, *args):
'''Builds a path to a source file.
Only available while the frontend is running.
'''
if not self._source_dir:
raise Exception('Error: accessing sources while inactive')
return os.path.join(self._source_dir, *args)
def dest(self, *args):
'''Builds a path in the output directory.
This method also ensures that the directory hierarchy exists in the
output directory.
Behaves correctly if the first argument is already within the output
directory.
'''
if (os.path.commonprefix([os.path.realpath(args[0]), self.output]) ==
self.output):
path = os.path.join(*args)
else:
path = os.path.join(self.output, *args)
return make_dir(path)
def prepare(self, arch, atom_types):
'''Called before elements are processed.'''
pass
def finalize(self, arch, atom_types):
'''Called after all elements have been processed.'''
pass
def run(self):
'''Runs this frontend through the contents of the archive.
Returns true if successful.
'''
with self._create_archive_dir() as archive_dir:
self._source_dir = archive_dir
# Convenience for loading metadata files below.
def load_metadata(*args):
with open(self.source(*args), 'r') as meta_file:
return json.load(meta_file)
manifest = load_metadata('meta', 'manifest.json')
types = set([p['type'] for p in manifest['parts']])
self.prepare(manifest['arch'], types)
# Process each SDK atom.
for part in manifest['parts']:
type = part['type']
atom = load_metadata(part['meta'])
getattr(self, 'install_%s_atom' % type, self._handle_atom)(atom)
self.finalize(manifest['arch'], types)
# Reset the source directory, which may be about to disappear.
self._source_dir = ''
return True
def _handle_atom(self, atom):
'''Default atom handler.'''
print('Ignored %s (%s)' % (atom['name'], atom['type']))
@contextlib.contextmanager
def _create_archive_dir(self):
if self._directory:
yield self._directory
elif self._archive:
temp_dir = tempfile.mkdtemp(prefix='fuchsia-bazel')
# Extract the tarball into the temporary directory.
# This is vastly more efficient than accessing files one by one via
# the tarfile API.
with tarfile.open(self._archive) as archive:
archive.extractall(temp_dir)
try:
yield temp_dir
finally:
shutil.rmtree(temp_dir, ignore_errors=True)
else:
raise Exception('Error: archive or directory must be set')