From 0c808f93833a505b989694ffe23a5313e8d4d6cb Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Wed, 9 Sep 2026 14:44:37 +0200 Subject: [PATCH] cmds.projects.lib.pkg_relations: Integrate lib.version - The pkg_relations() function is a horribly bad read. It contains a mind-bending amount of interwoven case distinctions that are not clearly reflected in the participating variable and function names. Much of it is version handling. That's intricate by nature, but much of it has now been implemented in the lib.version module, so moving the logic there makes the function a good deal more readable than before. That's most of what this commit does. - Use version syntax macros from lib.version instead of pkg_relations-defined macros, and fix the fallout in BaseCmdPkgRelations and CmdCreateFile. - Use lib.version also as a central place for dep string parsing from App. Signed-off-by: Jan Lindemann --- src/python/jw/pkg/App.py | 5 +- .../pkg/cmds/projects/BaseCmdPkgRelations.py | 8 +- .../jw/pkg/cmds/projects/CmdCreateFile.py | 5 +- .../jw/pkg/cmds/projects/lib/pkg_relations.py | 141 +++++------------- 4 files changed, 46 insertions(+), 113 deletions(-) diff --git a/src/python/jw/pkg/App.py b/src/python/jw/pkg/App.py index 07f23d2d..fe93c585 100644 --- a/src/python/jw/pkg/App.py +++ b/src/python/jw/pkg/App.py @@ -11,10 +11,11 @@ from enum import Enum, auto from functools import cache from typing import TYPE_CHECKING, override -from .lib.ExecApp import ExecApp as Base from .lib.Distro import Distro +from .lib.ExecApp import ExecApp as Base from .lib.log import DEBUG, ERR, log from .lib.ProjectConf import ProjectConf +from .lib.version.Dependency import Dependency if TYPE_CHECKING: import argparse @@ -435,7 +436,7 @@ class App(Base): return self.find_dir(name, ['/tmpl'], ['/opt/' + name + '/share/tmpl']) def strip_module_from_spec(self, mod: str) -> str: - return re.sub(r'-dev$|-devel$|-run$', '', re.split('([=><]+)', mod)[0].strip()) + return Dependency(mod).base_name @cache def get_value(self, project: str, section: str, key: str) -> str | None: diff --git a/src/python/jw/pkg/cmds/projects/BaseCmdPkgRelations.py b/src/python/jw/pkg/cmds/projects/BaseCmdPkgRelations.py index 66004f96..98c42c27 100644 --- a/src/python/jw/pkg/cmds/projects/BaseCmdPkgRelations.py +++ b/src/python/jw/pkg/cmds/projects/BaseCmdPkgRelations.py @@ -4,8 +4,8 @@ import re from typing import TYPE_CHECKING, cast, override +from ...lib.version.base import Syntax from .Cmd import Cmd, Parent -from .lib.pkg_relations import VersionSyntax from .lib.pkg_relations import pkg_relations as pkg_relations_list if TYPE_CHECKING: @@ -30,7 +30,11 @@ class BaseCmdPkgRelations(Cmd): no_subpackages = args.no_subpackages, dont_strip_revision = args.dont_strip_revision, expand_semver_revision_range = args.expand_semver_revision_range, - syntax = VersionSyntax[args.syntax.replace('-', '_')], + syntax = { + 'semver': Syntax.SEM_VER, + 'debian': Syntax.DEBIAN, + 'names-only': Syntax.NAMES_ONLY, + }[args.syntax], recursive = args.recursive, dont_expand_version_macros = args.dont_expand_version_macros, ignore = set(re.split(self.arg_sep, args.ignore)), diff --git a/src/python/jw/pkg/cmds/projects/CmdCreateFile.py b/src/python/jw/pkg/cmds/projects/CmdCreateFile.py index b852ac80..dfe5b95f 100644 --- a/src/python/jw/pkg/cmds/projects/CmdCreateFile.py +++ b/src/python/jw/pkg/cmds/projects/CmdCreateFile.py @@ -3,8 +3,9 @@ from enum import Enum, auto from typing import override from ...lib.log import WARNING, log +from ...lib.version.base import Syntax from .Cmd import Cmd, Parent -from .lib.pkg_relations import VersionSyntax, pkg_relations +from .lib.pkg_relations import pkg_relations from .lib.templates import ListDict, RenderValues, tmpl_render def key_value(s: str) -> tuple[str, str]: @@ -36,7 +37,7 @@ class CmdCreateFile(Cmd): # export flavours = ['run'], subsections = ['jw'], seed_pkgs = [module], - syntax = VersionSyntax.names_only, + syntax = Syntax.NAMES_ONLY, no_subpackages = True, recursive = True, quote = False, diff --git a/src/python/jw/pkg/cmds/projects/lib/pkg_relations.py b/src/python/jw/pkg/cmds/projects/lib/pkg_relations.py index 2b470d79..9a14fbe9 100644 --- a/src/python/jw/pkg/cmds/projects/lib/pkg_relations.py +++ b/src/python/jw/pkg/cmds/projects/lib/pkg_relations.py @@ -1,14 +1,7 @@ -import re - -from enum import Enum, auto - from ....App import App, Scope from ....lib.log import DEBUG, log - -class VersionSyntax(Enum): - semver = auto() - debian = auto() - names_only = auto() +from ....lib.version.base import Syntax +from ....lib.version.Dependency import Dependency def pkg_relations( app: App, @@ -20,7 +13,7 @@ def pkg_relations( no_subpackages: bool = False, dont_strip_revision: bool = False, expand_semver_revision_range: bool = False, - syntax: VersionSyntax = VersionSyntax.semver, + syntax: Syntax = Syntax.SEM_VER, recursive: bool = False, dont_expand_version_macros: bool = False, ignore: set[str] = set(), @@ -34,8 +27,7 @@ def pkg_relations( subsections = app.distro.os_cascade subsections.append('jw') - expand_semver_revision_range = expand_semver_revision_range - if syntax == VersionSyntax.debian: + if syntax == Syntax.DEBIAN: expand_semver_revision_range = True if skip_excluded: @@ -58,113 +50,48 @@ def pkg_relations( ), ) - version_pattern = re.compile('[0-9-.]*') ret: list[str] = [] for flavour in flavours: # build / release / run / devel cur_pkgs = seed_pkgs.copy() - visited = set() + visited_pkgs: set[str] = set() while len(cur_pkgs): cur_pkg = cur_pkgs.pop(0) - if cur_pkg in visited or cur_pkg in ignore: + if cur_pkg in visited_pkgs or cur_pkg in ignore: continue for subsec in subsections: - version: str | None = None section = 'pkg.' + rel_type + '.' + subsec - visited.add(cur_pkg) - value = app.get_value(cur_pkg, section, flavour) - if not value: + visited_pkgs.add(cur_pkg) + deps_spec = app.get_value(cur_pkg, section, flavour) + if not deps_spec: continue - deps = value.split(',') - for spec in deps: - dep = re.split('([=><]+)', spec) - if syntax == VersionSyntax.names_only: - dep = dep[:1] - dep = list(map(str.strip, dep)) - dep_name = re.sub('-dev$|-devel$|-run$', '', dep[0]) - if dep_name in ignore or dep[0] in ignore: + for dep in Dependency.parse_deps_spec( + deps_spec, + lookup_version = app.get_version, + ): + dep_name = dep.base_name + if dep_name in ignore or dep.full_name in ignore: continue - if no_subpackages: - dep[0] = dep_name - for i, item in enumerate(dep): - dep[i] = item.strip() if subsec == 'jw': - if (recursive and dep_name not in visited + 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: + if hide_jw_pkg and dep_name == 'jw-pkg': continue - if len(dep) == 3: - if dont_expand_version_macros and dep_name in cur_pkgs: - version = dep[2] - else: - version = app.get_version(dep_name) - if dep[2] == 'VERSION': - if 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) - if len(dep) != 3 or not expand_semver_revision_range: - expanded_deps = [dep] - else: - assert version is not None - expanded_deps = [] - semver = re.split(r'[.-]', version) - if len(semver) != 4: - expanded_deps = [dep] - else: - release = int(semver[2]) - major_minor = f'{semver[0]}.{semver[1]}' - match dep[1]: - case '>' | '>=': - expanded_deps.append([dep[0], dep[1], dep[2]]) - expanded_deps.append( - [dep[0], '<', f'{major_minor}.{release + 1}'] - ) - case '<' | '<=': - expanded_deps.append([dep[0], dep[1], dep[2]]) - case '=': - expanded_deps.append( - [dep[0], '>=', f'{major_minor}.{release}'] - ) - expanded_deps.append( - [dep[0], '<', f'{major_minor}.{release + 1}'] - ) - case _: - raise NotImplementedError( - ( - 'Expanding SemVer range ' - f'"{dep[0]} {dep[1]} {dep[3]}" ' - 'is not yet implemented' - ) - ) - for expanded_dep in expanded_deps: - if hide_self and dep_name in seed_pkgs: - continue - match syntax: - case VersionSyntax.semver: - pass - case VersionSyntax.names_only: - pass - case VersionSyntax.debian: - if len(expanded_dep) == 3: - match expanded_dep[1]: - case '<': - expanded_dep[1] = '<<' - case '>': - expanded_dep[1] = '>>' - case _: - pass - dep_str = ' '.join(expanded_dep) - if quote: - dep_str = '"' + dep_str + '"' - if dep_str not in ret: - log(DEBUG, f'Appending dependency >{dep_str}<') - ret.append(dep_str) + 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) return ret