Files
2026-04-12 19:17:04 +02:00

140 lines
4.3 KiB
Python

from __future__ import annotations
import os
import re
import shlex
import subprocess
import sys
from shutil import which
from scikit_build_core._logging import rich_print as _rich_print
from setup import ROOT_DIR
def rich_print(*args):
if sys.platform.startswith("win"):
# Workaround for:
# KeyError: 'WinError'
# File "[...]\scikit_build_core\_logging.py", line 122, in <genexpr>
# lambda m: "".join(colors()[x] for x in m.group(1).split()),
return print(*args)
return _rich_print(*args)
class Versioning:
COMMAND = "{git} describe --tags --long --dirty"
GIT_BRANCH = "{git} rev-parse --abbrev-ref HEAD"
IS_TAGGED_CMD = "{git} tag --list --points-at=HEAD"
FMT_DEV = "{tag}.dev0"
FMT_TAGGED = "{tag}"
CMAKE_VERSION_R = r"set\(LIEF_VERSION_(MAJOR|MINOR|PATCH)\s(\d+)\)"
def __init__(self):
self._git = which("git")
def _exec_git_cmd(self, cmd: str, **kwargs) -> str:
args = dict(kwargs)
args["git"] = self._git
cmd_list = shlex.split(cmd.format(**args))
return subprocess.check_output(
cmd_list, text=True, cwd=ROOT_DIR.as_posix()
).strip()
def has_git(self) -> bool:
return self._git is not None and (ROOT_DIR / ".git").is_dir()
def get_branch(self) -> str | None:
if not self.has_git():
return None
try:
return self._exec_git_cmd(Versioning.GIT_BRANCH)
except subprocess.SubprocessError as e:
rich_print(f"[red]Error: {e}")
return None
def format_version(self, version: str, fmt: str, is_dev: bool = False):
branch = self.get_branch()
if branch is not None and branch.startswith("release-"):
_, version = branch.split("release-")
return version
if branch is not None and branch.startswith("release/"):
_, version = branch.split("release/")
major, minor, patch = version.split(".")
if patch == "x":
return f"{major}.{minor}"
return version
parts = version.split("-")
assert len(parts) in (3, 4)
dirty = len(parts) == 4
tag, count, sha = parts[:3]
MA, MI, PA = map(int, tag.split(".")) # 0.9.0 -> (0, 9, 0)
if is_dev:
if MI == 17:
# Special case for the 0.17.0 which is the last version of
# the 0.X.Y series
tag = "1.0.0"
else:
tag = f"{MA}.{MI + 1}.{0}"
if count == "0" and not dirty:
return tag
return fmt.format(tag=tag, gitsha=sha.lstrip("g"))
def version_from_git(self) -> str | None:
if not self.has_git():
return None
try:
is_tagged = self._exec_git_cmd(Versioning.IS_TAGGED_CMD) != ""
git_version = self._exec_git_cmd(Versioning.COMMAND)
if is_tagged:
return self.format_version(
version=git_version, fmt=Versioning.FMT_TAGGED
)
return self.format_version(
version=git_version, fmt=Versioning.FMT_DEV, is_dev=True
)
except Exception as e:
rich_print(f"[red]Error: {e}")
return None
def version_from_cmake(self) -> str | None:
main_cmake = ROOT_DIR / "CMakeLists.txt"
cmakefile = main_cmake.read_text()
major: int | None = None
minor: int | None = None
patch: int | None = None
itre = re.finditer(Versioning.CMAKE_VERSION_R, cmakefile, flags=re.MULTILINE)
for cmake_version in itre:
typ, value = cmake_version.groups()
typ = typ.lower()
if typ == "major":
major = int(value)
elif typ == "minor":
minor = int(value)
elif typ == "patch":
patch = int(value)
if any(e is None for e in (major, minor, patch)):
return None
return f"{major}.{minor}.{patch}"
def get_version(self) -> str:
if version := os.getenv("LIEF_VERSION_ENV"):
return version
if version := self.version_from_git():
return version
if version := self.version_from_cmake():
return version
raise RuntimeError("Can't determine version")