We compare the same versions a lot, for example downstream with upstream, or git with pacman repo.
176 lines
4.4 KiB
Python
176 lines
4.4 KiB
Python
# Copyright 2016-2020 Christoph Reiter
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
import re
|
|
import sys
|
|
import logging
|
|
from itertools import zip_longest
|
|
from typing import Any
|
|
|
|
|
|
logger = logging.getLogger('app')
|
|
|
|
# log INFO for everything to stdout
|
|
root = logging.getLogger()
|
|
root.setLevel(logging.INFO)
|
|
handler = logging.StreamHandler(sys.stdout)
|
|
handler.setLevel(logging.DEBUG)
|
|
handler.setFormatter(logging.Formatter('%(name)s.%(levelname)s: %(message)s'))
|
|
root.addHandler(handler)
|
|
# for the app itself, also log DEBUG
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
|
|
def vercmp(v1: str, v2: str) -> int:
|
|
if v1 == v2:
|
|
return 0
|
|
|
|
def cmp(a: Any, b: Any) -> int:
|
|
res = (a > b) - (a < b)
|
|
assert isinstance(res, int)
|
|
return res
|
|
|
|
def split(v: str) -> tuple[str, str, str | None]:
|
|
if "~" in v:
|
|
e, v = v.split("~", 1)
|
|
else:
|
|
e, v = ("0", v)
|
|
|
|
r: str | None = None
|
|
if "-" in v:
|
|
v, r = v.rsplit("-", 1)
|
|
else:
|
|
v, r = (v, None)
|
|
|
|
return (e, v, r)
|
|
|
|
digit, alpha, other = range(3)
|
|
|
|
def get_type(c: str) -> int:
|
|
assert c
|
|
if c.isdigit():
|
|
return digit
|
|
elif c.isalpha():
|
|
return alpha
|
|
else:
|
|
return other
|
|
|
|
def parse(v: str) -> list[tuple[int, str]]:
|
|
parts: list[tuple[int, str]] = []
|
|
current = ""
|
|
for c in v:
|
|
if not current:
|
|
current = c
|
|
else:
|
|
current_type = get_type(current)
|
|
if get_type(c) == current_type:
|
|
current += c
|
|
else:
|
|
parts.append((current_type, current))
|
|
current = c
|
|
|
|
if current:
|
|
parts.append((get_type(current), current))
|
|
|
|
return parts
|
|
|
|
def rpmvercmp(v1: str, v2: str) -> int:
|
|
if v1 == v2:
|
|
return 0
|
|
|
|
for x1, x2 in zip_longest(parse(v1), parse(v2), fillvalue=None):
|
|
if x1 is None:
|
|
assert x2 is not None
|
|
t2, p2 = x2
|
|
if t2 == alpha:
|
|
return 1
|
|
return -1
|
|
elif x2 is None:
|
|
assert x1 is not None
|
|
t1, p1 = x1
|
|
if t1 == alpha:
|
|
return -1
|
|
return 1
|
|
else:
|
|
t1, p1 = x1
|
|
t2, p2 = x2
|
|
|
|
if t1 != t2:
|
|
if t1 == digit:
|
|
return 1
|
|
elif t2 == digit:
|
|
return -1
|
|
elif t1 == other:
|
|
return 1
|
|
elif t2 == other:
|
|
return -1
|
|
elif t1 == other:
|
|
ret = cmp(len(p1), len(p2))
|
|
if ret != 0:
|
|
return ret
|
|
elif t1 == digit:
|
|
ret = cmp(int(p1), int(p2))
|
|
if ret != 0:
|
|
return ret
|
|
elif t1 == alpha:
|
|
ret = cmp(p1, p2)
|
|
if ret != 0:
|
|
return ret
|
|
|
|
return 0
|
|
|
|
e1, v1, r1 = split(v1)
|
|
e2, v2, r2 = split(v2)
|
|
|
|
ret = rpmvercmp(e1, e2)
|
|
if ret == 0:
|
|
ret = rpmvercmp(v1, v2)
|
|
if ret == 0 and r1 is not None and r2 is not None:
|
|
ret = rpmvercmp(r1, r2)
|
|
|
|
return ret
|
|
|
|
|
|
def extract_upstream_version(version: str) -> str:
|
|
return version.rsplit(
|
|
"-")[0].split("+", 1)[0].split("~", 1)[-1].split(":", 1)[-1]
|
|
|
|
|
|
def strip_vcs(package_name: str) -> str:
|
|
if package_name.endswith(
|
|
("-cvs", "-svn", "-hg", "-darcs", "-bzr", "-git")):
|
|
return package_name.rsplit("-", 1)[0]
|
|
return package_name
|
|
|
|
|
|
def arch_version_to_msys(v: str) -> str:
|
|
return v.replace(":", "~")
|
|
|
|
|
|
def version_is_newer_than(v1: str, v2: str) -> bool:
|
|
return vercmp(v1, v2) == 1
|
|
|
|
|
|
def split_depends(deps: list[str]) -> dict[str, set[str]]:
|
|
r: dict[str, set[str]] = {}
|
|
for d in deps:
|
|
parts = re.split("([<>=]+)", d, 1)
|
|
first = parts[0].strip()
|
|
second = "".join(parts[1:]).strip()
|
|
r.setdefault(first, set()).add(second)
|
|
return r
|
|
|
|
|
|
def split_optdepends(deps: list[str]) -> dict[str, set[str]]:
|
|
r: dict[str, set[str]] = {}
|
|
for d in deps:
|
|
if ":" in d:
|
|
a, b = d.split(":", 1)
|
|
a, b = a.strip(), b.strip()
|
|
else:
|
|
a, b = d.strip(), ""
|
|
e = r.setdefault(a, set())
|
|
if b:
|
|
e.add(b)
|
|
return r
|