summaryrefslogtreecommitdiff
path: root/morphlib/builder2.py
diff options
context:
space:
mode:
authorSam Thursfield <sam.thursfield@codethink.co.uk>2015-01-05 09:56:03 +0000
committerSam Thursfield <sam.thursfield@codethink.co.uk>2015-01-05 09:57:44 +0000
commitf3197d814a5e883f47631a4f0acfa51bd1285daf (patch)
treeb1353461cfa944c62a7bfbbc8fb51664581bfe27 /morphlib/builder2.py
parenta4f5d580dfe9b1715e24dd1b49dde4f1d240b880 (diff)
parentfb7eb661b2b02bf049d24fa4a232f303b5058b1b (diff)
downloadmorph-f3197d814a5e883f47631a4f0acfa51bd1285daf.tar.gz
Merge remote-tracking branch 'origin/sam/rename-builder2'
Reviewed-By: Pedro Alvarez <pedro.alvarez@codethink.co.uk> Reviewed-By: Richard Ipsum <richard.ipsum@codethink.co.uk>
Diffstat (limited to 'morphlib/builder2.py')
-rw-r--r--morphlib/builder2.py720
1 files changed, 0 insertions, 720 deletions
diff --git a/morphlib/builder2.py b/morphlib/builder2.py
deleted file mode 100644
index 9e158ea1..00000000
--- a/morphlib/builder2.py
+++ /dev/null
@@ -1,720 +0,0 @@
-# Copyright (C) 2012-2014 Codethink Limited
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; version 2 of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-
-from collections import defaultdict
-import datetime
-import errno
-import json
-import logging
-import os
-from os.path import relpath
-import shutil
-import stat
-import tarfile
-import time
-import traceback
-import subprocess
-import tempfile
-import gzip
-
-import cliapp
-
-import morphlib
-from morphlib.artifactcachereference import ArtifactCacheReference
-import morphlib.gitversion
-
-SYSTEM_INTEGRATION_PATH = os.path.join('baserock', 'system-integration')
-
-def extract_sources(app, repo_cache, repo, sha1, srcdir): #pragma: no cover
- '''Get sources from git to a source directory, including submodules'''
-
- def extract_repo(repo, sha1, destdir):
- app.status(msg='Extracting %(source)s into %(target)s',
- source=repo.original_name,
- target=destdir)
-
- repo.checkout(sha1, destdir)
- morphlib.git.reset_workdir(app.runcmd, destdir)
- submodules = morphlib.git.Submodules(app, repo.path, sha1)
- try:
- submodules.load()
- except morphlib.git.NoModulesFileError:
- return []
- else:
- tuples = []
- for sub in submodules:
- cached_repo = repo_cache.get_repo(sub.url)
- sub_dir = os.path.join(destdir, sub.path)
- tuples.append((cached_repo, sub.commit, sub_dir))
- return tuples
-
- todo = [(repo, sha1, srcdir)]
- while todo:
- repo, sha1, srcdir = todo.pop()
- todo += extract_repo(repo, sha1, srcdir)
- set_mtime_recursively(srcdir)
-
-def set_mtime_recursively(root): # pragma: no cover
- '''Set the mtime for every file in a directory tree to the same.
-
- We do this because git checkout does not set the mtime to anything,
- and some projects (binutils, gperf for example) include formatted
- documentation and try to randomly build things or not because of
- the timestamps. This should help us get more reliable builds.
-
- '''
-
- now = time.time()
- for dirname, subdirs, basenames in os.walk(root.encode("utf-8"),
- topdown=False):
- for basename in basenames:
- pathname = os.path.join(dirname, basename)
- # we need the following check to ignore broken symlinks
- if os.path.exists(pathname):
- os.utime(pathname, (now, now))
- os.utime(dirname, (now, now))
-
-def ldconfig(runcmd, rootdir): # pragma: no cover
- '''Run ldconfig for the filesystem below ``rootdir``.
-
- Essentially, ``rootdir`` specifies the root of a new system.
- Only directories below it are considered.
-
- ``etc/ld.so.conf`` below ``rootdir`` is assumed to exist and
- be populated by the right directories, and should assume
- the root directory is ``rootdir``. Example: if ``rootdir``
- is ``/tmp/foo``, then ``/tmp/foo/etc/ld.so.conf`` should
- contain ``/lib``, not ``/tmp/foo/lib``.
-
- The ldconfig found via ``$PATH`` is used, not the one in ``rootdir``,
- since in bootstrap mode that might not yet exist, the various
- implementations should be compatible enough.
-
- '''
-
- # FIXME: use the version in ROOTDIR, since even in
- # bootstrap it will now always exist due to being part of build-essential
-
- conf = os.path.join(rootdir, 'etc', 'ld.so.conf')
- if os.path.exists(conf):
- logging.debug('Running ldconfig for %s' % rootdir)
- cache = os.path.join(rootdir, 'etc', 'ld.so.cache')
-
- # The following trickery with $PATH is necessary during the Baserock
- # bootstrap build: we are not guaranteed that PATH contains the
- # directory (/sbin conventionally) that ldconfig is in. Then again,
- # it might, and if so, we don't want to hardware a particular
- # location. So we add the possible locations to the end of $PATH
- env = dict(os.environ)
- old_path = env['PATH']
- env['PATH'] = '%s:/sbin:/usr/sbin:/usr/local/sbin' % old_path
- runcmd(['ldconfig', '-r', rootdir], env=env)
- else:
- logging.debug('No %s, not running ldconfig' % conf)
-
-
-def download_depends(constituents, lac, rac, metadatas=None):
- for constituent in constituents:
- if not lac.has(constituent):
- source = rac.get(constituent)
- target = lac.put(constituent)
- shutil.copyfileobj(source, target)
- target.close()
- source.close()
- if metadatas is not None:
- for metadata in metadatas:
- if not lac.has_artifact_metadata(constituent, metadata):
- if rac.has_artifact_metadata(constituent, metadata):
- src = rac.get_artifact_metadata(constituent, metadata)
- dst = lac.put_artifact_metadata(constituent, metadata)
- shutil.copyfileobj(src, dst)
- dst.close()
- src.close()
-
-
-def get_chunk_files(f): # pragma: no cover
- tar = tarfile.open(fileobj=f)
- for member in tar.getmembers():
- if member.type is not tarfile.DIRTYPE:
- yield member.name
- tar.close()
-
-
-def get_stratum_files(f, lac): # pragma: no cover
- for ca in (ArtifactCacheReference(a)
- for a in json.load(f, encoding='unicode-escape')):
- cf = lac.get(ca)
- for filename in get_chunk_files(cf):
- yield filename
- cf.close()
-
-
-class BuilderBase(object):
-
- '''Base class for building artifacts.'''
-
- def __init__(self, app, staging_area, local_artifact_cache,
- remote_artifact_cache, source, repo_cache, max_jobs,
- setup_mounts):
- self.app = app
- self.staging_area = staging_area
- self.local_artifact_cache = local_artifact_cache
- self.remote_artifact_cache = remote_artifact_cache
- self.source = source
- self.repo_cache = repo_cache
- self.max_jobs = max_jobs
- self.build_watch = morphlib.stopwatch.Stopwatch()
- self.setup_mounts = setup_mounts
-
- def save_build_times(self):
- '''Write the times captured by the stopwatch'''
- meta = {
- 'build-times': {}
- }
- for stage in self.build_watch.ticks.iterkeys():
- meta['build-times'][stage] = {
- 'start': '%s' % self.build_watch.start_time(stage),
- 'stop': '%s' % self.build_watch.stop_time(stage),
- 'delta': '%.4f' % self.build_watch.start_stop_seconds(stage)
- }
-
- logging.debug('Writing metadata to the cache')
- with self.local_artifact_cache.put_source_metadata(
- self.source, self.source.cache_key,
- 'meta') as f:
- json.dump(meta, f, indent=4, sort_keys=True,
- encoding='unicode-escape')
- f.write('\n')
-
- def create_metadata(self, artifact_name, contents=[]): # pragma: no cover
- '''Create metadata to artifact to allow it to be reproduced later.
-
- The metadata is represented as a dict, which later on will be
- written out as a JSON file.
-
- '''
-
- assert isinstance(self.source.repo,
- morphlib.cachedrepo.CachedRepo)
- meta = {
- 'artifact-name': artifact_name,
- 'source-name': self.source.name,
- 'kind': self.source.morphology['kind'],
- 'description': self.source.morphology['description'],
- 'repo': self.source.repo.url,
- 'repo-alias': self.source.repo_name,
- 'original_ref': self.source.original_ref,
- 'sha1': self.source.sha1,
- 'morphology': self.source.filename,
- 'cache-key': self.source.cache_key,
- 'cache-id': self.source.cache_id,
- 'morph-version': {
- 'ref': morphlib.gitversion.ref,
- 'tree': morphlib.gitversion.tree,
- 'commit': morphlib.gitversion.commit,
- 'version': morphlib.gitversion.version,
- },
- 'contents': contents,
- }
-
- return meta
-
- # Wrapper around open() to allow it to be overridden by unit tests.
- def _open(self, filename, mode): # pragma: no cover
- dirname = os.path.dirname(filename)
- if not os.path.exists(dirname):
- os.makedirs(dirname)
- return open(filename, mode)
-
- def write_metadata(self, instdir, artifact_name,
- contents=[]): # pragma: no cover
- '''Write the metadata for an artifact.
-
- The file will be located under the ``baserock`` directory under
- instdir, named after ``cache_key`` with ``.meta`` as the suffix.
- It will be in JSON format.
-
- '''
-
- meta = self.create_metadata(artifact_name, contents)
-
- basename = '%s.meta' % artifact_name
- filename = os.path.join(instdir, 'baserock', basename)
-
- # Unit tests use StringIO, which in Python 2.6 isn't usable with
- # the "with" statement. So we don't do it with "with".
- f = self._open(filename, 'w')
- json.dump(meta, f, indent=4, sort_keys=True, encoding='unicode-escape')
- f.close()
-
- def runcmd(self, *args, **kwargs):
- return self.staging_area.runcmd(*args, **kwargs)
-
-class ChunkBuilder(BuilderBase):
-
- '''Build chunk artifacts.'''
-
- def create_devices(self, destdir): # pragma: no cover
- '''Creates device nodes if the morphology specifies them'''
- morphology = self.source.morphology
- perms_mask = stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO
- if 'devices' in morphology and morphology['devices'] is not None:
- for dev in morphology['devices']:
- destfile = os.path.join(destdir, './' + dev['filename'])
- mode = int(dev['permissions'], 8) & perms_mask
- if dev['type'] == 'c':
- mode = mode | stat.S_IFCHR
- elif dev['type'] == 'b':
- mode = mode | stat.S_IFBLK
- else:
- raise IOError('Cannot create device node %s,'
- 'unrecognized device type "%s"'
- % (destfile, dev['type']))
- self.app.status(msg="Creating device node %s"
- % destfile)
- os.mknod(destfile, mode,
- os.makedev(dev['major'], dev['minor']))
- os.chown(destfile, dev['uid'], dev['gid'])
-
- def build_and_cache(self): # pragma: no cover
- with self.build_watch('overall-build'):
-
- builddir, destdir = self.staging_area.chroot_open(
- self.source, self.setup_mounts)
-
- stdout = (self.app.output
- if self.app.settings['build-log-on-stdout'] else None)
-
- cache = self.local_artifact_cache
- logpath = cache.get_source_metadata_filename(
- self.source, self.source.cache_key, 'build-log')
-
- _, temppath = tempfile.mkstemp(dir=os.path.dirname(logpath))
-
- try:
- self.get_sources(builddir)
- self.run_commands(builddir, destdir, temppath, stdout)
- self.create_devices(destdir)
-
- os.rename(temppath, logpath)
- except BaseException, e:
- logging.error('Caught exception: %s' % str(e))
- logging.info('Cleaning up staging area')
- self.staging_area.chroot_close()
- if os.path.isfile(temppath):
- with open(temppath) as f:
- for line in f:
- logging.error('OUTPUT FROM FAILED BUILD: %s' %
- line.rstrip('\n'))
-
- os.rename(temppath, logpath)
- else:
- logging.error("Couldn't find build log at %s", temppath)
-
- self.staging_area.abort()
- raise
-
- self.staging_area.chroot_close()
- built_artifacts = self.assemble_chunk_artifacts(destdir)
-
- self.save_build_times()
- return built_artifacts
-
-
- def run_commands(self, builddir, destdir,
- logfilepath, stdout=None): # pragma: no cover
- m = self.source.morphology
- bs = morphlib.buildsystem.lookup_build_system(m['build-system'])
-
- relative_builddir = self.staging_area.relative(builddir)
- relative_destdir = self.staging_area.relative(destdir)
- ccache_dir = self.staging_area.ccache_dir(self.source)
- extra_env = { 'DESTDIR': relative_destdir }
-
- steps = [
- ('pre-configure', False),
- ('configure', False),
- ('post-configure', False),
- ('pre-build', True),
- ('build', True),
- ('post-build', True),
- ('pre-test', False),
- ('test', False),
- ('post-test', False),
- ('pre-install', False),
- ('install', False),
- ('post-install', False),
- ]
- for step, in_parallel in steps:
- with self.build_watch(step):
- key = '%s-commands' % step
- cmds = m[key]
- if cmds:
- with open(logfilepath, 'a') as log:
- self.app.status(msg='Running %(key)s', key=key)
- log.write('# %s\n' % step)
-
- for cmd in cmds:
- if in_parallel:
- max_jobs = self.source.morphology['max-jobs']
- if max_jobs is None:
- max_jobs = self.max_jobs
- extra_env['MAKEFLAGS'] = '-j%s' % max_jobs
- else:
- extra_env['MAKEFLAGS'] = '-j1'
-
- try:
- with open(logfilepath, 'a') as log:
- log.write('# # %s\n' % cmd)
-
- # flushing is needed because writes from python and
- # writes from being the output in Popen have different
- # buffers, but flush handles both
- if stdout:
- stdout.flush()
-
- self.runcmd(['sh', '-c', cmd],
- extra_env=extra_env,
- cwd=relative_builddir,
- stdout=stdout or subprocess.PIPE,
- stderr=subprocess.STDOUT,
- logfile=logfilepath,
- ccache_dir=ccache_dir)
-
- if stdout:
- stdout.flush()
- except cliapp.AppException, e:
- if not stdout:
- with open(logfilepath, 'r') as log:
- self.app.output.write("%s failed\n" % step)
- shutil.copyfileobj(log, self.app.output)
- raise e
-
- def write_system_integration_commands(self, destdir,
- integration_commands, artifact_name): # pragma: no cover
-
- rel_path = SYSTEM_INTEGRATION_PATH
- dest_path = os.path.join(destdir, SYSTEM_INTEGRATION_PATH)
-
- scripts_created = []
-
- if not os.path.exists(dest_path):
- os.makedirs(dest_path)
-
- if artifact_name in integration_commands:
- prefixes_per_artifact = integration_commands[artifact_name]
- for prefix, commands in prefixes_per_artifact.iteritems():
- for index, script in enumerate(commands):
- script_name = "%s-%s-%04d" % (prefix,
- artifact_name,
- index)
- script_path = os.path.join(dest_path, script_name)
-
- with morphlib.savefile.SaveFile(script_path, 'w') as f:
- f.write("#!/bin/sh\nset -xeu\n")
- f.write(script)
- os.chmod(script_path, 0555)
-
- rel_script_path = os.path.join(SYSTEM_INTEGRATION_PATH,
- script_name)
- scripts_created += [rel_script_path]
-
- return scripts_created
-
- def assemble_chunk_artifacts(self, destdir): # pragma: no cover
- built_artifacts = []
- filenames = []
- source = self.source
- split_rules = source.split_rules
- morphology = source.morphology
- sys_tag = 'system-integration'
-
- def filepaths(destdir):
- for dirname, subdirs, basenames in os.walk(destdir):
- subdirsymlinks = [os.path.join(dirname, x) for x in subdirs
- if os.path.islink(os.path.join(dirname, x))]
- filenames = [os.path.join(dirname, x) for x in basenames]
- for relpath in (os.path.relpath(x, destdir) for x in
- [dirname] + subdirsymlinks + filenames):
- yield relpath
-
- with self.build_watch('determine-splits'):
- matches, overlaps, unmatched = \
- split_rules.partition(filepaths(destdir))
-
- system_integration = morphology.get(sys_tag) or {}
-
- with self.build_watch('create-chunks'):
- for chunk_artifact_name, chunk_artifact \
- in source.artifacts.iteritems():
- file_paths = matches[chunk_artifact_name]
- chunk_artifact = source.artifacts[chunk_artifact_name]
-
- def all_parents(path):
- while path != '':
- yield path
- path = os.path.dirname(path)
-
- def parentify(filenames):
- names = set()
- for name in filenames:
- names.update(all_parents(name))
- return sorted(names)
-
- extra_files = self.write_system_integration_commands(
- destdir, system_integration,
- chunk_artifact_name)
- extra_files += ['baserock/%s.meta' % chunk_artifact_name]
- parented_paths = parentify(file_paths + extra_files)
-
- with self.local_artifact_cache.put(chunk_artifact) as f:
- self.write_metadata(destdir, chunk_artifact_name,
- parented_paths)
-
- self.app.status(msg='Creating chunk artifact %(name)s',
- name=chunk_artifact_name)
- morphlib.bins.create_chunk(destdir, f, parented_paths)
- built_artifacts.append(chunk_artifact)
-
- for dirname, subdirs, files in os.walk(destdir):
- if files:
- raise Exception('DESTDIR %s is not empty: %s' %
- (destdir, files))
- return built_artifacts
-
- def get_sources(self, srcdir): # pragma: no cover
- s = self.source
- extract_sources(self.app, self.repo_cache, s.repo, s.sha1, srcdir)
-
-
-class StratumBuilder(BuilderBase):
- '''Build stratum artifacts.'''
-
- def is_constituent(self, artifact): # pragma: no cover
- '''True if artifact should be included in the stratum artifact'''
- return (artifact.source.morphology['kind'] == 'chunk' and \
- artifact.source.build_mode != 'bootstrap')
-
- def build_and_cache(self): # pragma: no cover
- with self.build_watch('overall-build'):
- constituents = [d for d in self.source.dependencies
- if self.is_constituent(d)]
-
- # the only reason the StratumBuilder has to download chunks is to
- # check for overlap now that strata are lists of chunks
- with self.build_watch('check-chunks'):
- for a_name, a in self.source.artifacts.iteritems():
- # download the chunk artifact if necessary
- download_depends(constituents,
- self.local_artifact_cache,
- self.remote_artifact_cache)
-
- with self.build_watch('create-chunk-list'):
- lac = self.local_artifact_cache
- for a_name, a in self.source.artifacts.iteritems():
- meta = self.create_metadata(
- a_name,
- [x.name for x in constituents])
- with lac.put_artifact_metadata(a, 'meta') as f:
- json.dump(meta, f, indent=4, sort_keys=True)
- with self.local_artifact_cache.put(a) as f:
- json.dump([c.basename() for c in constituents], f)
- self.save_build_times()
- return self.source.artifacts.values()
-
-
-class SystemBuilder(BuilderBase): # pragma: no cover
-
- '''Build system image artifacts.'''
-
- def __init__(self, *args, **kwargs):
- BuilderBase.__init__(self, *args, **kwargs)
- self.args = args
- self.kwargs = kwargs
-
- def build_and_cache(self):
- self.app.status(msg='Building system %(system_name)s',
- system_name=self.source.name)
-
- with self.build_watch('overall-build'):
- arch = self.source.morphology['arch']
-
- for a_name, artifact in self.source.artifacts.iteritems():
- handle = self.local_artifact_cache.put(artifact)
-
- try:
- fs_root = self.staging_area.destdir(self.source)
- self.unpack_strata(fs_root)
- self.write_metadata(fs_root, a_name)
- self.run_system_integration_commands(fs_root)
- unslashy_root = fs_root[1:]
- def uproot_info(info):
- info.name = relpath(info.name, unslashy_root)
- if info.islnk():
- info.linkname = relpath(info.linkname,
- unslashy_root)
- return info
- tar = tarfile.open(fileobj=handle, mode="w", name=a_name)
- self.app.status(msg='Constructing tarball of rootfs',
- chatty=True)
- tar.add(fs_root, recursive=True, filter=uproot_info)
- tar.close()
- except BaseException as e:
- logging.error(traceback.format_exc())
- self.app.status(msg='Error while building system',
- error=True)
- handle.abort()
- raise
- else:
- handle.close()
-
- self.save_build_times()
- return self.source.artifacts.itervalues()
-
- def unpack_one_stratum(self, stratum_artifact, target):
- '''Unpack a single stratum into a target directory'''
-
- cache = self.local_artifact_cache
- with cache.get(stratum_artifact) as stratum_file:
- artifact_list = json.load(stratum_file, encoding='unicode-escape')
- for chunk in (ArtifactCacheReference(a) for a in artifact_list):
- self.app.status(msg='Unpacking chunk %(basename)s',
- basename=chunk.basename(), chatty=True)
- with cache.get(chunk) as chunk_file:
- morphlib.bins.unpack_binary_from_file(chunk_file, target)
-
- target_metadata = os.path.join(
- target, 'baserock', '%s.meta' % stratum_artifact.name)
- with cache.get_artifact_metadata(stratum_artifact, 'meta') as meta_src:
- with morphlib.savefile.SaveFile(target_metadata, 'w') as meta_dst:
- shutil.copyfileobj(meta_src, meta_dst)
-
- def unpack_strata(self, path):
- '''Unpack strata into a directory.'''
-
- self.app.status(msg='Unpacking strata to %(path)s',
- path=path, chatty=True)
- with self.build_watch('unpack-strata'):
- for a_name, a in self.source.artifacts.iteritems():
- # download the stratum artifacts if necessary
- download_depends(self.source.dependencies,
- self.local_artifact_cache,
- self.remote_artifact_cache,
- ('meta',))
-
- # download the chunk artifacts if necessary
- for stratum_artifact in self.source.dependencies:
- f = self.local_artifact_cache.get(stratum_artifact)
- chunks = [ArtifactCacheReference(c) for c in json.load(f)]
- download_depends(chunks,
- self.local_artifact_cache,
- self.remote_artifact_cache)
- f.close()
-
- # unpack it from the local artifact cache
- for stratum_artifact in self.source.dependencies:
- self.unpack_one_stratum(stratum_artifact, path)
-
- ldconfig(self.app.runcmd, path)
-
- def write_metadata(self, instdir, artifact_name):
- BuilderBase.write_metadata(self, instdir, artifact_name)
-
- os_release_file = os.path.join(instdir, 'etc', 'os-release')
- dirname = os.path.dirname(os_release_file)
- if not os.path.exists(dirname):
- os.makedirs(dirname)
- with morphlib.savefile.SaveFile(os_release_file, 'w') as f:
- f.write('NAME="Baserock"\n')
- f.write('ID=baserock\n')
- f.write('HOME_URL="http://wiki.baserock.org"\n')
- f.write('SUPPORT_URL="http://wiki.baserock.org/mailinglist"\n')
- f.write('BUG_REPORT_URL="http://wiki.baserock.org/mailinglist"\n')
-
- os.chmod(os_release_file, 0644)
-
- def run_system_integration_commands(self, rootdir): # pragma: no cover
- ''' Run the system integration commands '''
-
- sys_integration_dir = os.path.join(rootdir, SYSTEM_INTEGRATION_PATH)
- if not os.path.isdir(sys_integration_dir):
- return
-
- env = {
- 'PATH': '/bin:/usr/bin:/sbin:/usr/sbin'
- }
-
- self.app.status(msg='Running the system integration commands')
-
- to_mount = (
- ('dev/shm', 'tmpfs', 'none'),
- ('tmp', 'tmpfs', 'none'),
- )
- try:
- for bin in sorted(os.listdir(sys_integration_dir)):
- argv = [os.path.join(SYSTEM_INTEGRATION_PATH, bin)]
- container_config = dict(
- root=rootdir, mounts=to_mount, mount_proc=True)
- cmdline = morphlib.util.containerised_cmdline(
- argv, **container_config)
- exit, out, err = self.app.runcmd_unchecked(
- cmdline, env=env)
- if exit != 0:
- logging.debug('Command returned code %i', exit)
- msg = error_message_for_containerised_commandline(
- argv, err, container_config)
- raise cliapp.AppException(msg)
- except BaseException, e:
- self.app.status(
- msg='Error while running system integration commands',
- error=True)
- raise
-
-
-class Builder(object): # pragma: no cover
-
- '''Helper class to build with the right BuilderBase subclass.'''
-
- classes = {
- 'chunk': ChunkBuilder,
- 'stratum': StratumBuilder,
- 'system': SystemBuilder,
- }
-
- def __init__(self, app, staging_area, local_artifact_cache,
- remote_artifact_cache, repo_cache, max_jobs, setup_mounts):
- self.app = app
- self.staging_area = staging_area
- self.local_artifact_cache = local_artifact_cache
- self.remote_artifact_cache = remote_artifact_cache
- self.repo_cache = repo_cache
- self.max_jobs = max_jobs
- self.setup_mounts = setup_mounts
-
- def build_and_cache(self, source):
- kind = source.morphology['kind']
- o = self.classes[kind](self.app, self.staging_area,
- self.local_artifact_cache,
- self.remote_artifact_cache, source,
- self.repo_cache, self.max_jobs,
- self.setup_mounts)
- self.app.status(msg='Builder.build: artifact %s with %s' %
- (source.name, repr(o)),
- chatty=True)
- built_artifacts = o.build_and_cache()
- self.app.status(msg='Builder.build: done',
- chatty=True)
- return built_artifacts