2025-11-16 18:05:10 +01:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
from argparse import Namespace, ArgumentParser
|
|
|
|
|
|
2026-01-25 15:18:27 +01:00
|
|
|
from ...lib.log import *
|
2025-11-16 18:05:10 +01:00
|
|
|
from ..Cmd import Cmd
|
2026-01-27 10:22:16 +01:00
|
|
|
from ..CmdProjects import CmdProjects
|
2025-11-16 18:05:10 +01:00
|
|
|
|
|
|
|
|
class BaseCmdPkgRelations(Cmd):
|
|
|
|
|
|
|
|
|
|
def pkg_relations(self, rel_type, args):
|
2026-03-15 10:00:56 +01:00
|
|
|
|
2025-11-16 18:05:10 +01:00
|
|
|
if args.subsections is None:
|
|
|
|
|
subsecs = self.app.os_cascade()
|
|
|
|
|
subsecs.append('jw')
|
|
|
|
|
else:
|
|
|
|
|
subsecs = args.subsections.split(',')
|
|
|
|
|
ignore = args.ignore.split(',')
|
|
|
|
|
flavours = args.flavour.split(',')
|
2026-03-15 10:00:56 +01:00
|
|
|
|
2026-03-15 10:13:26 +01:00
|
|
|
expand_semver_revision_range = args.expand_semver_revision_range
|
|
|
|
|
if args.syntax == 'debian':
|
|
|
|
|
expand_semver_revision_range = True
|
2026-03-15 10:00:56 +01:00
|
|
|
log(DEBUG, f'flavour="{args.flavour}", subsecs="{", ".join(subsecs)}", "ignore="{args.ignore}"')
|
|
|
|
|
|
|
|
|
|
version_pattern = re.compile("[0-9-.]*")
|
|
|
|
|
r: list[str] = []
|
2025-11-16 18:05:10 +01:00
|
|
|
for flavour in flavours:
|
|
|
|
|
for s in subsecs:
|
|
|
|
|
section = 'pkg.' + rel_type + '.' + s
|
|
|
|
|
visited = set()
|
|
|
|
|
modules = args.module.copy()
|
|
|
|
|
while len(modules):
|
|
|
|
|
m = modules.pop(0)
|
|
|
|
|
if m in visited or m in ignore:
|
|
|
|
|
continue
|
|
|
|
|
visited.add(m)
|
|
|
|
|
value = self.app.get_value(m, section, flavour)
|
|
|
|
|
if not value:
|
|
|
|
|
continue
|
|
|
|
|
deps = value.split(',')
|
|
|
|
|
for spec in deps:
|
|
|
|
|
dep = re.split('([=><]+)', spec)
|
2026-03-15 12:25:11 +01:00
|
|
|
if args.syntax == 'names-only':
|
2025-11-16 18:05:10 +01:00
|
|
|
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:
|
|
|
|
|
continue
|
|
|
|
|
if args.no_subpackages:
|
|
|
|
|
dep[0] = dep_name
|
|
|
|
|
for i, item in enumerate(dep):
|
|
|
|
|
dep[i] = item.strip()
|
|
|
|
|
if s == 'jw':
|
|
|
|
|
if args.recursive and not dep_name in visited and not dep_name in modules:
|
|
|
|
|
modules.append(dep_name)
|
|
|
|
|
if len(dep) == 3:
|
|
|
|
|
if args.dont_expand_version_macros and dep_name in args.module:
|
|
|
|
|
version = dep[2]
|
|
|
|
|
else:
|
|
|
|
|
version = self.app.get_value(dep_name, '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)
|
2026-03-15 10:13:26 +01:00
|
|
|
if not expand_semver_revision_range:
|
2026-03-15 10:00:56 +01:00
|
|
|
expanded_deps = [dep]
|
|
|
|
|
else:
|
|
|
|
|
expanded_deps = []
|
2026-03-15 10:13:26 +01:00
|
|
|
if expand_semver_revision_range and len(dep) == 3:
|
2026-03-15 10:00:56 +01:00
|
|
|
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(f'Expanding SemVer range "{dep[0]} {dep[1]} {dep[3]}" is not yet implemented')
|
|
|
|
|
for expanded_dep in expanded_deps:
|
2026-03-15 10:13:26 +01:00
|
|
|
match args.syntax:
|
|
|
|
|
case 'semver':
|
|
|
|
|
pass
|
|
|
|
|
case 'debian':
|
|
|
|
|
match expanded_dep[1]:
|
|
|
|
|
case '<':
|
|
|
|
|
expanded_dep[1] = '<<'
|
|
|
|
|
case '>':
|
|
|
|
|
expanded_dep[1] = '>>'
|
|
|
|
|
case '_':
|
|
|
|
|
raise NotImplementedError(f'Unknown dependency syntax "{args.syntax}" for dependency "{dep[0]} {dep[1]} {dep[3]}"')
|
2026-03-15 10:00:56 +01:00
|
|
|
dep_str = ' '.join(expanded_dep)
|
|
|
|
|
if not dep_str in r:
|
|
|
|
|
log(DEBUG, f'Appending dependency "{dep_str}"')
|
|
|
|
|
r.append(dep_str)
|
2025-11-16 18:05:10 +01:00
|
|
|
return args.delimiter.join(r)
|
|
|
|
|
|
|
|
|
|
def print_pkg_relations(self, rel_type, args_):
|
|
|
|
|
print(self.pkg_relations(rel_type, args_))
|
|
|
|
|
|
2026-01-27 10:22:16 +01:00
|
|
|
def __init__(self, parent: CmdProjects, relation: str, help: str) -> None:
|
|
|
|
|
super().__init__(parent, 'pkg-' + relation, help=help)
|
2025-11-16 18:05:10 +01:00
|
|
|
self.relation = relation
|
|
|
|
|
|
|
|
|
|
def add_arguments(self, parser: ArgumentParser) -> None:
|
|
|
|
|
super().add_arguments(parser)
|
|
|
|
|
parser.add_argument('-S', '--subsections', nargs='?', default=None, help='Subsections to consider, comma-separated')
|
|
|
|
|
parser.add_argument('-d', '--delimiter', nargs='?', default=', ', help='Output words delimiter')
|
|
|
|
|
parser.add_argument('flavour', help='Flavour')
|
|
|
|
|
parser.add_argument('module', nargs='*', help='Modules')
|
|
|
|
|
parser.add_argument('-p', '--no-subpackages', action='store_true',
|
|
|
|
|
default=False, help='Cut -run and -devel from package names')
|
|
|
|
|
parser.add_argument('--dont-strip-revision', action='store_true',
|
|
|
|
|
default=False, help='Always treat VERSION macro as VERSION-REVISION')
|
2026-03-15 10:00:56 +01:00
|
|
|
parser.add_argument('--expand-semver-revision-range', action='store_true',
|
|
|
|
|
default=False, help='Always treat =VERSION macro as >= VERSION-0 and < (VERSION+1)-0')
|
2026-03-15 12:25:11 +01:00
|
|
|
parser.add_argument('--syntax', choices=['semver', 'debian', 'names-only'],
|
2026-03-15 10:13:26 +01:00
|
|
|
default='semver', help='Output syntax')
|
2025-11-16 18:05:10 +01:00
|
|
|
parser.add_argument('--recursive', action='store_true',
|
|
|
|
|
default=False, help='Find dependencies recursively')
|
|
|
|
|
parser.add_argument('--dont-expand-version-macros', action='store_true',
|
|
|
|
|
default=False, help='Don\'t expand VERSION and REVISION macros')
|
|
|
|
|
parser.add_argument('--ignore', nargs='?', default='', help='Packages that '
|
|
|
|
|
'should be ignored together with their dependencies')
|
|
|
|
|
|
2026-01-27 14:31:25 +01:00
|
|
|
async def _run(self, args: Namespace) -> None:
|
2025-11-16 18:05:10 +01:00
|
|
|
return self.print_pkg_relations(self.relation, args)
|