#!/usr/bin/python -u from __future__ import print_function import sys import argparse from sets import Set from os.path import isdir from os.path import expanduser from os.path import basename from os.path import realpath import re # meaning of pkg.required.xxx variables # build: needs to be built and installed before this can be built # devel: needs to be installed before this-devel can be installed, i.e. before _other_ packages can be built against this # run: needs to be installed before this-run can be installed, i.e. before this and other packages can run with this # --------------------------------------------------------------------- helpers def debug(*objs): if args.debug: print("DEBUG: ", *objs, file=sys.stderr) def err(*objs): print("ERR: ", *objs, file=sys.stderr) def proj_dir(name): return projs_root + '/' + name def re_section(name): return re.compile('[' + name + ']' '.*?' '(?=[)', re.DOTALL) def get_os(): for d in [ projs_root + 'ytools/devutil/scripts', '/opt/ytools/bin' ]: script=d + '/get_os.sh' if os.path.isfile(script): cmd = '/bin/bash ' + script p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) (out, rr) = p.communicate() if rr: err("failed to run ", cmd) continue return out return "linux" # TODO: add support for customizing this in project.conf def htdocs_dir(name): pd = proj_dir(name) for r in [ pd + "/tools/html/htdocs", pd + "/htdocs", "/srv/www/proj/" + name ]: if isdir(r): return r return None def pkg_required_os_cascade(): os = get_os() # TODO: implement this #return [ 'os', 'os.linux', 'os.linux.suse', 'os.linux.suse.tumbleweed' ] ? return [ 'os', 'linux', 'suse', 'suse-tumbleweed' ] def strip_module_from_spec(mod): return re.sub(r'-devel$|-run$', '', re.split('([=><]+)', mod)[0].strip()) def get_section(path, section): r = '' file = open(path) pat = '[' + section + ']' in_section = False for line in file: if (line.rstrip() == pat): in_section = True continue if in_section: if len(line) and line[0] == '[': break r = r + line file.close() return r.rstrip() def read_value(path, section, key): debug("opening ", path) try: file = open(path) except: return None r = [] if not len(section): for line in file: r = re.findall('^ *' + key + ' *= *(.*)', line) if (len(r) > 0): break else: in_section = False pat = '[' + section + ']' for line in file: if (line.rstrip() == pat): in_section = True continue if in_section: if len(line) and line[0] == '[': break if key is None: r.append(line) else: r = re.findall('^ *' + key + ' *= *(.*)', line) if (len(r) > 0): break file.close() if len(r): return r[0] return None def get_value(name, section, key): #print("top_name = ", top_name) if top_name and name == top_name: proj_root = topdir else: proj_root = projs_root + '/' + name if section == 'version': file = open(proj_root + '/VERSION', 'r') r=file.read().replace('\n', '').replace('-dev', '') file.close() return r path = proj_root + '/make/project.conf' #print('path = ', path, 'top_name = ', top_name, 'name = ', name) return read_value(path, section, key) # scope 0: no children # scope 1: children # scope 2: recursive def add_modules_from_project_txt(buf, visited, spec, section, key, add_self, scope, names_only): name = strip_module_from_spec(spec) if names_only: spec = name if spec in buf: return if spec in visited: if add_self: buf.append(spec) return visited.add(spec) deps = get_value(name, section, key) debug("name = ", name, "section = ", section, "key = ", key, "deps = ", deps, "scope = ", scope, "visited = ", visited) if deps and scope > 0: if scope == 1: subscope = 0 else: subscope = 2 deps = deps.split(',') for dep in deps: add_modules_from_project_txt(buf, visited, dep, section, key, add_self=True, scope=subscope, names_only=names_only) if add_self: buf.append(spec) def get_modules_from_project_txt(names, section, keys, add_self, scope, names_only = True): if isinstance(keys, basestring): keys = [ keys ] #r = Set() r = [] for key in keys: visited = Set() for name in names: rr = [] add_modules_from_project_txt(rr, visited, name, section, key, add_self, scope, names_only) # TODO: this looks like a performance hogger for m in rr: if not m in r: r.append(m) return r def get_libname(names): vals = get_modules_from_project_txt(names, 'build', 'libname', scope = 1, add_self=False, names_only=True) if not vals: return ' '.join(names) if 'none' in vals: vals.remove('none') return ' '.join(reversed(vals)) # -L needs to contain more paths than libs linked with -l would require def get_ldpathflags(names): deps = get_modules_from_project_txt(names, 'pkg.required.jw', 'build', scope = 2, add_self=True, names_only=True) r = '' for m in deps: libname = get_libname([m]) if len(libname): r = r + ' -L' + proj_dir(m) + '/lib' print(r[1:]) def get_ldflags(names): #print(names) deps = get_modules_from_project_txt(names, 'pkg.required.jw', 'build', scope = 1, add_self=False, names_only=True) #print(deps) r = '' for m in reversed(deps): libname = get_libname([m]) if len(libname): #r = r + ' -L' + proj_dir(m) + '/lib -l' + libname r = r + ' -l' + libname if len(r): ldpathflags = get_ldpathflags(names) if ldpathflags: r = ldpathflags + ' ' + r return r[1::] return '' def commands(): f = open(sys.argv[0]) cmds = [] for line in f: debug("checking line ", line) rr = re.findall('^def *cmd_([a-z0-9_]+).*', line) if len(rr): cmds.append(rr[0].replace('_', '-')) f.close() return ' '.join(cmds) # --------------------------------------------------------------------- commands def cmd_commands(args_): print(commands()) def cmd_test(args_): parser = argparse.ArgumentParser(description='Test') parser.add_argument('blah', default='', help='The blah argument') args=parser.parse_args(args_) print("blah = " + args.blah) def cmd_ldlibpath(args_): parser = argparse.ArgumentParser(description='ldlibpath') parser.add_argument('module', nargs='*', help='Modules') args=parser.parse_args(args_) deps = get_modules_from_project_txt(args.module, 'pkg.required.jw', [ 'run', 'build' ], scope = 2, add_self=True, names_only=True) r = '' for m in deps: r = r + ':' + proj_dir(m) + '/lib' print(r[1:]) def cmd_exepath(args_): parser = argparse.ArgumentParser(description='exepath') parser.add_argument('module', nargs='*', help='Modules') args=parser.parse_args(args_) deps = get_modules_from_project_txt(args.module, 'pkg.required.jw', 'run', scope = 2, add_self=True, names_only=True) r = '' for m in deps: r = r + ':' + proj_dir(m) + '/bin' print(r[1:]) def cmd_libname(args_): parser = argparse.ArgumentParser(description='libname') parser.add_argument('module', nargs='*', help='Modules') args=parser.parse_args(args_) print(get_libname(args.module)) def cmd_ldflags(args_): parser = argparse.ArgumentParser(description='ldlibpath') parser.add_argument('module', nargs='*', help='Modules') args=parser.parse_args(args_) print(get_ldflags(args.module)) def cmd_cflags(args_): parser = argparse.ArgumentParser(description='cflags') parser.add_argument('module', nargs='*', help='Modules') args=parser.parse_args(args_) deps = get_modules_from_project_txt(args.module, 'pkg.required.jw', 'build', scope = 2, add_self=True, names_only=True) r = '' for m in reversed(deps): r = r + ' -I' + proj_dir(m) + '/include' print(r[1:]) def cmd_path(args_): parser = argparse.ArgumentParser(description='path') parser.add_argument('module', nargs='*', help='Modules') args=parser.parse_args(args_) deps = get_modules_from_project_txt(args.module, 'pkg.required.jw', 'run', scope = 2, add_self=True, names_only=True) r = '' for m in deps: r = r + ':' + proj_dir(m) + '/bin' print(r[1:]) def cmd_prereq(args_): parser = argparse.ArgumentParser(description='path') parser.add_argument('flavour', help='Flavour') parser.add_argument('module', nargs='*', help='Modules') args=parser.parse_args(args_) deps = get_modules_from_project_txt(args.module, 'pkg.required.jw', args.flavour, scope = 2, add_self=False, names_only=True) print(' '.join(deps)) def cmd_pkg_requires(args_): parser = argparse.ArgumentParser(description='pkg-requires') # TODO: implement Vendor evaluation parser.add_argument('vendor', help='Vendor', default='jw') parser.add_argument('flavour', help='Flavour') parser.add_argument('module', nargs='*', help='Modules') args=parser.parse_args(args_) r = [] for m in args.module: value = get_value(m, 'pkg.required.jw', args.flavour) if not value: continue deps = value.split(',') for spec in deps: dep = re.split('([=><]+)', spec) for i, item in enumerate(dep): dep[i] = item.strip() if len(dep) == 3: dep_project = re.sub(r'-devel$|-run$', '', dep[0]) version = get_value(dep_project, 'version', '') version_pattern=re.compile("[0-9-.]*") if dep[2] == 'VERSION': dep[2] = version.split('-')[0] elif dep[2] == 'VERSION-REVISION': dep[2] = version elif version_pattern.match(dep[2]): # dep[2] = dep[2] pass else: raise Exception("Unknown version specifier in " + spec) r.append(' '.join(dep)) print(', '.join(r)) def cmd_proj_dir(args_): parser = argparse.ArgumentParser(description='proj-dir') parser.add_argument('module', nargs='*', help='Modules') args=parser.parse_args(args_) r = [] for m in args.module: r.append(proj_dir(m)) print(' '.join(r)) def cmd_htdocs_dir(args_): parser = argparse.ArgumentParser(description='htdocs-dir') parser.add_argument('module', nargs='*', help='Modules') args=parser.parse_args(args_) r = [] for m in args.module: r.append(htdocs_dir(m)) print(' '.join(r)) def cmd_summary(args_): parser = argparse.ArgumentParser(description='summary') parser.add_argument('module', nargs='*', help='Modules') args=parser.parse_args(args_) r = [] for m in args.module: summary = get_value(m, "summary", None) if summary is not None: r.append(summary) print(' '.join(r)) def contains(small, big): for i in xrange(len(big)-len(small)+1): for j in xrange(len(small)): if big[i+j] != small[j]: break else: return i, i+len(small) return False def read_dep_graph(modules, section, graph): for m in modules: if m in graph: continue deps = get_modules_from_project_txt([ m ], 'pkg.required.jw', section, scope = 1, add_self=False, names_only=True) if not deps is None: graph[m] = deps for d in deps: read_dep_graph([ d ], section, graph) def flip_graph(graph): r = {} for m, deps in graph.iteritems(): for d in deps: if not d in r: r[d] = Set() r[d].add(m) return r def check_circular_deps(module, section, graph, unvisited, temp, path): if module in temp: debug('found circular dependency at module', module) return module if not module in unvisited: return None temp.add(module) if module in graph: for m in graph[module]: last = check_circular_deps(m, section, graph, unvisited, temp, path) if last is not None: path.insert(0, m) return last unvisited.remove(module) temp.remove(module) def cmd_check(args_): parser = argparse.ArgumentParser(description='check') parser.add_argument('module', nargs='*', help='Modules') parser.add_argument('--flavour', '-f', nargs='?', default = 'build') args=parser.parse_args(args_) graph = {} path = [] read_dep_graph(args.module, args.flavour, graph) unvisited = graph.keys() temp = Set() while len(unvisited) is not 0: m = unvisited[0] debug('checking circular dependency of', m) last = check_circular_deps(m, args.flavour, flip_graph(graph), unvisited, temp, path) if last is not None: debug('found circular dependency below', m, ', last is', last) print('found circular dependency in flavour', args.flavour, ':', ' -> '.join(path)) exit(1) print('no circular dependency found for flavour', args.flavour, ' in modules:', ' '.join(args.module)) exit(0) # -------------------------------------------------------------------- here we go global_args = [] skip = 0 for a in sys.argv[1::]: global_args.append(a) if a in [ '--prefix', '-p', '--topdir', '-t' ]: skip = 1 continue if skip > 0: skip = skip -1 continue if a[0] != '-': break topdir = None top_name = None parser = argparse.ArgumentParser(description='Project metadata evaluation') parser.add_argument('cmd', default='', help='Command') parser.add_argument('--debug', '-d', action='store_true', default=False, help='Output debug information to stderr') parser.add_argument('--topdir', '-t', nargs='?', help='Project Path') parser.add_argument('--prefix', '-p', nargs='?', default = expanduser("~") + '/local/src/cvs.stable/proj', help='Projects Path Prefix') parser.add_argument('arg', nargs='*', help='Command arguments') args=parser.parse_args(global_args) debug("----------------------------------------- running ", ' '.join(sys.argv)) projs_root = args.prefix if args.topdir: topdir = args.topdir top_name = read_value(topdir + '/make/project.conf', 'build', 'name') if not top_name: top_name = re.sub('-[0-9.-]*$', '', basename(realpath(topdir))) cmd = getattr(sys.modules[__name__], 'cmd_' + args.cmd.replace('-', '_')) cmd(sys.argv[(len(global_args) + 1)::])