# distbuild/artifact_reference.py -- Decode/encode ArtifactReference objects # # Copyright (C) 2012, 2014-2015 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, see . import json import logging import yaml import morphlib class ArtifactReference(object): # pragma: no cover '''Container for some basic information about an artifact.''' def __init__(self, basename, encoded): self._basename = basename self._dict = encoded def __str__(self): return self._basename def __repr__(self): return '' % self._basename def __getattr__(self, name): if not name.startswith('_'): return self._dict[name] else: super(ArtifactReference, self).__getattr__(name) def __setattr__(self, name, val): if not name.startswith('_'): self._dict[name] = val else: super(ArtifactReference, self).__setattr__(name, val) def basename(self): return self._basename def walk(self): done = set() def depth_first(a): if a not in done: done.add(a) for dep in a.dependencies: for ret in depth_first(dep): yield ret yield a return list(depth_first(self)) def encode_artifact(artifact, repo, ref): '''Encode part of an Artifact object and dependencies into string form.''' def get_source_dict(source): source_dict = { 'filename': source.filename, 'kind': source.morphology['kind'], 'source_name': source.name, 'source_repo': source.repo_name, 'source_ref': source.original_ref, 'source_sha1': source.sha1, 'source_artifact_names': [], 'dependencies': [] } for dependency in source.dependencies: source_dict['dependencies'].append(dependency.basename()) for source_artifact_name in source.artifacts: source_dict['source_artifact_names'].append(source_artifact_name) return source_dict def get_artifact_dict(a): if artifact.source.morphology['kind'] == 'system': # pragma: no cover arch = artifact.source.morphology['arch'] else: arch = artifact.arch a_dict = { 'arch': arch, 'cache_key': a.source.cache_key, 'name': a.name, 'repo': repo, 'ref': ref, } return a_dict encoded_artifacts = {} encoded_sources = {} root_filename = artifact.source.filename for a in artifact.walk(): if a.basename() not in encoded_artifacts: # pragma: no cover encoded_artifacts[a.basename()] = get_artifact_dict(a) encoded_sources[a.source.cache_key] = get_source_dict(a.source) content = { 'root-artifact': artifact.basename(), 'root-filename': root_filename, 'artifacts': encoded_artifacts, 'sources': encoded_sources } return json.dumps(yaml.dump(content)) def encode_artifact_reference(artifact): # pragma: no cover '''Encode an ArtifactReference object into string form. The ArtifactReference object is encoded such that it can be recreated by ``decode_artifact_reference``. ''' artifact_dict = { 'arch': artifact.arch, 'cache_key': artifact.cache_key, 'name': artifact.name, 'repo': artifact.repo, 'ref': artifact.ref } source_dict = { 'filename': artifact.filename, 'kind': artifact.kind, 'source_name': artifact.source_name, 'source_repo': artifact.source_repo, 'source_ref': artifact.source_ref, 'source_sha1': artifact.source_sha1, 'source_artifact_names': [], 'dependencies': [] } for dependency in artifact.dependencies: source_dict['dependencies'].append(dependency.basename()) for source_artifact_name in artifact.source_artifact_names: source_dict['source_artifact_names'].append(source_artifact_name) content = { 'root-artifact': artifact.basename(), 'root-filename': artifact.root_filename, 'artifacts': {artifact.basename(): artifact_dict}, 'sources': {artifact.cache_key: source_dict} } return json.dumps(yaml.dump(content)) def decode_artifact_reference(encoded): '''Decode an ArtifactReference object from `encoded`. The argument should be a string returned by ``encode_artifact`` or ``encode_artifact_reference``. The decoded ArtifactReference object will be sufficient to represent a build graph and contain enough information to allow `morph worker-build` to calculate a build graph and find the original Artifact object it needs to build. ''' content = yaml.load(json.loads(encoded)) root = content['root-artifact'] encoded_artifacts = content['artifacts'] encoded_sources = content['sources'] artifacts = {} # decode artifacts for basename, artifact_dict in encoded_artifacts.iteritems(): artifact_dict.update(encoded_sources[artifact_dict['cache_key']]) artifact = ArtifactReference(basename, artifact_dict) artifact.root_filename = content['root-filename'] artifacts[basename] = artifact # add dependencies for basename, a_dict in encoded_artifacts.iteritems(): artifact = artifacts[basename] artifact.dependencies = [artifacts.get(dep) for dep in artifact.dependencies] return artifacts[root]