diff --git a/src/python/jw/pkg/App.py b/src/python/jw/pkg/App.py index ae5682fe..ecaa8c9d 100644 --- a/src/python/jw/pkg/App.py +++ b/src/python/jw/pkg/App.py @@ -3,6 +3,8 @@ # This source code file is a merge of various build tools and a horrible mess. # +from typing import TypeAlias + import os, sys, argparse, pwd, re from functools import lru_cache from enum import Enum, auto @@ -10,12 +12,12 @@ from enum import Enum, auto from .lib.App import App as Base from .lib.log import * -# meaning of pkg.requires.xxx variables +# Meaning of pkg.requires.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 +# --------------------------------------------------------------------- Helpers class ResultCache(object): @@ -52,6 +54,8 @@ class Scope(Enum): One = auto() Subtree = auto() +Graph: TypeAlias = dict[str, set[str]] + # ----------------------------------------------------------------- class App class App(Base): @@ -85,7 +89,7 @@ class App(Base): return None raise Exception('No project path found for module "{}"'.format(name)) - def __find_dir(self, name: str, search_subdirs: list[str]=[], search_absdirs: list[str]=[], pretty: bool=True): + def __find_dir(self, name: str, search_subdirs: list[str]=[], search_absdirs: list[str]=[], pretty: bool=True) -> str|None: def format_pd(name: str, pd: str, pretty: bool): if not pretty: return pd @@ -114,226 +118,11 @@ class App(Base): return ret return None - def __init__(self): - super().__init__("jw-pkg swiss army knife", modules=["jw.pkg.cmds"]) - # -- Members without default values - self.__opt_os: str|None = None - self.__top_name: str|None = None - self.__os_cascade: list[str]|None = None - self.__res_cache = ResultCache() - self.__topdir: str|None = None - self.__pretty_topdir: str|None = None + def __get_project_refs_cached(self, buf, visited, spec, section, key, add_self, scope, names_only): + return self.__res_cache.run(self.__get_project_refs, [buf, visited, spec, section, key, add_self, scope, names_only]) - # -- Members with default values - self.__topdir_fmt = 'absolute' - self.__projs_root = pwd.getpwuid(os.getuid()).pw_dir + "/local/src/jw.dev/proj" - self.__pretty_projs_root = None - - def _add_arguments(self, parser): - super()._add_arguments(parser) - parser.add_argument('-t', '--topdir', default = None, help='Project Path') - parser.add_argument('--topdir-format', default = 'absolute', help='Output references to topdir as ' - + 'one of "make:", "unaltered", "absolute". Absolute topdir by default') - parser.add_argument('-p', '--prefix', default = None, - help='Parent directory of project source directories') - parser.add_argument('-O', '--os', default = None, help='Target operating system') - - async def _run(self, args: argparse.Namespace) -> None: - - self.__opt_os = args.os - self.__topdir = args.topdir - self.__pretty_topdir = self.__format_topdir(self.__topdir, args.topdir_format) - self.__topdir_fmt = args.topdir_format - if self.__topdir is not None: - self.__top_name = self.read_value(self.__topdir + '/make/project.conf', 'build', 'name') - if not self.__top_name: - self.__top_name = re.sub('-[0-9.-]*$', '', os.path.basename(os.path.realpath(self.__topdir))) - - if args.prefix is not None: - self.__projs_root = args.prefix - self.__pretty_projs_root = args.prefix - - return await super()._run(args) - - @property - def top_name(self): - return self.__top_name - - def find_dir(self, name: str, search_subdirs: list[str]=[], search_absdirs: list[str]=[], pretty: bool=True): - return self.__find_dir(name, search_subdirs, search_absdirs, pretty) - - def re_section(self, name): - return re.compile('[' + name + ']' - '.*?' - '(?=[)', - re.DOTALL) - - def remove_duplicates(self, seq): - seen = set() - seen_add = seen.add - return [x for x in seq if not (x in seen or seen_add(x))] - - @lru_cache(maxsize=None) - def get_os(self, args = ""): - import subprocess - for d in [ self.__projs_root + '/jw-pkg/scripts', '/opt/jw-pkg/bin' ]: - script = d + '/get-os.sh' - if os.path.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: - log(ERR, "failed to run ", cmd) - continue - out = re.sub('\n', '', out.decode('utf-8')) - return out - return "linux" - - # TODO: add support for customizing this in project.conf - def htdocs_dir(self, name: str) -> str: - return self.find_dir(name,["/src/html/htdocs", "/tools/html/htdocs", "/htdocs"], - ["/srv/www/proj/" + name]) - - # TODO: add support for customizing this in project.conf - def tmpl_dir(self, name: str) -> str: - return self.find_dir(name, ["/tmpl"], ["/opt/" + name + "/share/tmpl"]) - - def os_cascade(self): - import platform - if self.__os_cascade is not None: - return self.__os_cascade.copy() - r = [ 'os', platform.system().lower() ] - os = self.__opt_os if self.__opt_os is not None else self.get_os() - name = re.sub('-.*', '', os) - series = os - while True: - n = re.sub(r'\.[0-9]+$', '', series) - if n == series: - break - r.append(n) - series = n - if not name in r: - r.append(name) - if not os in r: - r.append(os) - # e.g. os, linux, suse, suse-tumbleweed - #return [ 'os', platform.system().lower(), name, os ] - self.__os_cascade = r - return r - - def strip_module_from_spec(self, mod): - return re.sub(r'-dev$|-devel$|-run$', '', re.split('([=><]+)', mod)[0].strip()) - - def get_section(self, 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() - - @lru_cache(maxsize=None) - def read_value(self, path, section, key): - - def scan_section(f, key): - if key is None: - r = '' - for line in f: - if len(line) and line[0] == '[': - break - r += line - return r if len(r) else None - lines = [] - cont_line = '' - for line in f: - if len(line) and line[0] == '[': - break - cont_line += line.rstrip() - if len(cont_line) and cont_line[-1] == '\\': - cont_line = cont_line[0:-1] - continue - lines.append(cont_line) - cont_line = '' - for line in lines: - #log(DEBUG, " looking for >%s< in line=>%s<" % (key, line)) - rr = re.findall('^ *' + key + ' *= *(.*)', line) - if len(rr) > 0: - return rr[0] - return None - - def scan_section_debug(f, key): - rr = scan_section(f, key) - #log(DEBUG, " returning", rr) - return rr - - try: - #log(DEBUG, "looking for {}::[{}].{}".format(path, section, key)) - with open(path, 'r') as f: - if not len(section): - rr = scan_section(f, key) - pat = '[' + section + ']' - for line in f: - if line.rstrip() == pat: - return scan_section(f, key) - return None - except: - log(DEBUG, path, "not found") - # TODO: handle this special case cleaner somewhere up the stack - if section == 'build' and key == 'libname': - return 'none' - return None - - def get_value(self, name, section, key): - log(DEBUG, "getting value [%s].%s for project %s (%s)" %(section, key, name, self.__top_name)) - if self.__top_name and name == self.__top_name: - proj_root = self.__topdir - else: - proj_root = self.__projs_root + '/' + name - log(DEBUG, "proj_root = " + proj_root) - - if section == 'version': - proj_version_dirs = [ proj_root ] - if proj_root != self.__topdir: - proj_version_dirs.append('/usr/share/doc/packages/' + name) - for d in proj_version_dirs: - version_path = d + '/VERSION' - try: - with open(version_path) as fd: - r = fd.read().replace('\n', '').replace('-dev', '') - fd.close() - return r - except EnvironmentError: - log(DEBUG, "ignoring unreadable file " + version_path) - continue - raise Exception("No version file found for project \"" + name + "\"") - - path = proj_root + '/make/project.conf' - #print('path = ', path, 'self.__top_name = ', self.__top_name, 'name = ', name) - return self.read_value(path, section, key) - - def collect_values(self, names, section, key): - r = "" - for n in names: - val = self.get_value(n, section, key) - if val: - r = r + " " + val - return self.remove_duplicates([x.strip() for x in r.split(",")]) - - def add_modules_from_project_txt_cached(self, buf, visited, spec, section, key, add_self, scope, names_only): - return self.__res_cache.run(self.add_modules_from_project_txt, [buf, visited, spec, section, key, add_self, scope, names_only]) - - def add_modules_from_project_txt(self, buf: list[str], visited: set[str], spec: str, - section: str, key: str, add_self: bool, scope: Scope, names_only: bool): + def __get_project_refs(self, buf: list[str], visited: set[str], spec: str, + section: str, key: str, add_self: bool, scope: Scope, names_only: bool) -> None: name = self.strip_module_from_spec(spec) if names_only: spec = name @@ -356,43 +145,304 @@ class App(Base): dep = dep.strip() if not(len(dep)): continue - self.add_modules_from_project_txt_cached(buf, visited, dep, + self.__get_project_refs_cached(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(self, names, sections, keys, add_self, scope, - names_only = True): + def __read_dep_graph(self, projects: list[str], section: str, graph: Graph) -> None: + for project in projects: + if project in graph: + continue + deps = self.get_project_refs([ project ], ['pkg.requires.jw'], section, + scope = Scope.One, add_self=False, names_only=True) + if not deps is None: + graph[project] = set(deps) + for dep in deps: + self.__read_dep_graph([ dep ], section, graph) + + def __flip_dep_graph(self, graph: Graph): + ret: Graph = {} + for project, deps in graph.items(): + for d in deps: + if not d in ret: + ret[d] = set() + ret[d].add(project) + return ret + + def __find_circular_deps_recursive(self, project: str, graph: Graph, unvisited: list[str], + temp: set[str], path: str) -> str|None: + if project in temp: + log(DEBUG, 'found circular dependency at project', project) + return project + if not project in unvisited: + return None + temp.add(project) + if project in graph: + for dep in graph[project]: + last = self.__find_circular_deps_recursive(dep, graph, unvisited, temp, path) + if last is not None: + path.insert(0, dep) + return last + unvisited.remove(project) + temp.remove(project) + + def __find_circular_deps(self, projects: list[str], flavours: list[str]) -> bool: + graph: Graph = {} + ret: list[str] = [] + self.__read_dep_graph(projects, flavours, graph) + unvisited = list(graph.keys()) + temp: set[str] = set() + while unvisited: + project = unvisited[0] + log(DEBUG, 'Checking circular dependency of', project) + last = self.__find_circular_deps_recursive(project, self.__flip_dep_graph(graph), unvisited, temp, ret) + if last is not None: + log(DEBUG, f'Found circular dependency below {project}, last is {last}') + return True + return False + + def __init__(self) -> None: + + super().__init__("jw-pkg swiss army knife", modules=["jw.pkg.cmds"]) + + # -- Members without default values + self.__opt_os: str|None = None + self.__top_name: str|None = None + self.__os_cascade: list[str]|None = None + self.__res_cache = ResultCache() + self.__topdir: str|None = None + self.__pretty_topdir: str|None = None + + # -- Members with default values + self.__topdir_fmt = 'absolute' + self.__projs_root = pwd.getpwuid(os.getuid()).pw_dir + "/local/src/jw.dev/proj" + self.__pretty_projs_root = None + + def _add_arguments(self, parser) -> None: + super()._add_arguments(parser) + parser.add_argument('-t', '--topdir', default = None, help='Project Path') + parser.add_argument('--topdir-format', default = 'absolute', help='Output references to topdir as ' + + 'one of "make:", "unaltered", "absolute". Absolute topdir by default') + parser.add_argument('-p', '--prefix', default = None, + help='Parent directory of project source directories') + parser.add_argument('-O', '--os', default = None, help='Target operating system') + + async def _run(self, args: argparse.Namespace) -> None: + self.__opt_os = args.os + self.__topdir = args.topdir + self.__pretty_topdir = self.__format_topdir(self.__topdir, args.topdir_format) + self.__topdir_fmt = args.topdir_format + if self.__topdir is not None: + self.__top_name = self.read_value(self.__topdir + '/make/project.conf', 'build', 'name') + if not self.__top_name: + self.__top_name = re.sub('-[0-9.-]*$', '', os.path.basename(os.path.realpath(self.__topdir))) + if args.prefix is not None: + self.__projs_root = args.prefix + self.__pretty_projs_root = args.prefix + return await super()._run(args) + + @property + def top_name(self): + return self.__top_name + + def find_dir(self, name: str, search_subdirs: list[str]=[], search_absdirs: list[str]=[], pretty: bool=True): + return self.__find_dir(name, search_subdirs, search_absdirs, pretty) + + @lru_cache(maxsize=None) + def get_os(self, args="") -> str: + import subprocess + for d in [ self.__projs_root + '/jw-pkg/scripts', '/opt/jw-pkg/bin' ]: + script = d + '/get-os.sh' + if os.path.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: + log(ERR, "failed to run ", cmd) + continue + out = re.sub('\n', '', out.decode('utf-8')) + return out + return "linux" + + # TODO: add support for customizing this in project.conf + def htdocs_dir(self, project: str) -> str: + return self.find_dir(project, ["/src/html/htdocs", "/tools/html/htdocs", "/htdocs"], + ["/srv/www/proj/" + project]) + + # TODO: add support for customizing this in project.conf + def tmpl_dir(self, name: str) -> str: + return self.find_dir(name, ["/tmpl"], ["/opt/" + name + "/share/tmpl"]) + + def os_cascade(self) -> list[str]: + import platform + if self.__os_cascade is None: + ret = [ 'os', platform.system().lower() ] + os = self.__opt_os if self.__opt_os is not None else self.get_os() + name = re.sub(r'-.*', '', os) + series = os + rx = re.compile(r'\.[0-9]+$') + while True: + n = re.sub(rx, '', series) + if n == series: + break + ret.append(n) + series = n + if not name in ret: + ret.append(name) + if not os in ret: + ret.append(os) + # e.g. os, linux, suse, suse-tumbleweed + #return [ 'os', platform.system().lower(), name, os ] + self.__os_cascade = ret + return self.__os_cascade + + def strip_module_from_spec(self, mod): + return re.sub(r'-dev$|-devel$|-run$', '', re.split('([=><]+)', mod)[0].strip()) + + @lru_cache(maxsize=None) + def get_section(self, path: str, section: str) -> str: + ret = '' + pat = '[' + section + ']' + in_section = False + file = open(path) + for line in file: + if (line.rstrip() == pat): + in_section = True + continue + if in_section: + if len(line) and line[0] == '[': + break + ret += line + file.close() + return ret.rstrip() + + @lru_cache(maxsize=None) + def read_value(self, path: str, section: str, key: str) -> str|None: + + def scan_section(f, key: str) -> str|None: + if key is None: + ret = '' + for line in f: + if len(line) and line[0] == '[': + break + ret += line + return ret if len(ret) else None + lines: list[str] = [] + cont_line = '' + for line in f: + if len(line) and line[0] == '[': + break + cont_line += line.rstrip() + if len(cont_line) and cont_line[-1] == '\\': + cont_line = cont_line[0:-1] + continue + lines.append(cont_line) + cont_line = '' + rx = re.compile(r'^\s*' + key + r'\s*=\s*(.*)\s*$') + for line in lines: + #log(DEBUG, " looking for >%s< in line=>%s<" % (key, line)) + m = re.search(rx, line) + if m is not None: + return m.group(1) + return None + + def scan_section_debug(f, key: str) -> str|None: + ret = scan_section(f, key) + #log(DEBUG, " returning", rr) + return ret + + try: + #log(DEBUG, "looking for {}::[{}].{}".format(path, section, key)) + with open(path, 'r') as f: + if not len(section): + rr = scan_section(f, key) + pat = '[' + section + ']' + for line in f: + if line.rstrip() == pat: + return scan_section(f, key) + return None + except: + log(DEBUG, path, "not found") + # TODO: handle this special case cleaner somewhere up the stack + if section == 'build' and key == 'libname': + return 'none' + return None + + @lru_cache(maxsize=None) + def get_value(self, project: str, section: str, key: str) -> str: + log(DEBUG, "getting value [%s].%s for project %s (%s)" %(section, key, project, self.__top_name)) + assert len(section) != 1 + if self.__top_name and project == self.__top_name: + proj_root = self.__topdir + else: + proj_root = self.__projs_root + '/' + project + if section == 'version': + proj_version_dirs = [ proj_root ] + if proj_root != self.__topdir: + proj_version_dirs.append('/usr/share/doc/packages/' + project) + for d in proj_version_dirs: + version_path = d + '/VERSION' + try: + with open(version_path) as fd: + ret = fd.read().replace('\n', '').replace('-dev', '') + fd.close() + return ret + except EnvironmentError: + log(DEBUG, f'"Ignoring unreadable file "{version_path}"') + continue + raise Exception(f'No version file found for project "{project}"') + + path = proj_root + '/make/project.conf' + #print('path = ', path, 'self.__top_name = ', self.__top_name, 'name = ', name) + return self.read_value(path, section, key) + + def get_values(self, projects: list[str], sections: list[str], keys: list[str]) -> list[str]: + """ + Collect a list of values from a list of given projects, sections and + keys, maintaining order + """ + ret: list[str] = [] + for p in projects: + for section in sections: + for key in keys: + vals = self.get_value(p, section, key) + if vals: + ret += [val.strip() for val in vals.split(",")] + return list(dict.fromkeys(ret)) # Remove duplicates, keep ordering + + def get_project_refs(self, projects: list[str], sections: list[str], + keys: str|list[str], add_self: bool, scope: Scope, names_only=True) -> list[str]: if isinstance(keys, str): keys = [ keys ] - #r = set() - r = [] + ret: list[str] = [] for section in sections: for key in keys: visited = set() - for name in names: - rr = [] - self.add_modules_from_project_txt_cached(rr, visited, name, section, key, add_self, scope, - names_only) + for name in projects: + rr: list[str] = [] + self.__get_project_refs_cached(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 + if not m in ret: + ret.append(m) + return ret - def get_libname(self, names): - vals = self.get_modules_from_project_txt(names, ['build'], 'libname', - scope = Scope.One, add_self=False, names_only=True) + def get_libname(self, projects) -> str: + vals = self.get_project_refs(projects, ['build'], 'libname', + scope = Scope.One, add_self=False, projects_only=True) if not vals: - return ' '.join(names) + return ' '.join(projects) if 'none' in vals: vals.remove('none') return ' '.join(reversed(vals)) - def is_excluded_from_build(self, module): - log(DEBUG, "checking if module " + module + " is excluded from build") - exclude = self.get_modules_from_project_txt([ module ], ['build'], 'exclude', + def is_excluded_from_build(self, project: str) -> str|None: + log(DEBUG, "checking if project " + project + " is excluded from build") + exclude = self.get_project_refs([ project ], ['build'], 'exclude', scope = Scope.One, add_self=False, names_only=True) cascade = self.os_cascade() + [ 'all' ] for p1 in exclude: @@ -401,47 +451,5 @@ class App(Base): return p1 return None - def contains(self, 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(self, modules, section, graph): - for m in modules: - if m in graph: - continue - deps = self.get_modules_from_project_txt([ m ], ['pkg.requires.jw'], section, - scope = Scope.One, add_self=False, names_only=True) - if not deps is None: - graph[m] = deps - for d in deps: - self.read_dep_graph([ d ], section, graph) - - def flip_graph(self, graph): - r = {} - for m, deps in graph.items(): - for d in deps: - if not d in r: - r[d] = set() - r[d].add(m) - return r - - def check_circular_deps(self, module, section, graph, unvisited, temp, path): - if module in temp: - log(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 = self.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 find_circular_deps(self, projects: list[str], flavours: list[str]) -> bool: + return self.__find_circular_deps(projects, flavours) diff --git a/src/python/jw/pkg/cmds/projects/CmdBuild.py b/src/python/jw/pkg/cmds/projects/CmdBuild.py index 190551b5..47ccdef6 100644 --- a/src/python/jw/pkg/cmds/projects/CmdBuild.py +++ b/src/python/jw/pkg/cmds/projects/CmdBuild.py @@ -37,7 +37,7 @@ class CmdBuild(Cmd): # export else: dep_cache[prereq_type]: dict[str, str] = {} - ret = self.app.get_modules_from_project_txt([ cur ], ['pkg.requires.jw'], + ret = self.app.get_project_refs([ cur ], ['pkg.requires.jw'], prereq_type, scope = Scope.Subtree, add_self=False, names_only=True) log(DEBUG, 'prerequisites = ' + ' '.join(ret)) if cur in ret: diff --git a/src/python/jw/pkg/cmds/projects/CmdCflags.py b/src/python/jw/pkg/cmds/projects/CmdCflags.py index cc32c1ae..913c899b 100644 --- a/src/python/jw/pkg/cmds/projects/CmdCflags.py +++ b/src/python/jw/pkg/cmds/projects/CmdCflags.py @@ -16,7 +16,7 @@ class CmdCflags(Cmd): # export parser.add_argument('module', nargs='*', help='Modules') async def _run(self, args: Namespace) -> None: - deps = self.app.get_modules_from_project_txt(args.module, ['pkg.requires.jw'], 'build', + deps = self.app.get_project_refs(args.module, ['pkg.requires.jw'], 'build', scope = Scope.Subtree, add_self=True, names_only=True) out = [] for m in reversed(deps): diff --git a/src/python/jw/pkg/cmds/projects/CmdCheck.py b/src/python/jw/pkg/cmds/projects/CmdCheck.py index 37dbcac8..92aabbef 100644 --- a/src/python/jw/pkg/cmds/projects/CmdCheck.py +++ b/src/python/jw/pkg/cmds/projects/CmdCheck.py @@ -17,19 +17,8 @@ class CmdCheck(Cmd): # export parser.add_argument('-f', '--flavour', nargs='?', default = 'build') async def _run(self, args: Namespace) -> None: - graph = {} - path = [] - self.app.read_dep_graph(args.module, args.flavour, graph) - unvisited = list(graph.keys()) - temp = set() - while len(unvisited) != 0: - m = unvisited[0] - log(DEBUG, 'Checking circular dependency of', m) - last = self.app.check_circular_deps(m, args.flavour, self.app.flip_graph(graph), unvisited, temp, path) - if last is not None: - log(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) + path = self.app.find_circular_deps(args.module, args.flavour) + if path: + print(f'Found circular dependency in flavour {args.flavour}:', ' -> '.join(path)) + exit(1) + print(f'No circular dependency found for flavour {args.flavour} in modules:', ' '.join(args.module)) diff --git a/src/python/jw/pkg/cmds/projects/CmdExepath.py b/src/python/jw/pkg/cmds/projects/CmdExepath.py index cdfb9188..6ae54625 100644 --- a/src/python/jw/pkg/cmds/projects/CmdExepath.py +++ b/src/python/jw/pkg/cmds/projects/CmdExepath.py @@ -16,7 +16,7 @@ class CmdExepath(Cmd): # export parser.add_argument('module', nargs='*', help='Modules') async def _run(self, args: Namespace) -> None: - deps = self.app.get_modules_from_project_txt(args.module, ['pkg.requires.jw'], [ 'run', 'build', 'devel' ], + deps = self.app.get_project_refs(args.module, ['pkg.requires.jw'], [ 'run', 'build', 'devel' ], scope = Scope.Subtree, add_self=True, names_only=True) out = [] for m in deps: diff --git a/src/python/jw/pkg/cmds/projects/CmdLdflags.py b/src/python/jw/pkg/cmds/projects/CmdLdflags.py index 5f55743e..5c2ca2b5 100644 --- a/src/python/jw/pkg/cmds/projects/CmdLdflags.py +++ b/src/python/jw/pkg/cmds/projects/CmdLdflags.py @@ -20,7 +20,7 @@ class CmdLdflags(Cmd): # export # -L needs to contain more paths than libs linked with -l would require def __get_ldpathflags(self, names: list[str], exclude: list[str] = []) -> str: - deps = self.app.get_modules_from_project_txt(names, ['pkg.requires.jw'], 'build', + deps = self.app.get_project_refs(names, ['pkg.requires.jw'], 'build', scope = Scope.Subtree, add_self=True, names_only=True) ret = [] for m in deps: @@ -38,7 +38,7 @@ class CmdLdflags(Cmd): # export return(' '.join(ret)) async def _run(self, args: Namespace) -> None: - deps = self.app.get_modules_from_project_txt(args.module, ['pkg.requires.jw'], 'build', + deps = self.app.get_project_refs(args.module, ['pkg.requires.jw'], 'build', scope = Scope.One, add_self=args.add_self, names_only=True) out = [] for m in reversed(deps): diff --git a/src/python/jw/pkg/cmds/projects/CmdLdlibpath.py b/src/python/jw/pkg/cmds/projects/CmdLdlibpath.py index c34dc763..c5fc3b16 100644 --- a/src/python/jw/pkg/cmds/projects/CmdLdlibpath.py +++ b/src/python/jw/pkg/cmds/projects/CmdLdlibpath.py @@ -16,7 +16,7 @@ class CmdLdlibpath(Cmd): # export parser.add_argument('module', nargs='*', help='Modules') async def _run(self, args: Namespace) -> None: - deps = self.app.get_modules_from_project_txt(args.module, ['pkg.requires.jw'], [ 'run', 'build', 'devel' ], + deps = self.app.get_project_refs(args.module, ['pkg.requires.jw'], [ 'run', 'build', 'devel' ], scope = Scope.Subtree, add_self=True, names_only=True) out = [] for m in deps: diff --git a/src/python/jw/pkg/cmds/projects/CmdPath.py b/src/python/jw/pkg/cmds/projects/CmdPath.py index 2fe9769a..d257f022 100644 --- a/src/python/jw/pkg/cmds/projects/CmdPath.py +++ b/src/python/jw/pkg/cmds/projects/CmdPath.py @@ -16,7 +16,7 @@ class CmdPath(Cmd): # export parser.add_argument('module', nargs='*', help='Modules') async def _run(self, args: Namespace) -> None: - deps = self.app.get_modules_from_project_txt(args.module, ['pkg.requires.jw'], 'run', + deps = self.app.get_project_refs(args.module, ['pkg.requires.jw'], 'run', scope = Scope.Subtree, add_self=True, names_only=True) out = [] for m in deps: diff --git a/src/python/jw/pkg/cmds/projects/CmdPrereq.py b/src/python/jw/pkg/cmds/projects/CmdPrereq.py index fe3d9d69..ad75fb38 100644 --- a/src/python/jw/pkg/cmds/projects/CmdPrereq.py +++ b/src/python/jw/pkg/cmds/projects/CmdPrereq.py @@ -18,6 +18,6 @@ class CmdPrereq(Cmd): # export parser.add_argument('module', nargs='*', help='Modules') async def _run(self, args: Namespace) -> None: - deps = self.app.get_modules_from_project_txt(args.module, ['pkg.requires.jw'], + deps = self.app.get_project_refs(args.module, ['pkg.requires.jw'], args.flavour, scope = Scope.Subtree, add_self=False, names_only=True) print(' '.join(deps)) diff --git a/src/python/jw/pkg/cmds/projects/CmdPythonpath.py b/src/python/jw/pkg/cmds/projects/CmdPythonpath.py index 115ceb5f..3d95fdc3 100644 --- a/src/python/jw/pkg/cmds/projects/CmdPythonpath.py +++ b/src/python/jw/pkg/cmds/projects/CmdPythonpath.py @@ -16,7 +16,7 @@ class CmdPythonpath(Cmd): # export p.add_argument('module', help='Modules', nargs='*') async def _run(self, args: Namespace) -> None: - deps = self.app.get_modules_from_project_txt(args.module, ['pkg.requires.jw'], [ 'run', 'build' ], + deps = self.app.get_project_refs(args.module, ['pkg.requires.jw'], [ 'run', 'build' ], scope = Scope.Subtree, add_self=True, names_only=True) out = [] for m in deps: diff --git a/src/python/jw/pkg/cmds/projects/CmdPythonpathOrig.py b/src/python/jw/pkg/cmds/projects/CmdPythonpathOrig.py index e35157cb..18a98a40 100644 --- a/src/python/jw/pkg/cmds/projects/CmdPythonpathOrig.py +++ b/src/python/jw/pkg/cmds/projects/CmdPythonpathOrig.py @@ -16,7 +16,7 @@ class CmdPythonpathOrig(Cmd): # export parser.add_argument('module', nargs='*', help='Modules') async def _run(self, args: Namespace) -> None: - deps = self.app.get_modules_from_project_txt(args.module, ['pkg.requires.jw'], [ 'run', 'build' ], + deps = self.app.get_project_refs(args.module, ['pkg.requires.jw'], [ 'run', 'build' ], scope = Scope.Subtree, add_self=True, names_only=True) r = '' for m in deps: diff --git a/src/python/jw/pkg/cmds/projects/CmdRequiredOsPkg.py b/src/python/jw/pkg/cmds/projects/CmdRequiredOsPkg.py index 53421467..28e8b21a 100644 --- a/src/python/jw/pkg/cmds/projects/CmdRequiredOsPkg.py +++ b/src/python/jw/pkg/cmds/projects/CmdRequiredOsPkg.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from typing import Iterable from argparse import Namespace, ArgumentParser from ...App import Scope @@ -29,7 +30,7 @@ class CmdRequiredOsPkg(Cmd): # export # TODO: This adds too much. Only the run dependencies of the build dependencies would be needed. flavours.append('run') log(DEBUG, "flavours = " + args.flavours) - deps = self.app.get_modules_from_project_txt(modules, ['pkg.requires.jw'], flavours, + deps = self.app.get_project_refs(modules, ['pkg.requires.jw'], flavours, scope = Scope.Subtree, add_self=True, names_only=True) if args.skip_excluded: for d in deps: @@ -38,15 +39,12 @@ class CmdRequiredOsPkg(Cmd): # export subsecs = self.app.os_cascade() log(DEBUG, "subsecs = ", subsecs) requires = [] - for s in subsecs: - for f in flavours: - vals = self.app.collect_values(deps, 'pkg.requires.' + s, f) + for sec in subsecs: + for flavour in flavours: + vals = self.app.get_values(deps, ['pkg.requires.' + sec], [flavour]) if vals: requires = requires + vals if args.quote: requires = [f'"{dep}"' for dep in requires] # TODO: add all not in build tree as -devel - r = '' - for m in requires: - r = r + ' ' + m - print(r[1:]) + print(' '.join(requires))