blob: 4ca7b002776b5341a2815f2a37598b414d563a65 [file] [log] [blame]
#
# 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