| # |
| # Routines to pull information out of YML config files: |
| # - config/Version.yml |
| # - config/Ports.yml |
| # - config/Modules.yml |
| # |
| import yaml |
| import re |
| import subprocess |
| from itertools import chain |
| |
| ## |
| # A Python exception class for our use |
| ## |
| class MadPackConfigError(Exception): |
| def __init__(self, value): |
| self.value = value |
| def __str__(self): |
| return repr(self.value) |
| |
| ## |
| # Load version string from Version.yml file. |
| # Typical Version.yml file: |
| # version: 0.01 |
| # @param configdir the directory where we can find Version.yml |
| ## |
| def get_version(configdir): |
| |
| try: |
| conf = yaml.load(open(configdir + '/Version.yml')) |
| except: |
| print "configyml : ERROR : missing or malformed Version.yml" |
| exit(2) |
| |
| try: |
| conf['version'] |
| except: |
| print "configyml : ERROR : malformed Version.yml" |
| exit(2) |
| |
| return str( conf['version']) |
| |
| |
| ## |
| # Load Ports.yml file |
| # @param configdir the directory where we can find Version.yml |
| ## |
| def get_ports(configdir): |
| |
| try: |
| conf = yaml.load(open(configdir + '/Ports.yml')) |
| except: |
| print "configyml : ERROR : missing or malformed Ports.yml" |
| exit(2) |
| |
| for port in conf: |
| try: |
| conf[port]['name'] |
| except: |
| print "configyml : ERROR : malformed Ports.yml: no name element for port " + port |
| exit(2) |
| |
| return conf |
| |
| ## |
| # Load modules |
| # |
| # @param configdir the directory where we can find the [port_id].yml file |
| # @param id the ID of the specific DB port |
| # @param src the directory of the source code for a specific port |
| ## |
| def get_modules( confdir): |
| |
| fname = "Modules.yml" |
| |
| try: |
| conf = yaml.load( open( confdir + '/' + fname)) |
| except: |
| print "configyml : ERROR : missing or malformed " + confdir + '/' + fname |
| raise Exception |
| |
| try: |
| conf['modules'] |
| except: |
| print "configyml : ERROR : missing modules section in " + fname |
| raise Exception |
| |
| conf = topsort_modules( conf) |
| |
| return conf |
| |
| ## |
| # Helper function |
| ## |
| def flatten(listOfLists): |
| "Flatten one level of nesting" |
| return chain.from_iterable(listOfLists) |
| |
| ## |
| # Quick and dirty topological sort |
| # Currently does dumb cycle detection. |
| # @param depdict an edgelist dictionary, e.g. {'b': ['a'], 'z': ['m', 'n'], 'm': ['a', 'b']} |
| ## |
| def topsort(depdict): |
| out = dict() |
| candidates = set() |
| curlevel = 0 |
| |
| while len(depdict) > 0: |
| found = 0 # flag to check if we find anything new this iteration |
| newdepdict = dict() |
| # find the keys with no values |
| keynoval = filter(lambda t: t[1] == [], depdict.iteritems()) |
| # find the values that are not keys |
| valnotkey = set(flatten(depdict.itervalues())) - set(depdict.iterkeys()) |
| |
| candidates = set([k[0] for k in keynoval]) | valnotkey |
| for c in candidates: |
| if c not in out: |
| found += 1 |
| out[c] = curlevel |
| |
| for k in depdict.iterkeys(): |
| if depdict[k] != []: |
| newdepdict[k] = filter(lambda v: v not in valnotkey, depdict[k]) |
| # newdepdict = dict(newdepdict) |
| if newdepdict == depdict: |
| raise MadPackConfigError(str(depdict)) |
| else: |
| depdict = newdepdict |
| if found > 0: |
| curlevel += 1 |
| |
| return out |
| |
| ## |
| # Top-sort the modules in conf |
| # @param conf a madpack configuration |
| ## |
| def topsort_modules(conf): |
| |
| depdict = dict() |
| for m in conf['modules']: |
| try: |
| depdict[m['name']] = m['depends'] |
| except: |
| depdict[m['name']] = [] |
| try: |
| module_dict = topsort(depdict) |
| except MadPackConfigError as e: |
| raise MadPackConfigError("invalid cyclic dependency between modules: " + e.value + "; check Modules.yml files") |
| missing = set(module_dict.keys()) - set(depdict.keys()) |
| inverted = dict() |
| if len(missing) > 0: |
| for k in depdict.iterkeys(): |
| for v in depdict[k]: |
| if v not in inverted: |
| inverted[v] = set() |
| inverted[v].add(k) |
| print "configyml : ERROR : required modules missing from Modules.yml: " |
| for m in missing: |
| print " " + m + " (required by " + str(list(inverted[m])) + ")" |
| exit(2) |
| conf['modules'] = sorted(conf['modules'], key=lambda m:module_dict[m['name']]) |
| return conf |