Unify the project dependency walks in App and pkg_relations() #118
2 changed files with 111 additions and 79 deletions
|
|
@ -198,7 +198,28 @@ class App(Base):
|
|||
except FileNotFoundError:
|
||||
return None
|
||||
|
||||
def __get_project_refs(
|
||||
def read_dep_edges(
|
||||
self,
|
||||
name: str,
|
||||
sections: list[str],
|
||||
key: str,
|
||||
) -> list[str]:
|
||||
"""The deduplicated dependency values of name for the given
|
||||
sections and key (flavour), in section and value order.
|
||||
"""
|
||||
ret: list[str] = []
|
||||
for section in sections:
|
||||
vals = self.get_value(name, section, key)
|
||||
log(DEBUG, f'name={name}, section={section}, key={key}, deps={vals}')
|
||||
if not vals:
|
||||
continue
|
||||
for val in vals.split(','):
|
||||
val = val.strip()
|
||||
if (len(val)) and (val not in ret):
|
||||
ret.append(val)
|
||||
return ret
|
||||
|
||||
def walk_project_deps(
|
||||
self,
|
||||
buf: list[str],
|
||||
visited: set[str],
|
||||
|
|
@ -208,7 +229,21 @@ class App(Base):
|
|||
add_self: bool,
|
||||
scope: Scope,
|
||||
names_only: bool,
|
||||
*,
|
||||
check_installed: bool = True,
|
||||
exclude: set[str] = set(),
|
||||
recurse: bool = True,
|
||||
) -> None:
|
||||
"""Walk the dependency edges of sections and key (flavour)
|
||||
starting at spec, appending the visited specs to buf in
|
||||
postorder; buf and visited are updated in place.
|
||||
|
||||
check_installed applies the installed-package check, which
|
||||
skips the dependencies of an installed run package and fails
|
||||
on an unmet dependency. exclude names specs that are neither
|
||||
traversed nor appended. recurse = False stops the walk at the
|
||||
starting spec.
|
||||
"""
|
||||
dep = Dependency(spec)
|
||||
name = dep.base_name
|
||||
mod = dep.full_name
|
||||
|
|
@ -216,52 +251,44 @@ class App(Base):
|
|||
spec = name
|
||||
if spec in buf:
|
||||
return
|
||||
if spec in exclude:
|
||||
return
|
||||
if spec in visited:
|
||||
if add_self:
|
||||
buf.append(spec)
|
||||
return
|
||||
visited.add(spec)
|
||||
needed_subpackage = 'devel' if mod.endswith(('-dev', '-devel')) else 'run'
|
||||
if not self.is_installed(name, devel = True):
|
||||
if not mod.endswith(('-dev', '-devel')):
|
||||
if self.is_installed(name, devel = False):
|
||||
return
|
||||
raise Exception(
|
||||
f'Unmet dependency on {mod}: the -{needed_subpackage} package '
|
||||
f'of project {name} is not installed'
|
||||
)
|
||||
vals_list: list[str] = []
|
||||
for section in sections:
|
||||
vals = self.get_value(name, section, key)
|
||||
log(
|
||||
DEBUG,
|
||||
(
|
||||
f'name={name}, section={section}, key={key}, deps={vals}, '
|
||||
f'scope={scope.name}, visited={visited}'
|
||||
),
|
||||
)
|
||||
if not vals:
|
||||
continue
|
||||
for val in vals.split(','):
|
||||
val = val.strip()
|
||||
if (len(val)) and (val not in vals_list):
|
||||
vals_list.append(val)
|
||||
if check_installed:
|
||||
needed_subpackage = 'devel' if mod.endswith(('-dev', '-devel')) else 'run'
|
||||
if not self.is_installed(name, devel = True):
|
||||
if not mod.endswith(('-dev', '-devel')):
|
||||
if self.is_installed(name, devel = False):
|
||||
return
|
||||
raise Exception(
|
||||
f'Unmet dependency on {mod}: the -{needed_subpackage} package '
|
||||
f'of project {name} is not installed'
|
||||
)
|
||||
vals_list = self.read_dep_edges(name, sections, key)
|
||||
match scope:
|
||||
case Scope.Self:
|
||||
buf += vals_list
|
||||
case Scope.One | Scope.Subtree:
|
||||
subscope = scope.Self if scope == Scope.One else scope
|
||||
for val in vals_list:
|
||||
self.__get_project_refs(
|
||||
buf,
|
||||
visited,
|
||||
val,
|
||||
sections,
|
||||
key,
|
||||
add_self = True,
|
||||
scope = subscope,
|
||||
names_only = names_only,
|
||||
)
|
||||
if recurse:
|
||||
subscope = scope.Self if scope == Scope.One else scope
|
||||
for val in vals_list:
|
||||
self.walk_project_deps(
|
||||
buf,
|
||||
visited,
|
||||
val,
|
||||
sections,
|
||||
key,
|
||||
add_self = True,
|
||||
scope = subscope,
|
||||
names_only = names_only,
|
||||
check_installed = check_installed,
|
||||
exclude = exclude,
|
||||
recurse = recurse,
|
||||
)
|
||||
if add_self:
|
||||
buf.append(spec)
|
||||
|
||||
|
|
@ -567,7 +594,7 @@ class App(Base):
|
|||
visited: set[str] = set()
|
||||
for name in projects:
|
||||
rr: list[str] = []
|
||||
self.__get_project_refs(
|
||||
self.walk_project_deps(
|
||||
rr, visited, name, sections, key, add_self, scope, names_only
|
||||
)
|
||||
# TODO: this looks like a performance hogger
|
||||
|
|
|
|||
|
|
@ -52,47 +52,52 @@ def pkg_relations(
|
|||
|
||||
ret: list[str] = []
|
||||
for flavour in flavours: # build / release / run / devel
|
||||
cur_pkgs = seed_pkgs.copy()
|
||||
visited_pkgs: set[str] = set()
|
||||
while len(cur_pkgs):
|
||||
cur_pkg = cur_pkgs.pop(0)
|
||||
if cur_pkg in visited_pkgs or cur_pkg in ignore:
|
||||
continue
|
||||
closure: list[str] = []
|
||||
visited: set[str] = set()
|
||||
for seed in seed_pkgs:
|
||||
app.walk_project_deps(
|
||||
closure,
|
||||
visited,
|
||||
seed,
|
||||
['pkg.' + rel_type + '.jw'],
|
||||
flavour,
|
||||
add_self = True,
|
||||
scope = Scope.Subtree,
|
||||
names_only = True,
|
||||
check_installed = False,
|
||||
exclude = ignore,
|
||||
recurse = recursive,
|
||||
)
|
||||
for cur_pkg in closure:
|
||||
for subsec in subsections:
|
||||
section = 'pkg.' + rel_type + '.' + subsec
|
||||
visited_pkgs.add(cur_pkg)
|
||||
deps_spec = app.get_value(cur_pkg, section, flavour)
|
||||
if not deps_spec:
|
||||
continue
|
||||
for dep in Dependency.parse_deps_spec(
|
||||
deps_spec,
|
||||
lookup_version = app.get_version,
|
||||
dependent_package = cur_pkg,
|
||||
):
|
||||
dep_name = dep.base_name
|
||||
if dep_name in ignore or dep.full_name in ignore:
|
||||
continue
|
||||
if subsec == 'jw':
|
||||
if (recursive and dep_name not in visited_pkgs
|
||||
and dep_name not in cur_pkgs):
|
||||
cur_pkgs.append(dep_name)
|
||||
if hide_jw_pkg and dep_name == 'jw-pkg':
|
||||
for deps_spec in app.read_dep_edges(cur_pkg, [section], flavour):
|
||||
for dep in Dependency.parse_deps_spec(
|
||||
deps_spec,
|
||||
lookup_version = app.get_version,
|
||||
dependent_package = cur_pkg,
|
||||
):
|
||||
dep_name = dep.base_name
|
||||
if dep_name in ignore or dep.full_name in ignore:
|
||||
continue
|
||||
expand_version_macros = subsec == 'jw'
|
||||
if dont_expand_version_macros and dep_name in cur_pkgs:
|
||||
expand_version_macros = False
|
||||
if hide_self and dep_name in seed_pkgs:
|
||||
continue
|
||||
dep_str = dep.constraint_str(
|
||||
untemplated = expand_version_macros,
|
||||
include_revision = dont_strip_revision,
|
||||
as_range = expand_semver_revision_range,
|
||||
no_subpackages = no_subpackages,
|
||||
syntax = syntax,
|
||||
quote = '"' if quote else None,
|
||||
)
|
||||
if dep_str in ret:
|
||||
continue
|
||||
log(DEBUG, f'Appending dependency >{dep_str}<')
|
||||
ret.append(dep_str)
|
||||
if subsec == 'jw':
|
||||
if hide_jw_pkg and dep_name == 'jw-pkg':
|
||||
continue
|
||||
expand_version_macros = subsec == 'jw'
|
||||
if dont_expand_version_macros and dep_name in visited:
|
||||
expand_version_macros = False
|
||||
if hide_self and dep_name in seed_pkgs:
|
||||
continue
|
||||
dep_str = dep.constraint_str(
|
||||
untemplated = expand_version_macros,
|
||||
include_revision = dont_strip_revision,
|
||||
as_range = expand_semver_revision_range,
|
||||
no_subpackages = no_subpackages,
|
||||
syntax = syntax,
|
||||
quote = '"' if quote else None,
|
||||
)
|
||||
if dep_str in ret:
|
||||
continue
|
||||
log(DEBUG, f'Appending dependency >{dep_str}<')
|
||||
ret.append(dep_str)
|
||||
return ret
|
||||
|
|
|
|||
Loading…
Reference in a new issue