|
|
6501920 |
#! /usr/bin/python3 -s
|
|
|
6501920 |
# Stripped down replacement for cargo2rpm parse-vendor-manifest
|
|
|
6501920 |
|
|
|
6501920 |
import re
|
|
|
6501920 |
import subprocess
|
|
|
6501920 |
import sys
|
|
|
6501920 |
from typing import Optional
|
|
|
6501920 |
|
|
|
6501920 |
|
|
|
6501920 |
VERSION_REGEX = re.compile(
|
|
|
6501920 |
r"""
|
|
|
6501920 |
^
|
|
|
6501920 |
(?P<major>0|[1-9]\d*)
|
|
|
6501920 |
\.(?P<minor>0|[1-9]\d*)
|
|
|
6501920 |
\.(?P<patch>0|[1-9]\d*)
|
|
|
6501920 |
(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?
|
|
|
6501920 |
(?:\+(?P<build>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
|
|
|
6501920 |
""",
|
|
|
6501920 |
re.VERBOSE,
|
|
|
6501920 |
)
|
|
|
6501920 |
|
|
|
6501920 |
|
|
|
6501920 |
class Version:
|
|
|
6501920 |
"""
|
|
|
6501920 |
Version that adheres to the "semantic versioning" format.
|
|
|
6501920 |
"""
|
|
|
6501920 |
|
|
|
6501920 |
def __init__(self, major: int, minor: int, patch: int, pre: Optional[str] = None, build: Optional[str] = None):
|
|
|
6501920 |
self.major: int = major
|
|
|
6501920 |
self.minor: int = minor
|
|
|
6501920 |
self.patch: int = patch
|
|
|
6501920 |
self.pre: Optional[str] = pre
|
|
|
6501920 |
self.build: Optional[str] = build
|
|
|
6501920 |
|
|
|
6501920 |
@staticmethod
|
|
|
6501920 |
def parse(version: str) -> "Version":
|
|
|
6501920 |
"""
|
|
|
6501920 |
Parses a version string and return a `Version` object.
|
|
|
6501920 |
Raises a `ValueError` if the string does not match the expected format.
|
|
|
6501920 |
"""
|
|
|
6501920 |
|
|
|
6501920 |
match = VERSION_REGEX.match(version)
|
|
|
6501920 |
if not match:
|
|
|
6501920 |
raise ValueError(f"Invalid version: {version!r}")
|
|
|
6501920 |
|
|
|
6501920 |
matches = match.groupdict()
|
|
|
6501920 |
|
|
|
6501920 |
major_str = matches["major"]
|
|
|
6501920 |
minor_str = matches["minor"]
|
|
|
6501920 |
patch_str = matches["patch"]
|
|
|
6501920 |
pre = matches["pre"]
|
|
|
6501920 |
build = matches["build"]
|
|
|
6501920 |
|
|
|
6501920 |
major = int(major_str)
|
|
|
6501920 |
minor = int(minor_str)
|
|
|
6501920 |
patch = int(patch_str)
|
|
|
6501920 |
|
|
|
6501920 |
return Version(major, minor, patch, pre, build)
|
|
|
6501920 |
|
|
|
6501920 |
def to_rpm(self) -> str:
|
|
|
6501920 |
"""
|
|
|
6501920 |
Formats the `Version` object as an equivalent RPM version string.
|
|
|
6501920 |
Characters that are invalid in RPM versions are replaced ("-" -> "_")
|
|
|
6501920 |
|
|
|
6501920 |
Build metadata (the optional `Version.build` attribute) is dropped, so
|
|
|
6501920 |
the conversion is not lossless for versions where this attribute is not
|
|
|
6501920 |
`None`. However, build metadata is not intended to be part of the
|
|
|
6501920 |
version (and is not even considered when doing version comparison), so
|
|
|
6501920 |
dropping it when converting to the RPM version format is correct.
|
|
|
6501920 |
"""
|
|
|
6501920 |
|
|
|
6501920 |
s = f"{self.major}.{self.minor}.{self.patch}"
|
|
|
6501920 |
if self.pre:
|
|
|
6501920 |
s += f"~{self.pre.replace('-', '_')}"
|
|
|
6501920 |
return s
|
|
|
6501920 |
|
|
|
6501920 |
|
|
|
6501920 |
def break_the_build(error: str):
|
|
|
6501920 |
"""
|
|
|
6501920 |
This function writes a string that is an invalid RPM dependency specifier,
|
|
|
6501920 |
which causes dependency generators to fail and break the build. The
|
|
|
6501920 |
additional error message is printed to stderr.
|
|
|
6501920 |
"""
|
|
|
6501920 |
|
|
|
6501920 |
print("*** FATAL ERROR ***")
|
|
|
6501920 |
print(error, file=sys.stderr)
|
|
|
6501920 |
|
|
|
6501920 |
|
|
|
6501920 |
def get_cargo_vendor_txt_paths_from_stdin() -> set[str]: # pragma nocover
|
|
|
6501920 |
"""
|
|
|
6501920 |
Read lines from standard input and filter out lines that look like paths
|
|
|
6501920 |
to `cargo-vendor.txt` files. This is how RPM generators pass lists of files.
|
|
|
6501920 |
"""
|
|
|
6501920 |
|
|
|
6501920 |
lines = {line.rstrip("\n") for line in sys.stdin.readlines()}
|
|
|
6501920 |
return {line for line in lines if line.endswith("/cargo-vendor.txt")}
|
|
|
6501920 |
|
|
|
6501920 |
|
|
|
6501920 |
def action_parse_vendor_manifest():
|
|
|
6501920 |
paths = get_cargo_vendor_txt_paths_from_stdin()
|
|
|
6501920 |
|
|
|
6501920 |
for path in paths:
|
|
|
6501920 |
with open(path) as file:
|
|
|
6501920 |
manifest = file.read()
|
|
|
6501920 |
|
|
|
6501920 |
for line in manifest.strip().splitlines():
|
|
|
6501920 |
crate, version = line.split(" v")
|
|
|
6501920 |
print(f"bundled(crate({crate})) = {Version.parse(version).to_rpm()}")
|
|
|
6501920 |
|
|
|
6501920 |
|
|
|
6501920 |
def main():
|
|
|
6501920 |
try:
|
|
|
6501920 |
action_parse_vendor_manifest()
|
|
|
6501920 |
exit(0)
|
|
|
6501920 |
|
|
|
6501920 |
# print an error message that is not a valid RPM dependency
|
|
|
6501920 |
# to cause the generator to break the build
|
|
|
6501920 |
except (IOError, ValueError) as exc:
|
|
|
6501920 |
break_the_build(str(exc))
|
|
|
6501920 |
exit(1)
|
|
|
6501920 |
|
|
|
6501920 |
break_the_build("Uncaught exception: This should not happen, please report a bug.")
|
|
|
6501920 |
exit(1)
|
|
|
6501920 |
|
|
|
6501920 |
|
|
|
6501920 |
if __name__ == "__main__":
|
|
|
6501920 |
main()
|