diff options
author | Lars Wirzenius <liw@liw.fi> | 2011-09-29 10:51:04 +0100 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2011-09-29 10:51:04 +0100 |
commit | 50a948a728a0768907700befe501bb743828b67b (patch) | |
tree | a4cbc074215f6b26b8577b85c3538392e0bee42b /morphlib/morphology.py | |
download | morph-50a948a728a0768907700befe501bb743828b67b.tar.gz |
Initial import.
Diffstat (limited to 'morphlib/morphology.py')
-rw-r--r-- | morphlib/morphology.py | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/morphlib/morphology.py b/morphlib/morphology.py new file mode 100644 index 00000000..d31a000c --- /dev/null +++ b/morphlib/morphology.py @@ -0,0 +1,196 @@ +# Copyright (C) 2011 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; either 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. + + +import json +import logging + + +class SchemaError(Exception): + + pass + + +class Morphology(object): + + '''Represent a morphology: description of how to build binaries.''' + + def __init__(self, fp, baseurl=None): + self._fp = fp + self._baseurl = baseurl or '' + self._load() + + def _load(self): + logging.debug('Loading morphology %s' % self._fp.name) + self._dict = json.load(self._fp) + + if 'name' not in self._dict: + raise self._error('must contain "name"') + + if not self.name: + raise self._error('"name" must not be empty') + + if 'kind' not in self._dict: + raise self._error('must contain "kind"') + + if self.kind == 'chunk': + self._validate_chunk() + self.source['repo'] = self._join_with_baseurl(self.source['repo']) + elif self.kind == 'stratum': + self._validate_stratum() + for source in self.sources.itervalues(): + source['repo'] = self._join_with_baseurl(source['repo']) + else: + raise self._error('kind must be chunk or stratum, not %s' % + self.kind) + + self.filename = self._fp.name + + def _validate_chunk(self): + valid_toplevel_keys = ['name', 'kind', 'source', 'configure-commands', + 'build-commands', 'test-commands', + 'install-commands'] + + if 'source' not in self._dict: + raise self._error('chunks must have "source" field') + + if type(self.source) != dict: + raise self._error('"source" must be a dictionary') + + if len(self.source) == 0: + raise self._error('"source" must not be empty') + + if 'repo' not in self.source: + raise self._error('"source" must contain "repo"') + + if not self.source['repo']: + raise self._error('"source" must contain non-empty "repo"') + + if 'ref' not in self.source: + raise self._error('"source" must contain "ref"') + + if not self.source['ref']: + raise self._error('"source" must contain non-empty "ref"') + + for key in self.source.keys(): + if key not in ('repo', 'ref'): + raise self._error('unknown key "%s" in "source"' % key) + + cmdlists = [ + (self.configure_commands, 'configure-commands'), + (self.build_commands, 'build-commands'), + (self.test_commands, 'test-commands'), + (self.install_commands, 'install-commands'), + ] + for value, name in cmdlists: + if type(value) != list: + raise self._error('"%s" must be a list' % name) + for x in value: + if type(x) != unicode: + raise self._error('"%s" must contain strings' % name) + + for key in self._dict.keys(): + if key not in valid_toplevel_keys: + raise self._error('unknown key "%s"' % key) + + def _validate_stratum(self): + valid_toplevel_keys = ['name', 'kind', 'sources'] + + if 'sources' not in self._dict: + raise self._error('stratum must contain "sources"') + + if type(self.sources) != dict: + raise self._error('"sources" must be a dict') + + if len(self.sources) == 0: + raise self._error('"sources" must not be empty') + + for name, source in self.sources.iteritems(): + if type(source) != dict: + raise self._error('"sources" must contain dicts') + if 'repo' not in source: + raise self._error('sources must have "repo"') + if type(source['repo']) != unicode: + raise self._error('"repo" must be a string') + if not source['repo']: + raise self._error('"repo" must be a non-empty string') + if 'ref' not in source: + raise self._error('sources must have "ref"') + if type(source['ref']) != unicode: + raise self._error('"ref" must be a string') + if not source['ref']: + raise self._error('"ref" must be a non-empty string') + for key in source: + if key not in ('repo', 'ref'): + raise self._error('unknown key "%s" in sources' % key) + + for key in self._dict.keys(): + if key not in valid_toplevel_keys: + raise self._error('unknown key "%s"' % key) + + @property + def name(self): + return self._dict['name'] + + @property + def kind(self): + return self._dict['kind'] + + @property + def source(self): + return self._dict['source'] + + @property + def sources(self): + return self._dict['sources'] + + @property + def configure_commands(self): + return self._dict.get('configure-commands', []) + + @property + def build_commands(self): + return self._dict.get('build-commands', []) + + @property + def test_commands(self): + return self._dict.get('test-commands', []) + + @property + def install_commands(self): + return self._dict.get('install-commands', []) + + @property + def manifest(self): + if self.kind == 'chunk': + return [(self.source['repo'], self.source['ref'])] + else: + return [(source['repo'], source['ref']) + for source in self.sources.itervalues()] + + def _join_with_baseurl(self, url): + is_relative = (':' not in url or + '/' not in url or + url.find('/') < url.find(':')) + if is_relative: + if not url.endswith('/'): + url += '/' + return self._baseurl + url + else: + return url + + def _error(self, msg): + return SchemaError('Morphology %s: %s' % (self._fp.name, msg)) + |