534 lines
17 KiB
Python
534 lines
17 KiB
Python
|
|
#!/usr/bin/python -u
|
||
|
|||
from __future__ import print_function
|
|||
|
|
import sys
|
||
import argparse
|
|||
from sets import Set
|
|||
|
|
from os.path import isfile
|
||
|
|
from os.path import isdir
|
||
|
|
from os.path import expanduser
|
||
from os.path import basename
|
|||
from os.path import realpath
|
|||
|
|
import subprocess
|
||
|
|
import re
|
||
|
|
import platform
|
||
|
|
|
||
|
project.conf: Renamed pkg.required to pkg.required.jw
Signed-off-by: Jan Lindemann <jan@janware.com>
|
# 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
|
|||
|
|||
|
project.conf: Renamed pkg.required to pkg.required.jw
Signed-off-by: Jan Lindemann <jan@janware.com>
|
def re_section(name):
|
||
return re.compile('[' + name + ']'
|
|||
'.*?'
|
|||
'(?=[)',
|
|||
re.DOTALL)
|
|||
|
|||
|
|
def remove_duplicates(seq):
|
||
seen = set()
|
|||
seen_add = seen.add
|
|||
return [x for x in seq if not (x in seen or seen_add(x))]
|
|||
|
|||
def get_os(args = ""):
|
|||
|
|
for d in [ projs_root + '/ytools/devutil/scripts', '/opt/ytools/bin' ]:
|
||
|
|
script = d + '/get_os.sh'
|
||
if isfile(script):
|
|||
|
|
cmd = '/bin/bash ' + script
|
||
|
|
if args:
|
||
cmd = cmd + ' ' + args
|
|||
|
|
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
|
||
(out, rr) = p.communicate()
|
|||
if rr:
|
|||
err("failed to run ", cmd)
|
|||
continue
|
|||
|
|
out = re.sub('\n', '', out)
|
||
|
|
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()
|
|||
|
|
name = re.sub('-.*', '', os)
|
||
# e.g. os, linux, suse, suse-tumbleweed
|
|||
return [ 'os', platform.system().lower(), name, os ]
|
|||
|
project.conf: Renamed pkg.required to pkg.required.jw
Signed-off-by: Jan Lindemann <jan@janware.com>
|
|
||
|
|
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)
|
|||
|
|
|
||
|
|
def collect_values(names, section, key):
|
||
r = ""
|
|||
for n in names:
|
|||
val = get_value(n, section, key)
|
|||
if val:
|
|||
r = r + " " + val
|
|||
return remove_duplicates([x.strip() for x in r.split(",")])
|
|||
|
|||
|
|
# 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, exclude = []):
|
||
|
project.conf: Renamed pkg.required to pkg.required.jw
Signed-off-by: Jan Lindemann <jan@janware.com>
|
deps = get_modules_from_project_txt(names, 'pkg.required.jw', 'build',
|
||
scope = 2, add_self=True, names_only=True)
|
|||
r = ''
|
|||
for m in deps:
|
|||
|
|
if m in exclude:
|
||
continue
|
|||
libname = get_libname([m])
|
|||
if len(libname):
|
|||
r = r + ' -L' + proj_dir(m) + '/lib'
|
|||
print(r[1:])
|
|||
|
|||
|
|
def get_ldflags(names, exclude = []):
|
||
#print(names)
|
|||
|
project.conf: Renamed pkg.required to pkg.required.jw
Signed-off-by: Jan Lindemann <jan@janware.com>
|
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):
|
|||
|
|
if m in exclude:
|
||
continue
|
|||
|
|
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, exclude)
|
||
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_required_pkg(args_):
|
||
parser = argparse.ArgumentParser(description='required-pkg')
|
|||
parser.add_argument('module', nargs='*', help='Modules')
|
|||
|
|
parser.add_argument('--flavours', help='Dependency flavours', default='build')
|
||
|
|
args=parser.parse_args(args_)
|
||
|
|
deps = get_modules_from_project_txt(args.module, 'pkg.required.jw', args.flavours.split(),
|
||
|
|
scope = 2, add_self=True, names_only=True)
|
||
subsecs = pkg_required_os_cascade()
|
|||
|
|
debug("subsecs = ", subsecs)
|
||
|
|
required = []
|
||
for s in subsecs:
|
|||
|
|
vals = collect_values(deps, 'pkg.required.' + s, args.flavours)
|
||
|
|
if vals:
|
||
required = required + vals
|
|||
# TODO: add all not in build tree as -devel
|
|||
r = ''
|
|||
for m in required:
|
|||
r = r + ' ' + m
|
|||
print(r[1:])
|
|||
|
|||
|
|
def cmd_ldlibpath(args_):
|
||
parser = argparse.ArgumentParser(description='ldlibpath')
|
|||
parser.add_argument('module', nargs='*', help='Modules')
|
|||
args=parser.parse_args(args_)
|
|||
|
project.conf: Renamed pkg.required to pkg.required.jw
Signed-off-by: Jan Lindemann <jan@janware.com>
|
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_)
|
|||
|
project.conf: Renamed pkg.required to pkg.required.jw
Signed-off-by: Jan Lindemann <jan@janware.com>
|
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')
|
|||
|
|
parser.add_argument('--exclude', action='append', help='Exclude Modules', default=[])
|
||
|
|
args=parser.parse_args(args_)
|
||
|
|
print(get_ldflags(args.module, args.exclude))
|
||
|
|
|
||
def cmd_cflags(args_):
|
|||
parser = argparse.ArgumentParser(description='cflags')
|
|||
|
|
parser.add_argument('module', nargs='*', help='Modules')
|
||
args=parser.parse_args(args_)
|
|||
|
project.conf: Renamed pkg.required to pkg.required.jw
Signed-off-by: Jan Lindemann <jan@janware.com>
|
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_)
|
|||
|
project.conf: Renamed pkg.required to pkg.required.jw
Signed-off-by: Jan Lindemann <jan@janware.com>
|
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_)
|
|||
|
project.conf: Renamed pkg.required to pkg.required.jw
Signed-off-by: Jan Lindemann <jan@janware.com>
|
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', '-V', nargs='?', default='jw', help='Package Vendor')
|
|||
|
|
parser.add_argument('flavour', help='Flavour')
|
||
parser.add_argument('module', nargs='*', help='Modules')
|
|||
parser.add_argument('--dont-strip-revision', action='store_true',
|
|||
default=False, help='Always treat VERSION macro as VERSION-REVISION')
|
|||
parser.add_argument('--dont-expand-version-macros', action='store_true',
|
|||
default=False, help='Don\'t expand VERSION and REVISION macros')
|
|||
|
|
args=parser.parse_args(args_)
|
||
debug('flavour = ', args.flavour, ', vendor = ', args.vendor)
|
|||
version_pattern=re.compile("[0-9-.]*")
|
|||
|
|
r = []
|
||
for m in args.module:
|
|||
|
project.conf: Renamed pkg.required to pkg.required.jw
Signed-off-by: Jan Lindemann <jan@janware.com>
|
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])
|
|||
if args.dont_expand_version_macros and dep_project in args.module:
|
|||
version = dep[2]
|
|||
else:
|
|||
version = get_value(dep_project, 'version', '')
|
|||
|
|
if dep[2] == 'VERSION':
|
||
if args.dont_strip_revision:
|
|||
dep[2] = version
|
|||
else:
|
|||
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
|
|||
|
project.conf: Renamed pkg.required to pkg.required.jw
Signed-off-by: Jan Lindemann <jan@janware.com>
|
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/jw.dev/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)::])
|
||
|